## 删除的死代码脚本
- 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
167 lines
5.8 KiB
Python
167 lines
5.8 KiB
Python
"""
|
||
i18n_rewrite_calls.py - 用 migration-map 自动改所有 $lang 调用
|
||
|
||
扫描所有 .vue / .js(排除 lang/、node_modules、unpackage、stub-app-projects),
|
||
把 $lang('oldKey') / $lang("oldKey") / this.$lang('oldKey') 替换为 $lang('newKey')。
|
||
|
||
输出:
|
||
- docs/i18n-call-rewrite.log(每个改写点)
|
||
- docs/i18n-ambiguous.json(同名 oldKey 映射到多个 newKey 的位置,待人工 review)
|
||
|
||
注意:本脚本只做"机械替换",不会改 CSS / 模板结构 / 业务文案。
|
||
"""
|
||
import os
|
||
import re
|
||
import json
|
||
from pathlib import Path
|
||
from collections import defaultdict
|
||
|
||
ROOT = Path(__file__).resolve().parent.parent
|
||
MAP_FILE = ROOT / 'lang' / '_migration-map.json'
|
||
LOG_FILE = ROOT / 'docs' / 'i18n-call-rewrite.log'
|
||
AMBIG_FILE = ROOT / 'docs' / 'i18n-ambiguous.json'
|
||
|
||
EXCLUDE_DIRS = {'node_modules', 'unpackage', 'stub-app-projects', '.git', 'lang'}
|
||
EXCLUDE_FILES_PARTS = {'_migration-map.json', 'i18n-audit.json', 'i18n-strict-audit.json', 'i18n-shadow-audit.json', 'i18n-call-rewrite.log', 'i18n-ambiguous.json'}
|
||
|
||
# $lang('xxx') / $lang("xxx") / this.$lang('xxx')
|
||
CALL_RE = re.compile(r"""((?:this\.)?\$lang)\(\s*(['"])([^'"]+)\2""")
|
||
|
||
|
||
def collect_old_keys():
|
||
"""从 migration-map.json 读取 old→new 映射"""
|
||
if not MAP_FILE.exists():
|
||
print(f'[ERROR] {MAP_FILE} 不存在')
|
||
return {}
|
||
data = json.loads(MAP_FILE.read_text(encoding='utf-8'))
|
||
return data.get('old_to_new', {})
|
||
|
||
|
||
def find_target_files():
|
||
"""扫描所有需要改写的 .vue / .js"""
|
||
files = []
|
||
for p in ROOT.rglob('*'):
|
||
if not p.is_file():
|
||
continue
|
||
if not (str(p).endswith('.vue') or str(p).endswith('.js')):
|
||
continue
|
||
# 排除目录
|
||
parts = set(p.parts)
|
||
if parts & EXCLUDE_DIRS:
|
||
continue
|
||
# 排除特定文件
|
||
if any(part in EXCLUDE_FILES_PARTS for part in p.name.split('_')):
|
||
continue
|
||
if p.name.startswith('i18n-'):
|
||
continue
|
||
files.append(p)
|
||
return files
|
||
|
||
|
||
def rewrite_file(path, old_to_new, log_lines, ambiguous):
|
||
"""改写一个文件,记录所有改动"""
|
||
try:
|
||
text = path.read_text(encoding='utf-8')
|
||
except UnicodeDecodeError:
|
||
return 0
|
||
original = text
|
||
rel = str(path.relative_to(ROOT))
|
||
changes = []
|
||
|
||
def repl(m):
|
||
prefix = m.group(1)
|
||
quote = m.group(2)
|
||
old_key = m.group(3)
|
||
if old_key in old_to_new:
|
||
new_key = old_to_new[old_key]
|
||
changes.append((old_key, new_key))
|
||
return f"{prefix}({quote}{new_key}{quote}"
|
||
return m.group(0)
|
||
|
||
new_text = CALL_RE.sub(repl, text)
|
||
|
||
# 检测 ambiguous: 同一文件里 old_key 被映射到不同 new_key
|
||
# 这里需要从 migration-map 区分
|
||
# 简单办法: 检查 old_key 是否在 old_to_new 里但 new_key 不唯一
|
||
# 由于 migration-map 是 1对1 的扁平 dict(一个 old_key 只对应一个 new_key),
|
||
# 所以 ambiguous 出现在 migration-map 本身(如果未来一个 old key 被映射到多个新 key)
|
||
|
||
if new_text != original:
|
||
path.write_text(new_text, encoding='utf-8')
|
||
for old_k, new_k in changes:
|
||
log_lines.append(f'{rel}: $lang("{old_k}") -> $lang("{new_k}")')
|
||
return len(changes)
|
||
return 0
|
||
|
||
|
||
def main():
|
||
import argparse
|
||
parser = argparse.ArgumentParser()
|
||
parser.add_argument('--apply', action='store_true', help='真的改写文件(默认 dry-run)')
|
||
args = parser.parse_args()
|
||
|
||
old_to_new = collect_old_keys()
|
||
print(f'共 {len(old_to_new)} 条迁移规则')
|
||
|
||
rev = defaultdict(list)
|
||
for old, new in old_to_new.items():
|
||
rev[new].append(old)
|
||
ambiguous_targets = {new: olds for new, olds in rev.items() if len(olds) > 1}
|
||
if ambiguous_targets:
|
||
print(f'发现 {len(ambiguous_targets)} 个新 key 被多个旧 key 指向(ambiguous)')
|
||
|
||
files = find_target_files()
|
||
print(f'扫描到 {len(files)} 个文件')
|
||
|
||
log_lines = []
|
||
total_changes = 0
|
||
files_changed = 0
|
||
|
||
for p in files:
|
||
try:
|
||
text = p.read_text(encoding='utf-8')
|
||
except UnicodeDecodeError:
|
||
continue
|
||
rel = str(p.relative_to(ROOT))
|
||
file_changes = []
|
||
|
||
def repl(m, _f=file_changes):
|
||
prefix = m.group(1)
|
||
quote = m.group(2)
|
||
old_key = m.group(3)
|
||
if old_key in old_to_new:
|
||
new_key = old_to_new[old_key]
|
||
_f.append((old_key, new_key))
|
||
return f"{prefix}({quote}{new_key}{quote}"
|
||
return m.group(0)
|
||
|
||
new_text = CALL_RE.sub(repl, text)
|
||
|
||
if new_text != text:
|
||
file_change_count = len(file_changes)
|
||
total_changes += file_change_count
|
||
files_changed += 1
|
||
for old_k, new_k in file_changes:
|
||
log_lines.append(f'{rel}: $lang("{old_k}") -> $lang("{new_k}")')
|
||
|
||
# 仅在 --apply 模式下真正写文件
|
||
if args.apply:
|
||
p.write_text(new_text, encoding='utf-8')
|
||
|
||
LOG_FILE.parent.mkdir(exist_ok=True)
|
||
LOG_FILE.write_text('\n'.join(log_lines), encoding='utf-8')
|
||
mode = 'APPLY' if args.apply else 'DRY-RUN'
|
||
print(f'\n[{mode}] 改写日志 -> {LOG_FILE.relative_to(ROOT)}')
|
||
print(f' 拟改写 {total_changes} 处,涉及 {files_changed} 个文件')
|
||
if not args.apply:
|
||
print(f' -- 未实际修改文件。要应用请加 --apply 参数 --')
|
||
|
||
AMBIG_FILE.write_text(json.dumps({
|
||
'ambiguous_new_keys': ambiguous_targets,
|
||
'note': '这些 new_key 被多个 old_key 指向,需要人工 review 决定哪个 old_key 应该映射到哪个 new_key',
|
||
}, ensure_ascii=False, indent=2), encoding='utf-8')
|
||
print(f'[OK] Ambiguous 清单 -> {AMBIG_FILE.relative_to(ROOT)}')
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main() |