## 删除的死代码脚本
- scripts/i18n_class_c.py (脚本自承'新架构下永远输出 0',纯防御性)
- scripts/full_migrate_i18n.py (其产出违反 CLAUDE.md 1.2 中文 key 铁律,已标 DEPRECATED)
- scripts/__pycache__/ (Python 缓存)
## 删除的旧 per-page lang 文件
- lang/zh-cn/<page>/<file>.js (140 个)
- lang/en-us/<page>/<file>.js (139 个)
- 共 279 个文件,运行时不再加载,0 个 key 被 .vue 调用(经 diagnose_keys.py 验证)
## 删除的备份和迁移表
- lang/_backup/ (git 已跟踪原文件)
- lang/_migration_map.json / lang/_migration-map.json (运行时不用)
## 删除的中间产物 JSON
- docs/i18n-rename-*.json (smart_rename 中间产物,已用完)
- docs/i18n-ambiguous.json / i18n-audit.json / i18n-call-rewrite.log /
i18n-clean-dead.log / i18n-file-texts.json / i18n-page-titles.json /
i18n-vue-extract.json (前一会话残留,无引用方)
- docs/i18n-{naming,shadow,strict}-audit.json (脚本会自动重新生成)
## 删除的 modular lang 文件
- lang/zh-cn/*.js / lang/en-us/*.js (20 个模块文件,内容是上一会话产生的死代码)
- lang/zh-cn/index.js / lang/en-us/index.js (聚合入口)
- lang/DEPRECATED.md (已不再相关)
## 删除的不成熟脚本
- scripts/check_perpage.py (诊断用,已完成)
- scripts/diagnose_keys.py (诊断用,已完成)
- scripts/smart_rename_i18n.py (v1-v3 都 bug,未修完)
## 现状
- lang/ 现在只有 zh-cn.js + en-us.js (单文件,运行时实际加载)
- 这两个文件已被 smart_rename 部分修复: camelCase → snake_case (8 个 key)
- 命名违规从 8236 降到 3505 (剩余都在单文件,可独立 commit 修复)
- 1362 个 orphan () 调用仍存在,详见 docs/i18n-handoff.md
169 lines
6.0 KiB
Python
169 lines
6.0 KiB
Python
"""
|
|
i18n_build_zhcn.py - 用真实中文 (docs/i18n-real-zh.json) 重建 lang/zh-cn.js
|
|
|
|
- key -> 真实中文 (来自 f258987 .vue 硬编码)
|
|
- 保留现有 pageTitles (用户说 pageTitles 用现在值即可)
|
|
- en-us 端留空 stub 保持对称
|
|
"""
|
|
import re
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
from collections import defaultdict
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
EN_FILE = ROOT / 'lang' / 'en-us.js'
|
|
ZH_FILE = ROOT / 'lang' / 'zh-cn.js'
|
|
REAL_ZH = ROOT / 'docs' / 'i18n-real-zh.json'
|
|
EXISTING_ZH = ROOT / 'docs' / 'i18n-existing-zh.json'
|
|
|
|
|
|
def parse_existing_keys(path):
|
|
"""读现有 lang/<file>.js 的所有 key -> value 映射"""
|
|
if not path.exists():
|
|
return {}
|
|
text = path.read_bytes().decode('utf-8', errors='ignore')
|
|
out = {}
|
|
indent_stack = [(-1, '')]
|
|
for ln in text.split('\n'):
|
|
if ln.strip().startswith('//') or not ln.strip(): continue
|
|
indent = len(ln) - len(ln.lstrip())
|
|
m = re.match(r"""^['\"]?([A-Za-z_][A-Za-z0-9_.]*)['\"]?\s*:\s*(.*)$""", ln.lstrip())
|
|
if not m: continue
|
|
key, rest = m.group(1), m.group(2).strip().rstrip(',')
|
|
while len(indent_stack) > 1 and indent_stack[-1][0] >= indent: indent_stack.pop()
|
|
parent = indent_stack[-1][1]
|
|
full_key = f'{parent}.{key}' if parent else key
|
|
if rest.endswith('{'): indent_stack.append((indent, full_key)); continue
|
|
v = rest
|
|
if (v.startswith('"') and v.endswith('"')) or (v.startswith("'") and v.endswith("'")):
|
|
if len(v) >= 2: v = v[1:-1]
|
|
out[full_key] = v
|
|
return out
|
|
|
|
|
|
def write_lang(path, d, locale):
|
|
"""写带双引号 key 的 lang/<locale>.js, UTF-8"""
|
|
grouped = defaultdict(dict)
|
|
top_level = {}
|
|
for k, v in d.items():
|
|
parts = k.split('.')
|
|
if len(parts) == 1:
|
|
top_level[k] = v
|
|
else:
|
|
ns = parts[0]
|
|
sub = '.'.join(parts[1:])
|
|
grouped[ns][sub] = v
|
|
|
|
lines = [
|
|
f'// Auto-generated by i18n_build_zhcn.py',
|
|
f'// Single authoritative lang source. Do not edit manually.',
|
|
f'// locale: {locale}',
|
|
f'// Generated: 2026-06-23',
|
|
'',
|
|
'export const lang = {',
|
|
]
|
|
written = set()
|
|
nested_ns = {'pageTitles', 'common', 'tabBar', 'customer'}
|
|
for ns in sorted(set(grouped.keys()) | set(top_level.keys())):
|
|
if ns in nested_ns and ns in grouped:
|
|
lines.append(f'\t"{ns}": {{')
|
|
for sub_k in sorted(grouped[ns].keys()):
|
|
sub_v = grouped[ns][sub_k]
|
|
escaped = sub_v.replace('\\', '\\\\').replace('"', '\\"')
|
|
lines.append(f'\t\t"{sub_k}": "{escaped}",')
|
|
lines.append('\t},')
|
|
written.add(ns)
|
|
for ns in sorted(grouped.keys()):
|
|
if ns in written: continue
|
|
lines.append(f'\t"{ns}": {{')
|
|
for sub_k in sorted(grouped[ns].keys()):
|
|
sub_v = grouped[ns][sub_k]
|
|
escaped = sub_v.replace('\\', '\\\\').replace('"', '\\"')
|
|
lines.append(f'\t\t"{sub_k}": "{escaped}",')
|
|
lines.append('\t},')
|
|
written.add(ns)
|
|
for k in sorted(top_level.keys()):
|
|
if k in written: continue
|
|
v = top_level[k]
|
|
escaped = v.replace('\\', '\\\\').replace('"', '\\"')
|
|
lines.append(f'\t"{k}": "{escaped}",')
|
|
|
|
lines.append('};')
|
|
lines.append('')
|
|
content = '\n'.join(lines)
|
|
path.write_bytes(content.encode('utf-8'))
|
|
|
|
|
|
def main():
|
|
# 1. 真实中文 (从 f258987 .vue 硬编码提取)
|
|
real_zh = json.loads(REAL_ZH.read_text(encoding='utf-8'))
|
|
print(f'真实中文: {len(real_zh)} 条')
|
|
|
|
# 2. 现有 pageTitles 保留 (用户要求)
|
|
# 解析当前 zh-cn.js, 取 pageTitles 部分
|
|
current_zh = parse_existing_keys(ZH_FILE)
|
|
page_titles = {k: v for k, v in current_zh.items() if k.startswith('pageTitles.')}
|
|
print(f'pageTitles 保留: {len(page_titles)} 条')
|
|
|
|
# 3. 现有 en-us 端英文 (用作 en-us 基础, 保留已有的英文翻译)
|
|
current_en = parse_existing_keys(EN_FILE)
|
|
print(f'现有 en-us keys: {len(current_en)}')
|
|
|
|
# 4. 构造新的 zh-cn
|
|
new_zh = {}
|
|
# 先 pageTitles
|
|
for k, v in page_titles.items():
|
|
new_zh[k] = v
|
|
# 再真实中文 (新 key) - 来自 f258987 硬编码
|
|
for text, new_key in real_zh.items():
|
|
new_zh[new_key] = text
|
|
# 再补充: f258987 时 .vue 已有的 $lang 调用 (key 直接用原始 key)
|
|
if EXISTING_ZH.exists():
|
|
existing = json.loads(EXISTING_ZH.read_text(encoding='utf-8'))
|
|
for k, v in existing.items():
|
|
if k not in new_zh:
|
|
new_zh[k] = v
|
|
print(f'已补 {len(existing)} 个 f258987 已有的 $lang 值')
|
|
# 保留现有 zh-cn 里非 pageTitles 的 key
|
|
for k, v in current_zh.items():
|
|
if k.startswith('pageTitles.'): continue
|
|
if k not in new_zh:
|
|
new_zh[k] = v
|
|
print(f'new zh-cn keys: {len(new_zh)}')
|
|
|
|
# 5. 构造新的 en-us: 保留已有, 缺中文对应的留空
|
|
new_en = {}
|
|
for k, v in current_en.items():
|
|
if k.startswith('pageTitles.'): continue
|
|
new_en[k] = v
|
|
# 真实中文对应的 key, 英文侧: 优先保留已有, 缺则空
|
|
for new_key in real_zh.values():
|
|
if new_key not in new_en:
|
|
new_en[new_key] = ''
|
|
# 已有 $lang 对应的 key 留空 (中文侧已有, en 端缺)
|
|
if EXISTING_ZH.exists():
|
|
existing = json.loads(EXISTING_ZH.read_text(encoding='utf-8'))
|
|
for k in existing.keys():
|
|
if k not in new_en:
|
|
new_en[k] = ''
|
|
# pageTitles 保留
|
|
for k, v in page_titles.items():
|
|
new_en[k] = v
|
|
print(f'new en-us keys: {len(new_en)}')
|
|
|
|
# 6. 写
|
|
apply = '--apply' in sys.argv
|
|
if not apply:
|
|
print('\n[DRY-RUN] 加 --apply 真写文件')
|
|
return 0
|
|
|
|
write_lang(ZH_FILE, new_zh, 'zh-cn')
|
|
print(f'[OK] {ZH_FILE.relative_to(ROOT)}: {len(new_zh)} keys')
|
|
write_lang(EN_FILE, new_en, 'en-us')
|
|
print(f'[OK] {EN_FILE.relative_to(ROOT)}: {len(new_en)} keys')
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main()) |