@chipmobilesdk/rn-i18n 0.1.2 → 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/package.json CHANGED
@@ -1,49 +1,46 @@
1
- {
2
- "name": "@chipmobilesdk/rn-i18n",
3
- "version": "0.1.2",
4
- "private": false,
5
- "license": "UNLICENSED",
6
- "main": "src/index.ts",
7
- "types": "src/index.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./src/index.ts",
11
- "react-native": "./src/index.ts",
12
- "default": "./src/index.ts"
13
- },
14
- "./tooling": {
15
- "types": "./src/tooling/index.ts",
16
- "node": "./src/tooling/index.ts",
17
- "default": "./src/tooling/index.ts"
18
- }
19
- },
20
- "files": [
21
- "src/index.ts",
22
- "src/i18n.ts",
23
- "src/config",
24
- "src/translation",
25
- "src/locale",
26
- "src/format",
27
- "src/react",
28
- "src/tooling",
29
- "skills",
30
- "README.md",
31
- "CHANGELOG.md"
32
- ],
33
- "sideEffects": false,
34
- "scripts": {
35
- "typecheck": "tsc -p tsconfig.json --noEmit",
36
- "pack:dry-run": "npm pack --dry-run"
37
- },
38
- "peerDependencies": {
39
- "react": ">=19",
40
- "react-native": ">=0.85"
41
- },
42
- "engines": {
43
- "node": ">=22.11.0"
44
- },
45
- "publishConfig": {
46
- "access": "public",
47
- "registry": "https://registry.npmjs.org/"
48
- }
49
- }
1
+ {
2
+ "name": "@chipmobilesdk/rn-i18n",
3
+ "version": "0.1.3",
4
+ "private": false,
5
+ "license": "UNLICENSED",
6
+ "main": "src/index.ts",
7
+ "types": "src/index.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./src/index.ts",
11
+ "react-native": "./src/index.ts",
12
+ "default": "./src/index.ts"
13
+ },
14
+ "./tooling": {
15
+ "types": "./src/tooling/index.ts",
16
+ "node": "./src/tooling/index.ts",
17
+ "default": "./src/tooling/index.ts"
18
+ }
19
+ },
20
+ "files": [
21
+ "src/index.ts",
22
+ "src/i18n.ts",
23
+ "src/config",
24
+ "src/translation",
25
+ "src/locale",
26
+ "src/format",
27
+ "src/react",
28
+ "src/tooling",
29
+ "skills",
30
+ "README.md",
31
+ "CHANGELOG.md"
32
+ ],
33
+ "sideEffects": false,
34
+ "scripts": {
35
+ "typecheck": "tsc -p tsconfig.json --noEmit",
36
+ "pack:check": "npm pack --dry-run"
37
+ },
38
+ "peerDependencies": {
39
+ "react": ">=19",
40
+ "react-native": ">=0.85"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public",
44
+ "registry": "https://registry.npmjs.org/"
45
+ }
46
+ }
@@ -1,62 +1,61 @@
1
- ---
2
- name: sdk-rn-i18n
3
- description: "Localization via @chipmobilesdk/rn-i18n: createI18n, useTranslation, useLocale, locale persistence, unit formatting, and the extraction/sync CLI. Use when adding translated strings, switching locale, formatting units, or syncing translation keys."
4
- metadata:
5
- package: "@chipmobilesdk/rn-i18n"
6
- packageVersion: "0.1.0"
7
- user-invocable: true
8
- disable-model-invocation: false
9
- ---
10
-
11
- # @chipmobilesdk/rn-i18n
12
-
13
- Localization with device-locale detection, persisted locale choice, typed translation
14
- access, unit/number formatting, and a CLI that extracts and synchronizes keys.
15
-
16
- ## Use this when
17
-
18
- - Adding or changing any user-facing string
19
- - Reading or switching the active locale
20
- - Formatting numbers, dates or units for a locale
21
- - Translation keys are missing, stale, or out of sync across locale files
22
-
23
- ## Never hardcode a user-facing string
24
-
25
- Every string rendered to a user goes through `useTranslation`. A literal in a component
26
- is a defect — it cannot be translated and will not appear in extraction output.
27
-
28
- ## Keys are generated, not hand-maintained
29
-
30
- Do not hand-edit locale JSON to add a key. Run the extraction/sync CLI so every locale
31
- file stays aligned. The monorepo exposes it as `npm run i18n:sync`. See
32
- `## Extraction & synchronization CLI`.
33
-
34
- ## Public API (v0.1.2)
35
-
36
- Runtime:
37
-
38
- - `createI18n(...)` → `I18n`
39
- - `I18nProvider` (+ `I18nProviderProps`)
40
- - `useTranslation()` → `UseTranslationResult`
41
- - `useLocale()` → `UseLocaleResult`
42
- - `defaultDeviceLocaleProvider` — device locale detection
43
- - `LOCALE_STORAGE_KEY` — where the chosen locale persists
44
- - `I18nConfigError` — thrown on invalid configuration at startup
45
-
46
- Types: `I18n`, `UnitFormatOptions`, `I18nProviderProps`, `UseLocaleResult`,
47
- `UseTranslationResult`
48
-
49
- `I18nConfigError` throws at startup by design — a misconfigured i18n setup should fail
50
- loudly, not silently render keys.
51
-
52
- ## Where the detail lives
53
-
54
- Read one section of `node_modules/@chipmobilesdk/rn-i18n/README.md`:
55
-
56
- | Need | Section |
57
- |---|---|
58
- | First integration | `## Installation`, `## Quick start` |
59
- | Full export list | `## Public API` |
60
- | Locale file layout, plurals | `## Translations` |
61
- | Adding/syncing keys | `## Extraction & synchronization CLI` |
62
- | Version/peer constraints | `## Compatibility` |
1
+ ---
2
+ name: sdk-rn-i18n
3
+ description: "Localization via @chipmobilesdk/rn-i18n: createI18n, useTranslation, useLocale, locale persistence, unit formatting, and the extraction/sync CLI. Use when adding translated strings, switching locale, formatting units, or syncing translation keys."
4
+ metadata:
5
+ package: "@chipmobilesdk/rn-i18n"
6
+ user-invocable: true
7
+ disable-model-invocation: false
8
+ ---
9
+
10
+ # @chipmobilesdk/rn-i18n
11
+
12
+ Localization with device-locale detection, persisted locale choice, typed translation
13
+ access, unit/number formatting, and a CLI that extracts and synchronizes keys.
14
+
15
+ ## Use this when
16
+
17
+ - Adding or changing any user-facing string
18
+ - Reading or switching the active locale
19
+ - Formatting numbers, dates or units for a locale
20
+ - Translation keys are missing, stale, or out of sync across locale files
21
+
22
+ ## Never hardcode a user-facing string
23
+
24
+ Every string rendered to a user goes through `useTranslation`. A literal in a component
25
+ is a defect — it cannot be translated and will not appear in extraction output.
26
+
27
+ ## Keys are generated, not hand-maintained
28
+
29
+ Do not hand-edit locale JSON to add a key. Run the extraction/sync CLI so every locale
30
+ file stays aligned. The monorepo exposes it as `npm run i18n:sync`. See
31
+ `## Extraction & synchronization CLI`.
32
+
33
+ ## Public API
34
+
35
+ Runtime:
36
+
37
+ - `createI18n(...)` → `I18n`
38
+ - `I18nProvider` (+ `I18nProviderProps`)
39
+ - `useTranslation()` → `UseTranslationResult`
40
+ - `useLocale()` → `UseLocaleResult`
41
+ - `defaultDeviceLocaleProvider` — device locale detection
42
+ - `LOCALE_STORAGE_KEY` — where the chosen locale persists
43
+ - `I18nConfigError` — thrown on invalid configuration at startup
44
+
45
+ Types: `I18n`, `UnitFormatOptions`, `I18nProviderProps`, `UseLocaleResult`,
46
+ `UseTranslationResult`
47
+
48
+ `I18nConfigError` throws at startup by design — a misconfigured i18n setup should fail
49
+ loudly, not silently render keys.
50
+
51
+ ## Where the detail lives
52
+
53
+ Read one section of `node_modules/@chipmobilesdk/rn-i18n/README.md`:
54
+
55
+ | Need | Section |
56
+ |---|---|
57
+ | First integration | `## Installation`, `## Quick start` |
58
+ | Full export list | `## Public API` |
59
+ | Locale file layout, plurals | `## Translations` |
60
+ | Adding/syncing keys | `## Extraction & synchronization CLI` |
61
+ | Version/peer constraints | `## Compatibility` |
@@ -1,152 +1,144 @@
1
- /**
2
- * Public configuration and contract types for @chipmobilesdk/rn-i18n.
3
- *
4
- * The package has zero runtime dependencies: formatting uses the Intl APIs
5
- * built into Hermes (Intl.NumberFormat, Intl.DateTimeFormat), while plural
6
- * rules and unit conversion are package-owned because Intl.PluralRules and
7
- * Intl unit-style formatting are not reliably available in Hermes.
8
- */
9
-
10
- export type LocaleDirection = 'ltr' | 'rtl';
11
-
12
- export type PluralCategory = 'zero' | 'one' | 'two' | 'few' | 'many' | 'other';
13
-
14
- export type PluralRule = (count: number) => PluralCategory;
15
-
16
- export type UnitDimension = 'length' | 'mass' | 'volume' | 'temperature';
17
-
18
- export type UnitId =
19
- | 'millimeter'
20
- | 'centimeter'
21
- | 'meter'
22
- | 'kilometer'
23
- | 'inch'
24
- | 'foot'
25
- | 'mile'
26
- | 'gram'
27
- | 'kilogram'
28
- | 'ounce'
29
- | 'pound'
30
- | 'milliliter'
31
- | 'liter'
32
- | 'gallon'
33
- | 'celsius'
34
- | 'fahrenheit'
35
- | 'kelvin';
36
-
37
- export interface LocaleConfig {
38
- /** BCP-47 language-region code, e.g. `vi-VN`, `en-US`, `ja-JP`. */
39
- code: string;
40
- displayName: string;
41
- direction: LocaleDirection;
42
- /**
43
- * Ordered locale codes consulted when a key is missing from this locale.
44
- * Every entry must be another supported locale; cycles, duplicates, and
45
- * self-references are rejected during setup validation.
46
- */
47
- fallbackChain: string[];
48
- /** ISO-4217 currency code used when no explicit currency is requested. */
49
- defaultCurrency: string;
50
- defaultUnit: UnitId;
51
- /**
52
- * Default Intl.DateTimeFormat options for this locale. Restricted to the
53
- * component options supported by Hermes on both Android and iOS.
54
- */
55
- defaultDateFormat: Intl.DateTimeFormatOptions;
56
- /** Default Intl.NumberFormat options for this locale. */
57
- defaultNumberFormat: Intl.NumberFormatOptions;
58
- /** Optional plural rule override; defaults to the built-in language rule. */
59
- pluralRule?: PluralRule;
60
- }
61
-
62
- export type TranslationNode = string | { [key: string]: TranslationNode };
63
-
64
- export type TranslationNamespace = { [key: string]: TranslationNode };
65
-
66
- /** App-owned translation resources keyed by locale code. */
67
- export type TranslationResources = { [localeCode: string]: TranslationNamespace };
68
-
69
- /**
70
- * A translation key intentionally resolved at runtime and therefore not
71
- * discoverable by static source scanning. Declared entries are protected
72
- * from removal during locale JSON synchronization.
73
- */
74
- export interface RuntimeTranslationEntry {
75
- key: string;
76
- /** Template text used when no locale resource provides a value. */
77
- defaultText: string;
78
- /**
79
- * Synchronization policy when the key is missing from a locale file:
80
- * `add` writes the default text, `report` only lists it. Default: `report`.
81
- */
82
- policy?: 'add' | 'report';
83
- /** Owning feature note surfaced in the extraction report. */
84
- description?: string;
85
- }
86
-
87
- export type MissingTranslationPolicy =
88
- | { mode: 'key' }
89
- | { mode: 'placeholder'; text: string };
90
-
91
- export type InvalidFormatPolicy =
92
- | { mode: 'placeholder'; text: string }
93
- | { mode: 'error' };
94
-
95
- /** App-injected persistence contract. The package never bundles a backend. */
96
- export interface LocaleStorageAdapter {
97
- getItem(key: string): Promise<string | null>;
98
- setItem(key: string, value: string): Promise<void>;
99
- removeItem(key: string): Promise<void>;
100
- }
101
-
102
- /** Returns the device locale, or undefined when detection is unavailable. */
103
- export type DeviceLocaleProvider = () => string | undefined;
104
-
105
- export interface I18nConfig {
106
- locales: LocaleConfig[];
107
- /** Recovery locale when persistence or device detection cannot decide. */
108
- primaryLocale: string;
109
- resources: TranslationResources;
110
- runtimeEntries?: RuntimeTranslationEntry[];
111
- /** Persist the selected locale across launches. Default: true. */
112
- persistLocale?: boolean;
113
- /** Required when `persistLocale` is not disabled. */
114
- storage?: LocaleStorageAdapter;
115
- deviceLocale?: DeviceLocaleProvider;
116
- /** Default: `{ mode: 'key' }`. */
117
- missingTranslation?: MissingTranslationPolicy;
118
- /** Default: `{ mode: 'placeholder', text: '—' }`. */
119
- invalidFormat?: InvalidFormatPolicy;
120
- }
121
-
122
- export type LocaleSelectionSource =
123
- | 'persisted'
124
- | 'device'
125
- | 'languageMatch'
126
- | 'primaryFallback'
127
- | 'runtimeSwitch';
128
-
129
- export interface ActiveLocalePreference {
130
- code: string;
131
- source: LocaleSelectionSource;
132
- /** True when an unsupported persisted locale was discarded. */
133
- recovered: boolean;
134
- }
135
-
136
- export type TranslateVars = Record<string, string | number>;
137
-
138
- export interface TranslateOptions {
139
- /** Selects the plural variant and is exposed as the `count` variable. */
140
- count?: number;
141
- }
142
-
143
- /** Configuration error listing every detected violation. */
144
- export class I18nConfigError extends Error {
145
- readonly issues: readonly string[];
146
-
147
- constructor(issues: readonly string[]) {
148
- super(`Invalid i18n configuration:\n- ${issues.join('\n- ')}`);
149
- this.name = 'I18nConfigError';
150
- this.issues = issues;
151
- }
152
- }
1
+ /**
2
+ * Public configuration and contract types for @chipmobilesdk/rn-i18n.
3
+ *
4
+ * The package has zero runtime dependencies: formatting uses the Intl APIs
5
+ * built into Hermes (Intl.NumberFormat, Intl.DateTimeFormat), while plural
6
+ * rules and unit conversion are package-owned because Intl.PluralRules and
7
+ * Intl unit-style formatting are not reliably available in Hermes.
8
+ */
9
+
10
+ export type LocaleDirection = 'ltr' | 'rtl';
11
+
12
+ export type PluralCategory = 'zero' | 'one' | 'two' | 'few' | 'many' | 'other';
13
+
14
+ export type PluralRule = (count: number) => PluralCategory;
15
+
16
+ export type UnitDimension = 'length' | 'mass' | 'volume' | 'temperature';
17
+
18
+ export type UnitId =
19
+ | 'millimeter'
20
+ | 'centimeter'
21
+ | 'meter'
22
+ | 'kilometer'
23
+ | 'inch'
24
+ | 'foot'
25
+ | 'mile'
26
+ | 'gram'
27
+ | 'kilogram'
28
+ | 'ounce'
29
+ | 'pound'
30
+ | 'milliliter'
31
+ | 'liter'
32
+ | 'gallon'
33
+ | 'celsius'
34
+ | 'fahrenheit'
35
+ | 'kelvin';
36
+
37
+ export interface LocaleConfig {
38
+ /** BCP-47 language-region code, e.g. `vi-VN`, `en-US`, `ja-JP`. */
39
+ code: string;
40
+ displayName: string;
41
+ direction: LocaleDirection;
42
+ /**
43
+ * Ordered locale codes consulted when a key is missing from this locale.
44
+ * Every entry must be another supported locale; cycles, duplicates, and
45
+ * self-references are rejected during setup validation.
46
+ */
47
+ fallbackChain: string[];
48
+ /** ISO-4217 currency code used when no explicit currency is requested. */
49
+ defaultCurrency: string;
50
+ defaultUnit: UnitId;
51
+ /**
52
+ * Default Intl.DateTimeFormat options for this locale. Restricted to the
53
+ * component options supported by Hermes on both Android and iOS.
54
+ */
55
+ defaultDateFormat: Intl.DateTimeFormatOptions;
56
+ /** Default Intl.NumberFormat options for this locale. */
57
+ defaultNumberFormat: Intl.NumberFormatOptions;
58
+ /** Optional plural rule override; defaults to the built-in language rule. */
59
+ pluralRule?: PluralRule;
60
+ }
61
+
62
+ export type TranslationNode = string | { [key: string]: TranslationNode };
63
+
64
+ export type TranslationNamespace = { [key: string]: TranslationNode };
65
+
66
+ /** App-owned translation resources keyed by locale code. */
67
+ export type TranslationResources = { [localeCode: string]: TranslationNamespace };
68
+
69
+ /**
70
+ * A translation key intentionally resolved at runtime and therefore not
71
+ * discoverable by static source scanning. Declared entries are protected
72
+ * from removal during locale JSON synchronization.
73
+ */
74
+ export interface RuntimeTranslationEntry {
75
+ key: string;
76
+ /** Template text used when no locale resource provides a value. */
77
+ defaultText: string;
78
+ /**
79
+ * Synchronization policy when the key is missing from a locale file:
80
+ * `add` writes the default text, `report` only lists it. Default: `report`.
81
+ */
82
+ policy?: 'add' | 'report';
83
+ /** Owning feature note surfaced in the extraction report. */
84
+ description?: string;
85
+ }
86
+
87
+ export type MissingTranslationPolicy = { mode: 'key' } | { mode: 'placeholder'; text: string };
88
+
89
+ export type InvalidFormatPolicy = { mode: 'placeholder'; text: string } | { mode: 'error' };
90
+
91
+ /** App-injected persistence contract. The package never bundles a backend. */
92
+ export interface LocaleStorageAdapter {
93
+ getItem(key: string): Promise<string | null>;
94
+ setItem(key: string, value: string): Promise<void>;
95
+ removeItem(key: string): Promise<void>;
96
+ }
97
+
98
+ /** Returns the device locale, or undefined when detection is unavailable. */
99
+ export type DeviceLocaleProvider = () => string | undefined;
100
+
101
+ export interface I18nConfig {
102
+ locales: LocaleConfig[];
103
+ /** Recovery locale when persistence or device detection cannot decide. */
104
+ primaryLocale: string;
105
+ resources: TranslationResources;
106
+ runtimeEntries?: RuntimeTranslationEntry[];
107
+ /** Persist the selected locale across launches. Default: true. */
108
+ persistLocale?: boolean;
109
+ /** Required when `persistLocale` is not disabled. */
110
+ storage?: LocaleStorageAdapter;
111
+ deviceLocale?: DeviceLocaleProvider;
112
+ /** Default: `{ mode: 'key' }`. */
113
+ missingTranslation?: MissingTranslationPolicy;
114
+ /** Default: `{ mode: 'placeholder', text: '—' }`. */
115
+ invalidFormat?: InvalidFormatPolicy;
116
+ }
117
+
118
+ export type LocaleSelectionSource =
119
+ 'persisted' | 'device' | 'languageMatch' | 'primaryFallback' | 'runtimeSwitch';
120
+
121
+ export interface ActiveLocalePreference {
122
+ code: string;
123
+ source: LocaleSelectionSource;
124
+ /** True when an unsupported persisted locale was discarded. */
125
+ recovered: boolean;
126
+ }
127
+
128
+ export type TranslateVars = Record<string, string | number>;
129
+
130
+ export interface TranslateOptions {
131
+ /** Selects the plural variant and is exposed as the `count` variable. */
132
+ count?: number;
133
+ }
134
+
135
+ /** Configuration error listing every detected violation. */
136
+ export class I18nConfigError extends Error {
137
+ readonly issues: readonly string[];
138
+
139
+ constructor(issues: readonly string[]) {
140
+ super(`Invalid i18n configuration:\n- ${issues.join('\n- ')}`);
141
+ this.name = 'I18nConfigError';
142
+ this.issues = issues;
143
+ }
144
+ }