# General Translation Integrations: Localiser Shopify Hydrogen avec React Router
URL: https://generaltranslation.com/fr/docs/integrations/shopify/guides/localizing-react-router.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Comment ajouter gt-react à une boutique Hydrogen sous React Router et garder le contexte de langue de Shopify synchronisé avec celui-ci.

Dans une boutique React Router, `gt-react` traduit les textes de votre code et Hydrogen communique avec Shopify. La partie propre à Shopify consiste à transmettre au contexte de la requête de Hydrogen le même paramètre régional que celui de la page.

*Note : ce guide s’applique à `@shopify/hydrogen` 2026.10 sous React Router 8, ou sous React Router 7 avec `future.v8_middleware: true` dans `react-router.config.ts`.*

<Callout type="info">
  **Modifié dans Hydrogen 2026.10 :** `createHydrogenContext` et le modèle propre à React Router ont été remplacés par `createShopifyRequestContext`, indépendant du framework. Il accepte les mêmes langue et pays `i18n`, ainsi qu’un `pathPrefix` facultatif. La compétence `migrate-to-hydrogen-2026-10` fournie avec Hydrogen prend en charge la mise à niveau.
</Callout>

## Avant de commencer [#before-start]

* Configurez Hydrogen dans votre application React Router avec `npx @shopify/hydrogen setup`. Votre middleware racine crée le contexte de la requête Shopify et le client Storefront, puis exécute `handleShopifyRoutes`.
* Suivez le [Démarrage rapide React Router](/docs/react/react-router-quickstart). Votre loader racine renvoie un paramètre régional et un instantané des traductions, et le `Layout` racine affiche [`GTProvider`](/docs/react/reference/components/gt-provider) ainsi que `<html lang>`. Dans ce guide, le [`<LocaleSelector>`](/docs/react/reference/components/locale-selector) du Démarrage rapide est remplacé par un sélecteur de langue qui modifie l&#39;URL.

## Résoudre le paramètre régional à partir de l&#39;URL [#request-locale]

Lisez le paramètre régional depuis le chemin de la requête afin que chaque paramètre régional ait sa propre URL. Utilisez un même module pour `gt-react` et pour Hydrogen :

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

// "/fr/products/x" -> "fr" ; les chemins sans préfixe utilisent les paramètres régionaux par défaut.
export function getRequestLocale(request: Request): string {
  const [, segment] = new URL(request.url).pathname.split('/');
  return gtConfig.locales.includes(segment) ? segment : gtConfig.defaultLocale;
}

// "/products/x" -> "/fr/products/x" ; pour les paramètres régionaux par défaut, le chemin reste sans préfixe.
export function localizePath(pathname: string, locale: string): string {
  if (locale === gtConfig.defaultLocale) return pathname;
  return pathname === '/' ? `/${locale}` : `/${locale}${pathname}`;
}

// "/fr/products/x" -> "/products/x" ; les chemins sans préfixe sont renvoyés tels quels.
export function stripLocale(pathname: string): string {
  const [, segment, ...rest] = pathname.split('/');
  return gtConfig.locales.includes(segment) ? `/${rest.join('/')}` : pathname;
}
```

Dans le loader racine, appelez `getRequestLocale(request)` au lieu de `parseLocale(request)`, puis transmettez le résultat à [`getTranslationsSnapshot`](/docs/react/reference/functions/get-translations-snapshot).

*Remarque : `parseLocale` lit le cookie du paramètre régional, puis l&#39;en-tête `Accept-Language`. Utilisé seul, il sert toutes les langues depuis une même URL : les moteurs de recherche n&#39;indexent donc qu&#39;une seule langue.*

Pour servir des chemins préfixés, imbriquez vos pages sous une route de mise en page facultative `:locale?`. Placez la route catch-all de Hydrogen en dernier, afin que les chemins sans correspondance atteignent toujours ses redirections :

```ts title="app/routes.ts"
import {type RouteConfig, index, route} from '@react-router/dev/routes';

export default [
  // Les pages s'affichent sans préfixe ou avec le préfixe d'un paramètre régional cible, par exemple /fr.
  route(':locale?', 'routes/locale.tsx', [
    index('routes/home.tsx'),
    route('products/:handle', 'routes/product.tsx'),
  ]),
  route('*', 'routes/catchall.tsx'),
] satisfies RouteConfig;
```

Dans la route de mise en page, renvoyez une erreur 404 lorsque le segment ne correspond à aucune locale cible configurée, afin que `/en/...` et les préfixes inconnus n&#39;affichent pas une copie en double d&#39;une page :

```tsx title="app/routes/locale.tsx"
import {Outlet} from 'react-router';
import gtConfig from '../../gt.config.json';

// Seules les locales cibles sont des préfixes valides ; les paramètres régionaux par défaut n'ont pas de préfixe.
export async function loader({params}: {params: {locale?: string}}) {
  if (params.locale && !gtConfig.locales.includes(params.locale)) {
    throw new Response(null, {status: 404});
  }
  return null;
}

export default function LocaleLayout() {
  return <Outlet />;
}
```

Avec les routes basées sur les fichiers, nommez plutôt la mise en page `($locale).tsx` et imbriquez-y les fichiers de page.

Les gestionnaires propres à Hydrogen, comme `/api/cart`, `/api/predictive-search`, `/checkout` et les routes Customer Account, répondent dans le middleware racine avant l&#39;affichage de toute page : ils restent donc sans préfixe.

### Conserver les gestionnaires de Hydrogen dans la langue de la page [#handler-locale]

Les requêtes vers `/api/cart` et `/api/predictive-search` n&#39;ont pas de préfixe du paramètre régional : `getRequestLocale` les résout donc vers les paramètres régionaux par défaut. Sur une page en français, le panier et les résultats de recherche afficheraient alors le texte du catalogue en anglais.

Enregistrez le paramètre régional de la page dans un cookie, et ne le lisez que pour ces requêtes `/api` sans préfixe. Pour les pages, l&#39;URL reste la seule source du paramètre régional. Remplacez `getRequestLocale` dans `app/lib/i18n.ts` :

```ts title="app/lib/i18n.ts"
export const LOCALE_COOKIE_NAME = 'shop_locale';

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

export function readCookie(request: Request, name: string): string | undefined {
  const cookie = request.headers.get('Cookie')?.split(/;\s*/).find((part) => part.startsWith(`${name}=`));
  return cookie?.slice(name.length + 1);
}

/** Les pages utilisent leur préfixe d'URL ; les gestionnaires `/api` de Hydrogen utilisent le paramètre régional de la page qui les a appelés. */
export function getRequestLocale(request: Request): string {
  const {pathname} = new URL(request.url);
  const [, segment] = pathname.split('/');
  if (gtConfig.locales.includes(segment)) return segment;
  if (!pathname.startsWith('/api/')) return gtConfig.defaultLocale;
  const remembered = readCookie(request, LOCALE_COOKIE_NAME);
  // Seul un paramètre régional configuré est considéré comme fiable ; toute autre valeur entraîne un repli sur celui par défaut.
  return remembered && allLocales.includes(remembered) ? remembered : gtConfig.defaultLocale;
}

/** Chargements de page et navigations côté client, mais pas les appels `/api` ni les préchargements spéculatifs. */
export function isPageNavigation(request: Request): boolean {
  const purpose = `${request.headers.get('Purpose') ?? ''} ${request.headers.get('Sec-Purpose') ?? ''}`;
  return (
    request.method === 'GET' &&
    !new URL(request.url).pathname.startsWith('/api/') &&
    !purpose.includes('prefetch')
  );
}
```

Le middleware racine présenté dans la section suivante définit le cookie lorsque le paramètre régional d&#39;une navigation de page diffère de celui mémorisé. Les préchargements ne le définissent pas : un lien préchargé vers une autre langue ne peut donc pas changer la langue du panier avant que l&#39;acheteur ne clique dessus.

## Transmettre la langue à Shopify [#storefront-language]

Associez chaque paramètre régional à son `LanguageCode` Shopify, puis transmettez-le à `createShopifyRequestContext` dans votre middleware racine. Le client Storefront renseigne alors `$language` et `$country` dans chaque requête qui déclare `@inContext(country: $country, language: $language)`.

```ts title="app/root.tsx"
import {
  createShopifyRequestContext,
  createStorefrontClient,
  handleShopifyRedirects,
  handleShopifyRoutes,
} from '@shopify/hydrogen';
import gtConfig from '../gt.config.json';
import {getRequestLocale, isPageNavigation, LOCALE_COOKIE_NAME, readCookie} from '~/lib/i18n';
import type {Route} from './+types/root';

// Une entrée pour chaque paramètre régional de gt.config.json.
const SHOPIFY_LANGUAGES = {en: 'EN', fr: 'FR', ja: 'JA'} as const;

export const middleware: Route.MiddlewareFunction[] = [
  async ({context, request}, next) => {
    const locale = getRequestLocale(request) as keyof typeof SHOPIFY_LANGUAGES;
    const requestContext = createShopifyRequestContext({
      request,
      // La langue suit la page ; le pays reste celui du marché où vous vendez.
      // pathPrefix maintient les redirections de variantes et de routes standard de Hydrogen dans les URL du paramètre régional.
      i18n: {
        language: SHOPIFY_LANGUAGES[locale],
        country: 'US',
        pathPrefix: locale === gtConfig.defaultLocale ? '' : `/${locale}`,
      },
    });
    const storefrontClient = createStorefrontClient({type: 'public', requestContext, config});

    const shopifyRoute = handleShopifyRoutes({request, requestContext, storefrontClient, routeTemplates, handlers});
    if (shopifyRoute) return shopifyRoute;

    context.set(storefrontClientContext, storefrontClient);

    const response = await next();
    if (response.status === 404) {
      const redirect = await handleShopifyRedirects({request, routeTemplates, storefrontClient});
      if (redirect) return redirect;
    }
    // Mémorise le paramètre régional de la page pour les gestionnaires `/api` de Hydrogen. Définissez le cookie avant
    // applyResponseHeaders, qui rend privée et non cachable toute réponse définissant un cookie.
    if (isPageNavigation(request) && readCookie(request, LOCALE_COOKIE_NAME) !== locale) {
      response.headers.append('Set-Cookie', `${LOCALE_COOKIE_NAME}=${locale}; Path=/; SameSite=Lax`);
    }
    storefrontClient.requestContext.applyResponseHeaders(response.headers);
    return response;
  },
];
```

`config`, `routeTemplates`, `handlers` et `storefrontClientContext` proviennent de votre configuration Hydrogen existante ; seuls `i18n` et le cookie de paramètre régional sont nouveaux. Une réponse de page qui définit le cookie n&#39;est pas mise en cache : cela ne se produit donc que lors de la première page consultée par un client et lorsqu&#39;il change de langue.

Ce guide conserve le marché américain pour toutes les langues : passer au français modifie donc les textes sans changer le pays de l&#39;acheteur ni la devise du panier. Pour gérer plusieurs marchés, laissez les clients choisir leur pays séparément, ou associez chaque paramètre régional à un marché et transférez le panier lorsque la langue change. (Voir [Vendre sur plusieurs marchés](/docs/integrations/shopify/guides/selling-in-markets)).

## Formater les prix [#prices]

Formatez le montant fourni par Shopify avec le code de devise que Shopify renvoie. [`<Currency>`](/docs/react/reference/components/currency) met en forme le montant selon la locale active, sans jamais le convertir.

```tsx title="app/components/LocalizedMoney.tsx"
import {Currency} from 'gt-react';

/** Formate le prix Shopify selon la locale active sans convertir la devise. */
export function LocalizedMoney({data}: {data: {amount?: string; currencyCode?: string}}) {
  // Les réponses optimistes du panier peuvent omettre temporairement leurs champs de coût.
  if (data.amount == null || !data.currencyCode) return null;
  return <Currency currency={data.currencyCode}>{data.amount}</Currency>;
}
```

## Créer des liens dans le paramètre régional [#links]

Dans votre code, gardez les chemins de route sans préfixe, et ajoutez le préfixe du paramètre régional actuel au moment d&#39;afficher un lien. Récupérez la locale active avec [`useLocale`](/docs/react/reference/hooks/use-locale) et passez `localizePath(to, locale)` au `Link` de React Router.

Pour changer de langue, modifiez le préfixe de l&#39;URL actuelle. Le loader racine lit alors le nouveau paramètre régional, et le panier reste intact. N&#39;utilisez pas [`<LocaleSelector>`](/docs/react/reference/components/locale-selector) dans ce cas : il enregistre le paramètre régional dans un cookie et recharge la même URL, qui se résout toujours vers l&#39;ancien paramètre régional.

```tsx title="app/components/LanguageSwitcher.tsx"
import {useLocale, useLocales} from 'gt-react';
import {useLocation, useNavigate} from 'react-router';
import {localizePath, stripLocale} from '~/lib/i18n';

/** Change de langue en modifiant le préfixe de l'URL ; le loader racine lit le nouveau paramètre régional depuis l'URL. */
export function LanguageSwitcher() {
  const locale = useLocale();
  const locales = useLocales();
  const {pathname, search} = useLocation();
  const navigate = useNavigate();
  return (
    <select
      aria-label="Language"
      value={locale}
      onChange={(event) => navigate(localizePath(stripLocale(pathname), event.target.value) + search)}
    >
      {locales.map((option) => (
        // Affiche chaque langue dans sa propre langue, par exemple « français » ou « 日本語 ».
        <option key={option} value={option} lang={option}>
          {new Intl.DisplayNames([option], {type: 'language'}).of(option)}
        </option>
      ))}
    </select>
  );
}
```

Pour créer des liens vers les routes Customer Account, comme `/account/login`, utilisez une simple balise `<a>` ou `<form>`, et non `Link`. Leurs gestionnaires effectuent une redirection vers Shopify, ce que la navigation côté client ne peut pas suivre.

## Générer des traductions [#generate]

Encapsulez le texte dans [`<T>`](/docs/react/reference/components/t), utilisez [`useGT`](/docs/react/reference/hooks/use-gt) pour les chaînes telles que les valeurs `aria-label`, puis exécutez [`npx gt translate`](/docs/cli/reference/commands/translate) avant `react-router build`. Les traductions intégrées au bundle par votre loader sont disponibles dès le premier affichage côté serveur : les réponses mises en cache les contiennent donc.

Pour que le texte reste encapsulé et traduit à mesure que la boutique évolue, connectez le dépôt à [Locadex](/docs/platform/locadex/quickstart). Locadex ouvre des pull requests contenant les nouvelles traductions, prêtes pour relecture.

## Next steps

- /docs/integrations/shopify/guides/localizing-nextjs
- /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.
