tera-system-ui 0.1.41 → 0.1.61

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 (171) hide show
  1. package/dist/components/accordion/Accordion.d.ts +15 -13
  2. package/dist/components/accordion/index.d.ts +1 -0
  3. package/dist/components/avatar/Avatar.d.ts +5 -3
  4. package/dist/components/avatar/index.d.ts +1 -0
  5. package/dist/components/brand-logo/BrandLogo.d.ts +5 -2
  6. package/dist/components/brand-logo/index.d.ts +1 -0
  7. package/dist/components/button/Button.d.ts +58 -14
  8. package/dist/components/button/Button.js +90 -27
  9. package/dist/components/button/Button.svelte +35 -30
  10. package/dist/components/button/Button.svelte.d.ts +1 -4
  11. package/dist/components/button/index.d.ts +1 -0
  12. package/dist/components/checkbox/Checkbox.d.ts +5 -2
  13. package/dist/components/checkbox/Checkbox.svelte +15 -8
  14. package/dist/components/checkbox/index.d.ts +1 -0
  15. package/dist/components/combobox/Combobox.d.ts +5 -2
  16. package/dist/components/combobox/index.d.ts +1 -0
  17. package/dist/components/dialog/Dialog.d.ts +8 -6
  18. package/dist/components/dialog/index.d.ts +1 -0
  19. package/dist/components/dropdown-menu/DropdownMenu.d.ts +26 -8
  20. package/dist/components/dropdown-menu/index.d.ts +1 -0
  21. package/dist/components/header/Header.d.ts +6 -3
  22. package/dist/components/header/Header.svelte.d.ts +1 -1
  23. package/dist/components/header/index.d.ts +1 -0
  24. package/dist/components/input/Input.d.ts +22 -6
  25. package/dist/components/input/Input.js +19 -10
  26. package/dist/components/input/Input.svelte +42 -9
  27. package/dist/components/input/index.d.ts +1 -0
  28. package/dist/components/label/Label.d.ts +33 -3
  29. package/dist/components/label/Label.js +14 -3
  30. package/dist/components/label/Label.svelte +7 -7
  31. package/dist/components/label/Label.svelte.d.ts +2 -2
  32. package/dist/components/label/index.d.ts +1 -0
  33. package/dist/components/language-picker-button/LanguagePickerButton.d.ts +5 -2
  34. package/dist/components/language-picker-button/index.d.ts +1 -0
  35. package/dist/components/light-dark-toggle/LightDarkToggle.d.ts +5 -2
  36. package/dist/components/light-dark-toggle/index.d.ts +1 -0
  37. package/dist/components/popover/Popover.d.ts +6 -3
  38. package/dist/components/popover/Popover.js +0 -1
  39. package/dist/components/popover/index.d.ts +1 -0
  40. package/dist/components/popover-responsive/PopoverResponsive.d.ts +5 -2
  41. package/dist/components/popover-responsive/index.d.ts +1 -0
  42. package/dist/components/select/Select.d.ts +19 -5
  43. package/dist/components/select/Select.js +18 -9
  44. package/dist/components/select/Select.svelte +17 -4
  45. package/dist/components/select/Select.svelte.d.ts +1 -1
  46. package/dist/components/select/index.d.ts +1 -0
  47. package/dist/components/side-navigation/SideNavigation.d.ts +6 -3
  48. package/dist/components/side-navigation/index.d.ts +1 -1
  49. package/dist/components/slider/Slider.d.ts +7 -4
  50. package/dist/components/slider/Slider.svelte +26 -51
  51. package/dist/components/slider/index.d.ts +1 -0
  52. package/dist/components/star-rating/StarRating.d.ts +5 -2
  53. package/dist/components/star-rating/StarRating.svelte +5 -5
  54. package/dist/components/star-rating/index.d.ts +1 -0
  55. package/dist/components/switch/Switch.d.ts +6 -2
  56. package/dist/components/switch/Switch.svelte +13 -7
  57. package/dist/components/switch/index.d.ts +1 -0
  58. package/dist/components/tabs/Tabs.svelte.d.ts +23 -3
  59. package/dist/components/tabs/index.d.ts +1 -0
  60. package/dist/components/tera-ui-context/TeraUiContext.d.ts +4 -6
  61. package/dist/components/tera-ui-context/index.d.ts +1 -0
  62. package/dist/components/text-area/TextArea.d.ts +22 -8
  63. package/dist/components/text-area/TextArea.js +19 -10
  64. package/dist/components/text-area/TextArea.svelte +36 -30
  65. package/dist/components/text-area/index.d.ts +1 -0
  66. package/dist/components/user-avatar-with-menu/UserAvatarWithMenu.d.ts +5 -2
  67. package/dist/components/user-avatar-with-menu/index.d.ts +1 -0
  68. package/dist/index.d.ts +24 -0
  69. package/dist/paraglide/README.md +93 -0
  70. package/dist/paraglide/messages/_index.d.ts +8 -7
  71. package/dist/paraglide/messages/_index.js +361 -360
  72. package/dist/paraglide/messages/ar.d.ts +9 -7
  73. package/dist/paraglide/messages/ar.js +16 -15
  74. package/dist/paraglide/messages/bg.d.ts +9 -7
  75. package/dist/paraglide/messages/bg.js +16 -15
  76. package/dist/paraglide/messages/bn.d.ts +9 -7
  77. package/dist/paraglide/messages/bn.js +16 -15
  78. package/dist/paraglide/messages/ca.d.ts +9 -7
  79. package/dist/paraglide/messages/ca.js +16 -15
  80. package/dist/paraglide/messages/cs.d.ts +9 -7
  81. package/dist/paraglide/messages/cs.js +16 -15
  82. package/dist/paraglide/messages/da.d.ts +9 -7
  83. package/dist/paraglide/messages/da.js +16 -15
  84. package/dist/paraglide/messages/de.d.ts +9 -7
  85. package/dist/paraglide/messages/de.js +16 -15
  86. package/dist/paraglide/messages/el.d.ts +9 -7
  87. package/dist/paraglide/messages/el.js +16 -15
  88. package/dist/paraglide/messages/en.d.ts +9 -7
  89. package/dist/paraglide/messages/en.js +16 -15
  90. package/dist/paraglide/messages/es.d.ts +9 -7
  91. package/dist/paraglide/messages/es.js +16 -15
  92. package/dist/paraglide/messages/fi.d.ts +9 -7
  93. package/dist/paraglide/messages/fi.js +16 -15
  94. package/dist/paraglide/messages/fr.d.ts +9 -7
  95. package/dist/paraglide/messages/fr.js +16 -15
  96. package/dist/paraglide/messages/he.d.ts +9 -7
  97. package/dist/paraglide/messages/he.js +16 -15
  98. package/dist/paraglide/messages/hi.d.ts +9 -7
  99. package/dist/paraglide/messages/hi.js +16 -15
  100. package/dist/paraglide/messages/hu.d.ts +9 -7
  101. package/dist/paraglide/messages/hu.js +16 -15
  102. package/dist/paraglide/messages/id.d.ts +9 -7
  103. package/dist/paraglide/messages/id.js +16 -15
  104. package/dist/paraglide/messages/it.d.ts +9 -7
  105. package/dist/paraglide/messages/it.js +16 -15
  106. package/dist/paraglide/messages/ja.d.ts +9 -7
  107. package/dist/paraglide/messages/ja.js +16 -15
  108. package/dist/paraglide/messages/ko.d.ts +9 -7
  109. package/dist/paraglide/messages/ko.js +16 -15
  110. package/dist/paraglide/messages/lt.d.ts +9 -7
  111. package/dist/paraglide/messages/lt.js +16 -15
  112. package/dist/paraglide/messages/lv.d.ts +9 -7
  113. package/dist/paraglide/messages/lv.js +16 -15
  114. package/dist/paraglide/messages/ms.d.ts +9 -7
  115. package/dist/paraglide/messages/ms.js +16 -15
  116. package/dist/paraglide/messages/nl.d.ts +9 -7
  117. package/dist/paraglide/messages/nl.js +16 -15
  118. package/dist/paraglide/messages/no.d.ts +9 -7
  119. package/dist/paraglide/messages/no.js +16 -15
  120. package/dist/paraglide/messages/pl.d.ts +9 -7
  121. package/dist/paraglide/messages/pl.js +16 -15
  122. package/dist/paraglide/messages/pt.d.ts +9 -7
  123. package/dist/paraglide/messages/pt.js +16 -15
  124. package/dist/paraglide/messages/ro.d.ts +9 -7
  125. package/dist/paraglide/messages/ro.js +16 -15
  126. package/dist/paraglide/messages/ru.d.ts +9 -7
  127. package/dist/paraglide/messages/ru.js +16 -15
  128. package/dist/paraglide/messages/sk.d.ts +9 -7
  129. package/dist/paraglide/messages/sk.js +16 -15
  130. package/dist/paraglide/messages/sl.d.ts +9 -7
  131. package/dist/paraglide/messages/sl.js +16 -15
  132. package/dist/paraglide/messages/sq.d.ts +9 -7
  133. package/dist/paraglide/messages/sq.js +16 -15
  134. package/dist/paraglide/messages/sr.d.ts +9 -7
  135. package/dist/paraglide/messages/sr.js +16 -15
  136. package/dist/paraglide/messages/sv.d.ts +9 -7
  137. package/dist/paraglide/messages/sv.js +16 -15
  138. package/dist/paraglide/messages/sw.d.ts +9 -7
  139. package/dist/paraglide/messages/sw.js +16 -15
  140. package/dist/paraglide/messages/ta.d.ts +9 -7
  141. package/dist/paraglide/messages/ta.js +16 -15
  142. package/dist/paraglide/messages/te.d.ts +9 -7
  143. package/dist/paraglide/messages/te.js +16 -15
  144. package/dist/paraglide/messages/th.d.ts +9 -7
  145. package/dist/paraglide/messages/th.js +16 -15
  146. package/dist/paraglide/messages/tl.d.ts +9 -7
  147. package/dist/paraglide/messages/tl.js +16 -15
  148. package/dist/paraglide/messages/tr.d.ts +9 -7
  149. package/dist/paraglide/messages/tr.js +16 -15
  150. package/dist/paraglide/messages/uk.d.ts +9 -7
  151. package/dist/paraglide/messages/uk.js +16 -15
  152. package/dist/paraglide/messages/vi.d.ts +9 -7
  153. package/dist/paraglide/messages/vi.js +16 -15
  154. package/dist/paraglide/messages/zh-CN.d.ts +9 -7
  155. package/dist/paraglide/messages/zh-CN.js +16 -15
  156. package/dist/paraglide/messages/zh-TW.d.ts +9 -7
  157. package/dist/paraglide/messages/zh-TW.js +16 -15
  158. package/dist/paraglide/messages.js +1 -1
  159. package/dist/paraglide/registry.js +1 -1
  160. package/dist/paraglide/runtime.d.ts +221 -45
  161. package/dist/paraglide/runtime.js +378 -65
  162. package/dist/paraglide/server.d.ts +40 -4
  163. package/dist/paraglide/server.js +83 -46
  164. package/dist/tera-i18n/projects/tera-system-ui/project.inlang/.meta.json +3 -0
  165. package/dist/tera-i18n/projects/tera-system-ui/project.inlang/README.md +103 -0
  166. package/dist/themes/tera-ui-base.css +70 -7
  167. package/dist/types/index.d.ts +25 -0
  168. package/dist/types/index.js +3 -0
  169. package/package.json +150 -36
  170. package/scripts/add-component-template.js +1 -1
  171. package/scripts/generate-ts-index.js +38 -12
@@ -1,4 +1,4 @@
1
- // eslint-disable
1
+ /* eslint-disable */
2
2
 
3
3
  /** @type {any} */
4
4
  const URLPattern = {}
@@ -245,6 +245,9 @@ export let serverAsyncLocalStorage = undefined;
245
245
  export const disableAsyncLocalStorage = false;
246
246
  export const experimentalMiddlewareLocaleSplitting = false;
247
247
  export const isServer = import.meta.env?.SSR ?? typeof window === 'undefined';
248
+ /** @type {Locale | undefined} */
249
+ // @ts-ignore - injected by bundlers at compile time
250
+ export const experimentalStaticLocale = undefined;
248
251
  /**
249
252
  * Sets the server side async local storage.
250
253
  *
@@ -281,6 +284,12 @@ let localeInitiallySet = false;
281
284
  /**
282
285
  * Get the current locale.
283
286
  *
287
+ * The locale is resolved using your configured strategies (URL, cookie, localStorage, etc.)
288
+ * in the order they are defined. In SSR contexts, the locale is retrieved from AsyncLocalStorage
289
+ * which is set by the `paraglideMiddleware()`.
290
+ *
291
+ * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/strategy - Configure locale detection strategies
292
+ *
284
293
  * @example
285
294
  * if (getLocale() === 'de') {
286
295
  * console.log('Germany 🇩🇪');
@@ -291,6 +300,9 @@ let localeInitiallySet = false;
291
300
  * @type {() => Locale}
292
301
  */
293
302
  export let getLocale = () => {
303
+ if (experimentalStaticLocale !== undefined) {
304
+ return assertIsLocale(experimentalStaticLocale);
305
+ }
294
306
  /** @type {string | undefined} */
295
307
  let locale;
296
308
  // if running in a server-side rendering context
@@ -331,7 +343,15 @@ export let getLocale = () => {
331
343
  }
332
344
  else if (isCustomStrategy(strat) && customClientStrategies.has(strat)) {
333
345
  const handler = customClientStrategies.get(strat);
334
- locale = handler.getLocale();
346
+ if (handler) {
347
+ const result = handler.getLocale();
348
+ // Handle both sync and async results - skip async in sync getLocale
349
+ if (result instanceof Promise) {
350
+ // Can't await in sync function, skip async strategies
351
+ continue;
352
+ }
353
+ locale = result;
354
+ }
335
355
  }
336
356
  // check if match, else continue loop
337
357
  if (locale !== undefined) {
@@ -348,17 +368,17 @@ export let getLocale = () => {
348
368
  throw new Error("No locale found. Read the docs https://inlang.com/m/gerre34r/library-inlang-paraglideJs/errors#no-locale-found");
349
369
  };
350
370
  /**
351
- * Overwrite the \`getLocale()\` function.
371
+ * Overwrite the `getLocale()` function.
352
372
  *
353
- * Use this function to overwrite how the locale is resolved. For example,
354
- * you can resolve the locale from the browser's preferred language,
355
- * a cookie, env variable, or a user's preference.
373
+ * Use this function to overwrite how the locale is resolved. This is useful
374
+ * for custom locale resolution or advanced use cases like SSG with concurrent rendering.
375
+ *
376
+ * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/strategy
356
377
  *
357
378
  * @example
358
379
  * overwriteGetLocale(() => {
359
- * // resolve the locale from a cookie. fallback to the base locale.
360
380
  * return Cookies.get('locale') ?? baseLocale
361
- * }
381
+ * });
362
382
  *
363
383
  * @type {(fn: () => Locale) => void}
364
384
  */
@@ -366,13 +386,37 @@ export const overwriteGetLocale = (fn) => {
366
386
  getLocale = fn;
367
387
  };
368
388
 
389
+ /**
390
+ * Navigates to the localized URL, or reloads the current page
391
+ *
392
+ * @param {string} [newLocation] The new location
393
+ * @return {undefined}
394
+ */
395
+ const navigateOrReload = (newLocation) => {
396
+ if (newLocation) {
397
+ // reload the page by navigating to the new url
398
+ window.location.href = newLocation;
399
+ }
400
+ else {
401
+ // reload the page to reflect the new locale
402
+ window.location.reload();
403
+ }
404
+ };
405
+ /**
406
+ * @typedef {(newLocale: Locale, options?: { reload?: boolean }) => void | Promise<void>} SetLocaleFn
407
+ */
369
408
  /**
370
409
  * Set the locale.
371
410
  *
372
- * Set locale reloads the site by default on the client. Reloading
373
- * can be disabled by passing \`reload: false\` as an option. If
374
- * reloading is disabled, you need to ensure that the UI is updated
375
- * to reflect the new locale.
411
+ * Updates the locale using your configured strategies (cookie, localStorage, URL, etc.).
412
+ * By default, this reloads the page on the client to reflect the new locale. Reloading
413
+ * can be disabled by passing `reload: false` as an option, but you'll need to ensure
414
+ * the UI updates to reflect the new locale.
415
+ *
416
+ * If any custom strategy's `setLocale` function is async, then this function
417
+ * will become async as well.
418
+ *
419
+ * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/strategy
376
420
  *
377
421
  * @example
378
422
  * setLocale('en');
@@ -380,7 +424,7 @@ export const overwriteGetLocale = (fn) => {
380
424
  * @example
381
425
  * setLocale('en', { reload: false });
382
426
  *
383
- * @type {(newLocale: Locale, options?: { reload?: boolean }) => void}
427
+ * @type {SetLocaleFn}
384
428
  */
385
429
  export let setLocale = (newLocale, options) => {
386
430
  const optionsWithDefaults = {
@@ -389,6 +433,7 @@ export let setLocale = (newLocale, options) => {
389
433
  };
390
434
  // locale is already set
391
435
  // https://github.com/opral/inlang-paraglide-js/issues/430
436
+ /** @type {Locale | undefined} */
392
437
  let currentLocale;
393
438
  try {
394
439
  currentLocale = getLocale();
@@ -396,6 +441,8 @@ export let setLocale = (newLocale, options) => {
396
441
  catch {
397
442
  // do nothing, no locale has been set yet.
398
443
  }
444
+ /** @type {Array<Promise<any>>} */
445
+ const customSetLocalePromises = [];
399
446
  /** @type {string | undefined} */
400
447
  let newLocation = undefined;
401
448
  for (const strat of strategy) {
@@ -411,9 +458,11 @@ export let setLocale = (newLocale, options) => {
411
458
  typeof window === "undefined") {
412
459
  continue;
413
460
  }
414
- const domain = cookieDomain || window.location.hostname;
415
461
  // set the cookie
416
- document.cookie = `${cookieName}=${newLocale}; path=/; max-age=${cookieMaxAge}; domain=${domain}`;
462
+ const cookieString = `${cookieName}=${newLocale}; path=/; max-age=${cookieMaxAge}`;
463
+ document.cookie = cookieDomain
464
+ ? `${cookieString}; domain=${cookieDomain}`
465
+ : cookieString;
417
466
  }
418
467
  else if (strat === "baseLocale") {
419
468
  // nothing to be set here. baseLocale is only a fallback
@@ -442,22 +491,34 @@ export let setLocale = (newLocale, options) => {
442
491
  }
443
492
  else if (isCustomStrategy(strat) && customClientStrategies.has(strat)) {
444
493
  const handler = customClientStrategies.get(strat);
445
- handler.setLocale(newLocale);
494
+ if (handler) {
495
+ let result = handler.setLocale(newLocale);
496
+ // Handle async setLocale
497
+ if (result instanceof Promise) {
498
+ result = result.catch((error) => {
499
+ throw new Error(`Custom strategy "${strat}" setLocale failed.`, {
500
+ cause: error,
501
+ });
502
+ });
503
+ customSetLocalePromises.push(result);
504
+ }
505
+ }
446
506
  }
447
507
  }
448
- if (!isServer &&
449
- optionsWithDefaults.reload &&
450
- window.location &&
451
- newLocale !== currentLocale) {
452
- if (newLocation) {
453
- // reload the page by navigating to the new url
454
- window.location.href = newLocation;
455
- }
456
- else {
457
- // reload the page to reflect the new locale
458
- window.location.reload();
508
+ const runReload = () => {
509
+ if (!isServer &&
510
+ optionsWithDefaults.reload &&
511
+ window.location &&
512
+ newLocale !== currentLocale) {
513
+ navigateOrReload(newLocation);
459
514
  }
515
+ };
516
+ if (customSetLocalePromises.length) {
517
+ return Promise.all(customSetLocalePromises).then(() => {
518
+ runReload();
519
+ });
460
520
  }
521
+ runReload();
461
522
  return;
462
523
  };
463
524
  /**
@@ -472,10 +533,10 @@ export let setLocale = (newLocale, options) => {
472
533
  * return Cookies.set('locale', newLocale)
473
534
  * });
474
535
  *
475
- * @param {(newLocale: Locale) => void} fn
536
+ * @param {SetLocaleFn} fn
476
537
  */
477
538
  export const overwriteSetLocale = (fn) => {
478
- setLocale = fn;
539
+ setLocale = /** @type {SetLocaleFn} */ (fn);
479
540
  };
480
541
 
481
542
  /**
@@ -522,7 +583,11 @@ export let overwriteGetUrlOrigin = (fn) => {
522
583
  * @returns {locale is Locale}
523
584
  */
524
585
  export function isLocale(locale) {
525
- return !locale ? false : locales.includes(locale);
586
+ if (typeof locale !== "string")
587
+ return false;
588
+ return !locale
589
+ ? false
590
+ : locales.some((item) => item.toLowerCase() === locale.toLowerCase());
526
591
  }
527
592
 
528
593
  /**
@@ -533,10 +598,15 @@ export function isLocale(locale) {
533
598
  * @throws {Error} If the input is not a locale.
534
599
  */
535
600
  export function assertIsLocale(input) {
536
- if (isLocale(input) === false) {
601
+ if (typeof input !== "string") {
602
+ throw new Error(`Invalid locale: ${input}. Expected a string.`);
603
+ }
604
+ const lowerInput = input.toLowerCase();
605
+ const matchedLocale = locales.find((item) => item.toLowerCase() === lowerInput);
606
+ if (!matchedLocale) {
537
607
  throw new Error(`Invalid locale: ${input}. Expected one of: ${locales.join(", ")}`);
538
608
  }
539
- return input;
609
+ return matchedLocale;
540
610
  }
541
611
 
542
612
  /**
@@ -549,6 +619,9 @@ export function assertIsLocale(input) {
549
619
  * they are defined. If a strategy returns an invalid locale,
550
620
  * it will fall back to the next strategy.
551
621
  *
622
+ * Note: Custom server strategies are not supported in this synchronous version.
623
+ * Use `extractLocaleFromRequestAsync` if you need custom server strategies with async getLocale methods.
624
+ *
552
625
  * @example
553
626
  * const locale = extractLocaleFromRequest(request);
554
627
  *
@@ -581,9 +654,10 @@ export const extractLocaleFromRequest = (request) => {
581
654
  else if (strat === "localStorage") {
582
655
  continue;
583
656
  }
584
- else if (isCustomStrategy(strat) && customServerStrategies.has(strat)) {
585
- const handler = customServerStrategies.get(strat);
586
- locale = handler.getLocale(request);
657
+ else if (isCustomStrategy(strat)) {
658
+ // Custom strategies are not supported in sync version
659
+ // Use extractLocaleFromRequestAsync for custom server strategies
660
+ continue;
587
661
  }
588
662
  if (locale !== undefined) {
589
663
  if (!isLocale(locale)) {
@@ -597,6 +671,57 @@ export const extractLocaleFromRequest = (request) => {
597
671
  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");
598
672
  };
599
673
 
674
+ /**
675
+ * Asynchronously extracts a locale from a request.
676
+ *
677
+ * This function supports async custom server strategies, unlike the synchronous
678
+ * `extractLocaleFromRequest`. Use this function when you have custom server strategies
679
+ * that need to perform asynchronous operations (like database calls) in their getLocale method.
680
+ *
681
+ * The function first processes any custom server strategies asynchronously, then falls back
682
+ * to the synchronous `extractLocaleFromRequest` for all other strategies.
683
+ *
684
+ * @see {@link https://github.com/opral/inlang-paraglide-js/issues/527#issuecomment-2978151022}
685
+ *
686
+ * @example
687
+ * // Basic usage
688
+ * const locale = await extractLocaleFromRequestAsync(request);
689
+ *
690
+ * @example
691
+ * // With custom async server strategy
692
+ * defineCustomServerStrategy("custom-database", {
693
+ * getLocale: async (request) => {
694
+ * const userId = extractUserIdFromRequest(request);
695
+ * return await getUserLocaleFromDatabase(userId);
696
+ * }
697
+ * });
698
+ *
699
+ * const locale = await extractLocaleFromRequestAsync(request);
700
+ *
701
+ * @type {(request: Request) => Promise<Locale>}
702
+ */
703
+ export const extractLocaleFromRequestAsync = async (request) => {
704
+ /** @type {string|undefined} */
705
+ let locale;
706
+ // Process custom strategies first, in order
707
+ for (const strat of strategy) {
708
+ if (isCustomStrategy(strat) && customServerStrategies.has(strat)) {
709
+ const handler = customServerStrategies.get(strat);
710
+ if (handler) {
711
+ /** @type {string|undefined} */
712
+ locale = await handler.getLocale(request);
713
+ }
714
+ // If we got a valid locale from this custom strategy, use it
715
+ if (locale !== undefined && isLocale(locale)) {
716
+ return assertIsLocale(locale);
717
+ }
718
+ }
719
+ }
720
+ // If no custom strategy provided a valid locale, fall back to sync version
721
+ locale = extractLocaleFromRequest(request);
722
+ return assertIsLocale(locale);
723
+ };
724
+
600
725
  /**
601
726
  * Extracts a cookie from the document.
602
727
  *
@@ -768,6 +893,8 @@ function defaultUrlPatternExtractLocale(url) {
768
893
  * For client-side UI components, use `localizeHref()` instead, which provides
769
894
  * a more convenient API with relative paths and automatic locale detection.
770
895
  *
896
+ * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/i18n-routing
897
+ *
771
898
  * @example
772
899
  * ```typescript
773
900
  * // Server middleware example
@@ -874,6 +1001,8 @@ function localizeUrlDefaultPattern(url, options) {
874
1001
  * For client-side UI components, use `deLocalizeHref()` instead, which provides
875
1002
  * a more convenient API with relative paths.
876
1003
  *
1004
+ * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/i18n-routing
1005
+ *
877
1006
  * @example
878
1007
  * ```typescript
879
1008
  * // Server middleware example
@@ -1065,6 +1194,122 @@ export function aggregateGroups(match) {
1065
1194
  };
1066
1195
  }
1067
1196
 
1197
+ /**
1198
+ * @typedef {object} ShouldRedirectServerInput
1199
+ * @property {Request} request
1200
+ * @property {string | URL} [url]
1201
+ * @property {ReturnType<typeof assertIsLocale>} [locale]
1202
+ *
1203
+ * @typedef {object} ShouldRedirectClientInput
1204
+ * @property {undefined} [request]
1205
+ * @property {string | URL} [url]
1206
+ * @property {ReturnType<typeof assertIsLocale>} [locale]
1207
+ *
1208
+ * @typedef {ShouldRedirectServerInput | ShouldRedirectClientInput} ShouldRedirectInput
1209
+ *
1210
+ * @typedef {object} ShouldRedirectResult
1211
+ * @property {boolean} shouldRedirect - Indicates whether the consumer should perform a redirect.
1212
+ * @property {ReturnType<typeof assertIsLocale>} locale - Locale resolved using the configured strategies.
1213
+ * @property {URL | undefined} redirectUrl - Destination URL when a redirect is required.
1214
+ */
1215
+ /**
1216
+ * Determines whether a redirect is required to align the current URL with the active locale.
1217
+ *
1218
+ * This helper mirrors the logic that powers `paraglideMiddleware`, but works in both server
1219
+ * and client environments. It evaluates the configured strategies in order, computes the
1220
+ * canonical localized URL, and reports when the current URL does not match.
1221
+ *
1222
+ * When called in the browser without arguments, the current `window.location.href` is used.
1223
+ *
1224
+ * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/i18n-routing#client-side-redirects
1225
+ *
1226
+ * @example
1227
+ * // Client side usage (e.g. TanStack Router beforeLoad hook)
1228
+ * async function beforeLoad({ location }) {
1229
+ * const decision = await shouldRedirect({ url: location.href });
1230
+ *
1231
+ * if (decision.shouldRedirect) {
1232
+ * throw redirect({ to: decision.redirectUrl.href });
1233
+ * }
1234
+ * }
1235
+ *
1236
+ * @example
1237
+ * // Server side usage with a Request
1238
+ * export async function handle(request) {
1239
+ * const decision = await shouldRedirect({ request });
1240
+ *
1241
+ * if (decision.shouldRedirect) {
1242
+ * return Response.redirect(decision.redirectUrl, 307);
1243
+ * }
1244
+ *
1245
+ * return render(request, decision.locale);
1246
+ * }
1247
+ *
1248
+ * @param {ShouldRedirectInput} [input]
1249
+ * @returns {Promise<ShouldRedirectResult>}
1250
+ */
1251
+ export async function shouldRedirect(input = {}) {
1252
+ const locale = /** @type {ReturnType<typeof assertIsLocale>} */ (await resolveLocale(input));
1253
+ if (!strategy.includes("url")) {
1254
+ return { shouldRedirect: false, locale, redirectUrl: undefined };
1255
+ }
1256
+ const currentUrl = resolveUrl(input);
1257
+ const localizedUrl = localizeUrl(currentUrl.href, { locale });
1258
+ const shouldRedirectToLocalizedUrl = normalizeUrl(localizedUrl.href) !== normalizeUrl(currentUrl.href);
1259
+ return {
1260
+ shouldRedirect: shouldRedirectToLocalizedUrl,
1261
+ locale,
1262
+ redirectUrl: shouldRedirectToLocalizedUrl ? localizedUrl : undefined,
1263
+ };
1264
+ }
1265
+ /**
1266
+ * Resolves the locale either from the provided input or by using the configured strategies.
1267
+ *
1268
+ * @param {ShouldRedirectInput} input
1269
+ * @returns {Promise<ReturnType<typeof assertIsLocale>>}
1270
+ */
1271
+ async function resolveLocale(input) {
1272
+ if (input.locale) {
1273
+ return assertIsLocale(input.locale);
1274
+ }
1275
+ if (input.request) {
1276
+ return extractLocaleFromRequestAsync(input.request);
1277
+ }
1278
+ return getLocale();
1279
+ }
1280
+ /**
1281
+ * Resolves the current URL from the provided input or runtime context.
1282
+ *
1283
+ * @param {ShouldRedirectInput} input
1284
+ * @returns {URL}
1285
+ */
1286
+ function resolveUrl(input) {
1287
+ if (input.request) {
1288
+ return new URL(input.request.url);
1289
+ }
1290
+ if (input.url instanceof URL) {
1291
+ return new URL(input.url.href);
1292
+ }
1293
+ if (typeof input.url === "string") {
1294
+ return new URL(input.url, getUrlOrigin());
1295
+ }
1296
+ if (typeof window !== "undefined" && window?.location?.href) {
1297
+ return new URL(window.location.href);
1298
+ }
1299
+ throw new Error("shouldRedirect() requires either a request, an absolute URL, or must run in a browser environment.");
1300
+ }
1301
+ /**
1302
+ * Normalize url for comparison by stripping the trailing slash.
1303
+ *
1304
+ * @param {string} url
1305
+ * @returns {string}
1306
+ */
1307
+ function normalizeUrl(url) {
1308
+ const urlObj = new URL(url);
1309
+ urlObj.pathname = urlObj.pathname.replace(/\/$/, "");
1310
+ return urlObj.href;
1311
+ }
1312
+
1068
1313
  /**
1069
1314
  * High-level URL localization function optimized for client-side UI usage.
1070
1315
  *
@@ -1076,6 +1321,8 @@ export function aggregateGroups(match) {
1076
1321
  * - Automatically detects current locale if not specified
1077
1322
  * - Handles string input/output instead of URL objects
1078
1323
  *
1324
+ * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/i18n-routing
1325
+ *
1079
1326
  * @example
1080
1327
  * ```typescript
1081
1328
  * // In a React/Vue/Svelte component
@@ -1134,6 +1381,8 @@ export function localizeHref(href, options) {
1134
1381
  * - Returns relative paths when possible
1135
1382
  * - Handles string input/output instead of URL objects
1136
1383
  *
1384
+ * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/i18n-routing
1385
+ *
1137
1386
  * @example
1138
1387
  * ```typescript
1139
1388
  * // In a React/Vue/Svelte component
@@ -1161,7 +1410,6 @@ export function localizeHref(href, options) {
1161
1410
  *
1162
1411
  * @param {string} href - The href to de-localize (can be relative or absolute)
1163
1412
  * @returns {string} The de-localized href, relative if input was relative
1164
- * @see deLocalizeUrl - For low-level URL de-localization in server contexts
1165
1413
  */
1166
1414
  export function deLocalizeHref(href) {
1167
1415
  const url = new URL(href, getUrlOrigin());
@@ -1188,26 +1436,47 @@ export function trackMessageCall(safeModuleId, locale) {
1188
1436
  }
1189
1437
 
1190
1438
  /**
1191
- * Generates a list of localized URLs for all provided URLs.
1439
+ * Generates localized URL variants for all provided URLs based on your configured locales and URL patterns.
1192
1440
  *
1193
- * This is useful for SSG (Static Site Generation) and sitemap generation.
1194
- * NextJS and other frameworks use this function for SSG.
1441
+ * This function is essential for Static Site Generation (SSG) where you need to tell your framework
1442
+ * which pages to pre-render at build time. It's also useful for generating sitemaps and
1443
+ * `<link rel="alternate" hreflang>` tags for SEO.
1444
+ *
1445
+ * The function respects your `urlPatterns` configuration - if you have translated pathnames
1446
+ * (e.g., `/about` → `/ueber-uns` for German), it will generate the correct localized paths.
1447
+ *
1448
+ * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/static-site-generation
1195
1449
  *
1196
1450
  * @example
1197
- * ```typescript
1198
- * const urls = generateStaticLocalizedUrls([
1199
- * "https://example.com/about",
1200
- * "https://example.com/blog",
1451
+ * // Basic usage - generate all locale variants for a list of paths
1452
+ * const localizedUrls = generateStaticLocalizedUrls([
1453
+ * "/",
1454
+ * "/about",
1455
+ * "/blog/post-1",
1201
1456
  * ]);
1202
- * urls[0].href // => "https://example.com/about"
1203
- * urls[1].href // => "https://example.com/blog"
1204
- * urls[2].href // => "https://example.com/de/about"
1205
- * urls[3].href // => "https://example.com/de/blog"
1206
- * ...
1207
- * ```
1457
+ * // Returns URL objects for each locale:
1458
+ * // ["/en/", "/de/", "/en/about", "/de/about", "/en/blog/post-1", "/de/blog/post-1"]
1208
1459
  *
1209
- * @param {(string | URL)[]} urls - List of URLs to generate localized versions for. Can be absolute URLs or paths.
1210
- * @returns {URL[]} List of localized URLs as URL objects
1460
+ * @example
1461
+ * // Use with framework SSG APIs
1462
+ * // SvelteKit
1463
+ * export function entries() {
1464
+ * const paths = ["/", "/about", "/contact"];
1465
+ * return generateStaticLocalizedUrls(paths).map(url => ({
1466
+ * locale: extractLocaleFromUrl(url)
1467
+ * }));
1468
+ * }
1469
+ *
1470
+ * @example
1471
+ * // Sitemap generation
1472
+ * const allPages = ["/", "/about", "/blog"];
1473
+ * const sitemapUrls = generateStaticLocalizedUrls(allPages);
1474
+ *
1475
+ * @param {(string | URL)[]} urls - List of canonical URLs or paths to generate localized versions for.
1476
+ * Can be absolute URLs (`https://example.com/about`) or paths (`/about`).
1477
+ * Paths are resolved against `http://localhost` internally.
1478
+ * @returns {URL[]} Array of URL objects representing all localized variants.
1479
+ * The order follows each input URL with all its locale variants before moving to the next URL.
1211
1480
  */
1212
1481
  export function generateStaticLocalizedUrls(urls) {
1213
1482
  const localizedUrls = new Set();
@@ -1290,52 +1559,54 @@ export function generateStaticLocalizedUrls(urls) {
1290
1559
  * @typedef {Array<Strategy>} Strategies
1291
1560
  */
1292
1561
  /**
1293
- * @typedef {{ getLocale: (request?: Request) => string | undefined }} CustomServerStrategyHandler
1562
+ * @typedef {{ getLocale: (request?: Request) => Promise<string | undefined> | (string | undefined) }} CustomServerStrategyHandler
1294
1563
  */
1295
1564
  /**
1296
- * @typedef {{ getLocale: () => string | undefined, setLocale: (locale: string) => void }} CustomClientStrategyHandler
1565
+ * @typedef {{ getLocale: () => Promise<string|undefined> | (string | undefined), setLocale: (locale: string) => Promise<void> | void }} CustomClientStrategyHandler
1297
1566
  */
1567
+ /** @type {Map<string, CustomServerStrategyHandler>} */
1298
1568
  export const customServerStrategies = new Map();
1569
+ /** @type {Map<string, CustomClientStrategyHandler>} */
1299
1570
  export const customClientStrategies = new Map();
1300
1571
  /**
1301
1572
  * Checks if the given strategy is a custom strategy.
1302
1573
  *
1303
1574
  * @param {any} strategy The name of the custom strategy to validate.
1304
- * Must be a string that starts with "custom-" followed by alphanumeric characters.
1575
+ * Must be a string that starts with "custom-" followed by alphanumeric characters, hyphens, or underscores.
1305
1576
  * @returns {boolean} Returns true if it is a custom strategy, false otherwise.
1306
1577
  */
1307
1578
  export function isCustomStrategy(strategy) {
1308
- return typeof strategy === "string" && /^custom-[A-Za-z0-9]+$/.test(strategy);
1579
+ return (typeof strategy === "string" && /^custom-[A-Za-z0-9_-]+$/.test(strategy));
1309
1580
  }
1310
1581
  /**
1311
1582
  * Defines a custom strategy that is executed on the server.
1312
1583
  *
1313
- * @param {any} strategy The name of the custom strategy to define. Must follow the pattern `custom-<name>` where
1314
- * `<name>` contains only alphanumeric characters.
1584
+ * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/strategy#write-your-own-strategy
1585
+ *
1586
+ * @param {any} strategy The name of the custom strategy to define. Must follow the pattern custom-name with alphanumeric characters, hyphens, or underscores.
1315
1587
  * @param {CustomServerStrategyHandler} handler The handler for the custom strategy, which should implement
1316
- * the method `getLocale`.
1588
+ * the method getLocale.
1317
1589
  * @returns {void}
1318
1590
  */
1319
1591
  export function defineCustomServerStrategy(strategy, handler) {
1320
1592
  if (!isCustomStrategy(strategy)) {
1321
- throw new Error(`Invalid custom strategy: "${strategy}". Must be a custom strategy following the pattern custom-<name>` +
1322
- " where <name> contains only alphanumeric characters.");
1593
+ throw new Error(`Invalid custom strategy: "${strategy}". Must be a custom strategy following the pattern custom-name.`);
1323
1594
  }
1324
1595
  customServerStrategies.set(strategy, handler);
1325
1596
  }
1326
1597
  /**
1327
1598
  * Defines a custom strategy that is executed on the client.
1328
1599
  *
1329
- * @param {any} strategy The name of the custom strategy to define. Must follow the pattern `custom-<name>` where
1330
- * `<name>` contains only alphanumeric characters.
1600
+ * @see https://inlang.com/m/gerre34r/library-inlang-paraglideJs/strategy#write-your-own-strategy
1601
+ *
1602
+ * @param {any} strategy The name of the custom strategy to define. Must follow the pattern custom-name with alphanumeric characters, hyphens, or underscores.
1331
1603
  * @param {CustomClientStrategyHandler} handler The handler for the custom strategy, which should implement the
1332
- * methods `getLocale` and `setLocale`.
1604
+ * methods getLocale and setLocale.
1333
1605
  * @returns {void}
1334
1606
  */
1335
1607
  export function defineCustomClientStrategy(strategy, handler) {
1336
1608
  if (!isCustomStrategy(strategy)) {
1337
- throw new Error(`Invalid custom strategy: "${strategy}". Must be a custom strategy following the pattern custom-<name>` +
1338
- " where <name> contains only alphanumeric characters.");
1609
+ throw new Error(`Invalid custom strategy: "${strategy}". Must be a custom strategy following the pattern custom-name.`);
1339
1610
  }
1340
1611
  customClientStrategies.set(strategy, handler);
1341
1612
  }
@@ -1351,3 +1622,45 @@ export function defineCustomClientStrategy(strategy, handler) {
1351
1622
  * @typedef {(typeof locales)[number]} Locale
1352
1623
  */
1353
1624
 
1625
+ /**
1626
+ * A branded type representing a localized string.
1627
+ *
1628
+ * Message functions return this type instead of `string`, enabling TypeScript
1629
+ * to distinguish translated strings from regular strings at compile time.
1630
+ * This allows you to enforce that only properly localized content is used
1631
+ * in your UI components.
1632
+ *
1633
+ * Since `LocalizedString` is a branded subtype of `string`, it remains fully
1634
+ * backward compatible—you can pass it anywhere a `string` is expected.
1635
+ *
1636
+ * @example
1637
+ * // Enforce localized strings in your components
1638
+ * function PageTitle(props: { title: LocalizedString }) {
1639
+ * return <h1>{props.title}</h1>
1640
+ * }
1641
+ *
1642
+ * // ✅ Correct: using a message function
1643
+ * <PageTitle title={m.welcome_title()} />
1644
+ *
1645
+ * // ❌ Type error: raw strings are not LocalizedString
1646
+ * <PageTitle title="Welcome" />
1647
+ *
1648
+ * @example
1649
+ * // LocalizedString is assignable to string (backward compatible)
1650
+ * const localized: LocalizedString = m.greeting()
1651
+ * const str: string = localized // ✅ works fine
1652
+ *
1653
+ * // But string is not assignable to LocalizedString
1654
+ * const raw: LocalizedString = "Hello" // ❌ Type error
1655
+ *
1656
+ * @example
1657
+ * // Catches accidental string concatenation
1658
+ * function showMessage(msg: LocalizedString) { ... }
1659
+ *
1660
+ * showMessage(m.hello()) // ✅
1661
+ * showMessage("Hello " + userName) // ❌ Type error
1662
+ * showMessage(m.hello_user({ name: userName })) // ✅ use params instead
1663
+ *
1664
+ * @typedef {string & { readonly __brand: 'LocalizedString' }} LocalizedString
1665
+ */
1666
+