@chipmobilesdk/rn-i18n 0.3.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.
Files changed (192) hide show
  1. package/CHANGELOG.md +157 -0
  2. package/README.md +28 -17
  3. package/lib/commonjs/config/catalog.js +29 -0
  4. package/lib/commonjs/config/catalog.js.map +1 -0
  5. package/lib/commonjs/config/diagnostics.js +56 -0
  6. package/lib/commonjs/config/diagnostics.js.map +1 -0
  7. package/lib/commonjs/config/errors.js +35 -0
  8. package/lib/commonjs/config/errors.js.map +1 -0
  9. package/lib/commonjs/config/types.js +13 -0
  10. package/lib/commonjs/config/types.js.map +1 -0
  11. package/lib/commonjs/config/validateConfig.js +134 -0
  12. package/lib/commonjs/config/validateConfig.js.map +1 -0
  13. package/lib/commonjs/format/currency.js +35 -0
  14. package/lib/commonjs/format/currency.js.map +1 -0
  15. package/lib/commonjs/format/date.js +36 -0
  16. package/lib/commonjs/format/date.js.map +1 -0
  17. package/lib/commonjs/format/invalid.js +24 -0
  18. package/lib/commonjs/format/invalid.js.map +1 -0
  19. package/lib/commonjs/format/number.js +29 -0
  20. package/lib/commonjs/format/number.js.map +1 -0
  21. package/lib/commonjs/format/units.js +184 -0
  22. package/lib/commonjs/format/units.js.map +1 -0
  23. package/lib/commonjs/i18n.js +234 -0
  24. package/lib/commonjs/i18n.js.map +1 -0
  25. package/lib/commonjs/index.js +93 -0
  26. package/lib/commonjs/index.js.map +1 -0
  27. package/lib/commonjs/locale/activeLocale.js +37 -0
  28. package/lib/commonjs/locale/activeLocale.js.map +1 -0
  29. package/lib/commonjs/locale/deviceLocale.js +52 -0
  30. package/lib/commonjs/locale/deviceLocale.js.map +1 -0
  31. package/lib/commonjs/locale/keyValueStorage.js +29 -0
  32. package/lib/commonjs/locale/keyValueStorage.js.map +1 -0
  33. package/lib/commonjs/locale/persistence.js +74 -0
  34. package/lib/commonjs/locale/persistence.js.map +1 -0
  35. package/lib/commonjs/package.json +1 -0
  36. package/lib/commonjs/react/I18nProvider.js +36 -0
  37. package/lib/commonjs/react/I18nProvider.js.map +1 -0
  38. package/lib/commonjs/react/hooks.js +47 -0
  39. package/lib/commonjs/react/hooks.js.map +1 -0
  40. package/lib/commonjs/translation/interpolate.js +24 -0
  41. package/lib/commonjs/translation/interpolate.js.map +1 -0
  42. package/lib/commonjs/translation/plurals.js +122 -0
  43. package/lib/commonjs/translation/plurals.js.map +1 -0
  44. package/lib/commonjs/translation/resolver.js +124 -0
  45. package/lib/commonjs/translation/resolver.js.map +1 -0
  46. package/lib/commonjs/translation/runtimeKeys.js +55 -0
  47. package/lib/commonjs/translation/runtimeKeys.js.map +1 -0
  48. package/lib/module/config/catalog.js +25 -0
  49. package/lib/module/config/catalog.js.map +1 -0
  50. package/lib/module/config/diagnostics.js +51 -0
  51. package/lib/module/config/diagnostics.js.map +1 -0
  52. package/lib/module/config/errors.js +30 -0
  53. package/lib/module/config/errors.js.map +1 -0
  54. package/lib/module/config/types.js +26 -0
  55. package/lib/module/config/types.js.map +1 -0
  56. package/lib/module/config/validateConfig.js +131 -0
  57. package/lib/module/config/validateConfig.js.map +1 -0
  58. package/lib/module/format/currency.js +32 -0
  59. package/lib/module/format/currency.js.map +1 -0
  60. package/lib/module/format/date.js +33 -0
  61. package/lib/module/format/date.js.map +1 -0
  62. package/lib/module/format/invalid.js +21 -0
  63. package/lib/module/format/invalid.js.map +1 -0
  64. package/lib/module/format/number.js +26 -0
  65. package/lib/module/format/number.js.map +1 -0
  66. package/lib/module/format/units.js +171 -0
  67. package/lib/module/format/units.js.map +1 -0
  68. package/lib/module/i18n.js +230 -0
  69. package/lib/module/i18n.js.map +1 -0
  70. package/lib/module/index.js +23 -0
  71. package/lib/module/index.js.map +1 -0
  72. package/lib/module/locale/activeLocale.js +33 -0
  73. package/lib/module/locale/activeLocale.js.map +1 -0
  74. package/lib/module/locale/deviceLocale.js +46 -0
  75. package/lib/module/locale/deviceLocale.js.map +1 -0
  76. package/lib/module/locale/keyValueStorage.js +25 -0
  77. package/lib/module/locale/keyValueStorage.js.map +1 -0
  78. package/lib/module/locale/persistence.js +66 -0
  79. package/lib/module/locale/persistence.js.map +1 -0
  80. package/lib/module/package.json +1 -0
  81. package/lib/module/react/I18nProvider.js +29 -0
  82. package/lib/module/react/I18nProvider.js.map +1 -0
  83. package/lib/module/react/hooks.js +42 -0
  84. package/lib/module/react/hooks.js.map +1 -0
  85. package/lib/module/translation/interpolate.js +20 -0
  86. package/lib/module/translation/interpolate.js.map +1 -0
  87. package/lib/module/translation/plurals.js +114 -0
  88. package/lib/module/translation/plurals.js.map +1 -0
  89. package/lib/module/translation/resolver.js +118 -0
  90. package/lib/module/translation/resolver.js.map +1 -0
  91. package/lib/module/translation/runtimeKeys.js +50 -0
  92. package/lib/module/translation/runtimeKeys.js.map +1 -0
  93. package/lib/typescript/commonjs/config/catalog.d.ts +18 -0
  94. package/lib/typescript/commonjs/config/catalog.d.ts.map +1 -0
  95. package/lib/typescript/commonjs/config/diagnostics.d.ts +21 -0
  96. package/lib/typescript/commonjs/config/diagnostics.d.ts.map +1 -0
  97. package/lib/typescript/commonjs/config/errors.d.ts +16 -0
  98. package/lib/typescript/commonjs/config/errors.d.ts.map +1 -0
  99. package/lib/typescript/commonjs/config/types.d.ts +127 -0
  100. package/lib/typescript/commonjs/config/types.d.ts.map +1 -0
  101. package/lib/typescript/commonjs/config/validateConfig.d.ts +8 -0
  102. package/lib/typescript/commonjs/config/validateConfig.d.ts.map +1 -0
  103. package/lib/typescript/commonjs/format/currency.d.ts +12 -0
  104. package/lib/typescript/commonjs/format/currency.d.ts.map +1 -0
  105. package/lib/typescript/commonjs/format/date.d.ts +14 -0
  106. package/lib/typescript/commonjs/format/date.d.ts.map +1 -0
  107. package/lib/typescript/commonjs/format/invalid.d.ts +12 -0
  108. package/lib/typescript/commonjs/format/invalid.d.ts.map +1 -0
  109. package/lib/typescript/commonjs/format/number.d.ts +8 -0
  110. package/lib/typescript/commonjs/format/number.d.ts.map +1 -0
  111. package/lib/typescript/commonjs/format/units.d.ts +23 -0
  112. package/lib/typescript/commonjs/format/units.d.ts.map +1 -0
  113. package/lib/typescript/commonjs/i18n.d.ts +46 -0
  114. package/lib/typescript/commonjs/i18n.d.ts.map +1 -0
  115. package/lib/typescript/commonjs/index.d.ts +27 -0
  116. package/lib/typescript/commonjs/index.d.ts.map +1 -0
  117. package/lib/typescript/commonjs/locale/activeLocale.d.ts +17 -0
  118. package/lib/typescript/commonjs/locale/activeLocale.d.ts.map +1 -0
  119. package/lib/typescript/commonjs/locale/deviceLocale.d.ts +19 -0
  120. package/lib/typescript/commonjs/locale/deviceLocale.d.ts.map +1 -0
  121. package/lib/typescript/commonjs/locale/keyValueStorage.d.ts +13 -0
  122. package/lib/typescript/commonjs/locale/keyValueStorage.d.ts.map +1 -0
  123. package/lib/typescript/commonjs/locale/persistence.d.ts +24 -0
  124. package/lib/typescript/commonjs/locale/persistence.d.ts.map +1 -0
  125. package/lib/typescript/commonjs/package.json +1 -0
  126. package/lib/typescript/commonjs/react/I18nProvider.d.ts +16 -0
  127. package/lib/typescript/commonjs/react/I18nProvider.d.ts.map +1 -0
  128. package/lib/typescript/commonjs/react/hooks.d.ts +21 -0
  129. package/lib/typescript/commonjs/react/hooks.d.ts.map +1 -0
  130. package/lib/typescript/commonjs/translation/interpolate.d.ts +8 -0
  131. package/lib/typescript/commonjs/translation/interpolate.d.ts.map +1 -0
  132. package/lib/typescript/commonjs/translation/plurals.d.ts +13 -0
  133. package/lib/typescript/commonjs/translation/plurals.d.ts.map +1 -0
  134. package/lib/typescript/commonjs/translation/resolver.d.ts +24 -0
  135. package/lib/typescript/commonjs/translation/resolver.d.ts.map +1 -0
  136. package/lib/typescript/commonjs/translation/runtimeKeys.d.ts +16 -0
  137. package/lib/typescript/commonjs/translation/runtimeKeys.d.ts.map +1 -0
  138. package/lib/typescript/module/config/catalog.d.ts +18 -0
  139. package/lib/typescript/module/config/catalog.d.ts.map +1 -0
  140. package/lib/typescript/module/config/diagnostics.d.ts +21 -0
  141. package/lib/typescript/module/config/diagnostics.d.ts.map +1 -0
  142. package/lib/typescript/module/config/errors.d.ts +16 -0
  143. package/lib/typescript/module/config/errors.d.ts.map +1 -0
  144. package/lib/typescript/module/config/types.d.ts +127 -0
  145. package/lib/typescript/module/config/types.d.ts.map +1 -0
  146. package/lib/typescript/module/config/validateConfig.d.ts +8 -0
  147. package/lib/typescript/module/config/validateConfig.d.ts.map +1 -0
  148. package/lib/typescript/module/format/currency.d.ts +12 -0
  149. package/lib/typescript/module/format/currency.d.ts.map +1 -0
  150. package/lib/typescript/module/format/date.d.ts +14 -0
  151. package/lib/typescript/module/format/date.d.ts.map +1 -0
  152. package/lib/typescript/module/format/invalid.d.ts +12 -0
  153. package/lib/typescript/module/format/invalid.d.ts.map +1 -0
  154. package/lib/typescript/module/format/number.d.ts +8 -0
  155. package/lib/typescript/module/format/number.d.ts.map +1 -0
  156. package/lib/typescript/module/format/units.d.ts +23 -0
  157. package/lib/typescript/module/format/units.d.ts.map +1 -0
  158. package/lib/typescript/module/i18n.d.ts +46 -0
  159. package/lib/typescript/module/i18n.d.ts.map +1 -0
  160. package/lib/typescript/module/index.d.ts +27 -0
  161. package/lib/typescript/module/index.d.ts.map +1 -0
  162. package/lib/typescript/module/locale/activeLocale.d.ts +17 -0
  163. package/lib/typescript/module/locale/activeLocale.d.ts.map +1 -0
  164. package/lib/typescript/module/locale/deviceLocale.d.ts +19 -0
  165. package/lib/typescript/module/locale/deviceLocale.d.ts.map +1 -0
  166. package/lib/typescript/module/locale/keyValueStorage.d.ts +13 -0
  167. package/lib/typescript/module/locale/keyValueStorage.d.ts.map +1 -0
  168. package/lib/typescript/module/locale/persistence.d.ts +24 -0
  169. package/lib/typescript/module/locale/persistence.d.ts.map +1 -0
  170. package/lib/typescript/module/package.json +1 -0
  171. package/lib/typescript/module/react/I18nProvider.d.ts +16 -0
  172. package/lib/typescript/module/react/I18nProvider.d.ts.map +1 -0
  173. package/lib/typescript/module/react/hooks.d.ts +21 -0
  174. package/lib/typescript/module/react/hooks.d.ts.map +1 -0
  175. package/lib/typescript/module/translation/interpolate.d.ts +8 -0
  176. package/lib/typescript/module/translation/interpolate.d.ts.map +1 -0
  177. package/lib/typescript/module/translation/plurals.d.ts +13 -0
  178. package/lib/typescript/module/translation/plurals.d.ts.map +1 -0
  179. package/lib/typescript/module/translation/resolver.d.ts +24 -0
  180. package/lib/typescript/module/translation/resolver.d.ts.map +1 -0
  181. package/lib/typescript/module/translation/runtimeKeys.d.ts +16 -0
  182. package/lib/typescript/module/translation/runtimeKeys.d.ts.map +1 -0
  183. package/package.json +41 -18
  184. package/src/index.ts +5 -3
  185. package/src/tooling/cli.ts +0 -76
  186. package/src/tooling/generateKeys.ts +0 -65
  187. package/src/tooling/index.ts +0 -27
  188. package/src/tooling/report.ts +0 -109
  189. package/src/tooling/runner.ts +0 -111
  190. package/src/tooling/scan.ts +0 -193
  191. package/src/tooling/sync.ts +0 -193
  192. package/src/tooling/types.ts +0 -119
package/CHANGELOG.md CHANGED
@@ -1,5 +1,162 @@
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
+
3
160
  ## 0.3.0
4
161
 
5
162
  ### 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` and the Node-only
19
- `@chipmobilesdk/rn-i18n/tooling`
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
- This package ships TypeScript sources and is consumed through the workspace or
24
- the public npm registry:
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
- Host frameworks are peer dependencies:
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 must be imported from `@chipmobilesdk/rn-i18n/tooling`.
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
- Node-only, exposed via `@chipmobilesdk/rn-i18n/tooling` and a CLI. Add an app
178
- config (`i18n-sync.config.json`) and a script:
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": "node --experimental-strip-types node_modules/@chipmobilesdk/rn-i18n/src/tooling/cli.ts --config i18n-sync.config.json"
201
+ "i18n:sync": "chip-i18n sync"
196
202
  }
197
203
  ```
198
204
 
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.
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.
@@ -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":[]}
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.formatDateValue = formatDateValue;
7
+ var _rnCore = require("@chipmobilesdk/rn-core");
8
+ var _invalid = require("./invalid.js");
9
+ /**
10
+ * Locale-aware date formatting through the Hermes built-in
11
+ * Intl.DateTimeFormat.
12
+ *
13
+ * Options are **merged** over the locale's configured default, the way
14
+ * `formatNumber` has always worked: `{ month: 'long' }` changes the month and
15
+ * keeps the year and day. Replacing the default wholesale, which this used to
16
+ * do, meant one option silently dropped every other component — and the two
17
+ * formatters disagreed about what an option meant (I18N-09). Pass
18
+ * `{ replaceDefaults: true }` for the old behaviour.
19
+ */
20
+ function formatDateValue(value, localeCode, defaults, override, invalidFormat, replaceDefaults = false) {
21
+ const date = value instanceof Date ? value : new Date(value);
22
+ if (Number.isNaN(date.getTime())) {
23
+ return (0, _invalid.applyInvalidFormatPolicy)(invalidFormat, `Cannot format invalid date value "${String(value)}" for locale "${localeCode}".`);
24
+ }
25
+ const options = replaceDefaults ? override ?? defaults : {
26
+ ...defaults,
27
+ ...override
28
+ };
29
+ // rn-core's cached formatter (I18N-03); a bad option is a format problem (I18N-08).
30
+ try {
31
+ return (0, _rnCore.getDateTimeFormat)(localeCode, options).format(date);
32
+ } catch (error) {
33
+ return (0, _invalid.applyInvalidFormatPolicy)(invalidFormat, `Cannot format a date: ${String(error)}`);
34
+ }
35
+ }
36
+ //# sourceMappingURL=date.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_rnCore","require","_invalid","formatDateValue","value","localeCode","defaults","override","invalidFormat","replaceDefaults","date","Date","Number","isNaN","getTime","applyInvalidFormatPolicy","String","options","getDateTimeFormat","format","error"],"sourceRoot":"../../../src","sources":["format/date.ts"],"mappings":";;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAEA,IAAAC,QAAA,GAAAD,OAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASE,eAAeA,CAC9BC,KAAoB,EACpBC,UAAkB,EAClBC,QAAoC,EACpCC,QAAgD,EAChDC,aAAkC,EAClCC,eAAe,GAAG,KAAK,EACd;EACT,MAAMC,IAAI,GAAGN,KAAK,YAAYO,IAAI,GAAGP,KAAK,GAAG,IAAIO,IAAI,CAACP,KAAK,CAAC;EAC5D,IAAIQ,MAAM,CAACC,KAAK,CAACH,IAAI,CAACI,OAAO,CAAC,CAAC,CAAC,EAAE;IACjC,OAAO,IAAAC,iCAAwB,EAC9BP,aAAa,EACb,qCAAqCQ,MAAM,CAACZ,KAAK,CAAC,iBAAiBC,UAAU,IAC9E,CAAC;EACF;EACA,MAAMY,OAAO,GAAGR,eAAe,GAAIF,QAAQ,IAAID,QAAQ,GAAI;IAAE,GAAGA,QAAQ;IAAE,GAAGC;EAAS,CAAC;EACvF;EACA,IAAI;IACH,OAAO,IAAAW,yBAAiB,EAACb,UAAU,EAAEY,OAAO,CAAC,CAACE,MAAM,CAACT,IAAI,CAAC;EAC3D,CAAC,CAAC,OAAOU,KAAK,EAAE;IACf,OAAO,IAAAL,iCAAwB,EAACP,aAAa,EAAE,yBAAyBQ,MAAM,CAACI,KAAK,CAAC,EAAE,CAAC;EACzF;AACD","ignoreList":[]}