@dawn-studio/zh-convert

中文文档
0.5.0发布于 2026/10/24Apache-2.0TypeScriptES Module近 30 天下载 2118

Chinese script conversion for Dawn Studio apps: Simplified <-> Traditional (phrase-aware, idempotent), lazy-loaded, never-throw. Engines: tongwen-core + tongwen-dict (MIT, default) and OpenCC via opencc-js (opt-in, config-based: s2t/s2tw/s2hk/s2twp/t2s/…)

原始使用文档
文档目录8 节
  1. Install
  2. Quick start
  3. API
  4. OpenCC engine (0.5.0+) — config-based conversion
  5. Config table (upstream OpenCC names ↔ opencc-js locales)
  6. ConvertOptions — every knob, your call
  7. Notes
  8. License

@dawn-studio/zh-convert

Chinese script conversion for Dawn Studio apps: Simplified ⇄ Traditional (phrase-aware, idempotent), lazy-loaded, never-throw. Extracted from OpenMusic's battle-tested zh-convert + i18n/detect modules.

Default engine: tongwen-core + tongwen-dict (both MIT) — declared as real dependencies, installed automatically. Since 0.5.0 there is also an opt-in OpenCC engine (config-based, see below). This package adds the house layer:

  • Lazy, memoized dict loads via dynamic import() — the ~72 KB s2t / ~22 KB t2s tables stay out of the initial bundle, fetched at most once.
  • Synchronous handles (s2tConvertLineSync, t2sConvertLineSync) published once warm — no-flash first renders.
  • Idempotent s2t merge — tongwen's s2t phrase table is keyed on Simplified, so direct s2t of an already-Traditional 周杰倫 misses the phrase and per-char maps 杰 → 傑 (→ 周傑倫). The merge folds to Simplified first, then per code point keeps the source spelling wherever t2s changed the char.
  • Never-throw — any import/build/convert failure degrades to identity, never a throw.
  • Japanese guard — the kana/hangul-first classifier means Japanese lines (kana present) are never wrongly Traditionalized.

Install

npm i @dawn-studio/zh-convert

Quick start

import {
  lockScriptSync,   // force one line into a script — the entry point most apps want
  warmScript,       // await dict builds so the first render doesn't flash
  s2tConvertLines,  // batch Simplified → Traditional (position-aligned)
  t2sConvertLines,  // batch Traditional → Simplified
  isChineseLine,    // predicate: safe to convert offline?
  detectLang,       // pure classifier: en | zh-Hant | zh-Hans | ja | ko
  shouldTranslate,  // per-text-unit "should this be translated?" decision
} from '@dawn-studio/zh-convert';

// 1. Warm early (e.g. app boot) when the target is Traditional:
await warmScript('zh-Hant');

// 2. Force any upstream text into the user's script — Chinese only, everything
//    else passes through untouched, never throws, never returns null:
lockScriptSync('周杰伦', 'zh-Hant'); // → '周杰倫'
lockScriptSync('周杰倫', 'zh-Hant'); // → '周杰倫' (idempotent — source spelling kept)
lockScriptSync('こんにちは', 'zh-Hant'); // → 'こんにちは' (Japanese untouched)

Batch conversion (lyrics, search results):

await s2tConvertLines(['头发', '', '简体中文']); // → ['頭髮', '', '簡體中文']
await t2sConvertLines(['止戰之殤']);              // → ['止战之殇']

No-flash sync path for render loops — returns null until warm:

import { warmS2T, s2tConvertLineSync } from '@dawn-studio/zh-convert';
warmS2T(); // fire-and-forget at boot
// ... in render:
const text = s2tConvertLineSync(name) ?? name;

API

ExportDescription
lockScriptSync(text, target, opts?)Force one line into 'zh-Hant'/'zh-Hans'. Never throws, never null. Use this.
warmScript(target)Await the dict build(s) a lock target needs. Never rejects.
s2tConvertLines(lines, opts?)Batch s2t, position-aligned, blanks preserved.
t2sConvertLines(lines, opts?)Batch t2s, position-aligned. No-op on already-Simplified input.
s2tConvertLineSync(text)Sync s2t (idempotent merge). null until warm. Gate on isChineseLine first.
t2sConvertLineSync(text)Sync t2s. null until warm.
warmS2T() / warmT2S()Fire-and-forget warmers.
isChineseLine(text)true only for detected Chinese — kana/hangul lines return false.
detectLang(text)Pure classifier → 'en' | 'zh-Hant' | 'zh-Hans' | 'ja' | 'ko'. Kana/hangul win over Han; ambiguous Han defaults to 'zh-Hant'.
shouldTranslate(text, target, whitelist)Per-unit translation decision; Chinese targets always route through the converter (detection is unreliable on the small char-set).
convertWithConfig(text, config, opts?)OpenCC path. Async config-based conversion (per-line guard + protection). Never throws — identity on failure.
convertWithConfigSync(text, config, opts?)OpenCC path. Sync conversion once warm; null while cold. No guard — gate first.
warmConfig(config)OpenCC path. Await a config's lazy build. Never rejects.
isConfigWarm(config)OpenCC path. Whether a config's sync handle is ready.
OPENCC_CONFIGS / openccConfig(name)OpenCC path. The preset config table / lookup (unknown names fall back to s2t).

OpenCC engine (0.5.0+) — config-based conversion

Modelled on OpenCC's preset config system — the same config names as upstream OpenCC and the online converter (?config=s2t). Implemented with opencc-js (the pure-JavaScript port of OpenCC; dictionaries derived from OpenCC data, MIT AND Apache-2.0) — not the official opencc npm package, which is a native addon and cannot run in browsers or on the edge.

import { convertWithConfig, convertWithConfigSync, warmConfig, OPENCC_CONFIGS } from '@dawn-studio/zh-convert';

await convertWithConfig('鼠标', 's2twp');        // → '滑鼠' (Taiwan phrases)
await convertWithConfig('軟體', 'tw2sp');        // → '软件' (Mainland phrases)
await warmConfig('s2t');                          // warm the sync handle…
convertWithConfigSync('汉字', 's2t');             // → '漢字' (null while cold)

Or route the existing APIs through OpenCC with ConvertOptions:

await s2tConvertLines(['鼠标'], { engine: 'opencc', config: 's2twp' }); // → ['滑鼠']
lockScriptSync('汉字', 'zh-Hant', { engine: 'opencc' }); // sync handle — warmConfig('s2t') first; cold → identity

The default engine stays tongwen; OpenCC is strictly opt-in. The OpenCC path keeps every house guarantee: lazy per-config loading (dynamic import(), never in the initial bundle), never-throw identity fallback, the same Chinese-only guard, and PROTECTED_WORDS protection (占 never becomes 佔 on OpenCC paths either; 占有→佔有 still works).

Config table (upstream OpenCC names ↔ opencc-js locales)

Rough script-lock equivalents: zh-Hant ≈ s2t, zh-Hans ≈ t2s — the regional configs (s2tw, s2hk, …) additionally normalize regional variants/phrases, which the script lock deliberately does not do.

configfrom → tomeaning
s2tcn → tSimplified → Traditional (OpenCC standard)
t2st → cnTraditional (OpenCC standard) → Simplified
s2twcn → twSimplified → Traditional (Taiwan standard)
tw2stw → cnTraditional (Taiwan) → Simplified
s2hkcn → hkSimplified → Traditional (Hong Kong standard)
hk2shk → cnTraditional (Hong Kong) → Simplified
s2twpcn → twpSimplified → Traditional (Taiwan, with Taiwan phrases: 鼠标→滑鼠, 软件→軟體)
tw2sptwp → cnTraditional (Taiwan) → Simplified (with Mainland phrases: 軟體→软件)
t2twt → twTraditional (OpenCC standard) → Traditional (Taiwan)
tw2ttw → tTraditional (Taiwan) → Traditional (OpenCC standard)
t2hkt → hkTraditional (OpenCC standard) → Traditional (Hong Kong)
hk2thk → tTraditional (Hong Kong) → Traditional (OpenCC standard)
s2hkpcn → hkpSimplified → Traditional (Hong Kong, with Hong Kong phrases)
hk2sphkp → cnTraditional (Hong Kong) → Simplified (with Mainland phrases; upstream: still developing)

ConvertOptions — every knob, your call

lockScriptSync, s2tConvertLines and t2sConvertLines all accept an optional opts: ConvertOptions. Nothing is hardcoded that a reasonable app would want to change:

import { lockScriptSync, type ConvertOptions } from '@dawn-studio/zh-convert';

// The "Chinese only" rule is just the default guard — replace it freely:
lockScriptSync('こんにちは世界', 'zh-Hant');                                  // → unchanged (default guard)
lockScriptSync('简体', 'zh-Hant', { guard: () => true });                     // → '簡體' (allow all)
lockScriptSync('简体', 'zh-Hant', { guard: (t) => t !== '简体' });            // → '简体' (skip this line)

await s2tConvertLines(['简体', '头发'], { guard: isChineseLine });            // explicit default
optiontypedefaultnotes
guard(text: string) => booleanisChineseLineRejected lines pass through untouched. The default is the kana/hangul-first classifier — it already does the right thing for mixed-language text.
protectedWordsreadonly string[]—Extra words shielded from conversion, added to PROTECTED_WORDS.
engine'tongwen' | 'opencc''tongwen'Conversion engine. 'opencc' routes the async batch converters directly; lockScriptSync uses the warm OpenCC sync handle (cold → identity).
configOpenCCConfigName's2t' / 't2s' per entry pointOpenCC config name; only used when engine: 'opencc'.

Notes

  • Engines & attribution: the tongwen dictionaries are MIT (tongwen-core/tongwen-dict); the OpenCC dictionaries come from OpenCC data via opencc-js (opencc-js is MIT AND Apache-2.0; OpenCC data is Apache-2.0). This package itself is Apache-2.0.
  • s2t and t2s dicts are built separately — a t2s-only caller never downloads s2t's 148 KB phrase table.
  • Deep-imports tongwen-core/esm/* (not the top-level index): the index eval-time references the DOM global NodeFilter and throws where there is no DOM.
  • Known ceiling of the default guard: a Japanese line with zero kana (pure kanji) detects as Chinese and will be converted. Override guard if your corpus needs stricter screening.

License

Apache-2.0

版本与发布记录

@dawn-studio/zh-convert 共发布 11 个版本,首次发布于 2026/10/23;过去 12 个月发布了 11 个版本。

版本发布日期距今
0.5.0latest2026/10/2420 天前
0.4.02026/10/2421 天前
0.3.02026/10/2421 天前
0.2.22026/10/2322 天前
0.2.12026/10/2322 天前
0.2.02026/10/2322 天前

关于 @dawn-studio/zh-convert 的常见问题

@dawn-studio/zh-convert 是什么?

Chinese script conversion for Dawn Studio apps: Simplified <-> Traditional (phrase-aware, idempotent), lazy-loaded, never-throw. Engines: tongwen-core + tongwen-dict (MIT, default) and OpenCC via opencc-js (opt-in, config-based: s2t/s2tw/s2hk/s2twp/t2s/…)

如何安装 @dawn-studio/zh-convert?

在项目目录运行 npm install @dawn-studio/zh-convert,或使用 pnpm add / yarn add / bun add。

@dawn-studio/zh-convert 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@dawn-studio/zh-convert 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@dawn-studio/zh-convert 可以商用吗?

@dawn-studio/zh-convert 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@dawn-studio/zh-convert 的下载量有多少?

根据 npm 官方下载统计,@dawn-studio/zh-convert 在 2026/10/12 – 2026/11/10 这 30 天内被下载 2,118 次,日均约 71 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@dawn-studio/zh-convert 还在维护吗?

最新版本 0.5.0 发布于 2026/10/24(20 天前);至今共发布 11 个版本,其中过去 12 个月发布了 11 个;首次发布于 2026/10/23。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。