release 1.19.0: domains.conf lang column drives UI i18n default
Per-stack en|fr lives in domains.conf; load_domain_profile sets TALER_DOMAIN_LANG. FP profiles use lang=fr; workstation LANG=en no longer overrides. HTML chrome resolves lang from the same conf.
This commit is contained in:
parent
8c99e13af1
commit
61f49f02db
7 changed files with 181 additions and 75 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
1.18.9
|
||||
1.19.0
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ Git tags: `vMAJOR.FEATURE.FIX` (e.g. `v1.8.0`). File `VERSION` omits the `v` pre
|
|||
|
||||
| Tag | Date (UTC) | Notes |
|
||||
|-----|------------|--------|
|
||||
| **v1.19.0** | 2026-07-19 | **Feature:** domains.conf **`lang`** column (en\|fr) is the global UI language default per stack; i18n + HTML read `TALER_DOMAIN_LANG` / conf (FP profiles `lang=fr`). |
|
||||
| **v1.18.9** | 2026-07-19 | **Bugfix:** UI lang — *lefrancpaysan*/*francpaysan* always default **fr** unless `TALER_MON_LANG_SET=1` / `--lang`; workstation `TALER_MON_LANG=en` no longer locks English (host-agent + console_to_html). |
|
||||
| **v1.18.8** | 2026-07-19 | **Bugfix:** e2e wallet — require `taler-helper-sqlite3` on PATH; use absolute `node` + `timeout` instead of fragile `perl -e alarm` for wcli; clearer accept-uri errors (no raw alarm-shift noise). |
|
||||
| **v1.18.7** | 2026-07-19 | **Bugfix:** mail/surface catalogs — TSA/Anastasis MX is **mail.anastasis.lu** (→ pixel), not non-resolving `anastasis.taler-systems.com`; add `anastasis.lu` + pixel host probes (fixes surface_err mail.*). **Also:** FP stage host-agent defaults include **e2e** + 1800s timeout; stagepaysan.env.example wires francpaysan-secrets + wallet-cli. |
|
||||
|
|
|
|||
52
domains.conf
52
domains.conf
|
|
@ -1,48 +1,52 @@
|
|||
# taler-monitoring domain profiles
|
||||
#
|
||||
# When you add a stack, declare the three public endpoints here:
|
||||
# name bank exchange merchant-backend currency local landing [canonical]
|
||||
# name bank exchange merchant-backend currency local landing lang [canonical]
|
||||
#
|
||||
# - name: what -d / TALER_DOMAIN matches (aliases allowed as extra lines)
|
||||
# - bank / exchange / merchant: hostname or full https:// URL
|
||||
# - currency: GOA | KUDOS | CHF | … (empty = report only)
|
||||
# - currency: GOA | KUDOS | CHF | … (empty or "-" = report only)
|
||||
# - local: 1 = koopa SSH/inside/e2e stack; 0 = public-only
|
||||
# - landing: 1 = check /intro landings + assets; 0 = skip (no GOA-style landings)
|
||||
# - lang: UI language default for this profile (en|fr). Source of truth for i18n
|
||||
# when TALER_MON_LANG_SET≠1. Workstation env TALER_MON_LANG=en does not
|
||||
# override a profile that sets fr (e.g. *lefrancpaysan*).
|
||||
# - canonical: optional TALER_DOMAIN label after alias match
|
||||
#
|
||||
# CLI overrides after load: --bank --exchange --merchant --currency
|
||||
# Legacy lines without lang (old 8th field = canonical only) still parse: lang→en.
|
||||
# CLI overrides: --lang / TALER_MON_LANG_SET=1
|
||||
# Alternate file: TALER_DOMAINS_CONF=/path/to/domains.conf
|
||||
|
||||
# Local GOA (koopa) — landings matter
|
||||
koopa bank.hacktivism.ch exchange.hacktivism.ch taler.hacktivism.ch GOA 1 1 hacktivism.ch
|
||||
hacktivism bank.hacktivism.ch exchange.hacktivism.ch taler.hacktivism.ch GOA 1 1 hacktivism.ch
|
||||
hacktivism.ch bank.hacktivism.ch exchange.hacktivism.ch taler.hacktivism.ch GOA 1 1
|
||||
koopa bank.hacktivism.ch exchange.hacktivism.ch taler.hacktivism.ch GOA 1 1 en hacktivism.ch
|
||||
hacktivism bank.hacktivism.ch exchange.hacktivism.ch taler.hacktivism.ch GOA 1 1 en hacktivism.ch
|
||||
hacktivism.ch bank.hacktivism.ch exchange.hacktivism.ch taler.hacktivism.ch GOA 1 1 en
|
||||
|
||||
# Public demo (KUDOS) — has intro-style pages; keep soft landing checks
|
||||
taler.net bank.demo.taler.net exchange.demo.taler.net backend.demo.taler.net KUDOS 0 1 demo.taler.net
|
||||
demo.taler.net bank.demo.taler.net exchange.demo.taler.net backend.demo.taler.net KUDOS 0 1
|
||||
taler.net bank.demo.taler.net exchange.demo.taler.net backend.demo.taler.net KUDOS 0 1 en demo.taler.net
|
||||
demo.taler.net bank.demo.taler.net exchange.demo.taler.net backend.demo.taler.net KUDOS 0 1 en
|
||||
|
||||
# Public test stack (TESTKUDOS) — no GOA-style landings
|
||||
test.taler.net bank.test.taler.net exchange.test.taler.net backend.test.taler.net TESTKUDOS 0 0
|
||||
test.taler.net bank.test.taler.net exchange.test.taler.net backend.test.taler.net TESTKUDOS 0 0 en
|
||||
|
||||
# TOPS CHF — multi-tenant merchant my.taler-ops.ch; no public GOA landings
|
||||
taler-ops.ch bank.taler-ops.ch exchange.taler-ops.ch my.taler-ops.ch CHF 0 0
|
||||
my.taler-ops.ch bank.taler-ops.ch exchange.taler-ops.ch my.taler-ops.ch CHF 0 0 taler-ops.ch
|
||||
taler-ops.ch bank.taler-ops.ch exchange.taler-ops.ch my.taler-ops.ch CHF 0 0 en
|
||||
my.taler-ops.ch bank.taler-ops.ch exchange.taler-ops.ch my.taler-ops.ch CHF 0 0 en taler-ops.ch
|
||||
# Stage merchant FQDN is stage.my.taler-ops.ch (on betel); mon HTML /monitoring* stage-only
|
||||
stage.taler-ops.ch bank.stage.taler-ops.ch exchange.stage.taler-ops.ch stage.my.taler-ops.ch CHF 0 0
|
||||
stage.my.taler-ops.ch bank.stage.taler-ops.ch exchange.stage.taler-ops.ch stage.my.taler-ops.ch CHF 0 0 stage.taler-ops.ch
|
||||
my.stage.taler-ops.ch bank.stage.taler-ops.ch exchange.stage.taler-ops.ch stage.my.taler-ops.ch CHF 0 0 stage.taler-ops.ch
|
||||
stage.taler-ops.ch bank.stage.taler-ops.ch exchange.stage.taler-ops.ch stage.my.taler-ops.ch CHF 0 0 en
|
||||
stage.my.taler-ops.ch bank.stage.taler-ops.ch exchange.stage.taler-ops.ch stage.my.taler-ops.ch CHF 0 0 en stage.taler-ops.ch
|
||||
my.stage.taler-ops.ch bank.stage.taler-ops.ch exchange.stage.taler-ops.ch stage.my.taler-ops.ch CHF 0 0 en stage.taler-ops.ch
|
||||
|
||||
# Franc Paysan (Infomaniak) — merchant host is monnaie.* (not taler./backend.)
|
||||
# Prod: no monetary launch yet; stage uses TESTPAYSAN (public HTTPS landings + shops)
|
||||
lefrancpaysan.ch bank.lefrancpaysan.ch exchange.lefrancpaysan.ch monnaie.lefrancpaysan.ch - 0 0
|
||||
monnaie.lefrancpaysan.ch bank.lefrancpaysan.ch exchange.lefrancpaysan.ch monnaie.lefrancpaysan.ch - 0 0 lefrancpaysan.ch
|
||||
bank.lefrancpaysan.ch bank.lefrancpaysan.ch exchange.lefrancpaysan.ch monnaie.lefrancpaysan.ch - 0 0 lefrancpaysan.ch
|
||||
exchange.lefrancpaysan.ch bank.lefrancpaysan.ch exchange.lefrancpaysan.ch monnaie.lefrancpaysan.ch - 0 0 lefrancpaysan.ch
|
||||
# UI default: French (lang=fr) for all FP prod hostnames
|
||||
lefrancpaysan.ch bank.lefrancpaysan.ch exchange.lefrancpaysan.ch monnaie.lefrancpaysan.ch - 0 0 fr
|
||||
monnaie.lefrancpaysan.ch bank.lefrancpaysan.ch exchange.lefrancpaysan.ch monnaie.lefrancpaysan.ch - 0 0 fr lefrancpaysan.ch
|
||||
bank.lefrancpaysan.ch bank.lefrancpaysan.ch exchange.lefrancpaysan.ch monnaie.lefrancpaysan.ch - 0 0 fr lefrancpaysan.ch
|
||||
exchange.lefrancpaysan.ch bank.lefrancpaysan.ch exchange.lefrancpaysan.ch monnaie.lefrancpaysan.ch - 0 0 fr lefrancpaysan.ch
|
||||
|
||||
# Stage TESTPAYSAN — landings on / + farmer shops under stage.monnaie…/shops/
|
||||
# name bank exchange merchant currency local landing [canonical]
|
||||
stage.lefrancpaysan.ch stage.bank.lefrancpaysan.ch stage.exchange.lefrancpaysan.ch stage.monnaie.lefrancpaysan.ch TESTPAYSAN 0 1
|
||||
stage.bank.lefrancpaysan.ch stage.bank.lefrancpaysan.ch stage.exchange.lefrancpaysan.ch stage.monnaie.lefrancpaysan.ch TESTPAYSAN 0 1 stage.lefrancpaysan.ch
|
||||
stage.exchange.lefrancpaysan.ch stage.bank.lefrancpaysan.ch stage.exchange.lefrancpaysan.ch stage.monnaie.lefrancpaysan.ch TESTPAYSAN 0 1 stage.lefrancpaysan.ch
|
||||
stage.monnaie.lefrancpaysan.ch stage.bank.lefrancpaysan.ch stage.exchange.lefrancpaysan.ch stage.monnaie.lefrancpaysan.ch TESTPAYSAN 0 1 stage.lefrancpaysan.ch
|
||||
# UI default: French (lang=fr)
|
||||
stage.lefrancpaysan.ch stage.bank.lefrancpaysan.ch stage.exchange.lefrancpaysan.ch stage.monnaie.lefrancpaysan.ch TESTPAYSAN 0 1 fr
|
||||
stage.bank.lefrancpaysan.ch stage.bank.lefrancpaysan.ch stage.exchange.lefrancpaysan.ch stage.monnaie.lefrancpaysan.ch TESTPAYSAN 0 1 fr stage.lefrancpaysan.ch
|
||||
stage.exchange.lefrancpaysan.ch stage.bank.lefrancpaysan.ch stage.exchange.lefrancpaysan.ch stage.monnaie.lefrancpaysan.ch TESTPAYSAN 0 1 fr stage.lefrancpaysan.ch
|
||||
stage.monnaie.lefrancpaysan.ch stage.bank.lefrancpaysan.ch stage.exchange.lefrancpaysan.ch stage.monnaie.lefrancpaysan.ch TESTPAYSAN 0 1 fr stage.lefrancpaysan.ch
|
||||
|
|
|
|||
|
|
@ -75,25 +75,15 @@ export CONTINUE_ON_ERROR="${CONTINUE_ON_ERROR:-1}"
|
|||
export AUTH401_CONTINUE="${AUTH401_CONTINUE:-${CONTINUE_ON_ERROR}}"
|
||||
export RUN_TIMEOUT="${RUN_TIMEOUT:-600}"
|
||||
export TALER_DOMAIN="${TALER_DOMAIN:-hacktivism.ch}"
|
||||
# UI language: --lang / TALER_MON_LANG_SET=1 locks; otherwise auto by domain.
|
||||
# Bare TALER_MON_LANG=en in workstation env (clementine) must NOT lock language —
|
||||
# *lefrancpaysan* / *francpaysan* always default to fr when SET≠1 (global rule).
|
||||
# UI language: --lang / TALER_MON_LANG_SET=1 locks.
|
||||
# Otherwise domains.conf column `lang` → TALER_DOMAIN_LANG → TALER_MON_LANG
|
||||
# (applied in suite apply_taler_domain / i18n_init). Leave SET=0 so -d can re-apply.
|
||||
# Bare workstation TALER_MON_LANG=en must not override profile lang=fr.
|
||||
if [ "${TALER_MON_LANG_SET:-0}" = "1" ]; then
|
||||
case "${TALER_MON_LANG:-en}" in
|
||||
fr|FR|fra|french) export TALER_MON_LANG=fr ;;
|
||||
*) export TALER_MON_LANG=en ;;
|
||||
esac
|
||||
else
|
||||
case "$TALER_DOMAIN" in
|
||||
*lefrancpaysan*|*francpaysan*) export TALER_MON_LANG=fr ;;
|
||||
*)
|
||||
case "${TALER_MON_LANG:-}" in
|
||||
fr|FR|fra|french) export TALER_MON_LANG=fr ;;
|
||||
*) export TALER_MON_LANG=en ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
# leave TALER_MON_LANG_SET unset/0 so suite i18n_init can re-apply per -d domain
|
||||
fi
|
||||
export TALER_MON_LANG
|
||||
|
||||
|
|
|
|||
39
i18n.sh
39
i18n.sh
|
|
@ -3,31 +3,37 @@
|
|||
#
|
||||
# TALER_MON_LANG=en|fr
|
||||
# TALER_MON_LANG_SET=1 → language is explicit (--lang only); do not override.
|
||||
# Auto (SET≠1): domain *lefrancpaysan* / *francpaysan* → fr always (global).
|
||||
# else keep env or en.
|
||||
# Auto (SET≠1), in order:
|
||||
# 1) TALER_DOMAIN_LANG from domains.conf profile (preferred — source of truth)
|
||||
# 2) fallback hostname heuristic *lefrancpaysan*/*francpaysan* → fr
|
||||
# 3) else keep workstation env TALER_MON_LANG or en
|
||||
#
|
||||
# Important: a bare TALER_MON_LANG=en in ~/.config/taler-monitoring/env is only the
|
||||
# workstation default for non-FP domains. It must NEVER lock English on FP
|
||||
# (*lefrancpaysan* / *francpaysan*) — local laptop and host-agent both follow this.
|
||||
# domains.conf column `lang` (see suite domains.conf) is the global definition.
|
||||
# Bare TALER_MON_LANG=en in ~/.config must not override a profile lang=fr.
|
||||
|
||||
: "${TALER_MON_LANG:=}"
|
||||
: "${TALER_MON_LANG_SET:=0}"
|
||||
: "${TALER_DOMAIN_LANG:=}"
|
||||
|
||||
i18n_init() {
|
||||
# Only TALER_MON_LANG_SET=1 is "explicit". Do not promote non-empty TALER_MON_LANG
|
||||
# to SET=1 — env files often ship LANG=en for the default GOA workstation.
|
||||
if [ "${TALER_MON_LANG_SET:-0}" != "1" ]; then
|
||||
case "${TALER_DOMAIN:-}" in
|
||||
*lefrancpaysan*|*francpaysan*)
|
||||
TALER_MON_LANG=fr
|
||||
;;
|
||||
*)
|
||||
# Non-FP: keep pre-set env value if any, else English
|
||||
if [ -z "${TALER_MON_LANG:-}" ]; then
|
||||
TALER_MON_LANG=en
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
if [ -n "${TALER_DOMAIN_LANG:-}" ]; then
|
||||
TALER_MON_LANG="$TALER_DOMAIN_LANG"
|
||||
else
|
||||
case "${TALER_DOMAIN:-}" in
|
||||
*lefrancpaysan*|*francpaysan*)
|
||||
# fallback if domain not (yet) in domains.conf
|
||||
TALER_MON_LANG=fr
|
||||
;;
|
||||
*)
|
||||
if [ -z "${TALER_MON_LANG:-}" ]; then
|
||||
TALER_MON_LANG=en
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
case "${TALER_MON_LANG}" in
|
||||
fr|FR|fra|french) TALER_MON_LANG=fr ;;
|
||||
|
|
@ -35,6 +41,7 @@ i18n_init() {
|
|||
esac
|
||||
export TALER_MON_LANG
|
||||
export TALER_MON_LANG_SET
|
||||
export TALER_DOMAIN_LANG
|
||||
}
|
||||
|
||||
i18n_tag() {
|
||||
|
|
|
|||
35
lib.sh
35
lib.sh
|
|
@ -166,13 +166,24 @@ set_taler_stack() {
|
|||
fi
|
||||
}
|
||||
|
||||
# Normalize domains.conf lang token → en|fr (empty if not a lang token).
|
||||
_domain_lang_token() {
|
||||
case "${1:-}" in
|
||||
en|EN|eng|english) printf 'en' ;;
|
||||
fr|FR|fra|french) printf 'fr' ;;
|
||||
*) printf '' ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Load first matching profile from domains.conf.
|
||||
# Fields (whitespace-separated; # comments; blank lines ignored):
|
||||
# name bank exchange merchant currency local[0|1] landing[0|1] [canonical_domain]
|
||||
# name bank exchange merchant currency local[0|1] landing[0|1] lang[en|fr] [canonical]
|
||||
# Legacy: 8th field may be canonical only (no lang) — lang defaults to en.
|
||||
# Sets TALER_DOMAIN_LANG from the profile (i18n source of truth when SET≠1).
|
||||
# Returns 0 if found, 1 if not.
|
||||
load_domain_profile() {
|
||||
local want="$1" conf="${TALER_DOMAINS_CONF:-}"
|
||||
local line name bank exchange merchant currency local_stack landing canon
|
||||
local line name bank exchange merchant currency local_stack landing canon profile_lang f8 f9
|
||||
[ -n "$want" ] || return 1
|
||||
[ -n "$conf" ] && [ -f "$conf" ] || return 1
|
||||
|
||||
|
|
@ -189,12 +200,30 @@ load_domain_profile() {
|
|||
currency="$5"
|
||||
local_stack="${6:-0}"
|
||||
landing="${7:-}"
|
||||
canon="${8:-$name}"
|
||||
f8="${8:-}"
|
||||
f9="${9:-}"
|
||||
canon="$name"
|
||||
profile_lang=""
|
||||
# Backward compat: old 7th field was canonical domain (contains a dot)
|
||||
if [ -n "$landing" ] && [[ "$landing" == *.* && "$landing" != "0" && "$landing" != "1" ]]; then
|
||||
canon="$landing"
|
||||
landing=""
|
||||
f8="${7:-}"
|
||||
f9="${8:-}"
|
||||
fi
|
||||
if [ -n "$f8" ]; then
|
||||
profile_lang=$(_domain_lang_token "$f8")
|
||||
if [ -n "$profile_lang" ]; then
|
||||
canon="${f9:-$name}"
|
||||
else
|
||||
# legacy: field 8 = canonical, optional field 9 = lang
|
||||
canon="$f8"
|
||||
profile_lang=$(_domain_lang_token "$f9")
|
||||
fi
|
||||
fi
|
||||
[ -z "$profile_lang" ] && profile_lang=en
|
||||
TALER_DOMAIN_LANG="$profile_lang"
|
||||
export TALER_DOMAIN_LANG
|
||||
set_taler_stack "$bank" "$exchange" "$merchant" "$currency" "$local_stack" "$landing" "$canon"
|
||||
return 0
|
||||
done <"$conf"
|
||||
|
|
|
|||
|
|
@ -45,35 +45,110 @@ def atomic_write_text(path: Path, text: str, encoding: str = "utf-8") -> None:
|
|||
except OSError:
|
||||
pass
|
||||
|
||||
def _norm_lang(v: str) -> str:
|
||||
v = (v or "").strip().lower()
|
||||
if v in ("fr", "fra", "french"):
|
||||
return "fr"
|
||||
if v in ("en", "eng", "english"):
|
||||
return "en"
|
||||
return ""
|
||||
|
||||
|
||||
def domain_lang_from_conf(host_hint: str = "") -> str:
|
||||
"""Look up UI lang from domains.conf (suite profile `lang` column).
|
||||
|
||||
Matches host_hint against profile name, bank, exchange, merchant, or
|
||||
canonical fields. Returns '' if no profile matches.
|
||||
"""
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
hint = (host_hint or "").lower()
|
||||
if not hint.strip():
|
||||
return ""
|
||||
|
||||
conf = os.environ.get("TALER_DOMAINS_CONF") or ""
|
||||
candidates = []
|
||||
if conf:
|
||||
candidates.append(Path(conf))
|
||||
# suite root next to site-gen/
|
||||
here = Path(__file__).resolve().parent
|
||||
candidates.append(here.parent / "domains.conf")
|
||||
# cwd / mon root
|
||||
candidates.append(Path("domains.conf"))
|
||||
|
||||
path = next((p for p in candidates if p.is_file()), None)
|
||||
if path is None:
|
||||
return ""
|
||||
|
||||
best = ""
|
||||
try:
|
||||
text = path.read_text(encoding="utf-8", errors="replace")
|
||||
except OSError:
|
||||
return ""
|
||||
|
||||
for raw in text.splitlines():
|
||||
line = raw.split("#", 1)[0].strip()
|
||||
if not line:
|
||||
continue
|
||||
parts = line.split()
|
||||
if len(parts) < 5:
|
||||
continue
|
||||
name, bank, exchange, merchant = parts[0], parts[1], parts[2], parts[3]
|
||||
# fields: … local landing lang [canonical]
|
||||
lang = ""
|
||||
canon = name
|
||||
if len(parts) >= 8:
|
||||
tok = _norm_lang(parts[7])
|
||||
if tok:
|
||||
lang = tok
|
||||
if len(parts) >= 9:
|
||||
canon = parts[8]
|
||||
else:
|
||||
# legacy: field 8 = canonical
|
||||
canon = parts[7]
|
||||
if len(parts) >= 9:
|
||||
lang = _norm_lang(parts[8])
|
||||
if not lang:
|
||||
lang = "en"
|
||||
hosts = {name.lower(), bank.lower(), exchange.lower(), merchant.lower(), canon.lower()}
|
||||
# strip scheme if present
|
||||
hosts = {re.sub(r"^https?://", "", h).split("/")[0] for h in hosts}
|
||||
if any(h and h in hint for h in hosts):
|
||||
# prefer longer / more specific name match later — keep last match
|
||||
# that appears in conf order; FP aliases all share fr.
|
||||
best = lang
|
||||
if name.lower() in hint or canon.lower() in hint:
|
||||
return lang
|
||||
return best
|
||||
|
||||
|
||||
def ui_lang(explicit: str = "", host_hint: str = "") -> str:
|
||||
"""en (default) or fr.
|
||||
|
||||
- TALER_MON_LANG_SET=1 → honour TALER_MON_LANG / explicit only (no host auto).
|
||||
- Else: any *lefrancpaysan* / *francpaysan* host or log hint → fr
|
||||
(workstation env TALER_MON_LANG=en must not force English on FP domains).
|
||||
- Else: explicit / env / en.
|
||||
- TALER_MON_LANG_SET=1 → honour TALER_MON_LANG / explicit only.
|
||||
- Else TALER_DOMAIN_LANG env (set by load_domain_profile).
|
||||
- Else domains.conf `lang` for host_hint.
|
||||
- Else fallback *lefrancpaysan*/*francpaysan* → fr.
|
||||
- Else explicit / env / en.
|
||||
"""
|
||||
import os
|
||||
|
||||
env_lang = (os.environ.get("TALER_MON_LANG") or "").strip().lower()
|
||||
domain_lang = _norm_lang(os.environ.get("TALER_DOMAIN_LANG") or "")
|
||||
lang = (explicit or env_lang or "").strip().lower()
|
||||
locked = (os.environ.get("TALER_MON_LANG_SET") or "").strip() == "1"
|
||||
is_fp = bool(
|
||||
re.search(r"lefrancpaysan|francpaysan", host_hint or "", re.I)
|
||||
)
|
||||
|
||||
def _norm(v: str) -> str:
|
||||
if v in ("fr", "fra", "french"):
|
||||
return "fr"
|
||||
if v in ("en", "eng", "english"):
|
||||
return "en"
|
||||
return ""
|
||||
|
||||
if locked:
|
||||
return _norm(lang) or "en"
|
||||
if is_fp:
|
||||
return _norm_lang(lang) or "en"
|
||||
if domain_lang:
|
||||
return domain_lang
|
||||
conf_lang = domain_lang_from_conf(host_hint)
|
||||
if conf_lang:
|
||||
return conf_lang
|
||||
if re.search(r"lefrancpaysan|francpaysan", host_hint or "", re.I):
|
||||
return "fr"
|
||||
return _norm(lang) or "en"
|
||||
return _norm_lang(lang) or "en"
|
||||
|
||||
|
||||
def ui(lang: str, key: str, **kwargs) -> str:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue