簡便なウェブサイト翻訳ツール（サービス）をつくってみた

これです。

i18n.page - ウェブサイト翻訳ツール
<https://i18n.page>

宣伝も兼ねて、と言いたいところですが、実現するまでいろいろ面倒だったので、その辺りの苦労をシェアした方が良いかと思い、記事にしてみました。

## まず、どのようなツール（サービス）か？

翻訳したいウェブサイト、または HTML ファイルを読み込んで、翻訳できそうな箇所を自動抽出、それを手動、または Google 翻訳にかけて対訳データを作ります。
![screen-shot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30945/eb7b02b6-0b6a-20b1-f178-b7e7aca41d18.png)
そしてそれらを JSON データに変換し、お手製の JavaScript ツール１つに含めてパッケージしたものを出力し、先のウェブサイトに読み込ませると、自動翻訳機能が実装されるというものです。

## お手軽に１つのファイルにまとまっている

この翻訳ツールで出力された JavaScript ファイル（i18n-page.js）には、翻訳データ、ツールバー、自動・手動翻訳切り替え表示UIもすべて含まれています。

![view_toobar_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30945/c437d67d-a642-12e7-d6e2-73124ce4e18d.png)

もちろん、UI を付属させずに「自動判定のみ」で、アクセスしてきたユーザーのブラウザ言語を判定して、表示内容を自動で切り替えることも可能です。

サンプルは、同上先ほどのサイトで確認できます。ページ画面端・右下に固定表示されているツールバーが「それ」です。

表示サンプル
<https://i18n.page>

## なぜこんなのを作ろうと思ったか？　ちゃんとウェブサイト翻訳やると意外とたいへん

元々、Windows アプリケーションを公開している<a href="https://hibara.org/" target="_blank" rel="noopener noreferrer">サイト</a>を運営しておりますが、意外と国際化（多言語対応）作業は面倒です。

### PHPで言語判定、英語ページ、日本語ページへと飛ばしていた

実際は、PHP で言語判定を行って、ブラウザ言語などが「日本語」以外なら、すべて英語ページへ飛ばすというプログラムを組んでいました。これはすなわち、日本語版ページと、英語版ページの両方を用意しなくてはならないということです。

実際は、`require_once` とかで、それぞれを読み分けていたので、私の開発環境（<a href="https://www.jetbrains.com/ja-jp/phpstorm/" target="_blank" rel="noopener noreferrer">PhpStrom</a>）上では、インテリセンスや、サジェスチョンなどは、完全に切れて表示されなくなっていました。ほとんど、無印テキストエディターで編集しているようなものでした。

これは、なかなかに不便なものです。

### WordPress のプラグイン利用は工数がかかる

WordPress を使って、プラグインを入れ多言語化対応を行うことも考えましたが、Windows ソフトウェアのビルドからの配布というフローが出来上がっていたので、WordPress を導入すると、そっちを作り直さなくてはなりません。これも面倒。ブログもほとんど真面目に書いていないし、書くときはたいていココ Qiita に記事を上げるので全面導入のメリットも感じられません。

### Google翻訳でサイト丸ごとやる

これも検討しましたが、Windows ソフトのバージョンアップのフローの中で、更新履歴やら Zip アーカイブの管理だとか、PHP + JavaScript の独自で動作しているので、あんまり相性がよろしくない。なによりも、完全な機械翻訳はしたくなかったのです。拙いなりに、ほとんどのページは <a href="https://www.deepl.com/home" target="_blank" rel="noopener noreferrer">DeepL</a> を使いながら（使ってるのかよ）自身で翻訳したので、そのデータも残っています。それを有効活用したいとも考えました。

### 何か他に良いツールはないか？

探してみましたが、あらかじめプレースホルダーを書いて、そこに自動で単語を差し込む仕組みだったりとか、あるいは「詳しいことはお見積もりを・・・」と、もはやカスタマイズ開発を外注しなくてはならないレベルでした。そもそも、あらかじめ HTML 内にプレースホルダーを書く時点で面倒極まりないです（一度構築してしまえば楽かもしれませんが・・・）。

## そこで、replace メソッドで行けるんじゃね？

そこで、ふと思ったのです。めちゃくちゃ力技。JavaScript の全文検索で、ズバリ言語ごと、一字一句同じならば全置換してしまえば良いと考えました。

```javascript
 document.body.innerHTML =
     document.body.innerHTML.replace(/アタッシェケース/g, "AttachéCase");
```

そして、実際にいけました。

「こりゃあ、いけるじゃん！　作ってみよ！」と、意気揚々と開発を始めたのですが、かなり終盤で行き詰まることになります。

そりゃそうですよね。こんなに簡単にできるのなら、他で作っている人がいるはずだもの。

## replace メソッドで innerHTML に対して全置換すると DOM 構造を破壊する

これに気づいたのは、出力した翻訳ツールの JavaScriptファイル（i18n-page.js）を読み込んだ後、他の JavaScript コードが動かなくなるところです。

けっこうな時間、ハマっていたように思います。

実は、`innerHTML`を、`replace`することは、読み込んだ HTML 内の DOM 構造を破壊してしまうことになっていたのです。

たしかに、先に読み込んだ HTML 構造を後から修正すれば、それが元の DOM かどうかが分からなくなるのは当然と言えば当然の話です。

**innerHTML より insertAdjacentHTML を使う**
<https://qiita.com/amamamaou/items/624c22adec32515e863b>

そして、上の記事中に出てくる `insertAdjacentHTML` では、挿入はできても置換はできないのです。これにはさすがに血の気が引きました。ここまで約二ヶ月作業してきたわけですから。こんなことに気づかないなんて。

## 救世主「findAndReplaceDOMText」が見つかる

途方に暮れ、どうにか `replace` を行っても DOM を破壊しないか、もしくはブラウザーで再解釈を行うような方法がないかとあがいていたときに見つけたのが、以下のライブラリです。

findAndReplaceDOMText
<https://github.com/padolsey/findAndReplaceDOMText>

単純な `innerHTML` への置換ではなく、きちんと DOM 構造を見てから置換を行っています。約３年前から更新が途絶えてはいるものの、パブリックドメインでソースが公開されているので、メンテナンスも可能です。なによりも、jQuery など他のライブラリに依存していないのが素晴らしい。

けっきょくこのライブラリを組み込むことで、前述の i18n-page.js の動作を実現することになりました。

## 置換順序によっては悲しい結果になる

ただ、とりあえず、やりたいことは実現したものの、じゃっかん残念な部分もあります。

文字列の置換順序によっては、正常に動作しないというものです。

|  日本語（基本言語）  |  英語（対訳）  |
| ---- | ---- |
|  開発  |  Development  |
|  開発実績があります。  |  We have experience in development. |

と、リスト化したとして、実際に２番目の翻訳は、以下のようになります。

> Development実績があります。

めちゃくちゃ悲しい。。。

文字列は全置換、しかもリストにある順番通りに実行されていくので、先に「開発」が文字列置換され、２番目の文字列は「リストに無い」と判定されてしまうのです。

これを回避するには、順番を逆にする必要があります。

|  日本語（基本言語）  |  英語（対訳）  |
| ---- | ---- |
|  開発実績があります。  |  We have experience in development. |
|  開発  |  Development  |

長い文字列を先に置換しておいてから、「単語」など短いフレーズをリスト順番後方に置くと、正常に

> We have experience in development.

と置換されるようになります。

ただ、この辺りは、プログラム的に対応可能なので、将来的に最適化ソート機能を実装しようかと思っています（要望があれば）。

## やや枯れてそうなツール群で開発

どのようなバックエンド、フロントエンドを使っているか気になる方もいらっしゃるかもしれませんが、もう塩々なやつかもしれません。

PHP7 + MySQL + Bootstrap4 です。

なぜなら、私がフロントエンドエンジニアでも、バックエンドエンジニアでもないから。ただの、Windows フリーソフト開発者です。どちらも不得手。

あと、AWS や、Azure でもなく、安くてまあまあ速く、信頼の<a href="https://www.sakura.ad.jp/" target="_blank" rel="noopener noreferrer">さくらインターネット</a>のレンサバを使っていたから。そもそも、そこに Window フリーソフトを公開していたので、費用を浮かすために一緒にホスティングしたかったのです。本当はだいぶ前に、本業でいじったことのある <a href="https://aws.amazon.com/jp/dynamodb/" target="_blank" rel="noopener noreferrer">DynamoDB</a> とか、使ってみたかった。

もうちょいシャレオツな UI にもしたかったのですが、「やりたいこと」が先にありましたので、組み上げる速度優先で迷わず「Bootstrap」を選びました。いやあ、めちゃくちゃ楽でした。

いまだったら、React とかイケてるんでしょうか？（知らない。適当）　これ以上に便利で、いかしたデザインで構築できるライブラリがあれば誰か教えてください。

## 無料です。スクリプトは MIT ライセンス

自分自身が必要に迫られたから作ったということもあり、他の方にも使っていただきたいなと思って、無料で公開しています。とはいえ、少なからずサーバ代や、Google 翻訳の API 使用料は持ち出しなので、しょっぱいバックエンドが落ちるようなら、さすがに広告でも付けて増強しようかとは思っています（場合によっては Google Cloud Translate API を止めるとか）。

なお、最終的に出力される JavaScriptファイル（i18n-page.min.js）は、MITライセンスです。商用、私用に関わらず自由にお使いいただけます。特にどこかにコピーライトを表記する必要もなく、そのJS ファイルに著作権表記を残しておいてもらえるとうれしいかな、といったところです。とりあえずユルいです。

あと、今のところ、オープンしたばかりなので、ベータ版扱いで公開しています。何かあったらごめんなさい。

i18n.page - ウェブサイト翻訳ツール
<https://i18n.page>

特に励ましと、フィードバック大歓迎です。