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 |
|
| 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.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.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. |
|
| **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
|
# taler-monitoring domain profiles
|
||||||
#
|
#
|
||||||
# When you add a stack, declare the three public endpoints here:
|
# 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)
|
# - name: what -d / TALER_DOMAIN matches (aliases allowed as extra lines)
|
||||||
# - bank / exchange / merchant: hostname or full https:// URL
|
# - 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
|
# - local: 1 = koopa SSH/inside/e2e stack; 0 = public-only
|
||||||
# - landing: 1 = check /intro landings + assets; 0 = skip (no GOA-style landings)
|
# - 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
|
# - 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
|
# Alternate file: TALER_DOMAINS_CONF=/path/to/domains.conf
|
||||||
|
|
||||||
# Local GOA (koopa) — landings matter
|
# Local GOA (koopa) — landings matter
|
||||||
koopa bank.hacktivism.ch exchange.hacktivism.ch taler.hacktivism.ch GOA 1 1 hacktivism.ch
|
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 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
|
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
|
# 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
|
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
|
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
|
# 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
|
# 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
|
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 taler-ops.ch
|
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 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.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 stage.taler-ops.ch
|
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 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.)
|
# Franc Paysan (Infomaniak) — merchant host is monnaie.* (not taler./backend.)
|
||||||
# Prod: no monetary launch yet; stage uses TESTPAYSAN (public HTTPS landings + shops)
|
# UI default: French (lang=fr) for all FP prod hostnames
|
||||||
lefrancpaysan.ch bank.lefrancpaysan.ch exchange.lefrancpaysan.ch monnaie.lefrancpaysan.ch - 0 0
|
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 lefrancpaysan.ch
|
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 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 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/
|
# Stage TESTPAYSAN — landings on / + farmer shops under stage.monnaie…/shops/
|
||||||
# name bank exchange merchant currency local landing [canonical]
|
# UI default: French (lang=fr)
|
||||||
stage.lefrancpaysan.ch stage.bank.lefrancpaysan.ch stage.exchange.lefrancpaysan.ch stage.monnaie.lefrancpaysan.ch TESTPAYSAN 0 1
|
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 stage.lefrancpaysan.ch
|
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 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 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 AUTH401_CONTINUE="${AUTH401_CONTINUE:-${CONTINUE_ON_ERROR}}"
|
||||||
export RUN_TIMEOUT="${RUN_TIMEOUT:-600}"
|
export RUN_TIMEOUT="${RUN_TIMEOUT:-600}"
|
||||||
export TALER_DOMAIN="${TALER_DOMAIN:-hacktivism.ch}"
|
export TALER_DOMAIN="${TALER_DOMAIN:-hacktivism.ch}"
|
||||||
# UI language: --lang / TALER_MON_LANG_SET=1 locks; otherwise auto by domain.
|
# UI language: --lang / TALER_MON_LANG_SET=1 locks.
|
||||||
# Bare TALER_MON_LANG=en in workstation env (clementine) must NOT lock language —
|
# Otherwise domains.conf column `lang` → TALER_DOMAIN_LANG → TALER_MON_LANG
|
||||||
# *lefrancpaysan* / *francpaysan* always default to fr when SET≠1 (global rule).
|
# (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
|
if [ "${TALER_MON_LANG_SET:-0}" = "1" ]; then
|
||||||
case "${TALER_MON_LANG:-en}" in
|
case "${TALER_MON_LANG:-en}" in
|
||||||
fr|FR|fra|french) export TALER_MON_LANG=fr ;;
|
fr|FR|fra|french) export TALER_MON_LANG=fr ;;
|
||||||
*) export TALER_MON_LANG=en ;;
|
*) export TALER_MON_LANG=en ;;
|
||||||
esac
|
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
|
fi
|
||||||
export TALER_MON_LANG
|
export TALER_MON_LANG
|
||||||
|
|
||||||
|
|
|
||||||
39
i18n.sh
39
i18n.sh
|
|
@ -3,31 +3,37 @@
|
||||||
#
|
#
|
||||||
# TALER_MON_LANG=en|fr
|
# TALER_MON_LANG=en|fr
|
||||||
# TALER_MON_LANG_SET=1 → language is explicit (--lang only); do not override.
|
# TALER_MON_LANG_SET=1 → language is explicit (--lang only); do not override.
|
||||||
# Auto (SET≠1): domain *lefrancpaysan* / *francpaysan* → fr always (global).
|
# Auto (SET≠1), in order:
|
||||||
# else keep env or en.
|
# 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
|
# domains.conf column `lang` (see suite domains.conf) is the global definition.
|
||||||
# workstation default for non-FP domains. It must NEVER lock English on FP
|
# Bare TALER_MON_LANG=en in ~/.config must not override a profile lang=fr.
|
||||||
# (*lefrancpaysan* / *francpaysan*) — local laptop and host-agent both follow this.
|
|
||||||
|
|
||||||
: "${TALER_MON_LANG:=}"
|
: "${TALER_MON_LANG:=}"
|
||||||
: "${TALER_MON_LANG_SET:=0}"
|
: "${TALER_MON_LANG_SET:=0}"
|
||||||
|
: "${TALER_DOMAIN_LANG:=}"
|
||||||
|
|
||||||
i18n_init() {
|
i18n_init() {
|
||||||
# Only TALER_MON_LANG_SET=1 is "explicit". Do not promote non-empty TALER_MON_LANG
|
# 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.
|
# to SET=1 — env files often ship LANG=en for the default GOA workstation.
|
||||||
if [ "${TALER_MON_LANG_SET:-0}" != "1" ]; then
|
if [ "${TALER_MON_LANG_SET:-0}" != "1" ]; then
|
||||||
case "${TALER_DOMAIN:-}" in
|
if [ -n "${TALER_DOMAIN_LANG:-}" ]; then
|
||||||
*lefrancpaysan*|*francpaysan*)
|
TALER_MON_LANG="$TALER_DOMAIN_LANG"
|
||||||
TALER_MON_LANG=fr
|
else
|
||||||
;;
|
case "${TALER_DOMAIN:-}" in
|
||||||
*)
|
*lefrancpaysan*|*francpaysan*)
|
||||||
# Non-FP: keep pre-set env value if any, else English
|
# fallback if domain not (yet) in domains.conf
|
||||||
if [ -z "${TALER_MON_LANG:-}" ]; then
|
TALER_MON_LANG=fr
|
||||||
TALER_MON_LANG=en
|
;;
|
||||||
fi
|
*)
|
||||||
;;
|
if [ -z "${TALER_MON_LANG:-}" ]; then
|
||||||
esac
|
TALER_MON_LANG=en
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
case "${TALER_MON_LANG}" in
|
case "${TALER_MON_LANG}" in
|
||||||
fr|FR|fra|french) TALER_MON_LANG=fr ;;
|
fr|FR|fra|french) TALER_MON_LANG=fr ;;
|
||||||
|
|
@ -35,6 +41,7 @@ i18n_init() {
|
||||||
esac
|
esac
|
||||||
export TALER_MON_LANG
|
export TALER_MON_LANG
|
||||||
export TALER_MON_LANG_SET
|
export TALER_MON_LANG_SET
|
||||||
|
export TALER_DOMAIN_LANG
|
||||||
}
|
}
|
||||||
|
|
||||||
i18n_tag() {
|
i18n_tag() {
|
||||||
|
|
|
||||||
35
lib.sh
35
lib.sh
|
|
@ -166,13 +166,24 @@ set_taler_stack() {
|
||||||
fi
|
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.
|
# Load first matching profile from domains.conf.
|
||||||
# Fields (whitespace-separated; # comments; blank lines ignored):
|
# 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.
|
# Returns 0 if found, 1 if not.
|
||||||
load_domain_profile() {
|
load_domain_profile() {
|
||||||
local want="$1" conf="${TALER_DOMAINS_CONF:-}"
|
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 "$want" ] || return 1
|
||||||
[ -n "$conf" ] && [ -f "$conf" ] || return 1
|
[ -n "$conf" ] && [ -f "$conf" ] || return 1
|
||||||
|
|
||||||
|
|
@ -189,12 +200,30 @@ load_domain_profile() {
|
||||||
currency="$5"
|
currency="$5"
|
||||||
local_stack="${6:-0}"
|
local_stack="${6:-0}"
|
||||||
landing="${7:-}"
|
landing="${7:-}"
|
||||||
canon="${8:-$name}"
|
f8="${8:-}"
|
||||||
|
f9="${9:-}"
|
||||||
|
canon="$name"
|
||||||
|
profile_lang=""
|
||||||
# Backward compat: old 7th field was canonical domain (contains a dot)
|
# Backward compat: old 7th field was canonical domain (contains a dot)
|
||||||
if [ -n "$landing" ] && [[ "$landing" == *.* && "$landing" != "0" && "$landing" != "1" ]]; then
|
if [ -n "$landing" ] && [[ "$landing" == *.* && "$landing" != "0" && "$landing" != "1" ]]; then
|
||||||
canon="$landing"
|
canon="$landing"
|
||||||
landing=""
|
landing=""
|
||||||
|
f8="${7:-}"
|
||||||
|
f9="${8:-}"
|
||||||
fi
|
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"
|
set_taler_stack "$bank" "$exchange" "$merchant" "$currency" "$local_stack" "$landing" "$canon"
|
||||||
return 0
|
return 0
|
||||||
done <"$conf"
|
done <"$conf"
|
||||||
|
|
|
||||||
|
|
@ -45,35 +45,110 @@ def atomic_write_text(path: Path, text: str, encoding: str = "utf-8") -> None:
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
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:
|
def ui_lang(explicit: str = "", host_hint: str = "") -> str:
|
||||||
"""en (default) or fr.
|
"""en (default) or fr.
|
||||||
|
|
||||||
- TALER_MON_LANG_SET=1 → honour TALER_MON_LANG / explicit only (no host auto).
|
- TALER_MON_LANG_SET=1 → honour TALER_MON_LANG / explicit only.
|
||||||
- Else: any *lefrancpaysan* / *francpaysan* host or log hint → fr
|
- Else TALER_DOMAIN_LANG env (set by load_domain_profile).
|
||||||
(workstation env TALER_MON_LANG=en must not force English on FP domains).
|
- Else domains.conf `lang` for host_hint.
|
||||||
- Else: explicit / env / en.
|
- Else fallback *lefrancpaysan*/*francpaysan* → fr.
|
||||||
|
- Else explicit / env / en.
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
|
|
||||||
env_lang = (os.environ.get("TALER_MON_LANG") or "").strip().lower()
|
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()
|
lang = (explicit or env_lang or "").strip().lower()
|
||||||
locked = (os.environ.get("TALER_MON_LANG_SET") or "").strip() == "1"
|
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:
|
if locked:
|
||||||
return _norm(lang) or "en"
|
return _norm_lang(lang) or "en"
|
||||||
if is_fp:
|
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 "fr"
|
||||||
return _norm(lang) or "en"
|
return _norm_lang(lang) or "en"
|
||||||
|
|
||||||
|
|
||||||
def ui(lang: str, key: str, **kwargs) -> str:
|
def ui(lang: str, key: str, **kwargs) -> str:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue