DeepLで翻訳機能を実装する

### 【概要】
---
翻訳機能のAPI実装になります。
今回は、`DeepL`での実装になりますので、`Google翻訳API`でない点についてご留意ください。
併せて、今回はDeepLの`Gem`を使用しますので、詳細は、下記からご確認お願いします。

https://github.com/DeepLcom/deepl-rb

### 【環境】
---

・Rails:3.2.2
・Ruby :7.0.8
・DeepL

### 【導入背景】
---
自身のアプリでレシピを取得するために、`EdamamsAPI`という海外のAPIを導入しました。
しかし、`英語`でリクエストを送信しない場合は、取得時に失敗するという問題点がありました。

`EdamamsAPI`については、下記になります。詳細が気になる方は下記の記事を参照してください。

https://qiita.com/Naoya_pro/items/a042cee805699ffb08d8

### 【Google翻訳APIとの相違点】
---
以上から、翻訳機能の実装に至ったのですが、翻訳機能について調査を行った結果、`GoogleAPI`と`DeepL`が候補に上がりました。相違点は下記になります。

|         | Google翻訳API | DeepL   |
|:-------:|:---------:|:-------:|
| メリット  | サポート言語が豊富・無料の文字数が多い|Google翻訳より翻訳精度が高い|
| デメリット |翻訳制度が悪い           | 無料の文字数が少ない        |

翻訳精度に関しては、下記のサイトが参考になるかと思います。

https://www.science.co.jp/nmt/blog/20529/

今回の実装では、リクエストの数を考えた時に`DeepL`の無料枠で問題ない範囲でしたので、後者で実装することにいたしました。

### 【DeepL側での準備】
---
ここからは実装フェーズになります。
まずは、下記から、ログインして登録などを行なってください。また、不明点がある場合、下記の手順書を参考にしてください。

https://www.deepl.com/translator

【手順書】
https://auto-worker.com/blog/?p=5030

以上が、DeepL側で実装する内容になります。ここからは、ソースを編集していきます。


### 【DeepLの実装_SRC編】
---

上記で取得した`認証key`の情報を使用して、APIを実施塩ていきたいと思います。なお、今回の実装では`dotenv-rails`のgemを使用して実装しておりますが、`credential`で実装していたただいても構いません。

### 【DeepLの実装_下準備】
---
1.`dotenv-rails`のgrm取得

```ruby:gemfile
gem 'dotenv-rails'
```

```ruby:ターミナル
bundle install
```

2.`env`ファイルに`認証KEY`の情報をセット

```:ターミナル
touch .env //.envファイルを作成
```

```ruby:ターミナル
DEEPL_KEY='先ほど取得したDeepL-keyの内容'
```

3.`deepl`のgemを取得
```ruby:gemfile
gem 'deepl-rb', require: 'deepl'
```

```ruby:ターミナル
bundle install
```

4.`httpclient`通信するためのgemを取得
```ruby:gemfile
gem 'httpclient'
```

```ruby:ターミナル
bundle install
```

### 【DeepLの実装】
---

ここからが、実装になります。

```ruby:app/controllers/edamams_controller.rb
class EdamamsController < ApplicationController
  skip_before_action :require_login, only: %i[index]

  require 'httpclient' # HTTP通信を行うためのライブラリ(API使用時に必要)
  require 'cgi' 
  require 'deepl' # gemを使用するため、ここで読み込み処理をかける

  def index
    query = params[:search].blank? ? 'nodata' : params[:search]

    translated_query = translate_to_english(query)

    client = HTTPClient.new
    res = client.get("https://api.edamam.com/api/recipes/v2?type=public&q=#{CGI.escape(translated_query)}&app_id=#{ENV.fetch('application_id', nil)}&app_key=#{ENV.fetch('application_keys', nil)}")
    @foods = JSON.parse(res.body)
  end

  private

  def translate_to_english(query)
    api_key = ENV['DEEPL_KEY']
    uri = ("https://api-free.deepl.com/v2/translate")
    client = HTTPClient.new
    client.ssl_config.clear_cert_store
    client.ssl_config.set_trust_ca("/etc/ssl/certs")
    params = {
      auth_key: api_key,
      text: query,
      target_lang: "EN"
    }

    response = client.get(uri, query: params)
    parsed_response = JSON.parse(response.body)

    translation_text = parsed_response["translations"][0]["text"]
    translation_text
  end

end
```

```ruby:app/views/edamams/index.html.erb
<div class="container">
  <div class="d-flex">
    <h3 class="text-center">海外のレシピ検索</h3>
    <div class="text-center"><%= render 'shared/how_to_edamams_api' %></div>
  </div>
  
  <%= form_with(url: edamams_path, method: :get, class: "search-form") do |f| %>
    <%= f.text_field :search, placeholder: "食材で検索してください", class: 'form-control' %>
    <%= f.submit '検索する', class: "btn-brown" %>
  <% end %>
</div>

<% if @foods.present? && @foods["hits"].present? %>
  <div class='m-3'>
    <% @foods["hits"].first(12).each_slice(3) do |recipes| %>
      <div class="row mb-3">
        <% recipes.each do |recipe| %>
        <div class='col-md-4'>
          <div class="card" style="background-color: #FFEEC1">
            <div class="card-body text-center">
              <h4 class="card-title" style='color: #6B493D; font-size: 16px;'><%= translate_to_japanese(recipe["recipe"]["label"]) %></h4>
              <%= image_tag recipe["recipe"]["image"], class: "card-img-bottom mx-auto", alt: "Card image", style: "width:70%; height: 70%; object-fit: contain;" %>
              <div class="text-center mt-3">
                <%= link_to '海外公式サイトへ', recipe["recipe"]["url"], class: "btn btn-warning", style: "color: #6B493D; font-size: 14px;" %>
              </div>
            </div>
          </div>
        </div>
      <% end %>
      </div>
    <% end %>
  </div>
<% end %>
```

今回の実装方針としては、
1.`入力された値を日本語化してEdamamsAPIとして渡す`
2.`EdamamsAPIから取得したjsonから英語を日本語に翻訳する`

上記の2点がポイントになります。


### 【ポイント１】
---
```ruby:app/controllers/edamams_controller.rb
require 'httpclient'
require 'deepl'

# 上記の記述でDeepL及びAPIを実行できるようにする。それぞれrequire対象の詳細についてはgithub参照。

def translate_to_english(query)
    api_key = ENV['DEEPL_KEY']
    uri = ("https://api-free.deepl.com/v2/translate")
    client = HTTPClient.new
    # 上記から、環境変数などのセットを行う。(有料プランの場合、uriが上記ではなくなります。)
    
    client.ssl_config.clear_cert_store
    client.ssl_config.set_trust_ca("/etc/ssl/certs")
    # SSLなど証明書の情報をここで設定しておく。
    
    params = {
      auth_key: api_key,
      text: query,
      target_lang: "EN"
    }
    # 取得した日本語を英語に変換するので"EN"

    response = client.get(uri, query: params)
    parsed_response = JSON.parse(response.body)

    translation_text = parsed_response["translations"][0]["text"]
    translation_text
    # json.parse後の中身に関して後述。
  end
```

実際に、どのような挙動をしているのか確認しましょう。

```ruby:app/controllers/edamams_controller.rb
def translate_to_english(query)
# 割愛
    translation_text = parsed_response["translations"][0]["text"]
    translation_text
    
    Rails.logger.info(parsed_response["translations"])
    # JSONの中身を検証。確認後に上記を削除します。

  end
```

```ruby:log/development.log
  Parameters: {"search"=>"バナナ", "commit"=>"検索する"} # 検索値として、【バナナ】を確認
[{"detected_source_language"=>"JA", "text"=>"banana"}]# 一部抜粋。json形式内のtextがbananaに翻訳されたことを確認
```

以上、コントローラから日本語から英語に変換する作業を紹介しました。
今度は英語から日本語に変換する作業を紹介します。

### 【ポイント2】
---
今回は、viewの表示をhelper配下にmoduleで日本語表記になるように、設定したいと思います。

まずは、呼び出し元を下記のようにします。

```ruby:app/views/edamams/index.html.erb
<% @foods["hits"].first(12).each_slice(3) do |recipes| %>
      <div class="row mb-3">
        <% recipes.each do |recipe| %>
            
            <h4 class="card-title" style='color: #6B493D; font-size: 16px;'><%= translate_to_japanese(recipe["recipe"]["label"]) %></h4> #ここで、moduleから呼び出せるように命名する。
```

(edamamsAPIの引数の詳細については本筋と逸れるので割愛します。確認したい場合、edamamsAPI取得後に、local環境を用意し、postmanなどで検証してください。)

https://qiita.com/knidik3/items/b5019b8289417fa1716a

```ruby:app/helpers/translations_helper.rb
module TranslationsHelper
    def translate_to_japanese(query)
      api_key = ENV['DEEPL_KEY']
      uri = "https://api-free.deepl.com/v2/translate"
      client = HTTPClient.new
      # 上記で環境変数の設定
      
      client.ssl_config.clear_cert_store
      client.ssl_config.set_trust_ca("/etc/ssl/certs")
      # SSLの設定
      
      params = {
        auth_key: api_key,
        text: query,
        target_lang: "JA"
      }
      # 英語から日本語に変換したいので、JAに設定。
  
      response = client.get(uri, query: params)
      parsed_response = JSON.parse(response.body)
  
      translation_japanese = parsed_response["translations"][0]["text"]
      translation_japanese
    end
  end
```

先ほどとの相違点としては、view側で表示したいので
1.helperに設定したこと
2.`target_lang: "JA"`で日本語翻訳するように設定したこと

上記の2点になります。

ここまでで、今回の実装要件を満たすことができました！
完成系が下記になります。

[![レシピ検索機能](https://i.gyazo.com/f1f3becda4d423b39d51ebd96a24b973.gif)](https://gyazo.com/f1f3becda4d423b39d51ebd96a24b973)

### 【引用】
---

https://github.com/DeepLcom/deepl-rb

https://www.science.co.jp/nmt/blog/20529/

https://qiita.com/Naoya_pro/items/a042cee805699ffb08d8

https://qiita.com/knidik3/items/b5019b8289417fa1716a