@symbiote-native/localization 0.2.0 → 1.0.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 (35) hide show
  1. package/README.md +66 -17
  2. package/build/angular/services/calendars.service/index.js +2 -2
  3. package/build/angular/services/locales.service/index.js +1 -1
  4. package/build/core/index.d.ts +1 -1
  5. package/build/core/index.js +1 -1
  6. package/build/react/hooks/use-calendars/index.js +1 -1
  7. package/build/solid/index.d.ts +3 -0
  8. package/build/solid/index.js +8 -0
  9. package/build/solid/primitives/create-calendars.d.ts +3 -0
  10. package/build/solid/primitives/create-calendars.js +14 -0
  11. package/build/solid/primitives/create-locales.d.ts +3 -0
  12. package/build/solid/primitives/create-locales.js +24 -0
  13. package/build/svelte/runes/use-locales.svelte.js +1 -1
  14. package/build/vue/composables/use-locales/index.js +1 -1
  15. package/build-ngc/angular/services/calendars.service/index.js +2 -2
  16. package/build-ngc/angular/services/calendars.service/index.js.map +1 -1
  17. package/build-ngc/angular/services/locales.service/index.js +1 -1
  18. package/build-ngc/angular/services/locales.service/index.js.map +1 -1
  19. package/build-ngc/core/index.d.ts +1 -1
  20. package/build-ngc/core/index.js +1 -1
  21. package/build-ngc/core/index.js.map +1 -1
  22. package/build-ngc/core/localization.js.map +1 -1
  23. package/build-ngc/core/native-module.js.map +1 -1
  24. package/package.json +40 -14
  25. package/src/angular/services/calendars.service/index.ts +16 -3
  26. package/src/angular/services/locales.service/index.ts +8 -1
  27. package/src/core/index.ts +6 -1
  28. package/src/core/localization.ts +2 -1
  29. package/src/core/native-module.ts +11 -3
  30. package/src/react/hooks/use-calendars/index.ts +5 -1
  31. package/src/solid/index.ts +9 -0
  32. package/src/solid/primitives/create-calendars.ts +23 -0
  33. package/src/solid/primitives/create-locales.ts +33 -0
  34. package/src/svelte/runes/use-locales.svelte.ts +6 -1
  35. package/src/vue/composables/use-locales/index.ts +6 -1
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Port of [`expo-localization`](https://docs.expo.dev/versions/latest/sdk/localization/) for
4
4
  [SymbioteNative](../../README.md) — the device's locale list and preferred calendar settings,
5
- reachable from every adapter (React, Vue, Angular), not just React.
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
8
8
  [`@symbiote-native/device`](../device), an `expo-modules-core`-based wrapper (see the
@@ -29,12 +29,12 @@ Unlike a plain RN native module, `expo-localization`'s native code is discovered
29
29
  into the native host app **once**, covering this package and every other `expo-modules-core`
30
30
  package (`@symbiote-native/battery`, `@symbiote-native/device`, ...) with zero further changes:
31
31
 
32
- | Platform | Touches |
33
- |---|---|
34
- | iOS | `ios/Podfile` — add `use_expo_modules!` |
35
- | iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
36
- | Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
37
- | 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) |
32
+ | Platform | Touches |
33
+ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
34
+ | iOS | `ios/Podfile` — add `use_expo_modules!` |
35
+ | iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
36
+ | Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
37
+ | 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) |
38
38
 
39
39
  Full mechanics — the Podfile pieces that normally ship inside the `expo` package, the `expo`
40
40
  peer-dependency exclusion list — live in the `symbiote-expo-native-module` skill. Reference
@@ -55,13 +55,18 @@ src/core/ types.ts — Locale, Weekday, CalendarIdentifier, Calend
55
55
  native module.
56
56
  src/react/hooks/ @symbiote-native/localization/react — useLocales, useCalendars
57
57
  src/vue/composables/ @symbiote-native/localization/vue — same two names, Vue lifecycle
58
+ src/svelte/runes/ @symbiote-native/localization/svelte — same two names, read as `.current`
59
+ src/solid/primitives/ @symbiote-native/localization/solid — createLocales, createCalendars
60
+ (each returns an Accessor)
58
61
  src/angular/services/ @symbiote-native/localization/angular — LocalesService, CalendarsService
59
62
  (`.connect()` returns a Signal)
60
63
  ```
61
64
 
62
65
  Two independent getters, each with its own native change listener and its own reactive hook per
63
66
  adapter — mirroring `@symbiote-native/battery`'s shape of shipping several distinct hooks in one
64
- package, not one combined hook. Each hook/composable/service seeds its return value from the
67
+ package, not one combined hook. Solid's naming differs on purpose: `create*`, not `use*`, which
68
+ Solid reserves for consuming something that already exists. Each hook/composable/rune/primitive/
69
+ service seeds its return value from the
65
70
  matching synchronous `get*()` call (no initial "loading" state needed — the native call is
66
71
  sync, not async) and recomputes it whenever the matching listener fires.
67
72
 
@@ -72,8 +77,8 @@ sync, not async) and recomputes it whenever the matching listener fires.
72
77
  import { useLocales, useCalendars } from '@symbiote-native/localization/react';
73
78
 
74
79
  function LocalizationScreen() {
75
- const locales = useLocales(); // Locale[], guaranteed at least 1 element
76
- const calendars = useCalendars(); // Calendar[], guaranteed at least 1 element
80
+ const locales = useLocales(); // Locale[], guaranteed at least 1 element
81
+ const calendars = useCalendars(); // Calendar[], guaranteed at least 1 element
77
82
 
78
83
  return (
79
84
  <>
@@ -89,7 +94,7 @@ function LocalizationScreen() {
89
94
  <script setup lang="ts">
90
95
  import { useLocales, useCalendars } from '@symbiote-native/localization/vue';
91
96
 
92
- const locales = useLocales(); // Ref<Locale[]>
97
+ const locales = useLocales(); // Ref<Locale[]>
93
98
  const calendars = useCalendars(); // Ref<Calendar[]>
94
99
  </script>
95
100
  <template>
@@ -98,15 +103,51 @@ const calendars = useCalendars(); // Ref<Calendar[]>
98
103
  </template>
99
104
  ```
100
105
 
106
+ ```svelte
107
+ <!-- Svelte -->
108
+ <script lang="ts">
109
+ import { useLocales, useCalendars } from '@symbiote-native/localization/svelte';
110
+
111
+ const locales = useLocales(); // { readonly current: Locale[] }
112
+ const calendars = useCalendars(); // { readonly current: Calendar[] }
113
+ </script>
114
+ <Text>{locales.current[0].languageTag}</Text>
115
+ <Text>{calendars.current[0].timeZone}</Text>
116
+ ```
117
+
118
+ ```tsx
119
+ // Solid — an accessor per getter; call it to read, so a component body that runs once still
120
+ // re-renders the leaf that reads it.
121
+ import {
122
+ createLocales,
123
+ createCalendars,
124
+ } from '@symbiote-native/localization/solid';
125
+
126
+ function LocalizationScreen() {
127
+ const locales = createLocales(); // Accessor<Locale[]>
128
+ const calendars = createCalendars(); // Accessor<Calendar[]>
129
+
130
+ return (
131
+ <>
132
+ <Text>{locales()[0].languageTag}</Text>
133
+ <Text>{calendars()[0].timeZone}</Text>
134
+ </>
135
+ );
136
+ }
137
+ ```
138
+
101
139
  ```ts
102
140
  // Angular — examples/expo-angular/src/screens/LocalizationScreen.ts
103
141
  import { Component, inject } from '@angular/core';
104
- import { LocalesService, CalendarsService } from '@symbiote-native/localization/angular';
142
+ import {
143
+ LocalesService,
144
+ CalendarsService,
145
+ } from '@symbiote-native/localization/angular';
105
146
 
106
- @Component({ /* ... */ })
147
+ @Component({/* ... */})
107
148
  export class LocalizationScreen {
108
- readonly locales = inject(LocalesService).connect(); // Signal<Locale[]>
109
- readonly calendars = inject(CalendarsService).connect(); // Signal<Calendar[]>
149
+ readonly locales = inject(LocalesService).connect(); // Signal<Locale[]>
150
+ readonly calendars = inject(CalendarsService).connect(); // Signal<Calendar[]>
110
151
  }
111
152
  ```
112
153
 
@@ -134,7 +175,15 @@ import { getLocales, getCalendars } from '@symbiote-native/localization';
134
175
  // hook/composable/service per getter:
135
176
  import { useLocales, useCalendars } from '@symbiote-native/localization/react';
136
177
  import { useLocales, useCalendars } from '@symbiote-native/localization/vue';
137
- import { LocalesService, CalendarsService } from '@symbiote-native/localization/angular';
178
+ import { useLocales, useCalendars } from '@symbiote-native/localization/svelte';
179
+ import {
180
+ createLocales,
181
+ createCalendars,
182
+ } from '@symbiote-native/localization/solid';
183
+ import {
184
+ LocalesService,
185
+ CalendarsService,
186
+ } from '@symbiote-native/localization/angular';
138
187
  ```
139
188
 
140
189
  Each hook/composable/service seeds its initial value from the matching synchronous `get*()`
@@ -146,7 +195,7 @@ mirroring upstream's own `useLocales`/`useCalendars`.
146
195
  No Fabric/Descriptor angle at all — localization is a pure synchronous-function + `EventEmitter`
147
196
  listener surface, never a view. Tests inject a fake native-module object in place of the real
148
197
  `requireNativeModule` resolution (`src/core/localization.test.ts`,
149
- `src/{react,vue,angular}/**/*.test.{ts,tsx}`, `vitest`), the same pattern
198
+ `src/{react,vue,svelte,solid,angular}/**/*.test.{ts,tsx}`, `vitest`), the same pattern
150
199
  `@symbiote-native/battery` and `@symbiote-native/device` use — no `installFabric()`, no
151
200
  ViewConfig for the core test; the adapter hook/composable/service tests do use `installFabric()`
152
201
  purely to mount a host component, same as every other sibling package. Native rendering itself is
@@ -32,8 +32,8 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
32
32
  }
33
33
  return useValue ? value : void 0;
34
34
  };
35
- import { effect, inject, Injectable, Injector, signal } from '@angular/core';
36
- import { addCalendarListener, getCalendars } from '../../../core/index.js';
35
+ import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
36
+ import { addCalendarListener, getCalendars, } from '../../../core/index.js';
37
37
  // Angular twin of React's `useCalendars` hook and Vue's `useCalendars` composable — mirrors
38
38
  // LocalesService's shape exactly.
39
39
  //
@@ -32,7 +32,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
32
32
  }
33
33
  return useValue ? value : void 0;
34
34
  };
35
- import { effect, inject, Injectable, Injector, signal } from '@angular/core';
35
+ import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
36
36
  import { addLocaleListener, getLocales } from '../../../core/index.js';
37
37
  // Angular twin of React's `useLocales` hook and Vue's `useLocales` composable. See
38
38
  // packages/battery's BatteryStateService for the `connect()` pattern rationale. getLocales() is
@@ -1,4 +1,4 @@
1
1
  export { getLocales, getCalendars } from './localization';
2
2
  export { addLocaleListener, addCalendarListener } from './native-module';
3
- export { Weekday, CalendarIdentifier, type Locale, type Calendar } from './types';
3
+ export { Weekday, CalendarIdentifier, type Locale, type Calendar, } from './types';
4
4
  export type { EventSubscription } from 'expo-modules-core';
@@ -1,3 +1,3 @@
1
1
  export { getLocales, getCalendars } from './localization.js';
2
2
  export { addLocaleListener, addCalendarListener } from './native-module.js';
3
- export { Weekday, CalendarIdentifier } from './types.js';
3
+ export { Weekday, CalendarIdentifier, } from './types.js';
@@ -1,7 +1,7 @@
1
1
  // React lifecycle wiring over the framework-agnostic core — mirrors use-locales' shape exactly.
2
2
  // Ported from expo-localization's own useCalendars hook (sdk-57).
3
3
  import { useEffect, useMemo, useReducer } from 'react';
4
- import { addCalendarListener, getCalendars } from '../../../core/index.js';
4
+ import { addCalendarListener, getCalendars, } from '../../../core/index.js';
5
5
  export function useCalendars() {
6
6
  const [invalidationKey, invalidate] = useReducer((key) => key + 1, 0);
7
7
  const calendars = useMemo(() => getCalendars(), [invalidationKey]);
@@ -0,0 +1,3 @@
1
+ export { createLocales } from './primitives/create-locales';
2
+ export { createCalendars } from './primitives/create-calendars';
3
+ export * from '../core';
@@ -0,0 +1,8 @@
1
+ // @symbiote-native/localization/solid: the Solid entry over the framework-agnostic core. Locales
2
+ // and calendars each get their own primitive, matching upstream's own useLocales/useCalendars
3
+ // being two separate hooks, not one combined hook — mirrors the lifecycle-bucket naming of
4
+ // adapters/solid/src/primitives (never `hooks/`/`composables/`/`runes/`, and `create*` rather
5
+ // than `use*`, which Solid reserves for consuming something that already exists).
6
+ export { createLocales } from './primitives/create-locales.js';
7
+ export { createCalendars } from './primitives/create-calendars.js';
8
+ export * from '../core/index.js';
@@ -0,0 +1,3 @@
1
+ import { type Accessor } from 'solid-js';
2
+ import { type Calendar } from '../../core';
3
+ export declare function createCalendars(): Accessor<Calendar[]>;
@@ -0,0 +1,14 @@
1
+ // Solid lifecycle wiring over the framework-agnostic core (core/localization.ts) — mirrors
2
+ // create-locales' shape exactly, including why no seed-vs-event guard is needed here.
3
+ import { createSignal, onCleanup } from 'solid-js';
4
+ import { addCalendarListener, getCalendars, } from '../../core/index.js';
5
+ export function createCalendars() {
6
+ const [calendars, setCalendars] = createSignal(getCalendars());
7
+ const subscription = addCalendarListener(() => {
8
+ setCalendars(getCalendars());
9
+ });
10
+ onCleanup(() => {
11
+ subscription.remove();
12
+ });
13
+ return calendars;
14
+ }
@@ -0,0 +1,3 @@
1
+ import { type Accessor } from 'solid-js';
2
+ import { type Locale } from '../../core';
3
+ export declare function createLocales(): Accessor<Locale[]>;
@@ -0,0 +1,24 @@
1
+ // Solid lifecycle wiring over the framework-agnostic core (core/localization.ts) — the twin of
2
+ // react/hooks/use-locales, vue/composables/use-locales and svelte/runes/use-locales.svelte. See
3
+ // adapters/solid/src/primitives/create-color-scheme.ts for why the bucket is `primitives/` and
4
+ // the name is `create*` rather than `use*`.
5
+ //
6
+ // Returns an ACCESSOR, never a snapshot: a Solid component body runs ONCE, so a returned array
7
+ // would pin the screen to the locale list the app booted with.
8
+ //
9
+ // getLocales() is a SYNCHRONOUS native read, so seed and subscribe both happen inline in the body
10
+ // and there is no seed-vs-event race to guard (unlike the battery primitives, whose seed is a
11
+ // promise) — and no re-read on mount either, which the Vue and Svelte twins need only because
12
+ // their subscription is deferred to an effect.
13
+ import { createSignal, onCleanup } from 'solid-js';
14
+ import { addLocaleListener, getLocales, } from '../../core/index.js';
15
+ export function createLocales() {
16
+ const [locales, setLocales] = createSignal(getLocales());
17
+ const subscription = addLocaleListener(() => {
18
+ setLocales(getLocales());
19
+ });
20
+ onCleanup(() => {
21
+ subscription.remove();
22
+ });
23
+ return locales;
24
+ }
@@ -7,7 +7,7 @@
7
7
  // Returns a boxed getter, not a bare `$state`: Svelte 5 reactivity is lexically scoped to the
8
8
  // declaring module and doesn't survive being returned raw from a plain function, so the caller
9
9
  // reads `.current` like unwrapping Vue's `Ref` via `.value`.
10
- import { addLocaleListener, getLocales } from '../../core/index.js';
10
+ import { addLocaleListener, getLocales, } from '../../core/index.js';
11
11
  export function useLocales() {
12
12
  let locales = $state(getLocales());
13
13
  $effect(() => {
@@ -4,7 +4,7 @@
4
4
  // setup — onMounted only wires the change listener, mirroring use-network-state's
5
5
  // onMounted/onUnmounted subscription shape otherwise.
6
6
  import { onMounted, onUnmounted, ref } from '@vue/runtime-core';
7
- import { addLocaleListener, getLocales } from '../../../core/index.js';
7
+ import { addLocaleListener, getLocales, } from '../../../core/index.js';
8
8
  export function useLocales() {
9
9
  const locales = ref(getLocales());
10
10
  let subscription;
@@ -1,5 +1,5 @@
1
- import { effect, inject, Injectable, Injector, signal } from '@angular/core';
2
- import { addCalendarListener, getCalendars } from '../../../core';
1
+ import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
2
+ import { addCalendarListener, getCalendars, } from '../../../core';
3
3
  import * as i0 from "@angular/core";
4
4
  // Angular twin of React's `useCalendars` hook and Vue's `useCalendars` composable — mirrors
5
5
  // LocalesService's shape exactly.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/calendars.service/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAiB,MAAM,eAAe,CAAC;;AAEjF,4FAA4F;AAC5F,kCAAkC;AAClC,EAAE;AACF,6DAA6D;AAC7D,mCAAmC;AAEnC,MAAM,OAAO,gBAAgB;IACV,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE7C,OAAO;QACL,MAAM,SAAS,GAAG,MAAM,CAAa,YAAY,EAAE;sFAAC,CAAC;QAErD,MAAM,CACJ,SAAS,CAAC,EAAE;YACV,MAAM,YAAY,GAAG,mBAAmB,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;YAC9E,SAAS,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,CAAC,EACD,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAC5B,CAAC;QAEF,OAAO,SAAS,CAAC,UAAU,EAAE,CAAC;IAChC,CAAC;uGAfU,gBAAgB;2GAAhB,gBAAgB,cADH,MAAM;;2FACnB,gBAAgB;kBAD5B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/calendars.service/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,MAAM,EACN,UAAU,EACV,QAAQ,EACR,MAAM,GAEP,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,mBAAmB,EACnB,YAAY,GAEb,MAAM,eAAe,CAAC;;AAEvB,4FAA4F;AAC5F,kCAAkC;AAClC,EAAE;AACF,6DAA6D;AAC7D,mCAAmC;AAEnC,MAAM,OAAO,gBAAgB;IACV,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE7C,OAAO;QACL,MAAM,SAAS,GAAG,MAAM,CAAa,YAAY,EAAE;sFAAC,CAAC;QAErD,MAAM,CACJ,SAAS,CAAC,EAAE;YACV,MAAM,YAAY,GAAG,mBAAmB,CAAC,GAAG,EAAE,CAC5C,SAAS,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,CAC9B,CAAC;YACF,SAAS,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,CAAC,EACD,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAC5B,CAAC;QAEF,OAAO,SAAS,CAAC,UAAU,EAAE,CAAC;IAChC,CAAC;uGAjBU,gBAAgB;2GAAhB,gBAAgB,cADH,MAAM;;2FACnB,gBAAgB;kBAD5B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE"}
@@ -1,4 +1,4 @@
1
- import { effect, inject, Injectable, Injector, signal } from '@angular/core';
1
+ import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
2
2
  import { addLocaleListener, getLocales } from '../../../core';
3
3
  import * as i0 from "@angular/core";
4
4
  // Angular twin of React's `useLocales` hook and Vue's `useLocales` composable. See
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/locales.service/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAe,MAAM,eAAe,CAAC;;AAE3E,mFAAmF;AACnF,gGAAgG;AAChG,0FAA0F;AAC1F,EAAE;AACF,yDAAyD;AACzD,iCAAiC;AAEjC,MAAM,OAAO,cAAc;IACR,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE7C,OAAO;QACL,MAAM,OAAO,GAAG,MAAM,CAAW,UAAU,EAAE;oFAAC,CAAC;QAE/C,MAAM,CACJ,SAAS,CAAC,EAAE;YACV,MAAM,YAAY,GAAG,iBAAiB,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;YACxE,SAAS,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,CAAC,EACD,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAC5B,CAAC;QAEF,OAAO,OAAO,CAAC,UAAU,EAAE,CAAC;IAC9B,CAAC;uGAfU,cAAc;2GAAd,cAAc,cADD,MAAM;;2FACnB,cAAc;kBAD1B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/locales.service/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,MAAM,EACN,UAAU,EACV,QAAQ,EACR,MAAM,GAEP,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAe,MAAM,eAAe,CAAC;;AAE3E,mFAAmF;AACnF,gGAAgG;AAChG,0FAA0F;AAC1F,EAAE;AACF,yDAAyD;AACzD,iCAAiC;AAEjC,MAAM,OAAO,cAAc;IACR,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE7C,OAAO;QACL,MAAM,OAAO,GAAG,MAAM,CAAW,UAAU,EAAE;oFAAC,CAAC;QAE/C,MAAM,CACJ,SAAS,CAAC,EAAE;YACV,MAAM,YAAY,GAAG,iBAAiB,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;YACxE,SAAS,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,CAAC,EACD,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAC5B,CAAC;QAEF,OAAO,OAAO,CAAC,UAAU,EAAE,CAAC;IAC9B,CAAC;uGAfU,cAAc;2GAAd,cAAc,cADD,MAAM;;2FACnB,cAAc;kBAD1B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE"}
@@ -1,4 +1,4 @@
1
1
  export { getLocales, getCalendars } from './localization';
2
2
  export { addLocaleListener, addCalendarListener } from './native-module';
3
- export { Weekday, CalendarIdentifier, type Locale, type Calendar } from './types';
3
+ export { Weekday, CalendarIdentifier, type Locale, type Calendar, } from './types';
4
4
  export type { EventSubscription } from 'expo-modules-core';
@@ -1,4 +1,4 @@
1
1
  export { getLocales, getCalendars } from './localization';
2
2
  export { addLocaleListener, addCalendarListener } from './native-module';
3
- export { Weekday, CalendarIdentifier } from './types';
3
+ export { Weekday, CalendarIdentifier, } from './types';
4
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAA8B,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EACL,OAAO,EACP,kBAAkB,GAGnB,MAAM,SAAS,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"localization.js","sourceRoot":"","sources":["../../src/core/localization.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,gGAAgG;AAChG,+BAA+B;AAC/B,OAAO,sBAAsB,MAAM,iBAAiB,CAAC;AAGrD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,UAAU,GAAmB,sBAAsB,CAAC,UAAU,CAAC;AAE5E;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,YAAY,GAAqB,sBAAsB,CAAC,YAAY,CAAC"}
1
+ {"version":3,"file":"localization.js","sourceRoot":"","sources":["../../src/core/localization.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,gGAAgG;AAChG,+BAA+B;AAC/B,OAAO,sBAAsB,MAAM,iBAAiB,CAAC;AAGrD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,UAAU,GAAmB,sBAAsB,CAAC,UAAU,CAAC;AAE5E;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,YAAY,GACvB,sBAAsB,CAAC,YAAY,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,+FAA+F;AAC/F,6EAA6E;AAC7E,gGAAgG;AAChG,4FAA4F;AAC5F,4EAA4E;AAC5E,OAAO,EAA0B,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGhF,MAAM,6BAA6B,GAAG,kBAAkB,CAAC;AAezD,MAAM,sBAAsB,GAAG,mBAAmB,CAChD,6BAA6B,CAC9B,CAAC;AAEF,MAAM,UAAU,iBAAiB;AAC/B,oFAAoF;AACpF,iFAAiF;AACjF,QAAmC;IAEnC,OAAO,sBAAsB,CAAC,WAAW,CAAC,yBAAyB,EAAE,QAAQ,CAAC,CAAC;AACjF,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,QAAmC;IACrE,OAAO,sBAAsB,CAAC,WAAW,CAAC,2BAA2B,EAAE,QAAQ,CAAC,CAAC;AACnF,CAAC;AAED,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,+FAA+F;AAC/F,6EAA6E;AAC7E,gGAAgG;AAChG,4FAA4F;AAC5F,4EAA4E;AAC5E,OAAO,EAA0B,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGhF,MAAM,6BAA6B,GAAG,kBAAkB,CAAC;AAezD,MAAM,sBAAsB,GAAG,mBAAmB,CAChD,6BAA6B,CAC9B,CAAC;AAEF,MAAM,UAAU,iBAAiB;AAC/B,oFAAoF;AACpF,iFAAiF;AACjF,QAAmC;IAEnC,OAAO,sBAAsB,CAAC,WAAW,CACvC,yBAAyB,EACzB,QAAQ,CACT,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,QAAmC;IAEnC,OAAO,sBAAsB,CAAC,WAAW,CACvC,2BAA2B,EAC3B,QAAQ,CACT,CAAC;AACJ,CAAC;AAED,eAAe,sBAAsB,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@symbiote-native/localization",
3
- "version": "0.2.0",
4
- "description": "expo-localization wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, and Angular adapters. Two independent synchronous getters (getLocales, getCalendars), each with its own native change listener and its own reactive hook per adapter.",
3
+ "version": "1.0.0",
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": {
7
7
  "type": "git",
@@ -13,6 +13,18 @@
13
13
  "url": "https://github.com/OneEyed1366/symbiote-native/issues"
14
14
  },
15
15
  "author": "Andrey Prokopenko <psevdoproger@gmail.com>",
16
+ "keywords": [
17
+ "react-native",
18
+ "symbiote-native",
19
+ "expo-localization",
20
+ "react",
21
+ "vue",
22
+ "svelte",
23
+ "solid",
24
+ "angular",
25
+ "locale",
26
+ "calendar"
27
+ ],
16
28
  "type": "module",
17
29
  "main": "./build/core/index.js",
18
30
  "module": "./build/core/index.js",
@@ -34,6 +46,10 @@
34
46
  "types": "./build/svelte/index.d.ts",
35
47
  "default": "./build/svelte/index.js"
36
48
  },
49
+ "./solid": {
50
+ "types": "./build/solid/index.d.ts",
51
+ "default": "./build/solid/index.js"
52
+ },
37
53
  "./angular": {
38
54
  "types": "./build-ngc/angular/index.d.ts",
39
55
  "react-native": "./build-ngc/angular/index.js",
@@ -57,17 +73,19 @@
57
73
  "expo-modules-core": "57.0.5"
58
74
  },
59
75
  "peerDependencies": {
60
- "@symbiote-native/angular": ">=0.6.2",
61
- "@symbiote-native/engine": ">=0.1.7",
62
- "@symbiote-native/react": ">=0.2.8",
63
- "@symbiote-native/svelte": ">=0.1.0",
64
- "@symbiote-native/vue": ">=0.3.8",
65
76
  "@angular/core": ">=20",
66
77
  "@vue/runtime-core": "^3.5.13",
67
78
  "react": ">=19.0.0",
68
79
  "react-native": ">=0.86",
80
+ "solid-js": ">=1.9.0",
69
81
  "svelte": ">=5.56.0",
70
- "vue": ">=3.5.0"
82
+ "vue": ">=3.5.0",
83
+ "@symbiote-native/angular": "^1.0.0",
84
+ "@symbiote-native/engine": "^0.4.0",
85
+ "@symbiote-native/react": "^1.0.0",
86
+ "@symbiote-native/solid": "^1.0.0",
87
+ "@symbiote-native/svelte": "^1.0.0",
88
+ "@symbiote-native/vue": "^1.0.0"
71
89
  },
72
90
  "peerDependenciesMeta": {
73
91
  "@symbiote-native/angular": {
@@ -76,6 +94,9 @@
76
94
  "@symbiote-native/react": {
77
95
  "optional": true
78
96
  },
97
+ "@symbiote-native/solid": {
98
+ "optional": true
99
+ },
79
100
  "@symbiote-native/svelte": {
80
101
  "optional": true
81
102
  },
@@ -91,6 +112,9 @@
91
112
  "react": {
92
113
  "optional": true
93
114
  },
115
+ "solid-js": {
116
+ "optional": true
117
+ },
94
118
  "svelte": {
95
119
  "optional": true
96
120
  },
@@ -106,14 +130,16 @@
106
130
  "@types/react": "^19.2.0",
107
131
  "@vue/runtime-core": "^3.5.13",
108
132
  "react": "19.2.3",
133
+ "solid-js": "^1.9.14",
109
134
  "svelte": "^5.56.0",
110
135
  "typescript": "~6.0.0",
111
- "@symbiote-native/angular": "0.7.0",
112
- "@symbiote-native/engine": "0.2.0",
113
- "@symbiote-native/react": "0.3.0",
114
- "@symbiote-native/svelte": "0.2.0",
115
- "@symbiote-native/test-utils": "0.1.6",
116
- "@symbiote-native/vue": "0.4.0"
136
+ "@symbiote-native/angular": "1.0.0",
137
+ "@symbiote-native/engine": "0.4.0",
138
+ "@symbiote-native/react": "1.0.0",
139
+ "@symbiote-native/solid": "1.0.0",
140
+ "@symbiote-native/svelte": "1.0.0",
141
+ "@symbiote-native/test-utils": "0.3.0",
142
+ "@symbiote-native/vue": "1.0.0"
117
143
  },
118
144
  "scripts": {
119
145
  "typecheck": "tsc --build",
@@ -1,5 +1,16 @@
1
- import { effect, inject, Injectable, Injector, signal, type Signal } from '@angular/core';
2
- import { addCalendarListener, getCalendars, type Calendar } from '../../../core';
1
+ import {
2
+ effect,
3
+ inject,
4
+ Injectable,
5
+ Injector,
6
+ signal,
7
+ type Signal,
8
+ } from '@angular/core';
9
+ import {
10
+ addCalendarListener,
11
+ getCalendars,
12
+ type Calendar,
13
+ } from '../../../core';
3
14
 
4
15
  // Angular twin of React's `useCalendars` hook and Vue's `useCalendars` composable — mirrors
5
16
  // LocalesService's shape exactly.
@@ -15,7 +26,9 @@ export class CalendarsService {
15
26
 
16
27
  effect(
17
28
  onCleanup => {
18
- const subscription = addCalendarListener(() => calendars.set(getCalendars()));
29
+ const subscription = addCalendarListener(() =>
30
+ calendars.set(getCalendars()),
31
+ );
19
32
  onCleanup(() => subscription.remove());
20
33
  },
21
34
  { injector: this.injector },
@@ -1,4 +1,11 @@
1
- import { effect, inject, Injectable, Injector, signal, type Signal } from '@angular/core';
1
+ import {
2
+ effect,
3
+ inject,
4
+ Injectable,
5
+ Injector,
6
+ signal,
7
+ type Signal,
8
+ } from '@angular/core';
2
9
  import { addLocaleListener, getLocales, type Locale } from '../../../core';
3
10
 
4
11
  // Angular twin of React's `useLocales` hook and Vue's `useLocales` composable. See
package/src/core/index.ts CHANGED
@@ -1,4 +1,9 @@
1
1
  export { getLocales, getCalendars } from './localization';
2
2
  export { addLocaleListener, addCalendarListener } from './native-module';
3
- export { Weekday, CalendarIdentifier, type Locale, type Calendar } from './types';
3
+ export {
4
+ Weekday,
5
+ CalendarIdentifier,
6
+ type Locale,
7
+ type Calendar,
8
+ } from './types';
4
9
  export type { EventSubscription } from 'expo-modules-core';
@@ -42,4 +42,5 @@ export const getLocales: () => Locale[] = ExpoLocalizationModule.getLocales;
42
42
  * }]
43
43
  * ```
44
44
  */
45
- export const getCalendars: () => Calendar[] = ExpoLocalizationModule.getCalendars;
45
+ export const getCalendars: () => Calendar[] =
46
+ ExpoLocalizationModule.getCalendars;
@@ -31,11 +31,19 @@ export function addLocaleListener(
31
31
  // invalidate signal for useLocales/the Vue composable/the Angular service below.
32
32
  listener: (event?: unknown) => void,
33
33
  ): EventSubscription {
34
- return ExpoLocalizationModule.addListener('onLocaleSettingsChanged', listener);
34
+ return ExpoLocalizationModule.addListener(
35
+ 'onLocaleSettingsChanged',
36
+ listener,
37
+ );
35
38
  }
36
39
 
37
- export function addCalendarListener(listener: (event?: unknown) => void): EventSubscription {
38
- return ExpoLocalizationModule.addListener('onCalendarSettingsChanged', listener);
40
+ export function addCalendarListener(
41
+ listener: (event?: unknown) => void,
42
+ ): EventSubscription {
43
+ return ExpoLocalizationModule.addListener(
44
+ 'onCalendarSettingsChanged',
45
+ listener,
46
+ );
39
47
  }
40
48
 
41
49
  export default ExpoLocalizationModule;
@@ -1,7 +1,11 @@
1
1
  // React lifecycle wiring over the framework-agnostic core — mirrors use-locales' shape exactly.
2
2
  // Ported from expo-localization's own useCalendars hook (sdk-57).
3
3
  import { useEffect, useMemo, useReducer } from 'react';
4
- import { addCalendarListener, getCalendars, type Calendar } from '../../../core';
4
+ import {
5
+ addCalendarListener,
6
+ getCalendars,
7
+ type Calendar,
8
+ } from '../../../core';
5
9
 
6
10
  export function useCalendars(): Calendar[] {
7
11
  const [invalidationKey, invalidate] = useReducer((key: number) => key + 1, 0);
@@ -0,0 +1,9 @@
1
+ // @symbiote-native/localization/solid: the Solid entry over the framework-agnostic core. Locales
2
+ // and calendars each get their own primitive, matching upstream's own useLocales/useCalendars
3
+ // being two separate hooks, not one combined hook — mirrors the lifecycle-bucket naming of
4
+ // adapters/solid/src/primitives (never `hooks/`/`composables/`/`runes/`, and `create*` rather
5
+ // than `use*`, which Solid reserves for consuming something that already exists).
6
+
7
+ export { createLocales } from './primitives/create-locales';
8
+ export { createCalendars } from './primitives/create-calendars';
9
+ export * from '../core';
@@ -0,0 +1,23 @@
1
+ // Solid lifecycle wiring over the framework-agnostic core (core/localization.ts) — mirrors
2
+ // create-locales' shape exactly, including why no seed-vs-event guard is needed here.
3
+ import { createSignal, onCleanup, type Accessor } from 'solid-js';
4
+ import {
5
+ addCalendarListener,
6
+ getCalendars,
7
+ type Calendar,
8
+ type EventSubscription,
9
+ } from '../../core';
10
+
11
+ export function createCalendars(): Accessor<Calendar[]> {
12
+ const [calendars, setCalendars] = createSignal<Calendar[]>(getCalendars());
13
+
14
+ const subscription: EventSubscription = addCalendarListener(() => {
15
+ setCalendars(getCalendars());
16
+ });
17
+
18
+ onCleanup(() => {
19
+ subscription.remove();
20
+ });
21
+
22
+ return calendars;
23
+ }
@@ -0,0 +1,33 @@
1
+ // Solid lifecycle wiring over the framework-agnostic core (core/localization.ts) — the twin of
2
+ // react/hooks/use-locales, vue/composables/use-locales and svelte/runes/use-locales.svelte. See
3
+ // adapters/solid/src/primitives/create-color-scheme.ts for why the bucket is `primitives/` and
4
+ // the name is `create*` rather than `use*`.
5
+ //
6
+ // Returns an ACCESSOR, never a snapshot: a Solid component body runs ONCE, so a returned array
7
+ // would pin the screen to the locale list the app booted with.
8
+ //
9
+ // getLocales() is a SYNCHRONOUS native read, so seed and subscribe both happen inline in the body
10
+ // and there is no seed-vs-event race to guard (unlike the battery primitives, whose seed is a
11
+ // promise) — and no re-read on mount either, which the Vue and Svelte twins need only because
12
+ // their subscription is deferred to an effect.
13
+ import { createSignal, onCleanup, type Accessor } from 'solid-js';
14
+ import {
15
+ addLocaleListener,
16
+ getLocales,
17
+ type EventSubscription,
18
+ type Locale,
19
+ } from '../../core';
20
+
21
+ export function createLocales(): Accessor<Locale[]> {
22
+ const [locales, setLocales] = createSignal<Locale[]>(getLocales());
23
+
24
+ const subscription: EventSubscription = addLocaleListener(() => {
25
+ setLocales(getLocales());
26
+ });
27
+
28
+ onCleanup(() => {
29
+ subscription.remove();
30
+ });
31
+
32
+ return locales;
33
+ }
@@ -7,7 +7,12 @@
7
7
  // Returns a boxed getter, not a bare `$state`: Svelte 5 reactivity is lexically scoped to the
8
8
  // declaring module and doesn't survive being returned raw from a plain function, so the caller
9
9
  // reads `.current` like unwrapping Vue's `Ref` via `.value`.
10
- import { addLocaleListener, getLocales, type EventSubscription, type Locale } from '../../core';
10
+ import {
11
+ addLocaleListener,
12
+ getLocales,
13
+ type EventSubscription,
14
+ type Locale,
15
+ } from '../../core';
11
16
 
12
17
  export function useLocales(): { readonly current: Locale[] } {
13
18
  let locales = $state<Locale[]>(getLocales());
@@ -4,7 +4,12 @@
4
4
  // setup — onMounted only wires the change listener, mirroring use-network-state's
5
5
  // onMounted/onUnmounted subscription shape otherwise.
6
6
  import { onMounted, onUnmounted, ref, type Ref } from '@vue/runtime-core';
7
- import { addLocaleListener, getLocales, type EventSubscription, type Locale } from '../../../core';
7
+ import {
8
+ addLocaleListener,
9
+ getLocales,
10
+ type EventSubscription,
11
+ type Locale,
12
+ } from '../../../core';
8
13
 
9
14
  export function useLocales(): Ref<Locale[]> {
10
15
  const locales = ref<Locale[]>(getLocales());