1010)
1111
1212
13+ _ZH_REASON_REPLACEMENTS = (
14+ ("feature snapshot guard blocked execution" , "特征快照校验阻止执行" ),
15+ ("feature snapshot required" , "需要特征快照" ),
16+ ("feature snapshot compute failed" , "特征快照计算失败" ),
17+ ("feature_snapshot_download_failed" , "特征快照下载失败" ),
18+ ("feature_snapshot_compute_failed" , "特征快照计算失败" ),
19+ ("feature_snapshot_path_missing" , "缺少特征快照路径" ),
20+ ("feature_snapshot_missing" , "特征快照不存在" ),
21+ ("feature_snapshot_stale" , "特征快照过旧" ),
22+ ("feature_snapshot_manifest_missing" , "缺少快照清单" ),
23+ ("feature_snapshot_profile_mismatch" , "快照策略名不匹配" ),
24+ ("feature_snapshot_config_name_mismatch" , "快照配置名不匹配" ),
25+ ("feature_snapshot_config_path_mismatch" , "快照配置路径不匹配" ),
26+ ("feature_snapshot_contract_version_mismatch" , "快照契约版本不匹配" ),
27+ ("outside_execution_window" , "当前不在执行窗口" ),
28+ ("pending_orders_detected" , "检测到未完成订单" ),
29+ ("same_day_execution_locked" , "当日执行锁已存在" ),
30+ ("same_day_fills_detected" , "检测到当日成交" ),
31+ ("insufficient_buying_power" , "购买力不足" ),
32+ ("missing_price" , "缺少报价" ),
33+ ("no_equity" , "无净值" ),
34+ ("fail_closed" , "关闭执行" ),
35+ ("reason=" , "原因=" ),
36+ ("fail_reason=" , "失败原因=" ),
37+ ("decision=" , "决策=" ),
38+ )
39+
40+
1341def _format_text (value , * , fallback : str ) -> str :
1442 text = str (value ).strip () if value is not None else ""
1543 return text or fallback
@@ -26,6 +54,21 @@ def _format_symbol_preview(symbols, *, limit: int = 3) -> str:
2654 return "," .join (shown )
2755
2856
57+ def _translator_uses_zh (translator ) -> bool :
58+ sample = str (translator ("no_trades" ))
59+ return any ("\u4e00 " <= ch <= "\u9fff " for ch in sample )
60+
61+
62+ def _localize_notification_text (text : str , * , translator ) -> str :
63+ value = str (text or "" ).strip ()
64+ if not value or not _translator_uses_zh (translator ):
65+ return value
66+ localized = value
67+ for source , target in _ZH_REASON_REPLACEMENTS :
68+ localized = localized .replace (source , target )
69+ return localized
70+
71+
2972def _summarize_target_changes (target_vs_current , * , limit : int = 5 ) -> str | None :
3073 rows = []
3174 for row in target_vs_current or ():
@@ -229,15 +272,17 @@ def build_dashboard(
229272 translator ("snapshot_as_of_detail" , value = _format_text (snapshot_as_of , fallback = "<none>" )) if snapshot_as_of else None ,
230273 ]
231274 diagnostics_text = " | " .join (part for part in diagnostics if part )
275+ localized_status_desc = _localize_notification_text (status_desc , translator = translator )
276+ localized_signal_desc = _localize_notification_text (signal_desc , translator = translator )
232277 return (
233278 f"{ translator ('equity' )} : ${ equity :,.2f} | { translator ('buying_power' )} : ${ buying_power :,.2f} \n "
234279 f"{ separator } \n "
235280 f"{ position_text } \n "
236281 f"{ separator } \n "
237282 f"{ diagnostics_text } \n "
238283 f"{ separator } \n "
239- f"{ status_icon } { status_desc } \n "
240- f"🎯 { signal_desc } \n "
284+ f"{ status_icon } { localized_status_desc } \n "
285+ f"🎯 { localized_signal_desc } \n "
241286 f"{ separator } \n "
242287 f"{ translator ('target_weights_title' )} :\n { target_text } "
243288 )
@@ -290,11 +335,11 @@ def run_strategy_core(
290335 fail_reason = signal_metadata .get ("fail_reason" )
291336 no_op_text = translator ("no_trades" )
292337 if decision :
293- no_op_text = f"{ no_op_text } | decision={ decision } "
338+ no_op_text = f"{ no_op_text } | { _localize_notification_text ( f' decision={ decision } ' , translator = translator ) } "
294339 if no_op_reason :
295- no_op_text = f"{ no_op_text } | reason={ no_op_reason } "
340+ no_op_text = f"{ no_op_text } | { _localize_notification_text ( f' reason={ no_op_reason } ' , translator = translator ) } "
296341 if fail_reason :
297- no_op_text = f"{ no_op_text } | fail_reason={ fail_reason } "
342+ no_op_text = f"{ no_op_text } | { _localize_notification_text ( f' fail_reason={ fail_reason } ' , translator = translator ) } "
298343 record = build_reconciliation_record (
299344 strategy_profile = signal_metadata .get ("strategy_profile" ),
300345 mode = "dry_run" if signal_metadata .get ("dry_run_only" ) else "paper" ,
0 commit comments