@astryxdesign/core 0.5.1-canary.8359f1a → 0.5.1
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/dist/i18n/InternationalizationContext.d.ts +0 -2
- package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
- package/dist/i18n/InternationalizationContext.js +1 -3
- package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
- package/dist/i18n/InternationalizationProvider.js +1 -3
- package/dist/i18n/resolve.d.ts +1 -1
- package/dist/i18n/resolve.d.ts.map +1 -1
- package/dist/i18n/resolve.js +38 -37
- package/dist/i18n/useTranslator.d.ts.map +1 -1
- package/dist/i18n/useTranslator.js +3 -2
- package/package.json +3 -3
- package/src/i18n/InternationalizationContext.ts +0 -3
- package/src/i18n/InternationalizationProvider.tsx +1 -8
- package/src/i18n/__tests__/resolve.test.ts +1 -23
- package/src/i18n/resolve.ts +49 -46
- package/src/i18n/useTranslator.ts +7 -2
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { getResolve } from './resolve';
|
|
2
1
|
import type { Locale, MessagesByLocale, Overrides } from './types';
|
|
3
2
|
export interface InternationalizationContextValue {
|
|
4
3
|
locale: Locale;
|
|
5
4
|
direction: 'ltr' | 'rtl';
|
|
6
5
|
messages: MessagesByLocale;
|
|
7
6
|
overrides?: Overrides;
|
|
8
|
-
translate: ReturnType<typeof getResolve>;
|
|
9
7
|
}
|
|
10
8
|
/**
|
|
11
9
|
* Default value falls through to the shipped en catalog in resolve().
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InternationalizationContext.d.ts","sourceRoot":"","sources":["../../src/i18n/InternationalizationContext.ts"],"names":[],"mappings":"AAyBA,OAAO,
|
|
1
|
+
{"version":3,"file":"InternationalizationContext.d.ts","sourceRoot":"","sources":["../../src/i18n/InternationalizationContext.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAC,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAC,MAAM,SAAS,CAAC;AAEjE,MAAM,WAAW,gCAAgC;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,KAAK,GAAG,KAAK,CAAC;IACzB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED;;;GAGG;AACH,eAAO,MAAM,2BAA2B,2DAKpC,CAAC"}
|
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
* - /packages/core/src/i18n/index.ts
|
|
23
23
|
*/
|
|
24
24
|
import { createContext } from 'react';
|
|
25
|
-
import { getResolve } from "./resolve.js";
|
|
26
25
|
/**
|
|
27
26
|
* Default value falls through to the shipped en catalog in resolve().
|
|
28
27
|
* A consumer that doesn't render a provider still gets English defaults.
|
|
@@ -30,7 +29,6 @@ import { getResolve } from "./resolve.js";
|
|
|
30
29
|
export const InternationalizationContext = /*#__PURE__*/createContext({
|
|
31
30
|
locale: 'en',
|
|
32
31
|
direction: 'ltr',
|
|
33
|
-
messages: {}
|
|
34
|
-
translate: getResolve('en', {})
|
|
32
|
+
messages: {}
|
|
35
33
|
});
|
|
36
34
|
InternationalizationContext.displayName = 'InternationalizationContext';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InternationalizationProvider.d.ts","sourceRoot":"","sources":["../../src/i18n/InternationalizationProvider.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAU,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAG9C,OAAO,KAAK,EAAC,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAC,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"InternationalizationProvider.d.ts","sourceRoot":"","sources":["../../src/i18n/InternationalizationProvider.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAU,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAG9C,OAAO,KAAK,EAAC,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAC,MAAM,SAAS,CAAC;AAEjE,MAAM,WAAW,iCAAiC;IAChD;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;;;;;;;;OAWG;IACH,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,EAC3C,MAAM,EACN,QAAQ,EACR,SAAS,EACT,GAAG,EACH,QAAQ,GACT,EAAE,iCAAiC,+BAWnC;yBAjBe,4BAA4B"}
|
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
import { useMemo } from 'react';
|
|
22
22
|
import { InternationalizationContext } from "./InternationalizationContext.js";
|
|
23
23
|
import { getLocaleDirection } from "./getLocaleDirection.js";
|
|
24
|
-
import { getResolve } from "./resolve.js";
|
|
25
24
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
26
25
|
/**
|
|
27
26
|
* Provides locale + additional messages + overrides to all astryx
|
|
@@ -39,8 +38,7 @@ export function InternationalizationProvider({
|
|
|
39
38
|
locale,
|
|
40
39
|
direction,
|
|
41
40
|
messages: messages ?? {},
|
|
42
|
-
overrides
|
|
43
|
-
translate: getResolve(locale, messages ?? {}, overrides)
|
|
41
|
+
overrides
|
|
44
42
|
}), [locale, direction, messages, overrides]);
|
|
45
43
|
return /*#__PURE__*/_jsx(InternationalizationContext, {
|
|
46
44
|
value: value,
|
package/dist/i18n/resolve.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ import type { Locale, MessagesByLocale, Overrides } from './types';
|
|
|
13
13
|
* shipped en catalog separately as the final source-of-truth.
|
|
14
14
|
*/
|
|
15
15
|
export declare function resolveLocaleChain(locale: Locale): Locale[];
|
|
16
|
-
export declare function
|
|
16
|
+
export declare function resolve(key: string, values: Record<string, unknown> | undefined, locale: Locale, messages: MessagesByLocale, overrides: Overrides | undefined): string;
|
|
17
17
|
/**
|
|
18
18
|
* Reset internal caches. Test-only.
|
|
19
19
|
* @internal
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/i18n/resolve.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAU,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAC,MAAM,SAAS,CAAC;AAwB1E;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAc3D;
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/i18n/resolve.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAU,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAC,MAAM,SAAS,CAAC;AAwB1E;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAc3D;AAsCD,wBAAgB,OAAO,CACrB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC3C,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,gBAAgB,EAC1B,SAAS,EAAE,SAAS,GAAG,SAAS,GAC/B,MAAM,CA0BR;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAGtC"}
|
package/dist/i18n/resolve.js
CHANGED
|
@@ -69,54 +69,55 @@ export function resolveLocaleChain(locale) {
|
|
|
69
69
|
}
|
|
70
70
|
return chain;
|
|
71
71
|
}
|
|
72
|
-
function
|
|
73
|
-
const lookup = {};
|
|
72
|
+
function lookup(key, locale, messages, overrides) {
|
|
74
73
|
const chain = resolveLocaleChain(locale);
|
|
74
|
+
|
|
75
75
|
// 1 + 2. Overrides (most specific to least specific in the chain)
|
|
76
76
|
if (overrides !== undefined) {
|
|
77
77
|
for (const tag of chain) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
lookup[key] = value;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
78
|
+
const value = overrides[tag]?.[key];
|
|
79
|
+
if (value !== undefined) {
|
|
80
|
+
return value;
|
|
84
81
|
}
|
|
85
82
|
}
|
|
86
83
|
}
|
|
84
|
+
|
|
85
|
+
// 3 + 4. Shipped catalogs (most specific to least specific)
|
|
87
86
|
for (const tag of chain) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
lookup[key] = value?.defaultMessage;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
87
|
+
const entry = messages[tag]?.[key];
|
|
88
|
+
if (entry !== undefined) {
|
|
89
|
+
return entry.defaultMessage;
|
|
94
90
|
}
|
|
95
91
|
}
|
|
96
|
-
|
|
92
|
+
|
|
93
|
+
// 5. Shipped en catalog — always present
|
|
94
|
+
const enEntry = EN_CATALOG[key];
|
|
95
|
+
if (enEntry !== undefined) {
|
|
96
|
+
return enEntry.defaultMessage;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 6. Nothing found
|
|
100
|
+
return null;
|
|
97
101
|
}
|
|
98
|
-
export function
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
// system.
|
|
118
|
-
return formatted;
|
|
119
|
-
};
|
|
102
|
+
export function resolve(key, values, locale, messages, overrides) {
|
|
103
|
+
const result = lookup(key, locale, messages, overrides);
|
|
104
|
+
if (result === null) {
|
|
105
|
+
// Fires ONLY when a key is missing from every source including the
|
|
106
|
+
// shipped `en` catalog — a real bug (typo, stale catalog, deleted key).
|
|
107
|
+
// Fallback to `en` from a non-en locale is expected and stays silent,
|
|
108
|
+
// matching the FormatJS / i18next default.
|
|
109
|
+
warnOnce(`astryx-i18n:${locale}::${key}`, 'astryx-i18n', `missing key: ${key} (locale: ${locale})`);
|
|
110
|
+
return key;
|
|
111
|
+
}
|
|
112
|
+
if (values === undefined) {
|
|
113
|
+
// Static string — skip the parser entirely for the common case
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
const formatted = getFormatter(result, locale).format(values);
|
|
117
|
+
// IntlMessageFormat.format returns string | (string | React elements) — we
|
|
118
|
+
// only ever pass string values so it will be a string; assert for the type
|
|
119
|
+
// system.
|
|
120
|
+
return formatted;
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTranslator.d.ts","sourceRoot":"","sources":["../../src/i18n/useTranslator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useTranslator.d.ts","sourceRoot":"","sources":["../../src/i18n/useTranslator.ts"],"names":[],"mappings":"AAyBA,MAAM,MAAM,YAAY,GAAG,CACzB,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,MAAM,CAAC;AAEZ;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,IAAI,YAAY,CAO5C"}
|
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
* - /packages/core/src/i18n/index.ts
|
|
19
19
|
* - /packages/core/src/i18n/t.client.ts
|
|
20
20
|
*/
|
|
21
|
-
import { use } from 'react';
|
|
21
|
+
import { use, useCallback } from 'react';
|
|
22
22
|
import { InternationalizationContext } from "./InternationalizationContext.js";
|
|
23
|
+
import { resolve } from "./resolve.js";
|
|
23
24
|
/**
|
|
24
25
|
* Returns a translator function bound to the current provider's locale.
|
|
25
26
|
* Safe to call from event handlers and effects.
|
|
@@ -34,5 +35,5 @@ import { InternationalizationContext } from "./InternationalizationContext.js";
|
|
|
34
35
|
*/
|
|
35
36
|
export function useTranslator() {
|
|
36
37
|
const ctx = use(InternationalizationContext);
|
|
37
|
-
return ctx.
|
|
38
|
+
return useCallback((key, values) => resolve(key, values, ctx.locale, ctx.messages, ctx.overrides), [ctx.locale, ctx.messages, ctx.overrides]);
|
|
38
39
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/core",
|
|
3
|
-
"version": "0.5.1
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"displayName": "Astryx Core",
|
|
5
5
|
"description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
|
|
6
6
|
"author": "Meta Open Source",
|
|
@@ -661,7 +661,6 @@
|
|
|
661
661
|
"react-dom": ">=19.0.0"
|
|
662
662
|
},
|
|
663
663
|
"devDependencies": {
|
|
664
|
-
"@astryxdesign/cli": "0.5.1-canary.8359f1a",
|
|
665
664
|
"@babel/cli": "^8.0.4",
|
|
666
665
|
"@babel/core": "^7.29.7",
|
|
667
666
|
"@babel/preset-react": "^8.0.1",
|
|
@@ -669,7 +668,8 @@
|
|
|
669
668
|
"@stylexjs/babel-plugin": "^0.19.0",
|
|
670
669
|
"@testing-library/dom": "^10.0.0",
|
|
671
670
|
"@testing-library/jest-dom": "^6.6.0",
|
|
672
|
-
"@testing-library/react": "^16.3.2"
|
|
671
|
+
"@testing-library/react": "^16.3.2",
|
|
672
|
+
"@astryxdesign/cli": "0.5.1"
|
|
673
673
|
},
|
|
674
674
|
"dependencies": {
|
|
675
675
|
"intl-messageformat": "^11.2.9"
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import {createContext} from 'react';
|
|
26
|
-
import {getResolve} from './resolve';
|
|
27
26
|
import type {Locale, MessagesByLocale, Overrides} from './types';
|
|
28
27
|
|
|
29
28
|
export interface InternationalizationContextValue {
|
|
@@ -31,7 +30,6 @@ export interface InternationalizationContextValue {
|
|
|
31
30
|
direction: 'ltr' | 'rtl';
|
|
32
31
|
messages: MessagesByLocale;
|
|
33
32
|
overrides?: Overrides;
|
|
34
|
-
translate: ReturnType<typeof getResolve>;
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
/**
|
|
@@ -43,6 +41,5 @@ export const InternationalizationContext =
|
|
|
43
41
|
locale: 'en',
|
|
44
42
|
direction: 'ltr',
|
|
45
43
|
messages: {},
|
|
46
|
-
translate: getResolve('en', {}),
|
|
47
44
|
});
|
|
48
45
|
InternationalizationContext.displayName = 'InternationalizationContext';
|
|
@@ -23,7 +23,6 @@ import {useMemo, type ReactNode} from 'react';
|
|
|
23
23
|
import {InternationalizationContext} from './InternationalizationContext';
|
|
24
24
|
import {getLocaleDirection} from './getLocaleDirection';
|
|
25
25
|
import type {Locale, MessagesByLocale, Overrides} from './types';
|
|
26
|
-
import {getResolve} from './resolve';
|
|
27
26
|
|
|
28
27
|
export interface InternationalizationProviderProps {
|
|
29
28
|
/**
|
|
@@ -86,13 +85,7 @@ export function InternationalizationProvider({
|
|
|
86
85
|
}: InternationalizationProviderProps) {
|
|
87
86
|
const direction = dir ?? getLocaleDirection(locale);
|
|
88
87
|
const value = useMemo(
|
|
89
|
-
() => ({
|
|
90
|
-
locale,
|
|
91
|
-
direction,
|
|
92
|
-
messages: messages ?? {},
|
|
93
|
-
overrides,
|
|
94
|
-
translate: getResolve(locale, messages ?? {}, overrides),
|
|
95
|
-
}),
|
|
88
|
+
() => ({locale, direction, messages: messages ?? {}, overrides}),
|
|
96
89
|
[locale, direction, messages, overrides],
|
|
97
90
|
);
|
|
98
91
|
return (
|
|
@@ -9,16 +9,9 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import {describe, expect, test, beforeEach, vi} from 'vitest';
|
|
12
|
-
import {__resetForTests,
|
|
12
|
+
import {__resetForTests, resolve, resolveLocaleChain} from '../resolve';
|
|
13
13
|
import type {Catalog, MessagesByLocale, Overrides} from '../types';
|
|
14
14
|
|
|
15
|
-
const resolve = (
|
|
16
|
-
...[key, values, locale, messages, overrides]: [
|
|
17
|
-
...Parameters<ReturnType<typeof getResolve>>,
|
|
18
|
-
...Parameters<typeof getResolve>,
|
|
19
|
-
]
|
|
20
|
-
) => getResolve(locale, messages, overrides)(key, values);
|
|
21
|
-
|
|
22
15
|
// Reset caches between tests so warn-once and formatter cache don't bleed.
|
|
23
16
|
beforeEach(() => {
|
|
24
17
|
__resetForTests();
|
|
@@ -216,21 +209,6 @@ describe('resolve — overrides', () => {
|
|
|
216
209
|
// Should fall through to en since fr has no messages and es override doesn't apply
|
|
217
210
|
expect(out).toBe('Go to next page');
|
|
218
211
|
});
|
|
219
|
-
|
|
220
|
-
test('override `pt` should take precedence over message `pt-BR`', () => {
|
|
221
|
-
const overrides: Overrides = {
|
|
222
|
-
pt: {key: 'override'},
|
|
223
|
-
};
|
|
224
|
-
const messages: MessagesByLocale = {
|
|
225
|
-
'pt-BR': {
|
|
226
|
-
key: {
|
|
227
|
-
defaultMessage: 'shipped',
|
|
228
|
-
},
|
|
229
|
-
},
|
|
230
|
-
};
|
|
231
|
-
const out = resolve('key', undefined, 'pt-BR', messages, overrides);
|
|
232
|
-
expect(out).toBe('override');
|
|
233
|
-
});
|
|
234
212
|
});
|
|
235
213
|
|
|
236
214
|
describe('resolve — ICU MessageFormat', () => {
|
package/src/i18n/resolve.ts
CHANGED
|
@@ -73,71 +73,74 @@ export function resolveLocaleChain(locale: Locale): Locale[] {
|
|
|
73
73
|
return chain;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
function
|
|
76
|
+
function lookup(
|
|
77
|
+
key: string,
|
|
77
78
|
locale: Locale,
|
|
78
79
|
messages: MessagesByLocale,
|
|
79
|
-
overrides
|
|
80
|
-
):
|
|
81
|
-
const lookup: Record<string, string> = {};
|
|
80
|
+
overrides: Overrides | undefined,
|
|
81
|
+
): string | null {
|
|
82
82
|
const chain = resolveLocaleChain(locale);
|
|
83
|
+
|
|
83
84
|
// 1 + 2. Overrides (most specific to least specific in the chain)
|
|
84
85
|
if (overrides !== undefined) {
|
|
85
86
|
for (const tag of chain) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
lookup[key] = value;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
87
|
+
const value = overrides[tag]?.[key];
|
|
88
|
+
if (value !== undefined) {
|
|
89
|
+
return value;
|
|
92
90
|
}
|
|
93
91
|
}
|
|
94
92
|
}
|
|
93
|
+
|
|
94
|
+
// 3 + 4. Shipped catalogs (most specific to least specific)
|
|
95
95
|
for (const tag of chain) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
lookup[key] = value?.defaultMessage;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
96
|
+
const entry = messages[tag]?.[key];
|
|
97
|
+
if (entry !== undefined) {
|
|
98
|
+
return entry.defaultMessage;
|
|
102
99
|
}
|
|
103
100
|
}
|
|
104
101
|
|
|
105
|
-
|
|
102
|
+
// 5. Shipped en catalog — always present
|
|
103
|
+
const enEntry = EN_CATALOG[key];
|
|
104
|
+
if (enEntry !== undefined) {
|
|
105
|
+
return enEntry.defaultMessage;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 6. Nothing found
|
|
109
|
+
return null;
|
|
106
110
|
}
|
|
107
111
|
|
|
108
|
-
export function
|
|
112
|
+
export function resolve(
|
|
113
|
+
key: string,
|
|
114
|
+
values: Record<string, unknown> | undefined,
|
|
109
115
|
locale: Locale,
|
|
110
116
|
messages: MessagesByLocale,
|
|
111
|
-
overrides
|
|
112
|
-
) {
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
return (key: string, values: Record<string, unknown> | undefined) => {
|
|
116
|
-
const result = lookup[key] ?? EN_CATALOG[key]?.defaultMessage;
|
|
117
|
-
if (result === undefined) {
|
|
118
|
-
// Fires ONLY when a key is missing from every source including the
|
|
119
|
-
// shipped `en` catalog — a real bug (typo, stale catalog, deleted key).
|
|
120
|
-
// Fallback to `en` from a non-en locale is expected and stays silent,
|
|
121
|
-
// matching the FormatJS / i18next default.
|
|
122
|
-
warnOnce(
|
|
123
|
-
`astryx-i18n:${locale}::${key}`,
|
|
124
|
-
'astryx-i18n',
|
|
125
|
-
`missing key: ${key} (locale: ${locale})`,
|
|
126
|
-
);
|
|
127
|
-
return key;
|
|
128
|
-
}
|
|
117
|
+
overrides: Overrides | undefined,
|
|
118
|
+
): string {
|
|
119
|
+
const result = lookup(key, locale, messages, overrides);
|
|
129
120
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
121
|
+
if (result === null) {
|
|
122
|
+
// Fires ONLY when a key is missing from every source including the
|
|
123
|
+
// shipped `en` catalog — a real bug (typo, stale catalog, deleted key).
|
|
124
|
+
// Fallback to `en` from a non-en locale is expected and stays silent,
|
|
125
|
+
// matching the FormatJS / i18next default.
|
|
126
|
+
warnOnce(
|
|
127
|
+
`astryx-i18n:${locale}::${key}`,
|
|
128
|
+
'astryx-i18n',
|
|
129
|
+
`missing key: ${key} (locale: ${locale})`,
|
|
130
|
+
);
|
|
131
|
+
return key;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (values === undefined) {
|
|
135
|
+
// Static string — skip the parser entirely for the common case
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
134
138
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
};
|
|
139
|
+
const formatted = getFormatter(result, locale).format(values);
|
|
140
|
+
// IntlMessageFormat.format returns string | (string | React elements) — we
|
|
141
|
+
// only ever pass string values so it will be a string; assert for the type
|
|
142
|
+
// system.
|
|
143
|
+
return formatted as string;
|
|
141
144
|
}
|
|
142
145
|
|
|
143
146
|
/**
|
|
@@ -19,8 +19,9 @@
|
|
|
19
19
|
* - /packages/core/src/i18n/t.client.ts
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import {use} from 'react';
|
|
22
|
+
import {use, useCallback} from 'react';
|
|
23
23
|
import {InternationalizationContext} from './InternationalizationContext';
|
|
24
|
+
import {resolve} from './resolve';
|
|
24
25
|
|
|
25
26
|
export type TranslatorFn = (
|
|
26
27
|
key: string,
|
|
@@ -41,5 +42,9 @@ export type TranslatorFn = (
|
|
|
41
42
|
*/
|
|
42
43
|
export function useTranslator(): TranslatorFn {
|
|
43
44
|
const ctx = use(InternationalizationContext);
|
|
44
|
-
return
|
|
45
|
+
return useCallback(
|
|
46
|
+
(key: string, values?: Record<string, unknown>) =>
|
|
47
|
+
resolve(key, values, ctx.locale, ctx.messages, ctx.overrides),
|
|
48
|
+
[ctx.locale, ctx.messages, ctx.overrides],
|
|
49
|
+
);
|
|
45
50
|
}
|