Sencha Ext JS でのグローバリゼーションとローカリゼーション
こんにちは、ゼノフィseoです。
Guest Blog Post
Ext JS はグローバリゼーション機能を初めから備えており、わずかな労力でフレームワークを拡張、追加の要件に対応することができます。Ext JS はローカリゼーションパッケージを同梱しており、インドネシア語からマセドニア語まで40以上の言語に対応、簡単に実装できます。
この記事では、特別な事情や要件に対応するためにJnesis でお客様に提示したソリューションを振り返りますが、まず、インターナショナリゼーションについてまとめましょう。
インターナショナリゼーションでの主な焦点は、アプリケーションで使用する言語の選択肢をユーザーに与えるということです。この機能のためにはアプリケーションが”翻訳可能”であることが必要であり、それはしばしば”i18n”や”インターナショナリゼーション”という言葉で表現されます。”翻訳可能”とは単に他の言語への変換が可能というだけでなく、付随する技術的な要件への対応も意味しています。
最も一般的なシチュエーションに対するグローバリゼーションおよびローカリゼーションのソリューションをSencha は提供しています。それは拡張でき、企業の状況を考慮に入れて対応することができます。
Ext JS アプリケーションをローカライズする方法
Sencha ローカリゼーションドキュメンテーションには、アプリケーションをローカライズするためにSenchaが推奨する方法が詳細に記されています。また、コミュニティメンバーであるSakiさんのブログ記事にもさらに情報があります。続く投稿記事では、以下の点について解説いたします:
- アプリケーションに言語ファイルを(シングルトンまたはオーバライドで)直接書き込む方法。これらは最終的にプロダクションビルドに統合されます
- コンフィギュレーションに定義されている言語セット分のプロダクションビルドをSencha Cmd を通じて生成する方法
- 言語が変更される度にアプリケーション全体をリロードする方法
- アプリケーション起動前に追加のリソースを簡単にロードする方法
- アプリケーションの中で部分的に異なる言語を使用する方法
ソースコードのエクスターナリゼーション
企業には独自のニーズがあり、その開発者はインターナショナリゼーションについて一般の開発者とは異なった考え方をしています。フレームワークを採択し、機能を拡張する以前にまず、ユーザーのニーズを注意深く評価しなくてはなりません(JnesisのテクニカルマネジャーであるVincent Munier のSencha Day 2015 でのプレゼンをご覧ください)。
ある程度の大きさの会社は概して同様に、他言語管理は開発フレームワークを越えた課題である、との結論に到達します。他言語対応は大きな組織にとっては一般的な関心事です。なぜなら製品開発や会社の発展におけるそれぞれ過程で異なる言語を必要とするからです。言語要件が定期的に変わるケースでは翻訳活動はアプリケーションの枠外で行われるべきで、たいていサードパーティ製のソフトウェアに頼り、ウェブサービスを通じてアプリケーションに取り込まれます。
このアプローチの利点は下記のとおりです:
- 言語を変更するためにアプリケーションのリビルドを必要としません。リロードすることで変更は即時に反映されます
- コードを書いた人が翻訳をする必要がなくなります
コードや、JavaScriptファイルがどのようにコーディングされているかを理解していない人に翻訳を任せると問題を引き起こしかねないとの議論にもなるでしょう。しかし、ノンプログラマーでも使用できる翻訳ツールを利用してコンテンツを翻訳し、そのデータをアプリケーションに読み込むことは十分に考え得る手段です。
上記で触れたとおり、公式のExt JS ソリューションはローカライゼーションガイドに記されています。
ツールをネイティブに適用できるようになった我々のアプローチをご説明します。苦労を伴うリファクタリングフェーズを避けるためにも、プロジェクト開始の時点でアプリケーションにインターナショナリゼーションを組み込んだ良い実例をご紹介します。
エレガントな方法
まずはSencha ローカライゼーションガイドやSaki さんのブログ記事を参考にしましょう。アプリケーションのメイン画面が構成される前に言語データをロードします。
このソリューションはExt JS 5 以前に可能だったものに近いです。アプリケーションの”index.html”ファイルの特定のリソースに依存関係を追加します。
このためにまず、下記のように”singleton”クラスの変数として翻訳をストアします:
Ext.define('Jnesis.Labels', { singleton: true, button: 'My english button', title: 'My english title' }); |
これによりアプリケーションのどこからでも翻訳にアクセスすることが可能となります。単に以下のように入力してください:
Jnesis.Labels.button
もしくは Jnesis.Labels.title
“override”することに比較してこのアプローチの優れている点は、ローカライズされた値が全て1つのファイルに存在し、簡単にそれらを使用することができることです。これは上でご説明したとおり、各要素を変数としているからです。
これを終えると、選択した言語の翻訳ファイルでデフォルトの言語ファイル(« Jnesis.Labels »)をオーバーライドするだけでよくなります。
これには2つの方法があります:
- ウェブサービスを使ってデータをロードします。サーバから応答されるJSONはこのようなものでしょう:
- Ext JS クラスのオーバーライドをロードします:
- “Ext.mixin.Mashup”など特定のクラスでロードファンクションおよびそれに関連するプロセスを無効にしました。これによりウェブサービスからの必要なファイルやJSONデータをすべて、”mixin”を取り込んだクラスを読み込む前に確実にロードします。
- Ext JS 6 モダンツールキットとの互換性を確実にしました。これは”initComponent”関数を持っていません。他のオプションは”config”プロパティ、および”apply”メソッドに関連付けられたジェネレーションにあります。
- アプリケーション全体をリロードしない動的な方法で翻訳変数を読み込み、解釈しました
{ "button": "Mon Bouton", "title": "Mon titre" } |
ここでデータをパースし、デフォルトのローカリゼーションシングルトンをオーバーライドします:
Ext.define ('Jnesis.Application', { launch: function () { Ext.Ajax.request({ url: 'get-localization', params:{locale:'fr'}, callback: function (options, success, response) { var data = Ext.decode(response.responseText, true); Ext.override(Jnesis.Labels, data); Ext.create('Jnesis.view.main.Main'); } }); } }); |
Ext.define('Jnesis.locale.fr.Labels', { override: 'Jnesis.Labels', button: 'Mon Bouton', title: 'Mon titre' }); |
そしてExt.Loader.loadscript
関数を使用して処理し、終わればメインビューをロードします:
Ext.define ('Jnesis.Application', { launch: function () { var lang = 'fr', url = 'resources/locale/'+lang+'/Labels.js'; Ext.Loader.loadScript({ url: url, onLoad: function (options) { Ext.create('Jnesis.view.main.Main'); } }); } }); |
新しいプロパティ(例えば”localized”) をベースコンポーネントに定義、アトリビュートを生成することで、ネイティブの継承メカニズムを使います。このアトリビュートはキーと値をもったシンプルなJavaScriptオブジェクトです(上記の Jnesis.Labels.button
やJnesis.Labels.title
を思い出してください)。
キーをストリングで定義することで、オブジェクトが読まれたときにのみ値は更新されます:
Ext.define('Jnesis.view.main.Main', { extend: 'Ext.panel.Panel', localized: { title: 'Jnesis.Labels.title' }, buttons: [{ localized: { text: 'Jnesis.Labels.button' }, handler: 'onClickButton' }] }); |
これが終わると、ベースクラスの”initComponent”メソッドをオーバーライドした上でキーを評価し、どのExt JS コンポーネントでも利用できるようにします:
Ext.define('overrides.localized.Component', { override: 'Ext.Component', initComponent: function() { var me = this, localized = me.localized, value; if (Ext.isObject(localized)) { for (var prop in localized) { value = localized[prop]; if (value) { me[prop] = eval(value); } } } me.callParent(arguments); } }); |
このソリューションはコンポーネントの宣言(コンテナ定義やアイテム定義) のどのような階層レベルでも透過的に働きます。
追加情報
後半のソリューションはExt JS 5 および6(クラシックツールキット) の両方に有効です。Jnesis の過去の適用例を以下に掲載します:
これらの改善に関する情報をさらにシェアするためにも、以下にコメントをお願いいたします。Jnesis エクステンションはSencha Market よりダウンロードしていただけます。