sage/merge_i18n.py
yumoqing 7dc17f8437 i18n: move bricks last in merge order for Tabular translation priority
All business modules overwrite common keys (Submit/Reset/Cancel)
with English→English, erasing bricks' zh translations. Moving
bricks to end of merge priority ensures framework built-in
strings always win.
2026-07-27 12:13:00 +08:00

167 lines
5.6 KiB
Python
Raw Permalink 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
模块清单与 build.sh 保持一致第29行 for m in ...
读取每个模块的 i18n/{lang}/msg.txt
合并输出到 wwwroot/i18n/{lang}/i18n.json + msg.txt。
冲突策略:按 build.sh 中模块顺序,后加载的覆盖先加载的。
sage 自身排在首位(优先级最低)。
"""
import json
import re
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_build_sh(build_sh_path):
"""从 build.sh 中提取模块清单第29行 for m in ...
build.sh 有两个 for m in 循环:
- 第21行: pip基础库apppublic, sqlor, ahserver, ...
- 第29行: Sage业务模块appbase, rbac, accounting, ...
我们取第二个(业务模块),与 sage 的 wwwroot 符号链接配置一致。
"""
with open(build_sh_path, 'r', encoding='utf-8') as f:
content = f.read()
# 匹配所有 "for m in ..." 行 —— 只到行尾(不用 \s 防止跨行匹配到 do
matches = re.findall(r'^for m in ((?:[a-z][\w-]*[ \t]*)+)$', content, re.MULTILINE)
if len(matches) < 2:
sys.exit(f"错误: 在 build.sh 中找到 {len(matches)} 个 for m in 行期望至少2个")
# 第二个 for m in 是 Sage 业务模块清单
modules_str = matches[1]
modules = [m for m in modules_str.strip().split() if '-' not in m]
# 业务模块 → bricks最后加载优先级最高确保框架内置文本不被覆盖
# bricks 是基础框架,不在 build.sh 业务模块循环中,需手动加入
result = ['sage'] + modules + ['bricks']
return result
def find_module_i18n_dir(module_name):
"""查找模块的 i18n 目录(先查 repos/ 同级目录,再查 sage/pkgs/ 内)"""
candidate = REPOS_DIR / module_name / 'i18n'
if candidate.is_dir():
return candidate
candidate = SAGE_DIR / 'pkgs' / 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():
build_sh_path = SAGE_DIR / 'build.sh'
if not build_sh_path.is_file():
print(f"错误: 找不到 {build_sh_path}")
sys.exit(1)
print(f"解析 build.sh: {build_sh_path}")
modules = parse_build_sh(build_sh_path)
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()