@inlang/paraglide-js 2.0.13 → 2.1.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/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 +2 -0
- 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 +6 -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.d.ts.map +1 -1
- package/dist/compiler/runtime/extract-locale-from-request.js +8 -36
- package/dist/compiler/runtime/extract-locale-from-request.test.js +83 -2
- package/dist/compiler/runtime/get-locale.d.ts.map +1 -1
- package/dist/compiler/runtime/get-locale.js +8 -26
- package/dist/compiler/runtime/get-locale.test.js +153 -0
- package/dist/compiler/runtime/set-locale.d.ts.map +1 -1
- package/dist/compiler/runtime/set-locale.js +6 -1
- package/dist/compiler/runtime/set-locale.test.js +140 -0
- 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 +62 -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 +4 -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 +13 -1
- package/dist/compiler/server/middleware.test.js +73 -0
- package/dist/services/env-variables/index.js +1 -1
- package/package.json +3 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Bundle, BundleNested, Message } from "@inlang/sdk";
|
|
1
|
+
import type { Bundle, BundleNested, Message, ProjectSettings } from "@inlang/sdk";
|
|
2
2
|
import type { Compiled } from "./types.js";
|
|
3
3
|
export type CompiledBundleWithMessages = {
|
|
4
4
|
/** The compilation result for the bundle index */
|
|
@@ -15,5 +15,6 @@ export declare const compileBundle: (args: {
|
|
|
15
15
|
bundle: BundleNested;
|
|
16
16
|
fallbackMap: Record<string, string | undefined>;
|
|
17
17
|
messageReferenceExpression: (locale: string, bundleId: string) => string;
|
|
18
|
+
settings?: ProjectSettings;
|
|
18
19
|
}) => CompiledBundleWithMessages;
|
|
19
20
|
//# sourceMappingURL=compile-bundle.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile-bundle.d.ts","sourceRoot":"","sources":["../../src/compiler/compile-bundle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"compile-bundle.d.ts","sourceRoot":"","sources":["../../src/compiler/compile-bundle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,MAAM,EACN,YAAY,EACZ,OAAO,EACP,eAAe,EACf,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAK3C,MAAM,MAAM,0BAA0B,GAAG;IACxC,kDAAkD;IAClD,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzB,gDAAgD;IAChD,QAAQ,EAAE;QACT,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;KACpC,CAAC;CACF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM;IACnC,MAAM,EAAE,YAAY,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAChD,0BAA0B,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IACzE,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC3B,KAAG,0BA2BH,CAAC"}
|
|
@@ -20,6 +20,7 @@ export const compileBundle = (args) => {
|
|
|
20
20
|
bundle: args.bundle,
|
|
21
21
|
availableLocales: Object.keys(args.fallbackMap),
|
|
22
22
|
messageReferenceExpression: args.messageReferenceExpression,
|
|
23
|
+
settings: args.settings,
|
|
23
24
|
}),
|
|
24
25
|
messages: compiledMessages,
|
|
25
26
|
};
|
|
@@ -29,6 +30,7 @@ const compileBundleFunction = (args) => {
|
|
|
29
30
|
const hasInputs = inputs.length > 0;
|
|
30
31
|
const safeBundleId = toSafeModuleId(args.bundle.id);
|
|
31
32
|
const isSafeBundleId = safeBundleId === args.bundle.id;
|
|
33
|
+
const isFullyTranslated = args.availableLocales.length === args.settings?.locales.length;
|
|
32
34
|
let code = `/**
|
|
33
35
|
* This function has been compiled by [Paraglide JS](https://inlang.com/m/gerre34r).
|
|
34
36
|
*
|
|
@@ -46,9 +48,8 @@ ${isSafeBundleId ? "export " : ""}const ${safeBundleId} = (inputs${hasInputs ? "
|
|
|
46
48
|
const locale = options.locale ?? getLocale()
|
|
47
49
|
trackMessageCall("${safeBundleId}", locale)
|
|
48
50
|
${args.availableLocales
|
|
49
|
-
.map((locale, index) => `${index > 0 ? " " : ""}if (locale === "${locale}") return ${args.messageReferenceExpression(locale, args.bundle.id)}(inputs)`)
|
|
50
|
-
.join("\n")}
|
|
51
|
-
return "${args.bundle.id}"
|
|
51
|
+
.map((locale, index) => `${index > 0 ? " " : ""}${!isFullyTranslated || index < args.availableLocales.length - 1 ? `if (locale === "${locale}") ` : ""}return ${args.messageReferenceExpression(locale, args.bundle.id)}(inputs)`)
|
|
52
|
+
.join("\n")}${!isFullyTranslated ? `\n return "${args.bundle.id}"` : ""}
|
|
52
53
|
};`;
|
|
53
54
|
if (isSafeBundleId === false) {
|
|
54
55
|
code += `\nexport { ${safeBundleId} as "${escapeForDoubleQuoteString(args.bundle.id)}" }`;
|
|
@@ -35,6 +35,70 @@ test("compiles to jsdoc", async () => {
|
|
|
35
35
|
},
|
|
36
36
|
bundle: mockBundle,
|
|
37
37
|
messageReferenceExpression: (locale) => `${toSafeModuleId(locale)}.blue_moon_bottle`,
|
|
38
|
+
settings: {
|
|
39
|
+
locales: ["en", "en-US"],
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
expect(result.bundle.code).toMatchInlineSnapshot(`"/**
|
|
43
|
+
* This function has been compiled by [Paraglide JS](https://inlang.com/m/gerre34r).
|
|
44
|
+
*
|
|
45
|
+
* - Changing this function will be over-written by the next build.
|
|
46
|
+
*
|
|
47
|
+
* - If you want to change the translations, you can either edit the source files e.g. \`en.json\`, or
|
|
48
|
+
* use another inlang app like [Fink](https://inlang.com/m/tdozzpar) or the [VSCode extension Sherlock](https://inlang.com/m/r7kp499g).
|
|
49
|
+
*
|
|
50
|
+
* @param {{ age: NonNullable<unknown> }} inputs
|
|
51
|
+
* @param {{ locale?: "en" | "en-US" }} options
|
|
52
|
+
* @returns {string}
|
|
53
|
+
*/
|
|
54
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
55
|
+
export const blue_moon_bottle = (inputs, options = {}) => {
|
|
56
|
+
if (experimentalMiddlewareLocaleSplitting && isServer === false) {
|
|
57
|
+
return /** @type {any} */ (globalThis).__paraglide_ssr.blue_moon_bottle(inputs)
|
|
58
|
+
}
|
|
59
|
+
const locale = options.locale ?? getLocale()
|
|
60
|
+
trackMessageCall("blue_moon_bottle", locale)
|
|
61
|
+
if (locale === "en") return en.blue_moon_bottle(inputs)
|
|
62
|
+
return en_us2.blue_moon_bottle(inputs)
|
|
63
|
+
};"`);
|
|
64
|
+
});
|
|
65
|
+
test("compiles to jsdoc with missing translation", async () => {
|
|
66
|
+
const mockBundle = {
|
|
67
|
+
id: "blue_moon_bottle",
|
|
68
|
+
declarations: [{ type: "input-variable", name: "age" }],
|
|
69
|
+
messages: [
|
|
70
|
+
{
|
|
71
|
+
id: "message-id",
|
|
72
|
+
bundleId: "blue_moon_bottle",
|
|
73
|
+
locale: "en",
|
|
74
|
+
selectors: [],
|
|
75
|
+
variants: [
|
|
76
|
+
{
|
|
77
|
+
id: "1",
|
|
78
|
+
messageId: "message-id",
|
|
79
|
+
matches: [],
|
|
80
|
+
pattern: [
|
|
81
|
+
{ type: "text", value: "Hello" },
|
|
82
|
+
{
|
|
83
|
+
type: "expression",
|
|
84
|
+
arg: { type: "variable-reference", name: "age" },
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
const result = compileBundle({
|
|
93
|
+
fallbackMap: {
|
|
94
|
+
en: "en",
|
|
95
|
+
"en-US": "en",
|
|
96
|
+
},
|
|
97
|
+
bundle: mockBundle,
|
|
98
|
+
messageReferenceExpression: (locale) => `${toSafeModuleId(locale)}.blue_moon_bottle`,
|
|
99
|
+
settings: {
|
|
100
|
+
locales: ["en", "en-US", "fr"],
|
|
101
|
+
},
|
|
38
102
|
});
|
|
39
103
|
expect(result.bundle.code).toMatchInlineSnapshot(`"/**
|
|
40
104
|
* This function has been compiled by [Paraglide JS](https://inlang.com/m/gerre34r).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile-project.d.ts","sourceRoot":"","sources":["../../src/compiler/compile-project.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAIrE,OAAO,EAEN,KAAK,eAAe,EACpB,MAAM,uBAAuB,CAAC;AAU/B;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAAU,MAAM;IAC1C,OAAO,EAAE,aAAa,CAAC;IACvB,eAAe,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,GAAG,SAAS,GAAG,QAAQ,CAAC,CAAC;CACrE,KAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"compile-project.d.ts","sourceRoot":"","sources":["../../src/compiler/compile-project.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAIrE,OAAO,EAEN,KAAK,eAAe,EACpB,MAAM,uBAAuB,CAAC;AAU/B;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAAU,MAAM;IAC1C,OAAO,EAAE,aAAa,CAAC;IACvB,eAAe,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,GAAG,SAAS,GAAG,QAAQ,CAAC,CAAC;CACrE,KAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAuEjC,CAAC;AAEF,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAC9C,OAAO,EAAE,CAAC,EAAE,EACZ,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,GACpB,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,CAY1B"}
|
|
@@ -52,6 +52,8 @@ export type CompilerOptions = {
|
|
|
52
52
|
* server-side takes the globalVariable (because cookie is unavailable),
|
|
53
53
|
* whereas both fallback to the base locale if not available.
|
|
54
54
|
*
|
|
55
|
+
* Custom strategies with the pattern `custom-[A-Za-z0-9]+` are supported.
|
|
56
|
+
*
|
|
55
57
|
* @default ["cookie", "globalVariable", "baseLocale"]
|
|
56
58
|
*/
|
|
57
59
|
strategy?: Runtime["strategy"];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compiler-options.d.ts","sourceRoot":"","sources":["../../src/compiler/compiler-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;CAcU,CAAC;AAE9C,MAAM,MAAM,eAAe,GAAG;IAC7B;;;;;;;;;;OAUG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;;;;;OAUG;IACH,MAAM,EAAE,MAAM,CAAC;IACf
|
|
1
|
+
{"version":3,"file":"compiler-options.d.ts","sourceRoot":"","sources":["../../src/compiler/compiler-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;CAcU,CAAC;AAE9C,MAAM,MAAM,eAAe,GAAG;IAC7B;;;;;;;;;;OAUG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;;;;;OAUG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/B;;;;;;;;;;;;;;OAcG;IACH,qCAAqC,CAAC,EAAE,OAAO,CAAC;IAChD;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC;;;;;;;;;;;;OAYG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IACrC;;;;OAIG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC;;;;;;;;;OASG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiDG;IACH,eAAe,CAAC,EAAE,gBAAgB,GAAG,iBAAiB,CAAC;IACvD;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;;OAIG;IACH,EAAE,CAAC,EAAE,GAAG,CAAC;CACT,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-runtime.d.ts","sourceRoot":"","sources":["../../../src/compiler/runtime/create-runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,EAAE;QAChB,QAAQ,EAAE,WAAW,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC;QACnD,UAAU,EAAE,WAAW,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC;QACvD,YAAY,EAAE,WAAW,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC,CAAC;QAC3D,YAAY,EAAE,eAAe,CAAC,cAAc,CAAC,CAAC;QAC9C,WAAW,CAAC,EAAE,eAAe,CAAC,aAAa,CAAC,CAAC;QAC7C,qCAAqC,EAAE,eAAe,CAAC,uCAAuC,CAAC,CAAC;QAChG,QAAQ,EAAE,eAAe,CAAC,UAAU,CAAC,CAAC;QACtC,eAAe,EAAE,eAAe,CAAC,iBAAiB,CAAC,CAAC;QACpD,wBAAwB,EAAE,WAAW,CACpC,eAAe,CAAC,0BAA0B,CAAC,CAC3C,CAAC;KACF,CAAC;CACF,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"create-runtime.d.ts","sourceRoot":"","sources":["../../../src/compiler/runtime/create-runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,EAAE;QAChB,QAAQ,EAAE,WAAW,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC;QACnD,UAAU,EAAE,WAAW,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC;QACvD,YAAY,EAAE,WAAW,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC,CAAC;QAC3D,YAAY,EAAE,eAAe,CAAC,cAAc,CAAC,CAAC;QAC9C,WAAW,CAAC,EAAE,eAAe,CAAC,aAAa,CAAC,CAAC;QAC7C,qCAAqC,EAAE,eAAe,CAAC,uCAAuC,CAAC,CAAC;QAChG,QAAQ,EAAE,eAAe,CAAC,UAAU,CAAC,CAAC;QACtC,eAAe,EAAE,eAAe,CAAC,iBAAiB,CAAC,CAAC;QACpD,wBAAwB,EAAE,WAAW,CACpC,eAAe,CAAC,0BAA0B,CAAC,CAC3C,CAAC;KACF,CAAC;CACF,GAAG,MAAM,CAwIT"}
|
|
@@ -64,6 +64,10 @@ ${injectCode("./extract-locale-from-request.js")}
|
|
|
64
64
|
|
|
65
65
|
${injectCode("./extract-locale-from-cookie.js")}
|
|
66
66
|
|
|
67
|
+
${injectCode("./extract-locale-from-header.js")}
|
|
68
|
+
|
|
69
|
+
${injectCode("./extract-locale-from-navigator.js")}
|
|
70
|
+
|
|
67
71
|
${injectCode("./extract-locale-from-url.js")}
|
|
68
72
|
|
|
69
73
|
${injectCode("./localize-url.js")}
|
|
@@ -74,6 +78,8 @@ ${injectCode("./track-message-call.js")}
|
|
|
74
78
|
|
|
75
79
|
${injectCode("./generate-static-localized-urls.js")}
|
|
76
80
|
|
|
81
|
+
${injectCode("./strategy.js")}
|
|
82
|
+
|
|
77
83
|
// ------ TYPES ------
|
|
78
84
|
|
|
79
85
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Extracts a cookie from the document.
|
|
3
3
|
*
|
|
4
|
-
* Will return undefined if the
|
|
4
|
+
* Will return undefined if the document is not available or if the cookie is not set.
|
|
5
5
|
* The `document` object is not available in server-side rendering, so this function should not be called in that context.
|
|
6
6
|
*
|
|
7
7
|
* @returns {string | undefined}
|
|
@@ -3,7 +3,7 @@ import { cookieName } from "./variables.js";
|
|
|
3
3
|
/**
|
|
4
4
|
* Extracts a cookie from the document.
|
|
5
5
|
*
|
|
6
|
-
* Will return undefined if the
|
|
6
|
+
* Will return undefined if the document is not available or if the cookie is not set.
|
|
7
7
|
* The `document` object is not available in server-side rendering, so this function should not be called in that context.
|
|
8
8
|
*
|
|
9
9
|
* @returns {string | undefined}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-locale-from-header.d.ts","sourceRoot":"","sources":["../../../src/compiler/runtime/extract-locale-from-header.js"],"names":[],"mappings":"AAWU,iDAAU,OAAO,GAAK,MAAM,CAAA"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { isLocale } from "./is-locale.js";
|
|
2
|
+
/**
|
|
3
|
+
* Extracts a locale from the accept-language header.
|
|
4
|
+
*
|
|
5
|
+
* Use the function on the server to extract the locale
|
|
6
|
+
* from the accept-language header that is sent by the client.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* const locale = extractLocaleFromHeader(request);
|
|
10
|
+
*
|
|
11
|
+
* @type {(request: Request) => Locale}
|
|
12
|
+
* @param {Request} request - The request object to extract the locale from.
|
|
13
|
+
* @returns {string|undefined} The negotiated preferred language.
|
|
14
|
+
*/
|
|
15
|
+
export function extractLocaleFromHeader(request) {
|
|
16
|
+
const acceptLanguageHeader = request.headers.get("accept-language");
|
|
17
|
+
if (acceptLanguageHeader) {
|
|
18
|
+
// Parse language preferences with their q-values and base language codes
|
|
19
|
+
const languages = acceptLanguageHeader
|
|
20
|
+
.split(",")
|
|
21
|
+
.map((lang) => {
|
|
22
|
+
const [tag, q = "1"] = lang.trim().split(";q=");
|
|
23
|
+
// Get both the full tag and base language code
|
|
24
|
+
const baseTag = tag?.split("-")[0]?.toLowerCase();
|
|
25
|
+
return {
|
|
26
|
+
fullTag: tag?.toLowerCase(),
|
|
27
|
+
baseTag,
|
|
28
|
+
q: Number(q),
|
|
29
|
+
};
|
|
30
|
+
})
|
|
31
|
+
.sort((a, b) => b.q - a.q);
|
|
32
|
+
for (const lang of languages) {
|
|
33
|
+
if (isLocale(lang.fullTag)) {
|
|
34
|
+
return lang.fullTag;
|
|
35
|
+
}
|
|
36
|
+
else if (isLocale(lang.baseTag)) {
|
|
37
|
+
return lang.baseTag;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-locale-from-header.test.d.ts","sourceRoot":"","sources":["../../../src/compiler/runtime/extract-locale-from-header.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { test, expect } from "vitest";
|
|
2
|
+
import { createParaglide } from "../create-paraglide.js";
|
|
3
|
+
import { newProject } from "@inlang/sdk";
|
|
4
|
+
test("returns the preferred locale from Accept-Language header", async () => {
|
|
5
|
+
const runtime = await createParaglide({
|
|
6
|
+
blob: await newProject({
|
|
7
|
+
settings: {
|
|
8
|
+
baseLocale: "en",
|
|
9
|
+
locales: ["en", "fr", "de"],
|
|
10
|
+
},
|
|
11
|
+
}),
|
|
12
|
+
});
|
|
13
|
+
// simple direct match
|
|
14
|
+
const request = new Request("http://example.com", {
|
|
15
|
+
headers: {
|
|
16
|
+
"Accept-Language": "en-US;q=0.8,fr;q=0.9,de;q=0.6",
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
const locale = runtime.extractLocaleFromHeader(request);
|
|
20
|
+
expect(locale).toBe("fr");
|
|
21
|
+
// language tag match
|
|
22
|
+
const request2 = new Request("http://example.com", {
|
|
23
|
+
headers: {
|
|
24
|
+
"Accept-Language": "en-US;q=0.8,nl;q=0.9,de;q=0.7",
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
const locale2 = runtime.extractLocaleFromHeader(request2);
|
|
28
|
+
// Since "nl" isn't in our locales, and "de" has the highest q-value after "nl"
|
|
29
|
+
expect(locale2).toBe("en");
|
|
30
|
+
// no match
|
|
31
|
+
const request3 = new Request("http://example.com", {
|
|
32
|
+
headers: {
|
|
33
|
+
"Accept-Language": "nl;q=0.9,es;q=0.6",
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
const locale3 = runtime.extractLocaleFromHeader(request3);
|
|
37
|
+
expect(locale3).toBeUndefined();
|
|
38
|
+
});
|
|
39
|
+
test("returns undefined if no Accept-Language header is present", async () => {
|
|
40
|
+
const runtime = await createParaglide({
|
|
41
|
+
blob: await newProject({
|
|
42
|
+
settings: {
|
|
43
|
+
baseLocale: "en",
|
|
44
|
+
locales: ["en", "fr", "de"],
|
|
45
|
+
},
|
|
46
|
+
}),
|
|
47
|
+
});
|
|
48
|
+
const request = new Request("http://example.com");
|
|
49
|
+
const locale = runtime.extractLocaleFromHeader(request);
|
|
50
|
+
expect(locale).toBeUndefined();
|
|
51
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-locale-from-navigator.d.ts","sourceRoot":"","sources":["../../../src/compiler/runtime/extract-locale-from-navigator.js"],"names":[],"mappings":"AAWU,8CAAM,MAAM,GAAG,SAAS,CAAA"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { isLocale } from "./is-locale.js";
|
|
2
|
+
/**
|
|
3
|
+
* Negotiates a preferred language from navigator.languages.
|
|
4
|
+
*
|
|
5
|
+
* Use the function on the client to extract the locale
|
|
6
|
+
* from the navigator.languages array.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* const locale = extractLocaleFromNavigator();
|
|
10
|
+
*
|
|
11
|
+
* @type {() => Locale | undefined}
|
|
12
|
+
* @returns {string | undefined}
|
|
13
|
+
*/
|
|
14
|
+
export function extractLocaleFromNavigator() {
|
|
15
|
+
if (!navigator?.languages?.length) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
const languages = navigator.languages.map((lang) => ({
|
|
19
|
+
fullTag: lang.toLowerCase(),
|
|
20
|
+
baseTag: lang.split("-")[0]?.toLowerCase(),
|
|
21
|
+
}));
|
|
22
|
+
for (const lang of languages) {
|
|
23
|
+
if (isLocale(lang.fullTag)) {
|
|
24
|
+
return lang.fullTag;
|
|
25
|
+
}
|
|
26
|
+
else if (isLocale(lang.baseTag)) {
|
|
27
|
+
return lang.baseTag;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-locale-from-navigator.test.d.ts","sourceRoot":"","sources":["../../../src/compiler/runtime/extract-locale-from-navigator.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { newProject } from "@inlang/sdk";
|
|
2
|
+
import { expect, test } from "vitest";
|
|
3
|
+
import { createParaglide } from "../create-paraglide.js";
|
|
4
|
+
test("returns the preferred locale from navigator.languages", async () => {
|
|
5
|
+
const originalNavigator = globalThis.navigator;
|
|
6
|
+
const runtime = await createParaglide({
|
|
7
|
+
blob: await newProject({
|
|
8
|
+
settings: {
|
|
9
|
+
baseLocale: "en",
|
|
10
|
+
locales: ["en", "fr", "de"],
|
|
11
|
+
},
|
|
12
|
+
}),
|
|
13
|
+
isServer: "false",
|
|
14
|
+
strategy: ["preferredLanguage"],
|
|
15
|
+
});
|
|
16
|
+
// Mock navigator.languages
|
|
17
|
+
Object.defineProperty(globalThis, "navigator", {
|
|
18
|
+
value: {
|
|
19
|
+
languages: ["fr-FR", "en-US", "de"],
|
|
20
|
+
},
|
|
21
|
+
configurable: true,
|
|
22
|
+
});
|
|
23
|
+
expect(runtime.getLocale()).toBe("fr");
|
|
24
|
+
// Restore original navigator
|
|
25
|
+
Object.defineProperty(globalThis, "navigator", {
|
|
26
|
+
value: originalNavigator,
|
|
27
|
+
configurable: true,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract-locale-from-request.d.ts","sourceRoot":"","sources":["../../../src/compiler/runtime/extract-locale-from-request.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"extract-locale-from-request.d.ts","sourceRoot":"","sources":["../../../src/compiler/runtime/extract-locale-from-request.js"],"names":[],"mappings":"AAcA;;;;;;;;;;;;;;GAcG;AACH,uCAFU,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,CAyCpC"}
|
|
@@ -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 { customServerStrategies, 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
|
*
|
|
@@ -33,10 +35,7 @@ export const extractLocaleFromRequest = (request) => {
|
|
|
33
35
|
}
|
|
34
36
|
else if (TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED &&
|
|
35
37
|
strat === "preferredLanguage") {
|
|
36
|
-
|
|
37
|
-
if (acceptLanguageHeader) {
|
|
38
|
-
locale = negotiatePreferredLanguageFromHeader(acceptLanguageHeader);
|
|
39
|
-
}
|
|
38
|
+
locale = extractLocaleFromHeader(request);
|
|
40
39
|
}
|
|
41
40
|
else if (strat === "globalVariable") {
|
|
42
41
|
locale = _locale;
|
|
@@ -47,6 +46,10 @@ export const extractLocaleFromRequest = (request) => {
|
|
|
47
46
|
else if (strat === "localStorage") {
|
|
48
47
|
continue;
|
|
49
48
|
}
|
|
49
|
+
else if (isCustomStrategy(strat) && customServerStrategies.has(strat)) {
|
|
50
|
+
const handler = customServerStrategies.get(strat);
|
|
51
|
+
locale = handler.getLocale(request);
|
|
52
|
+
}
|
|
50
53
|
if (locale !== undefined) {
|
|
51
54
|
if (!isLocale(locale)) {
|
|
52
55
|
locale = undefined;
|
|
@@ -58,34 +61,3 @@ export const extractLocaleFromRequest = (request) => {
|
|
|
58
61
|
}
|
|
59
62
|
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
63
|
};
|
|
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,84 @@ test("preferredLanguage precedence over url", async () => {
|
|
|
238
238
|
const locale = runtime.extractLocaleFromRequest(request);
|
|
239
239
|
expect(locale).toBe("de");
|
|
240
240
|
});
|
|
241
|
+
test("returns locale from custom strategy", 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
|
+
const locale = runtime.extractLocaleFromRequest(request);
|
|
261
|
+
expect(locale).toBe("fr");
|
|
262
|
+
});
|
|
263
|
+
test("falls back to next strategy when custom strategy returns undefined", async () => {
|
|
264
|
+
const runtime = await createParaglide({
|
|
265
|
+
blob: await newProject({
|
|
266
|
+
settings: {
|
|
267
|
+
baseLocale: "en",
|
|
268
|
+
locales: ["en", "fr"],
|
|
269
|
+
},
|
|
270
|
+
}),
|
|
271
|
+
strategy: ["custom-fallback", "baseLocale"],
|
|
272
|
+
});
|
|
273
|
+
runtime.defineCustomServerStrategy("custom-fallback", {
|
|
274
|
+
getLocale: () => undefined,
|
|
275
|
+
});
|
|
276
|
+
const request = new Request("http://example.com");
|
|
277
|
+
const locale = runtime.extractLocaleFromRequest(request);
|
|
278
|
+
expect(locale).toBe("en"); // Should fall back to baseLocale
|
|
279
|
+
});
|
|
280
|
+
test("custom strategy takes precedence over built-in strategies", async () => {
|
|
281
|
+
const runtime = await createParaglide({
|
|
282
|
+
blob: await newProject({
|
|
283
|
+
settings: {
|
|
284
|
+
baseLocale: "en",
|
|
285
|
+
locales: ["en", "fr", "de"],
|
|
286
|
+
},
|
|
287
|
+
}),
|
|
288
|
+
strategy: ["custom-priority", "cookie", "baseLocale"],
|
|
289
|
+
cookieName: "PARAGLIDE_LOCALE",
|
|
290
|
+
});
|
|
291
|
+
runtime.defineCustomServerStrategy("custom-priority", {
|
|
292
|
+
getLocale: () => "de",
|
|
293
|
+
});
|
|
294
|
+
const request = new Request("http://example.com", {
|
|
295
|
+
headers: {
|
|
296
|
+
cookie: "PARAGLIDE_LOCALE=fr", // Cookie has different locale
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
const locale = runtime.extractLocaleFromRequest(request);
|
|
300
|
+
expect(locale).toBe("de"); // Should use custom strategy, not cookie
|
|
301
|
+
});
|
|
302
|
+
test("multiple custom strategies work in order", async () => {
|
|
303
|
+
const runtime = await createParaglide({
|
|
304
|
+
blob: await newProject({
|
|
305
|
+
settings: {
|
|
306
|
+
baseLocale: "en",
|
|
307
|
+
locales: ["en", "fr", "de"],
|
|
308
|
+
},
|
|
309
|
+
}),
|
|
310
|
+
strategy: ["custom-first", "custom-second", "baseLocale"],
|
|
311
|
+
});
|
|
312
|
+
runtime.defineCustomServerStrategy("custom-first", {
|
|
313
|
+
getLocale: () => undefined,
|
|
314
|
+
});
|
|
315
|
+
runtime.defineCustomServerStrategy("custom-second", {
|
|
316
|
+
getLocale: () => "fr",
|
|
317
|
+
});
|
|
318
|
+
const request = new Request("http://example.com");
|
|
319
|
+
const locale = runtime.extractLocaleFromRequest(request);
|
|
320
|
+
expect(locale).toBe("fr"); // Should use second custom strategy
|
|
321
|
+
});
|
|
@@ -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,CAiEpB;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,17 @@ 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
|
+
locale = handler.getLocale();
|
|
73
|
+
}
|
|
69
74
|
// check if match, else continue loop
|
|
70
75
|
if (locale !== undefined) {
|
|
71
76
|
const asserted = assertIsLocale(locale);
|
|
@@ -80,29 +85,6 @@ export let getLocale = () => {
|
|
|
80
85
|
}
|
|
81
86
|
throw new Error("No locale found. Read the docs https://inlang.com/m/gerre34r/library-inlang-paraglideJs/errors#no-locale-found");
|
|
82
87
|
};
|
|
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
88
|
/**
|
|
107
89
|
* Overwrite the \`getLocale()\` function.
|
|
108
90
|
*
|