Skip to content

Commit d352c85

Browse files
spilt info to json (#181)
1 parent b25eb4d commit d352c85

111 files changed

Lines changed: 16558 additions & 11707 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.esh/commands/update-locales.py

Lines changed: 157 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,14 @@
4141
TRANSLATION_DELAY = 0.6
4242
# 同步最大迭代次数
4343
MAX_SYNC_ITERATIONS = 10
44-
# 占位符名称相似度修复阈值 (0.0 到 1.0,越高越严格,这里指 Levenshtein 距离与长度的比值,1.0 - dist/len)
45-
# 或者使用绝对距离阈值,如下面的实现。
46-
PLACEHOLDER_SIMILARITY_THRESHOLD_FACTOR = 0.7 # 用于计算最大可接受距离
47-
PLACEHOLDER_ABSOLUTE_DISTANCE_THRESHOLD = 3 # 或者一个固定的最大编辑距离
44+
# 占位符名称相似度修复阈值
45+
PLACEHOLDER_SIMILARITY_THRESHOLD_FACTOR = 0.7
46+
PLACEHOLDER_ABSOLUTE_DISTANCE_THRESHOLD = 3
47+
48+
# --- 新增配置: Info 文件处理 ---
49+
INFO_FILENAMES = {"info.json", "info.dynamic.json"}
50+
# 仅翻译这些字段,其他字段(如 version, author, home_page)直接复制
51+
TRANSLATABLE_INFO_FIELDS = {"name", "description", "description_markdown", "summary", "tags"}
4852
# ----------- 配置结束 -----------
4953

5054

@@ -413,6 +417,145 @@ def sync_unique_key(dict_has_key, dict_missing_key, lang_has_key, lang_missing_k
413417
return handle_missing_key_translation(dict_missing_key, dict_has_key, key, lang_missing_key, lang_has_key, path)
414418

415419

420+
# --- Info File Sync Logic ---
421+
422+
def sync_info_content(info_data, available_lang_codes):
423+
"""
424+
同步 info.json 类型的内容。
425+
结构通常为: { "en-UK": {...}, "zh-CN": {...} }
426+
"""
427+
changed = False
428+
429+
# 1. 确定参考源 (Source of Truth)
430+
source_lang = None
431+
for ref in REFERENCE_LANG_CODES:
432+
if ref in info_data:
433+
source_lang = ref
434+
break
435+
if not source_lang:
436+
# 如果找不到参考语言,尝试使用存在的第一个语言
437+
available_in_file = [k for k in info_data.keys() if k in available_lang_codes]
438+
if available_in_file:
439+
source_lang = available_in_file[0]
440+
441+
if not source_lang:
442+
print(f" - 跳过: 无法在文件中找到任何有效的参考语言块。")
443+
return False
444+
445+
source_obj = info_data[source_lang]
446+
if not isinstance(source_obj, (dict, OrderedDict)):
447+
return False
448+
449+
# 2. 遍历所有应该支持的语言
450+
for target_lang in available_lang_codes:
451+
if target_lang == source_lang:
452+
continue
453+
454+
# 如果目标语言块不存在,创建它
455+
if target_lang not in info_data:
456+
print(f" + 为 '{target_lang}' 创建新块 (基于 '{source_lang}')")
457+
info_data[target_lang] = OrderedDict()
458+
changed = True
459+
460+
target_obj = info_data[target_lang]
461+
462+
# 3. 同步字段
463+
for key, val in source_obj.items():
464+
if key not in target_obj:
465+
# 缺失键:决定是翻译还是复制
466+
if key in TRANSLATABLE_INFO_FIELDS:
467+
# 翻译
468+
print(f" + 翻译缺失字段 '{key}': {source_lang} -> {target_lang}")
469+
translated = translate_value(copy.deepcopy(val), source_lang, target_lang)
470+
if translated is not None:
471+
target_obj[key] = translated
472+
changed = True
473+
else:
474+
# 直接复制 (例如 version, author, provider)
475+
# print(f" + 复制字段 '{key}'")
476+
target_obj[key] = copy.deepcopy(val)
477+
changed = True
478+
479+
# 如果键存在,暂时不做覆盖更新,假设已有的是手动修正过的
480+
# 除非值为空字符串且源不为空?这里暂保持保守策略。
481+
482+
# 4. (可选) 排序:让 info.json 的语言顺序好看一点
483+
# 这里简单对顶层 key (语言) 排序,把非语言的 key 留着
484+
sorted_keys = sorted(info_data.keys(), key=lambda k: (0 if k in REFERENCE_LANG_CODES else 1, k))
485+
if list(info_data.keys()) != sorted_keys:
486+
new_ordered = OrderedDict((k, info_data[k]) for k in sorted_keys)
487+
info_data.clear()
488+
info_data.update(new_ordered)
489+
changed = True
490+
491+
return changed
492+
493+
def process_info_files(fount_dir, gitignore_spec, available_lang_codes):
494+
"""
495+
扫描并同步所有的 info.json / info.dynamic.json 文件
496+
不仅同步内容,还会强制统一缩进格式(缩进使用 Tab,末尾有空行)。
497+
"""
498+
print(f"\n--- 开始扫描并同步 Info JSON 文件 ---")
499+
print(f"目标文件名: {INFO_FILENAMES}")
500+
501+
count_processed = 0
502+
count_changed = 0
503+
504+
for root, dirs, files in os.walk(fount_dir):
505+
if gitignore_spec:
506+
dirs[:] = [d for d in dirs if not gitignore_spec.match_file(os.path.relpath(os.path.join(root, d), fount_dir))]
507+
508+
for filename in files:
509+
if filename not in INFO_FILENAMES:
510+
continue
511+
512+
filepath = os.path.join(root, filename)
513+
if gitignore_spec and gitignore_spec.match_file(os.path.relpath(filepath, fount_dir)):
514+
continue
515+
516+
try:
517+
# 1. 读取原始文件内容(字符串)以便后续比较格式
518+
with open(filepath, "r", encoding="utf-8") as f:
519+
original_content_str = f.read()
520+
521+
# 2. 解析数据
522+
# 使用 loads 而不是 load,因为我们已经读取了字符串
523+
try:
524+
data = json5.loads(original_content_str, object_pairs_hook=OrderedDict)
525+
except Exception as e:
526+
print(f" ! 解析 JSON5 失败 {os.path.relpath(filepath, fount_dir)}: {e}")
527+
continue
528+
529+
print(f" 正在处理: {os.path.relpath(filepath, fount_dir)}")
530+
531+
# 3. 执行内容同步逻辑 (sync_info_content 会修改 data 对象)
532+
content_has_logical_changes = sync_info_content(data, available_lang_codes)
533+
534+
# 4. 生成标准格式的新字符串
535+
# 强制使用 Tab 缩进,且末尾附加一个换行符
536+
new_content_str = json.dumps(data, ensure_ascii=False, indent="\t", default=str) + "\n"
537+
538+
# 5. 判定是否需要写入
539+
# 条件:(内容逻辑有变化) 或者 (原始文本与标准格式文本不一致)
540+
if content_has_logical_changes or original_content_str != new_content_str:
541+
action = "更新内容" if content_has_logical_changes else "格式化"
542+
543+
with open(filepath, "w", encoding="utf-8", newline="\n") as f:
544+
f.write(new_content_str)
545+
546+
print(f" -> 已保存 ({action})。")
547+
count_changed += 1
548+
else:
549+
print(" -> 无需变更。")
550+
pass
551+
552+
count_processed += 1
553+
554+
except Exception as e:
555+
print(f" ! 错误处理文件 {filepath}: {e}")
556+
557+
print(f"--- Info 文件处理完毕: 扫描 {count_processed} 个, 更新 {count_changed} 个 ---")
558+
416559
def normalize_and_sync_dicts(
417560
dict_a,
418561
dict_b,
@@ -486,7 +629,7 @@ def check_used_keys_in_fount(fount_dir, reference_loc_data, reference_lang_code,
486629
print(f"警告: fount 目录 '{fount_dir}' 不存在,跳过i18n键使用情况检查。")
487630
return
488631

489-
print(f"\n--- 开始在 FOUNT 目录 '{fount_dir}' 中检查 MJS/HTML 文件中的 i18n 键 ---")
632+
print(f"\n--- 开始在 fount 目录 '{fount_dir}' 中检查 MJS/HTML 文件中的 i18n 键 ---")
490633
regex_patterns = [re.compile(r"data-i18n=(?:\"([a-zA-Z0-9_.-]+)\"|'([a-zA-Z0-9_.-]+)')"), re.compile(r"(?:geti|I)18n\((?:\"([a-zA-Z0-9_.-]+)\"|'([a-zA-Z0-9_.-]+)')\)")]
491634
found_keys_in_fount_code = set()
492635

@@ -514,10 +657,10 @@ def check_used_keys_in_fount(fount_dir, reference_loc_data, reference_lang_code,
514657
print(f" 警告: 读取或解析文件 {filepath} 失败: {e}")
515658

516659
if not found_keys_in_fount_code:
517-
print(" 在 FOUNT 目录的 MJS/HTML 文件中没有检测到任何 i18n 键的使用。")
660+
print(" 在 fount 目录的 MJS/HTML 文件中没有检测到任何 i18n 键的使用。")
518661
return
519662

520-
print(f" 在 FOUNT 代码中检测到 {len(found_keys_in_fount_code)} 个唯一 i18n 键。正在与参考语言 '{reference_lang_code}' 数据比较...")
663+
print(f" 在 fount 代码中检测到 {len(found_keys_in_fount_code)} 个唯一 i18n 键。正在与参考语言 '{reference_lang_code}' 数据比较...")
521664
missing_keys_count = 0
522665
for key in sorted(list(found_keys_in_fount_code)):
523666
_, found = get_value_at_path(reference_loc_data, key)
@@ -526,10 +669,10 @@ def check_used_keys_in_fount(fount_dir, reference_loc_data, reference_lang_code,
526669
missing_keys_count += 1
527670

528671
if missing_keys_count == 0:
529-
print(f" 所有在 FOUNT 代码中使用的 {len(found_keys_in_fount_code)} 个 i18n 键均存在于参考语言 '{reference_lang_code}' 数据中。")
672+
print(f" 所有在 fount 代码中使用的 {len(found_keys_in_fount_code)} 个 i18n 键均存在于参考语言 '{reference_lang_code}' 数据中。")
530673
else:
531-
print(f" 总计: {missing_keys_count} 个在 FOUNT 代码中使用的键在参考语言数据中缺失。")
532-
print("--- FOUNT 目录i18n键检查完毕 ---")
674+
print(f" 总计: {missing_keys_count} 个在 fount 代码中使用的键在参考语言数据中缺失。")
675+
print("--- fount 目录i18n键检查完毕 ---")
533676

534677

535678
def extract_placeholders(text_string: str) -> list[str]:
@@ -1034,14 +1177,15 @@ def main():
10341177
gitignore_spec = load_gitignore_spec(FOUNT_DIR)
10351178

10361179
all_data, languages, lang_to_path = load_locale_files()
1180+
available_lang_codes = list(lang_to_path.keys()) # 收集所有可用语言代码
10371181

10381182
ref_path, ref_lang = find_reference_language(lang_to_path, all_data, languages)
10391183

10401184
first_ref_lang = next((lang for lang in REFERENCE_LANG_CODES if lang in lang_to_path), ref_lang)
10411185
if first_ref_lang:
10421186
check_used_keys_in_fount(FOUNT_DIR, all_data[lang_to_path[first_ref_lang]], first_ref_lang, gitignore_spec)
10431187
else:
1044-
print("\n警告: 未找到参考语言数据,跳过 FOUNT 目录i18n键检查。")
1188+
print("\n警告: 未找到参考语言数据,跳过 fount 目录i18n键检查。")
10451189

10461190
run_synchronization_loop(all_data, languages, ref_path)
10471191

@@ -1050,6 +1194,8 @@ def main():
10501194

10511195
save_locale_files(all_data, ref_path)
10521196

1197+
process_info_files(FOUNT_DIR, gitignore_spec, available_lang_codes)
1198+
10531199
ts_decl_path = os.path.join(LOCALE_DIR, "../decl/locale_data.ts")
10541200
generate_locale_data_ts(all_data[ref_path], ts_decl_path)
10551201

.github/workflows/update_locales.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ on:
88
paths:
99
- 'src/locales/**.json'
1010
- '**/home_registry.json'
11+
- '**/info.json'
12+
- '**/info.dynamic.json'
1113
tags-ignore:
1214
- '*'
1315
branches:

0 commit comments

Comments
 (0)