# General Translation Integrations: Shopify の商品を翻訳する
URL: https://generaltranslation.com/ja/docs/integrations/shopify/guides/translating-products.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: General Translation で Shopify の商品コンテンツを翻訳し、Shopify に保存するか、ストアフロントにバンドルする方法を説明します。

商品のタイトル、説明、オプションはコードではなく Shopify から取得されるため、`gt-react` や `gt-next` では翻訳されません。これらは一度だけ翻訳し、次の 2 つのいずれかの場所から配信します。

## 使い分けの指針 [#choosing]

* マーチャントがカタログを頻繁に編集する場合は、**翻訳を Shopify に保存します**。スクリプトが Admin API 経由で翻訳を登録し、ストアフロントは `@inContext(language:)` でそれを読み取ります。カタログを編集しても、ストアフロントを再ビルドする必要はありません。
* Shopify Admin へのアクセス権がない場合や、カタログの翻訳をコードと一緒にレビュー・デプロイしたい場合は、**翻訳をストアフロントにバンドルします**。CLI は、エクスポートした JSON ファイルをサイト上のテキストと同じ実行で翻訳します。サンプルのストアフロントはこの方法を採用しています。

*注：ダッシュボードには、ホスト型の Shopify 接続機能はありません。どちらの方法でも、ご自身の Shopify と General Translation の認証情報を使って、エクスポートを自分で実行する必要があります。*

## a) Shopify に翻訳を保存する [#store-in-shopify]

Shopify は、リソースの各フィールドの翻訳をロケールごとに保存します。スクリプトで Admin API を使って原文をエクスポートし、[`translateMany`](/docs/platform/core/reference/gt-class-methods/translation/translate-many) で翻訳したうえで、その結果を Shopify に登録します。

### 始める前に

以下を用意してください。

* Shopify 管理画面の **Settings &gt; Languages** で、対象言語を追加して公開しておくこと。
* ストアにインストールされたアプリの Shopify Admin API アクセストークン。`read_translations`、`write_translations`、`read_locales` スコープが付与されている必要があります。Storefront API トークンでは翻訳を書き込めません。
* `generaltranslation` のインストールと、**Runtime translation** 権限 (`project:translations:generate`) を持つプロジェクトの APIキー (Core ライブラリの[クイックスタート](/docs/platform/core/quickstart)を参照) 。

Admin トークンと APIキーはスクリプトの環境変数に保存してください。ストアフロントの環境変数やブラウザ向けバンドルには絶対に含めないでください。

### ソースコンテンツをエクスポートする

翻訳対象のリソースタイプごとに `translatableResources` をクエリします。各フィールドからは、`key`、翻訳元の `value`、そしてその原文を一意に識別する `digest` が返されます。

```graphql title="Admin API"
query CatalogExport($type: TranslatableResourceType!, $after: String) {
  translatableResources(first: 50, after: $after, resourceType: $type) {
    nodes {
      resourceId
      translatableContent { key value digest locale }
      translations(locale: "fr") { key value outdated }
    }
    pageInfo { hasNextPage endCursor }
  }
}
```

`PRODUCT`、`PRODUCT_OPTION`、`PRODUCT_OPTION_VALUE` などのリソースタイプを指定し、`endCursor` を使って結果をページングします。ダイジェストは翻訳の登録に必要なので、保存しておいてください。

### General Translation で翻訳する

商品タイトル、商品タイプ、SEO タイトルと説明文、オプション名とオプション値などのプレーンテキストフィールドを [`translateMany`](/docs/platform/core/reference/gt-class-methods/translation/translate-many) に送信します。短いラベルも正しく翻訳されるように、[`metadata.context`](/docs/platform/core/reference/types/entry-metadata#context) を追加してください。

```ts title="scripts/translate-catalog.ts"
import { GT } from 'generaltranslation';

const gt = new GT({
  apiKey: process.env.GT_API_KEY,
  projectId: process.env.GT_PROJECT_ID,
  sourceLocale: 'en',
});

const results = await gt.translateMany(
  fields.map((field) => ({
    source: field.value,
    metadata: {
      context: `Shopify ${field.resourceType} ${field.key}. Preserve product brand names, measurements, and model numbers.`,
    },
  })),
  { sourceLocale: 'en', targetLocale: 'fr' }
);
```

結果は入力と同じ順序で返され、成功・失敗は各結果ごとに個別に報告されます。失敗したエントリは登録せず、再試行してください。

商品の `body_html` などのリッチテキストフィールドには HTML が含まれます。[`metadata.fileFormat`](/docs/platform/core/reference/types/entry-metadata#file-format) によるフォーマット対応の翻訳は Markdown と MDX ドキュメントにしか対応していないため、マークアップを保持する処理を挟まずに生の HTML を [`translateMany`](/docs/platform/core/reference/gt-class-methods/translation/translate-many) に送信しないでください。

### Shopify に翻訳を登録する

各翻訳は、Admin API の [`translationsRegister`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/translationsRegister) ミューテーションで書き込みます。翻訳元のダイジェストを `translatableContentDigest` として渡してください。これにより、エクスポート後に翻訳元が変更された翻訳は Shopify 側で拒否されます。

```graphql title="Admin API"
mutation CatalogRegister($resourceId: ID!, $translations: [TranslationInput!]!) {
  translationsRegister(resourceId: $resourceId, translations: $translations) {
    translations { key locale value outdated }
    userErrors { field code }
  }
}
```

```json title="Variables"
{
  "resourceId": "gid://shopify/Product/123",
  "translations": [
    {
      "locale": "fr",
      "key": "title",
      "value": "Sweat à capuche en coton",
      "translatableContentDigest": "<digest from export>"
    }
  ]
}
```

`locale` には Shopify のロケールコードを使用してください。これは General Translation のロケールと異なる場合があります ([ロケールコード](/docs/integrations/shopify/reference/locale-handling#locale-codes)を参照) 。

<Callout type="warn">
  `translationsRegister` は、そのキーとロケールに対応する既存の翻訳を上書きします。マーチャントが管理画面で入力した翻訳や、他のアプリが書き込んだ翻訳も対象になります。書き込む前に、現在の翻訳とスクリプトが前回登録した値を比較し、自分で書き込んでいないフィールドはスキップしてください。
</Callout>

### ストアフロントで翻訳を読み取る

カタログのクエリに `@inContext(language: $language)` を追加し、ロケールに対応する Shopify の `LanguageCode` を渡します。Shopify は、翻訳済みの値があればその値を、なければソース値を返します。Hydrogen の Storefront クライアントは、リクエストコンテキストから `$language` を自動で設定します ([React Router を使用した Shopify Hydrogen のローカライズ](/docs/integrations/shopify/guides/localizing-react-router#storefront-language)を参照) 。

翻訳されたカタログのコンテンツは、Shopify とストアフロントのキャッシュが更新されると表示されます。再デプロイは不要です。

### 翻訳を最新の状態に保つ

マーチャントが商品を編集すると、そのフィールドのダイジェストが変わり、Shopify は既存の翻訳を `outdated` としてマークします。エクスポートを再実行し、ダイジェストが変わったフィールドと未翻訳のフィールドだけを翻訳して、結果を登録してください。

## b) 翻訳をストアフロントにバンドルする [#bundle]

*注: 以下の例は Next.js のサンプルストアフロントからの抜粋です。このストアフロントでは、[Next.js ガイド](/docs/integrations/shopify/guides/localizing-nextjs)のロケールのセットアップを使用しています。*

Storefront API を使ってカタログのテキストをエクスポートし、原文そのものをキーにします。短いラベル(タイトル、オプション名と値、代替テキスト)とリッチテキスト(商品説明、ページ本文)は別々のファイルに書き出し、ブラウザに届くのはラベルだけになるようにします。キーを一致させるため、リッチテキストはストアフロントがレンダリング時に行うのと同じ方法でサニタイズしてください。

```ts title="scripts/export-catalog.ts"
const labels = new Set<string>();
const content = new Set<string>();
for (const product of catalog.products.nodes) {
  labels.add(product.title).add(product.productType);
  content.add(product.description).add(sanitizeShopifyHtml(product.descriptionHtml));
  for (const image of product.images.nodes) if (image.altText) labels.add(image.altText);
  for (const option of product.options) {
    labels.add(option.name);
    for (const value of option.optionValues) labels.add(value.name);
  }
}

async function writeCatalog(file: string, texts: Set<string>): Promise<number> {
  const entries = [...texts].filter((text) => text.trim()).sort((a, b) => a.localeCompare(b, "en"));
  await writeFile(
    new URL(`../catalog/en/${file}`, import.meta.url),
    `${JSON.stringify(Object.fromEntries(entries.map((text) => [text, text])), null, 2)}\n`,
  );
  return entries.length;
}
```

エクスポートしたファイルを、`gt.config.json` の `files.json` 配下に既存の `files.gt` エントリと並べて追加します。これにより、[`npx gt translate`](/docs/cli/reference/commands/translate) の1回の実行で、UI テキストの翻訳と同時に `catalog/<locale>/labels.json` と `catalog/<locale>/content.json` も書き出されます：

```json title="gt.config.json"
{
  "files": {
    "json": { "include": ["catalog/[locale]/labels.json", "catalog/[locale]/content.json"] }
  }
}
```

現在の原文をキーにして翻訳を検索し、見つからない場合は原文にフォールバックします。こうすることで、最後のエクスポート以降に Shopify で編集された内容も最新のテキストで表示されます：

```ts title="lib/catalog/index.ts"
/** マーチャントのテキストを現在の英語の翻訳元テキストで検索し、翻訳が存在しない場合は翻訳元テキストをそのまま返します。 */
export function translateCatalogText(catalog: Catalog, text: string): string;
export function translateCatalogText(catalog: Catalog, text: string | undefined): string | undefined;
export function translateCatalogText(catalog: Catalog, text: string | undefined) {
  return (text && catalog[text]) || text;
}
```

```ts title="lib/catalog/server.ts"
/** カートなどのクライアントコンポーネント向けの短いラベル（タイトル、オプション名と値、代替テキスト）。 */
export function getCatalogLabels(locale: string): Catalog {
  return catalogs[locale]?.labels ?? {};
}

/** リクエストのロケールで Shopify のマーチャントテキストを翻訳する関数を返します。 */
export async function getCatalogText() {
  const catalog = catalogs[await getLocale()];
  const merged: Catalog = catalog ? { ...catalog.content, ...catalog.labels } : {};
  function ct(text: string): string;
  function ct(text: string | undefined): string | undefined;
  function ct(text: string | undefined) {
    return translateCatalogText(merged, text);
  }
  return ct;
}
```

* 翻訳済みのテキストは、翻訳元のテキストとして直接記述するのではなく、[`<Var>`](/docs/react/reference/components/var) を介して [`<T>`](/docs/react/reference/components/t) に渡してください。
* カートや予測検索などのクライアントコンポーネントでは、レイアウト内のコンテキストプロバイダーに `getCatalogLabels(locale)` を渡してください。リッチテキストや他のロケールのカタログはブラウザに送信しないでください。
* オプション値をバリアントの選択、URL の構築、Shopify への送信に使う箇所では、値をソース言語のまま保持してください。翻訳するのは表示用のラベルだけです。
* ブランド用語はそのまま残し、衣料品を表す語は翻訳するなど、商品名の翻訳方針を指定するには、ダッシュボードでプロジェクトのコンテキストグループに用語集の用語とカスタムプロンプトを追加してください。

## Next steps

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

## Sitemap

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