mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 21:41:09 +02:00
Translation infrastructure (@mana/shared-i18n + svelte-i18n + 35
per-module locale files with ~3500 lines across de/en/it/fr/es) is fully
wired, but 65/78 modules still hardcode German in .svelte templates
rather than calling {$_('module.key')}.
Adds:
- scripts/audit-i18n-coverage.mjs — scans lib/modules/**/*.svelte for
hardcoded German keywords (Abbrechen, Speichern, Löschen, etc.) in
files that don't import $_(). Reports per-module hit counts,
bucket (FULL/PARTIAL/NONE), and whether the locale file exists.
Supports --summary and --top N flags.
- pnpm run audit:i18n-coverage wires it into the audit:* family
(reporting only, not a CI gate — existing debt would fail
validate:all otherwise).
- docs/optimizable/i18n-migration-inventory.md — priority list,
per-module workflow, and prevention plan.
Top offenders: broadcast (26 hits), articles (24), events (23),
invoices (22), quiz (20), stretch (20), library (19), profile (17),
skilltree (15, PARTIAL), calendar (14, PARTIAL). Modules without a
locale file (broadcast/articles/events/invoices/…) need the locale
stubs scaffolded first.
Real string migration is per-site careful work (key naming, 5-language
parity, UI visual QA) and is left for per-module follow-up sessions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
91 lines
3.8 KiB
Markdown
91 lines
3.8 KiB
Markdown
# i18n Migration Inventory
|
||
|
||
**Status as of 2026-04-22.** Run `pnpm run audit:i18n-coverage` for current numbers.
|
||
|
||
## The gap
|
||
|
||
The unified Mana web app has `@mana/shared-i18n` + `svelte-i18n`
|
||
**fully wired** — per-module translation files live under
|
||
`apps/mana/apps/web/src/lib/i18n/locales/{module}/{de,en,it,fr,es}.json`
|
||
for 35 modules, with ~3500 lines of German and matching English /
|
||
Italian / French / Spanish. The infrastructure is done.
|
||
|
||
What's missing is the **usage**: most module `.svelte` templates
|
||
hardcode German strings like `"Abbrechen"`, `"+ Neues Mood"`,
|
||
`"Keine Daten"` instead of calling `{$_('module.key')}`.
|
||
|
||
Scanning `lib/modules/**/*.svelte`:
|
||
|
||
- **FULL** (4 modules) — all files import `$_()` from svelte-i18n
|
||
- **PARTIAL** (9 modules) — mixed usage
|
||
- **NONE** (65 modules) — German hardcoded throughout
|
||
|
||
## Why not auto-migrate?
|
||
|
||
String migration is per-site careful work:
|
||
- pick a key name (or reuse existing one from the locale file)
|
||
- pick the German source text (may differ slightly from existing keys)
|
||
- add key to all 5 language files (de/en/it/fr/es)
|
||
- replace in template, test UI visually
|
||
- tests covering copy may break
|
||
|
||
It's not a mechanical codemod. Each module is a session of its own.
|
||
|
||
## Priorities
|
||
|
||
Rank by `keyword-hits × user-impact`. Run the audit for current numbers:
|
||
|
||
```bash
|
||
pnpm run audit:i18n-coverage --summary --top 20
|
||
```
|
||
|
||
Top offenders (2026-04-22 snapshot):
|
||
|
||
| Rank | Module | Hits | Files | Locale? | Notes |
|
||
|---|---|---|---|---|---|
|
||
| 1 | broadcast | 26 | 10 | ✗ | Content broadcast compose + recipient UI |
|
||
| 2 | articles | 24 | 16 | ✗ | Reader view, highlights, filter labels |
|
||
| 3 | events | 23 | 12 | ✗ | RSVP, guest list, discovery |
|
||
| 4 | invoices | 22 | 9 | ✗ | Business-critical: line items, payment states |
|
||
| 5 | quiz | 20 | 3 | ✗ | Edit + play flows, question types |
|
||
| 6 | stretch | 20 | 6 | ✗ | Session flows, reminders, routines |
|
||
| 7 | library | 19 | 11 | ✗ | Book/media tracker: status, filtering |
|
||
| 8 | profile | 17 | 4 | ✓ | Interview flow, context overview |
|
||
| 9 | skilltree | 15 | 11 | ✓ | PARTIAL — modals done, ListView hardcoded |
|
||
| 10 | calendar | 14 | 15 | ✓ | PARTIAL — EventForm done, ListView labels |
|
||
|
||
**Already FULL:** `body`, `todo`, `times`, and the modules whose
|
||
keyword count is zero (either trivially small templates or consistent
|
||
use of `$_()`).
|
||
|
||
## Recommended workflow per module
|
||
|
||
1. **If no locale file exists** — run the skilltree/moodlit layout as
|
||
a template. Start with: `nav.*`, `common.{save,cancel,delete,confirm}`,
|
||
`list.{empty,count,newItem}`, `create.{title,save,namePlaceholder}`,
|
||
plus module-specific vocabulary.
|
||
2. **Extract German strings** into `de.json` — keep them verbatim so
|
||
existing copy doesn't regress.
|
||
3. **Translate to `en.json`** (required for sync across language
|
||
switch). Keep `it/fr/es.json` with English fallbacks if translator
|
||
not available.
|
||
4. **Replace in templates** — `import { _ } from 'svelte-i18n'` at the
|
||
top, then `{$_('module.key')}` inline. For attributes:
|
||
`placeholder={$_('module.namePlaceholder')}`.
|
||
5. **Update `i18n/index.ts`** — add the module to `registerLocale()` if
|
||
it isn't already listed (broadcast/articles/events/invoices/quiz/
|
||
stretch/library/wishes/guides/habits/dreams/firsts/companion/
|
||
ai-missions all need entries).
|
||
6. **Re-run the audit** — `pnpm run audit:i18n-coverage --summary` —
|
||
expect the module to move from NONE → FULL.
|
||
7. **Manual browser test** — the audit is pattern-based; visual QA
|
||
catches anything it missed.
|
||
|
||
## Prevention (future work)
|
||
|
||
The audit currently reports only — it doesn't fail CI. A future step
|
||
could graduate it to a gate that:
|
||
- blocks PRs that *add* hardcoded German to ListViews already migrated
|
||
- continues to tolerate existing debt until it's cleared
|
||
|
||
Until then: `audit:i18n-coverage` is the contract.
|