Skip to content

bug: Translation fallback doesn't resolve base locale to regional file (e.g. "da" → "da-DK") #2842

@mgquach

Description

@mgquach

Description

The load_translation method in config.py only falls back from regional to base locale (da-DKda), but never resolves a base locale to an available regional file (dada-DK). This means most users with regional translation files never actually see them.

Root Cause

In backend/chainlit/config.py, the fallback logic is:

parent_language = language.split("-")[0]  # "da" → "da"

# 1. Try exact match:  da.json
# 2. Try parent:       da.json  ← identical when input has no region!
# 3. Fall back to:     en-US.json

When a browser sends da (which is the default for most Danish browsers), steps 1 and 2 both look for da.json. Neither step ever tries da-DK.json, even though that file exists.

Impact

This affects all translations that use regional codes:

File Browser sends Found?
es.json es Yes
nl.json nl Yes
ja.json ja Yes
da-DK.json da No
de-DE.json de No
fr-FR.json fr No
zh-CN.json zh No
he-IL.json he No
el-GR.json el No

Users affected by this silently fall back to en-US with a warning in the logs:

WARNING - chainlit - Translation file for da not found. Using default translation en-US.

In a production deployment, this warning appeared 443 times in 4 hours — once per page load for every Danish user.

Suggested Fix

Add an upward lookup step that scans for {base_locale}-*.json when the exact and parent lookups fail:

def load_translation(self, language: str):
    parent_language = language.split("-")[0]
    translation_dir = Path(config_translation_dir)

    # 1. Exact match (e.g. "da-DK.json")
    exact = translation_dir / f"{language}.json"
    if is_path_inside(exact, translation_dir) and exact.is_file():
        return json.loads(exact.read_text(encoding="utf-8"))

    # 2. Parent/base language (e.g. "da.json")
    parent = translation_dir / f"{parent_language}.json"
    if is_path_inside(parent, translation_dir) and parent.is_file():
        logger.warning(f"Translation file for {language} not found. Using parent translation {parent_language}.")
        return json.loads(parent.read_text(encoding="utf-8"))

    # 3. Regional variant (e.g. "da" → find "da-DK.json")
    if language == parent_language:
        for candidate in sorted(translation_dir.glob(f"{parent_language}-*.json")):
            if is_path_inside(candidate, translation_dir) and candidate.is_file():
                variant = candidate.stem
                logger.info(f"Translation file for {language} not found. Using regional variant {variant}.")
                return json.loads(candidate.read_text(encoding="utf-8"))

    # 4. Default fallback
    default = translation_dir / f"{DEFAULT_LANGUAGE}.json"
    if is_path_inside(default, translation_dir) and default.is_file():
        logger.warning(f"Translation file for {language} not found. Using default translation {DEFAULT_LANGUAGE}.")
        return json.loads(default.read_text(encoding="utf-8"))

    return {}

Environment

  • Chainlit version: 2.10.0
  • Python version: 3.13
  • OS: Linux (AKS)

Workaround

Setting language = "da-DK" in config.toml under [UI] forces the specific locale, but this overrides the language for all users, including non-Danish speakers.

Alternatively, deploying a copy of da-DK.json as da.json works but shouldn't be necessary.

Metadata

Metadata

Assignees

No one assigned

    Labels

    backendPertains to the Python backend.bugSomething isn't workingstaleIssue has not had recent activity or appears to be solved. Stale issues will be automatically closed

    Type

    No type

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions