-
Notifications
You must be signed in to change notification settings - Fork 0
Localization
GrainLab supports 9 languages. This page explains how to add a new language or update existing translations.
On first load, src/i18n/index.ts :: getLocale() detects the language in this priority order:
-
localStorage.getItem('lang')— previously saved user preference. -
navigator.language— the browser's reported language. - Falls back to
'en'if no match is found.
The detected code must be one of the 9 supported LocaleCode values:
'en' | 'zh-CN' | 'zh-TW' | 'ja' | 'ko' | 'fr' | 'de' | 'es' | 'pt'The user's manual selection (via the top-right language picker) is saved to localStorage and takes priority on subsequent loads.
Each language is a single TypeScript file in src/i18n/:
src/i18n/
├── index.ts ← registers all locales, exports LOCALES list
├── en.ts ← English (canonical shape — all other files must mirror this)
├── zh-CN.ts
├── zh-TW.ts
├── ja.ts
├── ko.ts
├── fr.ts
├── de.ts
├── es.ts
└── pt.ts
Each file exports a default object. The shape is inferred from en.ts and is used as the MessageSchema type throughout the app — TypeScript will report an error if a locale file is missing a key.
// src/i18n/en.ts
export default {
app: { ... }, // toolbar button labels
canvas: { ... }, // canvas placeholder text
preset: { ... }, // film preset names and descriptions
control: { ... }, // effect parameter labels
export: { ... }, // export dialog strings
batch: { ... }, // batch panel strings
gallery: { ... }, // gallery / film strip strings
theme: { ... }, // dark/light mode toggle labels
lang: { ... }, // language selector labels
}- Open the target locale file, e.g.
src/i18n/fr.ts. - Find the key you want to update and edit the string value.
- Run
npm run devto confirm there are no runtime warnings. - Run
npm run buildto confirm there are no TypeScript type errors.
Copy src/i18n/en.ts to a new file, e.g. src/i18n/it.ts, and translate every string value. Keep all key names unchanged — only the values should differ.
// src/i18n/it.ts
export default {
app: {
title: 'GrainLab',
open: 'Apri',
compare: 'Confronta',
export: 'Esporta',
batch: 'Batch',
},
// ... translate all remaining keys ...
}import it from './it' // ① import
export type LocaleCode = '...' | 'it' // ② add to union type
export const LOCALES: { code: LocaleCode; label: string }[] = [
// ... existing entries ...
{ code: 'it', label: 'Italiano' }, // ③ add to display list
]
// Inside getLocale():
if (nav.startsWith('it')) return 'it' // ④ add auto-detection
// Inside createI18n messages:
messages: {
// ... existing ...
it, // ⑤ register messages
}npm run buildTypeScript will surface any missing translation keys. Fix them before submitting a PR.
When a new film preset is added (see Adding a Film Preset), its i18nKey must be added to all locale files under the preset key:
// All 9 locale files
preset: {
// ... existing presets ...
myNewFilm: { name: 'My New Film', desc: 'Description in this language.' },
},- The
en.tsfile is the source of truth for the schema. If you add a new key toen.ts, add it to all other locale files as well. - Untranslated strings automatically fall back to the
envalue at runtime (configured viafallbackLocale: 'en'increateI18n), so missing keys won't crash the app — but they should be filled in before release. - Right-to-left (RTL) languages are not currently supported at the CSS level.
Using GrainLab
Developer Docs