Files
pluriwave/openspec/changes/archive/2026-07-21-i18n-locale-audit/explore.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

66 lines
9.8 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.
# 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.