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.
Files changed (2) hide show
  1. package/README.md +151 -164
  2. package/package.json +6 -6
package/README.md CHANGED
@@ -1,97 +1,76 @@
1
1
  # react-native-i18njs
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/react-native-i18njs.svg)](https://www.npmjs.com/package/react-native-i18njs)
4
+ [![License](https://img.shields.io/npm/l/react-native-i18njs.svg)](https://www.npmjs.com/package/react-native-i18njs)
5
+ [![Platform](https://img.shields.io/badge/platform-react--native-blue.svg)](https://reactnative.dev)
6
+ [![TypeScript](https://img.shields.io/badge/types-included-blue.svg)](https://www.typescriptlang.org)
7
+
3
8
  一个轻量级、类型安全、零心智负担的 React Native 国际化解决方案。
4
9
 
5
- ## 特性
10
+ 专为 React Native 设计,集成了最佳实践,解决了常见的国际化痛点:繁琐的配置、类型缺失、复杂的 API 以及系统语言跟随问题。
6
11
 
7
- - 🚀 **开箱即用**:专为 React Native 设计,API 简单直观。
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 i18n-js react-native-localize
25
+ npm install react-native-i18njs
22
26
  # 或者
23
- yarn add react-native-i18njs i18n-js react-native-localize
27
+ yarn add react-native-i18njs
24
28
  ```
25
29
 
26
- > 插值语法说明:本库基于 `i18n-js`,默认使用 `%{name}`(不是 `{{name}}`)。
27
-
28
- ## 上手方式(两种都支持)
30
+ > **注意**:本库已内置 `i18n-js` `react-native-localize` 的稳定版本,无需手动安装 peer dependencies。
29
31
 
30
- - **方式 A:不使用 `I18nProvider`(推荐)**:任何地方都能直接 `t()`,没有 React 心智负担;适合工具层/启动阶段/非 UI 代码。
31
- - **方式 B:使用 `I18nProvider`(可选)**:需要“切换语言后 UI 自动刷新”时再用。
32
+ ## 🚀 快速开始
32
33
 
33
- ## 模块 1:初始化与全局函数(不需要 Provider)
34
+ ### 1. 定义翻译资源
34
35
 
35
- ### 1) 准备翻译资源
36
+ 建议在单独的文件中管理翻译资源,例如 `src/locales/index.ts`:
36
37
 
37
38
  ```ts
39
+ // src/locales/index.ts
38
40
  export const translations = {
39
- en: { welcome: 'Welcome', hello: 'Hello, %{name}!' },
40
- zh: { welcome: '欢迎', hello: '你好,%{name}!' },
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
- setLocale('zh');
59
- getLocale(); // 'zh'
60
- t('hello', { name: 'Trae' }); // '你好,Trae!'
61
- t('missing.key', { defaultValue: '默认文案' });
51
+ // 导出类型以获得类型提示
52
+ export type Translations = typeof translations.en;
62
53
  ```
63
54
 
64
- ### 不用 Provider 的典型场景
65
-
66
- - 网络层(如 axios 拦截器)拼接错误文案
67
- - 表单校验/工具函数/Toast 文案
68
- - Redux/Saga/Store 初始化阶段
69
- - React 组件之外(比如 navigation 配置、原生桥接回调)
70
-
71
- > 注意:不用 Provider 时,切换语言不会自动触发页面重渲染;如果你需要 UI 自动刷新,请看“模块 2”。
55
+ ### 2. 初始化
72
56
 
73
- ## 模块 2:React 自动重渲染(I18nProvider + useI18n)
57
+ 在你的 App 入口文件(如 `App.tsx`)中初始化:
74
58
 
75
59
  ```tsx
76
60
  import React from 'react';
77
- import { Text, Button } from 'react-native';
78
- import { I18nProvider, useI18n } from 'react-native-i18njs';
61
+ import { initI18n, I18nProvider } from 'react-native-i18njs';
62
+ import { translations } from './src/locales';
63
+ import Home from './src/Home';
79
64
 
80
- function Home() {
81
- const { t, locale, setLocale } = useI18n();
82
- return (
83
- <>
84
- <Text>{t('welcome')}</Text>
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
- ### `I18nProvider` 参数
81
+ ### 3. 在组件中使用
103
82
 
104
- - `readyGate?: boolean`:为 `true` 时,未 ready 前不渲染 children
105
- - `fallback?: React.ReactNode`:`readyGate` 期间显示的占位
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
- ### `useI18n<T>()`(类型安全 key)
90
+ export default function Home() {
91
+ // 传入泛型 Translations 以获得 key 的自动补全和类型检查
92
+ const { t, locale, setLocale } = useI18n<Translations>();
108
93
 
109
- ```ts
110
- type MyTranslations = typeof translations.en;
111
- const { t } = useI18n<MyTranslations>();
112
- t('welcome');
113
- t('home.title');
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
- ## 模块 3:动态加载翻译(loadTranslations)
109
+ ## 📖 核心功能详解
117
110
 
118
- ```ts
119
- import { loadTranslations } from 'react-native-i18njs';
111
+ ### 1. 非组件环境使用(Global API)
120
112
 
121
- loadTranslations({ fr: { welcome: 'Bonjour' } });
122
- ```
113
+ Redux、Axios 拦截器、工具函数等非组件环境中,你可以直接使用全局导出的 API。
123
114
 
124
- ## 模块 4:富文本(Trans)
115
+ #### 基础用法
125
116
 
126
- ```tsx
127
- import React from 'react';
128
- import { Text } from 'react-native';
129
- import { Trans } from 'react-native-i18njs';
117
+ ```ts
118
+ import { t, getLocale, setLocale } from 'react-native-i18njs';
119
+
120
+ // 获取当前语言
121
+ const current = getLocale();
130
122
 
131
- // translations.en:
132
- // { description: 'This is <bold>bold</bold> text.', agree: 'I agree to %{terms}' }
123
+ // 切换语言
124
+ setLocale('zh');
133
125
 
134
- <Trans i18nKey="description" components={{ bold: <Text style={{ fontWeight: 'bold' }} /> }} />
135
- <Trans i18nKey="agree" values={{ terms: <Text style={{ fontWeight: 'bold' }}>Terms</Text> }} />
126
+ // 直接翻译
127
+ const message = t('errors.network_timeout');
136
128
  ```
137
129
 
138
- ### `Trans` 参数
130
+ #### 进阶:监听语言变化
131
+
132
+ 如果你需要在组件外监听语言变更(例如同步更新全局状态),可以使用默认导出的实例:
139
133
 
140
- - `i18nKey: string`
141
- - `values?: Record<string, any>`:插值值(字符串/数字/ReactElement 都支持)
142
- - `components?: Record<string, React.ReactNode>`:标签映射(如 `bold/link`)
134
+ ```ts
135
+ import i18n from 'react-native-i18njs';
143
136
 
144
- ### `Trans` 能覆盖的场景
137
+ // 订阅语言变更
138
+ const unsubscribe = i18n.subscribe((locale) => {
139
+ console.log('Language changed to:', locale);
140
+ // 更新 API 默认 Header 或其他全局状态
141
+ });
145
142
 
146
- - 句子内局部加粗/斜体/变色/可点击链接(用 `components` 映射 `<bold></bold>` / `<link></link>`)
147
- - 插值里插入组件(用 `values` 传 ReactElement,例如把 “Terms” 做成可点击文本)
148
- - 嵌套标签(例如 `<bold>...<italic>...</italic>...</bold>`)
149
- - 自闭合标签(例如 `<br/>`),可映射成 `components={{ br: <Text>{'\n'}</Text> }}`
143
+ // 取消订阅
144
+ // unsubscribe();
145
+ ```
150
146
 
151
- ### `Trans` 的边界(不适合的场景)
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
- - 不支持标签属性/HTML(例如 `<a href="...">` / `<span style="...">`),需要你把“样式/点击行为”放到 `components` 或 `values` 传入的组件里
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
- ## 模块 5:Class 组件(withI18n)
162
+ 当翻译内容中包含样式或组件时,使用 `Trans` 组件:
160
163
 
161
164
  ```tsx
162
- import React from 'react';
165
+ import { Trans } from 'react-native-i18njs';
163
166
  import { Text } from 'react-native';
164
- import { withI18n } from 'react-native-i18njs';
165
167
 
166
- class Screen extends React.PureComponent<{ t: any }> {
167
- render() {
168
- return <Text>{this.props.t('welcome')}</Text>;
169
- }
170
- }
168
+ // 翻译资源:
169
+ // zh: { agreement: '我同意 <link>服务条款</link>' }
171
170
 
172
- export default withI18n(Screen);
171
+ <Trans
172
+ i18nKey="agreement"
173
+ components={{
174
+ link: <Text style={{ color: 'blue' }} onPress={openTerms} />,
175
+ }}
176
+ />
173
177
  ```
174
178
 
175
- ## 模块 6:初始化就绪(readyI18n / isI18nReady)
179
+ ### 3. 动态加载翻译
180
+
181
+ 适用于大型应用的分包加载场景:
176
182
 
177
183
  ```ts
178
- import { readyI18n, isI18nReady } from 'react-native-i18njs';
184
+ import { loadTranslations } from 'react-native-i18njs';
179
185
 
180
- await readyI18n();
181
- isI18nReady();
186
+ // 异步加载法语包
187
+ async function loadFrench() {
188
+ const fr = await import('./locales/fr');
189
+ loadTranslations({ fr: fr.default });
190
+ }
182
191
  ```
183
192
 
184
- ## 模块 7:数字/货币/日期格式化(formatNumber / formatCurrency / formatDate)
185
-
186
- `Intl` 是 JavaScript 自带的一套“国际化格式化”能力(浏览器和 Node 都有,React Native 取决于引擎/是否带完整 Intl)。
193
+ ### 4. 格式化工具
187
194
 
188
- 这三个方法基于 `Intl`,默认使用“当前 locale”来格式化,并且都支持传入 `options` 自定义格式。
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
- formatNumber(1234567.891, { maximumFractionDigits: 2 });
196
- formatCurrency(99.9, 'CNY', { currencyDisplay: 'narrowSymbol' });
197
- formatDate(Date.now(), { dateStyle: 'medium', timeStyle: 'short' });
198
- ```
199
-
200
- 通用性边界:
200
+ // 数字
201
+ formatNumber(1234.56); // "1,234.56"
201
202
 
202
- - 若运行环境没有完整 `Intl`(部分 RN/Hermes 场景),库会自动降级:
203
- - number:`String(n)`
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
- ## 配置参数(I18nOptions)
210
-
211
- ### `initI18n(translations, options?)`
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
- ## API 速查(按模块)
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
- ### 1) `Invalid hook call` / 多个 React 实例
224
+ ## 🧩 常见问题
239
225
 
240
- 如果你在 monorepo / workspace 中把本库以源码方式链接到示例或业务 App,可能出现重复 React 导致的 Hook 报错。参考本仓库示例的 Metro 配置做依赖指向与屏蔽:
226
+ ### TypeScript 类型提示不工作?
227
+ 确保你在使用 `useI18n<MyTranslations>()` 时传入了你的翻译类型定义。
241
228
 
242
- - 示例配置:[example/metro.config.js](./example/metro.config.js)
229
+ ### 安卓上语言检测不准确?
230
+ 请确保你的 `android/app/src/main/res` 目录下有对应的语言资源文件夹(如 `values-zh`),React Native 有时依赖这些原生配置来正确识别系统语言。
243
231
 
244
- ## 平台支持
232
+ ## 📄 License
245
233
 
246
- - **iOS / Android**: 完全支持。
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.1",
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",