support i18n
This commit is contained in:
parent
99db542326
commit
71e7fe7b09
11
README.md
11
README.md
@ -1,6 +1,6 @@
|
||||
# python Captive Portal
|
||||
|
||||
### Installation
|
||||
## Installation
|
||||
|
||||
Either clone repository (and install dependencies either through distribution or as virtualenv with `./setup-venv.sh`) or install as package.
|
||||
|
||||
@ -8,3 +8,12 @@ Either clone repository (and install dependencies either through distribution or
|
||||
|
||||
pipx install https://github.tik.uni-stuttgart.de/NKS/python-capport
|
||||
|
||||
In production put a reverse proxy in front of the local web ui (on 127.0.0.1:8000), and handle `/static` path either to `src/capport/api/static/` or your customized version of static files.
|
||||
|
||||
## Customization
|
||||
|
||||
Create `custom/templates` and put customized templates (from `src/capport/api/templates`) there.
|
||||
|
||||
Create `i18n/<langcode>` folders to put localized templates into.
|
||||
|
||||
Requests with a `setlang=<langcode>` query parameter will set the language and try to store the choice in a session cookie.
|
||||
|
@ -5,4 +5,5 @@ app = MyQuartApp(__name__)
|
||||
|
||||
|
||||
__import__('capport.api.setup')
|
||||
__import__('capport.api.lang')
|
||||
__import__('capport.api.views')
|
||||
|
@ -18,6 +18,26 @@ class DispatchingJinjaLoader(quart.templating.DispatchingJinjaLoader):
|
||||
def __init__(self, app: MyQuartApp) -> None:
|
||||
super().__init__(app)
|
||||
|
||||
def get_source(
|
||||
self, environment: jinja2.Environment, template: str
|
||||
) -> typing.Tuple[str, typing.Optional[str], typing.Optional[typing.Callable[[], bool]]]:
|
||||
"""Returns the template source from the environment.
|
||||
|
||||
This considers the loaders on the :attr:`app` and blueprints.
|
||||
"""
|
||||
langs: typing.List[str] = quart.g.langs
|
||||
for loader in self._loaders():
|
||||
for lang in langs:
|
||||
try:
|
||||
return loader.get_source(environment, os.path.join('i18n', lang, template))
|
||||
except jinja2.TemplateNotFound:
|
||||
continue
|
||||
try:
|
||||
return loader.get_source(environment, template)
|
||||
except jinja2.TemplateNotFound:
|
||||
continue
|
||||
raise jinja2.TemplateNotFound(template)
|
||||
|
||||
def _loaders(self) -> typing.Generator[jinja2.BaseLoader, None, None]:
|
||||
if self.app.custom_loader:
|
||||
yield self.app.custom_loader
|
||||
|
67
src/capport/api/lang.py
Normal file
67
src/capport/api/lang.py
Normal file
@ -0,0 +1,67 @@
|
||||
from __future__ import annotations
|
||||
|
||||
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
|
||||
lang = r.args.get('setlang')
|
||||
if lang:
|
||||
lang = lang.strip().lower()
|
||||
if lang and _VALID_LANGUAGE_NAMES.match(lang):
|
||||
s = quart.session
|
||||
s['lang'] = lang
|
||||
g.langs = [lang]
|
||||
return
|
||||
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)
|
Loading…
Reference in New Issue
Block a user