はじめに
以前投稿した 「私的に日英翻訳APIを利用して、個人辞書を作ってみた」 の記事では、Microsoft Translatorは検討対象としたものの実際には利用していませんでした。
AzureにIDを登録したので、Translation APIを利用してみることにしました。
DeepL API, GCTと合わせて利用するためのRubyコードを追加したので、その顛末を残しておきます。
方針
既にGitHubに公開しているコードにMicrosoft Translatorを利用するためのコードを追加する。
準備作業・環境
- Microsoft Azure Portal (利用開始はこちらから)
参考資料
- Microsoft Translator 公式トップページ
- Microsoft Translator ドキュメント
- http://hamasyou.com/blog/2014/02/14/microsoft-translator-api/
- GitHub - Microsoft公式Rubyサンプル
- RubyDoc - HTTPClient
基本的なMicrosoft Translatorの使用方法
Azure Portalにログインし、検索ウィンドウで、"Translator" を検索すると、Martplaceのカテゴリに表示されますのでクリックし、必要事項を入力し、サービスを作成します。
サンプルコードの確認
公式マニュアルにはcurlコマンドを利用したアクセス例があるので、シェル・スクリプトで動作を確認しておきます。
# !/bin/bash
curl -X POST "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to=ja" \
-H "Ocp-Apim-Subscription-Key:${MSATS_KEY}" \
-H "Ocp-Apim-Subscription-Region:japaneast" \
-H "Content-Type: application/json" \
-d "[{'Text':'Hello, what is your name?'}]" | jq .
こんな感じのスクリプトにしました。MSATS_KEY環境変数にAzure Portalに表示されるキー(秘密鍵)を設定して実行します。ここではリージョンに"japaneast"を設定している前提で埋め込んでいます。違うようであれば適宜変更してください。
$ env MSATS_KEY="your-secret-key" bash ./test-ts.sh
出力はjqを利用しているので、次のようになります。インストールされていないければスクリプトの後段のパイプを削除すれば1行で出力されます。
[
{
"detectedLanguage": {
"language": "en",
"score": 1
},
"translations": [
{
"text": "こんにちは、あなたの名前は何ですか?",
"to": "ja"
}
]
}
]
Prerequisites
- Docker Runtime (Docker Desktop for WSL2/macOS, Docker CE for Linuxなど)
- Ruby 2.7以降
準備作業
以下のコマンドで作成したディレクトリの中で作業を行ないます。
$ git clone https://github.com/YasuhiroABE/docker-example-transapi.git
$ cd docker-example-transapi/
$ git branch -b azure
$ make gen-code
$ cd code/
基本的な動作を確認するためのサンプルスクリプト
code/ディレクトリの環境で動作するか確認のためのスクリプトを作成してみます。
# !/usr/bin/ruby
## lib/ruby/以下のライブラリを利用する
require 'bundler/setup'
Bundler.require
require 'securerandom'
ATS_URI = "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to=ja"
TEXT = [ { "Text" => "I'm working at home." } ].to_json
headers = {
'Ocp-Apim-Subscription-Key' => ENV.has_key?("MSATS_KEY") ? ENV["MSATS_KEY"] : "your-secret-key",
'Ocp-Apim-Subscription-Region' => ENV.has_key?("MSATS_REGION") ? ENV["MSATS_REGION"] : "japaneast",
'Content-type' => 'application/json',
'X-ClientTraceId' => SecureRandom.uuid
}
clnt = HTTPClient.new
res = clnt.post(ATS_URI, { :body => TEXT, :header => headers } )
puts res.body
exit(0)
今回は既に存在しているGemfileなどを利用することにします。
$ make bundle-install
$ env MSATS_KEY="your-secret-key" ruby ./test-ts.rb | jq .
出力結果は、テキストが違うだけで、ほぼbashスクリプトと同じになるはずです。
HTTPClientの使い方
今回はPOSTメソッドを利用する点が、これまでのDeepLや、Google Cloud Translate(GCT)と異なる点です。
ほぼGCTと同じような動作ですが専用のライブラリが必要になるほどの複雑さもないのが、Microsoft Translateの特徴かなと思います。
RubyとHTTPClientを利用する場合には、postメソッドでヘッダーをどう渡すのか別りにくく、httpclient.rbを直接読むのが早かったです。
JSON形式にデコードしたリクエストを本文(Body)として、ヘッダーを適切に設定すること、postメソッドの第二引数に:bodyと:headerをキーとするハッシュを渡すことで適切に動作します。
変更の方針
Microsoft TranslatorのAPIドキュメント に目を通すと、GETかPOSTかの違いはありますが、HTTPClientを使うのが良さそうなので、deepl.rbのコードを少し変更する方針で作業を行ないます。
Microsoft Translateの追加作業
$ cp -ip lib/deepl.rb lib/ats.rb
$ emacs lib/ats.rb
ここでサンプルで作成したコードを使って、変更したコードは以下のようになりました。
MSATS_KEY、MSATS_REGION環境変数を利用しています。本来はURIなどの他のパラメータも環境変数で渡せるようにするべきだと思いましたが、とりあえず省略しています。
# coding: utf-8
require 'securerandom'
class AzureTranslate < DataAPI
def initialize
@ats_host = ENV.has_key?('ATS_HOST') ? ENV['ATS_HOST'] : "api.cognitive.microsofttranslator.com"
@ats_path = ENV.has_key?('ATS_PATH') ? ENV['ATS_PATH'] : "/translate"
@ats_query = { "api-version" => "3.0" }
@ats_key = ENV.has_key?('MSATS_KEY') ? ENV['MSATS_KEY'] : ""
@ats_region = ENV.has_key?('MSATS_REGION') ? ENV['MSATS_REGION'] : "japaneast"
end
## query: :to => "ja"
## :from => "en"
## :text => ""
def query_api(query = {})
ret = {}
## prepare URI
q = @ats_query
q = q.merge({ :to => query[:to] }) if query.has_key?(:to)
q = q.merge({ :from => query[:from] }) if query.has_key?(:from)
query_string = URI.encode_www_form(q)
url = URI::HTTPS.build({:host => @ats_host, :path => @ats_path,
:query => query_string})
## prepare Header
headers = {
'Ocp-Apim-Subscription-Key' => @ats_key,
'Ocp-Apim-Subscription-Region' => @ats_region,
'Content-type' => 'application/json',
'X-ClientTraceId' => SecureRandom.uuid
}
## prepare Body
body = [ { "Text" => query.has_key?(:text) ? query[:text] : "" } ].to_json
begin
client = HTTPClient.new
resp = client.post(url, { :body => body, :header => headers } )
ret = JSON.parse(resp.body)
rescue
puts "[error] query_api: failed to query: #{:host},#{:path},#{:query}."
ret = {}
end
return ret
end
def query_trans(source, to_lang, src_lang)
q = {
:text => source,
:to => to_lang,
:from => src_lang
}
query_api(q)
end
def trans(string, to_lang: AzureTranslate::EN, src_lang: AzureTranslate::JA)
s = CGI.unescapeHTML(string)
ret = { :original_text => s,
:translate_text => "",
:to_lang => to_lang }
trans_result = query_trans(s, to_lang, src_lang)
ret[:translate_text] = trans_result[0]["translations"][0]["text"]
return ret
end
end
これを利用するために変更したコードは次のようになります。
default_api.rbファイルなどを変更しています。
--- a/_docker/default_api.rb
+++ b/_docker/default_api.rb
@@ -1,4 +1,3 @@
-require 'json'
MyApp.add_route('GET', '/dict', {
@@ -111,7 +110,8 @@ MyApp.add_route('GET', '/trans', {
deepl = DeepLAPI.new
gct = GoogleCloudTranslate.new
- for result in [["deepl", deepl.trans(param[:q])], ["gct", gct.trans(param[:q])]]
+ ats = AzureTranslate.new
+ for result in [["deepl", deepl.trans(param[:q])], ["gct", gct.trans(param[:q])], ["ats", ats.trans(param[:q])]]
MyUtil::add_data(result[0], ret[:results], result[1])
end
ret.to_json
その他にも、DeepLやGCTにアクセスする方法なども見直して、最終的に実行結果は次のようになりました。
{
"version": "1.0",
"results": [
{
"engine": "deepl",
"original_text": "\"はじめまして\"",
"translate_text": "\"Nice to meet you.\"",
"to_lang": "en"
},
{
"engine": "gct",
"original_text": "\"はじめまして\"",
"translate_text": "\""nice to meet you"\"",
"to_lang": "en"
},
{
"engine": "ats",
"original_text": "\"はじめまして\"",
"translate_text": "\"Nice to meet you\"",
"to_lang": "en"
}
]
}
これらの一連の変更は、最新版のGitHubのmainブランチに反映されています。