# General Translation Integrations: Localiser une boutique Shopify Next.js
URL: https://generaltranslation.com/fr/docs/integrations/shopify/guides/localizing-nextjs.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Comment localiser une boutique Vercel Shop avec gt-next, des routes préfixées par paramètre régional et les Cache Components.

[Vercel Shop](https://github.com/vercel/shop) est un modèle Next.js App Router basé sur Hydrogen, le SDK de boutique de Shopify indépendant de tout framework. Avec `gt-next`, les paramètres régionaux par défaut sont servis sans préfixe, les autres paramètres régionaux sont servis sous un préfixe tel que `/fr/...`, et chaque page peut toujours être prérendue une fois par paramètre régional. Toutes les langues partagent un même marché Shopify ; pour attribuer à chaque langue son propre marché et sa propre devise, suivez ensuite le guide [Vendre sur plusieurs marchés](/docs/integrations/shopify/guides/selling-in-markets).

*Remarque : ce guide s’appuie sur le modèle avec Next.js 16, `cacheComponents: true`, `@shopify/hydrogen` 2026.10 et `gt-next` 11.4, soit les versions utilisées par la [boutique d’exemple](https://gt-shopify-vercel.vercel.app), disponible en ligne en anglais, français, japonais, coréen et indonésien.*

## Avant de commencer [#before-start]

Installez `gt-next`, ainsi que la CLI `gt` en tant que dépendance de développement, comme indiqué dans le [Démarrage rapide Next.js](/docs/react/nextjs-quickstart). Dans ce guide, le fichier `proxy.ts` déjà présent dans le modèle remplace le middleware du Démarrage rapide.

## Configurer gt-next [#configure]

### 1. Ajouter le plugin de configuration

Ajoutez [`withGTConfig`](/docs/react/nextjs/config) à la liste des plugins que le modèle transmet à `withShopConfig` :

```ts title="next.config.ts"
import { withGTConfig } from "gt-next/config";

export default withShopConfig(nextConfig, [
  (config: NextConfig) => withGTConfig(config), // [!code ++]
  shopConfig.botid.isEnabled && withBotId,
  shopConfig.agent.isEnabled && withEve,
]);
```

### 2. Ajouter les fonctions de requête

Avec Cache Components, le paramètre régional doit provenir du paramètre racine `[locale]`, et non des en-têtes ou des cookies. Créez les fichiers [`getLocale`](/docs/react/nextjs/reference/functions/get-locale) et [`getRegion`](/docs/react/nextjs/reference/functions/get-region) à la racine du projet ; `withGTConfig` les détecte. (Voir [Utiliser Cache Components](/docs/react/nextjs/cache-components#request-values).)

```ts title="getLocale.ts"
import { locale } from "next/root-params";

import { defaultLocale } from "@/lib/i18n";

// Le paramètre racine `[locale]` fait partie de la clé de cache statique : le lire permet donc aux pages de rester prérendables.
export default async function getLocale(): Promise<string> {
  return (await locale()) ?? defaultLocale;
}
```

```ts title="getRegion.ts"
import { locale } from "next/root-params";

import { defaultLocale, getCommerceLocale } from "@/lib/i18n";

// La région correspond au pays du marché Shopify dans lequel vend le paramètre régional de la page.
export default async function getRegion(): Promise<string> {
  return getCommerceLocale((await locale()) ?? defaultLocale).country;
}
```

`getCommerceLocale` associe un paramètre régional au pays et à la langue Shopify correspondants (voir [Interroger Shopify dans chaque langue](#shopify-language)).

### 3. Générer et charger les traductions localement

Les Cache Components nécessitent une fonction [`loadTranslations`](/docs/react/reference/functions/load-translations) personnalisée, car le chargement à distance n&#39;est pas fiable pendant le pré-rendu. Générez les traductions dans votre code source avec la CLI, puis importez-les depuis le bundle. (Voir [Stocker les traductions localement](/docs/react/guides/storing-translations).)

```json title="gt.config.json"
{
  "defaultLocale": "en",
  "locales": ["fr", "ja", "ko", "id"],
  "src": ["app/**/*.{ts,tsx}", "components/**/*.{ts,tsx}", "lib/**/*.{ts,tsx}"],
  "files": {
    "gt": { "output": "public/_gt/[locale].json" }
  }
}
```

* `src` ajoute `lib/`. Par défaut, la CLI n&#39;analyse que `src/`, `app/`, `pages/` et `components/`, or le modèle place les métadonnées et les libellés dans `lib/`.

```ts title="loadTranslations.ts"
// `pnpm translate` génère ces fichiers avant la compilation ; les inclure dans le bundle permet de prérendre les pages.
const translations: Record<string, () => Promise<{ default: object }>> = {
  fr: () => import("./public/_gt/fr.json"),
  ja: () => import("./public/_gt/ja.json"),
  ko: () => import("./public/_gt/ko.json"),
  id: () => import("./public/_gt/id.json"),
};

export default async function loadTranslations(locale: string): Promise<object> {
  const loaded = await translations[locale]?.();
  return loaded?.default ?? {};
}
```

Exécutez [`npx gt translate`](/docs/cli/reference/commands/translate) après avoir modifié vos textes, puis commitez les fichiers générés, ou bien exécutez la commande avant `next build`. Une fois les fichiers commités, la boutique peut être compilée sans clé API de General Translation.

## Router les requêtes en fonction du paramètre régional [#routing]

### Déplacer les routes sous `[locale]`

1. Déplacez toutes les routes de la boutique dans `app/[locale]/` et supprimez `app/layout.tsx`, afin que `[locale]` devienne un paramètre racine. Laissez `api/`, `md/`, `agent/`, `sitemap*`, `robots.ts`, `manifest.ts`, `llms.txt`, `global-error.tsx` et les icônes statiques directement sous `app/`.

2. Remplacez les arguments de type `PageProps<"/...">` et `LayoutProps<"/...">` par `"/[locale]/..."`.

3. Dans `app/[locale]/layout.tsx`, renvoyez tous les paramètres régionaux depuis `generateStaticParams`, rejetez les paramètres régionaux non pris en charge, définissez `<html lang>` et encapsulez le body dans [`GTProvider`](/docs/react/reference/components/gt-provider) :

   ```tsx title="app/[locale]/layout.tsx"
   import { GTProvider } from "gt-next";
   import { getLocale } from "gt-next/server";
   import { notFound } from "next/navigation";

   import { isSupportedLocale, locales } from "@/lib/i18n";

   export const ensureStatic = "prefetch";

   export function generateStaticParams() {
     return locales.map((locale) => ({ locale }));
   }

   export default async function RootLayout({ children }: LayoutProps<"/[locale]">) {
     const locale = await getLocale();
     if (!isSupportedLocale(locale)) notFound();
     return (
       <html lang={locale}>
         <body>
           <GTProvider>{/* lien d'évitement, fournisseur du panier, nav, main, footer */}{children}</GTProvider>
         </body>
       </html>
     );
   }
   ```

4. Ajoutez une route catch-all qui appelle `notFound()`, afin que les chemins sans correspondance affichent `app/[locale]/not-found.tsx` dans la mise en page localisée au lieu de la page 404 par défaut de Next.js :

   ```tsx title="app/[locale]/[...rest]/page.tsx"
   import { notFound } from "next/navigation";

   import { PLACEHOLDER_HANDLE } from "@/lib/static-params";

   export function generateStaticParams() {
     return [{ rest: [PLACEHOLDER_HANDLE] }];
   }

   export default function CatchAllPage(): never {
     notFound();
   }
   ```

<Callout type="warn">
  **Avertissement :** dans `gt-next` 11.4, [`GTProvider`](/docs/react/reference/components/gt-provider) lit `usePathname()` côté client. Avec Cache Components, le prérendu d&#39;une route dynamique sans paramètres statiques échoue donc. Renvoyez une valeur d&#39;espace réservé depuis `generateStaticParams` pour les routes qui n&#39;ont pas de paramètres statiques, comme la route catch-all et `account/orders/[id]`.
</Callout>

### Router les requêtes dans le proxy

Lisez une seule fois la liste des paramètres régionaux dans `gt.config.json`, puis extrayez le préfixe de chaque chemin :

```ts title="lib/i18n/index.ts"
import gtConfig from "@/gt.config.json";

export const defaultLocale = gtConfig.defaultLocale;
export const locales = [gtConfig.defaultLocale, ...gtConfig.locales];

export function isSupportedLocale(value: string | undefined): value is string {
  return value !== undefined && locales.includes(value);
}

/** Découpe `/fr/products/x` en `fr` et `/products/x` ; les chemins sans préfixe correspondent aux paramètres régionaux par défaut. */
export function splitLocalePath(pathname: string): { locale: string; pathname: string } {
  const [, segment, ...rest] = pathname.split("/");
  if (segment !== defaultLocale && isSupportedLocale(segment)) {
    return { locale: segment, pathname: `/${rest.join("/")}` };
  }
  return { locale: defaultLocale, pathname };
}

/** Les paramètres régionaux par défaut restent sans préfixe afin que chaque page conserve une URL canonique unique. */
export function localizePath(pathname: string, locale: string): string {
  if (locale === defaultLocale) return pathname;
  return pathname === "/" ? `/${locale}` : `/${locale}${pathname}`;
}
```

Dans `proxy.ts`, conservez tel quel l&#39;aiguillage `handleShopifyRoutes` du modèle, afin que Shopify traite en priorité les requêtes liées au panier, à la recherche prédictive et au compte client. Réécrivez ensuite toutes les autres requêtes de page vers leur route `/[locale]`. Ne touchez pas aux chemins d&#39;API ni aux autres chemins qui ne correspondent pas à des pages :

```ts title="proxy.ts"
// Le dispatch du modèle, inchangé : lorsque les comptes clients sont activés, la branche
// qui tient compte de la session ajoute les gestionnaires de compte et un gestionnaire de session.
const shopifyRoute = customerSession
  ? handleShopifyRoutes({
      ...routeOptions,
      handlers: customerHandlers(customerSession, isAuthPath),
      sessionManager: createCustomerSessionManager(request),
    })
  : handleShopifyRoutes({ ...routeOptions, handlers: [cartHandlers, predictiveSearchHandlers] });
if (shopifyRoute) return shopifyRoute;

const { locale, pathname: unprefixedPathname } = splitLocalePath(pathname);
const refinedPath = getRefinedCollectionPath(unprefixedPathname, request.nextUrl.search);
const localizedPath = isUnlocalizedPath(pathname)
  ? null
  : `/${locale}${refinedPath ?? (unprefixedPathname === "/" ? "" : unprefixedPathname)}`;
// Les pages se trouvent sous app/[locale] ; les paramètres régionaux par défaut sont servis sans préfixe et réécrits en interne.
const rewritePath =
  getMarkdownRewritePath(request) ?? (localizedPath === pathname ? null : localizedPath);
```

```ts title="proxy.ts"
const UNLOCALIZED_PATH = /^\/(?:api|md|agent|eve|_eve_internal|\.well-known|__shopify)(?:\/|$)/;

function isUnlocalizedPath(pathname: string): boolean {
  return UNLOCALIZED_PATH.test(pathname);
}

// Pages de la boutique, par opposition aux routes d’API et aux points de terminaison Shopify à extension, comme `/cart.js`.
function isPagePath(pathname: string): boolean {
  return !isUnlocalizedPath(pathname) && !/\.[a-z]+$/i.test(pathname);
}
```

Comme `splitLocalePath` ne considère jamais les paramètres régionaux par défaut comme un préfixe, `/en/products/x` est réécrit en `/en/en/products/x` et renvoie une erreur 404. Chaque page possède ainsi une URL canonique unique.

### Localiser les liens

Faites passer les `href` de type chaîne de caractères par [`useLocalizeHref`](/docs/react/nextjs/reference/hooks/use-localize-href) dans le composant `Link` du modèle, afin que les liens internes conservent le paramètre régional actuel. Faites de même pour `router.push`, `router.replace` et la navigation liée à la recherche.

```tsx title="components/ui/link.tsx"
import { useLocalizeHref } from "gt-next/link";

// Maintenir la navigation dans la locale active ; les paramètres régionaux par défaut restent sans préfixe.
const localizeHref = useLocalizeHref();
const localizedHref = typeof href === "string" ? localizeHref(href) : href;
```

Pour le sélecteur de langue, remplacez le préfixe de l&#39;URL actuelle et affichez le nom de chaque langue dans cette langue même :

```tsx title="components/nav/language-switcher.tsx"
"use client";

import { T, useLocale, useLocales } from "gt-next";
import { useId } from "react";

import { localizePath, splitLocalePath } from "@/lib/i18n";

/** Affiche le nom de chaque langue dans cette même langue (« Français », « 日本語 ») pour que les clients reconnaissent la leur. */
function getEndonym(locale: string): string {
  const name = new Intl.DisplayNames([locale], { type: "language" }).of(locale) ?? locale;
  return name.charAt(0).toLocaleUpperCase(locale) + name.slice(1);
}

export function LanguageSwitcher() {
  const id = useId();
  const locale = useLocale();
  const locales = useLocales();
  return (
    <>
      <label htmlFor={id} className="sr-only">
        <T>Language</T>
      </label>
      <select
        id={id}
        value={locale}
        onChange={(event) => {
          // Changer de paramètre régional modifie la mise en page racine : on navigue donc avec un rechargement complet de la page.
          const { pathname } = splitLocalePath(window.location.pathname);
          window.location.assign(
            `${localizePath(pathname, event.target.value)}${window.location.search}`,
          );
        }}
      >
        {locales.map((option) => (
          <option key={option} lang={option} value={option}>
            {getEndonym(option)}
          </option>
        ))}
      </select>
    </>
  );
}
```

### Ajouter les métadonnées SEO

Attribuez à chaque page une URL canonique dans son propre paramètre régional, ainsi qu’une variante `hreflang` pour chaque paramètre régional. Les fonctions `buildAlternates` et `buildOpenGraph` du modèle, dans `lib/seo/index.ts`, sont synchrones et ne peuvent pas lire le paramètre régional. Déplacez-les donc dans `lib/seo/server.ts` sous forme de fonctions asynchrones :

1. Dans `lib/seo/index.ts`, exportez `buildCanonicalPath`, puis supprimez `buildAlternates` et `buildOpenGraph`.
2. Ajoutez les versions asynchrones à `lib/seo/server.ts` :

   ```ts title="lib/seo/server.ts"
   import { getLocale } from "gt-next/server";
   import type { Metadata } from "next";

   import { localizePath, locales } from "@/lib/i18n";
   import { buildCanonicalPath } from "@/lib/seo";
   import type { SearchParamsInput } from "@/lib/seo/types";

   /** URL canonique pour le paramètre régional de la requête, plus les variantes hreflang pour chaque paramètre régional. */
   export async function buildAlternates({
     pathname,
     searchParams,
   }: {
     pathname: string;
     searchParams?: SearchParamsInput;
   }): Promise<Metadata["alternates"]> {
     const canonicalPath = buildCanonicalPath(pathname, searchParams);
     const locale = await getLocale();
     return {
       canonical: localizePath(canonicalPath, locale),
       languages: {
         ...Object.fromEntries(locales.map((code) => [code, localizePath(canonicalPath, code)])),
         "x-default": canonicalPath,
       },
     };
   }
   ```

   Rendez `buildOpenGraph` asynchrone de la même manière, puis localisez son URL et ses paramètres régionaux. Open Graph attend des valeurs au format `language_TERRITORY`, comme `fr_FR` : effectuez donc la conversion du paramètre régional à cet endroit, et conservez le tag simple pour `hreflang` et `<html lang>` :

   ```ts title="lib/seo/server.ts"
   /** Les paramètres régionaux Open Graph utilisent le format language_TERRITORY, comme fr_FR ; « fr » prend son territoire le plus probable. */
   function toOpenGraphLocale(locale: string): string {
     const { language, region } = new Intl.Locale(locale).maximize();
     return `${language}_${region}`;
   }

   // Dans buildOpenGraph, en plus des champs existants :
   const locale = await getLocale();
   return {
     url: localizePath(url, locale),
     locale: toOpenGraphLocale(locale),
     alternateLocale: locales.filter((code) => code !== locale).map(toOpenGraphLocale),
   };
   ```

   `maximize()` choisit le territoire le plus probable, par exemple `fr_FR` pour `fr` et `zh_TW` pour `zh-Hant`. Remplacez cette conversion par une table de correspondance explicite pour tout paramètre régional dont le territoire le plus probable n’est pas celui que vous ciblez.
3. Dans la fonction `generateMetadata` de chaque page, importez les deux utilitaires depuis `@/lib/seo/server` et appelez-les avec `await` :

   ```tsx title="app/[locale]/page.tsx"
   import { buildAlternates, buildOpenGraph } from "@/lib/seo/server"; // [!code highlight]

   export async function generateMetadata(): Promise<Metadata> {
     const gt = await getGT();
     const title = gt("Home");
     const description = gt("Explore featured products, curated collections, and seasonal campaigns.");
     return {
       title: `${title} | ${shopConfig.site.name}`,
       description,
       alternates: await buildAlternates({ pathname: "/" }), // [!code highlight]
       openGraph: await buildOpenGraph({ title, description, url: "/", type: "website" }), // [!code highlight]
     };
   }
   ```

   Une fois les anciens utilitaires supprimés, toute page qui importe encore depuis `@/lib/seo` échoue à la vérification des types, ce qui vous permet de repérer chaque appel oublié.

Listez également l’URL de chaque paramètre régional dans le plan de site, chacune accompagnée d’entrées `<xhtml:link rel="alternate" hreflang="...">` pour tous les paramètres régionaux.

## Interroger Shopify dans chaque langue [#shopify-language]

Associez chaque paramètre régional au pays et à la langue Shopify attendus par les appels à la Storefront API du modèle. Tous les paramètres régionaux partagent l&#39;unique marché de la boutique, si bien que seule la langue change :

```ts title="lib/i18n/index.ts"
import type { CommerceLocale } from "@/lib/config/types";

// Le marché sur lequel toutes les langues vendent. La devise provient toujours de la réponse de Shopify.
const STORE_COUNTRY: CommerceLocale["country"] = "US";

// La langue de la Storefront API correspondant à chaque paramètre régional de gt.config.json.
const SHOPIFY_LANGUAGES: Record<string, CommerceLocale["language"]> = {
  en: "EN",
  fr: "FR",
  ja: "JA",
  ko: "KO",
  id: "ID",
};

export function getCommerceLocale(locale: string): CommerceLocale {
  const language = SHOPIFY_LANGUAGES[locale];
  if (!language) throw new Error(`No Shopify language for locale: ${locale}`);
  return { country: STORE_COUNTRY, language };
}

/** Le proxy mémorise le paramètre régional de la page dans ce cookie, afin que les appels aux gestionnaires Hydrogen (`/api/cart`) utilisent la même langue. */
export const LOCALE_COOKIE_NAME = "shop_locale";
/** Le proxy transmet le paramètre régional résolu aux actions serveur et à l'initialisation du panier via cet en-tête de requête. */
export const LOCALE_HEADER_NAME = "x-shop-locale";
```

Chaque langue doit correspondre à un `LanguageCode` valide de la Storefront API (voir [Codes de paramètres régionaux](/docs/integrations/shopify/reference/locale-handling#locale-codes)). Si vous [stockez les traductions du catalogue dans Shopify](/docs/integrations/shopify/guides/translating-products#store-in-shopify), publiez également chaque langue dans la boutique : pour une langue non publiée, Shopify renvoie le texte source. Les traductions intégrées au bundle de la boutique ne nécessitent pas que les langues soient publiées.

### Transmettre la langue aux lectures du catalogue

Les getters de catalogue du modèle acceptent déjà une option `locale` de type `CommerceLocale` et la transmettent à `@inContext`. Transmettez la valeur du paramètre régional depuis chaque page et chaque composant. Les getters qui utilisent `"use cache"` la reçoivent en argument : chaque langue dispose ainsi de sa propre entrée de cache.

```ts title="lib/i18n/server.ts"
import { getLocale } from "gt-next/server";
import { headers } from "next/headers";

import type { CommerceLocale } from "@/lib/config/types";
import { defaultLocale, getCommerceLocale, isSupportedLocale, LOCALE_HEADER_NAME } from "@/lib/i18n";

/** Pays et langue Shopify correspondant au `[locale]` de la page ; à transmettre à chaque lecture du catalogue. */
export async function getRequestCommerceLocale(): Promise<CommerceLocale> {
  return getCommerceLocale(await getLocale());
}

/** Même valeur, lue depuis l'en-tête du proxy, pour le code dynamique sans paramètres de route (server actions, initialisation du panier). */
export async function getForwardedCommerceLocale(): Promise<CommerceLocale> {
  const locale = (await headers()).get(LOCALE_HEADER_NAME) ?? undefined;
  return getCommerceLocale(isSupportedLocale(locale) ? locale : defaultLocale);
}
```

```tsx title="app/[locale]/products/[handle]/page.tsx"
const product = await getProduct({
  handle,
  locale: await getRequestCommerceLocale(),
});
```

Les actions serveur, comme l&#39;achat rapide et **Load more**, ne peuvent pas lire les paramètres de route. Récupérez plutôt la valeur avec `getForwardedCommerceLocale()`.

### Exécuter les gestionnaires de Hydrogen dans la même langue

Les gestionnaires de panier et de recherche prédictive de Hydrogen lisent la langue et le pays dans l&#39;objet `i18n` du contexte de la requête. Les requêtes comme `/api/cart` n&#39;ont pas de préfixe du paramètre régional : le proxy mémorise donc le paramètre régional de la page dans un cookie, puis le transmet aux Composants serveur et aux actions serveur via un en-tête :

```ts title="proxy.ts"
import {
  defaultLocale,
  getCommerceLocale,
  isSupportedLocale,
  LOCALE_COOKIE_NAME,
  LOCALE_HEADER_NAME,
  splitLocalePath,
} from "@/lib/i18n";

const rememberedLocale = request.cookies.get(LOCALE_COOKIE_NAME)?.value;
const requestLocale = resolveRequestLocale(request);
// Les préchargements spéculatifs ne doivent pas modifier le panier du client ni la langue mémorisée.
const isPageNavigation =
  request.method === "GET" &&
  isPagePath(pathname) &&
  !request.headers.has("next-router-prefetch") &&
  request.headers.get("purpose") !== "prefetch" &&
  !request.headers.get("sec-purpose")?.includes("prefetch");

// Les gestionnaires de panier et de recherche de Hydrogen s'exécutent dans la langue du client.
const requestContext = createCustomerRequestContext(
  request,
  getCommerceLocale(requestLocale),
  requestLocale,
);

// Une fois que handleShopifyRoutes a refusé la requête :
const headers = forwardCustomerRefreshAttempt(
  request.nextUrl,
  requestContext.getForwardedRequestHeaders(),
);
headers.set(LOCALE_HEADER_NAME, requestLocale);

const response = rewritePath
  ? NextResponse.rewrite(new URL(`${rewritePath}${request.nextUrl.search}`, request.url), {
      request: { headers },
    })
  : NextResponse.next({ request: { headers } });

// Dans la réponse :
if (isPageNavigation && rememberedLocale !== locale) {
  response.cookies.set(LOCALE_COOKIE_NAME, locale, { path: "/", sameSite: "lax" });
}

/** Le paramètre régional de l'URL pour les pages ; le paramètre régional de page mémorisé pour les endpoints Shopify sans préfixe, comme `/api/cart`. */
function resolveRequestLocale(request: NextRequest): string {
  const { pathname } = request.nextUrl;
  const { locale } = splitLocalePath(pathname);
  if (locale !== defaultLocale || isPagePath(pathname)) return locale;
  const remembered = request.cookies.get(LOCALE_COOKIE_NAME)?.value;
  return isSupportedLocale(remembered) ? remembered : defaultLocale;
}
```

Conservez l&#39;import `forwardCustomerRefreshAttempt` du modèle depuis `@/lib/auth/request`. Appliquez-le avant de choisir entre `NextResponse.rewrite` et `NextResponse.next`, afin que les pages de compte réécrites reçoivent elles aussi l&#39;en-tête de tentative de rafraîchissement et ne puissent pas relancer indéfiniment un rafraîchissement de jeton qui a échoué.

Faites en sorte que `createCustomerRequestContext` accepte le paramètre régional Shopify et celui de la page, et transmettez le préfixe d&#39;URL du paramètre régional via `i18n.pathPrefix` : `/fr` pour les URL en français, et une chaîne de caractères vide pour les paramètres régionaux par défaut. Sans cela, Hydrogen ne reconnaît pas `/fr/products/hoodie?variant=123` comme une URL de produit et ignore la redirection de variante, si bien que la page risque d&#39;afficher la variante par défaut au lieu de celle indiquée dans le lien :

```ts title="lib/auth/request.ts"
import { defaultLocale } from "@/lib/i18n";

export function createCustomerRequestContext(
  request: Request,
  i18n: CommerceLocale = shopConfig.localization,
  locale: string = defaultLocale,
): ShopifyRequestContext {
  return createShopifyRequestContext({
    i18n: {
      country: i18n.country,
      language: i18n.language,
      // Hydrogen reconnaît et redirige les URL de produits situées sous ce préfixe.
      pathPrefix: locale === defaultLocale ? "" : `/${locale}`,
    },
    request,
  });
}
```

Grâce aux valeurs par défaut, les autres appelants du modèle continuent de fonctionner. Passez également `getCommerceLocale` à l&#39;initialisation du panier côté serveur (`lib/cart/server.ts`) au lieu de lire `shopConfig.localization`.

## Traduire les textes [#copy]

Gardez les textes intégrés directement dans chaque composant, comme dans le modèle :

* Encapsulez le JSX dans [`<T>`](/docs/react/reference/components/t). Encapsulez les valeurs dynamiques dans [`<Var>`](/docs/react/reference/components/var), [`<Num>`](/docs/react/reference/components/num), [`<Currency>`](/docs/react/reference/components/currency) ou [`<DateTime>`](/docs/react/reference/components/datetime), et les quantités dans [`<Plural>`](/docs/react/reference/components/plural). [`<T>`](/docs/react/reference/components/t) fonctionne aussi bien dans les Composants Serveur que dans les Composants Client.
* Traduisez les chaînes, comme les valeurs `aria-label`, les métadonnées et les props, avec [`getGT`](/docs/react/nextjs/reference/functions/get-gt) dans les Composants Serveur et [`useGT`](/docs/react/reference/hooks/use-gt) dans les Composants Client.
* Marquez les libellés définis en dehors d&#39;un composant, comme les options de tri, avec [`msg`](/docs/react/reference/functions/msg). Résolvez-les à l&#39;endroit où ils sont affichés avec [`getMessages`](/docs/react/nextjs/reference/functions/get-messages) ou [`useMessages`](/docs/react/reference/hooks/use-messages).

```tsx title="app/[locale]/page.tsx"
import { T } from "gt-next";
import { getGT } from "gt-next/server";

export async function generateMetadata(): Promise<Metadata> {
  const gt = await getGT();
  return { title: gt("Home") };
}

export default async function HomePage() {
  const gt = await getGT();
  return (
    <>
      <h1>
        <T>A storefront localized with General Translation</T>
      </h1>
      <ProductsGrid title={gt("Products")} />
    </>
  );
}
```

```tsx title="app/[locale]/search/page.tsx"
<T>
  <Plural
    n={total}
    one={<><Num>{total}</Num> result</>}
    other={<><Num>{total}</Num> results</>}
  />
</T>
```

<Callout type="warn">
  **Avertissement :** La CLI repère les chaînes `gt()` en suivant la variable à laquelle est assigné le résultat de [`getGT()`](/docs/react/nextjs/reference/functions/get-gt) ou de [`useGT()`](/docs/react/reference/hooks/use-gt). Effectuez cette assignation sur une ligne distincte, comme dans `const gt = await getGT()`. Les chaînes lues via un résultat de `Promise.all` déstructuré ne sont pas extraites et s&#39;affichent dans la langue source.
</Callout>

Formatez les prix Shopify à l&#39;aide du paramètre régional et du code de devise renvoyés par Shopify. La fonction `formatPrice(money, locale)` du modèle s&#39;appuie sur la fonction `formatMoney` de Hydrogen : ainsi, 36.95 EUR s&#39;affiche `36,95 €` en français.

## Traduire le contenu du catalogue [#catalog]

Les titres, descriptions et options des produits proviennent de Shopify. Vous pouvez soit stocker leurs traductions dans Shopify et les récupérer avec `@inContext(language:)`, que `getCommerceLocale` transmet déjà, soit les intégrer au bundle de la boutique via la CLI, comme le fait la boutique d’exemple. (Voir [Traduire les produits Shopify](/docs/integrations/shopify/guides/translating-products).)

## Next steps

- /docs/integrations/shopify/guides/localizing-react-router
- /docs/integrations/shopify/guides/translating-products
- /docs/integrations/shopify/guides/selling-in-markets

## Sitemap

See the full [sitemap](https://generaltranslation.com/sitemap.md) for all pages.
