Internationalization (i18n)
Relevant source files
The following files were used as context for generating this wiki page:
The Internationalization (i18n) system in Dreamcats provides a fully localized interface, supporting both English and Polish languages. It manages the translation of UI elements, game logs, legal documents, and tutorial content, ensuring a seamless experience for a global player base.
The system is built using i18next and react-i18next, integrating directly with the application's state management and backend preferences to persist language choices across sessions.
Architecture and Core Configuration
The i18n implementation relies on a centralized configuration that initializes the translation engine and defines the available resources.
Initialization and Resource Loading
The project uses i18next with the initReactI18next plugin. Translation files are imported directly as JSON objects for "instant availability," avoiding network latency for core UI text.
import enTranslation from '@/locales/en/translation.json';
import enCommon from '@/locales/en/common.json';
import plTranslation from '@/locales/pl/translation.json';
import plCommon from '@/locales/pl/common.json';
const resources = {
en: { translation: enTranslation, common: enCommon },
pl: { translation: plTranslation, common: plCommon },
};
Sources: src/i18n/config.ts:7-20
Configuration Parameters
The system is configured with specific namespaces and fallback behaviors to ensure robustness:
| Option | Value | Description |
|---|---|---|
fallbackLng | 'en' | Default language if the detected language is unavailable. |
supportedLngs | ['en', 'pl'] | List of allowed language codes. |
defaultNS | 'translation' | The default namespace used when no namespace is specified in the t() function. |
ns | ['translation', 'common'] | Available translation namespaces. |
interpolation | escapeValue: false | React already handles XSS protection, so additional escaping is disabled. |
Sources: src/i18n/config.ts:30-41
Language Detection and Persistence
The system employs a multi-layered approach to detect and save the user's preferred language, utilizing browser storage and backend synchronization.
Detection Logic
Language detection is handled by i18next-browser-languagedetector. It follows a specific order of priority:
- LocalStorage: Checks for a saved preference under the key
i18nextLng. - Navigator: Falls back to the browser's language settings.
Sources: src/i18n/config.ts:42-46
Data Flow for Language Preferences
User preferences, including language, flow from the UI through hooks to both local and remote storage.
The diagram shows how a language change request propagates through the system to ensure persistence across device refreshes and authenticated sessions.
Sources: src/hooks/useUserPreferences.ts:32-37, convex/userPreferences.ts:133-157
Component Integration
The application initializes the i18n config in the root App.tsx and provides a LanguageSwitcher component in the header for real-time adjustments.
Sources: src/App.tsx:16-17, src/App.tsx:189
Translation Management
Translations are organized into two primary namespaces: translation (main game logic, UI, and tutorial) and common (authentication, errors, and success messages).
Namespace Structure
translation.json: Contains deep-nested keys for complex UI structures like the Tutorial and Legal Modals.tutorial.*: Step-by-step instructions for the game.legal.*: Full text for Privacy Policy and Terms of Service.game.*: Real-time action logs and game board labels.
common.json: Contains reusable strings for the application shell.auth.*: Sign-in and profile management.errors.*: User-facing error messages for network or logic failures.
Sources: src/locales/en/translation.json, src/locales/en/common.json
Dynamic Interpolation
The system supports dynamic values within translation strings, such as player names or card values, using the {{variable}} syntax.
"playerTurn": "It's {{player}}'s turn.",
"peekResult": "Card value: {{value}}",
"roomCodeApplied": "Room Code Applied: {{code}}"
Sources: src/locales/en/translation.json:161, src/locales/en/translation.json:166, src/locales/en/translation.json:109
Technical Implementation Details
State and Store Testing
The application's global store (useAppStore) includes a locale state property, ensuring that components can reactively update when the language changes. This is verified through unit tests.
it('sets locale', () => {
const { setLocale } = useAppStore.getState();
setLocale('pl');
expect(useAppStore.getState().locale).toBe('pl');
});
Sources: src/tests/store.test.ts:77-82
Localization Utilities
The system utilizes a custom hook, useUserPreferences, to abstract the complexity of updating language across different storage mediums.
| Function | Logic |
|---|---|
setLanguage(lang) | Updates localStorage with the key i18nextLng. |
useTranslation() | Standard React hook for accessing the t function and i18n instance. |
safeLocalStorage | A wrapper used to prevent errors in environments where localStorage might be blocked or unavailable. |
Sources: src/hooks/useUserPreferences.ts:32-37, src/i18n/config.ts:25
Conclusion
The Internationalization system in Dreamcats is a robust, multi-layered implementation that balances performance (through direct JSON imports) with persistence (through LocalStorage and Convex backend sync). By utilizing namespaces and dynamic interpolation, the project maintains a clean separation between UI structure and localized content, facilitating easier expansion to additional languages in the future.