Comment traduire une application Next.js v13+ ?

Publié le 8 mars 2024
Dimitri Dumont avatar
Dimitri Dumont
Développeur front-end

Il est courant de devoir traduire le contenu de son site internet ou de son application web de nos jours. Avec les dernières versions de Next.js et l'app router, le mécanisme de traduction a évolué pour s'adapter aux server components.

Pour cela, il existe différentes librairies comme next-intl, react-i18next et next-translate. Le mécanisme de traduction reste le même de manière général : vous allez devoir configurer un middleware pour récupérer la langue de l'utilisateur, puis créer des fichiers de traductions et enfin les utiliser dans vos composants.

Pour ce guide, nous allons utiliser la librairie next-international qui est d'après notre expérience la plus simple à configurer et à mettre en place.

De plus, voici ses caractéristiques :

  • Peut être utilisée avec l'app router et le pages router de Next.js
  • 100% Type-safe
  • Librairie légère & simple
  • Elle peut être utilisée avec les server & client components ainsi que le rendu statique

1. Installation de next-international

Pour l'installation, vous pouvez exécuter cette commande depuis votre projet Next.js : yarn add next-international

Sur la documentation de next-international, il est recommandé de configurer l'option strict à true dans le fichier de configuration tsconfig.json. Ça permet à la librairie de fournir une meilleure expérience de développement en Typescript.

hexa web logo
Hexa web
Des conseils pour un projet web ?
Nous contacter

2. Création des fichiers de traduction

Une fois la librairie installée, vous pouvez créer les fichiers qui contiendront les traductions de votre application Next.js :

src/locales/en.ts
1export default {
2 'hello': 'Hello',
3 'hello.world': 'Hello world!',
4 'welcome': 'Hello {name}!'
5} as const
src/locales/fr.ts
1export default {
2 'hello': 'Bonjour',
3 'hello.world': 'Bonjour le monde !',
4 'welcome': 'Bonjour {name} !'
5} as const

3. Création des hooks

Nous allons créer des hooks pour utiliser les traductions dans nos composants. Pour cela, nous allons en définir deux différents, un premier pour les composants clients, un second pour les composants serveurs :

src/locales/client.ts
1"use client"
2import { createI18nClient } from 'next-international/client'
3
4export const { useI18n, useScopedI18n, I18nProviderClient } = createI18nClient({
5 en: () => import('./en'),
6 fr: () => import('./fr')
7})
src/locales/server.ts
1import { createI18nServer } from 'next-international/server'
2
3export const { getI18n, getScopedI18n, getStaticParams } = createI18nServer({
4 en: () => import('./en'),
5 fr: () => import('./fr')
6})

4. Configuration du router de Next.js

Pour déterminer quelle langue est utilisée dans nos pages, nous allons déplacer toutes les routes de notre application dans un dossier app/[locale]/ afin d'avoir un paramètre locale.

Pour les composants clients, vous pouvez les englober avec le provider créé précédemment :

src/app/[locale]/client/layout.tsx
1import { ReactElement } from 'react'
2import { I18nProviderClient } from '@/locales/client'
3
4export default function SubLayout({ params: { locale }, children }: { params: { locale: string }, children: ReactElement }) {
5 return (
6 <I18nProviderClient locale={locale}>
7 {children}
8 </I18nProviderClient>
9 )
10}

Vous pouvez également utiliser une prop fallback pour gérer le chargement de la langue utilisée par l'utilisateur.

5. Configuration du middleware

Il faut créer un middleware afin de rediriger l'utilisateur sur l'url avec la langue qu'il utilise en paramètre :

src/middleware.ts
1import { createI18nMiddleware } from 'next-international/middleware'
2import { NextRequest } from 'next/server'
3
4const I18nMiddleware = createI18nMiddleware({
5 locales: ['en', 'fr'],
6 defaultLocale: 'en',
7 urlMappingStrategy: 'rewriteDefault' // or 'rewrite' or 'redirect'
8})
9
10export function middleware(request: NextRequest) {
11 return I18nMiddleware(request)
12}
13
14export const config = {
15 matcher: ['/((?!api|static|.*\\..*|_next|favicon.ico|robots.txt).*)']
16}

En définissant le paramètre urlMappingStrategy: 'rewriteDefault', nous retirons la langue par défaut de l'url et nous gardons les autres langues dans l'url. Par exemple, si un utilisateur visite la page /en/products, il sera redirigé vers /products, alors que si il visite la page /fr/products, il restera sur /fr/products.

Pour en savoir plus, vous pouvez vous rendre sur la documentation de next-international : https://next-international.vercel.app/docs/app-middleware-configuration#rewrite-the-url-to-hide-the-locale.

hexa web logo
Hexa web
Des conseils pour un projet web ?
Nous contacter

6. Utilisation des traductions

Enfin, pour traduire le contenu de votre application Next.js, il ne reste plus qu'à utiliser les hooks et les fichiers de traductions créés précédemment :

src/ui/user-card.tsx
1'use client'
2import { useI18n } from '@/locales/client'
3
4export default function Page() {
5 const t = useI18n()
6
7 return (
8 <div>
9 <p>{t('hello')}</p>
10
11 <p>{t('hello.world')}</p>
12
13 <p>{t('welcome', { name: 'John' })}</p>
14 <p>{t('welcome', { name: <strong>John</strong> })}</p>
15 </div>
16 )
17}
src/app/[locale]/page.tsx
1import { getI18n } from '@/locales/server'
2
3export default async function Page() {
4 const t = await getI18n()
5
6 return (
7 <div>
8 <p>{t('hello')}</p>
9
10 <p>{t('hello.world')}</p>
11
12 <p>{t('welcome', { name: 'John' })}</p>
13 <p>{t('welcome', { name: <strong>John</strong> })}</p>
14 </div>
15 )
16}

Traductions scopées

Il arrive régulièrement d'avoir un grand nombre de traductions et de les organisées par groupe (ou scope). Afin d'éviter d'avoir des longues clés à passer en paramètre des hooks et des fonctions pour récupérer des traductions, vous pouvez utiliser le hook useScopedI18n et la fonction getScopedI18n pour récupérer un groupe de traductions.

src/ui/user-card.tsx
1'use client'
2import { useI18n, useScopedI18n } from '@/locales/client'
3
4export default function Page() {
5 const scopedT = useScopedI18n('index.header.links')
6 //const t = useI18n()
7
8 return (
9 <div>
10 <p>{scopedT('home')}</p>
11 {/*<p>{t('index.header.links.home')}</p>*/}
12 </div>
13 )
14}
src/app/[locale]/page.tsx
1import { getI18n, getScopedI18n } from '@/locales/server'
2
3export default async function Page() {
4 const scopedT = await getScopedI18n('index.header.links')
5 //const t = await getI18n()
6
7 return (
8 <div>
9 <p>{scopedT('home')}</p>
10 {/*<p>{t('index.header.links.home')}</p>*/}
11 </div>
12 )
13}

Pluriels

Pour gérer les traductions aux pluriels, il faut utiliser le caractère # après une clé de traduction, suivi de zero, one, two, few, many ou other :

src/locales/en.ts
1export default {
2 'cows#zero': 'No cows',
3 'cows#one': 'A cow',
4 'cows#other': '{count} cows'
5} as const

La traduction sera déterminée grâce au paramètre count passé aux hooks et aux fonctions qui récupèrent les traductions :

src/app/[locale]/page.tsx
1import { useI18n } from '@/locales/client'
2
3export default function Page() {
4 const t = useI18n()
5
6 return (
7 <div>
8 {/* Affiche : No cows */}
9 <p>{t('cows', { count: 0 })}</p>
10 {/* Affiche : A cow */}
11 <p>{t('cows', { count: 1 })}</p>
12 {/* Affiche : 3 cows */}
13 <p>{t('cows', { count: 3 })}</p>
14 </div>
15 )
16}

Si vous souhaitez plus d'informations sur l'utilisation la librairie next-international, vous pouvez vous rendre sur la documentation : https://next-international.vercel.app/docs.

Échangeons sur votre projet web

Présentez-nous votre projet web, nous vous recontacterons dans les prochaines 24h.