@inlang/paraglide-js 2.0.13 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/bundler-plugins/vite.d.ts +1 -1
  2. package/dist/bundler-plugins/vite.d.ts.map +1 -1
  3. package/dist/compiler/compile-bundle.d.ts +2 -1
  4. package/dist/compiler/compile-bundle.d.ts.map +1 -1
  5. package/dist/compiler/compile-bundle.js +4 -3
  6. package/dist/compiler/compile-bundle.test.js +64 -0
  7. package/dist/compiler/compile-project.d.ts.map +1 -1
  8. package/dist/compiler/compile-project.js +1 -0
  9. package/dist/compiler/compiler-options.d.ts +17 -3
  10. package/dist/compiler/compiler-options.d.ts.map +1 -1
  11. package/dist/compiler/runtime/create-runtime.d.ts.map +1 -1
  12. package/dist/compiler/runtime/create-runtime.js +8 -0
  13. package/dist/compiler/runtime/extract-locale-from-cookie.d.ts +1 -1
  14. package/dist/compiler/runtime/extract-locale-from-cookie.js +1 -1
  15. package/dist/compiler/runtime/extract-locale-from-header.d.ts +2 -0
  16. package/dist/compiler/runtime/extract-locale-from-header.d.ts.map +1 -0
  17. package/dist/compiler/runtime/extract-locale-from-header.js +43 -0
  18. package/dist/compiler/runtime/extract-locale-from-header.test.d.ts +2 -0
  19. package/dist/compiler/runtime/extract-locale-from-header.test.d.ts.map +1 -0
  20. package/dist/compiler/runtime/extract-locale-from-header.test.js +51 -0
  21. package/dist/compiler/runtime/extract-locale-from-navigator.d.ts +2 -0
  22. package/dist/compiler/runtime/extract-locale-from-navigator.d.ts.map +1 -0
  23. package/dist/compiler/runtime/extract-locale-from-navigator.js +31 -0
  24. package/dist/compiler/runtime/extract-locale-from-navigator.test.d.ts +2 -0
  25. package/dist/compiler/runtime/extract-locale-from-navigator.test.d.ts.map +1 -0
  26. package/dist/compiler/runtime/extract-locale-from-navigator.test.js +29 -0
  27. package/dist/compiler/runtime/extract-locale-from-request-async.d.ts +31 -0
  28. package/dist/compiler/runtime/extract-locale-from-request-async.d.ts.map +1 -0
  29. package/dist/compiler/runtime/extract-locale-from-request-async.js +55 -0
  30. package/dist/compiler/runtime/extract-locale-from-request-async.test.d.ts +2 -0
  31. package/dist/compiler/runtime/extract-locale-from-request-async.test.d.ts.map +1 -0
  32. package/dist/compiler/runtime/extract-locale-from-request-async.test.js +58 -0
  33. package/dist/compiler/runtime/extract-locale-from-request.d.ts +3 -0
  34. package/dist/compiler/runtime/extract-locale-from-request.d.ts.map +1 -1
  35. package/dist/compiler/runtime/extract-locale-from-request.js +12 -36
  36. package/dist/compiler/runtime/extract-locale-from-request.test.js +84 -2
  37. package/dist/compiler/runtime/get-locale.d.ts.map +1 -1
  38. package/dist/compiler/runtime/get-locale.js +16 -26
  39. package/dist/compiler/runtime/get-locale.test.js +154 -1
  40. package/dist/compiler/runtime/set-locale.d.ts.map +1 -1
  41. package/dist/compiler/runtime/set-locale.js +18 -3
  42. package/dist/compiler/runtime/set-locale.test.js +146 -6
  43. package/dist/compiler/runtime/strategy.d.ts +60 -0
  44. package/dist/compiler/runtime/strategy.d.ts.map +1 -0
  45. package/dist/compiler/runtime/strategy.js +60 -0
  46. package/dist/compiler/runtime/strategy.test.d.ts +2 -0
  47. package/dist/compiler/runtime/strategy.test.d.ts.map +1 -0
  48. package/dist/compiler/runtime/strategy.test.js +94 -0
  49. package/dist/compiler/runtime/type.d.ts +5 -0
  50. package/dist/compiler/runtime/type.d.ts.map +1 -1
  51. package/dist/compiler/runtime/variables.d.ts +2 -2
  52. package/dist/compiler/runtime/variables.d.ts.map +1 -1
  53. package/dist/compiler/runtime/variables.js +1 -1
  54. package/dist/compiler/server/middleware.d.ts.map +1 -1
  55. package/dist/compiler/server/middleware.js +14 -2
  56. package/dist/compiler/server/middleware.test.js +263 -0
  57. package/dist/services/env-variables/index.js +1 -1
  58. package/package.json +6 -6
@@ -1,2 +1,2 @@
1
- export declare const paraglideVitePlugin: (options: import("../index.js").CompilerOptions) => import("vite").Plugin<any> | import("vite").Plugin<any>[];
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,+GAAoC,CAAC"}
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,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEjE,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;CACzE,KAAG,0BA0BH,CAAC"}
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,CAsEjC,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"}
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"}
@@ -36,6 +36,7 @@ export const compileProject = async (args) => {
36
36
  bundle,
37
37
  fallbackMap,
38
38
  messageReferenceExpression: outputStructure.messageReferenceExpression,
39
+ settings,
39
40
  }));
40
41
  const output = {
41
42
  ["runtime.js"]: createRuntimeFile({
@@ -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 null, this defaults to the host portion of the current document location and the cookie is not available on subdomains.
108
- * Otherwise, subdomains are always included.
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 window.location.hostname
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;;;;;;;;;;;;;;OAcG;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
+ {"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,CAkIT"}
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 docuement is not available or if the cookie is not set.
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 docuement is not available or if the cookie is not set.
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,2 @@
1
+ export function extractLocaleFromHeader(request: Request): Locale;
2
+ //# sourceMappingURL=extract-locale-from-header.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=extract-locale-from-header.test.d.ts.map
@@ -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,2 @@
1
+ export function extractLocaleFromNavigator(): Locale | undefined;
2
+ //# sourceMappingURL=extract-locale-from-navigator.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=extract-locale-from-navigator.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=extract-locale-from-request-async.test.d.ts.map
@@ -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":"AAYA;;;;;;;;;;;;;;GAcG;AACH,uCAFU,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,CAyCpC"}
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"}