@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,2 +1,2 @@
|
|
|
1
|
-
export declare const paraglideVitePlugin: (options: import("../index.js").CompilerOptions) => import("
|
|
1
|
+
export declare const paraglideVitePlugin: (options: import("../index.js").CompilerOptions) => import("unplugin").VitePlugin<any> | import("unplugin").VitePlugin<any>[];
|
|
2
2
|
//# sourceMappingURL=vite.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../../src/bundler-plugins/vite.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,mBAAmB,+
|
|
1
|
+
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../../src/bundler-plugins/vite.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,mBAAmB,+HAAoC,CAAC"}
|
|
@@ -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"];
|
|
@@ -104,10 +106,22 @@ export type CompilerOptions = {
|
|
|
104
106
|
cookieMaxAge?: number;
|
|
105
107
|
/**
|
|
106
108
|
* The host to which the cookie will be sent.
|
|
107
|
-
* If
|
|
108
|
-
*
|
|
109
|
+
* If undefined or empty, the domain attribute is omitted from the cookie, scoping it to the exact current domain only (no subdomains).
|
|
110
|
+
* If specified, the cookie will be available to the specified domain and all its subdomains.
|
|
111
|
+
*
|
|
112
|
+
* Use this when you need cookies to be shared across subdomains (e.g., between `app.example.com` and `api.example.com`).
|
|
113
|
+
* The default behavior (no domain) ensures better compatibility with server-side cookies that don't specify a domain attribute.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```ts
|
|
117
|
+
* // Default: exact domain only (compatible with server-side cookies)
|
|
118
|
+
* cookieDomain: undefined // Cookie: "PARAGLIDE_LOCALE=en; path=/; max-age=34560000"
|
|
119
|
+
*
|
|
120
|
+
* // Subdomain sharing: available across all subdomains
|
|
121
|
+
* cookieDomain: "example.com" // Cookie: "PARAGLIDE_LOCALE=en; path=/; max-age=34560000; domain=example.com"
|
|
122
|
+
* ```
|
|
109
123
|
*
|
|
110
|
-
* @default
|
|
124
|
+
* @default "" (no domain attribute, exact domain only)
|
|
111
125
|
*/
|
|
112
126
|
cookieDomain?: string;
|
|
113
127
|
/**
|
|
@@ -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;;;;;;;;;;;;;;;;;;OAkBG;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,CA0IT"}
|
|
@@ -62,8 +62,14 @@ ${injectCode("./assert-is-locale.js")}
|
|
|
62
62
|
|
|
63
63
|
${injectCode("./extract-locale-from-request.js")}
|
|
64
64
|
|
|
65
|
+
${injectCode("./extract-locale-from-request-async.js")}
|
|
66
|
+
|
|
65
67
|
${injectCode("./extract-locale-from-cookie.js")}
|
|
66
68
|
|
|
69
|
+
${injectCode("./extract-locale-from-header.js")}
|
|
70
|
+
|
|
71
|
+
${injectCode("./extract-locale-from-navigator.js")}
|
|
72
|
+
|
|
67
73
|
${injectCode("./extract-locale-from-url.js")}
|
|
68
74
|
|
|
69
75
|
${injectCode("./localize-url.js")}
|
|
@@ -74,6 +80,8 @@ ${injectCode("./track-message-call.js")}
|
|
|
74
80
|
|
|
75
81
|
${injectCode("./generate-static-localized-urls.js")}
|
|
76
82
|
|
|
83
|
+
${injectCode("./strategy.js")}
|
|
84
|
+
|
|
77
85
|
// ------ TYPES ------
|
|
78
86
|
|
|
79
87
|
/**
|
|
@@ -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
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asynchronously extracts a locale from a request.
|
|
3
|
+
*
|
|
4
|
+
* This function supports async custom server strategies, unlike the synchronous
|
|
5
|
+
* `extractLocaleFromRequest`. Use this function when you have custom server strategies
|
|
6
|
+
* that need to perform asynchronous operations (like database calls) in their getLocale method.
|
|
7
|
+
*
|
|
8
|
+
* The function first processes any custom server strategies asynchronously, then falls back
|
|
9
|
+
* to the synchronous `extractLocaleFromRequest` for all other strategies.
|
|
10
|
+
*
|
|
11
|
+
* @see {@link https://github.com/opral/inlang-paraglide-js/issues/527#issuecomment-2978151022}
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // Basic usage
|
|
15
|
+
* const locale = await extractLocaleFromRequestAsync(request);
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // With custom async server strategy
|
|
19
|
+
* defineCustomServerStrategy("custom-database", {
|
|
20
|
+
* getLocale: async (request) => {
|
|
21
|
+
* const userId = extractUserIdFromRequest(request);
|
|
22
|
+
* return await getUserLocaleFromDatabase(userId);
|
|
23
|
+
* }
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* const locale = await extractLocaleFromRequestAsync(request);
|
|
27
|
+
*
|
|
28
|
+
* @type {(request: Request) => Promise<Locale>}
|
|
29
|
+
*/
|
|
30
|
+
export const extractLocaleFromRequestAsync: (request: Request) => Promise<Locale>;
|
|
31
|
+
//# sourceMappingURL=extract-locale-from-request-async.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-locale-from-request-async.d.ts","sourceRoot":"","sources":["../../../src/compiler/runtime/extract-locale-from-request-async.js"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,4CAFU,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,CAyB7C"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { customServerStrategies, isCustomStrategy } from "./strategy.js";
|
|
2
|
+
import { strategy } from "./variables.js";
|
|
3
|
+
import { assertIsLocale } from "./assert-is-locale.js";
|
|
4
|
+
import { isLocale } from "./is-locale.js";
|
|
5
|
+
import { extractLocaleFromRequest } from "./extract-locale-from-request.js";
|
|
6
|
+
/**
|
|
7
|
+
* Asynchronously extracts a locale from a request.
|
|
8
|
+
*
|
|
9
|
+
* This function supports async custom server strategies, unlike the synchronous
|
|
10
|
+
* `extractLocaleFromRequest`. Use this function when you have custom server strategies
|
|
11
|
+
* that need to perform asynchronous operations (like database calls) in their getLocale method.
|
|
12
|
+
*
|
|
13
|
+
* The function first processes any custom server strategies asynchronously, then falls back
|
|
14
|
+
* to the synchronous `extractLocaleFromRequest` for all other strategies.
|
|
15
|
+
*
|
|
16
|
+
* @see {@link https://github.com/opral/inlang-paraglide-js/issues/527#issuecomment-2978151022}
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* // Basic usage
|
|
20
|
+
* const locale = await extractLocaleFromRequestAsync(request);
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // With custom async server strategy
|
|
24
|
+
* defineCustomServerStrategy("custom-database", {
|
|
25
|
+
* getLocale: async (request) => {
|
|
26
|
+
* const userId = extractUserIdFromRequest(request);
|
|
27
|
+
* return await getUserLocaleFromDatabase(userId);
|
|
28
|
+
* }
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* const locale = await extractLocaleFromRequestAsync(request);
|
|
32
|
+
*
|
|
33
|
+
* @type {(request: Request) => Promise<Locale>}
|
|
34
|
+
*/
|
|
35
|
+
export const extractLocaleFromRequestAsync = async (request) => {
|
|
36
|
+
/** @type {string|undefined} */
|
|
37
|
+
let locale;
|
|
38
|
+
// Process custom strategies first, in order
|
|
39
|
+
for (const strat of strategy) {
|
|
40
|
+
if (isCustomStrategy(strat) && customServerStrategies.has(strat)) {
|
|
41
|
+
const handler = customServerStrategies.get(strat);
|
|
42
|
+
if (handler) {
|
|
43
|
+
/** @type {string|undefined} */
|
|
44
|
+
locale = await handler.getLocale(request);
|
|
45
|
+
}
|
|
46
|
+
// If we got a valid locale from this custom strategy, use it
|
|
47
|
+
if (locale !== undefined && isLocale(locale)) {
|
|
48
|
+
return assertIsLocale(locale);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// If no custom strategy provided a valid locale, fall back to sync version
|
|
53
|
+
locale = extractLocaleFromRequest(request);
|
|
54
|
+
return assertIsLocale(locale);
|
|
55
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-locale-from-request-async.test.d.ts","sourceRoot":"","sources":["../../../src/compiler/runtime/extract-locale-from-request-async.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { newProject } from "@inlang/sdk";
|
|
2
|
+
import { expect, test } from "vitest";
|
|
3
|
+
import { createParaglide } from "../create-paraglide.js";
|
|
4
|
+
test("returns locale from custom strategy which is async", async () => {
|
|
5
|
+
const runtime = await createParaglide({
|
|
6
|
+
blob: await newProject({
|
|
7
|
+
settings: {
|
|
8
|
+
baseLocale: "en",
|
|
9
|
+
locales: ["en", "fr", "de"],
|
|
10
|
+
},
|
|
11
|
+
}),
|
|
12
|
+
strategy: ["custom-header", "baseLocale"],
|
|
13
|
+
});
|
|
14
|
+
class FakeDB {
|
|
15
|
+
db = new Map();
|
|
16
|
+
constructor() {
|
|
17
|
+
this.db.set("1", "fr");
|
|
18
|
+
}
|
|
19
|
+
async getUserLocaleById(id) {
|
|
20
|
+
return this.db.get(id);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const db = new FakeDB();
|
|
24
|
+
async function getLocaleFromUserRequest(request) {
|
|
25
|
+
const userId = request?.headers.get("X-Custom-User-ID") ?? undefined;
|
|
26
|
+
if (!userId)
|
|
27
|
+
throw Error("No User ID");
|
|
28
|
+
const locale = await db.getUserLocaleById(userId);
|
|
29
|
+
return locale;
|
|
30
|
+
}
|
|
31
|
+
runtime.defineCustomServerStrategy("custom-header", {
|
|
32
|
+
getLocale: async (request) => (await getLocaleFromUserRequest(request)) ?? undefined,
|
|
33
|
+
});
|
|
34
|
+
const request = new Request("http://example.com", {
|
|
35
|
+
headers: {
|
|
36
|
+
"X-Custom-User-ID": "1",
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
const locale = await runtime.extractLocaleFromRequestAsync(request);
|
|
40
|
+
expect(locale).toBe("fr");
|
|
41
|
+
});
|
|
42
|
+
test("falls back to next strategy when custom strategy returns undefined", async () => {
|
|
43
|
+
const runtime = await createParaglide({
|
|
44
|
+
blob: await newProject({
|
|
45
|
+
settings: {
|
|
46
|
+
baseLocale: "en",
|
|
47
|
+
locales: ["en", "fr"],
|
|
48
|
+
},
|
|
49
|
+
}),
|
|
50
|
+
strategy: ["custom-fallback", "baseLocale"],
|
|
51
|
+
});
|
|
52
|
+
runtime.defineCustomServerStrategy("custom-fallback", {
|
|
53
|
+
getLocale: () => undefined,
|
|
54
|
+
});
|
|
55
|
+
const request = new Request("http://example.com");
|
|
56
|
+
const locale = await runtime.extractLocaleFromRequestAsync(request);
|
|
57
|
+
expect(locale).toBe("en"); // Should fall back to baseLocale
|
|
58
|
+
});
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
* they are defined. If a strategy returns an invalid locale,
|
|
9
9
|
* it will fall back to the next strategy.
|
|
10
10
|
*
|
|
11
|
+
* Note: Custom server strategies are not supported in this synchronous version.
|
|
12
|
+
* Use `extractLocaleFromRequestAsync` if you need custom server strategies with async getLocale methods.
|
|
13
|
+
*
|
|
11
14
|
* @example
|
|
12
15
|
* const locale = extractLocaleFromRequest(request);
|
|
13
16
|
*
|
|
@@ -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;;;;;;;;;;;;;;;;;GAiBG;AACH,uCAFU,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,CA0CpC"}
|