|
#!/usr/bin/env python3 |
|
import gzip |
|
import io |
|
import json |
|
import zipfile |
|
from pathlib import Path |
|
from urllib.request import urlopen, Request |
|
|
|
VSIX_URL = ( |
|
"https://marketplace.visualstudio.com/_apis/public/gallery/publishers/MS-CEINTL" |
|
"/vsextensions/vscode-language-pack-zh-hans/latest/vspackage" |
|
) |
|
TRANSLATIONS_PATH_IN_VSIX = "extension/translations/main.i18n.json" |
|
|
|
COMMIT = "0958016b2af9f09bb4257e0df4a95e2f90590f9f" |
|
SERVE_WEB_DIR = Path.home() / ".vscode/cli/serve-web" / COMMIT / "out" |
|
VSCODE_CDN_OUT_URL = f"https://main.vscode-cdn.net/stable/{COMMIT}/out" |
|
|
|
NLS_KEYS_FILE = SERVE_WEB_DIR / "nls.keys.json" |
|
NLS_MESSAGES_FILE = SERVE_WEB_DIR / "nls.messages.json" |
|
|
|
NLS_KEYS_URL = f"{VSCODE_CDN_OUT_URL}/nls.keys.json" |
|
NLS_MESSAGES_URL = f"{VSCODE_CDN_OUT_URL}/nls.messages.json" |
|
|
|
OUTPUT_FILE = Path.home() / "third_party/vscode/nls.messages.js" |
|
|
|
|
|
def download_url(url: str) -> bytes: |
|
req = Request(url, headers={"Accept": "*/*"}) |
|
with urlopen(req) as resp: |
|
response_bytes = resp.read() |
|
if response_bytes.startswith(b"\x1f\x8b"): |
|
return gzip.decompress(response_bytes) |
|
return response_bytes |
|
|
|
|
|
def download_json(url: str) -> object: |
|
return json.loads(download_url(url).decode("utf-8")) |
|
|
|
|
|
def download_translations() -> dict: |
|
print("Downloading language pack from Marketplace...") |
|
req = Request(VSIX_URL, headers={"Accept": "application/octet-stream"}) |
|
with urlopen(req) as resp: |
|
vsix_bytes = resp.read() |
|
print(f"Downloaded {len(vsix_bytes) / 1024 / 1024:.1f} MB") |
|
|
|
vsix_bytes = gzip.decompress(vsix_bytes) |
|
with zipfile.ZipFile(io.BytesIO(vsix_bytes)) as zf: |
|
with zf.open(TRANSLATIONS_PATH_IN_VSIX) as f: |
|
return json.load(f) |
|
|
|
|
|
def main() -> None: |
|
# 1. Download and extract translations from the VSIX |
|
translations = download_translations() |
|
contents: dict[str, dict[str, str]] = translations.get("contents", {}) |
|
|
|
# 2. Load build-time NLS metadata |
|
nls_keys: list[list] = json.loads(NLS_KEYS_FILE.read_text("utf-8")) |
|
nls_messages: list[str] = json.loads(NLS_MESSAGES_FILE.read_text("utf-8")) |
|
# # 2. Download build-time NLS metadata |
|
# print(f"Downloading VS Code NLS metadata for commit {COMMIT}...") |
|
# nls_keys = download_json(NLS_KEYS_URL) |
|
# if not isinstance(nls_keys, list): |
|
# raise ValueError("nls.keys.json must be a JSON array") |
|
|
|
# nls_messages = download_json(NLS_MESSAGES_URL) |
|
# if not isinstance(nls_messages, list) or not all( |
|
# isinstance(message, str) for message in nls_messages |
|
# ): |
|
# raise ValueError("nls.messages.json must be a JSON array of strings") |
|
|
|
# 3. Merge: iterate nls.keys.json in order, look up each key in the |
|
# language pack, fall back to the English message from nls.messages.json. |
|
result: list[str] = [] |
|
nls_index = 0 |
|
translated_count = 0 |
|
|
|
for module_id, nls_module_keys in nls_keys: |
|
module_translations = contents.get(module_id, {}) |
|
for nls_key in nls_module_keys: |
|
translated = module_translations.get(nls_key) |
|
if translated: |
|
result.append(translated) |
|
translated_count += 1 |
|
else: |
|
result.append(nls_messages[nls_index]) |
|
nls_index += 1 |
|
|
|
assert nls_index == len(nls_messages), ( |
|
f"Index mismatch: iterated {nls_index} keys but nls.messages.json has {len(nls_messages)} entries" |
|
) |
|
|
|
# 4. Write merged output as a JS file that sets the NLS globals |
|
messages_json = json.dumps(result, ensure_ascii=False) |
|
js_content = ( |
|
f"globalThis._VSCODE_NLS_MESSAGES = {messages_json};\n" |
|
f'globalThis._VSCODE_NLS_LANGUAGE = "zh-cn";\n' |
|
) |
|
OUTPUT_FILE.parent.mkdir(parents=True, exist_ok=True) |
|
OUTPUT_FILE.write_text(js_content, "utf-8") |
|
|
|
print(f"Total messages: {len(result)}") |
|
print(f"Translated: {translated_count}") |
|
print(f"English fallback: {len(result) - translated_count}") |
|
print(f"Written to: {OUTPUT_FILE}") |
|
|
|
|
|
if __name__ == "__main__": |
|
main() |