Translation
Translation翻訳

翻訳

任意のプロバイダーのAPIを使用してフィールドの値を翻訳するディレクティブ @strTranslate

説明

String 型の任意のフィールドにディレクティブ @strTranslate を追加することで、そのフィールドを目的の言語に翻訳できます。

たとえば、次のクエリは投稿の title フィールドと excerpt フィールドを英語からフランス語に翻訳します(デフォルトのAPIプロバイダーを使用):

{
  posts {
    enTitle: title
    frTitle: title @strTranslate(from: "en", to: "fr")
 
    enExcerpt: excerpt    
    frExcerpt: excerpt @strTranslate(from: "en", to: "fr")
  }
}

...結果として以下が生成されます:

{
  "data": {
    "posts": [
      {
        "enTitle": "Welcome to a single post full of blocks!",
        "frTitle": "Bienvenue dans un poste unique plein de blocs !",
        "enExcerpt": "When I look back on my past and think how much time I wasted on nothing, how much time has been lost in futilities, errors, laziness, incapacity to live; how little I appreciated it, how many times I sinned against my heart and soul-then my heart bleeds. Life is a gift, life is happiness, every…",
        "frExcerpt": "Quand je repense à mon passé et que je pense au temps que j'ai perdu pour rien, au temps perdu en futilités, en erreurs, en paresse, en incapacité de vivre ; combien je l'ai peu apprécié, combien de fois j'ai péché contre mon cœur et mon âme, alors mon cœur saigne. La vie est un cadeau, la vie est un bonheur, chaque…"
      },
      {
        "enTitle": "Explaining the privacy policy",
        "frTitle": "Expliquer la politique de confidentialité",
        "enExcerpt": "Our privacy policy is at https://gato-graphql-pro.lndo.site/privacy/, and we are based in Carimano.",
        "frExcerpt": "Notre politique de confidentialité se trouve sur https://gato-graphql-pro.lndo.site/privacy/, et nous sommes basés à Carimano."
      },
      {
        "enTitle": "HTTP caching improves performance",
        "frTitle": "La mise en cache HTTP améliore les performances",
        "enExcerpt": "Categories Block Latest Posts Block Did you know? We are not rich by what we possess but by what we can do without. Patience is the strength of the weak, impatience is the weakness of the strong.",
        "frExcerpt": "Catégories Bloquer les derniers messages Bloquer Le saviez-vous ? Nous ne sommes pas riches de ce que nous possédons mais de ce dont nous pouvons nous passer. La patience est la force du faible, l'impatience est la faiblesse du fort."
      }
    ]
  }
}

Schema Configuration

ディレクティブ @strTranslate には3つの引数が必要です:

  • provider: 翻訳に使用するプロバイダー
  • from: テキストの言語コード
  • to: 翻訳先の言語コード

これらのプロパティのデフォルト値は、設定ページの「Schema Configuration => Translation」タブで定義できます。クエリ内でいずれかの引数が指定されていない場合、これらの値が使用されます:

{
  posts {
    title @strTranslate
  }
}

また、デフォルト値を定義すると、GraphQLスキーマ内の対応する引数が必須でなくなります。

デフォルトでは、from のデフォルト値は WordPress で使用されている言語です。

設定による方法

設定ページの対応する入力欄に provider/from/to フィールドを入力し、「Save Changes (All)」をクリックします:

デフォルトの 'provider' と 'from'・'to' 言語の設定
デフォルトの 'provider' と 'from'・'to' 言語の設定

wp-config.php での設定

wp-config.php に定数を追加します:

  • GATOGRAPHQL_TRANSLATION_DEFAULT_PROVIDER
  • GATOGRAPHQL_TRANSLATION_DEFAULT_FROM_LANG_CODE
  • GATOGRAPHQL_TRANSLATION_DEFAULT_TO_LANG_CODE

例:

define( 'GATOGRAPHQL_TRANSLATION_DEFAULT_TO_LANG_CODE', 'fr' );

環境変数による方法

以下の環境変数を定義します:

  • TRANSLATION_DEFAULT_PROVIDER
  • TRANSLATION_DEFAULT_FROM_LANG_CODE
  • TRANSLATION_DEFAULT_TO_LANG_CODE

同期/非同期の多言語翻訳

ディレクティブ @strTranslate は翻訳する言語ごとにリクエストを送信します。複数言語への翻訳時に、リクエストを非同期(並列)で送信するか、同期(逐次)で送信するかを選択できます。

同期リクエストと非同期リクエスト:

  • 同期: 各翻訳リクエストは、前のリクエストが完了してから開始されます。低速ですが、レート制限に対して安全です。
  • 非同期: すべての翻訳リクエストが同時に送信されます。高速ですが、一度に多くのリクエストが送信された場合にレート制限に達する可能性があります。
sync/asyncモードを使用して複数言語を一度に翻訳する
sync/asyncモードを使用して複数言語を一度に翻訳する

リクエストタイムアウトと接続タイムアウト

サードパーティのプロバイダーを通じて長いドキュメントを翻訳すると時間がかかる場合があり、上流サーバーがハングすると、PHP自身がリクエストを終了させるまでPHPワーカーを占有してしまいます。

Webサーバーは max_execution_time ディレクティブ(php.ini で設定、またはホスティングのコントロールパネルから設定 — cPanelでは通常 「Select PHP Version」→「Options」 で確認でき、SiteGround / Kinsta Engine などのマネージドホストではPHP設定から確認できます)を通じて、すべてのPHPリクエストの最大実行時間を適用します。

このプラグインは設定ページの Plugin Configuration > Translation の下に2つのオプションを提供しています:

  • Request timeout: 翻訳プロバイダーからの完全なレスポンスを待つ最大時間(秒単位)
  • Connection timeout: 翻訳プロバイダーへの接続確立を待つ最大時間(秒単位)
翻訳のRequest timeoutとConnection timeoutの設定
翻訳のRequest timeoutとConnection timeoutの設定

これらの値はサーバーの max_execution_time より低く保つ必要があります。そうすることで、翻訳が止まった場合に汎用的なサーバータイムアウト(HTTP 502 / 504、または「Maximum execution time of N seconds exceeded」の空白ページ)が発生するのではなく、制御されたエラーメッセージで適切に失敗します。翻訳が定期的にタイムアウトする場合は、これら 両方 の値とサーバーの max_execution_time を同時に増やしてください。

APIリクエストのデバッグ

翻訳プロバイダー(ChatGPTClaudeGoogle Translate など)に送信されるリクエストとそのレスポンスをデバッグするには、ログの設定 で 🔵 Info 重要度レベルを有効にします。

有効にすると、ログには翻訳プロバイダーとのすべてのやり取りが api-requests エントリの下に保存されます。

ログ内のAIリクエスト
ログ内のAIリクエスト

ログに記録される内容

AIプロバイダーの場合、api-requests エントリには以下の詳細情報が含まれます:

  • 翻訳プロバイダーに送信されたプロンプト
  • 受信した完全なレスポンス
  • 通信中に発生したエラーや問題
  • 使用されたモデル
  • 使用されたトークン数
ログ内のAIリクエストの詳細
ログ内のAIリクエストの詳細

たとえば、以下の「Additional context」JSONはChatGPTに送信されたリクエストとそのレスポンスの詳細を示しています:

{
  "request": {
    "model": "gpt-4o-mini",
    "messages": [
      {
        "role": "system",
        "content": "You are a language translator."
      },
      {
        "role": "user",
        "content": "I'm working on internationalizing my application.\n\nI've created a JSON with sentences in English. Please translate the sentences to Spanish from .\n\nIf a sentence contains HTML, do not translate inside the HTML tags. Keep emojis exactly as they are, do not translate them.\n\nThis is the JSON:\n\n[\"Welcome to a single post full of blocks!\",\"Repeating the privacy policy\",\"Explaining the privacy policy\",\"HTTP caching improves performance\",\"Public or Private API mode, for extra security\",\"GraphQL or REST? Why not both?\",\"Customize the schema for each client\",\"Nested mutations are a must have\",\"Working on flat chain syntax next\",\"Released v0.6, check it out\"]"
      }
    ],
    "response_format": {
      "type": "json_schema",
      "json_schema": {
        "name": "translation_response",
        "strict": true,
        "schema": {
          "type": "object",
          "properties": {
            "translations": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "required": [
            "translations"
          ],
          "additionalProperties": false
        }
      }
    }
  },
  "response": {
    "id": "chatcmpl-BbjNiuO5Si1vhalfIXYU0hWiCmg12",
    "object": "chat.completion",
    "created": 1748332282,
    "model": "gpt-4o-mini-2024-07-18",
    "choices": [
      {
        "index": 0,
        "message": {
          "role": "assistant",
          "content": "{\"translations\":[\"¡Bienvenido a una publicación única llena de bloques!\",\"Repitiendo la política de privacidad\",\"Explicando la política de privacidad\",\"La caché HTTP mejora el rendimiento\",\"Modo API Público o Privado, para mayor seguridad\",\"¿GraphQL o REST? ¿Por qué no ambos?\",\"Personaliza el esquema para cada cliente\",\"Las mutaciones anidadas son imprescindibles\",\"Próximamente trabajando en la sintaxis de cadena plana\",\"Lanzada la versión v0.6, ¡échale un vistazo!\"]}",
          "refusal": null,
          "annotations": []
        },
        "logprobs": null,
        "finish_reason": "stop"
      }
    ],
    "usage": {
      "prompt_tokens": 184,
      "completion_tokens": 112,
      "total_tokens": 296,
      "prompt_tokens_details": {
        "cached_tokens": 0,
        "audio_tokens": 0
      },
      "completion_tokens_details": {
        "reasoning_tokens": 0,
        "audio_tokens": 0,
        "accepted_prediction_tokens": 0,
        "rejected_prediction_tokens": 0
      }
    },
    "service_tier": "default",
    "system_fingerprint": "fp_34a54ae93c"
  }
}