Skip to content

Supports the latest Typecho 1.3.0 and introduces an AI-driven, self-healing i18n translation pipeline - #64

Open
little-gt wants to merge 1 commit into
typecho:masterfrom
little-gt:20260905---Update-for-modern-translation-project
Open

little-gt wants to merge 1 commit into
typecho:masterfrom
little-gt:20260905---Update-for-modern-translation-project

Conversation

@little-gt

Copy link
Copy Markdown

This PR turns the language pack from a static (hand-maintained, 2018-era) artifact into a
reproducible, machine-translation-driven, and self-validating engineering project. It adds a
complete toolchain under tools/ that scans Typecho's PHP source, machine-translates every string
via Tencent Cloud TMT, hardens the output against the well-known failure modes of MT (broken
placeholders, emptied HTML tags, casing corruption), validates the result with a CI-ready checker,
and keeps human polish safe through a self-healing translation cache.

flowchart LR
    A[Typecho 1.3.0 PHP source] -->|tools/_i18n_analyze.py| B[original/messages.pot<br/>898 strings, Simplified-Chinese source of truth]
    B -->|tools/_tmt_translate.py<br/>TMT + hardening| C[original/projects/*.po<br/>10 languages]
    C -->|msgfmt| D[src/langs/*.mo<br/>install artifacts]
    C -->|tools/_po_check.py<br/>8-point validation| E[_po_check_report.json<br/>CI gate]
    C -->|tools/_build_tmt_cache.py<br/>re-seed| F[tools/runtime/_tmt_cache.json<br/>offline + polish-safe]
    F -->|served on next run| B
Loading

What this PR changes

1. Template extraction — tools/_i18n_analyze.py

  • Walks the Typecho source tree (.php only, skipping vendor/, usr/langs/, .git/, …),
    strips PHP comments while preserving line numbers, and extracts every i18n call:
    _t(), _e() (singular) and _n() (plural).
  • Unescapes PHP single/double-quoted literals correctly and emits original/messages.pot with
    accurate #: path/file.php:line references, so every string is traceable back to its source.
  • Plural strings are detected and emitted with msgid + msgid_plural.

2. Machine translation engine — tools/_tmt_translate.py

Calls the Tencent Cloud TMT TextTranslate API and regenerates all original/projects/*.po
files, keeping the POT's structure, ordering, and #: references intact. It is not a naive
"send string → store result" wrapper — it actively defends the output:

  • Placeholder auto-repair. MT frequently mangles placeholders:
    % s / % d / %1$ s (spaces inserted), %S / %D (wrong case), {sl ug} / {Cid}
    (split / re-cased). repair_placeholder_spacing() and restore_placeholder_case() re-assemble
    them losslessly — only the broken placeholder is fixed, normal inter-word spacing is untouched.
  • Segment-wise markup translation. Strings mixing prose and HTML
    (e.g. 分类 <a href="%s">%s</a> 已经被增加) are split by split_markup(); tags are preserved
    verbatim and only their inner text is translated, then re-assembled in original order. A
    two-strategy fallback (mask-entire-element-then-translate → per-segment translate) guarantees the
    tag structure never collapses into an empty link.
  • Verbatim-fallback & retry. If TMT echoes the Chinese source back (a known quirk for some
    punctuation-terminated sentences), _retry_untranslated() strips the trailing punctuation and
    retries. If the result still lacks placeholders, or the API errors / target is unsupported, the
    string falls back to the source — and is recorded in a report, never silently accepted.
  • Per-language plural correctness. nplurals=1 languages (zh_TW / ja_JP / ko_KR) get exactly
    one msgstr slot; nplurals=3 (ru_RU) and n>1 forms are taken from DEFAULT_PLURAL_FORMS,
    so no spurious "missing plural slot" false positives.
  • Thread-safe, rate-limited, resumable. ThreadPoolExecutor + a RateLimiter (QPS≈4, the TMT
    ceiling is 5), up to 3 retries per request, and an atomic cache write (tmp + os.replace) with a
    dirty-flag so it only flushes when something actually changed.

3. Validation gate — tools/_po_check.py

A CI-ready checker that reuses _tmt_translate.py's exact parsing and validation functions
(parse_po, is_safe_translation, check_html_integrity) so there is a single source of truth
between translator and checker. It runs 8 checks per file:

Check What it catches
Completeness empty msgstr, or strings still equal to the Chinese source
Placeholder integrity %s/%d/%{name}/{name}/HTML tags/entities dropped or mutated
POT alignment stale entries the POT no longer contains (extra)
Plural consistency msgstr count vs. header Plural-Forms
Chinese residual Simplified Chinese leaking into non-CJK targets (skipped for zh/ja/ko)
HTML structure tag-internal text emptied by MT (<a>…</a><a></a>)
Verbatim copies msgstr identical to source (skipped for zh_TW via OpenCC s2t; ja_JP exempt for pure-Hanzi)
Missing entries POT strings absent from a .po

It writes tools/runtime/_po_check_report.json, prints a human-readable table, and exits non-zero
when any issue is found — ready to drop into CI.

4. Self-healing cache & human polish — tools/_build_tmt_cache.py

_tmt_translate.py serves translations from tools/runtime/_tmt_cache.json (and on load
purges cached entries that equal the Chinese source, fail placeholder checks, or have emptied
HTML tags — so a one-time MT failure is never permanently frozen). _build_tmt_cache.py re-seeds
the whole cache from the current original/projects/*.po, so:

  • a later run works fully offline and costs zero API quota;
  • hand-polished .po edits survive the next translation instead of being silently overwritten
    (the documented polish loop is: edit .po_build_tmt_cache.pymsgfmt_po_check.py).

5. Docs, environment, and project hygiene

  • Virtual environment first. All scripts run inside .venv/; .gitignore now ignores
    .venv/. Dependencies are minimal: tencentcloud-sdk-python-common (TMT) and
    opencc-python-reimplemented (Traditional-Chinese / Hanzi detection). msgfmt (gettext) compiles .mo.
  • README updated in four languages (en / zh-cn / zh-cht / ja-jp), plus tools/README.md and a
    tools/SKILL.md maintenance playbook covering the full workflow, known MT failure modes and their
    handling, the cache self-healing rule, the Traditional-Chinese special case, and how to add a language.

Why this mechanism is advanced

Most i18n MT pipelines stop at "call the API, write the file." This one treats MT as an
untrusted component that must be validated, repaired, and corrected — a closed loop rather than
a fire-and-forget step:

  1. MT-resilience by construction. Every segment is post-edited (repair_placeholder_spacing,
    restore_placeholder_case) and re-validated before it is accepted. Broken placeholders and
    emptied HTML tags — the two most common, most damaging MT defects in UI strings — are caught and
    either fixed or rejected, never shipped.
  2. Structure-aware translation. Mixed prose+HTML is handled as a tree (split_markup), not a
    blob of text. Tags survive byte-for-byte; only inner text is translated, with a fallback strategy
    that still preserves correctness when the engine drops the masking tokens.
  3. Self-healing cache (feedback loop). The cache is not a dumb key/value store. On every load it
    evicts bad translations, and on every run bad results are not written back. The system
    improves over repeated runs instead of ossifying its first mistake — a property rarely found in
    translation scripts.
  4. Single source of truth for correctness. The translator and the checker share the exact same
    is_safe_translation / check_html_integrity / parse_po code, so "what we generate" and
    "what we accept" can never drift apart.
  5. Reproducible & offline-capable. From messages.pot you can deterministically rebuild the
    entire 10-language pack, and after _build_tmt_cache.py the heavy lifting needs no network at
    all. Human polish is a first-class, cache-safe operation.
  6. CI-ready quality gate. _po_check.py's non-zero exit on any defect makes the pack's health
    machine-enforceable, and its OpenCC-aware verbatim detection avoids the false positives that would
    otherwise make such a gate unusable for CJK targets.
  7. Zero external PO tooling. A hand-rolled, fully controlled PO parser/writer (escaping, headers,
    plural slots, #: references) avoids dependency on polib/Babel behavioral quirks and gives
    byte-exact, POT-faithful output.

Result

  • 10 languages maintained and fully synchronized: de_DE, en_US, es_ES, fr_FR, ja_JP, ko_KR,
    pt_BR, ru_RU, tr_TR, zh_TW.
  • original/messages.pot: 898 source strings (~904 translatable fields incl. plurals), extracted
    from Typecho 1.3.0.
  • tools/_po_check.py final run: 10/10 OK, 100% completeness, 0 issues across all checks
    (completeness, placeholders, POT alignment, Chinese residual, stale, plural, HTML, verbatim copies).
  • Cache fully seeded from current .po files (≈8970 entries) → subsequent runs are offline and
    polish-safe.

Quick start

python -m venv .venv && .venv\Scripts\activate   # or: source .venv/bin/activate
pip install tencentcloud-sdk-python-common opencc-python-reimplemented

python tools/_i18n_analyze.py      # scan Typecho source → messages.pot
python tools/_tmt_translate.py     # machine-translate all languages
msgfmt original/projects/xx_XX.po -o src/langs/xx_XX.mo
python tools/_po_check.py          # 8-point validation (CI gate)

Polish loop (hand-edit a .po):

python tools/_build_tmt_cache.py   # re-seed cache so edits survive
msgfmt original/projects/xx_XX.po -o src/langs/xx_XX.mo
python tools/_po_check.py

1. Introduced techniques and mechanisms to automatically extract translatable multilingual text from the latest Typecho projects, thereby enhancing the efficiency of template updates;
2. Integrated automated translation technology based on Tencent Cloud TMT, enabling the rapid establishment of a multilingual foundation while supporting the overwriting of TMT cache data—following manual refinement—for use in subsequent updates;
3. Incorporated AI-driven automated workflow capabilities (Skills) to make multilingual processing smarter.
Copilot AI lite review requested due to automatic review settings September 6, 2026 00:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants