Repository navigation
feat(i18n): ids estables de medidores entre Rust y JS (L2 #66) - #159
Conversation
…v6#66) Meter labels are Spanish text that JS logic matched on: short ring labels, alert dedupe and reset diffing, ring colors, history lookup and React keys. Translating them would silently break all of that. Add `Meter.id` (session, weekly, credits, model:<id>, ...) at every provider construction site and switch that logic to the id. Labels stay as today, so nothing visible changes. History samples now store the id and still match legacy label samples until they age out; stored alert memory keyed by label is honored so no alert repeats after upgrading.
|
Gracias @danielgap, review hecho — muy sólida, nada bloqueante y lista para merge. Solo dejo estas observaciones menores como seguimiento:
Ninguna de las cinco bloquea: recomiendo fusionar y llevar los puntos 1–2 al PR de catálogos. |
|
@danielgap el check de Windows fallaba porque tras el merge de Resuelto en |
…s nativos (L2/L3 #66) (#163) * feat(i18n): switch tray, window titles and Codex dialog by locale (#66) Add src-tauri/src/i18n.rs: a Locale parsed from the config (unknown -> es) and a Msg enum with an exhaustive es/en match. Spanish texts are the existing literals verbatim. The tray menu and the settings, detail and overlay window titles are rebuilt when a saved config changes locale, and use the configured locale at startup; the Codex consent dialog reads it when shown. Maintainer follow-ups from #159: Meter.id is documented as required and history asserts it in debug builds (legacy label fallback kept in a pure sample_key); the meters built inside anthropic_api, openai_api and copilot fetches are now pure, unit-tested constructors. * feat(i18n): typed es/en catalogs and language selector in Settings (#66) Add src/i18n: `es` built from the existing Spanish literals, `en` checked with `satisfies` so a missing or extra key fails to compile, `t()` with typed keys, {placeholder} interpolation and a locale -> es -> key fallback, a closed Intl locale map, and runtime tests for key presence and placeholder parity. Settings gets a "Idioma" selector in Apariencia that saves through the existing queue and broadcasts config-changed; the draft adopts the new locale so "Guardar cambios" cannot revert it. Settings and DetailPanel follow the locale with useLocale(); the Settings navigation and page titles are the first texts migrated. * fix(i18n): fictional channel fixture and draft reconcile on failed locale save (#66) - Use the fictional Quorb Beta channel/folder in the Rust catalog test instead of the retired Dev identity, so the dev-retirement guard passes without being relaxed (P1, review of #163). - changeLocale reverts the Settings draft to the persisted backend locale when the immediate save fails and rethrows; Settings shows the error next to the language selector instead of console-only logging (P2, review of #163). - Cover the reconciliation and the double-failure rethrow with unit tests and add settings.language.error to the es/en catalogs. * docs(i18n): regenerate inventory after merge with main (#66) --------- Co-authored-by: Quique <egdev6@gmail.com> Co-authored-by: Enrique Gómez Navarro <egdev6@users.noreply.github.com>
Related issue / Issue relacionada
Refs #66
ODD: fase L2, primer paso: ids estables Rust→JS, prerrequisito marcado en la aprobación de L0 (#66 (comment)). L1 entró en #152.
Scope / Alcance
Los meters llevan ahora un
idestable además de sulabel, y la lógica de JS deja de comparar textos en español. No cambia nada visible: mismas etiquetas, etiquetas cortas, colores y alertas.Rust:
Meter.id(model.rs) se fija en los 22 constructores deproviders/*.rs:session,weekly,weekly_opus,weekly_sonnet,weekly_apps,extra_usagesession,weekly,window_days,window_short,window_long,creditsmodel:<id>premium_requestsplan_total,plan_auto,plan_apikey_limit_daily,key_limit_weekly,key_limit_monthly,key_limit,balance_spentbudget_monthlyrate_requests,rate_tokens,customspend_period,tokens,tokens_periodEn Codex, si las dos ventanas coinciden, la segunda recibe un sufijo determinista (
session_18000).JS, cada sitio de lógica que comparaba texto:
format.tsshortLabelid. Si el id está vacío o es desconocido, vuelve a la etiqueta.alerts.ts(clave de memoria y diff entre reportes)id. Un meter con otra etiqueta pero el mismo id ya no produce alertas fantasma ni perdidas.meter-colors.tsidcuando faltawindowSeconds. La regex sobresemana/sesiónqueda solo para ids vacíos.ProviderDetails.tsx(historial) ykey=enProviderCard/ProviderDetailsid, con la etiqueta como respaldo.Datos persistidos, sin pérdida:
history.json: las muestras nuevas guardan elid. Las antiguas, con etiqueta, siguen encontrándose (id || label) hasta que caducan a los 30 días.alertMemory(localStorage): se respeta la clave antigua basada en la etiqueta, así que ninguna alerta de "superado" se repite al actualizar.Fuera de alcance:
message,plan,periodLabel,unity las notas deget_wm_info. Solo se muestran, ningún código los compara; irán en L4.docs/i18n-inventory.mdse regenera porque cambian líneas ensrc/. Si #158 (CI de #155) entra antes, actualizo esta rama y lo regenero.Coordination / Coordinación
Contribuidor: @danielgap
Acceptance and validation / Aceptación y validación
Meter.id.cargo test: 238/238.npx vitest run --dir src: 620/620.npx tsc --noEmit: OK.node scripts/i18n-inventory.mjs --check: exit 0.fetchasíncrono. Los ids están fijados y compilan. No hay prueba manual: no hay cambio visible.