---
name: chinese-traditional-conversion
description: Convert simplified Chinese to Traditional Chinese (zh-TW), covering characters, Taiwan vocabulary, and phrasing. Use when the user asks to convert 簡體 to 正體/繁體, localise mainland Chinese text for Taiwan, fix simplified characters left in a document or repository, check whether content is written in proper zh-TW, or normalise variant forms such as 着/裏/爲 to the Taiwan standard. Handles both files edited in place and text pasted into the conversation. Runs on Python 3 standard library alone, with no OpenCC and no installed packages.
license: GFDL-1.3-or-later
metadata:
  author: Jim@ChenJ.im
  code-license: GPL-3.0-or-later
  code-license-covers: scripts/
  data-sources:
    - name: Unicode Character Database (Unihan) 17.0.0
      license: Unicode-3.0
      license-name: UNICODE LICENSE V3
      license-url: https://www.unicode.org/license.txt
      source-url: https://www.unicode.org/Public/UCD/latest/ucd/Unihan.zip
      covers: assets/char_map.json, assets/ambiguous_chars.json
      notice: "© 1991-2026 Unicode, Inc. All rights reserved."
    - name: 國家教育研究院 兩岸對照名詞
      license: OGDL-Taiwan-1.0
      license-name: 政府資料開放授權條款-第1版
      license-url: https://data.gov.tw/license
      source-url: https://opendata.naer.edu.tw/
      catalogue-url: https://data.gov.tw/
      covers: assets/naer_terms.json
      notice: "資料來源：國家教育研究院，依政府資料開放授權條款第1版釋出。"
  referenced-services:
    - name: 樂詞網（雙語詞彙、學術名詞暨辭書資訊網）
      url: https://terms.naer.edu.tw/
      role: >-
        Queried at runtime as the arbiter for terminology the bundled tables do not
        settle. No content is bundled or redistributed; the site publishes no open
        licence and offers no API.
      notice: "Copyright © 國家教育研究院 版權所有"
---

# Convert to Traditional Chinese

Convert simplified Chinese into Traditional Chinese as written in Taiwan. The script settles
what is deterministic and reports what is not; deciding the reported items is your job.

**Never let the script guess and never guess yourself.** A character like `发` is `發` or `髮`
depending on the sentence around it. Tools that pick one silently are why this skill exists.

## Division of labour

`scripts/convert.py` owns the mechanical half: 6300+ deterministic character mappings, Taiwan
variant folding (`着`→`著`, `裏`→`裡`, `爲`→`為`), unambiguous vocabulary, and exhaustive
detection. It is exhaustive where you are not — it will not miss the one stray `发` on line 400.

You own the contextual half: every ambiguous character, every vocabulary item with more than one
Taiwan reading, and the phrasing that no dictionary encodes. You have the whole document; the
script has a table.

## Workflow

### 1. Establish the input

Files or directories go through the path form. Text pasted into the conversation goes through
`--stdin`. Convert files in place so the user reviews a `git diff`; never paste a converted copy
of a file back into the chat.

### 2. Run the mechanical pass

```bash
scripts/convert.py PATH...          # convert in place, print the review report
scripts/convert.py --check PATH...  # report only, write nothing
scripts/convert.py --diff PATH...   # unified diff, write nothing
scripts/convert.py --json PATH...   # structured findings, for triage without parsing prose
echo "文字" | scripts/convert.py --stdin   # result on stdout, report on stderr
```

On a directory of any size, run `--check` first. It tells you the scale of the job and which
files are affected before anything is written.

Add `--markdown` to apply markdown protection rules to `--stdin` input. Add `--include-code` to
convert protected regions too, which is almost never right.

Add `--no-naer` to suppress the terminology advisories described below, or `--naer-all` to
widen them to text that was already traditional.

Exit codes: `0` clean, `1` review items only, `2` must-fix items present, `3` usage error.
NAER advisories never change the exit code.
The script checks its own prerequisites and explains failures on stderr, so do not pre-verify
anything.

### 3. Resolve the report

Three sections, three different obligations.

**MUST FIX** — a simplified character no substitution resolves on its own. The document is not
zh-TW until every one is decided. Read the rule the report prints, look at the sentence, edit
the file.

**REVIEW** — the script applied a form that is valid zh-TW, which may still be the wrong one
here. `面` stays `面` unless it is `麵條`. Confirm or correct each one.

Occurrences a curated keep-rule already answers are detected but collapsed into a `✓` summary
line, because a report that lists `同` four hundred times cannot honestly be worked through. The
detection behind them is unchanged; `--review-all` lists them individually.

**PROTECTED** — simplified text inside code blocks, inline code, or URLs. Left untouched on
purpose. A simplified string literal is often deliberate. Change these only when the user asks.

**NAER** — 國家教育研究院 has ruled on this term and its Taiwan rendering differs from what the
text uses. The advisory prints the official rendering, the English headword, and the subject
domain. Nothing was substituted, and nothing has to be: this corpus records the *preferred*
academic rendering, not a correction. 除臭 is not wrong because a glossary prefers 去臭. Apply
one when the domain matches the document and the rendering is clearly better — `纳米` really
should be `奈米` — and ignore it otherwise.

Advisories are raised only where the span actually held a character that cannot stand in zh-TW.
These glossaries are specialist and their mainland column collides constantly with ordinary
words: the electrical-engineering set lists `日期` as `data`, a typo for `数据`, which would
otherwise flag every date in a document. Text already written in Traditional needs no
localising. This is narrower than the other tiers on purpose — vocabulary that matters for
ordinary writing lives in `term_map.json` and is converted regardless of how it is spelt, so
`軟件` still becomes `軟體` even with no simplified character in sight. Pass `--naer-all` when
reviewing Hong Kong or mainland material that is already written in Traditional.

An entry with no rule printed is one the character table has not been curated for yet. Decide it
from context as usual, and prefer the form used elsewhere in the same document.

**樂詞網 settles what the report cannot.** For a terminology question the tables do not answer,
query <https://terms.naer.edu.tw/search/?q=TERM> with WebFetch — it is the authority for Taiwan
academic and technical vocabulary. It is HTML with no API, so read the page rather than
expecting JSON. It does not rule on character-level ambiguity: whether `发` is `發` or `髮` is a
question for 教育部《重編國語辭典修訂本》, not for a terminology database.

### 4. Localise the phrasing

Character and vocabulary conversion produces correct but foreign-sounding Chinese. Read
`references/taiwan-vocabulary.md` and apply the sentence-level adjustments: `通过` as a
preposition becomes `透過` or `藉由`, `对…进行…` constructions unwind into direct verbs.

### 5. Verify

Re-run `--check`. Zero must-fix items is the gate. Review items may legitimately remain when you
have consciously judged each one, so exit `1` is an acceptable end state and exit `2` is not.

### 6. Report to the user

Say what was converted, which items you judged and why, and what you deliberately left alone.
Name any proper noun you were unsure about.

## Rules

- **The document's own usage wins.** Before accepting a vocabulary change, check what the author
  already does: `grep -c 函式 FILE` against `grep -c 函數 FILE`. A document that consistently says
  `基類` has a convention, and imposing `基底類別` on it is rewriting, not converting.
- **Conversion is not rewriting.** Preserve the author's voice, structure, and argument. Do not
  apply writing-style guidance. When the user wants the prose improved as well, that is
  `chinese-content-writing-guideline`, invoked separately and said out loud.
- **Proper nouns are reported, never converted.** `沈` is the surname 沈 or the city 瀋陽; `姜`
  is the surname 姜 or the spice 薑. The report flags these with a proper-noun warning. If the
  text does not settle it, ask the user.
- **Code, URLs, filenames, and identifiers stay simplified.** Converting a dictionary key or a
  path breaks the program.
- **Quoted material gets characters only.** Statutes, judgments, and other people's words take
  the character conversion and nothing else. Tell the user you limited it.
- **Every decision needs a reason you can state.** If you cannot state one, leave it and flag it
  for the user rather than guessing.

## Data tables

`assets/` holds three tables the script reads. They are data, not reading material — do not load
them into context.

| File | Contents |
| --- | --- |
| `char_map.json` | Deterministic character mappings, derived from Unihan plus the Taiwan folding layer |
| `ambiguous_chars.json` | Characters needing context, with the rule for each |
| `term_map.json` | Mainland-to-Taiwan vocabulary, split into unconditional and context-dependent |
| `naer_terms.json` | 29k 兩岸對照名詞 from 國家教育研究院. Advisory only — reported, never substituted |

`scripts/build_tables.py` regenerates the character tables from the Unicode Unihan database. It
is maintenance tooling, needs network access, and is never part of a conversion. Run it only to
refresh the tables against a new Unicode release; it preserves every human decision already
recorded. `--lint-terms` checks `term_map.json` for entries that contradict each other.

`--naer` rebuilds `naer_terms.json` from 國家教育研究院's cross-strait terminology on
data.gov.tw. It verifies each dataset carries licence `1` (政府資料開放授權條款第1版) before
using it, and drops any mainland form that is also a Taiwan rendering elsewhere in the corpus,
because no table can tell `訪問` the noun from `訪問` the verb. Add `--naer-rescan` to
re-discover the dataset list. A download that fails aborts the rebuild and leaves the existing
table in place, so a flaky connection cannot quietly ship a shrunken one; `--naer-allow-partial`
overrides that and records `partial: true` in the file.

Conversion itself never touches the network. Only `build_tables.py` does, and it treats the
upstreams as someone else's infrastructure: responses are revalidated against a disk cache in
`~/.cache/chinese-traditional-conversion`, so a rebuild that changes nothing transfers nothing
instead of re-fetching 15 MB; retries back off rather than hammering a struggling host, and a
permanent answer such as 404 is not retried at all. The dataset scan runs at four concurrent
requests and its result is cached, so it should rarely need re-running.

資料來源：國家教育研究院，依政府資料開放授權條款第1版釋出。
