mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-07-17 11:07:11 +08:00
feat(i18n): support Traditional Chinese (zh-tw) for console, logs and docs
This commit is contained in:
@@ -1864,6 +1864,7 @@ class ConfigHandler:
|
||||
json.dump(file_cfg, f, indent=4, ensure_ascii=False)
|
||||
|
||||
logger.info(f"[WebChannel] Config updated: {list(applied.keys())}")
|
||||
restarted = False
|
||||
|
||||
# Apply a language change immediately so backend logs, agent
|
||||
# replies and CLI output switch without a restart.
|
||||
@@ -1874,6 +1875,26 @@ class ConfigHandler:
|
||||
except Exception as lang_err:
|
||||
logger.warning(f"[WebChannel] Failed to apply language: {lang_err}")
|
||||
|
||||
# Trigger a dynamic restart of the web channel if web_password is changed,
|
||||
# so the socket re-binds to either 127.0.0.1 or 0.0.0.0 immediately.
|
||||
if "web_password" in applied:
|
||||
try:
|
||||
import sys
|
||||
app_module = sys.modules.get('__main__') or sys.modules.get('app')
|
||||
mgr = getattr(app_module, '_channel_mgr', None) if app_module else None
|
||||
if mgr:
|
||||
def delay_restart():
|
||||
time.sleep(0.5)
|
||||
mgr.restart("web")
|
||||
threading.Thread(
|
||||
target=delay_restart,
|
||||
daemon=True,
|
||||
).start()
|
||||
restarted = True
|
||||
logger.info("[WebChannel] Web channel restart scheduled due to password change")
|
||||
except Exception as restart_err:
|
||||
logger.warning(f"[WebChannel] Failed to trigger web channel restart: {restart_err}")
|
||||
|
||||
# Reset Bridge so that bot routing reflects the new config.
|
||||
# Without this, Bridge keeps its cached bot instance (e.g. LinkAIBot)
|
||||
# even after the user switches bot_type / use_linkai / model in UI.
|
||||
@@ -1886,7 +1907,7 @@ class ConfigHandler:
|
||||
except Exception as reset_err:
|
||||
logger.warning(f"[WebChannel] Failed to reset bridge: {reset_err}")
|
||||
|
||||
return json.dumps({"status": "success", "applied": applied}, ensure_ascii=False)
|
||||
return json.dumps({"status": "success", "applied": applied, "restarted": restarted}, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating config: {e}")
|
||||
return json.dumps({"status": "error", "message": str(e)})
|
||||
@@ -3667,11 +3688,13 @@ class ChannelsHandler:
|
||||
_require_auth()
|
||||
web.header('Content-Type', 'application/json; charset=utf-8')
|
||||
try:
|
||||
from common import i18n
|
||||
local_config = conf()
|
||||
active_channels = self._active_channel_set()
|
||||
# Desktop build ships without lark-oapi, so hide Feishu from the list.
|
||||
desktop_mode = os.environ.get("COW_DESKTOP") == "1"
|
||||
channels = []
|
||||
is_tw = i18n.get_language() == i18n.ZH_TW
|
||||
for ch_name, ch_def in self.CHANNEL_DEFS.items():
|
||||
if desktop_mode and ch_name == "feishu":
|
||||
continue
|
||||
@@ -3682,16 +3705,32 @@ class ChannelsHandler:
|
||||
display_val = self._mask_secret(str(raw_val))
|
||||
else:
|
||||
display_val = raw_val
|
||||
|
||||
label_val = f["label"]
|
||||
if is_tw and isinstance(label_val, str):
|
||||
label_val = i18n.to_traditional(label_val)
|
||||
elif is_tw and isinstance(label_val, dict):
|
||||
label_val = label_val.copy()
|
||||
label_val["zh-tw"] = i18n.to_traditional(label_val.get("zh", ""))
|
||||
|
||||
fields_out.append({
|
||||
"key": f["key"],
|
||||
"label": f["label"],
|
||||
"label": label_val,
|
||||
"type": f["type"],
|
||||
"value": display_val,
|
||||
"default": f.get("default", ""),
|
||||
})
|
||||
|
||||
label_val = ch_def["label"]
|
||||
if is_tw and isinstance(label_val, str):
|
||||
label_val = i18n.to_traditional(label_val)
|
||||
elif is_tw and isinstance(label_val, dict):
|
||||
label_val = label_val.copy()
|
||||
label_val["zh-tw"] = i18n.to_traditional(label_val.get("zh", ""))
|
||||
|
||||
ch_info = {
|
||||
"name": ch_name,
|
||||
"label": ch_def["label"],
|
||||
"label": label_val,
|
||||
"icon": ch_def["icon"],
|
||||
"color": ch_def["color"],
|
||||
"active": ch_name in active_channels,
|
||||
@@ -4248,16 +4287,26 @@ class ToolsHandler:
|
||||
web.header('Content-Type', 'application/json; charset=utf-8')
|
||||
try:
|
||||
from agent.tools.tool_manager import ToolManager
|
||||
from common import i18n
|
||||
tm = ToolManager()
|
||||
if not tm.tool_classes:
|
||||
tm.load_tools()
|
||||
tools = []
|
||||
lang = i18n.get_language()
|
||||
for name, cls in tm.tool_classes.items():
|
||||
try:
|
||||
instance = cls()
|
||||
desc = instance.description
|
||||
if lang == i18n.ZH_TW and desc:
|
||||
desc = i18n.to_traditional(desc)
|
||||
elif lang == "en" and name == "scheduler":
|
||||
desc = (
|
||||
"Create, query and manage scheduled tasks (reminders, periodic tasks, etc.).\n\n"
|
||||
"⚠️ IMPORTANT: Only use this tool when delayed or periodic execution is needed."
|
||||
)
|
||||
tools.append({
|
||||
"name": name,
|
||||
"description": instance.description,
|
||||
"description": desc,
|
||||
})
|
||||
except Exception:
|
||||
tools.append({"name": name, "description": ""})
|
||||
@@ -4274,10 +4323,17 @@ class SkillsHandler:
|
||||
try:
|
||||
from agent.skills.service import SkillService
|
||||
from agent.skills.manager import SkillManager
|
||||
from common import i18n
|
||||
workspace_root = _get_workspace_root()
|
||||
manager = SkillManager(custom_dir=os.path.join(workspace_root, "skills"))
|
||||
service = SkillService(manager)
|
||||
skills = service.query()
|
||||
if i18n.get_language() == i18n.ZH_TW:
|
||||
for skill in skills:
|
||||
if isinstance(skill, dict):
|
||||
for k, v in list(skill.items()):
|
||||
if k in ("name", "description", "display_name") and isinstance(v, str):
|
||||
skill[k] = i18n.to_traditional(v)
|
||||
return json.dumps({"status": "success", "skills": skills}, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
logger.error(f"[WebChannel] Skills API error: {e}")
|
||||
|
||||
Reference in New Issue
Block a user