Skip to content
On this page

Multi-Language Sites

nuxt-kql comes with built-in support for multi-language Kirby sites. For this to work, you will have to use the kirby-headless-starter, which provides a custom KQL endpoint to retrieve translated content.

To fetch language-specific content from your Kirby instance, pass a language option with your query request. (It is technically needed for non-primary languages only, but you can pass it with every KQL query.)

// Get the German translation of the about page
const { data } = await useKql(
    query: '"ueber-uns")'
    language: 'de'

Dynamic Routes

You will probably use some kind of dynamic routes in your Nuxt application. To make your life easier fetching translated content, create a useLocaleSlug composable to extract both a page's slug and its language code:

// `composables/language.ts`

// Extract language code and slug from path
export function useLocaleSlug(path: string, homePageId = 'home') {
  // Split path into segments and remove empty strings
  const [language,] = path.split('/').filter(Boolean)

  return {
    slug: rest.join('/') || homePageId,

Now you can simplify your templates. For example in pages/[].vue:

<script setup lang="ts">
// Route may be `en/about`
const route = useRoute()
// Get the language `en` as well as slug `about`
const { language, slug } = useLocaleSlug(route.path)

// Fetch the slug for the current language
const { data } = await useKql({
  query: `"${slug}")`
}, { language })

Handle Redirects

Take the imaginary about page from above as an example:

  • en/about – the default language's content
  • de/ueber-uns – translated content with a custom slug

In a multi-language setup, Kirby's page helper will always resolve the default page id (of the primary language). This behaviour my be unintentional, because the default slug about can be accessed within the other language: de/about, where only de/ueber-uns should be applicable.

We can handle that in our frontend:

const route = useRoute()
const { language, slug } = useLocaleSlug(route.path)

const { data } = await useKql(
    query: `"${slug}")`,
    select: {
      // ...
      // Make sure to get the slug
      slug: true,
  { language }

// `en/ueber-uns` can't be found, since the translated slug
// isn't accessible by the default language
if (!data.value?.result)
  console.error('Page not found')

// `de/about` can be found, but should be redirected to `de/ueber-uns`
if (data.value.result && data.value.result?.slug !== slug) {
  // SSR redirect to the translated slug

Released under the MIT License.