From e0f5483fdf4466c3c9b3d19d01f6c7e6f4ed641f Mon Sep 17 00:00:00 2001 From: lisry-1 Date: Sat, 31 Oct 2026 19:19:42 +0800 Subject: [PATCH] fix(goal): fall back gracefully for an unsupported reply locale (#7745) * fix(goal): fall back gracefully for an unsupported reply locale goal_reply_text and goal_status_text indexed _TEXT directly with the caller's locale, so a locale outside NotificationLocale (en-US/zh-CN) raised KeyError. The /connect handshake (connect_reply_text) normalizes with supported_locale and falls back to en-US; the /goal replies must do the same instead of surfacing an internal error to the user. Normalize the locale in both public entry points. Adds tests pinning the fallback for an unknown locale and the zh-CN wording for a supported one. * style(tests): format goal status locale assertion --------- Co-authored-by: lisry-1 <249596828+lisry-1@users.noreply.github.com> Co-authored-by: Willem Jiang --- backend/app/channels/goal_status.py | 3 +++ backend/tests/test_goal_status.py | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 backend/tests/test_goal_status.py diff --git a/backend/app/channels/goal_status.py b/backend/app/channels/goal_status.py index 750b4a7849..c6ee706432 100644 --- a/backend/app/channels/goal_status.py +++ b/backend/app/channels/goal_status.py @@ -16,6 +16,7 @@ from __future__ import annotations import math from collections.abc import Mapping +from app.channels.reply_locale import supported_locale from app.scheduler.notification_text import goal_reason_text from deerflow.persistence.scheduled_task_runs.finalization import CHECK_FAILURE_CODES from deerflow.runtime.goal import is_active_goal @@ -65,6 +66,7 @@ _WAITING_KEYS = {"needs_user_input": "waiting_for_you", "external_wait": "waitin def goal_reply_text(key: str, locale: str) -> str: """A fixed ``/goal`` reply (``none``, ``cleared``, ``*_failed``) in ``locale``.""" + locale = supported_locale(locale) return _TEXT[locale][key] @@ -102,6 +104,7 @@ def goal_status_text(goal: object, outcome: object, *, running: bool, locale: st ``running`` says whether the thread has a run pending or in progress. """ + locale = supported_locale(locale) text = _TEXT[locale] if is_active_goal_state(goal): lines = [text["goal"].format(objective=goal["objective"])] diff --git a/backend/tests/test_goal_status.py b/backend/tests/test_goal_status.py new file mode 100644 index 0000000000..8473873a0d --- /dev/null +++ b/backend/tests/test_goal_status.py @@ -0,0 +1,25 @@ +"""Tests for ``/goal`` reply text locale handling. + +``goal_status.py`` hosts its own reply wording and, unlike the ``/connect`` +handshake, used to index its ``_TEXT`` table directly with the caller's locale. +A locale outside ``NotificationLocale`` therefore raised ``KeyError`` instead of +falling back, which is the opposite of ``connect_reply_text``. These tests pin +the fallback behavior. +""" + +from app.channels.goal_status import goal_reply_text, goal_status_text + + +def test_goal_reply_text_unknown_locale_falls_back_to_default(): + """A locale outside NotificationLocale must fall back, not raise KeyError.""" + assert goal_reply_text("none", "ja-JP") == "No active goal." + + +def test_goal_reply_text_known_zh_locale(): + """A supported locale still selects its own wording.""" + assert goal_reply_text("none", "zh-CN") == "当前没有目标。" + + +def test_goal_status_text_unknown_locale_falls_back_to_default(): + """The status reply also falls back for an unsupported locale.""" + assert goal_status_text(None, None, running=False, locale="ja-JP") == "No active goal."