diff --git a/CHANGELOG.md b/CHANGELOG.md index 328683ef..b9c00b80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ - **PR #2165** by @starship-s — Pooled OpenAI Codex quota status surfaced in the Providers panel. Pre-fix, the Providers page presented Codex quota as if there were only one credential/account state, which was misleading when users authenticate through a credential pool with several usable credentials, temporarily exhausted credentials, failed probes, and different reset windows. Now the active provider quota card includes a credential-pool summary (available / exhausted / failed / checked counts), displays the best currently-available pool windows in the collapsed view as "Best of N", and exposes per-credential detail behind an expandable section. Exhausted credentials are intentionally NOT re-probed while their cooldown is active (matches credential-pool selection behavior, avoids generating failed quota calls from a status page). Manual refresh still means "probe now" but transient refresh failures preserve the last known-good snapshot. JWT decode (`_decode_jwt_claims_unverified`) is used only for token-shape classification (Codex OAuth JWT vs raw OpenAI API key), explicitly NOT for authorization — documented in the function docstring. Per-row plan labels only shown when verified account-limit data is available. Concurrent probing capped at `min(_CODEX_POOL_MAX_WORKERS=6, len(probe_items))` so page render time stays bounded on large pools. Transient `None` probe results are NOT cached (only known unavailable/exhausted states are cached); 32-test regression suite covering pool snapshot, concurrent probe, JWT detection, cache invalidation, transient-vs-known cache distinction, and i18n parity across all currently-supported locales. Scoped to OpenAI Codex (the only provider with the credential-pool/account-limit path needed to surface this accurately). +### Fixed + +- Settings i18n parity now covers every `settings_label_*` and `settings_desc_*` key referenced by `static/index.html`. Adds the missing Italian and French translations for the Fade text effect preference and cleans up existing settings-key parity drift in Portuguese and Traditional Chinese locale blocks. + ## [v0.51.64] — 2026-05-14 — Release AN (stage-357 — 3-PR small batch — docker_init k8s whoami fallback + PWA manifest session routes (closes #2226) + aux title test coverage) ### Fixed diff --git a/static/i18n.js b/static/i18n.js index db0ca5b0..7884a6f8 100644 --- a/static/i18n.js +++ b/static/i18n.js @@ -1427,6 +1427,8 @@ const LOCALES = { busy_interrupt_confirm: 'Interrotto — invio nuovo messaggio', settings_label_busy_input_mode: 'Modalità input occupato', settings_desc_busy_input_mode: "Controlla cosa succede quando invii un messaggio mentre l'agente è in esecuzione. Coda attende; Interrompi annulla e ricomincia; Steer inietta una correzione a metà turno senza interrompere (fallback a coda se agente o stream non disponibili).", + settings_label_fade_text_effect: 'Effetto dissolvenza testo', + settings_desc_fade_text_effect: 'Dissolve gradualmente le nuove parole trasmesse in streaming mentre l’assistente risponde. Simile a OpenWebUI; disattivato per impostazione predefinita per massime prestazioni.', settings_busy_input_mode_queue: 'Accoda follow-up', settings_busy_input_mode_interrupt: 'Interrompi turno corrente', settings_busy_input_mode_steer: 'Steer (correzione a metà turno)', @@ -8529,7 +8531,6 @@ const LOCALES = { onboarding_password_will_replace: '\u5c07\u6703\u53d6\u4ee3', onboarding_password_keep_existing: '\u4fdd\u7559\u76ee\u524d\u5bc6\u78bc', onboarding_password_remains_disabled: '\u6703\u7e7c\u7e8c\u4fdd\u6301\u95dc\u9589', - settings_label_sound: '\u901a\u77e5\u8072\u97f3', // boot.js cancelling: '\u6b63\u5728\u53d6\u6d88...', cancel_failed: '\u53d6\u6d88\u5931\u6557\uff1a', @@ -8676,7 +8677,6 @@ const LOCALES = { session_meta_segments: (n) => `${n} 段`, session_lineage_segment_untitled: '未命名段', session_lineage_segment_open: '開啟脈絡段', - settings_label_model: '\u9810\u8a2d\u6a21\u578b', skill_created: '\u6280\u80fd\u5df2\u5efa\u7acb', skill_file_load_failed: '\u8f09\u5165\u6a94\u6848\u5931\u6557\uff1a', skill_load_failed: '\u8f09\u5165\u6280\u80fd\u5931\u6557\uff1a', @@ -9589,6 +9589,7 @@ const LOCALES = { settings_title: 'Configurações', settings_save_btn: 'Salvar Configurações', settings_label_model: 'Modelo Padrão', + settings_desc_model: 'Usado para novas conversas. Conversas existentes mantêm o modelo selecionado.', settings_label_send_key: 'Tecla de Envio', settings_label_theme: 'Tema', settings_label_skin: 'Skin', @@ -11577,6 +11578,8 @@ const LOCALES = { busy_interrupt_confirm: 'Interrompu — envoi d\'un nouveau message', settings_label_busy_input_mode: 'Mode de saisie occupé', settings_desc_busy_input_mode: 'Contrôle ce qui se passe lorsque vous envoyez un message pendant l\'exécution de l\'agent. La file d\'attente attend ; L\'interruption s\'annule et recommence ; Steer injecte une correction à mi-tour sans interrompre (revient en file d\'attente lorsque l\'agent ou le flux est indisponible).', + settings_label_fade_text_effect: 'Effet de fondu du texte', + settings_desc_fade_text_effect: 'Affiche progressivement les nouveaux mots diffusés pendant que l’assistant répond. Similaire à OpenWebUI ; désactivé par défaut pour des performances maximales.', settings_busy_input_mode_queue: 'Suivi de file d\'attente', settings_busy_input_mode_interrupt: 'Interrompre le tour en cours', settings_busy_input_mode_steer: 'Direction (correction à mi-virage)', diff --git a/tests/test_provider_quota_status.py b/tests/test_provider_quota_status.py index 02ddc563..a70e0182 100644 --- a/tests/test_provider_quota_status.py +++ b/tests/test_provider_quota_status.py @@ -1264,6 +1264,23 @@ def test_provider_quota_i18n_keys_exist_for_all_locales(): assert len(re.findall(rf"^\s+{re.escape(key)}:", i18n, re.MULTILINE)) == locale_count, key +def test_settings_label_and_description_i18n_keys_exist_for_all_locales(): + """Settings labels/descriptions referenced by the page need every locale.""" + i18n = (ROOT / "static" / "i18n.js").read_text(encoding="utf-8") + index_html = (ROOT / "static" / "index.html").read_text(encoding="utf-8") + locale_count = len( + re.findall(r"^ (?:[A-Za-z_][A-Za-z0-9_]*|'[^']+'):\s*\{", i18n, re.MULTILINE) + ) + keys = sorted( + set(re.findall(r'data-i18n="(settings_(?:label|desc)_[a-z0-9_]+)"', index_html)) + ) + assert locale_count >= 1 + assert "settings_label_fade_text_effect" in keys + assert "settings_desc_fade_text_effect" in keys + for key in keys: + assert len(re.findall(rf"^\s+{re.escape(key)}:", i18n, re.MULTILINE)) == locale_count, key + + def test_provider_quota_styles_exist(): """Quota UI should have visible supported/unavailable/invalid states.""" css = (ROOT / "static" / "style.css").read_text(encoding="utf-8")