sage/merge_i18n.py

172 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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()