zh locale: translate skill names and descriptions

skilldesc.txt takes Longju's name, description and condition columns
and its affect texts where their format arguments match 40250's; a
reviewed overlay fills the gaps and renames the empire languages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-30 08:56:03 +09:00
co-authored by Claude Opus 5.5
parent f8bbfc7fb3
commit 34c6049199
4 changed files with 86 additions and 0 deletions
+43
View File
@@ -31,6 +31,8 @@ KEYED_FILES = ("locale_game.txt", "locale_interface.txt")
# Image folders whose labels are drawn text (the character window's tabs and stat labels). Each is taken
# from Longju as a whole: its .sub files carry their own coordinates into the folder's atlas.
IMAGE_DIRS = ("ui/windows",)
# skilldesc.txt columns taken from Longju: names, description, conditions (2-8), affect texts (17/20/23).
SKILL_COLUMNS = (2, 3, 4, 5, 6, 7, 8, 17, 20, 23)
def keyed_lines(path: Path, encoding: str):
@@ -71,6 +73,42 @@ def convert_names(source: Path, destination: Path, overlay: Path = None):
return {"converted": converted, "overlaid": len(overlaid), "unrepresentable_vnums": unrepresentable}
def merge_skilldesc(en: Path, zh: Path, destination: Path, overlay: Path = None):
"""40250 rows with Longju's text columns; an affect text keeps the English one unless its format
arguments match, since the client passes it to _snprintf with the min/max values."""
chinese = {}
for line in zh.read_bytes().decode("gb18030").splitlines():
parts = line.split("\t")
if parts[0].isdigit():
chinese[parts[0]] = parts
reviewed = {}
if overlay and overlay.is_file():
for line in overlay.read_text(encoding="utf-8").splitlines()[1:]:
vnum, column, text = line.split("\t")
reviewed[(vnum, int(column))] = text
rows, translated, incompatible = [], 0, []
for line in en.read_bytes().decode("cp1252").splitlines():
parts = line.split("\t")
if parts[0].isdigit():
source = chinese.get(parts[0], [])
for column in SKILL_COLUMNS:
if column >= len(parts):
continue
text = reviewed.get((parts[0], column))
if text is None and column < len(source) and source[column]:
text = source[column]
if text is None:
continue
if FORMAT.findall(parts[column]) != FORMAT.findall(text):
incompatible.append(f"{parts[0]}:{column}")
continue
parts[column] = text
translated += 1
rows.append("\t".join(parts))
destination.write_bytes(("\n".join(rows) + "\n").encode("gbk"))
return {"translated_cells": translated, "incompatible_format_cells": incompatible}
def tool(name: str) -> Path:
for build_dir in BUILD_DIRS:
path = REPO / build_dir / "src/port" / name
@@ -169,6 +207,11 @@ def build(en: Path, zh: Path, output: Path, client_base: Path,
if (zh / filename).is_file():
shutil.copyfile(zh / filename, locale_dir / filename)
if (zh / "skilldesc.txt").is_file():
report["files"]["skilldesc.txt"] = merge_skilldesc(
en / "skilldesc.txt", zh / "skilldesc.txt", locale_dir / "skilldesc.txt",
names_dir / "skilldesc_zh_overlay.txt" if names_dir else None)
for folder in IMAGE_DIRS:
english_files = {path.name for path in (en / folder).iterdir()}
chinese_files = {path.name for path in (zh / folder).iterdir()}
+17
View File
@@ -82,6 +82,23 @@ class LocaleBuildTest(unittest.TestCase):
self.assertEqual((root / "out.txt").read_bytes().decode("gbk"), "1\t金币\n2\t剑\n")
self.assertEqual(result["overlaid"], 1)
def test_skilldesc_keeps_format_arguments(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
blank = [""] * 22
en_row = ["20", "WARRIOR", "Sword Strike", "Bolt", "Hurl", "Attack.", "Long-Range", "Multi", "Faint"] + blank
en_row[17], en_row[20] = "Attack Power %.0f-%.0f", "Faint Chance %.0f%%"
zh_row = ["20", "WARRIOR", "剑风", "斩气决", "剑气冲霄", "快速挥动 |打击敌人", "远程攻击", "", "击晕"] + blank
zh_row[17] = "攻击力 %.0f-%.0f"
(root / "en.txt").write_bytes(("\t".join(en_row) + "\n").encode("cp1252"))
(root / "zh.txt").write_bytes(("\t".join(zh_row) + "\n").encode("gbk"))
(root / "overlay.txt").write_text("VNUM\tCOLUMN\tTEXT\n20\t7\t多目标攻击\n20\t20\t气绝 %.0f\n")
result = MODULE.merge_skilldesc(root / "en.txt", root / "zh.txt", root / "out.txt", root / "overlay.txt")
row = (root / "out.txt").read_bytes().decode("gbk").rstrip("\n").split("\t")
self.assertEqual(row[2:9], ["剑风", "斩气决", "剑气冲霄", "快速挥动 |打击敌人", "远程攻击", "多目标攻击", "击晕"])
self.assertEqual((row[17], row[20]), ("攻击力 %.0f-%.0f", "Faint Chance %.0f%%"))
self.assertEqual(result["incompatible_format_cells"], ["20:20"])
if __name__ == "__main__":
unittest.main()