Delphi で gRPC を使う Google Cloud Translation

· コンポーネント

Google Cloud Translation v3 は、100 を超える言語間でテキストを翻訳し、あるテキストの言語を検出することもできます。このサービスは gRPC 経由で公開されており、sgcWebSockets Enterprise には TsgcGRPCClient の上に構築された型付き Translation gRPC クライアントが付属しています。Protocol Buffers を手作業で組み立てる代わりに、リクエストオブジェクトに値を入れ、1 つのメソッドを呼び出し、型付きレスポンスから直接翻訳済みテキストを読み取ります。この記事では、その仕組みを順を追って説明し、付属のデモにある実際の Delphi コードを示します。

仕組み

gRPC は HTTP/2 上でフレーミングされた Protocol Buffers なので、Translation クライアントは sgcWebSockets の HTTP/2 スタックの上に配置されます。translate.googleapis.com のポート 443 に TLS で向けた TsgcHTTP2Client を作成し、それを TsgcGRPCClient に割り当てると、gRPC コンポーネントがメッセージのフレーミング、ヘッダー、ストリーム処理を代行します。

Translation ユニット sgcGRPC_Google_Translation は、v3 API を反映した型付きメッセージクラスを追加します。翻訳用の TsgcGRPCTranslationTranslateTextRequest...TranslateTextResponse、言語検出用の DetectLanguage ペア、言語一覧用の GetSupportedLanguages ペアです。各リクエストは protobuf にシリアライズする ToBytes メソッドを公開し、各レスポンスはレスポンスを解析する LoadFromBytes メソッドを公開します。それらのバイト列を gRPC クライアントの Call メソッドに渡して、結果を読み取ります。

認証

Cloud Translation には Google サービスアカウントのトークンが必要です。デモはサービスアカウントの JSON キー(クライアントメール、秘密鍵 ID、秘密鍵、プロジェクト ID)で認証し、ベアラートークンを取得します。これがその後 gRPC メタデータとして追加され、すべての呼び出しに付随します。

GetGRPCClient.DefaultMetadata.Clear;
GetGRPCClient.DefaultMetadata.Add('authorization', 'Bearer ' + FToken);

注目すべき点が 1 つあります。自己署名のサービスアカウント JWT はオーディエンスにバインドされるため、translate.googleapis.com に受け入れられるには、トークンが Translation エンドポイントを対象にしている必要があります。デモは、トークンを要求する前に JWT.API_Endpointhttps://translate.googleapis.com/ に設定します。

クライアントのセットアップ

HTTP/2 トランスポートと gRPC クライアントは一度だけ組み合わせて接続します。Translation サービスはワイヤ上で protobuf を期待するため、チャネルのコンテンツタイプは grpcProto です。

uses
  sgcHTTP2_Client, sgcGRPC_Types, sgcGRPC_Client,
  sgcGRPC_Google_Translation;

FHTTP2Client := TsgcHTTP2Client.Create(nil);
FHTTP2Client.Host := 'translate.googleapis.com';
FHTTP2Client.Port := 443;
FHTTP2Client.TLS  := True;

FGRPCClient := TsgcGRPCClient.Create(nil);
FGRPCClient.Client := FHTTP2Client;
FGRPCClient.ChannelOptions.ContentType  := grpcProto;
FGRPCClient.ChannelOptions.Compression  := grpcNoCompression;

テキストの翻訳

翻訳するには、TsgcGRPCTranslationTranslateTextRequest に値を入れます。Parent リソースパスを設定し、Contents に 1 つ以上の文字列を追加し、ソースとターゲットの言語コードおよび MIME タイプを設定します。ToBytes でシリアライズし、完全修飾されたサービス名とメソッドとともに Call に渡します。親パスは v3 形式の projects/<project-id>/locations/global に従います。

var
  oRequest: TsgcGRPCTranslationTranslateTextRequest;
  oResponse: TsgcGRPCResponse;
begin
  oRequest := TsgcGRPCTranslationTranslateTextRequest.Create;
  try
    oRequest.Parent := 'projects/' + txtProjectId.Text + '/locations/global';
    oRequest.Contents.Add(txtSourceText.Text);
    oRequest.SourceLanguageCode := txtSourceLang.Text;   // e.g. 'en'
    oRequest.TargetLanguageCode := txtTargetLang.Text;   // e.g. 'es'
    oRequest.MimeType := 'text/plain';

    oResponse := GetGRPCClient.Call
      ('google.cloud.translation.v3.TranslationService', 'TranslateText',
      oRequest.ToBytes);
  finally
    oRequest.Free;
  end;
end;

レスポンスは TsgcGRPCResponse として返ってきます。StatusCodegrpcOK のとき、生の Data バイト列を型付きレスポンスに読み込み、各翻訳を読み取ります。各 Translation には TranslatedText が含まれ、ソース言語が自動検出された場合は DetectedLanguageCode も含まれます。

var
  oResponse: TsgcGRPCTranslationTranslateTextResponse;
  i: Integer;
begin
  oResponse := TsgcGRPCTranslationTranslateTextResponse.Create;
  try
    oResponse.LoadFromBytes(aData);
    for i := 0 to oResponse.TranslationCount - 1 do
    begin
      DoLog('Translated: ' + oResponse.Translation(i).TranslatedText);
      if oResponse.Translation(i).DetectedLanguageCode <> '' then
        DoLog('Detected: ' + oResponse.Translation(i).DetectedLanguageCode);
    end;
  finally
    oResponse.Free;
  end;
end;

言語の検出

ソース言語が分からない場合は、翻訳はいったん置いておき、DetectLanguage を呼び出します。親パスと検査する Content を指定して TsgcGRPCTranslationDetectLanguageRequest を構築し、候補となる言語を読み取ります。各候補には LanguageCodeConfidence 値が付きます。

oRequest := TsgcGRPCTranslationDetectLanguageRequest.Create;
try
  oRequest.Parent := GetParentPath;
  oRequest.Content := txtSourceText.Text;
  oRequest.MimeType := 'text/plain';

  oResponse := GetGRPCClient.Call
    ('google.cloud.translation.v3.TranslationService', 'DetectLanguage',
    oRequest.ToBytes);
finally
  oRequest.Free;
end;

検出レスポンスを解析すると、各候補の言語コードと信頼度スコアが得られます。

oResponse := TsgcGRPCTranslationDetectLanguageResponse.Create;
try
  oResponse.LoadFromBytes(aData);
  for i := 0 to oResponse.LanguageCount - 1 do
    DoLog('Language: ' + oResponse.Language(i).LanguageCode +
      ' (confidence: ' + FloatToStr(oResponse.Language(i).Confidence) + ')');
finally
  oResponse.Free;
end;

サポート言語の一覧取得

3 つ目の操作 GetSupportedLanguages は、サービスがサポートするすべての言語を返します。表示名をどの言語で取得したいかを DisplayLanguageCode に設定すると、各エントリは LanguageCode と人間が読める DisplayName、そしてソースまたはターゲットとして使用できるかどうかのフラグとともに返ってきます。

oRequest := TsgcGRPCTranslationGetSupportedLanguagesRequest.Create;
try
  oRequest.Parent := GetParentPath;
  oRequest.DisplayLanguageCode := 'en';

  oResponse := GetGRPCClient.Call
    ('google.cloud.translation.v3.TranslationService',
    'GetSupportedLanguages', oRequest.ToBytes);
finally
  oRequest.Free;
end;

提供状況

Translation gRPC クライアントは sgcWebSockets Enterprise エディションの一部で、Delphi と C++Builder にわたり Windows、macOS、Linux、iOS、Android で動作します。サービスアカウントの読み込みと 3 つすべての操作を備えた、すぐに実行できる完全なサンプルは Demos\21.GRPC\12.Translation にあり、基盤となる gRPC クライアントは gRPC Client 製品ページに記載されています。

ご質問やご意見はありますか? お問い合わせください。コードを書いた本人から返信いたします。