|
"""Server functions for loading translations""" |
|
|
|
from __future__ import annotations |
|
|
|
import errno |
|
import json |
|
import re |
|
from collections import defaultdict |
|
from os.path import dirname |
|
from os.path import join as pjoin |
|
from typing import Any |
|
|
|
I18N_DIR = dirname(__file__) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TRANSLATIONS_CACHE: dict[str, Any] = {"nbjs": {}} |
|
|
|
|
|
_accept_lang_re = re.compile( |
|
r""" |
|
(?P<lang>[a-zA-Z]{1,8}(-[a-zA-Z]{1,8})?) |
|
(\s*;\s*q\s*=\s* |
|
(?P<qvalue>[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") |
|
|
|
lang = lang.replace("-", "_") |
|
qvalue = 1.0 if qvalue is None else float(qvalue) |
|
if qvalue == 0: |
|
continue |
|
by_q[qvalue].append(lang) |
|
|
|
res = [] |
|
for _, 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 = open(pjoin(I18N_DIR, language, "LC_MESSAGES", "nbjs.json"), encoding="utf-8") |
|
except OSError 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: dict[str, Any] = {} |
|
for language in lang_codes: |
|
if language == "en": |
|
|
|
combined.clear() |
|
else: |
|
combined.update(cached_load(language, domain)) |
|
|
|
combined[""] = {"domain": "nbjs"} |
|
|
|
return {"domain": domain, "locale_data": {domain: combined}} |
|
|