日本語で書かれたLocalizable.stringをGoogle Translation APIで翻訳してstructファイルを自動作成するツールをgolangで作る

## はじめに


以前Qiitaの記事でも書いた「[Go言語でiOS開発ツールを作成する：Localizable.stringsファイルからenumを生成する](https://qiita.com/BlueEventHorizon/items/88dcea8ceeec2a07f351)」というツールを拡張して、

「日本語で書かれたLocalizable.stringをGoogle Translation APIで翻訳してstructファイルを自動作成するツールをgolangで作る」

ことに挑戦してみました。

```
挑戦結果のGitHubリポジトリーはこちら
https://github.com/BlueEventHorizon/EnumGenerator
```

## 結果

例えば下記のようなLocalizable.stringがあったとすると、

```
"あなたの心が正しいと思うことをしなさい。どっちにしたって批判されるのだから。" = "あなたの心が正しいと思うことをしなさい。どっちにしたって批判されるのだから。";
"前進をしない人は、後退をしているのだ。" = "前進をしない人は、後退をしているのだ。";
"どんなに悔いても過去は変わらない。どれほど心配したところで未来もどうなるものでもない。いま、現在に最善を尽くすことである。" = "どんなに悔いても過去は変わらない。どれほど心配したところで未来もどうなるものでもない。いま、現在に最善を尽くすことである。";
"最も重要な決定とは、何をするかではなく、何をしないかを決めることだ。" = "最も重要な決定とは、何をするかではなく、何をしないかを決めることだ。";
"人生は楽ではない。そこが面白い。" = "人生は楽ではない。そこが面白い。";
"自分で自分をあきらめなければ、人生に「負け」はない。" = "ダイアログを自分で自分をあきらめなければ、人生に「負け」はない。";
```

生成結果は以下のようになりました。

```swift:Swift

import Foundation

struct LocalizableStrings {
    static let doWhatYouThinkIsRightBecauseYouAre = "あなたの心が正しいと思うことをしなさい。どっちにしたって批判されるのだから。"
    static let thoseWhoDoNotMoveForwardAreMovingBackwards = "前進をしない人は、後退をしているのだ。"
    static let thePastDoesntChangeNoMatterHowMuch = "どんなに悔いても過去は変わらない。どれほど心配したところで未来もどうなるものでもない。いま、現在に最善を尽くすことである。"
    static let theMostImportantDecisionIsNotWhatYou = "最も重要な決定とは、何をするかではなく、何をしないかを決めることだ。"
    static let lifeIsNotEasyThatIsInteresting = "人生は楽ではない。そこが面白い。"
    static let ifYouDontGiveUpYourselfThereIsNoLosing = "自分で自分をあきらめなければ、人生に「負け」はない。"
}

```

そもそも `Localizable.string` の記述が変❗️というのは置いておいてください・・・

## 翻訳

go言語を使ってGoogle Cloud Platformを利用するのはSwiftよりも有利です。それは、Googleから

- 十分なサンプルソースコードが提供されている。
- go言語用のライブリが提供されている。

からです。
サンプルソースコードは下記から入手することができます。

https://github.com/GoogleCloudPlatform/golang-samples

またこのサンプルソースコード内で利用されているライブラリは、
下記のように組み込むことができます。

```bash:Shell

$ go get -u cloud.google.com/go/translate
$ go get -u golang.org/x/text/language
$ go get -u google.golang.org/api/option

```

このライブラリを利用すると、go言語からは下記のようにシンプルなコードで済んでしまいます。

```go:Go

func TranslateText(targetLanguage, text string) (string, error) {
	ctx := context.Background()

	lang, err := language.Parse(targetLanguage)
	if err != nil {
		return "", err
	}

	client, err := translate.NewClient(ctx)
	if err != nil {
		return "", err
	}
	defer client.Close()

	resp, err := client.Translate(ctx, []string{text}, lang, nil)
	if err != nil {
		return "", err
	}
	result := resp[0].Text
	return result, nil
}

```

## Google Translation APIを利用するには

[Quickstart (Basic)](https://cloud.google.com/translate/docs/basic/setup-basic) 
https://cloud.google.com/translate/docs/basic/setup-basic

から簡単に始めることができます。

- GCP Console プロジェクトをセットアップする
- 環境変数 `GOOGLE_APPLICATION_CREDENTIALS` を設定する

GCP Console プロジェクトをセットアップは上記のリンクから行えます。
またGOOGLE_APPLICATION_CREDENTIALSに設定すべき「サービスアカウントキーをが含むJSON」も上記で入手できますので、例えばmacの場合だと自分のホームディレクトリに置き、下記のようにタイプすると一時的に使用可能になります。

```bash:Shell

$ export GOOGLE_APPLICATION_CREDENTIALS=~/xxxxxx.json

```

恒久的に設定するには、bash環境であれば、 `.bashrc` や、 `.bash_profile` に書き込みましょう。


## VSCodeでのデバッグ

VSCodeでは、上記のシェル環境のGOOGLE_APPLICATION_CREDENTIALSが有効では無いために、別途設定が必要です。
macであれば、.vscodeの下に、launch.jsonというファイルができれいるはずなので、

```json:launch.json

{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch",
            "type": "go",
            "request": "launch",
            "mode": "auto",
            "program": "${fileDirname}",
            "env": {
                "GOOGLE_APPLICATION_CREDENTIALS": "クレデンシャルへのフルパス/xxxxx.json"
            },
            "args": [""]
        }
    ]
}

```

"env"にクレデンシャルへのフルパスで書きます。なぜか、 ~/xxxxx.json というような記法ではダメなようです。

**追記2020/01/19**
${env:HOME}/xxxxx.jsonでうまく行きます。[mizutoki79](https://qiita.com/mizutoki79)さん、ありがとうございました。





## go言語のサンプルソースコードについて

多すぎて書ききれないので割愛します。
こちらを見るだけでその多さがわかると思います。

https://github.com/GoogleCloudPlatform/golang-samples


## Swiftのサンプルソースコードについて

サンプルソースコードは提供されています。
下記から入手可能です。

https://github.com/GoogleCloudPlatform/ios-docs-samples

しかしながら現在のところはサンプルソースコードが存在するサービスはそれほど多くはないようです。
go言語と違って直ぐ書けます😭

| サービス       |       説明 |   
|:-----------------|:------------------|
|speech   |      Samples that demonstrate the Cloud Speech API.|
|dialogflow   |  Samples that demonstrate the Dialogflow API.|
|solutions|Samples that demonstrate systems built on Google Cloud Platform.|
|screensaver|A screensaver that features Google Cloud Platform products.|
|text-to-speech|Samples that demonstrate the Cloud Text to Speech API.|
|natural-language|Samples that demonstrate the Cloud Natural language|

## 後処理

翻訳結果だけを見ると以下のようになっていました。

```

 Do what you think is right. Because you are criticized for either. <== "あなたの心が正しいと思うことをしなさい。どっちにしたって批判されるのだから。"
 Those who do not move forward are moving backwards. <== "前進をしない人は、後退をしているのだ。"
 The past doesn&#39;t change no matter how much you regret. No matter how worried you are, what will happen to the future. Now, do your best now. <== "どんなに悔いても過去は変わらない。どれほど心配したところで未来もどうなるものでもない。いま、現在に最善を尽くすことである。"
 The most important decision is not what you do, but what you do. <== "最も重要な決定とは、何をするかではなく、何をしないかを決めることだ。"
 Life is not easy. That is interesting. <== "人生は楽ではない。そこが面白い。"
 If you don&#39;t give up yourself, there is no “losing” in your life. <== "自分で自分をあきらめなければ、人生に「負け」はない。"

```


キャメルケースにするためのコードは既に作成済みですが、Google Translation APIの結果に使用できない文字が多く含まれています。
スペースや、ブランク、&#39;や“”もあります。
これらを削除、または置換していく必要があります。
go言語では文字列置換のための関数が用意されていますので、下記のようなコードを書くことで、これらの不要な文字を削除・置換することが容易です。


```go:Go

replacer := strings.NewReplacer(" ", "_", ".", "_", "+", "_", "-", "_")
keyword = replacer.Replace(keyword)

replacer = strings.NewReplacer("\"", "", "?", "", "!", "", "“", "", "”", "", ":", "", "[", "", "]", "", "`", "", "'", "")
keyword = replacer.Replace(keyword)

replacer = strings.NewReplacer("#", "", "$", "", "%", "", "=", "", "@", "", "\\", "", "(", "", ")", "", ",", "", "/", "")
keyword = replacer.Replace(keyword)

replacer = strings.NewReplacer("&39;", "", "&amp;", "", "&quot;", "")
keyword = replacer.Replace(keyword)

keyword = strings.Replace(keyword, ";", "", -1)

keyword = convertToCamelCase(keyword)

```

また、単純に全ての翻訳結果を採用してしまうとものすごく長い変数名になってしまうので、どこかで断ち切る必要があります。意味的に良い場所で断ち切るのは至難の技ですので、文字数で制限することにします。

しかし、単語の途中でブツ切れるのはかっこよくありません。
そこで、キャメルケースを生成する関数を手直しして下記のように変更しました。

```go:Go


func convertToCamelCase(text string) string {
	if text == "" {
		return text
	}

	var keyword string
	var foundUnderScore = false
	for i := 0; i < len(text); i++ {
		letter := text[i : i+1]
		if letter == "_" {
			// ====== ここで文字数制限する ======
			if i > 40 {
				break
			}
			foundUnderScore = true
			continue
		}
		if foundUnderScore {
			foundUnderScore = false
			keyword = keyword + strings.ToUpper(letter)
		} else {
			keyword = keyword + letter
		}
	}

	head := keyword[:1]
	rest := keyword[1:]
	keyword = strings.ToLower(head) + rest

	return keyword
}

```


## まとめ

go言語は、C/C++に近いローレベル（いろんな意味で）の言語でありながら、ライブラリのパワーが凄まじいと思います。
単なるローカルなツール作成だけではなく、サーバサイドでも大活躍する言語ですので、Objective-Cに慣れ親しんだ‼️iOSエンジニアの方は一度試してみてはいかがでしょうか。