Translate Skill
Manages translation files in apps/frontend/src/i18n/languages/.
Translation Files
- 15 languages: en (source), de, es, fr, hi, it, ja, ko, nl, pt, sv, tr, tw, vi, zh
- Format: flat JSON key-value pairs (keys = English phrases)
- Interpolation:
{{variableName}} - Library:
react-i18nextconfigured inapps/frontend/src/i18n/index.tsx - Language list defined in
apps/frontend/src/i18n/index.tsx(languagesarray)
When Adding New Translation Keys
- Add the new key to
en.jsonwith the English value - Add the same key to ALL other language files with the correct translation for the language of the file
- After adding to all files, sort ALL translation files alphabetically by key (case-insensitive)
When Removing Translation Keys
- Remove the key from ALL 15 language files
- Files should remain sorted after removal
Sorting Rules
- All translation JSON files MUST have keys sorted alphabetically (case-insensitive)
- Sorting applies to ALL language files, not just
en.json - After any modification, verify keys are sorted
Sort Procedure
For each .json file in apps/frontend/src/i18n/languages/:
- Parse the JSON
- Get all keys and sort them case-insensitively
- Rebuild the object with sorted keys
- Write back with 2-space indentation and trailing newline
Use a Node.js script or jq to sort. Example with Node.js:
node -e "
const fs = require('fs');
const glob = require('glob');
const files = glob.sync('apps/frontend/src/i18n/languages/*.json');
files.forEach(f => {
const obj = JSON.parse(fs.readFileSync(f, 'utf8'));
const sorted = Object.keys(obj)
.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }))
.reduce((acc, key) => { acc[key] = obj[key]; return acc; }, {});
fs.writeFileSync(f, JSON.stringify(sorted, null, 2) + '\n');
});
"
Sync Check
After any translation change, verify:
en.jsonhas all keys that exist in code (search fort("...") usage)- All non-English files have the EXACT same keys as
en.json - Missing keys in non-English files get the correct translation for the language of the file
- Extra keys in non-English files (not in
en.json) should be removed - All files are sorted alphabetically by key
Fixed Words
See apps/frontend/src/i18n/fixedWords.json. Grouped by language code ("all" applies to every language).
Two use cases:
- Untranslated words — value equals the English word (e.g.
"round": "round"keeps "round" in English for Italian) - Fixed translations — value is the mandatory translation for that concept (e.g.
"endorsement": "supporto"means always use "supporto" for endorsement in Italian)
When translating, ALWAYS check this file first. If a word has a fixed translation for the target language, use it consistently in every sentence.
VeChain Kit: bi-directional language sync
If the app uses @vechain/vechain-kit, keep Kit UI language in sync with the host app:
- App → Kit: Inside
VeChainKitProvider, subscribe toi18n.on("languageChanged", ...)and callsetLanguage(lng)fromuseCurrentLanguage()so when the user changes language in the app (e.g. footer selector), Kit updates. - Kit → App: Pass
language={i18n.language}andonLanguageChange={(lng) => i18n.changeLanguage(lng)}intoVeChainKitProviderso when the user changes language in Kit (e.g. wallet modal), the app updates.
See the vechain-kit skill reference translations-vechain-kit.md for the full implementation pattern.
Pre-commit and ESLint (missing / unused keys)
- Unused keys in en.json: Run a script that finds keys in
en.jsonnever used in code (t("..."),i18nKey="...") and exit non-zero so pre-commit fails. Run whenen.jsonor code is staged. - Missing keys in other locales: Run a script that compares each locale's keys to
en.jsonand fails if any locale has missing or extra keys. Run when any translation file is staged or in CI. - ESLint: Optionally add
eslint-plugin-i18next(or similar) to flag missing keys in the editor/lint.
See the vechain-kit skill reference translations-vechain-kit.md for a short summary table.