#!/usr/bin/env python3 """ fix_i18n_w1.py — 批量修复 W1:给硬编码中文 text 加 otext+i18n 标记 基于 check_i18n.py --json 报告的 violations(W1 且 git_ignored=False), 按 pos 从后往前精准插入,保持文件其余部分逐字节不变。 .ui: "text": "中文" → "otext": "中文", "text": "中文", "i18n": true .dspy: 'text': '中文' → 'otext': '中文', 'text': '中文', 'i18n': True (引号风格跟随原文) otext 无 i18n 的: "otext": "中文" → "otext": "中文", "i18n": true 用法: python3 scripts/fix_i18n_w1.py /tmp/i18n.json [--dry-run] """ import json, re, sys from collections import defaultdict def main(): report = json.load(open(sys.argv[1], encoding='utf-8')) dry = '--dry-run' in sys.argv viols = [v for v in report['violations'] if v['code'] == 'W1' and not v['git_ignored']] by_file = defaultdict(list) for v in viols: by_file[v['file']].append(v) total_fixed = 0 errors = [] for fp, vs in sorted(by_file.items()): raw = open(fp, encoding='utf-8').read() is_ui = fp.endswith('.ui') q_t = 'true' if is_ui else 'True' # 从后往前改,pos 不漂移 for v in sorted(vs, key=lambda x: -x['pos']): pos = v['pos'] # 在 pos 处重新匹配完整 key-value 片段 if v['why'].startswith('dspy otext') or v['why'].startswith('otext'): m = re.compile(r'(["\'])otext\1\s*:\s*(["\'])(.*?)\2').match(raw, pos) if not m: errors.append((fp, pos, 'otext 片段定位失败')) continue if '"i18n"' in raw[m.end():m.end()+40] or "'i18n'" in raw[m.end():m.end()+40]: continue # 已有 i18n 标记(可能重复报告) ins = f', {m.group(1)}i18n{m.group(1)}: {q_t}' raw = raw[:m.end()] + ins + raw[m.end():] total_fixed += 1 else: m = re.compile(r'(["\'])text\1\s*:\s*(["\'])(.*?)\2').match(raw, pos) if not m: errors.append((fp, pos, 'text 片段定位失败')) continue q, val = m.group(1), m.group(3) ins = f', {q}otext{q}: {q}{val}{q}, {q}i18n{q}: {q_t}' # 在 "text": "val" 前插 otext,在其后插 i18n: # 结果形如 "otext": "val", "text": "val", "i18n": true new_seg = f'{q}otext{q}: {q}{val}{q}, {q}text{q}: {q}{val}{q}, {q}i18n{q}: {q_t}' raw = raw[:m.start()] + new_seg + raw[m.end():] total_fixed += 1 if not dry: open(fp, 'w', encoding='utf-8').write(raw) print(f'{"DRY-RUN " if dry else ""}fixed={total_fixed} files={len(by_file)} errors={len(errors)}') for e in errors[:20]: print(' ERR', e) if __name__ == '__main__': main()