fix(i18n): overhaul i18n with [locale] routing, SSR translations, and hreflang SEO

Rewrites the i18n system introduced in PR #94 to use Next.js App Router
[locale] dynamic segments with SSR-rendered translations and proper
middleware locale routing.

- Add middleware locale detection: / rewrites to /zh-CN/ internally,
  /en and /ja pass through, /zh-CN/... redirects to bare path
- Move all 7 pages under app/[locale]/ with SSR translation injection
- Fix server→client serialization: pre-evaluate function-valued
  translations (makeSerializable) to eliminate hydration flash
- Fix language switch key flash: use hard navigation with localStorage-
  only persistence, avoiding React state update before page reload
- Add <link rel="alternate" hreflang> tags for multilingual SEO
- Fix Supabase setAll overwriting locale rewrite response
- Trim locales from 22 to 3 (zh-CN/en/ja), delete 19 incomplete files
- LLM-translate 240 firstact game preset JSONs (en + ja, landscape +
  portrait) and story titles via gemini-3.5-flash
- Delete 11 one-off migration scripts and outdated i18n docs
- Add useLocalePath hook and navigation utilities

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
yuanzonghao
2026-06-18 23:16:17 +08:00
parent 941b54c3f8
commit 0a7076d5b9
301 changed files with 2447 additions and 4358 deletions
-183
View File
@@ -1,183 +0,0 @@
# InfiPlot i18n Implementation
## Summary
A complete i18n infrastructure has been implemented for InfiPlot, enabling support for 22 languages:
- English (en)
- Simplified Chinese (zh-CN) - Source language
- Traditional Chinese Taiwan (zh-TW)
- Traditional Chinese Hong Kong (zh-HK)
- Japanese (ja)
- Korean (ko)
- Spanish (es)
- French (fr)
- German (de)
- Portuguese Brazil (pt-BR)
- Portuguese (pt)
- Russian (ru)
- Italian (it)
- Vietnamese (vi)
- Thai (th)
- Indonesian (id)
- Turkish (tr)
- Polish (pl)
- Dutch (nl)
- Ukrainian (uk)
- Hindi (hi)
- Czech (cs)
## What Was Implemented
### 1. Core i18n Infrastructure (`lib/i18n/`)
- **config.ts**: Locale configuration, locale names, storage key management
- **types.ts**: TypeScript types for translation system
- **utils.ts**: Helper functions for nested value access, string formatting
- **client.tsx**: React context provider and `useI18n()` hook for client components
- **server.ts**: Server-side translation utilities for Next.js App Router
- **index.ts**: Main export file
### 2. Translation Files (`lib/i18n/locales/`)
- **zh-CN.ts**: Complete source translations (Chinese)
- **en.ts**: Reference English translations
- Additional locale files will be generated by the translation script
### 3. Translation Script (`scripts/translate-i18n.mjs`)
A Node.js script that:
- Reads the source zh-CN translation file
- Uses LLM APIs (Gemini or OpenAI-compatible) to translate to all target languages
- Preserves structure and handles special cases:
- Placeholder variables (`{{email}}`, `{n}`, etc.)
- HTML tags (`<em>`, `<a>`, etc.)
- Select/message format syntax
- Proper nouns (InfiPlot, GitHub, Google, etc.)
- Generates TypeScript locale files
- Updates client.tsx and server.ts imports automatically
Usage:
```bash
# With Gemini
node scripts/translate-i18n.mjs --provider gemini --api-key YOUR_KEY
# With OpenAI-compatible API
TEXT_API_KEY=your_key TEXT_BASE_URL=https://api.openai.com/v1 node scripts/translate-i18n.mjs --provider openai
```
### 4. Components Updated with i18n
- ✅ CustomForm.tsx
- ✅ DialogueHistoryModal.tsx
- ✅ AuthModal.tsx
- ✅ PlayCanvas.tsx
- ✅ SettingsModal.tsx
- ✅ page.tsx (home page)
- ✅ layout.tsx (I18nProvider wrapper)
- ✅ LanguageSwitcher.tsx (new component)
## Current Status
### Completed
1. **i18n Infrastructure** - All core files in `lib/i18n/`
2. **Translation Files** - zh-CN.ts (source) and en.ts (reference) complete
3. **Stub Files** - Created for all 20 target languages (fallback to en)
4. **Component Integration** - All UI components now use `t()` function
5. **Language Switcher** - Added to page header with dropdown UI
6. **TypeScript Types** - Full type safety for translation system
### Remaining Work
1. **Generate Actual Translations**
- Run the translation script to translate stub files
- Review and edit generated translations for quality
- Test with native speakers if possible
2. **Update Metadata** (optional)
- Make page titles and descriptions dynamic based on locale
- Update `lang` attribute on html element dynamically
### Optional Enhancements
1. **Server-Side Rendering Support**
- Implement locale detection from Accept-Language header
- Add middleware for locale routing (e.g., /en/play, /zh-CN/play)
- Cache translations for better performance
2. **Date/Number Formatting**
- Add locale-specific formatting for dates, numbers, currencies
- Use Intl.DateTimeFormat and Intl.NumberFormat
3. **RTL Support**
- Currently no RTL locales, but infrastructure is in place
- Add layout mirroring if needed for future RTL languages
4. **Pluralization**
- Enhance formatTranslation to support ICU message format
- Handle singular/plural forms
## Translation Best Practices
When adding new strings:
1. Keep strings neutral where possible
2. Avoid culturally-specific references
3. Provide context for translators in comments
4. Test with longer strings (German, Russian can be 2-3x longer)
5. Keep placeholders consistent (`{{varName}}` or `{varName}`)
## API Keys Required
To generate translations, set one of:
- `GEMINI_API_KEY` for Google Gemini (recommended for cost)
- `TEXT_API_KEY` for OpenAI-compatible API
- `TEXT_BASE_URL` for custom OpenAI-compatible endpoints
## Files Modified
### New Files Created
- `lib/i18n/` (entire directory)
- `config.ts` - Locale configuration
- `client.tsx` - React context provider
- `server.ts` - Server-side utilities
- `utils.ts` - Helper functions
- `locales/zh-CN.ts` - Source translations
- `locales/en.ts` - English reference
- `locales/zh-TW.ts` - Traditional Chinese stub
- `locales/zh-HK.ts` - Hong Kong Chinese stub
- `locales/ja.ts` - Japanese stub
- `locales/ko.ts` - Korean stub
- `locales/es.ts` - Spanish stub
- `locales/fr.ts` - French stub
- `locales/de.ts` - German stub
- `locales/pt-BR.ts` - Portuguese Brazil stub
- `locales/pt.ts` - Portuguese stub
- `locales/ru.ts` - Russian stub
- `locales/it.ts` - Italian stub
- `locales/vi.ts` - Vietnamese stub
- `locales/th.ts` - Thai stub
- `locales/id.ts` - Indonesian stub
- `locales/tr.ts` - Turkish stub
- `locales/pl.ts` - Polish stub
- `locales/nl.ts` - Dutch stub
- `locales/uk.ts` - Ukrainian stub
- `locales/hi.ts` - Hindi stub
- `locales/cs.ts` - Czech stub
- `components/LanguageSwitcher.tsx` - Language selector component
- `scripts/translate-i18n.mjs` - Translation script
- `docs/i18n-implementation.md` - This documentation
### Modified Files
- `app/layout.tsx` - Added I18nProvider wrapper
- `app/page.tsx` - Added i18n to all strings and LanguageSwitcher
- `components/CustomForm.tsx` - Added i18n
- `components/DialogueHistoryModal.tsx` - Added i18n
- `components/AuthModal.tsx` - Added i18n
- `components/PlayCanvas.tsx` - Added i18n
- `components/SettingsModal.tsx` - Added i18n
## TypeScript
All type definitions are in place. Run `pnpm typecheck` to verify.