feat(i18n): add redesign strings and translate Escuchar rename to 11 locales

This commit is contained in:
2026-07-29 16:35:30 +02:00
parent 2959941485
commit 3ec41bb31e
27 changed files with 2715 additions and 909 deletions
+87 -52
View File
@@ -62,7 +62,7 @@
| 15b | `fix(grabaciones): wire the recordings library into Settings navigation` | 15 | 60-100 | Low | No |
| 16 | `feat(connectivity): restyle offline and reconnect banners` | 1 | 150-250 | Low | No |
| 17 | `feat(bienvenida): add monetization-free welcome screen` | 1 | ~~150-200~~**REALIZED: 361** (screen + test, 313 / ARB 22 / docs 48) — see the WU15-precedent note below | Low | No — same reasoning as WU15§ |
| 18 | `feat(i18n): add redesign strings and translate Escuchar rename to 11 locales` | all | ~0 eng. / 400-600 data | Medium (data volume, low logic risk) | No |
| 18 | `feat(i18n): add redesign strings and translate Escuchar rename to 11 locales` | all | ~~~0 eng. / 400-600 data~~ → **REALIZED: ~3,622** (2,714+ / 908-, 26 files: 11 ARB + 11 regenerated `gen/*.dart` + 4 new `test/l10n/*.dart` + this docs update) | Medium (data volume, low logic risk) | No — same estimating-lesson pattern as WU3a/WU7/WU15§, not a design risk |
\* **Re-derived after WU3a landed.** The original 500-700 figure was scaled pro-rata from WU3a's *estimate*, which
turned out to be wrong by roughly 4x — so the old WU3b number inherited the same error and has been replaced.
@@ -129,15 +129,19 @@ the proposal's narrative and dependency table.
## Non-Negotiable Guards (master checklist — verify before the branch is considered done)
- [ ] `test/pantallas/pantalla_alarma_sonando_dismiss_guard_test.dart` passes **unmodified** (touched by WU9, WU11 — if
a restyle appears to require editing it, STOP and escalate; do not edit the test).
- [ ] `git diff` is **empty** for `lib/servicios/navegacion_auto.dart` (WU2), `lib/servicios/servicio_ecualizador.dart`
(WU13), and the band-application block at `lib/servicios/servicio_audio.dart:749-762` (WU13).
- [ ] The equalizer renders exactly **5** sliders (WU13, task 13.1).
- [ ] No shipped string contains "PRO", a trial duration, or a price (WU17 task 17.2/17.6; WU18 task 18.6 final
whole-app grep).
- [ ] All 13 ARB files keep identical key sets (WU18 tasks 18.1, 18.5 — final parity check; WU1 only touches values
in 2 of the 13 files, so it cannot break parity on its own).
- [x] `test/pantallas/pantalla_alarma_sonando_dismiss_guard_test.dart` passes **unmodified** (touched by WU9, WU11 — if
a restyle appears to require editing it, STOP and escalate; do not edit the test). Re-confirmed in isolation
after WU18: 13/13 green, file diff empty vs `main`.
- [x] `git diff` is **empty** for `lib/servicios/navegacion_auto.dart` (WU2), `lib/servicios/servicio_ecualizador.dart`
(WU13), and the band-application block at `lib/servicios/servicio_audio.dart:749-762` (WU13). Re-confirmed
after WU18 (plus `lib/modelos/preset_ecualizador.dart` and the 3 other guarded test files — all 8 empty).
- [x] The equalizer renders exactly **5** sliders (WU13, task 13.1). Re-confirmed after WU18.
- [x] No shipped string contains "PRO", a trial duration, or a price (WU17 task 17.2/17.6; WU18 task 18.6 final
whole-app grep). Final ARB-only grep (18.6): zero hits for `\bPRO\b`, €, $, gratis/gratuit/"free version", or
a day-count trial phrase in any of the 13 `app_*.arb` files.
- [x] All 13 ARB files keep identical key sets (WU18 tasks 18.1, 18.5 — final parity check; WU1 only touches values
in 2 of the 13 files, so it cannot break parity on its own). Confirmed: 466 real keys in every one of the 13
files, zero missing, zero extra.
---
@@ -1127,53 +1131,84 @@ UI copy every other capability introduced
**Verify**: `flutter test && flutter analyze && dart format --set-exit-if-changed $(git diff --name-only --diff-filter=ACM HEAD -- '*.dart')`
**Modified tests**: ARB key-parity guard test (extend existing or add one if none exists)
- [ ] 18.1 RED — an ARB-parity test asserting all 13 `lib/l10n/app_*.arb` files contain an identical key set.
- [ ] 18.1b RED — **anti-copy guard (a key-parity test is NOT enough).** `l10n.yaml` sets
`template-arb-file: app_es.arb`, so any key missing from a locale silently falls back to **Spanish** — that is
why untranslated strings currently render as Spanish inside `app_localizations_ja.dart`, `_fr.dart`, etc.
A key-parity test would pass just as happily if all 11 locales simply COPIED the Spanish value.
Write a test that, for every non-`es` locale, collects every key whose value is byte-identical to the `app_es.arb`
value and requires each one to appear in an explicit allowlist (a checked-in file, e.g.
`test/l10n/identical_value_allowlist.dart`). Legitimately-identical values exist — brand names, "OK", "Radio",
codec names, pure symbols — so the allowlist is the escape hatch, but **each entry must be added deliberately,
never in bulk**. Any unlisted identical value fails the test.
Seed the allowlist ONLY with entries that are genuinely untranslatable; do not pre-populate it to make the
suite green.
- [ ] 18.2 RED — `navHome` exists as a key in all 13 locales, and `git diff` for this commit touches exactly 11
`navHome` **value** lines (en/es were WU1's job, task 1.9).
- [ ] 18.3 GREEN — add the ~35-45 new keys (accumulated from WU4-WU17's English/Spanish-only strings) to all 13
ARB files, sourcing translations for the 11 non-en/es locales.
- [ ] 18.4 GREEN — set the `navHome` value in the remaining 11 locales to a real translation of "Listen" (not a
mechanical copy of the English/Spanish pair).
- [ ] 18.5 REFACTOR — run the ARB-parity check across all 13 files; confirm zero missing/extra keys, AND that the
18.1b anti-copy guard passes with an allowlist containing only genuinely-untranslatable entries.
Sanity figures at the time this was written: `app_es.arb` (template) 540 keys, `app_en.arb` 561, and all 11
other locales 518 — i.e. the non-template locales were **22 keys behind**, and those 22 were rendering in
Spanish. WU18 is what closes that gap; the count will have grown as WU8-WU17 land, so re-measure rather than
trusting these numbers.
- [ ] 18.6 Verify — full `flutter test` suite green; `flutter analyze` clean; scoped `dart format` over the files
this change touched produces no diff (NEVER bare `dart format .` — see the warning at the top);
grep confirms no "PRO"/price/trial-duration string exists in any ARB file (final whole-app Success Criteria
check).
- [x] 18.1 RED — `test/l10n/arb_parity_test.dart` (+ `test/l10n/arb_test_helpers.dart` shared reader): an ARB-parity
test asserting all 13 `lib/l10n/app_*.arb` files contain an identical key set, plus a `navHome`-exists check
(task 18.2's first half). Confirmed RED against the pre-merge tree: 11 locales missing 72 keys each.
- [x] 18.1b RED — `test/l10n/arb_anti_copy_test.dart` + `test/l10n/identical_value_allowlist.dart` (the anti-copy
guard). **Scope correction found at apply time**: read literally ("for every non-`es` locale, collects every
key whose value is byte-identical") this must cover the WHOLE file, not only WU18's own new keys — a
narrower scope would leave 183 pre-existing identical values (mostly genuine `pt`/`es` Iberian-Romance
cognates, plus international genre names, "OK", brand names, symbols) completely unaudited. Built the
allowlist to cover both: 207 total entries (after de-duplicating two detection-script bugs found along the
way — see 18.5's note), each with an individual justification comment, grouped by category for readability
but never wildcarded.
**`en` is deliberately excluded** from this guard's locale loop (see the test file's own doc comment): `en`
and `es` have been hand-maintained together by every WU throughout this whole change (`en`/`es`-only new
keys, never left lagging), so `en` never carries the "silently fell back to the template" risk this guard
exists to catch — unlike the other 11, which this WU batch-translates for the first time.
- [x] 18.2 RED — `navHome` exists as a key in all 13 locales (part of 18.1's parity test); `git diff` for this
commit touches exactly **11** `navHome` **value** lines, confirmed via `git diff | grep -c '"navHome"'` = 22
(11 removed + 11 added) after the merge (en/es were WU1's job, task 1.9, untouched here).
- [x] 18.3 GREEN — added exactly **72** new keys (measured, not the ~35-45 estimate) to all 13 ARB files, with real,
idiomatic translations for the 11 non-en/es locales (ar, bn, de, fr, hi, id, it, ja, pt, ru, zh) — vocabulary
cross-checked against ~20 already-translated sibling keys in each locale (e.g. "station"/"favorites"/
"preset"/"OK"/genre-name conventions, register — `du`-informal `de`, `vous`-formal `fr`, `tu`-informal `it`,
polite-imperative `ru`/`ja`/`hi`/`bn`, `você`-imperative `pt` with no explicit pronoun) so new copy reads as
part of the same voice as existing copy, not machine-translated in isolation. 7 of the 72 keys carry ICU
placeholders/plurals; their `@key` metadata blocks were added to all 11 locales matching the template's own
established convention of replicating metadata into every locale file (confirmed via a diff scan: the
53 pre-existing metadata blocks shared between `es` and a sample locale were byte-identical). Arabic and
Russian plurals use full CLDR categories (`one`/`two`/`few`/`other` and `one`/`few`/`other`) matching the
precedent already set by the pre-existing `stationCount` key, rather than the simpler `=1`/`other` the ES/EN
template itself uses for these two new keys.
- [x] 18.4 GREEN — set `navHome` in the remaining 11 locales to a real translation of "Listen"/"Escuchar" (ar
استماع, bn শোনা, de Hören, fr Écouter, hi सुनना, id Dengar, it Ascolta, ja 聴く, pt Ouvir, ru Слушать,
zh 收听) — each cross-checked against that locale's OWN sibling nav-tab labels (`navSearch`/`navFavorites`/
etc.) for register/part-of-speech consistency, not a mechanical copy of "Listen" or "Escuchar".
- [x] 18.5 REFACTOR — re-ran the parity + anti-copy guard; **two detection-script bugs found and fixed along the
way, not left in the final allowlist**: (1) the first draft's collision scanner used PowerShell's default
`-eq`, which is **case-insensitive** — it falsely flagged 5 pairs (e.g. `de/durationMinutesOnly` "min" vs
"Min") as collisions; re-scanned with `-ceq` (case-sensitive, matching Dart's own `==`) and removed the 5
false positives. (2) the first allowlist draft was missing 7 genuine collisions the script had already found
but a manual transcription pass dropped (`settingsGroupAudioTitle` ×4, `endField`/`endLabel` fr, `alarmFadeInLabel`
pt) — added them back. Final state: zero missing/extra keys across all 13 files; anti-copy guard's own
"no stale entries" self-check (added as this test's second assertion, not just the one the task named) also
passes, so the allowlist is verified as exactly the current, real collision set — 207 entries, none stale,
none missing.
- [x] 18.6 Verify — full `flutter test`: **747/747 green, 2 skipped** (up from the 737-baseline this batch started
from — +6 WU17b tests + +4 WU18 tests, reconciles exactly). `flutter analyze`: 1 issue, identical to
baseline. Scoped `dart format` (11 regenerated `lib/l10n/gen/*.dart` + 4 new `test/l10n/*.dart` files, ARB
JSON files are not `dart format` targets): reformatted 3 of 15 files (whitespace/line-wrapping only), stable
on re-run. Grep confirms zero `\bPRO\b` (case-sensitive), €, $, "gratis"/"gratuit"/"free version", or
day-count trial-phrase hits in any `app_*.arb` file. Literal-encoding scan (Arabic, Bengali, Devanagari,
Japanese, Chinese, Cyrillic all touched): zero mojibake hits across all 11 modified ARB files, the 11
regenerated `gen/*.dart` files, and the 4 new test files; spot-checked several locales' rendered text
directly to confirm.
---
## Final Verification Sequence (run once, after WU18's commit)
1. `flutter test` — full suite green.
2. `flutter analyze` — clean.
3. `dart format --set-exit-if-changed $(git diff --name-only --diff-filter=ACM main -- '*.dart')` — no diff over the
files this change touched. **NEVER bare `dart format .`** — it reformats 27 unrelated pre-existing files and would
break the empty-diff guards in steps 4 and 5 below (Engram `reference/dart-format-scope-hazard`, id 2511).
4. `git diff --stat lib/servicios/navegacion_auto.dart lib/servicios/servicio_ecualizador.dart` — both empty.
5. `git diff lib/servicios/servicio_audio.dart` — band-application block (`749-762`) unchanged.
6. Grep `lib/` for `countrycodes` — zero hits.
7. Grep `lib/l10n/app_*.arb` for "PRO", a currency amount, or a day-count trial phrase — zero hits.
8. Confirm all 13 ARB files have identical key sets and `navHome` present in each.
9. Confirm the equalizer screen renders exactly 5 sliders.
10. Run `test/pantallas/pantalla_alarma_sonando_dismiss_guard_test.dart` in isolation — passes, file diff empty.
All 10 steps run and confirmed after the WU18 commit:
1. [x] `flutter test` — full suite green: **747/747, 2 skipped**.
2. [x] `flutter analyze` — clean: 1 issue, identical to the pre-branch baseline.
3. [x] `dart format --set-exit-if-changed $(git diff --name-only --diff-filter=ACM main -- '*.dart')` — no diff over
the files this change touched. **NEVER bare `dart format .`** — it reformats 27 unrelated pre-existing files and
would break the empty-diff guards in steps 4 and 5 below (Engram `reference/dart-format-scope-hazard`, id 2511).
4. [x] `git diff --stat lib/servicios/navegacion_auto.dart lib/servicios/servicio_ecualizador.dart` — both empty.
5. [x] `git diff lib/servicios/servicio_audio.dart` — band-application block (`749-762`) unchanged.
6. [x] Grep `lib/` for `countrycodes` — zero hits.
7. [x] Grep `lib/l10n/app_*.arb` for "PRO", a currency amount, or a day-count trial phrase — zero hits.
8. [x] Confirm all 13 ARB files have identical key sets and `navHome` present in each — 466 real keys each, confirmed.
9. [x] Confirm the equalizer screen renders exactly 5 sliders — confirmed.
10. [x] Run `test/pantallas/pantalla_alarma_sonando_dismiss_guard_test.dart` in isolation — 13/13 passes, file diff
empty vs `main`.
**All 18 work units (+ WU15b, WU17b, and the pre-existing controller-dispose bugfix) are now complete.** Branch
`feat/rediseno-funcional` is ready for `sdd-verify`.
## Next Step
`sdd-apply`implement WU1 first (unblocks everything else); honor the `size:exception` commits (WU3a, WU3b, WU10)
as recorded above rather than attempting to split them further.
`sdd-verify`validate the completed implementation against `specs/*/spec.md`; `sdd-apply`'s work on this change is
done.