9
2

More than 5 years have passed since last update.

PhoenixでMicrosoft Translator テキスト APIを利用してみる

Last updated at Posted at 2018-06-07

(この記事は、fukuoka.ex(その2) Elixir Advent Calendar 2017 - Adventarの19日目、自然言語処理 Advent Calendar 2017 - Qiita の16日目です)

昨日は@zacky1972さんの「ZEAM開発ログv0.1.6.1 Elixir / Rustler 小ネタ集〜 Rustler でリストからバイナリに変換」でした!

本題

前回の記事「Phoenix + Vue.js入門」ではPhoenix + Vueの構成で、DBに保存したレコードを画面表示するところまでを書きました。

今回は、PhoenixからMicrosoft Translator テキスト APIを叩いて結果を取得するまでを書いてみます。環境は前回記事と同様です。

事前準備

事前準備として、azureにサインアップ後、APIを有効にしてkeyを取得する必要があります。

keyの発行については以下の記事が参考になりました。
Microsoft Translator テキスト API で、日本語を英語に翻訳するサンプル

keyの発行が済んだら、次に進みましょう。

ゴール

「日本語のタイトル・本文と、それを英語に翻訳したタイトルと本文が画面に表示される」をゴールとします :checkered_flag:

httpoisonの導入

PhoenixからAPIを叩くために、Elixir製のHTTP clientであるhttpoisonを導入します。json parserのpoisonも合わせて入れておきましょう。

GitHub - edgurgel/httpoison: Yet Another HTTP client for Elixir powered by hackney
GitHub - devinus/poison: An incredibly fast, pure Elixir JSON library

mix.exsのapplications, depsに追記します。

  def application do
    [
      extra_applications: [:logger, :runtime_tools, :httpoison, :poison]
    ]
  end

  defp deps do
    [
     ...
      {:poison, "~> 3.1"},
      {:httpoison, "~> 1.0"}
    ]
  end

変更後、お決まりのmix deps.getを実行しておきます。


$ mix deps.get

APIを叩くmoduleを用意する

APIを叩くコードを書きます。実際にAPIを叩く部分を、lib/translate_api.exモジュールとして切り出してみました。

defmodule TranslateApi do
  @moduledoc """
  Microsoft Translate Api Client written by Elixir.
  """
  @translate_api_endpoint "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0"
  @issue_token_endpoint "https://api.cognitive.microsoft.com/sts/v1.0/issueToken"

  @doc """
  get authorization token from the token service.
  """
  def get_token() do
    {:ok, response} =
      HTTPoison.post(@issue_token_endpoint, "{}", [
        {"Ocp-Apim-Subscription-Key", "<API key>"}
      ])

    response.body()
  end

  @doc """
  translate texts by microsoft translate api.

  ## Examples
  translate(["hello", "world"], %{"from" => "en", "to" => "ja"})
  """
  def translate(texts \\ [], params \\ %{"to" => "en", "from" => "ja"}) do
    data = Enum.map(texts, fn text -> %{"Text" => text} end)

    {:ok, %HTTPoison.Response{body: body}} =
      HTTPoison.post(
        @translate_api_endpoint,
        Poison.encode!(data),
        [
          {"Content-Type", "application/json"},
          {"Authorization", "Bearer " <> get_token()}
        ],
        params: params
      )

    Poison.decode!(body)
    |> Enum.map(fn %{"translations" => [%{"text" => text}]} -> text end)
  end
end

translate()にリストを渡せば、日本語→英語へ翻訳した結果が取得できるようにしました。

TranslateApi.translate(["こんにちは", "世界"])["Hello", "World"]

APIの実行にtokenが必要なので、token取得用の関数を用意しました。10分で有効期限が切れるようなのですが、都度叩く必要はないので、そこは今後の課題です(汗)

post値は以下のようなJSONフォーマットにする必要があるので、data = Enum.map(texts, fn text -> %{"Text" => text} end)の部分でpost用のデータを作ってます。

[
  {
    "Text" => "翻訳したい文字"
  },
  {
    "Text" => "翻訳したい文字"
  },
  {
    "Text" => "翻訳したい文字"
  },
  ...
]

APIを叩いてレコードの値を翻訳→Viewに渡す

article_controller.exのindex()を修正します。

  def index(conn, _params) do
    articles = Blog.list_articles()
    |> Enum.map(fn article ->
      # 翻訳の実行
      [en_title, en_body] = TranslateApi.translate([article.title, article.body])

      # 結果をmapに追加
      Map.merge(article, %{en_title: en_title, en_body: en_body})
    end)

    render(conn, "index.json", articles: articles)
  end

DBから取得したレコードの各行に対して、タイトルと本文に対して翻訳を実行し、articleのマップに追加しています。
レコード数分リクエストが走っちゃうので、ちゃんと作るなら実装に工夫が必要そうです。

実はこれだけではviewで値は利用できません。article_view.exrender("article.json")部分に、翻訳結果のフィールドを追記します。

  def render("article.json", %{article: article}) do
    %{id: article.id, title: article.title, body: article.body, en_title: article.en_title, en_body: article.en_body}
  end

これで、view側に値が渡るようになりました。渡ってきた値を表示してみましょう。

<div v-for="article in articles">
  <h2>{{ article.title }}({{ article.en_title }})</h2>
  <pre>{{ article.body }}</pre>
  <pre>{{ article.en_body }}</pre>
  <hr>
</div>

翻訳した結果が表示できました!

Hello PhoenixVueExample! 2018-06-07 22-30-29.png

まとめ

Microsoft Translator テキスト APIを利用して文字列の翻訳ができました。「外部APIで取得したJSONを、独自のフォーマットに変形する」といった処理はパターンマッチのおかげで書きやすいです♪

次回は、@kobatako さんの「Slack botで通知したい投稿日のものを通知する with Qiita API
」です!お楽しみに!


:stars::stars::stars::stars::stars: 満員御礼!Elixir MeetUpを6月末に開催します :stars::stars::stars::stars::stars:
※応募多数により、増枠しました
「fukuoka.ex#11:DB/データサイエンスにコネクトするElixir」を6/22(金)19時に開催します。特別ゲストも迎え、非常に濃い2時間になること間違いなしです!ご興味のある方はぜひご参加ください!

image.png

9
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
2