# -*- coding: utf-8 -*- """显示层术语映射 (app_frontEnd/configs/terms/display_map.yaml): 内部说法 → 现场说法。 **只在渲染边界用**, 不改数据、不改任何比较/匹配逻辑 (那些内部词就在 handoff/本体数据里, 改数据会断匹配)。 用法: from src.windscada.terms import humanize; humanize('发电机(引用同族, 见族主台)') → '发电机(无本台独立证据, 参照同型机组)' 前端同源: `pairs()` 出的表经语言包 `__display_zh` 注入 app.js 的 displayText。 """ from __future__ import annotations try: from app_common.app_common_guanlan.api import install_root as _install_root except ImportError: # 极端兜底(包结构异常时按位置上跳) from pathlib import Path as _P def _install_root(_f): return _P(_f).resolve().parents[3] import functools, pathlib, sys ROOT = _install_root(__file__) sys.path.insert(0, str(ROOT)) from src import paths as P # 配置唯一取用口 MAP_FILE = P.config("terms", "display_map.yaml") # 配置唯一取用口 (src/paths.py) @functools.lru_cache(maxsize=1) def pairs() -> tuple[tuple[str, str], ...]: """(内部词, 现场词) 按内部词长度降序 —— 长词先替, 否则 '引用同族, 见族主台' 会被 '引用同族' 先吃掉半句.""" if not MAP_FILE.exists(): return () import yaml d = yaml.safe_load(MAP_FILE.read_text(encoding="utf-8")) or {} ps = [(str(i["from"]), str(i["to"])) for i in (d.get("items") or []) if i.get("from")] return tuple(sorted(ps, key=lambda kv: -len(kv[0]))) def humanize(s): """把内部词换成现场说法; 非字符串原样返回 (调用点可以无脑包).""" if not isinstance(s, str) or not s: return s for a, b in pairs(): if a in s: s = s.replace(a, b) return s