160 lines
6.3 KiB
Python
160 lines
6.3 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
from typing import Any, Iterable
|
|
|
|
I18N_SETTINGS_KEY = "i18n"
|
|
REFERENCE_LANGUAGE_CODE = "de"
|
|
SOURCE_LANGUAGE_CODE = "en"
|
|
|
|
DEFAULT_LANGUAGE_PACKAGES: tuple[dict[str, str], ...] = (
|
|
{"code": "de", "label": "German", "native_label": "Deutsch"},
|
|
{"code": "en", "label": "English", "native_label": "English"},
|
|
)
|
|
|
|
|
|
def normalize_language_code(value: object) -> str:
|
|
raw = str(value or "").strip().lower().replace("_", "-")
|
|
parts = [part for part in re.split(r"[^a-z0-9]+", raw) if part]
|
|
return "-".join(parts)
|
|
|
|
|
|
def language_code_candidates(value: object) -> tuple[str, ...]:
|
|
code = normalize_language_code(value)
|
|
if not code:
|
|
return ()
|
|
primary = code.split("-", 1)[0]
|
|
return tuple(dict.fromkeys((code, primary)))
|
|
|
|
|
|
def resolve_language_code(value: object, allowed_codes: Iterable[str]) -> str:
|
|
allowed = {normalize_language_code(code) for code in allowed_codes if normalize_language_code(code)}
|
|
for candidate in language_code_candidates(value):
|
|
if candidate in allowed:
|
|
return candidate
|
|
return ""
|
|
|
|
|
|
def i18n_settings(settings: dict[str, Any] | None) -> dict[str, Any]:
|
|
raw = (settings or {}).get(I18N_SETTINGS_KEY)
|
|
return dict(raw) if isinstance(raw, dict) else {}
|
|
|
|
|
|
def normalize_language_packages(raw_packages: object = None) -> list[dict[str, str]]:
|
|
packages: dict[str, dict[str, str]] = {}
|
|
for item in DEFAULT_LANGUAGE_PACKAGES:
|
|
packages[item["code"]] = dict(item)
|
|
|
|
if isinstance(raw_packages, (list, tuple)):
|
|
for raw in raw_packages:
|
|
if not isinstance(raw, dict):
|
|
continue
|
|
code = normalize_language_code(raw.get("code"))
|
|
if not code:
|
|
continue
|
|
label = str(raw.get("label") or code.upper()).strip()
|
|
native_label = str(raw.get("native_label") or raw.get("nativeLabel") or label).strip()
|
|
packages[code] = {"code": code, "label": label, "native_label": native_label}
|
|
|
|
return list(packages.values())
|
|
|
|
|
|
def system_language_packages(settings: dict[str, Any] | None) -> list[dict[str, str]]:
|
|
return normalize_language_packages(i18n_settings(settings).get("available_languages"))
|
|
|
|
|
|
def normalize_enabled_language_codes(
|
|
codes: object,
|
|
available_languages: Iterable[dict[str, Any]],
|
|
*,
|
|
default_locale: object = None,
|
|
fallback_codes: Iterable[str] = (REFERENCE_LANGUAGE_CODE, SOURCE_LANGUAGE_CODE),
|
|
) -> list[str]:
|
|
available_codes = [normalize_language_code(item.get("code")) for item in available_languages if isinstance(item, dict)]
|
|
available = {code for code in available_codes if code}
|
|
requested: list[str] = []
|
|
if isinstance(codes, (list, tuple, set)):
|
|
requested = [normalize_language_code(item) for item in codes]
|
|
if not requested:
|
|
requested = [normalize_language_code(item) for item in fallback_codes]
|
|
|
|
enabled: list[str] = []
|
|
for item in requested:
|
|
code = resolve_language_code(item, available)
|
|
if code and code not in enabled:
|
|
enabled.append(code)
|
|
|
|
default_code = resolve_language_code(default_locale, available)
|
|
if default_code and default_code not in enabled:
|
|
enabled.insert(0, default_code)
|
|
|
|
if enabled:
|
|
return enabled
|
|
if REFERENCE_LANGUAGE_CODE in available:
|
|
return [REFERENCE_LANGUAGE_CODE]
|
|
if SOURCE_LANGUAGE_CODE in available:
|
|
return [SOURCE_LANGUAGE_CODE]
|
|
return available_codes[:1]
|
|
|
|
|
|
def system_enabled_language_codes(settings: dict[str, Any] | None, *, default_locale: object = None) -> list[str]:
|
|
i18n = i18n_settings(settings)
|
|
raw_enabled = i18n.get("enabled_language_codes", i18n.get("enabled_languages"))
|
|
return normalize_enabled_language_codes(raw_enabled, system_language_packages(settings), default_locale=default_locale)
|
|
|
|
|
|
def tenant_enabled_language_codes(
|
|
tenant_settings: dict[str, Any] | None,
|
|
system_enabled_codes: Iterable[str],
|
|
*,
|
|
default_locale: object = None,
|
|
) -> list[str]:
|
|
enabled = [normalize_language_code(code) for code in system_enabled_codes if normalize_language_code(code)]
|
|
available = [{"code": code} for code in enabled]
|
|
raw_enabled = i18n_settings(tenant_settings).get("enabled_language_codes")
|
|
return normalize_enabled_language_codes(raw_enabled, available, default_locale=default_locale, fallback_codes=enabled)
|
|
|
|
|
|
def user_enabled_language_codes(user_settings: dict[str, Any] | None, tenant_enabled_codes: Iterable[str]) -> list[str]:
|
|
enabled = [normalize_language_code(code) for code in tenant_enabled_codes if normalize_language_code(code)]
|
|
available = [{"code": code} for code in enabled]
|
|
raw_enabled = i18n_settings(user_settings).get("enabled_language_codes")
|
|
return normalize_enabled_language_codes(raw_enabled, available, fallback_codes=enabled)
|
|
|
|
|
|
def preferred_language_code(user_settings: dict[str, Any] | None, allowed_codes: Iterable[str], *, default_locale: object = None) -> str:
|
|
enabled = [normalize_language_code(code) for code in allowed_codes if normalize_language_code(code)]
|
|
preferred = resolve_language_code(i18n_settings(user_settings).get("preferred_language"), enabled)
|
|
if preferred:
|
|
return preferred
|
|
default = resolve_language_code(default_locale, enabled)
|
|
if default:
|
|
return default
|
|
return enabled[0] if enabled else REFERENCE_LANGUAGE_CODE
|
|
|
|
|
|
def update_i18n_settings(settings: dict[str, Any] | None, **values: Any) -> dict[str, Any]:
|
|
updated = dict(settings or {})
|
|
current = i18n_settings(updated)
|
|
current.update(values)
|
|
updated[I18N_SETTINGS_KEY] = current
|
|
return updated
|
|
|
|
|
|
def system_i18n_payload(settings_item: Any | None) -> dict[str, object]:
|
|
settings = getattr(settings_item, "settings", None)
|
|
packages = system_language_packages(settings)
|
|
available_codes = [item["code"] for item in packages]
|
|
default_language = (
|
|
resolve_language_code(getattr(settings_item, "default_locale", None), available_codes)
|
|
or REFERENCE_LANGUAGE_CODE
|
|
)
|
|
enabled = system_enabled_language_codes(settings, default_locale=default_language)
|
|
if default_language not in enabled:
|
|
default_language = enabled[0] if enabled else REFERENCE_LANGUAGE_CODE
|
|
return {
|
|
"available_languages": packages,
|
|
"enabled_languages": enabled,
|
|
"default_language": default_language,
|
|
}
|