@chipmobilesdk/rn-i18n 0.1.2 → 0.2.0
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/CHANGELOG.md +168 -57
- package/README.md +277 -218
- package/package.json +49 -49
- package/skills/sdk-rn-i18n/SKILL.md +65 -62
- package/src/config/catalog.ts +25 -0
- package/src/config/diagnostics.ts +68 -0
- package/src/config/errors.ts +28 -0
- package/src/config/types.ts +150 -152
- package/src/config/validateConfig.ts +161 -174
- package/src/format/currency.ts +34 -27
- package/src/format/date.ts +32 -26
- package/src/format/invalid.ts +22 -16
- package/src/format/number.ts +30 -25
- package/src/format/units.ts +139 -151
- package/src/i18n.ts +231 -222
- package/src/index.ts +58 -50
- package/src/locale/activeLocale.ts +42 -45
- package/src/locale/deviceLocale.ts +50 -54
- package/src/locale/keyValueStorage.ts +27 -0
- package/src/locale/persistence.ts +59 -37
- package/src/react/I18nProvider.tsx +31 -35
- package/src/react/hooks.ts +66 -61
- package/src/tooling/cli.ts +76 -78
- package/src/tooling/generateKeys.ts +62 -68
- package/src/tooling/index.ts +27 -32
- package/src/tooling/report.ts +109 -117
- package/src/tooling/runner.ts +111 -126
- package/src/tooling/scan.ts +193 -210
- package/src/tooling/sync.ts +193 -198
- package/src/tooling/types.ts +119 -119
- package/src/translation/interpolate.ts +23 -23
- package/src/translation/plurals.ts +62 -69
- package/src/translation/resolver.ts +161 -146
- package/src/translation/runtimeKeys.ts +27 -27
package/README.md
CHANGED
|
@@ -1,218 +1,277 @@
|
|
|
1
|
-
# @chipmobilesdk/rn-i18n
|
|
2
|
-
|
|
3
|
-
Reusable localization for ChipMobileSdk-family React Native apps: per-locale
|
|
4
|
-
configuration, app-owned namespaced translations, typed keys, interpolation,
|
|
5
|
-
pluralization, fallback chains, runtime locale switching with app-injected
|
|
6
|
-
persistence, device-locale detection, locale-aware number/date/currency/unit
|
|
7
|
-
formatting, and a Node CLI that extracts and synchronizes translation keys.
|
|
8
|
-
|
|
9
|
-
> **License:** `UNLICENSED`. Published publicly for use by the owner's
|
|
10
|
-
> applications; no open-source license is granted.
|
|
11
|
-
|
|
12
|
-
- **Zero runtime dependencies.** Formatting uses the `Intl` APIs built into
|
|
13
|
-
Hermes; plural rules and unit conversion are package-owned.
|
|
14
|
-
- **No native code.** Storage and (optionally) device-locale detection are
|
|
15
|
-
injected by the consuming app.
|
|
16
|
-
- **Public package.** Published under the `@chipmobilesdk` npm organization.
|
|
17
|
-
- **Version**:
|
|
18
|
-
- **Entrypoints**: `@chipmobilesdk/rn-i18n` and the Node-only
|
|
19
|
-
`@chipmobilesdk/rn-i18n/tooling`
|
|
20
|
-
|
|
21
|
-
## Installation
|
|
22
|
-
|
|
23
|
-
This package ships TypeScript sources and is consumed through the workspace or
|
|
24
|
-
the public npm registry:
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
npm install @chipmobilesdk/rn-i18n
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
Host frameworks are peer dependencies:
|
|
31
|
-
|
|
32
|
-
```jsonc
|
|
33
|
-
// peerDependencies
|
|
34
|
-
"react": ">=19",
|
|
35
|
-
"react-native": ">=0.85"
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Quick start
|
|
39
|
-
|
|
40
|
-
```ts
|
|
41
|
-
import { createI18n } from '@chipmobilesdk/rn-i18n';
|
|
42
|
-
import enUS from './locales/en-US.json';
|
|
43
|
-
import viVN from './locales/vi-VN.json';
|
|
44
|
-
import { storageAdapter } from './storage';
|
|
45
|
-
import type { AppI18nKey } from './keys.generated';
|
|
46
|
-
|
|
47
|
-
export const i18n = createI18n<AppI18nKey>({
|
|
48
|
-
primaryLocale: 'en-US',
|
|
49
|
-
locales: [
|
|
50
|
-
{
|
|
51
|
-
code: 'en-US',
|
|
52
|
-
displayName: 'English (US)',
|
|
53
|
-
direction: 'ltr',
|
|
54
|
-
fallbackChain: [],
|
|
55
|
-
defaultCurrency: 'USD',
|
|
56
|
-
defaultUnit: 'mile',
|
|
57
|
-
defaultDateFormat: { year: 'numeric', month: 'short', day: 'numeric' },
|
|
58
|
-
defaultNumberFormat: { maximumFractionDigits: 2 },
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
code: 'vi-VN',
|
|
62
|
-
displayName: 'Tiếng Việt',
|
|
63
|
-
direction: 'ltr',
|
|
64
|
-
fallbackChain: ['en-US'],
|
|
65
|
-
defaultCurrency: 'VND',
|
|
66
|
-
defaultUnit: 'kilometer',
|
|
67
|
-
defaultDateFormat: { day: '2-digit', month: '2-digit', year: 'numeric' },
|
|
68
|
-
defaultNumberFormat: { maximumFractionDigits: 2 },
|
|
69
|
-
},
|
|
70
|
-
],
|
|
71
|
-
resources: { 'en-US': enUS, 'vi-VN': viVN },
|
|
72
|
-
storage: storageAdapter,
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
await i18n.init(); // persisted -> device -> primary fallback
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
|
104
|
-
|
|
105
|
-
|
|
|
106
|
-
|
|
|
107
|
-
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
{
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
`
|
|
156
|
-
|
|
157
|
-
##
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
```jsonc
|
|
163
|
-
{
|
|
164
|
-
"
|
|
165
|
-
"
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
1
|
+
# @chipmobilesdk/rn-i18n
|
|
2
|
+
|
|
3
|
+
Reusable localization for ChipMobileSdk-family React Native apps: per-locale
|
|
4
|
+
configuration, app-owned namespaced translations, typed keys, interpolation,
|
|
5
|
+
pluralization, fallback chains, runtime locale switching with app-injected
|
|
6
|
+
persistence, device-locale detection, locale-aware number/date/currency/unit
|
|
7
|
+
formatting, and a Node CLI that extracts and synchronizes translation keys.
|
|
8
|
+
|
|
9
|
+
> **License:** `UNLICENSED`. Published publicly for use by the owner's
|
|
10
|
+
> applications; no open-source license is granted.
|
|
11
|
+
|
|
12
|
+
- **Zero runtime dependencies.** Formatting uses the `Intl` APIs built into
|
|
13
|
+
Hermes; plural rules and unit conversion are package-owned.
|
|
14
|
+
- **No native code.** Storage and (optionally) device-locale detection are
|
|
15
|
+
injected by the consuming app.
|
|
16
|
+
- **Public package.** Published under the `@chipmobilesdk` npm organization.
|
|
17
|
+
- **Version**: see [CHANGELOG](CHANGELOG.md)
|
|
18
|
+
- **Entrypoints**: `@chipmobilesdk/rn-i18n` and the Node-only
|
|
19
|
+
`@chipmobilesdk/rn-i18n/tooling`
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
This package ships TypeScript sources and is consumed through the workspace or
|
|
24
|
+
the public npm registry:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @chipmobilesdk/rn-i18n
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Host frameworks are peer dependencies:
|
|
31
|
+
|
|
32
|
+
```jsonc
|
|
33
|
+
// peerDependencies
|
|
34
|
+
"react": ">=19",
|
|
35
|
+
"react-native": ">=0.85"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick start
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { createI18n } from '@chipmobilesdk/rn-i18n';
|
|
42
|
+
import enUS from './locales/en-US.json';
|
|
43
|
+
import viVN from './locales/vi-VN.json';
|
|
44
|
+
import { storageAdapter } from './storage';
|
|
45
|
+
import type { AppI18nKey } from './keys.generated';
|
|
46
|
+
|
|
47
|
+
export const i18n = createI18n<AppI18nKey>({
|
|
48
|
+
primaryLocale: 'en-US',
|
|
49
|
+
locales: [
|
|
50
|
+
{
|
|
51
|
+
code: 'en-US',
|
|
52
|
+
displayName: 'English (US)',
|
|
53
|
+
direction: 'ltr',
|
|
54
|
+
fallbackChain: [],
|
|
55
|
+
defaultCurrency: 'USD',
|
|
56
|
+
defaultUnit: 'mile',
|
|
57
|
+
defaultDateFormat: { year: 'numeric', month: 'short', day: 'numeric' },
|
|
58
|
+
defaultNumberFormat: { maximumFractionDigits: 2 },
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
code: 'vi-VN',
|
|
62
|
+
displayName: 'Tiếng Việt',
|
|
63
|
+
direction: 'ltr',
|
|
64
|
+
fallbackChain: ['en-US'],
|
|
65
|
+
defaultCurrency: 'VND',
|
|
66
|
+
defaultUnit: 'kilometer',
|
|
67
|
+
defaultDateFormat: { day: '2-digit', month: '2-digit', year: 'numeric' },
|
|
68
|
+
defaultNumberFormat: { maximumFractionDigits: 2 },
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
resources: { 'en-US': enUS, 'vi-VN': viVN },
|
|
72
|
+
storage: storageAdapter,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
await i18n.init(); // persisted -> device -> primary fallback
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
With a storage adapter that reads synchronously (see below), resolve the locale before
|
|
79
|
+
the first render instead, so the first frame is already in the user's language:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
if (!i18n.initSync()) await i18n.init(); // initSync is false only for an async-only store
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Wrap the app and use the hooks:
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
import { I18nProvider, useTranslation, useLocale } from '@chipmobilesdk/rn-i18n';
|
|
89
|
+
|
|
90
|
+
<I18nProvider i18n={i18n}>
|
|
91
|
+
<App />
|
|
92
|
+
</I18nProvider>;
|
|
93
|
+
|
|
94
|
+
const { t } = useTranslation<AppI18nKey>();
|
|
95
|
+
const { locale, direction, supportedLocales, setLocale } = useLocale();
|
|
96
|
+
|
|
97
|
+
t('welcome.message', { name: 'Dung' });
|
|
98
|
+
t('cart.items', { count }, { count }); // plural variant by count
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Public API
|
|
102
|
+
|
|
103
|
+
| Capability | Member |
|
|
104
|
+
|------------|--------|
|
|
105
|
+
| Setup + validation | `createI18n(config)`, `I18nConfigError` (an `SdkError` from `@chipmobilesdk/rn-core`, `code: 'invalid_config'`; `UnitConversionError` has `code: 'unit_conversion'`) |
|
|
106
|
+
| Translation lookup | `i18n.t`, `useTranslation` |
|
|
107
|
+
| Locale state + switching | `i18n.getLocale`, `i18n.setLocale`, `useLocale` |
|
|
108
|
+
| Locale metadata (RTL/LTR, defaults) | `i18n.getSupportedLocales` |
|
|
109
|
+
| Device-locale init | `i18n.init`, `i18n.initSync`, `defaultDeviceLocaleProvider` |
|
|
110
|
+
| Formatting | `i18n.formatNumber/formatDate/formatCurrency/formatUnit` |
|
|
111
|
+
| Unit conversion | `i18n.convertUnit`, `convertUnit`, `getUnitSymbol`, `SUPPORTED_UNITS`, `UnitConversionError` |
|
|
112
|
+
| Runtime-only declarations | `i18n.registerRuntimeEntries` |
|
|
113
|
+
| Persistence | `LOCALE_STORAGE_KEY`, `localeStorageFromKeyValue` |
|
|
114
|
+
| React | `I18nProvider` |
|
|
115
|
+
|
|
116
|
+
Public contract types also include the configuration, locale, translation,
|
|
117
|
+
formatting, unit, provider/adapter, and hook result types exported by
|
|
118
|
+
`src/index.ts`. Do not deep-import package internals. Node-only extraction and
|
|
119
|
+
synchronization APIs must be imported from `@chipmobilesdk/rn-i18n/tooling`.
|
|
120
|
+
|
|
121
|
+
### Storage adapter (required for persistence)
|
|
122
|
+
|
|
123
|
+
The package never bundles a storage backend (FR-046/FR-047). Inject one:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
import type { LocaleStorageAdapter } from '@chipmobilesdk/rn-i18n';
|
|
127
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
128
|
+
|
|
129
|
+
export const storageAdapter: LocaleStorageAdapter = {
|
|
130
|
+
getItem: key => AsyncStorage.getItem(key),
|
|
131
|
+
setItem: (key, value) => AsyncStorage.setItem(key, value),
|
|
132
|
+
removeItem: key => AsyncStorage.removeItem(key),
|
|
133
|
+
};
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
An adapter may also have `getItemSync(key)`. With it, `i18n.initSync()` reads the
|
|
137
|
+
saved locale synchronously and the app skips the frame it would otherwise render in
|
|
138
|
+
the fallback language. MMKV through rn-core's `KeyValuePort` is one line:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
import { openDurable } from '@chipmobilesdk/rn-core';
|
|
142
|
+
import { localeStorageFromKeyValue } from '@chipmobilesdk/rn-i18n';
|
|
143
|
+
import { createMMKV } from 'react-native-mmkv';
|
|
144
|
+
|
|
145
|
+
const storage = localeStorageFromKeyValue(openDurable(() => createMMKV({ id: 'app' })));
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
If persistence is enabled without an adapter, `createI18n` throws before any localized
|
|
149
|
+
content is served.
|
|
150
|
+
|
|
151
|
+
### Device-locale provider (optional)
|
|
152
|
+
|
|
153
|
+
The default reads `Intl.DateTimeFormat().resolvedOptions().locale`. Apps that
|
|
154
|
+
need richer detection can inject `deviceLocale` (e.g. backed by
|
|
155
|
+
`react-native-localize`); that dependency stays in the app, never the package.
|
|
156
|
+
|
|
157
|
+
## Translations
|
|
158
|
+
|
|
159
|
+
App-owned JSON, organized by locale and namespace; nested dot-path keys,
|
|
160
|
+
`{{name}}` interpolation, and plural groups:
|
|
161
|
+
|
|
162
|
+
```jsonc
|
|
163
|
+
{
|
|
164
|
+
"welcome": { "message": "Welcome, {{name}}!" },
|
|
165
|
+
"cart": { "items": { "one": "{{count}} item", "other": "{{count}} items" } }
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Plural categories deliberately come from package-owned rules rather than
|
|
170
|
+
depending on `Intl.PluralRules`, which keeps behavior consistent across the
|
|
171
|
+
supported Hermes environments. Built-ins cover `en` (one/other), `vi`/`ja`
|
|
172
|
+
(other-only), and a default `count === 1 -> one` rule; supply
|
|
173
|
+
`LocaleConfig.pluralRule` for richer languages.
|
|
174
|
+
|
|
175
|
+
## Extraction & synchronization CLI
|
|
176
|
+
|
|
177
|
+
Node-only, exposed via `@chipmobilesdk/rn-i18n/tooling` and a CLI. Add an app
|
|
178
|
+
config (`i18n-sync.config.json`) and a script:
|
|
179
|
+
|
|
180
|
+
```jsonc
|
|
181
|
+
{
|
|
182
|
+
"sourceGlobs": ["src/**/*.ts", "src/**/*.tsx"],
|
|
183
|
+
"patterns": [{ "regex": "[^\\w.]t\\(\\s*'(?<key>[A-Za-z0-9_.-]+)'" }],
|
|
184
|
+
"localesDir": "src/i18n/locales",
|
|
185
|
+
"locales": ["en-US", "vi-VN", "ja-JP"],
|
|
186
|
+
"runtimeEntriesFile": "src/i18n/runtime-keys.ts",
|
|
187
|
+
"generateKeysFile": "src/i18n/keys.generated.ts",
|
|
188
|
+
"keysTypeName": "AppI18nKey"
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
```jsonc
|
|
193
|
+
// package.json
|
|
194
|
+
"scripts": {
|
|
195
|
+
"i18n:sync": "node --experimental-strip-types node_modules/@chipmobilesdk/rn-i18n/src/tooling/cli.ts --config i18n-sync.config.json"
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Running it scans the source tree, then per locale: creates missing files, adds
|
|
200
|
+
missing keys (with the placeholder policy), preserves existing translations,
|
|
201
|
+
removes stale keys, protects declared runtime-only keys, regenerates the typed
|
|
202
|
+
key contract, and prints a report. Exit codes: `0` success/warnings, `1`
|
|
203
|
+
blocking findings (conflicts, malformed locale files), `2` invalid config.
|
|
204
|
+
|
|
205
|
+
Bind `t()` to the generated `AppI18nKey` so `tsc --noEmit` reports invalid or
|
|
206
|
+
stale key usage before release.
|
|
207
|
+
|
|
208
|
+
### Runtime-only keys
|
|
209
|
+
|
|
210
|
+
Keys resolved only at runtime (declared in `runtimeEntriesFile`) use the same
|
|
211
|
+
lookup, fallback, interpolation, and missing-key behavior, and are never
|
|
212
|
+
removed by synchronization. Policy `add` writes the default text to missing
|
|
213
|
+
locales; `report` only lists them.
|
|
214
|
+
|
|
215
|
+
### SDK copy
|
|
216
|
+
|
|
217
|
+
Every `@chipmobilesdk` package with user-facing copy exports it as a catalog
|
|
218
|
+
(`key -> English default`). `runtimeEntriesFromCatalog(catalog, { policy? })`
|
|
219
|
+
turns one into runtime entries, converting `{name}` variables to `{{name}}`, so an
|
|
220
|
+
app translates SDK copy like its own:
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
createI18n({
|
|
224
|
+
...config,
|
|
225
|
+
runtimeEntries: [
|
|
226
|
+
...runtimeEntriesFromCatalog(aiMessageSet.user),
|
|
227
|
+
...runtimeEntriesFromCatalog(paymentMessages.user),
|
|
228
|
+
],
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Formatting
|
|
233
|
+
|
|
234
|
+
`formatNumber`, `formatCurrency`, `formatDate` and `formatUnit` reuse one cached
|
|
235
|
+
`Intl` formatter per locale and options. A value or option they cannot format (a
|
|
236
|
+
`NaN`, an unknown currency code) is handled by the `invalidFormat` policy: the
|
|
237
|
+
placeholder text, or a thrown error in `error` mode, and an `invalid_format`
|
|
238
|
+
diagnostic either way. It never throws from inside `Intl` during a render. A
|
|
239
|
+
locale's `defaultCurrency` must be a three-letter ISO 4217 code, checked at startup.
|
|
240
|
+
|
|
241
|
+
## Diagnostics
|
|
242
|
+
|
|
243
|
+
Missing translations and variables, fallbacks, invalid formats, persistence
|
|
244
|
+
failures and a throwing locale listener are each reported once per lookup path as an
|
|
245
|
+
`I18nDiagnostic` (`code`, `message`, and the `key` and `locale` involved):
|
|
246
|
+
|
|
247
|
+
- to `onDiagnostic` in the config, when given;
|
|
248
|
+
- otherwise to `console.warn`, in development builds only;
|
|
249
|
+
- and always to the app's SDK sinks (`installSdkSink` from `@chipmobilesdk/rn-core`),
|
|
250
|
+
as `code` with `key` and `locale`.
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
const i18n = createI18n({ ...config, onDiagnostic: d => crashlytics.log(`${d.code} ${d.key ?? ''}`) });
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Compatibility
|
|
257
|
+
|
|
258
|
+
| Surface | Baseline |
|
|
259
|
+
|---------|----------|
|
|
260
|
+
| React / React Native | `19.x` / `0.85+` (Hermes) |
|
|
261
|
+
| TypeScript | `5.8+` |
|
|
262
|
+
| Node (tooling) | `>=22.11.0` |
|
|
263
|
+
| Platforms | Android, iOS (no native code) |
|
|
264
|
+
|
|
265
|
+
## Validation
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
npm test # package + demo suites
|
|
269
|
+
npm run typecheck --workspace @chipmobilesdk/rn-i18n # source, tests and the Node CLI
|
|
270
|
+
npm run pack:check --workspace @chipmobilesdk/rn-i18n # npm pack --workspace @chipmobilesdk/rn-i18n --dry-run
|
|
271
|
+
npm run i18n:sync # extraction + synchronization
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
The demo integration lives in `src/screens/I18nDemoScreen.tsx` and uses
|
|
275
|
+
`src/i18n/` for setup, translations, persistence, and generated keys.
|
|
276
|
+
|
|
277
|
+
See [CHANGELOG.md](CHANGELOG.md) for release notes and adoption guidance.
|
package/package.json
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@chipmobilesdk/rn-i18n",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"private": false,
|
|
5
|
-
"license": "UNLICENSED",
|
|
6
|
-
"main": "src/index.ts",
|
|
7
|
-
"types": "src/index.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./src/index.ts",
|
|
11
|
-
"react-native": "./src/index.ts",
|
|
12
|
-
"default": "./src/index.ts"
|
|
13
|
-
},
|
|
14
|
-
"./tooling": {
|
|
15
|
-
"types": "./src/tooling/index.ts",
|
|
16
|
-
"node": "./src/tooling/index.ts",
|
|
17
|
-
"default": "./src/tooling/index.ts"
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
"files": [
|
|
21
|
-
"src/index.ts",
|
|
22
|
-
"src/i18n.ts",
|
|
23
|
-
"src/config",
|
|
24
|
-
"src/translation",
|
|
25
|
-
"src/locale",
|
|
26
|
-
"src/format",
|
|
27
|
-
"src/react",
|
|
28
|
-
"src/tooling",
|
|
29
|
-
"skills",
|
|
30
|
-
"README.md",
|
|
31
|
-
"CHANGELOG.md"
|
|
32
|
-
],
|
|
33
|
-
"sideEffects": false,
|
|
34
|
-
"scripts": {
|
|
35
|
-
"typecheck": "tsc -p tsconfig.json
|
|
36
|
-
"pack:
|
|
37
|
-
},
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
},
|
|
45
|
-
"publishConfig": {
|
|
46
|
-
"access": "public",
|
|
47
|
-
"registry": "https://registry.npmjs.org/"
|
|
48
|
-
}
|
|
49
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@chipmobilesdk/rn-i18n",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"types": "src/index.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./src/index.ts",
|
|
11
|
+
"react-native": "./src/index.ts",
|
|
12
|
+
"default": "./src/index.ts"
|
|
13
|
+
},
|
|
14
|
+
"./tooling": {
|
|
15
|
+
"types": "./src/tooling/index.ts",
|
|
16
|
+
"node": "./src/tooling/index.ts",
|
|
17
|
+
"default": "./src/tooling/index.ts"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"src/index.ts",
|
|
22
|
+
"src/i18n.ts",
|
|
23
|
+
"src/config",
|
|
24
|
+
"src/translation",
|
|
25
|
+
"src/locale",
|
|
26
|
+
"src/format",
|
|
27
|
+
"src/react",
|
|
28
|
+
"src/tooling",
|
|
29
|
+
"skills",
|
|
30
|
+
"README.md",
|
|
31
|
+
"CHANGELOG.md"
|
|
32
|
+
],
|
|
33
|
+
"sideEffects": false,
|
|
34
|
+
"scripts": {
|
|
35
|
+
"typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.test.json && tsc -p tsconfig.tooling.json",
|
|
36
|
+
"pack:check": "npm pack --dry-run"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@chipmobilesdk/rn-core": "^0.1.0"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"react": ">=19",
|
|
43
|
+
"react-native": ">=0.85"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public",
|
|
47
|
+
"registry": "https://registry.npmjs.org/"
|
|
48
|
+
}
|
|
49
|
+
}
|