@chipmobilesdk/rn-i18n 0.1.2 → 0.2.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.
- package/CHANGELOG.md +168 -57
- package/README.md +277 -218
- package/package.json +49 -49
- package/skills/sdk-rn-i18n/SKILL.md +65 -62
- package/src/config/catalog.ts +25 -0
- package/src/config/diagnostics.ts +68 -0
- package/src/config/errors.ts +28 -0
- package/src/config/types.ts +150 -152
- package/src/config/validateConfig.ts +161 -174
- package/src/format/currency.ts +34 -27
- package/src/format/date.ts +32 -26
- package/src/format/invalid.ts +22 -16
- package/src/format/number.ts +30 -25
- package/src/format/units.ts +139 -151
- package/src/i18n.ts +231 -222
- package/src/index.ts +58 -50
- package/src/locale/activeLocale.ts +42 -45
- package/src/locale/deviceLocale.ts +50 -54
- package/src/locale/keyValueStorage.ts +27 -0
- package/src/locale/persistence.ts +59 -37
- package/src/react/I18nProvider.tsx +31 -35
- package/src/react/hooks.ts +66 -61
- package/src/tooling/cli.ts +76 -78
- package/src/tooling/generateKeys.ts +62 -68
- package/src/tooling/index.ts +27 -32
- package/src/tooling/report.ts +109 -117
- package/src/tooling/runner.ts +111 -126
- package/src/tooling/scan.ts +193 -210
- package/src/tooling/sync.ts +193 -198
- package/src/tooling/types.ts +119 -119
- package/src/translation/interpolate.ts +23 -23
- package/src/translation/plurals.ts +62 -69
- package/src/translation/resolver.ts +161 -146
- package/src/translation/runtimeKeys.ts +27 -27
package/src/format/units.ts
CHANGED
|
@@ -1,151 +1,139 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
error instanceof Error ? error.message : String(error),
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
const formatted = formatNumberValue(
|
|
144
|
-
converted,
|
|
145
|
-
localeCode,
|
|
146
|
-
numberDefaults,
|
|
147
|
-
numberOptions,
|
|
148
|
-
invalidFormat,
|
|
149
|
-
);
|
|
150
|
-
return `${formatted} ${getUnitSymbol(to)}`;
|
|
151
|
-
}
|
|
1
|
+
import type { InvalidFormatPolicy, UnitDimension, UnitId } from '../config/types';
|
|
2
|
+
import { applyInvalidFormatPolicy } from './invalid';
|
|
3
|
+
import { formatNumberValue } from './number';
|
|
4
|
+
|
|
5
|
+
interface UnitDefinition {
|
|
6
|
+
dimension: UnitDimension;
|
|
7
|
+
/** Factor to the dimension's base unit; unused for temperature. */
|
|
8
|
+
factor: number;
|
|
9
|
+
symbol: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Package-owned unit registry and conversion tables. Intl.NumberFormat unit
|
|
14
|
+
* style is not consistently available under Hermes, so the numeric part is
|
|
15
|
+
* formatted through Intl and the unit symbol is appended from this registry
|
|
16
|
+
* (research R4). Base units: meter, kilogram, liter, celsius.
|
|
17
|
+
*/
|
|
18
|
+
const UNIT_REGISTRY: Record<UnitId, UnitDefinition> = {
|
|
19
|
+
millimeter: { dimension: 'length', factor: 0.001, symbol: 'mm' },
|
|
20
|
+
centimeter: { dimension: 'length', factor: 0.01, symbol: 'cm' },
|
|
21
|
+
meter: { dimension: 'length', factor: 1, symbol: 'm' },
|
|
22
|
+
kilometer: { dimension: 'length', factor: 1000, symbol: 'km' },
|
|
23
|
+
inch: { dimension: 'length', factor: 0.0254, symbol: 'in' },
|
|
24
|
+
foot: { dimension: 'length', factor: 0.3048, symbol: 'ft' },
|
|
25
|
+
mile: { dimension: 'length', factor: 1609.344, symbol: 'mi' },
|
|
26
|
+
gram: { dimension: 'mass', factor: 0.001, symbol: 'g' },
|
|
27
|
+
kilogram: { dimension: 'mass', factor: 1, symbol: 'kg' },
|
|
28
|
+
ounce: { dimension: 'mass', factor: 0.028349523125, symbol: 'oz' },
|
|
29
|
+
pound: { dimension: 'mass', factor: 0.45359237, symbol: 'lb' },
|
|
30
|
+
milliliter: { dimension: 'volume', factor: 0.001, symbol: 'ml' },
|
|
31
|
+
liter: { dimension: 'volume', factor: 1, symbol: 'l' },
|
|
32
|
+
gallon: { dimension: 'volume', factor: 3.785411784, symbol: 'gal' },
|
|
33
|
+
celsius: { dimension: 'temperature', factor: 1, symbol: '°C' },
|
|
34
|
+
fahrenheit: { dimension: 'temperature', factor: 1, symbol: '°F' },
|
|
35
|
+
kelvin: { dimension: 'temperature', factor: 1, symbol: 'K' },
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const SUPPORTED_UNITS = Object.keys(UNIT_REGISTRY) as readonly UnitId[];
|
|
39
|
+
|
|
40
|
+
export const KNOWN_UNIT_IDS: ReadonlySet<string> = new Set(SUPPORTED_UNITS);
|
|
41
|
+
|
|
42
|
+
// Kept importable from here for compatibility; the errors live in config/errors.ts.
|
|
43
|
+
import { UnitConversionError } from '../config/errors';
|
|
44
|
+
|
|
45
|
+
export { UnitConversionError };
|
|
46
|
+
|
|
47
|
+
export function getUnitSymbol(unit: UnitId): string {
|
|
48
|
+
return UNIT_REGISTRY[unit].symbol;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function toCelsius(value: number, from: UnitId): number {
|
|
52
|
+
switch (from) {
|
|
53
|
+
case 'fahrenheit':
|
|
54
|
+
return ((value - 32) * 5) / 9;
|
|
55
|
+
case 'kelvin':
|
|
56
|
+
return value - 273.15;
|
|
57
|
+
default:
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function fromCelsius(value: number, to: UnitId): number {
|
|
63
|
+
switch (to) {
|
|
64
|
+
case 'fahrenheit':
|
|
65
|
+
return (value * 9) / 5 + 32;
|
|
66
|
+
case 'kelvin':
|
|
67
|
+
return value + 273.15;
|
|
68
|
+
default:
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Converts between compatible supported units. Unknown units or conversions
|
|
75
|
+
* across dimensions throw a UnitConversionError (FR-044/FR-045).
|
|
76
|
+
*/
|
|
77
|
+
export function convertUnitValue(value: number, from: UnitId, to: UnitId): number {
|
|
78
|
+
const fromDef = UNIT_REGISTRY[from];
|
|
79
|
+
const toDef = UNIT_REGISTRY[to];
|
|
80
|
+
if (!fromDef || !toDef) {
|
|
81
|
+
throw new UnitConversionError(`Unknown unit in conversion "${from}" -> "${to}".`);
|
|
82
|
+
}
|
|
83
|
+
if (fromDef.dimension !== toDef.dimension) {
|
|
84
|
+
throw new UnitConversionError(
|
|
85
|
+
`Cannot convert between incompatible dimensions "${fromDef.dimension}" (${from}) and "${toDef.dimension}" (${to}).`,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
if (!Number.isFinite(value)) {
|
|
89
|
+
throw new UnitConversionError(
|
|
90
|
+
`Cannot convert invalid value "${String(value)}" from "${from}" to "${to}".`,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
if (from === to) {
|
|
94
|
+
return value;
|
|
95
|
+
}
|
|
96
|
+
if (fromDef.dimension === 'temperature') {
|
|
97
|
+
return fromCelsius(toCelsius(value, from), to);
|
|
98
|
+
}
|
|
99
|
+
return (value * fromDef.factor) / toDef.factor;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface UnitFormatOptions extends Intl.NumberFormatOptions {
|
|
103
|
+
/** Unit the value is expressed in. Default: the locale's default unit. */
|
|
104
|
+
from?: UnitId;
|
|
105
|
+
/** Unit to display. Default: `from` (no conversion). */
|
|
106
|
+
to?: UnitId;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Converts (when requested) and formats a measurement value: locale-aware
|
|
111
|
+
* numeric part via Intl.NumberFormat plus the package-owned unit symbol.
|
|
112
|
+
*/
|
|
113
|
+
export function formatUnitValue(
|
|
114
|
+
value: number,
|
|
115
|
+
localeCode: string,
|
|
116
|
+
localeDefaultUnit: UnitId,
|
|
117
|
+
numberDefaults: Intl.NumberFormatOptions,
|
|
118
|
+
options: UnitFormatOptions | undefined,
|
|
119
|
+
invalidFormat: InvalidFormatPolicy,
|
|
120
|
+
): string {
|
|
121
|
+
const { from = localeDefaultUnit, to = from, ...numberOptions } = options ?? {};
|
|
122
|
+
let converted: number;
|
|
123
|
+
try {
|
|
124
|
+
converted = convertUnitValue(value, from, to);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
return applyInvalidFormatPolicy(
|
|
127
|
+
invalidFormat,
|
|
128
|
+
error instanceof Error ? error.message : String(error),
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
const formatted = formatNumberValue(
|
|
132
|
+
converted,
|
|
133
|
+
localeCode,
|
|
134
|
+
numberDefaults,
|
|
135
|
+
numberOptions,
|
|
136
|
+
invalidFormat,
|
|
137
|
+
);
|
|
138
|
+
return `${formatted} ${getUnitSymbol(to)}`;
|
|
139
|
+
}
|