Files
pluriwave/openspec/changes/archive/2026-07-21-i18n-locale-audit/design.md
T
Javier Bautista Fernández fb7fe8774b
Build & Deploy PluriWave / Análisis de código (push) Successful in 25s
Build & Deploy PluriWave / Build APK + AAB release (push) Successful in 2m24s
fix(l10n): repair corrupted duration abbreviations in ar/bn/hi/ja/ru/zh ARB files
Sleep-timer duration strings (durationHoursMinutesSeconds, durationMinutesSeconds,
durationMinutesOnly, durationSecondsOnly) contained literal "?" characters instead
of the native hour/minute/second abbreviation in 6 locales. Replaced with correct
native short-form units per locale, verified byte-exact against a pinned spec table
and against the already-correct neighboring hoursLabel/minutesLabel/secondsLabel
values in each file.
2026-07-21 09:56:18 +02:00

83 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Design: Repair corrupted sleep-timer duration abbreviations in 6 ARB locales
## Technical Approach
Content-only data repair. For each of the 6 corrupted ARB files (`app_{ar,bn,hi,ja,ru,zh}.arb`), replace the value of exactly 4 duration keys (`durationHoursMinutesSeconds`, `durationMinutesSeconds`, `durationMinutesOnly`, `durationSecondsOnly`, physical lines 19/27/34/40) with the byte-exact pinned strings from the spec table. No Dart, no CI, no generated-file changes. Validation is static-only (tooling hangs here). Maps directly to the proposal's "Approach" and the spec's four requirements.
## Architecture Decisions
### Decision: Full-value line replacement (not `?`-glyph swap)
| Option | Tradeoff | Decision |
|---|---|---|
| Swap only `?` → abbrev in place | Fails for ja/zh: pinned strings also collapse inter-unit spaces (`{hours}?? {minutes}? {seconds}?``{hours}時間{minutes}分{seconds}秒`), so a glyph-only swap yields wrong output | Rejected |
| Replace the entire quoted value on each target line | Spec pins the exact final string per cell; one deterministic edit per line, no per-locale spacing logic | **Chosen** |
**Rationale**: The spec table is the single source of truth. The edit unit is the whole line `"<key>": "<value>",`; `old_string` = current corrupted line, `new_string` = pinned line. Surrounding bytes (and the other ~386 keys) are never touched.
### Decision: Surgical single-line edits, no JSON re-serialization
| Option | Tradeoff | Decision |
|---|---|---|
| Parse → mutate → re-dump JSON | Reflows/re-escapes all ~386 keys, reorders/renormalizes unicode, huge noisy diff, risk to untouched cells | Rejected |
| Exact-string edit of only the 4 target lines/file | Diff is exactly 4 lines/file (24 total); all other bytes provably identical | **Chosen** |
### Decision: Static validation instead of flutter/dart tooling
`flutter build`, `gen-l10n`, `dart analyze`, `flutter analyze` all hang in this environment and MUST NOT run. A short Python one-off (stdlib `json`, byte reads) gives equivalent safety for a content-only change: JSON well-formedness, UTF-8 integrity, diff scope, residual-`?`, and placeholder integrity.
## Data Flow
spec table (24 pinned cells)
│ authoritative source
Edit tool ──(4 exact-string line edits/file)──▶ app_{locale}.arb ×6
static validator (Python + git diff)
│ pass/fail gate
single commit (6 files)
## File Changes
| File | Action | Description |
|------|--------|-------------|
| `lib/l10n/app_ar.arb` | Modify | Lines 19/27/34/40: values → pinned `ar` cells |
| `lib/l10n/app_bn.arb` | Modify | Lines 19/27/34/40: values → pinned `bn` cells |
| `lib/l10n/app_hi.arb` | Modify | Lines 19/27/34/40: values → pinned `hi` cells |
| `lib/l10n/app_ja.arb` | Modify | Lines 19/27/34/40: values → pinned `ja` cells (spaces collapse) |
| `lib/l10n/app_ru.arb` | Modify | Lines 19/27/34/40: values → pinned `ru` cells |
| `lib/l10n/app_zh.arb` | Modify | Lines 19/27/34/40: values → pinned `zh` cells (spaces collapse) |
**Explicitly out of scope (unchanged):** `lib/l10n/gen/app_localizations.dart` (generated; do NOT hand-edit — ICU placeholder shape `{hours}/{minutes}/{seconds}` is identical pre/post, so no regeneration is needed for these keys to render correctly), `app_localizations_ext.dart`, any CI/workflow file, and the `@durationXxx.placeholders` metadata blocks.
## Interfaces / Contracts
No interface change. ICU placeholder names, casing, and count are invariant: `{hours}`, `{minutes}`, `{seconds}`. The generated `AppLocalizations` method signatures are unaffected.
## Testing Strategy
| Layer | What to Test | Approach |
|-------|-------------|----------|
| Static | All 6 files parse as JSON | Python `json.load` each file |
| Static | UTF-8 integrity, no replacement char | Read bytes, `decode('utf-8')` strict; assert no `` |
| Static | Diff scope = only the 4 lines/file | `git diff --stat` / per-line diff; assert exactly 24 changed lines total, additions==deletions |
| Static | Zero literal `?` in 24 target values | Assert `'?' not in value` for each target key |
| Static | Placeholder integrity | Regex-extract `{...}` tokens from each fixed value; assert equals expected set/order per key; `@`-metadata blocks byte-unchanged |
| Static | Byte-exact match to spec | Assert each fixed value `==` its pinned spec cell |
`git diff --check` additionally guards against whitespace/EOL damage. No runtime/widget test (no code path changes; tooling forbidden).
## Threat Matrix
N/A — no routing, shell, subprocess, VCS/PR automation, executable-file classification, or process-integration boundary. Pure ARB content edit.
## Migration / Rollout
No migration. Single commit touching only 6 ARB files; `git revert` fully restores prior state with zero cross-dependencies.
## Open Questions
- [ ] None — the spec pins all 24 target cells byte-exactly; no content or architectural decision remains.