@chipmobilesdk/rn-i18n 0.2.0 → 0.4.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 +173 -0
- package/README.md +48 -17
- package/lib/commonjs/config/catalog.js +29 -0
- package/lib/commonjs/config/catalog.js.map +1 -0
- package/lib/commonjs/config/diagnostics.js +56 -0
- package/lib/commonjs/config/diagnostics.js.map +1 -0
- package/lib/commonjs/config/errors.js +35 -0
- package/lib/commonjs/config/errors.js.map +1 -0
- package/lib/commonjs/config/types.js +13 -0
- package/lib/commonjs/config/types.js.map +1 -0
- package/lib/commonjs/config/validateConfig.js +134 -0
- package/lib/commonjs/config/validateConfig.js.map +1 -0
- package/lib/commonjs/format/currency.js +35 -0
- package/lib/commonjs/format/currency.js.map +1 -0
- package/lib/commonjs/format/date.js +36 -0
- package/lib/commonjs/format/date.js.map +1 -0
- package/lib/commonjs/format/invalid.js +24 -0
- package/lib/commonjs/format/invalid.js.map +1 -0
- package/lib/commonjs/format/number.js +29 -0
- package/lib/commonjs/format/number.js.map +1 -0
- package/lib/commonjs/format/units.js +184 -0
- package/lib/commonjs/format/units.js.map +1 -0
- package/lib/commonjs/i18n.js +234 -0
- package/lib/commonjs/i18n.js.map +1 -0
- package/lib/commonjs/index.js +93 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/locale/activeLocale.js +37 -0
- package/lib/commonjs/locale/activeLocale.js.map +1 -0
- package/lib/commonjs/locale/deviceLocale.js +52 -0
- package/lib/commonjs/locale/deviceLocale.js.map +1 -0
- package/lib/commonjs/locale/keyValueStorage.js +29 -0
- package/lib/commonjs/locale/keyValueStorage.js.map +1 -0
- package/lib/commonjs/locale/persistence.js +74 -0
- package/lib/commonjs/locale/persistence.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/react/I18nProvider.js +36 -0
- package/lib/commonjs/react/I18nProvider.js.map +1 -0
- package/lib/commonjs/react/hooks.js +47 -0
- package/lib/commonjs/react/hooks.js.map +1 -0
- package/lib/commonjs/translation/interpolate.js +24 -0
- package/lib/commonjs/translation/interpolate.js.map +1 -0
- package/lib/commonjs/translation/plurals.js +122 -0
- package/lib/commonjs/translation/plurals.js.map +1 -0
- package/lib/commonjs/translation/resolver.js +124 -0
- package/lib/commonjs/translation/resolver.js.map +1 -0
- package/lib/commonjs/translation/runtimeKeys.js +55 -0
- package/lib/commonjs/translation/runtimeKeys.js.map +1 -0
- package/lib/module/config/catalog.js +25 -0
- package/lib/module/config/catalog.js.map +1 -0
- package/lib/module/config/diagnostics.js +51 -0
- package/lib/module/config/diagnostics.js.map +1 -0
- package/lib/module/config/errors.js +30 -0
- package/lib/module/config/errors.js.map +1 -0
- package/lib/module/config/types.js +26 -0
- package/lib/module/config/types.js.map +1 -0
- package/lib/module/config/validateConfig.js +131 -0
- package/lib/module/config/validateConfig.js.map +1 -0
- package/lib/module/format/currency.js +32 -0
- package/lib/module/format/currency.js.map +1 -0
- package/lib/module/format/date.js +33 -0
- package/lib/module/format/date.js.map +1 -0
- package/lib/module/format/invalid.js +21 -0
- package/lib/module/format/invalid.js.map +1 -0
- package/lib/module/format/number.js +26 -0
- package/lib/module/format/number.js.map +1 -0
- package/lib/module/format/units.js +171 -0
- package/lib/module/format/units.js.map +1 -0
- package/lib/module/i18n.js +230 -0
- package/lib/module/i18n.js.map +1 -0
- package/lib/module/index.js +23 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/locale/activeLocale.js +33 -0
- package/lib/module/locale/activeLocale.js.map +1 -0
- package/lib/module/locale/deviceLocale.js +46 -0
- package/lib/module/locale/deviceLocale.js.map +1 -0
- package/lib/module/locale/keyValueStorage.js +25 -0
- package/lib/module/locale/keyValueStorage.js.map +1 -0
- package/lib/module/locale/persistence.js +66 -0
- package/lib/module/locale/persistence.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/react/I18nProvider.js +29 -0
- package/lib/module/react/I18nProvider.js.map +1 -0
- package/lib/module/react/hooks.js +42 -0
- package/lib/module/react/hooks.js.map +1 -0
- package/lib/module/translation/interpolate.js +20 -0
- package/lib/module/translation/interpolate.js.map +1 -0
- package/lib/module/translation/plurals.js +114 -0
- package/lib/module/translation/plurals.js.map +1 -0
- package/lib/module/translation/resolver.js +118 -0
- package/lib/module/translation/resolver.js.map +1 -0
- package/lib/module/translation/runtimeKeys.js +50 -0
- package/lib/module/translation/runtimeKeys.js.map +1 -0
- package/lib/typescript/commonjs/config/catalog.d.ts +18 -0
- package/lib/typescript/commonjs/config/catalog.d.ts.map +1 -0
- package/lib/typescript/commonjs/config/diagnostics.d.ts +21 -0
- package/lib/typescript/commonjs/config/diagnostics.d.ts.map +1 -0
- package/lib/typescript/commonjs/config/errors.d.ts +16 -0
- package/lib/typescript/commonjs/config/errors.d.ts.map +1 -0
- package/lib/typescript/commonjs/config/types.d.ts +127 -0
- package/lib/typescript/commonjs/config/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/config/validateConfig.d.ts +8 -0
- package/lib/typescript/commonjs/config/validateConfig.d.ts.map +1 -0
- package/lib/typescript/commonjs/format/currency.d.ts +12 -0
- package/lib/typescript/commonjs/format/currency.d.ts.map +1 -0
- package/lib/typescript/commonjs/format/date.d.ts +14 -0
- package/lib/typescript/commonjs/format/date.d.ts.map +1 -0
- package/lib/typescript/commonjs/format/invalid.d.ts +12 -0
- package/lib/typescript/commonjs/format/invalid.d.ts.map +1 -0
- package/lib/typescript/commonjs/format/number.d.ts +8 -0
- package/lib/typescript/commonjs/format/number.d.ts.map +1 -0
- package/lib/typescript/commonjs/format/units.d.ts +23 -0
- package/lib/typescript/commonjs/format/units.d.ts.map +1 -0
- package/lib/typescript/commonjs/i18n.d.ts +46 -0
- package/lib/typescript/commonjs/i18n.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +27 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/locale/activeLocale.d.ts +17 -0
- package/lib/typescript/commonjs/locale/activeLocale.d.ts.map +1 -0
- package/lib/typescript/commonjs/locale/deviceLocale.d.ts +19 -0
- package/lib/typescript/commonjs/locale/deviceLocale.d.ts.map +1 -0
- package/lib/typescript/commonjs/locale/keyValueStorage.d.ts +13 -0
- package/lib/typescript/commonjs/locale/keyValueStorage.d.ts.map +1 -0
- package/lib/typescript/commonjs/locale/persistence.d.ts +24 -0
- package/lib/typescript/commonjs/locale/persistence.d.ts.map +1 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/commonjs/react/I18nProvider.d.ts +16 -0
- package/lib/typescript/commonjs/react/I18nProvider.d.ts.map +1 -0
- package/lib/typescript/commonjs/react/hooks.d.ts +21 -0
- package/lib/typescript/commonjs/react/hooks.d.ts.map +1 -0
- package/lib/typescript/commonjs/translation/interpolate.d.ts +8 -0
- package/lib/typescript/commonjs/translation/interpolate.d.ts.map +1 -0
- package/lib/typescript/commonjs/translation/plurals.d.ts +13 -0
- package/lib/typescript/commonjs/translation/plurals.d.ts.map +1 -0
- package/lib/typescript/commonjs/translation/resolver.d.ts +24 -0
- package/lib/typescript/commonjs/translation/resolver.d.ts.map +1 -0
- package/lib/typescript/commonjs/translation/runtimeKeys.d.ts +16 -0
- package/lib/typescript/commonjs/translation/runtimeKeys.d.ts.map +1 -0
- package/lib/typescript/module/config/catalog.d.ts +18 -0
- package/lib/typescript/module/config/catalog.d.ts.map +1 -0
- package/lib/typescript/module/config/diagnostics.d.ts +21 -0
- package/lib/typescript/module/config/diagnostics.d.ts.map +1 -0
- package/lib/typescript/module/config/errors.d.ts +16 -0
- package/lib/typescript/module/config/errors.d.ts.map +1 -0
- package/lib/typescript/module/config/types.d.ts +127 -0
- package/lib/typescript/module/config/types.d.ts.map +1 -0
- package/lib/typescript/module/config/validateConfig.d.ts +8 -0
- package/lib/typescript/module/config/validateConfig.d.ts.map +1 -0
- package/lib/typescript/module/format/currency.d.ts +12 -0
- package/lib/typescript/module/format/currency.d.ts.map +1 -0
- package/lib/typescript/module/format/date.d.ts +14 -0
- package/lib/typescript/module/format/date.d.ts.map +1 -0
- package/lib/typescript/module/format/invalid.d.ts +12 -0
- package/lib/typescript/module/format/invalid.d.ts.map +1 -0
- package/lib/typescript/module/format/number.d.ts +8 -0
- package/lib/typescript/module/format/number.d.ts.map +1 -0
- package/lib/typescript/module/format/units.d.ts +23 -0
- package/lib/typescript/module/format/units.d.ts.map +1 -0
- package/lib/typescript/module/i18n.d.ts +46 -0
- package/lib/typescript/module/i18n.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +27 -0
- package/lib/typescript/module/index.d.ts.map +1 -0
- package/lib/typescript/module/locale/activeLocale.d.ts +17 -0
- package/lib/typescript/module/locale/activeLocale.d.ts.map +1 -0
- package/lib/typescript/module/locale/deviceLocale.d.ts +19 -0
- package/lib/typescript/module/locale/deviceLocale.d.ts.map +1 -0
- package/lib/typescript/module/locale/keyValueStorage.d.ts +13 -0
- package/lib/typescript/module/locale/keyValueStorage.d.ts.map +1 -0
- package/lib/typescript/module/locale/persistence.d.ts +24 -0
- package/lib/typescript/module/locale/persistence.d.ts.map +1 -0
- package/lib/typescript/module/package.json +1 -0
- package/lib/typescript/module/react/I18nProvider.d.ts +16 -0
- package/lib/typescript/module/react/I18nProvider.d.ts.map +1 -0
- package/lib/typescript/module/react/hooks.d.ts +21 -0
- package/lib/typescript/module/react/hooks.d.ts.map +1 -0
- package/lib/typescript/module/translation/interpolate.d.ts +8 -0
- package/lib/typescript/module/translation/interpolate.d.ts.map +1 -0
- package/lib/typescript/module/translation/plurals.d.ts +13 -0
- package/lib/typescript/module/translation/plurals.d.ts.map +1 -0
- package/lib/typescript/module/translation/resolver.d.ts +24 -0
- package/lib/typescript/module/translation/resolver.d.ts.map +1 -0
- package/lib/typescript/module/translation/runtimeKeys.d.ts +16 -0
- package/lib/typescript/module/translation/runtimeKeys.d.ts.map +1 -0
- package/package.json +41 -18
- package/skills/sdk-rn-i18n/SKILL.md +27 -0
- package/src/config/diagnostics.ts +2 -0
- package/src/format/currency.ts +5 -0
- package/src/format/date.ts +10 -4
- package/src/i18n.ts +93 -9
- package/src/index.ts +5 -3
- package/src/locale/activeLocale.ts +10 -0
- package/src/locale/persistence.ts +19 -0
- package/src/translation/plurals.ts +74 -5
- package/src/translation/runtimeKeys.ts +28 -1
- package/src/tooling/cli.ts +0 -76
- package/src/tooling/generateKeys.ts +0 -62
- package/src/tooling/index.ts +0 -27
- package/src/tooling/report.ts +0 -109
- package/src/tooling/runner.ts +0 -111
- package/src/tooling/scan.ts +0 -193
- package/src/tooling/sync.ts +0 -193
- package/src/tooling/types.ts +0 -119
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,178 @@
|
|
|
1
1
|
# @chipmobilesdk/rn-i18n
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Every peer range has an upper bound, and no package declares a peer it does not use. ([#21](https://github.com/operationd8979/chip-mobile-sdk/pull/21))
|
|
8
|
+
|
|
9
|
+
A peer range is now a caret, a tilde or an explicit `<`, except for `react` and
|
|
10
|
+
`react-native`: an app brings exactly one of each, so a floor is the only useful thing
|
|
11
|
+
to say about them. Every other peer is a library the app installs beside us, and an
|
|
12
|
+
open `>=` was a promise about versions nobody had run — mmkv 3→4 lost entitlements and
|
|
13
|
+
pending purchases exactly that way.
|
|
14
|
+
|
|
15
|
+
**Four of the new ceilings exclude a version that is already published**, so an app
|
|
16
|
+
that took the open range at its word may now see a peer warning. The new bound is the
|
|
17
|
+
major this release is tested against, which is the thing the old range failed to say:
|
|
18
|
+
|
|
19
|
+
| Peer | Was | Now | Published above the ceiling |
|
|
20
|
+
|---|---|---|---|
|
|
21
|
+
| `react-native-app-auth` (rn-auth) | `>=8` | `^8.0.0` | 9.0.0 |
|
|
22
|
+
| `react-native-keychain` (rn-auth) | `>=10` | `^10.0.0` | 11.0.0 |
|
|
23
|
+
| `@op-engineering/op-sqlite` (rn-local-db) | `>=17.1.0` | `^17.1.0` | 18.x, 19.x |
|
|
24
|
+
| `@react-native-firebase/messaging` (rn-notification) | `>=26.0.0` | `^26.0.0` | 27.x |
|
|
25
|
+
|
|
26
|
+
The others — `react-native-vision-camera` `^5.1.1`, `react-native-svg` `^15.15.0`,
|
|
27
|
+
`react-native-unistyles` `^3.2.0`, `@dr.pogodin/react-native-fs` `^2.39.2`,
|
|
28
|
+
`react-native-quick-crypto` `^1.0.0`, `react-native-notify-kit` `^10.5.0`,
|
|
29
|
+
`react-native-localize` `^3.7.0`, `react-native-mmkv` `^4.0.0` — exclude nothing that
|
|
30
|
+
exists today. rn-payment's mmkv range was already bounded (`>=4.0.0 <5`) and is now
|
|
31
|
+
written as the caret that means the same thing.
|
|
32
|
+
|
|
33
|
+
Peers nothing imports are gone. `rn-theme`, `rn-local-db` and `rn-notification` asked
|
|
34
|
+
for `react` without having a component or a hook between them; `rn-auth`, `rn-ai` and
|
|
35
|
+
`rn-i18n` asked for `react-native` without touching a React Native API (rn-auth reaches
|
|
36
|
+
both native modules through `lazyPeer`, rn-i18n formats on `Intl`); `rn-notification`
|
|
37
|
+
asked for `@react-native-firebase/app`, which messaging declares itself. `rn-ai` no
|
|
38
|
+
longer peers on `@chipmobilesdk/rn-auth` at all — it asks for a `TokenProvider`, a
|
|
39
|
+
two-method interface rn-auth's `auth` satisfies, so an app with its own token source
|
|
40
|
+
installs nothing extra. An app that enables push or signs in through rn-auth installs
|
|
41
|
+
the same packages as before; it is the manifest that stopped overstating.
|
|
42
|
+
|
|
43
|
+
`react-native-localize` is declared optional, which is what the code already did: the
|
|
44
|
+
time-zone reader goes through `lazyPeer` and falls back to `Intl` without it. The
|
|
45
|
+
`peerDependenciesMeta` entries that said `optional: false` are gone — that is npm's
|
|
46
|
+
default, and writing it down only invited the question of which peers were undecided.
|
|
47
|
+
|
|
48
|
+
`npm run check:manifests` now fails on an unbounded peer range, a peer nothing in
|
|
49
|
+
`src/` imports, and a `peerDependenciesMeta` entry for either, so the policy holds
|
|
50
|
+
without anyone remembering it.
|
|
51
|
+
|
|
52
|
+
- The extraction CLI is a package of its own, and it runs from npm. ([#21](https://github.com/operationd8979/chip-mobile-sdk/pull/21))
|
|
53
|
+
|
|
54
|
+
`@chipmobilesdk/rn-i18n-tooling` is new: it builds to JavaScript, declares the Node it
|
|
55
|
+
needs (`>=22.13.0`) and exposes a `chip-i18n` binary.
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
npm i -D @chipmobilesdk/rn-i18n-tooling
|
|
59
|
+
npx chip-i18n sync # was: node --experimental-strip-types \
|
|
60
|
+
# node_modules/@chipmobilesdk/rn-i18n/src/tooling/cli.ts
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The tooling used to ship inside the runtime tarball as TypeScript source behind a
|
|
64
|
+
`./tooling` subpath, so the command the README documented worked in this monorepo and
|
|
65
|
+
nowhere else — a consumer had no `bin`, and `node` could not execute the `.ts`
|
|
66
|
+
(I18N-01, DEP-02). A test now installs the package into a throwaway app's
|
|
67
|
+
`node_modules` from exactly what `files` lists and runs the binary there, so "works
|
|
68
|
+
when published" is checked rather than asserted.
|
|
69
|
+
|
|
70
|
+
`@chipmobilesdk/rn-i18n` therefore has one entrypoint and no subpaths, and its tarball
|
|
71
|
+
no longer carries `src/tooling`. **Migration:** an app that imported
|
|
72
|
+
`@chipmobilesdk/rn-i18n/tooling` for the programmatic API installs the new package and
|
|
73
|
+
imports `@chipmobilesdk/rn-i18n-tooling` instead; the members are unchanged
|
|
74
|
+
(`runSync`, `scanSource`, `syncLocaleFiles`, `buildReport`, `renderReport`,
|
|
75
|
+
`generateKeysFile`, `renderKeysModule`, `validateSyncConfig`, `listSourceFiles`,
|
|
76
|
+
`globToRegExp`). An app that only ran the CLI replaces its script with `chip-i18n sync`.
|
|
77
|
+
|
|
78
|
+
Three things the move fixed while the code was in hand:
|
|
79
|
+
|
|
80
|
+
- **The scan starts where the glob does.** `listSourceFiles` walked the project root and
|
|
81
|
+
threw away almost everything it read; it now walks each glob's own base directory
|
|
82
|
+
(`src/screens/**/*.tsx` → `src/screens`), one walk per distinct base.
|
|
83
|
+
- **A match offset becomes a line number by binary search** over the file's line
|
|
84
|
+
starts, computed once. Counting newlines per match made a file with many keys
|
|
85
|
+
quadratic in its own length, and the ambiguous-usage snippet re-split the whole file
|
|
86
|
+
for every hit.
|
|
87
|
+
- **A sync with nothing to do writes nothing.** Each locale file and the generated keys
|
|
88
|
+
file are compared with what would be written — with line endings normalised, so a
|
|
89
|
+
Windows checkout's CRLF is not "a change" — and left alone when they match. Rewriting
|
|
90
|
+
identical bytes churned every mtime, which woke watchers and showed work in
|
|
91
|
+
`git status` that had not happened. The report says `Locale files: unchanged, nothing
|
|
92
|
+
written`.
|
|
93
|
+
|
|
94
|
+
`chip-i18n` also takes a command rather than only flags: `sync` is the one command,
|
|
95
|
+
`--help` prints usage, an unknown command exits 2, and a bare invocation still means
|
|
96
|
+
`sync` for a script that predates the name.
|
|
97
|
+
|
|
98
|
+
- Every package now ships built JavaScript with declaration files, instead of asking the
|
|
99
|
+
app to compile its TypeScript. ([#22](https://github.com/operationd8979/chip-mobile-sdk/pull/22))
|
|
100
|
+
|
|
101
|
+
Each tarball carries `lib/commonjs`, `lib/module` and `lib/typescript` (built with
|
|
102
|
+
`react-native-builder-bob`), and `src/` alongside them. What that changes for a
|
|
103
|
+
consumer:
|
|
104
|
+
|
|
105
|
+
- **Nothing to add to `transformIgnorePatterns`.** `require` resolves to the CommonJS
|
|
106
|
+
build, so an app's Jest loads this package without being told to transform it. That
|
|
107
|
+
was the one thing every consuming app had to know about us (DEP-04).
|
|
108
|
+
- **Typechecking reads our `.d.ts`** rather than compiling our source under our
|
|
109
|
+
`tsconfig`. A consumer's stricter — or looser — settings no longer meet our code.
|
|
110
|
+
- **`import` resolves to ESM**, so a bundler can tree-shake what it does not use.
|
|
111
|
+
|
|
112
|
+
No exported name changed, and no subpath moved: `.`, `./testing`, `./push`,
|
|
113
|
+
`./advanced`, `./app-auth`, `./keychain` and rn-core's twelve subpaths resolve exactly
|
|
114
|
+
as before, each now with its own declarations for both module systems. publint and
|
|
115
|
+
`attw --profile node16` pass on all twelve packages, which they could not before.
|
|
116
|
+
|
|
117
|
+
Inside the monorepo nothing changed either, and that is deliberate: every subpath also
|
|
118
|
+
declares a private `chipmobilesdk-source` condition pointing at its TypeScript, and
|
|
119
|
+
Metro, Jest and `tsc` here ask for that condition first. The demo app bundles 371
|
|
120
|
+
modules straight from `packages/*/src`, so editing a package still changes the app with
|
|
121
|
+
no build step, and coverage is still measured on the source. The condition is named for
|
|
122
|
+
this organization rather than using the conventional `source` because third-party
|
|
123
|
+
packages declare that one too — `@dr.pogodin/react-native-fs` does — and asking for it
|
|
124
|
+
pulled their sources into our stricter compile.
|
|
125
|
+
|
|
126
|
+
`npm run build:packages` builds everything, `prepack` rebuilds before any `npm pack`,
|
|
127
|
+
and `lib/` is git-ignored: the published bytes are produced by the release, never
|
|
128
|
+
committed. `npm run check:manifests` now fails a package whose `main`, `module`,
|
|
129
|
+
`types`, `source` condition, `files`, build script or `prepack` do not match this shape.
|
|
130
|
+
|
|
131
|
+
### Patch Changes
|
|
132
|
+
|
|
133
|
+
- Each package's public API and its weight are now recorded in the repository, so a change
|
|
134
|
+
to either is reviewed rather than discovered. ([#23](https://github.com/operationd8979/chip-mobile-sdk/pull/23))
|
|
135
|
+
|
|
136
|
+
`packages/<pkg>/etc/*.api.md` is an api-extractor report per entrypoint — twenty-seven
|
|
137
|
+
of them, generated from the declarations the package publishes and committed. A renamed
|
|
138
|
+
type, a widened union, one more optional field: all of it shows up in a pull request's
|
|
139
|
+
diff. `npm run check:api` fails when a report is out of date and `npm run api:update`
|
|
140
|
+
rewrites them.
|
|
141
|
+
|
|
142
|
+
A report that moves now needs a **minor or major** changeset for that package.
|
|
143
|
+
`check:changesets` enforces it, because the report is generated from what the package
|
|
144
|
+
publishes, and a patch release that moves the public surface is how a consumer's build
|
|
145
|
+
breaks on `npm update`. A report added for the first time is a baseline, not a change.
|
|
146
|
+
`./testing` deliberately has no report: test doubles are not a surface a release is
|
|
147
|
+
gated on.
|
|
148
|
+
|
|
149
|
+
`npm run check:size` holds every entrypoint to a budget in `.size-limit.json`, measured
|
|
150
|
+
on the ESM build with peers and rn-core treated as external — they are what an app
|
|
151
|
+
installs beside us, not our weight. Each budget started at today's measurement plus five
|
|
152
|
+
per cent, so growth becomes a decision someone makes instead of a drift nobody sees. The
|
|
153
|
+
current figures run from 500 B (`rn-core/lazy`) to 19 kB (rn-notification).
|
|
154
|
+
|
|
155
|
+
No behaviour or API changed in this release; the entry is here so the changelog records
|
|
156
|
+
when the reports and the budgets began.
|
|
157
|
+
- Updated dependencies ([#22](https://github.com/operationd8979/chip-mobile-sdk/pull/22), [#23](https://github.com/operationd8979/chip-mobile-sdk/pull/23)):
|
|
158
|
+
- @chipmobilesdk/rn-core@0.2.0
|
|
159
|
+
|
|
160
|
+
## 0.3.0
|
|
161
|
+
|
|
162
|
+
### Minor Changes
|
|
163
|
+
|
|
164
|
+
- `registerRuntimeEntries` notifies subscribers, so copy a lazily loaded feature registers after its screen mounted appears instead of leaving the key on screen until something else re-rendered. Entries registered at runtime get the same validation as configured ones: a missing key or a non-string default throws `I18nConfigError` there rather than surfacing at render time. ([#19](https://github.com/operationd8979/chip-mobile-sdk/pull/19))
|
|
165
|
+
|
|
166
|
+
`init()` runs once however many times it is called — StrictMode mounts an effect twice — and loses to the user: a `setLocale` made while the persisted read was in flight is no longer overwritten by the stored value. Setting the locale that is already active changes nothing and re-renders nothing.
|
|
167
|
+
|
|
168
|
+
`i18n.useDeviceLocale()` forgets the stored choice and detects again, which is the "System language" option a settings screen offers; the storage adapter's `removeItem`, required by the contract and called by nothing, is what it uses.
|
|
169
|
+
|
|
170
|
+
Plural rules cover a compact CLDR subset instead of three languages: English and its neighbours, Korean, Chinese, Thai, Indonesian and Malay (no singular), French and Portuguese (0 is singular), Russian, Ukrainian, Polish, Czech and Slovak (`few`/`many`), and Arabic (every category). A language outside the table still falls back to "1 is singular", but now reports a `plural_rule_default` diagnostic once, rather than shipping wrong plurals in silence.
|
|
171
|
+
|
|
172
|
+
`formatDate` **merges** its options over the locale's default date format, as `formatNumber` always has: `{ month: 'long' }` keeps the year and the day. Pass `formatDate(value, options, { replaceDefaults: true })` for the old behaviour of replacing them. `formatCurrency` deliberately does not apply the locale's default number options, because a currency's own minor units must win.
|
|
173
|
+
|
|
174
|
+
The generated keys file escapes a key holding an apostrophe or a backslash, which used to produce a file that did not parse and failed `tsc` for the whole app.
|
|
175
|
+
|
|
3
176
|
## 0.2.0
|
|
4
177
|
|
|
5
178
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -15,24 +15,27 @@ formatting, and a Node CLI that extracts and synchronizes translation keys.
|
|
|
15
15
|
injected by the consuming app.
|
|
16
16
|
- **Public package.** Published under the `@chipmobilesdk` npm organization.
|
|
17
17
|
- **Version**: see [CHANGELOG](CHANGELOG.md)
|
|
18
|
-
- **Entrypoints**: `@chipmobilesdk/rn-i18n
|
|
19
|
-
|
|
18
|
+
- **Entrypoints**: `@chipmobilesdk/rn-i18n`, one entrypoint and no subpaths. The
|
|
19
|
+
Node-only extraction and synchronization tooling is a separate package,
|
|
20
|
+
[`@chipmobilesdk/rn-i18n-tooling`](../rn-i18n-tooling/README.md)
|
|
20
21
|
|
|
21
22
|
## Installation
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
the
|
|
24
|
+
The tarball carries built JavaScript (CommonJS and ESM) with declaration files, and
|
|
25
|
+
the TypeScript it was built from. An app resolves the build, so there is nothing to add
|
|
26
|
+
to `transformIgnorePatterns` and typechecking reads the shipped `.d.ts`:
|
|
25
27
|
|
|
26
28
|
```bash
|
|
27
29
|
npm install @chipmobilesdk/rn-i18n
|
|
28
30
|
```
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
React is the one peer dependency. There is no `react-native` peer: the formatters
|
|
33
|
+
run on `Intl` and persistence is an injected port, so nothing here reaches for a
|
|
34
|
+
React Native API — the extraction CLI runs on plain Node for the same reason.
|
|
31
35
|
|
|
32
36
|
```jsonc
|
|
33
37
|
// peerDependencies
|
|
34
|
-
"react": ">=19"
|
|
35
|
-
"react-native": ">=0.85"
|
|
38
|
+
"react": ">=19"
|
|
36
39
|
```
|
|
37
40
|
|
|
38
41
|
## Quick start
|
|
@@ -115,8 +118,9 @@ t('cart.items', { count }, { count }); // plural variant by count
|
|
|
115
118
|
|
|
116
119
|
Public contract types also include the configuration, locale, translation,
|
|
117
120
|
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
|
|
121
|
+
`src/index.ts`. Do not deep-import package internals. The Node-only extraction and
|
|
122
|
+
synchronization APIs are in `@chipmobilesdk/rn-i18n-tooling`, a devDependency of the
|
|
123
|
+
app rather than a subpath of this package.
|
|
120
124
|
|
|
121
125
|
### Storage adapter (required for persistence)
|
|
122
126
|
|
|
@@ -174,8 +178,10 @@ supported Hermes environments. Built-ins cover `en` (one/other), `vi`/`ja`
|
|
|
174
178
|
|
|
175
179
|
## Extraction & synchronization CLI
|
|
176
180
|
|
|
177
|
-
|
|
178
|
-
|
|
181
|
+
The CLI is its own package — `npm i -D @chipmobilesdk/rn-i18n-tooling` — which builds
|
|
182
|
+
to JavaScript and exposes a `chip-i18n` binary. It used to ship inside this tarball as
|
|
183
|
+
TypeScript under `./tooling`, where the documented command could not run outside this
|
|
184
|
+
monorepo (I18N-01, DEP-02). Add an app config (`i18n-sync.config.json`) and a script:
|
|
179
185
|
|
|
180
186
|
```jsonc
|
|
181
187
|
{
|
|
@@ -192,15 +198,20 @@ config (`i18n-sync.config.json`) and a script:
|
|
|
192
198
|
```jsonc
|
|
193
199
|
// package.json
|
|
194
200
|
"scripts": {
|
|
195
|
-
"i18n:sync": "
|
|
201
|
+
"i18n:sync": "chip-i18n sync"
|
|
196
202
|
}
|
|
197
203
|
```
|
|
198
204
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
205
|
+
Or without a script: `npx chip-i18n sync`. `--config` names another file; it defaults
|
|
206
|
+
to `i18n-sync.config.json`, resolved from the working directory.
|
|
207
|
+
|
|
208
|
+
Running it scans the source tree — starting at each glob's own base directory, not the
|
|
209
|
+
project root — then per locale: creates missing files, adds missing keys (with the
|
|
210
|
+
placeholder policy), preserves existing translations, removes stale keys, protects
|
|
211
|
+
declared runtime-only keys, regenerates the typed key contract, and prints a report. A
|
|
212
|
+
run that finds nothing to change writes no file and says so, so it is safe in a watch
|
|
213
|
+
loop or a pre-commit hook. Exit codes: `0` success/warnings, `1` blocking findings
|
|
214
|
+
(conflicts, malformed locale files), `2` invalid config or usage.
|
|
204
215
|
|
|
205
216
|
Bind `t()` to the generated `AppI18nKey` so `tsc --noEmit` reports invalid or
|
|
206
217
|
stale key usage before release.
|
|
@@ -231,6 +242,14 @@ createI18n({
|
|
|
231
242
|
|
|
232
243
|
## Formatting
|
|
233
244
|
|
|
245
|
+
Options **merge** over the active locale's defaults, for every formatter:
|
|
246
|
+
`formatDate(value, { month: 'long' })` keeps the locale's year and day, exactly as
|
|
247
|
+
`formatNumber(value, { maximumFractionDigits: 0 })` keeps the rest of the locale's number
|
|
248
|
+
options. Pass `formatDate(value, options, { replaceDefaults: true })` to use only what you
|
|
249
|
+
give it. `formatCurrency` deliberately does not take the locale's default number options:
|
|
250
|
+
a currency carries its own minor units (yen has none), and generic number defaults would
|
|
251
|
+
override them.
|
|
252
|
+
|
|
234
253
|
`formatNumber`, `formatCurrency`, `formatDate` and `formatUnit` reuse one cached
|
|
235
254
|
`Intl` formatter per locale and options. A value or option they cannot format (a
|
|
236
255
|
`NaN`, an unknown currency code) is handled by the `invalidFormat` policy: the
|
|
@@ -238,6 +257,18 @@ placeholder text, or a thrown error in `error` mode, and an `invalid_format`
|
|
|
238
257
|
diagnostic either way. It never throws from inside `Intl` during a render. A
|
|
239
258
|
locale's `defaultCurrency` must be a three-letter ISO 4217 code, checked at startup.
|
|
240
259
|
|
|
260
|
+
## Switching, and following the device
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
await i18n.setLocale('vi-VN'); // the user chose a language
|
|
264
|
+
await i18n.useDeviceLocale(); // "System language": forget the choice, detect again
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
`init()` resolves the locale once — persisted, then device, then the primary fallback —
|
|
268
|
+
and is safe to call again (it returns the same promise). A `setLocale` during that read
|
|
269
|
+
wins: the stored value never overwrites a choice the user has just made. Setting the
|
|
270
|
+
locale already active changes nothing and re-renders nothing.
|
|
271
|
+
|
|
241
272
|
## Diagnostics
|
|
242
273
|
|
|
243
274
|
Missing translations and variables, fallbacks, invalid formats, persistence
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.runtimeEntriesFromCatalog = runtimeEntriesFromCatalog;
|
|
7
|
+
/**
|
|
8
|
+
* Runtime entries for a package's message catalog (I18N-13), so an app registers
|
|
9
|
+
* an SDK's copy in one line and translates it like its own:
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* createI18n({ ...config, runtimeEntries: runtimeEntriesFromCatalog(paymentMessages.user) });
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Catalogs mark variables `{name}`; rn-i18n's are `{{name}}`, so they are
|
|
16
|
+
* converted. `policy: 'add'` lets the sync CLI write missing keys into each locale
|
|
17
|
+
* file with the default text.
|
|
18
|
+
*/
|
|
19
|
+
function runtimeEntriesFromCatalog(catalog, options = {}) {
|
|
20
|
+
return Object.entries(catalog).map(([key, text]) => ({
|
|
21
|
+
key,
|
|
22
|
+
defaultText: text.replace(/(?<!\{)\{(\w+)\}(?!\})/g, '{{$1}}'),
|
|
23
|
+
policy: options.policy ?? 'report',
|
|
24
|
+
...(options.description === undefined ? {} : {
|
|
25
|
+
description: options.description
|
|
26
|
+
})
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["runtimeEntriesFromCatalog","catalog","options","Object","entries","map","key","text","defaultText","replace","policy","description","undefined"],"sourceRoot":"../../../src","sources":["config/catalog.ts"],"mappings":";;;;;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASA,yBAAyBA,CACxCC,OAAyC,EACzCC,OAA8E,GAAG,CAAC,CAAC,EACvD;EAC5B,OAAOC,MAAM,CAACC,OAAO,CAACH,OAAO,CAAC,CAACI,GAAG,CAAC,CAAC,CAACC,GAAG,EAAEC,IAAI,CAAC,MAAM;IACpDD,GAAG;IACHE,WAAW,EAAED,IAAI,CAACE,OAAO,CAAC,yBAAyB,EAAE,QAAQ,CAAC;IAC9DC,MAAM,EAAER,OAAO,CAACQ,MAAM,IAAI,QAAQ;IAClC,IAAIR,OAAO,CAACS,WAAW,KAAKC,SAAS,GAAG,CAAC,CAAC,GAAG;MAAED,WAAW,EAAET,OAAO,CAACS;IAAY,CAAC;EAClF,CAAC,CAAC,CAAC;AACJ","ignoreList":[]}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.createReporter = createReporter;
|
|
7
|
+
exports.defaultReporter = void 0;
|
|
8
|
+
exports.describeError = describeError;
|
|
9
|
+
var _rnCore = require("@chipmobilesdk/rn-core");
|
|
10
|
+
/** What rn-i18n reports. Stable: an app may branch on these. */
|
|
11
|
+
|
|
12
|
+
const SEVERITY = {
|
|
13
|
+
missing_translation: 'warn',
|
|
14
|
+
fallback_used: 'info',
|
|
15
|
+
missing_variable: 'warn',
|
|
16
|
+
invalid_value: 'warn',
|
|
17
|
+
invalid_format: 'warn',
|
|
18
|
+
locale_unsupported: 'warn',
|
|
19
|
+
plural_rule_default: 'warn',
|
|
20
|
+
locale_read_failed: 'warn',
|
|
21
|
+
locale_write_failed: 'warn',
|
|
22
|
+
listener_threw: 'error'
|
|
23
|
+
};
|
|
24
|
+
const channel = (0, _rnCore.createDiagnosticChannel)('rn-i18n');
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* One place every diagnostic goes through (I18N-11): the app's SDK sinks always
|
|
28
|
+
* (`installSdkSink` from rn-core), then `onDiagnostic` when the app gave one,
|
|
29
|
+
* otherwise `console.warn` in development builds and tests only.
|
|
30
|
+
*/
|
|
31
|
+
function createReporter(onDiagnostic) {
|
|
32
|
+
return diagnostic => {
|
|
33
|
+
channel.emit(diagnostic.code, SEVERITY[diagnostic.code], {
|
|
34
|
+
...(diagnostic.key === undefined ? {} : {
|
|
35
|
+
key: diagnostic.key
|
|
36
|
+
}),
|
|
37
|
+
...(diagnostic.locale === undefined ? {} : {
|
|
38
|
+
locale: diagnostic.locale
|
|
39
|
+
})
|
|
40
|
+
});
|
|
41
|
+
if (onDiagnostic) {
|
|
42
|
+
onDiagnostic(diagnostic);
|
|
43
|
+
} else if (typeof __DEV__ === 'undefined' || __DEV__) {
|
|
44
|
+
console.warn(`[rn-i18n] ${diagnostic.message}`);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** For the few call sites that run before or outside an i18n instance. */
|
|
50
|
+
const defaultReporter = exports.defaultReporter = createReporter();
|
|
51
|
+
|
|
52
|
+
/** Foreign error text, shaped before it reaches a message. */
|
|
53
|
+
function describeError(error) {
|
|
54
|
+
return (0, _rnCore.redactPatterns)(error instanceof Error ? error.message : String(error));
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=diagnostics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_rnCore","require","SEVERITY","missing_translation","fallback_used","missing_variable","invalid_value","invalid_format","locale_unsupported","plural_rule_default","locale_read_failed","locale_write_failed","listener_threw","channel","createDiagnosticChannel","createReporter","onDiagnostic","diagnostic","emit","code","key","undefined","locale","__DEV__","console","warn","message","defaultReporter","exports","describeError","error","redactPatterns","Error","String"],"sourceRoot":"../../../src","sources":["config/diagnostics.ts"],"mappings":";;;;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAMA;;AAuBA,MAAMC,QAAwD,GAAG;EAChEC,mBAAmB,EAAE,MAAM;EAC3BC,aAAa,EAAE,MAAM;EACrBC,gBAAgB,EAAE,MAAM;EACxBC,aAAa,EAAE,MAAM;EACrBC,cAAc,EAAE,MAAM;EACtBC,kBAAkB,EAAE,MAAM;EAC1BC,mBAAmB,EAAE,MAAM;EAC3BC,kBAAkB,EAAE,MAAM;EAC1BC,mBAAmB,EAAE,MAAM;EAC3BC,cAAc,EAAE;AACjB,CAAC;AAED,MAAMC,OAAO,GAAG,IAAAC,+BAAuB,EAAqB,SAAS,CAAC;;AAEtE;AACA;AACA;AACA;AACA;AACO,SAASC,cAAcA,CAACC,YAAoC,EAAyB;EAC3F,OAAOC,UAAU,IAAI;IACpBJ,OAAO,CAACK,IAAI,CAACD,UAAU,CAACE,IAAI,EAAEjB,QAAQ,CAACe,UAAU,CAACE,IAAI,CAAC,EAAE;MACxD,IAAIF,UAAU,CAACG,GAAG,KAAKC,SAAS,GAAG,CAAC,CAAC,GAAG;QAAED,GAAG,EAAEH,UAAU,CAACG;MAAI,CAAC,CAAC;MAChE,IAAIH,UAAU,CAACK,MAAM,KAAKD,SAAS,GAAG,CAAC,CAAC,GAAG;QAAEC,MAAM,EAAEL,UAAU,CAACK;MAAO,CAAC;IACzE,CAAC,CAAC;IACF,IAAIN,YAAY,EAAE;MACjBA,YAAY,CAACC,UAAU,CAAC;IACzB,CAAC,MAAM,IAAI,OAAOM,OAAO,KAAK,WAAW,IAAIA,OAAO,EAAE;MACrDC,OAAO,CAACC,IAAI,CAAC,aAAaR,UAAU,CAACS,OAAO,EAAE,CAAC;IAChD;EACD,CAAC;AACF;;AAEA;AACO,MAAMC,eAAe,GAAAC,OAAA,CAAAD,eAAA,GAAGZ,cAAc,CAAC,CAAC;;AAE/C;AACO,SAASc,aAAaA,CAACC,KAAc,EAAU;EACrD,OAAO,IAAAC,sBAAc,EAACD,KAAK,YAAYE,KAAK,GAAGF,KAAK,CAACJ,OAAO,GAAGO,MAAM,CAACH,KAAK,CAAC,CAAC;AAC9E","ignoreList":[]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.UnitConversionError = exports.I18nConfigError = void 0;
|
|
7
|
+
var _rnCore = require("@chipmobilesdk/rn-core");
|
|
8
|
+
/** The package's error codes. */
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Configuration error listing every detected violation. An `SdkError`
|
|
12
|
+
* (`@chipmobilesdk/rn-core`) with `pkg: 'rn-i18n'`.
|
|
13
|
+
*/
|
|
14
|
+
class I18nConfigError extends _rnCore.SdkError {
|
|
15
|
+
constructor(issues) {
|
|
16
|
+
super('rn-i18n', 'invalid_config', {
|
|
17
|
+
message: `Invalid i18n configuration:\n- ${issues.join('\n- ')}`
|
|
18
|
+
});
|
|
19
|
+
this.name = 'I18nConfigError';
|
|
20
|
+
this.issues = issues;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** A unit conversion the package cannot perform, such as across dimensions. */
|
|
25
|
+
exports.I18nConfigError = I18nConfigError;
|
|
26
|
+
class UnitConversionError extends _rnCore.SdkError {
|
|
27
|
+
constructor(message) {
|
|
28
|
+
super('rn-i18n', 'unit_conversion', {
|
|
29
|
+
message
|
|
30
|
+
});
|
|
31
|
+
this.name = 'UnitConversionError';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.UnitConversionError = UnitConversionError;
|
|
35
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_rnCore","require","I18nConfigError","SdkError","constructor","issues","message","join","name","exports","UnitConversionError"],"sourceRoot":"../../../src","sources":["config/errors.ts"],"mappings":";;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAEA;;AAGA;AACA;AACA;AACA;AACO,MAAMC,eAAe,SAASC,gBAAQ,CAAgB;EAG5DC,WAAWA,CAACC,MAAyB,EAAE;IACtC,KAAK,CAAC,SAAS,EAAE,gBAAgB,EAAE;MAClCC,OAAO,EAAE,kCAAkCD,MAAM,CAACE,IAAI,CAAC,MAAM,CAAC;IAC/D,CAAC,CAAC;IACF,IAAI,CAACC,IAAI,GAAG,iBAAiB;IAC7B,IAAI,CAACH,MAAM,GAAGA,MAAM;EACrB;AACD;;AAEA;AAAAI,OAAA,CAAAP,eAAA,GAAAA,eAAA;AACO,MAAMQ,mBAAmB,SAASP,gBAAQ,CAAgB;EAChEC,WAAWA,CAACE,OAAe,EAAE;IAC5B,KAAK,CAAC,SAAS,EAAE,iBAAiB,EAAE;MAAEA;IAAQ,CAAC,CAAC;IAChD,IAAI,CAACE,IAAI,GAAG,qBAAqB;EAClC;AACD;AAACC,OAAA,CAAAC,mBAAA,GAAAA,mBAAA","ignoreList":[]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "I18nConfigError", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _errors.I18nConfigError;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
var _errors = require("./errors.js");
|
|
13
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_errors","require"],"sourceRoot":"../../../src","sources":["config/types.ts"],"mappings":";;;;;;;;;;;AAqJA,IAAAA,OAAA,GAAAC,OAAA","ignoreList":[]}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.validateConfig = validateConfig;
|
|
7
|
+
var _types = require("./types.js");
|
|
8
|
+
/**
|
|
9
|
+
* Validates an I18nConfig before any localized content is served (FR-003,
|
|
10
|
+
* FR-046). Collects every violation and throws a single I18nConfigError so
|
|
11
|
+
* apps see the complete picture instead of fixing issues one at a time.
|
|
12
|
+
*/
|
|
13
|
+
function validateConfig(config, knownUnits) {
|
|
14
|
+
const issues = [];
|
|
15
|
+
const locales = Array.isArray(config.locales) ? config.locales : [];
|
|
16
|
+
if (locales.length === 0) {
|
|
17
|
+
issues.push('At least one supported locale must be declared.');
|
|
18
|
+
}
|
|
19
|
+
const codes = new Set();
|
|
20
|
+
for (const locale of locales) {
|
|
21
|
+
if (codes.has(locale.code)) {
|
|
22
|
+
issues.push(`Duplicate locale code "${locale.code}".`);
|
|
23
|
+
}
|
|
24
|
+
codes.add(locale.code);
|
|
25
|
+
}
|
|
26
|
+
for (const locale of locales) {
|
|
27
|
+
validateLocale(locale, codes, knownUnits, issues);
|
|
28
|
+
}
|
|
29
|
+
detectFallbackCycles(locales, codes, issues);
|
|
30
|
+
if (locales.length > 0 && !codes.has(config.primaryLocale)) {
|
|
31
|
+
issues.push(`Primary locale "${config.primaryLocale}" is not a supported locale.`);
|
|
32
|
+
}
|
|
33
|
+
for (const code of codes) {
|
|
34
|
+
const resource = config.resources?.[code];
|
|
35
|
+
if (resource === undefined || resource === null) {
|
|
36
|
+
issues.push(`Locale "${code}" has no translation resource.`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (config.persistLocale !== false && !config.storage) {
|
|
40
|
+
issues.push('Locale persistence is enabled but no storage adapter was injected. ' + 'Provide `storage` or set `persistLocale: false`.');
|
|
41
|
+
}
|
|
42
|
+
const runtimeKeys = new Set();
|
|
43
|
+
for (const entry of config.runtimeEntries ?? []) {
|
|
44
|
+
if (!entry.key) {
|
|
45
|
+
issues.push('Runtime-only entries must declare a non-empty key.');
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (runtimeKeys.has(entry.key)) {
|
|
49
|
+
issues.push(`Duplicate runtime-only entry "${entry.key}".`);
|
|
50
|
+
}
|
|
51
|
+
runtimeKeys.add(entry.key);
|
|
52
|
+
if (typeof entry.defaultText !== 'string') {
|
|
53
|
+
issues.push(`Runtime-only entry "${entry.key}" must declare default text.`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (issues.length > 0) {
|
|
57
|
+
throw new _types.I18nConfigError(issues);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function validateLocale(locale, codes, knownUnits, issues) {
|
|
61
|
+
const code = locale.code;
|
|
62
|
+
if (!code) {
|
|
63
|
+
issues.push('Every locale must declare a non-empty code.');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (!locale.displayName) {
|
|
67
|
+
issues.push(`Locale "${code}" must declare a display name.`);
|
|
68
|
+
}
|
|
69
|
+
if (locale.direction !== 'ltr' && locale.direction !== 'rtl') {
|
|
70
|
+
issues.push(`Locale "${code}" must declare direction "ltr" or "rtl".`);
|
|
71
|
+
}
|
|
72
|
+
if (!locale.defaultCurrency) {
|
|
73
|
+
issues.push(`Locale "${code}" must declare a default currency.`);
|
|
74
|
+
} else if (!/^[A-Z]{3}$/.test(locale.defaultCurrency)) {
|
|
75
|
+
// An ISO 4217 code; anything else would fail every currency format (I18N-08).
|
|
76
|
+
issues.push(`Locale "${code}" default currency "${locale.defaultCurrency}" is not a three-letter ISO 4217 code.`);
|
|
77
|
+
}
|
|
78
|
+
if (!knownUnits.has(locale.defaultUnit)) {
|
|
79
|
+
issues.push(`Locale "${code}" declares unknown default unit "${locale.defaultUnit}".`);
|
|
80
|
+
}
|
|
81
|
+
if (typeof locale.defaultDateFormat !== 'object' || !locale.defaultDateFormat) {
|
|
82
|
+
issues.push(`Locale "${code}" must declare a default date format.`);
|
|
83
|
+
}
|
|
84
|
+
if (typeof locale.defaultNumberFormat !== 'object' || !locale.defaultNumberFormat) {
|
|
85
|
+
issues.push(`Locale "${code}" must declare a default number format.`);
|
|
86
|
+
}
|
|
87
|
+
if (!Array.isArray(locale.fallbackChain)) {
|
|
88
|
+
issues.push(`Locale "${code}" must declare a fallback chain array.`);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const seen = new Set();
|
|
92
|
+
for (const entry of locale.fallbackChain) {
|
|
93
|
+
if (entry === code) {
|
|
94
|
+
issues.push(`Locale "${code}" must not include itself in its fallback chain.`);
|
|
95
|
+
}
|
|
96
|
+
if (seen.has(entry)) {
|
|
97
|
+
issues.push(`Locale "${code}" lists duplicate fallback-chain entry "${entry}".`);
|
|
98
|
+
}
|
|
99
|
+
seen.add(entry);
|
|
100
|
+
if (!codes.has(entry)) {
|
|
101
|
+
issues.push(`Locale "${code}" references unsupported fallback locale "${entry}".`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function detectFallbackCycles(locales, codes, issues) {
|
|
106
|
+
const chains = new Map(locales.map(locale => [locale.code, (locale.fallbackChain ?? []).filter(entry => codes.has(entry))]));
|
|
107
|
+
const states = new Map();
|
|
108
|
+
const visit = (code, path) => {
|
|
109
|
+
const state = states.get(code);
|
|
110
|
+
if (state === 'done') {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
if (state === 'visiting') {
|
|
114
|
+
issues.push(`Fallback cycle detected: ${[...path, code].join(' -> ')}.`);
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
states.set(code, 'visiting');
|
|
118
|
+
for (const next of chains.get(code) ?? []) {
|
|
119
|
+
if (visit(next, [...path, code])) {
|
|
120
|
+
states.set(code, 'done');
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
states.set(code, 'done');
|
|
125
|
+
return false;
|
|
126
|
+
};
|
|
127
|
+
for (const locale of locales) {
|
|
128
|
+
if (visit(locale.code, [])) {
|
|
129
|
+
// Report the first cycle only; one clear cycle message is actionable.
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=validateConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_types","require","validateConfig","config","knownUnits","issues","locales","Array","isArray","length","push","codes","Set","locale","has","code","add","validateLocale","detectFallbackCycles","primaryLocale","resource","resources","undefined","persistLocale","storage","runtimeKeys","entry","runtimeEntries","key","defaultText","I18nConfigError","displayName","direction","defaultCurrency","test","defaultUnit","defaultDateFormat","defaultNumberFormat","fallbackChain","seen","chains","Map","map","filter","states","visit","path","state","get","join","set","next"],"sourceRoot":"../../../src","sources":["config/validateConfig.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.formatCurrencyValue = formatCurrencyValue;
|
|
7
|
+
var _rnCore = require("@chipmobilesdk/rn-core");
|
|
8
|
+
var _invalid = require("./invalid.js");
|
|
9
|
+
/**
|
|
10
|
+
* Locale-aware currency formatting. The locale's configured default currency
|
|
11
|
+
* applies unless the caller passes an explicit currency override (FR-021).
|
|
12
|
+
*
|
|
13
|
+
* `locale.defaultNumberFormat` is deliberately **not** applied here: a currency
|
|
14
|
+
* carries its own minor units (yen has none, dinar has three), and the locale's
|
|
15
|
+
* generic number options would override them — ¥1,234.56 instead of ¥1,235.
|
|
16
|
+
* Pass the options you want for a currency explicitly (I18N-09).
|
|
17
|
+
*/
|
|
18
|
+
function formatCurrencyValue(value, localeCode, defaultCurrency, currencyOverride, options, invalidFormat) {
|
|
19
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
20
|
+
return (0, _invalid.applyInvalidFormatPolicy)(invalidFormat, `Cannot format invalid currency value "${String(value)}" for locale "${localeCode}".`);
|
|
21
|
+
}
|
|
22
|
+
const currency = currencyOverride ?? defaultCurrency;
|
|
23
|
+
// rn-core's cached formatter (I18N-03). An unknown currency code (`usd1`) used
|
|
24
|
+
// to throw out of render; it is a format problem now (I18N-08).
|
|
25
|
+
try {
|
|
26
|
+
return (0, _rnCore.getNumberFormat)(localeCode, {
|
|
27
|
+
...options,
|
|
28
|
+
style: 'currency',
|
|
29
|
+
currency
|
|
30
|
+
}).format(value);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
return (0, _invalid.applyInvalidFormatPolicy)(invalidFormat, `Cannot format currency "${currency}" for locale "${localeCode}": ${String(error)}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=currency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_rnCore","require","_invalid","formatCurrencyValue","value","localeCode","defaultCurrency","currencyOverride","options","invalidFormat","Number","isFinite","applyInvalidFormatPolicy","String","currency","getNumberFormat","style","format","error"],"sourceRoot":"../../../src","sources":["format/currency.ts"],"mappings":";;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAEA,IAAAC,QAAA,GAAAD,OAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASE,mBAAmBA,CAClCC,KAAa,EACbC,UAAkB,EAClBC,eAAuB,EACvBC,gBAAoC,EACpCC,OAA6C,EAC7CC,aAAkC,EACzB;EACT,IAAI,OAAOL,KAAK,KAAK,QAAQ,IAAI,CAACM,MAAM,CAACC,QAAQ,CAACP,KAAK,CAAC,EAAE;IACzD,OAAO,IAAAQ,iCAAwB,EAC9BH,aAAa,EACb,yCAAyCI,MAAM,CAACT,KAAK,CAAC,iBAAiBC,UAAU,IAClF,CAAC;EACF;EACA,MAAMS,QAAQ,GAAGP,gBAAgB,IAAID,eAAe;EACpD;EACA;EACA,IAAI;IACH,OAAO,IAAAS,uBAAe,EAACV,UAAU,EAAE;MAAE,GAAGG,OAAO;MAAEQ,KAAK,EAAE,UAAU;MAAEF;IAAS,CAAC,CAAC,CAACG,MAAM,CAACb,KAAK,CAAC;EAC9F,CAAC,CAAC,OAAOc,KAAK,EAAE;IACf,OAAO,IAAAN,iCAAwB,EAC9BH,aAAa,EACb,2BAA2BK,QAAQ,iBAAiBT,UAAU,MAAMQ,MAAM,CAACK,KAAK,CAAC,EAClF,CAAC;EACF;AACD","ignoreList":[]}
|