chrome.comでChromeの翻訳機能が役に立たない件


ここ1年、メインブラウザをFirefoxからGoogle Chromeに切り替え、Chromeの翻訳機能がかなり使えることに気付きました。
ほぼワンタッチで翻訳可能です。見ている部分だけ翻訳しているようですが、スクロールさせても、さほどストレスを感じません。時々ヘンな訳文を吐いたりしますが、翻訳精度も予想以上に良好で、重宝しています。

ところが、developer.chrome.comで翻訳機能を使うと、ソースコードまで翻訳されてしまい、使いにくくて困っていました。
例えば、拡張機能の作り方を説明するページ "[Getting Started Tutorial - Google Chrome](https://developer.chrome.com/extensions/getstarted)"
では、下のような状況になっています。

翻訳前のchrome.comのページ
![翻訳前のchrome.comのページ(before-en.png)](https://qiita-image-store.s3.amazonaws.com/0/255225/73f9be8d-e05d-9dfc-89a1-0eeed6b7cd7b.png)

翻訳後のchrome.comのページ
![翻訳後のchrome.comのページ(before-ja.png)](https://qiita-image-store.s3.amazonaws.com/0/255225/e5d0491b-52c6-b01a-279c-f654f722d4b1.png)

コレじゃソース読めないじゃないか。Chromeのお膝元で使えないってどういうことよ、もうぷんぷん。

仕方がないので、タブを複製してコード部分を読むときは原文のタブに、その他の時は訳文のタブに切り替えて対処してました。


## 原因
いままで、Chromeの翻訳機能そのものに問題があるのかなと、バクゼンと考えていたのですが、HTMLのコーディングが原因だと分かりました。
マイクロソフトやMozilla Developer Networkではあまり起きないに気付き、調べてみました。すると、HTMLのコードは`<pre><code>...</code></pre>`で囲むのが一般的だと言われていますが、chrome.comでは`<code>`で囲まれていないのです。

### chrome\.com "[Getting Started Tutorial](https://developer.chrome.com/extensions/getstarted)" のHTMLソース
<!-- l.1752 / #gc-pagecontent > article > div > pre:nth-child(20) -->
```html
<pre data-filename="background.js">
  chrome.runtime.onInstalled.addListener(function() {
    chrome.storage.sync.set({color: '#3aa757'}, function() {
      console.log("The color is green.");
    });
  });
</pre>
```
なお、ソースには見られませんが、この後スクリプトで pre要素に `class="prettyprint"` の属性が追加されています。

ほかのサイトでは、次のようになっています。

### マイクロソフト Visual Studio Code "[Build Node\.js Apps with VS Code](https://code.visualstudio.com/docs/nodejs/nodejs-tutorial)" のHTMLソース
<!-- l.734 / #main-content > div > div > div.col-sm-9.col-md-8.body > pre:nth-child(19) > code -->
```html
<pre>
  <code class="javascript">
    <span class="hljs-keyword">var</span> msg = 
    <span class="hljs-string">'Hello World'</span>;
    <span class="hljs-built_in">console</span>.log(msg);
  </code>
</pre>
```
ここでは、ソースレベルで `<pre><code>...</code></pre>`の記述が見られます。

### Mozilla Developer Network "[JavaScript basics \- Learn web development](https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics)" のHTMLソース
<!-- l.625 / #wikiArticle > ol:nth-child(15) > li:nth-child(4) > pre > code -->
```html
<li>Now add the following code to the <code>main.js</code> file:
  <pre class="brush: js">var myHeading = document.querySelector('h1');
    myHeading.textContent = 'Hello world!';
  </pre>
</li>
```

MDNの場合、ソースレベルでは`<code>`に囲まれていませんがいませんが、スクリプト対応で、次のとおり`<code>`が追加されています。

```html
<li>Now add the following code to the <code>main.js</code> file:
  <pre class="brush: js line-numbers  language-js">
    <code class=" language-js">
      <span class="token keyword">var</span> myHeading 
      <span class="token operator">=</span> 
      document<span class="token punctuation">.</span>
      <span class="token function">querySelector</span>
      <span class="token punctuation">(</span>
      <span class="token string">'h1'</span>
      <span class="token punctuation">)</span>
      <span class="token punctuation">;</span>
      myHeading<span class="token punctuation">.</span>textContent 
      <span class="token operator">=</span> 
      <span class="token string">'Hello world!'</span>
      <span class="token punctuation">;</span>
      <span class="line-numbers-rows"><span></span><span></span></span>
    </code>
  </pre>
</li>
```

ここで初めて、Chromeの翻訳機能は、`<code>`で囲まないと、翻訳されてしまうことに気付きました。文字列リテラルやコメント部分も翻訳されませんが、やむを得ません。
必要に応じて、別の翻訳アプリなどを使うとよいかもしれません。ちなみにChromeには拡張機能「どこでも翻訳 Translate Anywhere」を入れています。

## 対策
さてさて、対策を考えてみました。

* 手作業で書き換える。
    + 面倒くせー。ていうか、ほとんど意味なくね？
* 拡張機能で書き換える。
	+ developer.chrome.comのみの対策で、拡張機能を作るなんて、面倒くせー。
	+ preの中にcodeがないか調べて、ない場合は書き換える手法もあるけど。
	+ でも本格的やるなら、様々なサイトのコーディングを調べてみないと。
* ユーザースクリプトで書き換える。
	+ 割合現実的。

ということで、とりあえずユーザースクリプトで書き換えることにしました。
Chromeでのユーザースクリプトの管理には、拡張機能の [Tampermonkey](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo) を使っています。

```javascript

// ==UserScript==
// @name         developer.chrome.com 翻訳範囲の適正化
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  developer.chrome.com のコード部分をChromeに翻訳させないため、code要素を挿入する。
// @author       hidetoya
// @match        https://developer.chrome.com/*
// @grant        none
// ==/UserScript==

(function () {
  'use strict';
  let targets = document.querySelectorAll('pre.prettyprint');
  for (let tgtEl of targets) {
    wrapWithCodeEl(tgtEl);//<pre class="prettyprint"></pre>
  }
})();

//◆preの下にcodeを挿入 -- RangeオブジェクトAPIを利用してラッピングする
function wrapWithCodeEl(tgtEl) {
  'use strict';
  let preEl = tgtEl.cloneNode(false); //falseにすると子孫ノードは複製しない
  let codeEl = document.createElement('code');
  let wapper = preEl.appendChild(codeEl); //<pre><code></code></pre>

  let innerRng = document.createRange(); //または = new Range();
  //cf. rngOuter.selectNode(tgtEl); //contain the Node and its contents. つまり preとその中身
  innerRng.selectNodeContents(tgtEl); //contain the contents of a Node. つまり preの中身だけ(pre自身は含まない)
  innerRng.surroundContents(wapper); //preとcodeでpreの中身を囲む

  //selectionを利用した検証用
  //window.getSelection().removeAllRanges();
  //window.getSelection().addRange(innerRng);
} //f.WrapCodeEl
```

### AFTER
対策後のchrome.comのページ
![対策後のchrome.comのページ](https://qiita-image-store.s3.amazonaws.com/0/255225/c84e584d-32fb-dfc3-e2d9-7ec21e42c550.png)

`<code>`を挿入したことによる不具合もなさそうで、対策完了。

## あとがき
数年前マイクロソフトもExcelのリファレンスマニュアルでやらかしていたように記憶しています。
おそらく機械翻訳を使ったのでしょう。手作業ではあり得ないと思うのですが。翻訳してはいけないプロパティ名とかメソッド名が翻訳されていて、絶句しました。
今ではないとは思いますが…。

なお、GoogleかChromeの関係者がいらっしゃいましたら、改善のほどよろしくお願いします。

## 参考資料
* __[uhyohyo\.net 八章第一回　Rangeとは — JavaScript初級者から中級者になろう](https://uhyohyo.net/javascript/8_1.html)__ -- 解説がていねいで、いつもお世話になっております。リファレンスとしては使いにくいのが玉にキズ。
* __Range - Web APIs | MDN__
	+ https://developer.mozilla.org/en-US/docs/Web/API/Range  … 英語
		- Syntax: *range*.[selectNode](https://developer.mozilla.org/en-US/docs/Web/API/Range/selectNode)(referenceNode);
		- Syntax: *range*.[selectNodeContents](https://developer.mozilla.org/en-US/docs/Web/API/Range/selectNodeContents)(referenceNode);
		- Syntax: *range*.[surroundContents](https://developer.mozilla.org/en-US/docs/Web/API/Range/surroundContents)(newNode);
		- Syntax: *documentFragment* = *range*.[extractContents](https://developer.mozilla.org/en-US/docs/Web/API/Range/extractContents)();
		- see also: Example of **extractContents**
	+ https://developer.mozilla.org/ja/docs/Web/API/range  -- 翻訳。このページは翻訳されてますが、メソッドの各ページは未翻訳です。
* **Selection - Web APIs | MDN** -- 英語。今回は直接使いませんでしたがRangeと関連性があります。
	+ https://developer.mozilla.org/en-US/docs/Web/API/Selection
		- Syntax: *selection*.[addRange](https://developer.mozilla.org/en-US/docs/Web/API/Selection/addRange)(range)
		- see also: Example of **addRange**
	+ https://developer.mozilla.org/ja/docs/Web/API/Selection -- 翻訳。このページは翻訳されてますが、メソッドの各ページはほとんど未翻訳です。