@ankhorage/orchestrator-module-expo-localization 0.1.1 → 0.1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @ankhorage/orchestrator-module-expo-localization
2
2
 
3
+ ## 0.1.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 35a8eab: Generate localization wiring through `@ankh/runtime` renderer config (prop resolver + `setLanguage` action handler) instead of Surface translation context.
8
+
9
+ ## 0.1.2
10
+
11
+ ### Patch Changes
12
+
13
+ - Refresh the README copy so the published module overview and install usage stay aligned with the current messaging.
14
+
3
15
  ## 0.1.1
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -1,78 +1,16 @@
1
- # @ankhorage/orchestrator-module-expo-localization
1
+ # orchestrator-module-expo-localization
2
2
 
3
- `@ankhorage/orchestrator-module-expo-localization` is the first standalone module extracted for `@ankhorage/orchestrator`. It replaces the old localization plugin contract from `ankhorage4/packages/plugin-kit` with a pure module that only returns supported orchestrator actions.
3
+ Adds localization support to Expo apps.
4
4
 
5
- The repo follows the module-owned template pattern: generated runtime files come from checked-in [`templates/*.tpl`](./templates) assets that the module reads and materializes into orchestrator `write-files` actions.
6
-
7
- ## What it does
8
-
9
- - ensures `i18next`, `react-i18next`, and `expo-localization`
10
- - writes localization runtime files into `src/plugins/localization`
11
- - patches `src/app/_layout.tsx` to wrap the generated app shell with `LocalizationPluginProvider`
12
- - patches `app.config.ts` to register the Expo localization config plugin
13
- - keeps `ankh.config.json` aligned at `settings.localization`
14
-
15
- The module does not implement or depend on executor behavior outside the orchestrator contract. It only emits `ensure-packages`, `write-files`, `patch-text-block`, and `json-set`.
16
-
17
- ## Install
18
-
19
- ```sh
20
- bun add @ankhorage/orchestrator @ankhorage/orchestrator-module-expo-localization
21
- ```
22
-
23
- ## Usage
24
-
25
- ```ts
26
- import { createOrchestrator } from '@ankhorage/orchestrator';
27
- import { expoLocalizationModule } from '@ankhorage/orchestrator-module-expo-localization';
28
-
29
- const orchestrator = createOrchestrator({
30
- modules: [expoLocalizationModule],
31
- projectRoot: '/absolute/path/to/project',
32
- });
33
-
34
- await orchestrator.installModule('expo-localization', {
35
- config: {
36
- defaultLocale: 'en',
37
- locales: ['en', 'de'],
38
- translations: {
39
- en: { hello: 'Hello' },
40
- de: { hello: 'Hallo' },
41
- },
42
- },
43
- });
44
- ```
45
-
46
- ## Config
5
+ ## 🎯 What you get
6
+ - Localization setup instantly
7
+ - No boilerplate
8
+ - Fully reversible
47
9
 
10
+ ## 📦 Usage
48
11
  ```ts
49
- {
50
- defaultLocale?: string;
51
- locales?: string[];
52
- translations?: Record<string, Record<string, string>>;
53
- }
12
+ orchestrator.install('expo-localization')
54
13
  ```
55
14
 
56
- Defaults:
57
-
58
- - `defaultLocale`: `"en"`
59
- - `locales`: `["en"]`
60
- - `translations`: `{}`
61
-
62
- ## Assumptions
63
-
64
- - The target app already has a generated `src/app/_layout.tsx`.
65
- - The target app uses the standard `@/` alias layout generated by the surrounding Ankhorage toolchain.
66
-
67
- This package is meant for the newer `@ankhorage/orchestrator` stack and supersedes the old `@ankh/plugin-kit` localization plugin path that is planned for removal.
68
-
69
- ## Development
70
-
71
- ```sh
72
- bun install
73
- bun run format:check
74
- bun run lint
75
- bun run test
76
- bun run build
77
- bun run knip
78
- ```
15
+ ## 🧠 Why this exists
16
+ Standardizes localization setup across apps.
@@ -1 +1 @@
1
- {"version":3,"file":"templateFiles.d.ts","sourceRoot":"","sources":["../src/templateFiles.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AASpE,wBAAgB,2BAA2B,CAAC,IAAI,EAAE;IAChD,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACtD,GAAG,oBAAoB,EAAE,CAoCzB"}
1
+ {"version":3,"file":"templateFiles.d.ts","sourceRoot":"","sources":["../src/templateFiles.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AASpE,wBAAgB,2BAA2B,CAAC,IAAI,EAAE;IAChD,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACtD,GAAG,oBAAoB,EAAE,CAyCzB"}
@@ -22,6 +22,11 @@ export function buildLocalizationWriteFiles(args) {
22
22
  content: readTemplate('useT.ts.tpl'),
23
23
  overwrite: true,
24
24
  },
25
+ {
26
+ path: 'src/plugins/localization/runtimeLocalization.ts',
27
+ content: readTemplate('runtimeLocalization.ts.tpl'),
28
+ overwrite: true,
29
+ },
25
30
  {
26
31
  path: 'src/plugins/localization/LocalizationProvider.tsx',
27
32
  content: renderTemplate('LocalizationProvider.tsx.tpl', {
@@ -1 +1 @@
1
- {"version":3,"file":"templateFiles.js","sourceRoot":"","sources":["../src/templateFiles.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"templateFiles.js","sourceRoot":"","sources":["../src/templateFiles.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,10 +1,19 @@
1
1
  {
2
+ "license": "MIT",
2
3
  "name": "@ankhorage/orchestrator-module-expo-localization",
3
- "version": "0.1.1",
4
+ "version": "0.1.3",
4
5
  "description": "Expo localization module for @ankhorage/orchestrator.",
5
6
  "type": "module",
6
7
  "main": "./dist/index.js",
7
8
  "types": "./dist/index.d.ts",
9
+ "homepage": "https://github.com/ankhorage/orchestrator-module-expo-localization#readme",
10
+ "bugs": {
11
+ "url": "https://github.com/ankhorage/orchestrator-module-expo-localization/issues"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/ankhorage/orchestrator-module-expo-localization.git"
16
+ },
8
17
  "sideEffects": false,
9
18
  "files": [
10
19
  "dist",
@@ -28,22 +37,22 @@
28
37
  "knip": "knip",
29
38
  "test": "bun test",
30
39
  "changeset": "changeset",
31
- "changeset:version": "changeset version"
40
+ "version-packages": "changeset version"
32
41
  },
33
42
  "dependencies": {
34
- "@ankhorage/orchestrator": "^0.1.1"
43
+ "@ankhorage/orchestrator": "^0.2.1"
35
44
  },
36
45
  "devDependencies": {
37
- "@ankhorage/devtools": "^1.0.0",
38
- "@changesets/cli": "^2.30.0",
39
- "@types/bun": "^1.3.11",
40
- "@types/node": "^25.2.3",
41
- "eslint": "^10.2.0",
42
- "knip": "^5.83.1",
43
- "prettier": "^3.8.1",
46
+ "@ankhorage/devtools": "^1.0.1",
47
+ "@changesets/cli": "^2.31.0",
48
+ "@types/bun": "^1.3.13",
49
+ "@types/node": "^25.6.0",
50
+ "eslint": "^10.3.0",
51
+ "knip": "^5.88.1",
52
+ "prettier": "^3.8.3",
44
53
  "typescript": "^5.9.3"
45
54
  },
46
- "packageManager": "bun@1.3.11",
55
+ "packageManager": "bun@1.3.13",
47
56
  "publishConfig": {
48
57
  "access": "public"
49
58
  }
@@ -1,11 +1,14 @@
1
1
  /* generated by @ankhorage/orchestrator-module-expo-localization */
2
- import React, { useEffect, useState } from "react";
2
+ import { RuntimeRendererConfigProvider } from "@ankh/runtime";
3
+ import React, { useEffect, useMemo, useState } from "react";
3
4
  import * as ExpoLocalization from "expo-localization";
4
5
  import { useTranslation } from "react-i18next";
5
6
 
6
- import { TranslationProvider } from "@ankhorage/surface";
7
-
8
7
  import { initI18n } from "./i18n";
8
+ import {
9
+ createLocalizationActionHandlers,
10
+ createLocalizationNodePropsResolver,
11
+ } from "./runtimeLocalization";
9
12
 
10
13
  export interface LocalizationPluginConfig {
11
14
  defaultLocale?: string;
@@ -45,9 +48,18 @@ __RESOURCE_LINES__
45
48
  }
46
49
  }, [currentLocale, i18n.language]);
47
50
 
51
+ const runtimeRendererConfig = useMemo(() => {
52
+ const translate = i18n.isInitialized ? t : (key: string) => key;
53
+
54
+ return {
55
+ resolveNodeProps: createLocalizationNodePropsResolver(translate),
56
+ actionHandlers: createLocalizationActionHandlers(i18n),
57
+ };
58
+ }, [i18n, i18n.isInitialized, i18n.language, t]);
59
+
48
60
  return (
49
- <TranslationProvider t={i18n.isInitialized ? t : (key: string) => key} i18n={i18n}>
61
+ <RuntimeRendererConfigProvider value={runtimeRendererConfig}>
50
62
  {children}
51
- </TranslationProvider>
63
+ </RuntimeRendererConfigProvider>
52
64
  );
53
65
  }
@@ -0,0 +1,106 @@
1
+ /* generated by @ankhorage/orchestrator-module-expo-localization */
2
+ import type { RuntimeActionHandlers, RuntimeNodePropsResolver } from "@ankh/runtime";
3
+ import type { i18n as I18nInstance } from "i18next";
4
+
5
+ const LOCALIZED_PROP_PAIRS = [
6
+ ["i18nKey", "text"],
7
+ ["titleI18nKey", "title"],
8
+ ["descriptionI18nKey", "description"],
9
+ ["eyebrowI18nKey", "eyebrow"],
10
+ ["labelI18nKey", "label"],
11
+ ["helperTextI18nKey", "helperText"],
12
+ ["errorTextI18nKey", "errorText"],
13
+ ["metaI18nKey", "meta"],
14
+ ["childrenI18nKey", "children"],
15
+ ] as const;
16
+
17
+ function isNonEmptyString(value: unknown): value is string {
18
+ return typeof value === "string" && value.length > 0;
19
+ }
20
+
21
+ function hasOwnProp(props: Record<string, unknown>, key: string) {
22
+ return Object.prototype.hasOwnProperty.call(props, key);
23
+ }
24
+
25
+ function isRecord(value: unknown): value is Record<string, unknown> {
26
+ return typeof value === "object" && value !== null;
27
+ }
28
+
29
+ function resolveLocalizedPropValue(args: {
30
+ props: Record<string, unknown>;
31
+ keyProp: string;
32
+ targetProp: string;
33
+ translate: (key: string) => string;
34
+ }): string | undefined {
35
+ const { props, keyProp, targetProp, translate } = args;
36
+ const key = props[keyProp];
37
+
38
+ if (!isNonEmptyString(key)) {
39
+ return undefined;
40
+ }
41
+
42
+ const translated = translate(key);
43
+ if (isNonEmptyString(translated) && translated !== key) {
44
+ return translated;
45
+ }
46
+
47
+ if (hasOwnProp(props, targetProp)) {
48
+ return undefined;
49
+ }
50
+
51
+ return key;
52
+ }
53
+
54
+ function isSetLanguageAction(action: {
55
+ type: string;
56
+ payload?: unknown;
57
+ }): action is {
58
+ type: "setLanguage";
59
+ payload: { locale: string };
60
+ } {
61
+ return (
62
+ action.type === "setLanguage" &&
63
+ isRecord(action.payload) &&
64
+ isNonEmptyString(action.payload.locale)
65
+ );
66
+ }
67
+
68
+ export function createLocalizationNodePropsResolver(
69
+ translate: (key: string) => string,
70
+ ): RuntimeNodePropsResolver {
71
+ return ({ props }) => {
72
+ let resolvedProps: Record<string, unknown> | undefined;
73
+
74
+ for (const [keyProp, targetProp] of LOCALIZED_PROP_PAIRS) {
75
+ const localizedValue = resolveLocalizedPropValue({
76
+ props,
77
+ keyProp,
78
+ targetProp,
79
+ translate,
80
+ });
81
+
82
+ if (localizedValue === undefined) {
83
+ continue;
84
+ }
85
+
86
+ resolvedProps ??= { ...props };
87
+ resolvedProps[targetProp] = localizedValue;
88
+ }
89
+
90
+ return resolvedProps ?? props;
91
+ };
92
+ }
93
+
94
+ export function createLocalizationActionHandlers(
95
+ i18n: Pick<I18nInstance, "changeLanguage">,
96
+ ): RuntimeActionHandlers {
97
+ return {
98
+ setLanguage: ({ action }) => {
99
+ if (!isSetLanguageAction(action)) {
100
+ return;
101
+ }
102
+
103
+ void i18n.changeLanguage(action.payload.locale);
104
+ },
105
+ };
106
+ }