# General Translation Integrations: Next.js製Shopifyストアフロントのローカライズ
URL: https://generaltranslation.com/ja/docs/integrations/shopify/guides/localizing-nextjs.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: gt-next、ロケールプレフィックス付きルート、Cache Componentsを使用してVercel Shopストアフロントをローカライズする方法。

[Vercel Shop](https://github.com/vercel/shop) は、Shopifyのフレームワーク非依存なストアフロントSDKであるHydrogenをベースにしたNext.js App Routerテンプレートです。`gt-next` を使うと、デフォルトロケールはプレフィックスなしで配信され、その他のロケールは `/fr/...` のようなプレフィックス付きのパスで配信されます。また、すべてのページはロケールごとに1回ずつプリレンダリングできる状態が維持されます。このガイドでは、すべての言語を1つのShopifyマーケットで販売します。言語ごとに個別のマーケットと通貨を割り当てたい場合は、このガイドの完了後に [複数マーケットでの販売](/docs/integrations/shopify/guides/selling-in-markets) を参照してください。

*注: このガイドは、Next.js 16、`cacheComponents: true`、`@shopify/hydrogen` 2026.10、`gt-next` 11.4 を使用したテンプレートを前提としています。これらは [サンプルストアフロント](https://gt-shopify-vercel.vercel.app) で使用されているバージョンで、同ストアフロントは英語、フランス語、日本語、韓国語、インドネシア語で公開されています。*

## 始める前に [#before-start]

[Next.js クイックスタート](/docs/react/nextjs-quickstart)と同様に、`gt-next` をインストールし、`gt` CLI を開発依存関係としてインストールしてください。このガイドでは、クイックスタートのミドルウェアの代わりに、テンプレートにすでに含まれている `proxy.ts` を使用します。

## gt-next の設定 [#configure]

### 1. config プラグインを追加する

テンプレートが `withShopConfig` に渡しているプラグインのリストに、[`withGTConfig`](/docs/react/nextjs/config) を追加します：

```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. リクエスト関数を追加する

Cache Components を使用する場合、ロケールはヘッダーやクッキーではなく、`[locale]` ルートパラメータから取得する必要があります。プロジェクトルートに [`getLocale`](/docs/react/nextjs/reference/functions/get-locale) と [`getRegion`](/docs/react/nextjs/reference/functions/get-region) のファイルを作成してください。`withGTConfig` がこれらを検出します ([Cache Components の使用](/docs/react/nextjs/cache-components#request-values)を参照) 。

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

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

// `[locale]` ルートパラメータは静的キャッシュキーの一部なので、これを読み取ってもページのプリレンダリングは維持されます。
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";

// リージョンは、そのページのロケールで販売を行う Shopify マーケットの国です。
export default async function getRegion(): Promise<string> {
  return getCommerceLocale((await locale()) ?? defaultLocale).country;
}
```

`getCommerceLocale` は、ロケールを対応する Shopify の国と言語にマッピングします ([各言語で Shopify にクエリを実行する](#shopify-language)を参照) 。

### 3. 翻訳をローカルで生成して読み込む

プリレンダリング中にリモートから読み込むのは安全ではないため、Cache Components ではカスタムの [`loadTranslations`](/docs/react/reference/functions/load-translations) 関数が必要です。CLI で翻訳をソースコード内に書き出し、バンドルからインポートしてください ([翻訳をローカルに保存する](/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` には `lib/` を追加しています。CLI はデフォルトで `src/`、`app/`、`pages/`、`components/` のみをスキャンしますが、このテンプレートではメタデータとラベルを `lib/` に配置しているためです。

```ts title="loadTranslations.ts"
// `pnpm translate` がビルド前にこれらのファイルを書き出します。これらをバンドルに含めることで、ページのプリレンダリングが可能になります。
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 ?? {};
}
```

文言を変更したら [`npx gt translate`](/docs/cli/reference/commands/translate) を実行して生成されたファイルをコミットするか、`next build` の前に同コマンドを実行してください。生成ファイルをコミットしておけば、General Translation のAPIキーがなくてもストアフロントをビルドできます。

## ロケールに応じてリクエストをルーティングする [#routing]

### ルートを `[locale]` 配下に移動する

1. ストアフロントのすべてのルートを `app/[locale]/` に移動し、`app/layout.tsx` を削除して、`[locale]` をルートパラメータにします。`api/`、`md/`、`agent/`、`sitemap*`、`robots.ts`、`manifest.ts`、`llms.txt`、`global-error.tsx`、および静的アイコンは `app/` の直下に残します。

2. `PageProps<"/...">` と `LayoutProps<"/...">` の型引数を `"/[locale]/..."` に変更します。

3. `app/[locale]/layout.tsx` では、`generateStaticParams` からすべてのロケールを返し、サポート対象外のロケールを弾いたうえで `<html lang>` を設定し、body を [`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>{/* スキップリンク、カートプロバイダー、ナビゲーション、メイン、フッター */}{children}</GTProvider>
         </body>
       </html>
     );
   }
   ```

4. `notFound()` を呼び出すキャッチオールルートを追加します。これにより、どのルートにも一致しないパスでは、Next.js のデフォルトの 404 ページではなく、ローカライズされたレイアウト内で `app/[locale]/not-found.tsx` がレンダリングされます。

   ```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">
  **警告:** `gt-next` 11.4 では、[`GTProvider`](/docs/react/reference/components/gt-provider) がクライアント側で `usePathname()` を読み取ります。そのため Cache Components を使用している場合、静的パラメータを持たない動的ルートはプリレンダリングに失敗します。キャッチオールルートや `account/orders/[id]` のように静的パラメータを持たないルートでは、`generateStaticParams` からプレースホルダーを返してください。
</Callout>

### プロキシでリクエストをルーティングする

`gt.config.json` からロケールの一覧を一度だけ読み込み、各パスからロケールのプレフィックスを分離します。

```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);
}

/** `/fr/products/x` を `fr` と `/products/x` に分割します。プレフィックスのないパスはデフォルトロケールに属します。 */
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 };
}

/** デフォルトロケールにはプレフィックスを付けず、各ページのカノニカル URL を 1 つに保ちます。 */
export function localizePath(pathname: string, locale: string): string {
  if (locale === defaultLocale) return pathname;
  return pathname === "/" ? `/${locale}` : `/${locale}${pathname}`;
}
```

`proxy.ts` では、テンプレートにある `handleShopifyRoutes` のディスパッチをそのまま残し、カート、予測検索、顧客アカウントへのリクエストを Shopify が先に処理するようにします。そのうえで、それ以外のページリクエストはすべて対応する `/[locale]` ルートにリライトします。API などページ以外のパスには手を加えないでください：

```ts title="proxy.ts"
// テンプレートのディスパッチ処理はそのまま。顧客アカウントが有効な場合は、セッション対応の
// 分岐でアカウント用ハンドラーとセッションマネージャーが追加されます。
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)}`;
// ページは app/[locale] 配下に置かれます。デフォルトロケールはプレフィックスなしで配信され、内部でリライトされます。
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);
}

// ストアフロントのページを判定する（APIルートや、`/cart.js` のような拡張子付きの Shopify エンドポイントは対象外）。
function isPagePath(pathname: string): boolean {
  return !isUnlocalizedPath(pathname) && !/\.[a-z]+$/i.test(pathname);
}
```

`splitLocalePath` はデフォルトロケールをプレフィックスとして扱わないため、`/en/products/x` は `/en/en/products/x` に書き換えられ、404 になります。各ページのカノニカル URL は 1 つだけです。

### リンクをローカライズする

テンプレートの `Link` コンポーネント内で、文字列の href を [`useLocalizeHref`](/docs/react/nextjs/reference/hooks/use-localize-href) に通し、内部リンクで現在のロケールが維持されるようにします。`router.push`、`router.replace`、検索時のナビゲーションも同様に対応してください。

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

// 遷移先をアクティブなロケールのまま維持します。デフォルトロケールにはプレフィックスを付けません。
const localizeHref = useLocalizeHref();
const localizedHref = typeof href === "string" ? localizeHref(href) : href;
```

言語切り替え機能では、現在の URL のプレフィックスを置き換え、各言語名はその言語自体で表記します：

```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";

/** 各言語をその言語自身の表記（"Français"、"日本語"）で表示し、買い物客が自分の言語をすぐに見つけられるようにします。 */
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) => {
          // ロケールを切り替えるとルートレイアウトが変わるため、ページ全体をリロードして遷移します。
          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>
    </>
  );
}
```

### SEO メタデータを追加する

各ページに、そのページ自身のロケールのカノニカル URL と、すべてのロケールに対する `hreflang` の代替リンクを設定します。テンプレートの `lib/seo/index.ts` にある `buildAlternates` と `buildOpenGraph` は同期関数のためロケールを読み取れません。そこで、これらを非同期関数として `lib/seo/server.ts` に移動します。

1. `lib/seo/index.ts` で `buildCanonicalPath` をエクスポートし、`buildAlternates` と `buildOpenGraph` を削除します。
2. 非同期版を `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 と、すべてのロケールの hreflang 代替リンク。 */
   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,
       },
     };
   }
   ```

   `buildOpenGraph` も同じ方法で非同期にし、URL とロケールをローカライズします。Open Graph は `fr_FR` のような `language_TERRITORY` 形式の値を想定しているため、ここでロケールを変換します。`hreflang` と `<html lang>` には変換前のタグをそのまま使います。

   ```ts title="lib/seo/server.ts"
   /** Open Graph のロケールは fr_FR のような language_TERRITORY 形式。"fr" は最も一般的な地域に変換される。 */
   function toOpenGraphLocale(locale: string): string {
     const { language, region } = new Intl.Locale(locale).maximize();
     return `${language}_${region}`;
   }

   // buildOpenGraph 内で、既存のフィールドに加えて:
   const locale = await getLocale();
   return {
     url: localizePath(url, locale),
     locale: toOpenGraphLocale(locale),
     alternateLocale: locales.filter((code) => code !== locale).map(toOpenGraphLocale),
   };
   ```

   `maximize()` は、`fr` なら `fr_FR`、`zh-Hant` なら `zh_TW` のように、最も一般的な地域を選択します。この地域が実際の提供対象と異なるロケールがある場合は、変換処理を明示的なマッピングに置き換えてください。
3. 各ページの `generateMetadata` で、両方のヘルパーを `@/lib/seo/server` からインポートし、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]
     };
   }
   ```

   古いヘルパーを削除すると、`@/lib/seo` からインポートしたままのページは型チェックでエラーになります。これにより、修正漏れの呼び出し箇所をすべて洗い出せます。

サイトマップにもすべてのロケールの URL を記載し、それぞれにすべてのロケール分の `<xhtml:link rel="alternate" hreflang="...">` エントリを含めます。

## 各言語で Shopify にクエリを送信する [#shopify-language]

各ロケールを、テンプレートの Storefront API 呼び出しで指定する Shopify の国と言語にマッピングします。すべてのロケールはストアの単一のマーケットを共有するため、変わるのは言語だけです：

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

// すべての言語で共通の販売マーケット。通貨は常に Shopify のレスポンスから取得します。
const STORE_COUNTRY: CommerceLocale["country"] = "US";

// gt.config.json の各ロケールに対応する Storefront API の言語。
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 };
}

/** プロキシはこのクッキーにページのロケールを記憶するため、Hydrogen のハンドラー呼び出し（`/api/cart`）でも同じ言語が使われます。 */
export const LOCALE_COOKIE_NAME = "shop_locale";
/** プロキシは、解決済みのロケールをこのリクエストヘッダーに載せてサーバーアクションとカートのシードに渡します。 */
export const LOCALE_HEADER_NAME = "x-shop-locale";
```

各言語は、Storefront API の有効な `LanguageCode` でなければなりません ([ロケールコード](/docs/integrations/shopify/reference/locale-handling#locale-codes)を参照) 。[カタログの翻訳を Shopify に保存する](/docs/integrations/shopify/guides/translating-products#store-in-shopify)場合は、各言語をストアで公開しておく必要もあります。公開されていない言語に対しては、Shopify は原文を返します。ストアフロントにバンドルされた翻訳であれば、言語を公開する必要はありません。

### カタログの読み取りに言語を渡す

テンプレートのカタログ取得関数は、すでに `CommerceLocale` 型の `locale` オプションを受け取り、それを `@inContext` に渡しています。すべてのページとコンポーネントから、このロケールの値を渡してください。`"use 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";

/** ページの `[locale]` に対応する Shopify の国と言語。すべてのカタログ読み取りに渡します。 */
export async function getRequestCommerceLocale(): Promise<CommerceLocale> {
  return getCommerceLocale(await getLocale());
}

/** ルートパラメータを持たない動的コード（サーバーアクション、カートシード）向けに、同じ値をプロキシのヘッダーから取得します。 */
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(),
});
```

クイックショップや **Load more** などのサーバーアクションでは、ルートパラメータを読み取れません。代わりに `getForwardedCommerceLocale()` を使って値を取得してください。

### Hydrogen のハンドラーを同じ言語で実行する

Hydrogen のカートハンドラーと予測検索ハンドラーは、リクエストコンテキストの `i18n` から言語と国を取得します。`/api/cart` のようなリクエストにはロケールプレフィックスが含まれないため、プロキシはページのロケールをクッキーに保存し、ヘッダー経由でサーバーコンポーネントとサーバーアクションに渡します。

```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);
// 投機的なプリフェッチで、買い物客のカートや記憶済みの言語を変更してはならない。
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");

// Hydrogen のカートおよび検索ハンドラーは買い物客の言語で動作する。
const requestContext = createCustomerRequestContext(
  request,
  getCommerceLocale(requestLocale),
  requestLocale,
);

// handleShopifyRoutes がリクエストを処理しなかった場合:
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 } });

// レスポンスで:
if (isPageNavigation && rememberedLocale !== locale) {
  response.cookies.set(LOCALE_COOKIE_NAME, locale, { path: "/", sameSite: "lax" });
}

/** ページでは URL のロケールを、`/api/cart` のようなプレフィックスのない Shopify エンドポイントでは記憶済みのページロケールを返す。 */
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;
}
```

テンプレートにある `@/lib/auth/request` からの `forwardCustomerRefreshAttempt` のインポートは、そのまま残してください。これを `NextResponse.rewrite` と `NextResponse.next` のどちらを使うか判定する前に適用することで、リライトされたアカウントページにもリフレッシュ試行ヘッダーが付与され、失敗したトークンのリフレッシュが何度も再試行されるのを防げます。

`createCustomerRequestContext` で Shopify のロケールとページのロケールを受け取り、そのロケールの URL プレフィックスを `i18n.pathPrefix` として渡します。フランス語の URL では `/fr`、デフォルトロケールでは空文字列を指定します。これを指定しないと、Hydrogen は `/fr/products/hoodie?variant=123` を商品 URL として認識できず、バリアントのリダイレクトをスキップしてしまいます。その結果、リンク先のバリアントではなくデフォルトのバリアントがページに表示されることがあります。

```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 は、このプレフィックス配下の商品 URL を照合してリダイレクトします。
      pathPrefix: locale === defaultLocale ? "" : `/${locale}`,
    },
    request,
  });
}
```

デフォルト値があるため、テンプレート内の他の呼び出し元はそのまま動作します。サーバー側のカートシード (`lib/cart/server.ts`) でも、`shopConfig.localization` を読み取るのではなく、`getCommerceLocale` を渡すようにしてください。

## UI テキストを翻訳する [#copy]

テンプレートと同様に、テキストは各コンポーネント内にインラインで記述します。

* JSX は [`<T>`](/docs/react/reference/components/t) でラップします。動的な値は [`<Var>`](/docs/react/reference/components/var)、[`<Num>`](/docs/react/reference/components/num)、[`<Currency>`](/docs/react/reference/components/currency)、[`<DateTime>`](/docs/react/reference/components/datetime) のいずれかで、件数は [`<Plural>`](/docs/react/reference/components/plural) でラップします。[`<T>`](/docs/react/reference/components/t) はサーバーコンポーネントとクライアントコンポーネントの両方で動作します。
* `aria-label` の値、メタデータ、props などの文字列は、サーバーコンポーネントでは [`getGT`](/docs/react/nextjs/reference/functions/get-gt)、クライアントコンポーネントでは [`useGT`](/docs/react/reference/hooks/use-gt) を使って翻訳します。
* ソートオプションなど、コンポーネントの外部で定義するラベルは [`msg`](/docs/react/reference/functions/msg) でマークし、レンダリングする場所で [`getMessages`](/docs/react/nextjs/reference/functions/get-messages) または [`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">
  **警告:** CLI は、[`getGT()`](/docs/react/nextjs/reference/functions/get-gt) または [`useGT()`](/docs/react/reference/hooks/use-gt) の戻り値を代入した変数を追跡して、`gt()` の文字列を検出します。`const gt = await getGT()` のように、独立した行で代入してください。`Promise.all` の結果を分割代入して取得した `gt` 経由の文字列は抽出されず、ソース言語のままレンダリングされます。
</Callout>

Shopify の価格は、ロケールと Shopify が返す通貨コードを使って書式設定します。テンプレートの `formatPrice(money, locale)` は Hydrogen の `formatMoney` を使用するため、36.95 EUR はフランス語では `36,95 €` とレンダリングされます。

## カタログコンテンツを翻訳する [#catalog]

商品のタイトル、説明、オプションは Shopify から取得されます。これらの翻訳は、Shopify に保存して `@inContext(language:)` で読み込む (`getCommerceLocale` がすでにこの引数を渡しています) か、サンプルのストアフロントのように CLI を使ってストアフロントにバンドルするかのどちらかで扱えます ([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.
