This document describes the internationalization (i18n) system used in the Memos frontend, including supported languages, translation file structure, locale loading mechanisms, and how to add or modify translations. For backend configuration settings related to server locale, see Settings and Configuration UI.
The Memos frontend supports 39+ languages with translation files managed as JSON resources. All supported locales are defined in web/src/i18n.ts6-40 as the locales array:
| Region | Locales |
|---|---|
| European | de, en, en-GB, es, fr, it, nl, pl, pt-PT, cs, hr, hu, nb, ru, sl, sv, uk |
| Asian | zh-Hans, zh-Hant, ja, ko, hi, id, th, vi, mr, fa |
| Middle Eastern | ar, tr |
| Other | ca, gl, ka-GE, pt-BR |
The system includes fallback logic for locale variants. For example, zh-HK and zh-TW fall back to zh-Hant, while zh falls back to zh-Hans web/src/i18n.ts42-46
Sources: web/src/i18n.ts6-46
Architecture Summary:
The i18n system uses i18next with a custom lazy-loading backend that dynamically imports translation files only when needed. The LazyImportPlugin web/src/i18n.ts48-61 performs asynchronous imports from ./locales/${language}.json, reducing initial bundle size by ~200KB+.
Key design decisions:
import() statements, not bundled initiallyzh-HK → zh-Hant → en)localStorage (immediate) and user settings (server-side)loadLocale() applies the locale globally before persisting to the backendSources: web/src/i18n.ts1-78 web/src/components/LocaleSelect.tsx1-42
Translation files are JSON documents with a hierarchical namespace structure. Each file follows the same schema for consistency.
Key Namespaces:
| Namespace | Purpose | Example Keys |
|---|---|---|
common | Frequently used terms across app | save, cancel, delete, confirm, settings |
memo | Memo-related UI strings | delete-confirm, visibility, comment.self |
setting | Settings page sections | account-section.title, sso-section.create-sso |
editor | Memo editor interface | any-thoughts, save, saving, focus-mode |
message | Toast/notification messages | copied, update-succeed, archived-successfully |
auth | Authentication pages | sign-in-tip, sign-up-tip, create-your-account |
Translation keys support nested objects and variable interpolation:
Access patterns in code:
t("memo.comment.self") → "Comments"t("common.sign-in-with", { provider: "GitHub" }) → "Sign in with GitHub"t(\memo.visibility.${visibility.toLowerCase()}`)` → "Private"Sources: web/src/locales/en.json1-467 web/src/locales/zh-Hans.json1-469
Loading Flow Details:
Initial Load web/src/i18n.ts66-74:
navigator APIfallbackLng chainsUser Selection web/src/components/LocaleSelect.tsx15-20:
LocaleSelect component triggers loadLocale()onChange callbackPersistence web/src/components/Settings/PreferencesSection.tsx23-35:
loadLocale() stores in localStorage for instant recallupdateUserGeneralSetting mutation persists to backendgeneralSetting.locale field stores the preferenceLazy Import web/src/i18n.ts48-61:
LazyImportPlugin implements i18next BackendModule interfaceimport() with template string: import(\./locales/${matchedLanguage}.json`)`en-US → en)Sources: web/src/i18n.ts48-74 web/src/components/LocaleSelect.tsx1-42 web/src/components/Settings/PreferencesSection.tsx23-35
1. Add Locale Code
Add the locale identifier to the locales array in alphabetical order web/src/i18n.ts6-40:
2. Create Translation File
Create web/src/locales/{locale}.json using en.json as the reference template. The file must include all namespaces:
3. Translate String Values
en.json){{variable}}Example from zh-Hans.json:
4. Handle Special Cases
{{date}}) intact5. Configure Fallbacks (Optional)
For locale variants, add fallback configuration web/src/i18n.ts42-46:
6. Test Translation
pnpm buildSources: web/src/i18n.ts6-46 web/src/locales/en.json1-467
This renders: "Alice has a comment on your My First Memo."
Sources: web/src/components/Settings/PreferencesSection.tsx19 web/src/locales/en.json1-467
The LocaleSelect component provides a dropdown for language selection integrated with the preferences section.
The LocaleSelect component is rendered in the Preferences Section web/src/components/Settings/PreferencesSection.tsx74-76:
When a user changes the language:
LocaleSelect.handleSelectChange() web/src/components/LocaleSelect.tsx15-20 is calledloadLocale(locale) applies the change immediately to i18n instanceprops.onChange(locale) notifies parent componentPreferencesSection.handleLocaleSelectChange() web/src/components/Settings/PreferencesSection.tsx23-35 persists to user settingsupdateUserGeneralSetting mutation saves to backend with updateMask: ["locale"]The getLocaleDisplayName() utility web/src/components/LocaleSelect.tsx32 formats locale codes into human-readable names:
en → "English"zh-Hans → "中文 (简体)"zh-Hant → "中文 (繁體)"pt-BR → "Português (Brasil)"en-GB → "English (UK)"Sources: web/src/components/LocaleSelect.tsx1-42 web/src/components/Settings/PreferencesSection.tsx23-76
The most frequently used translation keys across the application:
| Key | Usage Context | Example Value (EN) |
|---|---|---|
common.save | Save buttons | "Save" |
common.cancel | Cancel buttons | "Cancel" |
common.delete | Delete buttons | "Delete" |
common.confirm | Confirmation dialogs | "Confirm" |
memo.delete-confirm | Memo deletion warning | "Are you sure you want to delete this memo?" |
message.update-succeed | Success toast | "Update succeeded" |
message.copied | Clipboard success | "Copied" |
editor.save | Editor save button | "Save" |
editor.any-thoughts | Editor placeholder | "Any thoughts..." |
setting.account-section.title | Settings section header | "Account Information" |
Sources: web/src/locales/en.json1-467
Refresh this wiki