- Page d'accueil
- /
- Blog
- /
- Nuxt Content & i18n : COMMENT Créer un blog international
Nuxt Content & i18n : COMMENT Créer un blog international
Nuxt.jsPublished on , updated onApprenez à créer un blog multilingue avec Nuxt Content et les modules i18n. Suivez ce guide étape par étape pour développer un blog international facilement !

Trouvez le projet complet de ce guide dans ce dépôt : https://github.com/braanj/blog-nuxt-content
Installation et configuration de nuxt content
Pour commencer, installons le module nuxt content en utilisant la commande npx
comme mentionné dans la documentation officielle.
npx nuxi@latest module add content
Cette commande va gérer la configuration en ajoutant le module à la propriété modules
dans le fichier nuxt.config.ts
.
export default { modules: ['@nuxt/content'] }
Installation et configuration de l'internationalisation (i18n)
Ensuite, installons et configurons le module nuxt i18n
(internationalisation) pour préparer le blog à un public international.
De la même manière, installez le module nuxt i18n en utilisant la commande npx
:
npx nuxi@latest module add @nuxtjs/i18n@next
Ajoutez la configuration ci-dessous dans le fichier nuxt.config.ts
.
export default defineNuxtConfig({
// ...
modules: [
// ...
'@nuxtjs/i18n',
],
i18n: {
baseUrl: process.env.NUXT_PUBLIC_SITE_URL,
defaultLocale: 'en',
lazy: true,
locales: [
{
code: 'en',
language: 'en-US',
file: 'en.json',
name: 'English',
},
{
code: 'fr',
language: 'fr-FR',
file: 'fr.json',
name: 'Français',
},
],
restructureDir: 'internationalization',
strategy: 'prefix',
detectBrowserLanguage: false,
},
})
Pour ce blog, deux langues seront configurées : Français et Anglais (comme langue par défaut). Les fichiers de traduction statiques locaux seront placés dans le répertoire internationalization/locales
, en utilisant le code de langue comme nom de fichier.
Pour la stratégie, prefix
sera utilisé, pour s'assurer que le code de langue apparaît dans les URLs.
Enfin, l'URL de base est définie dans le fichier .env
sous la variable NUXT_PUBLIC_SITE_URL
. Le module i18n se plaindra d'utiliser https://localhost:3000
comme valeur. Assurez-vous donc de le définir sur un nom de domaine réel en production.
Création des premiers articles en fichiers markdown
Lançons la machine nuxt content. Commencez par créer le dossier de contenu. À l'intérieur, ajoutez deux dossiers, chacun portant le code de langue comme nom. Puis ajoutez un dossier nommé blog
dans chacun d'eux.
Cela devrait ressembler à ceci :
Maintenant, ajoutons les fichiers markdown.
Créez deux fichiers :
first-article.md
dans le dossier/en/blog
.premiere-article.md
dans le dossier/fr/blog
.
Création de la page vue de l'article
Maintenant que les fichiers de contenu ont été créés, récupérons et affichons leur contenu. Mais d'abord, créons une page vue qui présentera le contenu.
Ajoutez un autre dossier nommé blog
sous le dossier pages
(le dossier pages n'est pas créé par défaut). À l'intérieur, créez un fichier vue nommé [slug].vue
.
Quelque chose comme ceci :
Récupération du contenu de l'article
Dans le fichier d'article unique : [slug].vue
, ajoutez le code suivant :
<script setup lang="ts">
const path = computed(() => useRoute().path)
const { data: article } = await useAsyncData('single-article', () =>
queryContent(path.value).findOne(),
)
</script>
<template>
<pre>{{ article }}</pre>
</template>
En local, naviguez vers l'article (version anglaise par exemple). Le chemin est le même que le chemin du fichier markdown : /en/blog/first-article
Pour tester la version française, naviguez simplement vers son chemin : /fr/blog/premiere-article
Affichage du contenu de l'article
Maintenant que le contenu des articles est récupéré correctement, affichons le contenu en utilisant le composant de rendu de contenu nuxt (ContentRenderer
).
<template>
<ContentRenderer v-if="article" :value="article">
<ContentRendererMarkdown :value="article" />
</ContentRenderer>
</template>
Ajouter et récupérer des articles connexes
Ajoutez deux ou trois articles markdown supplémentaires. Puis ajoutez le code suivant :
Dans la balise script
:
const { locale: currentLocale } = useI18n()
const { data: articles } = await useAsyncData('related-articles', async () => {
const data = await queryContent(currentLocale.value, 'blog')
.where({
_path: { $ne: path.value }, // Ignorer l'article actuel
})
.limit(3)
.find()
return data
})
Dans la balise template
:
<div v-if="articles && articles.length">
<ul>
<li v-for="(article, key) in articles" :key="`article-${key}`">
<nuxt-link :to="article._path"> {{ article.title }}</nuxt-link>
</li>
</ul>
</div>
Avec cela, l'utilisateur peut naviguer entre les articles connexes. Mais comment peut-il changer de langue ? Configurons cela à l'étape suivante.
Ajout d'un sélecteur de langue
Dans la mise en page default
, ajoutez le code suivant :
<script setup lang="ts">
const { locale: currentLocale, locales } = useI18n()
const switchLocalePath = useSwitchLocalePath()
</script>
<template>
<div>
<template v-for="locale in locales" :key="locale.code">
<NuxtLink
v-if="locale.code !== currentLocale"
:to="switchLocalePath(locale.code)"
:title="locale.language"
>
<span>{{ locale.name }}</span>
</NuxtLink>
</template>
<main>
<NuxtPage />
</main>
</div>
</template>
Lors du test du sélecteur de langue sur la page d'article, remarquez que le chemin lui-même n'est pas traduit. C'est parce que le module i18n
ne connaît pas la version locale
des URLs dynamiques. Corrigeons cela.
Tout d'abord, la version locale du même article doit être liée. Ajoutez une propriété slug
dans la section de métadonnées des fichiers markdown. Où le slug a deux propriétés :
- propriété
fr
contenant le slug de la version française - propriété
en
contenant le slug de la version anglaise.
Cela devrait ressembler à quelque chose comme ceci :
Reproduisez-le pour les autres articles. Puis ajoutez ce code au fichier [slug].vue
:
const i18nParams = {
en: { slug: article.value.slug.en },
fr: { slug: article.value.slug.fr },
}
const setI18nParams = useSetI18nParams()
setI18nParams(i18nParams)
Et voilà, l'utilisateur peut maintenant naviguer entre les articles et également changer de langue. Et tout cela est rendu possible par la magie de i18n avec nuxt content.
Mais ce n'est pas tout ! Dans les prochaines parties, nous créerons une page d'index pour notre blog.
Création, récupération et affichage du contenu de la page d'index du blog
Créez un fichier index.md
dans le dossier blog pour chaque langue. Puis ajoutez du contenu.
Cela devrait ressembler à quelque chose comme ceci :
Maintenant, créons une page d'index pour le blog, affichons le contenu. Puis affichons tous les articles disponibles.
Ajoutez ce code :
<script setup lang="ts">
const path = computed(() => useRoute().path)
const { data: article, error } = await useAsyncData('blog', () =>
queryContent(path.value).findOne(),
)
if (error.value) {
throw createError({
status: 404,
message: 'Article non trouvé !',
})
}
const { locale: currentLocale } = useI18n()
const { data: articles } = await useAsyncData('related-articles', async () => {
const data = await queryContent(currentLocale.value, 'blog')
.where({
_path: { $ne: path.value }, // Ignorer le chemin actuel
})
.find()
return data
})
</script>
<template>
<div>
<ContentRenderer v-if="article" :value="article">
<ContentRendererMarkdown :value="article" />
</ContentRenderer>
<div v-if="articles && articles.length">
<ul>
<li v-for="(article, key) in articles" :key="`article-${key}`">
<nuxt-link :to="article._path"> {{ article.title }}</nuxt-link>
</li>
</ul>
</div>
</div>
</template>
Ajoutons un peu de style
Mettez à jour le fichier vue par défaut pour inclure le style :
<script setup lang="ts">
const { locale: currentLocale, locales } = useI18n()
const switchLocalePath = useSwitchLocalePath()
const localePath = useLocalePath()
</script>
<template>
<div class="container">
<div class="flex justify-between">
<NuxtLink :to="localePath('index')">
<span>Blog Nuxt Content</span>
</NuxtLink>
<NuxtLink :to="localePath('blog')">
<span>Blog</span>
</NuxtLink>
<template v-for="locale in locales" :key="locale.code">
<NuxtLink
v-if="locale.code !== currentLocale"
:to="switchLocalePath(locale.code)"
:title="locale.language"
>
<span>{{ locale.name }}</span>
</NuxtLink>
</template>
</div>
<main>
<NuxtPage />
</main>
</div>
</template>
<style>
html,
body {
font-family:
system-ui,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
Oxygen,
Ubuntu,
Cantarell,
'Open Sans',
'Helvetica Neue',
sans-serif;
}
.container {
max-width: 800px;
margin-left: auto;
margin-right: auto;
padding: 1px;
}
.flex {
display: flex;
}
.justify-between {
justify-content: space-between;
}
a {
text-decoration: none;
}
/* lien visité */
a:visited {
color: green;
}
/* survol du lien */
a:hover {
color: #000;
}
/* lien actif */
a:active {
color: blue;
}
</style>
Puis ajoutez la configuration de nuxt content dans le fichier nuxt.config.ts
pour utiliser le thème github-light
:
export default {
// ...
content: {
highlight: {
theme: 'github-light',
},
},
// ...
}
Ajout de métadonnées
Maintenant que le blog est configuré et que les utilisateurs peuvent naviguer sans problème, il est temps d'ajouter des métadonnées aux pages.
Ajout du titre et de la description meta
Le titre a déjà été ajouté aux fichiers markdown. Ajoutez donc la propriété description
à tous les articles markdown.
Cela devrait ressembler à quelque chose comme ceci (exemple de contenu de blog) :
Pour l'afficher, ajoutez le code suivant au fichier blog index.vue
et au fichier article [slug].vue
(remplacez data
par article
dans la page vue de l'article).
if (data.value) {
useHead({
title: data.value.title,
meta: [
{
name: 'description',
content: data.value.description,
},
],
})
}
Ajout d'une image de couverture
Ajoutons une image de couverture aux pages (page de blog et pages d'articles). Ajoutez d'abord à chaque fichier markdown la propriété suivante dans la section d'en-tête :
cover:
src: https://picsum.photos/id/[REMPLACEZ-MOI]/500/300
alt: texte alternatif de description de l'image
Note : Pour les images, nous utiliserons Lorem Picsum comme moyen simple d'ajouter des images.
Dans la source de la couverture, remplacez [REMPLACEZ-MOI]
par un nombre commençant à 0. Consultez le site officiel de Lorem Picsum pour plus d'informations sur son utilisation.
Maintenant que les images de couverture ont été ajoutées, utilisons-les. Pour cela, nous allons introduire notre premier composant personnalisé : ArticlesList
.
<script setup lang="ts">
import type { ParsedContent } from '@nuxt/content'
defineProps<{
articles?: ParsedContent[]
}>()
</script>
<template>
<ul class="articles-list">
<li
class="article"
v-for="(article, key) in articles"
:key="`article-${key}`"
>
<nuxt-link :to="article._path">
<img
class="article-img"
:src="article.cover.src"
:alt="article.cover.alt"
/>
</nuxt-link>
<div class="article-text">
<nuxt-link :to="article._path"> {{ article.title }}</nuxt-link>
<small>{{ article.description }}</small>
</div>
</li>
</ul>
</template>
<style scoped>
.articles-list {
padding-left: 0;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 25px;
}
.article {
display: grid;
gap: 10px;
}
.article-img {
width: 100%;
height: auto;
border-radius: 5px;
}
.article-text {
display: flex;
flex-direction: column;
gap: 10px;
}
</style>
Ensuite, remplacez la liste d'articles dans les pages vue du blog et de l'article par ce composant. Affichez également l'image de couverture sur les pages de blog et d'article.
Dans la page d'article, remplacez le code template
:
<template>
<div v-if="article">
<h1>
{{ article.title }}
</h1>
<p>{{ article.description }}</p>
<div class="flex">
<img
class="cover-image"
:src="article.cover.src"
:alt="article.cover.alt"
/>
</div>
<ContentRenderer :value="article">
<ContentRendererMarkdown :value="article" />
</ContentRenderer>
<div v-if="articles && articles.length">
<h2>Articles</h2>
<LazyArticlesList :articles="articles" />
</div>
</div>
</template>
Et dans la page de blog, remplacez le code template
:
<template>
<div v-if="data">
<div class="flex relative">
<img class="cover-image" :src="data.cover.src" :alt="data.cover.alt" />
<h1 class="absolute centered">
{{ data.title }}
</h1>
</div>
<ContentRenderer :value="data">
<ContentRendererMarkdown :value="data" />
</ContentRenderer>
<LazyArticlesList v-if="articles && articles.length" :articles="articles" />
</div>
</template>
Conclusion
En conclusion, créer un blog international avec Nuxt Content et i18n est un processus transparent. En suivant ces étapes, le contenu localisé, le sélecteur de langue dynamique et une navigation robuste sont mis en place.
Restez à l'écoute pour la prochaine partie, où nous approfondirons le référencement, l'analyse et le déploiement pour propulser votre blog à un niveau supérieur !
N'oubliez pas de forker et d'utiliser le dépôt public : https://github.com/braanj/blog-nuxt-content
Merci de votre lecture !
Related articles

Découvrez tout sur le métier de développeur web : formations, compétences techniques et évolution de...