TSUNAGU GROUP TECHNOLOGIES

TGT TechBlogTGT TechBlog

フロントエンドからバックエンドまでの技術ナレッジ

FuelPHPの多言語化対応


エンジニアブログ第三回目となる今回は、FuelPHPの多言語化対応について紹介したいと思います。
弊社のサービスである、外国人留学生のための求人情報サイト「ニホンdeバイト」( https://nihondebaito.com )で多言語化対応をした事例から、多言語化対応時のポイントを抑えながら実際に言語の切り替えをブラウザで確認できるところまで紹介します。

多言語化とは

多言語化とは、様々な言語で利用するための設計や仕様などを組み込み、特定の言語に対応させることによって、利用者が自分の使用する言語にあわせて設定を切り替えて使用できるようにすることです。
様々な言語で利用するための設計や仕様などを組み込むことを「国際化(internationalization)」、特定の言語に対応させることを「地域化(localization)」といいます。
「国際化(internationalization)」の最初と最後の文字の間に18文字あることから「i18n」、「地域化(localization)」も同様の理由で「l10n」と呼ばれたりもします。
それでは、この多言語化対応について以下の環境で見ていきたいと思います。

環境

・FuelPHP: 1.7
・PHP: 5.5
・Smarty: 3.1

0.各種定義

多言語化対応するにあたって、表示する言語を取得する方法を決める必要があります。
Googleは多言語化のガイドライン( https://support.google.com/webmasters/answer/182192?hl=ja )を出しており、「URLから言語がわかるようにすること」を推奨しています。
archive_20160916_1
今回はこのガイドラインに沿って「gTLDを使用したサブディレクトリ構造」で表示する言語を判別する(切り替える)こととし、サブディレクトリに言語コードを指定することにします。
また、切り替え可能言語を日本語(ja)・英語(en)・中国語(簡体字)(zh)の3言語のみとします。
最終的に、下記ようなのURL構造で多言語化サイトを実現します。

  • 日本語:http://example.com/ja
  • 英語 :http://example.com/en
  • 中国語:http://example.com/zh

多言語化対応をするにあたって最低限必要な定義が出来たので、実際にプログラムを見ていきたいと思います。
※ FuelPHPのインストール等は本記事では取り扱いません。公式ページ等を参考に環境の準備をお願いします。
※ 本記事で特に指定していない部分については多言語化対応していない場合のFuelPHPと同じであるため、各環境に合わせて実装してください。

1. 切り替え可能言語をconfigに定義

fuel/app/config/config.php内に、「locales」というキーに対して1つ以上の「言語コード => ロケール」の値を持つ配列を追加します。
このlocales配列を使用して切り替え可能言語の制限をします(後述)。

return array(
    'language'           => 'ja', // Default language
    'language_fallback'  => 'en', // Fallback language when file isn't available for default language
    'locale'             => 'ja_JP.utf8', // PHP set_locale() setting, null to not set
    /***** 以下を追加 *****/
    'locales'            => array(
        'ja'    => 'ja_JP.utf8',
        'en'    => 'en_US.utf8',
        'zh'    => 'zh_CN.utf8',
    ),
);

2. URIクラスの拡張クラスを作成

Coreクラスの1つであるURIクラスを拡張し、URLから言語コードを取得して表示する言語を切り替える処理を追加します。
任意のディレクトリにURIクラスを定義するためのファイルを作成し、以下のコードを実装します。
※ Coreクラスの置き換えをするため、クラス名はオリジナルのクラスと同名である必要があります。
今回は、fuel/app/classes/extend/uri.phpを作成します。

class Uri extends Fuel\Core\Uri
{
    public function __construct($uri = NULL)
    {
        parent::__construct($uri);
        $this->detect_language();
    }
    public function detect_language()
    {
        if(!count($this->segments)) {
            // URL内に言語コードの指定がない場合の処理
            return;
        }
        if($this->segments[0] == 'test') {
            // 言語コード指定なしでアクセス可能にする場合の処理
            // この例では http://example.com/test/○○ のアクセスを許可
            return;
        }
        // URL内の言語コードを取得
        $str_language = $this->segments[0];
        // config.phpで定義したlocales配列を取得
        $ary_locales  = Config::get('locales');
        // URL内の言語コードがconfig.phpで定義したlocales配列のキーに含まれている場合は設定を行う
        if(array_key_exists($str_language, $ary_locales)) {
            // 言語コード部分を取り除く
            array_shift($this->segments);
            // ルーティング用にURIを再セット
            $this->uri = implode('/', $this->segments);
            // 言語設定を上書き
            Config::set('language', $str_language);
            // 必要に応じて設定されているlocaleの値を上書き
            // Config::set('locale', $ary_locales[$str_language]);
        } else {
            // 切り替え可能言語以外が指定されていた場合の処理
        }
    }
}

ポイントは、array_shift($this->segments) でURIセグメントから言語コードを除外することで、FuelPHPオリジナルのルーティング処理(Controller – Actionメソッドの関係)を変更することなく開発することが可能になります。

ここでの注意点は、URL内に言語コードの指定がない場合や、切り替え可能言語以外の言語コードが指定されていた場合の処理を入れなければアクセスの制限をかけられないことです。
また、言語コードを指定せずにアクセスできるページを作る必要がある場合などの例外的な対応も、detect_language()メソッドで行う必要があります。

3. URIクラスをオートローダにセット

コアクラスと同じ名前を持つクラスはデフォルトでは無視されてしまうため、オートローダに認識させる必要があります。
そのため、「2. URIクラスの拡張クラスを作成」で作成したURI拡張クラスをfuel/app/bootstrap.phpのオートローダにセットします。
これによってURIクラスの処理が置き換わり、表示する言語を切り替える処理が実行されるようになります。

Autoloader::add_classes(array(
    /***** 以下を追加 *****/
    'Uri' => APPPATH.'classes/extend/uri.php',
));

4. 言語ファイルの作成

言語ファイル(辞書ファイル・翻訳ファイルと呼ばれたりもします)とは、テンプレートで使用する各言語の文言を言語別に一括管理するためのファイルのことです。
基本的にテンプレートファイルには直接文言を書かずに言語ファイルで管理することで、言語別にテンプレートを作成する手間を省くことができます。
※ 言語によって大きくHTML構造が異なる場合などテンプレートを分けたほうが効率が良い場合もあります。
fuel/app/lang配下に言語毎のディレクトリ(ディレクトリ名は言語コード)を作成し、その中に言語ファイルを作成します。
コントローラー単位・ページ単位・モジュール単位等、環境に合った分割方法で複数言語ファイルを作成することが可能です。
今回はコントローラー単位で言語ファイルを分割することにします。

例として、Sampleコントローラーで使用する言語ファイルを定義します。
定義方法は配列に任意のキーで定義を書きreturnするだけです(FuelPHPの言語ファイルのデフォルトであるPHPを使用した場合のみ)。
同一ファイル名かつ同一キーで言語毎の言語ファイルに定義する必要があることのみ注意する必要があります。

fuel/app/lang/ja/sample.php
<?php
return array(
    'title' =--> 'アルバイト',
);
fuel/app/lang/en/sample.php
<?php
return array(
    'title' =--> 'part-time job',
);
fuel/app/lang/zh/sample.php
<?php
return array(
    'title' =--> '打工',
);

文言の中に可変値を入れたい場合の定義方法等は下記をご確認ください。
http://fuelphp.jp/docs/1.7/classes/lang.html

5. 言語ファイル読み込み処理をコントローラーに追加

言語ファイルは読み込まなければ使用することが出来ないため、下記のコードを任意の箇所で実行する必要があります。
今回はコントローラー単位で言語ファイルを分割したので、コントローラー(fuel/app/classes/controller/sample.php)のbeforeメソッドで呼び出すことにします。
呼び出した後はコントローラー内だけでなく、モデルクラス内やテンプレートファイル内でも使用することが出来ます。

class Controller_Sample extends Controller
{
    public function before()
    {
        parent::before();
        // 言語ファイルを読み込む
        // Lang::load('言語ファイル名', '変数名');
        Lang::load('sample', 'sample');
    }
}

6. テンプレートファイル作成

「4. 言語ファイルの作成」でも書きましたが、テンプレートの中に直接文言を書かずに言語ファイルから取得するようにします。
テンプレート内で以下のように記述をすることで、言語ファイルの文言を表示することが出来ます。

{* Lang::get('変数名.キー') *}
<h1>{Lang::get('sample.title')}</h1>
 

以上で多言語化対応は終了です。
実際にブラウザでアクセスすると、URL内の言語コードによって表示される文言が切り替わるかと思います。

あとがき

いかがでしたでしょうか。
多言語化対応はシステムの環境を整えること自体はそれほど難しくなく、それよりも翻訳の質の担保であったり、各言語に合ったUI/UXにすることの方が難しいかと思います。
実際、ニホンdeバイト( https://nihondebaito.com )でも多言語化対応の中で翻訳の質の担保が課題となりました。
訪日外国人数が増加している中で運営サービスや新サービスの多言語化を検討することがあるかと思います。
その際に、本記事が参考になれば幸いです。

執筆者プロフィール

シフトワークス事業部に所属する新卒2年目のF氏。
入社前にインターンとして1年間勤務していたというインディバルでは変わった経歴を持つ。
23歳にして腰痛と格闘中。