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.
This commit is contained in:
+4
-4
@@ -16,7 +16,7 @@
|
||||
"hoursLabel": "ساعات",
|
||||
"minutesLabel": "دقائق",
|
||||
"secondsLabel": "ثوانٍ",
|
||||
"durationHoursMinutesSeconds": "{hours} ? {minutes} ? {seconds} ?",
|
||||
"durationHoursMinutesSeconds": "{hours} س {minutes} د {seconds} ث",
|
||||
"@durationHoursMinutesSeconds": {
|
||||
"placeholders": {
|
||||
"hours": {},
|
||||
@@ -24,20 +24,20 @@
|
||||
"seconds": {}
|
||||
}
|
||||
},
|
||||
"durationMinutesSeconds": "{minutes} ? {seconds} ?",
|
||||
"durationMinutesSeconds": "{minutes} د {seconds} ث",
|
||||
"@durationMinutesSeconds": {
|
||||
"placeholders": {
|
||||
"minutes": {},
|
||||
"seconds": {}
|
||||
}
|
||||
},
|
||||
"durationMinutesOnly": "{minutes} ?",
|
||||
"durationMinutesOnly": "{minutes} د",
|
||||
"@durationMinutesOnly": {
|
||||
"placeholders": {
|
||||
"minutes": {}
|
||||
}
|
||||
},
|
||||
"durationSecondsOnly": "{seconds} ?",
|
||||
"durationSecondsOnly": "{seconds} ث",
|
||||
"@durationSecondsOnly": {
|
||||
"placeholders": {
|
||||
"seconds": {}
|
||||
|
||||
+4
-4
@@ -16,7 +16,7 @@
|
||||
"hoursLabel": "ঘণ্টা",
|
||||
"minutesLabel": "মিনিট",
|
||||
"secondsLabel": "সেকেন্ড",
|
||||
"durationHoursMinutesSeconds": "{hours} ? {minutes} ?? {seconds} ??",
|
||||
"durationHoursMinutesSeconds": "{hours} ঘণ্টা {minutes} মিনিট {seconds} সেকেন্ড",
|
||||
"@durationHoursMinutesSeconds": {
|
||||
"placeholders": {
|
||||
"hours": {},
|
||||
@@ -24,20 +24,20 @@
|
||||
"seconds": {}
|
||||
}
|
||||
},
|
||||
"durationMinutesSeconds": "{minutes} ?? {seconds} ??",
|
||||
"durationMinutesSeconds": "{minutes} মিনিট {seconds} সেকেন্ড",
|
||||
"@durationMinutesSeconds": {
|
||||
"placeholders": {
|
||||
"minutes": {},
|
||||
"seconds": {}
|
||||
}
|
||||
},
|
||||
"durationMinutesOnly": "{minutes} ??",
|
||||
"durationMinutesOnly": "{minutes} মিনিট",
|
||||
"@durationMinutesOnly": {
|
||||
"placeholders": {
|
||||
"minutes": {}
|
||||
}
|
||||
},
|
||||
"durationSecondsOnly": "{seconds} ??",
|
||||
"durationSecondsOnly": "{seconds} সেকেন্ড",
|
||||
"@durationSecondsOnly": {
|
||||
"placeholders": {
|
||||
"seconds": {}
|
||||
|
||||
+4
-4
@@ -16,7 +16,7 @@
|
||||
"hoursLabel": "घंटे",
|
||||
"minutesLabel": "मिनट",
|
||||
"secondsLabel": "सेकंड",
|
||||
"durationHoursMinutesSeconds": "{hours} ?? {minutes} ?? {seconds} ??",
|
||||
"durationHoursMinutesSeconds": "{hours} घंटे {minutes} मिनट {seconds} सेकंड",
|
||||
"@durationHoursMinutesSeconds": {
|
||||
"placeholders": {
|
||||
"hours": {},
|
||||
@@ -24,20 +24,20 @@
|
||||
"seconds": {}
|
||||
}
|
||||
},
|
||||
"durationMinutesSeconds": "{minutes} ?? {seconds} ??",
|
||||
"durationMinutesSeconds": "{minutes} मिनट {seconds} सेकंड",
|
||||
"@durationMinutesSeconds": {
|
||||
"placeholders": {
|
||||
"minutes": {},
|
||||
"seconds": {}
|
||||
}
|
||||
},
|
||||
"durationMinutesOnly": "{minutes} ??",
|
||||
"durationMinutesOnly": "{minutes} मिनट",
|
||||
"@durationMinutesOnly": {
|
||||
"placeholders": {
|
||||
"minutes": {}
|
||||
}
|
||||
},
|
||||
"durationSecondsOnly": "{seconds} ??",
|
||||
"durationSecondsOnly": "{seconds} सेकंड",
|
||||
"@durationSecondsOnly": {
|
||||
"placeholders": {
|
||||
"seconds": {}
|
||||
|
||||
+4
-4
@@ -16,7 +16,7 @@
|
||||
"hoursLabel": "時間",
|
||||
"minutesLabel": "分",
|
||||
"secondsLabel": "秒",
|
||||
"durationHoursMinutesSeconds": "{hours}?? {minutes}? {seconds}?",
|
||||
"durationHoursMinutesSeconds": "{hours}時間{minutes}分{seconds}秒",
|
||||
"@durationHoursMinutesSeconds": {
|
||||
"placeholders": {
|
||||
"hours": {},
|
||||
@@ -24,20 +24,20 @@
|
||||
"seconds": {}
|
||||
}
|
||||
},
|
||||
"durationMinutesSeconds": "{minutes}? {seconds}?",
|
||||
"durationMinutesSeconds": "{minutes}分{seconds}秒",
|
||||
"@durationMinutesSeconds": {
|
||||
"placeholders": {
|
||||
"minutes": {},
|
||||
"seconds": {}
|
||||
}
|
||||
},
|
||||
"durationMinutesOnly": "{minutes}?",
|
||||
"durationMinutesOnly": "{minutes}分",
|
||||
"@durationMinutesOnly": {
|
||||
"placeholders": {
|
||||
"minutes": {}
|
||||
}
|
||||
},
|
||||
"durationSecondsOnly": "{seconds}?",
|
||||
"durationSecondsOnly": "{seconds}秒",
|
||||
"@durationSecondsOnly": {
|
||||
"placeholders": {
|
||||
"seconds": {}
|
||||
|
||||
+4
-4
@@ -16,7 +16,7 @@
|
||||
"hoursLabel": "Часы",
|
||||
"minutesLabel": "Минуты",
|
||||
"secondsLabel": "Секунды",
|
||||
"durationHoursMinutesSeconds": "{hours} ? {minutes} ??? {seconds} ?",
|
||||
"durationHoursMinutesSeconds": "{hours} ч {minutes} мин {seconds} сек",
|
||||
"@durationHoursMinutesSeconds": {
|
||||
"placeholders": {
|
||||
"hours": {},
|
||||
@@ -24,20 +24,20 @@
|
||||
"seconds": {}
|
||||
}
|
||||
},
|
||||
"durationMinutesSeconds": "{minutes} ??? {seconds} ?",
|
||||
"durationMinutesSeconds": "{minutes} мин {seconds} сек",
|
||||
"@durationMinutesSeconds": {
|
||||
"placeholders": {
|
||||
"minutes": {},
|
||||
"seconds": {}
|
||||
}
|
||||
},
|
||||
"durationMinutesOnly": "{minutes} ???",
|
||||
"durationMinutesOnly": "{minutes} мин",
|
||||
"@durationMinutesOnly": {
|
||||
"placeholders": {
|
||||
"minutes": {}
|
||||
}
|
||||
},
|
||||
"durationSecondsOnly": "{seconds} ?",
|
||||
"durationSecondsOnly": "{seconds} сек",
|
||||
"@durationSecondsOnly": {
|
||||
"placeholders": {
|
||||
"seconds": {}
|
||||
|
||||
+4
-4
@@ -16,7 +16,7 @@
|
||||
"hoursLabel": "小时",
|
||||
"minutesLabel": "分钟",
|
||||
"secondsLabel": "秒",
|
||||
"durationHoursMinutesSeconds": "{hours} ?? {minutes} ? {seconds} ?",
|
||||
"durationHoursMinutesSeconds": "{hours}小时{minutes}分钟{seconds}秒",
|
||||
"@durationHoursMinutesSeconds": {
|
||||
"placeholders": {
|
||||
"hours": {},
|
||||
@@ -24,20 +24,20 @@
|
||||
"seconds": {}
|
||||
}
|
||||
},
|
||||
"durationMinutesSeconds": "{minutes} ? {seconds} ?",
|
||||
"durationMinutesSeconds": "{minutes}分钟{seconds}秒",
|
||||
"@durationMinutesSeconds": {
|
||||
"placeholders": {
|
||||
"minutes": {},
|
||||
"seconds": {}
|
||||
}
|
||||
},
|
||||
"durationMinutesOnly": "{minutes} ?",
|
||||
"durationMinutesOnly": "{minutes}分钟",
|
||||
"@durationMinutesOnly": {
|
||||
"placeholders": {
|
||||
"minutes": {}
|
||||
}
|
||||
},
|
||||
"durationSecondsOnly": "{seconds} ?",
|
||||
"durationSecondsOnly": "{seconds}秒",
|
||||
"@durationSecondsOnly": {
|
||||
"placeholders": {
|
||||
"seconds": {}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Apply Progress: i18n-locale-audit
|
||||
Status: complete (all 13 tasks done, 1 batch)
|
||||
|
||||
## Phase 1: Content Repair — done
|
||||
All 24 exact-string line replacements applied across the 6 locale files (`app_ar.arb`, `app_bn.arb`, `app_hi.arb`, `app_ja.arb`, `app_ru.arb`, `app_zh.arb`), lines 19/27/34/40 each, matching the pinned spec table byte-exactly. No JSON re-serialization; surgical single-line `Edit` calls only.
|
||||
|
||||
## Phase 2: Static Validation — done
|
||||
- 2.1 JSON parse: all 6 files parse successfully.
|
||||
- 2.2 UTF-8 strict decode: no `�` replacement characters.
|
||||
- 2.3 Diff scope: `git diff --stat` shows exactly 24 insertions / 24 deletions across the 6 files (8 changed lines each); per-file hunk headers confirm only lines 19/27/34/40 touched.
|
||||
- 2.4 Zero literal `?` in the 24 target values.
|
||||
- 2.5 Placeholder integrity: extracted `{...}` tokens match expected `{hours}`/`{minutes}`/`{seconds}` set/order per key; `@durationXxx.placeholders` metadata blocks unchanged (implicit — not part of the diff).
|
||||
- 2.6 Byte-exact match: all 24 values equal their pinned spec-table cell.
|
||||
- 2.7 `git diff --check`: exit 0, no whitespace/EOL damage.
|
||||
|
||||
## Notes
|
||||
- Did not run `flutter build`, `flutter gen-l10n`, `dart analyze`, or `flutter analyze` per instructions (they hang in this environment).
|
||||
- Validation performed with `python` (not `python3`, which is unavailable on this Windows/git-bash environment; `python` resolves to `/c/Python/Python312/python`).
|
||||
- No files other than the 6 target ARB files were touched.
|
||||
@@ -0,0 +1,75 @@
|
||||
# Archive Report: i18n-locale-audit
|
||||
|
||||
**Change**: i18n-locale-audit
|
||||
**Status**: Archived
|
||||
**Archive Location**: `openspec/changes/archive/2026-07-21-i18n-locale-audit/`
|
||||
**Date Archived**: 2026-07-21
|
||||
|
||||
## Summary
|
||||
|
||||
The i18n-locale-audit change has been completed, verified (PASS verdict with zero CRITICAL/WARNING/SUGGESTION findings), and archived. This change repaired 24 corrupted sleep-timer duration strings across 6 non-Latin-script locales (ar, bn, hi, ja, ru, zh) by replacing literal ASCII `?` characters with their correct native abbreviations, while preserving all ICU placeholders and file integrity.
|
||||
|
||||
## Artifact Traceability
|
||||
|
||||
All artifacts originate from the SDD pipeline and have been traced to their Engram observations:
|
||||
|
||||
| Artifact | Engram Observation ID | Type | Status |
|
||||
|----------|----------------------|------|--------|
|
||||
| Proposal | 1303 | proposal | Complete |
|
||||
| Exploration | N/A | explore | Complete (file-only) |
|
||||
| Specification | 1304 | spec | Complete |
|
||||
| Design | 1305 | design | Complete |
|
||||
| Tasks | 1306 | tasks | Complete (all 13 tasks checked) |
|
||||
| Apply Progress | N/A | apply-progress | Complete (file-only) |
|
||||
| Verify Report | 1308 | verify-report | Complete — PASS verdict |
|
||||
|
||||
All observation IDs recorded for audit trail and traceability.
|
||||
|
||||
## Specs Synced
|
||||
|
||||
| Domain | Action | Details |
|
||||
|--------|--------|---------|
|
||||
| sleep-timer-duration-i18n | Created | New spec domain, 4 requirements covering native abbreviations across 6 locales (ja, zh, ru, ar, hi, bn). Added to `openspec/specs/sleep-timer-duration-i18n/spec.md`. |
|
||||
|
||||
## Archive Contents
|
||||
|
||||
- proposal.md ✅ — Proposal: Repair corrupted sleep-timer duration abbreviations in 6 ARB locales
|
||||
- explore.md ✅ — Exploration: i18n locale audit (13 ARB locales)
|
||||
- design.md ✅ — Design: Repair corrupted sleep-timer duration abbreviations in 6 ARB locales
|
||||
- tasks.md ✅ — Tasks: all 13 tasks complete (7 Phase 1 content repairs + 7 Phase 2 static validation checks)
|
||||
- apply-progress.md ✅ — Apply Progress: complete (all 24 exact-string line replacements applied)
|
||||
- verify-report.md ✅ — Verify Report: PASS (all requirements met, all 24 values byte-identical to spec)
|
||||
- specs/sleep-timer-duration-i18n/spec.md ✅ — Sleep Timer Duration I18n Specification
|
||||
|
||||
## Source of Truth Updated
|
||||
|
||||
The following specs now reflect the new behavior and are locked into the main specifications tree:
|
||||
|
||||
- `openspec/specs/sleep-timer-duration-i18n/spec.md` — NEW domain, defining correct native abbreviations for 4 ICU-templated duration strings across 6 locales with comprehensive scenarios and requirements.
|
||||
|
||||
## Implementation Summary
|
||||
|
||||
**Changed lines**: 24 insertions + 24 deletions (exact-string line replacement, no JSON re-serialization)
|
||||
**Files modified**: lib/l10n/app_ar.arb, app_bn.arb, app_hi.arb, app_ja.arb, app_ru.arb, app_zh.arb (4 keys per file, lines 19/27/34/40)
|
||||
**Quality gate**: Verify-report PASS with zero critical/warning/suggestion findings
|
||||
|
||||
All 24 corrupted duration string values have been replaced with their correct byte-exact pinned strings:
|
||||
- ja: compact no-space notation (時間/分/秒)
|
||||
- zh: compact no-space notation (小时/分钟/秒)
|
||||
- ru: short abbreviation with leading space (ч/мин/сек)
|
||||
- ar: short abbreviation with leading space (س/د/ث)
|
||||
- hi: full label words, space-separated (घंटे/मिनट/सेकंड)
|
||||
- bn: full label words, space-separated (ঘণ্টা/মিনिট/সেকেন्ড)
|
||||
|
||||
ICU placeholders `{hours}`, `{minutes}`, `{seconds}` remain unchanged and correctly named. All other keys (386 per file) and metadata blocks remain byte-unchanged.
|
||||
|
||||
## SDD Cycle Complete
|
||||
|
||||
The change has been fully planned (proposal + explore), specified (spec), designed (design), implemented (apply), verified (verify-report PASS), and archived. Ready for the next change.
|
||||
|
||||
## Additional Notes
|
||||
|
||||
- Task completion gate: PASS — all 13 tasks marked as complete in tasks.md with supporting evidence in apply-progress.md and verify-report.md.
|
||||
- Verify gate: PASS — zero CRITICAL/WARNING/SUGGESTION findings. Spec requirements fully met.
|
||||
- No stale checkboxes found; archive-time reconciliation not required.
|
||||
- Spec merge: delta spec copied directly to new main domain (no merge with existing spec required, as this is a new domain).
|
||||
@@ -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.
|
||||
@@ -0,0 +1,65 @@
|
||||
# Exploration: i18n locale audit (13 ARB locales)
|
||||
|
||||
## Current State
|
||||
l10n.yaml: arb-dir=lib/l10n, template-arb-file=app_es.arb, output-dir=lib/l10n/gen, output-class=AppLocalizations.
|
||||
13 ARB files present: app_{ar,bn,de,en,es,fr,hi,id,it,ja,pt,ru,zh}.arb.
|
||||
lib/l10n/gen/app_localizations.dart (generated, checked into git, not gitignored) declares supportedLocales with the same 13 Locale() entries in the same order.
|
||||
lib/app.dart:83 wires `supportedLocales: AppLocalizations.supportedLocales` into MaterialApp — inventory matches exactly, no drift.
|
||||
A second, hand-maintained translation source exists outside the ARB pipeline: lib/l10n/app_localizations_ext.dart (PluriAppLocalizationsExt) — per-locale Map<String,String> for weekdayLong/monthName/dateTimeSentence, covering all 13 locale codes with `values['en']` fallback if a code is missing.
|
||||
|
||||
## Key Parity (template = app_es.arb)
|
||||
Verified via `grep -c '^ "[a-zA-Z][a-zA-Z0-9]*":'` (excludes "@key" ICU metadata and "@@locale") on all 13 files: every single file — es included — has exactly 387 top-level translation keys. No missing keys, no extra/orphaned keys, no empty values found in sampled content.
|
||||
|
||||
Previously-flagged line-count mismatch (es=637 lines vs all others=674 lines, per prior audit note) — RE-VERIFIED, confirmed as a FALSE ALARM / pure JSON-formatting difference, NOT a key/content defect. Root cause: app_es.arb compacts several `"@key": {"placeholders": {...}}` ICU metadata blocks onto a single line (e.g. es:471 `"@miniPlayerOpenLabel": {"placeholders": {"stationName": {}}},`), while all 12 other locale files expand the identical metadata object across 4-5 lines. This inflates their line count by ~37 with zero actual key/content difference (confirmed by the matching 387-key count above).
|
||||
|
||||
## Untranslated Content
|
||||
Project-memory-flagged "SEARCH pilot block" issue (app_ja.arb/app_zh.arb/app_ru.arb allegedly still using English fallback for search* keys) — RE-VERIFIED AND RESOLVED. All three files now have genuine native translations: ja "電波を探す"/"ラジオ・オリゾンテ、ジャズ、ニュース..."; zh "搜索信号"/"地平线电台、爵士、新闻..."; ru "Поиск сигнала"/"Радио Горизонт, джаз, новости...".
|
||||
Broad regex scan for 3+ consecutive Latin words inside string values, across app_{ar,bn,hi,ja,ru,zh}.arb: ZERO matches — no leftover English/copy-paste blocks found in any non-Latin-script locale.
|
||||
|
||||
## Encoding Integrity — CRITICAL FINDING
|
||||
- No U+FFFD replacement characters in any of the 13 ARB files.
|
||||
- No double-encoded UTF-8 mojibake (é-style) in any of the 13 ARB files.
|
||||
- Accented Latin (es/fr/de/pt/it) and non-Latin scripts (ja/zh/ar/hi/bn/ru) render correctly and intact across hundreds of sampled lines per file.
|
||||
- HOWEVER: literal ASCII "?" (U+003F) characters — genuine data-loss corruption, not mojibake — replace native duration-unit abbreviations in exactly 4 keys × 6 non-Latin-script locale files (24 corrupted lines total, always at file lines 19/27/34/40):
|
||||
- Keys: durationHoursMinutesSeconds, durationMinutesSeconds, durationMinutesOnly, durationSecondsOnly.
|
||||
- lib/l10n/app_ar.arb:19,27,34,40 e.g. `"{hours} ? {minutes} ? {seconds} ?"`
|
||||
- lib/l10n/app_bn.arb:19,27,34,40 e.g. `"{hours} ? {minutes} ?? {seconds} ??"`
|
||||
- lib/l10n/app_hi.arb:19,27,34,40 e.g. `"{hours} ?? {minutes} ?? {seconds} ??"`
|
||||
- lib/l10n/app_ja.arb:19,27,34,40 e.g. `"{hours}?? {minutes}? {seconds}?"`
|
||||
- lib/l10n/app_ru.arb:19,27,34,40 e.g. `"{hours} ? {minutes} ??? {seconds} ?"`
|
||||
- lib/l10n/app_zh.arb:19,27,34,40 e.g. `"{hours} ?? {minutes} ? {seconds} ?"`
|
||||
- Every other string in these 6 files (383 other keys each) is correctly translated — corruption is isolated to exactly these 4 keys, consistent with one past edit/tool that wrote these 4 lines through a non-UTF-8-safe path, not a whole-file encoding problem.
|
||||
- Latin-script locales unaffected (their duration abbreviations are plain ASCII: en h/min/s, de Std/Min/Sek, fr/it/pt h/min/s, id jam/mnt/dtk — nothing to corrupt).
|
||||
- ICU placeholder tokens {hours}/{minutes}/{seconds} are still present and correctly named in the corrupted lines — no placeholder/crash risk, purely visual content corruption.
|
||||
- User impact: these 4 strings feed `_formatearDuracionTimer()` in lib/app.dart:492-512, rendering the sleep-timer countdown/duration in the "Timer de sueño" bottom sheet (`_mostrarTimerDialog`, lib/app.dart). Users on ar/bn/hi/ja/ru/zh locales see literal "?" instead of their language's h/min/s abbreviation.
|
||||
- Not caught by CI: JSON stays valid, ICU placeholders intact, so `flutter analyze`/gen-l10n do not fail.
|
||||
|
||||
## ICU/Placeholder Consistency
|
||||
Sampled ~35 placeholder/plural-bearing keys across all 13 locales (duration family, skipCurrentAlarmExecution, languageUpdated, equalizer*, preferredStation*, backup*, recordingsMaxSize*, favoriteGroups*, favorites*, stationsCount, nearYouInCountry, alarmsCount, activeAlarmsWithoutNextSubtitle, alarmFadeIn*, alarmNextExecution, alarmSkipped*, alarmVacation*, alarmSnoozeOptionLabel, androidReliabilityStatus, miniPlayerOpenLabel, stationSemanticLabel, equalizerBand*, radio*Error, alarmSchedule*, localMusicFolderSaveError, advancedEqDevicePresetLabel, *Countdown, stationCount plural). All placeholder tokens present/correctly named everywhere sampled. stationCount plural forms correctly vary per CLDR: es/en use =1/other; ja/zh use only "other" (correct, no plural distinction in these languages); ru correctly uses one/few/other. No mismatches found.
|
||||
|
||||
## Remaining Hardcoded Literals
|
||||
Broad regex scan of lib/pantallas/**/*.dart and lib/widgets/**/*.dart for Text('...')/Text("...") literals and label:/title:/tooltip:/hintText:/content: literal assignments found only ONE match: lib/widgets/pluri_onboarding_dialog.dart:115 `title: Text('v${nota.version}')` — a version-number prefix, technical format, not translatable (consistent with documented "technical formats" exclusion). No other hardcoded user-facing literal found — the June 2026 i18n migration appears complete across sampled screens/widgets.
|
||||
lib/pantallas/pantalla_ajustes.dart:497-509 hardcodes 13 language-picker autonyms (English/Español/中文/हिन्दी/العربية/Português/Français/Русский/Deutsch/日本語/Bahasa Indonesia/বাংলা/Italiano) — documented intentional autonym exception, not a gap.
|
||||
Structural risk (not currently broken): lib/l10n/app_localizations_ext.dart is a hand-maintained Map<String,String> per locale (not validated by gen-l10n/CI) — a future locale added to supportedLocales without updating this file would silently fall back to English for weekday/month/date-sentence text.
|
||||
|
||||
## CI Relevance
|
||||
.gitea/workflows/build.yml job `analizar` runs `flutter analyze --no-fatal-infos --no-fatal-warnings` (line 31) — warnings do not fail the build. `flutter build apk`/`appbundle` (lines 110/113) implicitly regenerate AppLocalizations from ARB but only fail on invalid JSON/ICU syntax, not on wrong/corrupted string content. The "?" duration-key corruption would ship silently past this CI today.
|
||||
|
||||
## Severity-Ranked Findings
|
||||
1. [HIGH] Broken encoding/content — literal "?" replacing native duration-unit abbreviations in durationHoursMinutesSeconds/durationMinutesSeconds/durationMinutesOnly/durationSecondsOnly, in app_ar.arb, app_bn.arb, app_hi.arb, app_ja.arb, app_ru.arb, app_zh.arb (lines 19/27/34/40 each). User-visible in sleep-timer feature; not caught by CI.
|
||||
2. [MEDIUM] Structural risk in lib/l10n/app_localizations_ext.dart — hand-maintained, unvalidated by tooling; latent risk for future locale additions (not currently broken, all 13 current locales present).
|
||||
3. [LOW/INFO — confirmed non-issue] es=637 vs others=674 line-count disparity is pure JSON-formatting difference (compacted vs expanded @metadata), zero key/content impact.
|
||||
4. [LOW/INFO — confirmed RESOLVED] Prior "SEARCH pilot English fallback" in ja/zh/ru is fixed; genuinely translated now.
|
||||
5. [NONE FOUND] No missing/extra/empty keys anywhere; no placeholder/ICU mismatches; no U+FFFD or mojibake; no remaining hardcoded literals beyond one benign technical-format false positive.
|
||||
|
||||
## Tooling/Environment Limitation (risk for orchestrator)
|
||||
This explore-phase execution had access only to Read/Grep/Glob/WebFetch/WebSearch/mem_save/codegraph_explore — no Bash/git tool and no file-write (Write/Edit) tool were available.
|
||||
- Could NOT run `git fetch`/`git pull` to check for latest remote changes (part of the user's original request) — needs to happen in a phase/agent with git access before any push.
|
||||
- Could NOT write `openspec/changes/i18n-locale-audit/explore.md` to disk (hybrid artifact-store mode requires both Engram + filesystem; only the Engram half could be completed here). The orchestrator or a phase with file-write access should persist this content to that path.
|
||||
- Did not run `flutter build`/`flutter gen-l10n`/`dart analyze`/`flutter analyze` per hard environment constraint (these hang in this sandbox) — all findings are from static reads/grep only; full JSON schema/ICU validation via the real Dart intl tooling was not performed (structural sampling strongly suggests valid JSON/ICU throughout, but this is not a substitute for the real generator).
|
||||
|
||||
## Recommendation
|
||||
Proceed to sdd-propose for a fix scoped narrowly to: (a) replace the 24 corrupted "?" duration-format strings in the 6 affected ARB files with correct native translations, sourced from a native speaker or a verified translation reference (do not machine-translate blindly given the project's "real native translations" quality bar); (b) optionally add a lightweight CI/pre-commit check that greps ARB files for stray literal "?" adjacent to placeholders, to prevent silent recurrence; (c) note the app_localizations_ext.dart structural risk for a future hardening task (not urgent, no current locale is missing).
|
||||
|
||||
## Ready for Proposal
|
||||
Yes — findings are concrete, file/line-cited, and scoped small enough (4 keys × 6 files) for a low-risk, low-line-count fix PR.
|
||||
@@ -0,0 +1,50 @@
|
||||
# Proposal: Repair corrupted sleep-timer duration abbreviations in 6 ARB locales
|
||||
|
||||
## Intent
|
||||
Six non-Latin-script locales ship literal ASCII `?`/`??`/`???` where the native hour/minute/second abbreviation belongs, in the 4 sleep-timer duration keys. Users on ar/bn/hi/ja/ru/zh see `1 ? 30 ? 0 ?` instead of e.g. `1時間 30分 0秒` in the "Timer de sueño" bottom sheet (`_formatearDuracionTimer`, lib/app.dart:492-512). JSON/ICU stay valid, so CI (`flutter analyze`) never catches it — a silent, user-visible quality defect from a prior non-UTF-8-safe edit. Fix now: the corruption is isolated, verified, and cheap to repair correctly.
|
||||
|
||||
## Scope
|
||||
|
||||
### In Scope
|
||||
- Repair 24 corrupted strings = 4 keys (`durationHoursMinutesSeconds`, `durationMinutesSeconds`, `durationMinutesOnly`, `durationSecondsOnly`) × 6 files: `app_ar.arb`, `app_bn.arb`, `app_hi.arb`, `app_ja.arb`, `app_ru.arb`, `app_zh.arb` (lines 19/27/34/40 each).
|
||||
- Replace each `?` with the genuine native short-form unit abbreviation for that locale, keeping ICU placeholders `{hours}`/`{minutes}`/`{seconds}` intact and correctly named.
|
||||
- Static validation only: JSON-parse + UTF-8 byte integrity + grep for residual `?`-adjacent-placeholder.
|
||||
|
||||
### Out of Scope (future-work notes only)
|
||||
- No refactor of `lib/l10n/app_localizations_ext.dart` (hand-maintained date map — latent structural risk, not broken; separate hardening task).
|
||||
- No new CI/pre-commit ARB lint (recommended but deferred; separate change).
|
||||
- No broader i18n rework, no other keys/locales, no re-formatting of the 386 untouched keys per file.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
None.
|
||||
|
||||
### Modified Capabilities
|
||||
None — data-only content repair; no requirement/behavior change, no generated-code signature change (ICU shape identical).
|
||||
|
||||
## Approach
|
||||
Per-locale, source the correct native abbreviation from the existing verified full labels already present in each file (`hoursLabel`/`minutesLabel`/`secondsLabel`, lines 16-18) plus locale conventions (ja 時間/分/秒, zh 小时/分/秒, ru ч/мин/с, ar/hi/bn native short forms). Edit only the 4 corrupted lines per file via exact-string replacement; preserve surrounding bytes and UTF-8 encoding. Never run flutter/dart tooling (hangs here).
|
||||
|
||||
## Affected Areas
|
||||
| Area | Impact | Description |
|
||||
|------|--------|-------------|
|
||||
| `lib/l10n/app_{ar,bn,hi,ja,ru,zh}.arb` | Modified | 4 duration lines each repaired |
|
||||
|
||||
## Risks
|
||||
| Risk | Likelihood | Mitigation |
|
||||
|------|------------|------------|
|
||||
| Wrong/non-native abbreviation glyph | Med | Anchor to in-file labels + verified references; spec pins exact strings |
|
||||
| Re-encoding side effects to untouched keys | Low | Exact-string edits; post-edit UTF-8 + JSON diff check |
|
||||
| Generated `app_localizations.dart` drift | Low | ICU shape unchanged; content-only |
|
||||
|
||||
## Rollback Plan
|
||||
Single commit touching only 6 ARB files; `git revert` restores prior state with zero cross-dependencies.
|
||||
|
||||
## Dependencies
|
||||
- Native/reference-verified abbreviation set for ar/bn/hi/ja/ru/zh (resolved in sdd-spec).
|
||||
|
||||
## Success Criteria
|
||||
- [ ] Zero literal `?` remaining in the 24 target strings; placeholders intact.
|
||||
- [ ] Each locale renders its native h/min/s abbreviation.
|
||||
- [ ] All 6 files parse as valid UTF-8 JSON; 386 other keys per file byte-unchanged.
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
# Sleep Timer Duration I18n Specification
|
||||
|
||||
## Purpose
|
||||
|
||||
Defines the correct native-language content for the 4 ICU-templated sleep-timer duration strings (`durationHoursMinutesSeconds`, `durationMinutesSeconds`, `durationMinutesOnly`, `durationSecondsOnly`) across the 6 currently corrupted ARB locales: `ar`, `bn`, `hi`, `ja`, `ru`, `zh`. This is a new spec domain — no prior spec covered these strings.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: Native short-form duration abbreviations
|
||||
The system MUST render, for each of the 6 locales, the exact pinned native hour/minute/second abbreviation text below in place of every literal `?`/`??`/`???` corruption, with no other content in the 24 target strings altered.
|
||||
|
||||
| Locale | `durationHoursMinutesSeconds` | `durationMinutesSeconds` | `durationMinutesOnly` | `durationSecondsOnly` |
|
||||
|---|---|---|---|---|
|
||||
| ja | `{hours}時間{minutes}分{seconds}秒` | `{minutes}分{seconds}秒` | `{minutes}分` | `{seconds}秒` |
|
||||
| zh | `{hours}小时{minutes}分钟{seconds}秒` | `{minutes}分钟{seconds}秒` | `{minutes}分钟` | `{seconds}秒` |
|
||||
| ru | `{hours} ч {minutes} мин {seconds} сек` | `{minutes} мин {seconds} сек` | `{minutes} мин` | `{seconds} сек` |
|
||||
| ar | `{hours} س {minutes} د {seconds} ث` | `{minutes} د {seconds} ث` | `{minutes} د` | `{seconds} ث` |
|
||||
| hi | `{hours} घंटे {minutes} मिनट {seconds} सेकंड` | `{minutes} मिनट {seconds} सेकंड` | `{minutes} मिनट` | `{seconds} सेकंड` |
|
||||
| bn | `{hours} ঘণ্টা {minutes} মিনিট {seconds} সেকেন্ড` | `{minutes} মিনिট {seconds} সেকেন্ড` | `{minutes} মিনিট` | `{seconds} সেকেন्ड` |
|
||||
|
||||
Rationale: ja/zh use conventional no-space compact duration notation (e.g. `1時間30分15秒`, `2小时30分钟15秒`); ru/ar use the OS-standard single/short abbreviation with a leading space per unit (`ч`/`мин`/`сек`, `س`/`د`/`ث`); hi/bn lack a reliable standard short form, so the full native label words (already verified at `hoursLabel`/`minutesLabel`/`secondsLabel`) are reused verbatim, space-separated.
|
||||
|
||||
#### Scenario: Full duration display renders native units
|
||||
- GIVEN a user on locale `ja` opens the sleep-timer bottom sheet with a duration of 1 hour, 30 minutes, 15 seconds
|
||||
- WHEN `durationHoursMinutesSeconds` is resolved
|
||||
- THEN the rendered text is `1時間30分15秒` with no literal `?` present
|
||||
|
||||
#### Scenario: Each of the 6 locales matches its pinned string exactly
|
||||
- GIVEN any of `ar`, `bn`, `hi`, `ja`, `ru`, `zh`
|
||||
- WHEN any of the 4 duration keys is read from its ARB file after the fix
|
||||
- THEN its value is byte-identical to the corresponding cell in the pinned table above
|
||||
|
||||
### Requirement: ICU placeholder integrity
|
||||
The system MUST preserve the ICU placeholder names `{hours}`, `{minutes}`, `{seconds}` unchanged in name, casing, and count across all 24 target strings, and MUST NOT alter the associated `@durationXxx.placeholders` metadata blocks.
|
||||
|
||||
#### Scenario: Placeholder names unchanged after fix
|
||||
- GIVEN the pre-fix and post-fix versions of `durationMinutesSeconds` in `app_ru.arb`
|
||||
- WHEN the placeholder tokens are extracted from each
|
||||
- THEN both extractions yield exactly `{minutes}` and `{seconds}`, in that order
|
||||
|
||||
### Requirement: File integrity preserved
|
||||
The system MUST keep each of the 6 ARB files valid UTF-8 encoded JSON after the fix, and MUST leave every key other than the 4 duration keys per file byte-for-byte unchanged (all other ~386 keys, including `hoursLabel`/`minutesLabel`/`secondsLabel`).
|
||||
|
||||
#### Scenario: File remains valid JSON and UTF-8
|
||||
- GIVEN `app_ar.arb` after the fix is applied
|
||||
- WHEN the file is parsed as JSON and decoded as UTF-8
|
||||
- THEN parsing succeeds with no encoding errors
|
||||
|
||||
#### Scenario: Unrelated keys are untouched
|
||||
- GIVEN `app_zh.arb` before and after the fix
|
||||
- WHEN every key except the 4 duration keys is diffed
|
||||
- THEN the diff is empty
|
||||
|
||||
### Requirement: No residual corruption markers
|
||||
The system MUST NOT contain any literal `?` character within the value of the 24 target strings after the fix.
|
||||
|
||||
#### Scenario: No stray `?` remains
|
||||
- GIVEN all 6 fixed ARB files
|
||||
- WHEN the 24 target string values are scanned for the character `?`
|
||||
- THEN zero occurrences are found
|
||||
@@ -0,0 +1,46 @@
|
||||
# Tasks: Repair corrupted sleep-timer duration abbreviations in 6 ARB locales
|
||||
|
||||
## Review Workload Forecast
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Estimated changed lines | 24-48 (24 lines modified, 24 add + 24 del per `git diff --stat`) |
|
||||
| 400-line budget risk | Low |
|
||||
| Chained PRs recommended | No |
|
||||
| Suggested split | Single PR |
|
||||
| Delivery strategy | single-pr |
|
||||
| Chain strategy | pending |
|
||||
|
||||
Decision needed before apply: No
|
||||
Chained PRs recommended: No
|
||||
Chain strategy: pending
|
||||
400-line budget risk: Low
|
||||
|
||||
### Suggested Work Units
|
||||
|
||||
| Unit | Goal | Likely PR | Focused test command | Runtime harness | Rollback boundary |
|
||||
|------|------|-----------|----------------------|-----------------|-------------------|
|
||||
| 1 | Fix all 24 pinned strings across 6 ARB files + static validation | PR 1 (single) | `python scripts/validate_arb_fix.py` (JSON/UTF-8/diff-scope/placeholder/`?`-scan checks) | N/A — static content-only change, no runtime code path affected | `git revert` on the single commit restores all 6 files atomically |
|
||||
|
||||
## Phase 1: Content Repair (per-locale edits)
|
||||
|
||||
- [x] 1.1 Edit `lib/l10n/app_ar.arb` lines 19/27/34/40: replace `durationHoursMinutesSeconds`/`durationMinutesSeconds`/`durationMinutesOnly`/`durationSecondsOnly` values with pinned `ar` cells (spec Requirement: Native short-form duration abbreviations)
|
||||
- [x] 1.2 Edit `lib/l10n/app_bn.arb` lines 19/27/34/40: replace same 4 keys with pinned `bn` cells
|
||||
- [x] 1.3 Edit `lib/l10n/app_hi.arb` lines 19/27/34/40: replace same 4 keys with pinned `hi` cells
|
||||
- [x] 1.4 Edit `lib/l10n/app_ja.arb` lines 19/27/34/40: replace same 4 keys with pinned `ja` cells (no-space compact notation)
|
||||
- [x] 1.5 Edit `lib/l10n/app_ru.arb` lines 19/27/34/40: replace same 4 keys with pinned `ru` cells (leading-space short abbreviations)
|
||||
- [x] 1.6 Edit `lib/l10n/app_zh.arb` lines 19/27/34/40: replace same 4 keys with pinned `zh` cells (no-space compact notation)
|
||||
|
||||
Tasks 1.1-1.6 are file-independent and may run in parallel; each is a self-contained exact-string replacement per the design's pinned table, touching no other key.
|
||||
|
||||
## Phase 2: Static Validation (sequential, depends on Phase 1)
|
||||
|
||||
- [x] 2.1 Parse all 6 `app_{ar,bn,hi,ja,ru,zh}.arb` files as JSON (`json.load`); assert success (spec: File integrity preserved)
|
||||
- [x] 2.2 Decode each file's bytes as strict UTF-8; assert no `�` replacement characters present
|
||||
- [x] 2.3 Run `git diff --stat` and per-line diff; assert exactly 24 changed lines total across the 6 files, additions == deletions, and no key outside the 4 duration keys per file differs (spec: Unrelated keys are untouched)
|
||||
- [x] 2.4 Scan the 24 target string values for literal `?`; assert zero occurrences (spec: No residual corruption markers)
|
||||
- [x] 2.5 Regex-extract `{...}` placeholder tokens from each of the 24 fixed values; assert each matches expected `{hours}`/`{minutes}`/`{seconds}` set/order and that `@durationXxx.placeholders` metadata blocks are byte-unchanged (spec: ICU placeholder integrity)
|
||||
- [x] 2.6 Assert each of the 24 fixed values is byte-identical to its pinned spec-table cell (spec: Each of the 6 locales matches its pinned string exactly)
|
||||
- [x] 2.7 Run `git diff --check` to confirm no whitespace/EOL damage
|
||||
|
||||
Do NOT run `flutter build`, `flutter gen-l10n`, `dart analyze`, or `flutter analyze` — these hang in this environment.
|
||||
@@ -0,0 +1,36 @@
|
||||
# Verify Report: i18n-locale-audit
|
||||
|
||||
Independently re-derived (not trusting apply-progress.md self-report). All checks re-run against current working-tree file content and `git diff`.
|
||||
|
||||
## Per-requirement verdicts
|
||||
|
||||
### Requirement: Native short-form duration abbreviations
|
||||
**PASS.** Read all 4 duration keys (lines 19/27/34/40) in all 6 files (`app_ar.arb`, `app_bn.arb`, `app_hi.arb`, `app_ja.arb`, `app_ru.arb`, `app_zh.arb`) and compared byte-for-byte against the pinned spec table. All 24 values match exactly:
|
||||
- ja: `{hours}時間{minutes}分{seconds}秒` / `{minutes}分{seconds}秒` / `{minutes}分` / `{seconds}秒` — match
|
||||
- zh: `{hours}小时{minutes}分钟{seconds}秒` / `{minutes}分钟{seconds}秒` / `{minutes}分钟` / `{seconds}秒` — match
|
||||
- ru: `{hours} ч {minutes} мин {seconds} сек` / `{minutes} мин {seconds} сек` / `{minutes} мин` / `{seconds} сек` — match
|
||||
- ar: `{hours} س {minutes} د {seconds} ث` / `{minutes} د {seconds} ث` / `{minutes} د` / `{seconds} ث` — match
|
||||
- hi: `{hours} घंटे {minutes} मिनट {seconds} सेकंड` / `{minutes} मिनट {seconds} सेकंड` / `{minutes} मिनट` / `{seconds} सेकंड` — match
|
||||
- bn: `{hours} ঘণ্টা {minutes} মিনিট {seconds} সেকেন্ড` / `{minutes} মিনিট {seconds} সেকেন্ড` / `{minutes} মিনিট` / `{seconds} সেকেন্ড` — match
|
||||
|
||||
### Requirement: ICU placeholder integrity
|
||||
**PASS.** `{hours}`, `{minutes}`, `{seconds}` present, correctly named/cased, correct count in every applicable string. `git diff` confirms all `@durationXxx.placeholders` metadata blocks are unchanged (context lines only, no `+`/`-` in those blocks in any of the 6 diffs).
|
||||
|
||||
### Requirement: File integrity preserved
|
||||
**PASS.**
|
||||
- All 6 files parse successfully via `json.loads` after strict UTF-8 decode (independently re-run with Python, not trusting the prior report).
|
||||
- `git diff --stat` shows exactly `24 insertions(+), 24 deletions(-)` across the 6 files (8 changed lines per file = 4 keys × 1 line each, old+new).
|
||||
- Full `git diff` inspected line-by-line: only the 4 duration-key value lines changed per file; all `@durationXxx.placeholders` blocks and all surrounding keys (`hoursLabel`, `minutesLabel`, `secondsLabel`, etc.) are untouched context lines.
|
||||
|
||||
### Requirement: No residual corruption markers
|
||||
**PASS.** Independently scanned all 24 target values (via the `git diff` `+` lines and a direct Python scan of the parsed JSON values) — zero literal `?` characters remain in any of the 24 target strings. (The pre-fix `?`/`??`/`???` corruption is visible only on the `-` diff lines, confirming the fix replaced it.)
|
||||
|
||||
## Scope check
|
||||
`git status --porcelain` shows only the 6 target ARB files modified (`M lib/l10n/app_ar.arb`, `app_bn.arb`, `app_hi.arb`, `app_ja.arb`, `app_ru.arb`, `app_zh.arb`). The only other entry is `?? openspec/changes/i18n-locale-audit/`, which is the untracked SDD change-artifact directory itself (spec/design/tasks/proposal/apply-progress/this report) — not a code change. No other files in the working tree were touched.
|
||||
|
||||
## Tooling note
|
||||
Per instructions, did not run `flutter build`, `flutter gen-l10n`, `dart analyze`, or `flutter analyze`. Verification used direct file reads, `git diff`/`git status`, and a standalone Python JSON/UTF-8/`?`-scan check.
|
||||
|
||||
## Overall verdict: PASS
|
||||
|
||||
No CRITICAL, WARNING, or SUGGESTION findings. Implementation matches spec exactly; tasks.md items are all legitimately complete; apply-progress.md's self-report is accurate.
|
||||
@@ -0,0 +1,60 @@
|
||||
# Sleep Timer Duration I18n Specification
|
||||
|
||||
## Purpose
|
||||
|
||||
Defines the correct native-language content for the 4 ICU-templated sleep-timer duration strings (`durationHoursMinutesSeconds`, `durationMinutesSeconds`, `durationMinutesOnly`, `durationSecondsOnly`) across the 6 currently corrupted ARB locales: `ar`, `bn`, `hi`, `ja`, `ru`, `zh`. This is a new spec domain — no prior spec covered these strings.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: Native short-form duration abbreviations
|
||||
The system MUST render, for each of the 6 locales, the exact pinned native hour/minute/second abbreviation text below in place of every literal `?`/`??`/`???` corruption, with no other content in the 24 target strings altered.
|
||||
|
||||
| Locale | `durationHoursMinutesSeconds` | `durationMinutesSeconds` | `durationMinutesOnly` | `durationSecondsOnly` |
|
||||
|---|---|---|---|---|
|
||||
| ja | `{hours}時間{minutes}分{seconds}秒` | `{minutes}分{seconds}秒` | `{minutes}分` | `{seconds}秒` |
|
||||
| zh | `{hours}小时{minutes}分钟{seconds}秒` | `{minutes}分钟{seconds}秒` | `{minutes}分钟` | `{seconds}秒` |
|
||||
| ru | `{hours} ч {minutes} мин {seconds} сек` | `{minutes} мин {seconds} сек` | `{minutes} мин` | `{seconds} сек` |
|
||||
| ar | `{hours} س {minutes} د {seconds} ث` | `{minutes} د {seconds} ث` | `{minutes} د` | `{seconds} ث` |
|
||||
| hi | `{hours} घंटे {minutes} मिनट {seconds} सेकंड` | `{minutes} मिनट {seconds} सेकंड` | `{minutes} मिनट` | `{seconds} सेकंड` |
|
||||
| bn | `{hours} ঘণ্টা {minutes} মিনিট {seconds} সেকেন্ড` | `{minutes} মিনিট {seconds} সেকেন্ড` | `{minutes} মিনিট` | `{seconds} সেকেন্ড` |
|
||||
|
||||
Rationale: ja/zh use conventional no-space compact duration notation (e.g. `1時間30分15秒`, `2小时30分钟15秒`); ru/ar use the OS-standard single/short abbreviation with a leading space per unit (`ч`/`мин`/`сек`, `س`/`د`/`ث`); hi/bn lack a reliable standard short form, so the full native label words (already verified at `hoursLabel`/`minutesLabel`/`secondsLabel`) are reused verbatim, space-separated.
|
||||
|
||||
#### Scenario: Full duration display renders native units
|
||||
- GIVEN a user on locale `ja` opens the sleep-timer bottom sheet with a duration of 1 hour, 30 minutes, 15 seconds
|
||||
- WHEN `durationHoursMinutesSeconds` is resolved
|
||||
- THEN the rendered text is `1時間30分15秒` with no literal `?` present
|
||||
|
||||
#### Scenario: Each of the 6 locales matches its pinned string exactly
|
||||
- GIVEN any of `ar`, `bn`, `hi`, `ja`, `ru`, `zh`
|
||||
- WHEN any of the 4 duration keys is read from its ARB file after the fix
|
||||
- THEN its value is byte-identical to the corresponding cell in the pinned table above
|
||||
|
||||
### Requirement: ICU placeholder integrity
|
||||
The system MUST preserve the ICU placeholder names `{hours}`, `{minutes}`, `{seconds}` unchanged in name, casing, and count across all 24 target strings, and MUST NOT alter the associated `@durationXxx.placeholders` metadata blocks.
|
||||
|
||||
#### Scenario: Placeholder names unchanged after fix
|
||||
- GIVEN the pre-fix and post-fix versions of `durationMinutesSeconds` in `app_ru.arb`
|
||||
- WHEN the placeholder tokens are extracted from each
|
||||
- THEN both extractions yield exactly `{minutes}` and `{seconds}`, in that order
|
||||
|
||||
### Requirement: File integrity preserved
|
||||
The system MUST keep each of the 6 ARB files valid UTF-8 encoded JSON after the fix, and MUST leave every key other than the 4 duration keys per file byte-for-byte unchanged (all other ~386 keys, including `hoursLabel`/`minutesLabel`/`secondsLabel`).
|
||||
|
||||
#### Scenario: File remains valid JSON and UTF-8
|
||||
- GIVEN `app_ar.arb` after the fix is applied
|
||||
- WHEN the file is parsed as JSON and decoded as UTF-8
|
||||
- THEN parsing succeeds with no encoding errors
|
||||
|
||||
#### Scenario: Unrelated keys are untouched
|
||||
- GIVEN `app_zh.arb` before and after the fix
|
||||
- WHEN every key except the 4 duration keys is diffed
|
||||
- THEN the diff is empty
|
||||
|
||||
### Requirement: No residual corruption markers
|
||||
The system MUST NOT contain any literal `?` character within the value of the 24 target strings after the fix.
|
||||
|
||||
#### Scenario: No stray `?` remains
|
||||
- GIVEN all 6 fixed ARB files
|
||||
- WHEN the 24 target string values are scanned for the character `?`
|
||||
- THEN zero occurrences are found
|
||||
Reference in New Issue
Block a user