@turnipxenon/pineapple 5.3.15 → 5.3.17
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/external/paraglide/.prettierignore +1 -1
- package/dist/external/paraglide/README.md +162 -0
- package/dist/external/paraglide/messages/_index.d.ts +3 -8
- package/dist/external/paraglide/messages/_index.d.ts.map +1 -1
- package/dist/external/paraglide/messages/_index.js +3 -50
- package/dist/external/paraglide/messages/example_message.d.ts +19 -0
- package/dist/external/paraglide/messages/example_message.d.ts.map +1 -0
- package/dist/external/paraglide/messages/example_message.js +34 -0
- package/dist/external/paraglide/messages/package.json +4 -0
- package/dist/external/paraglide/messages/settings.d.ts +17 -0
- package/dist/external/paraglide/messages/settings.d.ts.map +1 -0
- package/dist/external/paraglide/messages/settings.js +33 -0
- package/dist/external/paraglide/registry.d.ts +13 -0
- package/dist/external/paraglide/registry.d.ts.map +1 -1
- package/dist/external/paraglide/registry.js +15 -0
- package/dist/external/paraglide/runtime.d.ts +340 -139
- package/dist/external/paraglide/runtime.d.ts.map +1 -1
- package/dist/external/paraglide/runtime.js +654 -168
- package/dist/external/paraglide/server.d.ts +47 -18
- package/dist/external/paraglide/server.d.ts.map +1 -1
- package/dist/external/paraglide/server.js +150 -35
- package/dist/modules/parsnip/external-images/externalImages.remote.d.ts +2 -2
- package/dist/modules/parsnip/external-images/externalImages.remote.d.ts.map +1 -1
- package/package.json +26 -26
- package/dist/external/paraglide/messages/en.d.ts +0 -5
- package/dist/external/paraglide/messages/en.d.ts.map +0 -1
- package/dist/external/paraglide/messages/en.js +0 -10
- package/dist/external/paraglide/messages/fr.d.ts +0 -5
- package/dist/external/paraglide/messages/fr.d.ts.map +0 -1
- package/dist/external/paraglide/messages/fr.js +0 -10
- package/dist/external/paraglide/messages/tl.d.ts +0 -5
- package/dist/external/paraglide/messages/tl.d.ts.map +0 -1
- package/dist/external/paraglide/messages/tl.js +0 -7
|
@@ -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} */ (["en",
|
|
23
|
+
export const locales = /** @type {const} */ (["en","fr","tl"]);
|
|
24
24
|
/** @type {string} */
|
|
25
25
|
export const cookieName = "PARAGLIDE_LOCALE";
|
|
26
26
|
/** @type {number} */
|
|
@@ -33,14 +33,26 @@ export const localStorageKey = "PARAGLIDE_LOCALE";
|
|
|
33
33
|
* @type {Array<"cookie" | "baseLocale" | "globalVariable" | "url" | "preferredLanguage" | "localStorage" | `custom-${string}`>}
|
|
34
34
|
*/
|
|
35
35
|
export const strategy = [
|
|
36
|
-
"url",
|
|
37
36
|
"cookie",
|
|
37
|
+
"globalVariable",
|
|
38
38
|
"baseLocale"
|
|
39
39
|
];
|
|
40
|
+
/**
|
|
41
|
+
* Route-level strategy overrides.
|
|
42
|
+
*
|
|
43
|
+
* `match` uses URLPattern syntax.
|
|
44
|
+
*
|
|
45
|
+
* @type {Array<{
|
|
46
|
+
* match: string;
|
|
47
|
+
* strategy?: Array<"cookie" | "baseLocale" | "globalVariable" | "url" | "preferredLanguage" | "localStorage" | `custom-${string}`>;
|
|
48
|
+
* exclude?: boolean;
|
|
49
|
+
* }>}
|
|
50
|
+
*/
|
|
51
|
+
export const routeStrategies = [];
|
|
40
52
|
/**
|
|
41
53
|
* The used URL patterns.
|
|
42
54
|
*
|
|
43
|
-
* @type {Array<{ pattern: string, localized: Array<[Locale, string]> }>
|
|
55
|
+
* @type {Array<{ pattern: string, localized: Array<[Locale, string]> }>}
|
|
44
56
|
*/
|
|
45
57
|
export const urlPatterns = [
|
|
46
58
|
{
|
|
@@ -81,9 +93,22 @@ export const urlPatterns = [
|
|
|
81
93
|
* @type {ParaglideAsyncLocalStorage | undefined}
|
|
82
94
|
*/
|
|
83
95
|
export let serverAsyncLocalStorage = undefined;
|
|
96
|
+
/**
|
|
97
|
+
* Returns the current server-side async local storage instance.
|
|
98
|
+
*
|
|
99
|
+
* Accessing the mutable value through a function keeps it observable when
|
|
100
|
+
* module interceptors wrap exported bindings and snapshot their initial value.
|
|
101
|
+
*
|
|
102
|
+
* @returns {ParaglideAsyncLocalStorage | undefined}
|
|
103
|
+
*/
|
|
104
|
+
export function getServerAsyncLocalStorage() {
|
|
105
|
+
return serverAsyncLocalStorage;
|
|
106
|
+
}
|
|
84
107
|
export const disableAsyncLocalStorage = false;
|
|
85
108
|
export const experimentalMiddlewareLocaleSplitting = false;
|
|
86
|
-
export const isServer =
|
|
109
|
+
export const isServer = typeof window === 'undefined';
|
|
110
|
+
/** @type {Locale | undefined} */
|
|
111
|
+
export const experimentalStaticLocale = undefined;
|
|
87
112
|
/**
|
|
88
113
|
* Sets the server side async local storage.
|
|
89
114
|
*
|
|
@@ -98,28 +123,37 @@ export function overwriteServerAsyncLocalStorage(value) {
|
|
|
98
123
|
serverAsyncLocalStorage = value;
|
|
99
124
|
}
|
|
100
125
|
const TREE_SHAKE_COOKIE_STRATEGY_USED = true;
|
|
101
|
-
const TREE_SHAKE_URL_STRATEGY_USED =
|
|
102
|
-
const TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED =
|
|
126
|
+
const TREE_SHAKE_URL_STRATEGY_USED = false;
|
|
127
|
+
const TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED = true;
|
|
103
128
|
const TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED = false;
|
|
104
129
|
const TREE_SHAKE_DEFAULT_URL_PATTERN_USED = true;
|
|
105
130
|
const TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED = false;
|
|
106
131
|
|
|
107
|
-
globalThis.__paraglide =
|
|
132
|
+
/** @type {any} */ (globalThis).__paraglide =
|
|
133
|
+
/** @type {any} */ (globalThis).__paraglide ?? {};
|
|
134
|
+
/** @type {any} */ (globalThis).__paraglide.ssr =
|
|
135
|
+
/** @type {any} */ (globalThis).__paraglide.ssr ?? {};
|
|
108
136
|
|
|
109
137
|
/**
|
|
110
138
|
* This is a fallback to get started with a custom
|
|
111
139
|
* strategy and avoid type errors.
|
|
112
140
|
*
|
|
113
141
|
* The implementation is overwritten
|
|
114
|
-
* by
|
|
142
|
+
* by `overwriteGetLocale()` and `defineSetLocale()`.
|
|
115
143
|
*
|
|
116
|
-
* @type {Locale|undefined}
|
|
144
|
+
* @type {Locale | undefined}
|
|
117
145
|
*/
|
|
118
146
|
let _locale;
|
|
119
147
|
let localeInitiallySet = false;
|
|
120
148
|
/**
|
|
121
149
|
* Get the current locale.
|
|
122
150
|
*
|
|
151
|
+
* The locale is resolved using your configured strategies (URL, cookie, localStorage, etc.)
|
|
152
|
+
* in the order they are defined. In SSR contexts, the locale is retrieved from AsyncLocalStorage
|
|
153
|
+
* which is set by the `paraglideMiddleware()`.
|
|
154
|
+
*
|
|
155
|
+
* @see https://paraglidejs.com/strategy - Configure locale detection strategies
|
|
156
|
+
*
|
|
123
157
|
* @example
|
|
124
158
|
* if (getLocale() === 'de') {
|
|
125
159
|
* console.log('Germany 🇩🇪');
|
|
@@ -127,11 +161,12 @@ let localeInitiallySet = false;
|
|
|
127
161
|
* console.log('Netherlands 🇳🇱');
|
|
128
162
|
* }
|
|
129
163
|
*
|
|
130
|
-
* @
|
|
164
|
+
* @returns {Locale} The current locale.
|
|
131
165
|
*/
|
|
132
166
|
export let getLocale = () => {
|
|
133
|
-
|
|
134
|
-
|
|
167
|
+
if (experimentalStaticLocale !== undefined) {
|
|
168
|
+
return experimentalStaticLocale;
|
|
169
|
+
}
|
|
135
170
|
// if running in a server-side rendering context
|
|
136
171
|
// retrieve the locale from the async local storage
|
|
137
172
|
if (serverAsyncLocalStorage) {
|
|
@@ -140,7 +175,48 @@ export let getLocale = () => {
|
|
|
140
175
|
return locale;
|
|
141
176
|
}
|
|
142
177
|
}
|
|
143
|
-
|
|
178
|
+
let strategyToUse = strategy;
|
|
179
|
+
if (!isServer && typeof window !== "undefined" && window.location?.href) {
|
|
180
|
+
strategyToUse = getStrategyForUrl(window.location.href);
|
|
181
|
+
}
|
|
182
|
+
const resolved = resolveLocaleWithStrategies(strategyToUse, typeof window !== "undefined" ? window.location?.href : undefined);
|
|
183
|
+
if (resolved) {
|
|
184
|
+
if (!localeInitiallySet) {
|
|
185
|
+
_locale = resolved;
|
|
186
|
+
// https://github.com/opral/inlang-paraglide-js/issues/455
|
|
187
|
+
localeInitiallySet = true;
|
|
188
|
+
setLocale(resolved, { reload: false });
|
|
189
|
+
}
|
|
190
|
+
return resolved;
|
|
191
|
+
}
|
|
192
|
+
throw new Error("No locale found. Read the docs https://paraglidejs.com/errors#no-locale-found");
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* Resolve locale for a given URL using route-aware strategies.
|
|
196
|
+
*
|
|
197
|
+
* @param {string | URL} url
|
|
198
|
+
* @returns {Locale}
|
|
199
|
+
*/
|
|
200
|
+
export function getLocaleForUrl(url) {
|
|
201
|
+
if (experimentalStaticLocale !== undefined) {
|
|
202
|
+
return experimentalStaticLocale;
|
|
203
|
+
}
|
|
204
|
+
const strategyToUse = getStrategyForUrl(url);
|
|
205
|
+
const resolved = resolveLocaleWithStrategies(strategyToUse, typeof url === "string" ? url : url.href);
|
|
206
|
+
if (resolved) {
|
|
207
|
+
return resolved;
|
|
208
|
+
}
|
|
209
|
+
throw new Error("No locale found. Read the docs https://paraglidejs.com/errors#no-locale-found");
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* @param {typeof strategy} strategyToUse
|
|
213
|
+
* @param {string | undefined} urlForUrlStrategy
|
|
214
|
+
* @returns {Locale | undefined}
|
|
215
|
+
*/
|
|
216
|
+
function resolveLocaleWithStrategies(strategyToUse, urlForUrlStrategy) {
|
|
217
|
+
/** @type {string | undefined} */
|
|
218
|
+
let locale;
|
|
219
|
+
for (const strat of strategyToUse) {
|
|
144
220
|
if (TREE_SHAKE_COOKIE_STRATEGY_USED && strat === "cookie") {
|
|
145
221
|
locale = extractLocaleFromCookie();
|
|
146
222
|
}
|
|
@@ -150,8 +226,8 @@ export let getLocale = () => {
|
|
|
150
226
|
else if (TREE_SHAKE_URL_STRATEGY_USED &&
|
|
151
227
|
strat === "url" &&
|
|
152
228
|
!isServer &&
|
|
153
|
-
typeof
|
|
154
|
-
locale = extractLocaleFromUrl(
|
|
229
|
+
typeof urlForUrlStrategy === "string") {
|
|
230
|
+
locale = extractLocaleFromUrl(urlForUrlStrategy);
|
|
155
231
|
}
|
|
156
232
|
else if (TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED &&
|
|
157
233
|
strat === "globalVariable" &&
|
|
@@ -177,47 +253,86 @@ export let getLocale = () => {
|
|
|
177
253
|
// Can't await in sync function, skip async strategies
|
|
178
254
|
continue;
|
|
179
255
|
}
|
|
180
|
-
|
|
256
|
+
if (result !== undefined) {
|
|
257
|
+
return assertIsLocale(result);
|
|
258
|
+
}
|
|
181
259
|
}
|
|
182
260
|
}
|
|
183
|
-
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
if (!localeInitiallySet) {
|
|
187
|
-
_locale = asserted;
|
|
188
|
-
// https://github.com/opral/inlang-paraglide-js/issues/455
|
|
189
|
-
localeInitiallySet = true;
|
|
190
|
-
setLocale(asserted, { reload: false });
|
|
191
|
-
}
|
|
192
|
-
return asserted;
|
|
261
|
+
const matchedLocale = toLocale(locale);
|
|
262
|
+
if (matchedLocale) {
|
|
263
|
+
return matchedLocale;
|
|
193
264
|
}
|
|
194
265
|
}
|
|
195
|
-
|
|
196
|
-
}
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
197
268
|
/**
|
|
198
|
-
* Overwrite the
|
|
269
|
+
* Overwrite the `getLocale()` function.
|
|
199
270
|
*
|
|
200
|
-
* Use this function to overwrite how the locale is resolved.
|
|
201
|
-
*
|
|
202
|
-
*
|
|
271
|
+
* Use this function to overwrite how the locale is resolved. This is useful
|
|
272
|
+
* for custom locale resolution or advanced use cases like SSG with concurrent rendering.
|
|
273
|
+
*
|
|
274
|
+
* @see https://paraglidejs.com/strategy
|
|
203
275
|
*
|
|
204
276
|
* @example
|
|
205
277
|
* overwriteGetLocale(() => {
|
|
206
|
-
* // resolve the locale from a cookie. fallback to the base locale.
|
|
207
278
|
* return Cookies.get('locale') ?? baseLocale
|
|
208
|
-
* }
|
|
279
|
+
* });
|
|
209
280
|
*
|
|
210
|
-
* @
|
|
281
|
+
* @param {() => Locale} fn - The new implementation for `getLocale()`.
|
|
211
282
|
*/
|
|
212
283
|
export const overwriteGetLocale = (fn) => {
|
|
213
284
|
getLocale = fn;
|
|
214
285
|
};
|
|
215
286
|
|
|
287
|
+
const rtlLanguages = new Set([
|
|
288
|
+
"ar",
|
|
289
|
+
"dv",
|
|
290
|
+
"fa",
|
|
291
|
+
"he",
|
|
292
|
+
"ks",
|
|
293
|
+
"ku",
|
|
294
|
+
"ps",
|
|
295
|
+
"sd",
|
|
296
|
+
"ug",
|
|
297
|
+
"ur",
|
|
298
|
+
"yi",
|
|
299
|
+
]);
|
|
300
|
+
/**
|
|
301
|
+
* Get writing direction for a locale.
|
|
302
|
+
*
|
|
303
|
+
* Uses `Intl.Locale` text info when available and falls back to a
|
|
304
|
+
* language-based RTL check for runtimes without `getTextInfo()`.
|
|
305
|
+
*
|
|
306
|
+
* @example
|
|
307
|
+
* getTextDirection(); // "ltr" or "rtl" for current locale
|
|
308
|
+
* getTextDirection("ar"); // "rtl"
|
|
309
|
+
* getTextDirection("en"); // "ltr"
|
|
310
|
+
*
|
|
311
|
+
* @param {string} [locale] - Target locale. If not provided, uses `getLocale()`
|
|
312
|
+
* @returns {"ltr" | "rtl"}
|
|
313
|
+
*/
|
|
314
|
+
export function getTextDirection(locale = getLocale()) {
|
|
315
|
+
try {
|
|
316
|
+
const intlLocale = /** @type {Intl.Locale & {
|
|
317
|
+
getTextInfo?: () => { direction?: string };
|
|
318
|
+
textInfo?: { direction?: string };
|
|
319
|
+
}} */ (new Intl.Locale(locale));
|
|
320
|
+
const direction = intlLocale.getTextInfo?.().direction ?? intlLocale.textInfo?.direction;
|
|
321
|
+
if (direction === "ltr" || direction === "rtl") {
|
|
322
|
+
return direction;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
catch {
|
|
326
|
+
// Ignore Intl.Locale parsing/runtime errors and use fallback below.
|
|
327
|
+
}
|
|
328
|
+
const language = locale.split("-")[0]?.toLowerCase();
|
|
329
|
+
return rtlLanguages.has(language ?? "") ? "rtl" : "ltr";
|
|
330
|
+
}
|
|
331
|
+
|
|
216
332
|
/**
|
|
217
333
|
* Navigates to the localized URL, or reloads the current page
|
|
218
334
|
*
|
|
219
335
|
* @param {string} [newLocation] The new location
|
|
220
|
-
* @return {undefined}
|
|
221
336
|
*/
|
|
222
337
|
const navigateOrReload = (newLocation) => {
|
|
223
338
|
if (newLocation) {
|
|
@@ -235,13 +350,18 @@ const navigateOrReload = (newLocation) => {
|
|
|
235
350
|
/**
|
|
236
351
|
* Set the locale.
|
|
237
352
|
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
353
|
+
* Updates the locale using your configured strategies (cookie, localStorage, URL, etc.).
|
|
354
|
+
* By default, this navigates the client to the localized URL or reloads the current
|
|
355
|
+
* document to reflect the new locale. `reload: false` is a narrow browser-only escape
|
|
356
|
+
* hatch for a fully client-rendered, non-URL-routed surface that owns its reactive
|
|
357
|
+
* updates and document state. It does not re-render the UI or update the document.
|
|
358
|
+
* Do not use it for normal locale pickers, URL-routed pages, or switching an SSR,
|
|
359
|
+
* SSG, or hydrated document. It is incompatible with per-locale builds.
|
|
242
360
|
*
|
|
243
|
-
* If any custom strategy's
|
|
244
|
-
*
|
|
361
|
+
* If any custom strategy's `setLocale` function is async, then this function
|
|
362
|
+
* will become async as well.
|
|
363
|
+
*
|
|
364
|
+
* @see https://paraglidejs.com/strategy
|
|
245
365
|
*
|
|
246
366
|
* @example
|
|
247
367
|
* setLocale('en');
|
|
@@ -256,6 +376,12 @@ export let setLocale = (newLocale, options) => {
|
|
|
256
376
|
reload: true,
|
|
257
377
|
...options,
|
|
258
378
|
};
|
|
379
|
+
if (experimentalStaticLocale !== undefined &&
|
|
380
|
+
newLocale !== experimentalStaticLocale &&
|
|
381
|
+
optionsWithDefaults.reload === false) {
|
|
382
|
+
console.warn(`Paraglide: setLocale(${JSON.stringify(newLocale)}, { reload: false }) cannot switch away from the statically built locale ${JSON.stringify(experimentalStaticLocale)}. A document navigation is required; reload has been forced to true.`);
|
|
383
|
+
optionsWithDefaults.reload = true;
|
|
384
|
+
}
|
|
259
385
|
// locale is already set
|
|
260
386
|
// https://github.com/opral/inlang-paraglide-js/issues/430
|
|
261
387
|
/** @type {Locale | undefined} */
|
|
@@ -266,11 +392,15 @@ export let setLocale = (newLocale, options) => {
|
|
|
266
392
|
catch {
|
|
267
393
|
// do nothing, no locale has been set yet.
|
|
268
394
|
}
|
|
269
|
-
/** @type {Array<Promise<
|
|
395
|
+
/** @type {Array<Promise<void>>} */
|
|
270
396
|
const customSetLocalePromises = [];
|
|
271
397
|
/** @type {string | undefined} */
|
|
272
398
|
let newLocation = undefined;
|
|
273
|
-
|
|
399
|
+
let strategyToUse = strategy;
|
|
400
|
+
if (!isServer && typeof window !== "undefined" && window.location?.href) {
|
|
401
|
+
strategyToUse = getStrategyForUrl(window.location.href);
|
|
402
|
+
}
|
|
403
|
+
for (const strat of strategyToUse) {
|
|
274
404
|
if (TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED &&
|
|
275
405
|
strat === "globalVariable") {
|
|
276
406
|
// a default for a custom strategy to get started quickly
|
|
@@ -288,6 +418,7 @@ export let setLocale = (newLocale, options) => {
|
|
|
288
418
|
document.cookie = cookieDomain
|
|
289
419
|
? `${cookieString}; domain=${cookieDomain}`
|
|
290
420
|
: cookieString;
|
|
421
|
+
clearLocaleCookieCache();
|
|
291
422
|
}
|
|
292
423
|
else if (strat === "baseLocale") {
|
|
293
424
|
// nothing to be set here. baseLocale is only a fallback
|
|
@@ -347,7 +478,7 @@ export let setLocale = (newLocale, options) => {
|
|
|
347
478
|
return;
|
|
348
479
|
};
|
|
349
480
|
/**
|
|
350
|
-
* Overwrite the
|
|
481
|
+
* Overwrite the `setLocale()` function.
|
|
351
482
|
*
|
|
352
483
|
* Use this function to overwrite how the locale is set. For example,
|
|
353
484
|
* modify a cookie, env variable, or a user's preference.
|
|
@@ -361,13 +492,13 @@ export let setLocale = (newLocale, options) => {
|
|
|
361
492
|
* @param {SetLocaleFn} fn
|
|
362
493
|
*/
|
|
363
494
|
export const overwriteSetLocale = (fn) => {
|
|
364
|
-
setLocale =
|
|
495
|
+
setLocale = fn;
|
|
365
496
|
};
|
|
366
497
|
|
|
367
498
|
/**
|
|
368
499
|
* The origin of the current URL.
|
|
369
500
|
*
|
|
370
|
-
* Defaults to "http://
|
|
501
|
+
* Defaults to "http://example.com" in non-browser environments. If this
|
|
371
502
|
* behavior is not desired, the implementation can be overwritten
|
|
372
503
|
* by `overwriteGetUrlOrigin()`.
|
|
373
504
|
*
|
|
@@ -388,14 +519,32 @@ export let getUrlOrigin = () => {
|
|
|
388
519
|
* Use this function in server environments to
|
|
389
520
|
* define how the URL origin is resolved.
|
|
390
521
|
*
|
|
391
|
-
* @
|
|
522
|
+
* @param {() => string} fn - The new implementation for `getUrlOrigin()`.
|
|
392
523
|
*/
|
|
393
524
|
export let overwriteGetUrlOrigin = (fn) => {
|
|
394
525
|
getUrlOrigin = fn;
|
|
395
526
|
};
|
|
396
527
|
|
|
397
528
|
/**
|
|
398
|
-
*
|
|
529
|
+
* Coerces a locale-like string to the canonical locale value used by the runtime.
|
|
530
|
+
*
|
|
531
|
+
* @param {unknown} value
|
|
532
|
+
* @returns {Locale | undefined}
|
|
533
|
+
*/
|
|
534
|
+
export function toLocale(value) {
|
|
535
|
+
if (typeof value !== "string") {
|
|
536
|
+
return undefined;
|
|
537
|
+
}
|
|
538
|
+
const lowerValue = value.toLowerCase();
|
|
539
|
+
for (const locale of locales) {
|
|
540
|
+
if (locale.toLowerCase() === lowerValue) {
|
|
541
|
+
return locale;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return undefined;
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Check if something is an available locale with the canonical project casing.
|
|
399
548
|
*
|
|
400
549
|
* @example
|
|
401
550
|
* if (isLocale(params.locale)) {
|
|
@@ -404,27 +553,32 @@ export let overwriteGetUrlOrigin = (fn) => {
|
|
|
404
553
|
* setLocale('en');
|
|
405
554
|
* }
|
|
406
555
|
*
|
|
407
|
-
*
|
|
556
|
+
* Use `toLocale()` when you want case-insensitive matching and canonicalization.
|
|
557
|
+
*
|
|
558
|
+
* @param {unknown} locale
|
|
408
559
|
* @returns {locale is Locale}
|
|
409
560
|
*/
|
|
410
561
|
export function isLocale(locale) {
|
|
411
|
-
return
|
|
562
|
+
return !!locale && locales.some((item) => item === locale);
|
|
412
563
|
}
|
|
413
|
-
|
|
414
564
|
/**
|
|
415
|
-
* Asserts that the input
|
|
565
|
+
* Asserts that the input can be normalized to a locale.
|
|
416
566
|
*
|
|
417
|
-
* @param {
|
|
418
|
-
* @returns {Locale} The input
|
|
567
|
+
* @param {unknown} input - The input to check.
|
|
568
|
+
* @returns {Locale} The input normalized to a Locale.
|
|
419
569
|
* @throws {Error} If the input is not a locale.
|
|
420
570
|
*/
|
|
421
571
|
export function assertIsLocale(input) {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
572
|
+
const locale = toLocale(input);
|
|
573
|
+
if (locale)
|
|
574
|
+
return locale;
|
|
575
|
+
throw new Error(`Invalid locale: ${input}. Expected one of: ${locales.join(", ")}`);
|
|
426
576
|
}
|
|
427
577
|
|
|
578
|
+
/**
|
|
579
|
+
* @typedef {object} ExtractLocaleFromRequestOptions
|
|
580
|
+
* @property {string | URL} [effectiveRequestUrl] - Effective request URL to use for route matching and locale detection with the URL strategy.
|
|
581
|
+
*/
|
|
428
582
|
/**
|
|
429
583
|
* Extracts a locale from a request.
|
|
430
584
|
*
|
|
@@ -441,21 +595,38 @@ export function assertIsLocale(input) {
|
|
|
441
595
|
* @example
|
|
442
596
|
* const locale = extractLocaleFromRequest(request);
|
|
443
597
|
*
|
|
444
|
-
* @
|
|
598
|
+
* @param {Request} request
|
|
599
|
+
* @param {ExtractLocaleFromRequestOptions} [options]
|
|
600
|
+
* @returns {Locale}
|
|
445
601
|
*/
|
|
446
|
-
export const extractLocaleFromRequest = (request) => {
|
|
602
|
+
export const extractLocaleFromRequest = (request, options = {}) => {
|
|
603
|
+
const effectiveRequestUrl = resolveEffectiveRequestUrl(request, options.effectiveRequestUrl);
|
|
604
|
+
return extractLocaleFromRequestWithStrategies(request, getStrategyForUrl(effectiveRequestUrl), effectiveRequestUrl);
|
|
605
|
+
};
|
|
606
|
+
/**
|
|
607
|
+
* Extracts a locale from a request using the provided strategy order.
|
|
608
|
+
*
|
|
609
|
+
* @param {Request} request
|
|
610
|
+
* @param {typeof strategy} strategies
|
|
611
|
+
* @param {string | URL} [url]
|
|
612
|
+
* @returns {Locale}
|
|
613
|
+
*/
|
|
614
|
+
export const extractLocaleFromRequestWithStrategies = (request, strategies, url = request.url) => {
|
|
615
|
+
const effectiveRequestUrl = resolveEffectiveRequestUrl(request, url);
|
|
447
616
|
/** @type {string|undefined} */
|
|
448
617
|
let locale;
|
|
449
|
-
for (const strat of
|
|
618
|
+
for (const strat of strategies) {
|
|
450
619
|
if (TREE_SHAKE_COOKIE_STRATEGY_USED && strat === "cookie") {
|
|
620
|
+
const cookiePrefix = cookieName + "=";
|
|
451
621
|
locale = request.headers
|
|
452
622
|
.get("cookie")
|
|
453
|
-
?.split(";
|
|
454
|
-
.
|
|
455
|
-
|
|
623
|
+
?.split(";")
|
|
624
|
+
.map((c) => c.trim())
|
|
625
|
+
.find((c) => c.startsWith(cookiePrefix))
|
|
626
|
+
?.slice(cookiePrefix.length);
|
|
456
627
|
}
|
|
457
628
|
else if (TREE_SHAKE_URL_STRATEGY_USED && strat === "url") {
|
|
458
|
-
locale = extractLocaleFromUrl(
|
|
629
|
+
locale = extractLocaleFromUrl(effectiveRequestUrl);
|
|
459
630
|
}
|
|
460
631
|
else if (TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED &&
|
|
461
632
|
strat === "preferredLanguage") {
|
|
@@ -475,17 +646,24 @@ export const extractLocaleFromRequest = (request) => {
|
|
|
475
646
|
// Use extractLocaleFromRequestAsync for custom server strategies
|
|
476
647
|
continue;
|
|
477
648
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
}
|
|
482
|
-
else {
|
|
483
|
-
return assertIsLocale(locale);
|
|
484
|
-
}
|
|
649
|
+
const matchedLocale = toLocale(locale);
|
|
650
|
+
if (matchedLocale) {
|
|
651
|
+
return matchedLocale;
|
|
485
652
|
}
|
|
486
653
|
}
|
|
487
|
-
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://
|
|
654
|
+
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://paraglidejs.com/errors#no-locale-found");
|
|
488
655
|
};
|
|
656
|
+
/**
|
|
657
|
+
* @param {Request} request
|
|
658
|
+
* @param {string | URL | undefined} effectiveRequestUrl
|
|
659
|
+
* @returns {URL}
|
|
660
|
+
*/
|
|
661
|
+
function resolveEffectiveRequestUrl(request, effectiveRequestUrl = request.url) {
|
|
662
|
+
if (effectiveRequestUrl instanceof URL) {
|
|
663
|
+
return new URL(effectiveRequestUrl.href);
|
|
664
|
+
}
|
|
665
|
+
return new URL(effectiveRequestUrl, request.url);
|
|
666
|
+
}
|
|
489
667
|
|
|
490
668
|
/**
|
|
491
669
|
* Asynchronously extracts a locale from a request.
|
|
@@ -514,11 +692,15 @@ export const extractLocaleFromRequest = (request) => {
|
|
|
514
692
|
*
|
|
515
693
|
* const locale = await extractLocaleFromRequestAsync(request);
|
|
516
694
|
*
|
|
517
|
-
* @
|
|
695
|
+
* @param {Request} request - The request object to extract the locale from.
|
|
696
|
+
* @param {{ effectiveRequestUrl?: string | URL }} [options] - Effective request URL to use for route matching and locale detection with the URL strategy.
|
|
697
|
+
* @returns {Promise<Locale>} The extracted locale.
|
|
518
698
|
*/
|
|
519
|
-
export const extractLocaleFromRequestAsync = async (request) => {
|
|
699
|
+
export const extractLocaleFromRequestAsync = async (request, options = {}) => {
|
|
520
700
|
/** @type {string|undefined} */
|
|
521
701
|
let locale;
|
|
702
|
+
const effectiveRequestUrl = resolveEffectiveRequestUrlFromRequestAsync(request, options.effectiveRequestUrl);
|
|
703
|
+
const strategy = getStrategyForUrl(effectiveRequestUrl);
|
|
522
704
|
// Process custom strategies first, in order
|
|
523
705
|
for (const strat of strategy) {
|
|
524
706
|
if (isCustomStrategy(strat) && customServerStrategies.has(strat)) {
|
|
@@ -528,34 +710,66 @@ export const extractLocaleFromRequestAsync = async (request) => {
|
|
|
528
710
|
locale = await handler.getLocale(request);
|
|
529
711
|
}
|
|
530
712
|
// If we got a valid locale from this custom strategy, use it
|
|
531
|
-
|
|
532
|
-
|
|
713
|
+
const matchedLocale = toLocale(locale);
|
|
714
|
+
if (matchedLocale) {
|
|
715
|
+
return matchedLocale;
|
|
533
716
|
}
|
|
534
717
|
}
|
|
535
718
|
}
|
|
536
719
|
// If no custom strategy provided a valid locale, fall back to sync version
|
|
537
|
-
|
|
538
|
-
return assertIsLocale(locale);
|
|
720
|
+
return extractLocaleFromRequestWithStrategies(request, strategy, effectiveRequestUrl);
|
|
539
721
|
};
|
|
722
|
+
/**
|
|
723
|
+
* @param {Request} request
|
|
724
|
+
* @param {string | URL | undefined} effectiveRequestUrl
|
|
725
|
+
* @returns {URL}
|
|
726
|
+
*/
|
|
727
|
+
function resolveEffectiveRequestUrlFromRequestAsync(request, effectiveRequestUrl = request.url) {
|
|
728
|
+
if (effectiveRequestUrl instanceof URL) {
|
|
729
|
+
return new URL(effectiveRequestUrl.href);
|
|
730
|
+
}
|
|
731
|
+
return new URL(effectiveRequestUrl, request.url);
|
|
732
|
+
}
|
|
540
733
|
|
|
734
|
+
const cookieNamePattern = cookieName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
735
|
+
const localeCookiePattern = new RegExp(`(?:^|;\\s*)${cookieNamePattern}=([^;]*)`);
|
|
736
|
+
const noCachedLocale = Symbol();
|
|
737
|
+
/** @type {Locale | undefined | typeof noCachedLocale} */
|
|
738
|
+
let cachedLocaleFromCookie = noCachedLocale;
|
|
739
|
+
/**
|
|
740
|
+
* Clears the cached locale from `document.cookie`.
|
|
741
|
+
*/
|
|
742
|
+
function clearLocaleCookieCache() {
|
|
743
|
+
cachedLocaleFromCookie = noCachedLocale;
|
|
744
|
+
}
|
|
745
|
+
function scheduleLocaleCookieCacheClear() {
|
|
746
|
+
if (typeof queueMicrotask === "function") {
|
|
747
|
+
queueMicrotask(clearLocaleCookieCache);
|
|
748
|
+
}
|
|
749
|
+
else {
|
|
750
|
+
Promise.resolve().then(clearLocaleCookieCache);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
541
753
|
/**
|
|
542
754
|
* Extracts a cookie from the document.
|
|
543
755
|
*
|
|
544
756
|
* Will return undefined if the document is not available or if the cookie is not set.
|
|
545
757
|
* The `document` object is not available in server-side rendering, so this function should not be called in that context.
|
|
546
758
|
*
|
|
547
|
-
* @returns {
|
|
759
|
+
* @returns {Locale | undefined}
|
|
548
760
|
*/
|
|
549
761
|
export function extractLocaleFromCookie() {
|
|
550
|
-
if (typeof document === "undefined"
|
|
762
|
+
if (typeof document === "undefined") {
|
|
551
763
|
return;
|
|
552
764
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
if (isLocale(locale)) {
|
|
556
|
-
return locale;
|
|
765
|
+
if (cachedLocaleFromCookie !== noCachedLocale) {
|
|
766
|
+
return cachedLocaleFromCookie;
|
|
557
767
|
}
|
|
558
|
-
|
|
768
|
+
const match = document.cookie.match(localeCookiePattern);
|
|
769
|
+
const locale = match?.[1];
|
|
770
|
+
cachedLocaleFromCookie = toLocale(locale);
|
|
771
|
+
scheduleLocaleCookieCacheClear();
|
|
772
|
+
return cachedLocaleFromCookie;
|
|
559
773
|
}
|
|
560
774
|
|
|
561
775
|
/**
|
|
@@ -567,9 +781,8 @@ export function extractLocaleFromCookie() {
|
|
|
567
781
|
* @example
|
|
568
782
|
* const locale = extractLocaleFromHeader(request);
|
|
569
783
|
*
|
|
570
|
-
* @type {(request: Request) => Locale}
|
|
571
784
|
* @param {Request} request - The request object to extract the locale from.
|
|
572
|
-
* @returns {
|
|
785
|
+
* @returns {Locale | undefined} The negotiated preferred language.
|
|
573
786
|
*/
|
|
574
787
|
export function extractLocaleFromHeader(request) {
|
|
575
788
|
const acceptLanguageHeader = request.headers.get("accept-language");
|
|
@@ -580,20 +793,22 @@ export function extractLocaleFromHeader(request) {
|
|
|
580
793
|
.map((lang) => {
|
|
581
794
|
const [tag, q = "1"] = lang.trim().split(";q=");
|
|
582
795
|
// Get both the full tag and base language code
|
|
583
|
-
const baseTag = tag?.split("-")[0]
|
|
796
|
+
const baseTag = tag?.split("-")[0];
|
|
584
797
|
return {
|
|
585
|
-
fullTag: tag
|
|
798
|
+
fullTag: tag,
|
|
586
799
|
baseTag,
|
|
587
800
|
q: Number(q),
|
|
588
801
|
};
|
|
589
802
|
})
|
|
590
803
|
.sort((a, b) => b.q - a.q);
|
|
591
804
|
for (const lang of languages) {
|
|
592
|
-
|
|
593
|
-
|
|
805
|
+
const fullLocale = toLocale(lang.fullTag);
|
|
806
|
+
if (fullLocale) {
|
|
807
|
+
return fullLocale;
|
|
594
808
|
}
|
|
595
|
-
|
|
596
|
-
|
|
809
|
+
const baseLocale = toLocale(lang.baseTag);
|
|
810
|
+
if (baseLocale) {
|
|
811
|
+
return baseLocale;
|
|
597
812
|
}
|
|
598
813
|
}
|
|
599
814
|
return undefined;
|
|
@@ -610,23 +825,24 @@ export function extractLocaleFromHeader(request) {
|
|
|
610
825
|
* @example
|
|
611
826
|
* const locale = extractLocaleFromNavigator();
|
|
612
827
|
*
|
|
613
|
-
* @
|
|
614
|
-
* @returns {string | undefined}
|
|
828
|
+
* @returns {Locale | undefined}
|
|
615
829
|
*/
|
|
616
830
|
export function extractLocaleFromNavigator() {
|
|
617
831
|
if (!navigator?.languages?.length) {
|
|
618
832
|
return undefined;
|
|
619
833
|
}
|
|
620
834
|
const languages = navigator.languages.map((lang) => ({
|
|
621
|
-
fullTag: lang
|
|
622
|
-
baseTag: lang.split("-")[0]
|
|
835
|
+
fullTag: lang,
|
|
836
|
+
baseTag: lang.split("-")[0],
|
|
623
837
|
}));
|
|
624
838
|
for (const lang of languages) {
|
|
625
|
-
|
|
626
|
-
|
|
839
|
+
const fullLocale = toLocale(lang.fullTag);
|
|
840
|
+
if (fullLocale) {
|
|
841
|
+
return fullLocale;
|
|
627
842
|
}
|
|
628
|
-
|
|
629
|
-
|
|
843
|
+
const baseLocale = toLocale(lang.baseTag);
|
|
844
|
+
if (baseLocale) {
|
|
845
|
+
return baseLocale;
|
|
630
846
|
}
|
|
631
847
|
}
|
|
632
848
|
return undefined;
|
|
@@ -645,6 +861,10 @@ let cachedLocale;
|
|
|
645
861
|
/**
|
|
646
862
|
* Extracts the locale from a given URL using native URLPattern.
|
|
647
863
|
*
|
|
864
|
+
* The built-in default `/:locale/...` routing is case-insensitive because it
|
|
865
|
+
* canonicalizes the first path segment with `toLocale()`. Custom `urlPatterns`
|
|
866
|
+
* keep URLPattern's normal exact matching semantics for path segments.
|
|
867
|
+
*
|
|
648
868
|
* @param {URL|string} url - The full URL from which to extract the locale.
|
|
649
869
|
* @returns {Locale|undefined} The extracted locale, or undefined if no locale is found.
|
|
650
870
|
*/
|
|
@@ -653,6 +873,7 @@ export function extractLocaleFromUrl(url) {
|
|
|
653
873
|
if (cachedUrl === urlString) {
|
|
654
874
|
return cachedLocale;
|
|
655
875
|
}
|
|
876
|
+
/** @type {Locale | undefined} */
|
|
656
877
|
let result;
|
|
657
878
|
if (TREE_SHAKE_DEFAULT_URL_PATTERN_USED) {
|
|
658
879
|
result = defaultUrlPatternExtractLocale(url);
|
|
@@ -663,11 +884,7 @@ export function extractLocaleFromUrl(url) {
|
|
|
663
884
|
for (const element of urlPatterns) {
|
|
664
885
|
for (const [locale, localizedPattern] of element.localized) {
|
|
665
886
|
const match = new URLPattern(localizedPattern, urlObj.href).exec(urlObj.href);
|
|
666
|
-
if (
|
|
667
|
-
continue;
|
|
668
|
-
}
|
|
669
|
-
// Check if the locale is valid
|
|
670
|
-
if (assertIsLocale(locale)) {
|
|
887
|
+
if (match) {
|
|
671
888
|
result = locale;
|
|
672
889
|
break;
|
|
673
890
|
}
|
|
@@ -683,20 +900,13 @@ export function extractLocaleFromUrl(url) {
|
|
|
683
900
|
/**
|
|
684
901
|
* https://github.com/opral/inlang-paraglide-js/issues/381
|
|
685
902
|
*
|
|
686
|
-
* @param {URL|string} url - The full URL from which to extract the locale.
|
|
687
|
-
* @returns {Locale|undefined} The extracted locale, or undefined if no locale is found.
|
|
903
|
+
* @param {URL | string} url - The full URL from which to extract the locale.
|
|
904
|
+
* @returns {Locale | undefined} The extracted locale, or undefined if no locale is found.
|
|
688
905
|
*/
|
|
689
906
|
function defaultUrlPatternExtractLocale(url) {
|
|
690
|
-
const urlObj = new URL(url, "http://
|
|
907
|
+
const urlObj = new URL(url, "http://example.com");
|
|
691
908
|
const pathSegments = urlObj.pathname.split("/").filter(Boolean);
|
|
692
|
-
|
|
693
|
-
const potentialLocale = pathSegments[0];
|
|
694
|
-
if (isLocale(potentialLocale)) {
|
|
695
|
-
return potentialLocale;
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
// everything else has to be the base locale
|
|
699
|
-
return baseLocale;
|
|
909
|
+
return toLocale(pathSegments[0]) || baseLocale;
|
|
700
910
|
}
|
|
701
911
|
|
|
702
912
|
/**
|
|
@@ -709,6 +919,8 @@ function defaultUrlPatternExtractLocale(url) {
|
|
|
709
919
|
* For client-side UI components, use `localizeHref()` instead, which provides
|
|
710
920
|
* a more convenient API with relative paths and automatic locale detection.
|
|
711
921
|
*
|
|
922
|
+
* @see https://paraglidejs.com/i18n-routing
|
|
923
|
+
*
|
|
712
924
|
* @example
|
|
713
925
|
* ```typescript
|
|
714
926
|
* // Server middleware example
|
|
@@ -737,15 +949,17 @@ function defaultUrlPatternExtractLocale(url) {
|
|
|
737
949
|
* ```
|
|
738
950
|
*
|
|
739
951
|
* @param {string | URL} url - The URL to localize. If string, must be absolute.
|
|
740
|
-
* @param {
|
|
741
|
-
* @param {
|
|
952
|
+
* @param {object} [options] - Options for localization
|
|
953
|
+
* @param {Locale} [options.locale] - Target locale. If not provided, uses getLocale()
|
|
742
954
|
* @returns {URL} The localized URL, always absolute
|
|
743
955
|
*/
|
|
744
956
|
export function localizeUrl(url, options) {
|
|
957
|
+
const targetLocale = options?.locale
|
|
958
|
+
? assertIsLocale(options?.locale)
|
|
959
|
+
: getLocale();
|
|
745
960
|
if (TREE_SHAKE_DEFAULT_URL_PATTERN_USED) {
|
|
746
|
-
return localizeUrlDefaultPattern(url,
|
|
961
|
+
return localizeUrlDefaultPattern(url, targetLocale);
|
|
747
962
|
}
|
|
748
|
-
const targetLocale = options?.locale ?? getLocale();
|
|
749
963
|
const urlObj = typeof url === "string" ? new URL(url) : url;
|
|
750
964
|
// Iterate over URL patterns
|
|
751
965
|
for (const element of urlPatterns) {
|
|
@@ -778,13 +992,11 @@ export function localizeUrl(url, options) {
|
|
|
778
992
|
* https://github.com/opral/inlang-paraglide-js/issues/381
|
|
779
993
|
*
|
|
780
994
|
* @param {string | URL} url
|
|
781
|
-
* @param {
|
|
782
|
-
* @param {string} [options.locale]
|
|
995
|
+
* @param {Locale} locale
|
|
783
996
|
* @returns {URL}
|
|
784
997
|
*/
|
|
785
|
-
function localizeUrlDefaultPattern(url,
|
|
998
|
+
function localizeUrlDefaultPattern(url, locale) {
|
|
786
999
|
const urlObj = typeof url === "string" ? new URL(url, getUrlOrigin()) : new URL(url);
|
|
787
|
-
const locale = options?.locale ?? getLocale();
|
|
788
1000
|
const currentLocale = extractLocaleFromUrl(urlObj);
|
|
789
1001
|
// If current locale matches target locale, no change needed
|
|
790
1002
|
if (currentLocale === locale) {
|
|
@@ -792,7 +1004,7 @@ function localizeUrlDefaultPattern(url, options) {
|
|
|
792
1004
|
}
|
|
793
1005
|
const pathSegments = urlObj.pathname.split("/").filter(Boolean);
|
|
794
1006
|
// If current path starts with a locale, remove it
|
|
795
|
-
if (pathSegments.length > 0 &&
|
|
1007
|
+
if (pathSegments.length > 0 && toLocale(pathSegments[0])) {
|
|
796
1008
|
pathSegments.shift();
|
|
797
1009
|
}
|
|
798
1010
|
// For base locale, don't add prefix
|
|
@@ -815,6 +1027,8 @@ function localizeUrlDefaultPattern(url, options) {
|
|
|
815
1027
|
* For client-side UI components, use `deLocalizeHref()` instead, which provides
|
|
816
1028
|
* a more convenient API with relative paths.
|
|
817
1029
|
*
|
|
1030
|
+
* @see https://paraglidejs.com/i18n-routing
|
|
1031
|
+
*
|
|
818
1032
|
* @example
|
|
819
1033
|
* ```typescript
|
|
820
1034
|
* // Server middleware example
|
|
@@ -878,7 +1092,7 @@ function deLocalizeUrlDefaultPattern(url) {
|
|
|
878
1092
|
const urlObj = typeof url === "string" ? new URL(url, getUrlOrigin()) : new URL(url);
|
|
879
1093
|
const pathSegments = urlObj.pathname.split("/").filter(Boolean);
|
|
880
1094
|
// If first segment is a locale, remove it
|
|
881
|
-
if (pathSegments.length > 0 &&
|
|
1095
|
+
if (pathSegments.length > 0 && toLocale(pathSegments[0])) {
|
|
882
1096
|
urlObj.pathname = "/" + pathSegments.slice(1).join("/");
|
|
883
1097
|
}
|
|
884
1098
|
return urlObj;
|
|
@@ -991,7 +1205,8 @@ function fillPattern(pattern, values, origin) {
|
|
|
991
1205
|
* Aggregates named groups from various parts of the URLPattern match result.
|
|
992
1206
|
*
|
|
993
1207
|
*
|
|
994
|
-
* @
|
|
1208
|
+
* @param {any} match - The URLPattern match result object.
|
|
1209
|
+
* @returns {Record<string, string | null | undefined>} An object containing all named groups from the match.
|
|
995
1210
|
*/
|
|
996
1211
|
export function aggregateGroups(match) {
|
|
997
1212
|
return {
|
|
@@ -1006,22 +1221,92 @@ export function aggregateGroups(match) {
|
|
|
1006
1221
|
};
|
|
1007
1222
|
}
|
|
1008
1223
|
|
|
1224
|
+
/** @type {string | undefined} */
|
|
1225
|
+
let cachedRouteStrategyUrl;
|
|
1226
|
+
/** @type {{ match: string; strategy?: typeof strategy; exclude?: boolean } | undefined} */
|
|
1227
|
+
let cachedRouteStrategy;
|
|
1228
|
+
/**
|
|
1229
|
+
* Match route policy against both the public URL and its canonical URL.
|
|
1230
|
+
*
|
|
1231
|
+
* The function is deliberately separate from variables.js: configuration is
|
|
1232
|
+
* inert data, while canonicalization and route selection form a routing layer.
|
|
1233
|
+
*
|
|
1234
|
+
* @param {string | URL} url
|
|
1235
|
+
* @returns {{ match: string; strategy?: typeof strategy; exclude?: boolean } | undefined}
|
|
1236
|
+
*/
|
|
1237
|
+
export function findMatchingRouteStrategy(url) {
|
|
1238
|
+
if (routeStrategies.length === 0) {
|
|
1239
|
+
return undefined;
|
|
1240
|
+
}
|
|
1241
|
+
const urlString = typeof url === "string" ? url : url.href;
|
|
1242
|
+
if (cachedRouteStrategyUrl === urlString) {
|
|
1243
|
+
return cachedRouteStrategy;
|
|
1244
|
+
}
|
|
1245
|
+
const publicUrl = new URL(urlString, "http://example.com");
|
|
1246
|
+
const canonicalUrl = deLocalizeUrl(publicUrl);
|
|
1247
|
+
const candidateUrls = canonicalUrl.href === publicUrl.href
|
|
1248
|
+
? [publicUrl]
|
|
1249
|
+
: [publicUrl, canonicalUrl];
|
|
1250
|
+
let match;
|
|
1251
|
+
for (const candidateUrl of candidateUrls) {
|
|
1252
|
+
for (const routeStrategy of routeStrategies) {
|
|
1253
|
+
const pattern = new URLPattern(routeStrategy.match, candidateUrl.href);
|
|
1254
|
+
if (pattern.exec(candidateUrl.href)) {
|
|
1255
|
+
match = routeStrategy;
|
|
1256
|
+
break;
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
if (match)
|
|
1260
|
+
break;
|
|
1261
|
+
}
|
|
1262
|
+
cachedRouteStrategyUrl = urlString;
|
|
1263
|
+
cachedRouteStrategy = match;
|
|
1264
|
+
return match;
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
* Returns the strategy to use for a specific URL.
|
|
1268
|
+
*
|
|
1269
|
+
* If no route strategy matches (or the matching rule is `exclude: true`),
|
|
1270
|
+
* the global strategy is returned.
|
|
1271
|
+
*
|
|
1272
|
+
* @param {string | URL} url
|
|
1273
|
+
* @returns {typeof strategy}
|
|
1274
|
+
*/
|
|
1275
|
+
export function getStrategyForUrl(url) {
|
|
1276
|
+
const routeStrategy = findMatchingRouteStrategy(url);
|
|
1277
|
+
if (routeStrategy &&
|
|
1278
|
+
routeStrategy.exclude !== true &&
|
|
1279
|
+
Array.isArray(routeStrategy.strategy)) {
|
|
1280
|
+
return routeStrategy.strategy;
|
|
1281
|
+
}
|
|
1282
|
+
return strategy;
|
|
1283
|
+
}
|
|
1284
|
+
/**
|
|
1285
|
+
* Returns whether the given URL is excluded from middleware i18n processing.
|
|
1286
|
+
*
|
|
1287
|
+
* @param {string | URL} url
|
|
1288
|
+
* @returns {boolean}
|
|
1289
|
+
*/
|
|
1290
|
+
export function isExcludedByRouteStrategy(url) {
|
|
1291
|
+
return findMatchingRouteStrategy(url)?.exclude === true;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1009
1294
|
/**
|
|
1010
1295
|
* @typedef {object} ShouldRedirectServerInput
|
|
1011
1296
|
* @property {Request} request
|
|
1012
|
-
* @property {string | URL} [
|
|
1013
|
-
* @property {
|
|
1297
|
+
* @property {string | URL} [effectiveRequestUrl] - Effective request URL to use for route matching, locale detection with the URL strategy, and redirect targets.
|
|
1298
|
+
* @property {Locale} [locale]
|
|
1014
1299
|
*
|
|
1015
1300
|
* @typedef {object} ShouldRedirectClientInput
|
|
1016
1301
|
* @property {undefined} [request]
|
|
1017
1302
|
* @property {string | URL} [url]
|
|
1018
|
-
* @property {
|
|
1303
|
+
* @property {Locale} [locale]
|
|
1019
1304
|
*
|
|
1020
1305
|
* @typedef {ShouldRedirectServerInput | ShouldRedirectClientInput} ShouldRedirectInput
|
|
1021
1306
|
*
|
|
1022
1307
|
* @typedef {object} ShouldRedirectResult
|
|
1023
1308
|
* @property {boolean} shouldRedirect - Indicates whether the consumer should perform a redirect.
|
|
1024
|
-
* @property {
|
|
1309
|
+
* @property {Locale} locale - Locale resolved using the configured strategies.
|
|
1025
1310
|
* @property {URL | undefined} redirectUrl - Destination URL when a redirect is required.
|
|
1026
1311
|
*/
|
|
1027
1312
|
/**
|
|
@@ -1033,6 +1318,8 @@ export function aggregateGroups(match) {
|
|
|
1033
1318
|
*
|
|
1034
1319
|
* When called in the browser without arguments, the current `window.location.href` is used.
|
|
1035
1320
|
*
|
|
1321
|
+
* @see https://paraglidejs.com/i18n-routing#redirects
|
|
1322
|
+
*
|
|
1036
1323
|
* @example
|
|
1037
1324
|
* // Client side usage (e.g. TanStack Router beforeLoad hook)
|
|
1038
1325
|
* async function beforeLoad({ location }) {
|
|
@@ -1055,15 +1342,33 @@ export function aggregateGroups(match) {
|
|
|
1055
1342
|
* return render(request, decision.locale);
|
|
1056
1343
|
* }
|
|
1057
1344
|
*
|
|
1345
|
+
* @example
|
|
1346
|
+
* // Server side usage behind a proxy where request.url is not public-facing
|
|
1347
|
+
* export async function handle(request) {
|
|
1348
|
+
* const effectiveRequestUrl = new URL(request.url);
|
|
1349
|
+
* effectiveRequestUrl.protocol = "https:";
|
|
1350
|
+
* effectiveRequestUrl.host = "example.com";
|
|
1351
|
+
*
|
|
1352
|
+
* const decision = await shouldRedirect({
|
|
1353
|
+
* request,
|
|
1354
|
+
* effectiveRequestUrl,
|
|
1355
|
+
* });
|
|
1356
|
+
*
|
|
1357
|
+
* if (decision.shouldRedirect) {
|
|
1358
|
+
* return Response.redirect(decision.redirectUrl, 307);
|
|
1359
|
+
* }
|
|
1360
|
+
* }
|
|
1361
|
+
*
|
|
1058
1362
|
* @param {ShouldRedirectInput} [input]
|
|
1059
1363
|
* @returns {Promise<ShouldRedirectResult>}
|
|
1060
1364
|
*/
|
|
1061
1365
|
export async function shouldRedirect(input = {}) {
|
|
1062
|
-
const
|
|
1063
|
-
|
|
1366
|
+
const currentUrl = resolveUrl(input);
|
|
1367
|
+
const locale = await resolveLocale(input, currentUrl);
|
|
1368
|
+
const strategy = getStrategyForUrl(currentUrl.href);
|
|
1369
|
+
if (isExcludedByRouteStrategy(currentUrl.href) || !strategy.includes("url")) {
|
|
1064
1370
|
return { shouldRedirect: false, locale, redirectUrl: undefined };
|
|
1065
1371
|
}
|
|
1066
|
-
const currentUrl = resolveUrl(input);
|
|
1067
1372
|
const localizedUrl = localizeUrl(currentUrl.href, { locale });
|
|
1068
1373
|
const shouldRedirectToLocalizedUrl = normalizeUrl(localizedUrl.href) !== normalizeUrl(currentUrl.href);
|
|
1069
1374
|
return {
|
|
@@ -1076,14 +1381,21 @@ export async function shouldRedirect(input = {}) {
|
|
|
1076
1381
|
* Resolves the locale either from the provided input or by using the configured strategies.
|
|
1077
1382
|
*
|
|
1078
1383
|
* @param {ShouldRedirectInput} input
|
|
1079
|
-
* @
|
|
1384
|
+
* @param {URL} currentUrl
|
|
1385
|
+
* @returns {Promise<Locale>}
|
|
1080
1386
|
*/
|
|
1081
|
-
async function resolveLocale(input) {
|
|
1082
|
-
|
|
1083
|
-
|
|
1387
|
+
async function resolveLocale(input, currentUrl) {
|
|
1388
|
+
const locale = toLocale(input.locale);
|
|
1389
|
+
if (locale) {
|
|
1390
|
+
return locale;
|
|
1084
1391
|
}
|
|
1085
1392
|
if (input.request) {
|
|
1086
|
-
return extractLocaleFromRequestAsync(input.request
|
|
1393
|
+
return extractLocaleFromRequestAsync(input.request, {
|
|
1394
|
+
effectiveRequestUrl: currentUrl,
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
if ("url" in input && typeof input.url !== "undefined") {
|
|
1398
|
+
return getLocaleForUrl(currentUrl.href);
|
|
1087
1399
|
}
|
|
1088
1400
|
return getLocale();
|
|
1089
1401
|
}
|
|
@@ -1094,13 +1406,21 @@ async function resolveLocale(input) {
|
|
|
1094
1406
|
* @returns {URL}
|
|
1095
1407
|
*/
|
|
1096
1408
|
function resolveUrl(input) {
|
|
1409
|
+
if ("effectiveRequestUrl" in input &&
|
|
1410
|
+
input.effectiveRequestUrl instanceof URL) {
|
|
1411
|
+
return new URL(input.effectiveRequestUrl.href);
|
|
1412
|
+
}
|
|
1413
|
+
if ("effectiveRequestUrl" in input &&
|
|
1414
|
+
typeof input.effectiveRequestUrl === "string") {
|
|
1415
|
+
return new URL(input.effectiveRequestUrl, input.request ? input.request.url : getUrlOrigin());
|
|
1416
|
+
}
|
|
1097
1417
|
if (input.request) {
|
|
1098
1418
|
return new URL(input.request.url);
|
|
1099
1419
|
}
|
|
1100
|
-
if (input.url instanceof URL) {
|
|
1420
|
+
if ("url" in input && input.url instanceof URL) {
|
|
1101
1421
|
return new URL(input.url.href);
|
|
1102
1422
|
}
|
|
1103
|
-
if (typeof input.url === "string") {
|
|
1423
|
+
if ("url" in input && typeof input.url === "string") {
|
|
1104
1424
|
return new URL(input.url, getUrlOrigin());
|
|
1105
1425
|
}
|
|
1106
1426
|
if (typeof window !== "undefined" && window?.location?.href) {
|
|
@@ -1131,6 +1451,8 @@ function normalizeUrl(url) {
|
|
|
1131
1451
|
* - Automatically detects current locale if not specified
|
|
1132
1452
|
* - Handles string input/output instead of URL objects
|
|
1133
1453
|
*
|
|
1454
|
+
* @see https://paraglidejs.com/i18n-routing
|
|
1455
|
+
*
|
|
1134
1456
|
* @example
|
|
1135
1457
|
* ```typescript
|
|
1136
1458
|
* // In a React/Vue/Svelte component
|
|
@@ -1154,8 +1476,8 @@ function normalizeUrl(url) {
|
|
|
1154
1476
|
* which provides more precise control over URL handling.
|
|
1155
1477
|
*
|
|
1156
1478
|
* @param {string} href - The href to localize (can be relative or absolute)
|
|
1157
|
-
* @param {
|
|
1158
|
-
* @param {
|
|
1479
|
+
* @param {object} [options] - Options for localization
|
|
1480
|
+
* @param {Locale} [options.locale] - Target locale. If not provided, uses `getLocale()`
|
|
1159
1481
|
* @returns {string} The localized href, relative if input was relative
|
|
1160
1482
|
*/
|
|
1161
1483
|
export function localizeHref(href, options) {
|
|
@@ -1189,6 +1511,8 @@ export function localizeHref(href, options) {
|
|
|
1189
1511
|
* - Returns relative paths when possible
|
|
1190
1512
|
* - Handles string input/output instead of URL objects
|
|
1191
1513
|
*
|
|
1514
|
+
* @see https://paraglidejs.com/i18n-routing
|
|
1515
|
+
*
|
|
1192
1516
|
* @example
|
|
1193
1517
|
* ```typescript
|
|
1194
1518
|
* // In a React/Vue/Svelte component
|
|
@@ -1216,7 +1540,6 @@ export function localizeHref(href, options) {
|
|
|
1216
1540
|
*
|
|
1217
1541
|
* @param {string} href - The href to de-localize (can be relative or absolute)
|
|
1218
1542
|
* @returns {string} The de-localized href, relative if input was relative
|
|
1219
|
-
* @see deLocalizeUrl - For low-level URL de-localization in server contexts
|
|
1220
1543
|
*/
|
|
1221
1544
|
export function deLocalizeHref(href) {
|
|
1222
1545
|
const url = new URL(href, getUrlOrigin());
|
|
@@ -1243,28 +1566,50 @@ export function trackMessageCall(safeModuleId, locale) {
|
|
|
1243
1566
|
}
|
|
1244
1567
|
|
|
1245
1568
|
/**
|
|
1246
|
-
* Generates
|
|
1569
|
+
* Generates localized URL variants for all provided URLs based on your configured locales and URL patterns.
|
|
1570
|
+
*
|
|
1571
|
+
* This function is essential for Static Site Generation (SSG) where you need to tell your framework
|
|
1572
|
+
* which pages to pre-render at build time. It's also useful for generating sitemaps and
|
|
1573
|
+
* `<link rel="alternate" hreflang>` tags for SEO.
|
|
1574
|
+
*
|
|
1575
|
+
* The function respects your `urlPatterns` configuration - if you have translated pathnames
|
|
1576
|
+
* (e.g., `/about` → `/ueber-uns` for German), it will generate the correct localized paths.
|
|
1247
1577
|
*
|
|
1248
|
-
*
|
|
1249
|
-
* NextJS and other frameworks use this function for SSG.
|
|
1578
|
+
* @see https://paraglidejs.com/static-site-generation
|
|
1250
1579
|
*
|
|
1251
1580
|
* @example
|
|
1252
|
-
*
|
|
1253
|
-
* const
|
|
1254
|
-
* "
|
|
1255
|
-
* "
|
|
1581
|
+
* // Basic usage - generate all locale variants for a list of paths
|
|
1582
|
+
* const localizedUrls = generateStaticLocalizedUrls([
|
|
1583
|
+
* "/",
|
|
1584
|
+
* "/about",
|
|
1585
|
+
* "/blog/post-1",
|
|
1256
1586
|
* ]);
|
|
1257
|
-
*
|
|
1258
|
-
*
|
|
1259
|
-
*
|
|
1260
|
-
*
|
|
1261
|
-
*
|
|
1262
|
-
*
|
|
1587
|
+
* // Returns URL objects for each locale:
|
|
1588
|
+
* // ["/en/", "/de/", "/en/about", "/de/about", "/en/blog/post-1", "/de/blog/post-1"]
|
|
1589
|
+
*
|
|
1590
|
+
* @example
|
|
1591
|
+
* // Use with framework SSG APIs
|
|
1592
|
+
* // SvelteKit
|
|
1593
|
+
* export function entries() {
|
|
1594
|
+
* const paths = ["/", "/about", "/contact"];
|
|
1595
|
+
* return generateStaticLocalizedUrls(paths).map(url => ({
|
|
1596
|
+
* locale: extractLocaleFromUrl(url)
|
|
1597
|
+
* }));
|
|
1598
|
+
* }
|
|
1263
1599
|
*
|
|
1264
|
-
* @
|
|
1265
|
-
*
|
|
1600
|
+
* @example
|
|
1601
|
+
* // Sitemap generation
|
|
1602
|
+
* const allPages = ["/", "/about", "/blog"];
|
|
1603
|
+
* const sitemapUrls = generateStaticLocalizedUrls(allPages);
|
|
1604
|
+
*
|
|
1605
|
+
* @param {(string | URL)[]} urls - List of canonical URLs or paths to generate localized versions for.
|
|
1606
|
+
* Can be absolute URLs (`https://example.com/about`) or paths (`/about`).
|
|
1607
|
+
* Paths are resolved against `http://localhost` internally.
|
|
1608
|
+
* @returns {URL[]} Array of URL objects representing all localized variants.
|
|
1609
|
+
* The order follows each input URL with all its locale variants before moving to the next URL.
|
|
1266
1610
|
*/
|
|
1267
1611
|
export function generateStaticLocalizedUrls(urls) {
|
|
1612
|
+
/** @type {Set<URL>} */
|
|
1268
1613
|
const localizedUrls = new Set();
|
|
1269
1614
|
// For default URL pattern, we can optimize the generation
|
|
1270
1615
|
if (TREE_SHAKE_DEFAULT_URL_PATTERN_USED) {
|
|
@@ -1357,7 +1702,7 @@ export const customClientStrategies = new Map();
|
|
|
1357
1702
|
/**
|
|
1358
1703
|
* Checks if the given strategy is a custom strategy.
|
|
1359
1704
|
*
|
|
1360
|
-
* @param {
|
|
1705
|
+
* @param {unknown} strategy The name of the custom strategy to validate.
|
|
1361
1706
|
* Must be a string that starts with "custom-" followed by alphanumeric characters, hyphens, or underscores.
|
|
1362
1707
|
* @returns {boolean} Returns true if it is a custom strategy, false otherwise.
|
|
1363
1708
|
*/
|
|
@@ -1367,7 +1712,9 @@ export function isCustomStrategy(strategy) {
|
|
|
1367
1712
|
/**
|
|
1368
1713
|
* Defines a custom strategy that is executed on the server.
|
|
1369
1714
|
*
|
|
1370
|
-
* @
|
|
1715
|
+
* @see https://paraglidejs.com/strategy#write-your-own-strategy
|
|
1716
|
+
*
|
|
1717
|
+
* @param {string} strategy The name of the custom strategy to define. Must follow the pattern custom-name with alphanumeric characters, hyphens, or underscores.
|
|
1371
1718
|
* @param {CustomServerStrategyHandler} handler The handler for the custom strategy, which should implement
|
|
1372
1719
|
* the method getLocale.
|
|
1373
1720
|
* @returns {void}
|
|
@@ -1381,7 +1728,9 @@ export function defineCustomServerStrategy(strategy, handler) {
|
|
|
1381
1728
|
/**
|
|
1382
1729
|
* Defines a custom strategy that is executed on the client.
|
|
1383
1730
|
*
|
|
1384
|
-
* @
|
|
1731
|
+
* @see https://paraglidejs.com/strategy#write-your-own-strategy
|
|
1732
|
+
*
|
|
1733
|
+
* @param {string} strategy The name of the custom strategy to define. Must follow the pattern custom-name with alphanumeric characters, hyphens, or underscores.
|
|
1385
1734
|
* @param {CustomClientStrategyHandler} handler The handler for the custom strategy, which should implement the
|
|
1386
1735
|
* methods getLocale and setLocale.
|
|
1387
1736
|
* @returns {void}
|
|
@@ -1394,13 +1743,150 @@ export function defineCustomClientStrategy(strategy, handler) {
|
|
|
1394
1743
|
}
|
|
1395
1744
|
|
|
1396
1745
|
// ------ TYPES ------
|
|
1397
|
-
|
|
1746
|
+
export {};
|
|
1398
1747
|
/**
|
|
1399
1748
|
* A locale that is available in the project.
|
|
1400
1749
|
*
|
|
1401
1750
|
* @example
|
|
1402
1751
|
* setLocale(request.locale as Locale)
|
|
1403
1752
|
*
|
|
1404
|
-
* @typedef {
|
|
1753
|
+
* @typedef {typeof locales[number]} Locale
|
|
1754
|
+
*/
|
|
1755
|
+
/**
|
|
1756
|
+
* A branded type representing a localized string.
|
|
1757
|
+
*
|
|
1758
|
+
* Message functions return this type instead of \`string\`, enabling TypeScript
|
|
1759
|
+
* to distinguish translated strings from regular strings at compile time.
|
|
1760
|
+
* This allows you to enforce that only properly localized content is used
|
|
1761
|
+
* in your UI components.
|
|
1762
|
+
*
|
|
1763
|
+
* Since \`LocalizedString\` is a branded subtype of \`string\`, it remains fully
|
|
1764
|
+
* backward compatible—you can pass it anywhere a \`string\` is expected.
|
|
1765
|
+
*
|
|
1766
|
+
* @example
|
|
1767
|
+
* // Enforce localized strings in your components
|
|
1768
|
+
* function PageTitle(props: { title: LocalizedString }) {
|
|
1769
|
+
* return <h1>{props.title}</h1>
|
|
1770
|
+
* }
|
|
1771
|
+
*
|
|
1772
|
+
* // ✅ Correct: using a message function
|
|
1773
|
+
* <PageTitle title={m.welcome_title()} />
|
|
1774
|
+
*
|
|
1775
|
+
* // ❌ Type error: raw strings are not LocalizedString
|
|
1776
|
+
* <PageTitle title="Welcome" />
|
|
1777
|
+
*
|
|
1778
|
+
* @example
|
|
1779
|
+
* // LocalizedString is assignable to string (backward compatible)
|
|
1780
|
+
* const localized: LocalizedString = m.greeting()
|
|
1781
|
+
* const str: string = localized // ✅ works fine
|
|
1782
|
+
*
|
|
1783
|
+
* // But string is not assignable to LocalizedString
|
|
1784
|
+
* const raw: LocalizedString = "Hello" // ❌ Type error
|
|
1785
|
+
*
|
|
1786
|
+
* @example
|
|
1787
|
+
* // Catches accidental string concatenation
|
|
1788
|
+
* function showMessage(msg: LocalizedString) { ... }
|
|
1789
|
+
*
|
|
1790
|
+
* showMessage(m.hello()) // ✅
|
|
1791
|
+
* showMessage("Hello " + userName) // ❌ Type error
|
|
1792
|
+
* showMessage(m.hello_user({ name: userName })) // ✅ use params instead
|
|
1793
|
+
*
|
|
1794
|
+
* @typedef {string & { readonly __brand: 'LocalizedString' }} LocalizedString
|
|
1795
|
+
*/
|
|
1796
|
+
/**
|
|
1797
|
+
* A single markup option passed to a tag instance.
|
|
1798
|
+
*
|
|
1799
|
+
* @typedef {{
|
|
1800
|
+
* name: string;
|
|
1801
|
+
* value: unknown;
|
|
1802
|
+
* }} MessageMarkupOption
|
|
1803
|
+
*/
|
|
1804
|
+
/**
|
|
1805
|
+
* A single static markup attribute attached to a tag instance.
|
|
1806
|
+
*
|
|
1807
|
+
* @typedef {{
|
|
1808
|
+
* name: string;
|
|
1809
|
+
* value: string | true;
|
|
1810
|
+
* }} MessageMarkupAttribute
|
|
1811
|
+
*/
|
|
1812
|
+
/**
|
|
1813
|
+
* Record of markup options for a tag instance.
|
|
1814
|
+
*
|
|
1815
|
+
* @typedef {Record<string, unknown>} MessageMarkupOptions
|
|
1816
|
+
*/
|
|
1817
|
+
/**
|
|
1818
|
+
* Record of markup attributes for a tag instance.
|
|
1819
|
+
*
|
|
1820
|
+
* @typedef {Record<string, string | true>} MessageMarkupAttributes
|
|
1821
|
+
*/
|
|
1822
|
+
/**
|
|
1823
|
+
* Type-level schema for a single markup tag.
|
|
1824
|
+
*
|
|
1825
|
+
* @typedef {{
|
|
1826
|
+
* options: MessageMarkupOptions;
|
|
1827
|
+
* attributes: MessageMarkupAttributes;
|
|
1828
|
+
* children: boolean;
|
|
1829
|
+
* }} MessageMarkupTag
|
|
1830
|
+
*/
|
|
1831
|
+
/**
|
|
1832
|
+
* Type-level schema for all markup tags in a message.
|
|
1833
|
+
*
|
|
1834
|
+
* @typedef {Record<string, MessageMarkupTag>} MessageMarkupSchema
|
|
1835
|
+
*/
|
|
1836
|
+
/**
|
|
1837
|
+
* Type-only metadata attached to compiled message functions.
|
|
1838
|
+
*
|
|
1839
|
+
* @template Inputs
|
|
1840
|
+
* @template Options
|
|
1841
|
+
* @template {MessageMarkupSchema} [Markup = MessageMarkupSchema]
|
|
1842
|
+
* @typedef {{
|
|
1843
|
+
* readonly __paraglide?: {
|
|
1844
|
+
* inputs: Inputs;
|
|
1845
|
+
* options: Options;
|
|
1846
|
+
* markup: Markup;
|
|
1847
|
+
* };
|
|
1848
|
+
* }} MessageMetadata
|
|
1849
|
+
*/
|
|
1850
|
+
/**
|
|
1851
|
+
* A compiled, framework-neutral message part.
|
|
1852
|
+
*
|
|
1853
|
+
* @typedef {{
|
|
1854
|
+
* type: "text";
|
|
1855
|
+
* value: string;
|
|
1856
|
+
* } | {
|
|
1857
|
+
* type: "markup-start";
|
|
1858
|
+
* name: string;
|
|
1859
|
+
* options: MessageMarkupOptions;
|
|
1860
|
+
* attributes: MessageMarkupAttributes;
|
|
1861
|
+
* } | {
|
|
1862
|
+
* type: "markup-end";
|
|
1863
|
+
* name: string;
|
|
1864
|
+
* options: MessageMarkupOptions;
|
|
1865
|
+
* attributes: MessageMarkupAttributes;
|
|
1866
|
+
* } | {
|
|
1867
|
+
* type: "markup-standalone";
|
|
1868
|
+
* name: string;
|
|
1869
|
+
* options: MessageMarkupOptions;
|
|
1870
|
+
* attributes: MessageMarkupAttributes;
|
|
1871
|
+
* }} MessagePart
|
|
1872
|
+
*/
|
|
1873
|
+
/**
|
|
1874
|
+
* A message function is a message for a specific locale.
|
|
1875
|
+
*
|
|
1876
|
+
* @example
|
|
1877
|
+
* m.hello({ name: 'world' })
|
|
1878
|
+
*
|
|
1879
|
+
* @typedef {(inputs?: Record<string, never>) => LocalizedString} MessageFunction
|
|
1880
|
+
*/
|
|
1881
|
+
/**
|
|
1882
|
+
* A message bundle function that selects the message to be returned.
|
|
1883
|
+
*
|
|
1884
|
+
* Uses `getLocale()` under the hood to determine the locale with an option.
|
|
1885
|
+
*
|
|
1886
|
+
* @template {string} T
|
|
1887
|
+
*
|
|
1888
|
+
* @example
|
|
1889
|
+
* * m.hello({ name: 'world' }, { locale: "en" })
|
|
1890
|
+
*
|
|
1891
|
+
* @typedef {(params: Record<string, never>, options: { locale: T }) => LocalizedString} MessageBundleFunction
|
|
1405
1892
|
*/
|
|
1406
|
-
|