Visual Studio Code 拡張機能のメッセージを言語設定に合わせてローカライズしてみる

Visual Studio Code 拡張機能のメッセージを言語設定に合わせてローカライズしてみる

昨年の Visual Studio Code Advent Calendar 2016VS Code 拡張機能のコマンドタイトル名をローカライズしてみる という記事を書いてみました。
この時は、vscode-nls モジュールの存在などは知っていたものの、どのように利用するかまでは理解できていなかった...

拡張機能のサンプル

VS Code の API を利用して拡張機能を作成するにあたり参考になるのが Microsoft/vscode-extension-samples で公開されるサンプルです。

この中に、拡張機能で利用するメッセージをローカライズするためのサンプル i18n-sample が含まれており、このサンプルの手法を利用することで、拡張機能のメッセージをローカライズすることができます。
これで、やっと理解できた。

vscode-extension-samples の clone

サンプルから、いくつかのファイルを拝借するので、まずは、Microsoft/vscode-extension-samples リポジトリを git clone し、手元に置いておきます:

$ git clone https://github.com/Microsoft/vscode-extension-samples.git

hello world を利用してメッセージをローカライズしてみる

今回も、下記で公開されている入門用 “Hello World” 拡張機能を利用してみたいと思います。

yo code を実行し、拡張機能の雛形を作成します。
ここでは、拡張機能の名前に hello-world とつけてみます。

この拡張機能は、Hello World コマンドを追加し、このコマンドを実行すると上部に "Hello World!" と表示して終了するだけの拡張機能です。

今回は、Hello Wolrd コマンド及びその実行結果として表示される "Hello World!" を、ローカライズしてみるお話です。

Hello World コマンド:

alt

Hello World コマンドの実行結果:

alt

ローカライズに必要な要件

ローカライズを提供するためには、下記のような要件が必要になります。ここでは、日本語へのローカライズを前提としています。

  1. 言語を構成する (Configure Language) で、”locale”:”ja” を設定し、VS Code の表示言語を日本語に設定します
  2. ローカライズに利用される全てのファイルは i18n フォルダの下に配置します
  3. このフォルダ自体は、手作業で作成します。(vscode-nls-dev モジュールを使って Transifix プロジェクトから、ローカライズされたファイルを pull することもできるように作られているようですが、プロジェクトなどは一切関係ないため今回は利用しません)
  4. i18n フォルダの下には、ローカライズする言語を表すサブフォルダを配置します。フォルダ名は、ISO 639-3 の規約に従います。例えば、日本語の場合は、jpn となります
  5. 言語名フォルダの下に、拡張機能のソースコードの配置と同じディレクトリ構造 (例えば、out/) に倣って <ファイル名>.i18n.json ファイルを作成します。この json ファイルの中身は、ローカライズ対象となるテキストの ”key”:”value” のペアとなり、このファイルから該当する key のペアとなる value すなわち、ローカライズされたテキストが表示に利用されます
  6. 拡張機能のトップレベルに package.nls.json ファイルがある場合は、package.i18n.json というファイル名を持つ、各言語用のパッケージを用意する必要があります。(日本語メッセージを作成する場合: i18n/jpn/package.i18n.json) package.nls.json については、Visual Studio Code Advent Calendar 2016 5日目 - VS Code 拡張機能のコマンドタイトル名をローカライズしてみる を参照してください

この条件に従い、メッセージをローカライズして行きます。

npm package のインストール

yo code で拡張機能の雛形を作成し、すぐに実行することはできますが、それだけではメッセージをローカライズすることができません。

まずは、ローカライズに必要な下記の npm パッケージを “Hello World” 拡張機能のローカルリポジトリにインストールします。

$ cd hello-world
$ npm -SD install gulp gulp-typescript del run-sequence vsce vscode-nls-dev 
  • vsce は、拡張機能をパッケージにしたり Marketplace へ公開するためなどに利用されます。
  • vscode-nls-dev は、Javascript や Typescript から外部にある文字列を抽出することを目的としたモジュールです。

続いて、配布に必要な下記の npm パケージを “Hello World” 拡張機能のローカルリポジトリにインストールします。

$ npm -S install vscode-nls
  • vscode-nls は、ローカライズとそれに利用するリソースを外部化するための npm モジュールです。主に VS Code 拡張機能のローカライズに利用されます。

hello-world/package.json の devDependenciesdependencies は下記のようになります。

  "devDependencies": {
    "@types/mocha": "^2.2.42",
    "@types/node": "^7.0.43",
    "del": "^3.0.0",
    "gulp": "^3.9.1",
    "gulp-typescript": "^3.2.3",
    "run-sequence": "^2.2.0",
    "typescript": "^2.6.1",
    "vsce": "^1.35.0",
    "vscode": "^1.1.6",
    "vscode-nls-dev": "^2.1.6"
  },
  "dependencies": {
    "vscode-nls": "^2.0.2"
  }

ファイルの配置

続いてローカライズを行うために必要なファイルを配置します。
ローカライズに必要な要件にもありましたが、ローカライズされたメッセージを格納するファイルを作成し配置する必要があります。

  • hello-world/i18/jpn/out ディレクトリを作成します。このディレクトリに、コード内のメッセージをローカライズするために必要な extension.i18n.json ファイルを配置します(作成は後ほど)。また、拡張機能パッケージのメッセージ(コマンド名や設定情報など)をローカライズに必要な package.i18n.json ファイル(こちらは、i18/jpn/package.i18n.json として)を配置します(作成は後ほど)
  • i18n-sample/gulpfile.js を hello-world ディレクトリのトップレベルに配置します

最終的なファイルの配置は下記のようになります:

hello-world
   |—i18n/
   |——jpn/
   |———package.i18n.json (作成は後ほど)
   |———out/
   |————extension.i18n.json (作成は後ほど)
   |-gulpfile.js

試しに、gulp task を実行して見ます。
メッセージとしては、特に何もなく、各タスクが実行されている事だけがわかります。

$ gulp
[18:32:39] Using gulpfile ~/hello-world/gulpfile.js
[18:32:39] Starting 'default'...
[18:32:39] Starting 'build'...
[18:32:39] Starting 'clean'...
[18:32:39] Finished 'clean' after 14 ms
[18:32:39] Starting 'internal-nls-compile'...
[18:32:40] Finished 'internal-nls-compile' after 932 ms
[18:32:40] Starting 'add-i18n'...
[18:32:40] Finished 'add-i18n' after 4.89 ms
[18:32:40] Finished 'build' after 953 ms
[18:32:40] Finished 'default' after 954 ms

この時、hello-world/out ディレクトリに変化があります。
明らかにローカライズに関連しそうなファイルができています。

hello-world
   |—out
   |——test/
   |——extension.js
   |——extension.js.map
   |——extension.nls.ja.json  // このファイルが作成される
   |——extension.nls.json     // このファイルが作成される

中身は、それぞれ下記のようになりますが、ローカライズに必要なファイルが作成されていないため、空に等しい内容となっています。

extension.nls.ja.json:

[]

extension.nls.json:

{
    "messages": [],
    "keys": []
}

でも、なんとなくローカライズの仕組みが見えてきました。

ローカライズ手順

準備が整ったので、'Hello World!' 拡張機能をローカライズして見ます。
まずは、extension.ts を編集して行きます。extension.ts には、コマンドを実行すると表示されるメッセージが実装されており、その部分を変更して行きます。

extension.ts に下記を追加

vscode-nls モジュールを import し、nls.config() を localize として割り当てる記述を extension.ts に追加します。

import * as nls from 'vscode-nls';
const localize = nls.config(process.env.VSCODE_NLS_CONFIG)();

コメントを除いたコードは、下記のようになります。

'use strict';
import * as vscode from 'vscode';

import * as nls from 'vscode-nls'; // 追加された行
const localize = nls.config(process.env.VSCODE_NLS_CONFIG)(); // 追加された行

export function activate(context: vscode.ExtensionContext) {
    console.log('Congratulations, your extension "hello-world" is now active!');
    let disposable = vscode.commands.registerCommand('extension.sayHello', () => {
        vscode.window.showInformationMessage('Hello World!');
    });
    context.subscriptions.push(disposable);
}

export function deactivate() {
}

"Hello World" 拡張機能で表示されるメッセージ 'Hello World!' を出力する処理は下記のように実装されています。

        vscode.window.showInformationMessage('Hello World!');

この 'Hello World!' の部分をローカライズします。
ソースコード上のこの部分を、'こんにちは世界!' に書き換えたら終了です。いや、それだとダメですね。

ローカライズしたいメッセージを localize() で囲う

まずは、ローカライズしたいメッセージを localize() で囲います。localize() は、keymessage の 2 つの引数が必要になります。
key には、message を判別するための固有の文字列を。message には、デフォルトで表示する文字列を割り当てます。

下記のように変更します:

        vscode.window.showInformationMessage(localize('helloWorld.text', 'Hello World!'));

ここでは、helloWorld.text という key が設定されているならば、その値を表示し、設定されていなければ、'Hello World!' を表示するという意味になります。
書いただけでは何も起きません。

試しに実行して見ましょう。ここで一つ気をつけなければいけない事は、ローカライズを反映するには、通常の F5 キーを押したデバッグのトランスパイルではなく、都度、変更を反映させるために、必ず gulp build を実行しなければならない点です。(本項の説明が、i18n-sample/gulpfile.js の利用を想定しているため、このような手順になっています)

統合ターミナルで gulp build を実行後、out/ ディレクトリ配下が再生成されます。その後、F5 キーでデバッグ実行する事が可能です。

また、gulp build 実行時に、extension.js が書き換えられます。この時に、gulp から vscode-nls-dev モジュールが呼び出されます。

localize() で囲んだ部分が下記のように変化している事がわかります:

out/extension.js:

        vscode.window.showInformationMessage(localize(0, null));

どのようにローカライズを行なっているか?

  1. ソースコード中の vscode-nls モジュールを利用した記述を対象に、vscode-nls-dev モジュールを使って書き換えを行います。書き換えは、トランスパイルで生成された JavaScript ファイルを、gulp を使って、後から書き換えます

  2. 例えば、下記のように記述された呼び出しは、

    localize(helloWorld.text, Hello World!)
    

    このように書き換えられます:

    localize(0null)
    

    最初のパラメータ (この例では 0) は、メッセージファイル内の key をあらわし、key に設定された value に置き換えられます。localize() が複数存在する場合は、最初のパラメータとなる数字が増えて行きます。

  3. i18n フォルダに配置されたファイルの内容は、”key”:”value” のペアからなる配列に変換され利用されます

ローカライズファイルの配置

作成は後ほどと説明していた 2 つのファイルを作成します。

hello-world
   |—i18n/
   |——jpn/
   |———package.i18n.json (作成は後ほど)
   |———out/
   |————extension.i18n.json (作成は後ほど)
   |-gulpfile.js

まずは、下記の内容で hello-world/i18n/jpn/out/extension.i18n.json を作成します。

{
    "helloWorld.text": "こんにちは世界!"
}

こレは、言語設定が、ja の場合、jpn ディレクトリ配下の extension.i18n.json ファイルに設定されたhelloWorld.text の値となる、こんにちは世界! を表示することを想定しています。

実行してみる

ここまでくれば、もう少し。
あとは、ターミナルから gulp build を実行します。

$ gulp build

そして、F5 キーでデバッグを実行し、コマンドパレットから Hello World コマンドを実行します:

alt

表示できました。

gulp build で生成される、out/extension.nls.ja.jsonout/extension.nls.json ファイルの中身を見てみると下記のようになっています:

extension.nls.ja.json:

[
    "こんにちは世界!"
]

extension.nls.json:

{
    "messages": [
        "Hello World!"
    ],
    "keys": [
        "helloWorld.text"
    ]
}

ちなみに、英語ロケールで実行すると、下記のように英語のメッセージが表示されます。

alt

このように、ソースコード中のメッセージをローカライズして行きます。

ローカライズしたいメッセージを localize() で囲い、ローカライズしたいメッセージを判別するための固有の文字列を割り当てた key を定義し、その値に表示する文字列を割り当てます。

コマンドのローカライズ

続いて、コマンドのローカライズを行います。

コマンドおよびコマンド名の定義は、package.json 内の "contributes" で定義される、"commands" で行われます。

'Hello World' 拡張機能は、下記のようになっています。これは、コマンド名として "Hello World" を割り当て、これが選択された場合に実行されるコマンドは extension.sayHello である事がわかります。

  "contributes": {
    "commands": [
      {
        "command": "extension.sayHello",
        "title": "Hello World"
      }
    ]
  },

ここでも、テキストを直接書き換えれば対応完了ですね。

基本的な対応については、VS Code 拡張機能のコマンドタイトル名をローカライズしてみるの手順と大きく変わりませんので、パクって行きます。

まず、package.json の変更:

まずは、コマンドの title%extension.sayHello.title% のように % で囲まれた文字列に置き換えます。これがキーになります

  "contributes": {
    "commands": [
      {
        "command": "extension.sayHello",
        "title": "%extension.sayHello.title%"
      }
    ]
  },

続いて packaje.nls.json の作成

package.json で設定したローカライズ用のキーにマッピングするための値を、下記のように追加します。
このファイルを用意することで、VS Code の言語が英語(en)に設定されているか、英語以外のローカライズ情報が用意されていない場合にこのファイルに記載されている情報が利用されることになります

{
  "extension.sayHello.title": "Hello World"
}

最後にローカライズ用の値が格納された i18n/jpn/packahe.i18n.json を作成

ここだけが、前回の記事と異なる部分です。
ローカライズされたメッセージを格納するファイルは、packahe.i18n.json として、i18n/jpn に配置し、言語名を含んだ package.nls.ja.json ファイルを作成する必要はありません。

gulp build の実行により、packahe.i18n.json ファイルから package.nls.ja.json ファイルを自動生成してくれます。

下記の例では、"extension.sayHello.title": に、"こんにちは世界!ってやってみよー" を指定することで、package.json に定義した "%extension.sayHello.title%" が置きかわるという仕組みです。

{
  "extension.sayHello.title": "こんにちは世界!ってやってみよー"
}

gulp build を実行

最後に gulp build を実行し、F5 キー でデバッグを実行してみましょう。
下記のように、コマンドが "こんにちは世界!ってやってみよー" となります。

alt

また、packaje.nls.json でデフォルトとなるコマンド名を定義しているため、ローカライズされたコマンドの下に、"Hello World" とも表示されています。
そのため、コマンドパレットでは、日本語化されたコマンド名と英語のコマンド名のどちらでも検索して実行する事が可能です。

パッケージ化してみる

せっかくなので、作成した 'Hello World' 拡張機能を VS Code へインストールできるように、拡張機能パッケージ化してみましょう。

パッケージ化も、ローカライズのために利用している gulpfile.js を使って行う事が可能です。下記のように実行してください:

$ gulp package

拡張機能をシングルファイルにパッケージ化した hello-world-0.0.1.vsix ファイルが生成されます。

拡張機能: VSIX からのインストール コマンドを実行し、hello-world-0.0.1.vsix を読み込むことで VS Code へ拡張機能をインストールする事ができます。

ちなみに、README.md が、ちゃんと編集されているかをチェックしており、そのままだとパッケージ化がエラーとなります。急いでいる場合は、冒頭にある下記の一文を削除すれば回避できます。

This is the README for your extension "hello-world". After writing up a brief description, we recommend including the following sections.
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.