@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.
Files changed (34) hide show
  1. package/dist/external/paraglide/messages/_index.d.ts.map +1 -1
  2. package/dist/external/paraglide/messages/_index.js +5 -3
  3. package/dist/external/paraglide/messages/en.js +1 -1
  4. package/dist/external/paraglide/messages/fr.js +1 -1
  5. package/dist/external/paraglide/messages/tl.js +1 -1
  6. package/dist/external/paraglide/messages.js +1 -1
  7. package/dist/external/paraglide/registry.js +1 -1
  8. package/dist/external/paraglide/runtime.d.ts +8 -189
  9. package/dist/external/paraglide/runtime.d.ts.map +1 -1
  10. package/dist/external/paraglide/runtime.js +90 -426
  11. package/dist/external/paraglide/server.d.ts +2 -4
  12. package/dist/external/paraglide/server.d.ts.map +1 -1
  13. package/dist/external/paraglide/server.js +20 -21
  14. package/dist/ui/components/MeltToaster/MeltToaster.svelte +61 -61
  15. package/dist/ui/components/ModalBase.svelte.d.ts +1 -8
  16. package/dist/ui/components/ModalBase.svelte.d.ts.map +1 -1
  17. package/dist/ui/components/NestedNavigation.svelte +106 -106
  18. package/dist/ui/components/accordion/PinyaAccordion.svelte.d.ts +1 -2
  19. package/dist/ui/components/accordion/PinyaAccordion.svelte.d.ts.map +1 -1
  20. package/dist/ui/components/accordion/PinyaAccordionItem.svelte.d.ts +1 -2
  21. package/dist/ui/components/accordion/PinyaAccordionItem.svelte.d.ts.map +1 -1
  22. package/dist/ui/elements/ImageIcon.svelte.d.ts +1 -4
  23. package/dist/ui/elements/ImageIcon.svelte.d.ts.map +1 -1
  24. package/dist/ui/elements/PinyaAnchorButton/PinyaAnchorButton.svelte.d.ts +1 -2
  25. package/dist/ui/elements/PinyaAnchorButton/PinyaAnchorButton.svelte.d.ts.map +1 -1
  26. package/dist/ui/elements/PinyaButton/PinyaButton.svelte.d.ts +1 -2
  27. package/dist/ui/elements/PinyaButton/PinyaButton.svelte.d.ts.map +1 -1
  28. package/dist/ui/elements/PinyaCard/PinyaCard.svelte.d.ts +1 -2
  29. package/dist/ui/elements/PinyaCard/PinyaCard.svelte.d.ts.map +1 -1
  30. package/dist/ui/elements/Placeholder.svelte.d.ts +1 -6
  31. package/dist/ui/elements/Placeholder.svelte.d.ts.map +1 -1
  32. package/dist/ui/elements/TextLink.svelte.d.ts +1 -2
  33. package/dist/ui/elements/TextLink.svelte.d.ts.map +1 -1
  34. package/package.json +16 -17
@@ -1,4 +1,4 @@
1
- /* eslint-disable */
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" | `custom-${string}`>}
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 = extractLocaleFromNavigator();
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 {SetLocaleFn}
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
- const cookieString = `${cookieName}=${newLocale}; path=/; max-age=${cookieMaxAge}`;
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
- const runReload = () => {
334
- if (!isServer &&
335
- optionsWithDefaults.reload &&
336
- window.location &&
337
- newLocale !== currentLocale) {
338
- navigateOrReload(newLocation);
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 {SetLocaleFn} fn
324
+ * @param {(newLocale: Locale) => void} fn
362
325
  */
363
326
  export const overwriteSetLocale = (fn) => {
364
- setLocale = /** @type {SetLocaleFn} */ (fn);
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
- locale = extractLocaleFromHeader(request);
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
- * Asynchronously extracts a locale from a request.
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
- * @example
503
- * // Basic usage
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
- export const extractLocaleFromRequestAsync = async (request) => {
520
- /** @type {string|undefined} */
521
- let locale;
522
- // Process custom strategies first, in order
523
- for (const strat of strategy) {
524
- if (isCustomStrategy(strat) && customServerStrategies.has(strat)) {
525
- const handler = customServerStrategies.get(strat);
526
- if (handler) {
527
- /** @type {string|undefined} */
528
- locale = await handler.getLocale(request);
529
- }
530
- // If we got a valid locale from this custom strategy, use it
531
- if (locale !== undefined && isLocale(locale)) {
532
- return assertIsLocale(locale);
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
- // If no custom strategy provided a valid locale, fall back to sync version
537
- locale = extractLocaleFromRequest(request);
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 document is not available or if the cookie is not set.
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
- result = defaultUrlPatternExtractLocale(url);
507
+ return defaultUrlPatternExtractLocale(url);
659
508
  }
660
- else {
661
- const urlObj = typeof url === "string" ? new URL(url) : url;
662
- // Iterate over URL patterns
663
- for (const element of urlPatterns) {
664
- for (const [locale, localizedPattern] of element.localized) {
665
- const match = new URLPattern(localizedPattern, urlObj.href).exec(urlObj.href);
666
- if (!match) {
667
- continue;
668
- }
669
- // Check if the locale is valid
670
- if (assertIsLocale(locale)) {
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
- cachedUrl = urlString;
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 currentLocale = getLocale();
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, { locale });
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 !== currentLocale) {
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
  /**