読者です 読者をやめる 読者になる 読者になる

SourceChord

C#とXAML好きなプログラマの備忘録。最近はWPF系の話題が中心です。

Express4 + i18nextでローカライズ

Node.js

Node.js + Expressな環境で、i18nextを使ってローカライズする方法です。

ざっと使い方をメモしときます。

ローカライズには、i18nextというライブラリを使ってみました。
i18next
http://i18next.com/
https://www.npmjs.com/package/i18next

これはブラウザ側でも、node.jsのサーバー側でも使える素敵なライブラリです。
また、Web界隈でのローカライズで一般的に使われている、po/moファイルとの連携手段も用意されているようです。
(po/moファイルをjson形式に変換して読み込めるみたい。詳しくは以下ページ。)
http://i18next.com/pages/ext_i18next-conv.html


Node.jsから使う場合の手順については、以下に書かれていますが、
http://i18next.com/node/index.html
説明がexpress3のものになっているようで、若干説明が古いです。
このページの説明を適宜読み替えて、express4で使ってみました。

手順

サンプルとして、express-generator(ejs指定)で出力したプロジェクトの雛形でローカライズをやってみます。

プロジェクトの雛形作成

まずはexpress-generatorで-eオプションを付けて、ejsを使ったプロジェクトの雛形を作ります。
プロジェクトの構造は以下のようになります。
f:id:minami_SC:20150517193848p:plain:w200

i18nextのインストール

npmから以下のコマンドでインストールできます。

npm install i18next

ローカライズファイルの用意

今回は日本語/英語の二種類に対応します。

ローカライズ用のデータは、localesという名前のフォルダの下に言語/地域ごとに分けて入れていきます。
↓みたいな感じ。
f:id:minami_SC:20150517194637p:plain

今回のサンプルでは、地域は入れず、ja/enという名前のフォルダを作成しました。
また、対応する言語のフォルダが無かった場合に、フォールバックして参照するための既定値として、devというフォルダも作ります。

translation.json

dev/ja/enそれぞれのフォルダに、jsonファイルでローカライズに使用するテキストを配置します。
dev/enのフォルダには、以下のようなファイルを置きます。

{
    "app": {
        "name": "i18nextTest",
        "sampleText": "This is localized text."
    }
}

jaフォルダのtranslation.jsonでは以下のように、日本語で文字列を書いておきます。

{
    "app": {
        "name": "i18nextのテスト",
        "sampleText": "これはローカライズされた文字列です。"
    }
}

コード

今回作ったプロジェクトとコードはこんな感じです。
雛形そのままのコードなどの説明は省略しています。

package.json

generatorで生成したものに、i18nextの依存を追加しただけです。

{
  "name": "i18nextTest",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "body-parser": "~1.12.0",
    "cookie-parser": "~1.3.4",
    "debug": "~2.1.1",
    "ejs": "~2.3.1",
    "express": "~4.12.2",
    "i18next": "^1.7.10",
    "morgan": "~1.5.1",
    "serve-favicon": "~2.2.0"
  }
}
app.js

generatorで作った雛形に、以下の4つの処理を追加しています。
・i18nextをrequire
・i18nextの初期化(initメソッド)
・appにi18nextのミドルウェアを登録
・ejsなどのテンプレートエンジンから、i18nextの関数を呼べるようにする。(registerAppHelperメソッド)

コード全体は以下の通り。

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var routes = require('./routes/index');
var users = require('./routes/users');

var i18n = require('i18next');
i18n.init({
    ignoreRoutes: ['public/'],
    useCookie: false,
    debug:true
});


var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

// ルーターの設定より前にi18nextのミドルウェアの追加処理をしておく。
app.use(i18n.handle);
// ejsなどの、各種テンプレートエンジン中から、i18nextの関数を使えるようにする。
i18n.registerAppHelper(app);

app.use('/', routes);
app.use('/users', users);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});


module.exports = app;
routes/index.js

ルーティング定義の部分は特になにもしていません。
express-generatorで生成したファイルのままです。

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;
view/index.ejs

ejsファイルに、ローカライズ対象のテキストを埋め込みます。
「<%= ・・・ %>」で囲われた中に、以下のように「t.(キー名)」という形でローカライズ対象の文言を展開するための関数呼び出しを書きます。

<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <h1><%= t('app.name')%> </h1>
    <span><%= t('app.sampleText')%></span>
  </body>
</html>

プロジェクトの構造は以下のとおりです。
f:id:minami_SC:20150517202448p:plain:w200

実行結果

ブラウザで表示し、言語設定を切り替えると、表示されるhtmlのテキストが切り替わります。

日本語 英語
f:id:minami_SC:20150517202104p:plain f:id:minami_SC:20150517202111p:plain