"""Server functions for loading translations """ import errno import io import json import re from collections import defaultdict from os.path import dirname from os.path import join as pjoin I18N_DIR = dirname(__file__) # Cache structure: # {'nbjs': { # Domain # 'zh-CN': { # Language code # : # ... # } # }} TRANSLATIONS_CACHE = {"nbjs": {}} _accept_lang_re = re.compile( r""" (?P[a-zA-Z]{1,8}(-[a-zA-Z]{1,8})?) (\s*;\s*q\s*=\s* (?P[01](.\d+)?) )?""", re.VERBOSE, ) def parse_accept_lang_header(accept_lang): """Parses the 'Accept-Language' HTTP header. Returns a list of language codes in *ascending* order of preference (with the most preferred language last). """ by_q = defaultdict(list) for part in accept_lang.split(","): m = _accept_lang_re.match(part.strip()) if not m: continue lang, qvalue = m.group("lang", "qvalue") # Browser header format is zh-CN, gettext uses zh_CN lang = lang.replace("-", "_") if qvalue is None: qvalue = 1.0 else: qvalue = float(qvalue) if qvalue == 0: continue # 0 means not accepted by_q[qvalue].append(lang) res = [] for qvalue, langs in sorted(by_q.items()): res.extend(sorted(langs)) return res def load(language, domain="nbjs"): """Load translations from an nbjs.json file""" try: f = io.open(pjoin(I18N_DIR, language, "LC_MESSAGES", "nbjs.json"), encoding="utf-8") except IOError as e: if e.errno != errno.ENOENT: raise return {} with f: data = json.load(f) return data["locale_data"][domain] def cached_load(language, domain="nbjs"): """Load translations for one language, using in-memory cache if available""" domain_cache = TRANSLATIONS_CACHE[domain] try: return domain_cache[language] except KeyError: data = load(language, domain) domain_cache[language] = data return data def combine_translations(accept_language, domain="nbjs"): """Combine translations for multiple accepted languages. Returns data re-packaged in jed1.x format. """ lang_codes = parse_accept_lang_header(accept_language) combined = {} for language in lang_codes: if language == "en": # en is default, all translations are in frontend. combined.clear() else: combined.update(cached_load(language, domain)) combined[""] = {"domain": "nbjs"} return {"domain": domain, "locale_data": {domain: combined}}