fix(l10n): repair corrupted duration abbreviations in ar/bn/hi/ja/ru/zh ARB files
Build & Deploy PluriWave / Análisis de código (push) Successful in 25s
Build & Deploy PluriWave / Build APK + AAB release (push) Successful in 2m24s

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.
This commit is contained in:
Javier Bautista Fernández
2026-07-21 09:56:18 +02:00
parent 12967e9894
commit fb7fe8774b
15 changed files with 517 additions and 24 deletions
@@ -0,0 +1,82 @@
# 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.