@inlang/paraglide-js 2.0.13 → 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.
Files changed (58) hide show
  1. package/dist/bundler-plugins/vite.d.ts +1 -1
  2. package/dist/bundler-plugins/vite.d.ts.map +1 -1
  3. package/dist/compiler/compile-bundle.d.ts +2 -1
  4. package/dist/compiler/compile-bundle.d.ts.map +1 -1
  5. package/dist/compiler/compile-bundle.js +4 -3
  6. package/dist/compiler/compile-bundle.test.js +64 -0
  7. package/dist/compiler/compile-project.d.ts.map +1 -1
  8. package/dist/compiler/compile-project.js +1 -0
  9. package/dist/compiler/compiler-options.d.ts +17 -3
  10. package/dist/compiler/compiler-options.d.ts.map +1 -1
  11. package/dist/compiler/runtime/create-runtime.d.ts.map +1 -1
  12. package/dist/compiler/runtime/create-runtime.js +8 -0
  13. package/dist/compiler/runtime/extract-locale-from-cookie.d.ts +1 -1
  14. package/dist/compiler/runtime/extract-locale-from-cookie.js +1 -1
  15. package/dist/compiler/runtime/extract-locale-from-header.d.ts +2 -0
  16. package/dist/compiler/runtime/extract-locale-from-header.d.ts.map +1 -0
  17. package/dist/compiler/runtime/extract-locale-from-header.js +43 -0
  18. package/dist/compiler/runtime/extract-locale-from-header.test.d.ts +2 -0
  19. package/dist/compiler/runtime/extract-locale-from-header.test.d.ts.map +1 -0
  20. package/dist/compiler/runtime/extract-locale-from-header.test.js +51 -0
  21. package/dist/compiler/runtime/extract-locale-from-navigator.d.ts +2 -0
  22. package/dist/compiler/runtime/extract-locale-from-navigator.d.ts.map +1 -0
  23. package/dist/compiler/runtime/extract-locale-from-navigator.js +31 -0
  24. package/dist/compiler/runtime/extract-locale-from-navigator.test.d.ts +2 -0
  25. package/dist/compiler/runtime/extract-locale-from-navigator.test.d.ts.map +1 -0
  26. package/dist/compiler/runtime/extract-locale-from-navigator.test.js +29 -0
  27. package/dist/compiler/runtime/extract-locale-from-request-async.d.ts +31 -0
  28. package/dist/compiler/runtime/extract-locale-from-request-async.d.ts.map +1 -0
  29. package/dist/compiler/runtime/extract-locale-from-request-async.js +55 -0
  30. package/dist/compiler/runtime/extract-locale-from-request-async.test.d.ts +2 -0
  31. package/dist/compiler/runtime/extract-locale-from-request-async.test.d.ts.map +1 -0
  32. package/dist/compiler/runtime/extract-locale-from-request-async.test.js +58 -0
  33. package/dist/compiler/runtime/extract-locale-from-request.d.ts +3 -0
  34. package/dist/compiler/runtime/extract-locale-from-request.d.ts.map +1 -1
  35. package/dist/compiler/runtime/extract-locale-from-request.js +12 -36
  36. package/dist/compiler/runtime/extract-locale-from-request.test.js +84 -2
  37. package/dist/compiler/runtime/get-locale.d.ts.map +1 -1
  38. package/dist/compiler/runtime/get-locale.js +16 -26
  39. package/dist/compiler/runtime/get-locale.test.js +154 -1
  40. package/dist/compiler/runtime/set-locale.d.ts.map +1 -1
  41. package/dist/compiler/runtime/set-locale.js +18 -3
  42. package/dist/compiler/runtime/set-locale.test.js +146 -6
  43. package/dist/compiler/runtime/strategy.d.ts +60 -0
  44. package/dist/compiler/runtime/strategy.d.ts.map +1 -0
  45. package/dist/compiler/runtime/strategy.js +60 -0
  46. package/dist/compiler/runtime/strategy.test.d.ts +2 -0
  47. package/dist/compiler/runtime/strategy.test.d.ts.map +1 -0
  48. package/dist/compiler/runtime/strategy.test.js +94 -0
  49. package/dist/compiler/runtime/type.d.ts +5 -0
  50. package/dist/compiler/runtime/type.d.ts.map +1 -1
  51. package/dist/compiler/runtime/variables.d.ts +2 -2
  52. package/dist/compiler/runtime/variables.d.ts.map +1 -1
  53. package/dist/compiler/runtime/variables.js +1 -1
  54. package/dist/compiler/server/middleware.d.ts.map +1 -1
  55. package/dist/compiler/server/middleware.js +14 -2
  56. package/dist/compiler/server/middleware.test.js +263 -0
  57. package/dist/services/env-variables/index.js +1 -1
  58. package/package.json +6 -6
@@ -1,7 +1,9 @@
1
1
  import { assertIsLocale } from "./assert-is-locale.js";
2
- import { baseLocale, cookieName, strategy, TREE_SHAKE_COOKIE_STRATEGY_USED, TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED, TREE_SHAKE_URL_STRATEGY_USED, } from "./variables.js";
2
+ import { extractLocaleFromHeader } from "./extract-locale-from-header.js";
3
3
  import { extractLocaleFromUrl } from "./extract-locale-from-url.js";
4
4
  import { isLocale } from "./is-locale.js";
5
+ import { isCustomStrategy } from "./strategy.js";
6
+ import { baseLocale, cookieName, strategy, TREE_SHAKE_COOKIE_STRATEGY_USED, TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED, TREE_SHAKE_URL_STRATEGY_USED, } from "./variables.js";
5
7
  /**
6
8
  * Extracts a locale from a request.
7
9
  *
@@ -12,6 +14,9 @@ import { isLocale } from "./is-locale.js";
12
14
  * they are defined. If a strategy returns an invalid locale,
13
15
  * it will fall back to the next strategy.
14
16
  *
17
+ * Note: Custom server strategies are not supported in this synchronous version.
18
+ * Use `extractLocaleFromRequestAsync` if you need custom server strategies with async getLocale methods.
19
+ *
15
20
  * @example
16
21
  * const locale = extractLocaleFromRequest(request);
17
22
  *
@@ -33,10 +38,7 @@ export const extractLocaleFromRequest = (request) => {
33
38
  }
34
39
  else if (TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED &&
35
40
  strat === "preferredLanguage") {
36
- const acceptLanguageHeader = request.headers.get("accept-language");
37
- if (acceptLanguageHeader) {
38
- locale = negotiatePreferredLanguageFromHeader(acceptLanguageHeader);
39
- }
41
+ locale = extractLocaleFromHeader(request);
40
42
  }
41
43
  else if (strat === "globalVariable") {
42
44
  locale = _locale;
@@ -47,6 +49,11 @@ export const extractLocaleFromRequest = (request) => {
47
49
  else if (strat === "localStorage") {
48
50
  continue;
49
51
  }
52
+ else if (isCustomStrategy(strat)) {
53
+ // Custom strategies are not supported in sync version
54
+ // Use extractLocaleFromRequestAsync for custom server strategies
55
+ continue;
56
+ }
50
57
  if (locale !== undefined) {
51
58
  if (!isLocale(locale)) {
52
59
  locale = undefined;
@@ -58,34 +65,3 @@ export const extractLocaleFromRequest = (request) => {
58
65
  }
59
66
  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");
60
67
  };
61
- /**
62
- * Negotiates a preferred language from a header.
63
- *
64
- * @param {string} header - The header to negotiate from.
65
- * @returns {string|undefined} The negotiated preferred language.
66
- */
67
- function negotiatePreferredLanguageFromHeader(header) {
68
- // Parse language preferences with their q-values and base language codes
69
- const languages = header
70
- .split(",")
71
- .map((lang) => {
72
- const [tag, q = "1"] = lang.trim().split(";q=");
73
- // Get both the full tag and base language code
74
- const baseTag = tag?.split("-")[0]?.toLowerCase();
75
- return {
76
- fullTag: tag?.toLowerCase(),
77
- baseTag,
78
- q: Number(q),
79
- };
80
- })
81
- .sort((a, b) => b.q - a.q);
82
- for (const lang of languages) {
83
- if (isLocale(lang.fullTag)) {
84
- return lang.fullTag;
85
- }
86
- else if (isLocale(lang.baseTag)) {
87
- return lang.baseTag;
88
- }
89
- }
90
- return undefined;
91
- }
@@ -1,6 +1,6 @@
1
- import { test, expect } from "vitest";
2
- import { createParaglide } from "../create-paraglide.js";
3
1
  import { newProject } from "@inlang/sdk";
2
+ import { expect, test } from "vitest";
3
+ import { createParaglide } from "../create-paraglide.js";
4
4
  test("returns the locale from the cookie", async () => {
5
5
  const runtime = await createParaglide({
6
6
  blob: await newProject({
@@ -238,3 +238,85 @@ test("preferredLanguage precedence over url", async () => {
238
238
  const locale = runtime.extractLocaleFromRequest(request);
239
239
  expect(locale).toBe("de");
240
240
  });
241
+ test("sync version no longer supports custom strategies", async () => {
242
+ const runtime = await createParaglide({
243
+ blob: await newProject({
244
+ settings: {
245
+ baseLocale: "en",
246
+ locales: ["en", "fr", "de"],
247
+ },
248
+ }),
249
+ strategy: ["custom-header", "baseLocale"],
250
+ });
251
+ // Define a custom strategy that extracts locale from a custom header
252
+ runtime.defineCustomServerStrategy("custom-header", {
253
+ getLocale: (request) => request?.headers.get("X-Custom-Locale") ?? undefined,
254
+ });
255
+ const request = new Request("http://example.com", {
256
+ headers: {
257
+ "X-Custom-Locale": "fr",
258
+ },
259
+ });
260
+ // Sync version skips custom strategies and falls back to baseLocale
261
+ const locale = runtime.extractLocaleFromRequest(request);
262
+ expect(locale).toBe("en"); // baseLocale fallback
263
+ });
264
+ test("sync version skips custom strategy and falls back to next built-in strategy", async () => {
265
+ const runtime = await createParaglide({
266
+ blob: await newProject({
267
+ settings: {
268
+ baseLocale: "en",
269
+ locales: ["en", "fr"],
270
+ },
271
+ }),
272
+ strategy: ["custom-fallback", "baseLocale"],
273
+ });
274
+ runtime.defineCustomServerStrategy("custom-fallback", {
275
+ getLocale: () => undefined,
276
+ });
277
+ const request = new Request("http://example.com");
278
+ const locale = runtime.extractLocaleFromRequest(request);
279
+ expect(locale).toBe("en"); // Should fall back to baseLocale (skipping custom strategy)
280
+ });
281
+ test("sync version uses built-in strategies instead of custom strategies", async () => {
282
+ const runtime = await createParaglide({
283
+ blob: await newProject({
284
+ settings: {
285
+ baseLocale: "en",
286
+ locales: ["en", "fr", "de"],
287
+ },
288
+ }),
289
+ strategy: ["custom-priority", "cookie", "baseLocale"],
290
+ cookieName: "PARAGLIDE_LOCALE",
291
+ });
292
+ runtime.defineCustomServerStrategy("custom-priority", {
293
+ getLocale: () => "de",
294
+ });
295
+ const request = new Request("http://example.com", {
296
+ headers: {
297
+ cookie: "PARAGLIDE_LOCALE=fr", // Cookie has different locale
298
+ },
299
+ });
300
+ const locale = runtime.extractLocaleFromRequest(request);
301
+ expect(locale).toBe("fr"); // Should use cookie since custom strategy is skipped in sync version
302
+ });
303
+ test("sync version skips all custom strategies and uses baseLocale", async () => {
304
+ const runtime = await createParaglide({
305
+ blob: await newProject({
306
+ settings: {
307
+ baseLocale: "en",
308
+ locales: ["en", "fr", "de"],
309
+ },
310
+ }),
311
+ strategy: ["custom-first", "custom-second", "baseLocale"],
312
+ });
313
+ runtime.defineCustomServerStrategy("custom-first", {
314
+ getLocale: () => undefined,
315
+ });
316
+ runtime.defineCustomServerStrategy("custom-second", {
317
+ getLocale: () => "fr",
318
+ });
319
+ const request = new Request("http://example.com");
320
+ const locale = runtime.extractLocaleFromRequest(request);
321
+ expect(locale).toBe("en"); // Should skip all custom strategies and use baseLocale
322
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"get-locale.d.ts","sourceRoot":"","sources":["../../../src/compiler/runtime/get-locale.js"],"names":[],"mappings":"AA+BA;;;;;;;;;;;GAWG;AACH,sBAFU,MAAM,MAAM,CA8DpB;AA4BF;;;;;;;;;;;;;;GAcG;AACH,iCAFU,CAAC,EAAE,EAAE,MAAM,MAAM,KAAK,IAAI,CAIlC"}
1
+ {"version":3,"file":"get-locale.d.ts","sourceRoot":"","sources":["../../../src/compiler/runtime/get-locale.js"],"names":[],"mappings":"AAgCA;;;;;;;;;;;GAWG;AACH,sBAFU,MAAM,MAAM,CAyEpB;AAEF;;;;;;;;;;;;;;GAcG;AACH,iCAFU,CAAC,EAAE,EAAE,MAAM,MAAM,KAAK,IAAI,CAIlC"}
@@ -1,9 +1,10 @@
1
1
  import { assertIsLocale } from "./assert-is-locale.js";
2
- import { isLocale } from "./is-locale.js";
3
- import { baseLocale, strategy, serverAsyncLocalStorage, TREE_SHAKE_COOKIE_STRATEGY_USED, TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED, TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED, TREE_SHAKE_URL_STRATEGY_USED, TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED, localStorageKey, isServer, } from "./variables.js";
4
2
  import { extractLocaleFromCookie } from "./extract-locale-from-cookie.js";
3
+ import { extractLocaleFromNavigator } from "./extract-locale-from-navigator.js";
5
4
  import { extractLocaleFromUrl } from "./extract-locale-from-url.js";
6
5
  import { setLocale } from "./set-locale.js";
6
+ import { customClientStrategies, isCustomStrategy } from "./strategy.js";
7
+ import { baseLocale, isServer, localStorageKey, serverAsyncLocalStorage, strategy, TREE_SHAKE_COOKIE_STRATEGY_USED, TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED, TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED, TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED, TREE_SHAKE_URL_STRATEGY_USED, } from "./variables.js";
7
8
  /**
8
9
  * This is a fallback to get started with a custom
9
10
  * strategy and avoid type errors.
@@ -59,13 +60,25 @@ export let getLocale = () => {
59
60
  else if (TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED &&
60
61
  strat === "preferredLanguage" &&
61
62
  !isServer) {
62
- locale = negotiatePreferredLanguageFromNavigator();
63
+ locale = extractLocaleFromNavigator();
63
64
  }
64
65
  else if (TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED &&
65
66
  strat === "localStorage" &&
66
67
  !isServer) {
67
68
  locale = localStorage.getItem(localStorageKey) ?? undefined;
68
69
  }
70
+ else if (isCustomStrategy(strat) && customClientStrategies.has(strat)) {
71
+ const handler = customClientStrategies.get(strat);
72
+ if (handler) {
73
+ const result = handler.getLocale();
74
+ // Handle both sync and async results - skip async in sync getLocale
75
+ if (result instanceof Promise) {
76
+ // Can't await in sync function, skip async strategies
77
+ continue;
78
+ }
79
+ locale = result;
80
+ }
81
+ }
69
82
  // check if match, else continue loop
70
83
  if (locale !== undefined) {
71
84
  const asserted = assertIsLocale(locale);
@@ -80,29 +93,6 @@ export let getLocale = () => {
80
93
  }
81
94
  throw new Error("No locale found. Read the docs https://inlang.com/m/gerre34r/library-inlang-paraglideJs/errors#no-locale-found");
82
95
  };
83
- /**
84
- * Negotiates a preferred language from navigator.languages.
85
- *
86
- * @returns {string|undefined} The negotiated preferred language.
87
- */
88
- function negotiatePreferredLanguageFromNavigator() {
89
- if (!navigator?.languages?.length) {
90
- return undefined;
91
- }
92
- const languages = navigator.languages.map((lang) => ({
93
- fullTag: lang.toLowerCase(),
94
- baseTag: lang.split("-")[0]?.toLowerCase(),
95
- }));
96
- for (const lang of languages) {
97
- if (isLocale(lang.fullTag)) {
98
- return lang.fullTag;
99
- }
100
- else if (isLocale(lang.baseTag)) {
101
- return lang.baseTag;
102
- }
103
- }
104
- return undefined;
105
- }
106
96
  /**
107
97
  * Overwrite the \`getLocale()\` function.
108
98
  *
@@ -180,6 +180,159 @@ test("initially sets the locale after resolving it for the first time", async ()
180
180
  // First call to getLocale should resolve and set the locale
181
181
  expect(runtime.getLocale()).toBe("de");
182
182
  // Cookie should be set, proving that the locale was initially set
183
- expect(globalThis.document.cookie).toBe("PARAGLIDE_LOCALE=de; path=/; max-age=34560000; domain=example.com");
183
+ expect(globalThis.document.cookie).toBe("PARAGLIDE_LOCALE=de; path=/; max-age=34560000");
184
184
  expect(globalThis.window.location.href).toBe("https://example.com/de/page");
185
185
  });
186
+ test("returns locale from custom strategy", async () => {
187
+ const runtime = await createParaglide({
188
+ blob: await newProject({
189
+ settings: {
190
+ baseLocale: "en",
191
+ locales: ["en", "fr", "de"],
192
+ },
193
+ }),
194
+ strategy: ["custom-client", "baseLocale"],
195
+ isServer: "false",
196
+ });
197
+ runtime.defineCustomClientStrategy("custom-client", {
198
+ getLocale: () => "fr",
199
+ setLocale: () => { },
200
+ });
201
+ const locale = runtime.getLocale();
202
+ expect(locale).toBe("fr");
203
+ });
204
+ test("falls back to next strategy when custom strategy returns undefined", async () => {
205
+ const runtime = await createParaglide({
206
+ blob: await newProject({
207
+ settings: {
208
+ baseLocale: "en",
209
+ locales: ["en", "fr"],
210
+ },
211
+ }),
212
+ strategy: ["custom-undefined", "baseLocale"],
213
+ isServer: "false",
214
+ });
215
+ runtime.defineCustomClientStrategy("custom-undefined", {
216
+ getLocale: () => undefined,
217
+ setLocale: () => { },
218
+ });
219
+ const locale = runtime.getLocale();
220
+ expect(locale).toBe("en"); // Should fall back to baseLocale
221
+ });
222
+ test("throws error if custom strategy returns invalid locale", async () => {
223
+ const runtime = await createParaglide({
224
+ blob: await newProject({
225
+ settings: {
226
+ baseLocale: "en",
227
+ locales: ["en", "fr"],
228
+ },
229
+ }),
230
+ strategy: ["custom-invalid", "baseLocale"],
231
+ isServer: "false",
232
+ });
233
+ runtime.defineCustomClientStrategy("custom-invalid", {
234
+ getLocale: () => "invalid-locale",
235
+ setLocale: () => { },
236
+ });
237
+ expect(() => runtime.getLocale()).toThrow();
238
+ });
239
+ test("custom strategy takes precedence over built-in strategies in getLocale", async () => {
240
+ // @ts-expect-error - global variable definition
241
+ globalThis.document = { cookie: "PARAGLIDE_LOCALE=fr" };
242
+ const runtime = await createParaglide({
243
+ blob: await newProject({
244
+ settings: {
245
+ baseLocale: "en",
246
+ locales: ["en", "fr", "de"],
247
+ },
248
+ }),
249
+ strategy: ["custom-priority", "cookie", "baseLocale"],
250
+ cookieName: "PARAGLIDE_LOCALE",
251
+ isServer: "false",
252
+ });
253
+ runtime.defineCustomClientStrategy("custom-priority", {
254
+ getLocale: () => "de",
255
+ setLocale: () => { },
256
+ });
257
+ const locale = runtime.getLocale();
258
+ expect(locale).toBe("de"); // Should use custom strategy, not cookie
259
+ });
260
+ test("multiple custom strategies work in order in getLocale", async () => {
261
+ const runtime = await createParaglide({
262
+ blob: await newProject({
263
+ settings: {
264
+ baseLocale: "en",
265
+ locales: ["en", "fr", "de"],
266
+ },
267
+ }),
268
+ strategy: ["custom-first", "custom-second", "baseLocale"],
269
+ isServer: "false",
270
+ });
271
+ // Define two custom strategies
272
+ runtime.defineCustomClientStrategy("custom-first", {
273
+ getLocale: () => undefined, // This one returns undefined
274
+ setLocale: () => { },
275
+ });
276
+ runtime.defineCustomClientStrategy("custom-second", {
277
+ getLocale: () => "fr", // This one returns a locale
278
+ setLocale: () => { },
279
+ });
280
+ const locale = runtime.getLocale();
281
+ expect(locale).toBe("fr"); // Should use second custom strategy
282
+ });
283
+ test("custom strategy works with session storage simulation", async () => {
284
+ // Simulate a custom strategy that reads from session storage
285
+ let sessionData = "de";
286
+ const runtime = await createParaglide({
287
+ blob: await newProject({
288
+ settings: {
289
+ baseLocale: "en",
290
+ locales: ["en", "fr", "de"],
291
+ },
292
+ }),
293
+ strategy: ["custom-session", "baseLocale"],
294
+ isServer: "false",
295
+ });
296
+ runtime.defineCustomClientStrategy("custom-session", {
297
+ getLocale: () => sessionData,
298
+ setLocale: (locale) => {
299
+ sessionData = locale;
300
+ },
301
+ });
302
+ const locale = runtime.getLocale();
303
+ expect(locale).toBe("de");
304
+ });
305
+ test("custom strategy integrates with locale initialization", async () => {
306
+ // @ts-expect-error - global variable definition
307
+ globalThis.window = {};
308
+ // Define a custom strategy that tracks if it was called
309
+ let getLocaleCalled = false;
310
+ let setLocaleCalled = false;
311
+ const runtime = await createParaglide({
312
+ blob: await newProject({
313
+ settings: {
314
+ baseLocale: "en",
315
+ locales: ["en", "fr", "de"],
316
+ },
317
+ }),
318
+ strategy: ["custom-tracking"],
319
+ isServer: "false",
320
+ });
321
+ runtime.defineCustomClientStrategy("custom-tracking", {
322
+ getLocale: () => {
323
+ getLocaleCalled = true;
324
+ return "fr";
325
+ },
326
+ setLocale: () => {
327
+ setLocaleCalled = true;
328
+ },
329
+ });
330
+ // First call should trigger the custom strategy
331
+ const locale = runtime.getLocale();
332
+ expect(locale).toBe("fr");
333
+ expect(getLocaleCalled).toBe(true);
334
+ // Setting the locale should also call the custom strategy
335
+ runtime.setLocale("de");
336
+ // The locale should be set internally after first resolution
337
+ expect(setLocaleCalled).toBe(true);
338
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"set-locale.d.ts","sourceRoot":"","sources":["../../../src/compiler/runtime/set-locale.js"],"names":[],"mappings":"AAeA;;;;;;;;;;;;;;;GAeG;AACH,sBAFU,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,KAAK,IAAI,CAkFnE;AAgBK,uCAFI,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,QAIrC"}
1
+ {"version":3,"file":"set-locale.d.ts","sourceRoot":"","sources":["../../../src/compiler/runtime/set-locale.js"],"names":[],"mappings":"AAgBA;;;;;;;;;;;;;;;GAeG;AACH,sBAFU,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,KAAK,IAAI,CA8FnE;AAgBK,uCAFI,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,QAIrC"}
@@ -1,6 +1,7 @@
1
1
  import { getLocale } from "./get-locale.js";
2
2
  import { localizeUrl } from "./localize-url.js";
3
- import { cookieMaxAge, cookieName, cookieDomain, isServer, localStorageKey, strategy, TREE_SHAKE_COOKIE_STRATEGY_USED, TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED, TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED, TREE_SHAKE_URL_STRATEGY_USED, } from "./variables.js";
3
+ import { customClientStrategies, isCustomStrategy } from "./strategy.js";
4
+ import { cookieDomain, cookieMaxAge, cookieName, isServer, localStorageKey, strategy, TREE_SHAKE_COOKIE_STRATEGY_USED, TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED, TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED, TREE_SHAKE_URL_STRATEGY_USED, } from "./variables.js";
4
5
  /**
5
6
  * Set the locale.
6
7
  *
@@ -46,9 +47,11 @@ export let setLocale = (newLocale, options) => {
46
47
  typeof window === "undefined") {
47
48
  continue;
48
49
  }
49
- const domain = cookieDomain || window.location.hostname;
50
50
  // set the cookie
51
- document.cookie = `${cookieName}=${newLocale}; path=/; max-age=${cookieMaxAge}; domain=${domain}`;
51
+ const cookieString = `${cookieName}=${newLocale}; path=/; max-age=${cookieMaxAge}`;
52
+ document.cookie = cookieDomain
53
+ ? `${cookieString}; domain=${cookieDomain}`
54
+ : cookieString;
52
55
  }
53
56
  else if (strat === "baseLocale") {
54
57
  // nothing to be set here. baseLocale is only a fallback
@@ -75,6 +78,18 @@ export let setLocale = (newLocale, options) => {
75
78
  // set the localStorage
76
79
  localStorage.setItem(localStorageKey, newLocale);
77
80
  }
81
+ else if (isCustomStrategy(strat) && customClientStrategies.has(strat)) {
82
+ const handler = customClientStrategies.get(strat);
83
+ if (handler) {
84
+ const result = handler.setLocale(newLocale);
85
+ // Handle async setLocale - fire and forget
86
+ if (result instanceof Promise) {
87
+ result.catch((error) => {
88
+ console.warn(`Custom strategy "${strat}" setLocale failed:`, error);
89
+ });
90
+ }
91
+ }
92
+ }
78
93
  }
79
94
  if (!isServer &&
80
95
  optionsWithDefaults.reload &&
@@ -22,7 +22,7 @@ test("sets the cookie to a different locale", async () => {
22
22
  globalThis.document.cookie = "PARAGLIDE_LOCALE=en";
23
23
  runtime.setLocale("de");
24
24
  // set the locale
25
- expect(globalThis.document.cookie).toBe("PARAGLIDE_LOCALE=de; path=/; max-age=34560000; domain=example.com");
25
+ expect(globalThis.document.cookie).toBe("PARAGLIDE_LOCALE=de; path=/; max-age=34560000");
26
26
  // reloads the site if window is available
27
27
  expect(globalThis.window.location.reload).toBeCalled();
28
28
  });
@@ -121,7 +121,7 @@ test("sets the cookie when it's an empty string", async () => {
121
121
  /** @ts-expect-error - client side api */
122
122
  globalThis.document = { cookie: "" };
123
123
  runtime.setLocale("en");
124
- expect(globalThis.document.cookie).toBe("PARAGLIDE_LOCALE=en; path=/; max-age=34560000; domain=example.com");
124
+ expect(globalThis.document.cookie).toBe("PARAGLIDE_LOCALE=en; path=/; max-age=34560000");
125
125
  });
126
126
  test("when strategy precedes URL, it should set the locale and re-direct to the URL", async () => {
127
127
  const runtime = await createParaglide({
@@ -152,7 +152,7 @@ test("when strategy precedes URL, it should set the locale and re-direct to the
152
152
  // Cookie strategy should determine locale as French
153
153
  expect(runtime.getLocale()).toBe("fr");
154
154
  runtime.setLocale("en");
155
- expect(globalThis.document.cookie).toBe("PARAGLIDE_LOCALE=en; path=/; max-age=34560000; domain=example.com");
155
+ expect(globalThis.document.cookie).toBe("PARAGLIDE_LOCALE=en; path=/; max-age=34560000");
156
156
  expect(globalThis.window.location.href).toBe("https://example.com/en/some-path");
157
157
  });
158
158
  // https://github.com/opral/inlang-paraglide-js/issues/430
@@ -178,12 +178,12 @@ test("should not reload when setting locale to current locale", async () => {
178
178
  // Setting to the current locale (en)
179
179
  runtime.setLocale("en");
180
180
  // Cookie should remain unchanged
181
- expect(globalThis.document.cookie).toBe("PARAGLIDE_LOCALE=en; path=/; max-age=34560000; domain=example.com");
181
+ expect(globalThis.document.cookie).toBe("PARAGLIDE_LOCALE=en; path=/; max-age=34560000");
182
182
  // Should not trigger a reload
183
183
  expect(globalThis.window.location.reload).not.toBeCalled();
184
184
  // Setting to a different locale should still work
185
185
  runtime.setLocale("de");
186
- expect(globalThis.document.cookie).toBe("PARAGLIDE_LOCALE=de; path=/; max-age=34560000; domain=example.com");
186
+ expect(globalThis.document.cookie).toBe("PARAGLIDE_LOCALE=de; path=/; max-age=34560000");
187
187
  expect(globalThis.window.location.reload).toBeCalled();
188
188
  });
189
189
  test("sets the locale to localStorage", async () => {
@@ -249,5 +249,145 @@ test("should set locale in all configured storage mechanisms regardless of which
249
249
  // Verify that all storage mechanisms are updated
250
250
  expect(globalThis.window.location.href).toBe("https://example.com/fr/page");
251
251
  expect(globalThis.localStorage.setItem).toHaveBeenCalledWith("PARAGLIDE_LOCALE", "fr");
252
- expect(globalThis.document.cookie).toBe("PARAGLIDE_LOCALE=fr; path=/; max-age=34560000; domain=example.com");
252
+ expect(globalThis.document.cookie).toBe("PARAGLIDE_LOCALE=fr; path=/; max-age=34560000");
253
+ });
254
+ test("calls setLocale on custom strategy", async () => {
255
+ let customLocale = "en";
256
+ let setLocaleCalled = false;
257
+ globalThis.window = {
258
+ location: { reload: vi.fn() },
259
+ };
260
+ const runtime = await createParaglide({
261
+ blob: await newProject({
262
+ settings: {
263
+ baseLocale: "en",
264
+ locales: ["en", "fr", "de"],
265
+ },
266
+ }),
267
+ strategy: ["custom-setter", "baseLocale"],
268
+ isServer: "false",
269
+ });
270
+ runtime.defineCustomClientStrategy("custom-setter", {
271
+ getLocale: () => customLocale,
272
+ setLocale: (locale) => {
273
+ customLocale = locale;
274
+ setLocaleCalled = true;
275
+ },
276
+ });
277
+ runtime.setLocale("fr");
278
+ expect(setLocaleCalled).toBe(true);
279
+ expect(customLocale).toBe("fr");
280
+ expect(globalThis.window.location.reload).toHaveBeenCalled();
281
+ });
282
+ test("calls setLocale on multiple custom strategies", async () => {
283
+ let customLocale1 = "en";
284
+ let customLocale2 = "en";
285
+ let setLocaleCalled1 = false;
286
+ let setLocaleCalled2 = false;
287
+ globalThis.window = {
288
+ location: { reload: vi.fn() },
289
+ };
290
+ const runtime = await createParaglide({
291
+ blob: await newProject({
292
+ settings: {
293
+ baseLocale: "en",
294
+ locales: ["en", "fr", "de"],
295
+ },
296
+ }),
297
+ strategy: ["custom-multi1", "custom-multi2", "baseLocale"],
298
+ isServer: "false",
299
+ });
300
+ runtime.defineCustomClientStrategy("custom-multi1", {
301
+ getLocale: () => customLocale1,
302
+ setLocale: (locale) => {
303
+ customLocale1 = locale;
304
+ setLocaleCalled1 = true;
305
+ },
306
+ });
307
+ runtime.defineCustomClientStrategy("custom-multi2", {
308
+ getLocale: () => customLocale2,
309
+ setLocale: (locale) => {
310
+ customLocale2 = locale;
311
+ setLocaleCalled2 = true;
312
+ },
313
+ });
314
+ runtime.setLocale("de");
315
+ expect(setLocaleCalled1).toBe(true);
316
+ expect(setLocaleCalled2).toBe(true);
317
+ expect(customLocale1).toBe("de");
318
+ expect(customLocale2).toBe("de");
319
+ });
320
+ test("custom strategy setLocale works with cookie and localStorage", async () => {
321
+ let customData = "en";
322
+ globalThis.document = { cookie: "" };
323
+ globalThis.localStorage = {
324
+ setItem: vi.fn(),
325
+ getItem: () => null,
326
+ };
327
+ globalThis.window = {
328
+ location: {
329
+ hostname: "example.com",
330
+ reload: vi.fn(),
331
+ },
332
+ };
333
+ const runtime = await createParaglide({
334
+ blob: await newProject({
335
+ settings: {
336
+ baseLocale: "en",
337
+ locales: ["en", "fr", "de"],
338
+ },
339
+ }),
340
+ strategy: ["custom-api", "localStorage", "cookie", "baseLocale"],
341
+ cookieName: "PARAGLIDE_LOCALE",
342
+ isServer: "false",
343
+ });
344
+ runtime.defineCustomClientStrategy("custom-api", {
345
+ getLocale: () => customData,
346
+ setLocale: (locale) => {
347
+ customData = locale;
348
+ },
349
+ });
350
+ runtime.setLocale("fr");
351
+ expect(customData).toBe("fr");
352
+ expect(globalThis.localStorage.setItem).toHaveBeenCalledWith("PARAGLIDE_LOCALE", "fr");
353
+ expect(globalThis.document.cookie).toBe("PARAGLIDE_LOCALE=fr; path=/; max-age=34560000");
354
+ });
355
+ test("custom strategy setLocale integrates with URL strategy", async () => {
356
+ let customStoredLocale = "en";
357
+ globalThis.window = {
358
+ location: {
359
+ hostname: "example.com",
360
+ href: "https://example.com/en/page",
361
+ reload: vi.fn(),
362
+ },
363
+ };
364
+ const runtime = await createParaglide({
365
+ blob: await newProject({
366
+ settings: {
367
+ baseLocale: "en",
368
+ locales: ["en", "fr", "de"],
369
+ },
370
+ }),
371
+ strategy: ["url", "custom-urlIntegration", "baseLocale"],
372
+ urlPatterns: [
373
+ {
374
+ pattern: "https://example.com/:locale/:path*",
375
+ localized: [
376
+ ["en", "https://example.com/en/:path*"],
377
+ ["fr", "https://example.com/fr/:path*"],
378
+ ["de", "https://example.com/de/:path*"],
379
+ ],
380
+ },
381
+ ],
382
+ isServer: "false",
383
+ });
384
+ runtime.defineCustomClientStrategy("custom-urlIntegration", {
385
+ getLocale: () => customStoredLocale,
386
+ setLocale: (locale) => {
387
+ customStoredLocale = locale;
388
+ },
389
+ });
390
+ runtime.setLocale("de");
391
+ expect(globalThis.window.location.href).toBe("https://example.com/de/page");
392
+ expect(customStoredLocale).toBe("de");
253
393
  });