deepblue-will’s diary

JS、CSS,Ruby、Railsなど仕事や趣味で試した技術系のことを書いていきます。

静的な多言語Webサイトを1ソースから作る方法

Webサイトの多言語対応をしないといけないことは最近だと珍しくありません。
この多言語化、色々方法はあります。
JSで切り替える方法、HTMLを二つ用意して文言のみ変える方法。

前者ですとSEO的によろしくないです。(Prerenderを使うという手もありますが。)
でも後者ですとデザインの変更が合った時、二つのファイルを修正しないといけないのでめんどくさいです。

そこでJadeとGulpを使って、1つのソースから日英二つのHTMLファイルを生成する方法を紹介します。
GulpでJadeをHTMLに変換する際に、文言を定義しているJSONファイルを流し込んで2つのHTMLを生成するという方法です。

Jade

Jade - Template Engine

簡単にいうと HTMLをものすごく簡単に書けるようになる やつです。
俗に Template Engineなんて言われてます。
同じようなやつに HamlSlim があります。

HTMLはタグで要素を囲んでコンテンツを作成していきますが、Jadeはインデントでそれを表現していきます。 記述量が圧倒的に少なくなるので、慣れると素のHTMLを書くのが馬鹿らしくなってきます。

jade

h1 HelloWorld
.content
  こんにちわ世界

html

<h1>HelloWorld</h1>
<div class="content">
  こんにちわ世界
</div>

Gulp

gulp.js - the streaming build system

いわゆる開発時に色々やってくれるタスクランナーというやつです。
似たようなやつにGruntがあります。
上記のJadeはあの記法のままではブラウザで表示できないのでHTMLに変換する必要があります。
その変換をしてくれるのがこのGulpです。
他にも設定次第でSassのコンパイルCoffeeScriptコンパイル、ローカルサーバーの起動、minify、lintなどなど様々なことができます。Gulpについてはまた別の期会に書こうと思います。

準備

JadeとGulpを使うためにはNode.jsが必要です。
また、以下のpackageが必要になります

  • gulp
  • gulp-jade
  • coffee-script (gulpfileをCoffeeScriptで書くのに必要)
  • gulp-load-plugins(gulpfile内で他のモジュールを読み込むのが楽になる)
  • browser-sync (ローカルサーバーとブラウザの同期)

適当なディレクトリを作って、コンソールで以下のコマンドを入力します。

$ npm init
// 色々聞かれるけど全部EnterでOK
$ npm install -g gulp
$ npm install --save-dev gulp gulp-jade coffee-script gulp-load-plugins browser-sync

構成

サンプルは以下の構成です。
普段はviewのindex.jadeを編集して、gulpでen/index.htmlja/index.htmlを自動生成するという流れです。
文言はそれぞれ日本語と英語ファイルを用意します。(locale/*.json)

├── en
│   └── index.html
├── gulpfile.coffee
├── ja
│   └── index.html
├── locale
│   ├── en.json
│   └── ja.json
├── package.json
└── view
    └── index.jade

ポイント1:文言ファイル

Webサイトで使用する文言をすべてjsonファイルに定義します。

ja.json

{
  "locale": "ja",
  "title": "多言語ページのサンプル",
  "content": "親譲りの無鉄砲で小供の時から損ばかりしている。小学校に居る時分学校の二階から飛び降りて一週間ほど腰を抜かした事がある。なぜそんな無闇をしたと聞く人があるかも知れぬ。別段深い理由でもない。新築の二階から首を出していたら、同級生の一人が冗談に、いくら威張っても、そこから飛び降りる事は出来まい。弱虫やーい。と囃したからである。小使に負ぶさって帰って来た時、おやじが大きな眼をして二階ぐらいから飛び降りて腰を抜かす奴があるかと云ったから、この次は抜かさずに飛んで見せますと答えた。(<strong>青空文庫</strong>より)",
  "nav": {
    "brand": "サンプル",
    "home": "ホーム",
    "dropdown": "ドロップダウン",
    "item1": "アイテム1",
    "item2": "アイテム2",
    "item3": "アイテム3"
  }
}

en.json

{
  "locale": "en",
  "title": "Sample of multi-language page",
  "content": "<strong>Lorem ipsum dolor sit amet</strong>, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate",
  "nav": {
    "brand": "Sample",
    "home": "Home",
    "dropdown": "Dropdown",
    "item1": "Item1",
    "item2": "Item2",
    "item3": "Item3"
  }
}

ポイント2:Jadeに変数を使用する

Jadeを編集する時に、文言を記述する箇所を変数で記述します。
変数名は上記のJsonに定義しているものにします。そうすることで、その変数がhtml変換時にJsonに定義されている文言に置き換わります。
変数は#{変数名}と書きます。Jsonがネストしている場合は#{変数名.変数名}とように.でつなぎます。また、定義されている文言内にタグがある場合は!{変数名}と頭に!をつけるとHTMLとして展開されます。

doctype html

html(lang="#{locale}")
  head
    title #{title}
    link(rel="stylesheet", href="../bower_components/bootstrap/dist/css/bootstrap.css")
  body

    nav.navbar.navbar-inverse.navbar-fixed-top
      .container
        .navbar-header
          a.navbar-brand #{nav.brand}
        .navbar-collapse.collapse
          ul.nav.navbar-nav
            li.active: a #{nav.home}
            li.dropdown
              a.dropdown-toggle(data-toggle="dropdown", role="button", aria-expanded="false")
                | #{nav.dropdown}
                span.caret
              ul.dropdown-menu
                li: a #{nav.item1}
                li: a #{nav.item2}
                li: a #{nav.item3}

    .container
      .jumbotron
        h1 #{title}
        p !{content}

    script(src="../bower_components/jquery/dist/jquery.min.js")
    script(src="../bower_components/bootstrap/dist/js/bootstrap.min.js")

ポイント3:gulpfile

gulpで何をするかはgulpfile.coffee/gulpfile.jsというファイルに記述していきます。 ここでは以下のことをしています。

  • js.jsonを使用してjadeをhtmlに変換
  • en.jsonを使用してjadeをhtmlに変換という2つの処理をしています。
  • ローカルサーバー起動時に上記を実行(ローカルサーバーはlocalhost:3000)
  • ローカルサーバー起動中はファイルを監視し、変更があったらjade→htmlを実行
  • gulp serverでローカルサーバーの起動

gulpfile.coffee

g = require 'gulp'
browserSync = require 'browser-sync'
$ = require('gulp-load-plugins')()
path = require 'path'

handleError = (errors) ->
  console.log "Error!", errors.message

jadeTask = (lang) ->
  locales = require "./locale/#{lang}.json"

  g.src 'view/**/*.jade'
  .pipe $.jade {
    locals: locales
    pretty: true
  }
  .on('error', handleError)
  .pipe g.dest "#{lang}/"

  # gulp実行中にlocaleの変更を反映するため
  delete(require.cache[path.resolve("locale/#{lang}.json")])

g.task 'jade:ja', -> jadeTask("ja")
g.task 'jade:en', -> jadeTask("en")
g.task 'jade', ["jade:ja", "jade:en"]

g.task 'server', ['jade'], ->
  browserSync.init
    server:
      baseDir: "."

  g.watch('view/**/*.jade', ['jade'])
  g.watch('locale/*.json', ['jade'])

まとめ

こんなかんじで、jsを使わず静的なHTMLファイルを1つのソースから生成することができます。
文言を外部にしていることで編集がしやすいというメリットもあります。 良かったら参考にしてみてください。

フロントエンドエンジニア養成読本 [HTML、CSS、JavaScriptの基本から現場で役立つ技術まで満載! ] (Software Design plus)

フロントエンドエンジニア養成読本 [HTML、CSS、JavaScriptの基本から現場で役立つ技術まで満載! ] (Software Design plus)