Senchaでの日本語と英語の切り替え

この記事は、 [Sencha Advent Calendar 2012](http://www.adventar.org/calendars/23) の16日目の記事です。

まさかの（元弊社）大先輩によるサポートが得られたところで、も少し頑張ってみます！
今回はSencha/Ext JSのロケールについて書いてみます。

## Senchaのlocale
Senchaシリーズでは言語の切り替えを、言語ごとに用意されているlocaleファイルによって実現しています。
メインとなるJavaScriptを読み込んだあとに、locale用JavaScriptを読み込むことで、クラスごとの言語プロパティ(ラベルとか)を上書いています。

```html:index.html
<script type="text/javascript" charset="utf-8" src="extjs/ext-all.js"></script>
<script type="text/javascript" charset="utf-8" src="extjs/locale/ext-lang-ja.js"></script>
```

上のHTMLで指定しているext-lang-ja.js を ext-lang-en.js に変えれば英語に、ext-lang-fr.jsに変えればフランス語になります。

アプリケーションを作るときに、いろいろな言語に対応できるようにしておくのは助かりますよね。Senchaではこれをフレームワーク側で準備してくれているという状況です。
実際の動きは、以下の言語切替えサンプルが分かりやすいかと。
[Multi-Lang Excample](http://dev.sencha.com/deploy/ext-4.0.0/examples/locale/multi-lang.html)

## 動的にやりたい！
先ほどのサンプルでは、切り替えのたびにページをリロードしています。
これは、ラベルなどといったDOMツリー上に生成されてしまったものを切り替えるのがめんどくさいからじゃないかな〜と推測しています。
クラス定義は書き換えるけど、インスタンスまでは面倒みれないよ。的な。

でもやっぱり動的に切り替えたいですよね。。。
で、昔 [@kotsutsumi](https://twitter.com/kotsutsumi) さんがやってたなーと思って、なんとなくおさらいしてみました。

* ボタンを押したらlocaleスクリプトをロード
* 各コンポーネントにロードしたことを通知（refresh処理）

みたいな流れです。
ロード通知に関しては、各コンポーネントがイベントを見るのがいいんですけど、小さめのサンプルなので割愛です。

### まずは
上の内容を適用するためのサンプルを準備、いつものようにAPIドキュメントのサンプルをパクります。
今回はDateフィールドに白羽の矢を。[Ext.form.field.Date](http://docs.xenophy.com/ext-js/4-0/#/api/Ext.form.field.Date)
![サンプルアプリ](http://farm9.staticflickr.com/8202/8274570623_7626da35a4.jpg)
Ext.ApplicationでMVC形式で作ると以下の感じです。

```javascript:sample-app.js
Ext.application({
    name      : 'MyApp',
    appFolder : 'MyApp',
    launch: function() {
        Ext.create('MyApp.view.FormPanel').render(Ext.getBody());
    }
});
```

```javascript:sample-view.js
Ext.define('MyApp.view.FormPanel', {
    extend   : 'Ext.form.Panel',
    alias    : 'widget.myapp-form',
    width       : 300,
    bodyPadding : 10,
    frame       : true,
    items : [{
        xtype   : 'datefield',
        anchor  : '100%',
        name    : 'from_date',
        fieldLabel: 'From',
        maxValue: new Date()
    }, {
        xtype   : 'datefield',
        anchor  : '100%',
        name    : 'to_date',
        fieldLabel: 'To',
        value   : new Date()
    }]
});
```

### localeロード処理を追加
言語切替えを動かすために、ボタン、locale用ファイルのロード処理を追加します。
まだ、英語(デフォルト)状態です。

![ボタン追加版](http://farm9.staticflickr.com/8481/8274629223_27c1038b9a.jpg)

```javascript:view/FormPanel.js
Ext.define('MyApp.view.FormPanel', {
    extend   : 'Ext.form.Panel',
    alias    : 'widget.myapp-form',
    requires : [
       'MyApp.view.ToDateField',
       'MyApp.view.FromDateField'
    ],
    width       : 300,
    bodyPadding : 10,
    frame       : true,
    items : [{
        xtype   : 'fromdatefield',
        anchor  : '100%',
        name    : 'from_date',
        maxValue: new Date()
    }, {
        xtype   : 'todatefield',
        anchor  : '100%',
        name    : 'to_date',
        value   : new Date()
    }],
    buttons : [{
        text : 'English',
        type : 'en'
    }, {
        text : 'Japanese',
        type : 'ja'
    }]
});
```

```javascript:controller/Main.js
Ext.define('MyApp.controller.Main', {
    extend: 'Ext.app.Controller',
    views : ['FormPanel'],
    refs  : [{
        ref     : 'form',
        selector: 'myapp-form'
    }],
    init : function() {
        var me = this;
        me.control({
            'myapp-form button': {
                click: function(btn) {
                    me.getScript(btn.type);
                }
            }
        });
    },
    getScript: function(lang) {
        var me = this;
        Ext.Ajax.request({
            url : 'extjs/locale/ext-lang-'+lang+'.js',
            success: function(response){
                new Function(response.responseText)();
            }
        });
        Ext.Ajax.request({
            url : 'MyApp/locale/lang-'+lang+'.js',
            success: function(response){
                new Function(response.responseText)();
                //ここはイベント化したほうが良いところ
                me.getForm().items.each(function(item) {
                    item.refreshLang();
                });
            }
        });
    }
});
```

ここで、MyApp.view.ToDateField、FromDateFieldは、リロード後のrefresh処理(refreshLang関数)を持たせたExt.form.field.Dateの拡張です。

localeファイルは以下のように準備します。
今回はラベルのみですね。本家と同様にoverrideで上書いています。

```javascript:locale/lang-ja.js
Ext.onReady(function() {
    Ext.define("MyApp.locale.ja.view.ToDateField", {
        override: "MyApp.view.ToDateField",
        fieldLabel : "トゥー"
    });
    Ext.define("MyApp.locale.ja.view.FromDateField", {
        override: "MyApp.view.FromDateField",
        fieldLabel : "フロム"
    });
});
```

こんな感じで準備すると、「Japanese」ボタンを押した時に↓みたいに言語が切り替わります。
「English」ボタンを押せばもとに戻ります。
![ラベル、バリデーション](http://farm9.staticflickr.com/8496/8274629071_5f0f257cb5.jpg)
![DatePicker](http://farm9.staticflickr.com/8208/8274629151_d682265b65.jpg)

## 気づいたこと

1. 今回のサンプルを作ってて気づいたのは、DatePickerがDOMの使い回しをしている。
なので、上のrefresh処理でこれを消しておかないと言語が上書きできなかった。

2. Ext JS本家のlocaleファイルがプロパティ足りてないやつがいいる。
(今回はext-lang-en.jsのDatePickerまわりに無いやつがいた)
これはだれに言ったらいいんだろうか？？

サンプルソースは以下に置いてみました。
[LangSample](https://github.com/MtBlue81/LangSample)

次回がいないですね。。。ピンチ！？(^_^;)
だれか〜〜〜ｗ
[Sencha Advent Calendar 2012](http://www.adventar.org/calendars/23)