“There are no solutions—there are only tradeoffs.” -Thomas Sowell
- Quarto can handle multilingual sites (as in multiple human languages), however it is not set up to do so easily by default.1
- As of early 2026, it seems to me that there are 3 options to choose from if you want to set this up:
- Setting up one quarto project (i.e. one
_quarto.yml) and using profiles to render language specific sub-sites - Setting up two (or more) separate quarto projects in separate sub-directories and hosting them together.
- A semi-automated way using
babelquartothat is quite similar to option i.
- Setting up one quarto project (i.e. one
- All options end up working, however there are some nuances in how each are set up and tradeoffs that you face. There is no clear winner in my mind.
- To trial the options myself, I built (and share alongside this blog) basic working examples of each.
Options
As far as I am aware, there are three options. In reality there are lots of nuances and similarities between the options (and there are off course different ways you can set up each). For simplicity however, let’s consider each.
Option 1: Quarto profiles
Quarto profiles is at a glance an effective option as it leverages Quarto functionality (which was technically intended for something else). Instead (or alongside) using profiles for environments, you set up language specific profiles and create a toggle to switch between them. Your project will leverage one _quarto.yml, meaning that you have one standard project with shared configuration. This may help in some nuanced ways, such creating one folder structure for your project (which helps simplify navigation), helps slightly simplify developing common components (as you could with some limitation use one qmd file for both languages for ex, or make writing code once (i.e. do not repeat yourself approach) a tad simpler.2
Overview
Setup a
_quarto-{language}.ymlfor each language profile and language specific metadata (such as navigation) alongside the a regular_quarto.ymlfor overall project setup (and for shared content).Render each specific language profile separately (i.e.
quarto render --profile enrenders the_quarto_en.ymlto_site/en) as well as a shared content that connects the two sub-sites (such as centralindex.qmd,404.qmd, etc).When it comes to writing content, there are some options:
- For simple cases, one
.qmdfile can be used with language specific sections (using conditional content feature, i.e.{.content-visible when-profile="en"}can contain content in that language.- Pros - quite easy to start with!
- Cons
- I could see this approach being simple at first but get really messy quickly.
- You have to get creative in setting metadata in other languages, and you face a low bar for metadata you can use. For example you if you want your page title to be in a language of your choice - you must use
_quarto-{language}.ymlto specify the title and can’t use thetitle:bar in the page. If you want to specify advanced metadata like description or tags, you have to do this yourself manually.3
- For more complex scenarios (such as to keep cleaner
qmdfiles or to add multilingual metadata), you can separate language specific pages (i.e.*.en.qmdand*.fr.qmd). For content that needs to be shared between both languages, a separate file can be created and integrated, such as using{{ < include _shared-content.qmd >}}.- Pros: can start separating content for cleanliness
- Cons: this makes the directory really messy as you basically double each file. In this case its almost easier to go to Option 2.
- For simple cases, one
A toggle between languages can be hard coded to redirect to the home page of the other language or handled dynamically (a custom metadata tag can be added to each
.qmdfile and referenced in the_quarto-{language}.yml (i.e.href: ../fr/content/blogs/tech-topics/multilingual-quarto/index.htmlwill referencefilename: "/blog/blog1"in eachqmdfile).
Challenges
- If translation of all page metadata is needed, it becomes necessary to specify the info using language specific
qmdfiles (similar to option 3 asbabelquartorequires the same). - I didn’t explore this, but image that if you want to use Quarto profiles for its other intended purpose (such as environments), then it could get more complex quite quickly as you’d be dealing with 4 or more files.
Notable examples / resources
This site is organized using this approach (see the repo). In setting it up, I also created a separate vanilla example: https://github.com/sergegoussev/multilingual-quarto-ex
I would also recommend:
Option 2: Separate Quarto projects in one repo
The second option for even more customization is to setup separate directories per language with separate _quarto.yml per language. This setup creates two (or more) isolated quarto projects. This is quite similar to using profiles with separate language directories, but ensures more isolation between language.
Overview
- Using separate projects ensures that you have maximum isolation between each language part of the site. However to not have to repeat yourself (such as to not have to duplicate css, code, or other content), you can put them in common folders.
- Each project is rendered separately and combined in a custom way
I’ll prob explore this option in a bit more detail and flush out the description.
/root
├── _common/
│ ├── index.qmd <-- home landing page
│ ├── 404.qmd
│ ├── styles.css
│ └── theme.scss
├── en/
│ ├── _quarto.yml <-- references ../_common/theme.scss
│ └── index.qmd
└── fr/
├── _quarto.yml <-- references ../_common/theme.scss
└── index.qmd
Examples / further reading
In exploring this option, I set up this example: https://github.com/sergegoussev/multilingual-separate-projects-example/
A few examples seem to be:
- Guillem Maya’s site seems to leverage this approach as the repo follows the structure.
Option 3: babelquarto
babelquarto is a third option. It requires the use of R (instead of quarto) and is not rendered from CRAN but from R Open Science. The option is very similar to option 1 if separate language specific files are used.
Overview
- Instead of doing the customization of language switching and being careful about rendering and redirects,
babelquartocan do the setup for you. As its an R package, you need to use R to render locally and within yourpublish.ymlto have GitHub render your site. Once set up, it basically makes it easier to maintain language switching as you don’t have to do this part yourself and letbabelquartodo it for you.
Nuances
The biggest issue that I can see is that the main language is rendered from the root directory, whereas the other languages use a sub-directory. In other words if English is the default, its rendered from
project/index.htmland French as a secondary would be rendered fromproject/fr/index.html. This is counter to i18n guidelines as consistency between languages is recommended.- This also makes it a tad less intuitive if you need to setup a default
index.htmlpage to give visitors a choice about which site they wish to proceed to (as with the other two options).
- This also makes it a tad less intuitive if you need to setup a default
As a separate R package, this option comes with its own nuances and tradeoffs, including:
Using it introduces a new dependency. You have to learn how to use it and keep an eye on its evolution and interaction with Quarto.
By learning and maintaining your skills in
babelquarto, you are adding a to your knowledge of Quarto in a way that does not make you more competent in Quarto itself. In other words, this option slightly broadens your cognitive load as you need to also know this package and still know quarto, whereas deeper expertise in just quarto may serve you well in the future.
As you are setting up and running R to render you need to ensure your setup is appropriate. For instance a GitHub runner needs to takes a bit longer to setup R and install your
renv.lockand also render compared to just using Quarto. T- Furthermore, walking away from Quarto CLI features means you are loosing some ability to customize things that the CLI gives you, such as rendering a specific language to a specific directory (say
quarto render —profile: en –)
- Furthermore, walking away from Quarto CLI features means you are loosing some ability to customize things that the CLI gives you, such as rendering a specific language to a specific directory (say
Notable examples / resources
In exploring this option, I created a separate vanilla example: https://github.com/sergegoussev/babelquarto-example.
If this sounds interesting, have a look at:
- A blog by Joel Nitta “Multilingual Webpages with `Babelquarto` (Part 1 of 2).” 2024. December 6, 2024
- He also provides a separate vanilla example (less complex than the one I set up): https://github.com/joelnitta/example-babelquarto/
babelquartohome site
Requirements for a multilingual site
It may be tough to choose between these options (I definitely struggled at first before diving in), hence we can introduce a few requirements to help choose the best option:
How ‘multilingual’ do you want to be? A dive into i18n standard shows that many things should be multilingual - from the data output to the URL itself. This obviously includes metadata in your quarto docs.
Do you intend to use things like quarto listings (such as to write a blog and generate it dynamically?)
Do you want to avoid extra things and do things as ‘vanilla’ as possible? For instance if you already know Quarto well enough but are less up to speed on
luaorR- thenbabelquartomay be less attractive.
Key takeaways
Choosing between the Quarto profiles option or
babelquartooption for me comes down to a choice betweenWhether using the custom R library to automate the work is sufficiently beneficial compared to the loss of flexibility and ability to rely on Quarto CLI defaults.
- Whether the language switch is a concern (i.e.
/for default and/frfor the secondary).
- Whether the language switch is a concern (i.e.
While at first glance, the Quarto profile option of using a single
.qmdfile with conditional content seemed like a great solution, this option will likely be quite hard to maintain and I could see defaulting to just having separate language specific files:A single document will get very messy! Say you are rendering lots of text and code – needing to switch between language will make the document long and hard to edit.
There is no default way to translate the metadata and you are forced to come up with hacks. Either a
luafilter4 or simply having two differentqmdfiles that have language specific content.
The language toggle is a pain. Notable solutions include:
The
babelquartooption deals with it in the most seamless way (its biggest selling feature)I found that
../other_lang/blogs/tech-topics/multilingual-quarto/index.htmlworked very well as long as you didn’t forget to specify the filename and the sub-folder path in eachqmdfile’s yml.Another option would naturally to be write a small
javascriptfile to do the work for all pages automatically, which will probably be simpler than remembering to keep the custom metadata correct in eachqmdfile.
Footnotes
There is a rich discussion on the topic here: https://github.com/quarto-dev/quarto-cli/issues/275↩︎
A few like Martin Angst wrote about it.↩︎
Check out the thread on multilingual Quarto for examples of this.↩︎