@bquery/bquery 1.7.0 → 1.8.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/README.md +760 -716
- package/dist/{a11y-C5QOVvRn.js → a11y-DVBCy09c.js} +3 -3
- package/dist/a11y-DVBCy09c.js.map +1 -0
- package/dist/a11y.es.mjs +1 -1
- package/dist/component/library.d.ts.map +1 -1
- package/dist/{component-CuuTijA6.js → component-L3-JfOFz.js} +5 -5
- package/dist/component-L3-JfOFz.js.map +1 -0
- package/dist/component.es.mjs +1 -1
- package/dist/{config-BW35FKuA.js → config-DhT9auRm.js} +1 -1
- package/dist/{config-BW35FKuA.js.map → config-DhT9auRm.js.map} +1 -1
- package/dist/{constraints-3lV9yyBw.js → constraints-D5RHQLmP.js} +1 -1
- package/dist/constraints-D5RHQLmP.js.map +1 -0
- package/dist/core/collection.d.ts +86 -0
- package/dist/core/collection.d.ts.map +1 -1
- package/dist/core/element.d.ts +28 -0
- package/dist/core/element.d.ts.map +1 -1
- package/dist/core/shared.d.ts +6 -0
- package/dist/core/shared.d.ts.map +1 -1
- package/dist/core-DdtZHzsS.js +168 -0
- package/dist/core-DdtZHzsS.js.map +1 -0
- package/dist/{core-Cjl7GUu8.js → core-EMYSLzaT.js} +289 -259
- package/dist/core-EMYSLzaT.js.map +1 -0
- package/dist/core.es.mjs +48 -47
- package/dist/{custom-directives-7wAShnnd.js → custom-directives-Dr4C5lVV.js} +1 -1
- package/dist/custom-directives-Dr4C5lVV.js.map +1 -0
- package/dist/{devtools-D2fQLhDN.js → devtools-BhB2iDPT.js} +2 -2
- package/dist/devtools-BhB2iDPT.js.map +1 -0
- package/dist/devtools.es.mjs +1 -1
- package/dist/{dnd-B8EgyzaI.js → dnd-NwZBYh4l.js} +1 -1
- package/dist/dnd-NwZBYh4l.js.map +1 -0
- package/dist/dnd.es.mjs +1 -1
- package/dist/{env-NeVmr4Gf.js → env-CTdvLaH2.js} +1 -1
- package/dist/env-CTdvLaH2.js.map +1 -0
- package/dist/forms/create-form.d.ts.map +1 -1
- package/dist/forms/index.d.ts +3 -2
- package/dist/forms/index.d.ts.map +1 -1
- package/dist/forms/types.d.ts +46 -0
- package/dist/forms/types.d.ts.map +1 -1
- package/dist/forms/use-field.d.ts +34 -0
- package/dist/forms/use-field.d.ts.map +1 -0
- package/dist/forms/validators.d.ts +25 -0
- package/dist/forms/validators.d.ts.map +1 -1
- package/dist/forms-UcRHsYxC.js +227 -0
- package/dist/forms-UcRHsYxC.js.map +1 -0
- package/dist/forms.es.mjs +14 -12
- package/dist/full.d.ts +17 -26
- package/dist/full.d.ts.map +1 -1
- package/dist/full.es.mjs +206 -181
- package/dist/full.iife.js +33 -33
- package/dist/full.iife.js.map +1 -1
- package/dist/full.umd.js +33 -33
- package/dist/full.umd.js.map +1 -1
- package/dist/function-Cybd57JV.js +33 -0
- package/dist/function-Cybd57JV.js.map +1 -0
- package/dist/{i18n-BnnhTFOS.js → i18n-kuF6Ekj6.js} +3 -3
- package/dist/i18n-kuF6Ekj6.js.map +1 -0
- package/dist/i18n.es.mjs +1 -1
- package/dist/index.es.mjs +251 -228
- package/dist/media/breakpoints.d.ts.map +1 -1
- package/dist/media/types.d.ts +2 -2
- package/dist/media/types.d.ts.map +1 -1
- package/dist/{media-Di2Ta22s.js → media-i-fB5WxI.js} +3 -3
- package/dist/media-i-fB5WxI.js.map +1 -0
- package/dist/media.es.mjs +1 -1
- package/dist/{motion-qPj_TYGv.js → motion-BJsAuULb.js} +2 -2
- package/dist/motion-BJsAuULb.js.map +1 -0
- package/dist/motion.es.mjs +1 -1
- package/dist/{mount-SM07RUa6.js → mount-B4Y8bk8Z.js} +5 -5
- package/dist/mount-B4Y8bk8Z.js.map +1 -0
- package/dist/{platform-CPbCprb6.js → platform-Dw2gE3zI.js} +3 -3
- package/dist/{platform-CPbCprb6.js.map → platform-Dw2gE3zI.js.map} +1 -1
- package/dist/platform.es.mjs +2 -2
- package/dist/plugin/registry.d.ts.map +1 -1
- package/dist/{plugin-cPoOHFLY.js → plugin-C2WuC8SF.js} +20 -18
- package/dist/plugin-C2WuC8SF.js.map +1 -0
- package/dist/plugin.es.mjs +1 -1
- package/dist/reactive/async-data.d.ts +28 -3
- package/dist/reactive/async-data.d.ts.map +1 -1
- package/dist/reactive/computed.d.ts +3 -0
- package/dist/reactive/computed.d.ts.map +1 -1
- package/dist/reactive/effect.d.ts +3 -0
- package/dist/reactive/effect.d.ts.map +1 -1
- package/dist/reactive/http.d.ts +194 -0
- package/dist/reactive/http.d.ts.map +1 -0
- package/dist/reactive/index.d.ts +2 -2
- package/dist/reactive/index.d.ts.map +1 -1
- package/dist/reactive/pagination.d.ts +126 -0
- package/dist/reactive/pagination.d.ts.map +1 -0
- package/dist/reactive/polling.d.ts +55 -0
- package/dist/reactive/polling.d.ts.map +1 -0
- package/dist/reactive/readonly.d.ts +20 -1
- package/dist/reactive/readonly.d.ts.map +1 -1
- package/dist/reactive/rest.d.ts +293 -0
- package/dist/reactive/rest.d.ts.map +1 -0
- package/dist/reactive/scope.d.ts +140 -0
- package/dist/reactive/scope.d.ts.map +1 -0
- package/dist/reactive/signal.d.ts +16 -2
- package/dist/reactive/signal.d.ts.map +1 -1
- package/dist/reactive/to-value.d.ts +57 -0
- package/dist/reactive/to-value.d.ts.map +1 -0
- package/dist/reactive/websocket.d.ts +285 -0
- package/dist/reactive/websocket.d.ts.map +1 -0
- package/dist/reactive-DwkhUJfP.js +1148 -0
- package/dist/reactive-DwkhUJfP.js.map +1 -0
- package/dist/reactive.es.mjs +38 -19
- package/dist/{registry-CWf368tT.js → registry-B08iilIh.js} +1 -1
- package/dist/{registry-CWf368tT.js.map → registry-B08iilIh.js.map} +1 -1
- package/dist/router/constraints.d.ts.map +1 -1
- package/dist/router/index.d.ts +1 -1
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/router.d.ts.map +1 -1
- package/dist/router/state.d.ts +25 -2
- package/dist/router/state.d.ts.map +1 -1
- package/dist/router-CQikC9Ed.js +492 -0
- package/dist/router-CQikC9Ed.js.map +1 -0
- package/dist/router.es.mjs +9 -8
- package/dist/ssr/hydrate.d.ts.map +1 -1
- package/dist/{ssr-B2qd_WBB.js → ssr-_dAcGdzu.js} +4 -4
- package/dist/ssr-_dAcGdzu.js.map +1 -0
- package/dist/ssr.es.mjs +1 -1
- package/dist/store/persisted.d.ts.map +1 -1
- package/dist/{store-DWpyH6p5.js → store-Cb3gPRve.js} +7 -7
- package/dist/store-Cb3gPRve.js.map +1 -0
- package/dist/store.es.mjs +2 -2
- package/dist/storybook.es.mjs.map +1 -1
- package/dist/{testing-CsqjNUyy.js → testing-C5Sjfsna.js} +8 -8
- package/dist/testing-C5Sjfsna.js.map +1 -0
- package/dist/testing.es.mjs +1 -1
- package/dist/{type-guards-Do9DWgNp.js → type-guards-BMX2c0LP.js} +1 -1
- package/dist/{type-guards-Do9DWgNp.js.map → type-guards-BMX2c0LP.js.map} +1 -1
- package/dist/untrack-D0fnO5k2.js +36 -0
- package/dist/untrack-D0fnO5k2.js.map +1 -0
- package/dist/view/custom-directives.d.ts.map +1 -1
- package/dist/view.es.mjs +4 -4
- package/package.json +177 -177
- package/src/a11y/announce.ts +131 -131
- package/src/a11y/audit.ts +314 -314
- package/src/a11y/index.ts +68 -68
- package/src/a11y/media-preferences.ts +255 -255
- package/src/a11y/roving-tab-index.ts +164 -164
- package/src/a11y/skip-link.ts +255 -255
- package/src/a11y/trap-focus.ts +184 -184
- package/src/a11y/types.ts +183 -183
- package/src/component/component.ts +599 -599
- package/src/component/html.ts +153 -153
- package/src/component/index.ts +52 -52
- package/src/component/library.ts +540 -542
- package/src/component/scope.ts +212 -212
- package/src/component/types.ts +310 -310
- package/src/core/collection.ts +876 -707
- package/src/core/element.ts +1015 -981
- package/src/core/env.ts +60 -60
- package/src/core/index.ts +49 -49
- package/src/core/shared.ts +77 -62
- package/src/core/utils/index.ts +148 -148
- package/src/devtools/devtools.ts +410 -410
- package/src/devtools/index.ts +48 -48
- package/src/devtools/types.ts +104 -104
- package/src/dnd/draggable.ts +296 -296
- package/src/dnd/droppable.ts +228 -228
- package/src/dnd/index.ts +62 -62
- package/src/dnd/sortable.ts +307 -307
- package/src/dnd/types.ts +293 -293
- package/src/forms/create-form.ts +320 -278
- package/src/forms/index.ts +70 -65
- package/src/forms/types.ts +203 -154
- package/src/forms/use-field.ts +231 -0
- package/src/forms/validators.ts +294 -265
- package/src/full.ts +554 -480
- package/src/i18n/formatting.ts +67 -67
- package/src/i18n/i18n.ts +200 -200
- package/src/i18n/index.ts +67 -67
- package/src/i18n/translate.ts +182 -182
- package/src/i18n/types.ts +171 -171
- package/src/index.ts +108 -108
- package/src/media/battery.ts +116 -116
- package/src/media/breakpoints.ts +129 -131
- package/src/media/clipboard.ts +80 -80
- package/src/media/device-sensors.ts +158 -158
- package/src/media/geolocation.ts +119 -119
- package/src/media/index.ts +76 -76
- package/src/media/media-query.ts +92 -92
- package/src/media/network.ts +115 -115
- package/src/media/types.ts +177 -177
- package/src/media/viewport.ts +84 -84
- package/src/motion/index.ts +57 -57
- package/src/motion/morph.ts +151 -151
- package/src/motion/parallax.ts +120 -120
- package/src/motion/reduced-motion.ts +66 -66
- package/src/motion/types.ts +271 -271
- package/src/motion/typewriter.ts +164 -164
- package/src/plugin/index.ts +37 -37
- package/src/plugin/registry.ts +284 -269
- package/src/plugin/types.ts +137 -137
- package/src/reactive/async-data.ts +250 -29
- package/src/reactive/computed.ts +144 -130
- package/src/reactive/effect.ts +29 -6
- package/src/reactive/http.ts +790 -0
- package/src/reactive/index.ts +60 -0
- package/src/reactive/pagination.ts +317 -0
- package/src/reactive/polling.ts +179 -0
- package/src/reactive/readonly.ts +52 -8
- package/src/reactive/rest.ts +859 -0
- package/src/reactive/scope.ts +276 -0
- package/src/reactive/signal.ts +61 -1
- package/src/reactive/to-value.ts +71 -0
- package/src/reactive/websocket.ts +849 -0
- package/src/router/bq-link.ts +279 -279
- package/src/router/constraints.ts +204 -201
- package/src/router/index.ts +49 -49
- package/src/router/match.ts +312 -312
- package/src/router/path-pattern.ts +52 -52
- package/src/router/query.ts +38 -38
- package/src/router/router.ts +421 -402
- package/src/router/state.ts +51 -3
- package/src/router/types.ts +139 -139
- package/src/router/use-route.ts +68 -68
- package/src/router/utils.ts +157 -157
- package/src/security/index.ts +12 -12
- package/src/ssr/hydrate.ts +84 -82
- package/src/ssr/index.ts +70 -70
- package/src/ssr/render.ts +508 -508
- package/src/ssr/serialize.ts +296 -296
- package/src/ssr/types.ts +81 -81
- package/src/store/create-store.ts +467 -467
- package/src/store/index.ts +27 -27
- package/src/store/persisted.ts +245 -249
- package/src/store/types.ts +247 -247
- package/src/store/utils.ts +135 -135
- package/src/storybook/index.ts +480 -480
- package/src/testing/index.ts +42 -42
- package/src/testing/testing.ts +593 -593
- package/src/testing/types.ts +170 -170
- package/src/view/custom-directives.ts +28 -30
- package/src/view/evaluate.ts +292 -292
- package/src/view/process.ts +108 -108
- package/dist/a11y-C5QOVvRn.js.map +0 -1
- package/dist/component-CuuTijA6.js.map +0 -1
- package/dist/constraints-3lV9yyBw.js.map +0 -1
- package/dist/core-Cjl7GUu8.js.map +0 -1
- package/dist/core-DnlyjbF2.js +0 -112
- package/dist/core-DnlyjbF2.js.map +0 -1
- package/dist/custom-directives-7wAShnnd.js.map +0 -1
- package/dist/devtools-D2fQLhDN.js.map +0 -1
- package/dist/dnd-B8EgyzaI.js.map +0 -1
- package/dist/env-NeVmr4Gf.js.map +0 -1
- package/dist/forms-C3yovgH9.js +0 -141
- package/dist/forms-C3yovgH9.js.map +0 -1
- package/dist/i18n-BnnhTFOS.js.map +0 -1
- package/dist/media-Di2Ta22s.js.map +0 -1
- package/dist/motion-qPj_TYGv.js.map +0 -1
- package/dist/mount-SM07RUa6.js.map +0 -1
- package/dist/plugin-cPoOHFLY.js.map +0 -1
- package/dist/reactive-Cfv0RK6x.js +0 -233
- package/dist/reactive-Cfv0RK6x.js.map +0 -1
- package/dist/router-BrthaP_z.js +0 -473
- package/dist/router-BrthaP_z.js.map +0 -1
- package/dist/ssr-B2qd_WBB.js.map +0 -1
- package/dist/store-DWpyH6p5.js.map +0 -1
- package/dist/testing-CsqjNUyy.js.map +0 -1
- package/dist/untrack-DJVQQ2WM.js +0 -33
- package/dist/untrack-DJVQQ2WM.js.map +0 -1
package/src/i18n/formatting.ts
CHANGED
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Number and date formatting helpers using Intl APIs.
|
|
3
|
-
* @module bquery/i18n
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { DateFormatOptions, NumberFormatOptions } from './types';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Formats a number using the Intl.NumberFormat API.
|
|
10
|
-
*
|
|
11
|
-
* @param value - The number to format
|
|
12
|
-
* @param locale - The locale code (e.g. 'en-US', 'de-DE')
|
|
13
|
-
* @param options - Intl.NumberFormat options
|
|
14
|
-
* @returns The formatted number string
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```ts
|
|
18
|
-
* formatNumber(1234.56, 'en-US'); // '1,234.56'
|
|
19
|
-
* formatNumber(1234.56, 'de-DE'); // '1.234,56'
|
|
20
|
-
* formatNumber(0.42, 'en-US', { style: 'percent' }); // '42%'
|
|
21
|
-
* formatNumber(9.99, 'en-US', { style: 'currency', currency: 'USD' }); // '$9.99'
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
export const formatNumber = (
|
|
25
|
-
value: number,
|
|
26
|
-
locale: string,
|
|
27
|
-
options?: NumberFormatOptions
|
|
28
|
-
): string => {
|
|
29
|
-
const { locale: _ignored, ...intlOptions } = options ?? {};
|
|
30
|
-
try {
|
|
31
|
-
return new Intl.NumberFormat(locale, intlOptions).format(value);
|
|
32
|
-
} catch {
|
|
33
|
-
// Fall back to basic toString on Intl errors
|
|
34
|
-
return String(value);
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Formats a date using the Intl.DateTimeFormat API.
|
|
40
|
-
*
|
|
41
|
-
* @param value - The date to format (Date object or timestamp)
|
|
42
|
-
* @param locale - The locale code
|
|
43
|
-
* @param options - Intl.DateTimeFormat options
|
|
44
|
-
* @returns The formatted date string
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* ```ts
|
|
48
|
-
* const date = new Date('2026-03-26');
|
|
49
|
-
* formatDate(date, 'en-US'); // '3/26/2026'
|
|
50
|
-
* formatDate(date, 'de-DE'); // '26.3.2026'
|
|
51
|
-
* formatDate(date, 'en-US', { dateStyle: 'long' }); // 'March 26, 2026'
|
|
52
|
-
* ```
|
|
53
|
-
*/
|
|
54
|
-
export const formatDate = (
|
|
55
|
-
value: Date | number,
|
|
56
|
-
locale: string,
|
|
57
|
-
options?: DateFormatOptions
|
|
58
|
-
): string => {
|
|
59
|
-
const { locale: _ignored, ...intlOptions } = options ?? {};
|
|
60
|
-
const date = typeof value === 'number' ? new Date(value) : value;
|
|
61
|
-
try {
|
|
62
|
-
return new Intl.DateTimeFormat(locale, intlOptions).format(date);
|
|
63
|
-
} catch {
|
|
64
|
-
// Fall back to toLocaleString on Intl errors
|
|
65
|
-
return date.toLocaleString();
|
|
66
|
-
}
|
|
67
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Number and date formatting helpers using Intl APIs.
|
|
3
|
+
* @module bquery/i18n
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { DateFormatOptions, NumberFormatOptions } from './types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Formats a number using the Intl.NumberFormat API.
|
|
10
|
+
*
|
|
11
|
+
* @param value - The number to format
|
|
12
|
+
* @param locale - The locale code (e.g. 'en-US', 'de-DE')
|
|
13
|
+
* @param options - Intl.NumberFormat options
|
|
14
|
+
* @returns The formatted number string
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* formatNumber(1234.56, 'en-US'); // '1,234.56'
|
|
19
|
+
* formatNumber(1234.56, 'de-DE'); // '1.234,56'
|
|
20
|
+
* formatNumber(0.42, 'en-US', { style: 'percent' }); // '42%'
|
|
21
|
+
* formatNumber(9.99, 'en-US', { style: 'currency', currency: 'USD' }); // '$9.99'
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export const formatNumber = (
|
|
25
|
+
value: number,
|
|
26
|
+
locale: string,
|
|
27
|
+
options?: NumberFormatOptions
|
|
28
|
+
): string => {
|
|
29
|
+
const { locale: _ignored, ...intlOptions } = options ?? {};
|
|
30
|
+
try {
|
|
31
|
+
return new Intl.NumberFormat(locale, intlOptions).format(value);
|
|
32
|
+
} catch {
|
|
33
|
+
// Fall back to basic toString on Intl errors
|
|
34
|
+
return String(value);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Formats a date using the Intl.DateTimeFormat API.
|
|
40
|
+
*
|
|
41
|
+
* @param value - The date to format (Date object or timestamp)
|
|
42
|
+
* @param locale - The locale code
|
|
43
|
+
* @param options - Intl.DateTimeFormat options
|
|
44
|
+
* @returns The formatted date string
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const date = new Date('2026-03-26');
|
|
49
|
+
* formatDate(date, 'en-US'); // '3/26/2026'
|
|
50
|
+
* formatDate(date, 'de-DE'); // '26.3.2026'
|
|
51
|
+
* formatDate(date, 'en-US', { dateStyle: 'long' }); // 'March 26, 2026'
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export const formatDate = (
|
|
55
|
+
value: Date | number,
|
|
56
|
+
locale: string,
|
|
57
|
+
options?: DateFormatOptions
|
|
58
|
+
): string => {
|
|
59
|
+
const { locale: _ignored, ...intlOptions } = options ?? {};
|
|
60
|
+
const date = typeof value === 'number' ? new Date(value) : value;
|
|
61
|
+
try {
|
|
62
|
+
return new Intl.DateTimeFormat(locale, intlOptions).format(date);
|
|
63
|
+
} catch {
|
|
64
|
+
// Fall back to toLocaleString on Intl errors
|
|
65
|
+
return date.toLocaleString();
|
|
66
|
+
}
|
|
67
|
+
};
|
package/src/i18n/i18n.ts
CHANGED
|
@@ -1,200 +1,200 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Core i18n factory function.
|
|
3
|
-
* @module bquery/i18n
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { computed, signal } from '../reactive/index';
|
|
7
|
-
import { isPrototypePollutionKey } from '../core/utils/object';
|
|
8
|
-
import { formatDate, formatNumber } from './formatting';
|
|
9
|
-
import { deepMerge, translate } from './translate';
|
|
10
|
-
import type {
|
|
11
|
-
DateFormatOptions,
|
|
12
|
-
I18nConfig,
|
|
13
|
-
I18nInstance,
|
|
14
|
-
LocaleLoader,
|
|
15
|
-
LocaleMessages,
|
|
16
|
-
Messages,
|
|
17
|
-
NumberFormatOptions,
|
|
18
|
-
TranslateParams,
|
|
19
|
-
} from './types';
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Creates a reactive internationalization instance.
|
|
23
|
-
*
|
|
24
|
-
* The returned object provides:
|
|
25
|
-
* - `$locale` — a reactive signal for the current locale
|
|
26
|
-
* - `t()` — translation with interpolation and pluralization
|
|
27
|
-
* - `tc()` — reactive translation that auto-updates on locale change
|
|
28
|
-
* - `loadLocale()` — register lazy-loaded locale files
|
|
29
|
-
* - `n()` — locale-aware number formatting
|
|
30
|
-
* - `d()` — locale-aware date formatting
|
|
31
|
-
*
|
|
32
|
-
* @param config - Initial configuration
|
|
33
|
-
* @returns An i18n instance
|
|
34
|
-
*
|
|
35
|
-
* @example
|
|
36
|
-
* ```ts
|
|
37
|
-
* import { createI18n } from '@bquery/bquery/i18n';
|
|
38
|
-
*
|
|
39
|
-
* const i18n = createI18n({
|
|
40
|
-
* locale: 'en',
|
|
41
|
-
* fallbackLocale: 'en',
|
|
42
|
-
* messages: {
|
|
43
|
-
* en: {
|
|
44
|
-
* greeting: 'Hello, {name}!',
|
|
45
|
-
* items: '{count} item | {count} items',
|
|
46
|
-
* },
|
|
47
|
-
* de: {
|
|
48
|
-
* greeting: 'Hallo, {name}!',
|
|
49
|
-
* items: '{count} Gegenstand | {count} Gegenstände',
|
|
50
|
-
* },
|
|
51
|
-
* },
|
|
52
|
-
* });
|
|
53
|
-
*
|
|
54
|
-
* i18n.t('greeting', { name: 'Ada' }); // 'Hello, Ada!'
|
|
55
|
-
* i18n.t('items', { count: 3 }); // '3 items'
|
|
56
|
-
*
|
|
57
|
-
* // Switch locale reactively
|
|
58
|
-
* i18n.$locale.value = 'de';
|
|
59
|
-
* i18n.t('greeting', { name: 'Ada' }); // 'Hallo, Ada!'
|
|
60
|
-
* ```
|
|
61
|
-
*/
|
|
62
|
-
export const createI18n = (config: I18nConfig): I18nInstance => {
|
|
63
|
-
const { locale: initialLocale, messages: initialMessages, fallbackLocale } = config;
|
|
64
|
-
|
|
65
|
-
const sanitizeLocaleMessages = (localeMessages: LocaleMessages): LocaleMessages =>
|
|
66
|
-
deepMerge(Object.create(null) as LocaleMessages, localeMessages);
|
|
67
|
-
|
|
68
|
-
// Deep-clone initial messages to prevent external mutation
|
|
69
|
-
const messages = Object.create(null) as Messages;
|
|
70
|
-
for (const [loc, msgs] of Object.entries(initialMessages)) {
|
|
71
|
-
if (isPrototypePollutionKey(loc)) {
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
messages[loc] = sanitizeLocaleMessages(msgs);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Reactive locale signal
|
|
78
|
-
const $locale = signal(initialLocale);
|
|
79
|
-
|
|
80
|
-
// Lazy-loader registry
|
|
81
|
-
const loaders = new Map<string, LocaleLoader>();
|
|
82
|
-
|
|
83
|
-
// Track which loaders have been invoked to avoid duplicate loads
|
|
84
|
-
const loadedLocales = new Set<string>(Object.keys(messages));
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Get messages for a locale, or undefined if not loaded.
|
|
88
|
-
*/
|
|
89
|
-
const getMessages = (loc: string): LocaleMessages | undefined => {
|
|
90
|
-
if (isPrototypePollutionKey(loc)) {
|
|
91
|
-
return undefined;
|
|
92
|
-
}
|
|
93
|
-
return messages[loc];
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Register a lazy-loader for a locale.
|
|
98
|
-
*/
|
|
99
|
-
const loadLocale = (loc: string, loader: LocaleLoader): void => {
|
|
100
|
-
if (isPrototypePollutionKey(loc)) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
loaders.set(loc, loader);
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Ensure a locale's messages are loaded.
|
|
108
|
-
*/
|
|
109
|
-
const ensureLocale = async (loc: string): Promise<void> => {
|
|
110
|
-
if (isPrototypePollutionKey(loc)) return;
|
|
111
|
-
if (loadedLocales.has(loc)) return;
|
|
112
|
-
|
|
113
|
-
const loader = loaders.get(loc);
|
|
114
|
-
if (!loader) {
|
|
115
|
-
throw new Error(`bQuery i18n: No messages or loader registered for locale "${loc}".`);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const loaded = await loader();
|
|
119
|
-
// Handle both default exports and direct objects
|
|
120
|
-
const msgs = (loaded as { default?: LocaleMessages }).default ?? (loaded as LocaleMessages);
|
|
121
|
-
messages[loc] = sanitizeLocaleMessages(msgs);
|
|
122
|
-
loadedLocales.add(loc);
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Translate a key path.
|
|
127
|
-
*/
|
|
128
|
-
const t = (key: string, params: TranslateParams = {}): string => {
|
|
129
|
-
const currentLocale = $locale.value;
|
|
130
|
-
const currentMessages = messages[currentLocale];
|
|
131
|
-
const fallbackMessages = fallbackLocale ? messages[fallbackLocale] : undefined;
|
|
132
|
-
|
|
133
|
-
return translate(currentMessages, key, params, fallbackMessages);
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Reactive translation — returns a computed signal.
|
|
138
|
-
*/
|
|
139
|
-
const tc = (key: string, params: TranslateParams = {}) => {
|
|
140
|
-
return computed(() => {
|
|
141
|
-
// Reading $locale.value creates a reactive dependency
|
|
142
|
-
const currentLocale = $locale.value;
|
|
143
|
-
const currentMessages = messages[currentLocale];
|
|
144
|
-
const fallbackMessages = fallbackLocale ? messages[fallbackLocale] : undefined;
|
|
145
|
-
|
|
146
|
-
return translate(currentMessages, key, params, fallbackMessages);
|
|
147
|
-
});
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Format a number with the current (or overridden) locale.
|
|
152
|
-
*/
|
|
153
|
-
const n = (value: number, options?: NumberFormatOptions): string => {
|
|
154
|
-
const loc = options?.locale ?? $locale.value;
|
|
155
|
-
return formatNumber(value, loc, options);
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Format a date with the current (or overridden) locale.
|
|
160
|
-
*/
|
|
161
|
-
const d = (value: Date | number, options?: DateFormatOptions): string => {
|
|
162
|
-
const loc = options?.locale ?? $locale.value;
|
|
163
|
-
return formatDate(value, loc, options);
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Merge additional messages into a locale.
|
|
168
|
-
*/
|
|
169
|
-
const mergeMessages = (loc: string, newMessages: LocaleMessages): void => {
|
|
170
|
-
if (isPrototypePollutionKey(loc)) {
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
if (!messages[loc]) {
|
|
174
|
-
messages[loc] = Object.create(null) as LocaleMessages;
|
|
175
|
-
loadedLocales.add(loc);
|
|
176
|
-
}
|
|
177
|
-
messages[loc] = deepMerge(messages[loc], sanitizeLocaleMessages(newMessages));
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* List all available locales (loaded + registered loaders).
|
|
182
|
-
*/
|
|
183
|
-
const availableLocales = (): string[] => {
|
|
184
|
-
const locales = new Set<string>([...loadedLocales, ...loaders.keys()]);
|
|
185
|
-
return Array.from(locales).sort();
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
return {
|
|
189
|
-
$locale,
|
|
190
|
-
t,
|
|
191
|
-
tc,
|
|
192
|
-
loadLocale,
|
|
193
|
-
ensureLocale,
|
|
194
|
-
n,
|
|
195
|
-
d,
|
|
196
|
-
getMessages,
|
|
197
|
-
mergeMessages,
|
|
198
|
-
availableLocales,
|
|
199
|
-
};
|
|
200
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Core i18n factory function.
|
|
3
|
+
* @module bquery/i18n
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { computed, signal } from '../reactive/index';
|
|
7
|
+
import { isPrototypePollutionKey } from '../core/utils/object';
|
|
8
|
+
import { formatDate, formatNumber } from './formatting';
|
|
9
|
+
import { deepMerge, translate } from './translate';
|
|
10
|
+
import type {
|
|
11
|
+
DateFormatOptions,
|
|
12
|
+
I18nConfig,
|
|
13
|
+
I18nInstance,
|
|
14
|
+
LocaleLoader,
|
|
15
|
+
LocaleMessages,
|
|
16
|
+
Messages,
|
|
17
|
+
NumberFormatOptions,
|
|
18
|
+
TranslateParams,
|
|
19
|
+
} from './types';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Creates a reactive internationalization instance.
|
|
23
|
+
*
|
|
24
|
+
* The returned object provides:
|
|
25
|
+
* - `$locale` — a reactive signal for the current locale
|
|
26
|
+
* - `t()` — translation with interpolation and pluralization
|
|
27
|
+
* - `tc()` — reactive translation that auto-updates on locale change
|
|
28
|
+
* - `loadLocale()` — register lazy-loaded locale files
|
|
29
|
+
* - `n()` — locale-aware number formatting
|
|
30
|
+
* - `d()` — locale-aware date formatting
|
|
31
|
+
*
|
|
32
|
+
* @param config - Initial configuration
|
|
33
|
+
* @returns An i18n instance
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* import { createI18n } from '@bquery/bquery/i18n';
|
|
38
|
+
*
|
|
39
|
+
* const i18n = createI18n({
|
|
40
|
+
* locale: 'en',
|
|
41
|
+
* fallbackLocale: 'en',
|
|
42
|
+
* messages: {
|
|
43
|
+
* en: {
|
|
44
|
+
* greeting: 'Hello, {name}!',
|
|
45
|
+
* items: '{count} item | {count} items',
|
|
46
|
+
* },
|
|
47
|
+
* de: {
|
|
48
|
+
* greeting: 'Hallo, {name}!',
|
|
49
|
+
* items: '{count} Gegenstand | {count} Gegenstände',
|
|
50
|
+
* },
|
|
51
|
+
* },
|
|
52
|
+
* });
|
|
53
|
+
*
|
|
54
|
+
* i18n.t('greeting', { name: 'Ada' }); // 'Hello, Ada!'
|
|
55
|
+
* i18n.t('items', { count: 3 }); // '3 items'
|
|
56
|
+
*
|
|
57
|
+
* // Switch locale reactively
|
|
58
|
+
* i18n.$locale.value = 'de';
|
|
59
|
+
* i18n.t('greeting', { name: 'Ada' }); // 'Hallo, Ada!'
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export const createI18n = (config: I18nConfig): I18nInstance => {
|
|
63
|
+
const { locale: initialLocale, messages: initialMessages, fallbackLocale } = config;
|
|
64
|
+
|
|
65
|
+
const sanitizeLocaleMessages = (localeMessages: LocaleMessages): LocaleMessages =>
|
|
66
|
+
deepMerge(Object.create(null) as LocaleMessages, localeMessages);
|
|
67
|
+
|
|
68
|
+
// Deep-clone initial messages to prevent external mutation
|
|
69
|
+
const messages = Object.create(null) as Messages;
|
|
70
|
+
for (const [loc, msgs] of Object.entries(initialMessages)) {
|
|
71
|
+
if (isPrototypePollutionKey(loc)) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
messages[loc] = sanitizeLocaleMessages(msgs);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Reactive locale signal
|
|
78
|
+
const $locale = signal(initialLocale);
|
|
79
|
+
|
|
80
|
+
// Lazy-loader registry
|
|
81
|
+
const loaders = new Map<string, LocaleLoader>();
|
|
82
|
+
|
|
83
|
+
// Track which loaders have been invoked to avoid duplicate loads
|
|
84
|
+
const loadedLocales = new Set<string>(Object.keys(messages));
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get messages for a locale, or undefined if not loaded.
|
|
88
|
+
*/
|
|
89
|
+
const getMessages = (loc: string): LocaleMessages | undefined => {
|
|
90
|
+
if (isPrototypePollutionKey(loc)) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
return messages[loc];
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Register a lazy-loader for a locale.
|
|
98
|
+
*/
|
|
99
|
+
const loadLocale = (loc: string, loader: LocaleLoader): void => {
|
|
100
|
+
if (isPrototypePollutionKey(loc)) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
loaders.set(loc, loader);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Ensure a locale's messages are loaded.
|
|
108
|
+
*/
|
|
109
|
+
const ensureLocale = async (loc: string): Promise<void> => {
|
|
110
|
+
if (isPrototypePollutionKey(loc)) return;
|
|
111
|
+
if (loadedLocales.has(loc)) return;
|
|
112
|
+
|
|
113
|
+
const loader = loaders.get(loc);
|
|
114
|
+
if (!loader) {
|
|
115
|
+
throw new Error(`bQuery i18n: No messages or loader registered for locale "${loc}".`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const loaded = await loader();
|
|
119
|
+
// Handle both default exports and direct objects
|
|
120
|
+
const msgs = (loaded as { default?: LocaleMessages }).default ?? (loaded as LocaleMessages);
|
|
121
|
+
messages[loc] = sanitizeLocaleMessages(msgs);
|
|
122
|
+
loadedLocales.add(loc);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Translate a key path.
|
|
127
|
+
*/
|
|
128
|
+
const t = (key: string, params: TranslateParams = {}): string => {
|
|
129
|
+
const currentLocale = $locale.value;
|
|
130
|
+
const currentMessages = messages[currentLocale];
|
|
131
|
+
const fallbackMessages = fallbackLocale ? messages[fallbackLocale] : undefined;
|
|
132
|
+
|
|
133
|
+
return translate(currentMessages, key, params, fallbackMessages);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Reactive translation — returns a computed signal.
|
|
138
|
+
*/
|
|
139
|
+
const tc = (key: string, params: TranslateParams = {}) => {
|
|
140
|
+
return computed(() => {
|
|
141
|
+
// Reading $locale.value creates a reactive dependency
|
|
142
|
+
const currentLocale = $locale.value;
|
|
143
|
+
const currentMessages = messages[currentLocale];
|
|
144
|
+
const fallbackMessages = fallbackLocale ? messages[fallbackLocale] : undefined;
|
|
145
|
+
|
|
146
|
+
return translate(currentMessages, key, params, fallbackMessages);
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Format a number with the current (or overridden) locale.
|
|
152
|
+
*/
|
|
153
|
+
const n = (value: number, options?: NumberFormatOptions): string => {
|
|
154
|
+
const loc = options?.locale ?? $locale.value;
|
|
155
|
+
return formatNumber(value, loc, options);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Format a date with the current (or overridden) locale.
|
|
160
|
+
*/
|
|
161
|
+
const d = (value: Date | number, options?: DateFormatOptions): string => {
|
|
162
|
+
const loc = options?.locale ?? $locale.value;
|
|
163
|
+
return formatDate(value, loc, options);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Merge additional messages into a locale.
|
|
168
|
+
*/
|
|
169
|
+
const mergeMessages = (loc: string, newMessages: LocaleMessages): void => {
|
|
170
|
+
if (isPrototypePollutionKey(loc)) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (!messages[loc]) {
|
|
174
|
+
messages[loc] = Object.create(null) as LocaleMessages;
|
|
175
|
+
loadedLocales.add(loc);
|
|
176
|
+
}
|
|
177
|
+
messages[loc] = deepMerge(messages[loc], sanitizeLocaleMessages(newMessages));
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* List all available locales (loaded + registered loaders).
|
|
182
|
+
*/
|
|
183
|
+
const availableLocales = (): string[] => {
|
|
184
|
+
const locales = new Set<string>([...loadedLocales, ...loaders.keys()]);
|
|
185
|
+
return Array.from(locales).sort();
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
$locale,
|
|
190
|
+
t,
|
|
191
|
+
tc,
|
|
192
|
+
loadLocale,
|
|
193
|
+
ensureLocale,
|
|
194
|
+
n,
|
|
195
|
+
d,
|
|
196
|
+
getMessages,
|
|
197
|
+
mergeMessages,
|
|
198
|
+
availableLocales,
|
|
199
|
+
};
|
|
200
|
+
};
|
package/src/i18n/index.ts
CHANGED
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Internationalization (i18n) module for bQuery.js.
|
|
3
|
-
*
|
|
4
|
-
* Provides a reactive, TypeScript-first internationalization API
|
|
5
|
-
* with interpolation, pluralization, lazy-loading, and locale-aware
|
|
6
|
-
* formatting — all backed by bQuery's signal-based reactivity system.
|
|
7
|
-
*
|
|
8
|
-
* @module bquery/i18n
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```ts
|
|
12
|
-
* import { createI18n } from '@bquery/bquery/i18n';
|
|
13
|
-
*
|
|
14
|
-
* const i18n = createI18n({
|
|
15
|
-
* locale: 'en',
|
|
16
|
-
* fallbackLocale: 'en',
|
|
17
|
-
* messages: {
|
|
18
|
-
* en: {
|
|
19
|
-
* greeting: 'Hello, {name}!',
|
|
20
|
-
* items: '{count} item | {count} items',
|
|
21
|
-
* nested: { deep: { key: 'Found it!' } },
|
|
22
|
-
* },
|
|
23
|
-
* de: {
|
|
24
|
-
* greeting: 'Hallo, {name}!',
|
|
25
|
-
* items: '{count} Gegenstand | {count} Gegenstände',
|
|
26
|
-
* },
|
|
27
|
-
* },
|
|
28
|
-
* });
|
|
29
|
-
*
|
|
30
|
-
* // Basic translation
|
|
31
|
-
* i18n.t('greeting', { name: 'Ada' }); // 'Hello, Ada!'
|
|
32
|
-
*
|
|
33
|
-
* // Pluralization
|
|
34
|
-
* i18n.t('items', { count: 1 }); // '1 item'
|
|
35
|
-
* i18n.t('items', { count: 5 }); // '5 items'
|
|
36
|
-
*
|
|
37
|
-
* // Reactive translation (auto-updates on locale change)
|
|
38
|
-
* const label = i18n.tc('greeting', { name: 'Ada' });
|
|
39
|
-
* console.log(label.value); // 'Hello, Ada!'
|
|
40
|
-
*
|
|
41
|
-
* i18n.$locale.value = 'de';
|
|
42
|
-
* console.log(label.value); // 'Hallo, Ada!'
|
|
43
|
-
*
|
|
44
|
-
* // Number & date formatting
|
|
45
|
-
* i18n.n(1234.56); // '1,234.56' (en)
|
|
46
|
-
* i18n.d(new Date(), { dateStyle: 'long' }); // 'March 26, 2026'
|
|
47
|
-
*
|
|
48
|
-
* // Lazy-load a locale
|
|
49
|
-
* i18n.loadLocale('fr', () => import('./locales/fr.json'));
|
|
50
|
-
* await i18n.ensureLocale('fr');
|
|
51
|
-
* i18n.$locale.value = 'fr';
|
|
52
|
-
* ```
|
|
53
|
-
*/
|
|
54
|
-
|
|
55
|
-
export { createI18n } from './i18n';
|
|
56
|
-
export { formatDate, formatNumber } from './formatting';
|
|
57
|
-
|
|
58
|
-
export type {
|
|
59
|
-
DateFormatOptions,
|
|
60
|
-
I18nConfig,
|
|
61
|
-
I18nInstance,
|
|
62
|
-
LocaleLoader,
|
|
63
|
-
LocaleMessages,
|
|
64
|
-
Messages,
|
|
65
|
-
NumberFormatOptions,
|
|
66
|
-
TranslateParams,
|
|
67
|
-
} from './types';
|
|
1
|
+
/**
|
|
2
|
+
* Internationalization (i18n) module for bQuery.js.
|
|
3
|
+
*
|
|
4
|
+
* Provides a reactive, TypeScript-first internationalization API
|
|
5
|
+
* with interpolation, pluralization, lazy-loading, and locale-aware
|
|
6
|
+
* formatting — all backed by bQuery's signal-based reactivity system.
|
|
7
|
+
*
|
|
8
|
+
* @module bquery/i18n
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { createI18n } from '@bquery/bquery/i18n';
|
|
13
|
+
*
|
|
14
|
+
* const i18n = createI18n({
|
|
15
|
+
* locale: 'en',
|
|
16
|
+
* fallbackLocale: 'en',
|
|
17
|
+
* messages: {
|
|
18
|
+
* en: {
|
|
19
|
+
* greeting: 'Hello, {name}!',
|
|
20
|
+
* items: '{count} item | {count} items',
|
|
21
|
+
* nested: { deep: { key: 'Found it!' } },
|
|
22
|
+
* },
|
|
23
|
+
* de: {
|
|
24
|
+
* greeting: 'Hallo, {name}!',
|
|
25
|
+
* items: '{count} Gegenstand | {count} Gegenstände',
|
|
26
|
+
* },
|
|
27
|
+
* },
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* // Basic translation
|
|
31
|
+
* i18n.t('greeting', { name: 'Ada' }); // 'Hello, Ada!'
|
|
32
|
+
*
|
|
33
|
+
* // Pluralization
|
|
34
|
+
* i18n.t('items', { count: 1 }); // '1 item'
|
|
35
|
+
* i18n.t('items', { count: 5 }); // '5 items'
|
|
36
|
+
*
|
|
37
|
+
* // Reactive translation (auto-updates on locale change)
|
|
38
|
+
* const label = i18n.tc('greeting', { name: 'Ada' });
|
|
39
|
+
* console.log(label.value); // 'Hello, Ada!'
|
|
40
|
+
*
|
|
41
|
+
* i18n.$locale.value = 'de';
|
|
42
|
+
* console.log(label.value); // 'Hallo, Ada!'
|
|
43
|
+
*
|
|
44
|
+
* // Number & date formatting
|
|
45
|
+
* i18n.n(1234.56); // '1,234.56' (en)
|
|
46
|
+
* i18n.d(new Date(), { dateStyle: 'long' }); // 'March 26, 2026'
|
|
47
|
+
*
|
|
48
|
+
* // Lazy-load a locale
|
|
49
|
+
* i18n.loadLocale('fr', () => import('./locales/fr.json'));
|
|
50
|
+
* await i18n.ensureLocale('fr');
|
|
51
|
+
* i18n.$locale.value = 'fr';
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
export { createI18n } from './i18n';
|
|
56
|
+
export { formatDate, formatNumber } from './formatting';
|
|
57
|
+
|
|
58
|
+
export type {
|
|
59
|
+
DateFormatOptions,
|
|
60
|
+
I18nConfig,
|
|
61
|
+
I18nInstance,
|
|
62
|
+
LocaleLoader,
|
|
63
|
+
LocaleMessages,
|
|
64
|
+
Messages,
|
|
65
|
+
NumberFormatOptions,
|
|
66
|
+
TranslateParams,
|
|
67
|
+
} from './types';
|