react-native-i18njs 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +151 -164
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,97 +1,76 @@
|
|
|
1
1
|
# react-native-i18njs
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/react-native-i18njs)
|
|
4
|
+
[](https://www.npmjs.com/package/react-native-i18njs)
|
|
5
|
+
[](https://reactnative.dev)
|
|
6
|
+
[](https://www.typescriptlang.org)
|
|
7
|
+
|
|
3
8
|
一个轻量级、类型安全、零心智负担的 React Native 国际化解决方案。
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
专为 React Native 设计,集成了最佳实践,解决了常见的国际化痛点:繁琐的配置、类型缺失、复杂的 API 以及系统语言跟随问题。
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
- 📦 **自动语言检测**:基于 `react-native-localize` 自动匹配设备语言。
|
|
9
|
-
- 🔄 **自动重渲染**:语言切换时,UI 自动更新。
|
|
10
|
-
- 🛡️ **TypeScript 支持**:完全使用 TypeScript 编写,提供完整的类型提示。
|
|
11
|
-
- 🧩 **基于 `i18n-js`**:轻量封装,默认使用 `i18n-js` 作为翻译引擎。
|
|
12
|
-
- 🔌 **Hooks 支持**:提供 `useI18n` Hook,方便在组件中使用。
|
|
13
|
-
- 📝 **富文本支持**:支持在翻译中嵌入组件。
|
|
14
|
-
- 🔢 **格式化支持**:基于 JS 内置 `Intl` 的数字/货币/日期格式化(可传 options 自定义;环境不支持会自动降级)。
|
|
12
|
+
## ✨ 特性
|
|
15
13
|
|
|
16
|
-
|
|
14
|
+
- � **零配置启动**:内置智能默认值,安装即用。
|
|
15
|
+
- 🛡️ **极致类型安全**:完全 TypeScript 编写,提供从 Key 到插值参数的完整类型推导。
|
|
16
|
+
- 📱 **自动跟随系统**:基于 `react-native-localize`,自动检测并响应设备语言变更。
|
|
17
|
+
- ⚡ **高性能**:基于 `i18n-js` 核心,轻量高效,无多余运行时开销。
|
|
18
|
+
- 🔌 **灵活 API**:同时支持 Hook (`useI18n`)、高阶组件 (`withTranslation`) 和全局函数 (`t`)。
|
|
19
|
+
- 📝 **富文本支持**:`Trans` 组件轻松处理嵌套样式和组件插值。
|
|
20
|
+
- 🌍 **格式化内置**:开箱即用的数字、货币、日期格式化支持。
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
## 📦 安装
|
|
19
23
|
|
|
20
24
|
```bash
|
|
21
|
-
npm install react-native-i18njs
|
|
25
|
+
npm install react-native-i18njs
|
|
22
26
|
# 或者
|
|
23
|
-
yarn add react-native-i18njs
|
|
27
|
+
yarn add react-native-i18njs
|
|
24
28
|
```
|
|
25
29
|
|
|
26
|
-
>
|
|
27
|
-
|
|
28
|
-
## 上手方式(两种都支持)
|
|
30
|
+
> **注意**:本库已内置 `i18n-js` 和 `react-native-localize` 的稳定版本,无需手动安装 peer dependencies。
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
- **方式 B:使用 `I18nProvider`(可选)**:需要“切换语言后 UI 自动刷新”时再用。
|
|
32
|
+
## 🚀 快速开始
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
### 1. 定义翻译资源
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
建议在单独的文件中管理翻译资源,例如 `src/locales/index.ts`:
|
|
36
37
|
|
|
37
38
|
```ts
|
|
39
|
+
// src/locales/index.ts
|
|
38
40
|
export const translations = {
|
|
39
|
-
en: {
|
|
40
|
-
|
|
41
|
+
en: {
|
|
42
|
+
welcome: 'Welcome',
|
|
43
|
+
hello: 'Hello, %{name}!',
|
|
44
|
+
},
|
|
45
|
+
zh: {
|
|
46
|
+
welcome: '欢迎',
|
|
47
|
+
hello: '你好,%{name}!',
|
|
48
|
+
},
|
|
41
49
|
};
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### 2) 初始化(只做一次)
|
|
45
|
-
|
|
46
|
-
```ts
|
|
47
|
-
import { initI18n } from 'react-native-i18njs';
|
|
48
|
-
import { translations } from './locales';
|
|
49
|
-
|
|
50
|
-
initI18n(translations, { defaultLocale: 'en', enableFallback: true });
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### 3) 在任何地方直接使用 `t()`
|
|
54
|
-
|
|
55
|
-
```ts
|
|
56
|
-
import { t, setLocale, getLocale } from 'react-native-i18njs';
|
|
57
50
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
t('hello', { name: 'Trae' }); // '你好,Trae!'
|
|
61
|
-
t('missing.key', { defaultValue: '默认文案' });
|
|
51
|
+
// 导出类型以获得类型提示
|
|
52
|
+
export type Translations = typeof translations.en;
|
|
62
53
|
```
|
|
63
54
|
|
|
64
|
-
###
|
|
65
|
-
|
|
66
|
-
- 网络层(如 axios 拦截器)拼接错误文案
|
|
67
|
-
- 表单校验/工具函数/Toast 文案
|
|
68
|
-
- Redux/Saga/Store 初始化阶段
|
|
69
|
-
- React 组件之外(比如 navigation 配置、原生桥接回调)
|
|
70
|
-
|
|
71
|
-
> 注意:不用 Provider 时,切换语言不会自动触发页面重渲染;如果你需要 UI 自动刷新,请看“模块 2”。
|
|
55
|
+
### 2. 初始化
|
|
72
56
|
|
|
73
|
-
|
|
57
|
+
在你的 App 入口文件(如 `App.tsx`)中初始化:
|
|
74
58
|
|
|
75
59
|
```tsx
|
|
76
60
|
import React from 'react';
|
|
77
|
-
import {
|
|
78
|
-
import {
|
|
61
|
+
import { initI18n, I18nProvider } from 'react-native-i18njs';
|
|
62
|
+
import { translations } from './src/locales';
|
|
63
|
+
import Home from './src/Home';
|
|
79
64
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
<Text>{t('hello', { name: 'User' })}</Text>
|
|
86
|
-
<Text>{locale}</Text>
|
|
87
|
-
<Button title="EN" onPress={() => setLocale('en')} />
|
|
88
|
-
<Button title="中文" onPress={() => setLocale('zh')} />
|
|
89
|
-
</>
|
|
90
|
-
);
|
|
91
|
-
}
|
|
65
|
+
// 初始化配置
|
|
66
|
+
initI18n(translations, {
|
|
67
|
+
defaultLocale: 'en',
|
|
68
|
+
enableFallback: true, // 找不到翻译时回退到默认语言
|
|
69
|
+
});
|
|
92
70
|
|
|
93
71
|
export default function App() {
|
|
94
72
|
return (
|
|
73
|
+
// 使用 Provider 以支持语言切换时的自动重渲染
|
|
95
74
|
<I18nProvider>
|
|
96
75
|
<Home />
|
|
97
76
|
</I18nProvider>
|
|
@@ -99,149 +78,157 @@ export default function App() {
|
|
|
99
78
|
}
|
|
100
79
|
```
|
|
101
80
|
|
|
102
|
-
###
|
|
81
|
+
### 3. 在组件中使用
|
|
103
82
|
|
|
104
|
-
|
|
105
|
-
|
|
83
|
+
```tsx
|
|
84
|
+
// src/Home.tsx
|
|
85
|
+
import React from 'react';
|
|
86
|
+
import { Text, Button, View } from 'react-native';
|
|
87
|
+
import { useI18n } from 'react-native-i18njs';
|
|
88
|
+
import { Translations } from './locales';
|
|
106
89
|
|
|
107
|
-
|
|
90
|
+
export default function Home() {
|
|
91
|
+
// 传入泛型 Translations 以获得 key 的自动补全和类型检查
|
|
92
|
+
const { t, locale, setLocale } = useI18n<Translations>();
|
|
108
93
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
t('
|
|
94
|
+
return (
|
|
95
|
+
<View>
|
|
96
|
+
<Text>{t('welcome')}</Text>
|
|
97
|
+
{/* 这里的 name 参数会有类型提示 */}
|
|
98
|
+
<Text>{t('hello', { name: 'Trae' })}</Text>
|
|
99
|
+
|
|
100
|
+
<Text>当前语言: {locale}</Text>
|
|
101
|
+
|
|
102
|
+
<Button title="Switch to English" onPress={() => setLocale('en')} />
|
|
103
|
+
<Button title="切换到中文" onPress={() => setLocale('zh')} />
|
|
104
|
+
</View>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
114
107
|
```
|
|
115
108
|
|
|
116
|
-
##
|
|
109
|
+
## 📖 核心功能详解
|
|
117
110
|
|
|
118
|
-
|
|
119
|
-
import { loadTranslations } from 'react-native-i18njs';
|
|
111
|
+
### 1. 非组件环境使用(Global API)
|
|
120
112
|
|
|
121
|
-
|
|
122
|
-
```
|
|
113
|
+
在 Redux、Axios 拦截器、工具函数等非组件环境中,你可以直接使用全局导出的 API。
|
|
123
114
|
|
|
124
|
-
|
|
115
|
+
#### 基础用法
|
|
125
116
|
|
|
126
|
-
```
|
|
127
|
-
import
|
|
128
|
-
|
|
129
|
-
|
|
117
|
+
```ts
|
|
118
|
+
import { t, getLocale, setLocale } from 'react-native-i18njs';
|
|
119
|
+
|
|
120
|
+
// 获取当前语言
|
|
121
|
+
const current = getLocale();
|
|
130
122
|
|
|
131
|
-
//
|
|
132
|
-
|
|
123
|
+
// 切换语言
|
|
124
|
+
setLocale('zh');
|
|
133
125
|
|
|
134
|
-
|
|
135
|
-
|
|
126
|
+
// 直接翻译
|
|
127
|
+
const message = t('errors.network_timeout');
|
|
136
128
|
```
|
|
137
129
|
|
|
138
|
-
|
|
130
|
+
#### 进阶:监听语言变化
|
|
131
|
+
|
|
132
|
+
如果你需要在组件外监听语言变更(例如同步更新全局状态),可以使用默认导出的实例:
|
|
139
133
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
- `components?: Record<string, React.ReactNode>`:标签映射(如 `bold/link`)
|
|
134
|
+
```ts
|
|
135
|
+
import i18n from 'react-native-i18njs';
|
|
143
136
|
|
|
144
|
-
|
|
137
|
+
// 订阅语言变更
|
|
138
|
+
const unsubscribe = i18n.subscribe((locale) => {
|
|
139
|
+
console.log('Language changed to:', locale);
|
|
140
|
+
// 更新 API 默认 Header 或其他全局状态
|
|
141
|
+
});
|
|
145
142
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
- 自闭合标签(例如 `<br/>`),可映射成 `components={{ br: <Text>{'\n'}</Text> }}`
|
|
143
|
+
// 取消订阅
|
|
144
|
+
// unsubscribe();
|
|
145
|
+
```
|
|
150
146
|
|
|
151
|
-
|
|
147
|
+
#### 实战示例:Axios 拦截器
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import axios from 'axios';
|
|
151
|
+
import { getLocale } from 'react-native-i18njs';
|
|
152
|
+
|
|
153
|
+
axios.interceptors.request.use((config) => {
|
|
154
|
+
// 动态获取当前语言,确保每次请求都携带最新的语言标识
|
|
155
|
+
config.headers['Accept-Language'] = getLocale();
|
|
156
|
+
return config;
|
|
157
|
+
});
|
|
158
|
+
```
|
|
152
159
|
|
|
153
|
-
|
|
154
|
-
- `components` 目前接收的是“元素”(例如 `<Text />`),不是组件函数(例如 `Text` / `Bold`),因为内部是 `cloneElement`
|
|
155
|
-
- `Trans` 总是返回一个外层 `<Text>`;并且被插入的组件也必须是 `Text` 可渲染的内容(不要把 `<View>` 直接塞进文本里)
|
|
156
|
-
- 不提供 ICU MessageFormat(select/plural/gender)语法;更复杂的语言结构建议拆 key + 用 JS 逻辑组合,或引入 ICU 方案
|
|
157
|
-
- 翻译里如果出现不成对/不匹配的标签,会按纯文本保留(避免静默吞字),建议在翻译侧保持标签成对
|
|
160
|
+
### 2. 富文本翻译 (`Trans` 组件)
|
|
158
161
|
|
|
159
|
-
|
|
162
|
+
当翻译内容中包含样式或组件时,使用 `Trans` 组件:
|
|
160
163
|
|
|
161
164
|
```tsx
|
|
162
|
-
import
|
|
165
|
+
import { Trans } from 'react-native-i18njs';
|
|
163
166
|
import { Text } from 'react-native';
|
|
164
|
-
import { withI18n } from 'react-native-i18njs';
|
|
165
167
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
return <Text>{this.props.t('welcome')}</Text>;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
168
|
+
// 翻译资源:
|
|
169
|
+
// zh: { agreement: '我同意 <link>服务条款</link>' }
|
|
171
170
|
|
|
172
|
-
|
|
171
|
+
<Trans
|
|
172
|
+
i18nKey="agreement"
|
|
173
|
+
components={{
|
|
174
|
+
link: <Text style={{ color: 'blue' }} onPress={openTerms} />,
|
|
175
|
+
}}
|
|
176
|
+
/>
|
|
173
177
|
```
|
|
174
178
|
|
|
175
|
-
|
|
179
|
+
### 3. 动态加载翻译
|
|
180
|
+
|
|
181
|
+
适用于大型应用的分包加载场景:
|
|
176
182
|
|
|
177
183
|
```ts
|
|
178
|
-
import {
|
|
184
|
+
import { loadTranslations } from 'react-native-i18njs';
|
|
179
185
|
|
|
180
|
-
|
|
181
|
-
|
|
186
|
+
// 异步加载法语包
|
|
187
|
+
async function loadFrench() {
|
|
188
|
+
const fr = await import('./locales/fr');
|
|
189
|
+
loadTranslations({ fr: fr.default });
|
|
190
|
+
}
|
|
182
191
|
```
|
|
183
192
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
`Intl` 是 JavaScript 自带的一套“国际化格式化”能力(浏览器和 Node 都有,React Native 取决于引擎/是否带完整 Intl)。
|
|
193
|
+
### 4. 格式化工具
|
|
187
194
|
|
|
188
|
-
|
|
195
|
+
利用 `Intl` 标准进行格式化:
|
|
189
196
|
|
|
190
197
|
```ts
|
|
191
|
-
import { useI18n } from 'react-native-i18njs';
|
|
192
|
-
|
|
193
198
|
const { formatNumber, formatCurrency, formatDate } = useI18n();
|
|
194
199
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
formatDate(Date.now(), { dateStyle: 'medium', timeStyle: 'short' });
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
通用性边界:
|
|
200
|
+
// 数字
|
|
201
|
+
formatNumber(1234.56); // "1,234.56"
|
|
201
202
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
- currency:``${n} ${currency}``
|
|
205
|
-
- date:`toISOString()`
|
|
206
|
-
- 如果你希望所有平台表现完全一致,请在你的 App 侧引入 `Intl` polyfill。
|
|
207
|
-
- 如果你想用“非当前 locale”做格式化,请直接使用 `new Intl.NumberFormat('xx', options)` 自行处理。
|
|
203
|
+
// 货币
|
|
204
|
+
formatCurrency(99.99, 'USD'); // "$99.99"
|
|
208
205
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
- `translations: Record<string, any>`:翻译资源(locale 为 key)
|
|
214
|
-
- `options?`:
|
|
215
|
-
- `defaultLocale?: string`:默认 `'en'`
|
|
216
|
-
- `enableFallback?: boolean`:默认 `true`
|
|
217
|
-
- `followSystem?: boolean`:默认 `true`;用户手动 `setLocale` 后不再跟随系统
|
|
218
|
-
- `fallbackLocales?: string[] | ((locale: string) => string[])`:自定义回退链(按顺序)
|
|
219
|
-
- `missingBehavior?: 'key' | 'empty' | 'throw'`:默认 `'key'`
|
|
220
|
-
- `onMissingKey?: (key: string, locale: string) => void`:缺失 key 回调
|
|
221
|
-
- `onLocaleChange?: (locale: string) => void`:语言变更回调
|
|
206
|
+
// 日期
|
|
207
|
+
formatDate(new Date(), { dateStyle: 'full' }); // "Tuesday, October 10, 2023"
|
|
208
|
+
```
|
|
222
209
|
|
|
223
|
-
##
|
|
210
|
+
## ⚙️ 配置选项 (I18nOptions)
|
|
224
211
|
|
|
225
|
-
|
|
226
|
-
- `initI18n`, `t`, `setLocale`, `getLocale`, `loadTranslations`, `readyI18n`, `isI18nReady`
|
|
227
|
-
- **React 绑定(可选)**
|
|
228
|
-
- `I18nProvider`, `useI18n`, `I18nContext`, `withI18n`
|
|
229
|
-
- **组件**
|
|
230
|
-
- `Trans`
|
|
231
|
-
- **类型**
|
|
232
|
-
- `I18nOptions`, `Translations`, `Path`
|
|
233
|
-
- **默认导出**
|
|
234
|
-
- `i18nService`:服务单例(`formatNumber/formatCurrency/formatDate/subscribe/updateLocale` 等)
|
|
212
|
+
`initI18n` 接受的第二个参数对象:
|
|
235
213
|
|
|
236
|
-
|
|
214
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
215
|
+
|------|------|--------|------|
|
|
216
|
+
| `defaultLocale` | `string` | `'en'` | 默认语言 |
|
|
217
|
+
| `enableFallback` | `boolean` | `true` | 是否启用回退机制 |
|
|
218
|
+
| `followSystem` | `boolean` | `true` | 是否初始化时自动跟随系统语言 |
|
|
219
|
+
| `fallbackLocales` | `string[]` \| `func` | - | 自定义回退链 |
|
|
220
|
+
| `missingBehavior` | `'key'` \| `'empty'` \| `'throw'` | `'key'` | 缺失翻译时的行为 |
|
|
221
|
+
| `onMissingKey` | `function` | - | 缺失 key 的回调 |
|
|
222
|
+
| `onLocaleChange` | `function` | - | 语言变更回调 |
|
|
237
223
|
|
|
238
|
-
|
|
224
|
+
## 🧩 常见问题
|
|
239
225
|
|
|
240
|
-
|
|
226
|
+
### TypeScript 类型提示不工作?
|
|
227
|
+
确保你在使用 `useI18n<MyTranslations>()` 时传入了你的翻译类型定义。
|
|
241
228
|
|
|
242
|
-
|
|
229
|
+
### 安卓上语言检测不准确?
|
|
230
|
+
请确保你的 `android/app/src/main/res` 目录下有对应的语言资源文件夹(如 `values-zh`),React Native 有时依赖这些原生配置来正确识别系统语言。
|
|
243
231
|
|
|
244
|
-
##
|
|
232
|
+
## 📄 License
|
|
245
233
|
|
|
246
|
-
|
|
247
|
-
- **Web**: 需要配置 `webpack` 以支持 `react-native-localize`。请参考 [react-native-localize 文档](https://github.com/zoontek/react-native-localize)。
|
|
234
|
+
ISC
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-i18njs",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "一个轻量级、类型安全、零心智负担的 React Native 国际化解决方案。",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -32,11 +32,13 @@
|
|
|
32
32
|
"build": "tsup",
|
|
33
33
|
"prepack": "npm run build"
|
|
34
34
|
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"i18n-js": "^4.5.1",
|
|
37
|
+
"react-native-localize": "^3.6.1"
|
|
38
|
+
},
|
|
35
39
|
"peerDependencies": {
|
|
36
|
-
"i18n-js": ">=4 <5",
|
|
37
40
|
"react": ">=16.8",
|
|
38
|
-
"react-native": ">=0.64"
|
|
39
|
-
"react-native-localize": ">=3 <4"
|
|
41
|
+
"react-native": ">=0.64"
|
|
40
42
|
},
|
|
41
43
|
"devDependencies": {
|
|
42
44
|
"@types/jest": "^30.0.0",
|
|
@@ -44,11 +46,9 @@
|
|
|
44
46
|
"@types/react-native": "^0.72.8",
|
|
45
47
|
"@types/react-test-renderer": "^19.1.0",
|
|
46
48
|
"esbuild": "^0.25.0",
|
|
47
|
-
"i18n-js": "^4.5.1",
|
|
48
49
|
"jest": "^30.2.0",
|
|
49
50
|
"react": "^19.2.3",
|
|
50
51
|
"react-native": "^0.83.1",
|
|
51
|
-
"react-native-localize": "^3.6.1",
|
|
52
52
|
"react-native-web": "^0.20.0",
|
|
53
53
|
"react-test-renderer": "^19.2.3",
|
|
54
54
|
"ts-jest": "^29.4.6",
|