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.
83 lines
5.2 KiB
Markdown
83 lines
5.2 KiB
Markdown
# 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.
|