Skip to content

Per-language _language-*.yml files have drifted from _language.yml #14822

Open
0 of 5 issues completed
Open

Description

@cderv

Keys added to src/resources/language/_language.yml are not propagated to the per-language translation files, and nothing in the test suite or CI catches it. Auditing the 30 base language files against _language.yml at b69ed2dde16d1b502dcdddace4eebae5038550e3:

  • tools-share and tools-download are missing from 25 of the 30. They were added in January 2024 for the book tools menu, so all those languages have been showing English "Download" and "Share" there since.
  • ko is also missing title-block-modified, added September 2022.
  • sr-Latn is also missing code-links-title, launch-binder-title, launch-dev-container-title, listing-page-field-wordcount and listing-page-words, all added in 2023.
  • lt still carries search, which was renamed to search-label in May 2023. The Lithuanian search label falls back to English.
  • sv still carries callout-danger-title, which was renamed to callout-caution-title in February 2023. The Swedish caution callout title falls back to English.

Only bg, es, eu, is and sl are complete.

The region variants de-CH, fr-CA, pt-BR and zh-TW are not affected. They are read as overlays on top of their base language, so being partial is the point:

if (lang) {
// enumerate variations dictated by this lang
const subtags = lang.split("-");
for (let i = 0; i < subtags.length; i++) {
variations.push(subtags.slice(0, i + 1).join("-"));
}

A check would stop this recurring

We could add a test that reads _language.yml and every _language-*.yml and fails when a base language file misses a key, or when any file carries a key that no longer exists in _language.yml. The region variants would be exempt from the missing-key half but not from the unknown-key half, which is what would have caught lt and sv.

Without something like that the drift restarts the next time a key is added. This is separate from #14772, which is about custom keys supplied by users.

Worth saying that a check only surfaces the gaps. Filling them is translation work for people who speak the language, not something to bulk fill with machine output.

Keys added since the audit

The audit above is a snapshot. Keys added later land in _language.yml only, so they start out missing from all translations: revealjs-menu-button-label from #14251 and link-external-newwindow-label from #9469. Each new key gets its own sub-issue (#15126, #15260) so that translating one key stays a good first issue.

Activity

  1. self-assigned this
    on Aug 28, 2026
  2. added this to the v1.11 milestone on Aug 28, 2026
  3. cderv commented on Sep 1, 2026

    @cderv
    MemberAuthor

    #14685 adds a new skip-to-content key. It got seeded in de, es, fr, it, ja, nl, pt, pt-BR, zh and zh-TW, plus the base _language.yml.

    Same drift as above for the rest: bg, ca, cs, da, el, eu, fi, he, id, is, ko, lt, nb, nn, pl, ru, sk, sl, sr-Latn, sv, tr and ua fall back to English for it. de-CH and fr-CA stay exempt as region overlays.

  4. modified the milestones: v1.11, v1.12 on Oct 29, 2026
  5. cderv commented on Nov 3, 2026

    @cderv
    MemberAuthor

    I would like to change what I wrote above about not bulk filling the gaps with machine output.

    Two weeks after #15126 opened, two languages have the key and one PR is open. Meanwhile each new key starts missing in about 30 files, so the English fallback is not going away by community PRs alone. And contributors already use AI to translate, with no context about what the string is. "Dismiss announcement" easily becomes "reject the announcement", "Slide menu" becomes a single slide. I think a careful machine translation we check ourselves is better than English fallback, or than unchecked AI output in PRs.

    Here is what I tested on a sample of 11 languages and 5 keys. A machine translation service alone gets about 1 value in 5 wrong when it only gets the English string. Giving it the context of the key (which sense of the word, singular or plural, a few existing strings from the same file) fixes most of these. A second engine is no confidence signal, it repeats the same mistakes. A reviewer that reads the context and the file's existing strings did catch every real mismatch, and proposed the existing human value when there was one.

    So the plan, in stages:

    1. A groundwork PR, ready now:
      • a unit test that fails on stale keys, on missing keys not listed in a known-gaps file, on gap entries that are no longer missing, and on {0} placeholders that differ from the English value. Region overlays are exempt from the missing-key part only.
      • the lt and sv stale keys renamed, keeping their existing values.
      • translator comments in _language.yml above each key that is missing somewhere, saying what the string is and which sense to use.
      • a rewritten src/resources/language/README.md (the table was out of date) with contributing guidance, and a short AGENTS.md for AI tools pointing at the same.
    2. One PR per key, using the sub-issues: machine translation with the key's context, then two independent reviews that don't see each other. A value is committed only if both reviews accept it, or both propose the same correction. Anything else stays English, stays on the known-gaps list, and gets listed on the sub-issue with the candidates and the reason, for native speakers to pick or fix. Each PR lists every value it adds and says they are AI-generated.
    3. The single-language leftovers (ko, sr-Latn) in one PR.

    Order for step 2 would be keys with existing human translations first, to calibrate: skip-to-content, tools-share/tools-download, revealjs-menu-button-label, then link-external-newwindow-label, then announcement-dismiss-label. I would open sub-issues in the same format for the keys that don't have one yet. I won't touch pt-BR or any key with an open community PR.

    The per-key sub-issues stay open as good first issues for the values the reviews don't agree on, and native speakers are still welcome to improve any committed value. I'll open the groundwork PR now, then the per-key PRs one after the other.

  6. mcanouil commented on Nov 3, 2026

    @mcanouil
    Collaborator

    I was thinking the same thing, but first started with opening issues to track the missing pieces, then I also saw the PRs coming up.
    Might be more efficient to have a careful global translation that can be fixed when needed by community rather than doing the other way around.

  7. cderv commented on Nov 4, 2026

    @cderv
    MemberAuthor

    The Korean and Serbian (Latin) leftovers are translated in #15292: title-block-modified in ko, and code-links-title, launch-binder-title, launch-dev-container-title, listing-page-field-wordcount and listing-page-words in sr-Latn. Each value is a machine translation that two independent reviewers accepted, or a correction both reviewers proposed.

    Nothing is disputed for these six, so there is nothing for native speakers to pick between. Two values differ from the first machine output, in case someone wants to double check them:

    • ko title-block-modified: "수정" instead of "수정됨", to take the same noun form as title-block-published ("공개").
    • sr-Latn code-links-title: "Linkovi ka Kodu" instead of "Linkovi ka kodu", to take the title casing of the other headings in the file.

    tools-download in sr-Latn is not part of #15292 and stays on the known gaps list.

    Rendered output

    Read from the HTML of a website rendered with lang: sr-Latn (listing and code links) and of a page with lang: ko and date-modified.

    Language Key Where Rendered
    sr-Latn listing-page-field-wordcount listing table column header Broj reči
    sr-Latn listing-page-words listing table cell for a 1500 word post 1 500 reči
    sr-Latn code-links-title heading of the code links list in the margin Linkovi ka Kodu
    sr-Latn launch-binder-title code link Pokreni Binder
    sr-Latn launch-dev-container-title code link Pokreni Dev Container
    ko title-block-modified title block label above the modified date 수정 (next to 공개 for the published date)

    No literal {0} is left in the listing page.

    The two code links need a GitHub project, so the demo folder is a git repository with a GitHub origin remote.

    # _quarto.yml
    project:
      type: website
    website:
      title: Demo
      navbar:
        left:
          - href: index.qmd
    format:
      html:
        lang: sr-Latn
        code-links:
          - binder
          - devcontainer
    ---
    title: Demo listing
    listing:
      contents: posts
      type: table
      fields: [date, title, word-count]
    ---
    ---
    title: Post A
    date: 2026-01-01
    ---
    
    lorem ipsum dolor sit amet (repeated to 1500 words)
    ---
    title: 한국어 문서
    lang: ko
    date: 2026-01-01
    date-modified: 2026-02-02
    ---
    
    본문.
  8. cderv commented on Nov 4, 2026

    @cderv
    MemberAuthor

    The per-key translations are merged. What is still missing is 11 values across 5 keys, all of them cases where the two 🤖 reviewers did not agree on the wording. They stay in English and on the list in tests/unit/language-known-gaps.yml.

    Each comment below has the candidates and what each reviewer said, so a native speaker can pick one and send a PR (the language README explains how to drop the entry from the list):

    None of these is kept community-only, and no open PR touches these files for these keys. I am leaving this issue open until the list is empty.

  9. modified the milestones: v1.12, Future on Nov 4, 2026
  10. removed their assignment
    on Nov 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions