from __future__ import annotations import os.path import re import typing import quart from .app import app _VALID_LANGUAGE_NAMES = re.compile(r'^[-a-z0-9_]+$') def parse_accept_language(value: str) -> typing.List[str]: value = value.strip() if not value or value == '*': return [] tuples = [] for entry in value.split(','): attrs = entry.split(';') name = attrs.pop(0).strip().lower() q = 1.0 for attr in attrs: if not '=' in attr: continue key, value = attr.split('=', maxsplit=1) if key.strip().lower() == 'q': try: q = float(value.strip()) except ValueError: q = 0.0 if q >= 0.0: tuples.append((q, name)) tuples.sort() have = set() result = [] for (_q, name) in tuples: if name in have: continue if name == '*': break have.add(name) if _VALID_LANGUAGE_NAMES.match(name): result.append(name) short_name = name.split('-', maxsplit=1)[0].split('_', maxsplit=1)[0] if not short_name or short_name in have: continue have.add(short_name) result.append(short_name) return result @app.before_request def detect_language(): g = quart.g r = quart.request s = quart.session if 'setlang' in r.args: lang = r.args.get('setlang').strip().lower() if lang and _VALID_LANGUAGE_NAMES.match(lang): if s.get('lang') != lang: s['lang'] = lang g.langs = [lang] return else: # reset language s.pop('lang', None) lang = s.get('lang') if lang: lang = lang.strip().lower() if lang and _VALID_LANGUAGE_NAMES.match(lang): g.langs = [lang] return acc_lang = ','.join(r.headers.get_all('Accept-Language')) g.langs = parse_accept_language(acc_lang) async def render_i18n_template(template, /, **kwargs) -> str: langs: typing.List[str] = quart.g.langs if not langs: return await quart.render_template(template, **kwargs) names = [ os.path.join('i18n', lang, template) for lang in langs ] names.append(template) return await quart.render_template(names, **kwargs)