material-uiのi18n対策のひとつ

Material Designを実現するReactコンポーネントセットであるmaterial-uiは有用な上で、ソースを読んでも大変参考になるものが多くあります。中でもDatePickerのコードはReactでの動作エフェクトの作り方の他、コンポーネントを組み合わせてコンポーネントを作るというReactの基本的なことまで、多様なテクニックの良質なサンプルともなってます。しかしそんなmaterial-uiもL10nについては手が付いてません(v0.14.2現在)。

https://github.com/callemall/material-ui/blob/v0.14.2/src/utils/date-time.js

  warning(locale === 'en-US',
    'Wrong usage of DateTimeFormat. The ' + locale + ' locale is not supported.');

上記リンクの日付フォーマット関数を見れば様子がわかろうというもの。おいおい中身は"en-US"一択かよ。つまりは一応i18nのきっかけは作ってくれてるけど、中身の実装が追いついていないようです。コア開発者は昼間の仕事としてやってる風で日々の進化も早く、磨かれていってる感が週単位ぐらいで見て取れるのでそのうち対応するのでしょう。

こちらはなんちゃってで日本語対応だけしてみました。中国語と英語はライブラリに丸投げです。

const moment = require("moment");
require('moment/locale/ja');
require('moment/locale/zh-tw');

// material-uiのDatePickerコンポーネントの表示を国際化する
function MomentFormat(locale, options) {
    this.format = function(date) {
        var format1 = "ddd, MMM DD";
        var format2 = "MMMM YYYY";
        var format3 = "MMM/DD/YYYY";
        if(locale && locale.startsWith("ja")) {
            format1 = "M月D日, dddd";
            format2 = "YYYY年 M月";
            format3 = "YYYY年M月D日(ddd)"
        }
        let m = moment(date);
        m.locale(locale);
        // 将来への意欲は感じるが、現時点は無意味なmaterial-uiのoptions!
        if(!options) {
            return m.format(format3);
        } else if (options.month === 'short' &&
            options.weekday === 'short' &&
            options.day === '2-digit') {
            return m.format(format1);
        }
        return m.format(format2);
    };
}

export default MomentFormat;

こんなのを書いて、次のように使う。formatDateはプレースホルダ用でDateTimeFormatとlocaleは入力ダイアログ用。

<DatePicker autoOk={true} wordings={{cancel: this.state.captionCancel}}
                      formatDate={new MomentFormat(this.state.locale).format}
                      DateTimeFormat={MomentFormat} locale={this.state.locale}/>

で、こうなる。ここまでやってもダイアログの最上部の年表示は仕込めず数字だけ。「2016年」と年だけの表示でもi18nすべきことは米国人には想像できなかったか。

f:id:masataka_k:20160125054915p:plain

日付のL10nでmoment.jsを使ってますが、こちらのキモはロケールの読み込み方法です。

require('moment/locale/ja');

これをやらないとmoment.locale()が空っぽの関数のまま。requireして初めてlocale("ja")が効くようになります。アプリケーションとして対応するロケール毎に同じことをやってあげれば良いのですね。私は全く読めませんがアラビア語などもmoment.jsは対応しています。

追記 v.0.14.3

さて、後日(1/27)にmaterial-uiをv.0.14.3にアップデートしたらDatePickerもちょっと進化しました。

f:id:masataka_k:20160128152944p:plain

なんと!曜日がi18nされた!ただし相変わらずen-US以外へのL10nはされてない。そのため、以下に書き直し。"narrow"なるOptionが増えてましたので、そちらに対応しました。

"use strict";
const moment = require("moment");
require('moment/locale/ja');
require('moment/locale/zh-tw');
function MuiDateFormat(locale, options) {
    this.format = function(date) {
        var format1 = "ddd, MMM DD";
        var format2 = "MMMM YYYY";
        var format3 = "MMM/DD/YYYY";
        if (locale && locale.startsWith("ja")) {
            format1 = "M月D日 dddd";
            format2 = "YYYY年 M月";
            format3 = "YYYY年M月D日(ddd)"
        }
        let m = moment(date);
        m.locale(locale);
        // ガンバレ、その意気だ!
        if (!options) {
            return m.format(format3);
        } else if (options.month === 'short' &&
            options.weekday === 'short' &&
            options.day === '2-digit') {
            return m.format(format1);
        } else if (options.month === 'long' &&
            options.year === 'numeric') {
            return m.format(format2);
        } else if (options.weekday === 'narrow') {
            return m.format("dd");
        }
    };
}
export default MuiDateFormat;