Skip to content

Localization/i18n: lang + language term files, Q1-compatible (bd-llhlzd7p)#398

Merged
cscheid merged 9 commits into
mainfrom
feature/bd-llhlzd7p-localization-i18n
Jul 17, 2026
Merged

Localization/i18n: lang + language term files, Q1-compatible (bd-llhlzd7p)#398
cscheid merged 9 commits into
mainfrom
feature/bd-llhlzd7p-localization-i18n

Conversation

@cscheid

@cscheid cscheid commented Jul 17, 2026

Copy link
Copy Markdown
Member

Quarto 1-compatible localization for rendered documents (bd-llhlzd7p). Quarto's own messages stay in English; documents localize via:

  • lang (BCP 47) — selects the term set, flows to <html lang> (including revealjs, which previously hardcoded en).
  • Shipped translations — the 35 _language*.yml term files from quarto-cli (commit 45caede32, verbatim, provenance README) copied to resources/language/ and embedded via include_dir! on both native and wasm32.
  • language: — user overrides: inline map, per-language subkeys (fr: {…}), or a path to a custom YAML file; project-root _language.yml (+ _language-<tag>.yml siblings) auto-detected in projects. Key names match Q1 exactly so existing translations carry over.

Design

One resolved term table per render (vs. Q1's four delivery channels): a new LanguageResolveStage directly after metadata-merge resolves English base → subtag walk (ptpt-BR, missing intermediates tolerated) → project-root file → user language: (plain keys, then lang-matching subkeys), and injects it at the reserved quarto.language metadata subtree. Templates read $quarto.language.<key>$ through the ordinary metadata walk; transforms use LanguageTerms::from_meta. Unknown term keys warn with source locations but stay resolvable (usable from custom templates). Design doc: claude-notes/plans/2026-07-17-localization-i18n-design.md.

Consumers converted

  • Callout default titles (callout-<type>-title; capitalize() stays the fallback for custom types).
  • Crossref: registry built-in display names localized before crossref.custom extends it (user config wins); reference text prefers crossref-<type>-prefix with Q1's prefix→title fallback; proof labels via environment-proof-title. Theorem sugar now reads display names from the registry, retiring its duplicate English table.
  • TOC default title (toc-title-document; user toc-title still wins). Note: the default English string changes from "Table of Contents" to Q1's exact "Table of contents".
  • Title-block labels computed in Rust (labels.author single/plural by author count, labels.published, labels.abstract), replacing hardcoded template strings.

Testing

  • 26 catalog/resolution unit tests + 10 pipeline tests (written first, verified failing).
  • 7 smoke-all fixtures (es/fr, incl. inline-override and plural-authors) running under the Rust, WASM Vitest, and Playwright runners.
  • End-to-end: q2 render of a Spanish document inspected (<html lang="es">, "Tabla de contenidos", "Autor/a", "Fecha de publicación", "Resumen", "Tabla 1: Una tabla", "Teorema 1 (Pitágoras)", "Demostración.", "Advertencia"); revealjs verified via render_to_file integration test.
  • cargo xtask verify: all legs green except quarto-hub-mcp "live:" tests, which time out against wss://sync.automerge.org (currently down; environmental, unrelated).

Discovered strands

  • bd-gutochbq — quarto-yaml parses explicitly-quoted "" as Null (worked around in parse_term_file).
  • bd-51k5yz4e — caption-form pipe tables (: Caption {#tbl-N}) never get a numbered caption prefix (pre-existing, language-independent).
  • bd-xzaiqpjq — expose quarto.language to Lua filters (blocked on pandoc.Meta support: bd-2llqjsms, bd-a9g50za2).

🤖 Generated with Claude Code

cscheid and others added 8 commits July 17, 2026 10:03
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-llhlzd7p)

Copy the 35 _language*.yml term files from quarto-cli (commit 45caede32,
verbatim, with provenance README) into resources/language/, embed them in
quarto-core via include_dir!, and add parse_term_file + TermLayer with
catalog integrity tests.

Upstream oddities discovered and handled: two stray keys (allowlisted),
sr-Latn shipping without a sr parent file, and quarto-yaml parsing
explicitly-quoted empty strings as Null (filed bd-gutochbq).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
resolve_language(lang, extra_layers) merges the embedded base catalog, the
_language-<tag>.yml subtag walk (most general first, missing intermediates
tolerated), then caller-supplied structured layers (plain keys, then
lang-matching per-language subkeys). LanguageTerms exposes get/entry plus
crossref_title/crossref_prefix with Q1's prefix→title fallback.

structured_layer_from_config reads user 'language:' metadata (handles both
literal strings and markdown-interpreted PandocInlines via as_plain_text);
unknown term keys warn with the key's source location but stay resolvable.
parse_language_file accepts both Q1 custom-file forms (flat map,
per-language subkeys) and rejects non-string values.

18 new unit tests (written first, verified failing) in
tests/integration/language_resolve.rs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…bd-llhlzd7p)

New pipeline stage directly after metadata-merge (native html/preview, WASM,
and analysis pipelines) that resolves the document's term table from lang +
language: metadata and injects it at meta.quarto.language as literal string
scalars — the single transport for templates ($quarto.language.<key>$ via
the ordinary metadata walk) and transforms (LanguageTerms::from_meta).

Supports the inline map form, the language: <file>.yml form (doc dir then
project root; missing file is a source-located error diagnostic), and
project-root _language.yml auto-detection with _language-<tag>.yml siblings
(projects only). Only the quarto.language subtree is reserved; other
user-set quarto.* keys are preserved.

10 pipeline tests (written first) in tests/integration/language_pipeline.rs;
full workspace suite green (10109 passed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, reveal lang (bd-llhlzd7p)

- Callout default titles use callout-<type>-title (capitalize() stays the
  fallback for custom/unknown types and stage-less tests).
- RefTypeRegistry::localize_builtin_display_names seeds localized display
  names before crossref.custom extends the registry (user config wins);
  reference text prefers crossref-<type>-prefix with Q1's prefix→title
  fallback; proof labels use environment-proof-title.
- TheoremSugarTransform reads display names from the (localized) registry,
  retiring the duplicate English table to a test-only fallback.
- TOC default title from toc-title-document (user toc-title still wins).
- FULL_HTML_TEMPLATE title-block labels become $labels.author/published/
  abstract$, computed in Rust with author single/plural by count (Q1
  computeLabels parity); user labels: metadata overrides.
- Revealjs scaffold emits the document's lang instead of hardcoded 'en'.

7 smoke-all fixtures (es/fr, written first) + reveal lang integration test;
full workspace suite green. Filed bd-51k5yz4e (pre-existing: caption-form
pipe tables get no numbered caption prefix, any language).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
docs/guides/authoring/document-language.qmd covering lang, the language:
key (inline, per-language subkeys, custom files), shipped translations,
project-root _language.yml auto-detection, crossref title/prefix keys, and
the $quarto.language.*$ template namespace. Rendered with q2 (170/170
files) and added to the site sidebar.

Note: default TOC title changed from 'Table of Contents' to Q1's exact
'Table of contents' as a side effect of sourcing it from the term catalog.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…2 (bd-llhlzd7p)

The full-verify WASM leg caught language.rs failing to compile for
wasm32-unknown-unknown: include_dir was declared in quarto-core's
native-only dependency section (all prior include_dir! uses are
native-gated at their use sites). Localization must work in the WASM
preview pipeline (LanguageResolveStage runs there), so the dependency —
and the ~150KB of embedded term YAML — now applies to both targets.

Verified: npm run build:wasm succeeds; WASM Vitest suite (129 tests,
including the localization smoke-all fixtures) passes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@posit-snyk-bot

posit-snyk-bot commented Jul 17, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

…e title-block labels via compute_labels (bd-llhlzd7p)

PR #396 landed a partial-based title block whose labels.* metadata is
computed by AuthorsNormalizeTransform::compute_labels with per-document
*-title overrides. That supersedes this branch's template-context labels
helper (removed): compute_labels is now term-aware instead, with
precedence per label: *-title override > language term > English literal.
Terms wired: title-block-author-single/plural, -affiliation-single/plural,
-published, -modified, -keywords, section-title-abstract. doi and
description keep English literals (no Q1 term key exists — Q1 hardcodes
them too).

Post-merge: quarto-core suite (2579), localization smoke fixtures, and
full workspace (10207) green; Spanish end-to-end render re-inspected
(Autor/a, Fecha de publicación, Resumen through the new partials).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cscheid
cscheid merged commit 3bffb3c into main Jul 17, 2026
8 checks passed
@cscheid
cscheid deleted the feature/bd-llhlzd7p-localization-i18n branch July 17, 2026 16:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants