fix(yoga): Chinese combination text from a Chinese source, not 未判定 (#91 fix2 F3, BUG-1418)

Removing the English-to-Chinese back-translation left every BVR semantic
note and every yogas_doshas raj/dhana "H*_Lord(...) in H*" sentence as
未判定 in the Chinese yoga table and pages.

- yoga_engine: semantic conditions return BiText(Chinese, old note). The
  Chinese comes from references/yoga_semantic_notes_zh.json, one sentence per
  note, so dedup keeps the same equality classes. English-only custom
  templates get the same lookup. combination_en is unchanged.
- yogas_doshas: raj/dhana rows add combination_zh; combination is unchanged.
- chinese_combination prefers a clean combination_zh.

Which yogas fire, their strength and category are unchanged (fictional
charts 0-19, same rows as staging; only set-iteration order differs).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HwMNZEzdzmrbPhuRNmZe3C
This commit is contained in:
Jesse_Chen
2026-10-22 21:55:02 +08:00
co-authored by Claude Opus 5.5
parent 00c2b4d498
commit ac3dccbac8
6 changed files with 382 additions and 3 deletions
+46 -3
View File
@@ -162,6 +162,48 @@ STRENGTH_EN = {
}
# Chinese wording for the English ``note`` of semantic (BVR / PyJHora batch)
# conditions. The note stays the English twin. One note maps to one Chinese
# sentence, so the dedup key keeps the same equality classes (BUG-1418).
_SEMANTIC_NOTES_ZH_PATH = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "..", "references", "yoga_semantic_notes_zh.json")
# A condition without a note used to print its type key. Keep one fixed
# sentence per type so two matches of the same type still dedupe together.
_SEMANTIC_TYPE_ZH = {
"planet_in_house": "行星落在规则指定的宫位",
"planet_in_lagna": "行星落在上升宫",
}
@functools.lru_cache(maxsize=1)
def _semantic_notes_zh() -> Dict[str, str]:
try:
with open(_SEMANTIC_NOTES_ZH_PATH, "r", encoding="utf-8") as handle:
data = json.load(handle)
except (OSError, ValueError):
return {}
return {str(key): str(value) for key, value in data.items()} if isinstance(data, dict) else {}
def _semantic_text(cond: Dict, ctype: str):
"""``cond.get("note", ctype)`` with Chinese text; the old value stays as ``.en``."""
if "note" in cond:
note = cond.get("note")
zh = _semantic_notes_zh().get(str(note)) if note else None
return BiText(zh, note) if zh else note
zh = _SEMANTIC_TYPE_ZH.get(str(ctype))
return BiText(zh, ctype) if zh else ctype
def _template_text(zh: str, en) -> BiText:
"""``BiText(zh, en)``; a rule template written only in English gets its Chinese sentence (BUG-1418)."""
if not any("\u4e00" <= char <= "\u9fff" for char in zh):
chinese = _semantic_notes_zh().get(zh)
if chinese:
return BiText(chinese, zh if en is None else en)
return BiText(zh, en)
def _combo(cond: Dict, default_zh: str, **values) -> BiText:
"""Format the rule's combination template in Chinese (unchanged) and in English."""
zh = cond.get("combo_template", default_zh).format(**values)
@@ -172,7 +214,7 @@ def _combo(cond: Dict, default_zh: str, **values) -> BiText:
en = en_template.format(**{key: (value.en if isinstance(value, BiText) else value) for key, value in values.items()})
except (KeyError, IndexError, ValueError):
en = None
return BiText(zh, en)
return _template_text(zh, en)
def _get_dignity_level(planet: str, sign: str) -> str:
@@ -1465,7 +1507,7 @@ class YogaEngine:
combo = cond.get("combo_template", "自定义条件满足")
if isinstance(combo, str):
# Custom templates were never formatted; keep that, and pair the English text.
combo = BiText(combo, cond.get("combo_template_en") if "combo_template" in cond else DEFAULT_COMBO_EN["自定义条件满足"])
combo = _template_text(combo, cond.get("combo_template_en") if "combo_template" in cond else DEFAULT_COMBO_EN["自定义条件满足"])
if callable(combo):
combo = combo(ctx, bindings)
strength = cond.get("strength", rule.get("strength", "中"))
@@ -1788,7 +1830,8 @@ class YogaEngine:
def _eval_semantic_condition(self, ctype, cond, ctx, rule, bindings):
"""兼容批量抽取规则中的语义型条件。返回保守布尔匹配。"""
def success(combo=None):
return [{"combination": combo or cond.get("note", ctype), "strength": rule.get("strength", "中")}]
text = combo or _semantic_text(cond, ctype)
return [{"combination": text, "strength": rule.get("strength", "中")}]
def house(ref):
if isinstance(ref, int):