Skip to content

Internationalization (i18n)

Overview

MysticX supports 12 locales using next-intl with inline dictionaries rather than static JSON translation files.

Supported Locales

CodeLanguageScript Direction
enEnglishLTR
zh_CNSimplified ChineseLTR
zh_TWTraditional ChineseLTR
jaJapaneseLTR
koKoreanLTR
ptPortugueseLTR
esSpanishLTR
frFrenchLTR
deGermanLTR
arArabicRTL
idIndonesianLTR
nlDutchLTR

URL Structure

Routes follow the pattern /{locale}/path:

/en/pricing
/zh-CN/pricing
/ja/pricing

URL locales use hyphen format (next-intl convention): zh-CN, zh-TW.

Note: Inside TypeScript code and database JSON keys, the underscore form is used (zh_CN, zh_TW). The URL segment is always hyphenated.

Translation Approach

User-Facing Pages

Use the useTrans (client) and getTrans (server) hooks with inline dictionaries:

tsx
const trans = useTrans();

return (
  <h1>{trans({
    en: 'Welcome to MysticX',
    zh_CN: '欢迎来到 MysticX',
    ja: 'MysticX へようこそ',
    ko: 'MysticX에 오신 것을 환영합니다',
    pt: 'Bem-vindo ao MysticX',
    es: 'Bienvenido a MysticX',
    fr: 'Bienvenue sur MysticX',
    de: 'Willkommen bei MysticX',
    ar: 'مرحبا بكم في MysticX',
    zh_TW: '歡迎來到 MysticX',
    id: 'Selamat datang di MysticX',
    nl: 'Welkom bij MysticX',
  })}</h1>
);

Admin Panel

English-only. Do NOT use useTrans in admin pages.

Database Content

Localized database content uses TLocalizedString JSON fields:

json
{
  "en": "The Fool",
  "zh_CN": "愚者",
  "ja": "愚者",
  "ko": "바보"
}

Types: TLocalizedString for strings, TLocalized<T> for generic typed content.

Adding a New Locale

Follow these phases when adding a new locale:

  1. Database migration — Add the new locale key to all JSON fields across 13 models
  2. Infrastructure — Register in routing config, type definitions, and normalization bridge
  3. Localization — Auth error messages, SEO metadata
  4. App Router pages — Update all trans({}) calls (127+ files across 3 batches)
  5. Components and lib — Update shared component translations

Phase 1 (DB) must complete before deploying code changes. TypeScript will flag missing locale keys at compile time.

Tarot Spread Names

Canonical spread name translations are in docs/tarot-spreads.md. Do NOT invent new translations for spread names.

Key Rules

  • Never use Record<string, string> for localized content. Use TLocalized<T> or TLocalizedString.
  • Never use \uXXXX unicode escapes. Write actual characters directly (e.g., '正在解读…').
  • Use escaped single quotes for strings containing apostrophes: 'L\'IA'.
  • Admin panel is English-only — no translation needed.

Internal documentation for MysticX team