多言語対応で検索すると、フロント側だけ、サーバー側だけを対象とした記事ばかりがヒットします。
言い換えるとjavascriptだけの対応もしくはC#だけの対応の記事ばかりです。
よくあるのはjsはすべての言語情報を保持した辞書jsonを保持させて切り替えパターン。
C#だとResources.resx使うパターン。
それぞれ参考にはなるものの、現実にはサーバー側が返すメッセージがあったり、サーバーが応答が返らないときに出すメッセージはjsに埋め込んでいたりして、簡単に分離できるケースばかりではないんじゃなかろうか。
私が感じる問題点をいくつか列挙します。
- 全言語の情報を保持したJSONって無駄なデータ多くない?
日英くらいならいいけど、言語が増えるほど無駄が増大する。 - ページごとに辞書JSON作るの大変そう。
- 辞書JSONだとエディタで入力補完効かない?
- js/C#両プランを採用するとResources.resxとjsonでファイル管理が煩雑。
- Resources.resxを複数ファイルで横断管理するのは大変
理想だけを語るなら、
翻訳用の辞書ファイルは1か所で管理したい。
辞書ファイルはKey, En, Jp, … のような表形式で一括管理。
ビルド時に辞書ファイルからResources.resxとjs用の辞書を生成。
ついでにjsで補完を効かせたいので、d.tsファイルも自動生成。
ということで便利なものないかなーと検索していたら見つけたのがこのVisualStudioの拡張機能。
ResXManager
Resources.resxは複数ファイルになるとキーのコピペが面倒だと感じていたのですが、なんと便利な拡張機能がありました。
まさにKey, En, Jp, … のような表形式で一括管理でき理想通りです。
自力で辞書管理アプリを作ろうかとも思っていたのですが、ここまで出来上がったライブラリがあるなら使った方が楽ですね。ソース上からResources.resxに転記する機能もあったりして非常に便利です。
ということで辞書ファイルの管理はResources.resxで一本化を狙います。
ここからjs用の辞書ファイルを生成できれば理想は達成できそうです。
ちなみにResxの管理画面からResources.resxを更新した時、Resources.Designer.csが更新されないケースが発生しました。直接Resources.resxを開いて編集すれば更新されるのですが、Key名を変えたりした時にDesigner.csが更新されないと正しくコンパイルエラーにならないので少し困ります。
解決方法はわかりませんが、ソリューションエクスプローラーのResources.resxを右クリックから「カスタムツールの実行」で再生成できます。
jsの辞書管理の方式
jsonとはせずにjsファイルで配布するのがいいかなと思ってます。
jsとjsonは連想配列ならフォーマットはほぼ同じですし、jsの方が柔軟に対応できます。
#dictionary.json
const dictionary = {
Msg: 'aaa',
ButtonText: 'bbb'
}
// module exportするなりご自由に。
辞書ファイル生成とともにd.tsも生成する。
#dictionary.d.ts
declare const dic: {
Msg: string,
ButtonText: string,
}
各ページごとに辞書ファイルを生成するか、全ページの辞書ファイルを生成するかは状況によりけり。
今回は全ページ共通で1つの辞書ファイルを作成し、全ページで読み込ませます。
ファイルサイズが肥大化する上にロードにも時間が掛かりますが、初回ロード後はブラウザのキャッシュで2回目以降のリクエストは速くなるはずです。
個別ページで辞書を作成する場合は、各ページごとに辞書jsへのリンクを貼るか、
サーバーサイドでレンダリングしているなら、html内のscriptタグ内に直書きするのもありでしょう。Resources.resxから当該ページのjsで使われているKeyValueのみを展開しているケースを実際に業務でも見かけました。ただし、このやり方は個人的には好きじゃないですね。Resources.resxに記載した上に、htmlにも列挙が必要なので完全に2度手間です。
辞書jsの生成
ビルドイベントでビルド後に生成させることにします。
コマンドラインで実行できるようにするため、辞書jsを生成するだけのコンソールアプリケーションを作成します。このexeに対してResources.resxのパスを渡して変換させます。
以下のページが非常に参考になります。
サーバー内の辞書切り替え
var culture = "ja-JP";
Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture);
Thread.CurrentThread.CurrentCulture = new CultureInfo(culture);
コメント