Drupal の翻訳システムをJavaScript にも適用してみた

  •  
 
トビウオ2024年3月29日 - 13:38 に投稿

概要

Drupal は多言語対応 CMS の一つです。つまり、URL・ユーザーアカウント・ドメインなど、様々な条件をトリガーに、Web ページ単位もしくはフィールド単位で翻訳することができます。公式サイトでも説明されているように、必要なモジュールをインストールし (有効にし)、言語設定から対応させたい言語を有効にすれば、その言語向けのコンテンツを作成できるようになります。


しかしそれだけだと、PHP などで動的に生成されたページに対応できません。そのため、動的生成に関連した一部の言語向けに、Drupalの翻訳システムを利用するための関数が提供されています。

Translation API overview | Translation API (Code text) | Drupal Wiki guide on Drupal.org

この記事によると、PHP 言語の場合、Drupal に組み込みの t() 関数を使用することで翻訳を行えます。また、 JavaScript においても同様に、Drupal.t() メソッドが利用可能です。ただ、このDrupal.t() メソッドが曲者だったので、使い方のコツについてまとめました。

「変換」というより「置換」

そもそも、Drupal.t()はどのように「翻訳」しているのでしょうか。

「DurpalサーバーにJavaScriptがHTTPリクエストを送っている?」

否。それだとawaitなどで待たないと描画されませんし、そんな記述をせずとも動作します。つまり、Drupal で当該 JavaScript を含むWebページを表示する際は、クライアントサイドで翻訳作業を行っているわけです。

「あれ、特別なコードを書いた覚えはないんだけど?」

ごもっとも。先ほども書いたように、ドキュメントを読んだ限り、次のような単純なコードで動作します。

Drupal.t('Hello, world!');

しかし、次のようなコードは思ったように動作しません。

const a = 'Hello, world!';
Drupal.t(a);

しかししかし、次のようなコードだと正常に動作します。1行目のような、「一見すると無駄なコード片」の有無により、同じコード (Drupal.t(a);) の可否が決まってしまうのです。

Drupal.t('Hello, world!');  // !?
const a = 'Hello, world!';
Drupal.t(a);

この奇妙な挙動の正体は、先程挙げた公式ドキュメントをじっくり読むと見えてきます。

There are exceptions where you could call t($variable):
- If all possible values of the $variable have previously been passed into t() as literal strings.
- If the value of the $variable is coming from a recognized source of translatable text (see sections below), such as a YML or Twig file.

つまり、「DrupalシステムがJavaScriptやYAMLやTwigを読み込んで、翻訳できる文字列の対応表を作成し、そこに当てはまったもののみ翻訳される」のです。言い換えると、この問題をJavaScriptだけで解決しようとした場合、先ほど挙げた例のように、 「『Drupal.t('Hello, world!');』のような行を大量に並べたJavaScriptを用意する」 必要があります。

具体的なコーディング例

まず、「翻訳したい対象のワード」をただ羅列したファイルを用意します (以下「翻訳サンプル」と呼ぶ)。ビルド対象として仕込みたいので、例えばtranslation.jstranslation.tsといったファイル名で保存します。

Drupal.t("Account");
Drupal.t("Document");
Drupal.t("User");
Drupal.t("User Data");

次に、先ほどのファイルをどこかでimportしつつ、Drupal.t()を用いて普通に開発します。翻訳サンプルのご利益により、普通のJavaScriptのように、文字結合でもなんでも使って大丈夫になります。

const func = (text, args) => {
    return Drupal.t(text, args);  // OK
}

console.log(Drupal.t('Document'));        // OK
console.log(Drupal.t('User' + ' Data'));  // OK

ここで気になるのは、TypeScriptで開発する際のことです。Drupalの型定義がないと、TypeScriptのコンパイラがそれを検知し、コンパイルエラーになってしまいます。そのため、Drupalの型定義を外部から追加する必要があります。手動でDrupal.d.tsを書いてもいいですが、ありがたいことに、自作して公開してくださっている方もいらっしゃいました。

まとめ

Drupal の翻訳システムを JavaScript から利用する際は、少々トリッキーな記述が必要になると分かりました。クセさえ理解すれば問題なく動作するのですが……。

おまけ: Drupal.t() の定義を読んでみよう

具体的には、/core/misc/drupal.jsのことを指します。

  Drupal.t = function (str, args, options) {
    options = options || {};
    options.context = options.context || '';

    // Fetch the localized version of the string.
    if (
      typeof drupalTranslations !== 'undefined' &&
      drupalTranslations.strings &&
      drupalTranslations.strings[options.context] &&
      drupalTranslations.strings[options.context][str]
    ) {
      str = drupalTranslations.strings[options.context][str];
    }

    if (args) {
      str = Drupal.formatString(str, args);
    }
    return str;
  };

ここで「str = drupalTranslations.strings[options.context][str];」という記述をよく見てください。前述の if 文から、「drupalTranslationsが定義されている」「翻訳したい文字列 (str) に対応する翻訳データ (drupalTranslations.strings[options.context][str]) が存在する」条件を満たさないと、翻訳されないのだということが分かります。「// Fetch the localized version of the string.」などとコメントに書かれていますが、別に Fetch API なんて使っていないのです。

ちなみに、キーワードに対応した置換処理 (「Hello, @username .」の「@username」を置換する場合など) については、同ファイル内の Drupal.formatString() の実装に詳しく書かれています。

コメントを追加

プレーンテキスト

  • HTMLタグは利用できません。
  • 行と段落は自動的に折り返されます。
  • ウェブページのアドレスとメールアドレスは自動的にリンクに変換されます。
CAPTCHA
この質問はあなたが人間の訪問者であるかどうかをテストし、自動化されたスパム送信を防ぐためのものです。