Skip to content

fix(i18n): Correct zh-TW translations to match Traditional Chinese conventions#4129

Merged
tanzhenxin merged 6 commits into
QwenLM:mainfrom
MikeWang0316tw:feat/fix-zh-tw-translations
May 15, 2026
Merged

fix(i18n): Correct zh-TW translations to match Traditional Chinese conventions#4129
tanzhenxin merged 6 commits into
QwenLM:mainfrom
MikeWang0316tw:feat/fix-zh-tw-translations

Conversation

@MikeWang0316tw

Copy link
Copy Markdown
Contributor

This PR fixes ~131 lines of Traditional Chinese (zh-TW) translations in packages/cli/src/i18n/locales/zh-TW.js that used Simplified Chinese character forms instead of standard Traditional Chinese usage.

Changes

The following categories of corrections were applied:

Category Count Example
文件檔案 47 "指定文件" → "指定檔案"
45 "作為" → "作為"
44 "啟動" → "啟動"
曆史歷史 6 "聊天曆史" → "聊天歷史"
鏈接連結 4 "鏈接擴展" → "連結擴展"
菜單選單 3 "命令菜單" → "命令選單"

Rationale

These corrections align the zh-TW locale with standard Traditional Chinese usage:

  • 檔案 vs 文件: In Traditional Chinese usage, "檔案" is the preferred term for "file" in computing contexts (e.g., 檔案路徑, 檔案編輯), while "文件" is more commonly used for "document" (e.g., 公文文件).
  • 啟動 vs 啓動: The character "啟" is the standard form in Traditional Chinese; "啓" is an outdated variant.
  • 歷史 vs 曆史: "歷史" (history) is correct; "曆史" (calendar dates) is a different word entirely.
  • 連結 vs 鏈接: "連結" is the standard term in Traditional Chinese for "link"; "鏈接" is a Simplified Chinese variant.
  • 選單 vs 菜單: "選單" is the standard term in Traditional Chinese for UI menus; "菜單" typically refers to restaurant menus.

Testing

  • Manual testing via npm run dev to verify UI displays correctly with updated translations.
  • No code logic changes; purely translation string updates.

…nventions

Fix ~131 lines of Traditional Chinese (zh-TW) translations that used
Simplified Chinese character forms instead of standard Traditional
Chinese usage.

Changes:
- 文件 → 檔案 (47 occurrences)
- 爲 → 為 (45 occurrences)
- 啓 → 啟 (44 occurrences)
- 曆史 → 歷史 (6 occurrences)
- 鏈接 → 連結 (4 occurrences)
- 菜單 → 選單 (3 occurrences)
wenshao
wenshao previously approved these changes May 14, 2026

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM! ✅ The 6 pattern corrections (文件→檔案, 爲→為, 啓→啟, 曆史→歷史, 鏈接→連結, 菜單→選單) are applied correctly and consistently across all ~131 affected entries.

One minor note for a future pass: 服務器 (mainland usage) is used in ~15 unchanged lines while 伺服器 (Taiwan standard for server) appears only once. This pattern was not part of the current fix, but could be addressed in a follow-up to further align with Traditional Chinese conventions.

— DeepSeek/deepseek-v4-pro via Qwen Code /review

Align with Traditional Chinese convention where 伺服器 is the standard
term for 'server' in computing contexts.

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggestions for future-proofing (non-blocking, all translations correct ✅):

[1] Misleading "Auto-generated" header comment (line 7-8 of zh-TW.js): The file header says "Auto-generated: structure from en.js, values from zh-TW (manual) or opencc(zh.js s2t)" — but no generation script exists in the repo, and OpenCC's standard s2t cannot handle Taiwan-specific vocabulary (檔案 vs 文件, 伺服器 vs 服務器, 選單 vs 菜單). A future maintainer seeing this comment might regenerate the entire file with raw OpenCC output, silently reverting all the manual corrections in this PR. Consider updating to:

// Traditional Chinese (zh-TW) translations for Qwen Code CLI
// Bootstrapped from en.js structure with opencc(zh.js s2t),
// then extensively hand-corrected for Taiwan vocabulary conventions.
// This file is the authoritative source — do not overwrite with auto-generated output.

[2] Regression testing — No tests detect Simplified Chinese characters creeping back into zh-TW.js. Existing tests only verify key existence, not character-level correctness. Consider adding a CI check (e.g., a grep on known Simplified-only codepoints) to prevent the very regression this PR is fixing from recurring.

[3] New-key workflow for zh-TWstrictParity: true in languages.ts means CI blocks merges when new English keys lack zh-TW translations. Non-Chinese-speaking contributors may naturally reach for OpenCC s2t from zh.js — but standard OpenCC doesn't handle Taiwan-specific vocabulary. Consider documenting zh-TW-specific vocabulary requirements in the i18n maintenance docs to prevent terminology drift over time.

— DeepSeek/deepseek-v4-pro via Qwen Code /review

…rite

Clarify that the file is the authoritative source and should not be
overwritten with auto-generated output, to prevent future maintainers
from regenerating with raw OpenCC and losing manual corrections.
@MikeWang0316tw

Copy link
Copy Markdown
Contributor Author

Thanks for the review and suggestions, @wenshao!
[2] & [3] make sense — will work on them, but they'll take a bit more time.

Addresses reviewer feedback on PR QwenLM#4129 (points 2 and 3):

- scripts/check-i18n.ts: Iterate over parsed zh-TW translation values
  (not raw file content) and report the offending key. Replace the
  earlier substring list with ZH_TW_FORBIDDEN_PATTERNS, which targets
  the three real regression categories: variant Traditional characters
  produced by OpenCC s2t (爲, 啓), Mainland-Chinese vocabulary (服務器,
  菜單, 鏈接), and pure Simplified characters. Excludes 禁用 / 配置 /
  文件 / 打開 to avoid false positives on Taiwan-valid usage.
- scripts/tests/check-i18n.test.ts: Cover the new check, including
  negative cases for Taiwan-valid vocabulary.
- docs/users/features/language.md: Document zh-TW maintenance — the
  vocabulary table, why raw OpenCC s2t output is not acceptable, and
  where the CI-enforced list lives.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
Comment thread docs/users/features/language.md Outdated
| 菜單 / 菜单 | 選單 | Taiwan term for "menu" |
| 鏈接 / 链接 | 連結 | Taiwan term for "link" |
| 打開 | 開啟 | Taiwan-preferred verb for "open" (UI) |
| 爲 / 啓 / 曆 / 鏈接 | 為 / 啟 / 歷 / 連結 | Variant Traditional forms from raw OpenCC s2t — Taiwan uses the right-hand forms |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] The table row 爲 / 啓 / 曆 / 鏈接 → 為 / 啟 / 歷 / 連結 presents as a variant character that should be unconditionally replaced with . However, is context-dependent: it is correct in calendar terms (日曆, 農曆, 西曆), while is used for "history" (歷史, 經歷).

The code (ZH_TW_FORBIDDEN_PATTERNS) correctly only flags the bigram 曆史, not standalone . But a future maintainer reading this table could globally replace all with , corrupting correct calendar translations.

Suggested change
| 爲 / 啓 / / 鏈接 | 為 / 啟 / / 連結 | Variant Traditional forms from raw OpenCC s2t — Taiwan uses the right-hand forms |
| 爲 / 啓 / 歷史 / 鏈接 | 為 / 啟 / 歷史 / 連結 | Variant Traditional forms from raw OpenCC s2t — Taiwan uses the right-hand forms. 註:「歷」vs「曆」為上下文相關(日曆 用「曆」);CI 僅檢查 `曆史` 雙字詞。|

— DeepSeek/deepseek-v4-pro via Qwen Code /review

Comment thread scripts/check-i18n.ts
@@ -433,6 +507,19 @@ export async function checkI18n(
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] The CI check has no escape hatch or allowlist mechanism. findForbiddenZhTwPatterns() returns findings that are unconditionally pushed as blocking errors. If a legitimate translation value needs to contain a forbidden substring (e.g., as part of a system name, or in a quoted phrase), CI will permanently fail until someone modifies the script itself.

This is the most likely production-breaking scenario: a contributor adds an innocent translation → CI blocks all merges → oncall must reverse-engineer check-i18n under time pressure.

Suggested change
}
// Add an allowlist before the for loop:
const ZH_TW_ALLOWED_EXCEPTIONS: ReadonlySet<string> = new Set([
// Add keys here that legitimately need forbidden substrings
]);
// Inside checkI18n, before iterating findings:
for (const { key, pattern, preferred } of findForbiddenZhTwPatterns(zhTWTranslations)) {
if (ZH_TW_ALLOWED_EXCEPTIONS.has(key)) continue;
errors.push(...);
}

— DeepSeek/deepseek-v4-pro via Qwen Code /review

Comment thread scripts/check-i18n.ts
}
}

// Check zh-TW.js for Taiwan-vocabulary regressions (raw OpenCC output,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] The zh-TW check is silently skipped if zh-TW is not in SUPPORTED_LANGUAGES or if loadTranslationsFile fails for zh-TW.js. There is no warning that the vocabulary protection is deactivated. This hidden coupling means a future refactor of SUPPORTED_LANGUAGES could unwittingly disable the check for months.

Suggested change
// Check zh-TW.js for Taiwan-vocabulary regressions (raw OpenCC output,
if (zhTWTranslations) {
// ... existing check ...
} else if (supportedLanguages.some(l => l.code === 'zh-TW')) {
errors.push(
'zh-TW locale is configured but failed to load; Taiwan vocabulary check is NOT active',
);
}

— DeepSeek/deepseek-v4-pro via Qwen Code /review

Comment thread scripts/check-i18n.ts
* lets us report the offending key, and avoids matching characters inside
* file-level comments or JS syntax.
*/
export function findForbiddenZhTwPatterns(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] Hierarchical patterns cause duplicate CI errors: 历史 (Simplified) matches both { pattern: '历史' } (vocabulary-level) and { pattern: '历' } (character-level). Same for 链接 matching 链接 and . This produces noisy CI output that may confuse developers about what exactly to fix.

Suggested change
export function findForbiddenZhTwPatterns(
// After matching a pattern, break to avoid redundant matches:
for (const { pattern, preferred } of ZH_TW_FORBIDDEN_PATTERNS) {
if (candidate.includes(pattern)) {
findings.push({ key, pattern, preferred });
break; // Stop after first (longest/most specific) match
}
}

Alternatively, order ZH_TW_FORBIDDEN_PATTERNS with longer (more specific) patterns first, then break on first match.

— DeepSeek/deepseek-v4-pro via Qwen Code /review

Comment thread docs/users/features/language.md Outdated

| Avoid | Use instead | Reason |
| ------------------- | ------------------- | -------------------------------------------------------------------------------- |
| 文件 (file) | 檔案 | Taiwan term for filesystem files |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] The conventions table says "CI enforces a subset of them automatically" but doesn't indicate which rows are CI-enforced vs style suggestions. 文件 and 打開 are listed as "Avoid" but are deliberately excluded from CI (code comments explain why). Contributors may waste time investigating why their 打開 usage passes CI, or reviewers may block PRs for 文件 that CI intentionally allows.

Suggested change
| 文件 (file) | 檔案 | Taiwan term for filesystem files |
| Avoid | Use instead | CI enforced? | Reason |
| ------------------- | ------------------- | ------------ | ------ |
| 文件 (file) | 檔案 | No ||

Or add a footnote column marking non-enforced rows with an asterisk.

— DeepSeek/deepseek-v4-pro via Qwen Code /review

- check-i18n.ts: Sort ZH_TW_FORBIDDEN_PATTERNS longest-first and break
  on first match so e.g. `历史` reports the specific bigram instead of
  also firing the bare `历` rule (no duplicate CI errors).
- check-i18n.ts: Add ZH_TW_ALLOWED_EXCEPTIONS escape hatch so a future
  legitimate translation (e.g. 區塊鏈 in a UI string) can opt out by key
  without weakening the global pattern list.
- docs/users/features/language.md: Add a "CI enforced?" column so
  contributors can tell which rows block CI vs. which are review-only
  style guidance. Replace bare `曆` in the table with the `曆史` bigram
  and note that `曆` is correct in calendar terms (日曆, 農曆, 西曆) —
  prevents a future maintainer from globally replacing 曆→歷.
- Tests: Cover the dedup behavior on overlapping patterns.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
wenshao
wenshao previously approved these changes May 14, 2026
Comment thread scripts/check-i18n.ts
if (ZH_TW_ALLOWED_EXCEPTIONS.has(key)) continue;
const candidates = Array.isArray(value) ? value : [value];
for (const candidate of candidates) {
if (typeof candidate !== 'string') continue;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] includes() matching can produce false positives for bigram forbidden patterns across Chinese word boundaries. For example, 區塊鏈接口 (blockchain interface — legitimate Taiwan Chinese) contains + adjacently, which triggers the 鏈接 pattern. The code already acknowledges this limitation for single characters (bare is deliberately excluded because it's valid in 區塊鏈), but bigram patterns like 鏈接 and 服務器 have the same vulnerability. Consider documenting this known limitation in the ZH_TW_FORBIDDEN_PATTERNS_RAW JSDoc or in language.md so contributors can quickly recognize it when they encounter a surprising CI failure.

Suggested change
if (typeof candidate !== 'string') continue;
// Note: includes() matching does not respect Chinese word boundaries.
// Bigram patterns like 鏈接 may match across word boundaries
// (e.g. 區塊鏈接口 = 區塊鏈 + 接口), causing false positives.
// Affected keys should be added to ZH_TW_ALLOWED_EXCEPTIONS.

— DeepSeek/deepseek-v4-pro via Qwen Code /review

Document the known limitation that `includes()`-based pattern matching
does not respect Chinese word boundaries — a bigram like `鏈接` will
false-positive on `區塊鏈接口` (區塊鏈 + 接口). Direct contributors to
`ZH_TW_ALLOWED_EXCEPTIONS` when this happens instead of weakening the
pattern list.

Co-Authored-By: Claude Opus 4.7 <[email protected]>

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No issues found. LGTM! ✅

— DeepSeek/deepseek-v4-pro via Qwen Code /review

@tanzhenxin tanzhenxin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved. The zh-TW locale changes are scoped to translation strings, and the added i18n guard checks parsed translation values so it can report offending keys without matching comments or syntax. The focused script test and full i18n check both pass locally.

@tanzhenxin
tanzhenxin merged commit 02a65f9 into QwenLM:main May 15, 2026
10 checks passed
TaimoorSiddiquiOfficial pushed a commit to TaimoorSiddiquiOfficial/HopCode that referenced this pull request May 15, 2026
…nventions (QwenLM#4129)

* fix(i18n): Correct zh-TW translations to match Traditional Chinese conventions

Fix ~131 lines of Traditional Chinese (zh-TW) translations that used
Simplified Chinese character forms instead of standard Traditional
Chinese usage.

Changes:
- 文件 → 檔案 (47 occurrences)
- 爲 → 為 (45 occurrences)
- 啓 → 啟 (44 occurrences)
- 曆史 → 歷史 (6 occurrences)
- 鏈接 → 連結 (4 occurrences)
- 菜單 → 選單 (3 occurrences)

* fix(i18n): Replace 服務器 with 伺服器 (15 occurrences)

Align with Traditional Chinese convention where 伺服器 is the standard
term for 'server' in computing contexts.

* fix(i18n): Update zh-TW.js header comment to prevent accidental overwrite

Clarify that the file is the authoritative source and should not be
overwritten with auto-generated output, to prevent future maintainers
from regenerating with raw OpenCC and losing manual corrections.

* fix(i18n): Add zh-TW regression check and maintenance docs

Addresses reviewer feedback on PR QwenLM#4129 (points 2 and 3):

- scripts/check-i18n.ts: Iterate over parsed zh-TW translation values
  (not raw file content) and report the offending key. Replace the
  earlier substring list with ZH_TW_FORBIDDEN_PATTERNS, which targets
  the three real regression categories: variant Traditional characters
  produced by OpenCC s2t (爲, 啓), Mainland-Chinese vocabulary (服務器,
  菜單, 鏈接), and pure Simplified characters. Excludes 禁用 / 配置 /
  文件 / 打開 to avoid false positives on Taiwan-valid usage.
- scripts/tests/check-i18n.test.ts: Cover the new check, including
  negative cases for Taiwan-valid vocabulary.
- docs/users/features/language.md: Document zh-TW maintenance — the
  vocabulary table, why raw OpenCC s2t output is not acceptable, and
  where the CI-enforced list lives.

Co-Authored-By: Claude Opus 4.7 <[email protected]>

* fix(i18n): Address review feedback on zh-TW check (QwenLM#4129)

- check-i18n.ts: Sort ZH_TW_FORBIDDEN_PATTERNS longest-first and break
  on first match so e.g. `历史` reports the specific bigram instead of
  also firing the bare `历` rule (no duplicate CI errors).
- check-i18n.ts: Add ZH_TW_ALLOWED_EXCEPTIONS escape hatch so a future
  legitimate translation (e.g. 區塊鏈 in a UI string) can opt out by key
  without weakening the global pattern list.
- docs/users/features/language.md: Add a "CI enforced?" column so
  contributors can tell which rows block CI vs. which are review-only
  style guidance. Replace bare `曆` in the table with the `曆史` bigram
  and note that `曆` is correct in calendar terms (日曆, 農曆, 西曆) —
  prevents a future maintainer from globally replacing 曆→歷.
- Tests: Cover the dedup behavior on overlapping patterns.

Co-Authored-By: Claude Opus 4.7 <[email protected]>

* docs(i18n): Note word-boundary limitation of zh-TW substring check

Document the known limitation that `includes()`-based pattern matching
does not respect Chinese word boundaries — a bigram like `鏈接` will
false-positive on `區塊鏈接口` (區塊鏈 + 接口). Direct contributors to
`ZH_TW_ALLOWED_EXCEPTIONS` when this happens instead of weakening the
pattern list.

Co-Authored-By: Claude Opus 4.7 <[email protected]>

---------

Co-authored-by: Claude Opus 4.7 <[email protected]>
(cherry picked from commit 02a65f9)
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.

3 participants