重构i18n,现在game configs的配表,除了姓名这种外,都是统一的配表了。 对应的配表的名称和desc需要去i18n里取,但是其他配置不需要重复配置了。 这大大简化了之后新增i18n的心智负担。
116 lines
3.5 KiB
Python
116 lines
3.5 KiB
Python
import csv
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
from typing import Dict, List, Optional
|
|
|
|
# Configuration
|
|
PROJECT_ROOT = Path(__file__).parent.parent.parent
|
|
SOURCE_DIR = PROJECT_ROOT / "static" / "game_configs"
|
|
TEMPLATE_DIR = PROJECT_ROOT / "src" / "i18n" / "locales" / "templates"
|
|
POT_FILE = TEMPLATE_DIR / "game_configs.pot"
|
|
|
|
def ensure_dir(path: Path):
|
|
if not path.exists():
|
|
path.mkdir(parents=True)
|
|
|
|
def read_csv(path: Path) -> List[Dict[str, str]]:
|
|
if not path.exists():
|
|
return []
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
reader = csv.reader(f)
|
|
lines = list(reader)
|
|
if len(lines) < 2:
|
|
return []
|
|
|
|
headers = lines[0]
|
|
if headers and headers[0].startswith('\ufeff'):
|
|
headers[0] = headers[0][1:]
|
|
|
|
data = []
|
|
# Skip headers and comment row
|
|
# Heuristic: if row 2 looks like comments (Chinese/Types), skip it
|
|
start_idx = 1
|
|
if len(lines) > 1:
|
|
# Check if second row is likely a comment row (contains non-ascii or matches known structure)
|
|
if any(ord(c) > 127 for c in "".join(lines[1])):
|
|
start_idx = 2
|
|
|
|
for row in lines[start_idx:]:
|
|
if not row: continue
|
|
item = {}
|
|
for i, h in enumerate(headers):
|
|
if i < len(row):
|
|
item[h.strip()] = row[i].strip()
|
|
data.append(item)
|
|
return data
|
|
|
|
def escape_po_string(s: str) -> str:
|
|
return s.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n')
|
|
|
|
def generate_pot_entry(msgid: str, comment: str = "") -> str:
|
|
s = ""
|
|
if comment:
|
|
s += f"#. {comment}\n"
|
|
s += f'msgid "{escape_po_string(msgid)}"\n'
|
|
s += f'msgstr ""\n\n'
|
|
return s
|
|
|
|
def main():
|
|
print(f"Extracting translations from {SOURCE_DIR}...")
|
|
ensure_dir(TEMPLATE_DIR)
|
|
|
|
entries = []
|
|
|
|
# Header
|
|
header = f"""# Game Configs Translations
|
|
# Generated by tools/i18n/extract_csv.py
|
|
#
|
|
msgid ""
|
|
msgstr ""
|
|
"Content-Type: text/plain; charset=UTF-8\\n"
|
|
"Content-Transfer-Encoding: 8bit\\n"
|
|
"Project-Id-Version: Cultivation World Simulator\\n"
|
|
|
|
"""
|
|
entries.append(header)
|
|
|
|
count = 0
|
|
|
|
# Sort files for stable output
|
|
for csv_file in sorted(SOURCE_DIR.glob("*.csv")):
|
|
filename = csv_file.name
|
|
|
|
# Skip name files as they are handled differently (file suffix)
|
|
if "given_name" in filename or "last_name" in filename:
|
|
continue
|
|
|
|
data = read_csv(csv_file)
|
|
|
|
for item in data:
|
|
# Check for name_id
|
|
name_id = item.get("name_id")
|
|
if name_id:
|
|
ref_text = item.get("name", "")
|
|
entries.append(generate_pot_entry(name_id, f"{filename}: {ref_text}"))
|
|
count += 1
|
|
|
|
# Check for desc_id
|
|
desc_id = item.get("desc_id")
|
|
if desc_id:
|
|
ref_text = item.get("desc", "")
|
|
# Truncate long descriptions in comments
|
|
if len(ref_text) > 50:
|
|
ref_text = ref_text[:47] + "..."
|
|
entries.append(generate_pot_entry(desc_id, f"{filename}: {ref_text}"))
|
|
count += 1
|
|
|
|
with open(POT_FILE, "w", encoding="utf-8") as f:
|
|
f.write("".join(entries))
|
|
|
|
print(f"Extracted {count} keys to {POT_FILE}")
|
|
print("Don't forget to update your .po files using msgmerge or Poedit!")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|