まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:ejs

「国際化」に対応したウェブアプリケーションを Node.js で作る方法を調べたので、メモ替わりに残しておきます。

ここでの「国際化(internationalization, i18n)」はウェブブラウザで設定した言語によって自動的に英語表記にしたり、日本語にしたり、・・・という切り替えを行えるようなものです。自動翻訳とかそういうものではありません。またブラウザで設定した言語は HTTP リクエスト時に "Accept-Language" ヘッダで送信されることになるので、後述の動作確認は curl コマンドでこのヘッダを指定して行っています。

このような国際化対応アプリケーションを Node.js で、正確には Node.js + Express + EJS の環境で作ってみました。

Node.js で国際化対応アプリケーションを作る場合、i18n というパッケージを使うのが手っ取り早いです:
https://www.npmjs.com/package/i18n

ソースコード(app.js)はこんな感じにしました。余計な部分を削ぎ落として、最小限必要な部分だけを残しています(赤字部分が i18n 関連の箇所です):
//. app.js

var express = require( 'express' ),
    fs = require( 'fs' ),
    ejs = require( 'ejs' ),
    i18n = require( 'i18n' ),
    request = require( 'request' ),
    session = require( 'express-session' ),
    app = express();

var port = 3000;

app.set( 'views', __dirname + '/public' );
app.set( 'view engine', 'ejs' );

i18n.configure({
  locales: ['en', 'ja'],
  directory: __dirname + '/locales'
});
app.use( i18n.init );

app.get( '/', function( req, res ){
  res.render( 'index' );
});

app.listen( port );
console.log( "server starting on " + port + " ..." );

今回は英語(en)と日本語(ja)に対応したアプリケーションにしました。

また '/' にアクセスした時に ejs の index テンプレートを使った画面が表示されるような内容にしています。ちなみに index テンプレート(public/index.ejs)の内容は以下のようになっています:
<html>
<head>
<title><%= __('subject') %></title>
</head>
<body>
<h1><%= __('subject') %></h1>
<hr/>
<%= __('body') %>
</body>
</html>


テンプレート内で subject と body という2つの変数を使った表記を行っています。実際にはこれらの部分に言語設定に合わせた内容が表示されることになります。

そして言語ファイルを以下のように用意します:

(英語用: locales/en.json)
{
  "subject": "subject",
  "body": "body"
}


(日本語用: locales/ja.json)
{
  "subject": "サブジェクト",
  "body": "本文"
}

英語設定で利用した場合、上記の subject 変数部分は "subject", body 変数部分は "body" と表示されます。また日本語設定の場合、それぞれ "サブジェクト" と "本文" となります。


これで準備できました。 npm install して実行(node app)します:
$ npm install
$ node app

確認は別の端末から curl で行いました。まずは Accept-Language を en(英語)にしてアクセス:
$ curl http://localhost:3000/ -H 'Accept-Language: en'

<html>
<head>
<title>subject</title>
</head>
<body>
<h1>subject</h1>
<hr/>
body
</body>
</html>
>

次は日本語設定でアクセスした場合:
$ curl http://localhost:3000/ -H 'Accept-Language: ja'

<html>
<head>
<title>サブジェクト</title>
</head>
<body>
<h1>サブジェクト</h1>
<hr/>
本文
</body>
</html>


期待通りに動いています!


アプリケーションの国際化そのものはこれだけで出来ました。そして問題になるのは「どうやって色んな言語用の JSON リソースファイルを用意するか?」です。1つ1つ翻訳サービスなどを使いながら作る、という方法もありますが、そんな言語リソースファイルの翻訳作業は IBM Cloud の Globalization Pipeline サービスを使うと英語のリソースファイルから各言語に翻訳したリソースファイルをまとめて作ることができてとても便利です。このサービスについては以前のブログで使い方も含めて紹介しているので参照ください:
Globalization Pipeline サービスがリリースされました!


と、最後は宣伝でしたw

今回のエントリ内容は、このエントリの続編的な内容です。実際に作業する準備の手順も含まれているので、実際に試す前に一度内容をご確認ください:
Bluemix 上の Node.js を使う


Express は Node.js 上で使う MVC フレームワークです。いわゆる "MEAN"(mongoDB + Express + AngularJS + Node.js) スタックを構成する要素の1つです。

また EJS は Node.js 上で利用可能なテンプレートエンジンです。HTML ベースのテンプレートにルック&フィールの UI をデザインし、その中身を変数化して動的に埋めていくことで Web アプリケーションの見栄えを作っていくことができます。

設計とデザイン(見た目)とを別チームに分けて開発するようなスタイルでは、こういったテンプレートエンジンと MVC フレームワークを活用して、デザインチームにテンプレートを、設計チームに MVC を含むプログラミング部分を担当してもらう、といった体制で開発することは一般的です。


Express も EJS も、Node.js ではそれなりにメジャーなコンポーネントで、通常はパッケージマネージャーである npm を使ってインストールして利用するものです。では npm 環境が使えない Bluemix では、これらをどのようにして導入して使えばいいのか? という疑問が出てきます。

その答は、上記の前エントリ内でも述べていますが、package.json ファイルを使って必要なモジュールを動的に指定して用意する、ということになります。Express も EJS もこの方法で Bluemix 内の Node.js ランタイム内に導入可能です。以下にその具体的な手順を紹介します。


今回作成するファイルはこの3つです:
|- public/
|    |- hello.ejs
|- app.js
|- package.json

まずはテンプレートを用意します。 public というフォルダを作り、その下に hello.ejs というテキストファイルを以下の内容で(UTF-8 で)作ります。これが見栄えの部分です:
<html>
<h1><%= title %></h1>
<div><%- content %></div>
<div><%= n %> views</div>
</html>

title, content, n という3つの変数が定義されていて、それらの値を使った HTML が記述されています。EJS ではテンプレート内での変数の書き方は2種類あって、<%= ~ %> で括られた部分はエスケープされて出力され、<%- ~ %> で括られた箇所はエスケープされません(HTML タグが HTML タグとして有効になります)。今回の例では content 変数に HTML タグが含まれている場合は HTML タグが有効になります。


次はシステム設計です。 前回の server.js を元に、app.js を以下の内容で作成します:
var express = require( 'express' );
var app = express();

var fs = require( 'fs' );
var ejs = require( 'ejs' );
var template = fs.readFileSync( __dirname + '/public/hello.ejs', 'utf-8' );

var cfenv = require( 'cfenv' );
var appEnv = cfenv.getAppEnv();

var n = 0;

app.get( '/', function( req, res ){
  n ++;
  var data = ejs.render( template, {
    title: "ハローワールド!",
    content: "ようこそ <strong>Express</strong> + <i>EJS</i> の世界へ",
    n: n
  });
  
  res.writeHead( 200, { 'Content-Type': 'text/html' } );
  res.write( data );
  res.end();
});

app.listen( appEnv.port );



Express を使って http アプリケーションが作られ、ドキュメントルート('/')へのリクエスト時に表示する内容は EJS のテンプレートをベースに、その変数部分だけが動的に指定されて表示されるようになっています(EJS のテンプレートパスを指定する際に fs モジュールも使っています)。変数 title, content, n は変数 data 内にまとめて指定しています。なお変数 content の内容は HTML タグを含むテキストです。また変数 n はイクンリメント変数なので、リロードする度に1つずつ値が増えるようにしています。 cfenv モジュールに関しては上述の前回の内容を参照してください。

最後に package.json を以下の内容で用意します:
{
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "cfenv": "1.0.x",
    "express": "4.12.x",
    "ejs": "2.3.x",
    "fs": "0.0.x"
  }
}

"scripts" の中で app.js を起動するよう指定している箇所は前回と同様です。加えて、今回は Express, EJS(とファイルシステム利用のための FS )のモジュールを利用したいので、"dependencies" の中でこれらのモジュールを動的にロードするよう指定しています。


これら3つのファイルが用意できたら、cf ツールで Bluemix 上の Node.js ランタイムにプッシュして、ブラウザでドキュメントルートにアクセスしてみます:
2015100705


hello.ejs テンプレートをベースとしたページが表示されており、また変数 content に含まれている HTML タグが有効になっていることがわかると思います。また同じページをリロードすると変数 n の値がインクリメントされて、1つずつ増えていくのがわかります:
2015100706


npm でインストールしていたモジュールを package.json で指定して動的にインストールする、という違いが分かってしまえば、Bluemix 上でもそんなに違和感なく Node.js 環境が使えるようになります。







 

このページのトップヘ