CDN版Vue3でお手軽多言語対応

## はじめに
CDNで単一ファイルコンポーネントを作成する方法は[こちら](https://qiita.com/konbraphat51/items/768c5c414fca988ffa97)で紹介しました。
これに追加で、CDN版i18nを用いて多言語対応する方法をご紹介します。

## 結論
```diff_html:index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
    <title>TITLE</title>
    
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <script src="https://unpkg.com/vue-i18n@9"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue3-sfc-loader/dist/vue3-sfc-loader.js"></script>
    
    </script>
</head>
<body>
    <div id="app"></div>

    <script>
+       //i18n初期設定
+       const i18n = VueI18n.createI18n({
+            legacy: false,
+            locale: navigator.language,
+            fallbackLocale: "en",
+        });

        //vue3-sfc-loader初期設定
        const options = {
            moduleCache: {
                vue: Vue
            },

            //read subdirectory vue component file.
            async getFile(url) {
                
                const res = await fetch(url);
                if ( !res.ok )
                throw Object.assign(new Error(res.statusText + ' ' + url), { res });
                return {
                getContentData: asBinary => asBinary ? res.arrayBuffer() : res.text(),
                }
            },
            
            addStyle(textContent) {
        
                const style = Object.assign(document.createElement('style'), { textContent });
                const ref = document.head.getElementsByTagName('style')[0] || null;
                document.head.insertBefore(style, ref);
            },

+            //<i18n>タグの読み込み
+            customBlockHandler(block, filename, options) {
+
+                if ( block.type !== 'i18n' )
+                  return
+        
+                const messages = JSON.parse(block.content);
+                for ( let locale in messages )
+                  i18n.global.mergeLocaleMessage(locale, messages[locale]);
+            }
        }
        const { loadModule } = window["vue3-sfc-loader"];
        
        //Appコンポーネントをマウント
        const app = Vue.createApp(
            Vue.defineAsyncComponent(
                () => loadModule("./src/components/App.vue", options)
            ),
        )
        app.use(i18n);
        app.mount('#app');
    </script>
</body>
</html>
```

```diff_vue: App.vue
<template>
+    {{ t("example.explain") }}
</template>

<script>
    export default Vue.defineComponent({
        name: 'App',
        components: {
        },
+        setup() {
+            //set up i18n
+            const { t } = VueI18n.useI18n()
+            return { t }
+        },
    })
</script>
+<i18n>
+    {
+        "en": {
+            "example": {
+                "explain": "This is how you use i18n"
+            }
+        }
+        "ja": {
+            "example": {
+                "explain": "こうやってi18nを使います"
+            }
+        }
+    }
+</i18n>
```

言語選択コンポーネントが欲しいならこちらをお使いください。
```vue: LanguageSelection.vue
<template>
    <select v-model="$i18n.locale">
        <option value="en">English</option>
        <option value="ja">日本語</option>
    </select>
</template>

<script>
    export default {
        name: 'LanguageSelection'
    }
</script>
```

## 解説
* i18nのCDN版を用いています。

* `index.html`の処理はメンドウなのでテンプレートと理解して大丈夫だと思います。

* `.vue`ファイルに、`<i18n>`タグとして、`json`形式で翻訳情報を書き込みます。
HTML上に表示するには、`{{t("PATH")}}`のように書き込みます。

* i18nの言語設定の変数は`i18n.global.locale.value`にあります。（`i18n`は`index.html`で定義したグローバル変数）もし、テキスト以外に言語設定にレスポンスしたい場合は、`computed`にこの変数を組み込めば対応できます。