Internationalization (i18n)

December 21, 2025 · View on GitHub

iDO supports multiple languages through react-i18next. This guide covers adding translations and managing locales.

Current Languages

  • English (en) - Source language
  • Chinese Simplified (zh-CN) - Complete translation

File Structure

src/locales/
├── index.ts           # Locale configuration
├── en.ts              # English (source)
└── zh-CN.ts           # Chinese Simplified

Adding Translations

Step 1: Add to English (Source)

// src/locales/en.ts
export const en = {
  myFeature: {
    title: 'My Feature',
    description: 'This is my feature',
    actions: {
      save: 'Save',
      cancel: 'Cancel',
      delete: 'Delete'
    },
    messages: {
      success: 'Saved successfully',
      error: 'Failed to save'
    }
  }
}

Step 2: Add Corresponding Chinese

// src/locales/zh-CN.ts
export const zhCN = {
  myFeature: {
    title: '我的功能',
    description: '这是我的功能',
    actions: {
      save: '保存',
      cancel: '取消',
      delete: '删除'
    },
    messages: {
      success: '保存成功',
      error: '保存失败'
    }
  }
}

Step 3: Validate

pnpm check-i18n

This command checks:

  • ✅ All keys in en.ts exist in zh-CN.ts
  • ✅ No extra keys in zh-CN.ts
  • ✅ Structure matches exactly

Using Translations

In Components

import { useTranslation } from 'react-i18next'

function MyComponent() {
  const { t } = useTranslation()
  
  return (
    <div>
      <h1>{t('myFeature.title')}</h1>
      <p>{t('myFeature.description')}</p>
      <button>{t('myFeature.actions.save')}</button>
    </div>
  )
}

With Variables

// In locale file
export const en = {
  greeting: 'Hello, {{name}}!',
  itemCount: 'You have {{count}} items'
}

// In component
t('greeting', { name: 'Alice' })
// → "Hello, Alice!"

t('itemCount', { count: 5 })
// → "You have 5 items"

Pluralization

// In locale file
export const en = {
  items: 'item',
  items_plural: 'items'
}

// In component
t('items', { count: 1 })   // → "item"
t('items', { count: 5 })   // → "items"

Changing Language

In Settings

Users can change language in Settings → Language

Programmatically

import { i18n } from '@/lib/i18n'

// Change language
await i18n.changeLanguage('zh-CN')

// Get current language
const currentLang = i18n.language  // 'en' or 'zh-CN'

Adding a New Language

1. Create Locale File

// src/locales/ja-JP.ts
export const jaJP = {
  // Copy structure from en.ts
  // Translate all values
}

2. Register in Index

// src/locales/index.ts
import { jaJP } from './ja-JP'

export const resources = {
  en: { translation: en },
  'zh-CN': { translation: zhCN },
  'ja-JP': { translation: jaJP }  // Add new language
}

3. Add to Settings

// src/components/settings/LanguageSettings.tsx
const languages = [
  { code: 'en', label: 'English' },
  { code: 'zh-CN', label: '简体中文' },
  { code: 'ja-JP', label: '日本語' }  // Add new option
]

4. Validate

pnpm check-i18n

Best Practices

Key Naming

// ✅ Use hierarchical structure
{
  activity: {
    timeline: {
      title: 'Activity Timeline',
      empty: 'No activities'
    }
  }
}

// ❌ Avoid flat structure
{
  activityTimelineTitle: 'Activity Timeline',
  activityTimelineEmpty: 'No activities'
}

Keep Source Updated

Always update English (en.ts) first, then other languages.

# After updating en.ts
pnpm check-i18n
# → Shows which keys need translation in zh-CN.ts

Use Semantic Keys

// ✅ Semantic key names
{
  actions: {
    save: 'Save',
    cancel: 'Cancel'
  }
}

// ❌ Generic names
{
  button1: 'Save',
  button2: 'Cancel'
}

Avoid Hard-coded Strings

// ❌ Hard-coded
<button>Save</button>

// ✅ Translated
<button>{t('actions.save')}</button>

Backend i18n

Backend uses separate TOML files for LLM prompts:

backend/config/
├── prompts_en.toml    # English prompts
└── prompts_zh.toml    # Chinese prompts

Language is selected in config.toml:

[language]
default_language = "en"  # or "zh"

Troubleshooting

Translation Not Showing

// Check console for missing key warnings
// i18next will show: "key 'myFeature.title' not found"

// Verify key path
console.log(t('myFeature.title'))

Validation Failing

# Run validation to see errors
pnpm check-i18n

# Common issues:
# - Missing key in translation
# - Extra key in translation  
# - Structure mismatch (object vs string)

Language Not Persisting

Check that language setting is saved to localStorage:

// Should be handled by i18n config
// If not working, check src/lib/i18n.ts

Resources