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

5.2 KiB
Raw Blame History

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.