Skip to Content
SharedInternationalization (EN / UZ / RU)

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.json defines the full key set; uz/ru fall back to en for 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 client LanguageProvider + useTranslation() hook; derives DictionaryKeys from en.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 appLang cookie (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 and placeholder, title, aria-label, and toast/error strings.
  • Module‑scope label maps can’t call t(). Store a labelKey: DictionaryKeys and 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.