Translate Purpose-built Apps for Multiple Languages
Summary
Task: Configure multi-language support for app names, tab names, and UI text
Time estimate: 20-30 minutes
Skill level: Intermediate
Prerequisites
- Purpose-built App project initialized
- Understanding of plugin translation files
- List of target languages
- Translations prepared for all user-visible text
Steps
Step 1: Identify Fields to Translate
Purpose-built Apps have several user-visible fields that require translation:
| Field | Location | Translation Method |
|---|---|---|
| App name | Plugin configuration | name property in translation file |
| Tab names | Tab configuration | nameKey property + translation file |
| Tab titles (config page) | Tab configuration settings | tabTitle.key property + translation file |
| Tab descriptions (config page) | Tab configuration settings | tabDescription.key property + translation file |
| Custom Block strings | Custom Block plugins | Custom Block translation files |
Step 2: Create Plugin Translation Files
Create or update your plugin's translation files. The platform only loads translations from the public/translations/ folder in your plugin, and each file is named [language-code].json.
File structure:
your-plugin/
├── config.json
└── public/
└── translations/
├── en.json
├── es.json
├── fr.json
└── de.json
Example en.json:
{
"name": "Customer Support Portal",
"homeTab": "Home",
"ticketsTab": "Support Tickets",
"reportsTab": "Analytics",
"homeTabTitle": "Home Dashboard",
"homeTabDescription": "Overview of support metrics and recent activity",
"ticketsTabTitle": "Support Tickets",
"ticketsTabDescription": "View and manage customer support tickets"
}
Example es.json:
{
"name": "Portal de Atención al Cliente",
"homeTab": "Inicio",
"ticketsTab": "Tickets de Soporte",
"reportsTab": "Analíticas",
"homeTabTitle": "Panel de Inicio",
"homeTabDescription": "Resumen de métricas de soporte y actividad reciente",
"ticketsTabTitle": "Tickets de Soporte",
"ticketsTabDescription": "Ver y administrar tickets de atención al cliente"
}
Step 3: Configure App Name Translation
The app name uses the name property from your translation files. This is automatically handled by the plugin system - no additional configuration needed in config.json.
In your translation files:
// public/translations/en.json
{
"name": "Customer Support Portal"
}
// public/translations/es.json
{
"name": "Portal de Atención al Cliente"
}
// public/translations/fr.json
{
"name": "Portail de Support Client"
}
Step 4: Configure Tab Name Translation
Set the nameKey property for each tab to reference a translation key:
{
"metadata": {
"tabs": [
{
"nameKey": "homeTab",
"type": "content",
"href": "/dashboard"
},
{
"nameKey": "ticketsTab",
"type": "content",
"href": "/tickets"
},
{
"nameKey": "reportsTab",
"type": "content",
"href": "/reports"
}
]
}
}
Corresponding translation entries:
{
"homeTab": "Home",
"ticketsTab": "Support Tickets",
"reportsTab": "Analytics"
}
Step 5: Configure Configuration Page Translations
For the app configuration page, translate tab titles and descriptions using the key property:
{
"metadata": {
"tabs": [
{
"nameKey": "homeTab",
"configurationSettings": {
"configurable": true,
"tabTitle": {
"key": "homeTabTitle"
},
"tabDescription": {
"key": "homeTabDescription"
}
}
},
{
"nameKey": "ticketsTab",
"configurationSettings": {
"configurable": true,
"tabTitle": {
"key": "ticketsTabTitle"
},
"tabDescription": {
"key": "ticketsTabDescription"
}
}
}
]
}
}
tabTitle/tabDescription (and their Key counterparts) live under each tab's configurationSettings object, not on the tab directly.
Corresponding translation entries:
{
"homeTabTitle": "Home Dashboard",
"homeTabDescription": "Overview of support metrics and recent activity",
"ticketsTabTitle": "Support Tickets",
"ticketsTabDescription": "View and manage customer support tickets"
}
Step 6: Translate Custom Block Strings
Custom Blocks use a separate, generic Extension SDK translation mechanism (the Plugin Client's getText), not the PBA-specific nameKey/tabTitleKey system used in the rest of this guide. Their translation files still live under public/translations/, but are named with an uppercase language code (EN.json, ES.json) and use a { meta, strings } shape instead of flat key-value pairs.
Custom Block translation structure:
custom-block-plugin/
├── config.json
├── public/
│ └── translations/
│ ├── EN.json
│ └── ES.json
└── src/
└── App.tsx
Example public/translations/EN.json:
{
"meta": {
"summary": "Custom Block Language Strings - EN"
},
"strings": {
"welcomeMessage": {
"text": "Welcome",
"context": "Heading shown at the top of the custom block"
},
"instructions": {
"text": "Follow the steps below",
"context": "Instructional text below the heading"
}
}
}
In Custom Block code:
// `client` is passed into your component via props
function MyCustomBlock({ client }) {
return (
<div>
<h1>{client.getText('welcomeMessage')}</h1>
<p>{client.getText('instructions')}</p>
</div>
);
}
See getText in the Plugin Client reference for the full API, and the Custom Block documentation for complete translation guidance.
Step 7: Test Translations
- Save all translation files and
config.json - Rebuild and deploy your Purpose-built App
- Log in to Qualtrics with different language settings
- Verify app name appears in correct language
- Check that tab names translate properly
- Review configuration page translations
- Test Custom Block translations (if applicable)
Checkpoint: Your Purpose-built App should display in the user's preferred language across all components.
Common Issues and Solutions
Issue: Translations Not Appearing
Symptoms: Text shows as translation keys (e.g., homeTab instead of "Home")
Causes:
- Missing translation file for user's language
- Typo in translation key
- Translation key not defined in translation file
Solution:
Verify translation key exists in all language files:
// ❌ Missing in public/translations/es.json
{
"homeTab": "Home"
}
// ✅ Present in all files
// public/translations/en.json
{"homeTab": "Home"}
// public/translations/es.json
{"homeTab": "Inicio"}
Ensure key names match exactly between config.json and translation files:
// config.json
"nameKey": "homeTab"
// public/translations/en.json
"homeTab": "Home" // ✅ Matches
"home_tab": "Home" // ❌ Doesn't match
Issue: Some Languages Work, Others Don't
Symptoms: English works but Spanish shows keys
Causes:
- Missing translation file for specific language
- Incomplete translations in language file
Solution:
Ensure all language files exist and have complete translations:
public/translations/en.json ✅
public/translations/es.json ✅
public/translations/fr.json ✅
public/translations/de.json ❌ Missing
Verify each language file has all keys:
// public/translations/en.json
{
"homeTab": "Home",
"ticketsTab": "Tickets",
"reportsTab": "Reports"
}
// public/translations/es.json - ❌ Missing reportsTab
{
"homeTab": "Inicio",
"ticketsTab": "Tickets"
}
Issue: Fallback Language Not Working
Symptoms: Missing translations show keys instead of English fallback
Causes:
- No fallback language configured
- Primary translation file missing
Solution:
Always provide public/translations/en.json as the fallback:
public/translations/en.json ✅ Required - base language
public/translations/es.json Optional
public/translations/fr.json Optional
Alternative Approaches
Using Value Instead of Key
For simple apps or testing, you can use direct values instead of translation keys:
{
"tabTitle": {
"value": "Home Dashboard" // Direct value, not translated
}
}
Appropriate for:
- Single-language deployments
- Rapid prototyping
- Non-translated content (proper nouns, brand names)
Hybrid Approach
Mix translated and non-translated content:
{
"tabs": [
{
"nameKey": "homeTab", // Translated
"tabTitle": {
"value": "Acme Corp Portal" // Not translated (brand name)
}
}
]
}
Best Practices
Translation Management
- Use descriptive translation keys (
homeTabTitlenott1) - Keep translation files in version control
- Document what each key is used for
- Review translations with native speakers
- Test all supported languages before release
Content Guidelines
- Keep text concise (tab names especially)
- Avoid idioms that don't translate well
- Consider text expansion in some languages (German is often longer)
- Test UI layout with longest translations
- Use consistent terminology across translations
Maintenance
- Update all language files when adding new text
- Archive obsolete translation keys
- Use translation management tools for large projects
- Document which languages are officially supported
- Plan for adding new languages in the future
Recap
You've successfully configured multi-language support for your Purpose-built App. Your app now:
- Displays in users' preferred languages
- Translates app names and tab names
- Supports configuration page translations
- Provides consistent multilingual experience
- Scales to additional languages as needed
This makes your Purpose-built App accessible to global users and enhances the user experience across different regions.
Related Content
- Purpose-built App Deep Dive - Core concepts and architecture
- Custom Block Overview - Translating Custom Block content
- Purpose-built App Command Reference - Complete translation configuration options
- Enable User Configuration - Configuration page setup