43 lines
2.5 KiB
Python
Executable File
43 lines
2.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Validate language-pack content and emit the same-shaped bundle used by UI QA.
|
|
Rust build.rs performs the actual compile-time discovery and embedding.
|
|
"""
|
|
from pathlib import Path
|
|
import argparse
|
|
import json
|
|
import re
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
def bundle(root=ROOT):
|
|
languages=[];locales=set()
|
|
for path in sorted((root/'langs').glob('*.json')):
|
|
assert re.fullmatch(r'[A-Za-z0-9_-]{1,64}',path.stem),path
|
|
assert path.stat().st_size<=262144,path
|
|
pack=json.loads(path.read_text(encoding='utf-8'));assert set(pack)=={'meta','translations'},f'unexpected top-level language-pack keys: {set(pack)-{"meta","translations"}} in {path}';meta=pack['meta'];translations=pack['translations']
|
|
assert all(isinstance(meta.get(key),str) and 0<len(meta[key].encode())<=128 for key in ['locale','name','native_name','direction']),path
|
|
assert meta['direction'] in ('ltr','rtl'),path
|
|
assert re.fullmatch(r'[A-Za-z0-9]{1,8}(?:-[A-Za-z0-9]{1,8})*',meta['locale']),path
|
|
assert meta['locale'].lower() not in locales, 'duplicate locale'
|
|
locales.add(meta['locale'].lower())
|
|
assert isinstance(translations,dict) and len(translations)<=4096,path
|
|
assert all(isinstance(v,str) and 0<len(k.encode())<=128 and len(v.encode())<=16384 for k,v in translations.items()),path
|
|
languages.append({'id':path.stem,**pack})
|
|
assert 1<=len(languages)<=128
|
|
fallback=next(pack for pack in languages if pack['id']=='en_US')['translations']
|
|
html=(root/'src/admin/web/index.html').read_text()
|
|
keys=set(re.findall(r'data-i18n(?:-placeholder|-aria)?="([^"]+)"',html))
|
|
js='\n'.join(p.read_text() for p in (root/'src/admin/web').glob('**/*.js'))
|
|
keys.update(re.findall(r'\bt\("([^"\n]+)"\)',js))
|
|
keys.update(re.findall(r'"(vh[A-Z][A-Za-z]+)"',js))
|
|
assert keys<=fallback.keys(),f'Missing fallback keys: {keys-fallback.keys()}'
|
|
for language in languages:
|
|
translations=language['translations']
|
|
assert keys<=translations.keys(),f"Missing UI keys in {language['id']}: {keys-translations.keys()}"
|
|
result={'default':'en_US','languages':languages}
|
|
assert len(json.dumps(result,ensure_ascii=False).encode())<=8388608
|
|
return result
|
|
if __name__=='__main__':
|
|
parser=argparse.ArgumentParser();parser.add_argument('--output',type=Path);args=parser.parse_args()
|
|
data=bundle()
|
|
if args.output:args.output.write_text(json.dumps(data,ensure_ascii=False)+'\n')
|
|
print(f"PASS: {len(data['languages'])} language packs, metadata, fallback UI keys and bounds.")
|