Repository navigation
Internationalization
Canonical source:
docs/05-frontend/i18n-implementation.md— this page is rendered from it on every publish; edit the source.
Complete guide to the 13-language URL-authoritative i18n system, RTL support, and adding new translations.
| Code | Language | Direction | Locale File |
|---|---|---|---|
en |
English | LTR | src/lib/i18n/locales/en.ts |
ar |
Arabic | RTL | src/lib/i18n/locales/ar.ts |
de |
German | LTR | src/lib/i18n/locales/de.ts |
es |
Spanish | LTR | src/lib/i18n/locales/es.ts |
fr |
French | LTR | src/lib/i18n/locales/fr.ts |
it |
Italian | LTR | src/lib/i18n/locales/it.ts |
pt |
Portuguese | LTR | src/lib/i18n/locales/pt.ts |
ru |
Russian | LTR | src/lib/i18n/locales/ru.ts |
hi |
Hindi | LTR | src/lib/i18n/locales/hi.ts |
ja |
Japanese | LTR | src/lib/i18n/locales/ja.ts |
th |
Thai | LTR | src/lib/i18n/locales/th.ts |
fa |
Persian | RTL | src/lib/i18n/locales/fa.ts |
zh |
Simplified Chinese | LTR | src/lib/i18n/locales/zh.ts |
src/lib/i18n/
locales/
en.ts # English (primary, all keys defined here first)
ar.ts # Arabic
de.ts # German
es.ts # Spanish
fr.ts # French
it.ts # Italian
pt.ts # Portuguese
ru.ts # Russian
hi.ts # Hindi
ja.ts # Japanese
th.ts # Thai
fa.ts # Persian
zh.ts # Simplified Chinese
index.ts # LocaleProvider, useTranslation hook export
src/types/i18n.types.ts # Type-safe translation key definitions
src/hooks/use-locale.ts # Locale management hook
src/enums/locale.enum.ts # Locale enum values
The LocaleProvider wraps the entire application in src/app/providers.tsx:
<LocaleProvider initialLocale={serverLocale}>
<ThemeProvider>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</ThemeProvider>
</LocaleProvider>Each locale file exports a typed dictionary object:
// src/lib/i18n/locales/en.ts
import type { TranslationDictionary } from '@/types/i18n.types';
export const en: TranslationDictionary = {
common: {
save: 'Save',
cancel: 'Cancel',
delete: 'Delete',
edit: 'Edit',
create: 'Create',
search: 'Search',
loading: 'Loading...',
noResults: 'No results found',
// ... more common keys
},
auth: {
login: 'Login',
logout: 'Logout',
email: 'Email',
password: 'Password',
// ...
},
chat: {
newThread: 'New Chat',
sendMessage: 'Send',
messageSendFailed: 'Failed to send message',
// ...
},
// ... more domain sections
};import { useTranslation } from '@/lib/i18n';
function MyComponent(): ReactElement {
const { t } = useTranslation();
return (
<div>
<h1>{t('dashboard.title')}</h1>
<p>{t('common.noResults')}</p>
<Button>{t('common.save')}</Button>
</div>
);
}-
Never hardcode user-facing text -- always use
t('key') -
All 13 locale files must stay in sync -- every key in
en.tsmust exist in all others - Keys are type-safe -- TypeScript will error if you use a key that does not exist
- Domain-organized -- keys are grouped by feature area (common, auth, chat, connectors, etc.)
Edit src/types/i18n.types.ts to add the new key:
export type TranslationDictionary = {
common: {
// ... existing keys
newKey: string; // Add here
};
// ...
};// src/lib/i18n/locales/en.ts
common: {
// ... existing
newKey: 'The English text',
},You must add the corresponding translation to each file:
-
ar.ts-- Arabic translation -
de.ts-- German translation -
es.ts-- Spanish translation -
fr.ts-- French translation -
it.ts-- Italian translation -
pt.ts-- Portuguese translation -
ru.ts-- Russian translation -
hi.ts-- Hindi translation -
ja.ts-- Japanese translation -
th.ts-- Thai translation -
fa.ts-- Persian translation -
zh.ts-- Simplified Chinese translation
const { t } = useTranslation();
return <span>{t('common.newKey')}</span>;- Key added to
src/types/i18n.types.ts - English text added to
en.ts - Arabic translation added to
ar.ts - German translation added to
de.ts - Spanish translation added to
es.ts - French translation added to
fr.ts - Italian translation added to
it.ts - Portuguese translation added to
pt.ts - Russian translation added to
ru.ts - Hindi translation added to
hi.ts - Japanese translation added to
ja.ts - Thai translation added to
th.ts - Persian translation added to
fa.ts - Simplified Chinese translation added to
zh.ts - Used via
t('key')in component (never hardcoded)
Arabic and Persian use right-to-left text direction. The application handles this by:
-
Global
dirattribute: For/ar/*and/fa/*,dir="rtl"is server-rendered on the HTML root -
CSS logical properties: Tailwind CSS logical properties (
ms-,me-,ps-,pe-) are preferred overml-,mr-,pl-,pr-for correct RTL behavior - Layout mirroring: Sidebar, navigation, and content areas are automatically mirrored
// CORRECT -- uses logical properties (works in both LTR and RTL)
<div className="ms-4 ps-2" />
// LESS IDEAL -- physical properties (may need RTL override)
<div className="ml-4 pl-2" />To test Arabic RTL layout:
- Go to Settings page
- Change language to Arabic
- Verify sidebar appears on the right
- Verify text alignment is right-to-left
- Verify form inputs align correctly
The locale prefix is authoritative. Middleware validates it, stamps
x-claw-locale, and the root layout passes that server locale to
LocaleProvider. Storage and account preferences can influence a future
redirect, but cannot change the language rendered for an already-prefixed URL.
The user's language preference is stored in the user profile via the settings page:
- User selects language in Settings
-
useUpdatePreferencesmutation sends to backend - Backend stores in user preferences
- On next login,
usePreferenceBootstrapreads the saved preference and applies it - The
UserLanguagePreferenceenum maps to locale codes
Keys are organized by domain to keep them manageable:
| Section | Purpose | Example Keys |
|---|---|---|
common |
Shared across features | save, cancel, delete, loading, error |
auth |
Authentication screens | login, logout, email, password |
chat |
Chat feature | newThread, sendMessage, noMessages |
connectors |
Connector management | create, testConnection, syncModels |
models |
Model management | localModels, pullModel, assignRole |
routing |
Routing policies | createPolicy, autoMode, decisions |
memory |
Memory management | createMemory, memoryTypes, toggle |
files |
File management | upload, processing, download |
audit |
Audit logs | auditLog, usageStats, actions |
logs |
Log viewer | clientLogs, serverLogs, level |
settings |
User settings | appearance, language, changePassword |
admin |
Admin page | userManagement, roles |
dashboard |
Dashboard | title, recentActivity, systemHealth |
health |
Health/observability | healthy, degraded, unhealthy |
validation |
Form validation messages | required, tooLong, invalidEmail |
errors |
Error messages | serverError, networkError |
A test in src/lib/i18n/__tests__/ verifies that all 13 locale files have the
same keys and interpolation placeholders as English. This prevents missing or
structurally incompatible translations from reaching production.
The test iterates over all keys in en.ts and asserts they exist in every other locale file.
ClawAI Wiki · source-controlled under wiki/ · generated/mirrored from the current repository knowledge layer.
ClawAI Wiki
Product
- Product-Vision
- Flagship-Features
- Business-Overview
- Requirements-Register
- Drift-Log
- Runtime-Managed-Connector-Providers
- Threads
- ADR-Index
Architecture
- System-at-a-Glance
- Architecture-Map
- System-Architecture
- Message-Flow
- End-to-End-Data-Flow
- Routing-Engine
- Event-Bus
- Security-Architecture
Platform
- Backend-Services
- Frontend-Architecture
- Shared-Packages
- Database-Reference
- API-Reference
- Provider-Catalog
- Memory-and-Context-Architecture
- File-Support-Architecture
- Research-and-Web-Crawling
Runtime & Ops
- Docker-Guide
- Nginx-Reference
- CI-CD-Pipeline
- Logging-and-Observability
- Operational-Runbooks
- Troubleshooting
Quality
Engineering OS
- AI-Native-Engineering-OS
- Generated-Manifests
- Rules-Catalog
- Skills-Catalog
- Context-Layer
- Engineering-Memory
- Tooling-and-Automation
- Scripts-and-Operations
- ESLint-and-Architecture-Enforcement
- Extended-Work-Skills
Agents
Reference