Internationalization (i18n) Support
The micro app template has a built-in lightweight internationalization (i18n) solution, supporting multi-language translation, placeholder replacement, config references, etc. The i18n feature is integrated into the @sun-panel/micro-app SDK. The component base class (SunPanelWidgetElement / SunPanelPageElement) will automatically initialize i18n in the onConnected lifecycle; developers do not need to manually call initI18n.
Requirements:
- Sun-Panel main version upgraded to
v2.0.0-dev-14+ - Micro app template upgraded to
v1.1.0+ - SDK module (@sun-panel/micro-app) upgraded to
v1.0.18+ app.config.jsconfigured with{appJsonVersion: '1.1'}, version 1.1 or above
Directory Structure
your-app/
├── locales/ # i18n language files (the only translation source)
│ ├── zh-CN.js # Chinese language pack
│ └── en-US.js # English language pack
├── config/
│ └── app.config.js # App config (declares language file list)
└── ...Quick Start
1. Configure App Info
In config/app.config.js, reference translation keys using the $t:KEY format, and declare the language file mapping in the locales object:
export default {
// Config file format version
// 1.1: appInfo uses $t: reference, translation files in locales/ directory
appJsonVersion: '1.1',
// App info (uses $t: variable reference for translation)
appInfo: {
appName: '$t:APP_NAME',
description: '$t:APP_DESCRIPTION',
networkDescription: '$t:NETWORK_DESCRIPTION',
},
// Language file mapping (format: { language code: file name })
// File name relative to locales/ directory, multiple language codes can point to the same file
locales: {
'zh-CN': 'zh-CN.json',
'en-US': 'en-US.json',
// 'zh-TW': 'zh-CN.json', // Example: Traditional Chinese can point to the Simplified Chinese file
},
// Default language (fallback when the main app's language doesn't exist in the micro app)
// If not set, defaults to 'en-US'
defaultLocale: 'en-US',
// ...other config
};TIP
Values in config/* use the $t:translation key name format for reference. At build time, the system automatically resolves these references and replaces them with the corresponding language's translation text.
2. Create Language Pack Files
Create the corresponding language pack files in the locales/ directory. The file name must match the value in the locales object (without the .json suffix, using the .js source file):
// locales/zh-CN.js
export default {
APP_NAME: 'Hello World',
APP_DESCRIPTION: 'Sun-Panel Demo Micro App',
NETWORK_DESCRIPTION: 'No need to link to any third-party websites',
WIDGET_HELLO_NAME: 'Hello World Widget',
WIDGET_HELLO_DESCRIPTION: 'A demo widget',
GREETING: 'Hello, {name}!',
// ...more translations
};// locales/en-US.js
export default {
APP_NAME: 'Hello World',
APP_DESCRIPTION: 'Sun-Panel Demo Micro App',
NETWORK_DESCRIPTION: 'For demonstration purposes',
WIDGET_HELLO_NAME: 'Hello World Widget',
WIDGET_HELLO_DESCRIPTION: 'A demo widget',
GREETING: 'Hello, {name}!',
// ...more translations
};3. Use in Components
After inheriting the component base class, i18n will be automatically initialized in the onConnected lifecycle, and developers can directly use translation via this.t():
// In widgetConfig.js
import { SunPanelWidgetElement } from '@sun-panel/micro-app';
class MyWidget extends SunPanelWidgetElement {
render() {
const config = {
name: this.t('WIDGET_HELLO_NAME'),
description: this.t('WIDGET_HELLO_DESCRIPTION'),
};
return config;
}
}No need to manually call initI18n
The component base class SunPanelWidgetElement / SunPanelPageElement automatically initializes i18n in the onConnected lifecycle, no need to manually import and call initI18n. Just use this.t() directly.
Using in Micro App Components
this.t(key, args?) - Translation Function (Recommended)
An instance method provided by the component base class. Translates the specified key according to the current language environment, supporting named placeholder replacement.
Parameters:
key(string) - Translation key nameargs(Record<string, any>) - Optional named placeholder replacement parameter object
Return value: The translated string (returns the key itself if not found)
Example:
// Used inside a component
this.t('APP_NAME'); // => 'Hello World'
this.t('GREETING', { name: 'John' }); // => 'Hello, John!'
this.t('WELCOME_USER_ROLE', { name: 'Li', role: 'Admin' }); // => 'Welcome Li, you are Admin't(key, args?) - Translation Function (Independently Exported)
Translates the specified key according to the current language environment, supporting named placeholder replacement.
Parameters:
key(string) - Translation key nameargs(Record<string, any>) - Optional named placeholder replacement parameter object
Return value: The translated string (returns the key itself if not found)
Example:
import { t } from '@sun-panel/micro-app';
// Basic translation
t('APP_NAME'); // => 'Hello World'
t('SETTINGS'); // => 'Settings'
// Translation with named placeholders
t('GREETING', { name: 'John' }); // => 'Hello, John!'
t('WELCOME_USER_ROLE', { name: 'Li', role: 'Admin' }); // => 'Welcome Li, you are Admin'setLocale(locale) - Switch Language Independently
This approach is only suitable for manually and dynamically switching the current language environment independently from the main app's language, and re-initializing the resolver.
Parameters:
locale(string) - Target language identifier, such as'en-US'
Example:
import { setLocale } from '@sun-panel/micro-app';
// Component code
class WidgetConfig extends SunPanelPageElement {
// ...
// Language switch handler
async handleLanguageChange(locale) {
try {
await setLocale(locale);
this.requestUpdate(); // Update page, important!!!
console.log(`Language changed to: ${locale}`);
} catch (error) {
console.error('Failed to change language:', error);
}
}
// ...
}getLocale() - Get Current Language
Returns the identifier string of the current language environment.
Return value: string - Current language identifier
Placeholders
Placeholders use the {name} format, where name is the key in the parameter object:
// locales/zh-CN.js
export default {
GREETING: 'Hello, {name}!',
WELCOME_USER_ROLE: 'Welcome {name}, you are {role}',
};
// locales/en-US.js
export default {
GREETING: 'Hello, {name}!',
WELCOME_USER_ROLE: 'Welcome {name}, you are {role}',
};t('GREETING', { name: 'John' }); // => 'Hello, John!'
t('WELCOME_USER_ROLE', { name: 'Li', role: 'Admin' }); // => 'Welcome Li, you are Admin'If the parameter corresponding to a placeholder is missing, the placeholder is preserved as-is (such as {name}).
Language Fallback Mechanism
When a translation key is not found in the current language, the system falls back in the following order:
- Full language code - such as
zh-CN - Language short code - Remove the region suffix, such as
zh - Default language - Specified by the
defaultLocalefield inapp.config.js, defaulting to'en-US'
// Assume current language is zh-TW, but only zh-CN and en-US translations exist
// Lookup order: zh-TW → zh → en-US (assuming defaultLocale is 'en-US')
t('SETTINGS'); // If neither zh-TW nor zh has it, it will be fetched from en-USFallback Chain Example
| Current Language (locale) | defaultLocale | Fallback Chain | Description |
|---|---|---|---|
zh-CN | en-US | ['zh-CN', 'zh', 'en-US'] | Chinese falls back to English |
en-US | en-US | ['en-US', 'en'] | English falls back to itself, defaultLocale not duplicated |
zh-CN | zh-CN | ['zh-CN', 'zh'] | defaultLocale not duplicated when same as current language |
Configure Default Language
Set the defaultLocale field in config/app.config.js to specify the default fallback language:
export default {
// ...other config
// Language file mapping
locales: {
'zh-CN': 'zh-CN.json',
'en-US': 'en-US.json',
},
// Default language (fallback when the main app's language doesn't exist in the micro app)
// If not set, defaults to 'en-US'
defaultLocale: 'en-US',
// ...other config
};How it works
The defaultLocale passing path:
- Declare the
defaultLocalevalue inapp.config.js main.jsreads this value when initializing the SDK and passes it in:initSDK({ defaultLocale: appConfig.defaultLocale || 'en-US' })- The SDK internally passes it to i18n initialization:
initI18n({ locale, defaultLocale }) - During translation lookup,
defaultLocaleserves as the final fallback of the fallback chain, after all other fallbacks
Additionally, when the SDK asynchronously loads translation files, it loads both the current language and defaultLocale's translation files at the same time, ensuring fallback translation data is available.
Notes:
- The value of
defaultLocalemust be one of the language codes already defined in thelocalesobject - It is recommended to set it to the app's main supported language (e.g., a Chinese app can set it to
'zh-CN') - If not set, the default is
'en-US'
If the key doesn't exist in all fallback languages, the key itself is returned (such as MISSING_KEY).
Build Process
Development Mode
In development mode, the locales/*.js source files are loaded directly via Vite dynamic import:
locales/zh-CN.js → import() dynamic loading → i18n instanceProduction Build
At build time, locales-generator.js compiles locales/*.js into JSON format, output to the dist/locales/ directory:
locales/zh-CN.js → locales-generator.js → dist/locales/zh-CN.json
locales/en-US.js → locales-generator.js → dist/locales/en-US.jsonIn production mode, i18n loads translation files via fetch from {staticPath}/locales/*.json.
Best Practices
1. Key Naming Convention
Translation keys are not strictly required to use a certain naming method; follow personal preference. Nested translation objects are not yet supported, so try not to use . in key names. The example below uses UPPER_SNAKE_CASE:
export default {
APP_NAME: 'App Name',
WIDGET_HELLO_NAME: 'Widget Name',
SETTINGS_TITLE: 'Settings Title',
WIDGET_CONFIG_BUTTON_SAVE: 'Save Widget',
};2. File Naming
The language pack file name should correspond to the value of the locales object in app.config.js:
| Language | Source File | locales Declaration |
|---|---|---|
| Simplified Chinese | locales/zh-CN.js | 'zh-CN': 'zh-CN.json' |
| English | locales/en-US.js | 'en-US': 'en-US.json' |
| Traditional Chinese | locales/zh-TW.js | 'zh-TW': 'zh-TW.json' |
3. Keep Translations Synchronized
All language pack files should contain the same keys. When adding a translation, remember to update all language pack files synchronously.
4. Translate Only Text
Only translate user-visible text content. Do not internationalize the following:
- Config key names (such as
showLogo,textOption) - CSS class names
- API paths
- Data node identifiers
Notes
- Translation source location: All translations should be placed uniformly in the
locales/directory. Do not create language files in thesrc/directory. - Do not directly modify build artifacts:
dist/locales/is build output and modifications will be overwritten. - Language identifier format: Follow the IETF BCP 47 standard (such as
zh-CN,en-US).