mcpField
H
hebrew-i18n

hebrew-i18n

github.com/skills-il开发2659 次安装2 周前更新
代码仓库

关于 hebrew-i18n

面向 Web 开发者的希伯来语国际化技能,提供 RTL 布局、本地化格式和复数规则的实施指导。内容涵盖 React、Vue、Next.js、Angular 等框架及双向文本处理。

主要功能

  1. 01根元素语言与方向设置
  2. 02希伯来语复数规则处理
  3. 03日期、数字和货币格式
  4. 04RTL CSS 与双向文本指导
  5. 05多框架集成示例

适用场景

  1. 01为 Web 应用添加希伯来语支持
  2. 02排查 RTL 布局和混合文字显示问题
  3. 03实现希伯来语复数与地区格式

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/skills-il/localization --skill hebrew-i18n

安装后在 Claude Code 中输入/hebrew-i18n确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

evidence.json
metadata.json
optimization-log.json
SKILL_HE.md
references/
bidi.md
pluralization.md
scripts/
generate_i18n.py

详细介绍

希伯来语国际化

说明

第 1 步:设置国际化框架

Direction 应放在根 <html> 元素上,而不是包装用的 div 上。在运行时切换语言的单页应用中,每次区域设置变化时都要更新它:

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,如果它是 undefined,就添加一个 polyfill(例如 intl-pluralrules)。

Vue(vue-i18n): vue-i18n 的复数形式用竖线分隔,而不是 ICU。vue-i18n 11.x(当前发布线)按位置而不是按 Intl.PluralRules 选择情况:三种情况被读作 zero | one | other,因此没有规则时 t('days', 1) 会输出第二种情况。对于希伯来语,请注册一条规则,将 1、2 以及其他所有计数映射到这三种情况,并按 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 主分支上的一项破坏性变更,并未包含在 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>
  );
}

由 Server Component 渲染时,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 步:希伯来语复数形式

希伯来语有三种国际化框架必须处理的复数类别:

类别希伯来语术语计数示例
one(单数)יחיד1,以及小于 1 的小数פריט אחד(一个项目)
two(双数)זוגי2שני פריטים(两个项目)——使用特殊的双数形式
other(复数)רבים0、3+、1.0 及以上的小数5 פריטים(5 个项目)

注意:较旧的 many 类别(用于 20 或 100 这样的整数)已在 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 locale 扩展就足够了。当你需要节日表、parashat hashavua 或希伯来历中的日期运算时,请使用 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-XXXXXXX054-1234567
电话(座机)0X-XXXXXXX02-6234567
Teudat Zehut(身份证)XXXXXXXXX123456782(9 位数字,含校验位)
邮政编码XXXXXXX6100000(7 位数字)
货币X,XXX.XX ₪1,234.50 ₪(Intl 输出会添加不可见的方向标记)
第 5 步:使用逻辑属性的 RTL CSS

对于面向 i18n 的布局,请始终使用 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-4ms-4RTL 中为右外边距
mr-4me-4RTL 中为左外边距
pl-4ps-4RTL 中为右内边距
pr-4pe-4RTL 中为左内边距
text-lefttext-startRTL 中右对齐
text-righttext-endRTL 中左对齐
rounded-l-lgrounded-s-lgRTL 中为右侧圆角
border-r-2border-e-2RTL 中为左边框
第 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、银行卡和身份证字段为 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:格式化以色列日期和货币

用户说:“如何为以色列用户格式化日期和价格?” 结果:使用带 he-IL 语言区域的 Intl.DateTimeFormat 来格式化以色列格式的日期(日在月之前;短格式以点分隔,即 DD.MM.YYYY),使用 he-IL-u-ca-hebrew 格式化希伯来历日期,使用带 ILS 货币的 Intl.NumberFormat 格式化谢克尔,并在比较或导出格式化字符串之前去除不可见的方向标记。

示例 3:修复双向文本问题

用户说:“我的希伯来语界面中电话号码和英文文本显示不正确” 结果:将电话号码包裹在 dir="ltr" 的 span 中,使用 unicode-bidi: isolate 隔离英文内容,对用户生成的内容使用 bdi 元素,并用希伯来语/英语混合字符串进行测试。

示例 4:希伯来语复数形式

用户说:“我的希伯来语翻译显示了错误的复数形式” 结果:按照你的框架所期望的语法实现三个类别(单数/双数/其他)(react-intl/next-intl 使用 ICU,vue-i18n 使用管道符加复数规则,i18next 使用键后缀),处理时间单位的双数形式,并在 React Native 上添加 Intl.PluralRules polyfill。

示例 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),用 createNextIntlPlugin 包装 next.config.ts,为希伯来语区域在 <html> 上设置 dir="rtl",创建使用 ICU 复数语法的 he.json 和 en.json,并使用 Tailwind 逻辑实用工具类(ms-*、me-*、text-start)实现支持 RTL 的样式。

捆绑资源

脚本
  • 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 属性、框架特定的双向解决方案,以及希伯来语/英语/数字混合内容的常见陷阱。

注意事项

  • Agent 可能只在 body 元素或包装 div 上设置 dir="rtl",但必须在 <html> 层级设置 RTL 方向,才能正确影响滚动条、默认文本对齐和 CSS 逻辑属性。在 SPA 中,当 locale 变化时更新 document.documentElement.dir。
  • 希伯来语复数形式很复杂:有单数、双数(部分名词)和复数形式。Agent 可能实现简单的英语式单数/复数(1 与多),从而遗漏双数形式(例如 yomayim = 2 天)。
  • Agent 会为每个框架编写 ICU 复数形式字符串。vue-i18n 期望以管道符分隔的 case(并且在 11.x 上还需要希伯来语复数规则),i18next 期望 _one/_two/_other 键后缀;在这两者中,ICU 字符串要么按字面渲染,要么永远不做复数化。
  • 希伯来语的 i18n 键不应使用英文文本作为键(例如 t('Submit')),因为希伯来语翻译可能短得多或长得多,从而破坏布局。请使用语义化键(例如 t('form.submit'))。
  • Agent 常常忘记在 RTL 中反转图标位置:箭头、chevron 和进度指示器应水平镜像。在希伯来语 UI 中,"next" 箭头应指向左,而不是右。
  • Tailwind space-x-* 的行为因版本而异。在 v3 中它设置物理左/右边距且不会翻转,因此在 RTL 下需添加 space-x-reverse,或改用 gap-*。在 v4 中它设置 margin-inline-start/end,已遵循 dir,因此为 RTL 添加 space-x-reverse 会双重反转间距。两者中都优先使用 gap-* 搭配 flex/grid。

故障排查

错误:"Plural forms not matching Hebrew grammar"

原因:i18n 框架未配置希伯来语的三类复数规则,或消息使用了框架无法解析的语法 解决方案:希伯来语使用 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 polyfill。

错误:"Date showing MM/DD/YYYY instead of DD/MM/YYYY"

原因:日期格式化使用了 en-US locale 而不是 he-IL 解决方案:使用 new Intl.DateTimeFormat('he-IL'),或用 he-IL locale 配置你的日期库。绝不要为以色列用户假定美式日期格式。

错误:"Formatted price fails an equality test or parses to the wrong number"

原因:Intl.NumberFormat('he-IL', { style: 'currency', currency: 'ILS' }) 会插入 U+200F 方向标记、不换行空格和分组逗号 解决方案:比较和存储原始数字。对于文本比较,用 /[\u200E\u200F]/g 去除标记,并将 U+00A0 规范化为空格。不要对格式化字符串使用 parseFloat;它会在第一个逗号处停止。

错误:"Numbers appear reversed in RTL context"

原因:RTL 方向影响数字显示顺序 解决方案:希伯来语中的数字始终为 LTR。对数字内容使用 dir="ltr",或依赖 Unicode 双向算法,它默认能正确处理数字。问题通常出在周围的标点,而不是数字本身。

评论

支持 Markdown