hebrew-i18n について
ヘブライ語対応を追加するウェブ開発者向けの国際化スキルです。RTLレイアウト、ロケール別の書式、複数形規則、双方向テキストを扱い、React、Vue、Next.js、Angularなどの例を紹介します。
主な機能
- 01ルート要素の言語・方向設定
- 02ヘブライ語の複数形規則への対応
- 03日付、数値、通貨の書式設定
- 04RTL CSSと双方向テキストの指針
- 05フレームワーク統合例
活用シーン
- 01ウェブアプリにヘブライ語対応を追加
- 02RTLレイアウトや混在テキストの表示問題を解決
- 03ヘブライ語の複数形とロケール書式を実装
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ skills-il/ localization --skill hebrew-i18nインストール後、Claude Code で/hebrew-i18nを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
evidence.jsonmetadata.jsonoptimization-log.jsonSKILL_HE.mdreferences/bidi.mdpluralization.mdscripts/generate_i18n.py詳細
ヘブライ語の国際化
手順
ステップ 1:国際化フレームワークを設定する
方向はラッパーのdivではなく、ルートの<html>要素に設定します。実行時に言語を切り替えるシングルページアプリでは、ロケールが変わるたびに更新してください:
function applyLocale(locale) {
document.documentElement.lang = locale;
document.documentElement.dir = locale === 'he' ? 'rtl' : 'ltr';
}
React(react-intl):
import { useEffect } from 'react';
import { IntlProvider } from 'react-intl';
import heMessages from './locales/he.json';
function App() {
useEffect(() => applyLocale('he'), []);
return (
<IntlProvider locale="he" messages={heMessages}>
{/* App content */}
</IntlProvider>
);
}
React(react-i18next): i18next はデフォルトで ICU の複数形構文を使用しません。Intl.PluralRulesからキーのサフィックスを選ぶため、ヘブライ語では_one、_two、_otherのキーが必要です:
{
"days_one": "יום אחד",
"days_two": "יומיים",
"days_other": "{{count}} ימים"
}
t('days', { count })として呼び出します。i18next v24 以降、Intl.PluralRulesがない場合のフォールバックはありません。そのため、英語形式の_one/_otherだけが解決され、_twoの形式は表示されないままになります。i18next のドキュメントには、React Native の Hermes エンジンにはIntl.PluralRulesがないと記載されています。対象環境でtypeof Intl.PluralRulesを確認し、未定義の場合はintl-pluralrulesなどのポリフィルを追加してください。
Vue(vue-i18n): vue-i18n の複数形は ICU 形式ではなく、パイプで区切ります。現行リリース系列の vue-i18n 11.x は、Intl.PluralRulesではなく位置によってケースを選択します。3つのケースはzero | one | otherとして読み込まれるため、ルールがない場合、t('days', 1)は2番目のケースを表示します。ヘブライ語では、1、2、およびそれ以外のすべての数を3つのケースに割り当てるルールを登録し、t('days', count)として呼び出してください:
import { createI18n } from 'vue-i18n';
// cases: one | two | other
const hePlural = (choice) => (choice === 1 ? 0 : choice === 2 ? 1 : 2);
const i18n = createI18n({
legacy: false, // 11.x defaults to legacy mode, which ignores pluralRules
locale: 'he',
fallbackLocale: 'en',
pluralRules: { he: hePlural }, // in legacy mode the option is pluralizationRules
messages: { he: heMessages, en: enMessages }, // he: { days: 'יום אחד | יומיים | {count} ימים' }
});
// t('days', 2) -> "יומיים"
Intl.PluralRulesによる自動選択は vue-i18n の main ブランチでの破壊的変更であり、11.x リリースには含まれていません。
Next.js App Router(next-intl):
// src/i18n/routing.ts
import { defineRouting } from 'next-intl/routing';
export const routing = defineRouting({
locales: ['he', 'en'],
defaultLocale: 'he',
});
// src/proxy.ts (named middleware.ts before Next.js 16)
import createMiddleware from 'next-intl/middleware';
import { routing } from './i18n/routing';
export default createMiddleware(routing);
export const config = {
matcher: '/((?!api|trpc|_next|_vercel|.*\\..*).*)'
};
// src/i18n/request.ts (supplies locale and messages to getTranslations / useTranslations)
import { hasLocale } from 'next-intl';
import { getRequestConfig } from 'next-intl/server';
import { routing } from './routing';
import { notFound } from 'next/navigation';
import * as rootParams from 'next/root-params';
export default getRequestConfig(async ({ locale }) => {
if (!locale) {
const paramValue = await rootParams.locale();
if (hasLocale(routing.locales, paramValue)) {
locale = paramValue;
} else {
notFound();
}
}
return {
locale,
messages: (await import(`../../messages/${locale}.json`)).default
};
});
next/root-paramsは Next.js 16.3 からデフォルトで利用できます。それ以前の Next.js では、next.config.tsのexperimental.rootParamsで有効にする必要があります。next.config.tsでnext-intl/pluginのcreateNextIntlPluginラッパーを使い、request.tsを組み込んでください。
// src/app/[locale]/layout.tsx
import { NextIntlClientProvider } from 'next-intl';
import { getLocale } from 'next-intl/server';
export default async function LocaleLayout({ children }) {
const locale = await getLocale();
return (
<html lang={locale} dir={locale === 'he' ? 'rtl' : 'ltr'}>
<body>
<NextIntlClientProvider>{children}</NextIntlClientProvider>
</body>
</html>
);
}
サーバーコンポーネントからレンダリングする場合、NextIntlClientProviderはrequest.tsからロケールとメッセージを引き継ぐため、getMessages()を呼び出して渡す必要はありません。
Angular:
// angular.json -- add Hebrew locale
"i18n": {
"sourceLocale": "en",
"locales": {
"he": "src/locale/messages.he.xlf"
}
}
また、ビルドオプションで"localize": true(またはロケール ID の配列)を設定してください。設定しないと、CLI はヘブライ語ビルドを生成しません。
ステップ 2:ヘブライ語の複数形
ヘブライ語には、国際化フレームワークで処理する必要がある複数形のカテゴリーが3つあります:
| カテゴリー | ヘブライ語の用語 | 数 | 例 |
|---|---|---|---|
| one(単数) | יחיד | 1、および1未満の小数 | פריט אחד(アイテム1個) |
| two(双数) | זוגי | 2 | שני פריטים(アイテム2個) -- 特殊な双数形を使用 |
| other(複数) | רבים | 0、3以上、1.0以上の小数 | 5 פריטים(アイテム5個) |
注:20や100などの切りのよい数に使われていた古いmanyカテゴリーは、Unicode CLDR のバージョン42(2022年)で削除されました。現代ヘブライ語の複数形ルールではone、two、otherのみを使用し、切りのよい数はotherに分類されます。manyの分岐は追加しないでください。現在の ICU/CLDR ランタイムでは、どれも実行されないコードになります。また、Intl.PluralRules('he').select(0.5)はoneを返すため、小数には独自の表現(「חצי שעה」など)を使い、個数メッセージには渡さないでください。
ルールと例外の詳細については、references/pluralization.mdを参照してください。
ICU MessageFormat パターン(react-intl、next-intl):
{count, plural,
one {פריט אחד}
two {שני פריטים}
other {{count} פריטים}
}
ヘブライ語の一般的な複数形パターン:
| 単数形(יחיד) | 双数形(זוגי) | 複数形(רבים) | パターン |
|---|---|---|---|
| יום(日) | יומיים(2日) | ימים(日) | 不規則な双数形 |
| שעה(時間) | שעתיים(2時間) | שעות(時間) | 女性形の双数接尾辞 -תיים |
| חודש(月) | חודשיים(2か月) | חודשים(月) | 男性形の双数接尾辞 -יים |
| שבוע(週) | שבועיים(2週間) | שבועות(週) | 男性形の双数接尾辞 -יים |
| שנה(年) | שנתיים(2年) | שנים(年) | 不規則な双数形 |
ステップ 3:日付と時刻の書式設定
イスラエルの日付形式: 月より先に日を記載し、MM/DD/YYYY 形式は使用しません。Intl.DateTimeFormat('he-IL') は短い日付をドット区切り(DD.MM.YYYY)で表示することに注意してください。例:04.03.2026。スラッシュ区切りが必要な場合は、各要素を手動で書式設定してください。
// Using Intl.DateTimeFormat
const formatter = new Intl.DateTimeFormat('he-IL', {
year: 'numeric',
month: 'long',
day: 'numeric',
});
// Output: "4 במרץ 2026"
// Short format
const shortFormatter = new Intl.DateTimeFormat('he-IL', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
});
// Output: "04.03.2026" (he-IL uses dot separators, DD.MM.YYYY, not slashes)
// Hebrew calendar, no library needed
new Intl.DateTimeFormat('he-IL-u-ca-hebrew', { dateStyle: 'long' }).format(new Date(2026, 2, 4));
// Output: "ט״ו באדר תשפ״ו"
ヘブライ語の曜日と月の名前:
| 曜日 | ヘブライ語 | 略称 |
|---|---|---|
| 日曜日 | יום ראשון | א׳ |
| 月曜日 | יום שני | ב׳ |
| 火曜日 | יום שלישי | ג׳ |
| 水曜日 | יום רביעי | ד׳ |
| 木曜日 | יום חמישי | ה׳ |
| 金曜日 | יום שישי | ו׳ |
| 土曜日 | שבת | ש׳ |
イスラエルのビジネス週は日曜日から木曜日までです(月曜日から金曜日ではありません)。
ヘブライ暦の日付: 表示するだけであれば、上記の -u-ca-hebrew ロケール拡張で十分です。祝日一覧、週のパラシャー、またはヘブライ暦の日付計算が必要な場合は、hebcal などのライブラリを使用してください。
ステップ 4:数値と通貨の書式設定
// Israeli number format: 1,000.50 (comma for thousands, dot for decimal)
const numFormatter = new Intl.NumberFormat('he-IL');
numFormatter.format(1234567.89); // "1,234,567.89"
// Israeli Shekel currency
const currFormatter = new Intl.NumberFormat('he-IL', {
style: 'currency',
currency: 'ILS',
});
currFormatter.format(1234.50); // looks like "1,234.50 ₪"
通貨文字列は単純な ASCII ではありません。数字の前と ₪ 記号の前に、不可視の RIGHT-TO-LEFT MARK 文字(U+200F)が含まれ、通常のスペースの代わりに NO-BREAK SPACE(U+00A0)が使われています。スナップショットテスト、=== による比較、CSV エクスポート、またこの文字列に対する parseFloat は失敗します。テキストを比較する場合に限り、s.replace(/[\u200E\u200F]/g, '') でマークを取り除いてください。書式設定済みの出力を数値に戻すために解析してはいけません。マークを取り除いた後でも、parseFloat('1,234.50 ₪') は桁区切りのカンマで処理を止めるため、1 を返します。元の数値を保持するか、formatToParts() で各要素を取得してください。
イスラエル固有の数値形式:
| 種類 | 形式 | 例 |
|---|---|---|
| 電話番号(携帯) | 05X-XXXXXXX | 054-1234567 |
| 電話番号(固定電話) | 0X-XXXXXXX | 02-6234567 |
| テウダット・ゼフート(ID) | XXXXXXXXX | 123456782(チェックディジットを含む9桁) |
| 郵便番号 | XXXXXXX | 6100000(7桁) |
| 通貨 | X,XXX.XX ₪ | 1,234.50 ₪(Intl の出力には不可視の方向マークが追加されます) |
ステップ 5:論理プロパティを使用した RTL CSS
国際化に対応したレイアウトでは、常に CSS の論理プロパティを使用してください。
/* Base RTL setup */
html[lang="he"] {
direction: rtl;
}
/* Logical properties -- work in both LTR and RTL */
.card {
margin-inline-start: 1rem; /* right margin in RTL */
padding-inline-end: 0.5rem; /* left padding in RTL */
border-inline-start: 3px solid blue; /* right border in RTL */
text-align: start; /* right in RTL, left in LTR */
}
/* Flexbox automatically reverses in RTL */
.nav {
display: flex;
gap: 1rem;
/* No direction override needed -- flex respects dir attribute */
}
Tailwind CSS の RTL(v3.3 以降、v4 を含む):
Tailwind には論理プロパティのユーティリティと RTL バリアントが用意されています。
<div class="ms-4 me-2 ps-3 pe-1 text-start">
</div>
<div class="ltr:ml-4 rtl:mr-4 ltr:text-left rtl:text-right">
</div>
| 物理プロパティ(避ける) | 論理プロパティ(推奨) | RTL での動作 |
|---|---|---|
ml-4 | ms-4 | RTL では右マージン |
mr-4 | me-4 | RTL では左マージン |
pl-4 | ps-4 | RTL では右パディング |
pr-4 | pe-4 | RTL では左パディング |
text-left | text-start | RTL では右揃え |
text-right | text-end | RTL では左揃え |
rounded-l-lg | rounded-s-lg | RTL では右側の角 |
border-r-2 | border-e-2 | RTL では左ボーダー |
ステップ 6:双方向テキストとフォーム入力
詳しいパターンとエッジケースについては、references/bidi.md を参照してください。
<p dir="rtl">
הזמנה מספר <span dir="ltr">ORD-12345</span> אושרה
</p>
<p dir="rtl">
המשתמש <bdi>JohnDoe123</bdi> נרשם
</p>
<textarea dir="auto" name="comment"></textarea>
<label for="email">אימייל</label>
<input id="email" type="email" dir="ltr">
dir="auto" は最初に現れる強い方向性を持つ文字を使用します。そのため、英語の名前で始まるヘブライ語のコメントは、左から右への基準方向になります。入力内容が不明な場合に使用し、方向が分かっている場合は明示的な dir を指定してください(メール、電話番号、URL、カード、ID の各フィールドには dir="ltr" を指定します)。
イスラエルのアプリでよくある双方向テキストのケース:
| コンテンツの種類 | 方向 | 対応方法 |
|---|---|---|
| ヘブライ語テキスト | RTL | デフォルト。特別な対応は不要 |
| ヘブライ語内の英語テキスト | LTR | dir="ltr" の span で囲む |
| 電話番号 | LTR | dir="ltr" または <bdo> で囲む |
| URL とメールアドレス | LTR | dir="ltr" の span で囲む |
| ヘブライ語とコードの混在 | 両方 | unicode-bidi: isolate を使用 |
| 通貨の金額 | LTR の数字 + RTL の記号 | Intl.NumberFormat を使用 |
| ユーザーが入力したテキスト | 不明 | フィールドと表示要素に dir="auto" を指定 |
ステップ 7:フレームワーク固有の RTL 統合
Tailwind を使用する Next.js App Router: このスニペットは Tailwind のユーティリティのみを示しています。ステップ 1 のレイアウトにある NextIntlClientProvider とロケール処理はそのまま維持してください。維持しないと、useTranslations を呼び出すクライアントコンポーネントでエラーが発生します。
// app/[locale]/layout.tsx
export default async function LocaleLayout({ children, params }) {
const { locale } = await params;
return (
<html lang={locale} dir={locale === 'he' ? 'rtl' : 'ltr'}>
<body className="font-sans">
{/* Tailwind logical utilities auto-flip based on dir attribute */}
<main className="ms-4 me-4 text-start">{children}</main>
</body>
</html>
);
}
// components/NavBar.tsx -- uses rtl: variant for icon flipping
export function NavBar() {
return (
<nav className="flex items-center gap-4">
<button className="ltr:rotate-0 rtl:rotate-180">
<ChevronRight /> {/* Flips to point left in RTL */}
</button>
</nav>
);
}
Vuetify を使用する Vue:
import { createVuetify } from 'vuetify';
const vuetify = createVuetify({
locale: {
locale: 'he',
fallback: 'en',
rtl: { he: true },
},
});
Angular Material:
import { BidiModule } from '@angular/cdk/bidi';
@NgModule({
imports: [BidiModule],
})
export class AppModule {}
// In template:
// <div dir="rtl">...</div>
React Native: ヘブライ語では I18nManager.allowRTL(true) と I18nManager.forceRTL(true) を呼び出します。この設定は保存され、アプリの再読み込み後にのみ反映されるため、起動時に方向を切り替えるか、即座に切り替わることを期待させずに再読み込みを促してください。
例
例 1:既存の React アプリにヘブライ語を追加する
ユーザーの発言:「React アプリにヘブライ語のサポートを追加したいです」
結果:react-intl(ICU の複数形)または react-i18next(_one/_two/_other キー)をセットアップし、he.json メッセージファイルを作成します。ロケールが変更されたら document.documentElement に lang と dir="rtl" を設定し、ハードコードされた文字列を翻訳キーに置き換えます。自由入力欄には dir="auto" を指定し、混在コンテンツの双方向テキストを適切に処理します。
例 2:イスラエルの日付と通貨をフォーマットする
ユーザーの発言:「イスラエルのユーザー向けに日付や価格をフォーマットするにはどうすればよいですか?」
結果:イスラエル形式の日付(曜日ではなく日が月より先で、短縮形式はドット区切りの DD.MM.YYYY)には he-IL ロケールを指定した Intl.DateTimeFormat を使用します。ヘブライ暦の日付には he-IL-u-ca-hebrew を使用し、シェケルの表示には ILS 通貨を指定した Intl.NumberFormat を使用します。また、フォーマット済み文字列の比較やエクスポートの前に、不可視の方向マークを取り除きます。
例 3:双方向テキストの問題を修正する
ユーザーの発言:「ヘブライ語 UI で電話番号や英語のテキストが正しく表示されません」
結果:電話番号を dir="ltr" の span で囲み、英語コンテンツを unicode-bidi: isolate で分離し、ユーザー生成コンテンツには bdi 要素を使用します。ヘブライ語と英語が混在する文字列でテストしてください。
例 4:ヘブライ語の複数形
ユーザーの発言:「ヘブライ語の翻訳で複数形が正しく表示されません」
結果:フレームワークで想定される構文(react-intl/next-intl では ICU、vue-i18n ではパイプと複数形ルール、i18next ではキーの接尾辞)で 3 つのカテゴリー(one/two/other)を実装し、時間の単位には双数形を処理します。React Native では Intl.PluralRules のポリフィルも追加してください。
例 5:Next.js App Router にヘブライ語を追加する
ユーザーの発言:「Next.js App Router プロジェクトにヘブライ語と英語のサポートを追加したいです」
結果:next-intl をインストールし、[locale] ルートセグメントを作成します。i18n/routing.ts、i18n/request.ts、proxy.ts(Next.js 16 より前は middleware.ts)を追加し、next.config.ts を createNextIntlPlugin でラップします。ヘブライ語ロケールでは <html> に dir="rtl" を設定し、ICU 複数形構文を使って he.json と en.json を作成します。RTL 対応スタイルには Tailwind の論理ユーティリティ(ms-*、me-*、text-start)を使用します。
同梱リソース
スクリプト
scripts/generate_i18n.py:ヘブライ語の i18n メッセージファイルを生成します。各フレームワーク固有の構文(react-intl と next-intl では ICU、vue-i18n ではパイプ区切りで、ステップ 1 のヘブライ語複数形ルールと併用)で、ヘブライ語の複数形を含む翻訳 JSON の構造をひな形として作成します。実行方法:python scripts/generate_i18n.py --help
リファレンス
references/pluralization.md:一般的な語のカテゴリー(時間、数量、物)における単数形、双数形、複数形を含む、ヘブライ語の複数形ルールの完全版、現行の CLDR ルールの文面、ICU MessageFormat のパターン、小数やヘブライ語の数詞の一致に関するエッジケース。references/bidi.md:ヘブライ語アプリケーションにおける双方向テキスト処理のパターン。Unicode 双方向アルゴリズムの概要、HTML の dir 属性の使い方、CSS の unicode-bidi プロパティ、フレームワーク固有の双方向テキスト対応策、ヘブライ語・英語・数字が混在するコンテンツでよくある問題を説明します。
注意点
- エージェントは
dir="rtl"をbody要素またはラッパーのdivにのみ設定することがありますが、スクロールバー、既定のテキスト配置、CSS論理プロパティに正しく反映させるには、RTLの方向を<html>レベルで設定する必要があります。SPAでは、ロケールが変わるときにdocument.documentElement.dirを更新してください。 - ヘブライ語の複数形は複雑です。単数形、双数形(一部の名詞)、複数形があります。エージェントは英語のような単数形/複数形(1と複数)だけを実装し、双数形(例:yomayim=2日)を見落とすことがあります。
- エージェントは、あらゆるフレームワークでICUの複数形文字列を記述します。vue-i18nではパイプ区切りのケース(11.xではヘブライ語の複数形ルールも)が必要で、i18nextでは
_one/_two/_otherのキー接尾辞が必要です。どちらもICU文字列を使用すると、そのまま表示されるか、複数形になりません。 - ヘブライ語のi18nキーに英語のテキスト(例:
t('Submit'))を使わないでください。ヘブライ語の翻訳は英語より大幅に短くなったり長くなったりして、レイアウトが崩れることがあります。意味に基づくキー(例:t('form.submit'))を使用してください。 - エージェントは、RTLでアイコンの位置を反転し忘れることがよくあります。矢印、山形、進行状況インジケーターは水平方向に反転してください。ヘブライ語UIでは、「次へ」を示す矢印は右ではなく左を向く必要があります。
- Tailwindの
space-x-*の動作はバージョンによって異なります。v3では物理的な左右のマージンが設定され、反転されないため、RTLではspace-x-reverseを追加するか、gap-*を使用してください。v4ではmargin-inline-start/endが設定され、dirに従って反転されるため、RTLでspace-x-reverseを追加すると間隔が二重に反転します。どちらのバージョンでも、flex/gridではgap-*を推奨します。
トラブルシューティング
エラー:「複数形がヘブライ語の文法と一致しません」
原因:i18nフレームワークがヘブライ語の3種類の複数形ルールに設定されていないか、メッセージがフレームワークで解析できない構文になっています
解決策:ヘブライ語ではone/two/otherを使います(英語のようなone/otherだけではありません)。react-intlとnext-intlでは、twoカテゴリを含むICU MessageFormatを使用してください。vue-i18nでは、ヘブライ語のpluralRules関数とone | two | other形式のパイプ区切りを使用してください。i18nextでは、_one/_two/_otherキーを使用してください。React Nativeでは、Intl.PluralRulesのポリフィルを追加してください。
エラー:「日付がDD/MM/YYYYではなくMM/DD/YYYYで表示されます」
原因:日付の書式設定にhe-ILではなくen-USロケールを使用しています
解決策:new Intl.DateTimeFormat('he-IL')を使用するか、日付ライブラリをhe-ILロケールで設定してください。イスラエルのユーザーにアメリカ式の日付形式を想定しないでください。
エラー:「書式設定された価格の等価性テストに失敗するか、誤った数値として解析されます」
原因:Intl.NumberFormat('he-IL', { style: 'currency', currency: 'ILS' })は、U+200F方向マーク、改行なしスペース、桁区切りのカンマを挿入します
解決策:生の数値を比較して保存してください。テキストを比較する場合は、/[\u200E\u200F]/gでマークを取り除き、U+00A0をスペースに正規化してください。書式設定済みの文字列にparseFloatを使用しないでください。最初のカンマで解析が止まります。
エラー:「RTLコンテキストで数字が逆順に表示されます」
原因:RTLの方向指定が数字の表示順に影響しています
解決策:ヘブライ語でも数字は常にLTRです。数値コンテンツにdir="ltr"を使用するか、既定で数字を正しく扱うUnicode双方向アルゴリズムを利用してください。問題は通常、数字そのものではなく、周囲の句読点にあります。