mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 14:16:12 +00:00
Retroactive cleanup of CHANGELOG.md and docs/audit/v1.4.X-summary.md files. Removes maintainer's real name, specific health figures tied to the live tenant, internal release-process jargon, and stray German strings in English-only release notes. Pre-release PII-grep gate added to the v1.4.20 release pipeline. No git history rewrite. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
129 lines
6.4 KiB
Markdown
129 lines
6.4 KiB
Markdown
# v1.4.15 — i18n coverage audit
|
||
|
||
**Date**: 2026-05-09
|
||
**Stage**: C4
|
||
**Scope**: every `t("…")` / `useTranslations()` call in `src/`
|
||
vs. `messages/en.json` and `messages/de.json`. Goal: ensure every
|
||
v1.4.15-introduced key (cleanup A1–A4, B-mobile, B1–B6, C2, C3) has a
|
||
non-empty, non-placeholder translation in BOTH locales and follows the
|
||
namespace conventions baked in by the Stage 4b admin reorg.
|
||
|
||
## Method
|
||
|
||
1. `grep -rh -oE 't\("[a-zA-Z][a-zA-Z0-9_.-]*"' src/` — extract every
|
||
call-site key (1318 unique).
|
||
2. Walk both JSON files manually (no `JSON.parse`, since duplicate keys
|
||
silently shadow earlier values) — same parser used by the existing
|
||
`i18n-locale-integrity.test.ts`.
|
||
3. Compare:
|
||
- **drift**: keys in EN but not DE, or vice versa
|
||
- **empty**: `value === ""`
|
||
- **placeholders**: `value === keyLastSegment`, or `/\b(TODO|FIXME|XXX|TBD)\b/i`
|
||
4. Cross-locale identical strings checked per-namespace — many are
|
||
legitimate (brand names, ICU placeholder strings, anglicisms used
|
||
in DE) so the heuristic only flagged for review, not auto-fix.
|
||
|
||
## Findings
|
||
|
||
| Check | Count | Notes |
|
||
| ---------------------------------- | ----: | -------------------------------- |
|
||
| Total keys (EN) | 1817 | |
|
||
| Total keys (DE) | 1817 | |
|
||
| Keys only in EN | 0 | full parity |
|
||
| Keys only in DE | 0 | full parity |
|
||
| Empty values (EN) | 0 | |
|
||
| Empty values (DE) | 0 | |
|
||
| TODO/FIXME placeholders (EN) | 0 | |
|
||
| TODO/FIXME placeholders (DE) | 0 | |
|
||
| Used-in-`src/` but missing in EN | 0 | after excluding allow-list below |
|
||
| EN==key (last segment, both EN+DE) | 5 | all legitimate (see §Legitimate) |
|
||
|
||
### Allow-list — used-key matches that are NOT i18n keys
|
||
|
||
These appeared in the grep but are deliberately excluded:
|
||
|
||
- `job.*` — Wide Event names passed to `withBackgroundEvent("job.x")`,
|
||
not i18n.
|
||
- `scheduler.tick` — Wide Event name.
|
||
- `empty.measurements.*` — JSDoc `@example` block in
|
||
`src/components/ui/empty-state.tsx`, never executed.
|
||
- `settings.testConnection.errors.` — dynamic concat root in
|
||
`src/components/settings/test-connection-button.tsx`. Concrete
|
||
error-codes (`generic`, `credentials_rejected`, `timeout`, …) are
|
||
all present.
|
||
- `nope.also.missing`, `telegram.__definitely_missing__` — vitest
|
||
fixtures asserting fallback behaviour
|
||
(`src/lib/__tests__/server-translator.test.ts`).
|
||
|
||
### Legitimate EN==DE==key cases
|
||
|
||
5 entries where both locales return the key's last segment unchanged:
|
||
|
||
| Path | Value | Reason |
|
||
| --------------------------------- | --------- | --------------------------------------------------- |
|
||
| `settings.ntfy` | `ntfy` | brand name, not translated |
|
||
| `classifications.bp.Optimal` | `Optimal` | identical word in EN and DE |
|
||
| `classifications.bp.Normal` | `Normal` | identical |
|
||
| `classifications.pulse.Normal` | `Normal` | identical |
|
||
| `classifications.bodyFat.Fitness` | `Fitness` | identical (anglicism in DE medical/fitness context) |
|
||
|
||
A further 25 entries are EN==key but DE has an actual translation
|
||
(`classifications.bmi.Underweight` → `Untergewicht`, etc.) — these are
|
||
fine.
|
||
|
||
### v1.4.15 new namespaces — coverage check
|
||
|
||
| Namespace | Origin | EN keys | DE keys | Drift | Empty |
|
||
| ------------------------------- | ---------- | ---------: | ------: | ----: | ----: |
|
||
| `admin.section.backups.*` | B1 backups | 35 | 35 | 0 | 0 |
|
||
| `achievements.*` | B4 | 106 | 106 | 0 | 0 |
|
||
| `onboarding.tour.*` | B5 | 18 | 18 | 0 | 0 |
|
||
| `admin.overview.*` | A2 | 16 | 16 | 0 | 0 |
|
||
| `settings.notificationStatus.*` | B3 | 13 | 13 | 0 | 0 |
|
||
| `settings.integrationStatus.*` | B2 | 6 | 6 | 0 | 0 |
|
||
| `doctorReport.*` | B6 | 90 | 90 | 0 | 0 |
|
||
| `quickAdd.*` (`common.add` etc) | A3 | (`common`) | — | 0 | 0 |
|
||
|
||
### Naming consistency
|
||
|
||
Repo doc note: Stage 4b moved admin keys under
|
||
`admin.section.<slug>.*`. Verified all v1.4.15-introduced admin
|
||
sections follow that pattern:
|
||
|
||
```
|
||
admin.section.{system-status,general,services,integrations,
|
||
feedback,reminders,users,api-tokens,login-overview,
|
||
backups,danger-zone}
|
||
```
|
||
|
||
Pre-Phase-4b legacy keys remain at the flat `admin.*` namespace (e.g.
|
||
`admin.bugReportRepo`, `admin.userManagement`) — those are out of
|
||
scope for this audit (renaming would touch many components for zero
|
||
behavioural gain). New stage-B code did NOT introduce inconsistent
|
||
flat keys; e.g. B5 used `onboarding.tour.*` (not `tour.*`); B4 used
|
||
`achievements.*` (not flat `badges.*`); B1 used `admin.section.backups.*`
|
||
(not flat `backups.*`); B2/B3 added `settings.{integrationStatus,notificationStatus}.*`
|
||
under the existing `settings` umbrella.
|
||
|
||
## Outcome
|
||
|
||
**Zero gaps closed**. The five parallel stage-B work-streams and the
|
||
C2/C3 stages each added their own EN+DE translations as part of their
|
||
own commits, so by the time C4 ran sequentially after Batch 2 there was
|
||
nothing left to back-fill. The audit instead converted to a
|
||
quality-assurance sweep: tightening `i18n-locale-integrity.test.ts` to
|
||
lock in the non-empty + non-placeholder invariants going forward.
|
||
|
||
## Test guard
|
||
|
||
`src/lib/__tests__/i18n-locale-integrity.test.ts` is extended with:
|
||
|
||
- assertion that no value in either locale file is the empty string
|
||
- assertion that no value equals its key's last segment (with an
|
||
explicit allow-list for the 5 legitimate cases above)
|
||
- non-fatal warning (logged via `console.warn` in CI) for values
|
||
containing `\bTODO\b` / `\bFIXME\b` / `\bXXX\b`
|
||
|
||
These guards prevent regression: any future change that ships a
|
||
`"key": ""` or `"bugReport": "bugReport"` value will see CI go red.
|