feat(report): English reader edition from the same packet; yoga combination English twins (E1/E2/E3 wiring)

- /api/professional_report_reference accepts languages [zh,en]; one packet, zh
  byte-identical, markdown_en + reader_dasha_applicability_en, or
  english_unavailable when a Chinese character would leak.
- pl9_reader_english renders the parity volume with report_language=en and a
  fixed term table; yoga_engine carries combination_en / effects_en / strength_en.
- 75 fictional charts: zero Han, same line count, same numbers outside yoga tables.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
This commit is contained in:
Jesse_Chen
2026-09-29 19:26:00 +08:00
co-authored by Claude Opus 5.5
parent 0c06ab1d78
commit 419e1f4d37
8 changed files with 833 additions and 53 deletions
+22 -6
View File
@@ -57,6 +57,12 @@ def _pl9_export_markdown_for_edition(packet: dict, edition: str) -> str:
_bind_engine_symbols()
normalized = str(edition or "").strip()
if normalized == "reader_main":
if _pl9_report_language(packet) == 'en':
try:
from scripts.pl9_reader_english import render_pl9_user_markdown_en
except ModuleNotFoundError: # pragma: no cover - direct scripts/ execution
from pl9_reader_english import render_pl9_user_markdown_en
return render_pl9_user_markdown_en(packet)
return _render_pl9_user_markdown(packet)
if normalized in {"pl9_parity", "pl9_fixed_204"}:
return render_pl9_parity_markdown(packet)
@@ -83,15 +89,18 @@ def _pl9_english_report_title(packet: dict) -> str:
return f"{name}'s Vedic Astrology Chart Report" if name else "Vedic Astrology Chart Report"
def _dignity_label(value) -> str:
def _dignity_label(value, language: str = 'zh') -> str:
"""Engine dignity labels are bilingual (``入旺(Exalted)``); the reader shows one name.
The glossary pass would otherwise translate the English half too and print
``入旺(入旺)``. Anything not in that shape passes through unchanged.
``入旺(入旺)``. Anything not in that shape passes through unchanged. The
English edition takes the English half.
"""
text = str(value or '').strip()
match = re.fullmatch(r'([^()()]+?)\s*[((][A-Za-z][A-Za-z \-]*[))]', text)
return match.group(1).strip() if match else text
match = re.fullmatch(r'([^()()]+?)\s*[((]([A-Za-z][A-Za-z \-]*)[))]', text)
if not match:
return text
return match.group(2).strip() if language == 'en' else match.group(1).strip()
def _pl9_report_language(packet: dict) -> str:
@@ -459,6 +468,7 @@ def _render_pl9_user_markdown(packet: dict) -> str:
def render_pl9_parity_markdown(packet: dict) -> str:
"""Render only PL9 page-family material admitted to the parity main volume."""
_bind_engine_symbols()
_lang = _pl9_report_language(packet)
def _unwrap(raw_value):
if isinstance(raw_value, dict) and 'result' in raw_value and 'response_envelope' in raw_value:
return raw_value.get('result')
@@ -538,6 +548,9 @@ def render_pl9_parity_markdown(packet: dict) -> str:
}
sign_map = globals().get('SIGNS_CN', {})
raw = str(value or '').strip()
if _lang == 'en':
return raw or '-'
return label_map.get(raw) or sign_map.get(raw) or raw or '-'
def _annual_field_text(field, *keys) -> str:
@@ -804,7 +817,7 @@ def render_pl9_parity_markdown(packet: dict) -> str:
lines.append(
f"| {_cell(_token(planet))} | {_cell(_token(row.get('rasi_lord')))} | "
f"{_cell(_token(row.get('nakshatra_lord')))} | {_cell(_token(row.get('sub_lord')))} | "
f"{_cell(_token(row.get('sub_sub_lord')))} | {_cell(_dignity_label(row.get('status')))} | {_cell(_num(row.get('sb'), 2))} |"
f"{_cell(_token(row.get('sub_sub_lord')))} | {_cell(_dignity_label(row.get('status'), _lang))} | {_cell(_num(row.get('sb'), 2))} |"
)
lines.extend([
'',
@@ -2367,7 +2380,7 @@ def render_pl9_parity_markdown(packet: dict) -> str:
placement = f"{_token(lord_row.get('sign'))} H{lord_row.get('house') or '-'} {_degree(lord_row.get('degree_in_sign'))}"
lines.append(
f"| {index} | {_cell(_token(house.get('cusp_sign') or house.get('sign')))} | {_cell(_token(lord))} | "
f"{_cell(placement)} | {_cell(_dignity_label(lord_row.get('status') or lord_row.get('dignity')))} | {_cell('active' if str(lord) in active_lords else 'background')} |"
f"{_cell(placement)} | {_cell(_dignity_label(lord_row.get('status') or lord_row.get('dignity'), _lang))} | {_cell('active' if str(lord) in active_lords else 'background')} |"
)
lines.append('')
bhavesh_pages = _list(bhavesh_pack.get('pages'))
@@ -2556,4 +2569,7 @@ def render_pl9_parity_markdown(packet: dict) -> str:
continue
seen_blocks.add(value)
lines.extend([_public_report_text(value), ''])
if _lang == 'en':
# The English edition does its own public-wording pass (pl9_reader_english).
return '\n'.join(lines)
return _sanitize_pl9_public_markdown('\n'.join(lines))