@symbiote-native/localization 3.0.2 → 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -32
- package/package.json +15 -15
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @symbiote-native/localization
|
|
2
2
|
|
|
3
3
|
Port of [`expo-localization`](https://docs.expo.dev/versions/latest/sdk/localization/) for
|
|
4
|
-
[SymbioteNative](../../README.md)
|
|
4
|
+
[SymbioteNative](../../README.md) - the device's locale list and preferred calendar settings,
|
|
5
5
|
reachable from every adapter (React, Vue, Svelte, Solid, Angular), not just React.
|
|
6
6
|
|
|
7
7
|
Built the same way as [`@symbiote-native/battery`](../battery) and
|
|
@@ -25,41 +25,41 @@ npx @symbiote-native/cli add --localization
|
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
Either way: installs `@symbiote-native/localization` and wires the native autolinking
|
|
28
|
-
automatically
|
|
28
|
+
automatically - see [`@symbiote-native/cli`](../cli).
|
|
29
29
|
|
|
30
30
|
<details>
|
|
31
|
-
<summary>Manual install (no CLI
|
|
31
|
+
<summary>Manual install (no CLI - installing and wiring native autolinking by hand)</summary>
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
34
|
npm install @symbiote-native/localization
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
Depends on `expo-localization` and `expo-modules-core` directly (regular dependencies, pinned to
|
|
38
|
-
exact versions
|
|
38
|
+
exact versions - never a caret range, since this package's `core/` is hand-ported against one
|
|
39
39
|
specific native API shape and a newer resolve could silently drift the two apart). Never install
|
|
40
|
-
`expo-localization` yourself, and never add the `expo` package to this project
|
|
40
|
+
`expo-localization` yourself, and never add the `expo` package to this project - it bundles its
|
|
41
41
|
own Metro/Babel pipeline that conflicts with this project's own.
|
|
42
42
|
|
|
43
43
|
### Required one-time step: native autolinking wiring
|
|
44
44
|
|
|
45
45
|
Unlike a plain RN native module, `expo-localization`'s native code is discovered by
|
|
46
|
-
`expo-modules-autolinking`, not RN's own `react-native.config.cjs` mechanism
|
|
46
|
+
`expo-modules-autolinking`, not RN's own `react-native.config.cjs` mechanism - this needs wiring
|
|
47
47
|
into the native host app **once**, covering this package and every other `expo-modules-core`
|
|
48
48
|
package (`@symbiote-native/battery`, `@symbiote-native/device`, ...) with zero further changes:
|
|
49
49
|
|
|
50
50
|
| Platform | Touches |
|
|
51
51
|
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
52
|
-
| iOS | `ios/Podfile`
|
|
53
|
-
| iOS | `AppDelegate.swift`
|
|
54
|
-
| Android | `settings.gradle` / `app/build.gradle`
|
|
55
|
-
| Android | `MainApplication.kt`
|
|
52
|
+
| iOS | `ios/Podfile` - add `use_expo_modules!` |
|
|
53
|
+
| iOS | `AppDelegate.swift` - Expo's runtime-bootstrap hook |
|
|
54
|
+
| Android | `settings.gradle` / `app/build.gradle` - resolve and include the Expo Gradle projects |
|
|
55
|
+
| Android | `MainApplication.kt` - Expo's bootstrap hook, plus a hand-written native-module name map (there's no `expo` meta-package here to auto-generate one) |
|
|
56
56
|
|
|
57
|
-
Full mechanics
|
|
58
|
-
peer-dependency exclusion list
|
|
57
|
+
Full mechanics - the Podfile pieces that normally ship inside the `expo` package, the `expo`
|
|
58
|
+
peer-dependency exclusion list - live in the `symbiote-expo-native-module` skill. Reference
|
|
59
59
|
implementation: `examples/expo-react/ios/Podfile` and
|
|
60
60
|
`examples/expo-react/android/app/src/main/java/com/canaryexpo/MainApplication.kt`.
|
|
61
61
|
|
|
62
|
-
No platform permission string is needed
|
|
62
|
+
No platform permission string is needed - locale and calendar settings are read-only system
|
|
63
63
|
state with no runtime permission prompt on either platform.
|
|
64
64
|
|
|
65
65
|
</details>
|
|
@@ -67,33 +67,33 @@ state with no runtime permission prompt on either platform.
|
|
|
67
67
|
## Shape
|
|
68
68
|
|
|
69
69
|
```
|
|
70
|
-
src/core/ types.ts
|
|
70
|
+
src/core/ types.ts - Locale, Weekday, CalendarIdentifier, Calendar, hand-ported
|
|
71
71
|
verbatim from Localization.types.ts. native-module.ts resolves the
|
|
72
72
|
native module through expo-modules-core's requireNativeModule and
|
|
73
|
-
exposes addLocaleListener/addCalendarListener. localization.ts
|
|
73
|
+
exposes addLocaleListener/addCalendarListener. localization.ts,
|
|
74
74
|
getLocales/getCalendars, synchronous getters delegating straight to the
|
|
75
75
|
native module.
|
|
76
|
-
src/react/hooks/ @symbiote-native/localization/react
|
|
77
|
-
src/vue/composables/ @symbiote-native/localization/vue
|
|
78
|
-
src/svelte/runes/ @symbiote-native/localization/svelte
|
|
79
|
-
src/solid/primitives/ @symbiote-native/localization/solid
|
|
76
|
+
src/react/hooks/ @symbiote-native/localization/react - useLocales, useCalendars
|
|
77
|
+
src/vue/composables/ @symbiote-native/localization/vue - same two names, Vue lifecycle
|
|
78
|
+
src/svelte/runes/ @symbiote-native/localization/svelte - same two names, read as `.current`
|
|
79
|
+
src/solid/primitives/ @symbiote-native/localization/solid - createLocales, createCalendars
|
|
80
80
|
(each returns an Accessor)
|
|
81
|
-
src/angular/services/ @symbiote-native/localization/angular
|
|
81
|
+
src/angular/services/ @symbiote-native/localization/angular - LocalesService, CalendarsService
|
|
82
82
|
(`.connect()` returns a Signal)
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
Two independent getters, each with its own native change listener and its own reactive hook per
|
|
86
|
-
adapter
|
|
86
|
+
adapter - mirroring `@symbiote-native/battery`'s shape of shipping several distinct hooks in one
|
|
87
87
|
package, not one combined hook. Solid's naming differs on purpose: `create*`, not `use*`, which
|
|
88
88
|
Solid reserves for consuming something that already exists. Each hook/composable/rune/primitive/
|
|
89
89
|
service seeds its return value from the
|
|
90
|
-
matching synchronous `get*()` call (no initial "loading" state needed
|
|
90
|
+
matching synchronous `get*()` call (no initial "loading" state needed - the native call is
|
|
91
91
|
sync, not async) and recomputes it whenever the matching listener fires.
|
|
92
92
|
|
|
93
93
|
## Use it
|
|
94
94
|
|
|
95
95
|
```tsx
|
|
96
|
-
// React
|
|
96
|
+
// React - examples/expo-react/screens/LocalizationScreen.tsx
|
|
97
97
|
import { useLocales, useCalendars } from '@symbiote-native/localization/react';
|
|
98
98
|
|
|
99
99
|
function LocalizationScreen() {
|
|
@@ -110,7 +110,7 @@ function LocalizationScreen() {
|
|
|
110
110
|
```
|
|
111
111
|
|
|
112
112
|
```vue
|
|
113
|
-
<!-- Vue
|
|
113
|
+
<!-- Vue - examples/expo-vue-sfc/screens/LocalizationScreen.vue -->
|
|
114
114
|
<script setup lang="ts">
|
|
115
115
|
import { useLocales, useCalendars } from '@symbiote-native/localization/vue';
|
|
116
116
|
|
|
@@ -140,7 +140,7 @@ const calendars = useCalendars(); // Ref<Calendar[]>
|
|
|
140
140
|
```
|
|
141
141
|
|
|
142
142
|
```tsx
|
|
143
|
-
// Solid
|
|
143
|
+
// Solid - an accessor per getter; call it to read, so a component body that runs once still
|
|
144
144
|
// re-renders the leaf that reads it.
|
|
145
145
|
import {
|
|
146
146
|
createLocales,
|
|
@@ -161,7 +161,7 @@ function LocalizationScreen() {
|
|
|
161
161
|
```
|
|
162
162
|
|
|
163
163
|
```ts
|
|
164
|
-
// Angular
|
|
164
|
+
// Angular - examples/expo-angular/src/screens/LocalizationScreen.ts
|
|
165
165
|
import { Component, inject } from '@angular/core';
|
|
166
166
|
import {
|
|
167
167
|
LocalesService,
|
|
@@ -179,7 +179,7 @@ export class LocalizationScreen {
|
|
|
179
179
|
|
|
180
180
|
Two independent synchronous getters (`getLocales`, `getCalendars`), each with its own
|
|
181
181
|
listener-based subscription (`addLocaleListener`/`addCalendarListener`) and its own adapter-level
|
|
182
|
-
lifecycle hook
|
|
182
|
+
lifecycle hook - one hook/composable/service per getter, matching upstream's own `useLocales`/
|
|
183
183
|
`useCalendars` being two separate hooks, not one combined hook.
|
|
184
184
|
|
|
185
185
|
```ts
|
|
@@ -189,7 +189,7 @@ addLocaleListener(listener): EventSubscription // fires when loc
|
|
|
189
189
|
addCalendarListener(listener): EventSubscription // fires when calendar settings change
|
|
190
190
|
```
|
|
191
191
|
|
|
192
|
-
Plus `Locale`, `Weekday` (enum), `CalendarIdentifier` (enum), `Calendar`
|
|
192
|
+
Plus `Locale`, `Weekday` (enum), `CalendarIdentifier` (enum), `Calendar` - ported from upstream's
|
|
193
193
|
`Localization.types.ts`.
|
|
194
194
|
|
|
195
195
|
```ts
|
|
@@ -211,16 +211,26 @@ import {
|
|
|
211
211
|
```
|
|
212
212
|
|
|
213
213
|
Each hook/composable/service seeds its initial value from the matching synchronous `get*()`
|
|
214
|
-
call, then subscribes to the matching listener for updates, and unsubscribes on unmount
|
|
214
|
+
call, then subscribes to the matching listener for updates, and unsubscribes on unmount:
|
|
215
215
|
mirroring upstream's own `useLocales`/`useCalendars`.
|
|
216
216
|
|
|
217
|
+
## Common questions
|
|
218
|
+
|
|
219
|
+
- **Locale changed in Settings, app shows the old one.** On Android re-read the locales when the
|
|
220
|
+
app returns to the foreground (`AppState`); on iOS they stay fixed while the app runs.
|
|
221
|
+
- **Empty `getLocales()`?** No, it has at least one entry, in the user's order.
|
|
222
|
+
- **In-app language picker?** Often unnecessary: iOS and Android support per-app language.
|
|
223
|
+
|
|
224
|
+
Sources: [Expo docs: Localization](https://docs.expo.dev/versions/latest/sdk/localization/),
|
|
225
|
+
[React Native localization with Expo](https://better-i18n.com/en/blog/react-native-expo-localization/).
|
|
226
|
+
|
|
217
227
|
## Test it
|
|
218
228
|
|
|
219
|
-
No Fabric/Descriptor angle at all
|
|
229
|
+
No Fabric/Descriptor angle at all - localization is a pure synchronous-function + `EventEmitter`
|
|
220
230
|
listener surface, never a view. Tests inject a fake native-module object in place of the real
|
|
221
231
|
`requireNativeModule` resolution (`src/core/localization.test.ts`,
|
|
222
232
|
`src/{react,vue,svelte,solid,angular}/**/*.test.{ts,tsx}`, `vitest`), the same pattern
|
|
223
|
-
`@symbiote-native/battery` and `@symbiote-native/device` use
|
|
233
|
+
`@symbiote-native/battery` and `@symbiote-native/device` use - no `installFabric()`, no
|
|
224
234
|
ViewConfig for the core test; the adapter hook/composable/service tests do use `installFabric()`
|
|
225
235
|
purely to mount a host component, same as every other sibling package. Native rendering itself is
|
|
226
|
-
verified on-device
|
|
236
|
+
verified on-device - see the parent [README](../../README.md).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/localization",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.3",
|
|
4
4
|
"description": "expo-localization wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, Svelte, Solid, and Angular adapters. Two independent synchronous getters (getLocales, getCalendars), each with its own native change listener and its own reactive hook per adapter.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"expo-localization": "57.0.1",
|
|
73
|
-
"expo-modules-core": "57.0.
|
|
73
|
+
"expo-modules-core": "57.0.20"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
76
|
"@angular/core": ">=20",
|
|
@@ -80,12 +80,12 @@
|
|
|
80
80
|
"solid-js": ">=1.9.0",
|
|
81
81
|
"svelte": ">=5.56.0",
|
|
82
82
|
"vue": ">=3.5.0",
|
|
83
|
-
"@symbiote-native/angular": "^3.
|
|
84
|
-
"@symbiote-native/engine": "^1.
|
|
85
|
-
"@symbiote-native/react": "^3.0
|
|
86
|
-
"@symbiote-native/solid": "^3.0
|
|
87
|
-
"@symbiote-native/svelte": "^3.0
|
|
88
|
-
"@symbiote-native/vue": "^3.0
|
|
83
|
+
"@symbiote-native/angular": "^3.2.0",
|
|
84
|
+
"@symbiote-native/engine": "^1.5.0",
|
|
85
|
+
"@symbiote-native/react": "^3.2.0",
|
|
86
|
+
"@symbiote-native/solid": "^3.1.0",
|
|
87
|
+
"@symbiote-native/svelte": "^3.1.0",
|
|
88
|
+
"@symbiote-native/vue": "^3.2.0"
|
|
89
89
|
},
|
|
90
90
|
"peerDependenciesMeta": {
|
|
91
91
|
"@symbiote-native/angular": {
|
|
@@ -133,13 +133,13 @@
|
|
|
133
133
|
"solid-js": "^1.9.14",
|
|
134
134
|
"svelte": "^5.56.0",
|
|
135
135
|
"typescript": "~6.0.0",
|
|
136
|
-
"@symbiote-native/angular": "3.
|
|
137
|
-
"@symbiote-native/engine": "1.
|
|
138
|
-
"@symbiote-native/react": "3.0
|
|
139
|
-
"@symbiote-native/solid": "3.0
|
|
140
|
-
"@symbiote-native/svelte": "3.0
|
|
141
|
-
"@symbiote-native/test-utils": "0.4.
|
|
142
|
-
"@symbiote-native/vue": "3.0
|
|
136
|
+
"@symbiote-native/angular": "3.2.0",
|
|
137
|
+
"@symbiote-native/engine": "1.5.0",
|
|
138
|
+
"@symbiote-native/react": "3.2.0",
|
|
139
|
+
"@symbiote-native/solid": "3.1.0",
|
|
140
|
+
"@symbiote-native/svelte": "3.1.0",
|
|
141
|
+
"@symbiote-native/test-utils": "0.4.6",
|
|
142
|
+
"@symbiote-native/vue": "3.2.0"
|
|
143
143
|
},
|
|
144
144
|
"scripts": {
|
|
145
145
|
"typecheck": "tsc --build",
|