293 lines
No EOL
6.7 KiB
Markdown
293 lines
No EOL
6.7 KiB
Markdown
# JavaScript/TypeScript i18n 样板
|
||
|
||
## 目录
|
||
|
||
- [概述](#概述)
|
||
- [翻译函数](#翻译函数)
|
||
- [占位符样板](#占位符样板)
|
||
- [翻译器评论](#翻译器评论)
|
||
- [复杂字符串样板](#复杂字符串样板)
|
||
- [常见陷阱](#常见陷阱)
|
||
- [快速命令参考](#快速命令参考)
|
||
|
||
## 概述
|
||
|
||
WooCommerce 使用来自 `@wordpress/i18n` 的 WordPress i18n 函数来处理可翻译字符串。像 "WooPayments" 这样的品牌名称应使用占位符以提高翻译灵活性。
|
||
|
||
```typescript
|
||
import { __, sprintf } from '@wordpress/i18n';
|
||
```
|
||
|
||
## 翻译函数
|
||
|
||
### 基础翻译
|
||
|
||
```typescript
|
||
// 简单字符串
|
||
__( '保存更改', 'woocommerce' )
|
||
|
||
// 带占位符的字符串
|
||
sprintf(
|
||
/* 译: %s: 支付提供者名称 (例如: WooPayments) */
|
||
__( '设置 %s', 'woocommerce' ),
|
||
'WooPayments'
|
||
)
|
||
```
|
||
|
||
### 使用 `_n` 的复数形式
|
||
|
||
```typescript
|
||
import { _n, sprintf } from '@wordpress/i18n';
|
||
|
||
sprintf(
|
||
/* 译: %d: 项目数量 */
|
||
_n(
|
||
'%d 个项目已选中',
|
||
'%d 个项目已选中',
|
||
count,
|
||
'woocommerce'
|
||
),
|
||
count
|
||
)
|
||
```
|
||
|
||
### 使用 `createInterpolateElement` 进行插值元素
|
||
|
||
```typescript
|
||
import { createInterpolateElement } from '@wordpress/element';
|
||
import { __, sprintf } from '@wordpress/i18n';
|
||
|
||
createInterpolateElement(
|
||
sprintf(
|
||
/* translators: 1: 支付提供者名称 */
|
||
__( '为您的商店启用 <strong>%1$s</strong>。', 'woocommerce' ),
|
||
'WooPayments'
|
||
),
|
||
{
|
||
strong: <strong />,
|
||
}
|
||
)
|
||
```
|
||
|
||
## 占位符样板
|
||
|
||
### 单个占位符
|
||
|
||
使用 `%s` 表示单个占位符:
|
||
|
||
```typescript
|
||
sprintf(
|
||
/* translators: %s: 支付提供者名称(例如:WooPayments) */
|
||
__( '通过 %s 收款', 'woocommerce' ),
|
||
'WooPayments'
|
||
)
|
||
```
|
||
|
||
### 多个相同占位符
|
||
|
||
当同一数值多次出现时,请使用带编号的占位符 `%1$s`:
|
||
|
||
```typescript
|
||
sprintf(
|
||
/* translators: 1: 支付提供者名称(例如,WooPayments) */
|
||
__(
|
||
'使用 %1$s 即表示您同意我们的条款。通过 %1$s 进行的支付是安全的。',
|
||
'woocommerce'
|
||
),
|
||
'WooPayments'
|
||
)
|
||
```
|
||
|
||
### 多个不同的占位符
|
||
|
||
使用带编号的占位符 `%1$s`、`%2$s` 来表示不同的值:
|
||
|
||
```typescript
|
||
sprintf(
|
||
/* translators: 1: 支付提供者名称, 2: 扩展名称 */
|
||
_n(
|
||
'正在安装 %1$s 将启用 %2$s 扩展。',
|
||
'正在安装 %1$s 将启用 %2$s 扩展。',
|
||
extensionCount,
|
||
'woocommerce'
|
||
),
|
||
'WooPayments',
|
||
extensionNames
|
||
)
|
||
```
|
||
|
||
## 译: 评论
|
||
|
||
### 评论位置
|
||
|
||
译者评论必须**紧接在 `__()` 或 `_n()` 函数之前**,而不是在 `sprintf()` 之前:
|
||
|
||
```typescript
|
||
// ❌ 错误 - 评论在 sprintf 之前
|
||
/* translators: %s: 支付提供者名称 */
|
||
sprintf(
|
||
__( 'Set up %s', 'woocommerce' ),
|
||
'WooPayments'
|
||
)
|
||
|
||
// ✅ 正确 - 评论在 sprintf 内部,在 __() 之前
|
||
sprintf(
|
||
/* translators: %s: 支付提供者名称 */
|
||
__( 'Set up %s', 'woocommerce' ),
|
||
'WooPayments'
|
||
)
|
||
```
|
||
|
||
### 数字占位符的评论格式
|
||
|
||
当使用像 `%1$s` 这样的数字占位符时,在评论中只使用数字:
|
||
|
||
```typescript
|
||
// ❌ 错误 - 在评论中使用 %1$s
|
||
/* translators: %1$s: Provider name, %2$s: Country */
|
||
|
||
// ✅ 正确 - 只使用数字
|
||
/* translators: 1: Provider name, 2: Country */
|
||
```
|
||
|
||
### 描述性评论
|
||
|
||
总是为译:提供上下文:
|
||
|
||
```typescript
|
||
// ❌ 错误 - 无上下文
|
||
/* translators: %s: 名称 */
|
||
|
||
// ✅ 正确 - 清晰的上下文
|
||
/* translators: %s: 支付提供者名称 (例如,WooPayments) */
|
||
```
|
||
|
||
## 复杂字符串样板
|
||
|
||
### 结合使用 `sprintf`、`_n` 和 `createInterpolateElement`
|
||
|
||
```typescript
|
||
installText: ( extensionsString: string ) => {
|
||
const count = extensionsString.split( ', ' ).length;
|
||
return createInterpolateElement(
|
||
sprintf(
|
||
/* translators: 1: Provider name, 2: Extension names */
|
||
_n(
|
||
'正在安装 <strong>%1$s</strong> 会启用 <strong>%2$s</strong>。',
|
||
'正在安装 <strong>%1$s</strong> 会启用 <strong>%2$s</strong>。',
|
||
count,
|
||
'woocommerce'
|
||
),
|
||
'WooPayments',
|
||
extensionsString
|
||
),
|
||
{ strong: <strong /> }
|
||
);
|
||
}
|
||
```
|
||
|
||
### 带链接的字符串
|
||
|
||
```typescript
|
||
createInterpolateElement(
|
||
sprintf(
|
||
/* 译: 1: 支付提供者名称 */
|
||
__(
|
||
'了解更多关于<a>%1$s</a>的产品特点。',
|
||
'woocommerce'
|
||
),
|
||
'WooPayments'
|
||
),
|
||
{
|
||
a: (
|
||
<a
|
||
href="https://example.com"
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
/>
|
||
),
|
||
}
|
||
)
|
||
```
|
||
|
||
## 常见陷阱
|
||
|
||
### 花式撇号
|
||
|
||
TypeScript 文件可能使用花式撇号(`'` U+2019)而非直撇号(`'` U+0027)。编辑时,请保留原始字符:
|
||
|
||
```typescript
|
||
// 原文使用花式撇号 - 请保留它
|
||
__( 'I don't want to install another plugin', 'woocommerce' )
|
||
// ^ 这是 U+2019,不是 U+0027
|
||
```
|
||
|
||
### ESLint i18n 规则
|
||
|
||
`@wordpress/i18n-translator-comments` 规则要求注释直接位于翻译函数之前:
|
||
|
||
```typescript
|
||
// ❌ ESLint 错误 - 注释未紧邻 __()
|
||
const title = sprintf(
|
||
/* translators: %s: Provider name */
|
||
|
||
__( 'Set up %s', 'woocommerce' ),
|
||
'WooPayments'
|
||
);
|
||
|
||
// ✅ 正确 - 注释直接位于 __() 之前
|
||
const title = sprintf(
|
||
/* translators: %s: Provider name */
|
||
__( 'Set up %s', 'woocommerce' ),
|
||
'WooPayments'
|
||
);
|
||
```
|
||
|
||
### 品牌名称
|
||
|
||
始终为品牌名称使用占位符以提高翻译灵活性:
|
||
|
||
```typescript
|
||
// ✅ 正确 - 为品牌名称使用占位符
|
||
title: sprintf(
|
||
/* 译: %s: 支付提供者名称 */
|
||
__( '通过 %s 收款', 'woocommerce' ),
|
||
'WooPayments'
|
||
)
|
||
|
||
// ✅ 正确 - 在描述中使用占位符
|
||
description: sprintf(
|
||
/* 译: %s: 支付提供者名称 */
|
||
__( '启用 PayPal 与 %s 并存', 'woocommerce' ),
|
||
'WooPayments'
|
||
)
|
||
|
||
// ❌ 错误 - 硬编码品牌名称
|
||
title: __( '通过 WooPayments 收款', 'woocommerce' )
|
||
```
|
||
|
||
## 快速命令参考
|
||
|
||
```bash
|
||
# 检查特定文件
|
||
npx eslint client/path/to/file.tsx
|
||
|
||
# 修复特定文件
|
||
npx eslint --fix client/path/to/file.tsx
|
||
|
||
# 类型检查
|
||
pnpm run ts:check
|
||
|
||
# ❌ 从不检查整个代码库
|
||
pnpm run lint # 否 - 会检查所有内容
|
||
```
|
||
|
||
## 摘要
|
||
|
||
| 样板 | 例子 |
|
||
|-------------------------------|----------------------------------------------|
|
||
| **单个占位符** | `sprintf( __( '设置 %s' ), '名称' )` |
|
||
| **重复占位符** | `sprintf( __( '%1$s 通过 %1$s' ), '名称' )` |
|
||
| **多个占位符** | `sprintf( __( '%1$s 在 %2$s' ), 'A', 'B' )` |
|
||
| **评论格式 (简单)** | `/* 译: %s: 提供者名称 */` |
|
||
| **评论格式 (带数值)** | `/* 译: 1: 提供者, 2: 国家 */` | |