应用程序 UI 本地化
@codaco/app-i18n 支持的网络国际化外观
Canvas应用铬. 议定书授权的内容、收集的数据和
持续识别器保留自己的语言和身份.
信件和目录
导入 defineMessages, (中文(简体) ), defineMessage, (中文(简体) ), createAppIntl及其类型
@codaco/app-i18n/messages。本条目在 React 服务器组件和
普通 JavaScript 语境 。 使用 useAppIntl 在客户端组件中; 它使
没有安装提供者时的英语默认值 .
给每个描述符一个稳定的名字空间,一个英文 defaultMessage,以及
向翻译解释背景的描述。 保持句子完整;
使用 ICU 复数/ 选择、 富文本标记和数字/ 日期参数而不是
加入已翻译的碎片.
import { defineMessages } from '@codaco/app-i18n/messages';
import { useAppIntl } from '@codaco/app-i18n/react';
const messages = defineMessages({
selected: {
id: 'example.records.selected',
defaultMessage:
'{count, plural, one {# record selected} other {# records selected}}',
description: 'Selection count above the researcher records table.',
},
});
function SelectedCount({ count }: { count: number }) {
return useAppIntl().formatMessage(messages.selected, { count });
}
主机装入目录 common每个被消耗的包装 和自己的应用,
一次使用一个地方(参见 正在装入目录) (中文(简体) ). 每个地方
内 ecosystemLocales 每个共享目录中有完整的目录
软件包,但 en-GB,这是一个稀有的经过审查的英语功能。 联合国
src/locales/en.json 文件是生成的提取文物,从不运行时间
导入: 描述符默认提供英语运行时间倒计时。 保留
应用程序目录 src/locales/<canonical-BCP-47-tag>.json 页:1
构建时间汇编.
一个应用程序声明它实际支持的子集; 在其中添加一个地块
生态系统不需要其他应用程序来做广告. defineAppLocales
仅在明确启用时才添加诊断伪地. 不要坚持
作为生产选择的诊断选择.
使用 AppMessage 从 @codaco/app-i18n/react 用于队列对话框或土司 :
<AppMessage message={messages.selected} values={{ count }} />
节点即使排入队列也订阅到活动提供者.
当操作开始冻结旧语言时格式化为字符串.
Fresco UI 对话框标题、描述、动作标签和 describeError
调用符接受这些节点.
对于现有的仅字符串错误/结果合同, createMessageError 从
@codaco/app-i18n/messages 保存一个简单文字描述符及其命名值
不捕捉活动区域。 它保留源默认并编译
ICU AST(重症监护室). Fresco UI 已有的字段/窗体出错渲染器解析这些消息
在语言切换时显示时间并保存服务器拒绝。 使用
AppErrorMessage 从 @codaco/app-i18n/react 用于存储的其他字符串错误,或
formatMessageError(error, intl) ?? error 在字符串渲染器中。 普通类
审定/诊断文本保持不变。 运输清单用途
{ dependencies: { list: dependencyIds } },因此组合格式化为
读者的语言 而不是切换前的捕捉.
当整个信件包含单独拥有的被翻译的标签时,通过
{ rule: { messageError: createMessageError(ruleDescriptor) } }清单项目
还接受这个清晰的包装。 普通的字符串总是字面数据,
即使它们恰好像一个编码错误。 此保存共享规则名称
和无名属性标签反应,而不重复其翻译.
正在装入目录
每个拥有目录的软件包都会导出装入器的地图:每个地方都有它
翻译,一个函数返回 import('./<tag>.json')。每种语言都是
因此,它自己的块,一个主机只下载它所下载的块。 联合国
没有装货机 也不需要.
| 软件包 | 条目 | 导出 |
|---|---|---|
@codaco/app-i18n | @codaco/app-i18n/common | commonCatalogLoaders |
@codaco/fresco-ui | @codaco/fresco-ui/locales | frescoUiCatalogLoaders |
@codaco/interview | @codaco/interview/locales | interviewCatalogLoaders |
@codaco/network-exporters | @codaco/network-exporters/locales | networkExporterCatalogLoaders |
@codaco/protocol-builder | @codaco/protocol-builder/locales | protocolBuilderCatalogLoaders |
@codaco/protocol-utilities | @codaco/protocol-utilities/locales | protocolUtilitiesCatalogLoaders |
@codaco/protocol-validation | @codaco/protocol-validation/locales | protocolValidationCatalogLoaders |
一个主机以同样的形状宣布自己的装载器,并传递它的一切
改为: createCatalogSource 从 @codaco/app-i18n/locales,按合并顺序排列:
常见的先是共享软件包,然后是应用程序. 后来加载器在共享上获胜
身份证.
import { commonCatalogLoaders } from '@codaco/app-i18n/common';
import {
createCatalogSource,
type CatalogLoaders,
} from '@codaco/app-i18n/locales';
import { frescoUiCatalogLoaders } from '@codaco/fresco-ui/locales';
const appCatalogLoaders: CatalogLoaders = {
'es': () => import('./es.json'),
'zh-Hans': () => import('./zh-Hans.json'),
};
export const catalogSource = createCatalogSource(
commonCatalogLoaders,
frescoUiCatalogLoaders,
appCatalogLoaders,
);
来源一次合并并缓存一个地块 :
load(locale)决定合并目录。 同时和重复的电话 共享一个请求,在负载失败后启动新请求.attempt(locale)返回该地块已有的负载,包括其 上次失败的尝试, 并且只有在没有尝试时才开始一次。 挂起这个 而不是说load: 暂停使用load将开始一个新的 每次重试一次 都不要和解 A 类load尝试 在飞行中加入时作为尝试。 之前失败的 任何加入的尝试(如第一次渲染前的预装)都不可交付 因此,第一次尝试 之后仍然尝试新的.peek(locale)返回已装入的合并目录, 否则undefined。没有软件包翻译的地块(英语,伪地块)是 总是准备好一个空目录.subscribe(onLoad)返回未订阅的选项.
useLocaleCatalog(source, locale, preloaded?) 从 @codaco/app-i18n/react
返回时 { locale, messages, failure } 给予 AppI18nProvider通过它
以结果代替请求的地块, 因为两者不同, 而开关
正在装入,当装入失败时:
import { AppI18nProvider, useLocaleCatalog } from '@codaco/app-i18n/react';
import type { ReactNode } from 'react';
import { appLocales } from './locales';
import { catalogSource } from './locales/catalogs';
export function I18n({
locale,
children,
}: {
locale: string;
children: ReactNode;
}) {
const rendered = useLocaleCatalog(catalogSource, locale);
return (
<AppI18nProvider
locale={rendered.locale}
locales={appLocales}
messages={rendered.messages}
loadFailure={rendered.failure}
>
{children}
</AppI18nProvider>
);
}
钩子将当前语言保留在屏幕上,直到新语言装入,
然后在一次渲染中改变 所以开关从不通过英语或者
显示半翻译界面。 屏幕上什么都没有 也没有
要保留的语言, 因此第一个负载暂停: 使提供者处于
暂停边界, 或等待 source.load(locale) 在第一次变身之前
从不暂停.
永不停息界面的负载 。 开关留在当前
语言,第一个负载返回到英语,每个描述符
携带。 无论哪种方式,钩子都返回它无法装入的语言
failure,并不断尝试: 当设备回到网上,
在每次失败都加倍的等待之后 最多30秒 A 类
为页面寿命保留失败模块导入的浏览器, 如 Chrome
做,没有请求就回答失败的每一个重复,所以
在那里,语言只有在重新装入后才到达。 给 failure 通知提供者
作为 loadFailure,并告诉用户: useLocaleLoadFailure() 返回时
无法加载的语言的登记条目(locale)和
一个放在屏幕上shown),与 error,以及
LocaleLoadFailureToast 从
@codaco/fresco-ui 把它当作一个祝酒词,直到它被解职或
语言到达.
服务器主机加载请求的目录,格式,并发送相同的
向客户端发送信息作为道具。 把他们当成钩子 preloaded 参数
在不下载的情况下制作该语言的水合物和水合物,持续时间最长
preloaded.locale 匹配 locale数字 :
// On the server, per request:
const messages = await catalogSource.load(locale);
const intl = createAppIntl({ locale, messages });
// In the client provider, with `messages` received as a prop:
const rendered = useLocaleCatalog(catalogSource, locale, { locale, messages });
在不需要资源的地方, 如一次性服务器, 一个故事书
装潢或测试, loadCatalog(locale, ...loaders) 从
@codaco/app-i18n/locales 装入并合并一个单一区域 :
import { commonCatalogLoaders } from '@codaco/app-i18n/common';
import { loadCatalog } from '@codaco/app-i18n/locales';
import { frescoUiCatalogLoaders } from '@codaco/fresco-ui/locales';
const messages = await loadCatalog(
'es',
commonCatalogLoaders,
frescoUiCatalogLoaders,
);
装填器不带电源就导入JSON import 属性, 所以目录负载
只能通过捆绑机;节点自己的无害环境管理装载机无法读取. 软件包或应用程序
拥有目录的人声称 src/locales/<tag>.json 有一个
装入器装入它,没有装入器在缺少的文件上点出
checkCatalogLoaders(localesDir, loaders) 从 @codaco/app-i18n/catalog-guards。 。 。 .
它解决了一系列问题,当装入器正常时会空出.
东道国的责任
挂载 AppI18nProvider 与活动区域、所支持的注册和
locale 来自目录来源的消息 。 内容 onLocaleChange 召回代表
(一) 东道方的持久性; null 意思是自动谈判. resolveAppLocale 手柄卡通化,
最适合的浏览器匹配, 和明确的英文倒置 。 中国的请求
由脚本匹配,所以 zh-TW, (中文(简体) ), zh-HK 财务报告和审定财务报表 zh-MO 选择 zh-Hant 财务报告和审定财务报表
zh, (中文(简体) ), zh-CN 财务报告和审定财务报表 zh-SG 选择 zh-Hans,即使一个通用 zh 随行
浏览器列表中的区域标记 。 HTTP 主机可以获取所请求的标记
使用 parseAcceptLanguage 从根 @codaco/protocol-validation 输出.
外部提供者管理文档 lang 财务报告和审定财务报表 dir嵌入式参与者
采访或预览有一个独立的提供者,其地址是: manageDocument={false}
及其自身的要素 lang 财务报告和审定财务报表 dir。保留当前协议
runtime语言独立于研究者偏好.
服务器主机先解决请求的账户/ 设备/ 浏览器偏好
渲染,然后将初始化序列化给客户端. 创建每个服务器
用于与 createAppIntl,通过目录
source.load(locale); 不将特定请求的本地状态放入
模块全局或跨用户缓存。 使用相同的明示 timeZone 两者
用于确定日期/时间水分的服务器格式和客户端提供者.
Vite 构建
地点 appI18n() 从 @codaco/app-i18n/vite 在框架插件之前。 这个
编译源默认和导入到 ICU 的本地目录并删除
生产应用程序中的运行时间解析器构建。 一个出版的软件包
拥有描述符/目录使用 appI18n({ build: 'library' }) 来编译它们
并让其消费者选择解析器.
下一个带涡轮包的J
使用 @codaco/app-i18n/next-loader 用于源默认和源语言目录.
共享加载器应用与 Vite 相同的编译器并保存客户端
指令。 。 。 源码规则保留了原扩展名, 所以下个仍然
进行其正常的TypeScript/JSX变换;只有JSON规则出JavaScript.
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
turbopack: {
rules: {
'*.{js,jsx,ts,tsx}': {
condition: { not: 'foreign' },
loaders: ['@codaco/app-i18n/next-loader'],
},
'**/src/locales/*.json': {
condition: { not: 'foreign' },
loaders: ['@codaco/app-i18n/next-loader'],
as: '*.js',
},
},
resolveAlias: {
...(process.env.NODE_ENV === 'production'
? {
'@formatjs/icu-messageformat-parser':
'@formatjs/icu-messageformat-parser/no-parser.js',
}
: {}),
},
},
};
export default nextConfig;
工作空间包导出源, 因此规则也会编译其描述符
和目录。 已发布软件包导出预编译 dist 文物和必须
在库建过程中编译自己的消息. 一个非目录 JSON 文件
仍然是普通数据。 不另名运行时解析器, 除非每个来源
编译默认和运行时间目录.
审定和生成指导
没有框架的工人/CLI诊断方法保持不变。 研究员 主机可以通过自有软件包选择本地化的演示文稿 :
@codaco/protocol-validation/messages: 协议文件错误描述符, 验证规则标签、可诉矛盾摘要和formatMigrationNotes(version, notes, intl)用于移民审批指南. 已知的迁移版本, 翻译每个完整的 Markdown 句号同时 保存列表结构和字面协议默认;未知版本 保留他们提供的笔记。 核心移徙说明仍是英文.@codaco/protocol-utilities/messages: 选择生成冲突指导 (d) 原有技术诊断.@codaco/network-exporters/messages: 选择导出进度描述符 由稳定的事件阶段标识.@codaco/protocol-builder: 本地化的舞台界面显示名称, 保存 持续的协议命名独立于活动区域.
每个软件包通过其翻译提供翻译 ./locales 输出。 这个
验证/用户/出口商中的信息和地址条目可选
@codaco/app-i18n 端点;其现有的引擎切入点不获取
反应依赖。 选择本地化演示文稿的主机安装端点和
添加软件包的目录加载器( 列在
正在装入目录他们的目录来源.
翻译来源
更改英文句子使其翻译无效。 每个本地目录
有一份英文的忠实记录 它的录入来自一个兄弟
src/locales/<tag>.source.json和目录的守卫失败 当一个记录
句子已经不再匹配今天的 en.json。 。 。 .
没有它,一个重写英文字符串 留下每一个翻译说老 与每一个其他的守卫 仍然绿色:目录是完整的, ICU 参数仍然匹配,无一是空白的,错误的拷贝在屏幕上。 这个 而非假设 协议在英语更改后被下载,以表示否 文件是写出来的.
因此,当翻译背后的英语变化时:
pnpm --filter <pkg> i18n:extract重新生成en.json和警卫 以 ID 开始失败, 它从英文译出 它现在说, 和翻译本身.- 从这三句里决定 大多数英语编辑是 社论——大写,逗号,同义词——译名还原 说对了 有些改变意义,翻译必须 在它回到读者面前之前重写.
pnpm --filter <pkg> i18n:stamp重写记录并打印每对 它接受了。 阅读该报告,并在重新翻译的同时予以执行; 副车厢是可审查的被接受的记录.
从磁盘中读取这些文件; 从不导入 。 所以英国人才会这样
复制出捆绑, 名称会备份它: 地方标记不能包含
点, 因此扫描本地目录的构建步骤不会出错
es.source.json 为目录.
只有 defaultMessage 记录,而不是 description。描述是
其编辑原因并不改变
将两人连成一对 将守卫解雇
澄清并训练人们不读就重新加印
习惯会让这个警卫一文不值.
核查
运行每个所有者 i18n:extract 脚本并输入生成的英文目录 .
编目警卫检查新鲜度 身份证 翻译描述 完整
西班牙语、英国英语、有效伊斯兰法院联盟、占位符/丰富文本等同
每一个忠实的目录都有一个装入器,每个翻译
仍然记录着它用英文做的.
此外,还审计了JSX以外的表面和复制品:
目录无法找到从未被提取的字符串 。 锻炼生产
构建、活的地盘变化、主机偏好持续以及独立
审查翻译的含义和布局.