feat: add merge_i18n.py — 合并所有模块i18n到wwwroot/i18n
This commit is contained in:
parent
3948f623ac
commit
8aea8d9d9a
171
merge_i18n.py
Normal file
171
merge_i18n.py
Normal file
@ -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()
|
||||
Loading…
x
Reference in New Issue
Block a user