# Source: https://content.nuxt.com/raw/docs/integrations/i18n.md

# Nuxt i18n module

> Learn how to create multi-language websites using Nuxt Content with the @nuxtjs/i18n module.

Nuxt Content integrates with `@nuxtjs/i18n` to create multi-language websites. When both modules are configured together, you can organize content by language and automatically serve the correct content based on the user's locale.

## Setup

<prose-steps>

### Install the required module

```bash [terminal]
npm install @nuxtjs/i18n
```

### Configure your `nuxt.config.ts`

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  modules: ['@nuxt/content', '@nuxtjs/i18n'],
  i18n: {
    locales: [
      { code: 'en', name: 'English', language: 'en-US', dir: 'ltr' },
      { code: 'fr', name: 'French', language: 'fr-FR' },
      { code: 'fa', name: 'Farsi', language: 'fa-IR', dir: 'rtl' },
    ],
    strategy: 'prefix_except_default',
    defaultLocale: 'en',
  }
})
```

### Define collections for each language

Create separate collections for each language in your `content.config.ts`:

```ts [content.config.ts]
const commonSchema = ...;

export default defineContentConfig({
  collections: {
    // English content collection
    content_en: defineCollection({
      type: 'page',
      source: {
        include: 'en/**',
        prefix: '',
      },
      schema: commonSchema,
    }),
    // French content collection
    content_fr: defineCollection({
      type: 'page',
      source: {
        include: 'fr/**',
        prefix: '',
      },
      schema: commonSchema,
    }),
    // Farsi content collection
    content_fa: defineCollection({
      type: 'page',
      source: {
        include: 'fa/**',
        prefix: '',
      },
      schema: commonSchema,
    }),
  },
})
```

### Create dynamic pages

Create a catch-all page that fetches content based on the current locale:

```vue [pages/[...slug].vue]
<script setup lang="ts">
import { withLeadingSlash } from 'ufo'
import type { Collections } from '@nuxt/content'

const route = useRoute()
const { locale } = useI18n()
const slug = computed(() => withLeadingSlash(String(route.params.slug)))

const { data: page } = await useAsyncData('page-' + slug.value, async () => {
  // Build collection name based on current locale
  const collection = ('content_' + locale.value) as keyof Collections
  const content = await queryCollection(collection).path(slug.value).first()

  // Optional: fallback to default locale if content is missing
  if (!content && locale.value !== 'en') {
    return await queryCollection('content_en').path(slug.value).first()
  }

  return content
}, {
  watch: [locale], // Refetch when locale changes
})
</script>

<template>
  <ContentRenderer v-if="page" :value="page" />
  <div v-else>
    <h1>Page not found</h1>
    <p>This page doesn't exist in {{ locale }} language.</p>
  </div>
</template>
```

</prose-steps>

That's it! 🚀 Your multi-language content site is ready.

## Content Structure

Organize your content files in language-specific folders to match your collections:

```text
content/
  en/
    index.md
    about.md
    blog/
      post-1.md
  fr/
    index.md
    about.md
    blog/
      post-1.md
  fa/
    index.md
    about.md
```

Each language folder should contain the same structure to ensure content parity across locales.

## Fallback Strategy

You can implement a fallback strategy to show content from the default locale when content is missing in the current locale:

```ts [pages/[...slug].vue]
const { data: page } = await useAsyncData('page-' + slug.value, async () => {
  const collection = ('content_' + locale.value) as keyof Collections
  let content = await queryCollection(collection).path(slug.value).first()

  // Fallback to default locale if content is missing
  if (!content && locale.value !== 'en') {
    content = await queryCollection('content_en').path(slug.value).first()
  }

  return content
})
```

<prose-warning>

Make sure to handle missing content gracefully and provide clear feedback to users when content is not available in their preferred language.

</prose-warning>

## Complete Examples

You can see a complete working example:

- **Source**: [https://github.com/nuxt/content/tree/main/examples/i18n](https://github.com/nuxt/content/tree/main/examples/i18n)
- **Live Demo**: [https://content3-i18n.nuxt.dev/](https://content3-i18n.nuxt.dev/)