Vue@3&Vue-i18n@9で<select>を使って言語切り替えをする方法
はじめに(やりたいことと経緯)
ウェブページなどの多言語対応をしたい。そのときに、以下の2点が要件。
- ブラウザの自然言語の種類を取得して、それに合わせて情報を表示する。
- 利用者が
<select>
を使って自然言語の種類を切り替えて表示する。
今まで、i18next[1]というフレームワークを使っていたが、選択された言語(日本語や英語など)をHTMLのタグに流し込むときにdocument.getElementById("hoge").textContent = "This text is different!";
を大量に書かないといけなくて面倒だし可読性が落ちます。もちろん、賢く工夫をして書けば、もう少し綺麗に単純に書くことは出来るけど、良い機会なのでVue.js[2]を使ったVue I18n[3]を使ってみようと思いました。
コード&実行結果
環境
利用している環境です。今回のコードはVue Ver.2 と Vue I18n Ver.8では上手く動かないはずです。Vue Ver.2&Vue I18n Ver.8の書き方は調べれば結構出てくるので、そちらを参考にしてください。
- Vue Ver.3
- Vue I18n Ver.9
ブラウザの自然言語の種類を取得して、それに合わせて情報を表示する
Vue I18nの公式ガイド[4]とリポジトリのIssue[5]を参考に、以下のコードを書いて動かしました。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Vue I18n Test</title>
<script src="https://cdn.jsdelivr.net/npm/vue@3"></script>
<script src="https://unpkg.com/vue-i18n@9"></script>
</head>
<body>
<div id="yokan">
<span> {{ $t("message.hello") }} </span>
</div>
</body>
<script>
/**
* ブラウザの設定言語を取得
*/
const langBrowser = navigator.language.split("-")[0];
console.log("使用されている言語は" + langBrowser + "です。");
const messages = {
en: {
message: {
hello: "Hi!",
},
},
ja: {
message: {
hello: "やあ!",
},
},
};
const i18n = VueI18n.createI18n({
locale: langBrowser,
fallbackLocale: "en",
messages,
});
const app = Vue.createApp({});
app.use(i18n);
app.mount("#yokan");
</script>
</html>
実行結果はこんな感じ
<select>
を使って自然言語の種類を切り替えて表示する
利用者が後述しますが、これが少し、困りました。とりあえず、確実に動くであろうコードと、その実行結果を以下に貼り付けます。2つ方法があり、正攻法と思われるものから貼り付けます。実行結果は殆ど同じになるので、ひとつだけ最後に貼っています。
Vue.jsの双方向バインディングを利用した方法
Vue I18nのガイド[6]をベースに、Vue.jsの双方向バインディング[7]を利用したコードです。こちらが正攻法かと思われます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Vue I18n Test</title>
<script src="https://cdn.jsdelivr.net/npm/vue@3"></script>
<script src="https://unpkg.com/vue-i18n@9"></script>
</head>
<body>
<div id="yokan">
<span> {{ $t("message.hello") }} </span>
<select
v-model="selected"
name="lang"
class="legal-item"
id="select-lang"
v-on:change="switchLang"
>
<option value="en">English</option>
<option value="ja">日本語</option>
</select>
</div>
</body>
<script>
/**
* ブラウザの設定言語を取得
*/
const langBrowser = navigator.language.split("-")[0];
console.log("使用されている言語は" + langBrowser + "です。");
const binding = {
data() {
return {
selected: langBrowser,
};
},
methods: {
switchLang() {
this.$i18n.locale = this.selected;
},
},
};
const messages = {
en: {
message: {
hello: "Hi!",
},
},
ja: {
message: {
hello: "やあ!",
},
},
};
const i18n = VueI18n.createI18n({
locale: langBrowser,
fallbackLocale: "en",
messages,
});
const app = Vue.createApp(binding);
app.use(i18n);
const vm = app.mount("#yokan");
</script>
</html>
<select>
をイベントハンドラで監視して、変更時にlocaleを変更
こちらはVue I18nのガイド[6:1]をベースに、stack overflowの記事[8]を参考に書きました。
下のコードにしれっとコメントで書いていますが、VueとVue I18nの設定は、今回の紹介したコードと全く同じなのに、僕の場合
vm.$forceUpdate(); // なぜか必要な場合と、不要な場合がある。
これがないと上手く反映されませんでした。vm.$forceUpdate();
が必要な時と不要な時の違いとしては、必要な時の環境では使用しているVueとVue I18nはnpmで引っ張ってきた*.prod.js
であるということです。もしかしたら、そういう仕様なのかもしれません。
なかなか、思うような参考記事が見つからず、苦労しました。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Vue I18n Test</title>
<script src="https://cdn.jsdelivr.net/npm/vue@3"></script>
<script src="https://unpkg.com/vue-i18n@9"></script>
</head>
<body>
<div id="yokan">
<span> {{ $t("message.hello") }} </span>
</div>
<select name="lang" class="legal-item" id="select-lang">
<option value="en">English</option>
<option value="ja">日本語</option>
</select>
</body>
<script>
/**
* ブラウザの設定言語を取得
*/
const langBrowser = navigator.language.split("-")[0];
if (langBrowser === "en" || langBrowser === "ja")
document.getElementById("select-lang").value = langBrowser;
else document.getElementById("select-lang").value = "en";
console.log("使用されている言語は" + langBrowser + "です。");
const messages = {
en: {
message: {
hello: "Hi!",
},
},
ja: {
message: {
hello: "やあ!",
},
},
};
const i18n = VueI18n.createI18n({
locale: langBrowser,
fallbackLocale: "en",
messages,
});
const app = Vue.createApp({});
app.use(i18n);
const vm = app.mount("#yokan");
/**
* <select>のイベントハンドラ
*/
document.getElementById("select-lang").addEventListener("change", () => {
vm.$i18n.locale = document.getElementById("select-lang").value;
vm.$forceUpdate(); // なぜか必要な場合と、不要な場合がある。
console.log(
"表示言語を" +
document.getElementById("select-lang").value +
"に変更しました。"
);
});
</script>
</html>
実行結果
さいごに
双方向バインディングを使った方法は、手元の環境では問題なく動いたので、こちらで書いたら良いと思います。
7年前ぐらいに、Polymer + Typescriptでフレームワークのバージョンアップや整合性で痛い目にあって以来、やはり基本の基本で動くものが不変で安心だと思ってしまい、フレームワークは気安く使わずWebの標準規格(HTML Living standard、CSS3、Javascript、Web Componentsなどブラウザだけで動くもの)を使おうと決めていましたが、最近は利用者が多く安定したフレームワークも多いし、この機会に慣れて使ってみようと思います。
-
i18next documentation https://www.i18next.com/ (2022-04-22閲覧) ↩︎
-
Vue.js(Ver.3) 日本語 https://v3.ja.vuejs.org ↩︎
-
Vue I18n 日本語 https://vue-i18n.intlify.dev/ja/index.html (2022-04-22閲覧) ↩︎
-
はじめよう / Vue I18n https://vue-i18n.intlify.dev/ja/guide/#javascript (2022-04-22閲覧) ↩︎
-
Default locale based on browser settings? / (kazupon /
vue-i18n) Issue https://github.com/kazupon/vue-i18n/issues/220 (2022-04-22閲覧) ↩︎ -
Scope and Locale Changing / Vue I18n https://vue-i18n.intlify.dev/guide/essentials/scope.html#global-scope-1 (2022-04-22閲覧) ↩︎ ↩︎
-
ユーザー入力の制御 / Vue.js https://v3.ja.vuejs.org/guide/introduction.html#%E5%AE%A3%E8%A8%80%E7%9A%84%E3%83%AC%E3%83%B3%E3%82%BF%E3%82%99%E3%83%AA%E3%83%B3%E3%82%AF%E3%82%99 (2022-04-22閲覧) ↩︎
-
How can I translate app without reloading page in vue.js? / adib on stack overflow https://stackoverflow.com/questions/55361211/how-can-i-translate-app-without-reloading-page-in-vue-js (2022-04-22閲覧) ↩︎
Discussion