fix(report): project annual sections and thicken dasha reading

完整数据版第二轮。年运章写出资料包里已有的节,没有数据的不新算。大运解释补回可能段落。瑜伽作用不再断言寿命。术语替换不进标识符。罗睺和计都不写守护第未列出宫。未合入 staging。
This commit is contained in:
jesse-ux
2026-10-08 22:30:32 +08:00
committed by Jesse_Chen
parent 25d067bf12
commit b8eef75871
14 changed files with 1148 additions and 165 deletions
+74 -8
View File
@@ -11,6 +11,48 @@ import re
from typing import Any
def replace_natural_language_term(text: str, raw: str, public: str) -> str:
"""Replace one glossary term in prose only.
Identifiers and backtick spans stay as written. A trailing English plural
``s`` is consumed with the English word, so it is not left after Chinese.
"""
if not text or not raw:
return text
plural = "s?" if re.search(r"[A-Za-z]$", raw) and not raw.endswith("s") else ""
pattern = re.compile(rf"(?<![A-Za-z0-9_]){re.escape(raw)}{plural}(?![A-Za-z0-9_])")
parts = re.split(r"(`[^`]*`)", text)
return "".join(
part if index % 2 else pattern.sub(public, part)
for index, part in enumerate(parts)
)
_LIFESPAN_ASSERTION = re.compile(
r"长寿|寿命长|短寿|寿命|longevity|lifespan|long life|short life",
re.IGNORECASE,
)
_LIFESPAN_EFFECT_ZH = "传统上与体质和恢复力有关,仅作参考"
_LIFESPAN_EFFECT_EN = "Traditionally associated with constitution and recovery. Reference only."
def strip_han_plural_s(text: str) -> str:
"""Remove a Latin plural s left on Chinese after a glossary replace."""
return re.sub(r"([一-鿿])s\b", r"\1", text)
def soften_lifespan_wording(text: str, language: str = "zh") -> str:
"""Drop lifespan assertions from an effect cell. Yoga names are not passed here."""
if not text or not _LIFESPAN_ASSERTION.search(str(text)):
return text
if language == "en":
return _LIFESPAN_EFFECT_EN
return _LIFESPAN_EFFECT_ZH
ZH_TERM_REPLACEMENTS: tuple[tuple[str, str], ...] = (
("Natural friends", "天然友星"),
("Natural enemies", "天然敌星"),
@@ -221,10 +263,22 @@ _ZH_LITERAL_REPLACEMENTS = tuple(
if not re.fullmatch(r"[A-Za-z0-9_ /:-]+", raw)
)
_ZH_REGEX_REPLACEMENT_MAP = {raw: public for raw, public in _ZH_REGEX_REPLACEMENTS}
def _zh_regex_body(raw: str) -> str:
body = re.escape(raw)
if re.search(r"[A-Za-z]$", raw) and not raw.endswith("s"):
body += "s?"
return body
_ZH_REGEX_REPLACEMENT_PATTERN = re.compile(
r"(?<![A-Za-z])("
+ "|".join(re.escape(raw) for raw, _public in sorted(_ZH_REGEX_REPLACEMENTS, key=lambda item: len(item[0]), reverse=True))
+ r")(?![A-Za-z])"
r"(?<![A-Za-z0-9_])("
+ "|".join(
_zh_regex_body(raw)
for raw, _public in sorted(_ZH_REGEX_REPLACEMENTS, key=lambda item: len(item[0]), reverse=True)
)
+ r")(?![A-Za-z0-9_])"
)
_ZH_NAKSHATRA_GLOSSES = {
@@ -262,11 +316,23 @@ _ZH_NAKSHATRA_GLOSS_PATTERN = re.compile(
)
def _zh_regex_public(token: str) -> str:
if token in _ZH_REGEX_REPLACEMENT_MAP:
return _ZH_REGEX_REPLACEMENT_MAP[token]
if token.endswith("s") and token[:-1] in _ZH_REGEX_REPLACEMENT_MAP:
return _ZH_REGEX_REPLACEMENT_MAP[token[:-1]]
return token
def _replace_zh_regex_terms(markdown: str) -> str:
return _ZH_REGEX_REPLACEMENT_PATTERN.sub(
lambda match: _ZH_REGEX_REPLACEMENT_MAP[match.group(1)],
markdown,
)
def apply(chunk: str) -> str:
return _ZH_REGEX_REPLACEMENT_PATTERN.sub(
lambda match: _zh_regex_public(match.group(1)),
chunk,
)
parts = re.split(r"(`[^`]*`)", markdown)
return "".join(part if index % 2 else apply(part) for index, part in enumerate(parts))
def normalize_zh_markdown_terms(markdown: str) -> str:
@@ -321,7 +387,7 @@ def normalize_zh_markdown_terms(markdown: str) -> str:
text = _normalize_zh_dasha_prose(text)
text = _thicken_zh_dasha_density(text)
text = _normalize_mula_gloss(_restore_zh_nakshatra_glosses(_collapse_duplicate_zh_parentheses(text)))
return text
return strip_han_plural_s(text)
def _normalize_zh_bhava_factor_text(markdown: str) -> str: