@turnipxenon/pineapple 5.0.0-alpha.5 → 5.0.0-alpha.6
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/messages/_index.d.ts.map +1 -1
- package/dist/external/paraglide/messages/_index.js +5 -3
- package/dist/external/paraglide/messages/en.js +1 -1
- package/dist/external/paraglide/messages/fr.js +1 -1
- package/dist/external/paraglide/messages/tl.js +1 -1
- package/dist/external/paraglide/messages.js +1 -1
- package/dist/external/paraglide/registry.js +1 -1
- package/dist/external/paraglide/runtime.d.ts +8 -189
- package/dist/external/paraglide/runtime.d.ts.map +1 -1
- package/dist/external/paraglide/runtime.js +90 -426
- package/dist/external/paraglide/server.d.ts +2 -4
- package/dist/external/paraglide/server.d.ts.map +1 -1
- package/dist/external/paraglide/server.js +20 -21
- package/dist/ui/components/MeltToaster/MeltToaster.svelte +61 -61
- package/dist/ui/components/ModalBase.svelte.d.ts +1 -8
- package/dist/ui/components/ModalBase.svelte.d.ts.map +1 -1
- package/dist/ui/components/NestedNavigation.svelte +106 -106
- package/dist/ui/components/accordion/PinyaAccordion.svelte.d.ts +1 -2
- package/dist/ui/components/accordion/PinyaAccordion.svelte.d.ts.map +1 -1
- package/dist/ui/components/accordion/PinyaAccordionItem.svelte.d.ts +1 -2
- package/dist/ui/components/accordion/PinyaAccordionItem.svelte.d.ts.map +1 -1
- package/dist/ui/elements/ImageIcon.svelte.d.ts +1 -4
- package/dist/ui/elements/ImageIcon.svelte.d.ts.map +1 -1
- package/dist/ui/elements/PinyaAnchorButton/PinyaAnchorButton.svelte.d.ts +1 -2
- package/dist/ui/elements/PinyaAnchorButton/PinyaAnchorButton.svelte.d.ts.map +1 -1
- package/dist/ui/elements/PinyaButton/PinyaButton.svelte.d.ts +1 -2
- package/dist/ui/elements/PinyaButton/PinyaButton.svelte.d.ts.map +1 -1
- package/dist/ui/elements/PinyaCard/PinyaCard.svelte.d.ts +1 -2
- package/dist/ui/elements/PinyaCard/PinyaCard.svelte.d.ts.map +1 -1
- package/dist/ui/elements/Placeholder.svelte.d.ts +1 -6
- package/dist/ui/elements/Placeholder.svelte.d.ts.map +1 -1
- package/dist/ui/elements/TextLink.svelte.d.ts +1 -2
- package/dist/ui/elements/TextLink.svelte.d.ts.map +1 -1
- package/package.json +16 -17
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// eslint-disable
|
|
2
2
|
|
|
3
3
|
/** @type {any} */
|
|
4
4
|
const URLPattern = {}
|
|
@@ -26,11 +26,9 @@ export const cookieName = "PARAGLIDE_LOCALE";
|
|
|
26
26
|
/** @type {number} */
|
|
27
27
|
export const cookieMaxAge = 34560000;
|
|
28
28
|
/** @type {string} */
|
|
29
|
-
export const cookieDomain = "";
|
|
30
|
-
/** @type {string} */
|
|
31
29
|
export const localStorageKey = "PARAGLIDE_LOCALE";
|
|
32
30
|
/**
|
|
33
|
-
* @type {Array<"cookie" | "baseLocale" | "globalVariable" | "url" | "preferredLanguage" | "localStorage"
|
|
31
|
+
* @type {Array<"cookie" | "baseLocale" | "globalVariable" | "url" | "preferredLanguage" | "localStorage">}
|
|
34
32
|
*/
|
|
35
33
|
export const strategy = [
|
|
36
34
|
"url",
|
|
@@ -161,25 +159,13 @@ export let getLocale = () => {
|
|
|
161
159
|
else if (TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED &&
|
|
162
160
|
strat === "preferredLanguage" &&
|
|
163
161
|
!isServer) {
|
|
164
|
-
locale =
|
|
162
|
+
locale = negotiatePreferredLanguageFromNavigator();
|
|
165
163
|
}
|
|
166
164
|
else if (TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED &&
|
|
167
165
|
strat === "localStorage" &&
|
|
168
166
|
!isServer) {
|
|
169
167
|
locale = localStorage.getItem(localStorageKey) ?? undefined;
|
|
170
168
|
}
|
|
171
|
-
else if (isCustomStrategy(strat) && customClientStrategies.has(strat)) {
|
|
172
|
-
const handler = customClientStrategies.get(strat);
|
|
173
|
-
if (handler) {
|
|
174
|
-
const result = handler.getLocale();
|
|
175
|
-
// Handle both sync and async results - skip async in sync getLocale
|
|
176
|
-
if (result instanceof Promise) {
|
|
177
|
-
// Can't await in sync function, skip async strategies
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
locale = result;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
169
|
// check if match, else continue loop
|
|
184
170
|
if (locale !== undefined) {
|
|
185
171
|
const asserted = assertIsLocale(locale);
|
|
@@ -194,6 +180,29 @@ export let getLocale = () => {
|
|
|
194
180
|
}
|
|
195
181
|
throw new Error("No locale found. Read the docs https://inlang.com/m/gerre34r/library-inlang-paraglideJs/errors#no-locale-found");
|
|
196
182
|
};
|
|
183
|
+
/**
|
|
184
|
+
* Negotiates a preferred language from navigator.languages.
|
|
185
|
+
*
|
|
186
|
+
* @returns {string|undefined} The negotiated preferred language.
|
|
187
|
+
*/
|
|
188
|
+
function negotiatePreferredLanguageFromNavigator() {
|
|
189
|
+
if (!navigator?.languages?.length) {
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
const languages = navigator.languages.map((lang) => ({
|
|
193
|
+
fullTag: lang.toLowerCase(),
|
|
194
|
+
baseTag: lang.split("-")[0]?.toLowerCase(),
|
|
195
|
+
}));
|
|
196
|
+
for (const lang of languages) {
|
|
197
|
+
if (isLocale(lang.fullTag)) {
|
|
198
|
+
return lang.fullTag;
|
|
199
|
+
}
|
|
200
|
+
else if (isLocale(lang.baseTag)) {
|
|
201
|
+
return lang.baseTag;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
197
206
|
/**
|
|
198
207
|
* Overwrite the \`getLocale()\` function.
|
|
199
208
|
*
|
|
@@ -213,25 +222,6 @@ export const overwriteGetLocale = (fn) => {
|
|
|
213
222
|
getLocale = fn;
|
|
214
223
|
};
|
|
215
224
|
|
|
216
|
-
/**
|
|
217
|
-
* Navigates to the localized URL, or reloads the current page
|
|
218
|
-
*
|
|
219
|
-
* @param {string} [newLocation] The new location
|
|
220
|
-
* @return {undefined}
|
|
221
|
-
*/
|
|
222
|
-
const navigateOrReload = (newLocation) => {
|
|
223
|
-
if (newLocation) {
|
|
224
|
-
// reload the page by navigating to the new url
|
|
225
|
-
window.location.href = newLocation;
|
|
226
|
-
}
|
|
227
|
-
else {
|
|
228
|
-
// reload the page to reflect the new locale
|
|
229
|
-
window.location.reload();
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
/**
|
|
233
|
-
* @typedef {(newLocale: Locale, options?: { reload?: boolean }) => void | Promise<void>} SetLocaleFn
|
|
234
|
-
*/
|
|
235
225
|
/**
|
|
236
226
|
* Set the locale.
|
|
237
227
|
*
|
|
@@ -240,16 +230,13 @@ const navigateOrReload = (newLocation) => {
|
|
|
240
230
|
* reloading is disabled, you need to ensure that the UI is updated
|
|
241
231
|
* to reflect the new locale.
|
|
242
232
|
*
|
|
243
|
-
* If any custom strategy's \`setLocale\` function is async, then this
|
|
244
|
-
* function will become async as well.
|
|
245
|
-
*
|
|
246
233
|
* @example
|
|
247
234
|
* setLocale('en');
|
|
248
235
|
*
|
|
249
236
|
* @example
|
|
250
237
|
* setLocale('en', { reload: false });
|
|
251
238
|
*
|
|
252
|
-
* @type {
|
|
239
|
+
* @type {(newLocale: Locale, options?: { reload?: boolean }) => void}
|
|
253
240
|
*/
|
|
254
241
|
export let setLocale = (newLocale, options) => {
|
|
255
242
|
const optionsWithDefaults = {
|
|
@@ -258,7 +245,6 @@ export let setLocale = (newLocale, options) => {
|
|
|
258
245
|
};
|
|
259
246
|
// locale is already set
|
|
260
247
|
// https://github.com/opral/inlang-paraglide-js/issues/430
|
|
261
|
-
/** @type {Locale | undefined} */
|
|
262
248
|
let currentLocale;
|
|
263
249
|
try {
|
|
264
250
|
currentLocale = getLocale();
|
|
@@ -266,8 +252,6 @@ export let setLocale = (newLocale, options) => {
|
|
|
266
252
|
catch {
|
|
267
253
|
// do nothing, no locale has been set yet.
|
|
268
254
|
}
|
|
269
|
-
/** @type {Array<Promise<any>>} */
|
|
270
|
-
const customSetLocalePromises = [];
|
|
271
255
|
/** @type {string | undefined} */
|
|
272
256
|
let newLocation = undefined;
|
|
273
257
|
for (const strat of strategy) {
|
|
@@ -278,16 +262,11 @@ export let setLocale = (newLocale, options) => {
|
|
|
278
262
|
_locale = newLocale;
|
|
279
263
|
}
|
|
280
264
|
else if (TREE_SHAKE_COOKIE_STRATEGY_USED && strat === "cookie") {
|
|
281
|
-
if (isServer ||
|
|
282
|
-
typeof document === "undefined" ||
|
|
283
|
-
typeof window === "undefined") {
|
|
265
|
+
if (isServer || typeof document === "undefined") {
|
|
284
266
|
continue;
|
|
285
267
|
}
|
|
286
268
|
// set the cookie
|
|
287
|
-
|
|
288
|
-
document.cookie = cookieDomain
|
|
289
|
-
? `${cookieString}; domain=${cookieDomain}`
|
|
290
|
-
: cookieString;
|
|
269
|
+
document.cookie = `${cookieName}=${newLocale}; path=/; max-age=${cookieMaxAge}`;
|
|
291
270
|
}
|
|
292
271
|
else if (strat === "baseLocale") {
|
|
293
272
|
// nothing to be set here. baseLocale is only a fallback
|
|
@@ -314,36 +293,20 @@ export let setLocale = (newLocale, options) => {
|
|
|
314
293
|
// set the localStorage
|
|
315
294
|
localStorage.setItem(localStorageKey, newLocale);
|
|
316
295
|
}
|
|
317
|
-
else if (isCustomStrategy(strat) && customClientStrategies.has(strat)) {
|
|
318
|
-
const handler = customClientStrategies.get(strat);
|
|
319
|
-
if (handler) {
|
|
320
|
-
let result = handler.setLocale(newLocale);
|
|
321
|
-
// Handle async setLocale
|
|
322
|
-
if (result instanceof Promise) {
|
|
323
|
-
result = result.catch((error) => {
|
|
324
|
-
throw new Error(`Custom strategy "${strat}" setLocale failed.`, {
|
|
325
|
-
cause: error,
|
|
326
|
-
});
|
|
327
|
-
});
|
|
328
|
-
customSetLocalePromises.push(result);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
296
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
297
|
+
if (!isServer &&
|
|
298
|
+
optionsWithDefaults.reload &&
|
|
299
|
+
window.location &&
|
|
300
|
+
newLocale !== currentLocale) {
|
|
301
|
+
if (newLocation) {
|
|
302
|
+
// reload the page by navigating to the new url
|
|
303
|
+
window.location.href = newLocation;
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
// reload the page to reflect the new locale
|
|
307
|
+
window.location.reload();
|
|
339
308
|
}
|
|
340
|
-
};
|
|
341
|
-
if (customSetLocalePromises.length) {
|
|
342
|
-
return Promise.all(customSetLocalePromises).then(() => {
|
|
343
|
-
runReload();
|
|
344
|
-
});
|
|
345
309
|
}
|
|
346
|
-
runReload();
|
|
347
310
|
return;
|
|
348
311
|
};
|
|
349
312
|
/**
|
|
@@ -358,10 +321,10 @@ export let setLocale = (newLocale, options) => {
|
|
|
358
321
|
* return Cookies.set('locale', newLocale)
|
|
359
322
|
* });
|
|
360
323
|
*
|
|
361
|
-
* @param {
|
|
324
|
+
* @param {(newLocale: Locale) => void} fn
|
|
362
325
|
*/
|
|
363
326
|
export const overwriteSetLocale = (fn) => {
|
|
364
|
-
setLocale =
|
|
327
|
+
setLocale = fn;
|
|
365
328
|
};
|
|
366
329
|
|
|
367
330
|
/**
|
|
@@ -435,9 +398,6 @@ export function assertIsLocale(input) {
|
|
|
435
398
|
* they are defined. If a strategy returns an invalid locale,
|
|
436
399
|
* it will fall back to the next strategy.
|
|
437
400
|
*
|
|
438
|
-
* Note: Custom server strategies are not supported in this synchronous version.
|
|
439
|
-
* Use `extractLocaleFromRequestAsync` if you need custom server strategies with async getLocale methods.
|
|
440
|
-
*
|
|
441
401
|
* @example
|
|
442
402
|
* const locale = extractLocaleFromRequest(request);
|
|
443
403
|
*
|
|
@@ -459,7 +419,10 @@ export const extractLocaleFromRequest = (request) => {
|
|
|
459
419
|
}
|
|
460
420
|
else if (TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED &&
|
|
461
421
|
strat === "preferredLanguage") {
|
|
462
|
-
|
|
422
|
+
const acceptLanguageHeader = request.headers.get("accept-language");
|
|
423
|
+
if (acceptLanguageHeader) {
|
|
424
|
+
locale = negotiatePreferredLanguageFromHeader(acceptLanguageHeader);
|
|
425
|
+
}
|
|
463
426
|
}
|
|
464
427
|
else if (strat === "globalVariable") {
|
|
465
428
|
locale = _locale;
|
|
@@ -470,11 +433,6 @@ export const extractLocaleFromRequest = (request) => {
|
|
|
470
433
|
else if (strat === "localStorage") {
|
|
471
434
|
continue;
|
|
472
435
|
}
|
|
473
|
-
else if (isCustomStrategy(strat)) {
|
|
474
|
-
// Custom strategies are not supported in sync version
|
|
475
|
-
// Use extractLocaleFromRequestAsync for custom server strategies
|
|
476
|
-
continue;
|
|
477
|
-
}
|
|
478
436
|
if (locale !== undefined) {
|
|
479
437
|
if (!isLocale(locale)) {
|
|
480
438
|
locale = undefined;
|
|
@@ -486,62 +444,42 @@ export const extractLocaleFromRequest = (request) => {
|
|
|
486
444
|
}
|
|
487
445
|
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");
|
|
488
446
|
};
|
|
489
|
-
|
|
490
447
|
/**
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
* This function supports async custom server strategies, unlike the synchronous
|
|
494
|
-
* `extractLocaleFromRequest`. Use this function when you have custom server strategies
|
|
495
|
-
* that need to perform asynchronous operations (like database calls) in their getLocale method.
|
|
496
|
-
*
|
|
497
|
-
* The function first processes any custom server strategies asynchronously, then falls back
|
|
498
|
-
* to the synchronous `extractLocaleFromRequest` for all other strategies.
|
|
499
|
-
*
|
|
500
|
-
* @see {@link https://github.com/opral/inlang-paraglide-js/issues/527#issuecomment-2978151022}
|
|
448
|
+
* Negotiates a preferred language from a header.
|
|
501
449
|
*
|
|
502
|
-
* @
|
|
503
|
-
*
|
|
504
|
-
* const locale = await extractLocaleFromRequestAsync(request);
|
|
505
|
-
*
|
|
506
|
-
* @example
|
|
507
|
-
* // With custom async server strategy
|
|
508
|
-
* defineCustomServerStrategy("custom-database", {
|
|
509
|
-
* getLocale: async (request) => {
|
|
510
|
-
* const userId = extractUserIdFromRequest(request);
|
|
511
|
-
* return await getUserLocaleFromDatabase(userId);
|
|
512
|
-
* }
|
|
513
|
-
* });
|
|
514
|
-
*
|
|
515
|
-
* const locale = await extractLocaleFromRequestAsync(request);
|
|
516
|
-
*
|
|
517
|
-
* @type {(request: Request) => Promise<Locale>}
|
|
450
|
+
* @param {string} header - The header to negotiate from.
|
|
451
|
+
* @returns {string|undefined} The negotiated preferred language.
|
|
518
452
|
*/
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
453
|
+
function negotiatePreferredLanguageFromHeader(header) {
|
|
454
|
+
// Parse language preferences with their q-values and base language codes
|
|
455
|
+
const languages = header
|
|
456
|
+
.split(",")
|
|
457
|
+
.map((lang) => {
|
|
458
|
+
const [tag, q = "1"] = lang.trim().split(";q=");
|
|
459
|
+
// Get both the full tag and base language code
|
|
460
|
+
const baseTag = tag?.split("-")[0]?.toLowerCase();
|
|
461
|
+
return {
|
|
462
|
+
fullTag: tag?.toLowerCase(),
|
|
463
|
+
baseTag,
|
|
464
|
+
q: Number(q),
|
|
465
|
+
};
|
|
466
|
+
})
|
|
467
|
+
.sort((a, b) => b.q - a.q);
|
|
468
|
+
for (const lang of languages) {
|
|
469
|
+
if (isLocale(lang.fullTag)) {
|
|
470
|
+
return lang.fullTag;
|
|
471
|
+
}
|
|
472
|
+
else if (isLocale(lang.baseTag)) {
|
|
473
|
+
return lang.baseTag;
|
|
534
474
|
}
|
|
535
475
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
return assertIsLocale(locale);
|
|
539
|
-
};
|
|
476
|
+
return undefined;
|
|
477
|
+
}
|
|
540
478
|
|
|
541
479
|
/**
|
|
542
480
|
* Extracts a cookie from the document.
|
|
543
481
|
*
|
|
544
|
-
* Will return undefined if the
|
|
482
|
+
* Will return undefined if the docuement is not available or if the cookie is not set.
|
|
545
483
|
* The `document` object is not available in server-side rendering, so this function should not be called in that context.
|
|
546
484
|
*
|
|
547
485
|
* @returns {string | undefined}
|
|
@@ -558,90 +496,6 @@ export function extractLocaleFromCookie() {
|
|
|
558
496
|
return undefined;
|
|
559
497
|
}
|
|
560
498
|
|
|
561
|
-
/**
|
|
562
|
-
* Extracts a locale from the accept-language header.
|
|
563
|
-
*
|
|
564
|
-
* Use the function on the server to extract the locale
|
|
565
|
-
* from the accept-language header that is sent by the client.
|
|
566
|
-
*
|
|
567
|
-
* @example
|
|
568
|
-
* const locale = extractLocaleFromHeader(request);
|
|
569
|
-
*
|
|
570
|
-
* @type {(request: Request) => Locale}
|
|
571
|
-
* @param {Request} request - The request object to extract the locale from.
|
|
572
|
-
* @returns {string|undefined} The negotiated preferred language.
|
|
573
|
-
*/
|
|
574
|
-
export function extractLocaleFromHeader(request) {
|
|
575
|
-
const acceptLanguageHeader = request.headers.get("accept-language");
|
|
576
|
-
if (acceptLanguageHeader) {
|
|
577
|
-
// Parse language preferences with their q-values and base language codes
|
|
578
|
-
const languages = acceptLanguageHeader
|
|
579
|
-
.split(",")
|
|
580
|
-
.map((lang) => {
|
|
581
|
-
const [tag, q = "1"] = lang.trim().split(";q=");
|
|
582
|
-
// Get both the full tag and base language code
|
|
583
|
-
const baseTag = tag?.split("-")[0]?.toLowerCase();
|
|
584
|
-
return {
|
|
585
|
-
fullTag: tag?.toLowerCase(),
|
|
586
|
-
baseTag,
|
|
587
|
-
q: Number(q),
|
|
588
|
-
};
|
|
589
|
-
})
|
|
590
|
-
.sort((a, b) => b.q - a.q);
|
|
591
|
-
for (const lang of languages) {
|
|
592
|
-
if (isLocale(lang.fullTag)) {
|
|
593
|
-
return lang.fullTag;
|
|
594
|
-
}
|
|
595
|
-
else if (isLocale(lang.baseTag)) {
|
|
596
|
-
return lang.baseTag;
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
return undefined;
|
|
600
|
-
}
|
|
601
|
-
return undefined;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
/**
|
|
605
|
-
* Negotiates a preferred language from navigator.languages.
|
|
606
|
-
*
|
|
607
|
-
* Use the function on the client to extract the locale
|
|
608
|
-
* from the navigator.languages array.
|
|
609
|
-
*
|
|
610
|
-
* @example
|
|
611
|
-
* const locale = extractLocaleFromNavigator();
|
|
612
|
-
*
|
|
613
|
-
* @type {() => Locale | undefined}
|
|
614
|
-
* @returns {string | undefined}
|
|
615
|
-
*/
|
|
616
|
-
export function extractLocaleFromNavigator() {
|
|
617
|
-
if (!navigator?.languages?.length) {
|
|
618
|
-
return undefined;
|
|
619
|
-
}
|
|
620
|
-
const languages = navigator.languages.map((lang) => ({
|
|
621
|
-
fullTag: lang.toLowerCase(),
|
|
622
|
-
baseTag: lang.split("-")[0]?.toLowerCase(),
|
|
623
|
-
}));
|
|
624
|
-
for (const lang of languages) {
|
|
625
|
-
if (isLocale(lang.fullTag)) {
|
|
626
|
-
return lang.fullTag;
|
|
627
|
-
}
|
|
628
|
-
else if (isLocale(lang.baseTag)) {
|
|
629
|
-
return lang.baseTag;
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
return undefined;
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
/**
|
|
636
|
-
* If extractLocaleFromUrl is called many times on the same page and the URL
|
|
637
|
-
* hasn't changed, we don't need to recompute it every time which can get expensive.
|
|
638
|
-
* We might use a LRU cache if needed, but for now storing only the last result is enough.
|
|
639
|
-
* https://github.com/opral/monorepo/pull/3575#discussion_r2066731243
|
|
640
|
-
*/
|
|
641
|
-
/** @type {string|undefined} */
|
|
642
|
-
let cachedUrl;
|
|
643
|
-
/** @type {Locale|undefined} */
|
|
644
|
-
let cachedLocale;
|
|
645
499
|
/**
|
|
646
500
|
* Extracts the locale from a given URL using native URLPattern.
|
|
647
501
|
*
|
|
@@ -649,36 +503,24 @@ let cachedLocale;
|
|
|
649
503
|
* @returns {Locale|undefined} The extracted locale, or undefined if no locale is found.
|
|
650
504
|
*/
|
|
651
505
|
export function extractLocaleFromUrl(url) {
|
|
652
|
-
const urlString = typeof url === "string" ? url : url.href;
|
|
653
|
-
if (cachedUrl === urlString) {
|
|
654
|
-
return cachedLocale;
|
|
655
|
-
}
|
|
656
|
-
let result;
|
|
657
506
|
if (TREE_SHAKE_DEFAULT_URL_PATTERN_USED) {
|
|
658
|
-
|
|
507
|
+
return defaultUrlPatternExtractLocale(url);
|
|
659
508
|
}
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
for (const
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
result = locale;
|
|
672
|
-
break;
|
|
673
|
-
}
|
|
509
|
+
const urlObj = typeof url === "string" ? new URL(url) : url;
|
|
510
|
+
// Iterate over URL patterns
|
|
511
|
+
for (const element of urlPatterns) {
|
|
512
|
+
for (const [locale, localizedPattern] of element.localized) {
|
|
513
|
+
const match = new URLPattern(localizedPattern, urlObj.href).exec(urlObj.href);
|
|
514
|
+
if (!match) {
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
// Check if the locale is valid
|
|
518
|
+
if (assertIsLocale(locale)) {
|
|
519
|
+
return locale;
|
|
674
520
|
}
|
|
675
|
-
if (result)
|
|
676
|
-
break;
|
|
677
521
|
}
|
|
678
522
|
}
|
|
679
|
-
|
|
680
|
-
cachedLocale = result;
|
|
681
|
-
return result;
|
|
523
|
+
return undefined;
|
|
682
524
|
}
|
|
683
525
|
/**
|
|
684
526
|
* https://github.com/opral/inlang-paraglide-js/issues/381
|
|
@@ -1006,120 +848,6 @@ export function aggregateGroups(match) {
|
|
|
1006
848
|
};
|
|
1007
849
|
}
|
|
1008
850
|
|
|
1009
|
-
/**
|
|
1010
|
-
* @typedef {object} ShouldRedirectServerInput
|
|
1011
|
-
* @property {Request} request
|
|
1012
|
-
* @property {string | URL} [url]
|
|
1013
|
-
* @property {ReturnType<typeof assertIsLocale>} [locale]
|
|
1014
|
-
*
|
|
1015
|
-
* @typedef {object} ShouldRedirectClientInput
|
|
1016
|
-
* @property {undefined} [request]
|
|
1017
|
-
* @property {string | URL} [url]
|
|
1018
|
-
* @property {ReturnType<typeof assertIsLocale>} [locale]
|
|
1019
|
-
*
|
|
1020
|
-
* @typedef {ShouldRedirectServerInput | ShouldRedirectClientInput} ShouldRedirectInput
|
|
1021
|
-
*
|
|
1022
|
-
* @typedef {object} ShouldRedirectResult
|
|
1023
|
-
* @property {boolean} shouldRedirect - Indicates whether the consumer should perform a redirect.
|
|
1024
|
-
* @property {ReturnType<typeof assertIsLocale>} locale - Locale resolved using the configured strategies.
|
|
1025
|
-
* @property {URL | undefined} redirectUrl - Destination URL when a redirect is required.
|
|
1026
|
-
*/
|
|
1027
|
-
/**
|
|
1028
|
-
* Determines whether a redirect is required to align the current URL with the active locale.
|
|
1029
|
-
*
|
|
1030
|
-
* This helper mirrors the logic that powers `paraglideMiddleware`, but works in both server
|
|
1031
|
-
* and client environments. It evaluates the configured strategies in order, computes the
|
|
1032
|
-
* canonical localized URL, and reports when the current URL does not match.
|
|
1033
|
-
*
|
|
1034
|
-
* When called in the browser without arguments, the current `window.location.href` is used.
|
|
1035
|
-
*
|
|
1036
|
-
* @example
|
|
1037
|
-
* // Client side usage (e.g. TanStack Router beforeLoad hook)
|
|
1038
|
-
* async function beforeLoad({ location }) {
|
|
1039
|
-
* const decision = await shouldRedirect({ url: location.href });
|
|
1040
|
-
*
|
|
1041
|
-
* if (decision.shouldRedirect) {
|
|
1042
|
-
* throw redirect({ to: decision.redirectUrl.href });
|
|
1043
|
-
* }
|
|
1044
|
-
* }
|
|
1045
|
-
*
|
|
1046
|
-
* @example
|
|
1047
|
-
* // Server side usage with a Request
|
|
1048
|
-
* export async function handle(request) {
|
|
1049
|
-
* const decision = await shouldRedirect({ request });
|
|
1050
|
-
*
|
|
1051
|
-
* if (decision.shouldRedirect) {
|
|
1052
|
-
* return Response.redirect(decision.redirectUrl, 307);
|
|
1053
|
-
* }
|
|
1054
|
-
*
|
|
1055
|
-
* return render(request, decision.locale);
|
|
1056
|
-
* }
|
|
1057
|
-
*
|
|
1058
|
-
* @param {ShouldRedirectInput} [input]
|
|
1059
|
-
* @returns {Promise<ShouldRedirectResult>}
|
|
1060
|
-
*/
|
|
1061
|
-
export async function shouldRedirect(input = {}) {
|
|
1062
|
-
const locale = /** @type {ReturnType<typeof assertIsLocale>} */ (await resolveLocale(input));
|
|
1063
|
-
if (!strategy.includes("url")) {
|
|
1064
|
-
return { shouldRedirect: false, locale, redirectUrl: undefined };
|
|
1065
|
-
}
|
|
1066
|
-
const currentUrl = resolveUrl(input);
|
|
1067
|
-
const localizedUrl = localizeUrl(currentUrl.href, { locale });
|
|
1068
|
-
const shouldRedirectToLocalizedUrl = normalizeUrl(localizedUrl.href) !== normalizeUrl(currentUrl.href);
|
|
1069
|
-
return {
|
|
1070
|
-
shouldRedirect: shouldRedirectToLocalizedUrl,
|
|
1071
|
-
locale,
|
|
1072
|
-
redirectUrl: shouldRedirectToLocalizedUrl ? localizedUrl : undefined,
|
|
1073
|
-
};
|
|
1074
|
-
}
|
|
1075
|
-
/**
|
|
1076
|
-
* Resolves the locale either from the provided input or by using the configured strategies.
|
|
1077
|
-
*
|
|
1078
|
-
* @param {ShouldRedirectInput} input
|
|
1079
|
-
* @returns {Promise<ReturnType<typeof assertIsLocale>>}
|
|
1080
|
-
*/
|
|
1081
|
-
async function resolveLocale(input) {
|
|
1082
|
-
if (input.locale) {
|
|
1083
|
-
return assertIsLocale(input.locale);
|
|
1084
|
-
}
|
|
1085
|
-
if (input.request) {
|
|
1086
|
-
return extractLocaleFromRequestAsync(input.request);
|
|
1087
|
-
}
|
|
1088
|
-
return getLocale();
|
|
1089
|
-
}
|
|
1090
|
-
/**
|
|
1091
|
-
* Resolves the current URL from the provided input or runtime context.
|
|
1092
|
-
*
|
|
1093
|
-
* @param {ShouldRedirectInput} input
|
|
1094
|
-
* @returns {URL}
|
|
1095
|
-
*/
|
|
1096
|
-
function resolveUrl(input) {
|
|
1097
|
-
if (input.request) {
|
|
1098
|
-
return new URL(input.request.url);
|
|
1099
|
-
}
|
|
1100
|
-
if (input.url instanceof URL) {
|
|
1101
|
-
return new URL(input.url.href);
|
|
1102
|
-
}
|
|
1103
|
-
if (typeof input.url === "string") {
|
|
1104
|
-
return new URL(input.url, getUrlOrigin());
|
|
1105
|
-
}
|
|
1106
|
-
if (typeof window !== "undefined" && window?.location?.href) {
|
|
1107
|
-
return new URL(window.location.href);
|
|
1108
|
-
}
|
|
1109
|
-
throw new Error("shouldRedirect() requires either a request, an absolute URL, or must run in a browser environment.");
|
|
1110
|
-
}
|
|
1111
|
-
/**
|
|
1112
|
-
* Normalize url for comparison by stripping the trailing slash.
|
|
1113
|
-
*
|
|
1114
|
-
* @param {string} url
|
|
1115
|
-
* @returns {string}
|
|
1116
|
-
*/
|
|
1117
|
-
function normalizeUrl(url) {
|
|
1118
|
-
const urlObj = new URL(url);
|
|
1119
|
-
urlObj.pathname = urlObj.pathname.replace(/\/$/, "");
|
|
1120
|
-
return urlObj.href;
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
851
|
/**
|
|
1124
852
|
* High-level URL localization function optimized for client-side UI usage.
|
|
1125
853
|
*
|
|
@@ -1159,18 +887,15 @@ function normalizeUrl(url) {
|
|
|
1159
887
|
* @returns {string} The localized href, relative if input was relative
|
|
1160
888
|
*/
|
|
1161
889
|
export function localizeHref(href, options) {
|
|
1162
|
-
const
|
|
1163
|
-
const locale = options?.locale ?? currentLocale;
|
|
890
|
+
const locale = options?.locale ?? getLocale();
|
|
1164
891
|
const url = new URL(href, getUrlOrigin());
|
|
1165
|
-
const localized = localizeUrl(url,
|
|
892
|
+
const localized = localizeUrl(url, options);
|
|
1166
893
|
// if the origin is identical and the href is relative,
|
|
1167
894
|
// return the relative path
|
|
1168
895
|
if (href.startsWith("/") && url.origin === localized.origin) {
|
|
1169
896
|
// check for cross origin localization in which case an absolute URL must be returned.
|
|
1170
|
-
if (locale !==
|
|
1171
|
-
const localizedCurrentLocale = localizeUrl(url, {
|
|
1172
|
-
locale: currentLocale,
|
|
1173
|
-
});
|
|
897
|
+
if (locale !== getLocale()) {
|
|
898
|
+
const localizedCurrentLocale = localizeUrl(url, { locale: getLocale() });
|
|
1174
899
|
if (localizedCurrentLocale.origin !== localized.origin) {
|
|
1175
900
|
return localized.href;
|
|
1176
901
|
}
|
|
@@ -1332,67 +1057,6 @@ export function generateStaticLocalizedUrls(urls) {
|
|
|
1332
1057
|
return Array.from(localizedUrls);
|
|
1333
1058
|
}
|
|
1334
1059
|
|
|
1335
|
-
/**
|
|
1336
|
-
* @typedef {"cookie" | "baseLocale" | "globalVariable" | "url" | "preferredLanguage" | "localStorage"} BuiltInStrategy
|
|
1337
|
-
*/
|
|
1338
|
-
/**
|
|
1339
|
-
* @typedef {`custom_${string}`} CustomStrategy
|
|
1340
|
-
*/
|
|
1341
|
-
/**
|
|
1342
|
-
* @typedef {BuiltInStrategy | CustomStrategy} Strategy
|
|
1343
|
-
*/
|
|
1344
|
-
/**
|
|
1345
|
-
* @typedef {Array<Strategy>} Strategies
|
|
1346
|
-
*/
|
|
1347
|
-
/**
|
|
1348
|
-
* @typedef {{ getLocale: (request?: Request) => Promise<string | undefined> | (string | undefined) }} CustomServerStrategyHandler
|
|
1349
|
-
*/
|
|
1350
|
-
/**
|
|
1351
|
-
* @typedef {{ getLocale: () => Promise<string|undefined> | (string | undefined), setLocale: (locale: string) => Promise<void> | void }} CustomClientStrategyHandler
|
|
1352
|
-
*/
|
|
1353
|
-
/** @type {Map<string, CustomServerStrategyHandler>} */
|
|
1354
|
-
export const customServerStrategies = new Map();
|
|
1355
|
-
/** @type {Map<string, CustomClientStrategyHandler>} */
|
|
1356
|
-
export const customClientStrategies = new Map();
|
|
1357
|
-
/**
|
|
1358
|
-
* Checks if the given strategy is a custom strategy.
|
|
1359
|
-
*
|
|
1360
|
-
* @param {any} strategy The name of the custom strategy to validate.
|
|
1361
|
-
* Must be a string that starts with "custom-" followed by alphanumeric characters, hyphens, or underscores.
|
|
1362
|
-
* @returns {boolean} Returns true if it is a custom strategy, false otherwise.
|
|
1363
|
-
*/
|
|
1364
|
-
export function isCustomStrategy(strategy) {
|
|
1365
|
-
return (typeof strategy === "string" && /^custom-[A-Za-z0-9_-]+$/.test(strategy));
|
|
1366
|
-
}
|
|
1367
|
-
/**
|
|
1368
|
-
* Defines a custom strategy that is executed on the server.
|
|
1369
|
-
*
|
|
1370
|
-
* @param {any} strategy The name of the custom strategy to define. Must follow the pattern custom-name with alphanumeric characters, hyphens, or underscores.
|
|
1371
|
-
* @param {CustomServerStrategyHandler} handler The handler for the custom strategy, which should implement
|
|
1372
|
-
* the method getLocale.
|
|
1373
|
-
* @returns {void}
|
|
1374
|
-
*/
|
|
1375
|
-
export function defineCustomServerStrategy(strategy, handler) {
|
|
1376
|
-
if (!isCustomStrategy(strategy)) {
|
|
1377
|
-
throw new Error(`Invalid custom strategy: "${strategy}". Must be a custom strategy following the pattern custom-name.`);
|
|
1378
|
-
}
|
|
1379
|
-
customServerStrategies.set(strategy, handler);
|
|
1380
|
-
}
|
|
1381
|
-
/**
|
|
1382
|
-
* Defines a custom strategy that is executed on the client.
|
|
1383
|
-
*
|
|
1384
|
-
* @param {any} strategy The name of the custom strategy to define. Must follow the pattern custom-name with alphanumeric characters, hyphens, or underscores.
|
|
1385
|
-
* @param {CustomClientStrategyHandler} handler The handler for the custom strategy, which should implement the
|
|
1386
|
-
* methods getLocale and setLocale.
|
|
1387
|
-
* @returns {void}
|
|
1388
|
-
*/
|
|
1389
|
-
export function defineCustomClientStrategy(strategy, handler) {
|
|
1390
|
-
if (!isCustomStrategy(strategy)) {
|
|
1391
|
-
throw new Error(`Invalid custom strategy: "${strategy}". Must be a custom strategy following the pattern custom-name.`);
|
|
1392
|
-
}
|
|
1393
|
-
customClientStrategies.set(strategy, handler);
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
1060
|
// ------ TYPES ------
|
|
1397
1061
|
|
|
1398
1062
|
/**
|