rune-lab 0.3.0 → 0.4.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/README.md +1 -1
- package/dist/core/design-tokens/props.d.ts +52 -0
- package/dist/core/design-tokens/props.d.ts.map +1 -0
- package/dist/core/design-tokens/props.js +34 -0
- package/dist/core/exchange-rate/strategies.d.ts +52 -0
- package/dist/core/exchange-rate/strategies.d.ts.map +1 -0
- package/dist/core/exchange-rate/strategies.js +72 -0
- package/dist/core/index.d.ts +8 -3
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +8 -3
- package/dist/core/internal/message-resolver.d.ts +1 -1
- package/dist/core/internal/message-resolver.d.ts.map +1 -1
- package/dist/core/layout/types.d.ts +60 -0
- package/dist/core/layout/types.d.ts.map +1 -0
- package/dist/core/layout/types.js +4 -0
- package/dist/core/money/index.d.ts +1 -1
- package/dist/core/money/index.d.ts.map +1 -1
- package/dist/core/money/index.js +1 -1
- package/dist/core/money/money-primitive.d.ts +101 -0
- package/dist/core/money/money-primitive.d.ts.map +1 -0
- package/dist/core/money/money-primitive.js +161 -0
- package/dist/core/money/money.d.ts +74 -2
- package/dist/core/money/money.d.ts.map +1 -1
- package/dist/core/money/money.js +120 -2
- package/dist/core/shortcuts/types.d.ts +60 -0
- package/dist/core/shortcuts/types.d.ts.map +1 -0
- package/dist/core/shortcuts/types.js +4 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +6 -4
- package/dist/state/api.svelte.js +2 -2
- package/dist/state/app.svelte.js +1 -1
- package/dist/state/auth/index.d.ts +2 -2
- package/dist/state/auth/index.js +1 -1
- package/dist/state/auth/session.svelte.d.ts +1 -1
- package/dist/state/auth/session.svelte.js +7 -5
- package/dist/state/auth/types.d.ts +1 -1
- package/dist/state/cart.svelte.d.ts +1 -1
- package/dist/state/cart.svelte.js +1 -1
- package/dist/state/commands.svelte.d.ts +7 -7
- package/dist/state/commands.svelte.js +1 -1
- package/dist/state/composables/useMoney.d.ts +19 -3
- package/dist/state/composables/useMoney.js +70 -6
- package/dist/state/composables/useMoneyFilter.d.ts +20 -0
- package/dist/state/composables/useMoneyFilter.js +81 -0
- package/dist/state/composables/usePersistence.d.ts +1 -1
- package/dist/state/composables/usePersistence.js +1 -1
- package/dist/state/composables/useRuneLab.d.ts +1 -1
- package/dist/state/composables/useRuneLab.js +2 -2
- package/dist/state/composables/useShortcuts.d.ts +33 -0
- package/dist/state/composables/useShortcuts.js +75 -0
- package/dist/state/context.d.ts +1 -0
- package/dist/state/context.js +1 -0
- package/dist/state/createConfigStore.svelte.d.ts +4 -31
- package/dist/state/createConfigStore.svelte.js +62 -51
- package/dist/state/currency.svelte.d.ts +13 -9
- package/dist/state/currency.svelte.js +26 -10
- package/dist/state/currency.test.d.ts +1 -0
- package/dist/state/currency.test.js +35 -0
- package/dist/state/exchange-rate.svelte.d.ts +43 -0
- package/dist/state/exchange-rate.svelte.js +145 -0
- package/dist/state/exchange-rate.test.d.ts +1 -0
- package/dist/state/exchange-rate.test.js +75 -0
- package/dist/state/index.d.ts +26 -19
- package/dist/state/index.js +25 -18
- package/dist/state/language.svelte.d.ts +3 -10
- package/dist/state/language.svelte.js +4 -5
- package/dist/state/layout.svelte.d.ts +1 -1
- package/dist/state/layout.svelte.js +4 -4
- package/dist/state/persistence/drivers.d.ts +1 -1
- package/dist/state/persistence/drivers.js +9 -7
- package/dist/state/persistence/drivers.test.d.ts +1 -0
- package/dist/state/persistence/drivers.test.js +79 -0
- package/dist/state/persistence/provider.d.ts +23 -0
- package/dist/state/persistence/provider.js +43 -0
- package/dist/state/persistence/provider.test.d.ts +1 -0
- package/dist/state/persistence/provider.test.js +51 -0
- package/dist/state/registry/index.d.ts +44 -0
- package/dist/state/registry/index.js +58 -0
- package/dist/state/registry/registry.test.d.ts +1 -0
- package/dist/state/registry/registry.test.js +112 -0
- package/dist/state/registry/types.d.ts +20 -0
- package/dist/state/registry/types.js +3 -0
- package/dist/state/shortcuts.svelte.js +4 -4
- package/dist/state/theme.svelte.d.ts +3 -10
- package/dist/state/theme.svelte.js +8 -8
- package/dist/state/toast-bridge.d.ts +1 -1
- package/dist/state/toast.svelte.js +1 -1
- package/dist/ui/components/ApiMonitor.svelte +2 -2
- package/dist/ui/components/Icon.svelte +1 -1
- package/dist/ui/components/RuneProvider.svelte +28 -8
- package/dist/ui/components/RuneProvider.svelte.d.ts +12 -5
- package/dist/ui/components/Toaster.svelte +1 -1
- package/dist/ui/components/money/MoneyDisplay.svelte +91 -18
- package/dist/ui/components/money/MoneyDisplay.svelte.d.ts +15 -3
- package/dist/ui/components/money/MoneyDisplay.svelte.test.d.ts +1 -1
- package/dist/ui/components/money/MoneyDisplay.svelte.test.js +45 -2
- package/dist/ui/components/money/MoneyInput.svelte +123 -42
- package/dist/ui/components/money/MoneyInput.svelte.d.ts +14 -5
- package/dist/ui/features/command-palette/CommandPalette.svelte +3 -3
- package/dist/ui/features/config/APP_CONFIGURATIONS.d.ts +29 -0
- package/dist/ui/features/config/APP_CONFIGURATIONS.js +38 -0
- package/dist/ui/features/config/CurrencySelector.svelte +10 -36
- package/dist/ui/features/config/LanguageSelector.svelte +10 -33
- package/dist/ui/features/config/ResourceSelector.svelte +92 -0
- package/dist/ui/features/config/ResourceSelector.svelte.d.ts +25 -0
- package/dist/ui/features/config/ThemeSelector.svelte +11 -34
- package/dist/ui/features/shortcuts/ShortcutBinder.svelte +17 -0
- package/dist/ui/features/shortcuts/ShortcutBinder.svelte.d.ts +7 -0
- package/dist/ui/features/shortcuts/ShortcutPalette.svelte +3 -3
- package/dist/ui/index.d.ts +5 -1
- package/dist/ui/index.js +8 -3
- package/dist/ui/layout/ConnectedNavigationPanel.svelte +7 -8
- package/dist/ui/layout/ConnectedNavigationPanel.svelte.d.ts +1 -1
- package/dist/ui/layout/ConnectedWorkspaceStrip.svelte +5 -3
- package/dist/ui/layout/ConnectedWorkspaceStrip.svelte.d.ts +1 -1
- package/dist/ui/layout/NavigationPanel.svelte +1 -1
- package/dist/ui/layout/NavigationPanel.svelte.d.ts +1 -1
- package/dist/ui/layout/WorkspaceLayout.svelte +9 -1
- package/dist/ui/layout/WorkspaceLayout.svelte.d.ts +7 -0
- package/dist/ui/layout/WorkspaceStrip.svelte +1 -1
- package/dist/ui/layout/WorkspaceStrip.svelte.d.ts +1 -1
- package/dist/ui/layout/connection-factory.d.ts +50 -0
- package/dist/ui/layout/connection-factory.js +58 -0
- package/dist/ui/layout/index.d.ts +2 -2
- package/dist/ui/layout/index.js +1 -1
- package/dist/ui/paraglide/README.md +53 -0
- package/dist/ui/paraglide/runtime.d.ts +105 -124
- package/dist/ui/paraglide/runtime.js +162 -127
- package/dist/ui/paraglide/server.d.ts +6 -17
- package/dist/ui/paraglide/server.js +11 -20
- package/dist/ui/primitives/DatePicker.svelte +1 -1
- package/package.json +8 -8
- package/dist/state/daisyui.d.ts +0 -4
|
@@ -20,7 +20,7 @@ export const baseLocale = "en";
|
|
|
20
20
|
* throw new Error('Locale is not available');
|
|
21
21
|
* }
|
|
22
22
|
*/
|
|
23
|
-
export const locales = /** @type {const} */ (["es",
|
|
23
|
+
export const locales = /** @type {const} */ (["es","fr","it","pt","en","de","ru","hi","ar","zh","ja","ko","vi"]);
|
|
24
24
|
/** @type {string} */
|
|
25
25
|
export const cookieName = "PARAGLIDE_LOCALE";
|
|
26
26
|
/** @type {number} */
|
|
@@ -52,7 +52,7 @@ export const routeStrategies = [];
|
|
|
52
52
|
/**
|
|
53
53
|
* The used URL patterns.
|
|
54
54
|
*
|
|
55
|
-
* @type {Array<{ pattern: string, localized: Array<[Locale, string]> }>
|
|
55
|
+
* @type {Array<{ pattern: string, localized: Array<[Locale, string]> }>}
|
|
56
56
|
*/
|
|
57
57
|
export const urlPatterns = [
|
|
58
58
|
{
|
|
@@ -115,11 +115,11 @@ export const urlPatterns = [
|
|
|
115
115
|
];
|
|
116
116
|
/** @type {string | undefined} */
|
|
117
117
|
let cachedRouteStrategyUrl;
|
|
118
|
-
/** @type {{ match: string; strategy?:
|
|
118
|
+
/** @type {{ match: string; strategy?: typeof strategy; exclude?: boolean } | undefined} */
|
|
119
119
|
let cachedRouteStrategy;
|
|
120
120
|
/**
|
|
121
121
|
* @param {string | URL} url
|
|
122
|
-
* @returns {{ match: string; strategy?:
|
|
122
|
+
* @returns {{ match: string; strategy?: typeof strategy; exclude?: boolean } | undefined}
|
|
123
123
|
*/
|
|
124
124
|
function findMatchingRouteStrategy(url) {
|
|
125
125
|
if (routeStrategies.length === 0) {
|
|
@@ -156,7 +156,6 @@ export function getStrategyForUrl(url) {
|
|
|
156
156
|
if (routeStrategy &&
|
|
157
157
|
routeStrategy.exclude !== true &&
|
|
158
158
|
Array.isArray(routeStrategy.strategy)) {
|
|
159
|
-
// @ts-ignore - runtime value is injected and validated by compiler types.
|
|
160
159
|
return routeStrategy.strategy;
|
|
161
160
|
}
|
|
162
161
|
return strategy;
|
|
@@ -194,7 +193,6 @@ export const disableAsyncLocalStorage = false;
|
|
|
194
193
|
export const experimentalMiddlewareLocaleSplitting = false;
|
|
195
194
|
export const isServer = typeof window === 'undefined';
|
|
196
195
|
/** @type {Locale | undefined} */
|
|
197
|
-
// @ts-ignore - injected by bundlers at compile time
|
|
198
196
|
export const experimentalStaticLocale = undefined;
|
|
199
197
|
/**
|
|
200
198
|
* Sets the server side async local storage.
|
|
@@ -216,16 +214,19 @@ const TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED = false;
|
|
|
216
214
|
const TREE_SHAKE_DEFAULT_URL_PATTERN_USED = true;
|
|
217
215
|
const TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED = false;
|
|
218
216
|
|
|
219
|
-
globalThis.__paraglide =
|
|
217
|
+
/** @type {any} */ (globalThis).__paraglide =
|
|
218
|
+
/** @type {any} */ (globalThis).__paraglide ?? {};
|
|
219
|
+
/** @type {any} */ (globalThis).__paraglide.ssr =
|
|
220
|
+
/** @type {any} */ (globalThis).__paraglide.ssr ?? {};
|
|
220
221
|
|
|
221
222
|
/**
|
|
222
223
|
* This is a fallback to get started with a custom
|
|
223
224
|
* strategy and avoid type errors.
|
|
224
225
|
*
|
|
225
226
|
* The implementation is overwritten
|
|
226
|
-
* by
|
|
227
|
+
* by `overwriteGetLocale()` and `defineSetLocale()`.
|
|
227
228
|
*
|
|
228
|
-
* @type {Locale|undefined}
|
|
229
|
+
* @type {Locale | undefined}
|
|
229
230
|
*/
|
|
230
231
|
let _locale;
|
|
231
232
|
let localeInitiallySet = false;
|
|
@@ -245,11 +246,11 @@ let localeInitiallySet = false;
|
|
|
245
246
|
* console.log('Netherlands 🇳🇱');
|
|
246
247
|
* }
|
|
247
248
|
*
|
|
248
|
-
* @
|
|
249
|
+
* @returns {Locale} The current locale.
|
|
249
250
|
*/
|
|
250
251
|
export let getLocale = () => {
|
|
251
252
|
if (experimentalStaticLocale !== undefined) {
|
|
252
|
-
return
|
|
253
|
+
return experimentalStaticLocale;
|
|
253
254
|
}
|
|
254
255
|
// if running in a server-side rendering context
|
|
255
256
|
// retrieve the locale from the async local storage
|
|
@@ -283,7 +284,7 @@ export let getLocale = () => {
|
|
|
283
284
|
*/
|
|
284
285
|
export function getLocaleForUrl(url) {
|
|
285
286
|
if (experimentalStaticLocale !== undefined) {
|
|
286
|
-
return
|
|
287
|
+
return experimentalStaticLocale;
|
|
287
288
|
}
|
|
288
289
|
const strategyToUse = getStrategyForUrl(url);
|
|
289
290
|
const resolved = resolveLocaleWithStrategies(strategyToUse, typeof url === "string" ? url : url.href);
|
|
@@ -337,11 +338,14 @@ function resolveLocaleWithStrategies(strategyToUse, urlForUrlStrategy) {
|
|
|
337
338
|
// Can't await in sync function, skip async strategies
|
|
338
339
|
continue;
|
|
339
340
|
}
|
|
340
|
-
|
|
341
|
+
if (result !== undefined) {
|
|
342
|
+
return assertIsLocale(result);
|
|
343
|
+
}
|
|
341
344
|
}
|
|
342
345
|
}
|
|
343
|
-
|
|
344
|
-
|
|
346
|
+
const matchedLocale = toLocale(locale);
|
|
347
|
+
if (matchedLocale) {
|
|
348
|
+
return matchedLocale;
|
|
345
349
|
}
|
|
346
350
|
}
|
|
347
351
|
return undefined;
|
|
@@ -359,7 +363,7 @@ function resolveLocaleWithStrategies(strategyToUse, urlForUrlStrategy) {
|
|
|
359
363
|
* return Cookies.get('locale') ?? baseLocale
|
|
360
364
|
* });
|
|
361
365
|
*
|
|
362
|
-
* @
|
|
366
|
+
* @param {() => Locale} fn - The new implementation for `getLocale()`.
|
|
363
367
|
*/
|
|
364
368
|
export const overwriteGetLocale = (fn) => {
|
|
365
369
|
getLocale = fn;
|
|
@@ -414,7 +418,6 @@ export function getTextDirection(locale = getLocale()) {
|
|
|
414
418
|
* Navigates to the localized URL, or reloads the current page
|
|
415
419
|
*
|
|
416
420
|
* @param {string} [newLocation] The new location
|
|
417
|
-
* @return {undefined}
|
|
418
421
|
*/
|
|
419
422
|
const navigateOrReload = (newLocation) => {
|
|
420
423
|
if (newLocation) {
|
|
@@ -465,7 +468,7 @@ export let setLocale = (newLocale, options) => {
|
|
|
465
468
|
catch {
|
|
466
469
|
// do nothing, no locale has been set yet.
|
|
467
470
|
}
|
|
468
|
-
/** @type {Array<Promise<
|
|
471
|
+
/** @type {Array<Promise<void>>} */
|
|
469
472
|
const customSetLocalePromises = [];
|
|
470
473
|
/** @type {string | undefined} */
|
|
471
474
|
let newLocation = undefined;
|
|
@@ -550,7 +553,7 @@ export let setLocale = (newLocale, options) => {
|
|
|
550
553
|
return;
|
|
551
554
|
};
|
|
552
555
|
/**
|
|
553
|
-
* Overwrite the
|
|
556
|
+
* Overwrite the `setLocale()` function.
|
|
554
557
|
*
|
|
555
558
|
* Use this function to overwrite how the locale is set. For example,
|
|
556
559
|
* modify a cookie, env variable, or a user's preference.
|
|
@@ -564,7 +567,7 @@ export let setLocale = (newLocale, options) => {
|
|
|
564
567
|
* @param {SetLocaleFn} fn
|
|
565
568
|
*/
|
|
566
569
|
export const overwriteSetLocale = (fn) => {
|
|
567
|
-
setLocale =
|
|
570
|
+
setLocale = fn;
|
|
568
571
|
};
|
|
569
572
|
|
|
570
573
|
/**
|
|
@@ -591,14 +594,32 @@ export let getUrlOrigin = () => {
|
|
|
591
594
|
* Use this function in server environments to
|
|
592
595
|
* define how the URL origin is resolved.
|
|
593
596
|
*
|
|
594
|
-
* @
|
|
597
|
+
* @param {() => string} fn - The new implementation for `getUrlOrigin()`.
|
|
595
598
|
*/
|
|
596
599
|
export let overwriteGetUrlOrigin = (fn) => {
|
|
597
600
|
getUrlOrigin = fn;
|
|
598
601
|
};
|
|
599
602
|
|
|
600
603
|
/**
|
|
601
|
-
*
|
|
604
|
+
* Coerces a locale-like string to the canonical locale value used by the runtime.
|
|
605
|
+
*
|
|
606
|
+
* @param {unknown} value
|
|
607
|
+
* @returns {Locale | undefined}
|
|
608
|
+
*/
|
|
609
|
+
export function toLocale(value) {
|
|
610
|
+
if (typeof value !== "string") {
|
|
611
|
+
return undefined;
|
|
612
|
+
}
|
|
613
|
+
const lowerValue = value.toLowerCase();
|
|
614
|
+
for (const locale of locales) {
|
|
615
|
+
if (locale.toLowerCase() === lowerValue) {
|
|
616
|
+
return locale;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
return undefined;
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Check if something is an available locale with the canonical project casing.
|
|
602
623
|
*
|
|
603
624
|
* @example
|
|
604
625
|
* if (isLocale(params.locale)) {
|
|
@@ -607,34 +628,26 @@ export let overwriteGetUrlOrigin = (fn) => {
|
|
|
607
628
|
* setLocale('en');
|
|
608
629
|
* }
|
|
609
630
|
*
|
|
610
|
-
*
|
|
631
|
+
* Use `toLocale()` when you want case-insensitive matching and canonicalization.
|
|
632
|
+
*
|
|
633
|
+
* @param {unknown} locale
|
|
611
634
|
* @returns {locale is Locale}
|
|
612
635
|
*/
|
|
613
636
|
export function isLocale(locale) {
|
|
614
|
-
|
|
615
|
-
return false;
|
|
616
|
-
return !locale
|
|
617
|
-
? false
|
|
618
|
-
: locales.some((item) => item.toLowerCase() === locale.toLowerCase());
|
|
637
|
+
return !!locale && locales.some((item) => item === locale);
|
|
619
638
|
}
|
|
620
|
-
|
|
621
639
|
/**
|
|
622
|
-
* Asserts that the input
|
|
640
|
+
* Asserts that the input can be normalized to a locale.
|
|
623
641
|
*
|
|
624
|
-
* @param {
|
|
625
|
-
* @returns {Locale} The input
|
|
642
|
+
* @param {unknown} input - The input to check.
|
|
643
|
+
* @returns {Locale} The input normalized to a Locale.
|
|
626
644
|
* @throws {Error} If the input is not a locale.
|
|
627
645
|
*/
|
|
628
646
|
export function assertIsLocale(input) {
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
const matchedLocale = locales.find((item) => item.toLowerCase() === lowerInput);
|
|
634
|
-
if (!matchedLocale) {
|
|
635
|
-
throw new Error(`Invalid locale: ${input}. Expected one of: ${locales.join(", ")}`);
|
|
636
|
-
}
|
|
637
|
-
return matchedLocale;
|
|
647
|
+
const locale = toLocale(input);
|
|
648
|
+
if (locale)
|
|
649
|
+
return locale;
|
|
650
|
+
throw new Error(`Invalid locale: ${input}. Expected one of: ${locales.join(", ")}`);
|
|
638
651
|
}
|
|
639
652
|
|
|
640
653
|
/**
|
|
@@ -653,7 +666,8 @@ export function assertIsLocale(input) {
|
|
|
653
666
|
* @example
|
|
654
667
|
* const locale = extractLocaleFromRequest(request);
|
|
655
668
|
*
|
|
656
|
-
* @
|
|
669
|
+
* @param {Request} request
|
|
670
|
+
* @returns {Locale}
|
|
657
671
|
*/
|
|
658
672
|
export const extractLocaleFromRequest = (request) => {
|
|
659
673
|
return extractLocaleFromRequestWithStrategies(request, getStrategyForUrl(request.url));
|
|
@@ -697,13 +711,9 @@ export const extractLocaleFromRequestWithStrategies = (request, strategies) => {
|
|
|
697
711
|
// Use extractLocaleFromRequestAsync for custom server strategies
|
|
698
712
|
continue;
|
|
699
713
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
}
|
|
704
|
-
else {
|
|
705
|
-
return assertIsLocale(locale);
|
|
706
|
-
}
|
|
714
|
+
const matchedLocale = toLocale(locale);
|
|
715
|
+
if (matchedLocale) {
|
|
716
|
+
return matchedLocale;
|
|
707
717
|
}
|
|
708
718
|
}
|
|
709
719
|
throw new Error("No locale found. There is an error in your strategy. Try adding 'baseLocale' as the very last strategy. Read more here https://inlang.com/m/gerre34r/library-inlang-paraglideJs/errors#no-locale-found");
|
|
@@ -736,7 +746,8 @@ export const extractLocaleFromRequestWithStrategies = (request, strategies) => {
|
|
|
736
746
|
*
|
|
737
747
|
* const locale = await extractLocaleFromRequestAsync(request);
|
|
738
748
|
*
|
|
739
|
-
* @
|
|
749
|
+
* @param {Request} request - The request object to extract the locale from.
|
|
750
|
+
* @returns {Promise<Locale>} The extracted locale.
|
|
740
751
|
*/
|
|
741
752
|
export const extractLocaleFromRequestAsync = async (request) => {
|
|
742
753
|
/** @type {string|undefined} */
|
|
@@ -751,14 +762,14 @@ export const extractLocaleFromRequestAsync = async (request) => {
|
|
|
751
762
|
locale = await handler.getLocale(request);
|
|
752
763
|
}
|
|
753
764
|
// If we got a valid locale from this custom strategy, use it
|
|
754
|
-
|
|
755
|
-
|
|
765
|
+
const matchedLocale = toLocale(locale);
|
|
766
|
+
if (matchedLocale) {
|
|
767
|
+
return matchedLocale;
|
|
756
768
|
}
|
|
757
769
|
}
|
|
758
770
|
}
|
|
759
771
|
// If no custom strategy provided a valid locale, fall back to sync version
|
|
760
|
-
|
|
761
|
-
return assertIsLocale(locale);
|
|
772
|
+
return extractLocaleFromRequestWithStrategies(request, strategy);
|
|
762
773
|
};
|
|
763
774
|
|
|
764
775
|
/**
|
|
@@ -767,7 +778,7 @@ export const extractLocaleFromRequestAsync = async (request) => {
|
|
|
767
778
|
* Will return undefined if the document is not available or if the cookie is not set.
|
|
768
779
|
* The `document` object is not available in server-side rendering, so this function should not be called in that context.
|
|
769
780
|
*
|
|
770
|
-
* @returns {
|
|
781
|
+
* @returns {Locale | undefined}
|
|
771
782
|
*/
|
|
772
783
|
export function extractLocaleFromCookie() {
|
|
773
784
|
if (typeof document === "undefined" || !document.cookie) {
|
|
@@ -775,10 +786,7 @@ export function extractLocaleFromCookie() {
|
|
|
775
786
|
}
|
|
776
787
|
const match = document.cookie.match(new RegExp(`(^| )${cookieName}=([^;]+)`));
|
|
777
788
|
const locale = match?.[2];
|
|
778
|
-
|
|
779
|
-
return locale;
|
|
780
|
-
}
|
|
781
|
-
return undefined;
|
|
789
|
+
return toLocale(locale);
|
|
782
790
|
}
|
|
783
791
|
|
|
784
792
|
/**
|
|
@@ -790,9 +798,8 @@ export function extractLocaleFromCookie() {
|
|
|
790
798
|
* @example
|
|
791
799
|
* const locale = extractLocaleFromHeader(request);
|
|
792
800
|
*
|
|
793
|
-
* @type {(request: Request) => Locale}
|
|
794
801
|
* @param {Request} request - The request object to extract the locale from.
|
|
795
|
-
* @returns {
|
|
802
|
+
* @returns {Locale | undefined} The negotiated preferred language.
|
|
796
803
|
*/
|
|
797
804
|
export function extractLocaleFromHeader(request) {
|
|
798
805
|
const acceptLanguageHeader = request.headers.get("accept-language");
|
|
@@ -803,20 +810,22 @@ export function extractLocaleFromHeader(request) {
|
|
|
803
810
|
.map((lang) => {
|
|
804
811
|
const [tag, q = "1"] = lang.trim().split(";q=");
|
|
805
812
|
// Get both the full tag and base language code
|
|
806
|
-
const baseTag = tag?.split("-")[0]
|
|
813
|
+
const baseTag = tag?.split("-")[0];
|
|
807
814
|
return {
|
|
808
|
-
fullTag: tag
|
|
815
|
+
fullTag: tag,
|
|
809
816
|
baseTag,
|
|
810
817
|
q: Number(q),
|
|
811
818
|
};
|
|
812
819
|
})
|
|
813
820
|
.sort((a, b) => b.q - a.q);
|
|
814
821
|
for (const lang of languages) {
|
|
815
|
-
|
|
816
|
-
|
|
822
|
+
const fullLocale = toLocale(lang.fullTag);
|
|
823
|
+
if (fullLocale) {
|
|
824
|
+
return fullLocale;
|
|
817
825
|
}
|
|
818
|
-
|
|
819
|
-
|
|
826
|
+
const baseLocale = toLocale(lang.baseTag);
|
|
827
|
+
if (baseLocale) {
|
|
828
|
+
return baseLocale;
|
|
820
829
|
}
|
|
821
830
|
}
|
|
822
831
|
return undefined;
|
|
@@ -833,23 +842,24 @@ export function extractLocaleFromHeader(request) {
|
|
|
833
842
|
* @example
|
|
834
843
|
* const locale = extractLocaleFromNavigator();
|
|
835
844
|
*
|
|
836
|
-
* @
|
|
837
|
-
* @returns {string | undefined}
|
|
845
|
+
* @returns {Locale | undefined}
|
|
838
846
|
*/
|
|
839
847
|
export function extractLocaleFromNavigator() {
|
|
840
848
|
if (!navigator?.languages?.length) {
|
|
841
849
|
return undefined;
|
|
842
850
|
}
|
|
843
851
|
const languages = navigator.languages.map((lang) => ({
|
|
844
|
-
fullTag: lang
|
|
845
|
-
baseTag: lang.split("-")[0]
|
|
852
|
+
fullTag: lang,
|
|
853
|
+
baseTag: lang.split("-")[0],
|
|
846
854
|
}));
|
|
847
855
|
for (const lang of languages) {
|
|
848
|
-
|
|
849
|
-
|
|
856
|
+
const fullLocale = toLocale(lang.fullTag);
|
|
857
|
+
if (fullLocale) {
|
|
858
|
+
return fullLocale;
|
|
850
859
|
}
|
|
851
|
-
|
|
852
|
-
|
|
860
|
+
const baseLocale = toLocale(lang.baseTag);
|
|
861
|
+
if (baseLocale) {
|
|
862
|
+
return baseLocale;
|
|
853
863
|
}
|
|
854
864
|
}
|
|
855
865
|
return undefined;
|
|
@@ -868,6 +878,10 @@ let cachedLocale;
|
|
|
868
878
|
/**
|
|
869
879
|
* Extracts the locale from a given URL using native URLPattern.
|
|
870
880
|
*
|
|
881
|
+
* The built-in default `/:locale/...` routing is case-insensitive because it
|
|
882
|
+
* canonicalizes the first path segment with `toLocale()`. Custom `urlPatterns`
|
|
883
|
+
* keep URLPattern's normal exact matching semantics for path segments.
|
|
884
|
+
*
|
|
871
885
|
* @param {URL|string} url - The full URL from which to extract the locale.
|
|
872
886
|
* @returns {Locale|undefined} The extracted locale, or undefined if no locale is found.
|
|
873
887
|
*/
|
|
@@ -876,6 +890,7 @@ export function extractLocaleFromUrl(url) {
|
|
|
876
890
|
if (cachedUrl === urlString) {
|
|
877
891
|
return cachedLocale;
|
|
878
892
|
}
|
|
893
|
+
/** @type {Locale | undefined} */
|
|
879
894
|
let result;
|
|
880
895
|
if (TREE_SHAKE_DEFAULT_URL_PATTERN_USED) {
|
|
881
896
|
result = defaultUrlPatternExtractLocale(url);
|
|
@@ -886,11 +901,7 @@ export function extractLocaleFromUrl(url) {
|
|
|
886
901
|
for (const element of urlPatterns) {
|
|
887
902
|
for (const [locale, localizedPattern] of element.localized) {
|
|
888
903
|
const match = new URLPattern(localizedPattern, urlObj.href).exec(urlObj.href);
|
|
889
|
-
if (
|
|
890
|
-
continue;
|
|
891
|
-
}
|
|
892
|
-
// Check if the locale is valid
|
|
893
|
-
if (assertIsLocale(locale)) {
|
|
904
|
+
if (match) {
|
|
894
905
|
result = locale;
|
|
895
906
|
break;
|
|
896
907
|
}
|
|
@@ -906,20 +917,13 @@ export function extractLocaleFromUrl(url) {
|
|
|
906
917
|
/**
|
|
907
918
|
* https://github.com/opral/inlang-paraglide-js/issues/381
|
|
908
919
|
*
|
|
909
|
-
* @param {URL|string} url - The full URL from which to extract the locale.
|
|
910
|
-
* @returns {Locale|undefined} The extracted locale, or undefined if no locale is found.
|
|
920
|
+
* @param {URL | string} url - The full URL from which to extract the locale.
|
|
921
|
+
* @returns {Locale | undefined} The extracted locale, or undefined if no locale is found.
|
|
911
922
|
*/
|
|
912
923
|
function defaultUrlPatternExtractLocale(url) {
|
|
913
924
|
const urlObj = new URL(url, "http://dummy.com");
|
|
914
925
|
const pathSegments = urlObj.pathname.split("/").filter(Boolean);
|
|
915
|
-
|
|
916
|
-
const potentialLocale = pathSegments[0];
|
|
917
|
-
if (isLocale(potentialLocale)) {
|
|
918
|
-
return potentialLocale;
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
// everything else has to be the base locale
|
|
922
|
-
return baseLocale;
|
|
926
|
+
return toLocale(pathSegments[0]) || baseLocale;
|
|
923
927
|
}
|
|
924
928
|
|
|
925
929
|
/**
|
|
@@ -962,15 +966,17 @@ function defaultUrlPatternExtractLocale(url) {
|
|
|
962
966
|
* ```
|
|
963
967
|
*
|
|
964
968
|
* @param {string | URL} url - The URL to localize. If string, must be absolute.
|
|
965
|
-
* @param {
|
|
966
|
-
* @param {
|
|
969
|
+
* @param {object} [options] - Options for localization
|
|
970
|
+
* @param {Locale} [options.locale] - Target locale. If not provided, uses getLocale()
|
|
967
971
|
* @returns {URL} The localized URL, always absolute
|
|
968
972
|
*/
|
|
969
973
|
export function localizeUrl(url, options) {
|
|
974
|
+
const targetLocale = options?.locale
|
|
975
|
+
? assertIsLocale(options?.locale)
|
|
976
|
+
: getLocale();
|
|
970
977
|
if (TREE_SHAKE_DEFAULT_URL_PATTERN_USED) {
|
|
971
|
-
return localizeUrlDefaultPattern(url,
|
|
978
|
+
return localizeUrlDefaultPattern(url, targetLocale);
|
|
972
979
|
}
|
|
973
|
-
const targetLocale = options?.locale ?? getLocale();
|
|
974
980
|
const urlObj = typeof url === "string" ? new URL(url) : url;
|
|
975
981
|
// Iterate over URL patterns
|
|
976
982
|
for (const element of urlPatterns) {
|
|
@@ -1003,13 +1009,11 @@ export function localizeUrl(url, options) {
|
|
|
1003
1009
|
* https://github.com/opral/inlang-paraglide-js/issues/381
|
|
1004
1010
|
*
|
|
1005
1011
|
* @param {string | URL} url
|
|
1006
|
-
* @param {
|
|
1007
|
-
* @param {string} [options.locale]
|
|
1012
|
+
* @param {Locale} locale
|
|
1008
1013
|
* @returns {URL}
|
|
1009
1014
|
*/
|
|
1010
|
-
function localizeUrlDefaultPattern(url,
|
|
1015
|
+
function localizeUrlDefaultPattern(url, locale) {
|
|
1011
1016
|
const urlObj = typeof url === "string" ? new URL(url, getUrlOrigin()) : new URL(url);
|
|
1012
|
-
const locale = options?.locale ?? getLocale();
|
|
1013
1017
|
const currentLocale = extractLocaleFromUrl(urlObj);
|
|
1014
1018
|
// If current locale matches target locale, no change needed
|
|
1015
1019
|
if (currentLocale === locale) {
|
|
@@ -1017,7 +1021,7 @@ function localizeUrlDefaultPattern(url, options) {
|
|
|
1017
1021
|
}
|
|
1018
1022
|
const pathSegments = urlObj.pathname.split("/").filter(Boolean);
|
|
1019
1023
|
// If current path starts with a locale, remove it
|
|
1020
|
-
if (pathSegments.length > 0 &&
|
|
1024
|
+
if (pathSegments.length > 0 && toLocale(pathSegments[0])) {
|
|
1021
1025
|
pathSegments.shift();
|
|
1022
1026
|
}
|
|
1023
1027
|
// For base locale, don't add prefix
|
|
@@ -1105,7 +1109,7 @@ function deLocalizeUrlDefaultPattern(url) {
|
|
|
1105
1109
|
const urlObj = typeof url === "string" ? new URL(url, getUrlOrigin()) : new URL(url);
|
|
1106
1110
|
const pathSegments = urlObj.pathname.split("/").filter(Boolean);
|
|
1107
1111
|
// If first segment is a locale, remove it
|
|
1108
|
-
if (pathSegments.length > 0 &&
|
|
1112
|
+
if (pathSegments.length > 0 && toLocale(pathSegments[0])) {
|
|
1109
1113
|
urlObj.pathname = "/" + pathSegments.slice(1).join("/");
|
|
1110
1114
|
}
|
|
1111
1115
|
return urlObj;
|
|
@@ -1218,7 +1222,8 @@ function fillPattern(pattern, values, origin) {
|
|
|
1218
1222
|
* Aggregates named groups from various parts of the URLPattern match result.
|
|
1219
1223
|
*
|
|
1220
1224
|
*
|
|
1221
|
-
* @
|
|
1225
|
+
* @param {any} match - The URLPattern match result object.
|
|
1226
|
+
* @returns {Record<string, string | null | undefined>} An object containing all named groups from the match.
|
|
1222
1227
|
*/
|
|
1223
1228
|
export function aggregateGroups(match) {
|
|
1224
1229
|
return {
|
|
@@ -1237,18 +1242,18 @@ export function aggregateGroups(match) {
|
|
|
1237
1242
|
* @typedef {object} ShouldRedirectServerInput
|
|
1238
1243
|
* @property {Request} request
|
|
1239
1244
|
* @property {string | URL} [url]
|
|
1240
|
-
* @property {
|
|
1245
|
+
* @property {Locale} [locale]
|
|
1241
1246
|
*
|
|
1242
1247
|
* @typedef {object} ShouldRedirectClientInput
|
|
1243
1248
|
* @property {undefined} [request]
|
|
1244
1249
|
* @property {string | URL} [url]
|
|
1245
|
-
* @property {
|
|
1250
|
+
* @property {Locale} [locale]
|
|
1246
1251
|
*
|
|
1247
1252
|
* @typedef {ShouldRedirectServerInput | ShouldRedirectClientInput} ShouldRedirectInput
|
|
1248
1253
|
*
|
|
1249
1254
|
* @typedef {object} ShouldRedirectResult
|
|
1250
1255
|
* @property {boolean} shouldRedirect - Indicates whether the consumer should perform a redirect.
|
|
1251
|
-
* @property {
|
|
1256
|
+
* @property {Locale} locale - Locale resolved using the configured strategies.
|
|
1252
1257
|
* @property {URL | undefined} redirectUrl - Destination URL when a redirect is required.
|
|
1253
1258
|
*/
|
|
1254
1259
|
/**
|
|
@@ -1289,7 +1294,7 @@ export function aggregateGroups(match) {
|
|
|
1289
1294
|
*/
|
|
1290
1295
|
export async function shouldRedirect(input = {}) {
|
|
1291
1296
|
const currentUrl = resolveUrl(input);
|
|
1292
|
-
const locale =
|
|
1297
|
+
const locale = await resolveLocale(input, currentUrl);
|
|
1293
1298
|
const strategy = getStrategyForUrl(currentUrl.href);
|
|
1294
1299
|
if (isExcludedByRouteStrategy(currentUrl.href) || !strategy.includes("url")) {
|
|
1295
1300
|
return { shouldRedirect: false, locale, redirectUrl: undefined };
|
|
@@ -1307,11 +1312,12 @@ export async function shouldRedirect(input = {}) {
|
|
|
1307
1312
|
*
|
|
1308
1313
|
* @param {ShouldRedirectInput} input
|
|
1309
1314
|
* @param {URL} currentUrl
|
|
1310
|
-
* @returns {Promise<
|
|
1315
|
+
* @returns {Promise<Locale>}
|
|
1311
1316
|
*/
|
|
1312
1317
|
async function resolveLocale(input, currentUrl) {
|
|
1313
|
-
|
|
1314
|
-
|
|
1318
|
+
const locale = toLocale(input.locale);
|
|
1319
|
+
if (locale) {
|
|
1320
|
+
return locale;
|
|
1315
1321
|
}
|
|
1316
1322
|
if (input.request) {
|
|
1317
1323
|
return extractLocaleFromRequestAsync(input.request);
|
|
@@ -1390,8 +1396,8 @@ function normalizeUrl(url) {
|
|
|
1390
1396
|
* which provides more precise control over URL handling.
|
|
1391
1397
|
*
|
|
1392
1398
|
* @param {string} href - The href to localize (can be relative or absolute)
|
|
1393
|
-
* @param {
|
|
1394
|
-
* @param {
|
|
1399
|
+
* @param {object} [options] - Options for localization
|
|
1400
|
+
* @param {Locale} [options.locale] - Target locale. If not provided, uses `getLocale()`
|
|
1395
1401
|
* @returns {string} The localized href, relative if input was relative
|
|
1396
1402
|
*/
|
|
1397
1403
|
export function localizeHref(href, options) {
|
|
@@ -1523,6 +1529,7 @@ export function trackMessageCall(safeModuleId, locale) {
|
|
|
1523
1529
|
* The order follows each input URL with all its locale variants before moving to the next URL.
|
|
1524
1530
|
*/
|
|
1525
1531
|
export function generateStaticLocalizedUrls(urls) {
|
|
1532
|
+
/** @type {Set<URL>} */
|
|
1526
1533
|
const localizedUrls = new Set();
|
|
1527
1534
|
// For default URL pattern, we can optimize the generation
|
|
1528
1535
|
if (TREE_SHAKE_DEFAULT_URL_PATTERN_USED) {
|
|
@@ -1615,7 +1622,7 @@ export const customClientStrategies = new Map();
|
|
|
1615
1622
|
/**
|
|
1616
1623
|
* Checks if the given strategy is a custom strategy.
|
|
1617
1624
|
*
|
|
1618
|
-
* @param {
|
|
1625
|
+
* @param {unknown} strategy The name of the custom strategy to validate.
|
|
1619
1626
|
* Must be a string that starts with "custom-" followed by alphanumeric characters, hyphens, or underscores.
|
|
1620
1627
|
* @returns {boolean} Returns true if it is a custom strategy, false otherwise.
|
|
1621
1628
|
*/
|
|
@@ -1627,7 +1634,7 @@ export function isCustomStrategy(strategy) {
|
|
|
1627
1634
|
*
|
|
1628
1635
|
* @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/strategy#write-your-own-strategy
|
|
1629
1636
|
*
|
|
1630
|
-
* @param {
|
|
1637
|
+
* @param {string} strategy The name of the custom strategy to define. Must follow the pattern custom-name with alphanumeric characters, hyphens, or underscores.
|
|
1631
1638
|
* @param {CustomServerStrategyHandler} handler The handler for the custom strategy, which should implement
|
|
1632
1639
|
* the method getLocale.
|
|
1633
1640
|
* @returns {void}
|
|
@@ -1643,7 +1650,7 @@ export function defineCustomServerStrategy(strategy, handler) {
|
|
|
1643
1650
|
*
|
|
1644
1651
|
* @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/strategy#write-your-own-strategy
|
|
1645
1652
|
*
|
|
1646
|
-
* @param {
|
|
1653
|
+
* @param {string} strategy The name of the custom strategy to define. Must follow the pattern custom-name with alphanumeric characters, hyphens, or underscores.
|
|
1647
1654
|
* @param {CustomClientStrategyHandler} handler The handler for the custom strategy, which should implement the
|
|
1648
1655
|
* methods getLocale and setLocale.
|
|
1649
1656
|
* @returns {void}
|
|
@@ -1656,26 +1663,25 @@ export function defineCustomClientStrategy(strategy, handler) {
|
|
|
1656
1663
|
}
|
|
1657
1664
|
|
|
1658
1665
|
// ------ TYPES ------
|
|
1659
|
-
|
|
1666
|
+
export {};
|
|
1660
1667
|
/**
|
|
1661
1668
|
* A locale that is available in the project.
|
|
1662
1669
|
*
|
|
1663
1670
|
* @example
|
|
1664
1671
|
* setLocale(request.locale as Locale)
|
|
1665
1672
|
*
|
|
1666
|
-
* @typedef {
|
|
1673
|
+
* @typedef {typeof locales[number]} Locale
|
|
1667
1674
|
*/
|
|
1668
|
-
|
|
1669
1675
|
/**
|
|
1670
1676
|
* A branded type representing a localized string.
|
|
1671
1677
|
*
|
|
1672
|
-
* Message functions return this type instead of
|
|
1678
|
+
* Message functions return this type instead of \`string\`, enabling TypeScript
|
|
1673
1679
|
* to distinguish translated strings from regular strings at compile time.
|
|
1674
1680
|
* This allows you to enforce that only properly localized content is used
|
|
1675
1681
|
* in your UI components.
|
|
1676
1682
|
*
|
|
1677
|
-
* Since
|
|
1678
|
-
* backward compatible—you can pass it anywhere a
|
|
1683
|
+
* Since \`LocalizedString\` is a branded subtype of \`string\`, it remains fully
|
|
1684
|
+
* backward compatible—you can pass it anywhere a \`string\` is expected.
|
|
1679
1685
|
*
|
|
1680
1686
|
* @example
|
|
1681
1687
|
* // Enforce localized strings in your components
|
|
@@ -1707,19 +1713,32 @@ export function defineCustomClientStrategy(strategy, handler) {
|
|
|
1707
1713
|
*
|
|
1708
1714
|
* @typedef {string & { readonly __brand: 'LocalizedString' }} LocalizedString
|
|
1709
1715
|
*/
|
|
1710
|
-
|
|
1716
|
+
/**
|
|
1717
|
+
* A single markup option passed to a tag instance.
|
|
1718
|
+
*
|
|
1719
|
+
* @typedef {{
|
|
1720
|
+
* name: string;
|
|
1721
|
+
* value: unknown;
|
|
1722
|
+
* }} MessageMarkupOption
|
|
1723
|
+
*/
|
|
1724
|
+
/**
|
|
1725
|
+
* A single static markup attribute attached to a tag instance.
|
|
1726
|
+
*
|
|
1727
|
+
* @typedef {{
|
|
1728
|
+
* name: string;
|
|
1729
|
+
* value: string | true;
|
|
1730
|
+
* }} MessageMarkupAttribute
|
|
1731
|
+
*/
|
|
1711
1732
|
/**
|
|
1712
1733
|
* Record of markup options for a tag instance.
|
|
1713
1734
|
*
|
|
1714
1735
|
* @typedef {Record<string, unknown>} MessageMarkupOptions
|
|
1715
1736
|
*/
|
|
1716
|
-
|
|
1717
1737
|
/**
|
|
1718
1738
|
* Record of markup attributes for a tag instance.
|
|
1719
1739
|
*
|
|
1720
1740
|
* @typedef {Record<string, string | true>} MessageMarkupAttributes
|
|
1721
1741
|
*/
|
|
1722
|
-
|
|
1723
1742
|
/**
|
|
1724
1743
|
* Type-level schema for a single markup tag.
|
|
1725
1744
|
*
|
|
@@ -1729,19 +1748,17 @@ export function defineCustomClientStrategy(strategy, handler) {
|
|
|
1729
1748
|
* children: boolean;
|
|
1730
1749
|
* }} MessageMarkupTag
|
|
1731
1750
|
*/
|
|
1732
|
-
|
|
1733
1751
|
/**
|
|
1734
1752
|
* Type-level schema for all markup tags in a message.
|
|
1735
1753
|
*
|
|
1736
1754
|
* @typedef {Record<string, MessageMarkupTag>} MessageMarkupSchema
|
|
1737
1755
|
*/
|
|
1738
|
-
|
|
1739
1756
|
/**
|
|
1740
1757
|
* Type-only metadata attached to compiled message functions.
|
|
1741
1758
|
*
|
|
1742
1759
|
* @template Inputs
|
|
1743
1760
|
* @template Options
|
|
1744
|
-
* @template {MessageMarkupSchema} Markup
|
|
1761
|
+
* @template {MessageMarkupSchema} [Markup = MessageMarkupSchema]
|
|
1745
1762
|
* @typedef {{
|
|
1746
1763
|
* readonly __paraglide?: {
|
|
1747
1764
|
* inputs: Inputs;
|
|
@@ -1750,7 +1767,6 @@ export function defineCustomClientStrategy(strategy, handler) {
|
|
|
1750
1767
|
* };
|
|
1751
1768
|
* }} MessageMetadata
|
|
1752
1769
|
*/
|
|
1753
|
-
|
|
1754
1770
|
/**
|
|
1755
1771
|
* A compiled, framework-neutral message part.
|
|
1756
1772
|
*
|
|
@@ -1774,4 +1790,23 @@ export function defineCustomClientStrategy(strategy, handler) {
|
|
|
1774
1790
|
* attributes: MessageMarkupAttributes;
|
|
1775
1791
|
* }} MessagePart
|
|
1776
1792
|
*/
|
|
1777
|
-
|
|
1793
|
+
/**
|
|
1794
|
+
* A message function is a message for a specific locale.
|
|
1795
|
+
*
|
|
1796
|
+
* @example
|
|
1797
|
+
* m.hello({ name: 'world' })
|
|
1798
|
+
*
|
|
1799
|
+
* @typedef {(inputs?: Record<string, never>) => LocalizedString} MessageFunction
|
|
1800
|
+
*/
|
|
1801
|
+
/**
|
|
1802
|
+
* A message bundle function that selects the message to be returned.
|
|
1803
|
+
*
|
|
1804
|
+
* Uses `getLocale()` under the hood to determine the locale with an option.
|
|
1805
|
+
*
|
|
1806
|
+
* @template {string} T
|
|
1807
|
+
*
|
|
1808
|
+
* @example
|
|
1809
|
+
* * m.hello({ name: 'world' }, { locale: "en" })
|
|
1810
|
+
*
|
|
1811
|
+
* @typedef {(params: Record<string, never>, options: { locale: T }) => LocalizedString} MessageBundleFunction
|
|
1812
|
+
*/
|