Internationalization (i18n) (Plugins)
The Tuff
provides a comprehensive internationalization (i18n) system that allows plugins to support multiple languages. This guide explains how to implement localization in your plugins.
Language Files
Plugins can provide their own language files to support different locales. Language files are JSON files that map language keys to translated strings.
File Structure
Language files should be placed in a locales
directory within your plugin:
my-plugin/
locales/
en.json
es.json
fr.json
zh.json
plugin.js
manifest.json
Language File Format
Each language file contains key-value pairs where the key is a unique identifier for a string and the value is the translated string:
{
"plugin-name": "My Plugin",
"welcome-message": "Welcome to My Plugin",
"settings-title": "Plugin Settings",
"save-button": "Save",
"cancel-button": "Cancel",
"error-message": "An error occurred: {errorMessage}"
}
Placeholders
You can include placeholders in your translation strings that will be replaced with dynamic values:
{
"file-saved": "File '{filename}' has been saved successfully",
"items-selected": "{count} items selected"
}
Using Translations in Plugins
To use translations in your plugin, import the i18n
object from the plugin SDK:
// In your plugin's main file or preload script
import { i18n } from '@polyglot-toolbox/plugin-sdk';
// Get a translated string
const welcomeMessage = i18n.t('welcome-message');
console.log(welcomeMessage); // "Welcome to My Plugin" (in the current language)
// Get a translated string with placeholders
const fileSavedMessage = i18n.t('file-saved', { filename: 'document.txt' });
console.log(fileSavedMessage); // "File 'document.txt' has been saved successfully"
// Get a translated string with pluralization
const itemsSelected = i18n.t('items-selected', { count: 5 });
console.log(itemsSelected); // "5 items selected"
Setting the Language
The application language can be set by the user in the application settings. Plugins automatically use the current application language.
Detecting Language Changes
You can listen for language changes to update your plugin's UI:
import { i18n } from '@polyglot-toolbox/plugin-sdk';
// Listen for language changes
i18n.onLanguageChange((newLanguage) => {
console.log('Language changed to:', newLanguage);
// Update your plugin's UI with the new language
this.updateUI();
});
// Remove the listener when your plugin is unloaded
// i18n.offLanguageChange(listenerFunction);
Pluralization
The i18n system supports pluralization for languages with complex plural rules:
Simple Pluralization
For simple cases, you can use placeholders:
{
"file-count": "{count} file",
"file-count_plural": "{count} files"
}
// In a language with simple pluralization (like English)
const oneFile = i18n.t('file-count', { count: 1 }); // "1 file"
const manyFiles = i18n.t('file-count', { count: 5 }); // "5 files"
Complex Pluralization
For languages with more complex plural rules (like Russian or Arabic), you can define multiple plural forms:
{
"file-count_0": "{count} файлов", // 0 files
"file-count_1": "{count} файл", // 1 file
"file-count_2": "{count} файла", // 2-4 files
"file-count_3": "{count} файлов" // 5+ files
}
The system will automatically select the correct plural form based on the language's rules.
Date and Number Formatting
The i18n system also provides utilities for formatting dates and numbers according to the current locale:
import { i18n } from '@polyglot-toolbox/plugin-sdk';
// Format a date
const formattedDate = i18n.formatDate(new Date(), 'short');
console.log(formattedDate); // "10/15/2023" (for en-US)
// Format a number
const formattedNumber = i18n.formatNumber(1234.56);
console.log(formattedNumber); // "1,234.56" (for en-US)
// Format a currency
const formattedCurrency = i18n.formatCurrency(1234.56, 'USD');
console.log(formattedCurrency); // "$1,234.56" (for en-US)
Best Practices for Internationalization
When implementing i18n in your plugin, consider the following best practices:
1. Use Descriptive Keys
Choose keys that clearly describe the string's purpose:
// Good
{
"settings-save-button": "Save Settings",
"settings-cancel-button": "Cancel"
}
// Avoid
{
"button1": "Save Settings",
"button2": "Cancel"
}
2. Provide Context
Include comments in your language files to provide context for translators:
{
// Button text for saving user preferences
"save-preferences": "Save Preferences",
// Error message shown when a file cannot be saved
"save-file-error": "Unable to save file: {errorMessage}"
}
3. Handle Pluralization Properly
Always consider how your strings will be pluralized in different languages:
// Good - Using separate keys for singular and plural
{
"file-count": "{count} file",
"file-count_plural": "{count} files"
}
// Avoid - Trying to handle pluralization in code
{
"file-count": "{count} file{count > 1 ? 's' : ''}" // This doesn't work for all languages
}
4. Avoid Concatenating Translated Strings
Instead of concatenating translated strings, use placeholders:
// Good
const message = i18n.t('file-saved-at-time', {
filename: 'document.txt',
time: '14:30'
});
// Avoid
const message = i18n.t('file-saved') + ' at ' + '14:30'; // This breaks in RTL languages
5. Test with Different Languages
Test your plugin with different languages, especially those with different text directions (like Arabic or Hebrew) and different pluralization rules.
Example: Multilingual Plugin
Here's a complete example of a plugin that supports multiple languages:
// plugin.js
import { i18n, toast } from '@polyglot-toolbox/plugin-sdk';
class MultilingualPlugin {
constructor() {
this.init();
}
init() {
// Set up UI with translated strings
this.updateUI();
// Listen for language changes
i18n.onLanguageChange(() => {
this.updateUI();
});
// Set up event listeners
this.setupEventListeners();
}
updateUI() {
// Update UI elements with translated strings
const titleElement = document.getElementById('plugin-title');
if (titleElement) {
titleElement.textContent = i18n.t('plugin-title');
}
const descriptionElement = document.getElementById('plugin-description');
if (descriptionElement) {
descriptionElement.textContent = i18n.t('plugin-description');
}
const saveButton = document.getElementById('save-button');
if (saveButton) {
saveButton.textContent = i18n.t('save-button');
}
const cancelButton = document.getElementById('cancel-button');
if (cancelButton) {
cancelButton.textContent = i18n.t('cancel-button');
}
}
setupEventListeners() {
const saveButton = document.getElementById('save-button');
if (saveButton) {
saveButton.addEventListener('click', () => {
this.saveSettings();
});
}
const cancelButton = document.getElementById('cancel-button');
if (cancelButton) {
cancelButton.addEventListener('click', () => {
this.cancelSettings();
});
}
}
saveSettings() {
try {
// Save settings logic here
console.log('Settings saved');
// Show success message in the user's language
toast.success(i18n.t('settings-saved-success'));
} catch (error) {
console.error('Failed to save settings:', error);
// Show error message in the user's language
toast.error(i18n.t('settings-saved-error', {
errorMessage: error.message
}));
}
}
cancelSettings() {
console.log('Settings canceled');
// Show info message in the user's language
toast.info(i18n.t('settings-canceled'));
}
}
// Initialize the plugin
new MultilingualPlugin();
Corresponding language files:
locales/en.json
:
{
"plugin-title": "Multilingual Plugin",
"plugin-description": "A plugin that supports multiple languages",
"save-button": "Save",
"cancel-button": "Cancel",
"settings-saved-success": "Settings saved successfully",
"settings-saved-error": "Failed to save settings: {errorMessage}",
"settings-canceled": "Settings changes have been canceled"
}
locales/es.json
:
{
"plugin-title": "Plugin Multilingüe",
"plugin-description": "Un plugin que soporta múltiples idiomas",
"save-button": "Guardar",
"cancel-button": "Cancelar",
"settings-saved-success": "Configuración guardada con éxito",
"settings-saved-error": "Error al guardar la configuración: {errorMessage}",
"settings-canceled": "Los cambios de configuración han sido cancelados"
}
locales/fr.json
:
{
"plugin-title": "Plugin Multilingue",
"plugin-description": "Un plugin qui prend en charge plusieurs langues",
"save-button": "Enregistrer",
"cancel-button": "Annuler",
"settings-saved-success": "Paramètres enregistrés avec succès",
"settings-saved-error": "Échec de l'enregistrement des paramètres : {errorMessage}",
"settings-canceled": "Les modifications de paramètres ont été annulées"
}
This plugin demonstrates proper use of the i18n system by:
- Providing language files for multiple locales
- Using descriptive keys for translations
- Handling placeholders in translated strings
- Listening for language changes to update the UI
- Using translated strings for user-facing messages, including toast notifications
For more information about the plugin SDK and available APIs, see the Plugin SDK documentation.