初心者のためのExtJS入門

ExtJSを使うので、ついでにまとめていきます

多言語対応[classic]

今回は言語の切り替えを試してみます。

おおまかなポイントを列挙しました。

  • 言語はプルダウンで選択する
  • 選択した言語はローカルストレージで保持する
  • ロケールファイルをロードしてから、ビューを作成する

app.jsonの修正

localeの定義は消します。

ext-localeのrequireは残します。

言語はプルダウンで選択する

まずは言語選択用のプルダウンを設置します。

/**
 * メインパネル。
 *
 * @class Sample.view.main.Panel
 * @extend Ext.panel.Panel
 */
Ext.define('Sample.view.main.Panel', {
    extend: 'Ext.panel.Panel',
    xtype: 'main_panel',

    requires: [
        'Ext.picker.Date'
    ],

    title: '多言語対応',

    tools: [
        {
            xtype: 'combo',
            displayField: 'text',
            valueField: 'value',
            queryMode: 'local',
            editable: false,
            forceSelection: true,
            store: {
                fields: ['text', 'value'],
                data: [
                    {'text': 'English', 'value': 'en'},
                    {'text': '日本語', 'value': 'ja'}
                ]
            }
        }
    ],

    items: [
        {
            xtype: 'datepicker'
        }
    ]

});

f:id:sham-memo:20170319164154p:plain

選択した言語はローカルストレージで保持する

ビューコントローラを追加し、言語プルダウンを変更したらローカルストレージに保存するようにします。

/**
 * メインパネル。
 *
 * @class Sample.view.main.Panel
 * @extend Ext.panel.Panel
 */
Ext.define('Sample.view.main.Panel', {
    extend: 'Ext.panel.Panel',
    xtype: 'main_panel',

    requires: [
        'Ext.picker.Date',
        'Sample.view.main.ViewController'
    ],

    controller: 'main',

    title: '多言語対応',

    tools: [
        {
            xtype: 'combo',
            displayField: 'text',
            valueField: 'value',
            queryMode: 'local',
            editable: false,
            forceSelection: true,
            store: {
                fields: ['text', 'value'],
                data: [
                    {'text': 'English', 'value': 'en'},
                    {'text': '日本語', 'value': 'ja'}
                ]
            },
            value: Ext.util.LocalStorage.get('sample').getItem('locale') || 'en',
            listeners: {
                change: 'onChangeLocaleCombo'
            }
        }
    ],

    items: [
        {
            xtype: 'datepicker'
        }
    ]

});

/**
 * ビューコントローラクラス。
 *
 * @class Sample.view.main.ViewController
 * @extend Ext.app.ViewController
 */
Ext.define('Sample.view.main.ViewController', {
    extend: 'Ext.app.ViewController',
    alias: 'controller.main',

    requires: [
        'Ext.util.LocalStorage'
    ],

    /**
     * 言語プルダウンchangeイベント時の処理。
     *
     * @param {Ext.form.field.Combo} field プルダウン
     * @param {String} value 値
     */
    onChangeLocaleCombo: function (field, value) {
        var store = Ext.util.LocalStorage.get('sample'),
            currentLocale = store.getItem('locale');

        if (currentLocale !== value) {
            store.setItem('locale', value);
            window.location.reload();
        }
    }
});

これでローカルストレージに選択した言語が保存されます。

保存後は、ロケールファイルをロードするために、window.location.reloadでブラウザをリロードさせます。

ロケールファイルをロードしてから、ビューを作成する

ここからはApplication.jsを修正していきます。

まずはapp.jsのmainViewを削除します。mainViewにはビューポートとなるコンポーネントを定義していますが、これが定義されているとロケールファイルをロードする前にビューが作成されてしまうためです。

とりあえずコメントアウトしました。

/*
 * This file is generated and updated by Sencha Cmd. You can edit this file as
 * needed for your application, but these edits will have to be merged by
 * Sencha Cmd when upgrading.
 */
Ext.application({
    name: 'Sample',

    extend: 'Sample.Application',

    requires: [
        'Sample.view.main.Panel'
    ]

    // The name of the initial view to create. With the classic toolkit this class
    // will gain a "viewport" plugin if it does not extend Ext.Viewport. With the
    // modern toolkit, the main view will be added to the Viewport.
    //
    //mainView: 'Sample.view.main.Panel'
    
    //-------------------------------------------------------------------------
    // Most customizations should be made to Sample.Application. If you need to
    // customize this file, doing so below this section reduces the likelihood
    // of merge conflicts when upgrading to new versions of Sencha Cmd.
    //-------------------------------------------------------------------------
});

次にApplication.jsでロケールファイルをロードします。

/**
 * アプリケーションクラス。
 *
 * @class Sample.Application
 * @extend Ext.app.Application
 */
Ext.define('Sample.Application', {
    extend: 'Ext.app.Application',
    
    name: 'Sample',

    requires: [
        'Ext.util.LocalStorage'
    ],

    /**
     * ローンチ処理。
     */
    launch: function () {
        var me = this;

        me.loadLocaleFile(function () {
            me.setMainView('Sample.view.main.Panel');
        });
    },

    /**
     * ロケールファイルをロードする。
     * @param {Function} [cb] コールバック
     */
    loadLocaleFile: function (cb) {
        var me = this,
            store = Ext.util.LocalStorage.get('sample'),
            locale = store.getItem('locale') || 'en';

        Ext.Loader.loadScript({
            url: me.getLocaleUrl(locale),
            onLoad: function () {
                if (cb) {
                    cb();
                }
            }
        });
    },

    /**
     * ロケールファイルのURLを返す。
     *
     * @param {String} locale 言語名
     * @returns {String} ロケールファイルのURL
     */
    getLocaleUrl: function (locale) {
        // MEMO: プロジェクト毎のロケールファイルのパスを設定する
        return '/ext/classic/locale/overrides/' + locale + '/ext-locale-' + locale + '.js';
    }
});

ロケールファイルをロードした後に、Ext.ApplicationのsetMainViewを呼び出すことで順番を担保しています。

ちなみに上記コードでは、ロケールファイルのパスはサンプル用なので、実際には適宜変更することになります。開発環境や本番環境で差異がある場合もあるでしょう。

f:id:sham-memo:20170319185052p:plain

f:id:sham-memo:20170319185103p:plain

良い感じになりました。

補足

これはあくまでも一例です。

ロケールファイルをロードできれば良いだけなので、scriptタグでロードしようが、JQueryでロードしようが、どういう方法でやっても問題ありません。