intor 2.0.3 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,9 +4,10 @@ var React7 = require('react');
4
4
  var logry = require('logry');
5
5
  var Keyv = require('keyv');
6
6
  var intorTranslator = require('intor-translator');
7
+ var formatUrl = require('next/dist/shared/lib/router/utils/format-url');
7
8
  var NextLink = require('next/link');
8
9
  var navigation = require('next/navigation');
9
- var formatUrl = require('next/dist/shared/lib/router/utils/format-url');
10
+ var headers = require('next/headers');
10
11
 
11
12
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
13
 
@@ -47,66 +48,6 @@ function useConfig() {
47
48
  return context;
48
49
  }
49
50
  var LocaleContext = React7__namespace.createContext(void 0);
50
-
51
- // src/adapters/next/shared/utils/build-cookie-string.ts
52
- var buildCookieString = (cookie, locale) => {
53
- const parts = [];
54
- parts.push(`${cookie.name}=${encodeURIComponent(locale)}`);
55
- if (cookie.maxAge) {
56
- const expires = new Date(Date.now() + cookie.maxAge * 1e3).toUTCString();
57
- parts.push(`expires=${expires}`);
58
- parts.push(`max-age=${cookie.maxAge}`);
59
- }
60
- parts.push(`path=${cookie.path ?? "/"}`);
61
- if (cookie.domain) {
62
- parts.push(`domain=${cookie.domain}`);
63
- }
64
- if (cookie.sameSite) {
65
- parts.push(
66
- `SameSite=${cookie.sameSite[0].toUpperCase()}${cookie.sameSite.slice(1).toLowerCase()}`
67
- );
68
- }
69
- if (cookie.secure !== false) {
70
- parts.push(`Secure`);
71
- }
72
- return parts.join("; ");
73
- };
74
-
75
- // src/adapters/next/shared/utils/set-locale-cookie-browser.ts
76
- var setLocaleCookieBrowser = ({
77
- cookie,
78
- locale
79
- }) => {
80
- if (typeof window === "undefined") return;
81
- if (cookie.disabled || !cookie.autoSetCookie) return;
82
- const cookieString = buildCookieString(cookie, locale);
83
- document.cookie = cookieString;
84
- };
85
-
86
- // src/adapters/next/contexts/locale/utils/change-locale.ts
87
- var changeLocale = ({
88
- currentLocale,
89
- newLocale,
90
- loaderOptions,
91
- cookie,
92
- setLocale,
93
- refetchMessages
94
- }) => {
95
- if (typeof document === "undefined") return;
96
- const loaderType = loaderOptions?.type;
97
- if (newLocale === currentLocale) return;
98
- if (loaderType === "import") {
99
- console.warn(
100
- `[Intor] You are using dynamic import to switch languages. Please make sure to use the wrapped <Link> component to trigger a page reload, ensuring that the translation data is dynamically updated.`
101
- );
102
- }
103
- setLocale(newLocale);
104
- setLocaleCookieBrowser({ cookie, locale: newLocale });
105
- document.documentElement.lang = newLocale;
106
- if (loaderType === "api" && refetchMessages) {
107
- void refetchMessages(newLocale);
108
- }
109
- };
110
51
  var MessagesContext = React7__namespace.createContext(void 0);
111
52
 
112
53
  // src/modules/config/constants/cache.constants.ts
@@ -130,16 +71,22 @@ var DEFAULT_FORMATTER_CONFIG = {
130
71
  };
131
72
  function getLogger({
132
73
  id,
133
- formatterConfig = DEFAULT_FORMATTER_CONFIG,
74
+ formatterConfig,
75
+ preset,
134
76
  ...options
135
77
  }) {
136
78
  const pool = getGlobalLoggerPool();
137
79
  let logger = pool.get(id);
138
80
  if (!logger) {
139
- logger = logry.logry({ id, formatterConfig, ...options });
81
+ logger = logry.logry({
82
+ id,
83
+ formatterConfig: !formatterConfig && !preset ? DEFAULT_FORMATTER_CONFIG : formatterConfig,
84
+ preset,
85
+ ...options
86
+ });
140
87
  pool.set(id, logger);
141
88
  if (pool.size > 1e3) {
142
- const keys = Array.from(pool.keys());
89
+ const keys = [...pool.keys()];
143
90
  for (const key of keys.slice(0, 200)) pool.delete(key);
144
91
  }
145
92
  }
@@ -154,7 +101,7 @@ function getGlobalMessagesPool() {
154
101
 
155
102
  // src/shared/utils/merge-messages.ts
156
103
  var mergeMessages = (staticMessages = {}, loadedMessages = {}) => {
157
- const result = Object.keys(staticMessages).length ? { ...staticMessages } : {};
104
+ const result = Object.keys(staticMessages).length > 0 ? { ...staticMessages } : {};
158
105
  for (const locale in loadedMessages) {
159
106
  const loaded = loadedMessages[locale];
160
107
  if (!result[locale]) {
@@ -173,7 +120,7 @@ var mergeMessages = (staticMessages = {}, loadedMessages = {}) => {
173
120
  var CACHE_KEY_DELIMITER = "|";
174
121
  var sanitize = (k) => k.replaceAll(/[\u200B-\u200D\uFEFF]/g, "").replaceAll(/[\r\n]/g, "").trim();
175
122
  var normalizeCacheKey = (key, delimiter = CACHE_KEY_DELIMITER) => {
176
- if (!key) return null;
123
+ if (key === null || key === void 0) return null;
177
124
  if (Array.isArray(key)) {
178
125
  if (key.length === 0) return null;
179
126
  const normalized = key.map((k) => {
@@ -197,53 +144,90 @@ var resolveNamespaces = ({
197
144
  pathname
198
145
  }) => {
199
146
  const { loader } = config;
200
- const {
201
- routeNamespaces = {},
202
- namespaces: fallbackNamespaces
203
- } = loader;
204
- const { unprefixedPathname } = extractPathname({ config, pathname });
205
- const standardizedPathname = standardizePathname({
206
- config,
207
- pathname: unprefixedPathname
208
- });
147
+ const { routeNamespaces = {}, namespaces } = loader || {};
148
+ const standardizedPathname = standardizePathname({ config, pathname });
209
149
  const placeholderRemovedPathname = standardizedPathname.replace(
210
150
  `/${PREFIX_PLACEHOLDER}`,
211
151
  ""
212
152
  );
213
- const defaultNamespaces = routeNamespaces.default ?? [];
214
- const exactMatchNamespaces = routeNamespaces[standardizedPathname] ?? routeNamespaces[placeholderRemovedPathname];
215
- if (exactMatchNamespaces) {
216
- return [...defaultNamespaces, ...exactMatchNamespaces];
217
- }
218
- let bestMatch = "";
219
- let bestNamespaces;
153
+ const collected = [
154
+ ...routeNamespaces.default || [],
155
+ // default
156
+ ...namespaces || [],
157
+ // default
158
+ ...routeNamespaces[standardizedPathname] || [],
159
+ // exact match
160
+ ...routeNamespaces[placeholderRemovedPathname] || []
161
+ // exact match
162
+ ];
220
163
  const prefixPatterns = Object.keys(routeNamespaces).filter(
221
164
  (pattern) => pattern.endsWith("/*")
222
165
  );
223
166
  for (const pattern of prefixPatterns) {
224
167
  const basePath = pattern.replace(/\/\*$/, "");
225
- if (standardizedPathname.startsWith(basePath)) {
226
- if (basePath.length > bestMatch.length) {
227
- bestMatch = basePath;
228
- bestNamespaces = routeNamespaces[pattern];
229
- }
168
+ if (standardizedPathname.startsWith(basePath) || placeholderRemovedPathname.startsWith(basePath)) {
169
+ collected.push(...routeNamespaces[pattern] || []);
230
170
  }
231
171
  }
232
- const matchedNamespaces = bestNamespaces ?? routeNamespaces["/*"] ?? fallbackNamespaces ?? [];
233
- if (matchedNamespaces.length > 0) {
234
- return [...defaultNamespaces, ...matchedNamespaces];
235
- } else {
236
- return [...defaultNamespaces];
172
+ return [...new Set(collected)];
173
+ };
174
+
175
+ // src/shared/utils/locale/normalize-locale.ts
176
+ var toCanonical = (input) => {
177
+ try {
178
+ return Intl.getCanonicalLocales(input)[0]?.toLowerCase();
179
+ } catch {
180
+ return;
237
181
  }
238
182
  };
183
+ var normalizeLocale = (locale = "", supportedLocales = []) => {
184
+ if (!locale || supportedLocales.length === 0) return;
185
+ const canonicalLocale = toCanonical(locale);
186
+ if (!canonicalLocale) return;
187
+ const supportedCanonicalMap = /* @__PURE__ */ new Map();
188
+ for (const l of supportedLocales) {
189
+ const normalized = toCanonical(l);
190
+ if (normalized) {
191
+ supportedCanonicalMap.set(normalized, l);
192
+ }
193
+ }
194
+ if (supportedCanonicalMap.has(canonicalLocale)) {
195
+ return supportedCanonicalMap.get(canonicalLocale);
196
+ }
197
+ const baseLang = canonicalLocale.split("-")[0];
198
+ for (const [key, original] of supportedCanonicalMap) {
199
+ const supportedBase = key.split("-")[0];
200
+ if (supportedBase === baseLang) {
201
+ return original;
202
+ }
203
+ }
204
+ return;
205
+ };
206
+
207
+ // src/shared/utils/locale/resolve-preferred-locale.ts
208
+ var resolvePreferredLocale = (acceptLanguageHeader, supportedLocales) => {
209
+ if (!acceptLanguageHeader || !supportedLocales || supportedLocales.length === 0) {
210
+ return;
211
+ }
212
+ const supportedLocalesSet = new Set(supportedLocales);
213
+ const preferred = acceptLanguageHeader.split(",").map((part) => {
214
+ const [lang, qValue] = part.split(";");
215
+ const q = qValue ? Number.parseFloat(qValue.split("=")[1]) : 1;
216
+ if (Number.isNaN(q)) {
217
+ return { lang: lang.trim(), q: 0 };
218
+ }
219
+ return { lang: lang.trim(), q };
220
+ }).toSorted((a, b) => b.q - a.q).find(({ lang }) => supportedLocalesSet.has(lang))?.lang;
221
+ return preferred;
222
+ };
239
223
 
240
224
  // src/shared/utils/pathname/normalize-pathname.ts
241
225
  var normalizePathname = (rawPathname, options = {}) => {
242
226
  const length = rawPathname.length;
243
227
  let start = 0;
244
228
  let end = length - 1;
245
- while (start <= end && rawPathname.charCodeAt(start) <= 32) start++;
246
- while (end >= start && rawPathname.charCodeAt(end) <= 32) end--;
229
+ while (start <= end && (rawPathname.codePointAt(start) ?? 0) <= 32) start++;
230
+ while (end >= start && (rawPathname.codePointAt(end) ?? 0) <= 32) end--;
247
231
  if (start > end) return "/";
248
232
  let result = "";
249
233
  let hasSlash = false;
@@ -254,11 +238,7 @@ var normalizePathname = (rawPathname, options = {}) => {
254
238
  hasSlash = true;
255
239
  }
256
240
  } else {
257
- if (hasSlash || result === "") {
258
- result += "/" + char;
259
- } else {
260
- result += char;
261
- }
241
+ result += hasSlash || result === "" ? "/" + char : char;
262
242
  hasSlash = false;
263
243
  }
264
244
  }
@@ -282,8 +262,8 @@ var extractPathname = ({
282
262
  } else if (basePath && normalizedPathname === basePath) {
283
263
  prefixedPathname = "/";
284
264
  }
285
- const pathParts = prefixedPathname.split("/").filter(Boolean);
286
- const maybeLocale = pathParts[0] || "";
265
+ const pathPart = prefixedPathname.split("/").find(Boolean);
266
+ const maybeLocale = pathPart || "";
287
267
  const isLocalePrefixed = config.supportedLocales?.includes(maybeLocale);
288
268
  let unprefixedPathname = prefixedPathname;
289
269
  if (prefix === "all") {
@@ -316,7 +296,7 @@ var standardizePathname = ({
316
296
  PREFIX_PLACEHOLDER,
317
297
  normalizePathname(pathname)
318
298
  ];
319
- const standardizedPathname = parts.join("/").replace(/\/{2,}/g, "/");
299
+ const standardizedPathname = parts.join("/").replaceAll(/\/{2,}/g, "/");
320
300
  return normalizePathname(standardizedPathname);
321
301
  };
322
302
 
@@ -334,13 +314,13 @@ var fetchMessages = async ({
334
314
  const params = new URLSearchParams(searchParams);
335
315
  params.append("locale", locale);
336
316
  const url = `${apiUrl}?${params.toString()}`;
337
- const headers = {
317
+ const headers2 = {
338
318
  "Content-Type": "application/json",
339
319
  ...apiHeaders
340
320
  };
341
321
  const response = await fetch(url, {
342
322
  method: "GET",
343
- headers,
323
+ headers: headers2,
344
324
  cache: "no-store"
345
325
  });
346
326
  if (!response.ok) {
@@ -420,16 +400,19 @@ var loadApiMessages = async ({
420
400
  logger.warn("No apiUrl provided. Skipping fetch.");
421
401
  return;
422
402
  }
423
- const pool = getGlobalMessagesPool();
403
+ let pool;
404
+ if (cache.enabled) {
405
+ pool = getGlobalMessagesPool();
406
+ }
424
407
  const key = normalizeCacheKey([
425
408
  loggerOptions.id,
426
409
  basePath,
427
410
  locale,
428
- [...fallbackLocales ?? []].sort().join(","),
429
- [...namespaces ?? []].sort().join(",")
411
+ (fallbackLocales ?? []).toSorted().join(","),
412
+ (namespaces ?? []).toSorted().join(",")
430
413
  ]);
431
414
  if (cache.enabled && key) {
432
- const cached = await pool.get(key);
415
+ const cached = await pool?.get(key);
433
416
  if (cached) {
434
417
  logger.debug("Messages cache hit.", { key });
435
418
  return cached;
@@ -445,7 +428,7 @@ var loadApiMessages = async ({
445
428
  });
446
429
  if (messages) {
447
430
  if (cache.enabled && key) {
448
- await pool.set(key, messages, cache.ttl);
431
+ await pool?.set(key, messages, cache.ttl);
449
432
  }
450
433
  return messages;
451
434
  }
@@ -463,7 +446,7 @@ var loadApiMessages = async ({
463
446
  searchParams: decodeURIComponent(searchParams.toString())
464
447
  });
465
448
  if (cache.enabled && key) {
466
- await pool.set(key, fallbackResult.messages, cache.ttl);
449
+ await pool?.set(key, fallbackResult.messages, cache.ttl);
467
450
  }
468
451
  return fallbackResult.messages;
469
452
  }
@@ -547,6 +530,8 @@ function useMessages() {
547
530
  throw new Error("useMessages must be used within a MessagesProvider");
548
531
  return context;
549
532
  }
533
+
534
+ // src/adapters/next/contexts/locale/utils/use-init-lazy-load.ts
550
535
  var useInitLazyLoad = ({
551
536
  loaderOptions,
552
537
  currentLocale
@@ -561,6 +546,41 @@ var useInitLazyLoad = ({
561
546
  }
562
547
  }, [lazyLoad, currentLocale, refetchMessages, isFirstLoadedRef]);
563
548
  };
549
+
550
+ // src/adapters/next/shared/utils/build-cookie-string.ts
551
+ var buildCookieString = (cookie, locale) => {
552
+ const parts = [`${cookie.name}=${encodeURIComponent(locale)}`];
553
+ if (cookie.maxAge) {
554
+ const expires = new Date(Date.now() + cookie.maxAge * 1e3).toUTCString();
555
+ parts.push(`expires=${expires}`, `max-age=${cookie.maxAge}`);
556
+ }
557
+ parts.push(`path=${cookie.path ?? "/"}`);
558
+ if (cookie.domain) {
559
+ parts.push(`domain=${cookie.domain}`);
560
+ }
561
+ if (cookie.sameSite) {
562
+ parts.push(
563
+ `SameSite=${cookie.sameSite[0].toUpperCase()}${cookie.sameSite.slice(1).toLowerCase()}`
564
+ );
565
+ }
566
+ if (cookie.secure !== false) {
567
+ parts.push(`Secure`);
568
+ }
569
+ return parts.join("; ");
570
+ };
571
+
572
+ // src/adapters/next/shared/utils/set-locale-cookie-browser.ts
573
+ var setLocaleCookieBrowser = ({
574
+ cookie,
575
+ locale
576
+ }) => {
577
+ if (globalThis.window === void 0) return;
578
+ if (cookie.disabled || !cookie.autoSetCookie) return;
579
+ const cookieString = buildCookieString(cookie, locale);
580
+ document.cookie = cookieString;
581
+ };
582
+
583
+ // src/adapters/next/contexts/locale/utils/use-init-locale-cookie.ts
564
584
  var useInitLocaleCookie = ({
565
585
  config,
566
586
  locale
@@ -569,8 +589,8 @@ var useInitLocaleCookie = ({
569
589
  if (typeof document === "undefined") return;
570
590
  const { cookie, routing } = config;
571
591
  const { firstVisit } = routing;
572
- const cookies = document.cookie.split(";").map((c) => c.trim());
573
- const isCookieExists = cookies.some((c) => c.startsWith(`${cookie.name}=`));
592
+ const cookies2 = document.cookie.split(";").map((c) => c.trim());
593
+ const isCookieExists = cookies2.some((c) => c.startsWith(`${cookie.name}=`));
574
594
  if (isCookieExists) return;
575
595
  if (!firstVisit.redirect) return;
576
596
  if (cookie.disabled || !cookie.autoSetCookie) return;
@@ -578,6 +598,31 @@ var useInitLocaleCookie = ({
578
598
  }, []);
579
599
  };
580
600
 
601
+ // src/adapters/next/contexts/locale/utils/change-locale.ts
602
+ var changeLocale = ({
603
+ currentLocale,
604
+ newLocale,
605
+ loaderOptions,
606
+ cookie,
607
+ setLocale,
608
+ refetchMessages
609
+ }) => {
610
+ if (typeof document === "undefined") return;
611
+ const loaderType = loaderOptions?.type;
612
+ if (newLocale === currentLocale) return;
613
+ if (loaderType === "import") {
614
+ console.warn(
615
+ `[Intor] You are using dynamic import to switch languages. Please make sure to use the wrapped <Link> component to trigger a page reload, ensuring that the translation data is dynamically updated.`
616
+ );
617
+ }
618
+ setLocale(newLocale);
619
+ setLocaleCookieBrowser({ cookie, locale: newLocale });
620
+ document.documentElement.lang = newLocale;
621
+ if (loaderType === "api" && refetchMessages) {
622
+ void refetchMessages(newLocale);
623
+ }
624
+ };
625
+
581
626
  // src/adapters/next/contexts/locale/provider.tsx
582
627
  function LocaleProvider({
583
628
  value: { initialLocale },
@@ -676,7 +721,7 @@ function useTranslator() {
676
721
  const context = React7__namespace.useContext(TranslatorContext);
677
722
  if (!context)
678
723
  throw new Error(
679
- "useIntorTranslator must be used within IntorTranslatorProvider"
724
+ "useTranslator must be used within IntorTranslatorProvider"
680
725
  );
681
726
  return context;
682
727
  }
@@ -689,23 +734,22 @@ var IntorProvider = ({
689
734
  return /* @__PURE__ */ React7__namespace.createElement(ConfigProvider, { value: { config, pathname } }, /* @__PURE__ */ React7__namespace.createElement(MessagesProvider, { value: { messages } }, /* @__PURE__ */ React7__namespace.createElement(LocaleProvider, { value: { initialLocale } }, /* @__PURE__ */ React7__namespace.createElement(TranslatorProvider, null, children))));
690
735
  };
691
736
 
692
- // src/adapters/next/hooks/use-intor/use-intor.ts
693
- function useIntor(preKey) {
737
+ // src/adapters/next/hooks/use-translator/use-translator.ts
738
+ function useTranslator2(preKey) {
694
739
  const { translator } = useTranslator();
695
740
  const { setLocale } = useLocale();
696
- const sharedMethod = {
741
+ const props = {
697
742
  messages: translator.messages,
698
743
  locale: translator.locale,
699
744
  isLoading: translator.isLoading,
700
745
  setLocale
701
746
  };
702
- if (preKey) {
703
- const { hasKey, t } = translator.scoped(preKey);
704
- return { ...sharedMethod, hasKey, t };
705
- } else {
706
- const { hasKey, t } = translator;
707
- return { ...sharedMethod, hasKey, t };
708
- }
747
+ const scoped = translator.scoped(preKey);
748
+ return {
749
+ ...props,
750
+ hasKey: preKey ? scoped.hasKey : translator.hasKey,
751
+ t: preKey ? scoped.t : translator.t
752
+ };
709
753
  }
710
754
 
711
755
  // src/adapters/next/shared/utils/locale-prefix-pathname.ts
@@ -725,15 +769,11 @@ var localePrefixPathname = ({
725
769
  );
726
770
  }
727
771
  if (prefix === "except-default") {
728
- if (locale === config.defaultLocale) {
729
- return normalizePathname(
730
- standardizedPathname.replaceAll(`/${PREFIX_PLACEHOLDER}`, "")
731
- );
732
- } else {
733
- return normalizePathname(
734
- standardizedPathname.replaceAll(PREFIX_PLACEHOLDER, locale)
735
- );
736
- }
772
+ return locale === config.defaultLocale ? normalizePathname(
773
+ standardizedPathname.replaceAll(`/${PREFIX_PLACEHOLDER}`, "")
774
+ ) : normalizePathname(
775
+ standardizedPathname.replaceAll(PREFIX_PLACEHOLDER, locale)
776
+ );
737
777
  }
738
778
  return normalizePathname(
739
779
  standardizedPathname.replaceAll(`/${PREFIX_PLACEHOLDER}`, "")
@@ -766,10 +806,7 @@ var localizePathname = ({
766
806
  };
767
807
  };
768
808
 
769
- // src/adapters/next/tools/utils/should-full-reload.ts
770
- var shouldFullReload = (loaderOptions) => {
771
- return loaderOptions?.type === "import" || loaderOptions?.type === "api" && loaderOptions.fullReload === true;
772
- };
809
+ // src/adapters/next/navigation/use-pathname.ts
773
810
  var usePathname = () => {
774
811
  const { config } = useConfig();
775
812
  const { locale } = useLocale();
@@ -781,75 +818,90 @@ var usePathname = () => {
781
818
  });
782
819
  return localePrefixedPathname;
783
820
  };
784
- var Link = ({
785
- href,
821
+
822
+ // src/adapters/next/navigation/utils/should-full-reload.ts
823
+ var shouldFullReload = ({
824
+ config,
825
+ targetPathname,
786
826
  locale,
787
- children,
788
- onClick,
789
- ...props
827
+ currentLocale
790
828
  }) => {
829
+ const loader = config.loader;
830
+ if (!loader || !loader.type) return false;
831
+ if (loader.type === "api" && !loader.fullReload) return false;
832
+ const { maybeLocale, isLocalePrefixed } = extractPathname({
833
+ config,
834
+ pathname: targetPathname
835
+ });
836
+ const isDifferentLocale = locale && locale !== currentLocale || isLocalePrefixed && maybeLocale !== currentLocale;
837
+ return isDifferentLocale ? true : false;
838
+ };
839
+
840
+ // src/adapters/next/navigation/utils/use-locale-switch.ts
841
+ var useLocaleSwitch = () => {
791
842
  const { config } = useConfig();
792
843
  const { locale: currentLocale, setLocale } = useLocale();
793
- const targetLocale = locale || currentLocale;
794
844
  const pathname = usePathname();
795
- const formattedUrl = href ? typeof href === "string" ? href : formatUrl.formatUrl(href) : void 0;
796
- const { localePrefixedPathname } = localizePathname({
797
- config,
798
- pathname: formattedUrl ?? pathname,
799
- locale: targetLocale
800
- });
801
- const resolvedHref = localePrefixedPathname;
802
- const handleClick = (e) => {
803
- onClick?.(e);
804
- if (shouldFullReload(config.loader)) {
845
+ const resolveHref = ({
846
+ href,
847
+ locale
848
+ }) => {
849
+ const isLocaleValid = locale && config.supportedLocales?.includes(locale);
850
+ const targetLocale = isLocaleValid ? locale : currentLocale;
851
+ const targetPathname = href ?? pathname;
852
+ const isExternal = targetPathname.startsWith("http");
853
+ const resolvedHref = !isExternal ? localizePathname({
854
+ config,
855
+ pathname: targetPathname,
856
+ locale: targetLocale
857
+ }).localePrefixedPathname : targetPathname;
858
+ return { resolvedHref, isExternal, targetLocale, targetPathname };
859
+ };
860
+ const switchLocale = ({
861
+ href,
862
+ locale
863
+ }) => {
864
+ const { resolvedHref, isExternal, targetLocale, targetPathname } = resolveHref({ href, locale });
865
+ if (isExternal) return;
866
+ if (shouldFullReload({ config, targetPathname, locale, currentLocale })) {
805
867
  setLocaleCookieBrowser({ cookie: config.cookie, locale: targetLocale });
806
- window.location.href = resolvedHref;
868
+ globalThis.location.href = resolvedHref;
807
869
  return;
808
870
  } else {
809
871
  setLocale(targetLocale);
810
872
  }
873
+ return resolvedHref;
874
+ };
875
+ return { resolveHref, switchLocale };
876
+ };
877
+
878
+ // src/adapters/next/navigation/link.tsx
879
+ var Link = ({
880
+ href,
881
+ locale,
882
+ children,
883
+ onClick,
884
+ ...props
885
+ }) => {
886
+ const formatted = typeof href === "string" ? href : href ? formatUrl.formatUrl(href) : void 0;
887
+ const { resolveHref, switchLocale } = useLocaleSwitch();
888
+ const { resolvedHref } = resolveHref({ href: formatted, locale });
889
+ const handleClick = (e) => {
890
+ onClick?.(e);
891
+ switchLocale({ href: formatted, locale });
811
892
  };
812
893
  return /* @__PURE__ */ React7__namespace.createElement(NextLink__default.default, { href: resolvedHref, onClick: handleClick, ...props }, children);
813
894
  };
814
895
  var useRouter = () => {
815
- const { config } = useConfig();
816
- const { locale: currentLocale, setLocale } = useLocale();
817
896
  const { push, replace, ...rest } = navigation.useRouter();
897
+ const { switchLocale } = useLocaleSwitch();
818
898
  const pushWithLocale = (href, options) => {
819
- const targetLocale = options?.locale || currentLocale;
820
- if (options?.locale) {
821
- const { localePrefixedPathname } = localizePathname({
822
- config,
823
- pathname: href,
824
- locale: targetLocale
825
- });
826
- href = localePrefixedPathname;
827
- }
828
- if (shouldFullReload(config.loader)) {
829
- window.location.href = href;
830
- return;
831
- } else {
832
- setLocale(targetLocale);
833
- }
834
- push(href, options);
899
+ const resolvedHref = switchLocale({ href, locale: options?.locale });
900
+ if (resolvedHref) push(resolvedHref, options);
835
901
  };
836
902
  const replaceWithLocale = (href, options) => {
837
- const targetLocale = options?.locale || currentLocale;
838
- if (options?.locale) {
839
- const { localePrefixedPathname } = localizePathname({
840
- config,
841
- pathname: href,
842
- locale: targetLocale
843
- });
844
- href = localePrefixedPathname;
845
- }
846
- if (shouldFullReload(config.loader)) {
847
- window.location.href = href;
848
- return;
849
- } else {
850
- setLocale(targetLocale);
851
- }
852
- replace(href, options);
903
+ const resolvedHref = switchLocale({ href, locale: options?.locale });
904
+ if (resolvedHref) replace(resolvedHref, options);
853
905
  };
854
906
  return {
855
907
  push: pushWithLocale,
@@ -861,10 +913,62 @@ var useRouter = () => {
861
913
  // src/adapters/next/shared/constants/pathname-header-name.ts
862
914
  var PATHNAME_HEADER_NAME = "x-intor-pathname";
863
915
 
916
+ // src/adapters/next/server/get-i18n-context.ts
917
+ var getI18nContext = async (config) => {
918
+ const baseLogger = getLogger({ id: config.id, ...config.logger });
919
+ const logger = baseLogger.child({ scope: "next-adapter" });
920
+ const cookiesStore = await headers.cookies();
921
+ const headersStore = await headers.headers();
922
+ const { defaultLocale, supportedLocales = [], cookie, routing } = config;
923
+ let locale;
924
+ if (!cookie.disabled) {
925
+ const localeFromCookie = cookiesStore.get(cookie.name)?.value;
926
+ locale = normalizeLocale(localeFromCookie, supportedLocales);
927
+ if (locale) {
928
+ logger.trace("Locale retrieved from cookie.", { locale });
929
+ }
930
+ }
931
+ if (!locale && routing.firstVisit.localeSource === "browser") {
932
+ const aLHeader = headersStore.get("accept-language") || void 0;
933
+ const preferredLocale = resolvePreferredLocale(aLHeader, supportedLocales);
934
+ locale = normalizeLocale(preferredLocale, supportedLocales);
935
+ logger.trace("Locale retrieved from header.", { locale });
936
+ }
937
+ const pathname = headersStore.get(PATHNAME_HEADER_NAME);
938
+ if (pathname) {
939
+ logger.trace("Pathname retrieved from header.", { pathname });
940
+ }
941
+ return {
942
+ locale: locale || defaultLocale,
943
+ pathname: pathname || ""
944
+ };
945
+ };
946
+
947
+ // src/adapters/next/navigation/redirect.ts
948
+ var redirect = async ({
949
+ config,
950
+ locale,
951
+ url,
952
+ type
953
+ }) => {
954
+ if (url.startsWith("http")) {
955
+ navigation.redirect(url);
956
+ }
957
+ const isLocaleValid = locale && config.supportedLocales?.includes(locale);
958
+ const { locale: detectedLocale } = await getI18nContext(config);
959
+ const { localePrefixedPathname } = localizePathname({
960
+ config,
961
+ pathname: url,
962
+ locale: isLocaleValid ? locale : detectedLocale
963
+ });
964
+ navigation.redirect(localePrefixedPathname, type);
965
+ };
966
+
864
967
  exports.IntorProvider = IntorProvider;
865
968
  exports.Link = Link;
866
969
  exports.PATHNAME_HEADER_NAME = PATHNAME_HEADER_NAME;
867
970
  exports.TranslateHandlersProvider = TranslateHandlersProvider;
868
- exports.useIntor = useIntor;
971
+ exports.redirect = redirect;
869
972
  exports.usePathname = usePathname;
870
973
  exports.useRouter = useRouter;
974
+ exports.useTranslator = useTranslator2;