【canopy-i18n】型安全に使えるi18nライブラリ【TypeScript】
こんにちは、フリーランスエンジニアの太田雅昭です。
Node.js/TypeScriptでのi18n、型ズレや初期設定の重さに悩まされがちです。この記事では、最小限のAPIで型安全に使える「canopy-i18n」を紹介します。
i18nライブラリへの不満点
現在数々のi18nライブラリがあります。用途によって使いやすさは変わってくるかと思いますが、最近のモノレポ, TypeScriptなどでのコーディングスタイルに合っているものがないように思います。具体的には以下のようなことがあります。
- 型安全性が弱い: 翻訳キーのタイポや引数不足が実行時まで発覚しない。
- 初期設定が煩雑: 設定ファイルやプラグインが多く、導入に腰が重い。
- テンプレート制約: 独自タグや制限付き構文で表現力が足りない。
- 深いデータ適用が面倒: ネストしたオブジェクト/配列にロケールを一括適用しづらい。
- フォールバック粒度が粗い: 画面単位でしかフォールバックできず、細かい制御が難しい。
canopy-i18nで解決できること
canopy-i18nは、下記のような特徴があります。
- 型安全なロケール/キー: 許可ロケールを固定し、コンパイル時に検出。
- メッセージ単位のフォールバック: 欠落時はフォールバックロケールへ退避。
- 自由なテンプレート: ただの関数(または文字列)なので、条件分岐や外部フォーマッタもそのまま使える。
- 深い適用を一発: 定義ツリー全体にロケール適用。
- 軽量・シンプル: 小さなAPI面と実装で、学習・保守コストが低い。
使い方(最小セット)
インストール
npm i canopy-i18n
# or
pnpm add canopy-i18n
メッセージ定義と利用
import { createMessageBuilder, applyLocaleDeep } from 'canopy-i18n';
// 1) ロケール宣言(フォールバック: ja)
const builder = createMessageBuilder(['ja', 'en'] as const, 'ja');
// 2) メッセージ定義(文字列 or 関数テンプレート)
const title = builder({
ja: 'タイトルテスト',
en: 'Title Test',
});
const greet = builder<{ name: string; age: number }>({
ja: c => `こんにちは、${c.name}さん。あなたは${c.age}歳です。`,
en: c => `Hello, ${c.name}. You are ${c.age} years old.`,
});
// 3) メッセージツリーを組み立て
const messages = {
title,
nested: { hello: builder({ ja: 'こんにちは', en: 'Hello' }) },
greet,
};
// 4) ツリー全体にロケール適用して利用
const m = applyLocaleDeep(messages, 'en');
console.log(m.title.render()); // "Title Test"
console.log(m.nested.hello.render()); // "Hello"
console.log(m.greet.render({ name: 'Tanaka', age: 20 }));
Next.jsでの使用例
Next.jsでのサンプルを試すこともできます。
git clone https://github.com/mohhh-ok/canopy-i18n
cd canopy-i18n/examples/next-app
pnpm install
pnpm dev