@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.
- package/dist/bundler-plugins/vite.d.ts +1 -1
- package/dist/bundler-plugins/vite.d.ts.map +1 -1
- package/dist/compiler/compile-bundle.d.ts +2 -1
- package/dist/compiler/compile-bundle.d.ts.map +1 -1
- package/dist/compiler/compile-bundle.js +4 -3
- package/dist/compiler/compile-bundle.test.js +64 -0
- package/dist/compiler/compile-project.d.ts.map +1 -1
- package/dist/compiler/compile-project.js +1 -0
- package/dist/compiler/compiler-options.d.ts +17 -3
- package/dist/compiler/compiler-options.d.ts.map +1 -1
- package/dist/compiler/runtime/create-runtime.d.ts.map +1 -1
- package/dist/compiler/runtime/create-runtime.js +8 -0
- package/dist/compiler/runtime/extract-locale-from-cookie.d.ts +1 -1
- package/dist/compiler/runtime/extract-locale-from-cookie.js +1 -1
- package/dist/compiler/runtime/extract-locale-from-header.d.ts +2 -0
- package/dist/compiler/runtime/extract-locale-from-header.d.ts.map +1 -0
- package/dist/compiler/runtime/extract-locale-from-header.js +43 -0
- package/dist/compiler/runtime/extract-locale-from-header.test.d.ts +2 -0
- package/dist/compiler/runtime/extract-locale-from-header.test.d.ts.map +1 -0
- package/dist/compiler/runtime/extract-locale-from-header.test.js +51 -0
- package/dist/compiler/runtime/extract-locale-from-navigator.d.ts +2 -0
- package/dist/compiler/runtime/extract-locale-from-navigator.d.ts.map +1 -0
- package/dist/compiler/runtime/extract-locale-from-navigator.js +31 -0
- package/dist/compiler/runtime/extract-locale-from-navigator.test.d.ts +2 -0
- package/dist/compiler/runtime/extract-locale-from-navigator.test.d.ts.map +1 -0
- package/dist/compiler/runtime/extract-locale-from-navigator.test.js +29 -0
- package/dist/compiler/runtime/extract-locale-from-request-async.d.ts +31 -0
- package/dist/compiler/runtime/extract-locale-from-request-async.d.ts.map +1 -0
- package/dist/compiler/runtime/extract-locale-from-request-async.js +55 -0
- package/dist/compiler/runtime/extract-locale-from-request-async.test.d.ts +2 -0
- package/dist/compiler/runtime/extract-locale-from-request-async.test.d.ts.map +1 -0
- package/dist/compiler/runtime/extract-locale-from-request-async.test.js +58 -0
- package/dist/compiler/runtime/extract-locale-from-request.d.ts +3 -0
- package/dist/compiler/runtime/extract-locale-from-request.d.ts.map +1 -1
- package/dist/compiler/runtime/extract-locale-from-request.js +12 -36
- package/dist/compiler/runtime/extract-locale-from-request.test.js +84 -2
- package/dist/compiler/runtime/get-locale.d.ts.map +1 -1
- package/dist/compiler/runtime/get-locale.js +16 -26
- package/dist/compiler/runtime/get-locale.test.js +154 -1
- package/dist/compiler/runtime/set-locale.d.ts.map +1 -1
- package/dist/compiler/runtime/set-locale.js +18 -3
- package/dist/compiler/runtime/set-locale.test.js +146 -6
- package/dist/compiler/runtime/strategy.d.ts +60 -0
- package/dist/compiler/runtime/strategy.d.ts.map +1 -0
- package/dist/compiler/runtime/strategy.js +60 -0
- package/dist/compiler/runtime/strategy.test.d.ts +2 -0
- package/dist/compiler/runtime/strategy.test.d.ts.map +1 -0
- package/dist/compiler/runtime/strategy.test.js +94 -0
- package/dist/compiler/runtime/type.d.ts +5 -0
- package/dist/compiler/runtime/type.d.ts.map +1 -1
- package/dist/compiler/runtime/variables.d.ts +2 -2
- package/dist/compiler/runtime/variables.d.ts.map +1 -1
- package/dist/compiler/runtime/variables.js +1 -1
- package/dist/compiler/server/middleware.d.ts.map +1 -1
- package/dist/compiler/server/middleware.js +14 -2
- package/dist/compiler/server/middleware.test.js +263 -0
- package/dist/services/env-variables/index.js +1 -1
- package/package.json +6 -6
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { assertIsLocale } from "./assert-is-locale.js";
|
|
2
|
-
import {
|
|
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
|
-
|
|
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":"
|
|
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 =
|
|
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
|
|
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":"
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
});
|