159 lines
4.8 KiB
Python
159 lines
4.8 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import time
|
|
import urllib.parse
|
|
import urllib.request
|
|
from urllib.error import URLError
|
|
from pathlib import Path
|
|
|
|
|
|
def _decode_utf8_keep_newlines(path: Path) -> str:
|
|
return path.read_bytes().decode("utf-8-sig")
|
|
|
|
|
|
def translate_en_to_zh(
|
|
text: str,
|
|
*,
|
|
timeout_s: int = 45,
|
|
retries: int = 6,
|
|
backoff_s: float = 1.0,
|
|
) -> str:
|
|
"""
|
|
Uses Google Translate's public endpoint (no API key) to translate English -> zh-CN.
|
|
|
|
Note: This is a best-effort helper for generating a local offline mapping file.
|
|
"""
|
|
if not text.strip():
|
|
return ""
|
|
|
|
q = urllib.parse.quote(text)
|
|
url = (
|
|
"https://translate.googleapis.com/translate_a/single"
|
|
"?client=gtx&sl=en&tl=zh-CN&dt=t&q="
|
|
+ q
|
|
)
|
|
headers = {"User-Agent": "pz-config-editor/1.0 (+https://translate.googleapis.com)"}
|
|
req = urllib.request.Request(url, headers=headers)
|
|
|
|
last_err: Exception | None = None
|
|
for attempt in range(retries):
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=timeout_s) as resp:
|
|
data = resp.read().decode("utf-8")
|
|
payload = json.loads(data)
|
|
parts = payload[0] or []
|
|
return "".join((p[0] or "") for p in parts)
|
|
except (TimeoutError, URLError, json.JSONDecodeError) as e:
|
|
last_err = e
|
|
time.sleep(backoff_s * (attempt + 1))
|
|
|
|
raise RuntimeError(f"Translate failed after {retries} retries: {last_err}") from last_err
|
|
|
|
|
|
def extract_sandbox_comment_blocks(lua_path: Path) -> dict[str, str]:
|
|
"""
|
|
Returns: { "Zombies": "comment lines...", "ZombieLore.Speed": "comment lines..." }
|
|
"""
|
|
lines = _decode_utf8_keep_newlines(lua_path).splitlines()
|
|
|
|
def is_ident(s: str) -> bool:
|
|
return s.isidentifier()
|
|
|
|
comments: list[str] = []
|
|
table_stack: list[str] = []
|
|
out: dict[str, str] = {}
|
|
|
|
for line in lines:
|
|
stripped = line.strip()
|
|
if stripped.startswith("--"):
|
|
comments.append(stripped[2:].lstrip())
|
|
continue
|
|
|
|
if not stripped:
|
|
comments = []
|
|
continue
|
|
|
|
if stripped.endswith("= {"):
|
|
name = stripped.split("=", 1)[0].strip()
|
|
if is_ident(name):
|
|
table_stack.append(name)
|
|
comments = []
|
|
continue
|
|
|
|
if stripped == "},":
|
|
if table_stack:
|
|
table_stack.pop()
|
|
comments = []
|
|
continue
|
|
|
|
if "=" in stripped and stripped.endswith(","):
|
|
key = stripped.split("=", 1)[0].strip()
|
|
if not is_ident(key):
|
|
comments = []
|
|
continue
|
|
|
|
effective_stack = table_stack[:]
|
|
if effective_stack[:1] == ["SandboxVars"]:
|
|
effective_stack = effective_stack[1:]
|
|
setting_path = ".".join(effective_stack + [key]) if effective_stack else key
|
|
out[setting_path] = "\n".join(comments).strip()
|
|
comments = []
|
|
continue
|
|
|
|
comments = []
|
|
|
|
return out
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description="Generate SandboxVars English->Chinese comment mapping JSON.")
|
|
parser.add_argument("--lua", default="server_SandboxVars.lua", help="Path to server_SandboxVars.lua")
|
|
parser.add_argument("--out", default=str(Path("i18n") / "sandboxvars_zh.json"), help="Output JSON path")
|
|
parser.add_argument("--sleep", type=float, default=0.12, help="Sleep between requests (seconds)")
|
|
parser.add_argument("--resume", action="store_true", help="If output exists, load and continue")
|
|
args = parser.parse_args()
|
|
|
|
lua_path = Path(args.lua)
|
|
out_path = Path(args.out)
|
|
out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
blocks = extract_sandbox_comment_blocks(lua_path)
|
|
|
|
mapping: dict[str, str] = {
|
|
"VERSION": "SandboxVars 文件版本号(通常不用改)。",
|
|
"StartYear": "开局年份(通常与开局日期/月份一起使用)。",
|
|
}
|
|
if args.resume and out_path.exists():
|
|
try:
|
|
mapping.update(json.loads(out_path.read_text(encoding="utf-8")))
|
|
except Exception:
|
|
pass
|
|
|
|
cache: dict[str, str] = {}
|
|
total = 0
|
|
for key, en in blocks.items():
|
|
if key in mapping:
|
|
continue
|
|
total += 1
|
|
if en in cache:
|
|
mapping[key] = cache[en]
|
|
continue
|
|
try:
|
|
zh = translate_en_to_zh(en)
|
|
except Exception as e:
|
|
print(f"[WARN] {key}: {e}")
|
|
zh = ""
|
|
cache[en] = zh
|
|
mapping[key] = zh
|
|
time.sleep(args.sleep)
|
|
|
|
out_path.write_text(json.dumps(mapping, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
print(f"Wrote {len(mapping)} entries to {out_path} (translated {total} blocks).")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|