From 840cbe4729ac8960c0dc0d23aca0405335f9ecb4 Mon Sep 17 00:00:00 2001 From: Evgeny Zinoviev Date: Mon, 19 Feb 2024 03:44:40 +0300 Subject: user language support, other important fixes --- bin/web_kbn.py | 57 +++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 16 deletions(-) (limited to 'bin') diff --git a/bin/web_kbn.py b/bin/web_kbn.py index 18b50ad..21ae2ef 100644 --- a/bin/web_kbn.py +++ b/bin/web_kbn.py @@ -14,7 +14,8 @@ from io import StringIO from aiohttp import web from typing import Optional, Union from urllib.parse import quote_plus -from homekit.config import config, AppConfigUnit, is_development_mode, Translation +from contextvars import ContextVar +from homekit.config import config, AppConfigUnit, is_development_mode, Translation, Language from homekit.camera import IpcamConfig from homekit.util import homekit_path, filesize_fmt, seconds_to_human_readable_string, json_serial, validate_ipv4 from homekit.modem import E3372, ModemsConfig, MacroNetWorkType @@ -45,10 +46,10 @@ common_static_files = [ 'app.js', 'app.css' ] -static_version = 3 +static_version = 4 routes = web.RouteTableDef() -webkbn_strings = Translation('web_kbn') logger = logging.getLogger(__name__) +lang_context_var = ContextVar('lang', default=Translation.DEFAULT_LANGUAGE) def get_js_link(file, version=static_version) -> str: @@ -201,8 +202,29 @@ def get_current_upstream() -> str: return upstream -def lang(key: str): - return webkbn_strings.get()[key] +def get_preferred_lang(req: web.Request) -> Language: + lang_cookie = req.cookies.get('lang', None) + if lang_cookie is None: + return Translation.DEFAULT_LANGUAGE + try: + return Language(lang_cookie) + except ValueError: + logger.debug(f"unsupported lang_cookie value: {lang_cookie}") + return Translation.DEFAULT_LANGUAGE + + +@web.middleware +async def language_middleware(request, handler): + lang_context_var.set(get_preferred_lang(request)) + return await handler(request) + + +def lang(key, unit='web_kbn'): + strings = Translation(unit) + if isinstance(key, str) and '.' in key: + return strings.get(lang_context_var.get()).get(key) + else: + return strings.get(lang_context_var.get())[key] async def render(req: web.Request, @@ -214,7 +236,8 @@ async def render(req: web.Request, context = {} context = { **context, - 'head_static': get_head_static(assets) + 'head_static': get_head_static(assets), + 'user_lang': lang_context_var.get().value } if title is not None: context['title'] = title @@ -231,6 +254,8 @@ async def index(req: web.Request): cc = IpcamConfig() ctx['camzones'] = cc['zones'].keys() ctx['allcams'] = cc.get_all_cam_names() + ctx['lang_enum'] = Language + ctx['lang_selected'] = lang_context_var.get() return await render(req, 'index', title=lang('sitename'), @@ -240,7 +265,7 @@ async def index(req: web.Request): @routes.get('/modems.cgi') async def modems(req: web.Request): return await render(req, 'modems', - title='Состояние модемов', + title=lang('modem_statuses'), context=dict(modems=ModemsConfig())) @@ -280,9 +305,9 @@ async def modems_verbose(req: web.Request): ['Dialup connection', dialup_conn] ] - modem_name = ModemsConfig().getfullname(modem) + modem_name = Translation('modems').get(lang_context_var.get())[modem]['full'] return await render(req, 'modem_verbose', - title=f'Подробная информация о модеме "{modem_name}"', + title=lang('modem_verbose_info_about_modem') % (modem_name,), context=dict(data=data, modem_name=modem_name)) @@ -296,7 +321,7 @@ async def sms(req: web.Request): cl = get_modem_client(ModemsConfig()[modem]) messages = cl.sms_list(1, 20, is_outbox) return await render(req, 'sms', - title=f"SMS-сообщения ({'исходящие' if is_outbox else 'входящие'}, {modem})", + title=lang('sms_page_title') % (lang('sms_outbox') if is_outbox else lang('sms_inbox'), modem), context=dict( modems=ModemsConfig(), selected_modem=modem, @@ -512,6 +537,7 @@ async def routing_dhcp(req: web.Request): def init_web_app(app: web.Application): + app.middlewares.append(language_middleware) aiohttp_jinja2.setup( app, loader=jinja2.FileSystemLoader(homekit_path('web', 'kbn_templates')), @@ -519,12 +545,11 @@ def init_web_app(app: web.Application): ) env = aiohttp_jinja2.get_env(app) - def filter_lang(key, unit='web_kbn'): - strings = Translation(unit) - if isinstance(key, str) and '.' in key: - return strings.get().get(key) - else: - return strings.get()[key] + # @pass_context is used only to prevent jinja2 from caching the result of lang filter results of constant values. + # as of now i don't know a better way of doing it + @jinja2.pass_context + def filter_lang(ctx, key, unit='web_kbn'): + return lang(key, unit) env.filters['tojson'] = lambda obj: json.dumps(obj, separators=(',', ':'), default=json_serial) env.filters['lang'] = filter_lang -- cgit v1.2.3