This document provides a comprehensive guide for adding new language translations and updating existing translations in the memos application. It covers the localization file structure, the i18n system architecture, and step-by-step procedures for contributing translations.
For information about the overall frontend architecture and how i18n integrates with other systems, see Application Setup and Routing. For UI framework details, see UI Framework and Components.
The memos frontend uses a JSON-based i18n system with lazy loading to support 18+ languages while minimizing initial bundle size.
Sources: web/src/locales/en.json1-468 web/src/locales/zh-Hans.json1-469 web/src/locales/zh-Hant.json1-469
The i18n system loads translation files on-demand based on the user's selected language. This reduces the initial JavaScript bundle size and improves application load time.
| Feature | Implementation |
|---|---|
| Default Language | English (en) |
| Supported Languages | 18+ languages |
| Loading Strategy | Lazy (on-demand) |
| File Format | JSON |
| Cache Behavior | Persistent in memory during session |
| Fallback | English for missing keys |
Sources: High-level architecture diagrams provided in context
Each locale file follows a nested JSON structure organized by feature area:
Sources: web/src/locales/en.json1-468 web/src/locales/zh-Hans.json1-469
| Category | Purpose | Example Keys |
|---|---|---|
about | Application information | description, official-website, github-repository |
auth | Authentication flows | sign-in-tip, create-your-account, new-password |
common | Shared UI elements | save, cancel, delete, settings, profile |
days | Day abbreviations | mon, tue, wed, thu, fri, sat, sun |
editor | Memo editor interface | any-thoughts, save, slash-commands, focus-mode |
filters | Memo filtering | has-code, has-link, has-task-list |
inbox | Notification system | memo-comment, version-update, unread |
markdown | Markdown syntax | checkbox, code-block, content-syntax |
memo | Memo display/actions | delete-confirm, visibility, load-more, archived-at |
message | User feedback messages | copied, deleted-successfully, update-succeed |
reference | Memo references | add-references, embedded-usage |
resource | File attachments | create-dialog, upload-method, reset-link |
router | Navigation | back-to-top, go-to-home |
setting | Settings panels | account-section, sso-section, storage-section |
tag | Tag management | create-tag, rename-tag, delete-confirm |
tooltip | Tooltips | upload-attachment, select-visibility, link-memo |
Sources: web/src/locales/en.json2-467
Translation strings support variable interpolation using double curly braces:
Common interpolation variables:
{{provider}} - OAuth provider name{{user}} / {{username}} - User names{{count}} - Numeric counts{{date}} - Date values{{size}} - File sizes{{url}} - URLs{{name}} / {{title}} / {{description}} - Generic identifiers{{memo}} - Memo identifiersSources: web/src/locales/en.json92 web/src/locales/en.json158 web/src/locales/en.json136
Create a new JSON file in web/src/locales/ using the appropriate locale code:
| Locale Code Format | Example | Description |
|---|---|---|
| Two-letter ISO 639-1 | en, fr, de, ja | Standard language codes |
| Language-Region | zh-Hans, zh-Hant, pt-BR | Language with region/script variant |
File naming convention: {locale}.json
Examples:
web/src/locales/ar.json (Arabic)web/src/locales/pl.json (Polish)web/src/locales/th.json (Thai)Sources: web/src/locales/en.json1 web/src/locales/zh-Hans.json1 web/src/locales/pt-BR.json1
Start with the English locale as the base template:
The English file serves as the reference because it contains all translation keys and is always kept up-to-date.
Sources: web/src/locales/en.json1-468
Translate each string value while maintaining:
{{variable}} syntax intactExample Translation:
Sources: web/src/locales/en.json84 web/src/locales/es.json61 web/src/locales/zh-Hans.json84
Some languages require different pluralization rules. Use descriptive keys:
For complex pluralization, the translation may need to handle it contextually within the interpolated string.
Sources: web/src/locales/en.json40-41
Before committing, validate the JSON:
Common validation errors:
Sources: General JSON validation practices
Check if the i18n configuration automatically detects new locale files. If manual registration is required, update the i18n initialization code to include the new locale.
Sources: Inferred from lazy loading architecture
The table of contents mentions a "locale updater script" which helps maintain translation consistency across all locale files.
To update specific translation strings:
Example - Updating a button label:
Sources: web/src/locales/en.json84
When new features are added, translation keys may be missing from non-English locales:
en.jsonSources: Inferred from locale file structure
Sources: Inferred from i18n architecture and locale file structure
| Convention | Example | Usage |
|---|---|---|
| Kebab-case | sign-in-tip, create-your-account | Standard for all keys |
| Descriptive | delete-confirm, archive-warning | Indicates purpose/context |
| Nested objects | setting.account-section.title | Organizes related keys |
| Action-based | succeed-copy-link, failed-to-embed-memo | User feedback messages |
Sources: web/src/locales/en.json14 web/src/locales/en.json159 web/src/locales/en.json284
The setting category is the most complex, with deep nesting:
Nesting levels:
setting - Top level{feature}-section - Feature area (account, sso, storage, etc.)Sources: web/src/locales/en.json253-441
Components access translations through the t() function:
Sources: web/src/locales/en.json92 web/src/locales/en.json188
Different UI contexts use specific translation categories:
| UI Area | Translation Category | Example Keys |
|---|---|---|
| Authentication pages | auth.* | sign-in-tip, create-your-account |
| Memo editor | editor.* | any-thoughts, slash-commands, save |
| Memo display | memo.* | load-more, show-more, visibility.* |
| User settings | setting.account-section.* | title, email-note |
| Admin settings | setting.member-section.* | create-a-member, archive-warning |
| System settings | setting.system-section.* | server-name, max-upload-size |
| SSO configuration | setting.sso-section.* | authorization-endpoint, client-id |
| Storage settings | setting.storage-section.* | endpoint, bucket, region |
Sources: web/src/locales/en.json9-15 web/src/locales/en.json120-128 web/src/locales/en.json275-286
Before submitting translations:
en.json are present{{var}} are preserved| Issue | Problem | Solution |
|---|---|---|
| Missing interpolation | "Sign in with GitHub" instead of "Sign in with {{provider}}" | Always preserve {{variables}} exactly |
| Broken JSON | Trailing commas, unescaped quotes | Validate JSON before committing |
| Key mismatch | Changed key names during translation | Never modify keys, only values |
| Incorrect nesting | Flattened structure instead of nested objects | Match the hierarchy exactly |
| Literal translation | Translating idioms word-for-word | Use natural language expressions |
Sources: Inferred from common i18n issues and locale file structure
| Language | Locale Code | File | Completeness Status |
|---|---|---|---|
| English | en | web/src/locales/en.json | ✅ Base language (100%) |
| Chinese (Simplified) | zh-Hans | web/src/locales/zh-Hans.json | ✅ Complete |
| Chinese (Traditional) | zh-Hant | web/src/locales/zh-Hant.json | ✅ Complete |
| Vietnamese | vi | web/src/locales/vi.json | ⚠️ Partial (many keys) |
| French | fr | web/src/locales/fr.json | ✅ Complete |
| Portuguese (Brazil) | pt-BR | web/src/locales/pt-BR.json | ✅ Complete |
| Korean | ko | web/src/locales/ko.json | ✅ Complete |
| German | de | web/src/locales/de.json | ✅ Complete |
| Japanese | ja | web/src/locales/ja.json | ✅ Complete |
| Turkish | tr | web/src/locales/tr.json | ✅ Complete |
| Russian | ru | web/src/locales/ru.json | ⚠️ Partial |
| Dutch | nl | web/src/locales/nl.json | ✅ Complete |
| Croatian | hr | web/src/locales/hr.json | ✅ Complete |
| Italian | it | web/src/locales/it.json | ✅ Complete |
| Spanish | es | web/src/locales/es.json | ⚠️ Partial |
| Swedish | sv | web/src/locales/sv.json | ⚠️ Partial |
Note: Completeness is estimated based on file size and key coverage. Contributors are encouraged to review and improve all locales.
Sources: web/src/locales/en.json1 web/src/locales/zh-Hans.json1 web/src/locales/vi.json1 web/src/locales/fr.json1 web/src/locales/pt-BR.json1 web/src/locales/ko.json1 web/src/locales/de.json1 web/src/locales/ja.json1 web/src/locales/tr.json1 web/src/locales/ru.json1 web/src/locales/nl.json1 web/src/locales/hr.json1 web/src/locales/it.json1 web/src/locales/es.json1 web/src/locales/sv.json1
When submitting translation contributions:
Title format: feat: Add [Language] locale or fix: Update [Language] translations
Description should include:
Example:
Sources: Standard GitHub contribution workflow
Symptom: UI displays English text when other language is selected
Cause: Translation key is missing from locale file
Solution:
Symptom: Application fails to load or crashes when selecting a language
Cause: Invalid JSON syntax in locale file
Common issues:
Solution: Use a JSON validator or linter before committing
Symptom: UI displays literal {{variable}} text
Cause: Interpolation variable name mismatch between locale file and component
Solution: Ensure variable names in translations match exactly what the component provides
Sources: Common i18n troubleshooting scenarios
Refresh this wiki