Umi Uyuraのブログ

プログラミング関連の作業ログ

Browserify環境でMoment.jsの国際化を利用しようとしたときのメモ

ちょっとした日時計算をするだけのペライチなWebページを作ろうとしていました。

日時情報を取り扱う処理を実装するので、定番の Moment.js を組み込んでいたのですが。

UI部分は最近取り組んでいるReactを使うことにして、JSXの変換も済ませた形で組み込むのが良いと思ったので、開発環境としてはBrowserify + Babelify (+ Watchify)としつつ、Moment.jsも含めて、npmで諸々導入していました。

そのついでに、特に必須というわけではなかったのですが、日付や時刻の表記をMoment.js使えば国際化するの簡単なんじゃじゃない?と思ってやってみたところハマってしまったので、そのメモです。

ハマったこと

ハマった点は、以下の3つ。

  • クライアント(ブラウザ)のタイムゾーンが取れない
  • Moment.jsをrequireで読み込んだ場合、ロケールデータが読み込まれない
  • ブラウザの言語設定の取り方は複数ある

クライアント(ブラウザ)のタイムゾーンが取れない

国際化をするにあたり、まずは結果表示時にタイムゾーン名を表示しようと思ったのですが、Moment.jsにタイムゾーンを取得するAPIがありませんでした。

Moment.jsの拡張ライブラリのような Moment Timezone というものがあるのですが、こちらを見ても、 タイムゾーンを指定して 処理をするAPIはあるのですが、肝心の取得をするものが見当たりません。

そこで調べていると、以下の記事を発見しました。

JavaScript でタイムゾーン名を取得する方法 - Qiita

これによると、そもそもJavaScriptのDateには、タイムゾーン名を取得するためのインターフェイスが用意されていないから、ということでした。

紹介されている jsTimezoneDetect というライブラリを使うことで、ある程度判定してくれるようですが、あくまで予測であって正確なものではないということです。

ちなみに、jsTimezoneDetectもnpmから導入できます。

$ npm install jstimezonedetect

Moment.jsをrequireで読み込んだ場合、ロケールデータが読み込まれない

次に、Moment.jsの format() で得られる結果を国際化しようとして、ハマりました。

以前にもWebサイトの制作でMoment.js自体は使ったことがあり、そのときはロケール設定さえすれば問題なく日本語フォーマットにできた覚えがあったので、特につまずくことはないと思っていたのですが。

Node.jsでロケールデータを読み込む場合、公式のドキュメントによると、 moment.locale('ja') を最初に呼び出したのタイミングで読み込まれるようです。

Loading locales in NodeJS

ところが、Browserifyを使って変換・結合されたJavaScriptを確認すると、ロケールデータが含まれていませんでした。

おそらく、 moment.locale() で指定する言語が明確に指定されていないため、読み込まれないのだと推測しました。

調べていたところ、別のライブラリのIssueで対応策を発見。

How to use different locales with browserify/npm build? · Issue #10 · zippyui/react-date-picker

これによると、以下のように require で個別にロケールデータを読み込むというもの。

require('moment/locale/ja');

しかし、ユーザーの環境に応じてロケールデータを切り替えたい場合は、この方法だと全部の require を書く必要があります。

少し面倒だったので、以下のようにて指定したみたところ、一括で全ロケールデータを読み込んでくれました。

require('moment/min/locales');

注意点は、その後 moment.locale()ロケールを設定しないと、最後に読み込んでいる zh-tw が使われてしまう模様。

アプリの最初にデフォルトのロケールを設定してあげるのが良さそうです。

ブラウザの言語設定の取り方は複数ある

クライアント(ブラウザ)に合ったロケールを指定するために、今度は言語設定を参照したいわけですが、そのAPIがブラウザによって若干違うらしいということもわかりました。

ブラウザの言語をJavascriptから調べる。 – @masuidrive blog

上記のサイトでは先頭2文字を切り出していましたが、Moment.jsではハイフン区切りのものと一致しない場合は自動的にハイフン前の部分で探してくれるらしい( en-NZ がなければ en を探す、など)ので、 .substr(0,2) の部分は省いた形で利用させていただくことにしました。

function browserLanguage() {
  try {
    return (navigator.browserLanguage || navigator.language || navigator.userLanguage);
  }
  catch(e) {
    return undefined;
  }
}

感想

ふだん国際化を意識することがあまりなかったこともあり、ブラウザ周りの言語関係は迂闊に手を出すとハマるということがわかりました。

参考

WEB+DB PRESS Vol.87

WEB+DB PRESS Vol.87