diff --git a/merge_i18n.py b/merge_i18n.py new file mode 100644 index 00000000..d11841f4 --- /dev/null +++ b/merge_i18n.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +""" +合并所有Sage模块的i18n内容到 wwwroot/i18n + +从 load_path.py 中提取sage引用的所有模块, +读取每个模块的 i18n/{lang}/msg.txt, +合并输出到 wwwroot/i18n/{lang}/i18n.json。 + +冲突策略:后加载的模块覆盖先加载的,sage自身覆盖所有。 +""" + +import os +import re +import json +import sys +from pathlib import Path + +# --- 配置 --- +SAGE_DIR = Path(__file__).resolve().parent # /d/ymq/repos/sage +REPOS_DIR = SAGE_DIR.parent # /d/ymq/repos +I18N_OUTPUT_DIR = SAGE_DIR / 'wwwroot' / 'i18n' # 输出目标 +LANGS = ['zh', 'en', 'jp', 'ko'] # 支持的语言 + + +def parse_load_path(load_path_file): + """从 load_path.py 中提取模块名列表 + + load_path.py 中每行格式: /module_name/rest/of/path ... + 只提取行首的第一个路径段作为模块名。 + """ + modules = set() + # 已知的非模块路径前缀(系统路径、资源目录等) + skip_prefixes = { + 'conf', 'logs', 'files', 'script', 'skills', 'plugins', + 'py3', 'bin', 'initln', 'docs', 'agents', 'hermes_work', + 'bricks', 'imgs', 'i18n', 'public', 'api', 'shell', + } + + with open(load_path_file, 'r', encoding='utf-8') as f: + for line in f: + line = line.strip() + if not line or line.startswith('#'): + continue + # 匹配行首的 /module_name/ + m = re.match(r'^/\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*/', line) + if not m: + continue + mod = m.group(1) + if mod not in skip_prefixes: + modules.add(mod) + + # sage 自身始终排在前面(优先级最低,因为 load_path 中 sage 是根路径) + modules.discard('sage') + result = ['sage'] + sorted(modules) + return result + + +def find_module_i18n_dir(module_name): + """查找模块的 i18n 目录""" + candidate = REPOS_DIR / module_name / 'i18n' + if candidate.is_dir(): + return candidate + return None + + +def parse_msg_txt(filepath): + """解析 msg.txt,格式: key: value(每行一个)""" + result = {} + if not filepath.is_file(): + return result + + with open(filepath, 'r', encoding='utf-8') as f: + for line in f: + line = line.rstrip('\n').rstrip('\r') + if not line: + continue + # 格式: key: value + idx = line.find(': ') + if idx > 0: + key = line[:idx] + value = line[idx+2:] + result[key] = value + else: + # 可能只有 key 没有 value,或者格式不对,跳过 + idx = line.find(':') + if idx > 0: + key = line[:idx] + value = line[idx+1:] + result[key] = value + return result + + +def merge_i18n(modules): + """合并所有模块的 i18n,返回 {lang: {key: value}}""" + merged = {lang: {} for lang in LANGS} + + for module_name in modules: + i18n_dir = find_module_i18n_dir(module_name) + if not i18n_dir: + print(f" [跳过] {module_name}: 未找到i18n目录") + continue + + for lang in LANGS: + msg_file = i18n_dir / lang / 'msg.txt' + if msg_file.is_file(): + translations = parse_msg_txt(msg_file) + count = len(translations) + before = len(merged[lang]) + merged[lang].update(translations) + after = len(merged[lang]) + new_keys = after - before + if new_keys > 0 or count > 0: + print(f" {module_name}/{lang}: {count}条, 新增{new_keys}个唯一key") + else: + print(f" [警告] {module_name}/{lang}: msg.txt 不存在") + + return merged + + +def write_output(merged): + """将合并结果写入 wwwroot/i18n/{lang}/i18n.json 和 msg.txt""" + for lang in LANGS: + lang_dir = I18N_OUTPUT_DIR / lang + lang_dir.mkdir(parents=True, exist_ok=True) + + data = merged[lang] + + # 写入 i18n.json(前端读取此文件) + json_file = lang_dir / 'i18n.json' + with open(json_file, 'w', encoding='utf-8') as f: + json.dump(data, f, ensure_ascii=False, indent='\t') + + # 同时写入 msg.txt(人类可读格式) + msg_file = lang_dir / 'msg.txt' + with open(msg_file, 'w', encoding='utf-8') as f: + for key, value in data.items(): + f.write(f"{key}: {value}\n") + + print(f" 写入: {json_file} ({len(data)} 条)") + print(f" 写入: {msg_file} ({len(data)} 条)") + + +def main(): + load_path_file = SAGE_DIR / 'load_path.py' + if not load_path_file.is_file(): + print(f"错误: 找不到 {load_path_file}") + sys.exit(1) + + print(f"解析 load_path.py: {load_path_file}") + modules = parse_load_path(load_path_file) + print(f"发现 {len(modules)} 个模块:") + for m in modules: + has_i18n = (REPOS_DIR / m / 'i18n').is_dir() + status = '✓' if has_i18n else '✗' + print(f" {status} {m}") + + print(f"\n开始合并 i18n...") + merged = merge_i18n(modules) + + print(f"\n合并结果:") + for lang in LANGS: + print(f" {lang}: {len(merged[lang])} 个唯一key") + + print(f"\n写入 wwwroot/i18n...") + write_output(merged) + + print(f"\n完成!") + + +if __name__ == '__main__': + main()