Everything described in this post still works, but it looks like a clean way to have multi-language Quarto websites I had missed until now (March 2024) is babelquarto!
It’s quite clever and works via file-based routing and rendering. You register a language and then append a language code to your ‘.qmd’ files, eg. ‘about.de.qmd’ to render an additional language from your “main” language.
Disclaimer: I have not tried it out yet beyond a toy example and it is not 100% equivalent to the implementation via Quarto profiles described in this post, but it certainly is very cool.
You can use Quarto project profiles to generate versions of a website in different languages in different subfolders (eg. _site/en and _site/de). If you deploy _site/ creating a navbar item with the href ../en/index.qmd lets you toggle between sites. You’ll also need to implement redirect from the root domain. Limitations: Not quite a complete multi-language implementation (you’ll always start from home after a toggle), preview only possible for one language version at a time.
I recently moved over my website to Quarto. If you don’t know Quarto yet, it’s a new multi-lingual (as in R/ Python/ Julia/ Observable) scientific publishing system. It’s basically expanding on Rmarkdown, making it possible to create beautiful output directly from your data science workflow.
I liked the modern look of out of the box Quarto output and what seemed like streamlined and finetuned workflows. I took re-implementing my website in Quarto as a chance to explore it more deeply and at least for creating websites, I am not going back. The general workflow is well thought out and seemless, congrats RStudio posit. I mostly relied on the official Quarto documentation and this excellent guide on blogging with Quarto.
In the process of moving my homepage, I wanted to tick off another to do and have a website in both German and English.
Quarto Project Profiles
I was quite stuck at first, also because most info on multi-language features of Quarto refer to multiple programming languages - not what I was looking for. But some nice people on Twitter gave me some pointers to a new feature of Quarto. Project Profiles! These had literally been released that day in the pre-release version of Quarto.
They just released support for project profiles which includes some examples of multi-languate website generation. https://t.co/x2QmcRfbKv
— Gordon Shotwell (@gshotwell) September 11, 2022
This means, at the time of writing, if you want to experiment with the workflow I describe here, you need to install the pre-release version.
Profiles are pretty cool - they basically let you set different ways to generate output from your project.
This is useful in many ways but we’ll focus on using project profiles to generate output in two different languages from the same set of files.
Creating language profiles
Profiles work in that you create additional \_quarto.yml
files with a profile name next to a basis _quarto.yml
file. For an english profile (creating the English version of your website), this would be \_quarto-english.yml
.
In the basis _quarto.yml
file you need to set which profiles you want to make available to render.
Below you can compare two profile .yml files for English and German and the basis _quarto.yml
file. You’ll notice that I set different names for the website title as well as for the navigation items.
project:
title: "Mario Angst | Sustainability | Digitalization | Governance"
output-dir: ./_site/en
website:
navbar:
left:
- href: about.qmd
text: About me
- icon: book
href: publications.qmd
text: Publications
- href: software.qmd
text: Software
- href: blog/index.qmd
text: Blog
right:
- href: ../de
text: Deutsch
- href: ../en
text: English
lang: en
format:
html:
theme: morph
css: styles.css
toc: true
project:
title: "Mario Angst | Nachhaltigkeit | Digitalisierung | Governance"
output-dir: ./_site/de
website:
navbar:
left:
- href: about.qmd
text: Über mich
- href: software.qmd
text: Software
- icon: book
href: publications.qmd
text: Publikationen
- href: ../en/blog
text: Blog (englisch)
right:
- href: ../de
text: Deutsch
- href: ../en
text: English
lang: de
format:
html:
theme: morph
css: styles.css
toc: true
project:
type: website
profile:
default: english
group: [english, german]
Previewing and rendering language profiles
With the setup above, we can already render and preview our website in different language versions using (in the terminal):
For english:
quarto preview --profile english
For german:
quarto preview --profile german
This let’s us preview the website with website titles and navigation in the chosen language.
Website content
Up until now, we only changed the language of the scaffolding (title, navigation) of the homepage. How do we create website content in different languages?
This is where the next trick with profiles comes in. You can tell Quarto which parts of a document are in which language. It’s quite simple. Here is for example how my index.qmd
file looks like:
index.qmd
::: {.content-visible when-profile="english"}
Postdoc at the [Digital Society Initiative](https://www.dsi.uzh.ch/de.html) of the University of Zürich
Urban sustainability transformations \
Governance networks \
Digitalization
Project lead [SDGnets\@ZH](https://www.sdgnets.ch/)
Project member ["Government as/is a platform"](https://dizh.ch/en/2022/06/07/government-as-is-a-platform-2/)
:::
::: {.content-visible when-profile="english"}
Postdoc, [Digitalisierungsinitiative der Universität Zürich](https://www.dsi.uzh.ch/de.html)
Forschung zu
- nachhaltiger Stadtentwicklung
- Digitalisierung und Nachhaltigkeit
- Governance-Netzwerken
Projektleitung [SDGnets\@ZH](https://www.sdgnets.ch/)
:::
Two languages in one index.qmd document - and you can mark which content should be rendered with which language profile by enclosing content (eg. for German content) using ::: {.content-visible when-profile="german"}
then adding German content and then closing again with :::
I like this setup for the types of websites Quarto is likely to be used for because this means that your different language versions always live in the same document, making it easy to keep them in sync.
If you now render or preview your website again in a language profile, only the content is included for the language profile you specified. We can now generate two different websites in two different languages.
Combining the two language websites
Up until now, we always generated websites in different languages separate from each other. How do we combine them and make it possible for the user to switch between them?
This is where it gets a bit trickier and where this approach is not at the level of a complete multi-language website as it would be state of the art. Also, this might not generalize, depending on where you publish your page.
Basically, if you were eagle-eyed, you saw that in our language profiles we set a right navbar section like this:
quarto-english.yml
(...)
navbar:
right:
- href: ../de
text: Deutsch
- href: ../en
text: English
(...)
We also set the output directory for our language specific websites to two different folders. For english in _quarto-english.yml:
project:
output-dir: ./_site/en
(...)
For German in _quarto-german.yml
:
project:
output-dir: ./_site/de
(...)
Now, if you publish your website somewhere that lets you specify a folder from where your site is published, you can indicate the _site folder as your publishing folder.
I use Netlify and it’s super easy in this regard - assuming you are are publishing from a Github repository, just specify _site as the folder to publish from as the publish directory under Build settings. The _site folder now contains two subfolders with your two language version and is at the root of your site, eg. marioangst.com. By going to marioangst.com/en, you’ll get the english version. To switch to the German version, what happens when you press “Deutsch” is that the prompt ../de
is just command line speak for “Go up one level” (the ..
part) and then go to the de folder (the /de
part). And we can switch :)!
What is important is that the language toggle implemented in this way always leads the user to the home (index.qmd) of the website. It does not directly switch between subsections of the page, eg. from German publications to English publications. Works fine for a small page, not really suitable for a large one, where you might have to think about some more clever routing/ redirects.
Edit (2023-09-22): Melissa van Bussel suggested a clever way (see the comments) to embedd a (pretty!) button in the quarto website body-header field, which on click launches a little bit of javascript to replace eg. the “/en/” substring in the current website url with “/de/” - making it possible to switch between individual website pages, yay! For example, for the example in this post, one could replace the right navbar items in quarto-english.yml with:
quarto-english.yml
(...)
website:
body-header: |
<link rel="stylesheet" href=https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css><button onclick="window.location = location.href.replace('/en/','/de/');" class="btn"><i class="fa fa-language"></i> Deutsch </button>
I tried it for my own page and it’s super cool - the only possible problem I saw was that it does not work if you have some content only in one language (with a specific url), which you do not have in the other language (eg. you might have some subsites only in one language, eg. a German-only blog).
Default language
Now, where should the user land when they visit the root domain (eg. marioangst.com in my case)? This can be approached with different levels of complexity.
The easiest way (I think) would be to also render one additional language profile at at the root domain. You would need to change the navbar language toggles to
./en
respectively./de
only.A bit more complicated might be to set up a redirect with your publishing option. With Netlify, this was easiest though. I just included a _redirect file in the _site/ directory like this:
_redirect
/ /en
/en/de /de
/ /de 302 Language=de
This is a lot cleaner - anyone requesting the root is forwarded to english (my default) version. So if you enter marioangst.com you’ll end up at marioangst.com/en
- The most complex way would be to route the user based on eg. their browser settings. Here’s a start - for giggles, I included something like this in the above Netlify redirect for German.
Don’t forget to render and other caveats
Important with this approach is that you’ll always have to render both language profiles before deploying the page (or set up your CI/CD tools, eg. Github actions, in this way). If you render locally, a simple bash/ powershell script already helps though (or you might even have a little local Make script if you edit your page a lot). For example, on my Windows machine I have got the following:
render_all.ps1
quarto render --profile german
quarto render --profile english
Another caveat: This setup will make it possible for you to preview (with quarto preview
) the individual language sites but not the multi-language site. But as the multi-language component is basically a single toggle, I guess that does not matter that much and if you render the page you actually get the multi-language page.
I hope this helped and we’ll get to see more Quarto websites in multiple languages soon. Most of the world does not speak English :)! Feedback and more clever ways to do this always welcome.