Skip to content

i18n: 6 translation keys missing across 6 locales, and nothing enforces parity #2993

Description

@dokterbob

🤖 Filed by @dokterbob's agent (Claude Code) on @dokterbob's behalf.

Describe the bug

Six translation keys present in en-US.json are missing from other locale files in backend/chainlit/translations/. Users on those locales fall through to a missing-key render rather than translated text.

Separately — and arguably the more important half — nothing currently catches this, which is why the gaps accumulated. See "Why this wasn't caught" below.

The gaps

Comparing every file in backend/chainlit/translations/ against en-US.json (the ground truth, per lint_translations() in backend/chainlit/config.py):

Locale Missing key en-US value
ar-SA chat.favorites.remove "Remove favorite"
da-DK chat.favorites.remove "Remove favorite"
de-DE components.DatePickerInput { "placeholder": { "single": "Pick a date", "range": "Pick a date range" } }
it components.DatePickerInput (as above)
ko components.DatePickerInput (as above)
ja chat.fileUpload.browse "Browse Files"

The other 17 locales are at full parity.

Origin of each key:

Why this wasn't caught

chainlit lint-translations does not check the translation files in this repo. lint_translations() (backend/chainlit/config.py:692) loads en-US.json from the package as ground truth, then iterates config_translation_dir — the consuming app's .chainlit/translations/ directory. Run inside a checkout, it lints whatever stale copy happens to sit in .chainlit/ (which is gitignored), not backend/chainlit/translations/.

backend/tests/test_translations.py unit-tests compare_json_structures / lint_translation_json thoroughly, but only against synthetic in-test dictionaries — it never loads the real locale files. And no CI workflow references translations at all.

So a PR that adds a key to en-US.json and forgets the other 22 files goes green. That's exactly what happened three times above.

To Reproduce

import json, os
from chainlit.translations import compare_json_structures

d = "backend/chainlit/translations"
truth = json.load(open(os.path.join(d, "en-US.json"), encoding="utf-8"))
for f in sorted(os.listdir(d)):
    if f.endswith(".json"):
        for e in compare_json_structures(truth, json.load(open(os.path.join(d, f), encoding="utf-8"))):
            print(f, e)

Expected behavior

  1. All locale files carry every key in en-US.json.
  2. A test in backend/tests/test_translations.py walks the real backend/chainlit/translations/ directory and asserts parity against en-US.json, so this fails in CI instead of accumulating silently.

Item 2 is the durable fix; item 1 without it just resets the counter.

Notes for whoever picks this up

The six translations themselves are a nice good first issue for native speakers of ar-SA, da-DK, de-DE, it, ko, ja.

The parity test is the part worth doing carefully — it should fail loudly and name the locale and key, and it needs to tolerate the fact that en-US.json is both a locale file and the ground truth (comparing it to itself is trivially clean).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdev-toolingRelating to developer/contributor toolings.good first issueGood for newcomers

    Type

    No type

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions