` :
+
+```tsx filename="app/page.tsx" highlight={7} switcher
+import Modal from './ui/modal'
+import Cart from './ui/cart'
+
+export default function Page() {
+ return (
+
+
+
+ )
+}
+```
+
+```jsx filename="app/page.js" highlight={7} switcher
+import Modal from './ui/modal'
+import Cart from './ui/cart'
+
+export default function Page() {
+ return (
+
+
+
+ )
+}
+```
+
+Dans ce motif, tous les composants Serveur seront rendus côté serveur à l'avance, y compris ceux passés comme props. Le RSC Payload résultant contiendra des références indiquant où les composants Client doivent être rendus dans l'arbre des composants.
+
+### Fournisseurs de contexte (Context providers)
+
+Le [contexte React](https://react.dev/learn/passing-data-deeply-with-context) est couramment utilisé pour partager un état global comme le thème actuel. Cependant, le contexte React n'est pas pris en charge dans les composants Serveur.
+
+Pour utiliser un contexte, créez un composant Client qui accepte `children` :
+
+```tsx filename="app/theme-provider.tsx" switcher
+'use client'
+
+import { createContext } from 'react'
+
+export const ThemeContext = createContext({})
+
+export default function ThemeProvider({
+ children,
+}: {
+ children: React.ReactNode
+}) {
+ return {children}
+}
+```
+
+```jsx filename="app/theme-provider.js" switcher
+'use client'
+
+import { createContext } from 'react'
+
+export const ThemeContext = createContext({})
+
+export default function ThemeProvider({ children }) {
+ return {children}
+}
+```
+
+Ensuite, importez-le dans un composant Serveur (par exemple `layout`) :
+
+```tsx filename="app/layout.tsx" switcher
+import ThemeProvider from './theme-provider'
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode
+}) {
+ return (
+
+
+ {children}
+
+
+ )
+}
+```
+
+```jsx filename="app/layout.js" switcher
+import ThemeProvider from './theme-provider'
+
+export default function RootLayout({ children }) {
+ return (
+
+
+ {children}
+
+
+ )
+}
+```
+
+Votre composant Serveur pourra maintenant rendre directement votre fournisseur, et tous les autres composants Client dans votre application pourront consommer ce contexte.
+
+> **Bon à savoir** : Vous devriez rendre les fournisseurs aussi profondément que possible dans l'arbre – notez comment `ThemeProvider` n'englobe que `{children}` au lieu de tout le document ``. Cela facilite l'optimisation par Next.js des parties statiques de vos composants Serveur.
+
+### Composants tiers
+
+Lorsque vous utilisez un composant tiers qui dépend de fonctionnalités réservées au client, vous pouvez l'encapsuler dans un composant Client pour vous assurer qu'il fonctionne comme prévu.
+
+Par exemple, le composant ` ` peut être importé depuis le package `acme-carousel`. Ce composant utilise `useState`, mais il n'a pas encore la directive `"use client"`.
+
+Si vous utilisez ` ` dans un composant Client, il fonctionnera comme prévu :
+
+```tsx filename="app/gallery.tsx" switcher
+'use client'
+
+import { useState } from 'react'
+import { Carousel } from 'acme-carousel'
+
+export default function Gallery() {
+ const [isOpen, setIsOpen] = useState(false)
+
+ return (
+
+ setIsOpen(true)}>View pictures
+ {/* Fonctionne, car Carousel est utilisé dans un composant Client */}
+ {isOpen && }
+
+ )
+}
+```
+
+```jsx filename="app/gallery.js" switcher
+'use client'
+
+import { useState } from 'react'
+import { Carousel } from 'acme-carousel'
+
+export default function Gallery() {
+ const [isOpen, setIsOpen] = useState(false)
+
+ return (
+
+ setIsOpen(true)}>View pictures
+ {/* Fonctionne, car Carousel est utilisé dans un composant Client */}
+ {isOpen && }
+
+ )
+}
+```
+
+Cependant, si vous essayez de l'utiliser directement dans un composant Serveur, vous verrez une erreur. C'est parce que Next.js ne sait pas que ` ` utilise des fonctionnalités réservées au client.
+
+Pour résoudre ce problème, vous pouvez encapsuler les composants tiers qui dépendent de fonctionnalités réservées au client dans vos propres composants Client :
+
+```tsx filename="app/carousel.tsx" switcher
+'use client'
+
+import { Carousel } from 'acme-carousel'
+
+export default Carousel
+```
+
+```jsx filename="app/carousel.js" switcher
+'use client'
+
+import { Carousel } from 'acme-carousel'
+
+export default Carousel
+```
+
+Maintenant, vous pouvez utiliser ` ` directement dans un composant Serveur :
+
+```tsx filename="app/page.tsx" switcher
+import Carousel from './carousel'
+
+export default function Page() {
+ return (
+
+
View pictures
+ {/* Fonctionne, car Carousel est un composant Client */}
+
+
+ )
+}
+```
+
+```jsx filename="app/page.js" switcher
+import Carousel from './carousel'
+
+export default function Page() {
+ return (
+
+
View pictures
+ {/* Fonctionne, car Carousel est un composant Client */}
+
+
+ )
+}
+```
+
+> **Conseil pour les auteurs de bibliothèques**
+>
+> Si vous créez une bibliothèque de composants, ajoutez la directive `"use client"` aux points d'entrée qui dépendent de fonctionnalités réservées au client. Cela permet à vos utilisateurs d'importer des composants dans des composants Serveur sans avoir besoin de créer des wrappers.
+>
+> Il est à noter que certains bundlers peuvent supprimer les directives `"use client"`. Vous pouvez trouver un exemple de configuration d'esbuild pour inclure la directive `"use client"` dans les dépôts [React Wrap Balancer](https://github.com/shuding/react-wrap-balancer/blob/main/tsup.config.ts#L10-L13) et [Vercel Analytics](https://github.com/vercel/analytics/blob/main/packages/web/tsup.config.js#L26-L30).
+
+### Prévenir l'intoxication de l'environnement
+
+Les modules JavaScript peuvent être partagés entre les modules des composants Serveur et Client. Cela signifie qu'il est possible d'importer accidentellement du code réservé au serveur dans le client. Par exemple, considérez la fonction suivante :
+
+```ts filename="lib/data.ts" switcher
+export async function getData() {
+ const res = await fetch('https://external-service.com/data', {
+ headers: {
+ authorization: process.env.API_KEY,
+ },
+ })
+
+ return res.json()
+}
+```
+
+```js filename="lib/data.js" switcher
+export async function getData() {
+ const res = await fetch('https://external-service.com/data', {
+ headers: {
+ authorization: process.env.API_KEY,
+ },
+ })
+
+ return res.json()
+}
+```
+
+Cette fonction contient une `API_KEY` qui ne devrait jamais être exposée au client.
+
+Dans Next.js, seules les variables d'environnement préfixées par `NEXT_PUBLIC_` sont incluses dans le bundle client. Si les variables ne sont pas préfixées, Next.js les remplace par une chaîne vide.
+
+Par conséquent, même si `getData()` peut être importée et exécutée côté client, elle ne fonctionnera pas comme prévu.
+
+Pour éviter une utilisation accidentelle dans les composants Client, vous pouvez utiliser le package [`server-only`](https://www.npmjs.com/package/server-only).
+
+```bash filename="Terminal"
+npm install server-only
+```
+
+Ensuite, importez le package dans un fichier contenant du code réservé au serveur :
+
+```js filename="lib/data.js"
+import 'server-only'
+
+export async function getData() {
+ const res = await fetch('https://external-service.com/data', {
+ headers: {
+ authorization: process.env.API_KEY,
+ },
+ })
+
+ return res.json()
+}
+```
+
+Désormais, si vous essayez d'importer ce module dans un composant Client, une erreur sera générée lors de la compilation.
+
+> **Bon à savoir** : Le package correspondant [`client-only`](https://www.npmjs.com/package/client-only) peut être utilisé pour marquer les modules contenant une logique réservée au client, comme du code accédant à l'objet `window`.
diff --git a/apps/docs/content/fr/docs/01-app/01-getting-started/08-fetching-data.mdx b/apps/docs/content/fr/docs/01-app/01-getting-started/08-fetching-data.mdx
new file mode 100644
index 00000000..cfcafa55
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/01-getting-started/08-fetching-data.mdx
@@ -0,0 +1,658 @@
+---
+source-updated-at: 2025-06-01T01:32:20.000Z
+translation-updated-at: 2025-06-02T20:04:39.188Z
+title: Comment récupérer des données et les streamer
+nav_title: Récupération de données
+description: Commencez à récupérer des données et à streamer du contenu dans votre application.
+related:
+ title: Référence API
+ description: Apprenez-en plus sur les fonctionnalités mentionnées dans cette page en consultant la référence API.
+ links:
+ - app/api-reference/functions/fetch
+ - app/api-reference/file-conventions/loading
+ - app/api-reference/config/next-config-js/logging
+ - app/api-reference/config/next-config-js/taint
+---
+
+Cette page vous guidera sur la manière de récupérer des données dans les [composants Serveur et Client](/docs/app/getting-started/server-and-client-components), et comment [streamer](#streaming) les composants qui dépendent de données.
+
+## Récupération de données
+
+### Composants Serveur
+
+Vous pouvez récupérer des données dans les composants Serveur en utilisant :
+
+1. L'[API `fetch`](#avec-lapi-fetch)
+2. Un [ORM ou une base de données](#avec-un-orm-ou-une-base-de-données)
+
+#### Avec l'API `fetch`
+
+Pour récupérer des données avec l'API `fetch`, transformez votre composant en une fonction asynchrone et utilisez `await` sur l'appel à `fetch`. Par exemple :
+
+```tsx filename="app/blog/page.tsx" switcher
+export default async function Page() {
+ const data = await fetch('https://api.vercel.app/blog')
+ const posts = await data.json()
+ return (
+
+ {posts.map((post) => (
+ {post.title}
+ ))}
+
+ )
+}
+```
+
+```jsx filename="app/blog/page.js" switcher
+export default async function Page() {
+ const data = await fetch('https://api.vercel.app/blog')
+ const posts = await data.json()
+ return (
+
+ {posts.map((post) => (
+ {post.title}
+ ))}
+
+ )
+}
+```
+
+> **Bon à savoir :**
+>
+> - Les réponses de `fetch` ne sont pas mises en cache par défaut. Cependant, Next.js [prérendra](/docs/app/getting-started/partial-prerendering#static-rendering) la route et le résultat sera mis en cache pour améliorer les performances. Si vous souhaitez opter pour un [rendu dynamique](/docs/app/getting-started/partial-prerendering#dynamic-rendering), utilisez l'option `{ cache: 'no-store' }`. Consultez la [référence de l'API `fetch`](/docs/app/api-reference/functions/fetch).
+> - Pendant le développement, vous pouvez logger les appels à `fetch` pour une meilleure visibilité et un meilleur débogage. Consultez la [référence de l'API `logging`](/docs/app/api-reference/config/next-config-js/logging).
+
+#### Avec un ORM ou une base de données
+
+Comme les composants Serveur sont rendus côté serveur, vous pouvez interroger une base de données en toute sécurité en utilisant un ORM ou un client de base de données. Transformez votre composant en une fonction asynchrone et utilisez `await` sur l'appel :
+
+```tsx filename="app/blog/page.tsx" switcher
+import { db, posts } from '@/lib/db'
+
+export default async function Page() {
+ const allPosts = await db.select().from(posts)
+ return (
+
+ {allPosts.map((post) => (
+ {post.title}
+ ))}
+
+ )
+}
+```
+
+```jsx filename="app/blog/page.js" switcher
+import { db, posts } from '@/lib/db'
+
+export default async function Page() {
+ const allPosts = await db.select().from(posts)
+ return (
+
+ {allPosts.map((post) => (
+ {post.title}
+ ))}
+
+ )
+}
+```
+
+### Composants Client
+
+Il existe deux façons de récupérer des données dans les composants Client, en utilisant :
+
+1. Le [hook `use` de React](https://react.dev/reference/react/use)
+2. Une bibliothèque communautaire comme [SWR](https://swr.vercel.app/) ou [React Query](https://tanstack.com/query/latest)
+
+#### Streamer des données avec le hook `use`
+
+Vous pouvez utiliser le [hook `use` de React](https://react.dev/reference/react/use) pour [streamer](#streaming) des données du serveur vers le client. Commencez par récupérer des données dans votre composant Serveur, puis passez la promesse à votre composant Client en tant que prop :
+
+```tsx filename="app/blog/page.tsx" switcher
+import Posts from '@/app/ui/posts
+import { Suspense } from 'react'
+
+export default function Page() {
+ // Ne pas utiliser await sur la fonction de récupération de données
+ const posts = getPosts()
+
+ return (
+ Loading...}>
+
+
+ )
+}
+```
+
+```jsx filename="app/blog/page.js" switcher
+import Posts from '@/app/ui/posts
+import { Suspense } from 'react'
+
+export default function Page() {
+ // Ne pas utiliser await sur la fonction de récupération de données
+ const posts = getPosts()
+
+ return (
+ Loading...}>
+
+
+ )
+}
+```
+
+Ensuite, dans votre composant Client, utilisez le hook `use` pour lire la promesse :
+
+```tsx filename="app/ui/posts.tsx" switcher
+'use client'
+import { use } from 'react'
+
+export default function Posts({
+ posts,
+}: {
+ posts: Promise<{ id: string; title: string }[]>
+}) {
+ const allPosts = use(posts)
+
+ return (
+
+ {allPosts.map((post) => (
+ {post.title}
+ ))}
+
+ )
+}
+```
+
+```jsx filename="app/ui/posts.js" switcher
+'use client'
+import { use } from 'react'
+
+export default function Posts({ posts }) {
+ const posts = use(posts)
+
+ return (
+
+ {posts.map((post) => (
+ {post.title}
+ ))}
+
+ )
+}
+```
+
+Dans l'exemple ci-dessus, le composant `` est enveloppé dans une limite [``](https://react.dev/reference/react/Suspense). Cela signifie que le fallback sera affiché pendant que la promesse est en cours de résolution. Apprenez-en plus sur le [streaming](#streaming).
+
+#### Bibliothèques communautaires
+
+Vous pouvez utiliser une bibliothèque communautaire comme [SWR](https://swr.vercel.app/) ou [React Query](https://tanstack.com/query/latest) pour récupérer des données dans les composants Client. Ces bibliothèques ont leurs propres sémantiques pour la mise en cache, le streaming et d'autres fonctionnalités. Par exemple, avec SWR :
+
+```tsx filename="app/blog/page.tsx" switcher
+'use client'
+import useSWR from 'swr'
+
+const fetcher = (url) => fetch(url).then((r) => r.json())
+
+export default function BlogPage() {
+ const { data, error, isLoading } = useSWR(
+ 'https://api.vercel.app/blog',
+ fetcher
+ )
+
+ if (isLoading) return Loading...
+ if (error) return Error: {error.message}
+
+ return (
+
+ {data.map((post: { id: string; title: string }) => (
+ {post.title}
+ ))}
+
+ )
+}
+```
+
+```jsx filename="app/blog/page.js" switcher
+'use client'
+
+import useSWR from 'swr'
+
+const fetcher = (url) => fetch(url).then((r) => r.json())
+
+export default function BlogPage() {
+ const { data, error, isLoading } = useSWR(
+ 'https://api.vercel.app/blog',
+ fetcher
+ )
+
+ if (isLoading) return Loading...
+ if (error) return Error: {error.message}
+
+ return (
+
+ {data.map((post) => (
+ {post.title}
+ ))}
+
+ )
+}
+```
+
+## Déduplication des requêtes avec `React.cache`
+
+La déduplication est le processus de _prévention des requêtes en double_ pour la même ressource pendant un rendu. Elle vous permet de récupérer les mêmes données dans différents composants tout en évitant plusieurs requêtes réseau vers votre source de données.
+
+Si vous utilisez `fetch`, les requêtes peuvent être dédupliquées en ajoutant `cache: 'force-cache'`. Cela signifie que vous pouvez appeler la même URL avec les mêmes options en toute sécurité, et une seule requête sera effectuée.
+
+Si vous _n'utilisez pas_ `fetch`, mais plutôt un ORM ou une base de données directement, vous pouvez envelopper votre récupération de données avec la fonction [React `cache`](https://react.dev/reference/react/cache).
+
+```tsx filename="app/lib/data.ts" switcher
+import { cache } from 'react'
+import { db, posts, eq } from '@/lib/db'
+
+export const getPost = cache(async (id: string) => {
+ const post = await db.query.posts.findFirst({
+ where: eq(posts.id, parseInt(id)),
+ })
+})
+```
+
+```jsx filename="app/lib/data.js" switcher
+import { cache } from 'react'
+import { db, posts, eq } from '@/lib/db'
+import { notFound } from 'next/navigation'
+
+export const getPost = cache(async (id) => {
+ const post = await db.query.posts.findFirst({
+ where: eq(posts.id, parseInt(id)),
+ })
+})
+```
+
+## Streaming
+
+> **Avertissement :** Le contenu ci-dessous suppose que l'option de configuration [`dynamicIO`](/docs/app/api-reference/config/next-config-js/dynamicIO) est activée dans votre application. Ce drapeau a été introduit dans Next.js 15 canary.
+
+Lorsque vous utilisez `async/await` dans les composants Serveur, Next.js optera pour un [rendu dynamique](/docs/app/getting-started/partial-prerendering#dynamic-rendering). Cela signifie que les données seront récupérées et rendues côté serveur pour chaque requête utilisateur. S'il y a des requêtes de données lentes, toute la route sera bloquée pour le rendu.
+
+Pour améliorer le temps de chargement initial et l'expérience utilisateur, vous pouvez utiliser le streaming pour diviser le HTML de la page en petits morceaux et les envoyer progressivement du serveur vers le client.
+
+
+
+Il existe deux façons d'implémenter le streaming dans votre application :
+
+1. En enveloppant une page avec un [fichier `loading.js`](#avec-loadingjs)
+2. En enveloppant un composant avec [``](#avec-suspense)
+
+### Avec `loading.js`
+
+Vous pouvez créer un fichier `loading.js` dans le même dossier que votre page pour streamer **toute la page** pendant que les données sont en cours de récupération. Par exemple, pour streamer `app/blog/page.js`, ajoutez le fichier dans le dossier `app/blog`.
+
+
+
+```tsx filename="app/blog/loading.tsx" switcher
+export default function Loading() {
+ // Définissez l'interface utilisateur de chargement ici
+ return Loading...
+}
+```
+
+```jsx filename="app/blog/loading.js" switcher
+export default function Loading() {
+ // Définissez l'interface utilisateur de chargement ici
+ return Loading...
+}
+```
+
+Lors de la navigation, l'utilisateur verra immédiatement la mise en page et un [état de chargement](#creation-detats-de-chargement-significatifs) pendant que la page est en cours de rendu. Le nouveau contenu sera automatiquement remplacé une fois le rendu terminé.
+
+
+
+En arrière-plan, `loading.js` sera imbriqué dans `layout.js` et enveloppera automatiquement le fichier `page.js` et tous les enfants en dessous dans une limite ``.
+
+
+
+Cette approche fonctionne bien pour les segments de route (layouts et pages), mais pour un streaming plus granulaire, vous pouvez utiliser ``.
+
+### Avec ``
+
+`` vous permet d'être plus précis sur les parties de la page à streamer. Par exemple, vous pouvez immédiatement afficher tout contenu de page qui se trouve en dehors de la limite ``, et streamer la liste des articles de blog à l'intérieur de la limite.
+
+```tsx filename="app/blog/page.tsx" switcher
+import { Suspense } from 'react'
+import BlogList from '@/components/BlogList'
+import BlogListSkeleton from '@/components/BlogListSkeleton'
+
+export default function BlogPage() {
+ return (
+
+ {/* Ce contenu sera envoyé au client immédiatement */}
+
+
+ {/* Tout contenu enveloppé dans une limite sera streamé */}
+ }>
+
+
+
+
+ )
+}
+```
+
+```jsx filename="app/blog/page.js" switcher
+import { Suspense } from 'react'
+import BlogList from '@/components/BlogList'
+import BlogListSkeleton from '@/components/BlogListSkeleton'
+
+export default function BlogPage() {
+ return (
+
+ {/* Ce contenu sera envoyé au client immédiatement */}
+
+
+ {/* Tout contenu enveloppé dans une limite sera streamé */}
+ }>
+
+
+
+
+ )
+}
+```
+
+### Création d'états de chargement significatifs
+
+Un état de chargement instantané est une interface utilisateur de fallback qui est affichée immédiatement à l'utilisateur après la navigation. Pour la meilleure expérience utilisateur, nous recommandons de concevoir des états de chargement qui sont significatifs et aident les utilisateurs à comprendre que l'application répond. Par exemple, vous pouvez utiliser des squelettes et des spinners, ou une petite partie significative des écrans futurs comme une photo de couverture, un titre, etc.
+
+En développement, vous pouvez prévisualiser et inspecter l'état de chargement de vos composants en utilisant les [React Devtools](https://react.dev/learn/react-developer-tools).
+
+## Exemples
+
+### Récupération séquentielle de données
+
+La récupération séquentielle de données se produit lorsque des composants imbriqués dans un arbre récupèrent chacun leurs propres données et que les requêtes ne sont pas [dédupliquées](/docs/app/deep-dive/caching#request-memoization), ce qui entraîne des temps de réponse plus longs.
+
+
+
+Il peut y avoir des cas où vous voulez ce modèle parce qu'une récupération dépend du résultat de l'autre.
+
+Par exemple, le composant `` ne commencera à récupérer des données que lorsque le composant `` aura terminé de récupérer des données, car `` dépend de la prop `artistID` :
+
+```tsx filename="app/artist/[username]/page.tsx" switcher
+export default async function Page({
+ params,
+}: {
+ params: Promise<{ username: string }>
+}) {
+ const { username } = await params
+ // Récupérer les informations de l'artiste
+ const artist = await getArtist(username)
+
+ return (
+ <>
+ {artist.name}
+ {/* Afficher une interface utilisateur de fallback pendant que le composant Playlists charge */}
+ Loading...}>
+ {/* Passer l'ID de l'artiste au composant Playlists */}
+
+
+ >
+ )
+}
+
+async function Playlists({ artistID }: { artistID: string }) {
+ // Utiliser l'ID de l'artiste pour récupérer les playlists
+ const playlists = await getArtistPlaylists(artistID)
+
+ return (
+
+ {playlists.map((playlist) => (
+ {playlist.name}
+ ))}
+
+ )
+}
+```
+
+```jsx filename="app/artist/[username]/page.js" switcher
+export default async function Page({ params }) {
+ const { username } = await params
+ // Récupérer les informations de l'artiste
+ const artist = await getArtist(username)
+
+ return (
+ <>
+ {artist.name}
+ {/* Afficher une interface utilisateur de fallback pendant que le composant Playlists charge */}
+ Loading...}>
+ {/* Passer l'ID de l'artiste au composant Playlists */}
+
+
+ >
+ )
+}
+
+async function Playlists({ artistID }) {
+ // Utiliser l'ID de l'artiste pour récupérer les playlists
+ const playlists = await getArtistPlaylists(artistID)
+
+ return (
+
+ {playlists.map((playlist) => (
+ {playlist.name}
+ ))}
+
+ )
+}
+```
+
+Pour améliorer l'expérience utilisateur, vous devez utiliser [React ``](/docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense) pour afficher un `fallback` pendant que les données sont en cours de récupération. Cela permettra le [streaming](#streaming) et empêchera que toute la route soit bloquée par les requêtes de données séquentielles.
+
+### Récupération de données en parallèle
+
+La récupération de données en parallèle se produit lorsque les requêtes de données dans une route sont initiées de manière anticipée et démarrent en même temps.
+
+Par défaut, [les layouts et les pages](/docs/app/getting-started/layouts-and-pages) sont rendus en parallèle. Ainsi, chaque segment commence à récupérer les données dès que possible.
+
+Cependant, dans _n'importe quel_ composant, plusieurs requêtes `async`/`await` peuvent toujours être séquentielles si elles sont placées les unes après les autres. Par exemple, `getAlbums` sera bloqué jusqu'à ce que `getArtist` soit résolu :
+
+```tsx filename="app/artist/[username]/page.tsx" switcher
+import { getArtist, getAlbums } from '@/app/lib/data'
+
+export default async function Page({ params }) {
+ // Ces requêtes seront séquentielles
+ const { username } = await params
+ const artist = await getArtist(username)
+ const albums = await getAlbums(username)
+ return {artist.name}
+}
+```
+
+Vous pouvez initier les requêtes en parallèle en les définissant en dehors des composants qui utilisent les données, et en les résolvant ensemble, par exemple avec [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) :
+
+```tsx filename="app/artist/[username]/page.tsx" highlight={3,8,23} switcher
+import Albums from './albums'
+
+async function getArtist(username: string) {
+ const res = await fetch(`https://api.example.com/artist/${username}`)
+ return res.json()
+}
+
+async function getAlbums(username: string) {
+ const res = await fetch(`https://api.example.com/artist/${username}/albums`)
+ return res.json()
+}
+
+export default async function Page({
+ params,
+}: {
+ params: Promise<{ username: string }>
+}) {
+ const { username } = await params
+ const artistData = getArtist(username)
+ const albumsData = getAlbums(username)
+
+ // Initie les deux requêtes en parallèle
+ const [artist, albums] = await Promise.all([artistData, albumsData])
+
+ return (
+ <>
+ {artist.name}
+
+ >
+ )
+}
+```
+
+```jsx filename="app/artist/[username]/page.js" highlight={3,8,19} switcher
+import Albums from './albums'
+
+async function getArtist(username) {
+ const res = await fetch(`https://api.example.com/artist/${username}`)
+ return res.json()
+}
+
+async function getAlbums(username) {
+ const res = await fetch(`https://api.example.com/artist/${username}/albums`)
+ return res.json()
+}
+
+export default async function Page({ params }) {
+ const { username } = await params
+ const artistData = getArtist(username)
+ const albumsData = getAlbums(username)
+
+ // Initie les deux requêtes en parallèle
+ const [artist, albums] = await Promise.all([artistData, albumsData])
+
+ return (
+ <>
+ {artist.name}
+
+ >
+ )
+}
+```
+
+> **Bon à savoir :** Si une requête échoue lors de l'utilisation de `Promise.all`, l'ensemble de l'opération échouera. Pour gérer cela, vous pouvez utiliser la méthode [`Promise.allSettled`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled) à la place.
+
+### Préchargement des données
+
+Vous pouvez précharger les données en créant une fonction utilitaire que vous appelez de manière anticipée avant les requêtes bloquantes. `- ` s'affiche conditionnellement en fonction de la fonction `checkIsAvailable()`.
+
+Vous pouvez appeler `preload()` avant `checkIsAvailable()` pour initier de manière anticipée les dépendances de données de `
`. Au moment où ` ` est rendu, ses données ont déjà été récupérées.
+
+```tsx filename="app/item/[id]/page.tsx" switcher
+import { getItem } from '@/lib/data'
+
+export default async function Page({
+ params,
+}: {
+ params: Promise<{ id: string }>
+}) {
+ const { id } = await params
+ // commence le chargement des données de l'item
+ preload(id)
+ // effectue une autre tâche asynchrone
+ const isAvailable = await checkIsAvailable()
+
+ return isAvailable ? : null
+}
+
+export const preload = (id: string) => {
+ // void évalue l'expression donnée et retourne undefined
+ // https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/void
+ void getItem(id)
+}
+export async function Item({ id }: { id: string }) {
+ const result = await getItem(id)
+ // ...
+}
+```
+
+```jsx filename="app/item/[id]/page.js" switcher
+import { getItem } from '@/lib/data'
+
+export default async function Page({ params }) {
+ const { id } = await params
+ // commence le chargement des données de l'item
+ preload(id)
+ // effectue une autre tâche asynchrone
+ const isAvailable = await checkIsAvailable()
+
+ return isAvailable ? : null
+}
+
+export const preload = (id) => {
+ // void évalue l'expression donnée et retourne undefined
+ // https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/void
+ void getItem(id)
+}
+export async function Item({ id }) {
+ const result = await getItem(id)
+ // ...
+```
+
+De plus, vous pouvez utiliser la fonction [`cache` de React](https://react.dev/reference/react/cache) et le package [`server-only`](https://www.npmjs.com/package/server-only) pour créer une fonction utilitaire réutilisable. Cette approche vous permet de mettre en cache la fonction de récupération de données et de vous assurer qu'elle n'est exécutée que sur le serveur.
+
+```ts filename="utils/get-item.ts" switcher
+import { cache } from 'react'
+import 'server-only'
+import { getItem } from '@/lib/data'
+
+export const preload = (id: string) => {
+ void getItem(id)
+}
+
+export const getItem = cache(async (id: string) => {
+ // ...
+})
+```
+
+```js filename="utils/get-item.js" switcher
+import { cache } from 'react'
+import 'server-only'
+import { getItem } from '@/lib/data'
+
+export const preload = (id) => {
+ void getItem(id)
+}
+
+export const getItem = cache(async (id) => {
+ // ...
+})
+```
diff --git a/apps/docs/content/fr/docs/01-app/01-getting-started/09-caching-and-revalidating.mdx b/apps/docs/content/fr/docs/01-app/01-getting-started/09-caching-and-revalidating.mdx
new file mode 100644
index 00000000..b9f1bea5
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/01-getting-started/09-caching-and-revalidating.mdx
@@ -0,0 +1,252 @@
+---
+source-updated-at: 2025-06-01T01:32:20.000Z
+translation-updated-at: 2025-06-02T20:01:56.984Z
+title: Comment mettre en cache et revalider les données
+nav_title: Cache et revalidation
+description: Apprenez à mettre en cache et revalider les données dans votre application.
+related:
+ title: Référence API
+ description: En savoir plus sur les fonctionnalités mentionnées dans cette page en consultant la référence API.
+ links:
+ - app/api-reference/functions/fetch
+ - app/api-reference/functions/unstable_cache
+ - app/api-reference/functions/revalidatePath
+ - app/api-reference/functions/revalidateTag
+---
+
+La mise en cache (caching) est une technique qui consiste à stocker le résultat de récupération de données et d'autres calculs afin que les requêtes futures pour les mêmes données puissent être servies plus rapidement, sans avoir à refaire le travail. La revalidation vous permet quant à elle de mettre à jour les entrées du cache sans avoir à reconstruire entièrement votre application.
+
+Next.js fournit plusieurs API pour gérer la mise en cache et la revalidation. Ce guide vous expliquera quand et comment les utiliser.
+
+- [`fetch`](#fetch)
+- [`unstable_cache`](#unstable_cache)
+- [`revalidatePath`](#revalidatepath)
+- [`revalidateTag`](#revalidatetag)
+
+## `fetch`
+
+Par défaut, les requêtes [`fetch`](/docs/app/api-reference/functions/fetch) ne sont pas mises en cache. Vous pouvez mettre en cache des requêtes individuelles en définissant l'option `cache` sur `'force-cache'`.
+
+```tsx filename="app/page.tsx" switcher
+export default async function Page() {
+ const data = await fetch('https://...', { cache: 'force-cache' })
+}
+```
+
+```jsx filename="app/page.jsx" switcher
+export default async function Page() {
+ const data = await fetch('https://...', { cache: 'force-cache' })
+}
+```
+
+> **Bon à savoir** : Bien que les requêtes `fetch` ne soient pas mises en cache par défaut, Next.js effectuera un [prérendu (prerender)](/docs/app/getting-started/partial-prerendering#static-rendering) des routes contenant des requêtes `fetch` et mettra en cache le HTML. Si vous souhaitez garantir qu'une route est [dynamique](/docs/app/getting-started/partial-prerendering#dynamic-rendering), utilisez l'[API `connection`](/docs/app/api-reference/functions/connection).
+
+Pour revalider les données retournées par une requête `fetch`, vous pouvez utiliser l'option `next.revalidate`.
+
+```tsx filename="app/page.tsx" switcher
+export default async function Page() {
+ const data = await fetch('https://...', { next: { revalidate: 3600 } })
+}
+```
+
+```jsx filename="app/page.jsx" switcher
+export default async function Page() {
+ const data = await fetch('https://...', { next: { revalidate: 3600 } })
+}
+```
+
+Cela revalidera les données après un nombre spécifié de secondes.
+
+Consultez la [référence API de `fetch`](/docs/app/api-reference/functions/fetch) pour en savoir plus.
+
+## `unstable_cache`
+
+`unstable_cache` vous permet de mettre en cache le résultat de requêtes de base de données et d'autres fonctions asynchrones. Pour l'utiliser, enveloppez `unstable_cache` autour de la fonction. Par exemple :
+
+```tsx filename="app/lib/data.ts swichter
+import { db } from '@/lib/db'
+export async function getUserById(id: string) {
+ return db
+ .select()
+ .from(users)
+ .where(eq(users.id, id))
+ .then((res) => res[0])
+}
+```
+
+```jsx filename="app/lib/data.js" switcher
+import { db } from '@/lib/db'
+
+export async function getUserById(id) {
+ return db
+ .select()
+ .from(users)
+ .where(eq(users.id, id))
+ .then((res) => res[0])
+}
+```
+
+```tsx filename="app/page.tsx" highlight={2,11,13} switcher
+import { unstable_cache } from 'next/cache'
+import { getUserById } from '@/app/lib/data'
+
+export default async function Page({
+ params,
+}: {
+ params: Promise<{ userId: string }>
+}) {
+ const { userId } = await params
+
+ const getCachedUser = unstable_cache(
+ async () => {
+ return getUserById(userId)
+ },
+ [userId] // ajoute l'ID utilisateur à la clé de cache
+ )
+}
+```
+
+```jsx filename="app/page.jsx" highlight={2,7,9} switcher
+import { unstable_cache } from 'next/cache';
+import { getUserById } from '@/app/lib/data';
+
+export default async function Page({ params } }) {
+ const { userId } = await params
+
+ const getCachedUser = unstable_cache(
+ async () => {
+ return getUserById(userId)
+ },
+ [userId] // ajoute l'ID utilisateur à la clé de cache
+ );
+}
+```
+
+La fonction accepte un troisième objet optionnel pour définir comment le cache doit être revalidé. Il accepte :
+
+- `tags` : un tableau d'étiquettes (tags) utilisées par Next.js pour revalider le cache.
+- `revalidate` : le nombre de secondes après lesquelles le cache doit être revalidé.
+
+```tsx filename="app/page.tsx" highlight={6-9} switcher
+const getCachedUser = unstable_cache(
+ async () => {
+ return getUserById(userId)
+ },
+ [userId],
+ {
+ tags: ['user'],
+ revalidate: 3600,
+ }
+)
+```
+
+```jsx filename="app/page.js" highlight={6-9} switcher
+const getCachedUser = unstable_cache(
+ async () => {
+ return getUserById(userId)
+ },
+ [userId],
+ {
+ tags: ['user'],
+ revalidate: 3600,
+ }
+)
+```
+
+Consultez la [référence API de `unstable_cache`](/docs/app/api-reference/functions/unstable_cache) pour en savoir plus.
+
+## `revalidateTag`
+
+`revalidateTag` est utilisé pour revalider des entrées de cache basées sur une étiquette (tag) suite à un événement. Pour l'utiliser avec `fetch`, commencez par étiqueter la fonction avec l'option `next.tags` :
+
+```tsx filename="app/lib/data.ts" highlight={3-5} switcher
+export async function getUserById(id: string) {
+ const data = await fetch(`https://...`, {
+ next: {
+ tags: ['user'],
+ },
+ })
+}
+```
+
+```jsx filename="app/lib/data.js" highlight={3-5} switcher
+export async function getUserById(id) {
+ const data = await fetch(`https://...`, {
+ next: {
+ tags: ['user'],
+ },
+ })
+}
+```
+
+Alternativement, vous pouvez marquer une fonction `unstable_cache` avec l'option `tags` :
+
+```tsx filename="app/lib/data.ts" highlight={6-8} switcher
+export const getUserById = unstable_cache(
+ async (id: string) => {
+ return db.query.users.findFirst({ where: eq(users.id, id) })
+ },
+ ['user'], // Nécessaire si les variables ne sont pas passées en paramètres
+ {
+ tags: ['user'],
+ }
+)
+```
+
+```jsx filename="app/lib/data.js" highlight={6-8} switcher
+export const getUserById = unstable_cache(
+ async (id) => {
+ return db.query.users.findFirst({ where: eq(users.id, id) })
+ },
+ ['user'], // Nécessaire si les variables ne sont pas passées en paramètres
+ {
+ tags: ['user'],
+ }
+)
+```
+
+Puis, appelez `revalidateTag` dans un [Route Handler](/docs/app/api-reference/file-conventions/route) ou une Server Action :
+
+```tsx filename="app/lib/actions.ts" highlight={1} switcher
+import { revalidateTag } from 'next/cache'
+
+export async function updateUser(id: string) {
+ // Modifier les données
+ revalidateTag('user')
+}
+```
+
+```jsx filename="app/lib/actions.js" highlight={1} switcher
+import { revalidateTag } from 'next/cache'
+
+export async function updateUser(id) {
+ // Modifier les données
+ revalidateTag('user')
+}
+```
+
+Vous pouvez réutiliser la même étiquette dans plusieurs fonctions pour toutes les revalider en une seule fois.
+
+Consultez la [référence API de `revalidateTag`](/docs/app/api-reference/functions/revalidateTag) pour en savoir plus.
+
+## `revalidatePath`
+
+`revalidatePath` est utilisé pour revalider une route suite à un événement. Pour l'utiliser, appelez-le dans un [Route Handler](/docs/app/api-reference/file-conventions/route) ou une Server Action :
+
+```tsx filename="app/lib/actions.ts" highlight={1} switcher
+import { revalidatePath } from 'next/cache'
+
+export async function updateUser(id: string) {
+ // Modifier les données
+ revalidatePath('/profile')
+```
+
+```jsx filename="app/lib/actions.js" highlight={1} switcher
+import { revalidatePath } from 'next/cache'
+
+export async function updateUser(id) {
+ // Modifier les données
+ revalidatePath('/profile')
+```
+
+Consultez la [référence API de `revalidatePath`](/docs/app/api-reference/functions/revalidatePath) pour en savoir plus.
\ No newline at end of file
diff --git a/apps/docs/content/fr/docs/01-app/01-getting-started/10-updating-data.mdx b/apps/docs/content/fr/docs/01-app/01-getting-started/10-updating-data.mdx
new file mode 100644
index 00000000..adf3850c
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/01-getting-started/10-updating-data.mdx
@@ -0,0 +1,352 @@
+---
+source-updated-at: 2025-06-01T01:32:20.000Z
+translation-updated-at: 2025-06-02T20:02:02.492Z
+title: Comment mettre à jour les données
+nav_title: Mise à jour des données
+description: Apprenez comment mettre à jour les données dans votre application Next.js.
+related:
+ title: Référence API
+ description: En savoir plus sur les fonctionnalités mentionnées dans cette page en consultant la référence API.
+ links:
+ - app/api-reference/functions/revalidatePath
+ - app/api-reference/functions/revalidateTag
+ - app/api-reference/functions/redirect
+---
+
+Vous pouvez mettre à jour les données dans Next.js en utilisant les [Fonctions Serveur (Server Functions)](https://react.dev/reference/rsc/server-functions) de React. Cette page explique comment [créer](#creating-server-functions) et [invoquer](#invoking-server-functions) des Fonctions Serveur.
+
+## Fonctions Serveur
+
+Une Fonction Serveur est une fonction asynchrone exécutée sur le serveur. Les Fonctions Serveur sont intrinsèquement asynchrones car elles sont invoquées par le client via une requête réseau. Lorsqu'elles sont invoquées dans le cadre d'une `action`, elles sont également appelées **Actions Serveur (Server Actions)**.
+
+Par convention, une `action` est une fonction asynchrone passée à `startTransition`. Les Fonctions Serveur sont automatiquement encapsulées avec `startTransition` lorsque :
+
+- Elles sont passées à un `
+ >
+ )
+}
+```
+
+### Streaming
+
+Le Streaming divise la route en morceaux et les transmet progressivement au client dès qu'ils sont prêts. Cela permet à l'utilisateur de voir certaines parties de la page immédiatement, avant que tout le contenu n'ait fini de se rendre.
+
+
+
+Dans le Prérendu Partiel, les composants dynamiques encapsulés dans Suspense commencent à être transmis depuis le serveur en parallèle.
+
+
+
+Pour réduire la surcharge réseau, la réponse complète—y compris le HTML statique et les parties dynamiques transmises—est envoyée dans une **seule requête HTTP**. Cela évite des allers-retours supplémentaires et améliore à la fois le chargement initial et les performances globales.
+
+## Activation du Prérendu Partiel
+
+Vous pouvez activer PPR en ajoutant l'option [`ppr`](https://rc.nextjs.org/docs/app/api-reference/next-config-js/ppr) à votre fichier `next.config.ts` :
+
+```ts filename="next.config.ts" highlight={5} switcher
+import type { NextConfig } from 'next'
+
+const nextConfig: NextConfig = {
+ experimental: {
+ ppr: 'incremental',
+ },
+}
+
+export default nextConfig
+```
+
+```js filename="next.config.js" highlight={4} switcher
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ experimental: {
+ ppr: 'incremental',
+ },
+}
+```
+
+La valeur `'incremental'` vous permet d'adopter PPR pour des routes spécifiques :
+
+```tsx filename="/app/dashboard/layout.tsx"
+export const experimental_ppr = true
+
+export default function Layout({ children }: { children: React.ReactNode }) {
+ // ...
+}
+```
+
+```jsx filename="/app/dashboard/layout.js"
+export const experimental_ppr = true
+
+export default function Layout({ children }) {
+ // ...
+}
+```
+
+Les routes qui n'ont pas `experimental_ppr` auront par défaut la valeur `false` et ne seront pas prérendues avec PPR. Vous devez explicitement activer PPR pour chaque route.
+
+> **Bon à savoir** :
+>
+> - `experimental_ppr` s'appliquera à tous les enfants du segment de route, y compris les mises en page et les pages imbriquées. Vous n'avez pas besoin de l'ajouter à chaque fichier, seulement au segment supérieur d'une route.
+> - Pour désactiver PPR pour les segments enfants, vous pouvez définir `experimental_ppr` sur `false` dans le segment enfant.
+
+## Exemples
+
+### API Dynamiques
+
+Lors de l'utilisation d'API dynamiques qui nécessitent d'examiner la requête entrante, Next.js passera en mode rendu dynamique pour la route. Pour continuer à utiliser PPR, encapsulez le composant avec Suspense. Par exemple, le composant ` ` est dynamique car il utilise l'API `cookies` :
+
+```jsx filename="app/user.js" switcher
+import { cookies } from 'next/headers'
+
+export async function User() {
+ const session = (await cookies()).get('session')?.value
+ return '...'
+}
+```
+
+```tsx filename="app/user.tsx" switcher
+import { cookies } from 'next/headers'
+
+export async function User() {
+ const session = (await cookies()).get('session')?.value
+ return '...'
+}
+```
+
+Le composant ` ` sera transmis en streaming tandis que tout autre contenu à l'intérieur de ` ` sera prérendu et fera partie de l'enveloppe statique.
+
+```tsx filename="app/page.tsx" switcher
+import { Suspense } from 'react'
+import { User, AvatarSkeleton } from './user'
+
+export const experimental_ppr = true
+
+export default function Page() {
+ return (
+
+ Ceci sera prérendu
+ }>
+
+
+
+ )
+}
+```
+
+```jsx filename="app/page.js" switcher
+import { Suspense } from 'react'
+import { User, AvatarSkeleton } from './user'
+
+export const experimental_ppr = true
+
+export default function Page() {
+ return (
+
+ Ceci sera prérendu
+ }>
+
+
+
+ )
+}
+```
+
+### Passage de props dynamiques
+
+Les composants ne passent en mode rendu dynamique que lorsque la valeur est accédée. Par exemple, si vous lisez `searchParams` depuis un composant ` `, vous pouvez transmettre cette valeur à un autre composant en tant que prop :
+
+```tsx filename="app/page.tsx" switcher
+import { Table, TableSkeleton } from './table'
+import { Suspense } from 'react'
+
+export default function Page({
+ searchParams,
+}: {
+ searchParams: Promise<{ sort: string }>
+}) {
+ return (
+
+ Ceci sera prérendu
+ }>
+
+
+
+ )
+}
+```
+
+```jsx filename="app/page.js" switcher
+import { Table, TableSkeleton } from './table'
+import { Suspense } from 'react'
+
+export default function Page({ searchParams }) {
+ return (
+
+ Ceci sera prérendu
+ }>
+
+
+
+ )
+}
+```
+
+À l'intérieur du composant Table, l'accès à la valeur de `searchParams` rendra le composant dynamique tandis que le reste de la page sera prérendu.
+
+```tsx filename="app/table.tsx" switcher
+export async function Table({
+ searchParams,
+}: {
+ searchParams: Promise<{ sort: string }>
+}) {
+ const sort = (await searchParams).sort === 'true'
+ return '...'
+}
+```
+
+```jsx filename="app/table.js" switcher
+export async function Table({ searchParams }) {
+ const sort = (await searchParams).sort === 'true'
+ return '...'
+}
+```
\ No newline at end of file
diff --git a/apps/docs/content/fr/docs/01-app/01-getting-started/13-metadata-and-og-images.mdx b/apps/docs/content/fr/docs/01-app/01-getting-started/13-metadata-and-og-images.mdx
new file mode 100644
index 00000000..7b3aa034
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/01-getting-started/13-metadata-and-og-images.mdx
@@ -0,0 +1,322 @@
+---
+source-updated-at: 2025-06-01T01:32:20.000Z
+translation-updated-at: 2025-06-02T20:02:14.557Z
+title: Comment ajouter des métadonnées et créer des images OG
+nav_title: Métadonnées et images OG
+description: Apprenez à ajouter des métadonnées à vos pages et à créer des images OG dynamiques.
+related:
+ title: Référence API
+ description: En savoir plus sur les API de métadonnées mentionnées dans cette page.
+ links:
+ - app/api-reference/functions/generate-metadata
+ - app/api-reference/functions/generate-viewport
+ - app/api-reference/functions/image-response
+ - app/api-reference/file-conventions/metadata
+ - app/api-reference/file-conventions/metadata/app-icons
+ - app/api-reference/file-conventions/metadata/opengraph-image
+ - app/api-reference/file-conventions/metadata/robots
+ - app/api-reference/file-conventions/metadata/sitemap
+---
+
+Les API de métadonnées peuvent être utilisées pour définir les métadonnées de votre application afin d'améliorer le SEO et la partageabilité sur le web. Elles incluent :
+
+1. [L'objet statique `metadata`](#static-metadata)
+2. [La fonction dynamique `generateMetadata`](#generated-metadata)
+3. Des [conventions de fichiers](/docs/app/api-reference/file-conventions/metadata) spéciales qui peuvent être utilisées pour ajouter des [favicons](#favicons) et des [images OG](#static-open-graph-images) statiques ou générés dynamiquement.
+
+Avec toutes ces options, Next.js générera automatiquement les balises `` pertinentes pour votre page, qui peuvent être inspectées dans les outils de développement du navigateur.
+
+## Champs par défaut
+
+Il existe deux balises `meta` par défaut qui sont toujours ajoutées, même si une route ne définit pas de métadonnées :
+
+- La [balise meta charset](https://developer.mozilla.org/docs/Web/HTML/Element/meta#attr-charset) définit l'encodage des caractères du site.
+- La [balise meta viewport](https://developer.mozilla.org/docs/Web/HTML/Viewport_meta_tag) définit la largeur et l'échelle de la fenêtre d'affichage pour s'adapter à différents appareils.
+
+```html
+
+
+```
+
+Les autres champs de métadonnées peuvent être définis avec l'objet `Metadata` (pour les [métadonnées statiques](#static-metadata)) ou la fonction `generateMetadata` (pour les [métadonnées générées](#generated-metadata)).
+
+## Métadonnées statiques
+
+Pour définir des métadonnées statiques, exportez un [objet `Metadata`](/docs/app/api-reference/functions/generate-metadata#metadata-object) depuis un fichier [`layout.js`](/docs/app/api-reference/file-conventions/layout) ou [`page.js`](/docs/app/api-reference/file-conventions/page) statique. Par exemple, pour ajouter un titre et une description à la route du blog :
+
+```tsx filename="app/blog/layout.tsx" switcher
+import type { Metadata } from 'next'
+
+export const metadata: Metadata = {
+ title: 'Mon Blog',
+ description: '...',
+}
+
+export default function Page() {}
+```
+
+```jsx filename="app/blog/layout.tsx" switcher
+export const metadata = {
+ title: 'Mon Blog',
+ description: '...',
+}
+
+export default function Page() {}
+```
+
+Vous pouvez consulter la liste complète des options disponibles dans la [documentation de `generateMetadata`](/docs/app/api-reference/functions/generate-metadata#metadata-fields).
+
+## Métadonnées générées
+
+Vous pouvez utiliser la fonction [`generateMetadata`](/docs/app/api-reference/functions/generate-metadata) pour récupérer des métadonnées qui dépendent de données. Par exemple, pour récupérer le titre et la description d'un article de blog spécifique :
+
+```tsx filename="app/blog/[slug]/page.tsx" switcher
+import type { Metadata, ResolvingMetadata } from 'next'
+
+type Props = {
+ params: Promise<{ slug: string }>
+ searchParams: Promise<{ [key: string]: string | string[] | undefined }>
+}
+
+export async function generateMetadata(
+ { params, searchParams }: Props,
+ parent: ResolvingMetadata
+): Promise {
+ const slug = (await params).slug
+
+ // Récupérer les informations de l'article
+ const post = await fetch(`https://api.vercel.app/blog/${slug}`).then((res) =>
+ res.json()
+ )
+
+ return {
+ title: post.title,
+ description: post.description,
+ }
+}
+
+export default function Page({ params, searchParams }: Props) {}
+```
+
+```jsx filename="app/blog/[slug]/page.js" switcher
+export async function generateMetadata({ params, searchParams }, parent) {
+ const slug = (await params).slug
+
+ // Récupérer les informations de l'article
+ const post = await fetch(`https://api.vercel.app/blog/${slug}`).then((res) =>
+ res.json()
+ )
+
+ return {
+ title: post.title,
+ description: post.description,
+ }
+}
+
+export default function Page({ params, searchParams }) {}
+```
+
+En arrière-plan, Next.js diffusera les métadonnées séparément de l'interface utilisateur et les injectera dans le HTML dès qu'elles seront résolues.
+
+### Mémorisation des requêtes de données
+
+Il peut y avoir des cas où vous devez récupérer les **mêmes** données pour les métadonnées et la page elle-même. Pour éviter les requêtes en double, vous pouvez utiliser la fonction [`cache`](https://react.dev/reference/react/cache) de React pour mémoriser la valeur de retour et ne récupérer les données qu'une seule fois. Par exemple, pour récupérer les informations d'un article de blog à la fois pour les métadonnées et la page :
+
+```tsx filename="app/lib/data.ts" highlight={5} switcher
+import { cache } from 'react'
+import { db } from '@/app/lib/db'
+
+// getPost sera utilisé deux fois, mais exécuté une seule fois
+export const getPost = cache(async (slug: string) => {
+ const res = await db.query.posts.findFirst({ where: eq(posts.slug, slug) })
+ return res
+})
+```
+
+```jsx filename="app/lib/data.js" highlight={5} switcher
+import { cache } from 'react'
+import { db } from '@/app/lib/db'
+
+// getPost sera utilisé deux fois, mais exécuté une seule fois
+export const getPost = cache(async (slug) => {
+ const res = await db.query.posts.findFirst({ where: eq(posts.slug, slug) })
+ return res
+})
+```
+
+```tsx filename="app/blog/[slug]/page.tsx" switcher
+import { getPost } from '@/app/lib/data'
+
+export async function generateMetadata({
+ params,
+}: {
+ params: { slug: string }
+}) {
+ const post = await getPost(params.slug)
+ return {
+ title: post.title,
+ description: post.description,
+ }
+}
+
+export default async function Page({ params }: { params: { slug: string } }) {
+ const post = await getPost(params.slug)
+ return {post.title}
+}
+```
+
+```jsx filename="app/blog/[slug]/page.js" switcher
+import { getPost } from '@/app/lib/data'
+
+export async function generateMetadata({ params }) {
+ const post = await getPost(params.slug)
+ return {
+ title: post.title,
+ description: post.description,
+ }
+}
+
+export default async function Page({ params }) {
+ const post = await getPost(params.slug)
+ return {post.title}
+}
+```
+
+## Métadonnées basées sur des fichiers
+
+Les fichiers spéciaux suivants sont disponibles pour les métadonnées :
+
+- [favicon.ico, apple-icon.jpg et icon.jpg](/docs/app/api-reference/file-conventions/metadata/app-icons)
+- [opengraph-image.jpg et twitter-image.jpg](/docs/app/api-reference/file-conventions/metadata/opengraph-image)
+- [robots.txt](/docs/app/api-reference/file-conventions/metadata/robots)
+- [sitemap.xml](/docs/app/api-reference/file-conventions/metadata/sitemap)
+
+Vous pouvez les utiliser pour des métadonnées statiques, ou générer ces fichiers dynamiquement avec du code.
+
+## Favicons
+
+Les favicons sont de petites icônes qui représentent votre site dans les favoris et les résultats de recherche. Pour ajouter un favicon à votre application, créez un fichier `favicon.ico` et placez-le à la racine du dossier app.
+
+
+
+> Vous pouvez également générer des favicons dynamiquement avec du code. Consultez la [documentation sur les favicons](/docs/app/api-reference/file-conventions/metadata/app-icons) pour plus d'informations.
+
+## Images Open Graph statiques
+
+Les images Open Graph (OG) sont des images qui représentent votre site sur les réseaux sociaux. Pour ajouter une image OG statique à votre application, créez un fichier `opengraph-image.png` à la racine du dossier app.
+
+
+
+Vous pouvez également ajouter des images OG pour des routes spécifiques en créant un fichier `opengraph-image.png` plus profondément dans la structure des dossiers. Par exemple, pour créer une image OG spécifique à la route `/blog`, ajoutez un fichier `opengraph-image.jpg` dans le dossier `blog`.
+
+
+
+L'image la plus spécifique prendra le pas sur toute image OG située au-dessus dans la structure des dossiers.
+
+> D'autres formats d'image tels que `jpeg`, `png` et `webp` sont également pris en charge. Consultez la [documentation sur les images Open Graph](/docs/app/api-reference/file-conventions/metadata/opengraph-image) pour plus d'informations.
+
+## Images Open Graph générées dynamiquement
+
+Le constructeur [`ImageResponse`](/docs/app/api-reference/functions/image-response) vous permet de générer des images dynamiques en utilisant JSX et CSS. Ceci est utile pour les images OG qui dépendent de données.
+
+Par exemple, pour générer une image OG unique pour chaque article de blog, ajoutez un fichier `opengraph-image.ts` dans le dossier `blog` et importez le constructeur `ImageResponse` depuis `next/og` :
+
+```tsx filename="app/blog/[slug]/opengraph-image.ts" switcher
+import { ImageResponse } from 'next/og'
+import { getPost } from '@/app/lib/data'
+
+// Métadonnées de l'image
+export const size = {
+ width: 1200,
+ height: 630,
+}
+
+export const contentType = 'image/png'
+
+// Génération de l'image
+export default async function Image({ params }: { params: { slug: string } }) {
+ const post = await getPost(params.slug)
+
+ return new ImageResponse(
+ (
+ // Élément JSX ImageResponse
+
+ {post.title}
+
+ )
+ )
+}
+```
+
+```jsx filename="app/blog/[slug]/opengraph-image.js" switcher
+import { ImageResponse } from 'next/og'
+import { getPost } from '@/app/lib/data'
+
+// Métadonnées de l'image
+export const size = {
+ width: 1200,
+ height: 630,
+}
+
+export const contentType = 'image/png'
+
+// Génération de l'image
+export default async function Image({ params }) {
+ const post = await getPost(params.slug)
+
+ return new ImageResponse(
+ (
+ // Élément JSX ImageResponse
+
+ {post.title}
+
+ )
+ )
+}
+```
+
+`ImageResponse` prend en charge les propriétés CSS courantes, y compris flexbox et le positionnement absolu, les polices personnalisées, le retour à la ligne du texte, le centrage et les images imbriquées. [Consultez la liste complète des propriétés CSS prises en charge](/docs/app/api-reference/functions/image-response).
+
+> **Bon à savoir** :
+>
+> - Des exemples sont disponibles dans le [Vercel OG Playground](https://og-playground.vercel.app/).
+> - `ImageResponse` utilise [`@vercel/og`](https://vercel.com/docs/og-image-generation), [`satori`](https://github.com/vercel/satori) et `resvg` pour convertir HTML et CSS en PNG.
+> - Seuls flexbox et un sous-ensemble de propriétés CSS sont pris en charge. Les mises en page avancées (par exemple `display: grid`) ne fonctionneront pas.
\ No newline at end of file
diff --git a/apps/docs/content/fr/docs/01-app/01-getting-started/14-deploying.mdx b/apps/docs/content/fr/docs/01-app/01-getting-started/14-deploying.mdx
new file mode 100644
index 00000000..c5f3ca2c
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/01-getting-started/14-deploying.mdx
@@ -0,0 +1,82 @@
+---
+source-updated-at: 2025-06-01T01:32:20.000Z
+translation-updated-at: 2025-06-02T20:00:57.397Z
+title: Comment déployer votre application Next.js
+nav_title: Déploiement
+description: Apprenez comment déployer votre application Next.js.
+---
+
+Next.js peut être déployé en tant que serveur Node.js, conteneur Docker, export statique ou adapté pour fonctionner sur différentes plateformes.
+
+| Option de déploiement | Fonctionnalités prises en charge |
+| -------------------------------- | -------------------------------- |
+| [Serveur Node.js](#nodejs-server) | Toutes |
+| [Conteneur Docker](#docker) | Toutes |
+| [Export statique](#static-export)| Limitées |
+| [Adaptateurs](#adapters) | Spécifiques à la plateforme |
+
+## Serveur Node.js
+
+Next.js peut être déployé sur n'importe quel fournisseur prenant en charge Node.js. Assurez-vous que votre `package.json` contient les scripts `"build"` et `"start"` :
+
+```json filename="package.json"
+{
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start"
+ }
+}
+```
+
+Ensuite, exécutez `npm run build` pour construire votre application et `npm run start` pour démarrer le serveur Node.js. Ce serveur prend en charge toutes les fonctionnalités de Next.js. Si nécessaire, vous pouvez également passer à un [serveur personnalisé](/docs/app/guides/custom-server).
+
+Les déploiements Node.js prennent en charge toutes les fonctionnalités de Next.js. Apprenez comment les [configurer](/docs/app/guides/self-hosting) pour votre infrastructure.
+
+### Modèles
+
+- [Flightcontrol](https://github.com/nextjs/deploy-flightcontrol)
+- [Railway](https://github.com/nextjs/deploy-railway)
+- [Replit](https://github.com/nextjs/deploy-replit)
+
+## Docker
+
+Next.js peut être déployé sur n'importe quel fournisseur prenant en charge les conteneurs [Docker](https://www.docker.com/). Cela inclut les orchestrateurs de conteneurs comme Kubernetes ou un fournisseur cloud exécutant Docker.
+
+Les déploiements Docker prennent en charge toutes les fonctionnalités de Next.js. Apprenez comment les [configurer](/docs/app/guides/self-hosting) pour votre infrastructure.
+
+### Modèles
+
+- [Docker](https://github.com/vercel/next.js/tree/canary/examples/with-docker)
+- [Docker Multi-Environnement](https://github.com/vercel/next.js/tree/canary/examples/with-docker-multi-env)
+- [DigitalOcean](https://github.com/nextjs/deploy-digitalocean)
+- [Fly.io](https://github.com/nextjs/deploy-fly)
+- [Google Cloud Run](https://github.com/nextjs/deploy-google-cloud-run)
+- [Render](https://github.com/nextjs/deploy-render)
+- [SST](https://github.com/nextjs/deploy-sst)
+
+## Export statique
+
+Next.js permet de démarrer comme un site statique ou une [Application à Page Unique (SPA)](/docs/app/guides/single-page-applications), puis éventuellement de passer à des fonctionnalités nécessitant un serveur.
+
+Comme Next.js prend en charge les [exports statiques](/docs/app/guides/static-exports), il peut être déployé et hébergé sur n'importe quel serveur web capable de servir des assets statiques HTML/CSS/JS. Cela inclut des outils comme AWS S3, Nginx ou Apache.
+
+Fonctionner en tant qu'[export statique](/docs/app/guides/static-exports) **ne prend pas en charge** les fonctionnalités de Next.js nécessitant un serveur. [En savoir plus](/docs/app/guides/static-exports#unsupported-features).
+
+### Modèles
+
+- [GitHub Pages](https://github.com/nextjs/deploy-github-pages)
+
+## Adaptateurs
+
+Next.js peut être adapté pour fonctionner sur différentes plateformes afin de supporter leurs capacités d'infrastructure.
+
+Consultez la documentation de chaque fournisseur pour connaître les fonctionnalités Next.js prises en charge :
+
+- [AWS Amplify Hosting](https://docs.amplify.aws/nextjs/start/quickstart/nextjs-app-router-client-components)
+- [Cloudflare](https://developers.cloudflare.com/workers/frameworks/framework-guides/nextjs)
+- [Deno Deploy](https://docs.deno.com/examples/next_tutorial)
+- [Netlify](https://docs.netlify.com/frameworks/next-js/overview/#next-js-support-on-netlify)
+- [Vercel](https://vercel.com/docs/frameworks/nextjs)
+
+> **Note :** Nous travaillons sur une [API d'adaptateurs de déploiement](https://github.com/vercel/next.js/discussions/77740) pour que toutes les plateformes puissent l'adopter. Une fois terminée, nous ajouterons une documentation sur la création de vos propres adaptateurs.
\ No newline at end of file
diff --git a/apps/docs/content/fr/docs/01-app/01-getting-started/15-upgrading.mdx b/apps/docs/content/fr/docs/01-app/01-getting-started/15-upgrading.mdx
new file mode 100644
index 00000000..710e2872
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/01-getting-started/15-upgrading.mdx
@@ -0,0 +1,54 @@
+---
+source-updated-at: 2025-06-01T01:32:20.000Z
+translation-updated-at: 2025-06-02T20:00:14.344Z
+title: Comment mettre à jour votre application Next.js
+nav_title: Mise à jour
+description: Apprenez comment mettre à jour votre application Next.js vers la dernière version.
+related:
+ title: Guides de version
+ description: Consultez les guides de version pour des instructions détaillées de mise à jour.
+ links:
+ - app/guides/upgrading/version-15
+ - app/guides/upgrading/version-14
+---
+
+## Dernière version
+
+Pour mettre à jour vers la dernière version de Next.js, vous pouvez utiliser le codemod `upgrade` :
+
+```bash filename="Terminal"
+npx @next/codemod@canary upgrade latest
+```
+
+Si vous préférez effectuer la mise à jour manuellement, installez les dernières versions de Next.js et React :
+
+```bash filename="Terminal"
+npm i next@latest react@latest react-dom@latest eslint-config-next@latest
+```
+
+## Version canari
+
+Pour mettre à jour vers la dernière version canari, assurez-vous d'être sur la dernière version stable de Next.js et que tout fonctionne comme prévu. Ensuite, exécutez la commande suivante :
+
+```bash filename="Terminal"
+npm i next@canary
+```
+
+### Fonctionnalités disponibles en version canari
+
+Les fonctionnalités suivantes sont actuellement disponibles en version canari :
+
+**Mise en cache** :
+
+- [`"use cache"`](/docs/app/api-reference/directives/use-cache)
+- [`cacheLife`](/docs/app/api-reference/functions/cacheLife)
+- [`cacheTag`](/docs/app/api-reference/functions/cacheTag)
+- [`dynamicIO`](/docs/app/api-reference/config/next-config-js/dynamicIO)
+
+**Authentification** :
+
+- [`forbidden`](/docs/app/api-reference/functions/forbidden)
+- [`unauthorized`](/docs/app/api-reference/functions/unauthorized)
+- [`forbidden.js`](/docs/app/api-reference/file-conventions/forbidden)
+- [`unauthorized.js`](/docs/app/api-reference/file-conventions/unauthorized)
+- [`authInterrupts`](/docs/app/api-reference/config/next-config-js/authInterrupts)
\ No newline at end of file
diff --git a/apps/docs/content/fr/docs/01-app/01-getting-started/index.mdx b/apps/docs/content/fr/docs/01-app/01-getting-started/index.mdx
new file mode 100644
index 00000000..26a6b0ce
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/01-getting-started/index.mdx
@@ -0,0 +1,23 @@
+---
+source-updated-at: 2025-05-16T04:52:11.000Z
+translation-updated-at: 2025-06-02T20:00:02.107Z
+title: Premiers pas
+description: Apprenez à créer des applications web full-stack avec le routeur App de Next.js.
+---
+
+Bienvenue dans la documentation de Next.js !
+
+Cette section **Premiers pas** vous aidera à créer votre première application Next.js et à découvrir les fonctionnalités principales que vous utiliserez dans chaque projet.
+
+## Prérequis
+
+Notre documentation suppose une certaine familiarité avec le développement web. Avant de commencer, il est recommandé d'être à l'aise avec :
+
+- HTML
+- CSS
+- JavaScript
+- React
+
+Si vous débutez avec React ou avez besoin d'une remise à niveau, nous vous recommandons de commencer par notre [cours sur les fondamentaux de React](/learn/react-foundations), et le [cours sur les fondamentaux de Next.js](/learn/dashboard-app) qui vous guide dans la construction d'une application tout en apprenant.
+
+## Prochaines étapes
\ No newline at end of file
diff --git a/apps/docs/content/fr/docs/01-app/02-guides/analytics.mdx b/apps/docs/content/fr/docs/01-app/02-guides/analytics.mdx
new file mode 100644
index 00000000..4532c2c1
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/02-guides/analytics.mdx
@@ -0,0 +1,234 @@
+---
+source-updated-at: 2025-05-19T22:31:51.000Z
+translation-updated-at: 2025-06-02T20:01:10.825Z
+title: Comment ajouter des analyses à votre application Next.js
+nav_title: Analytique
+description: Mesurez et suivez les performances des pages avec Next.js Speed Insights
+---
+
+{/* Le contenu de ce document est partagé entre le routeur app et pages. Vous pouvez utiliser le composant `Contenu ` pour ajouter du contenu spécifique au routeur Pages. Tout contenu partagé ne doit pas être encapsulé dans un composant. */}
+
+Next.js dispose d'un support intégré pour mesurer et rapporter les métriques de performance. Vous pouvez soit utiliser le hook [`useReportWebVitals`](/docs/app/api-reference/functions/use-report-web-vitals) pour gérer vous-même le reporting, soit utiliser le [service managé](https://vercel.com/analytics?utm_source=next-site&utm_medium=docs&utm_campaign=next-website) de Vercel qui collecte et visualise automatiquement les métriques pour vous.
+
+## Instrumentation côté client
+
+Pour des besoins plus avancés en analyse et monitoring, Next.js fournit un fichier `instrumentation-client.js|ts` qui s'exécute avant le code frontend de votre application. C'est idéal pour configurer des outils globaux d'analyse, de suivi d'erreurs ou de monitoring de performance.
+
+Pour l'utiliser, créez un fichier `instrumentation-client.js` ou `instrumentation-client.ts` à la racine de votre application :
+
+```js filename="instrumentation-client.js"
+// Initialiser l'analyse avant le démarrage de l'app
+console.log('Analyse initialisée')
+
+// Configurer le suivi global des erreurs
+window.addEventListener('error', (event) => {
+ // Envoyer à votre service de suivi d'erreurs
+ reportError(event.error)
+})
+```
+
+## Implémentation personnalisée
+
+
+
+```jsx filename="pages/_app.js"
+import { useReportWebVitals } from 'next/web-vitals'
+
+function MyApp({ Component, pageProps }) {
+ useReportWebVitals((metric) => {
+ console.log(metric)
+ })
+
+ return
+}
+```
+
+Consultez la [Référence API](/docs/pages/api-reference/functions/use-report-web-vitals) pour plus d'informations.
+
+
+
+
+
+```jsx filename="app/_components/web-vitals.js"
+'use client'
+
+import { useReportWebVitals } from 'next/web-vitals'
+
+export function WebVitals() {
+ useReportWebVitals((metric) => {
+ console.log(metric)
+ })
+}
+```
+
+```jsx filename="app/layout.js"
+import { WebVitals } from './_components/web-vitals'
+
+export default function Layout({ children }) {
+ return (
+
+
+
+ {children}
+
+
+ )
+}
+```
+
+> Comme le hook `useReportWebVitals` nécessite la directive `'use client'`, l'approche la plus performante est de créer un composant séparé que le layout racine importe. Cela limite la frontière client exclusivement au composant `WebVitals`.
+
+Consultez la [Référence API](/docs/app/api-reference/functions/use-report-web-vitals) pour plus d'informations.
+
+
+
+## Web Vitals
+
+Les [Web Vitals](https://web.dev/vitals/) sont un ensemble de métriques utiles qui visent à capturer l'expérience utilisateur d'une page web. Les Web Vitals suivants sont inclus :
+
+- [Time to First Byte](https://developer.mozilla.org/docs/Glossary/Time_to_first_byte) (TTFB)
+- [First Contentful Paint](https://developer.mozilla.org/docs/Glossary/First_contentful_paint) (FCP)
+- [Largest Contentful Paint](https://web.dev/lcp/) (LCP)
+- [First Input Delay](https://web.dev/fid/) (FID)
+- [Cumulative Layout Shift](https://web.dev/cls/) (CLS)
+- [Interaction to Next Paint](https://web.dev/inp/) (INP)
+
+Vous pouvez traiter tous les résultats de ces métriques en utilisant la propriété `name`.
+
+
+
+```jsx filename="pages/_app.js"
+import { useReportWebVitals } from 'next/web-vitals'
+
+function MyApp({ Component, pageProps }) {
+ useReportWebVitals((metric) => {
+ switch (metric.name) {
+ case 'FCP': {
+ // traiter les résultats FCP
+ }
+ case 'LCP': {
+ // traiter les résultats LCP
+ }
+ // ...
+ }
+ })
+
+ return
+}
+```
+
+
+
+
+
+```tsx filename="app/_components/web-vitals.tsx" switcher
+'use client'
+
+import { useReportWebVitals } from 'next/web-vitals'
+
+export function WebVitals() {
+ useReportWebVitals((metric) => {
+ switch (metric.name) {
+ case 'FCP': {
+ // traiter les résultats FCP
+ }
+ case 'LCP': {
+ // traiter les résultats LCP
+ }
+ // ...
+ }
+ })
+}
+```
+
+```jsx filename="app/_components/web-vitals.js" switcher
+'use client'
+
+import { useReportWebVitals } from 'next/web-vitals'
+
+export function WebVitals() {
+ useReportWebVitals((metric) => {
+ switch (metric.name) {
+ case 'FCP': {
+ // traiter les résultats FCP
+ }
+ case 'LCP': {
+ // traiter les résultats LCP
+ }
+ // ...
+ }
+ })
+}
+```
+
+
+
+
+
+## Métriques personnalisées
+
+En plus des métriques principales listées ci-dessus, il existe des métriques personnalisées supplémentaires qui mesurent le temps nécessaire à l'hydratation et au rendu de la page :
+
+- `Next.js-hydration` : Temps nécessaire pour que la page démarre et termine son hydratation (en ms)
+- `Next.js-route-change-to-render` : Temps nécessaire pour qu'une page commence à s'afficher après un changement de route (en ms)
+- `Next.js-render` : Temps nécessaire pour qu'une page termine de s'afficher après un changement de route (en ms)
+
+Vous pouvez traiter les résultats de ces métriques séparément :
+
+```js
+export function reportWebVitals(metric) {
+ switch (metric.name) {
+ case 'Next.js-hydration':
+ // traiter les résultats d'hydratation
+ break
+ case 'Next.js-route-change-to-render':
+ // traiter les résultats de changement de route vers rendu
+ break
+ case 'Next.js-render':
+ // traiter les résultats de rendu
+ break
+ default:
+ break
+ }
+}
+```
+
+Ces métriques fonctionnent dans tous les navigateurs supportant l'[API User Timing](https://caniuse.com/#feat=user-timing).
+
+
+
+## Envoi des résultats à des systèmes externes
+
+Vous pouvez envoyer les résultats à n'importe quel endpoint pour mesurer et suivre les performances réelles des utilisateurs sur votre site. Par exemple :
+
+```js
+useReportWebVitals((metric) => {
+ const body = JSON.stringify(metric)
+ const url = 'https://example.com/analytics'
+
+ // Utiliser `navigator.sendBeacon()` si disponible, sinon `fetch()`.
+ if (navigator.sendBeacon) {
+ navigator.sendBeacon(url, body)
+ } else {
+ fetch(url, { body, method: 'POST', keepalive: true })
+ }
+})
+```
+
+> **Bon à savoir** : Si vous utilisez [Google Analytics](https://analytics.google.com/analytics/web/), utiliser la valeur `id` vous permet de construire manuellement des distributions de métriques (pour calculer des percentiles, etc.)
+
+> ```js
+> useReportWebVitals((metric) => {
+> // Utiliser `window.gtag` si vous avez initialisé Google Analytics comme dans cet exemple :
+> // https://github.com/vercel/next.js/blob/canary/examples/with-google-analytics
+> window.gtag('event', metric.name, {
+> value: Math.round(
+> metric.name === 'CLS' ? metric.value * 1000 : metric.value
+> ), // les valeurs doivent être des entiers
+> event_label: metric.id, // id unique au chargement de la page actuelle
+> non_interaction: true, // évite d'affecter le taux de rebond.
+> })
+> })
+> ```
+>
+> En savoir plus sur [l'envoi de résultats à Google Analytics](https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics).
\ No newline at end of file
diff --git a/apps/docs/content/fr/docs/01-app/02-guides/authentication.mdx b/apps/docs/content/fr/docs/01-app/02-guides/authentication.mdx
new file mode 100644
index 00000000..a96693eb
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/02-guides/authentication.mdx
@@ -0,0 +1,1653 @@
+---
+source-updated-at: 2025-06-01T01:32:20.000Z
+translation-updated-at: 2025-06-02T20:10:01.613Z
+title: Comment implémenter l'authentification dans Next.js
+nav_title: Authentification
+description: Découvrez comment implémenter l'authentification dans votre application Next.js.
+---
+
+Comprendre l'authentification est crucial pour protéger les données de votre application. Cette page vous guidera à travers les fonctionnalités de React et Next.js à utiliser pour implémenter l'authentification.
+
+Avant de commencer, il est utile de décomposer le processus en trois concepts :
+
+1. **[Authentification](#authentication)** : Vérifie si l'utilisateur est bien celui qu'il prétend être. Cela nécessite que l'utilisateur prouve son identité avec quelque chose qu'il connaît, comme un nom d'utilisateur et un mot de passe.
+2. **[Gestion de session](#session-management)** : Suit l'état d'authentification de l'utilisateur à travers les requêtes.
+3. **[Autorisation](#authorization)** : Détermine quelles routes et données l'utilisateur peut accéder.
+
+Ce diagramme montre le flux d'authentification utilisant les fonctionnalités de React et Next.js :
+
+
+
+Les exemples de cette page parcourent une authentification basique avec nom d'utilisateur et mot de passe à des fins éducatives. Bien que vous puissiez implémenter une solution d'authentification personnalisée, pour une sécurité accrue et une simplicité, nous recommandons d'utiliser une bibliothèque d'authentification. Celles-ci offrent des solutions intégrées pour l'authentification, la gestion de session et l'autorisation, ainsi que des fonctionnalités supplémentaires comme les connexions sociales, l'authentification multi-facteurs et le contrôle d'accès basé sur les rôles. Vous trouverez une liste dans la section [Bibliothèques d'authentification](#auth-libraries).
+
+## Authentification
+
+
+
+### Fonctionnalité d'inscription et de connexion
+
+Vous pouvez utiliser l'élément [``](https://react.dev/reference/react-dom/components/form) avec les [Actions Serveur](/docs/app/building-your-application/data-fetching/server-actions-and-mutations) de React et `useActionState` pour capturer les identifiants utilisateur, valider les champs du formulaire et appeler l'API ou la base de données de votre fournisseur d'authentification.
+
+Comme les Actions Serveur s'exécutent toujours sur le serveur, elles fournissent un environnement sécurisé pour gérer la logique d'authentification.
+
+Voici les étapes pour implémenter la fonctionnalité d'inscription/connexion :
+
+#### 1. Capturer les identifiants utilisateur
+
+Pour capturer les identifiants utilisateur, créez un formulaire qui invoque une Action Serveur lors de la soumission. Par exemple, un formulaire d'inscription qui accepte le nom, l'email et le mot de passe de l'utilisateur :
+
+```tsx filename="app/ui/signup-form.tsx" switcher
+import { signup } from '@/app/actions/auth'
+
+export function SignupForm() {
+ return (
+
+
+ Nom
+
+
+
+ Email
+
+
+
+ Mot de passe
+
+
+ S'inscrire
+
+ )
+}
+```
+
+```jsx filename="app/ui/signup-form.js" switcher
+import { signup } from '@/app/actions/auth'
+
+export function SignupForm() {
+ return (
+
+
+ Nom
+
+
+
+ Email
+
+
+
+ Mot de passe
+
+
+ S'inscrire
+
+ )
+}
+```
+
+```tsx filename="app/actions/auth.ts" switcher
+export async function signup(formData: FormData) {}
+```
+
+```jsx filename="app/actions/auth.js" switcher
+export async function signup(formData) {}
+```
+
+#### 2. Valider les champs du formulaire côté serveur
+
+Utilisez l'Action Serveur pour valider les champs du formulaire côté serveur. Si votre fournisseur d'authentification ne fournit pas de validation de formulaire, vous pouvez utiliser une bibliothèque de validation de schéma comme [Zod](https://zod.dev/) ou [Yup](https://github.com/jquense/yup).
+
+En utilisant Zod comme exemple, vous pouvez définir un schéma de formulaire avec des messages d'erreur appropriés :
+
+```ts filename="app/lib/definitions.ts" switcher
+import { z } from 'zod'
+
+export const SignupFormSchema = z.object({
+ name: z
+ .string()
+ .min(2, { message: 'Le nom doit contenir au moins 2 caractères.' })
+ .trim(),
+ email: z.string().email({ message: 'Veuillez entrer un email valide.' }).trim(),
+ password: z
+ .string()
+ .min(8, { message: 'Doit contenir au moins 8 caractères' })
+ .regex(/[a-zA-Z]/, { message: 'Doit contenir au moins une lettre.' })
+ .regex(/[0-9]/, { message: 'Doit contenir au moins un chiffre.' })
+ .regex(/[^a-zA-Z0-9]/, {
+ message: 'Doit contenir au moins un caractère spécial.',
+ })
+ .trim(),
+})
+
+export type FormState =
+ | {
+ errors?: {
+ name?: string[]
+ email?: string[]
+ password?: string[]
+ }
+ message?: string
+ }
+ | undefined
+```
+
+```js filename="app/lib/definitions.js" switcher
+import { z } from 'zod'
+
+export const SignupFormSchema = z.object({
+ name: z
+ .string()
+ .min(2, { message: 'Le nom doit contenir au moins 2 caractères.' })
+ .trim(),
+ email: z.string().email({ message: 'Veuillez entrer un email valide.' }).trim(),
+ password: z
+ .string()
+ .min(8, { message: 'Doit contenir au moins 8 caractères' })
+ .regex(/[a-zA-Z]/, { message: 'Doit contenir au moins une lettre.' })
+ .regex(/[0-9]/, { message: 'Doit contenir au moins un chiffre.' })
+ .regex(/[^a-zA-Z0-9]/, {
+ message: 'Doit contenir au moins un caractère spécial.',
+ })
+ .trim(),
+})
+```
+
+Pour éviter des appels inutiles à l'API ou à la base de données de votre fournisseur d'authentification, vous pouvez `return` prématurément dans l'Action Serveur si des champs du formulaire ne correspondent pas au schéma défini.
+
+```ts filename="app/actions/auth.ts" switcher
+import { SignupFormSchema, FormState } from '@/app/lib/definitions'
+
+export async function signup(state: FormState, formData: FormData) {
+ // Valider les champs du formulaire
+ const validatedFields = SignupFormSchema.safeParse({
+ name: formData.get('name'),
+ email: formData.get('email'),
+ password: formData.get('password'),
+ })
+
+ // Si des champs sont invalides, retourner prématurément
+ if (!validatedFields.success) {
+ return {
+ errors: validatedFields.error.flatten().fieldErrors,
+ }
+ }
+
+ // Appeler le fournisseur ou la base de données pour créer un utilisateur...
+}
+```
+
+```js filename="app/actions/auth.js" switcher
+import { SignupFormSchema } from '@/app/lib/definitions'
+
+export async function signup(state, formData) {
+ // Valider les champs du formulaire
+ const validatedFields = SignupFormSchema.safeParse({
+ name: formData.get('name'),
+ email: formData.get('email'),
+ password: formData.get('password'),
+ })
+
+ // Si des champs sont invalides, retourner prématurément
+ if (!validatedFields.success) {
+ return {
+ errors: validatedFields.error.flatten().fieldErrors,
+ }
+ }
+
+ // Appeler le fournisseur ou la base de données pour créer un utilisateur...
+}
+```
+
+De retour dans votre ` `, vous pouvez utiliser le hook `useActionState` de React pour afficher les erreurs de validation pendant la soumission du formulaire :
+
+```tsx filename="app/ui/signup-form.tsx" switcher highlight={7,15,21,27-36}
+'use client'
+
+import { signup } from '@/app/actions/auth'
+import { useActionState } from 'react'
+
+export default function SignupForm() {
+ const [state, action, pending] = useActionState(signup, undefined)
+
+ return (
+
+
+ Nom
+
+
+ {state?.errors?.name && {state.errors.name}
}
+
+
+ Email
+
+
+ {state?.errors?.email && {state.errors.email}
}
+
+
+ Mot de passe
+
+
+ {state?.errors?.password && (
+
+
Le mot de passe doit :
+
+ {state.errors.password.map((error) => (
+ - {error}
+ ))}
+
+
+ )}
+
+ S'inscrire
+
+
+ )
+}
+```
+
+```jsx filename="app/ui/signup-form.js" switcher highlight={7,15,21,27-36}
+'use client'
+
+import { signup } from '@/app/actions/auth'
+import { useActionState } from 'react'
+
+export default function SignupForm() {
+ const [state, action, pending] = useActionState(signup, undefined)
+
+ return (
+
+
+ Nom
+
+
+ {state?.errors?.name && {state.errors.name}
}
+
+
+ Email
+
+
+ {state?.errors?.email && {state.errors.email}
}
+
+
+ Mot de passe
+
+
+ {state?.errors?.password && (
+
+
Le mot de passe doit :
+
+ {state.errors.password.map((error) => (
+ - {error}
+ ))}
+
+
+ )}
+
+ S'inscrire
+
+
+ )
+}
+```
+
+> **Bon à savoir :**
+>
+> - Dans React 19, `useFormStatus` inclut des clés supplémentaires sur l'objet retourné, comme data, method et action. Si vous n'utilisez pas React 19, seule la clé `pending` est disponible.
+> - Avant de muter des données, vous devriez toujours vous assurer qu'un utilisateur est également autorisé à effectuer l'action. Voir [Authentification et Autorisation](#authorization).
+
+#### 3. Créer un utilisateur ou vérifier ses identifiants
+
+Après avoir validé les champs du formulaire, vous pouvez créer un nouveau compte utilisateur ou vérifier si l'utilisateur existe en appelant l'API ou la base de données de votre fournisseur d'authentification.
+
+Reprenons l'exemple précédent :
+
+```tsx filename="app/actions/auth.tsx" switcher
+export async function signup(state: FormState, formData: FormData) {
+ // 1. Valider les champs du formulaire
+ // ...
+
+ // 2. Préparer les données pour l'insertion dans la base de données
+ const { name, email, password } = validatedFields.data
+ // Par exemple, hacher le mot de passe de l'utilisateur avant de le stocker
+ const hashedPassword = await bcrypt.hash(password, 10)
+
+ // 3. Insérer l'utilisateur dans la base de données ou appeler l'API d'une bibliothèque d'authentification
+ const data = await db
+ .insert(users)
+ .values({
+ name,
+ email,
+ password: hashedPassword,
+ })
+ .returning({ id: users.id })
+
+ const user = data[0]
+
+ if (!user) {
+ return {
+ message: 'Une erreur est survenue lors de la création de votre compte.',
+ }
+ }
+
+ // À FAIRE :
+ // 4. Créer une session utilisateur
+ // 5. Rediriger l'utilisateur
+}
+```
+
+```jsx filename="app/actions/auth.js" switcher
+export async function signup(state, formData) {
+ // 1. Valider les champs du formulaire
+ // ...
+
+ // 2. Préparer les données pour l'insertion dans la base de données
+ const { name, email, password } = validatedFields.data
+ // Par exemple, hacher le mot de passe de l'utilisateur avant de le stocker
+ const hashedPassword = await bcrypt.hash(password, 10)
+
+ // 3. Insérer l'utilisateur dans la base de données ou appeler l'API d'une bibliothèque
+ const data = await db
+ .insert(users)
+ .values({
+ name,
+ email,
+ password: hashedPassword,
+ })
+ .returning({ id: users.id })
+
+ const user = data[0]
+
+ if (!user) {
+ return {
+ message: 'Une erreur est survenue lors de la création de votre compte.',
+ }
+ }
+
+ // À FAIRE :
+ // 4. Créer une session utilisateur
+ // 5. Rediriger l'utilisateur
+}
+```
+
+Après avoir créé avec succès le compte utilisateur ou vérifié ses identifiants, vous pouvez créer une session pour gérer l'état d'authentification de l'utilisateur. Selon votre stratégie de gestion des sessions, celle-ci peut être stockée dans un cookie, une base de données, ou les deux. Passez à la section [Gestion des sessions](#session-management) pour en savoir plus.
+
+> **Conseils :**
+>
+> - L'exemple ci-dessus est détaillé car il décompose les étapes d'authentification à des fins pédagogiques. Cela montre qu'implémenter votre propre solution sécurisée peut rapidement devenir complexe. Envisagez d'utiliser une [bibliothèque d'authentification](#auth-libraries) pour simplifier le processus.
+> - Pour améliorer l'expérience utilisateur, vous pouvez vérifier les emails ou noms d'utilisateur en double plus tôt dans le flux d'inscription. Par exemple, lorsque l'utilisateur saisit un nom d'utilisateur ou lorsque le champ perd le focus. Cela peut éviter des soumissions inutiles du formulaire et fournir un retour immédiat à l'utilisateur. Vous pouvez limiter la fréquence de ces vérifications avec des bibliothèques comme [use-debounce](https://www.npmjs.com/package/use-debounce).
+
+
+
+
+
+Voici les étapes pour implémenter un formulaire d'inscription et/ou de connexion :
+
+1. L'utilisateur soumet ses identifiants via un formulaire.
+2. Le formulaire envoie une requête traitée par une route API.
+3. Après vérification réussie, le processus est terminé, indiquant que l'utilisateur est authentifié.
+4. Si la vérification échoue, un message d'erreur est affiché.
+
+Prenons l'exemple d'un formulaire de connexion où les utilisateurs peuvent saisir leurs identifiants :
+
+```tsx filename="pages/login.tsx" switcher
+import { FormEvent } from 'react'
+import { useRouter } from 'next/router'
+
+export default function LoginPage() {
+ const router = useRouter()
+
+ async function handleSubmit(event: FormEvent) {
+ event.preventDefault()
+
+ const formData = new FormData(event.currentTarget)
+ const email = formData.get('email')
+ const password = formData.get('password')
+
+ const response = await fetch('/api/auth/login', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ email, password }),
+ })
+
+ if (response.ok) {
+ router.push('/profile')
+ } else {
+ // Gérer les erreurs
+ }
+ }
+
+ return (
+
+
+
+ Se connecter
+
+ )
+}
+```
+
+```jsx filename="pages/login.jsx" switcher
+import { FormEvent } from 'react'
+import { useRouter } from 'next/router'
+
+export default function LoginPage() {
+ const router = useRouter()
+
+ async function handleSubmit(event) {
+ event.preventDefault()
+
+ const formData = new FormData(event.currentTarget)
+ const email = formData.get('email')
+ const password = formData.get('password')
+
+ const response = await fetch('/api/auth/login', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ email, password }),
+ })
+
+ if (response.ok) {
+ router.push('/profile')
+ } else {
+ // Gérer les erreurs
+ }
+ }
+
+ return (
+
+
+
+ Se connecter
+
+ )
+}
+```
+
+Le formulaire ci-dessus a deux champs pour capturer l'email et le mot de passe de l'utilisateur. Lors de la soumission, il déclenche une fonction qui envoie une requête POST à une route API (`/api/auth/login`).
+
+Vous pouvez ensuite appeler l'API de votre fournisseur d'authentification dans la route API pour gérer l'authentification :
+
+```ts filename="pages/api/auth/login.ts" switcher
+import type { NextApiRequest, NextApiResponse } from 'next'
+import { signIn } from '@/auth'
+
+export default async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse
+) {
+ try {
+ const { email, password } = req.body
+ await signIn('credentials', { email, password })
+
+ res.status(200).json({ success: true })
+ } catch (error) {
+ if (error.type === 'CredentialsSignin') {
+ res.status(401).json({ error: 'Identifiants invalides.' })
+ } else {
+ res.status(500).json({ error: 'Une erreur est survenue.' })
+ }
+ }
+}
+```
+
+```js filename="pages/api/auth/login.js" switcher
+import { signIn } from '@/auth'
+
+export default async function handler(req, res) {
+ try {
+ const { email, password } = req.body
+ await signIn('credentials', { email, password })
+
+ res.status(200).json({ success: true })
+ } catch (error) {
+ if (error.type === 'CredentialsSignin') {
+ res.status(401).json({ error: 'Identifiants invalides.' })
+ } else {
+ res.status(500).json({ error: 'Une erreur est survenue.' })
+ }
+ }
+}
+```
+
+
+
+## Gestion des sessions
+
+La gestion des sessions garantit que l'état authentifié de l'utilisateur est conservé entre les requêtes. Elle implique la création, le stockage, le rafraîchissement et la suppression des sessions ou des jetons.
+
+Il existe deux types de sessions :
+
+1. [**Sans état (Stateless)**](#stateless-sessions) : Les données de session (ou un jeton) sont stockées dans les cookies du navigateur. Le cookie est envoyé avec chaque requête, permettant de vérifier la session côté serveur. Cette méthode est plus simple, mais peut être moins sécurisée si mal implémentée.
+2. [**Base de données (Database)**](#database-sessions) : Les données de session sont stockées dans une base de données, le navigateur de l'utilisateur ne recevant que l'ID de session chiffré. Cette méthode est plus sécurisée, mais peut être complexe et utiliser plus de ressources serveur.
+
+> **Bon à savoir :** Bien que vous puissiez utiliser l'une ou l'autre méthode, ou les deux, nous recommandons d'utiliser une bibliothèque de gestion de sessions comme [iron-session](https://github.com/vvo/iron-session) ou [Jose](https://github.com/panva/jose).
+
+### Sessions sans état (Stateless)
+
+
+
+Pour créer et gérer des sessions sans état, vous devez suivre quelques étapes :
+
+1. Générer une clé secrète, qui sera utilisée pour signer votre session, et la stocker comme [variable d'environnement](/docs/app/guides/environment-variables).
+2. Écrire une logique pour chiffrer/déchiffrer les données de session à l'aide d'une bibliothèque de gestion de sessions.
+3. Gérer les cookies avec l'API [`cookies`](/docs/app/api-reference/functions/cookies) de Next.js.
+
+En plus de ce qui précède, envisagez d'ajouter des fonctionnalités pour [mettre à jour (ou rafraîchir)](#updating-or-refreshing-sessions) la session lorsque l'utilisateur revient sur l'application, et [supprimer](#deleting-the-session) la session lorsque l'utilisateur se déconnecte.
+
+> **Bon à savoir :** Vérifiez si votre [bibliothèque d'authentification](#auth-libraries) inclut la gestion des sessions.
+
+#### 1. Générer une clé secrète
+
+Il existe plusieurs façons de générer une clé secrète pour signer votre session. Par exemple, vous pouvez utiliser la commande `openssl` dans votre terminal :
+
+```bash filename="terminal"
+openssl rand -base64 32
+```
+
+Cette commande génère une chaîne aléatoire de 32 caractères que vous pouvez utiliser comme clé secrète et stocker dans votre [fichier de variables d'environnement](/docs/app/guides/environment-variables) :
+
+```bash filename=".env"
+SESSION_SECRET=votre_clé_secrète
+```
+
+Vous pouvez ensuite référencer cette clé dans votre logique de gestion des sessions :
+
+```js filename="app/lib/session.js"
+const secretKey = process.env.SESSION_SECRET
+```
+
+#### 2. Chiffrer et déchiffrer les sessions
+
+Ensuite, vous pouvez utiliser votre [bibliothèque de gestion de sessions](#session-management-libraries) préférée pour chiffrer et déchiffrer les sessions. Continuons avec l'exemple précédent en utilisant [Jose](https://www.npmjs.com/package/jose) (compatible avec le [Edge Runtime](/docs/app/api-reference/edge)) et le package [`server-only`](https://www.npmjs.com/package/server-only) de React pour garantir que votre logique de gestion des sessions ne s'exécute que côté serveur.
+
+```tsx filename="app/lib/session.ts" switcher
+import 'server-only'
+import { SignJWT, jwtVerify } from 'jose'
+import { SessionPayload } from '@/app/lib/definitions'
+
+const secretKey = process.env.SESSION_SECRET
+const encodedKey = new TextEncoder().encode(secretKey)
+
+export async function encrypt(payload: SessionPayload) {
+ return new SignJWT(payload)
+ .setProtectedHeader({ alg: 'HS256' })
+ .setIssuedAt()
+ .setExpirationTime('7d')
+ .sign(encodedKey)
+}
+
+export async function decrypt(session: string | undefined = '') {
+ try {
+ const { payload } = await jwtVerify(session, encodedKey, {
+ algorithms: ['HS256'],
+ })
+ return payload
+ } catch (error) {
+ console.log('Échec de la vérification de la session')
+ }
+}
+```
+
+```jsx filename="app/lib/session.js" switcher
+import 'server-only'
+import { SignJWT, jwtVerify } from 'jose'
+
+const secretKey = process.env.SESSION_SECRET
+const encodedKey = new TextEncoder().encode(secretKey)
+
+export async function encrypt(payload) {
+ return new SignJWT(payload)
+ .setProtectedHeader({ alg: 'HS256' })
+ .setIssuedAt()
+ .setExpirationTime('7d')
+ .sign(encodedKey)
+}
+
+export async function decrypt(session) {
+ try {
+ const { payload } = await jwtVerify(session, encodedKey, {
+ algorithms: ['HS256'],
+ })
+ return payload
+ } catch (error) {
+ console.log('Échec de la vérification de la session')
+ }
+}
+```
+
+> **Conseils :**
+>
+> - Le payload doit contenir les données utilisateur **minimales** et uniques qui seront utilisées dans les requêtes suivantes, comme l'ID de l'utilisateur, son rôle, etc. Il ne doit pas contenir d'informations personnelles comme un numéro de téléphone, une adresse email, des informations de carte de crédit, etc., ou des données sensibles comme des mots de passe.
+
+#### 3. Définir les cookies (options recommandées)
+
+Pour stocker la session dans un cookie, utilisez l'API [`cookies`](/docs/app/api-reference/functions/cookies) de Next.js. Le cookie doit être défini côté serveur et inclure les options recommandées :
+
+- **HttpOnly** : Empêche JavaScript côté client d'accéder au cookie.
+- **Secure** : Utilise https pour envoyer le cookie.
+- **SameSite** : Spécifie si le cookie peut être envoyé avec des requêtes cross-site.
+- **Max-Age ou Expires** : Supprime le cookie après une certaine période.
+- **Path** : Définit le chemin URL pour le cookie.
+
+Consultez [MDN](https://developer.mozilla.org/fr/docs/Web/HTTP/Cookies) pour plus d'informations sur ces options.
+
+```ts filename="app/lib/session.ts" switcher
+import 'server-only'
+import { cookies } from 'next/headers'
+
+export async function createSession(userId: string) {
+ const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
+ const session = await encrypt({ userId, expiresAt })
+ const cookieStore = await cookies()
+
+ cookieStore.set('session', session, {
+ httpOnly: true,
+ secure: true,
+ expires: expiresAt,
+ sameSite: 'lax',
+ path: '/',
+ })
+}
+```
+
+```js filename="app/lib/session.js" switcher
+import 'server-only'
+import { cookies } from 'next/headers'
+
+export async function createSession(userId) {
+ const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
+ const session = await encrypt({ userId, expiresAt })
+ const cookieStore = await cookies()
+
+ cookieStore.set('session', session, {
+ httpOnly: true,
+ secure: true,
+ expires: expiresAt,
+ sameSite: 'lax',
+ path: '/',
+ })
+}
+```
+
+Dans votre Server Action, vous pouvez invoquer la fonction `createSession()` et utiliser l'API [`redirect()`](/docs/app/guides/redirecting) pour rediriger l'utilisateur vers la page appropriée :
+
+```ts filename="app/actions/auth.ts" switcher
+import { createSession } from '@/app/lib/session'
+
+export async function signup(state: FormState, formData: FormData) {
+ // Étapes précédentes :
+ // 1. Valider les champs du formulaire
+ // 2. Préparer les données pour l'insertion dans la base de données
+ // 3. Insérer l'utilisateur dans la base de données ou appeler l'API d'une bibliothèque
+
+ // Étapes actuelles :
+ // 4. Créer une session utilisateur
+ await createSession(user.id)
+ // 5. Rediriger l'utilisateur
+ redirect('/profile')
+}
+```
+
+```js filename="app/actions/auth.js" switcher
+import { createSession } from '@/app/lib/session'
+
+export async function signup(state, formData) {
+ // Étapes précédentes :
+ // 1. Valider les champs du formulaire
+ // 2. Préparer les données pour l'insertion dans la base de données
+ // 3. Insérer l'utilisateur dans la base de données ou appeler l'API d'une bibliothèque
+
+ // Étapes actuelles :
+ // 4. Créer une session utilisateur
+ await createSession(user.id)
+ // 5. Rediriger l'utilisateur
+ redirect('/profile')
+}
+```
+
+> **Conseils :**
+>
+> - **Les cookies doivent être définis côté serveur** pour éviter toute manipulation côté client.
+> - 🎥 Regardez : En savoir plus sur les sessions sans état et l'authentification avec Next.js → [YouTube (11 minutes)](https://www.youtube.com/watch?v=DJvM2lSPn6w).
+
+#### Mise à jour (ou rafraîchissement) des sessions
+
+Vous pouvez également prolonger la durée d'expiration de la session. C'est utile pour maintenir l'utilisateur connecté après qu'il revient sur l'application. Par exemple :
+
+```ts filename="app/lib/session.ts" switcher
+import 'server-only'
+import { cookies } from 'next/headers'
+import { decrypt } from '@/app/lib/session'
+
+export async function updateSession() {
+ const session = (await cookies()).get('session')?.value
+ const payload = await decrypt(session)
+
+ if (!session || !payload) {
+ return null
+ }
+
+ const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
+
+ const cookieStore = await cookies()
+ cookieStore.set('session', session, {
+ httpOnly: true,
+ secure: true,
+ expires: expires,
+ sameSite: 'lax',
+ path: '/',
+ })
+}
+```
+
+```js filename="app/lib/session.js" switcher
+import 'server-only'
+import { cookies } from 'next/headers'
+import { decrypt } from '@/app/lib/session'
+
+export async function updateSession() {
+ const session = (await cookies()).get('session')?.value
+ const payload = await decrypt(session)
+
+ if (!session || !payload) {
+ return null
+ }
+
+ const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)(
+ await cookies()
+ ).set('session', session, {
+ httpOnly: true,
+ secure: true,
+ expires: expires,
+ sameSite: 'lax',
+ path: '/',
+ })
+}
+```
+
+> **Conseil :** Vérifiez si votre bibliothèque d'authentification prend en charge les jetons de rafraîchissement, qui peuvent être utilisés pour prolonger la session de l'utilisateur.
+
+#### Suppression de la session
+
+Pour supprimer la session, vous pouvez supprimer le cookie :
+
+```ts filename="app/lib/session.ts" switcher
+import 'server-only'
+import { cookies } from 'next/headers'
+
+export async function deleteSession() {
+ const cookieStore = await cookies()
+ cookieStore.delete('session')
+}
+```
+
+```js filename="app/lib/session.js" switcher
+import 'server-only'
+import { cookies } from 'next/headers'
+
+export async function deleteSession() {
+ const cookieStore = await cookies()
+ cookieStore.delete('session')
+}
+```
+
+Vous pouvez ensuite réutiliser la fonction `deleteSession()` dans votre application, par exemple lors de la déconnexion :
+
+```ts filename="app/actions/auth.ts" switcher
+import { cookies } from 'next/headers'
+import { deleteSession } from '@/app/lib/session'
+
+export async function logout() {
+ await deleteSession()
+ redirect('/login')
+}
+```
+
+```js filename="app/actions/auth.js" switcher
+import { cookies } from 'next/headers'
+import { deleteSession } from '@/app/lib/session'
+
+export async function logout() {
+ await deleteSession()
+ redirect('/login')
+}
+```
+
+
+
+
+
+#### Définition et suppression des cookies
+
+Vous pouvez utiliser les [Routes API](/docs/pages/building-your-application/routing/api-routes) pour définir la session comme un cookie côté serveur :
+
+```ts filename="pages/api/login.ts" switcher
+import { serialize } from 'cookie'
+import type { NextApiRequest, NextApiResponse } from 'next'
+import { encrypt } from '@/app/lib/session'
+
+export default function handler(req: NextApiRequest, res: NextApiResponse) {
+ const sessionData = req.body
+ const encryptedSessionData = encrypt(sessionData)
+
+ const cookie = serialize('session', encryptedSessionData, {
+ httpOnly: true,
+ secure: process.env.NODE_ENV === 'production',
+ maxAge: 60 * 60 * 24 * 7, // Une semaine
+ path: '/',
+ })
+ res.setHeader('Set-Cookie', cookie)
+ res.status(200).json({ message: 'Cookie défini avec succès !' })
+}
+```
+
+```js filename="pages/api/login.js" switcher
+import { serialize } from 'cookie'
+import { encrypt } from '@/app/lib/session'
+
+export default function handler(req, res) {
+ const sessionData = req.body
+ const encryptedSessionData = encrypt(sessionData)
+
+ const cookie = serialize('session', encryptedSessionData, {
+ httpOnly: true,
+ secure: process.env.NODE_ENV === 'production',
+ maxAge: 60 * 60 * 24 * 7, // Une semaine
+ path: '/',
+ })
+ res.setHeader('Set-Cookie', cookie)
+ res.status(200).json({ message: 'Cookie défini avec succès !' })
+}
+```
+
+
+
+### Sessions de base de données
+
+Pour créer et gérer des sessions de base de données, vous devrez suivre ces étapes :
+
+1. Créez une table dans votre base de données pour stocker les sessions et les données (ou vérifiez si votre bibliothèque d'authentification gère cela).
+2. Implémentez des fonctionnalités pour insérer, mettre à jour et supprimer des sessions.
+3. Chiffrez l'ID de session avant de le stocker dans le navigateur de l'utilisateur, et assurez-vous que la base de données et le cookie restent synchronisés (ceci est facultatif, mais recommandé pour les vérifications d'authentification optimistes dans le [Middleware](#optimistic-checks-with-middleware-optional)).
+
+
+
+Par exemple :
+
+```ts filename="app/lib/session.ts" switcher
+import cookies from 'next/headers'
+import { db } from '@/app/lib/db'
+import { encrypt } from '@/app/lib/session'
+
+export async function createSession(id: number) {
+ const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
+
+ // 1. Créez une session dans la base de données
+ const data = await db
+ .insert(sessions)
+ .values({
+ userId: id,
+ expiresAt,
+ })
+ // Retournez l'ID de session
+ .returning({ id: sessions.id })
+
+ const sessionId = data[0].id
+
+ // 2. Chiffrez l'ID de session
+ const session = await encrypt({ sessionId, expiresAt })
+
+ // 3. Stockez la session dans les cookies pour des vérifications d'authentification optimistes
+ const cookieStore = await cookies()
+ cookieStore.set('session', session, {
+ httpOnly: true,
+ secure: true,
+ expires: expiresAt,
+ sameSite: 'lax',
+ path: '/',
+ })
+}
+```
+
+```js filename="app/lib/session.js" switcher
+import cookies from 'next/headers'
+import { db } from '@/app/lib/db'
+import { encrypt } from '@/app/lib/session'
+
+export async function createSession(id) {
+ const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
+
+ // 1. Créez une session dans la base de données
+ const data = await db
+ .insert(sessions)
+ .values({
+ userId: id,
+ expiresAt,
+ })
+ // Retournez l'ID de session
+ .returning({ id: sessions.id })
+
+ const sessionId = data[0].id
+
+ // 2. Chiffrez l'ID de session
+ const session = await encrypt({ sessionId, expiresAt })
+
+ // 3. Stockez la session dans les cookies pour des vérifications d'authentification optimistes
+ const cookieStore = await cookies()
+ cookieStore.set('session', session, {
+ httpOnly: true,
+ secure: true,
+ expires: expiresAt,
+ sameSite: 'lax',
+ path: '/',
+ })
+}
+```
+
+> **Conseils** :
+>
+> - Pour un accès plus rapide, vous pouvez envisager d'ajouter une mise en cache côté serveur pour la durée de vie de la session. Vous pouvez également conserver les données de session dans votre base de données principale et combiner les requêtes de données pour réduire le nombre de requêtes.
+> - Vous pouvez choisir d'utiliser des sessions de base de données pour des cas d'utilisation plus avancés, comme suivre la dernière fois qu'un utilisateur s'est connecté, le nombre d'appareils actifs, ou donner aux utilisateurs la possibilité de se déconnecter de tous les appareils.
+
+Après avoir implémenté la gestion des sessions, vous devrez ajouter une logique d'autorisation pour contrôler ce que les utilisateurs peuvent accéder et faire dans votre application. Passez à la section [Autorisation](#authorization) pour en savoir plus.
+
+
+
+
+
+**Création d'une session côté serveur** :
+
+```ts filename="pages/api/create-session.ts" switcher
+import db from '../../lib/db'
+import type { NextApiRequest, NextApiResponse } from 'next'
+
+export default async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse
+) {
+ try {
+ const user = req.body
+ const sessionId = generateSessionId()
+ await db.insertSession({
+ sessionId,
+ userId: user.id,
+ createdAt: new Date(),
+ })
+
+ res.status(200).json({ sessionId })
+ } catch (error) {
+ res.status(500).json({ error: 'Erreur interne du serveur' })
+ }
+}
+```
+
+```js filename="pages/api/create-session.js" switcher
+import db from '../../lib/db'
+
+export default async function handler(req, res) {
+ try {
+ const user = req.body
+ const sessionId = generateSessionId()
+ await db.insertSession({
+ sessionId,
+ userId: user.id,
+ createdAt: new Date(),
+ })
+
+ res.status(200).json({ sessionId })
+ } catch (error) {
+ res.status(500).json({ error: 'Erreur interne du serveur' })
+ }
+}
+```
+
+
+
+## Autorisation
+
+Une fois qu'un utilisateur est authentifié et qu'une session est créée, vous pouvez implémenter l'autorisation pour contrôler ce que l'utilisateur peut accéder et faire dans votre application.
+
+Il existe deux principaux types de vérifications d'autorisation :
+
+1. **Optimiste** : Vérifie si l'utilisateur est autorisé à accéder à une route ou à effectuer une action en utilisant les données de session stockées dans le cookie. Ces vérifications sont utiles pour des opérations rapides, comme afficher/masquer des éléments d'interface ou rediriger les utilisateurs en fonction des permissions ou rôles.
+2. **Sécurisée** : Vérifie si l'utilisateur est autorisé à accéder à une route ou à effectuer une action en utilisant les données de session stockées dans la base de données. Ces vérifications sont plus sécurisées et sont utilisées pour des opérations nécessitant un accès à des données sensibles ou des actions.
+
+Pour les deux cas, nous recommandons :
+
+- De créer une [Couche d'Accès aux Données (DAL)](#creating-a-data-access-layer-dal) pour centraliser votre logique d'autorisation.
+- D'utiliser des [Objets de Transfert de Données (DTO)](#using-data-transfer-objects-dto) pour ne retourner que les données nécessaires.
+- D'utiliser optionnellement le [Middleware](#optimistic-checks-with-middleware-optional) pour effectuer des vérifications optimistes.
+
+### Vérifications optimistes avec Middleware (Optionnel)
+
+Il existe des cas où vous pouvez vouloir utiliser le [Middleware](/docs/app/building-your-application/routing/middleware) et rediriger les utilisateurs en fonction des permissions :
+
+- Pour effectuer des vérifications optimistes. Comme le Middleware s'exécute sur chaque route, c'est un bon moyen de centraliser la logique de redirection et de pré-filtrer les utilisateurs non autorisés.
+- Pour protéger les routes statiques qui partagent des données entre utilisateurs (par exemple, du contenu derrière un paywall).
+
+Cependant, comme le Middleware s'exécute sur chaque route, y compris les routes [préchargées](/docs/app/building-your-application/routing/linking-and-navigating#2-prefetching), il est important de ne lire la session que depuis le cookie (vérifications optimistes), et d'éviter les vérifications en base de données pour prévenir les problèmes de performance.
+
+Par exemple :
+
+```tsx filename="middleware.ts" switcher
+import { NextRequest, NextResponse } from 'next/server'
+import { decrypt } from '@/app/lib/session'
+import { cookies } from 'next/headers'
+
+// 1. Spécifiez les routes protégées et publiques
+const protectedRoutes = ['/dashboard']
+const publicRoutes = ['/login', '/signup', '/']
+
+export default async function middleware(req: NextRequest) {
+ // 2. Vérifiez si la route actuelle est protégée ou publique
+ const path = req.nextUrl.pathname
+ const isProtectedRoute = protectedRoutes.includes(path)
+ const isPublicRoute = publicRoutes.includes(path)
+
+ // 3. Déchiffrez la session depuis le cookie
+ const cookie = (await cookies()).get('session')?.value
+ const session = await decrypt(cookie)
+
+ // 4. Redirigez vers /login si l'utilisateur n'est pas authentifié
+ if (isProtectedRoute && !session?.userId) {
+ return NextResponse.redirect(new URL('/login', req.nextUrl))
+ }
+
+ // 5. Redirigez vers /dashboard si l'utilisateur est authentifié
+ if (
+ isPublicRoute &&
+ session?.userId &&
+ !req.nextUrl.pathname.startsWith('/dashboard')
+ ) {
+ return NextResponse.redirect(new URL('/dashboard', req.nextUrl))
+ }
+
+ return NextResponse.next()
+}
+
+// Routes sur lesquelles le Middleware ne doit pas s'exécuter
+export const config = {
+ matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
+}
+```
+
+```js filename="middleware.js" switcher
+import { NextResponse } from 'next/server'
+import { decrypt } from '@/app/lib/session'
+import { cookies } from 'next/headers'
+
+// 1. Spécifiez les routes protégées et publiques
+const protectedRoutes = ['/dashboard']
+const publicRoutes = ['/login', '/signup', '/']
+
+export default async function middleware(req) {
+ // 2. Vérifiez si la route actuelle est protégée ou publique
+ const path = req.nextUrl.pathname
+ const isProtectedRoute = protectedRoutes.includes(path)
+ const isPublicRoute = publicRoutes.includes(path)
+
+ // 3. Déchiffrez la session depuis le cookie
+ const cookie = (await cookies()).get('session')?.value
+ const session = await decrypt(cookie)
+
+ // 5. Redirigez vers /login si l'utilisateur n'est pas authentifié
+ if (isProtectedRoute && !session?.userId) {
+ return NextResponse.redirect(new URL('/login', req.nextUrl))
+ }
+
+ // 6. Redirigez vers /dashboard si l'utilisateur est authentifié
+ if (
+ isPublicRoute &&
+ session?.userId &&
+ !req.nextUrl.pathname.startsWith('/dashboard')
+ ) {
+ return NextResponse.redirect(new URL('/dashboard', req.nextUrl))
+ }
+
+ return NextResponse.next()
+}
+
+// Routes sur lesquelles le Middleware ne doit pas s'exécuter
+export const config = {
+ matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
+}
+```
+
+Bien que le Middleware puisse être utile pour des vérifications initiales, il ne devrait pas être votre seule ligne de défense pour protéger vos données. La majorité des vérifications de sécurité devraient être effectuées aussi près que possible de votre source de données, voir [Couche d'Accès aux Données (DAL)](#creating-a-data-access-layer-dal) pour plus d'informations.
+
+> **Conseils** :
+>
+> - Dans le Middleware, vous pouvez également lire les cookies en utilisant `req.cookies.get('session').value`.
+> - Le Middleware utilise le [Edge Runtime](/docs/app/api-reference/edge), vérifiez si votre bibliothèque d'authentification et votre bibliothèque de gestion de session sont compatibles.
+> - Vous pouvez utiliser la propriété `matcher` dans le Middleware pour spécifier sur quelles routes le Middleware doit s'exécuter. Cependant, pour l'authentification, il est recommandé que le Middleware s'exécute sur toutes les routes.
+
+
+
+### Création d'une Couche d'Accès aux Données (DAL)
+
+Nous recommandons de créer une DAL pour centraliser vos requêtes de données et votre logique d'autorisation.
+
+La DAL devrait inclure une fonction qui vérifie la session de l'utilisateur lors de ses interactions avec votre application. Au minimum, la fonction devrait vérifier si la session est valide, puis rediriger ou retourner les informations utilisateur nécessaires pour effectuer d'autres requêtes.
+
+Par exemple, créez un fichier séparé pour votre DAL qui inclut une fonction `verifySession()`. Utilisez ensuite l'API [cache](https://react.dev/reference/react/cache) de React pour mémoïser la valeur de retour de la fonction pendant un rendu React :
+
+```tsx filename="app/lib/dal.ts" switcher
+import 'server-only'
+
+import { cookies } from 'next/headers'
+import { decrypt } from '@/app/lib/session'
+
+export const verifySession = cache(async () => {
+ const cookie = (await cookies()).get('session')?.value
+ const session = await decrypt(cookie)
+
+ if (!session?.userId) {
+ redirect('/login')
+ }
+
+ return { isAuth: true, userId: session.userId }
+})
+```
+
+```js filename="app/lib/dal.js" switcher
+import 'server-only'
+
+import { cookies } from 'next/headers'
+import { decrypt } from '@/app/lib/session'
+
+export const verifySession = cache(async () => {
+ const cookie = (await cookies()).get('session')?.value
+ const session = await decrypt(cookie)
+
+ if (!session.userId) {
+ redirect('/login')
+ }
+
+ return { isAuth: true, userId: session.userId }
+})
+```
+
+Vous pouvez ensuite invoquer la fonction `verifySession()` dans vos requêtes de données, Actions Serveur, ou Gestionnaires de Route :
+
+```tsx filename="app/lib/dal.ts" switcher
+export const getUser = cache(async () => {
+ const session = await verifySession()
+ if (!session) return null
+
+ try {
+ const data = await db.query.users.findMany({
+ where: eq(users.id, session.userId),
+ // Retournez explicitement les colonnes dont vous avez besoin plutôt que l'objet utilisateur entier
+ columns: {
+ id: true,
+ name: true,
+ email: true,
+ },
+ })
+
+ const user = data[0]
+
+ return user
+ } catch (error) {
+ console.log('Échec de la récupération de l\'utilisateur')
+ return null
+ }
+})
+```
+
+```jsx filename="app/lib/dal.js" switcher
+export const getUser = cache(async () => {
+ const session = await verifySession()
+ if (!session) return null
+
+ try {
+ const data = await db.query.users.findMany({
+ where: eq(users.id, session.userId),
+ // Retournez explicitement les colonnes dont vous avez besoin plutôt que l'objet utilisateur entier
+ columns: {
+ id: true,
+ name: true,
+ email: true,
+ },
+ })
+
+ const user = data[0]
+
+ return user
+ } catch (error) {
+ console.log('Échec de la récupération de l\'utilisateur')
+ return null
+ }
+})
+```
+
+> **Conseil** :
+>
+> - Une DAL peut être utilisée pour protéger les données récupérées au moment de la requête. Cependant, pour les routes statiques qui partagent des données entre utilisateurs, les données seront récupérées au moment de la construction et non au moment de la requête. Utilisez le [Middleware](#optimistic-checks-with-middleware-optional) pour protéger les routes statiques.
+> - Pour des vérifications sécurisées, vous pouvez vérifier si la session est valide en comparant l'ID de session avec votre base de données. Utilisez la fonction [cache](https://react.dev/reference/react/cache) de React pour éviter des requêtes en double inutiles vers la base de données pendant un rendu.
+> - Vous pouvez souhaiter consolider les requêtes de données liées dans une classe JavaScript qui exécute `verifySession()` avant toute méthode.
+
+### Utilisation des objets de transfert de données (DTO)
+
+Lors de la récupération de données, il est recommandé de ne retourner que les données nécessaires qui seront utilisées dans votre application, et non des objets entiers. Par exemple, si vous récupérez des données utilisateur, vous pourriez ne retourner que l'ID et le nom de l'utilisateur, plutôt que l'objet utilisateur entier qui pourrait contenir des mots de passe, numéros de téléphone, etc.
+
+Cependant, si vous n'avez pas le contrôle sur la structure des données retournées, ou si vous travaillez en équipe et souhaitez éviter que des objets entiers soient transmis au client, vous pouvez utiliser des stratégies comme spécifier quels champs peuvent être exposés en toute sécurité au client.
+
+```tsx filename="app/lib/dto.ts" switcher
+import 'server-only'
+import { getUser } from '@/app/lib/dal'
+
+function canSeeUsername(viewer: User) {
+ return true
+}
+
+function canSeePhoneNumber(viewer: User, team: string) {
+ return viewer.isAdmin || team === viewer.team
+}
+
+export async function getProfileDTO(slug: string) {
+ const data = await db.query.users.findMany({
+ where: eq(users.slug, slug),
+ // Return specific columns here
+ })
+ const user = data[0]
+
+ const currentUser = await getUser(user.id)
+
+ // Or return only what's specific to the query here
+ return {
+ username: canSeeUsername(currentUser) ? user.username : null,
+ phonenumber: canSeePhoneNumber(currentUser, user.team)
+ ? user.phonenumber
+ : null,
+ }
+}
+```
+
+```js filename="app/lib/dto.js" switcher
+import 'server-only'
+import { getUser } from '@/app/lib/dal'
+
+function canSeeUsername(viewer) {
+ return true
+}
+
+function canSeePhoneNumber(viewer, team) {
+ return viewer.isAdmin || team === viewer.team
+}
+
+export async function getProfileDTO(slug) {
+ const data = await db.query.users.findMany({
+ where: eq(users.slug, slug),
+ // Return specific columns here
+ })
+ const user = data[0]
+
+ const currentUser = await getUser(user.id)
+
+ // Or return only what's specific to the query here
+ return {
+ username: canSeeUsername(currentUser) ? user.username : null,
+ phonenumber: canSeePhoneNumber(currentUser, user.team)
+ ? user.phonenumber
+ : null,
+ }
+}
+```
+
+En centralisant vos requêtes de données et votre logique d'autorisation dans une couche d'accès aux données (DAL) et en utilisant des DTO, vous pouvez garantir que toutes les requêtes de données sont sécurisées et cohérentes, ce qui facilite la maintenance, l'audit et le débogage à mesure que votre application évolue.
+
+> **Bon à savoir** :
+>
+> - Il existe plusieurs façons de définir un DTO, qu'il s'agisse d'utiliser `toJSON()`, des fonctions individuelles comme dans l'exemple ci-dessus, ou des classes JavaScript. Comme il s'agit de modèles JavaScript et non d'une fonctionnalité React ou Next.js, nous vous recommandons de faire des recherches pour trouver le modèle le plus adapté à votre application.
+> - En savoir plus sur les bonnes pratiques de sécurité dans notre [article sur la sécurité dans Next.js](/blog/security-nextjs-server-components-actions).
+
+### Composants serveur
+
+Les vérifications d'authentification dans les [composants serveur](/docs/app/getting-started/server-and-client-components) sont utiles pour un accès basé sur les rôles. Par exemple, pour afficher conditionnellement des composants en fonction du rôle de l'utilisateur :
+
+```tsx filename="app/dashboard/page.tsx" switcher
+import { verifySession } from '@/app/lib/dal'
+
+export default function Dashboard() {
+ const session = await verifySession()
+ const userRole = session?.user?.role // En supposant que 'role' fait partie de l'objet session
+
+ if (userRole === 'admin') {
+ return
+ } else if (userRole === 'user') {
+ return
+ } else {
+ redirect('/login')
+ }
+}
+```
+
+```jsx filename="app/dashboard/page.jsx" switcher
+import { verifySession } from '@/app/lib/dal'
+
+export default function Dashboard() {
+ const session = await verifySession()
+ const userRole = session.role // En supposant que 'role' fait partie de l'objet session
+
+ if (userRole === 'admin') {
+ return
+ } else if (userRole === 'user') {
+ return
+ } else {
+ redirect('/login')
+ }
+}
+```
+
+Dans cet exemple, nous utilisons la fonction `verifySession()` de notre DAL pour vérifier les rôles 'admin', 'user' et non autorisés. Ce modèle garantit que chaque utilisateur interagit uniquement avec les composants appropriés à son rôle.
+
+### Layouts et vérifications d'authentification
+
+En raison du [rendu partiel (Partial Rendering)](/docs/app/building-your-application/routing/linking-and-navigating#4-partial-rendering), soyez prudent lorsque vous effectuez des vérifications dans les [layouts](/docs/app/api-reference/file-conventions/layout) car ceux-ci ne sont pas re-rendus lors de la navigation, ce qui signifie que la session utilisateur ne sera pas vérifiée à chaque changement de route.
+
+À la place, vous devriez effectuer les vérifications près de votre source de données ou du composant qui sera rendu conditionnellement.
+
+Par exemple, considérez un layout partagé qui récupère les données utilisateur et affiche l'image de l'utilisateur dans une navigation. Au lieu d'effectuer la vérification d'authentification dans le layout, vous devriez récupérer les données utilisateur (`getUser()`) dans le layout et effectuer la vérification d'authentification dans votre DAL.
+
+Cela garantit que partout où `getUser()` est appelé dans votre application, la vérification d'authentification est effectuée, et empêche les développeurs d'oublier de vérifier que l'utilisateur est autorisé à accéder aux données.
+
+```tsx filename="app/layout.tsx" switcher
+export default async function Layout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ const user = await getUser();
+
+ return (
+ // ...
+ )
+}
+```
+
+```jsx filename="app/layout.js" switcher
+export default async function Layout({ children }) {
+ const user = await getUser();
+
+ return (
+ // ...
+ )
+}
+```
+
+```ts filename="app/lib/dal.ts" switcher
+export const getUser = cache(async () => {
+ const session = await verifySession()
+ if (!session) return null
+
+ // Obtenir l'ID utilisateur depuis la session et récupérer les données
+})
+```
+
+```js filename="app/lib/dal.js" switcher
+export const getUser = cache(async () => {
+ const session = await verifySession()
+ if (!session) return null
+
+ // Obtenir l'ID utilisateur depuis la session et récupérer les données
+})
+```
+
+> **Bon à savoir :**
+>
+> - Un modèle courant dans les SPA est de `return null` dans un layout ou un composant de haut niveau si un utilisateur n'est pas autorisé. Ce modèle **n'est pas recommandé** car les applications Next.js ont plusieurs points d'entrée, ce qui n'empêchera pas les segments de route imbriqués et les actions serveur d'être accessibles.
+
+### Actions serveur
+
+Traitez les [actions serveur](/docs/app/building-your-application/data-fetching/server-actions-and-mutations) avec les mêmes considérations de sécurité que les points de terminaison d'API publics, et vérifiez si l'utilisateur est autorisé à effectuer une mutation.
+
+Dans l'exemple ci-dessous, nous vérifions le rôle de l'utilisateur avant de permettre à l'action de se poursuivre :
+
+```ts filename="app/lib/actions.ts" switcher
+'use server'
+import { verifySession } from '@/app/lib/dal'
+
+export async function serverAction(formData: FormData) {
+ const session = await verifySession()
+ const userRole = session?.user?.role
+
+ // Retourner prématurément si l'utilisateur n'est pas autorisé à effectuer l'action
+ if (userRole !== 'admin') {
+ return null
+ }
+
+ // Poursuivre l'action pour les utilisateurs autorisés
+}
+```
+
+```js filename="app/lib/actions.js" switcher
+'use server'
+import { verifySession } from '@/app/lib/dal'
+
+export async function serverAction() {
+ const session = await verifySession()
+ const userRole = session.user.role
+
+ // Retourner prématurément si l'utilisateur n'est pas autorisé à effectuer l'action
+ if (userRole !== 'admin') {
+ return null
+ }
+
+ // Poursuivre l'action pour les utilisateurs autorisés
+}
+```
+
+### Gestionnaires de route
+
+Traitez les [gestionnaires de route (Route Handlers)](/docs/app/building-your-application/routing/route-handlers) avec les mêmes considérations de sécurité que les points de terminaison d'API publics, et vérifiez si l'utilisateur est autorisé à accéder au gestionnaire de route.
+
+Par exemple :
+
+```ts filename="app/api/route.ts" switcher
+import { verifySession } from '@/app/lib/dal'
+
+export async function GET() {
+ // Authentification utilisateur et vérification du rôle
+ const session = await verifySession()
+
+ // Vérifier si l'utilisateur est authentifié
+ if (!session) {
+ // L'utilisateur n'est pas authentifié
+ return new Response(null, { status: 401 })
+ }
+
+ // Vérifier si l'utilisateur a le rôle 'admin'
+ if (session.user.role !== 'admin') {
+ // L'utilisateur est authentifié mais n'a pas les bonnes permissions
+ return new Response(null, { status: 403 })
+ }
+
+ // Continuer pour les utilisateurs autorisés
+}
+```
+
+```js filename="app/api/route.js" switcher
+import { verifySession } from '@/app/lib/dal'
+
+export async function GET() {
+ // Authentification utilisateur et vérification du rôle
+ const session = await verifySession()
+
+ // Vérifier si l'utilisateur est authentifié
+ if (!session) {
+ // L'utilisateur n'est pas authentifié
+ return new Response(null, { status: 401 })
+ }
+
+ // Vérifier si l'utilisateur a le rôle 'admin'
+ if (session.user.role !== 'admin') {
+ // L'utilisateur est authentifié mais n'a pas les bonnes permissions
+ return new Response(null, { status: 403 })
+ }
+
+ // Continuer pour les utilisateurs autorisés
+}
+```
+
+L'exemple ci-dessus démontre un gestionnaire de route avec une vérification de sécurité à deux niveaux. Il vérifie d'abord une session active, puis vérifie si l'utilisateur connecté est un 'admin'.
+
+## Fournisseurs de contexte
+
+L'utilisation de fournisseurs de contexte pour l'authentification fonctionne grâce à l'[imbrication (interleaving)](/docs/app/getting-started/server-and-client-components#examples#interleaving-server-and-client-components). Cependant, le `context` de React n'est pas pris en charge dans les composants serveur, ce qui les rend uniquement applicables aux composants client.
+
+Cela fonctionne, mais tout composant serveur enfant sera d'abord rendu côté serveur et n'aura pas accès aux données de session du fournisseur de contexte :
+
+```tsx filename="app/layout.ts" switcher
+import { ContextProvider } from 'auth-lib'
+
+export default function RootLayout({ children }) {
+ return (
+
+
+ {children}
+
+
+ )
+}
+```
+
+```tsx filename="app/ui/profile.ts switcher
+'use client';
+
+import { useSession } from "auth-lib";
+
+export default function Profile() {
+ const { userId } = useSession();
+ const { data } = useSWR(`/api/user/${userId}`, fetcher)
+
+ return (
+ // ...
+ );
+}
+```
+
+```jsx filename="app/ui/profile.js switcher
+'use client';
+
+import { useSession } from "auth-lib";
+
+export default function Profile() {
+ const { userId } = useSession();
+ const { data } = useSWR(`/api/user/${userId}`, fetcher)
+
+ return (
+ // ...
+ );
+}
+```
+
+Si les données de session sont nécessaires dans les composants client (par exemple pour la récupération de données côté client), utilisez l'API [`taintUniqueValue`](https://react.dev/reference/react/experimental_taintUniqueValue) de React pour empêcher que des données de session sensibles soient exposées au client.
+
+
+
+
+
+### Création d'une couche d'accès aux données (DAL)
+
+#### Protection des routes API
+
+Les routes API dans Next.js sont essentielles pour gérer la logique côté serveur et la gestion des données. Il est crucial de sécuriser ces routes pour garantir que seuls les utilisateurs autorisés peuvent accéder à des fonctionnalités spécifiques. Cela implique généralement de vérifier le statut d'authentification de l'utilisateur et ses permissions basées sur les rôles.
+
+Voici un exemple de sécurisation d'une route API :
+
+```ts filename="pages/api/route.ts" switcher
+import { NextApiRequest, NextApiResponse } from 'next'
+
+export default async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse
+) {
+ const session = await getSession(req)
+
+ // Vérifier si l'utilisateur est authentifié
+ if (!session) {
+ res.status(401).json({
+ error: 'L\'utilisateur n\'est pas authentifié',
+ })
+ return
+ }
+
+ // Vérifier si l'utilisateur a le rôle 'admin'
+ if (session.user.role !== 'admin') {
+ res.status(401).json({
+ error: 'Accès non autorisé : L\'utilisateur n\'a pas les privilèges admin.',
+ })
+ return
+ }
+
+ // Poursuivre la route pour les utilisateurs autorisés
+ // ... implémentation de la route API
+}
+```
+
+```js filename="pages/api/route.js" switcher
+export default async function handler(req, res) {
+ const session = await getSession(req)
+
+ // Vérifier si l'utilisateur est authentifié
+ if (!session) {
+ res.status(401).json({
+ error: 'L\'utilisateur n\'est pas authentifié',
+ })
+ return
+ }
+
+ // Vérifier si l'utilisateur a le rôle 'admin'
+ if (session.user.role !== 'admin') {
+ res.status(401).json({
+ error: 'Accès non autorisé : L\'utilisateur n\'a pas les privilèges admin.',
+ })
+ return
+ }
+
+ // Poursuivre la route pour les utilisateurs autorisés
+ // ... implémentation de la route API
+}
+```
+
+Cet exemple démontre une route API avec une vérification de sécurité à deux niveaux pour l'authentification et l'autorisation. Il vérifie d'abord une session active, puis vérifie si l'utilisateur connecté est un 'admin'. Cette approche garantit un accès sécurisé, limité aux utilisateurs authentifiés et autorisés, maintenant une sécurité robuste pour le traitement des requêtes.
+
+
+
+## Ressources
+
+Maintenant que vous avez appris l'authentification dans Next.js, voici des bibliothèques compatibles avec Next.js et des ressources pour vous aider à implémenter une authentification et une gestion de session sécurisées :
+
+### Bibliothèques d'authentification
+
+- [Auth0](https://auth0.com/docs/quickstart/webapp/nextjs/01-login)
+- [Better Auth](https://www.better-auth.com/docs/integrations/next)
+- [Clerk](https://clerk.com/docs/quickstarts/nextjs)
+- [Kinde](https://kinde.com/docs/developer-tools/nextjs-sdk)
+- [Logto](https://docs.logto.io/quick-starts/next-app-router)
+- [NextAuth.js](https://authjs.dev/getting-started/installation?framework=next.js)
+- [Ory](https://www.ory.sh/docs/getting-started/integrate-auth/nextjs)
+- [Stack Auth](https://docs.stack-auth.com/getting-started/setup)
+- [Supabase](https://supabase.com/docs/guides/getting-started/quickstarts/nextjs)
+- [Stytch](https://stytch.com/docs/guides/quickstarts/nextjs)
+- [WorkOS](https://workos.com/docs/user-management/nextjs)
+
+### Bibliothèques de gestion de session
+
+- [Iron Session](https://github.com/vvo/iron-session)
+- [Jose](https://github.com/panva/jose)
+
+## Pour aller plus loin
+
+Pour continuer à apprendre sur l'authentification et la sécurité, consultez les ressources suivantes :
+
+- [Comment penser la sécurité dans Next.js](/blog/security-nextjs-server-components-actions)
+- [Comprendre les attaques XSS](https://vercel.com/guides/understanding-xss-attacks)
+- [Comprendre les attaques CSRF](https://vercel.com/guides/understanding-csrf-attacks)
+- [The Copenhagen Book](https://thecopenhagenbook.com/)
diff --git a/apps/docs/content/fr/docs/01-app/02-guides/ci-build-caching.mdx b/apps/docs/content/fr/docs/01-app/02-guides/ci-build-caching.mdx
new file mode 100644
index 00000000..6173196a
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/02-guides/ci-build-caching.mdx
@@ -0,0 +1,171 @@
+---
+source-updated-at: 2025-05-16T04:52:11.000Z
+translation-updated-at: 2025-06-02T20:00:42.623Z
+title: Comment configurer la mise en cache des builds pour l'intégration continue (CI)
+nav_title: Mise en cache des builds CI
+description: Apprenez à configurer l'intégration continue pour mettre en cache les builds Next.js
+---
+
+Pour améliorer les performances de build, Next.js enregistre un cache dans `.next/cache` qui est partagé entre les builds.
+
+Pour tirer parti de ce cache dans les environnements d'intégration continue (CI), votre workflow CI devra être configuré pour persister correctement le cache entre les builds.
+
+> Si votre CI n'est pas configuré pour persister `.next/cache` entre les builds, vous pourriez voir une erreur [No Cache Detected](/docs/messages/no-cache) (Aucun cache détecté).
+
+Voici quelques exemples de configurations de cache pour les principaux fournisseurs de CI :
+
+## Vercel
+
+La mise en cache de Next.js est automatiquement configurée pour vous. Aucune action n'est requise de votre part. Si vous utilisez Turborepo sur Vercel, [apprenez-en plus ici](https://vercel.com/docs/monorepos/turborepo).
+
+## CircleCI
+
+Modifiez votre étape `save_cache` dans `.circleci/config.yml` pour inclure `.next/cache` :
+
+```yaml
+steps:
+ - save_cache:
+ key: dependency-cache-{{ checksum "yarn.lock" }}
+ paths:
+ - ./node_modules
+ - ./.next/cache
+```
+
+Si vous n'avez pas de clé `save_cache`, veuillez suivre la [documentation de CircleCI sur la configuration de la mise en cache des builds](https://circleci.com/docs/2.0/caching/).
+
+## Travis CI
+
+Ajoutez ou fusionnez ce qui suit dans votre `.travis.yml` :
+
+```yaml
+cache:
+ directories:
+ - $HOME/.cache/yarn
+ - node_modules
+ - .next/cache
+```
+
+## GitLab CI
+
+Ajoutez ou fusionnez ce qui suit dans votre `.gitlab-ci.yml` :
+
+```yaml
+cache:
+ key: ${CI_COMMIT_REF_SLUG}
+ paths:
+ - node_modules/
+ - .next/cache/
+```
+
+## Netlify CI
+
+Utilisez les [Netlify Plugins](https://www.netlify.com/products/build/plugins/) avec [`@netlify/plugin-nextjs`](https://www.npmjs.com/package/@netlify/plugin-nextjs).
+
+## AWS CodeBuild
+
+Ajoutez (ou fusionnez) ce qui suit dans votre `buildspec.yml` :
+
+```yaml
+cache:
+ paths:
+ - 'node_modules/**/*' # Cache `node_modules` pour accélérer `yarn` ou `npm i`
+ - '.next/cache/**/*' # Cache Next.js pour accélérer les reconstructions d'application
+```
+
+## GitHub Actions
+
+En utilisant [actions/cache](https://github.com/actions/cache) de GitHub, ajoutez l'étape suivante dans votre fichier de workflow :
+
+```yaml
+uses: actions/cache@v4
+with:
+ # Voir ici pour la mise en cache avec `yarn`, `bun` ou d'autres gestionnaires de paquets https://github.com/actions/cache/blob/main/examples.md ou vous pouvez utiliser la mise en cache avec actions/setup-node https://github.com/actions/setup-node
+ path: |
+ ~/.npm
+ ${{ github.workspace }}/.next/cache
+ # Génère un nouveau cache lorsque les paquets ou les fichiers sources changent.
+ key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
+ # Si les fichiers sources ont changé mais pas les paquets, reconstruisez à partir d'un cache précédent.
+ restore-keys: |
+ ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
+```
+
+## Bitbucket Pipelines
+
+Ajoutez ou fusionnez ce qui suit dans votre `bitbucket-pipelines.yml` au niveau supérieur (même niveau que `pipelines`) :
+
+```yaml
+definitions:
+ caches:
+ nextcache: .next/cache
+```
+
+Puis référencez-le dans la section `caches` de l'étape de votre pipeline :
+
+```yaml
+- step:
+ name: your_step_name
+ caches:
+ - node
+ - nextcache
+```
+
+## Heroku
+
+En utilisant le [cache personnalisé](https://devcenter.heroku.com/articles/nodejs-support#custom-caching) de Heroku, ajoutez un tableau `cacheDirectories` dans votre package.json de niveau supérieur :
+
+```javascript
+"cacheDirectories": [".next/cache"]
+```
+
+## Azure Pipelines
+
+En utilisant la [tâche Cache](https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/cache) d'Azure Pipelines, ajoutez la tâche suivante à votre fichier yaml de pipeline quelque part avant la tâche qui exécute `next build` :
+
+```yaml
+- task: Cache@2
+ displayName: 'Cache .next/cache'
+ inputs:
+ key: next | $(Agent.OS) | yarn.lock
+ path: '$(System.DefaultWorkingDirectory)/.next/cache'
+```
+
+## Jenkins (Pipeline)
+
+En utilisant le plugin [Job Cacher](https://www.jenkins.io/doc/pipeline/steps/jobcacher/) de Jenkins, ajoutez l'étape de build suivante à votre `Jenkinsfile` là où vous exécuteriez normalement `next build` ou `npm install` :
+
+```yaml
+stage("Restore npm packages") {
+ steps {
+ // Écrit un fichier de verrouillage dans le cache basé sur le hash GIT_COMMIT
+ writeFile file: "next-lock.cache", text: "$GIT_COMMIT"
+
+ cache(caches: [
+ arbitraryFileCache(
+ path: "node_modules",
+ includes: "**/*",
+ cacheValidityDecidingFile: "package-lock.json"
+ )
+ ]) {
+ sh "npm install"
+ }
+ }
+}
+stage("Build") {
+ steps {
+ // Écrit un fichier de verrouillage dans le cache basé sur le hash GIT_COMMIT
+ writeFile file: "next-lock.cache", text: "$GIT_COMMIT"
+
+ cache(caches: [
+ arbitraryFileCache(
+ path: ".next/cache",
+ includes: "**/*",
+ cacheValidityDecidingFile: "next-lock.cache"
+ )
+ ]) {
+ // aka `next build`
+ sh "npm run build"
+ }
+ }
+}
+```
\ No newline at end of file
diff --git a/apps/docs/content/fr/docs/01-app/02-guides/content-security-policy.mdx b/apps/docs/content/fr/docs/01-app/02-guides/content-security-policy.mdx
new file mode 100644
index 00000000..99b69d9b
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/02-guides/content-security-policy.mdx
@@ -0,0 +1,299 @@
+---
+source-updated-at: 2025-05-19T22:31:51.000Z
+translation-updated-at: 2025-06-02T20:01:08.973Z
+title: Comment configurer une Politique de Sécurité du Contenu (CSP) pour votre application Next.js
+nav_title: Politique de Sécurité du Contenu
+description: Apprenez à configurer une Politique de Sécurité du Contenu (CSP) pour votre application Next.js.
+related:
+ links:
+ - app/building-your-application/routing/middleware
+ - app/api-reference/functions/headers
+---
+
+{/* Le contenu de ce document est partagé entre le routeur app et pages. Vous pouvez utiliser le composant `Content ` pour ajouter du contenu spécifique au routeur Pages. Tout contenu partagé ne doit pas être encapsulé dans un composant. */}
+
+La [Politique de Sécurité du Contenu (CSP)](https://developer.mozilla.org/docs/Web/HTTP/CSP) est importante pour protéger votre application Next.js contre diverses menaces de sécurité telles que le cross-site scripting (XSS), le clickjacking et d'autres attaques par injection de code.
+
+En utilisant CSP, les développeurs peuvent spécifier quelles origines sont autorisées pour les sources de contenu, scripts, feuilles de style, images, polices, objets, médias (audio, vidéo), iframes et plus encore.
+
+
+ Exemples
+
+- [CSP strict](https://github.com/vercel/next.js/tree/canary/examples/with-strict-csp)
+
+
+
+## Nonces
+
+Un [nonce](https://developer.mozilla.org/docs/Web/HTML/Global_attributes/nonce) est une chaîne de caractères unique et aléatoire créée pour une utilisation unique. Il est utilisé conjointement avec CSP pour autoriser sélectivement certains scripts ou styles inline à s'exécuter, contournant ainsi les directives strictes de CSP.
+
+### Pourquoi utiliser un nonce ?
+
+Même si les CSP sont conçus pour bloquer les scripts malveillants, il existe des scénarios légitimes où des scripts inline sont nécessaires. Dans ces cas, les nonces offrent un moyen d'autoriser ces scripts à s'exécuter s'ils possèdent le nonce correct.
+
+### Ajout d'un nonce avec Middleware
+
+Le [Middleware](/docs/app/building-your-application/routing/middleware) vous permet d'ajouter des en-têtes et de générer des nonces avant le rendu de la page.
+
+À chaque affichage d'une page, un nouveau nonce doit être généré. Cela signifie que vous **devez utiliser le rendu dynamique pour ajouter des nonces**.
+
+Par exemple :
+
+```ts filename="middleware.ts" switcher
+import { NextRequest, NextResponse } from 'next/server'
+
+export function middleware(request: NextRequest) {
+ const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
+ const cspHeader = `
+ default-src 'self';
+ script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
+ style-src 'self' 'nonce-${nonce}';
+ img-src 'self' blob: data:;
+ font-src 'self';
+ object-src 'none';
+ base-uri 'self';
+ form-action 'self';
+ frame-ancestors 'none';
+ upgrade-insecure-requests;
+`
+ // Remplacer les sauts de ligne et les espaces
+ const contentSecurityPolicyHeaderValue = cspHeader
+ .replace(/\s{2,}/g, ' ')
+ .trim()
+
+ const requestHeaders = new Headers(request.headers)
+ requestHeaders.set('x-nonce', nonce)
+
+ requestHeaders.set(
+ 'Content-Security-Policy',
+ contentSecurityPolicyHeaderValue
+ )
+
+ const response = NextResponse.next({
+ request: {
+ headers: requestHeaders,
+ },
+ })
+ response.headers.set(
+ 'Content-Security-Policy',
+ contentSecurityPolicyHeaderValue
+ )
+
+ return response
+}
+```
+
+```js filename="middleware.js" switcher
+import { NextResponse } from 'next/server'
+
+export function middleware(request) {
+ const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
+ const cspHeader = `
+ default-src 'self';
+ script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
+ style-src 'self' 'nonce-${nonce}';
+ img-src 'self' blob: data:;
+ font-src 'self';
+ object-src 'none';
+ base-uri 'self';
+ form-action 'self';
+ frame-ancestors 'none';
+ upgrade-insecure-requests;
+`
+ // Remplacer les sauts de ligne et les espaces
+ const contentSecurityPolicyHeaderValue = cspHeader
+ .replace(/\s{2,}/g, ' ')
+ .trim()
+
+ const requestHeaders = new Headers(request.headers)
+ requestHeaders.set('x-nonce', nonce)
+ requestHeaders.set(
+ 'Content-Security-Policy',
+ contentSecurityPolicyHeaderValue
+ )
+
+ const response = NextResponse.next({
+ request: {
+ headers: requestHeaders,
+ },
+ })
+ response.headers.set(
+ 'Content-Security-Policy',
+ contentSecurityPolicyHeaderValue
+ )
+
+ return response
+}
+```
+
+Par défaut, le Middleware s'exécute sur toutes les requêtes. Vous pouvez filtrer le Middleware pour qu'il s'exécute sur des chemins spécifiques en utilisant un [`matcher`](/docs/app/building-your-application/routing/middleware#matcher).
+
+Nous recommandons d'ignorer les préchargements (depuis `next/link`) et les ressources statiques qui n'ont pas besoin de l'en-tête CSP.
+
+```ts filename="middleware.ts" switcher
+export const config = {
+ matcher: [
+ /*
+ * Correspond à tous les chemins de requête sauf ceux commençant par :
+ * - api (routes API)
+ * - _next/static (fichiers statiques)
+ * - _next/image (fichiers d'optimisation d'image)
+ * - favicon.ico (fichier favicon)
+ */
+ {
+ source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
+ missing: [
+ { type: 'header', key: 'next-router-prefetch' },
+ { type: 'header', key: 'purpose', value: 'prefetch' },
+ ],
+ },
+ ],
+}
+```
+
+```js filename="middleware.js" switcher
+export const config = {
+ matcher: [
+ /*
+ * Correspond à tous les chemins de requête sauf ceux commençant par :
+ * - api (routes API)
+ * - _next/static (fichiers statiques)
+ * - _next/image (fichiers d'optimisation d'image)
+ * - favicon.ico (fichier favicon)
+ */
+ {
+ source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
+ missing: [
+ { type: 'header', key: 'next-router-prefetch' },
+ { type: 'header', key: 'purpose', value: 'prefetch' },
+ ],
+ },
+ ],
+}
+```
+
+### Lecture du nonce
+
+
+ Vous pouvez fournir le nonce à votre page en utilisant
+ [`getServerSideProps`](/docs/pages/building-your-application/data-fetching/get-server-side-props) :
+
+```tsx filename="pages/index.tsx" switcher
+import Script from 'next/script'
+
+import type { GetServerSideProps } from 'next'
+
+export default function Page({ nonce }) {
+ return (
+
+ )
+}
+
+export const getServerSideProps: GetServerSideProps = async ({ req }) => {
+ const nonce = req.headers['x-nonce']
+ return { props: { nonce } }
+}
+```
+
+```jsx filename="pages/index.jsx" switcher
+import Script from 'next/script'
+export default function Page({ nonce }) {
+ return (
+
+ )
+}
+
+export async function getServerSideProps({ req }) {
+ const nonce = req.headers['x-nonce']
+ return { props: { nonce } }
+}
+```
+
+
+
+
+
+Vous pouvez lire le nonce depuis un [Composant Serveur](/docs/app/getting-started/server-and-client-components) en utilisant [`headers`](/docs/app/api-reference/functions/headers) :
+
+```tsx filename="app/page.tsx" switcher
+import { headers } from 'next/headers'
+import Script from 'next/script'
+
+export default async function Page() {
+ const nonce = (await headers()).get('x-nonce')
+
+ return (
+
+ )
+}
+```
+
+```jsx filename="app/page.jsx" switcher
+import { headers } from 'next/headers'
+import Script from 'next/script'
+
+export default async function Page() {
+ const nonce = (await headers()).get('x-nonce')
+
+ return (
+
+ )
+}
+```
+
+
+
+## Sans Nonces
+
+Pour les applications qui ne nécessitent pas de nonces, vous pouvez définir l'en-tête CSP directement dans votre fichier [`next.config.js`](/docs/app/api-reference/config/next-config-js) :
+
+```js filename="next.config.js"
+const cspHeader = `
+ default-src 'self';
+ script-src 'self' 'unsafe-eval' 'unsafe-inline';
+ style-src 'self' 'unsafe-inline';
+ img-src 'self' blob: data:;
+ font-src 'self';
+ object-src 'none';
+ base-uri 'self';
+ form-action 'self';
+ frame-ancestors 'none';
+ upgrade-insecure-requests;
+`
+
+module.exports = {
+ async headers() {
+ return [
+ {
+ source: '/(.*)',
+ headers: [
+ {
+ key: 'Content-Security-Policy',
+ value: cspHeader.replace(/\n/g, ''),
+ },
+ ],
+ },
+ ]
+ },
+}
+```
+
+## Historique des versions
+
+Nous recommandons d'utiliser Next.js `v13.4.20+` pour gérer et appliquer correctement les nonces.
\ No newline at end of file
diff --git a/apps/docs/content/fr/docs/01-app/02-guides/css-in-js.mdx b/apps/docs/content/fr/docs/01-app/02-guides/css-in-js.mdx
new file mode 100644
index 00000000..30622e25
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/02-guides/css-in-js.mdx
@@ -0,0 +1,324 @@
+---
+source-updated-at: 2025-06-01T01:32:20.000Z
+translation-updated-at: 2025-06-02T20:01:37.766Z
+title: Comment utiliser les bibliothèques CSS-in-JS
+nav_title: CSS-in-JS
+description: Utilisation des bibliothèques CSS-in-JS avec Next.js
+---
+
+{/* Le contenu de ce document est partagé entre le routeur app et pages. Vous pouvez utiliser le composant `Content ` pour ajouter du contenu spécifique au routeur Pages. Tout contenu partagé ne doit pas être encapsulé dans un composant. */}
+
+
+
+> **Avertissement :** L'utilisation de CSS-in-JS avec les nouvelles fonctionnalités de React comme les Server Components et le Streaming nécessite que les auteurs de bibliothèques prennent en charge la dernière version de React, y compris le [rendu concurrent](https://react.dev/blog/2022/03/29/react-v18#what-is-concurrent-react).
+
+Les bibliothèques suivantes sont prises en charge dans les Client Components du répertoire `app` (par ordre alphabétique) :
+
+- [`ant-design`](https://ant.design/docs/react/use-with-next#using-app-router)
+- [`chakra-ui`](https://chakra-ui.com/getting-started/nextjs-app-guide)
+- [`@fluentui/react-components`](https://react.fluentui.dev/?path=/docs/concepts-developer-server-side-rendering-next-js-appdir-setup--page)
+- [`kuma-ui`](https://kuma-ui.com)
+- [`@mui/material`](https://mui.com/material-ui/guides/next-js-app-router/)
+- [`@mui/joy`](https://mui.com/joy-ui/integrations/next-js-app-router/)
+- [`pandacss`](https://panda-css.com)
+- [`styled-jsx`](#styled-jsx)
+- [`styled-components`](#styled-components)
+- [`stylex`](https://stylexjs.com)
+- [`tamagui`](https://tamagui.dev/docs/guides/next-js#server-components)
+- [`tss-react`](https://tss-react.dev/)
+- [`vanilla-extract`](https://vanilla-extract.style)
+
+Les bibliothèques suivantes travaillent actuellement à leur prise en charge :
+
+- [`emotion`](https://github.com/emotion-js/emotion/issues/2928)
+
+> **Bon à savoir** : Nous testons différentes bibliothèques CSS-in-JS et nous ajouterons plus d'exemples pour les bibliothèques qui prennent en charge les fonctionnalités de React 18 et/ou le répertoire `app`.
+
+## Configuration de CSS-in-JS dans `app`
+
+La configuration de CSS-in-JS est un processus en trois étapes qui implique :
+
+1. Un **registre de styles** pour collecter toutes les règles CSS lors d'un rendu.
+2. Le nouveau hook `useServerInsertedHTML` pour injecter les règles avant tout contenu qui pourrait les utiliser.
+3. Un Client Component qui encapsule votre application avec le registre de styles lors du rendu côté serveur initial.
+
+### `styled-jsx`
+
+L'utilisation de `styled-jsx` dans les Client Components nécessite la version `v5.1.0`. Tout d'abord, créez un nouveau registre :
+
+```tsx filename="app/registry.tsx" switcher
+'use client'
+
+import React, { useState } from 'react'
+import { useServerInsertedHTML } from 'next/navigation'
+import { StyleRegistry, createStyleRegistry } from 'styled-jsx'
+
+export default function StyledJsxRegistry({
+ children,
+}: {
+ children: React.ReactNode
+}) {
+ // Ne créez la feuille de style qu'une seule fois avec un état initial paresseux
+ // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
+ const [jsxStyleRegistry] = useState(() => createStyleRegistry())
+
+ useServerInsertedHTML(() => {
+ const styles = jsxStyleRegistry.styles()
+ jsxStyleRegistry.flush()
+ return <>{styles}>
+ })
+
+ return {children}
+}
+```
+
+```jsx filename="app/registry.js" switcher
+'use client'
+
+import React, { useState } from 'react'
+import { useServerInsertedHTML } from 'next/navigation'
+import { StyleRegistry, createStyleRegistry } from 'styled-jsx'
+
+export default function StyledJsxRegistry({ children }) {
+ // Ne créez la feuille de style qu'une seule fois avec un état initial paresseux
+ // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
+ const [jsxStyleRegistry] = useState(() => createStyleRegistry())
+
+ useServerInsertedHTML(() => {
+ const styles = jsxStyleRegistry.styles()
+ jsxStyleRegistry.flush()
+ return <>{styles}>
+ })
+
+ return {children}
+}
+```
+
+Ensuite, encapsulez votre [layout racine](/docs/app/api-reference/file-conventions/layout#root-layout) avec le registre :
+
+```tsx filename="app/layout.tsx" switcher
+import StyledJsxRegistry from './registry'
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode
+}) {
+ return (
+
+
+ {children}
+
+
+ )
+}
+```
+
+```jsx filename="app/layout.js" switcher
+import StyledJsxRegistry from './registry'
+
+export default function RootLayout({ children }) {
+ return (
+
+
+ {children}
+
+
+ )
+}
+```
+
+[Voir un exemple ici](https://github.com/vercel/app-playground/tree/main/app/styling/styled-jsx).
+
+### Styled Components
+
+Voici un exemple de configuration pour `styled-components@6` ou version ultérieure :
+
+Tout d'abord, activez styled-components dans `next.config.js`.
+
+```js filename="next.config.js"
+module.exports = {
+ compiler: {
+ styledComponents: true,
+ },
+}
+```
+
+Ensuite, utilisez l'API `styled-components` pour créer un composant de registre global afin de collecter toutes les règles de style CSS générées lors d'un rendu, et une fonction pour retourner ces règles. Utilisez ensuite le hook `useServerInsertedHTML` pour injecter les styles collectés dans le registre dans la balise HTML `` du layout racine.
+
+```tsx filename="lib/registry.tsx" switcher
+'use client'
+
+import React, { useState } from 'react'
+import { useServerInsertedHTML } from 'next/navigation'
+import { ServerStyleSheet, StyleSheetManager } from 'styled-components'
+
+export default function StyledComponentsRegistry({
+ children,
+}: {
+ children: React.ReactNode
+}) {
+ // Ne créez la feuille de style qu'une seule fois avec un état initial paresseux
+ // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
+ const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet())
+
+ useServerInsertedHTML(() => {
+ const styles = styledComponentsStyleSheet.getStyleElement()
+ styledComponentsStyleSheet.instance.clearTag()
+ return <>{styles}>
+ })
+
+ if (typeof window !== 'undefined') return <>{children}>
+
+ return (
+
+ {children}
+
+ )
+}
+```
+
+```jsx filename="lib/registry.js" switcher
+'use client'
+
+import React, { useState } from 'react'
+import { useServerInsertedHTML } from 'next/navigation'
+import { ServerStyleSheet, StyleSheetManager } from 'styled-components'
+
+export default function StyledComponentsRegistry({ children }) {
+ // Ne créez la feuille de style qu'une seule fois avec un état initial paresseux
+ // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
+ const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet())
+
+ useServerInsertedHTML(() => {
+ const styles = styledComponentsStyleSheet.getStyleElement()
+ styledComponentsStyleSheet.instance.clearTag()
+ return <>{styles}>
+ })
+
+ if (typeof window !== 'undefined') return <>{children}>
+
+ return (
+
+ {children}
+
+ )
+}
+```
+
+Encapsulez les `children` du layout racine avec le composant de registre de styles :
+
+```tsx filename="app/layout.tsx" switcher
+import StyledComponentsRegistry from './lib/registry'
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode
+}) {
+ return (
+
+
+ {children}
+
+
+ )
+}
+```
+
+```jsx filename="app/layout.js" switcher
+import StyledComponentsRegistry from './lib/registry'
+
+export default function RootLayout({ children }) {
+ return (
+
+
+ {children}
+
+
+ )
+}
+```
+
+[Voir un exemple ici](https://github.com/vercel/app-playground/tree/main/app/styling/styled-components).
+
+> **Bon à savoir** :
+>
+> - Pendant le rendu côté serveur, les styles seront extraits vers un registre global et injectés dans le `` de votre HTML. Cela garantit que les règles de style sont placées avant tout contenu qui pourrait les utiliser. À l'avenir, nous pourrions utiliser une future fonctionnalité de React pour déterminer où injecter les styles.
+> - Pendant le streaming, les styles de chaque morceau seront collectés et ajoutés aux styles existants. Après l'hydratation côté client, `styled-components` prendra le relais comme d'habitude et injectera tout style dynamique supplémentaire.
+> - Nous utilisons spécifiquement un Client Component au niveau supérieur de l'arborescence pour le registre de styles car c'est plus efficace pour extraire les règles CSS de cette manière. Cela évite de régénérer les styles lors des rendus serveur suivants et empêche leur envoi dans le payload des Server Components.
+> - Pour les cas d'utilisation avancés où vous devez configurer des propriétés individuelles de la compilation styled-components, vous pouvez consulter notre [référence API Next.js pour styled-components](/docs/architecture/nextjs-compiler#styled-components) pour en savoir plus.
+
+
+
+
+
+
+ Exemples
+
+- [Styled JSX](https://github.com/vercel/next.js/tree/canary/examples/with-styled-jsx)
+- [Styled Components](https://github.com/vercel/next.js/tree/canary/examples/with-styled-components)
+- [Emotion](https://github.com/vercel/next.js/tree/canary/examples/with-emotion)
+- [Linaria](https://github.com/vercel/next.js/tree/canary/examples/with-linaria)
+- [Styletron](https://github.com/vercel/next.js/tree/canary/examples/with-styletron)
+- [Cxs](https://github.com/vercel/next.js/tree/canary/examples/with-cxs)
+- [Fela](https://github.com/vercel/next.js/tree/canary/examples/with-fela)
+- [Stitches](https://github.com/vercel/next.js/tree/canary/examples/with-stitches)
+
+
+
+Il est possible d'utiliser n'importe quelle solution CSS-in-JS existante. La plus simple est les styles en ligne :
+
+```jsx
+function HiThere() {
+ return salut
+}
+
+export default HiThere
+```
+
+Nous incluons [styled-jsx](https://github.com/vercel/styled-jsx) pour fournir un support des CSS scopés isolés.
+L'objectif est de supporter le "shadow CSS" similaire aux Web Components, qui malheureusement [ne supportent pas le rendu côté serveur et sont JS uniquement](https://github.com/w3c/webcomponents/issues/71).
+
+Voir les exemples ci-dessus pour d'autres solutions CSS-in-JS populaires (comme Styled Components).
+
+Un composant utilisant `styled-jsx` ressemble à ceci :
+
+```jsx
+function HelloWorld() {
+ return (
+
+ Bonjour le monde
+
scopé !
+
+
+
+ )
+}
+
+export default HelloWorld
+```
+
+Veuillez consulter la [documentation de styled-jsx](https://github.com/vercel/styled-jsx) pour plus d'exemples.
+
+### Désactivation de JavaScript
+
+Oui, si vous désactivez JavaScript, le CSS sera toujours chargé dans la version de production (`next start`). Pendant le développement, nous nécessitons que JavaScript soit activé pour fournir la meilleure expérience de développement avec [Fast Refresh](https://nextjs.org/blog/next-9-4#fast-refresh).
+
+
\ No newline at end of file
diff --git a/apps/docs/content/fr/docs/01-app/02-guides/custom-server.mdx b/apps/docs/content/fr/docs/01-app/02-guides/custom-server.mdx
new file mode 100644
index 00000000..d73b4fe8
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/02-guides/custom-server.mdx
@@ -0,0 +1,123 @@
+---
+source-updated-at: 2025-05-16T04:52:11.000Z
+translation-updated-at: 2025-06-02T20:00:31.897Z
+title: Comment configurer un serveur personnalisé dans Next.js
+nav_title: Serveur personnalisé
+description: Démarrer une application Next.js de manière programmatique en utilisant un serveur personnalisé.
+---
+
+{/* Le contenu de ce document est partagé entre le routeur d'application et celui des pages. Vous pouvez utiliser le composant `Contenu ` pour ajouter du contenu spécifique au routeur des pages. Tout contenu partagé ne doit pas être encapsulé dans un composant. */}
+
+Next.js inclut son propre serveur avec `next start` par défaut. Si vous avez un backend existant, vous pouvez toujours l'utiliser avec Next.js (ce n'est pas un serveur personnalisé). Un serveur Next.js personnalisé vous permet de démarrer un serveur de manière programmatique pour des motifs personnalisés. La plupart du temps, vous n'aurez pas besoin de cette approche. Cependant, elle est disponible si vous en avez besoin.
+
+> **Bon à savoir** :
+>
+> - Avant de décider d'utiliser un serveur personnalisé, gardez à l'esprit qu'il ne devrait être utilisé que lorsque le routeur intégré de Next.js ne peut pas répondre aux exigences de votre application. Un serveur personnalisé supprimera des optimisations de performance importantes, comme **[l'Optimisation Statique Automatique](/docs/pages/building-your-application/rendering/automatic-static-optimization).**
+> - Lorsque vous utilisez le mode de sortie autonome, il ne trace pas les fichiers du serveur personnalisé. Ce mode produit plutôt un fichier minimal séparé `server.js`. Ces deux modes ne peuvent pas être utilisés ensemble.
+
+Consultez l'[exemple suivant](https://github.com/vercel/next.js/tree/canary/examples/custom-server) d'un serveur personnalisé :
+
+```ts filename="server.ts" switcher
+import { createServer } from 'http'
+import { parse } from 'url'
+import next from 'next'
+
+const port = parseInt(process.env.PORT || '3000', 10)
+const dev = process.env.NODE_ENV !== 'production'
+const app = next({ dev })
+const handle = app.getRequestHandler()
+
+app.prepare().then(() => {
+ createServer((req, res) => {
+ const parsedUrl = parse(req.url!, true)
+ handle(req, res, parsedUrl)
+ }).listen(port)
+
+ console.log(
+ `> Server listening at http://localhost:${port} as ${
+ dev ? 'development' : process.env.NODE_ENV
+ }`
+ )
+})
+```
+
+```js filename="server.js" switcher
+import { createServer } from 'http'
+import { parse } from 'url'
+import next from 'next'
+
+const port = parseInt(process.env.PORT || '3000', 10)
+const dev = process.env.NODE_ENV !== 'production'
+const app = next({ dev })
+const handle = app.getRequestHandler()
+
+app.prepare().then(() => {
+ createServer((req, res) => {
+ const parsedUrl = parse(req.url, true)
+ handle(req, res, parsedUrl)
+ }).listen(port)
+
+ console.log(
+ `> Server listening at http://localhost:${port} as ${
+ dev ? 'development' : process.env.NODE_ENV
+ }`
+ )
+})
+```
+
+> `server.js` ne passe pas par le compilateur ou le processus de bundling de Next.js. Assurez-vous que la syntaxe et le code source requis par ce fichier sont compatibles avec la version actuelle de Node.js que vous utilisez. [Voir un exemple](https://github.com/vercel/next.js/tree/canary/examples/custom-server).
+
+Pour exécuter le serveur personnalisé, vous devrez mettre à jour les `scripts` dans `package.json` comme suit :
+
+```json filename="package.json"
+{
+ "scripts": {
+ "dev": "node server.js",
+ "build": "next build",
+ "start": "NODE_ENV=production node server.js"
+ }
+}
+```
+
+Alternativement, vous pouvez configurer `nodemon` ([exemple](https://github.com/vercel/next.js/tree/canary/examples/custom-server)). Le serveur personnalisé utilise l'import suivant pour connecter le serveur avec l'application Next.js :
+
+```js
+import next from 'next'
+
+const app = next({})
+```
+
+L'import `next` ci-dessus est une fonction qui reçoit un objet avec les options suivantes :
+
+| Option | Type | Description |
+| ------------ | ------------------ | ----------------------------------------------------------------------------------- |
+| `conf` | `Object` | Le même objet que vous utiliseriez dans `next.config.js`. Par défaut `{}` |
+| `dev` | `Boolean` | (_Optionnel_) Si Next.js doit être lancé en mode développement. Par défaut `false` |
+| `dir` | `String` | (_Optionnel_) Emplacement du projet Next.js. Par défaut `'.'` |
+| `quiet` | `Boolean` | (_Optionnel_) Masquer les messages d'erreur contenant des informations serveur. Par défaut `false` |
+| `hostname` | `String` | (_Optionnel_) Le nom d'hôte derrière lequel le serveur s'exécute |
+| `port` | `Number` | (_Optionnel_) Le port derrière lequel le serveur s'exécute |
+| `httpServer` | `node:http#Server` | (_Optionnel_) Le serveur HTTP derrière lequel Next.js s'exécute |
+| `turbo` | `Boolean` | (_Optionnel_) Activer Turbopack |
+
+L'`app` retournée peut ensuite être utilisée pour laisser Next.js gérer les requêtes comme nécessaire.
+
+
+
+## Désactiver le routage par système de fichiers
+
+Par défaut, `Next` servira chaque fichier dans le dossier `pages` sous un chemin correspondant au nom du fichier. Si votre projet utilise un serveur personnalisé, ce comportement peut entraîner le même contenu étant servi depuis plusieurs chemins, ce qui peut poser des problèmes de SEO et d'expérience utilisateur.
+
+Pour désactiver ce comportement et empêcher le routage basé sur les fichiers dans `pages`, ouvrez `next.config.js` et désactivez la configuration `useFileSystemPublicRoutes` :
+
+```js filename="next.config.js"
+module.exports = {
+ useFileSystemPublicRoutes: false,
+}
+```
+
+> Note : `useFileSystemPublicRoutes` désactive les routes basées sur les noms de fichiers pour le SSR ; le routage côté client peut toujours accéder à ces chemins. Lorsque vous utilisez cette option, vous devriez protéger contre la navigation vers les routes que vous ne voulez pas de manière programmatique.
+
+> Vous pourriez aussi vouloir configurer le routeur côté client pour interdire les redirections côté client vers les routes basées sur les noms de fichiers ; pour cela, référez-vous à [`router.beforePopState`](/docs/pages/api-reference/functions/use-router#routerbeforepopstate).
+
+
\ No newline at end of file
diff --git a/apps/docs/content/fr/docs/01-app/02-guides/debugging.mdx b/apps/docs/content/fr/docs/01-app/02-guides/debugging.mdx
new file mode 100644
index 00000000..47d18c12
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/02-guides/debugging.mdx
@@ -0,0 +1,181 @@
+---
+source-updated-at: 2025-05-16T04:52:11.000Z
+translation-updated-at: 2025-06-02T20:01:06.315Z
+title: Comment utiliser les outils de débogage avec Next.js
+nav_title: Débogage
+description: Apprenez à déboguer votre application Next.js avec VS Code, Chrome DevTools ou Firefox DevTools.
+---
+
+{/* Le contenu de ce document est partagé entre le routeur d'application et le routeur de pages. Vous pouvez utiliser le composant `Content ` pour ajouter du contenu spécifique au routeur de pages. Tout contenu partagé ne doit pas être encapsulé dans un composant. */}
+
+Cette documentation explique comment déboguer votre code frontend et backend Next.js avec un support complet des source maps en utilisant le [débogueur VS Code](https://code.visualstudio.com/docs/editor/debugging), [Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools) ou [Firefox DevTools](https://firefox-source-docs.mozilla.org/devtools-user/).
+
+Tout débogueur capable de se connecter à Node.js peut également être utilisé pour déboguer une application Next.js. Vous trouverez plus de détails dans le [Guide de débogage](https://nodejs.org/en/docs/guides/debugging-getting-started/) de Node.js.
+
+## Débogage avec VS Code
+
+Créez un fichier nommé `.vscode/launch.json` à la racine de votre projet avec le contenu suivant :
+
+```json filename="launch.json"
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Next.js : débogage côté serveur",
+ "type": "node-terminal",
+ "request": "launch",
+ "command": "npm run dev"
+ },
+ {
+ "name": "Next.js : débogage côté client",
+ "type": "chrome",
+ "request": "launch",
+ "url": "http://localhost:3000"
+ },
+ {
+ "name": "Next.js : débogage côté client (Firefox)",
+ "type": "firefox",
+ "request": "launch",
+ "url": "http://localhost:3000",
+ "reAttach": true,
+ "pathMappings": [
+ {
+ "url": "webpack://_N_E",
+ "path": "${workspaceFolder}"
+ }
+ ]
+ },
+ {
+ "name": "Next.js : débogage full stack",
+ "type": "node",
+ "request": "launch",
+ "program": "${workspaceFolder}/node_modules/next/dist/bin/next",
+ "runtimeArgs": ["--inspect"],
+ "skipFiles": ["/**"],
+ "serverReadyAction": {
+ "action": "debugWithEdge",
+ "killOnServerStop": true,
+ "pattern": "- Local:.+(https?://.+)",
+ "uriFormat": "%s",
+ "webRoot": "${workspaceFolder}"
+ }
+ }
+ ]
+}
+```
+
+> **Remarque** : Pour utiliser le débogage Firefox dans VS Code, vous devrez installer l'extension [Firefox Debugger](https://marketplace.visualstudio.com/items?itemName=firefox-devtools.vscode-firefox-debug).
+
+`npm run dev` peut être remplacé par `yarn dev` si vous utilisez Yarn ou `pnpm dev` si vous utilisez pnpm.
+
+Dans la configuration "Next.js : débogage full stack", `serverReadyAction.action` spécifie quel navigateur ouvrir lorsque le serveur est prêt. `debugWithEdge` signifie lancer le navigateur Edge. Si vous utilisez Chrome, changez cette valeur en `debugWithChrome`.
+
+Si vous [modifiez le numéro de port](/docs/pages/api-reference/cli/next#next-dev-options) sur lequel votre application démarre, remplacez le `3000` dans `http://localhost:3000` par le port que vous utilisez.
+
+Si vous exécutez Next.js depuis un répertoire autre que la racine (par exemple si vous utilisez Turborepo), vous devez ajouter `cwd` aux tâches de débogage côté serveur et full stack. Par exemple : `"cwd": "${workspaceFolder}/apps/web"`.
+
+Allez maintenant dans le panneau Debug (`Ctrl+Shift+D` sur Windows/Linux, `⇧+⌘+D` sur macOS), sélectionnez une configuration de lancement, puis appuyez sur `F5` ou sélectionnez **Debug: Start Debugging** dans la palette de commandes pour démarrer votre session de débogage.
+
+## Utilisation du débogueur dans Jetbrains WebStorm
+
+Cliquez sur le menu déroulant listant les configurations d'exécution, puis cliquez sur `Edit Configurations...`. Créez une configuration de débogage `JavaScript Debug` avec `http://localhost:3000` comme URL. Personnalisez selon vos préférences (par exemple, navigateur pour le débogage, enregistrer comme fichier projet), puis cliquez sur `OK`. Exécutez cette configuration de débogage, et le navigateur sélectionné devrait s'ouvrir automatiquement. À ce stade, vous devriez avoir 2 applications en mode débogage : l'application NextJS node et l'application client/navigateur.
+
+## Débogage avec les DevTools des navigateurs
+
+### Code côté client
+
+Démarrez votre serveur de développement comme d'habitude en exécutant `next dev`, `npm run dev` ou `yarn dev`. Une fois le serveur démarré, ouvrez `http://localhost:3000` (ou votre URL alternative) dans votre navigateur préféré.
+
+Pour Chrome :
+
+- Ouvrez les outils de développement de Chrome (`Ctrl+Shift+J` sur Windows/Linux, `⌥+⌘+I` sur macOS)
+- Allez dans l'onglet **Sources**
+
+Pour Firefox :
+
+- Ouvrez les outils de développement de Firefox (`Ctrl+Shift+I` sur Windows/Linux, `⌥+⌘+I` sur macOS)
+- Allez dans l'onglet **Debugger**
+
+Dans les deux navigateurs, chaque fois que votre code côté client atteint une instruction [`debugger`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/debugger), l'exécution du code sera mise en pause et ce fichier apparaîtra dans la zone de débogage. Vous pouvez également rechercher des fichiers pour définir des points d'arrêt manuellement :
+
+- Dans Chrome : Appuyez sur `Ctrl+P` sur Windows/Linux ou `⌘+P` sur macOS
+- Dans Firefox : Appuyez sur `Ctrl+P` sur Windows/Linux ou `⌘+P` sur macOS, ou utilisez l'arborescence des fichiers dans le panneau de gauche
+
+Notez que lors de la recherche, vos fichiers sources auront des chemins commençant par `webpack://_N_E/./`.
+
+### Code côté serveur
+
+Pour déboguer le code côté serveur Next.js avec les DevTools des navigateurs, vous devez passer le flag [`--inspect`](https://nodejs.org/api/cli.html#cli_inspect_host_port) au processus Node.js sous-jacent :
+
+```bash filename="Terminal"
+NODE_OPTIONS='--inspect' next dev
+```
+
+> **Bon à savoir** : Utilisez `NODE_OPTIONS='--inspect=0.0.0.0'` pour permettre un accès de débogage distant en dehors de localhost, par exemple lors de l'exécution de l'application dans un conteneur Docker.
+
+Si vous utilisez `npm run dev` ou `yarn dev`, vous devez mettre à jour le script `dev` dans votre `package.json` :
+
+```json filename="package.json"
+{
+ "scripts": {
+ "dev": "NODE_OPTIONS='--inspect' next dev"
+ }
+}
+```
+
+Le lancement du serveur de développement Next.js avec le flag `--inspect` ressemblera à ceci :
+
+```bash filename="Terminal"
+Debugger listening on ws://127.0.0.1:9229/0cf90313-350d-4466-a748-cd60f4e47c95
+For help, see: https://nodejs.org/en/docs/inspector
+ready - started server on 0.0.0.0:3000, url: http://localhost:3000
+```
+
+Pour Chrome :
+
+1. Ouvrez un nouvel onglet et visitez `chrome://inspect`
+2. Cliquez sur **Configure...** pour vous assurer que les deux ports de débogage sont listés
+3. Ajoutez `localhost:9229` et `localhost:9230` s'ils ne sont pas déjà présents
+4. Cherchez votre application Next.js dans la section **Remote Target**
+5. Cliquez sur **inspect** pour ouvrir une fenêtre séparée des DevTools
+6. Allez dans l'onglet **Sources**
+
+Pour Firefox :
+
+1. Ouvrez un nouvel onglet et visitez `about:debugging`
+2. Cliquez sur **This Firefox** dans la barre latérale gauche
+3. Sous **Remote Targets**, trouvez votre application Next.js
+4. Cliquez sur **Inspect** pour ouvrir le débogueur
+5. Allez dans l'onglet **Debugger**
+
+Le débogage du code côté serveur fonctionne de manière similaire au débogage côté client. Lors de la recherche de fichiers (`Ctrl+P`/`⌘+P`), vos fichiers sources auront des chemins commençant par `webpack://{nom-de-l-application}/./` (où `{nom-de-l-application}` sera remplacé par le nom de votre application selon votre fichier `package.json`).
+
+### Inspection des erreurs serveur avec les DevTools des navigateurs
+
+Lorsque vous rencontrez une erreur, l'inspection du code source peut aider à retracer la cause racine des erreurs.
+
+Next.js affichera une icône Node.js sous l'indicateur de version Next.js dans l'overlay d'erreur. En cliquant sur cette icône, l'URL des DevTools est copiée dans votre presse-papiers. Vous pouvez ouvrir un nouvel onglet du navigateur avec cette URL pour inspecter le processus serveur Next.js.
+
+### Débogage sur Windows
+
+Les utilisateurs Windows peuvent rencontrer un problème en utilisant `NODE_OPTIONS='--inspect'` car cette syntaxe n'est pas supportée sur les plateformes Windows. Pour contourner ce problème, installez le package [`cross-env`](https://www.npmjs.com/package/cross-env) comme dépendance de développement (`-D` avec `npm` et `yarn`) et remplacez le script `dev` par ce qui suit.
+
+```json filename="package.json"
+{
+ "scripts": {
+ "dev": "cross-env NODE_OPTIONS='--inspect' next dev"
+ }
+}
+```
+
+`cross-env` définira la variable d'environnement `NODE_OPTIONS` quel que soit le système d'exploitation (y compris Mac, Linux et Windows) et vous permettra de déboguer de manière cohérente entre les appareils et les systèmes d'exploitation.
+
+> **Bon à savoir** : Assurez-vous que Windows Defender est désactivé sur votre machine. Ce service externe vérifie _chaque lecture de fichier_, ce qui a été signalé comme augmentant considérablement le temps de Fast Refresh avec `next dev`. Il s'agit d'un problème connu, non lié à Next.js, mais qui affecte le développement avec Next.js.
+
+## Plus d'informations
+
+Pour en savoir plus sur l'utilisation d'un débogueur JavaScript, consultez la documentation suivante :
+
+- [Débogage Node.js dans VS Code : Points d'arrêt](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_breakpoints)
+- [Chrome DevTools : Déboguer JavaScript](https://developers.google.com/web/tools/chrome-devtools/javascript)
+- [Firefox DevTools : Débogueur](https://firefox-source-docs.mozilla.org/devtools-user/debugger/)
\ No newline at end of file
diff --git a/apps/docs/content/fr/docs/01-app/02-guides/draft-mode.mdx b/apps/docs/content/fr/docs/01-app/02-guides/draft-mode.mdx
new file mode 100644
index 00000000..6233f7d6
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/02-guides/draft-mode.mdx
@@ -0,0 +1,217 @@
+---
+source-updated-at: 2025-05-19T22:31:51.000Z
+translation-updated-at: 2025-06-02T20:00:44.032Z
+title: Comment prévisualiser du contenu avec le mode brouillon dans Next.js
+nav_title: Mode brouillon
+description: Next.js propose un mode brouillon pour basculer entre les pages statiques et dynamiques. Découvrez ici comment cela fonctionne avec App Router.
+related:
+ title: Prochaines étapes
+ description: Consultez la référence API pour plus d'informations sur l'utilisation du mode brouillon.
+ links:
+ - app/api-reference/functions/draft-mode
+---
+
+Le **mode brouillon** vous permet de prévisualiser du contenu en cours de rédaction depuis votre CMS headless dans votre application Next.js. Ceci est particulièrement utile pour les pages statiques générées au moment de la construction, car il vous permet de basculer vers un [rendu dynamique](/docs/app/getting-started/partial-prerendering#dynamic-rendering) et de voir les modifications en cours sans avoir à reconstruire l'intégralité de votre site.
+
+Cette page vous guide pas à pas pour activer et utiliser le mode brouillon.
+
+## Étape 1 : Créer un gestionnaire de route
+
+Créez un [gestionnaire de route](/docs/app/building-your-application/routing/route-handlers). Il peut porter n'importe quel nom, par exemple `app/api/draft/route.ts`.
+
+```ts filename="app/api/draft/route.ts" switcher
+export async function GET(request: Request) {
+ return new Response('')
+}
+```
+
+```js filename="app/api/draft/route.js" switcher
+export async function GET() {
+ return new Response('')
+}
+```
+
+Ensuite, importez la fonction [`draftMode`](/docs/app/api-reference/functions/draft-mode) et appelez la méthode `enable()`.
+
+```ts filename="app/api/draft/route.ts" switcher
+import { draftMode } from 'next/headers'
+
+export async function GET(request: Request) {
+ const draft = await draftMode()
+ draft.enable()
+ return new Response('Draft mode is enabled')
+}
+```
+
+```js filename="app/api/draft/route.js" switcher
+import { draftMode } from 'next/headers'
+
+export async function GET(request) {
+ const draft = await draftMode()
+ draft.enable()
+ return new Response('Draft mode is enabled')
+}
+```
+
+Cette action définit un **cookie** pour activer le mode brouillon. Les requêtes ultérieures contenant ce cookie déclencheront le mode brouillon et modifieront le comportement des pages générées statiquement.
+
+Vous pouvez tester cela manuellement en visitant `/api/draft` et en examinant les outils de développement de votre navigateur. Notez l'en-tête de réponse `Set-Cookie` avec un cookie nommé `__prerender_bypass`.
+
+## Étape 2 : Accéder au gestionnaire de route depuis votre CMS headless
+
+> Ces étapes supposent que votre CMS headless prend en charge la configuration d'**URLs de brouillon personnalisées**. Si ce n'est pas le cas, vous pouvez toujours utiliser cette méthode pour sécuriser vos URLs de brouillon, mais vous devrez construire et accéder à l'URL manuellement. Les étapes spécifiques varieront selon le CMS headless utilisé.
+
+Pour accéder de manière sécurisée au gestionnaire de route depuis votre CMS headless :
+
+1. Créez une **chaîne de jeton secrète** à l'aide d'un générateur de jetons de votre choix. Ce secret ne sera connu que par votre application Next.js et votre CMS headless.
+2. Si votre CMS headless prend en charge les URLs de brouillon personnalisées, spécifiez une URL de brouillon (en supposant que votre gestionnaire de route se trouve à `app/api/draft/route.ts`). Par exemple :
+
+```bash filename="Terminal"
+https:///api/draft?secret=&slug=
+```
+
+> - `` doit être votre domaine de déploiement.
+> - `` doit être remplacé par le jeton secret que vous avez généré.
+> - `` doit être le chemin de la page que vous souhaitez visualiser. Si vous voulez voir `/posts/un`, utilisez `&slug=/posts/un`.
+>
+> Votre CMS headless peut vous permettre d'inclure une variable dans l'URL de brouillon pour que `` soit défini dynamiquement en fonction des données du CMS, comme ceci : `&slug=/posts/{entry.fields.slug}`
+
+3. Dans votre gestionnaire de route, vérifiez que le secret correspond et que le paramètre `slug` existe (sinon, la requête doit échouer), appelez `draftMode.enable()` pour définir le cookie. Ensuite, redirigez le navigateur vers le chemin spécifié par `slug` :
+
+```ts filename="app/api/draft/route.ts" switcher
+import { draftMode } from 'next/headers'
+import { redirect } from 'next/navigation'
+
+export async function GET(request: Request) {
+ // Analyser les paramètres de la chaîne de requête
+ const { searchParams } = new URL(request.url)
+ const secret = searchParams.get('secret')
+ const slug = searchParams.get('slug')
+
+ // Vérifier le secret et les paramètres suivants
+ // Ce secret ne doit être connu que par ce gestionnaire de route et le CMS
+ if (secret !== 'MY_SECRET_TOKEN' || !slug) {
+ return new Response('Jeton invalide', { status: 401 })
+ }
+
+ // Interroger le CMS headless pour vérifier si le `slug` fourni existe
+ // getPostBySlug implémenterait la logique de récupération nécessaire auprès du CMS headless
+ const post = await getPostBySlug(slug)
+
+ // Si le slug n'existe pas, empêcher l'activation du mode brouillon
+ if (!post) {
+ return new Response('Slug invalide', { status: 401 })
+ }
+
+ // Activer le mode brouillon en définissant le cookie
+ const draft = await draftMode()
+ draft.enable()
+
+ // Rediriger vers le chemin du post récupéré
+ // Nous ne redirigeons pas vers searchParams.slug pour éviter les vulnérabilités de redirection ouverte
+ redirect(post.slug)
+}
+```
+
+```js filename="app/api/draft/route.js" switcher
+import { draftMode } from 'next/headers'
+import { redirect } from 'next/navigation'
+
+export async function GET(request) {
+ // Analyser les paramètres de la chaîne de requête
+ const { searchParams } = new URL(request.url)
+ const secret = searchParams.get('secret')
+ const slug = searchParams.get('slug')
+
+ // Vérifier le secret et les paramètres suivants
+ // Ce secret ne doit être connu que par ce gestionnaire de route et le CMS
+ if (secret !== 'MY_SECRET_TOKEN' || !slug) {
+ return new Response('Jeton invalide', { status: 401 })
+ }
+
+ // Interroger le CMS headless pour vérifier si le `slug` fourni existe
+ // getPostBySlug implémenterait la logique de récupération nécessaire auprès du CMS headless
+ const post = await getPostBySlug(slug)
+
+ // Si le slug n'existe pas, empêcher l'activation du mode brouillon
+ if (!post) {
+ return new Response('Slug invalide', { status: 401 })
+ }
+
+ // Activer le mode brouillon en définissant le cookie
+ const draft = await draftMode()
+ draft.enable()
+
+ // Rediriger vers le chemin du post récupéré
+ // Nous ne redirigeons pas vers searchParams.slug pour éviter les vulnérabilités de redirection ouverte
+ redirect(post.slug)
+}
+```
+
+Si cela réussit, le navigateur sera redirigé vers le chemin que vous souhaitez visualiser avec le cookie de mode brouillon.
+
+## Étape 3 : Prévisualiser le contenu en brouillon
+
+L'étape suivante consiste à mettre à jour votre page pour vérifier la valeur de `draftMode().isEnabled`.
+
+Si vous demandez une page pour laquelle le cookie est défini, les données seront récupérées au **moment de la requête** (au lieu du moment de la construction).
+
+De plus, la valeur de `isEnabled` sera `true`.
+
+```tsx filename="app/page.tsx" switcher
+// page qui récupère des données
+import { draftMode } from 'next/headers'
+
+async function getData() {
+ const { isEnabled } = await draftMode()
+
+ const url = isEnabled
+ ? 'https://draft.example.com'
+ : 'https://production.example.com'
+
+ const res = await fetch(url)
+
+ return res.json()
+}
+
+export default async function Page() {
+ const { title, desc } = await getData()
+
+ return (
+
+ {title}
+ {desc}
+
+ )
+}
+```
+
+```jsx filename="app/page.js" switcher
+// page qui récupère des données
+import { draftMode } from 'next/headers'
+
+async function getData() {
+ const { isEnabled } = await draftMode()
+
+ const url = isEnabled
+ ? 'https://draft.example.com'
+ : 'https://production.example.com'
+
+ const res = await fetch(url)
+
+ return res.json()
+}
+
+export default async function Page() {
+ const { title, desc } = await getData()
+
+ return (
+
+ {title}
+ {desc}
+
+ )
+}
+```
+
+Si vous accédez au gestionnaire de route de brouillon (avec `secret` et `slug`) depuis votre CMS headless ou manuellement via l'URL, vous devriez maintenant pouvoir voir le contenu en brouillon. Et si vous mettez à jour votre brouillon sans le publier, vous devriez pouvoir visualiser les modifications.
\ No newline at end of file
diff --git a/apps/docs/content/fr/docs/01-app/02-guides/environment-variables.mdx b/apps/docs/content/fr/docs/01-app/02-guides/environment-variables.mdx
new file mode 100644
index 00000000..914af1d0
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/02-guides/environment-variables.mdx
@@ -0,0 +1,280 @@
+---
+source-updated-at: 2025-05-16T04:52:11.000Z
+translation-updated-at: 2025-06-02T20:01:24.687Z
+title: Comment utiliser les variables d'environnement dans Next.js
+nav_title: Variables d'environnement
+description: Apprenez à ajouter et accéder aux variables d'environnement dans votre application Next.js.
+---
+
+{/* Le contenu de ce document est partagé entre le routeur App et Pages. Vous pouvez utiliser le composant `Contenu ` pour ajouter du contenu spécifique au routeur Pages. Tout contenu partagé ne doit pas être encapsulé dans un composant. */}
+
+Next.js inclut un support natif pour les variables d'environnement, ce qui vous permet de :
+
+- [Utiliser `.env` pour charger les variables d'environnement](#chargement-des-variables-denvironnement)
+- [Inclure les variables d'environnement pour le navigateur en les préfixant avec `NEXT_PUBLIC_`](#inclusion-des-variables-denvironnement-pour-le-navigateur)
+
+> **Avertissement :** Le modèle par défaut de `create-next-app` garantit que tous les fichiers `.env` sont ajoutés à votre `.gitignore`. Vous ne devez presque jamais commettre ces fichiers dans votre dépôt.
+
+## Chargement des variables d'environnement
+
+Next.js prend en charge nativement le chargement des variables d'environnement depuis les fichiers `.env*` vers `process.env`.
+
+```txt filename=".env"
+DB_HOST=localhost
+DB_USER=myuser
+DB_PASS=mypassword
+```
+
+
+
+Cela charge automatiquement `process.env.DB_HOST`, `process.env.DB_USER` et `process.env.DB_PASS` dans l'environnement Node.js, vous permettant de les utiliser dans les [méthodes de récupération de données de Next.js](/docs/pages/building-your-application/data-fetching) et les [routes API](/docs/pages/building-your-application/routing/api-routes).
+
+Par exemple, en utilisant [`getStaticProps`](/docs/pages/building-your-application/data-fetching/get-static-props) :
+
+```js filename="pages/index.js"
+export async function getStaticProps() {
+ const db = await myDB.connect({
+ host: process.env.DB_HOST,
+ username: process.env.DB_USER,
+ password: process.env.DB_PASS,
+ })
+ // ...
+}
+```
+
+
+
+
+
+> **Remarque :** Next.js prend également en charge les variables multilignes dans vos fichiers `.env*` :
+>
+> ```bash
+> # .env
+>
+> # vous pouvez écrire avec des sauts de ligne
+> PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
+> ...
+> Kh9NV...
+> ...
+> -----END DSA PRIVATE KEY-----"
+>
+> # ou avec `\n` entre guillemets doubles
+> PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nKh9NV...\n-----END DSA PRIVATE KEY-----\n"
+> ```
+
+> **Remarque :** Si vous utilisez un dossier `/src`, notez que Next.js chargera les fichiers .env **uniquement** depuis le dossier parent et **pas** depuis le dossier `/src`.
+> Cela charge automatiquement `process.env.DB_HOST`, `process.env.DB_USER` et `process.env.DB_PASS` dans l'environnement Node.js, vous permettant de les utiliser dans les [Route Handlers](/docs/app/building-your-application/routing/route-handlers).
+
+Par exemple :
+
+```js filename="app/api/route.js"
+export async function GET() {
+ const db = await myDB.connect({
+ host: process.env.DB_HOST,
+ username: process.env.DB_USER,
+ password: process.env.DB_PASS,
+ })
+ // ...
+}
+```
+
+
+
+### Chargement des variables d'environnement avec `@next/env`
+
+Si vous devez charger des variables d'environnement en dehors de l'exécution de Next.js, comme dans un fichier de configuration racine pour un ORM ou un outil de test, vous pouvez utiliser le package `@next/env`.
+
+Ce package est utilisé en interne par Next.js pour charger les variables d'environnement depuis les fichiers `.env*`.
+
+Pour l'utiliser, installez le package et utilisez la fonction `loadEnvConfig` pour charger les variables d'environnement :
+
+```bash
+npm install @next/env
+```
+
+```tsx filename="envConfig.ts" switcher
+import { loadEnvConfig } from '@next/env'
+
+const projectDir = process.cwd()
+loadEnvConfig(projectDir)
+```
+
+```jsx filename="envConfig.js" switcher
+import { loadEnvConfig } from '@next/env'
+
+const projectDir = process.cwd()
+loadEnvConfig(projectDir)
+```
+
+Ensuite, vous pouvez importer la configuration là où vous en avez besoin. Par exemple :
+
+```tsx filename="orm.config.ts" switcher
+import './envConfig.ts'
+
+export default defineConfig({
+ dbCredentials: {
+ connectionString: process.env.DATABASE_URL!,
+ },
+})
+```
+
+```jsx filename="orm.config.js" switcher
+import './envConfig.js'
+
+export default defineConfig({
+ dbCredentials: {
+ connectionString: process.env.DATABASE_URL,
+ },
+})
+```
+
+### Référencement d'autres variables
+
+Next.js étendra automatiquement les variables qui utilisent `$` pour référencer d'autres variables, par exemple `$VARIABLE` dans vos fichiers `.env*`. Cela vous permet de référencer d'autres secrets. Par exemple :
+
+```txt filename=".env"
+TWITTER_USER=nextjs
+TWITTER_URL=https://x.com/$TWITTER_USER
+```
+
+Dans l'exemple ci-dessus, `process.env.TWITTER_URL` sera défini sur `https://x.com/nextjs`.
+
+> **Bon à savoir :** Si vous devez utiliser une variable avec un `$` dans la valeur réelle, elle doit être échappée, par exemple `\$`.
+
+## Inclusion des variables d'environnement pour le navigateur
+
+Les variables d'environnement non préfixées par `NEXT_PUBLIC_` ne sont disponibles que dans l'environnement Node.js, ce qui signifie qu'elles ne sont pas accessibles depuis le navigateur (le client s'exécute dans un _environnement_ différent).
+
+Pour rendre la valeur d'une variable d'environnement accessible dans le navigateur, Next.js peut "intégrer" une valeur, au moment de la construction, dans le bundle JavaScript qui est livré au client, remplaçant toutes les références à `process.env.[variable]` par une valeur codée en dur. Pour cela, vous devez simplement préfixer la variable avec `NEXT_PUBLIC_`. Par exemple :
+
+```txt filename="Terminal"
+NEXT_PUBLIC_ANALYTICS_ID=abcdefghijk
+```
+
+Cela indiquera à Next.js de remplacer toutes les références à `process.env.NEXT_PUBLIC_ANALYTICS_ID` dans l'environnement Node.js par la valeur de l'environnement dans lequel vous exécutez `next build`, vous permettant de l'utiliser n'importe où dans votre code. Elle sera intégrée dans tout JavaScript envoyé au navigateur.
+
+> **Remarque :** Après la construction, votre application ne répondra plus aux changements de ces variables d'environnement. Par exemple, si vous utilisez un pipeline Heroku pour promouvoir des slugs construits dans un environnement vers un autre, ou si vous construisez et déployez une seule image Docker dans plusieurs environnements, toutes les variables `NEXT_PUBLIC_` seront figées avec la valeur évaluée au moment de la construction. Ces valeurs doivent donc être définies de manière appropriée lors de la construction du projet. Si vous avez besoin d'accéder à des valeurs d'environnement au runtime, vous devrez configurer votre propre API pour les fournir au client (soit à la demande, soit lors de l'initialisation).
+
+```js filename="pages/index.js"
+import setupAnalyticsService from '../lib/my-analytics-service'
+
+// 'NEXT_PUBLIC_ANALYTICS_ID' peut être utilisé ici car il est préfixé par 'NEXT_PUBLIC_'.
+// Il sera transformé au moment de la construction en `setupAnalyticsService('abcdefghijk')`.
+setupAnalyticsService(process.env.NEXT_PUBLIC_ANALYTICS_ID)
+
+function HomePage() {
+ return Hello World
+}
+
+export default HomePage
+```
+
+Notez que les recherches dynamiques ne seront _pas_ intégrées, comme :
+
+```js
+// Ceci ne sera PAS intégré, car il utilise une variable
+const varName = 'NEXT_PUBLIC_ANALYTICS_ID'
+setupAnalyticsService(process.env[varName])
+
+// Ceci ne sera PAS intégré, car il utilise une variable
+const env = process.env
+setupAnalyticsService(env.NEXT_PUBLIC_ANALYTICS_ID)
+```
+
+### Variables d'environnement au runtime
+
+Next.js peut prendre en charge à la fois les variables d'environnement au moment de la construction et au runtime.
+
+**Par défaut, les variables d'environnement ne sont disponibles que sur le serveur**. Pour exposer une variable d'environnement au navigateur, elle doit être préfixée par `NEXT_PUBLIC_`. Cependant, ces variables publiques seront intégrées dans le bundle JavaScript lors de `next build`.
+
+
+
+Pour lire les variables d'environnement au runtime, nous recommandons d'utiliser `getServerSideProps` ou [d'adopter progressivement le routeur App](/docs/app/guides/migrating/app-router-migration).
+
+
+
+
+
+Vous pouvez lire en toute sécurité les variables d'environnement sur le serveur pendant le rendu dynamique :
+
+```tsx filename="app/page.ts" switcher
+import { connection } from 'next/server'
+
+export default async function Component() {
+ await connection()
+ // les cookies, en-têtes et autres APIs dynamiques
+ // opteront également pour le rendu dynamique, ce qui signifie
+ // que cette variable d'environnement est évaluée au runtime
+ const value = process.env.MY_VALUE
+ // ...
+}
+```
+
+```jsx filename="app/page.js" switcher
+import { connection } from 'next/server'
+
+export default async function Component() {
+ await connection()
+ // les cookies, en-têtes et autres APIs dynamiques
+ // opteront également pour le rendu dynamique, ce qui signifie
+ // que cette variable d'environnement est évaluée au runtime
+ const value = process.env.MY_VALUE
+ // ...
+}
+```
+
+
+
+Cela vous permet d'utiliser une seule image Docker qui peut être promue à travers plusieurs environnements avec différentes valeurs.
+
+**Bon à savoir :**
+
+- Vous pouvez exécuter du code au démarrage du serveur en utilisant la [fonction `register`](/docs/app/guides/instrumentation).
+- Nous ne recommandons pas d'utiliser l'option [`runtimeConfig`](/docs/pages/api-reference/config/next-config-js/runtime-configuration), car cela ne fonctionne pas avec le mode de sortie autonome. Nous recommandons plutôt [d'adopter progressivement](/docs/app/guides/migrating/app-router-migration) le routeur App si vous avez besoin de cette fonctionnalité.
+
+## Variables d'environnement de test
+
+En plus des environnements `development` et `production`, il existe une 3ème option disponible : `test`. De la même manière que vous pouvez définir des valeurs par défaut pour les environnements de développement ou de production, vous pouvez faire de même avec un fichier `.env.test` pour l'environnement de `test` (bien que celui-ci ne soit pas aussi courant que les deux précédents). Next.js ne chargera pas les variables d'environnement depuis `.env.development` ou `.env.production` dans l'environnement de `test`.
+
+Cela est utile lors de l'exécution de tests avec des outils comme `jest` ou `cypress` où vous devez définir des variables d'environnement spécifiques uniquement pour les tests. Les valeurs par défaut de test seront chargées si `NODE_ENV` est défini sur `test`, bien que vous n'ayez généralement pas besoin de le faire manuellement car les outils de test s'en chargeront pour vous.
+
+Il y a une petite différence entre l'environnement de `test`, et les environnements `development` et `production` que vous devez garder à l'esprit : `.env.local` ne sera pas chargé, car vous attendez que les tests produisent les mêmes résultats pour tout le monde. Ainsi, chaque exécution de test utilisera les mêmes valeurs d'environnement par défaut en ignorant votre `.env.local` (qui est destiné à remplacer l'ensemble par défaut).
+
+> **Bon à savoir :** comme pour les variables d'environnement par défaut, le fichier `.env.test` doit être inclus dans votre dépôt, mais `.env.test.local` ne doit pas l'être, car `.env*.local` sont destinés à être ignorés via `.gitignore`.
+
+Lors de l'exécution de tests unitaires, vous pouvez vous assurer de charger vos variables d'environnement de la même manière que Next.js en utilisant la fonction `loadEnvConfig` du package `@next/env`.
+
+```js
+// Ce qui suit peut être utilisé dans un fichier de configuration global Jest ou similaire pour votre configuration de test
+import { loadEnvConfig } from '@next/env'
+
+export default async () => {
+ const projectDir = process.cwd()
+ loadEnvConfig(projectDir)
+}
+```
+
+## Ordre de chargement des variables d'environnement
+
+Les variables d'environnement sont recherchées dans les emplacements suivants, dans l'ordre, en s'arrêtant dès que la variable est trouvée.
+
+1. `process.env`
+1. `.env.$(NODE_ENV).local`
+1. `.env.local` (Non vérifié lorsque `NODE_ENV` est `test`.)
+1. `.env.$(NODE_ENV)`
+1. `.env`
+
+Par exemple, si `NODE_ENV` est `development` et que vous définissez une variable à la fois dans `.env.development.local` et `.env`, la valeur dans `.env.development.local` sera utilisée.
+
+> **Bon à savoir :** Les valeurs autorisées pour `NODE_ENV` sont `production`, `development` et `test`.
+
+## Bon à savoir
+
+- Si vous utilisez un [dossier `/src`](/docs/app/api-reference/file-conventions/src-folder), les fichiers `.env.*` doivent rester à la racine de votre projet.
+- Si la variable d'environnement `NODE_ENV` n'est pas définie, Next.js l'attribue automatiquement à `development` lors de l'exécution de la commande `next dev`, ou à `production` pour toutes les autres commandes.
+
+## Historique des versions
+
+| Version | Changements |
+| -------- | --------------------------------------------- |
+| `v9.4.0` | Support de `.env` et `NEXT_PUBLIC_` introduit. |
\ No newline at end of file
diff --git a/apps/docs/content/fr/docs/01-app/02-guides/forms.mdx b/apps/docs/content/fr/docs/01-app/02-guides/forms.mdx
new file mode 100644
index 00000000..073302ee
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/02-guides/forms.mdx
@@ -0,0 +1,493 @@
+---
+source-updated-at: 2025-06-01T01:32:20.000Z
+translation-updated-at: 2025-06-02T20:01:36.631Z
+title: Comment créer des formulaires avec les Server Actions
+nav_title: Formulaires
+description: Apprenez à créer des formulaires dans Next.js avec les Server Actions de React.
+---
+
+Les Server Actions de React sont des [fonctions serveur](https://react.dev/reference/rsc/server-functions) qui s'exécutent côté serveur. Elles peuvent être appelées dans les composants Serveur et Client pour gérer les soumissions de formulaires. Ce guide vous montrera comment créer des formulaires dans Next.js avec les Server Actions.
+
+## Fonctionnement
+
+React étend l'élément HTML [``](https://developer.mozilla.org/docs/Web/HTML/Element/form) pour permettre l'invocation des Server Actions via l'attribut [`action`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/form#action).
+
+Lorsqu'elle est utilisée dans un formulaire, la fonction reçoit automatiquement l'objet [`FormData`](https://developer.mozilla.org/docs/Web/API/FormData/FormData). Vous pouvez ensuite extraire les données en utilisant les [méthodes natives de FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData#instance_methods) :
+
+```tsx filename="app/invoices/page.tsx" switcher
+export default function Page() {
+ async function createInvoice(formData: FormData) {
+ 'use server'
+
+ const rawFormData = {
+ customerId: formData.get('customerId'),
+ amount: formData.get('amount'),
+ status: formData.get('status'),
+ }
+
+ // muter les données
+ // revalider le cache
+ }
+
+ return ...
+}
+```
+
+```jsx filename="app/invoices/page.js" switcher
+export default function Page() {
+ async function createInvoice(formData) {
+ 'use server'
+
+ const rawFormData = {
+ customerId: formData.get('customerId'),
+ amount: formData.get('amount'),
+ status: formData.get('status'),
+ }
+
+ // muter les données
+ // revalider le cache
+ }
+
+ return ...
+}
+```
+
+> **Bon à savoir :** Pour les formulaires avec plusieurs champs, vous pouvez utiliser la méthode [`entries()`](https://developer.mozilla.org/en-US/docs/Web/API/FormData/entries) avec [`Object.fromEntries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries) de JavaScript. Par exemple : `const rawFormData = Object.fromEntries(formData)`.
+
+## Passage d'arguments supplémentaires
+
+En dehors des champs de formulaire, vous pouvez passer des arguments supplémentaires à une fonction serveur en utilisant la méthode JavaScript [`bind`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind). Par exemple, pour passer l'argument `userId` à la fonction serveur `updateUser` :
+
+```tsx filename="app/client-component.tsx" highlight={6} switcher
+'use client'
+
+import { updateUser } from './actions'
+
+export function UserProfile({ userId }: { userId: string }) {
+ const updateUserWithId = updateUser.bind(null, userId)
+
+ return (
+
+
+ Mettre à jour le nom
+
+ )
+}
+```
+
+```jsx filename="app/client-component.js" highlight={6} switcher
+'use client'
+
+import { updateUser } from './actions'
+
+export function UserProfile({ userId }) {
+ const updateUserWithId = updateUser.bind(null, userId)
+
+ return (
+
+
+ Mettre à jour le nom
+
+ )
+}
+```
+
+La fonction serveur recevra `userId` comme argument supplémentaire :
+
+```ts filename="app/actions.ts" switcher
+'use server'
+
+export async function updateUser(userId: string, formData: FormData) {}
+```
+
+```js filename="app/actions.js" switcher
+'use server'
+
+export async function updateUser(userId, formData) {}
+```
+
+> **Bon à savoir :**
+>
+> - Une alternative consiste à passer les arguments sous forme de champs cachés dans le formulaire (par exemple ` `). Cependant, la valeur fera partie du HTML rendu et ne sera pas encodée.
+> - `bind` fonctionne dans les composants Serveur et Client et prend en charge l'amélioration progressive.
+
+## Validation des formulaires
+
+Les formulaires peuvent être validés côté client ou serveur.
+
+- Pour la **validation côté client**, vous pouvez utiliser les attributs HTML comme `required` et `type="email"` pour une validation basique.
+- Pour la **validation côté serveur**, vous pouvez utiliser une bibliothèque comme [zod](https://zod.dev/) pour valider les champs du formulaire. Par exemple :
+
+```tsx filename="app/actions.ts" switcher
+'use server'
+
+import { z } from 'zod'
+
+const schema = z.object({
+ email: z.string({
+ invalid_type_error: 'Email invalide',
+ }),
+})
+
+export default async function createUser(formData: FormData) {
+ const validatedFields = schema.safeParse({
+ email: formData.get('email'),
+ })
+
+ // Retour anticipé si les données du formulaire sont invalides
+ if (!validatedFields.success) {
+ return {
+ errors: validatedFields.error.flatten().fieldErrors,
+ }
+ }
+
+ // Muter les données
+}
+```
+
+```jsx filename="app/actions.js" switcher
+'use server'
+
+import { z } from 'zod'
+
+const schema = z.object({
+ email: z.string({
+ invalid_type_error: 'Email invalide',
+ }),
+})
+
+export default async function createsUser(formData) {
+ const validatedFields = schema.safeParse({
+ email: formData.get('email'),
+ })
+
+ // Retour anticipé si les données du formulaire sont invalides
+ if (!validatedFields.success) {
+ return {
+ errors: validatedFields.error.flatten().fieldErrors,
+ }
+ }
+
+ // Muter les données
+}
+```
+
+## Erreurs de validation
+
+Pour afficher les erreurs ou messages de validation, transformez le composant qui définit le `` en composant Client et utilisez [`useActionState`](https://react.dev/reference/react/useActionState) de React.
+
+Avec `useActionState`, la signature de la fonction serveur changera pour recevoir un nouveau paramètre `prevState` ou `initialState` comme premier argument.
+
+```tsx filename="app/actions.ts" highlight={4} switcher
+'use server'
+
+import { z } from 'zod'
+
+export async function createUser(initialState: any, formData: FormData) {
+ const validatedFields = schema.safeParse({
+ email: formData.get('email'),
+ })
+ // ...
+}
+```
+
+```jsx filename="app/actions.ts" highlight={4} switcher
+'use server'
+
+import { z } from 'zod'
+
+// ...
+
+export async function createUser(initialState, formData) {
+ const validatedFields = schema.safeParse({
+ email: formData.get('email'),
+ })
+ // ...
+}
+```
+
+Vous pouvez ensuite afficher conditionnellement le message d'erreur en fonction de l'objet `state`.
+
+```tsx filename="app/ui/signup.tsx" highlight={11,18-20} switcher
+'use client'
+
+import { useActionState } from 'react'
+import { createUser } from '@/app/actions'
+
+const initialState = {
+ message: '',
+}
+
+export function Signup() {
+ const [state, formAction, pending] = useActionState(createUser, initialState)
+
+ return (
+
+ Email
+
+ {/* ... */}
+ {state?.message}
+ S'inscrire
+
+ )
+}
+```
+
+```jsx filename="app/ui/signup.js" highlight={11,18-20} switcher
+'use client'
+
+import { useActionState } from 'react'
+import { createUser } from '@/app/actions'
+
+const initialState = {
+ message: '',
+}
+
+export function Signup() {
+ const [state, formAction, pending] = useActionState(createUser, initialState)
+
+ return (
+
+ Email
+
+ {/* ... */}
+ {state?.message}
+ S'inscrire
+
+ )
+}
+```
+
+## États d'attente
+
+Le hook [`useActionState`](https://react.dev/reference/react/useActionState) expose un booléen `pending` qui peut être utilisé pour afficher un indicateur de chargement ou désactiver le bouton de soumission pendant l'exécution de l'action.
+
+```tsx filename="app/ui/signup.tsx" highlight={7,12} switcher
+'use client'
+
+import { useActionState } from 'react'
+import { createUser } from '@/app/actions'
+
+export function Signup() {
+ const [state, formAction, pending] = useActionState(createUser, initialState)
+
+ return (
+
+ {/* Autres éléments du formulaire */}
+ S'inscrire
+
+ )
+}
+```
+
+```jsx filename="app/ui/signup.js" highlight={7,12} switcher
+'use client'
+
+import { useActionState } from 'react'
+import { createUser } from '@/app/actions'
+
+export function Signup() {
+ const [state, formAction, pending] = useActionState(createUser, initialState)
+
+ return (
+
+ {/* Autres éléments du formulaire */}
+ S'inscrire
+
+ )
+}
+```
+
+Alternativement, vous pouvez utiliser le hook [`useFormStatus`](https://react.dev/reference/react-dom/hooks/useFormStatus) pour afficher un indicateur de chargement pendant l'exécution de l'action. Avec ce hook, vous devrez créer un composant séparé pour afficher l'indicateur. Par exemple, pour désactiver le bouton pendant l'attente :
+
+```tsx filename="app/ui/button.tsx" highlight={6} switcher
+'use client'
+
+import { useFormStatus } from 'react-dom'
+
+export function SubmitButton() {
+ const { pending } = useFormStatus()
+
+ return (
+
+ S'inscrire
+
+ )
+}
+```
+
+```jsx filename="app/ui/button.js" highlight={6} switcher
+'use client'
+
+import { useFormStatus } from 'react-dom'
+
+export function SubmitButton() {
+ const { pending } = useFormStatus()
+
+ return (
+
+ S'inscrire
+
+ )
+}
+```
+
+Vous pouvez ensuite imbriquer le composant `SubmitButton` dans le formulaire :
+
+```tsx filename="app/ui/signup.tsx" switcher
+import { SubmitButton } from './button'
+import { createUser } from '@/app/actions'
+
+export function Signup() {
+ return (
+
+ {/* Autres éléments du formulaire */}
+
+
+ )
+}
+```
+
+```jsx filename="app/ui/signup.js" switcher
+import { SubmitButton } from './button'
+import { createUser } from '@/app/actions'
+
+export function Signup() {
+ return (
+
+ {/* Autres éléments du formulaire */}
+
+
+ )
+}
+```
+
+> **Bon à savoir :** Dans React 19, `useFormStatus` inclut des clés supplémentaires sur l'objet retourné, comme data, method et action. Si vous n'utilisez pas React 19, seule la clé `pending` est disponible.
+
+## Mises à jour optimistes
+
+Vous pouvez utiliser le hook React [`useOptimistic`](https://react.dev/reference/react/useOptimistic) pour mettre à jour l'interface de manière optimiste avant que la fonction serveur ne termine son exécution, plutôt que d'attendre la réponse :
+
+```tsx filename="app/page.tsx" switcher
+'use client'
+
+import { useOptimistic } from 'react'
+import { send } from './actions'
+
+type Message = {
+ message: string
+}
+
+export function Thread({ messages }: { messages: Message[] }) {
+ const [optimisticMessages, addOptimisticMessage] = useOptimistic<
+ Message[],
+ string
+ >(messages, (state, newMessage) => [...state, { message: newMessage }])
+
+ const formAction = async (formData: FormData) => {
+ const message = formData.get('message') as string
+ addOptimisticMessage(message)
+ await send(message)
+ }
+
+ return (
+
+ {optimisticMessages.map((m, i) => (
+
{m.message}
+ ))}
+
+
+ Envoyer
+
+
+ )
+}
+```
+
+```jsx filename="app/page.js" switcher
+'use client'
+
+import { useOptimistic } from 'react'
+import { send } from './actions'
+
+export function Thread({ messages }) {
+ const [optimisticMessages, addOptimisticMessage] = useOptimistic(
+ messages,
+ (state, newMessage) => [...state, { message: newMessage }]
+ )
+
+ const formAction = async (formData) => {
+ const message = formData.get('message')
+ addOptimisticMessage(message)
+ await send(message)
+ }
+
+ return (
+
+ {optimisticMessages.map((m) => (
+
{m.message}
+ ))}
+
+
+ Envoyer
+
+
+ )
+}
+```
+
+## Éléments de formulaire imbriqués
+
+Vous pouvez appeler des Server Actions dans des éléments imbriqués à l'intérieur de `` comme ``, ` ` et ` `. Ces éléments acceptent la prop `formAction` ou des gestionnaires d'événements.
+
+C'est utile lorsque vous souhaitez appeler plusieurs Server Actions dans un formulaire. Par exemple, vous pouvez créer un élément `` spécifique pour enregistrer un brouillon de publication en plus de le publier. Voir la [documentation React sur ``](https://react.dev/reference/react-dom/components/form#handling-multiple-submission-types) pour plus d'informations.
+
+## Soumission programmatique de formulaire
+
+Vous pouvez déclencher une soumission de formulaire programmatiquement en utilisant la méthode [`requestSubmit()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/requestSubmit). Par exemple, lorsque l'utilisateur soumet un formulaire avec le raccourci clavier `⌘` + `Entrée`, vous pouvez écouter l'événement `onKeyDown` :
+
+```tsx filename="app/entry.tsx" switcher
+'use client'
+
+export function Entry() {
+ const handleKeyDown = (e: React.KeyboardEvent) => {
+ if (
+ (e.ctrlKey || e.metaKey) &&
+ (e.key === 'Enter' || e.key === 'NumpadEnter')
+ ) {
+ e.preventDefault()
+ e.currentTarget.form?.requestSubmit()
+ }
+ }
+
+ return (
+
+
+
+ )
+}
+```
+
+```jsx filename="app/entry.js" switcher
+'use client'
+
+export function Entry() {
+ const handleKeyDown = (e) => {
+ if (
+ (e.ctrlKey || e.metaKey) &&
+ (e.key === 'Enter' || e.key === 'NumpadEnter')
+ ) {
+ e.preventDefault()
+ e.currentTarget.form?.requestSubmit()
+ }
+ }
+
+ return (
+
+
+
+ )
+}
+```
+
+Cela déclenchera la soumission du `` ancêtre le plus proche, ce qui invoquera la fonction serveur.
\ No newline at end of file
diff --git a/apps/docs/content/fr/docs/01-app/02-guides/incremental-static-regeneration.mdx b/apps/docs/content/fr/docs/01-app/02-guides/incremental-static-regeneration.mdx
new file mode 100644
index 00000000..5fa84f3a
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/02-guides/incremental-static-regeneration.mdx
@@ -0,0 +1,614 @@
+---
+source-updated-at: 2025-06-01T01:32:20.000Z
+translation-updated-at: 2025-06-02T20:02:45.750Z
+title: Comment implémenter la régénération statique incrémentielle (ISR)
+nav_title: ISR
+description: Apprenez à créer ou mettre à jour des pages statiques au moment de l'exécution avec la régénération statique incrémentielle.
+---
+
+
+ Exemples
+
+- [Next.js Commerce](https://vercel.com/templates/next.js/nextjs-commerce)
+- [ISR à la demande](https://on-demand-isr.vercel.app)
+- [Next.js Forms](https://github.com/vercel/next.js/tree/canary/examples/next-forms)
+
+
+
+La régénération statique incrémentielle (ISR) vous permet de :
+
+- Mettre à jour du contenu statique sans reconstruire tout le site
+- Réduire la charge du serveur en servant des pages statiques pré-rendues pour la plupart des requêtes
+- Garantir que les en-têtes `cache-control` appropriés sont automatiquement ajoutés aux pages
+- Gérer un grand nombre de pages de contenu sans temps de construction `next build` longs
+
+Voici un exemple minimal :
+
+
+
+```tsx filename="app/blog/[id]/page.tsx" switcher
+interface Post {
+ id: string
+ title: string
+ content: string
+}
+
+// Next.js invalidera le cache lorsqu'une
+// requête arrive, au maximum une fois toutes les 60 secondes.
+export const revalidate = 60
+
+// Nous pré-rendrons uniquement les paramètres de `generateStaticParams` au moment de la construction.
+// Si une requête arrive pour un chemin qui n'a pas été généré,
+// Next.js rendra la page côté serveur à la demande.
+export const dynamicParams = true // ou false, pour renvoyer une 404 sur les chemins inconnus
+
+export async function generateStaticParams() {
+ const posts: Post[] = await fetch('https://api.vercel.app/blog').then((res) =>
+ res.json()
+ )
+ return posts.map((post) => ({
+ id: String(post.id),
+ }))
+}
+
+export default async function Page({
+ params,
+}: {
+ params: Promise<{ id: string }>
+}) {
+ const { id } = await params
+ const post: Post = await fetch(`https://api.vercel.app/blog/${id}`).then(
+ (res) => res.json()
+ )
+ return (
+
+ {post.title}
+ {post.content}
+
+ )
+}
+```
+
+```jsx filename="app/blog/[id]/page.js" switcher
+// Next.js invalidera le cache lorsqu'une
+// requête arrive, au maximum une fois toutes les 60 secondes.
+export const revalidate = 60
+
+// Nous pré-rendrons uniquement les paramètres de `generateStaticParams` au moment de la construction.
+// Si une requête arrive pour un chemin qui n'a pas été généré,
+// Next.js rendra la page côté serveur à la demande.
+export const dynamicParams = true // ou false, pour renvoyer une 404 sur les chemins inconnus
+
+export async function generateStaticParams() {
+ const posts = await fetch('https://api.vercel.app/blog').then((res) =>
+ res.json()
+ )
+ return posts.map((post) => ({
+ id: String(post.id),
+ }))
+}
+
+export default async function Page({ params }) {
+ const { id } = await params
+ const post = await fetch(`https://api.vercel.app/blog/${id}`).then((res) =>
+ res.json()
+ )
+ return (
+
+ {post.title}
+ {post.content}
+
+ )
+}
+```
+
+
+
+
+
+```tsx filename="pages/blog/[id].tsx" switcher
+import type { GetStaticPaths, GetStaticProps } from 'next'
+
+interface Post {
+ id: string
+ title: string
+ content: string
+}
+
+interface Props {
+ post: Post
+}
+
+export const getStaticPaths: GetStaticPaths = async () => {
+ const posts = await fetch('https://api.vercel.app/blog').then((res) =>
+ res.json()
+ )
+ const paths = posts.map((post: Post) => ({
+ params: { id: String(post.id) },
+ }))
+
+ // Nous pré-rendrons uniquement ces chemins au moment de la construction.
+ // { fallback: 'blocking' } rendra les pages côté serveur
+ // à la demande si le chemin n'existe pas.
+ return { paths, fallback: false }
+}
+
+export const getStaticProps: GetStaticProps = async ({
+ params,
+}: {
+ params: { id: string }
+}) => {
+ const post = await fetch(`https://api.vercel.app/blog/${params.id}`).then(
+ (res) => res.json()
+ )
+
+ return {
+ props: { post },
+ // Next.js invalidera le cache lorsqu'une
+ // requête arrive, au maximum une fois toutes les 60 secondes.
+ revalidate: 60,
+ }
+}
+
+export default function Page({ post }: Props) {
+ return (
+
+ {post.title}
+ {post.content}
+
+ )
+}
+```
+
+```jsx filename="pages/blog/[id].jsx" switcher
+export async function getStaticPaths() {
+ const posts = await fetch('https://api.vercel.app/blog').then((res) =>
+ res.json()
+ )
+ const paths = posts.map((post) => ({
+ params: { id: post.id },
+ }))
+
+ // Nous pré-rendrons uniquement ces chemins au moment de la construction.
+ // { fallback: false } signifie que les autres routes renverront une 404.
+ return { paths, fallback: false }
+}
+
+export async function getStaticProps({ params }) {
+ const post = await fetch(`https://api.vercel.app/blog/${params.id}`).then(
+ (res) => res.json()
+ )
+
+ return {
+ props: { post },
+ // Next.js invalidera le cache lorsqu'une
+ // requête arrive, au maximum une fois toutes les 60 secondes.
+ revalidate: 60,
+ }
+}
+
+export default function Page({ post }) {
+ return (
+
+ {post.title}
+ {post.content}
+
+ )
+}
+```
+
+
+
+Voici comment cet exemple fonctionne :
+
+1. Pendant `next build`, tous les articles de blog connus sont générés (il y en a 25 dans cet exemple)
+2. Toutes les requêtes vers ces pages (par exemple `/blog/1`) sont mises en cache et instantanées
+3. Après 60 secondes écoulées, la prochaine requête affichera toujours la page mise en cache (obsolète)
+4. Le cache est invalidé et une nouvelle version de la page commence à être générée en arrière-plan
+5. Une fois générée avec succès, Next.js affichera et mettra en cache la page mise à jour
+6. Si `/blog/26` est demandé, Next.js générera et mettra en cache cette page à la demande
+
+## Référence
+
+
+
+### Configuration du segment de route
+
+- [`revalidate`](/docs/app/api-reference/file-conventions/route-segment-config#revalidate)
+- [`dynamicParams`](/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams)
+
+### Fonctions
+
+- [`revalidatePath`](/docs/app/api-reference/functions/revalidatePath)
+- [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag)
+
+
+
+
+
+### Fonctions
+
+- [`getStaticProps`](/docs/pages/building-your-application/data-fetching/get-static-props)
+- [`res.revalidate`](/docs/pages/building-your-application/routing/api-routes#response-helpers)
+
+
+
+## Exemples
+
+
+
+### Revalidation basée sur le temps
+
+Ceci récupère et affiche une liste d'articles de blog sur `/blog`. Après une heure, le cache pour cette page est invalidé lors de la prochaine visite. Ensuite, en arrière-plan, une nouvelle version de la page est générée avec les derniers articles de blog.
+
+```tsx filename="app/blog/page.tsx" switcher
+interface Post {
+ id: string
+ title: string
+ content: string
+}
+
+export const revalidate = 3600 // invalider toutes les heures
+
+export default async function Page() {
+ const data = await fetch('https://api.vercel.app/blog')
+ const posts: Post[] = await data.json()
+ return (
+
+ Articles de blog
+
+ {posts.map((post) => (
+ {post.title}
+ ))}
+
+
+ )
+}
+```
+
+```jsx filename="app/blog/page.js" switcher
+export const revalidate = 3600 // invalider toutes les heures
+
+export default async function Page() {
+ const data = await fetch('https://api.vercel.app/blog')
+ const posts = await data.json()
+ return (
+
+ Articles de blog
+
+ {posts.map((post) => (
+ {post.title}
+ ))}
+
+
+ )
+}
+```
+
+Nous recommandons de définir un temps de revalidation élevé. Par exemple, 1 heure au lieu de 1 seconde. Si vous avez besoin de plus de précision, envisagez d'utiliser la revalidation à la demande. Si vous avez besoin de données en temps réel, envisagez de passer au [rendu dynamique](/docs/app/getting-started/partial-prerendering#dynamic-rendering).
+
+### Revalidation à la demande avec `revalidatePath`
+
+Pour une méthode plus précise de revalidation, invalidez les pages à la demande avec la fonction `revalidatePath`.
+
+Par exemple, cette Action Serveur serait appelée après l'ajout d'un nouvel article. Peu importe comment vous récupérez vos données dans votre Composant Serveur, que ce soit en utilisant `fetch` ou en vous connectant à une base de données, cela effacera le cache pour toute la route et permettra au Composant Serveur de récupérer des données fraîches.
+
+```ts filename="app/actions.ts" switcher
+'use server'
+
+import { revalidatePath } from 'next/cache'
+
+export async function createPost() {
+ // Invalider la route /posts dans le cache
+ revalidatePath('/posts')
+}
+```
+
+```js filename="app/actions.js" switcher
+'use server'
+
+import { revalidatePath } from 'next/cache'
+
+export async function createPost() {
+ // Invalider la route /posts dans le cache
+ revalidatePath('/posts')
+}
+```
+
+[Voir une démo](https://on-demand-isr.vercel.app) et [explorer le code source](https://github.com/vercel/on-demand-isr).
+
+### Revalidation à la demande avec `revalidateTag`
+
+Pour la plupart des cas d'usage, préférez invalider des chemins entiers. Si vous avez besoin d'un contrôle plus granulaire, vous pouvez utiliser la fonction `revalidateTag`. Par exemple, vous pouvez taguer des appels `fetch` individuels :
+
+```tsx filename="app/blog/page.tsx" switcher
+export default async function Page() {
+ const data = await fetch('https://api.vercel.app/blog', {
+ next: { tags: ['posts'] },
+ })
+ const posts = await data.json()
+ // ...
+}
+```
+
+```jsx filename="app/blog/page.js" switcher
+export default async function Page() {
+ const data = await fetch('https://api.vercel.app/blog', {
+ next: { tags: ['posts'] },
+ })
+ const posts = await data.json()
+ // ...
+}
+```
+
+Si vous utilisez un ORM ou vous connectez à une base de données, vous pouvez utiliser `unstable_cache` :
+
+```tsx filename="app/blog/page.tsx" switcher
+import { unstable_cache } from 'next/cache'
+import { db, posts } from '@/lib/db'
+
+const getCachedPosts = unstable_cache(
+ async () => {
+ return await db.select().from(posts)
+ },
+ ['posts'],
+ { revalidate: 3600, tags: ['posts'] }
+)
+
+export default async function Page() {
+ const posts = getCachedPosts()
+ // ...
+}
+```
+
+```jsx filename="app/blog/page.js" switcher
+import { unstable_cache } from 'next/cache'
+import { db, posts } from '@/lib/db'
+
+const getCachedPosts = unstable_cache(
+ async () => {
+ return await db.select().from(posts)
+ },
+ ['posts'],
+ { revalidate: 3600, tags: ['posts'] }
+)
+
+export default async function Page() {
+ const posts = getCachedPosts()
+ // ...
+}
+```
+
+Vous pouvez ensuite utiliser `revalidateTag` dans une [Action Serveur](/docs/app/building-your-application/data-fetching/server-actions-and-mutations) ou un [Gestionnaire de Route](/docs/app/building-your-application/routing/route-handlers) :
+
+```ts filename="app/actions.ts" switcher
+'use server'
+
+import { revalidateTag } from 'next/cache'
+
+export async function createPost() {
+ // Invalider toutes les données taguées avec 'posts' dans le cache
+ revalidateTag('posts')
+}
+```
+
+```js filename="app/actions.js" switcher
+'use server'
+
+import { revalidateTag } from 'next/cache'
+
+export async function createPost() {
+ // Invalider toutes les données taguées avec 'posts' dans le cache
+ revalidateTag('posts')
+}
+```
+
+
+
+
+
+### Validation à la demande avec `res.revalidate()`
+
+Pour une méthode plus précise de revalidation, utilisez `res.revalidate` pour générer une nouvelle page à la demande depuis un Routeur API.
+
+Par exemple, cette Route API peut être appelée à `/api/revalidate?secret=` pour revalider un article de blog donné. Créez un token secret connu uniquement par votre application Next.js. Ce secret sera utilisé pour empêcher un accès non autorisé à la Route API de revalidation.
+
+```ts filename="pages/api/revalidate.ts" switcher
+import type { NextApiRequest, NextApiResponse } from 'next'
+
+export default async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse
+) {
+ // Vérifier le secret pour confirmer que c'est une requête valide
+ if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
+ return res.status(401).json({ message: 'Token invalide' })
+ }
+
+ try {
+ // Ce devrait être le chemin réel et non un chemin réécrit
+ // par exemple pour "/posts/[id]" ce devrait être "/posts/1"
+ await res.revalidate('/posts/1')
+ return res.json({ revalidated: true })
+ } catch (err) {
+ // S'il y a une erreur, Next.js continuera
+ // à afficher la dernière page générée avec succès
+ return res.status(500).send('Erreur lors de la revalidation')
+ }
+}
+```
+
+```js filename="pages/api/revalidate.js" switcher
+export default async function handler(req, res) {
+ // Vérifier le secret pour confirmer que c'est une requête valide
+ if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
+ return res.status(401).json({ message: 'Token invalide' })
+ }
+
+ try {
+ // Ce devrait être le chemin réel et non un chemin réécrit
+ // par exemple pour "/posts/[id]" ce devrait être "/posts/1"
+ await res.revalidate('/posts/1')
+ return res.json({ revalidated: true })
+ } catch (err) {
+ // S'il y a une erreur, Next.js continuera
+ // à afficher la dernière page générée avec succès
+ return res.status(500).send('Erreur lors de la revalidation')
+ }
+}
+```
+
+Si vous utilisez la revalidation à la demande, vous n'avez pas besoin de spécifier un temps `revalidate` dans `getStaticProps`. Next.js utilisera la valeur par défaut `false` (pas de revalidation) et ne revalidera la page qu'à la demande lorsque `res.revalidate()` est appelé.
+
+
+
+### Gestion des exceptions non capturées
+
+
+
+Si une erreur est levée lors de la tentative de revalidation des données, les dernières données générées avec succès continueront d'être servies depuis le cache. Lors de la prochaine requête, Next.js réessayera de revalider les données. [En savoir plus sur la gestion des erreurs](/docs/app/getting-started/error-handling).
+
+
+
+
+
+S'il y a une erreur dans `getStaticProps` lors de la régénération en arrière-plan, ou si vous lancez une erreur manuellement, la dernière page générée avec succès continuera d'être affichée. Lors de la prochaine requête, Next.js réessayera d'appeler `getStaticProps`.
+
+```tsx filename="pages/blog/[id].tsx" switcher
+import type { GetStaticProps } from 'next'
+
+interface Post {
+ id: string
+ title: string
+ content: string
+}
+
+interface Props {
+ post: Post
+}
+
+export const getStaticProps: GetStaticProps = async ({
+ params,
+}: {
+ params: { id: string }
+}) => {
+ // Si cette requête lève une erreur non capturée, Next.js ne
+ // invalidera pas la page actuellement affichée et
+ // réessayera getStaticProps lors de la prochaine requête.
+ const res = await fetch(`https://api.vercel.app/blog/${params.id}`)
+ const post: Post = await res.json()
+
+ if (!res.ok) {
+ // S'il y a une erreur serveur, vous pourriez vouloir
+ // lever une erreur au lieu de retourner pour que le cache ne soit pas mis à jour
+ // jusqu'à la prochaine requête réussie.
+ throw new Error(`Échec de la récupération des articles, statut reçu ${res.status}`)
+ }
+
+ return {
+ props: { post },
+ // Next.js invalidera le cache lorsqu'une
+ // requête arrive, au maximum une fois toutes les 60 secondes.
+ revalidate: 60,
+ }
+}
+```
+
+```jsx filename="pages/blog/[id].jsx" switcher
+export async function getStaticProps({ params }) {
+ // Si cette requête lève une erreur non capturée, Next.js ne
+ // invalidera pas la page actuellement affichée et
+ // réessayera getStaticProps lors de la prochaine requête.
+ const res = await fetch(`https://api.vercel.app/blog/${params.id}`)
+ const post = await res.json()
+
+ if (!res.ok) {
+ // S'il y a une erreur serveur, vous pourriez vouloir
+ // lever une erreur au lieu de retourner pour que le cache ne soit pas mis à jour
+ // jusqu'à la prochaine requête réussie.
+ throw new Error(`Échec de la récupération des articles, statut reçu ${res.status}`)
+ }
+
+ return {
+ props: { post },
+ // Next.js invalidera le cache lorsqu'une
+ // requête arrive, au maximum une fois toutes les 60 secondes.
+ revalidate: 60,
+ }
+}
+```
+
+
+
+### Personnalisation de l'emplacement du cache
+
+Vous pouvez configurer l'emplacement du cache Next.js si vous souhaitez persister les pages et données mises en cache vers un stockage durable, ou partager le cache entre plusieurs conteneurs ou instances de votre application Next.js. [En savoir plus](/docs/app/guides/self-hosting#caching-and-isr).
+
+## Dépannage
+
+### Débogage des données mises en cache en développement local
+
+Si vous utilisez l'API `fetch`, vous pouvez ajouter des logs supplémentaires pour comprendre quelles requêtes sont mises en cache ou non. [En savoir plus sur l'option `logging`](/docs/app/api-reference/config/next-config-js/logging).
+
+```jsx filename="next.config.js"
+module.exports = {
+ logging: {
+ fetches: {
+ fullUrl: true,
+ },
+ },
+}
+```
+
+### Vérification du comportement en production
+
+Pour vérifier que vos pages sont correctement mises en cache et revalidées en production, vous pouvez tester localement en exécutant `next build` puis `next start` pour lancer le serveur Next.js en mode production.
+
+Cela vous permettra de tester le comportement de l'ISR (Incremental Static Regeneration) tel qu'il fonctionnerait dans un environnement de production. Pour un débogage approfondi, ajoutez la variable d'environnement suivante à votre fichier `.env` :
+
+```bash filename=".env"
+NEXT_PRIVATE_DEBUG_CACHE=1
+```
+
+Cela fera en sorte que le serveur Next.js enregistre dans la console les succès et les échecs du cache ISR. Vous pouvez inspecter la sortie pour voir quelles pages sont générées lors de `next build`, ainsi que comment les pages sont mises à jour lorsque les chemins sont accédés à la demande.
+
+## Mises en garde
+
+
+
+- L'ISR n'est pris en charge que lors de l'utilisation du runtime Node.js (par défaut).
+- L'ISR n'est pas pris en charge lors de la création d'une [exportation statique](/docs/app/guides/static-exports).
+- Si vous avez plusieurs requêtes `fetch` dans une route rendue statiquement et que chacune a une fréquence de `revalidate` différente, le temps le plus court sera utilisé pour l'ISR. Cependant, ces fréquences de revalidation seront toujours respectées par le [cache de données](/docs/app/deep-dive/caching#data-cache).
+- Si l'une des requêtes `fetch` utilisées sur une route a un temps de `revalidate` de `0` ou un `no-store` explicite, la route sera [rendue dynamiquement](/docs/app/getting-started/partial-prerendering#dynamic-rendering).
+- Le middleware ne sera pas exécuté pour les requêtes ISR à la demande, ce qui signifie que toute réécriture de chemin ou logique dans le middleware ne sera pas appliquée. Assurez-vous de revalider le chemin exact. Par exemple, `/post/1` au lieu d'un chemin réécrit `/post-1`.
+
+
+
+
+
+- L'ISR n'est pris en charge que lors de l'utilisation du runtime Node.js (par défaut).
+- L'ISR n'est pas pris en charge lors de la création d'une [exportation statique](/docs/app/guides/static-exports).
+- Le middleware ne sera pas exécuté pour les requêtes ISR à la demande, ce qui signifie que toute réécriture de chemin ou logique dans le middleware ne sera pas appliquée. Assurez-vous de revalider le chemin exact. Par exemple, `/post/1` au lieu d'un chemin réécrit `/post-1`.
+
+
+
+## Prise en charge par les plateformes
+
+| Option de déploiement | Pris en charge |
+| ------------------------------------------------------------------- | ------------------ |
+| [Serveur Node.js](/docs/app/getting-started/deploying#nodejs-server) | Oui |
+| [Conteneur Docker](/docs/app/getting-started/deploying#docker) | Oui |
+| [Exportation statique](/docs/app/getting-started/deploying#static-export) | Non |
+| [Adaptateurs](/docs/app/getting-started/deploying#adapters) | Dépend de la plateforme |
+
+Apprenez comment [configurer l'ISR](/docs/app/guides/self-hosting#caching-and-isr) lors de l'hébergement autonome de Next.js.
+
+## Historique des versions
+
+| Version | Modifications |
+| --------- | ---------------------------------------------------------------------------------------- |
+| `v14.1.0` | Le `cacheHandler` personnalisé est stable. |
+| `v13.0.0` | L'App Router est introduit. |
+| `v12.2.0` | Pages Router : L'ISR à la demande est stable |
+| `v12.0.0` | Pages Router : [ISR avec prise en compte des bots](/blog/next-12#bot-aware-isr-fallback) ajouté. |
+| `v9.5.0` | Pages Router : [ISR stable introduit](/blog/next-9-5). |
diff --git a/apps/docs/content/fr/docs/01-app/02-guides/index.mdx b/apps/docs/content/fr/docs/01-app/02-guides/index.mdx
new file mode 100644
index 00000000..72e51f2e
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/02-guides/index.mdx
@@ -0,0 +1,65 @@
+---
+source-updated-at: 2025-06-01T01:32:20.000Z
+translation-updated-at: 2025-06-02T19:59:29.804Z
+title: Guides
+description: Apprenez à implémenter des modèles d'interface utilisateur courants et des cas d'utilisation avec Next.js
+---
+
+### Récupération de données
+
+- [Utilisation de l'API `fetch`](/docs/app/getting-started/fetching-data#with-the-fetch-api)
+- [Utilisation d'un ORM ou d'un client de base de données](/docs/app/getting-started/fetching-data#with-an-orm-or-database)
+- [Lecture des paramètres de recherche côté serveur](/docs/app/api-reference/file-conventions/page)
+- [Lecture des paramètres de recherche côté client](/docs/app/api-reference/functions/use-search-params)
+
+### Révalidation des données
+
+- [Utilisation de l'ISR pour révalider les données après un certain temps](/docs/app/guides/incremental-static-regeneration#time-based-revalidation)
+- [Utilisation de l'ISR pour révalider les données à la demande](/docs/app/guides/incremental-static-regeneration#on-demand-revalidation-with-revalidatepath)
+
+### Formulaires
+
+- [Affichage d'un état pendant la soumission d'un formulaire](/docs/app/guides/forms)
+- [Validation côté serveur des formulaires](/docs/app/guides/forms)
+- [Gestion des erreurs attendues](/docs/app/getting-started/error-handling#handling-expected-errors)
+- [Gestion des exceptions inattendues](/docs/app/getting-started/error-handling#handling-uncaught-exceptions)
+- [Affichage des mises à jour optimistes de l'interface](/docs/app/guides/forms#optimistic-updates)
+- [Soumission programmatique de formulaires](/docs/app/guides/forms#programmatic-form-submission)
+
+### Actions serveur
+
+- [Passage de valeurs supplémentaires](/docs/app/guides/forms)
+- [Révalidation des données](/docs/app/building-your-application/data-fetching/server-actions-and-mutations#revalidating-data)
+- [Redirection](/docs/app/building-your-application/data-fetching/server-actions-and-mutations#redirecting)
+- [Définition de cookies](/docs/app/api-reference/functions/cookies#setting-a-cookie)
+- [Suppression de cookies](/docs/app/api-reference/functions/cookies#deleting-cookies)
+
+### Métadonnées
+
+- [Création d'un flux RSS](/docs/app/building-your-application/routing/route-handlers#non-ui-responses)
+- [Création d'une image Open Graph](/docs/app/api-reference/file-conventions/metadata/opengraph-image)
+- [Création d'un sitemap](/docs/app/api-reference/file-conventions/metadata/sitemap)
+- [Création d'un fichier robots.txt](/docs/app/api-reference/file-conventions/metadata/robots)
+- [Création d'une page 404 personnalisée](/docs/app/api-reference/file-conventions/not-found)
+- [Création d'une page 500 personnalisée](/docs/app/api-reference/file-conventions/error)
+
+### Authentification
+
+- [Création d'un formulaire d'inscription](/docs/app/guides/authentication#sign-up-and-login-functionality)
+- [Gestion de session sans état basée sur les cookies](/docs/app/guides/authentication#stateless-sessions)
+- [Gestion de session avec état basée sur une base de données](/docs/app/guides/authentication#database-sessions)
+- [Gestion des autorisations](/docs/app/guides/authentication#authorization)
+
+### Tests
+
+- [Vitest](/docs/app/guides/testing/vitest)
+- [Jest](/docs/app/guides/testing/jest)
+- [Playwright](/docs/app/guides/testing/playwright)
+- [Cypress](/docs/app/guides/testing/cypress)
+
+### Déploiement
+
+- [Création d'un Dockerfile](/docs/app/getting-started/deploying#docker)
+- [Création d'une exportation statique (SPA)](/docs/app/guides/static-exports)
+- [Configuration du cache en auto-hébergement](/docs/app/guides/self-hosting#configuring-caching)
+- [Configuration de l'optimisation d'images en auto-hébergement](/docs/app/guides/self-hosting#image-optimization)
\ No newline at end of file
diff --git a/apps/docs/content/fr/docs/01-app/02-guides/instrumentation.mdx b/apps/docs/content/fr/docs/01-app/02-guides/instrumentation.mdx
new file mode 100644
index 00000000..1d2e3026
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/02-guides/instrumentation.mdx
@@ -0,0 +1,98 @@
+---
+source-updated-at: 2025-05-19T22:31:51.000Z
+translation-updated-at: 2025-06-02T19:59:38.520Z
+title: Comment configurer l'instrumentation
+nav_title: Instrumentation
+description: Apprenez à utiliser l'instrumentation pour exécuter du code au démarrage du serveur dans votre application Next.js
+related:
+ title: En savoir plus sur l'Instrumentation
+ links:
+ - app/api-reference/file-conventions/instrumentation
+---
+
+{/* Le contenu de ce document est partagé entre le routeur App et Pages. Vous pouvez utiliser le composant `Contenu ` pour ajouter du contenu spécifique au routeur Pages. Tout contenu partagé ne doit pas être encapsulé dans un composant. */}
+
+L'instrumentation est le processus d'utilisation de code pour intégrer des outils de surveillance et de journalisation dans votre application. Cela vous permet de suivre les performances et le comportement de votre application, et de déboguer des problèmes en production.
+
+## Convention
+
+Pour configurer l'instrumentation, créez un fichier `instrumentation.ts|js` dans le **répertoire racine** de votre projet (ou dans le dossier [`src`](/docs/app/api-reference/file-conventions/src-folder) si vous en utilisez un).
+
+Ensuite, exportez une fonction `register` dans ce fichier. Cette fonction sera appelée **une fois** lorsqu'une nouvelle instance du serveur Next.js sera initialisée.
+
+Par exemple, pour utiliser Next.js avec [OpenTelemetry](https://opentelemetry.io/) et [@vercel/otel](https://vercel.com/docs/observability/otel-overview) :
+
+```ts filename="instrumentation.ts" switcher
+import { registerOTel } from '@vercel/otel'
+
+export function register() {
+ registerOTel('next-app')
+}
+```
+
+```js filename="instrumentation.js" switcher
+import { registerOTel } from '@vercel/otel'
+
+export function register() {
+ registerOTel('next-app')
+}
+```
+
+Consultez l'exemple [Next.js avec OpenTelemetry](https://github.com/vercel/next.js/tree/canary/examples/with-opentelemetry) pour une implémentation complète.
+
+> **Bon à savoir** :
+>
+> - Le fichier `instrumentation` doit se trouver à la racine de votre projet et non dans les répertoires `app` ou `pages`. Si vous utilisez le dossier `src`, placez le fichier dans `src` à côté de `pages` et `app`.
+> - Si vous utilisez l'option de configuration [`pageExtensions`](/docs/app/api-reference/config/next-config-js/pageExtensions) pour ajouter un suffixe, vous devrez également mettre à jour le nom du fichier `instrumentation` pour correspondre.
+
+## Exemples
+
+### Importer des fichiers avec effets de bord
+
+Parfois, il peut être utile d'importer un fichier dans votre code en raison des effets de bord qu'il provoque. Par exemple, vous pourriez importer un fichier qui définit un ensemble de variables globales, sans jamais utiliser explicitement le fichier importé dans votre code. Vous auriez tout de même accès aux variables globales déclarées par le package.
+
+Nous recommandons d'importer les fichiers en utilisant la syntaxe JavaScript `import` dans votre fonction `register`. L'exemple suivant démontre une utilisation basique de `import` dans une fonction `register` :
+
+```ts filename="instrumentation.ts" switcher
+export async function register() {
+ await import('package-with-side-effect')
+}
+```
+
+```js filename="instrumentation.js" switcher
+export async function register() {
+ await import('package-with-side-effect')
+}
+```
+
+> **Bon à savoir :**
+>
+> Nous recommandons d'importer le fichier depuis la fonction `register`, plutôt qu'en haut du fichier. En procédant ainsi, vous pouvez regrouper tous vos effets de bord en un seul endroit dans votre code, et éviter toute conséquence involontaire due à une importation globale en haut du fichier.
+
+### Importer du code spécifique à l'environnement d'exécution
+
+Next.js appelle `register` dans tous les environnements, il est donc important d'importer conditionnellement tout code qui ne prend pas en charge des environnements d'exécution spécifiques (par exemple [Edge ou Node.js](/docs/app/api-reference/edge)). Vous pouvez utiliser la variable d'environnement `NEXT_RUNTIME` pour obtenir l'environnement actuel :
+
+```ts filename="instrumentation.ts" switcher
+export async function register() {
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
+ await import('./instrumentation-node')
+ }
+
+ if (process.env.NEXT_RUNTIME === 'edge') {
+ await import('./instrumentation-edge')
+ }
+}
+```
+
+```js filename="instrumentation.js" switcher
+export async function register() {
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
+ await import('./instrumentation-node')
+ }
+
+ if (process.env.NEXT_RUNTIME === 'edge') {
+ await import('./instrumentation-edge')
+ }
+}
+```
\ No newline at end of file
diff --git a/apps/docs/content/fr/docs/01-app/02-guides/internationalization.mdx b/apps/docs/content/fr/docs/01-app/02-guides/internationalization.mdx
new file mode 100644
index 00000000..70202eb4
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/02-guides/internationalization.mdx
@@ -0,0 +1,218 @@
+---
+source-updated-at: 2025-05-21T18:33:43.000Z
+translation-updated-at: 2025-06-02T20:00:07.005Z
+title: Internationalisation
+description: Ajoutez la prise en charge de plusieurs langues avec un routage internationalisé et un contenu localisé.
+---
+
+Next.js vous permet de configurer le routage et le rendu du contenu pour prendre en charge plusieurs langues. Adapter votre site à différentes locales inclut du contenu traduit (localisation) et des routes internationalisées.
+
+## Terminologie
+
+- **Locale :** Un identifiant pour un ensemble de préférences linguistiques et de format. Cela inclut généralement la langue préférée de l'utilisateur et éventuellement sa région géographique.
+ - `en-US` : Anglais tel que parlé aux États-Unis
+ - `nl-NL` : Néerlandais tel que parlé aux Pays-Bas
+ - `nl` : Néerlandais, sans région spécifique
+
+## Aperçu du routage
+
+Il est recommandé d'utiliser les préférences linguistiques du navigateur pour sélectionner la locale à utiliser. Changer votre langue préférée modifiera l'en-tête `Accept-Language` envoyé à votre application.
+
+Par exemple, en utilisant les bibliothèques suivantes, vous pouvez examiner une `Request` entrante pour déterminer quelle locale sélectionner, en fonction des `Headers`, des locales que vous prévoyez de supporter et de la locale par défaut.
+
+```js filename="middleware.js"
+import { match } from '@formatjs/intl-localematcher'
+import Negotiator from 'negotiator'
+
+let headers = { 'accept-language': 'en-US,en;q=0.5' }
+let languages = new Negotiator({ headers }).languages()
+let locales = ['en-US', 'nl-NL', 'nl']
+let defaultLocale = 'en-US'
+
+match(languages, locales, defaultLocale) // -> 'en-US'
+```
+
+Le routage peut être internationalisé soit par sous-chemin (`/fr/products`), soit par domaine (`my-site.fr/products`). Avec ces informations, vous pouvez maintenant rediriger l'utilisateur en fonction de la locale dans le [Middleware](/docs/app/building-your-application/routing/middleware).
+
+```js filename="middleware.js"
+import { NextResponse } from "next/server";
+
+let locales = ['en-US', 'nl-NL', 'nl']
+
+// Obtenir la locale préférée, similaire à l'exemple ci-dessus ou en utilisant une bibliothèque
+function getLocale(request) { ... }
+
+export function middleware(request) {
+ // Vérifier si un chemin contient une locale supportée
+ const { pathname } = request.nextUrl
+ const pathnameHasLocale = locales.some(
+ (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
+ )
+
+ if (pathnameHasLocale) return
+
+ // Rediriger s'il n'y a pas de locale
+ const locale = getLocale(request)
+ request.nextUrl.pathname = `/${locale}${pathname}`
+ // Par exemple, une requête entrante /products
+ // La nouvelle URL est maintenant /en-US/products
+ return NextResponse.redirect(request.nextUrl)
+}
+
+export const config = {
+ matcher: [
+ // Ignorer tous les chemins internes (_next)
+ '/((?!_next).*)',
+ // Optionnel : n'exécuter que sur l'URL racine (/)
+ // '/'
+ ],
+}
+```
+
+Enfin, assurez-vous que tous les fichiers spéciaux dans `app/` sont imbriqués sous `app/[lang]`. Cela permet au routeur Next.js de gérer dynamiquement différentes locales dans la route et de transmettre le paramètre `lang` à chaque layout et page. Par exemple :
+
+```tsx filename="app/[lang]/page.tsx" switcher
+// Vous avez maintenant accès à la locale courante
+// Par exemple, /en-US/products -> `lang` est "en-US"
+export default async function Page({
+ params,
+}: {
+ params: Promise<{ lang: string }>
+}) {
+ const { lang } = await params
+ return ...
+}
+```
+
+```jsx filename="app/[lang]/page.js" switcher
+// Vous avez maintenant accès à la locale courante
+// Par exemple, /en-US/products -> `lang` est "en-US"
+export default async function Page({ params }) {
+ const { lang } = await params
+ return ...
+}
+```
+
+Le layout racine peut également être imbriqué dans le nouveau dossier (par exemple `app/[lang]/layout.js`).
+
+## Localisation
+
+Changer le contenu affiché en fonction de la locale préférée de l'utilisateur, ou localisation, n'est pas spécifique à Next.js. Les modèles décrits ci-dessous fonctionneraient de la même manière avec n'importe quelle application web.
+
+Supposons que nous voulions prendre en charge du contenu en anglais et en néerlandais dans notre application. Nous pourrions maintenir deux "dictionnaires" différents, qui sont des objets nous donnant une correspondance entre une clé et une chaîne localisée. Par exemple :
+
+```json filename="dictionaries/en.json"
+{
+ "products": {
+ "cart": "Add to Cart"
+ }
+}
+```
+
+```json filename="dictionaries/nl.json"
+{
+ "products": {
+ "cart": "Toevoegen aan Winkelwagen"
+ }
+}
+```
+
+Nous pouvons ensuite créer une fonction `getDictionary` pour charger les traductions pour la locale demandée :
+
+```ts filename="app/[lang]/dictionaries.ts" switcher
+import 'server-only'
+
+const dictionaries = {
+ en: () => import('./dictionaries/en.json').then((module) => module.default),
+ nl: () => import('./dictionaries/nl.json').then((module) => module.default),
+}
+
+export const getDictionary = async (locale: 'en' | 'nl') =>
+ dictionaries[locale]()
+```
+
+```js filename="app/[lang]/dictionaries.js" switcher
+import 'server-only'
+
+const dictionaries = {
+ en: () => import('./dictionaries/en.json').then((module) => module.default),
+ nl: () => import('./dictionaries/nl.json').then((module) => module.default),
+}
+
+export const getDictionary = async (locale) => dictionaries[locale]()
+```
+
+Étant donné la langue actuellement sélectionnée, nous pouvons récupérer le dictionnaire dans un layout ou une page.
+
+```tsx filename="app/[lang]/page.tsx" switcher
+import { getDictionary } from './dictionaries'
+
+export default async function Page({
+ params,
+}: {
+ params: Promise<{ lang: 'en' | 'nl' }>
+}) {
+ const { lang } = await params
+ const dict = await getDictionary(lang) // en
+ return {dict.products.cart} // Add to Cart
+}
+```
+
+```jsx filename="app/[lang]/page.js" switcher
+import { getDictionary } from './dictionaries'
+
+export default async function Page({ params }) {
+ const { lang } = await params
+ const dict = await getDictionary(lang) // en
+ return {dict.products.cart} // Add to Cart
+}
+```
+
+Comme tous les layouts et pages dans le répertoire `app/` sont par défaut des [Composants Serveur](/docs/app/getting-started/server-and-client-components), nous n'avons pas à nous soucier de la taille des fichiers de traduction affectant la taille de notre bundle JavaScript côté client. Ce code ne s'exécutera **que sur le serveur**, et seul le HTML résultant sera envoyé au navigateur.
+
+## Rendu statique
+
+Pour générer des routes statiques pour un ensemble donné de locales, nous pouvons utiliser `generateStaticParams` avec n'importe quelle page ou layout. Cela peut être global, par exemple dans le layout racine :
+
+```tsx filename="app/[lang]/layout.tsx" switcher
+export async function generateStaticParams() {
+ return [{ lang: 'en-US' }, { lang: 'de' }]
+}
+
+export default async function RootLayout({
+ children,
+ params,
+}: Readonly<{
+ children: React.ReactNode
+ params: Promise<{ lang: 'en-US' | 'de' }>
+}>) {
+ return (
+
+ {children}
+
+ )
+}
+```
+
+```jsx filename="app/[lang]/layout.js" switcher
+export async function generateStaticParams() {
+ return [{ lang: 'en-US' }, { lang: 'de' }]
+}
+
+export default async function RootLayout({ children, params }) {
+ return (
+
+ {children}
+
+ )
+}
+```
+
+## Ressources
+
+- [Routage i18n et traductions minimales](https://github.com/vercel/next.js/tree/canary/examples/i18n-routing)
+- [`next-intl`](https://next-intl.dev)
+- [`next-international`](https://github.com/QuiiBz/next-international)
+- [`next-i18n-router`](https://github.com/i18nexus/next-i18n-router)
+- [`paraglide-next`](https://inlang.com/m/osslbuzt/paraglide-next-i18n)
+- [`lingui`](https://lingui.dev)
\ No newline at end of file
diff --git a/apps/docs/content/fr/docs/01-app/02-guides/json-ld.mdx b/apps/docs/content/fr/docs/01-app/02-guides/json-ld.mdx
new file mode 100644
index 00000000..ed648df8
--- /dev/null
+++ b/apps/docs/content/fr/docs/01-app/02-guides/json-ld.mdx
@@ -0,0 +1,87 @@
+---
+source-updated-at: 2025-06-01T01:32:20.000Z
+translation-updated-at: 2025-06-02T19:59:17.515Z
+title: Comment implémenter JSON-LD dans votre application Next.js
+nav_title: JSON-LD
+description: Apprenez à ajouter JSON-LD à votre application Next.js pour décrire votre contenu aux moteurs de recherche et à l'IA.
+---
+
+[JSON-LD](https://json-ld.org/) est un format de données structurées qui peut être utilisé par les moteurs de recherche et l'IA pour les aider à comprendre la structure de la page au-delà du simple contenu. Par exemple, vous pouvez l'utiliser pour décrire une personne, un événement, une organisation, un film, un livre, une recette et bien d'autres types d'entités.
+
+Notre recommandation actuelle pour JSON-LD est de rendre les données structurées sous forme de balise `
+```
+
+Ou en utilisant la propriété `dangerouslySetInnerHTML` :
+
+```jsx
+
+```
+
+> **Avertissement** : Une propriété `id` doit être attribuée pour les scripts inline afin que Next.js puisse suivre et optimiser le script.
+
+### Exécution de code supplémentaire
+
+Des gestionnaires d'événements peuvent être utilisés avec le composant Script pour exécuter du code supplémentaire après qu'un certain événement se produit :
+
+- `onLoad` : Exécute du code après que le script a fini de charger.
+- `onReady` : Exécute du code après que le script a fini de charger et à chaque fois que le composant est monté.
+- `onError` : Exécute du code si le script échoue à charger.
+
+
+
+Ces gestionnaires ne fonctionneront que lorsque `next/script` est importé et utilisé à l'intérieur d'un [composant Client](/docs/app/getting-started/server-and-client-components) où `"use client"` est défini comme première ligne de code :
+
+```tsx filename="app/page.tsx" switcher
+'use client'
+
+import Script from 'next/script'
+
+export default function Page() {
+ return (
+ <>
+