How Open Notebook Manages Translation Keys for Multi-Language UI Support
Open Notebook uses i18next with react-i18next to manage translation keys through a centralized locale catalog, automatic language detection, and flat-key namespacing in JSON files.
Open Notebook implements a robust internationalization (i18n) system using i18next and react-i18next to handle translation keys across its React frontend. This architecture stores all language strings in JSON files under frontend/src/lib/locales/, enabling seamless multi-language UI support with automatic fallback to English. By leveraging flat-key naming conventions and browser-based language detection, the application delivers translated content without requiring complex code changes when adding new languages.
i18next Configuration and Initialization
The translation system bootstrap resides in frontend/src/lib/i18n.ts. This file creates the i18next instance and configures the language detection strategy.
The configuration imports the locale catalog from ./locales and initializes LanguageDetector to read the user's preferred language from localStorage or the browser's navigator.language. Key settings include:
fallbackLng: 'en-US'– Ensures English displays when translation keys are missinguseSuspense: false– Prevents React suspense boundaries from blocking renderingcaches: ['localStorage']– Persists language selection across sessions
// frontend/src/lib/i18n.ts
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import resources from './locales';
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources,
fallbackLng: 'en-US',
interpolation: { escapeValue: false },
useSuspense: false,
detection: {
order: ['localStorage', 'navigator'],
caches: ['localStorage'],
},
});
Locale Catalog Structure and Flat-Key Convention
All translation keys live in a single resources object generated from JSON files under frontend/src/lib/locales/. Each language (e.g., en-US, fr-FR, es-ES) serves as a top-level key containing namespaced translation tables.
The project follows the flat-key convention, grouping UI text by feature using dot notation:
common.title– Shared interface elementssources.addUrl– Source management featuresnavigation.settings– Navigation labels
Adding a new language requires only creating a new JSON file in the locales folder and updating the import in the configuration. The bundler automatically incorporates new translations without modifying component code.
Using Translation Keys in React Components
Components access translation keys via the useTranslation hook from react-i18next. The hook returns a t function that resolves the current language automatically based on the detector configuration.
When a key is missing in the selected language, i18next falls back to the en-US entry. This ensures the UI never displays raw key strings to users.
// frontend/src/components/sources/steps/SourceTypeStep.tsx
import { useTranslation } from 'react-i18next';
export function SourceTypeStep() {
const { t } = useTranslation();
return (
<>
<label>{t('sources.title')}</label>
<button>{t('sources.addUrl')}</button>
</>
);
}
The t function accepts the namespace-key string directly, eliminating the need to import translation files into individual components.
Language Detection and Runtime Switching
Open Notebook implements automatic language detection on first load through a detector chain that checks localStorage first, then falls back to navigator.language. If a stored language exists, it takes precedence; otherwise, the browser's locale determines the initial language.
Components can programmatically change languages using i18n.changeLanguage(newLng), which automatically persists the selection to localStorage due to the detector's cache configuration.
import { useTranslation } from 'react-i18next';
export function LanguageSelector() {
const { i18n } = useTranslation();
const changeLanguage = (lng: string) => {
i18n.changeLanguage(lng);
};
return (
<select
onChange={(e) => changeLanguage(e.target.value)}
defaultValue={i18n.language}
>
<option value="en-US">English</option>
<option value="fr-FR">Français</option>
<option value="es-ES">Español</option>
</select>
);
}
The frontend/src/lib/i18n-events.ts file provides additional helpers for emitting language-change events throughout the UI when components need to react to locale updates.
Adding New Translation Keys and Languages
Extending the translation system requires no code changes beyond editing JSON files:
- Insert the key-value pair into every language's JSON file under the appropriate namespace:
// frontend/src/lib/locales/en-US.json
{
"common": {
"title": "Open Notebook",
"welcome": "Welcome"
},
"newFeature": {
"label": "New Feature"
}
}
-
Add corresponding translations to
fr-FR.json,es-ES.json, and other locale files. -
Reference the key in components using
t('newFeature.label').
The bundler automatically re-imports the catalog when files change, and i18next's type-safe patterns help identify missing keys during development.
Summary
- i18next powers the translation system in
frontend/src/lib/i18n.tswithfallbackLng: 'en-US'and browser-based detection - Translation keys follow a flat-key convention (
namespace.key) stored in JSON files underfrontend/src/lib/locales/ - Components use the
useTranslationhook to access thetfunction for resolving keys - Language detection prioritizes
localStorageovernavigator.languageand persists user selection automatically - Adding languages requires only new JSON files; adding keys requires only JSON edits and
t()references
Frequently Asked Questions
How does Open Notebook handle missing translation keys?
When a translation key is missing in the selected language, i18next automatically falls back to the en-US locale as configured in frontend/src/lib/i18n.ts. This ensures users always see English text rather than raw key strings or blank spaces.
Can I add a new language without modifying React components?
Yes. Adding a new language requires only creating a new JSON file in frontend/src/lib/locales/ with the appropriate translations and updating the resources import in i18n.ts. The useTranslation hook automatically recognizes new languages once they are added to the configuration.
Where does Open Notebook store the user's language preference?
The application stores the user's language preference in localStorage via the LanguageDetector configuration with caches: ['localStorage']. This setting ensures returning users see the interface in their previously selected language immediately on load.
What is the difference between useTranslation() and i18n.changeLanguage()?
The useTranslation() hook provides the t function for rendering translated text and accessing the current i18n instance, while i18n.changeLanguage() is a method to programmatically switch the active language. The latter triggers the storage cache update and re-renders all components using translation keys.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →