Internationalization (EN / UZ / RU)
A small, hand‑rolled system — no i18n library. English is the source; Uzbek is the default shown language; Russian is the third.
Pieces
src/i18n/locales/{en,uz,ru}.json— flat{ "<camelCaseKey>": "<value>" }dictionaries.en.jsondefines the full key set;uz/rufall back toenfor any missing key. All three must stay in key parity (same keys, same order).src/i18n/config.ts— non‑client constants (LOCALES,DEFAULT_LOCALE = 'uz',LOCALE_COOKIE = 'appLang',isLocale). Safe to import from the edge proxy.src/i18n/index.tsx— the clientLanguageProvider+useTranslation()hook; derivesDictionaryKeysfromen.json.src/i18n/dictionary.ts—getT(locale), a server‑side translator for Server Components that can’t use the hook (e.g. the login page).
URL‑driven locale
The active language comes from the URL segment (/en, /uz, /ru), not from
state. LanguageProvider receives lang from the route params (root
[locale]/layout.tsx). The language switcher calls setLang, which:
- writes the
appLangcookie (so the proxy honors it on locale‑less requests), - navigates to the same path under the new locale.
Using it
'use client'
import { useTranslation } from '@/i18n'
const { t } = useTranslation()
<button>{t('save')}</button>
t('daysBefore', { days: 30 }) // interpolation: "30 days before"Rules:
- Everything user‑facing goes through
t()— JSX text andplaceholder,title,aria-label, and toast/error strings. - Module‑scope label maps can’t call
t(). Store alabelKey: DictionaryKeysand resolve at render (t(meta.labelKey)) — pattern used across the codebase. - Never translate dynamic data: hotel/room/service/client names, prices, phone numbers, INN, contract numbers, or brand text (“Easy Service”).
Adding a string
Add the key to all three JSON files at the same position, then use t('key').
There’s a project skill (.claude/skills/i18n-translations/) that documents this
and a detection grep for stray English.
Not translated
Telegram messages are intentionally Russian regardless of the app locale (see telegram.md); CLI scripts print emoji/English.