In the middle of the desert you can say anything you want

19 Jun 2024

Quarto multilanguage website

Config files

_quarto-profilename.yml ONLY, the rest won’t get parsed

  default: en
  # mutually exclusive group: you can do only one
  # (otherwise `--profile one,two` does multiple)
    - [en, de, uk]

Page content

# `unless-profile` exists as well
::: {.content-visible when-profile="en"}
This content will only appear in the advanced version.

Links are going to be interesting!

Currently /de is German, / is English.

Main home page is .. from DE lang, or /de from EN.

Menu items:

  • ENG as-is. (`href: whatever.qmd)
  • DEU: href: ../de/lehre.html — note the HTML bit!

But when previewing DEU, all of these pages are at / — ergo menu items don’t work, as they lead to a non-existing ../de/...

ALSO: shows nicely how one can link to other languages from the menu!

- icon: book
href: publications.qmd
text: Publikationen
- href: ../en/blog
text: Blog (englisch)

Site language

Website Options – Quarto tells me I can do this in each _quarto-de.yml etc. profile:

    lang: de
	#lang: ua
	#lang: en

This changes the interface to follow the corresponding quarto-cli/src/resources/language/_language.yml at main · quarto-dev/quarto-cli


How do I change front matter (e.g. title)?

Not dealt with in any of the approaches: quarto’s native Document Language1 thing

So: - How do I do different post titles per language? - How do I change site language, so _languages.yml, conditionally?

  • Project Basics – Quarto discusses the various approaches to metadata

    • And I can conditionally include stuff
  • … I could literally do a bash script that puts a _metadata.yaml, builds with a proflie, then removes that file. Oh this would be painful

  • Skimming Website Options – Quarto doesn’t really help

    • except that I can set html format lang: de from within profiles! NICE
Do variables magic
# works
title-en: "Publications and Awards"
title: "{\{< meta title-en >}}"

If only I could do per-language attributes as shown in the docu2:

    title-block-published: "Updated"
    title-block-published: "Mis à jour"

It would be so cool if one could overwrite the other variables

    title: german post title

The above would nicely get a language from the profile _quarto-lang.yml and automatically change the things. Can I do this for titles and front-matter?

I can get the current profile from the env variable

profile: {\{< env QUARTO_PROFILE >}}

If I could just

title: vars['titles']['postname'][QUARTO_PROFILE]
Use scripts

Quarto lua filters

OK let’s do this. No choice.

Learn Lua in Y Minutes

First3 attempt to write anything in lua:

function Meta(m)
  local profiles = quarto.project.profile
  local profile = profiles[1]
  if profile then
    print("Profile: " .. profile)
    m.active_profile = profile

  if profile and m.titles and m.titles[profile] then
    cleantitle = pandoc.utils.stringify(m.titles[profile])
    oldtitle = pandoc.utils.stringify(m.title)
    m.title = cleantitle
    print("Profile:" .. profile)
    print("Old title:" .. oldtitle)
    print("New title:" .. cleantitle)

  return m

I’d need to make it more robust:

  • multiple profiles? Not now
  • No titles set in titles — use the default one


  • Always require a title
  • Optionally add array of titles, indexed by language=profile name
  • If present use one of these.
function Meta(m)
  local profiles = quarto.project.profile
  if not profiles then
    -- TODO: missing YAML key? Empty YAML key?..
    -- TODO even more later: filter multiple profiles to use the language one
    return m

  local profile = profiles[1]
  -- If we have a named profile, save it, otherwise return
  if profile then
    print("Profile: " .. profile)
    m.active_profile = profile
    return m

  if m.titles then
    local titles = m.titles
    if titles[profile] then
      newtitle = pandoc.utils.stringify(titles[profile])
      oldtitle = pandoc.utils.stringify(m.title)
      -- log both if they differ
      if newtitle ~= oldtitle then
        m.title = newtitle
        -- print("Old title:" .. oldtitle)
        -- print("New title:" .. newtitle)
        print(oldtitle .. " => " .. newtitle)
      print("Title for profile " .. profile .. " not found among ")
      for lang, title in pairs(titles) do -- Table iteration.
        print("    " .. lang .. ": " .. pandoc.utils.stringify(title))

  return m

Main problems:

  • listing pages use the old title anyway
  • supports only title, not e.g. description (used, again, in listings)
    • and side menus!

  1. Document Language – Quarto ↩︎ ↩︎

  2. [Document Language (alternates) – Quarto](↩︎

  3. I think Master Thesis pandoc required lua magic and I tried some small pandoc filter bits, але це було давно і неправда. ↩︎

Nel mezzo del deserto posso dire tutto quello che voglio.