ブラウザにおける多言語対応

# この記事の読者
* WebでPHPをあまり使ったことがない方
* 多言語対応させたいけど、SQLなどは使いたくない方
* この記事を楽しく読める人

# きっかけ
自分の運営している団体でWebを多言語対応させよう思った時に、WordPressなどは使わず、一からWebを構築しているため、自ら多言語対応をする必要があった。

# 前提条件
* 使えるのはHTML、CSS、JavaScriptの３つだけ。
* 外部ライブラリを使うのは問題ない
* 翻訳を担当するのは別の人

# 結論
JSONファイルを作成→配列から読み込み

# 追記（2022/12/24）
@think49 様より、コード改善のアドバイスをいただきました。
誠に感謝しかございません。
のちにその修正コードを追記させていただきます。

# 詳細
例として以下のHTMLファイルを用意。
```html:index.html
<!DOCTYPE html>
<html>
    <head>
        <title>test<title>
    </head>
    <body>
        <h1 name="enterd"> 果物</p>
        <p name="enterd">りんご</p>
    </body>
</html>
```

```JSON:data.json
[
    {
        "lang":"ja",
        "value":{
                "title":"果物",
                "text":"りんご"
            }
    },
    {
        "lang":"en",
        "value":{
                "title":"Fruits",
                "text":"Apple"
            }
    }
]
```

```javascript:main.js
//初期値の設定
let langNum = 3;

//パラメータの取得
function getParam(name, url) {
    if (!url) url = window.location.href;
    name = name.replace(/[\[\]]/g, "\\$&");
    var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
        results = regex.exec(url);
    if (!results) return null;
    if (!results[2]) return '';
    return decodeURIComponent(results[2].replace(/\+/g, " "));
};

//言語のパラメータがある場合は取得、ない場合はブラウザの言語を取得
if (getParam("lang") == null) {
    const browserLangugage = window.navigator.language;
    setLanguage(browserLangugage);
    console.log("No language")
} else {
    setLanguage(getParam("lang"));
    console.log("Para")
}

//JSONから言語の取得をするための変換
function setLanguage(lang) {
    if (lang == "ja") {
        langNum = 0;
        console.log(langNum);
    } else if (lang == "en") {
        langNum = 1;
        console.log(langNum);
    } else if (lang == "es") {
        langNum = 2;
        console.log(langNum);
    } else {
        langNum = 1;
        console.log(langNum);
    };
};

//JSONファイルから言語データを取得
fetch('./data.json')
    .then(function (response) {
        console.log(response);
        return response.json();
    })
    .then(function (data) {
        console.log(langNum);
        let valueArray = Object.values(data[langNum].value);
        console.log(valueArray);
        return valueArray;
    })
    .then(function (valuesOfInfo) {
        const length = valuesOfInfo.length;
        for (let i = 0; i < length; i++) {
            document.getElementsByName("enterd")[i].innerHTML = valuesOfInfo[i];
        }
    })
```

JSONファイルの中が配列なので、パラメータで指定されている言語と対応する番号（配列インデックス）に変換する必要がある。
たとえば、URLは表記は以下の通りになる。
```
https://example.com/?lang=ja
```
だが、JSONのファイルの中では一つ目の配列にデータが入っているので、パラメータが`ja`なら`langNum`変数に0を入れる、のように対応させている。

# 利点
* 他の言語の追加が容易である
* 編集が簡単

# 改善すべきポイント
* JSONを配列に変換しているので、一つでも忘れたら修正作業が大変

# 筆者の戯言
結局、Webページの多言語対応ってどのようにやるのが正解なんですかね？

# 修正1 (2022/12/24)
@think49　の修正版コードを追記します。
詳しくは、コメント欄を参照いただけると幸いです。
再度、 @think49 様に感謝申し上げます。

```html:sample.html
<!DOCTYPE html>
<html lang="ja">

<head>
	<meta charset="utf-8">
	<title>sample</title>
</head>

<body>
	<template id="sample">
		<h1>${title}</h1>
		<p>${text}</p>
	</template>
	<script>
		'use strict';
		function replaceTextNode (entries, ...contextNodeList) {
			for (let contextNode of contextNodeList) {
				const xpathResult = document.evaluate('descendant::text()', contextNode, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
				let i = xpathResult.snapshotLength;

				while (i--) {
					const textNode = xpathResult.snapshotItem(i);
					for (let [key, value] of entries){
						textNode.data = textNode.data.replace(new RegExp('\\${' + key + '}', 'g'), value);
					}
				}
			}
		}

		fetch('lang.json')
		.then(response => response.json())
		.then(contentEntries => {
			const lang = new URLSearchParams(location.search).get('lang');
			const contents = contentEntries[lang];

			if (!contents) return;
			document.documentElement.lang = lang;

			const df = document.getElementById('sample').content.cloneNode(true);
			replaceTextNode(Object.entries(contents), ...df.childNodes);
			document.body.appendChild(df);
		});
	</script>

</body>

</html>
```

```JSON:lang.json
{
  "ja":{
    "title": "果物",
    "text": "りんご"
  },
  "en":{
    "title": "Fruits",
    "text": "Apple"
  }
}
```