### Fase 20 — Euskera locale (eu) **v1.0 · 4/4.** Última fase del ciclo v1.0. Tras cerrarla, se taggea `v1.0.0`. **Estado: 📋 Planificada.** Añadir Euskera (Basque, ISO 639-1 `eu`) como tercer idioma soportado, sumándose a `en` y `es`. Tras esta fase la app está disponible para los hogares vasco-parlantes. **Estado actual (auditado 2026-05-19):** - Paraglide en `apps/web/messages/{en,es}.json`. Compile-time tree-shaken por idioma. - Selector de idioma actual: `users.language text` (probablemente `en` | `es` por check constraint o convención). Auto-detect ya en Fase 10.8. - ~300+ keys en cada locale (estimado por el tamaño actual). **Decisiones clave:** - **Machine-translate seed + flag for review (opción B):** generar `messages/eu.json` con traducción automática (DeepL-formatted-text o GPT-class) marcando cada valor con sufijo `[needs review]` para que la persona euskera-hablante pueda repasar y borrar el marcador. - Soporte de idiomas declarados en la lista canónica: `apps/web/src/lib/i18n/supported.ts` (verificar nombre exacto, puede que viva en `paraglide` config). Añadir `'eu'`. - `users.language` accept `'eu'` en el check constraint (si existe). Si no hay constraint, añadirlo ahora con 3 valores. - Auto-detect (Fase 10.8): reconocer `eu` / `eu-ES` / `eu-FR` en `accept-language`. - Selector visible: `/settings` ya tiene un control de idioma. Añadir opción "Euskera". --- #### 20.1 Migración: language check **Fichero:** `supabase/migrations/028_users_language_eu.sql`. ```sql -- Drop the existing check (if it exists; some seeds may not have it). alter table public.users drop constraint if exists users_language_check; -- Re-add with eu allowed. alter table public.users add constraint users_language_check check (language in ('en','es','eu')); ``` **Tareas:** - [ ] **20.1.1** Migración 028. - [ ] **20.1.2** Tests pgTAP `supabase/tests/020_users_language_eu.sql`: - update a `'eu'` accepted. - update a `'fr'` rejected by check. - [ ] **20.1.3** `just db-types`. --- #### 20.2 Locale file **Fichero:** nuevo `apps/web/messages/eu.json`. - [ ] **20.2.1** Pipeline de generación (manual, una vez): 1. Tomar `messages/es.json` como source-of-truth (lengua romance + base traductor más fiable hacia euskera que `en`). 2. Traducir cada valor con DeepL API o GPT con prompt "Translate to Euskera (Basque), keep ICU placeholders intact, preserve punctuation, formal register". 3. Marcar cada valor traducido con sufijo ` [needs review]` antes del cierre de la string. **Excepción**: las keys con placeholders ICU (`{name}`, `{count}`) llevan también el sufijo pero la review debe verificar que los placeholders no se hayan movido a posiciones gramaticalmente raras para euskera (SOV order vs SVO). - [ ] **20.2.2** Validar el JSON: misma key set que `en.json` y `es.json`. Script `scripts/check-locales.mjs` que diff las keys y falla si faltan o sobran. - [ ] **20.2.3** Bundle Paraglide rebuild: `pnpm --filter @colectivo/web exec paraglide-compile` (o cualquiera que sea el comando — verificar en `package.json`). --- #### 20.3 Wiring **Ficheros:** - `apps/web/src/lib/i18n/*` (configuración de Paraglide — locales soportados). - `apps/web/src/lib/utils/accept-language.ts` (Fase 10.8 — añadir `'eu'` al set conocido). - `apps/web/src/routes/(app)/settings/+page.svelte` (selector de idioma). **Tareas:** - [ ] **20.3.1** Añadir `'eu'` al array de idiomas en la config de Paraglide (`project.inlang/settings.json` u otro fichero del plugin). - [ ] **20.3.2** `accept-language.ts` parser: reconocer `eu`, `eu-ES`, `eu-FR` → `'eu'`. - [ ] **20.3.3** Selector de idioma en `/settings`: nueva opción "Euskera". Label en cada idioma: - `en`: "Basque" - `es`: "Euskera" - `eu`: "Euskera" - [ ] **20.3.4** Cambiar idioma a `eu` recarga Paraglide tags sin reload de página (`setLanguageTag('eu')`) + escribe `users.language = 'eu'`. --- #### 20.4 Tests - [ ] **20.4.1** Vitest unit `apps/web/src/lib/utils/accept-language.test.ts` (extender): - LANG-U-EU-01 `eu` → `'eu'`. - LANG-U-EU-02 `eu-ES` → `'eu'`. - LANG-U-EU-03 `eu-FR;q=0.5,en` → `'eu'` (preferencia primaria). - [ ] **20.4.2** Vitest integración `language-bootstrap.test.ts` (extender): - LANG-INT-EU-01 user setea language=eu, update OK. - LANG-INT-EU-02 user intenta language='fr' → check constraint violation. - [ ] **20.4.3** Playwright `tests/e2e/euskera.test.ts`: - **EU-01** Ana cambia idioma a Euskera en `/settings` → strings nav cambian (Listas → "Erosketak", o lo que diga el JSON), sin reload. - **EU-02** Eva (usuario nuevo) con header `Accept-Language: eu` → onboarding renderiza en Euskera. --- #### 20.5 Review checklist (post-translation) Antes del v1.0.0 release, abrir issue/asignar tarea para alguien euskera-hablante: - [ ] **20.5.1** Repasar `messages/eu.json`, eliminar el sufijo ` [needs review]` de cada string corregido. - [ ] **20.5.2** Para keys con plurales (`item_count_one`, `item_count_other` si existen), verificar que ambos son correctos (euskera no distingue como inglés/español; puede ser una sola forma). - [ ] **20.5.3** Términos del dominio: "colectivo", "lista", "tarea", "nota", "compra". Acordar la traducción con el cliente (no asumir que la máquina la acertó). --- #### 20.Z Verificación + cierre - [ ] **20.Z.1** `just test-all` verde. Suma esperada: +2 pgTAP + 3 unit + 2 integración + 2 e2e = +9. - [ ] **20.Z.2** Smoke manual: rotar las 3 pantallas top-level (`/lists`, `/tasks`, `/notes`) en cada idioma. Ninguna debe mostrar la key cruda (`__SKIPPED__` o el id). - [ ] **20.Z.3** Bundle-size check: el chunk de `eu` es lazy-loaded por Paraglide; verificar que solo se carga cuando el user lo elige. No debe pesar más que `es.json`. - [ ] **20.Z.4** Entrada en CHANGELOG: `[new] Euskera (Basque) locale (Fase 20). Machine-translated seed pending native review.` - [ ] **20.Z.5** Documentar en `docs/history/fase-20-euskera-locale.md`. --- #### 20.ZZ v1.0.0 release - [ ] **20.ZZ.1** Bump `package.json` version → `1.0.0`. - [ ] **20.ZZ.2** Editar `CHANGELOG.md`: mover entradas de `[Unreleased]` a `## [v1.0.0] — YYYY-MM-DD`. Incluir resumen de las 4 fases del ciclo (17–20). - [ ] **20.ZZ.3** `git tag -a v1.0.0 -m "v1.0.0 — versioning + shopping list flow + emoji avatars + euskera"`. - [ ] **20.ZZ.4** Deploy desde el tag. Smoke en prod. - [ ] **20.ZZ.5** Anuncio interno + nota sobre euskera review. --- ### Riesgos / notas - **Calidad de la traducción automática**: euskera no es un idioma soportado de primera por DeepL (verificar fecha actual). GPT-4 class lo hace pero con errores. El sufijo `[needs review]` es explícito para no shipear texto incorrecto sin marcar. - **ICU placeholders en euskera**: el orden de palabras (SOV) puede empujar `{name}` a posiciones no naturales en inglés/español. Revisar. - **Selector ya soporta `system` (auto-detect)**: por la Fase 10.8 ya hay lógica de fallback. Asegurar que `eu` propaga por todo el árbol. - **Test fixture users**: si los seeds (Ana, Borja, …) tienen `language='es'` hardcoded, ningún test rompe — solo los nuevos. ### Scope explícito fuera - Galego, Català, Português, Francés. Solo Euskera en este release. - Pluralización ICU avanzada (one/two/few/many/other) — euskera usa pluralización simple. - Traducción de los strings de Keycloak (login screen). Ese es el operador del Keycloak externo. - Localización del CHANGELOG. Solo en inglés. - Localización de los nombres de los emojis (Fase 19). Aria-label sigue siendo el codepoint name CLDR en inglés.