运行您的工作区
语言和国际化
互客鱼附带 130+ 种语言的翻译,开箱即用,英语、西班牙语、法语和土耳其语 端到端覆盖,长尾(德语、印地语、孟加拉语、阿拉伯语、希伯来语、中文、日语、 韩语、越南语、每种流行的欧洲/亚洲/非洲语言,以及 RTL 脚本)覆盖 UI chrome — 按钮、导航、表单、状态药丸。给定区域设置尚未翻译的每个键自动回退到英语源, 因此当个别翻译者贡献长尾时,UI 永远不会中断。
添加新语言只是放下一个文件
LocaleResolver::supported() 服务通过在请求时扫描
lang/*.json 自动发现区域设置。要添加新语言,放下一个
lang/<code>.json 文件(例如瑞典语的 lang/sv.json)
— 无需代码更改、无需迁移、无需服务重启。下次页面加载会拾取它,选择器模态框
列出它,SetLocale 中间件接受 ?locale=sv,它出现在枚举支持区域
设置的每个 API 响应中。
选择器从 App\Services\I18n\LocaleCatalog::ENTRIES 提取元数据
(本机名称、英文名称、旗帜 emoji、RTL 标志)— 130+ 种流行语言的精选列表。
如果您为目录中没有的代码提供 JSON 文件,它仍然有效 — 选择器回退到代码本身、
🌐 地球 emoji 和 LTR 方向。贡献目录元数据只是升级视觉效果。
从右到左语言
目录将阿拉伯语、希伯来语、波斯语、乌尔都语、普什图语、信德语、迪维希语、 意第绪语和维吾尔语标记为 RTL。渲染管道相应镜像:
- Blade 根模板(管理 SPA 的
resources/views/app.blade.php、 营销网站的resources/views/marketing/_layout.blade.php、 潜在客户捕获电子邮件的resources/views/emails/leads/captured.blade.php) 在活动区域设置的目录条目具有rtl => true时在<html>上发出dir="rtl"。 - Tailwind v4 逻辑属性携带布局:每个
ms-/me-/ps-/pe-/start-/end-/text-start/text-end实用程序类根据文档方向自动翻转。代码库专门使用逻辑属性;物理ml-/mr-/pl-/pr-/left-/right-类在早期的 codemod 中被清除。 - Radix
<DirectionProvider>包装 React 应用在resources/js/app.tsx,因此每个 Radix 原语(DropdownMenu、Popover、 Tooltip、Select、Sheet、ContextMenu)获得正确的对齐 + 动画方向,无需每个组件代码。 useIsRtl()/useDirection()hooks 在resources/js/lib/direction.ts中从共享 i18n 目录读取当前区域 设置的 RTL 标志。当组件需要显式 JS 方向逻辑时使用它们。- 方向图标(后退、前进、chevrons)包装
<DirArrow direction="forward|back" />来自resources/js/components/dir-icon.tsx,因此图标本身翻转。 方向装饰性的图标(paper-plane 发送、undo)使用.flip-rtlCSS 实用程序而不是交换字形。 - 访客小部件在启动时读取
init.agent.locale,如果区域设置是 RTL, 则在其 shadow root 上设置dir="rtl"— 小部件内的每个 Tailwind 逻辑属性类然后像管理 SPA 一样翻转。 <Sidebar>组件根据其方向将其sideprop 默认为视觉起始边缘(LTR 中的side="left",RTL 中的side="right"),因此 vanilla<Sidebar />始终固定到视觉起始。
tests/Feature/I18n/RtlDirTest.php 回归测试断言每个 RTL
区域设置在管理 Inertia 根、营销布局和潜在客户捕获电子邮件上产生
dir="rtl";LTR 区域设置相反产生 dir="ltr"。
区域设置选择器模态框
管理 shell 和营销网站在您点击语言药丸时打开可搜索的 Dialog。列表显示 每个区域设置的本机名称 + 英文名称 + 旗帜,可按代码或名称过滤。两个表面上 相同的组件。有 130+ 个条目,旧的下拉菜单不可滚动 — 模态框扩展到数千种语言。
解析顺序
SetLocale 中间件在每个 web 请求的会话中间件之后运行,并遍历此优先级列表:
- 显式
?locale=<slug>查询(允许列表)。 - 经过身份验证的用户的
users.locale列。 pb_localecookie(由 geo-banner 切换路径设置,为未授权访客跨会话持久化)。- 浏览器的
Accept-Language头(最高 q-value 获胜)。 - 来自
config/app.php的应用程序默认值。
Geo-suggested 区域设置横幅
当 Cloudflare 的 CF-IPCountry 头映射到我们提供的区域设置且
访客的当前区域设置是其他内容时,会出现一个细长的横幅询问
“Switch to Español?” 横幅从不自动切换 — 惊喜 = 糟糕的 UX。
两个操作:
- Switch to <language> — PATCHes
/locale/switch。 登录时设置users.locale并始终设置pb_localecookie (1 年生命周期),然后用新语言重新加载。 - ✕ — POSTs
/locale/dismiss-suggestion, 设置pb_locale_dismiss=1cookie(180 天生命周期)。 横幅在该设备上永远不会再次出现。
抑制规则(服务器端,在 LocaleResolver::suggestionFor 中):
- 访客已经关闭 → null。
- 当前区域设置已经与建议匹配 → null。
- 没有
CF-IPCountry头(本地开发、非 CF 部署)或值为XX/T1→ null。 - 国家不在
COUNTRY_TO_LOCALE中 → null。
国家 → 区域设置映射覆盖西班牙语拉丁美洲 + 西班牙(es)、法语欧洲 + 魁北克(fr)、土耳其(tr)。 其他国家没有建议。
横幅安装在管理 SPA(app-sidebar-layout)、每个 Inertia
营销页面(marketing-shell)上,并通过 Blade
{{ __('Switch to :language?') }}
在 resources/views/marketing/_layout.blade.php 中用于任何未来的
Blade-rendered 营销页面。
相同的 LocaleResolver 服务支持小部件。小部件传递在顶部添加一个
额外的步骤:智能体的 language_default — 管理员可以固定垂直特定的
语言,即使访客的浏览器不同意。
字符串在哪里
lang/{locale}.json— JSON 字典,由管理 React SPA、小部件、 营销 Blade 页面和邮件模板使用。英语源字符串充当键。lang/{locale}/auth.php、validation.php、passwords.php、pagination.php— Laravel 的命名空间 PHP 文件用于内置框架消息。lang/_glossary.md— 术语锁,以便未来的字符串与之前的运行一致翻译。
在哪里添加翻译
每当您在代码中添加面向用户的字符串时:
- 后端 Blade:包装为
English source。 - 管理 React:导入 hook 并调用
const { t } = useT();,然后t('English source')。 - 小部件:从
core/i18n.ts导入t并调用t('English source')。 将键添加到WidgetCopy::KEYS,以便服务器将其具体化到/init负载中。
翻译后的键添加到 lang/<locale>.json。
缺失的键静默回退到英文源 — UI 永远不会中断。tests/Feature/I18nTest::every supported
locale ships a parseable JSON dictionary 回归测试断言每个发布的语言环境文件都是有效的 JSON,
具有非空字符串值;一个姊妹测试防止引入拼写错误的键(语言环境文件中任何在 en.json
中不存在的键都会使 CI 失败)。
添加新语言环境
- 放置一个
lang/<slug>.json文件。仅此一项就使语言环境出现在选择器中, 并通过 SetLocale 中间件接受?locale=<slug>。LocaleResolver::supported()在下一个请求时自动发现该文件。 - (可选,但推荐)在
app/Services/I18n/LocaleCatalog::ENTRIES中添加一个条目, 包含语言环境的native名称、english名称、flagemoji 和rtl标志。没有条目,选择器仍然工作 — 它只是将语言环境代码显示为标签和 🌐 地球仪。 - (可选)将
lang/en/{auth,validation,passwords,pagination}.php复制到lang/<slug>/如果您想要 Fortify / 验证消息被翻译。没有这些文件, Laravel 会回退到英语。 - 运行
php artisan test --filter=I18nTest和--filter=LocaleResolverTest以确认新语言环境不会引入幻象键。 - (可选)更新
lang/_glossary.md添加一列,以便未来的翻译保持一致性。
启用语言环境不需要代码更改。之前的 LocaleResolver::SUPPORTED 常量已被删除;
选择器、验证规则和中间件都从 LocaleResolver::supported() 读取,它返回自动发现的集合。
每用户与每访客语言环境
管理员和操作员 在 /settings/locale 设置他们的首选语言。
该选择持久化到 users.locale 并应用于每个后续请求,包括代表他们发送的电子邮件。
访客 默认看到智能体的 language_default。如果智能体没有固定语言,
小部件回退到访客的浏览器语言环境,然后回退到英语。小部件中没有语言环境切换器 —
这是一个深思熟虑的决定,因此访客体验与管理员配置的内容相匹配。
覆盖率
每个面向客户的表面 — 访客小部件、营销网站(主页、定价、工作原理、集成、变更日志、隐私、条款)、
管理 SPA(每个客户管理员和平台管理员页面,包括智能体自定义、来源、精选答案、知识、游乐场、行为、
CTA、潜在客户、对话、实验、计费、集成、分析、工作流、每个设置选项卡)、身份验证 + 入职流程、
交易电子邮件和验证消息 — 都通过 useT() 或 __() 连接。
英文源(lang/en.json)是事实来源,目前拥有约 2,300 个键。
每种语言环境的覆盖率各不相同。en、es、fr 和 tr
完全端到端翻译(en.json 中的每个键都有一个本地化值)。其他约 130 个自动发现的语言环境文件
以最常用的 UI chrome 翻译开始(约 130 个键:按钮、导航、表单、状态药丸),并随着翻译人员的贡献而扩展。
给定语言环境尚未翻译的键通过 Laravel 的标准 JSON 键行为回退到英文源,因此 UI 永远不会中断;
用户在长尾填充时看到部分翻译。
随时检查语言环境的覆盖率:
node -e 'const fs=require("fs"); const en=JSON.parse(fs.readFileSync("lang/en.json")); const m=JSON.parse(fs.readFileSync("lang/<code>.json")); const total=Object.keys(en).length; const translated=Object.keys(en).filter(k=>k in m && m[k]!==en[k]).length; console.log(`${translated}/${total} = ${Math.round(translated/total*100)}% covered`);'
resources/views/documentation/pages/ 下的文档页面根据政策保持英语 —
翻译密集的技术文章是一个文案项目,而不是工程。如果需要特定交易的翻译文档,请在 Kanban 板上跟踪后续。
浏览器 SEO + 可访问性
- 营销布局、管理 Inertia 根目录和交易电子邮件上的
<html lang>属性在每次渲染时反映已解析的语言环境。 - 验证消息、密码重置电子邮件和 Fortify 身份验证消息都流经 Laravel 的翻译器 — 它们会自动获取用户的语言环境。
- 小部件的首次绘制已经使用正确的语言 — 服务器在
/init时具体化agent.copy, 因此在翻译副本水合之前永远不会有英语闪现的时刻。