1
/
5

hi18n (i18nライブラリ) の使い方

hi18nとは

hi18n は現在Wantedlyで開発中の、TypeScript/JavaScript向け翻訳テキスト管理ライブラリ (i18nライブラリの一種) です。

開発の動機や設計思想、詳しい特徴などは別の記事で紹介していますが、大まかには以下の特徴があります。

  • 翻訳IDや翻訳の引数に正しく型がつく。
  • Reactのような宣言的な設計との相性がよい。
  • JavaScriptの既存の開発環境 (Webpackなど) に自然に統合される。
    • たとえば、hi18nのために特別な設定をしなくても、ホットリロードが使えるようになる。
    • 翻訳データの分割ロードが必要な場合も、モジュールバンドラーの機能を使って行う。

本稿では実際に使いたい・試したい人のための情報として、導入手順と使い方を紹介します。

導入とセットアップ

ここではReact + TypeScript環境を想定した手順を紹介します。まず必要なパッケージを入れます。

npm install @hi18n/core @hi18n/react-context @hi18n/react
npm install -D @hi18n/cli

# または:
yarn add @hi18n/core @hi18n/react-context @hi18n/react
yarn add -D @hi18n/cli

次に翻訳ファイルを用意します。1ファイル構成も可能ですが、ここでは複数ファイル構成で紹介します。

// src/locale/index.ts
// (他の名前でもOK)
// 翻訳可能な文字列の一覧をここに定義する。

import { Book, Message } from "@hi18n/core";
import catalogEn from "./en";
import catalogJa from "./ja";

export type Vocabulary = {
// 凡例: "翻訳ID": Message<{ 引数 }>; (引数がないときは単に Message でOK)
"example/greeting": Message<{ name: string }>;
};

// 各言語の翻訳データをまとめたオブジェクト
export const book = new Book<Vocabulary>({
en: catalogEn,
ja: catalogJa,
});
// src/locale/en.ts
// (他の名前でもOK)
// 各言語での翻訳をここに定義する。

import { Catalog, msg } from "@hi18n/core";
import type { Vocabulary } from ".";

export default new Catalog<Vocabulary>({
// 凡例: "翻訳ID": msg(翻訳文字列),
"example/greeting": msg("Hello, {name}!"),
});
// src/locale/ja.ts (enと同様)

import { Catalog, msg } from "@hi18n/core";
import type { Vocabulary } from ".";

export default new Catalog<Vocabulary>({
"example/greeting": msg("こんにちは、{name}さん!"),
});

翻訳IDの同期のためのコマンドを用意します。

// package.json
{
"scripts": {
"i18n:sync": "hi18n sync 'src/**/*.ts' 'src/**/*.tsx'"
}
}

これで基本の導入は完了です。 (ESLintを設定している場合はESLint pluginの設定もおすすめしていますが、導入方法の説明は省略します)

翻訳の呼び出し

定義した翻訳を呼び出すにはいくつかの方法があります。

useI18n を使う方法

Reactを使う場合の基本の方法です。まずルート付近でロケールを設定します。

// 自前でReactDOMを呼んでいる場合の例
import { LocaleProvider } from "@hi18n/react";

const root = ReactDOMClient.createRoot(/* ... */);
root.render(
<LocaleProvider locales="ja">
{/* ... */}
</LocaleProvider>
);

利用側ではuseI18nを呼び出します。

import { useI18n } from "@hi18n/react";
// 最初に定義したBookインスタンス (翻訳データ) を明示的にimportする
import { book } from "../../locale";

const Greet: React.FC = () => {
// 現在のロケールと指定したbookを使った翻訳を開始する
const { t } = useI18n(book);
return <>{t("example/greeting", { name: "太郎" })}</>;
};

getTranslatorを使う方法

Reactに依存しない方法です。ロケール情報は自前で管理する必要があります。

import { getTranslator } from "@hi18n/core";
// 最初に定義したBookインスタンス (翻訳データ) を明示的にimportする
import { book } from "../../locale";

const { t } = getTranslator(book, "en");
console.log(t("example/greeting", { name: "太郎" }));

<Translate> を使う方法

<Translate> はReactの要素を含むコンテンツの翻訳に適した方法です。

import { Translate } from "@hi18n/react";
// 最初に定義したBookインスタンス (翻訳データ) を明示的にimportする
import { book } from "../../locale";

const Greet: React.FC = () => {
return <Translate book={book} id="example/greeting" name="太郎" />;
};

<Translate> では翻訳の引数としてReactの要素を渡すことができます。

const UnreadMessages: React.FC = () => {
const unreadCount = 2;
if (unreadCount === 0) return null;

// en: "You have <link>{count,plural,one{# unread message}other{# unread messages}}</link>"
// ja: "<link>{count,number}通の未読メッセージ</link>があります"
return <Translate book={book} id="example/unread" count={unreadCount}>
<a key="link" href="https://example.com/inbox" />
</Translate>;
};

翻訳IDを同期する

hi18n sync コマンドを使うことで翻訳IDを同期できます。

hi18n sync <globs...> [--exclude <glob>]

たとえば、package.jsonで以下のようにコマンドを定義している場合

// package.json
{
"scripts": {
"i18n:sync": "hi18n sync 'src/**/*.ts' 'src/**/*.tsx'"
}
}

次のようにして翻訳IDの同期を行えます。

npm run i18n:sync

# または:

yarn i18n:sync

この処理はプロジェクト中のTypeScript/JavaScriptソースを書き換えるので注意してください。これにより以下の2つの作業が行われます。

  • 使われていない翻訳の削除 (コメントアウト)
  • 必要だが定義されていない翻訳のためのコードを生成

もし誤って利用箇所を消してしまい、翻訳がコメントアウトしてしまっても戻すのは簡単です。利用箇所を戻して再度同期処理を行えば、コメントの中身から翻訳が復元されます。

翻訳の同期処理は翻訳の型部分 (Message<{ ... }>{ ... } の部分) に関してはノータッチです。ここはプログラマーが直接編集することが想定されています。

--check オプションをつけると、変更が必要なときに変更を適用するのではなくエラーにすることができます。これはGitHub ActionsなどのCIで同期がとれていることを確認するのに便利です。

npm run i18n:sync --check

# または:

yarn i18n:sync --check

翻訳IDを追加する

新しい翻訳が必要なとき、手動でそれらを追加してもいいですが、ある程度ツールに任せることもできます。

コードを書くときに、 t のかわりに t.todo を使い、 Translate のかわりに Translate.Todo を使うようにします。

t.todo("example/new");
<Translate.Todo book={book} id="example/new" />;

その後同期を行い、翻訳の中身を追加したら "TODO" を削除します。

その他できること

  • 複数形による分岐と数値フォーマット。 Intl が使える環境である必要があります。
  • 複数の翻訳IDから動的に内容を選択するには `t.dynamic` または `Translate.Dynamic` を `translationId` と組み合わせて使います。
    • Linguiからの移行支援用のルールなども含まれています。
  • ページやコンポーネント単位で翻訳データを分割するには、単に複数の独立したBookインスタンスを作ればOKです。あとはモジュールバンドラーがうまくやってくれるはずです。

次に読む

hi18n (i18nライブラリ) の使い方: メッセージフォーマット編 | Wantedly Engineer Blog
hi18n は現在Wantedlyで開発中の、 TypeScript/JavaScript向け翻訳テキスト管理ライブラリ ( i18nライブラリの一種) です。 基本の使い方は以下の記事で説明しています。 本稿では発展的な使い方として、メッセージフォーマットの構文を紹介します。 詳細な説明に入る前に、メッセージフォーマットでできることを大まかに紹介します。 波括弧で引数名を囲むことで、動的に生成された文字列を間に挟むことができます。引数名のかわりに番号を入れることもできます。 文字列のかわりに数値や日付時
https://www.wantedly.com/companies/wantedly/post_articles/407312
Wantedly, Inc.'s job postings
10 Likes
10 Likes

Weekly ranking

Show other rankings
If this story triggered your interest, go ahead and visit them to learn more