@inlang/paraglide-js 2.27.0 → 2.28.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 (171) hide show
  1. package/README.md +9 -0
  2. package/dist/bundler-plugins/per-locale-build/index.js +14 -7
  3. package/dist/bundler-plugins/per-locale-build/index.js.map +1 -1
  4. package/dist/bundler-plugins/per-locale-build/vite-environments.test.js +235 -2
  5. package/dist/bundler-plugins/per-locale-build/vite-environments.test.js.map +1 -1
  6. package/dist/bundler-plugins/unplugin.d.ts +1 -1
  7. package/dist/bundler-plugins/unplugin.js +79 -10
  8. package/dist/bundler-plugins/unplugin.js.map +1 -1
  9. package/dist/bundler-plugins/unplugin.test.js +334 -1
  10. package/dist/bundler-plugins/unplugin.test.js.map +1 -1
  11. package/dist/bundler-plugins/vite.js +2 -1
  12. package/dist/bundler-plugins/vite.js.map +1 -1
  13. package/dist/bundler-plugins/vite.test.js +1 -1
  14. package/dist/bundler-plugins/vite.test.js.map +1 -1
  15. package/dist/cli/commands/compile/command.js +13 -0
  16. package/dist/cli/commands/compile/command.js.map +1 -1
  17. package/dist/cli/commands/compile/command.test.js +87 -3
  18. package/dist/cli/commands/compile/command.test.js.map +1 -1
  19. package/dist/cli/commands/init/command.d.ts +5 -0
  20. package/dist/cli/commands/init/command.js +213 -59
  21. package/dist/cli/commands/init/command.js.map +1 -1
  22. package/dist/cli/commands/init/command.test.d.ts +1 -0
  23. package/dist/cli/commands/init/command.test.js +123 -0
  24. package/dist/cli/commands/init/command.test.js.map +1 -0
  25. package/dist/cli/init-options.d.ts +11 -0
  26. package/dist/cli/init-options.js +2 -0
  27. package/dist/cli/init-options.js.map +1 -0
  28. package/dist/cli/steps/add-inlang-check.js +2 -2
  29. package/dist/cli/steps/add-inlang-check.js.map +1 -1
  30. package/dist/cli/steps/add-message-lint.d.ts +34 -0
  31. package/dist/cli/steps/add-message-lint.js +105 -0
  32. package/dist/cli/steps/add-message-lint.js.map +1 -0
  33. package/dist/cli/steps/add-message-lint.test.d.ts +1 -0
  34. package/dist/cli/steps/add-message-lint.test.js +74 -0
  35. package/dist/cli/steps/add-message-lint.test.js.map +1 -0
  36. package/dist/cli/steps/add-vite-plugin.d.ts +18 -0
  37. package/dist/cli/steps/add-vite-plugin.js +34 -6
  38. package/dist/cli/steps/add-vite-plugin.js.map +1 -1
  39. package/dist/cli/steps/add-vite-plugin.test.js +25 -9
  40. package/dist/cli/steps/add-vite-plugin.test.js.map +1 -1
  41. package/dist/cli/steps/check-for-uncomitted-changes.d.ts +3 -0
  42. package/dist/cli/steps/check-for-uncomitted-changes.js +15 -3
  43. package/dist/cli/steps/check-for-uncomitted-changes.js.map +1 -1
  44. package/dist/cli/steps/detect-bundler.d.ts +1 -0
  45. package/dist/cli/steps/detect-bundler.js +7 -1
  46. package/dist/cli/steps/detect-bundler.js.map +1 -1
  47. package/dist/cli/steps/initialize-inlang-project.d.ts +4 -3
  48. package/dist/cli/steps/initialize-inlang-project.js +60 -157
  49. package/dist/cli/steps/initialize-inlang-project.js.map +1 -1
  50. package/dist/cli/steps/maybe-add-machine-translation.d.ts +4 -0
  51. package/dist/cli/steps/maybe-add-machine-translation.js +11 -6
  52. package/dist/cli/steps/maybe-add-machine-translation.js.map +1 -1
  53. package/dist/cli/steps/maybe-add-sherlock.d.ts +2 -0
  54. package/dist/cli/steps/maybe-add-sherlock.js +12 -6
  55. package/dist/cli/steps/maybe-add-sherlock.js.map +1 -1
  56. package/dist/cli/steps/maybe-add-sherlock.test.d.ts +1 -0
  57. package/dist/cli/steps/maybe-add-sherlock.test.js +44 -0
  58. package/dist/cli/steps/maybe-add-sherlock.test.js.map +1 -0
  59. package/dist/cli/steps/prompt-for-outdir.d.ts +2 -0
  60. package/dist/cli/steps/prompt-for-outdir.js +4 -0
  61. package/dist/cli/steps/prompt-for-outdir.js.map +1 -1
  62. package/dist/cli/steps/update-package-json.js +8 -13
  63. package/dist/cli/steps/update-package-json.js.map +1 -1
  64. package/dist/cli/steps/update-ts-config.d.ts +6 -0
  65. package/dist/cli/steps/update-ts-config.js +27 -4
  66. package/dist/cli/steps/update-ts-config.js.map +1 -1
  67. package/dist/cli/utils.d.ts +2 -0
  68. package/dist/cli/utils.js +6 -0
  69. package/dist/cli/utils.js.map +1 -1
  70. package/dist/cli/utils.test.d.ts +1 -0
  71. package/dist/cli/utils.test.js +16 -0
  72. package/dist/cli/utils.test.js.map +1 -0
  73. package/dist/compiler/compile-annotation.js +6 -1
  74. package/dist/compiler/compile-annotation.js.map +1 -1
  75. package/dist/compiler/compile-bundle.d.ts +4 -0
  76. package/dist/compiler/compile-bundle.js +67 -2
  77. package/dist/compiler/compile-bundle.js.map +1 -1
  78. package/dist/compiler/compile-bundle.test.js +214 -1
  79. package/dist/compiler/compile-bundle.test.js.map +1 -1
  80. package/dist/compiler/compile-lifecycle.test.d.ts +1 -0
  81. package/dist/compiler/compile-lifecycle.test.js +69 -0
  82. package/dist/compiler/compile-lifecycle.test.js.map +1 -0
  83. package/dist/compiler/compile-local-variable.test.js +14 -14
  84. package/dist/compiler/compile-local-variable.test.js.map +1 -1
  85. package/dist/compiler/compile-message.exact-number.test.js +1 -1
  86. package/dist/compiler/compile-message.exact-number.test.js.map +1 -1
  87. package/dist/compiler/compile-message.locals.test.js +1 -1
  88. package/dist/compiler/compile-message.locals.test.js.map +1 -1
  89. package/dist/compiler/compile-message.test.js +2 -2
  90. package/dist/compiler/compile-message.test.js.map +1 -1
  91. package/dist/compiler/compile-pattern.test.js +5 -5
  92. package/dist/compiler/compile-pattern.test.js.map +1 -1
  93. package/dist/compiler/compile-project.js +1 -0
  94. package/dist/compiler/compile-project.js.map +1 -1
  95. package/dist/compiler/compile-project.test.js +94 -1
  96. package/dist/compiler/compile-project.test.js.map +1 -1
  97. package/dist/compiler/compile.js +36 -17
  98. package/dist/compiler/compile.js.map +1 -1
  99. package/dist/compiler/compile.test.js +70 -0
  100. package/dist/compiler/compile.test.js.map +1 -1
  101. package/dist/compiler/compiler-options.d.ts +41 -0
  102. package/dist/compiler/compiler-options.js.map +1 -1
  103. package/dist/compiler/output-structure/message-modules.js +1 -1
  104. package/dist/compiler/output-structure/message-modules.js.map +1 -1
  105. package/dist/compiler/registry.js +71 -13
  106. package/dist/compiler/registry.js.map +1 -1
  107. package/dist/compiler/registry.test.d.ts +1 -0
  108. package/dist/compiler/registry.test.js +291 -0
  109. package/dist/compiler/registry.test.js.map +1 -0
  110. package/dist/compiler/runtime/create-runtime.d.ts +2 -0
  111. package/dist/compiler/runtime/create-runtime.js +9 -0
  112. package/dist/compiler/runtime/create-runtime.js.map +1 -1
  113. package/dist/compiler/runtime/extract-locale-from-cookie.js +8 -1
  114. package/dist/compiler/runtime/extract-locale-from-cookie.js.map +1 -1
  115. package/dist/compiler/runtime/extract-locale-from-cookie.test.js +26 -0
  116. package/dist/compiler/runtime/extract-locale-from-cookie.test.js.map +1 -1
  117. package/dist/compiler/runtime/extract-locale-from-header.js +47 -5
  118. package/dist/compiler/runtime/extract-locale-from-header.js.map +1 -1
  119. package/dist/compiler/runtime/extract-locale-from-header.test.js +58 -0
  120. package/dist/compiler/runtime/extract-locale-from-header.test.js.map +1 -1
  121. package/dist/compiler/runtime/get-locale.js +7 -1
  122. package/dist/compiler/runtime/get-locale.js.map +1 -1
  123. package/dist/compiler/runtime/get-locale.test.js +95 -0
  124. package/dist/compiler/runtime/get-locale.test.js.map +1 -1
  125. package/dist/compiler/runtime/localize-href.d.ts +8 -0
  126. package/dist/compiler/runtime/localize-href.js +60 -0
  127. package/dist/compiler/runtime/localize-href.js.map +1 -1
  128. package/dist/compiler/runtime/localize-href.test.js +227 -1
  129. package/dist/compiler/runtime/localize-href.test.js.map +1 -1
  130. package/dist/compiler/runtime/set-locale.js +24 -6
  131. package/dist/compiler/runtime/set-locale.js.map +1 -1
  132. package/dist/compiler/runtime/set-locale.test.js +182 -0
  133. package/dist/compiler/runtime/set-locale.test.js.map +1 -1
  134. package/dist/compiler/runtime/type.d.ts +2 -0
  135. package/dist/compiler/runtime/type.js.map +1 -1
  136. package/dist/compiler/runtime/type.test.js +34 -0
  137. package/dist/compiler/runtime/type.test.js.map +1 -1
  138. package/dist/compiler/runtime/variables.d.ts +2 -0
  139. package/dist/compiler/runtime/variables.js +7 -0
  140. package/dist/compiler/runtime/variables.js.map +1 -1
  141. package/dist/compiler/server/middleware.d.ts +8 -2
  142. package/dist/compiler/server/middleware.js +175 -7
  143. package/dist/compiler/server/middleware.js.map +1 -1
  144. package/dist/compiler/server/middleware.test.js +750 -3
  145. package/dist/compiler/server/middleware.test.js.map +1 -1
  146. package/dist/eslint/index.d.ts +4 -0
  147. package/dist/eslint/index.js +8 -0
  148. package/dist/eslint/index.js.map +1 -0
  149. package/dist/eslint/no-dynamic-message-access.d.ts +3 -0
  150. package/dist/eslint/no-dynamic-message-access.js +150 -0
  151. package/dist/eslint/no-dynamic-message-access.js.map +1 -0
  152. package/dist/eslint/no-dynamic-message-access.test.d.ts +1 -0
  153. package/dist/eslint/no-dynamic-message-access.test.js +126 -0
  154. package/dist/eslint/no-dynamic-message-access.test.js.map +1 -0
  155. package/dist/eslint/oxlint.test.d.ts +1 -0
  156. package/dist/eslint/oxlint.test.js +64 -0
  157. package/dist/eslint/oxlint.test.js.map +1 -0
  158. package/dist/services/config/config-schema.d.ts +2 -0
  159. package/dist/services/config/config-schema.js +2 -0
  160. package/dist/services/config/config-schema.js.map +1 -1
  161. package/dist/services/config/load-paraglide-config.test.js +12 -0
  162. package/dist/services/config/load-paraglide-config.test.js.map +1 -1
  163. package/dist/services/env-variables/index.js +1 -1
  164. package/dist/services/env-variables/index.js.map +1 -1
  165. package/dist/services/file-handling/write-output.bench.js +51 -11
  166. package/dist/services/file-handling/write-output.bench.js.map +1 -1
  167. package/dist/services/file-handling/write-output.js +60 -20
  168. package/dist/services/file-handling/write-output.js.map +1 -1
  169. package/dist/services/file-handling/write-output.test.js +175 -0
  170. package/dist/services/file-handling/write-output.test.js.map +1 -1
  171. package/package.json +14 -4
@@ -1 +1 @@
1
- {"version":3,"file":"extract-locale-from-cookie.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/extract-locale-from-cookie.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { test, expect } from \"vitest\";\nimport { createParaglide } from \"../create-paraglide.js\";\nimport { newProject } from \"@inlang/sdk\";\n\ntest(\"returns undefined if document is not available\", async () => {\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = undefined;\n\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t});\n\n\texpect(runtime.extractLocaleFromCookie()).toBeUndefined();\n});\n\ntest(\"matches the locale of a cookie\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {};\n\tglobalThis.document.cookie =\n\t\t\"OTHER_COOKIE=fr; PARAGLIDE_LOCALE=de; ANOTHER_COOKIE=en; EXPIRES_COOKIE=es; Max-Age=3600\";\n\n\tconst locale = runtime.extractLocaleFromCookie();\n\texpect(locale).toBe(\"de\");\n});\n\ntest(\"caches the locale during immediate repeated cookie reads\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\tlet cookieReadCount = 0;\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {\n\t\tget cookie() {\n\t\t\tcookieReadCount += 1;\n\t\t\treturn \"OTHER_COOKIE=fr; PARAGLIDE_LOCALE=de; ANOTHER_COOKIE=en;\";\n\t\t},\n\t};\n\n\texpect(runtime.extractLocaleFromCookie()).toBe(\"de\");\n\texpect(runtime.extractLocaleFromCookie()).toBe(\"de\");\n\texpect(cookieReadCount).toBe(1);\n});\n\ntest(\"cache expires after a microtask\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\tlet cookie = \"PARAGLIDE_LOCALE=en;\";\n\tlet cookieReadCount = 0;\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {\n\t\tget cookie() {\n\t\t\tcookieReadCount += 1;\n\t\t\treturn cookie;\n\t\t},\n\t};\n\n\texpect(runtime.extractLocaleFromCookie()).toBe(\"en\");\n\tcookie = \"PARAGLIDE_LOCALE=de;\";\n\texpect(runtime.extractLocaleFromCookie()).toBe(\"en\");\n\n\tawait Promise.resolve();\n\n\texpect(runtime.extractLocaleFromCookie()).toBe(\"de\");\n\texpect(cookieReadCount).toBe(2);\n});\n\ntest(\"clears the cache after setLocale writes the cookie\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\tisServer: \"false\",\n\t});\n\n\tlet cookie = \"PARAGLIDE_LOCALE=en;\";\n\tlet cookieReadCount = 0;\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {\n\t\tget cookie() {\n\t\t\tcookieReadCount += 1;\n\t\t\treturn cookie;\n\t\t},\n\t\tset cookie(value) {\n\t\t\tcookie = value;\n\t\t},\n\t};\n\t(globalThis as any).window = {\n\t\tlocation: { href: \"https://example.com\", reload() {} },\n\t};\n\n\texpect(runtime.extractLocaleFromCookie()).toBe(\"en\");\n\truntime.setLocale(\"de\", { reload: false });\n\texpect(runtime.extractLocaleFromCookie()).toBe(\"de\");\n\texpect(cookieReadCount).toBe(2);\n});\n\ntest(\"escapes regex characters in cookie names\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE.LOCALE[app]\",\n\t});\n\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {};\n\tglobalThis.document.cookie =\n\t\t\"PARAGLIDE_LOCALE_APP_=en;PARAGLIDE.LOCALE[app]=de;\";\n\n\texpect(runtime.extractLocaleFromCookie()).toBe(\"de\");\n});\n\ntest(\"returns undefined for an empty cookie locale\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {};\n\tglobalThis.document.cookie = \"OTHER_COOKIE=de;PARAGLIDE_LOCALE=;\";\n\n\texpect(runtime.extractLocaleFromCookie()).toBeUndefined();\n});\n\ntest(\"canonicalizes cookie locale casing\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de-CH\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {};\n\tglobalThis.document.cookie = \"PARAGLIDE_LOCALE=de-ch;\";\n\n\texpect(runtime.extractLocaleFromCookie()).toBe(\"de-CH\");\n});\n\n// useful scenario that avoids throws if the cookie uses an old locale that is\n// not supported anymore or development on localhost shares multiple apps with\n// different locales\ntest(\"returns undefined if the locale is not defined in the locales\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {};\n\tglobalThis.document.cookie = \"PARAGLIDE_LOCALE=fr;\";\n\n\tconst locale = runtime.extractLocaleFromCookie();\n\texpect(locale).toBeUndefined();\n});\n"]}
1
+ {"version":3,"file":"extract-locale-from-cookie.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/extract-locale-from-cookie.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { test, expect } from \"vitest\";\nimport { createParaglide } from \"../create-paraglide.js\";\nimport { newProject } from \"@inlang/sdk\";\n\ntest(\"returns undefined if document is not available\", async () => {\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = undefined;\n\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t});\n\n\texpect(runtime.extractLocaleFromCookie()).toBeUndefined();\n});\n\ntest(\"matches the locale of a cookie\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {};\n\tglobalThis.document.cookie =\n\t\t\"OTHER_COOKIE=fr; PARAGLIDE_LOCALE=de; ANOTHER_COOKIE=en; EXPIRES_COOKIE=es; Max-Age=3600\";\n\n\tconst locale = runtime.extractLocaleFromCookie();\n\texpect(locale).toBe(\"de\");\n});\n\ntest(\"caches the locale during immediate repeated cookie reads\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\tlet cookieReadCount = 0;\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {\n\t\tget cookie() {\n\t\t\tcookieReadCount += 1;\n\t\t\treturn \"OTHER_COOKIE=fr; PARAGLIDE_LOCALE=de; ANOTHER_COOKIE=en;\";\n\t\t},\n\t};\n\n\texpect(runtime.extractLocaleFromCookie()).toBe(\"de\");\n\texpect(runtime.extractLocaleFromCookie()).toBe(\"de\");\n\texpect(cookieReadCount).toBe(1);\n});\n\ntest(\"cache expires after a microtask\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\tlet cookie = \"PARAGLIDE_LOCALE=en;\";\n\tlet cookieReadCount = 0;\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {\n\t\tget cookie() {\n\t\t\tcookieReadCount += 1;\n\t\t\treturn cookie;\n\t\t},\n\t};\n\n\texpect(runtime.extractLocaleFromCookie()).toBe(\"en\");\n\tcookie = \"PARAGLIDE_LOCALE=de;\";\n\texpect(runtime.extractLocaleFromCookie()).toBe(\"en\");\n\n\tawait Promise.resolve();\n\n\texpect(runtime.extractLocaleFromCookie()).toBe(\"de\");\n\texpect(cookieReadCount).toBe(2);\n});\n\ntest(\"clears the cache after setLocale writes the cookie\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\tisServer: \"false\",\n\t});\n\n\tlet cookie = \"PARAGLIDE_LOCALE=en;\";\n\tlet cookieReadCount = 0;\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {\n\t\tget cookie() {\n\t\t\tcookieReadCount += 1;\n\t\t\treturn cookie;\n\t\t},\n\t\tset cookie(value) {\n\t\t\tcookie = value;\n\t\t},\n\t};\n\t(globalThis as any).window = {\n\t\tlocation: { href: \"https://example.com\", reload() {} },\n\t};\n\n\texpect(runtime.extractLocaleFromCookie()).toBe(\"en\");\n\truntime.setLocale(\"de\", { reload: false });\n\texpect(runtime.extractLocaleFromCookie()).toBe(\"de\");\n\texpect(cookieReadCount).toBe(2);\n});\n\ntest(\"escapes regex characters in cookie names\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE.LOCALE[app]\",\n\t});\n\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {};\n\tglobalThis.document.cookie =\n\t\t\"PARAGLIDE_LOCALE_APP_=en;PARAGLIDE.LOCALE[app]=de;\";\n\n\texpect(runtime.extractLocaleFromCookie()).toBe(\"de\");\n});\n\ntest(\"returns undefined for an empty cookie locale\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {};\n\tglobalThis.document.cookie = \"OTHER_COOKIE=de;PARAGLIDE_LOCALE=;\";\n\n\texpect(runtime.extractLocaleFromCookie()).toBeUndefined();\n});\n\ntest(\"canonicalizes cookie locale casing\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de-CH\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {};\n\tglobalThis.document.cookie = \"PARAGLIDE_LOCALE=de-ch;\";\n\n\texpect(runtime.extractLocaleFromCookie()).toBe(\"de-CH\");\n});\n\n// useful scenario that avoids throws if the cookie uses an old locale that is\n// not supported anymore or development on localhost shares multiple apps with\n// different locales\ntest(\"returns undefined if the locale is not defined in the locales\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {};\n\tglobalThis.document.cookie = \"PARAGLIDE_LOCALE=fr;\";\n\n\tconst locale = runtime.extractLocaleFromCookie();\n\texpect(locale).toBeUndefined();\n});\n\n// sandboxed iframes without allow-same-origin have an opaque origin and throw\n// a SecurityError when document.cookie is read\ntest(\"returns undefined if reading document.cookie throws\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\tlet cookieReadCount = 0;\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {\n\t\tget cookie(): string {\n\t\t\tcookieReadCount += 1;\n\t\t\tthrow new DOMException(\"The operation is insecure.\", \"SecurityError\");\n\t\t},\n\t};\n\n\texpect(runtime.extractLocaleFromCookie()).toBeUndefined();\n\texpect(runtime.extractLocaleFromCookie()).toBeUndefined();\n\t// a failed read is not cached\n\texpect(cookieReadCount).toBe(2);\n});\n"]}
@@ -1,4 +1,6 @@
1
1
  import { toLocale } from "./check-locale.js";
2
+ // qvalue grammar from https://www.rfc-editor.org/rfc/rfc9110#section-12.4.2
3
+ const qvaluePattern = /^(?:0(?:\.\d{0,3})?|1(?:\.0{0,3})?)$/;
2
4
  /**
3
5
  * Extracts a locale from the accept-language header.
4
6
  *
@@ -15,18 +17,21 @@ export function extractLocaleFromHeader(request) {
15
17
  const acceptLanguageHeader = request.headers.get("accept-language");
16
18
  if (acceptLanguageHeader) {
17
19
  // Parse language preferences with their q-values and base language codes
18
- const languages = acceptLanguageHeader
19
- .split(",")
20
+ const languages = splitOutsideQuotes(acceptLanguageHeader, ",")
20
21
  .map((lang) => {
21
- const [tag, q = "1"] = lang.trim().split(";q=");
22
+ const [tag = "", ...params] = splitOutsideQuotes(lang, ";").map((p) => p.trim());
23
+ const weight = params.find((param) => /^q(?:\s|=|$)/i.test(param));
24
+ const q = weight ? weight.replace(/^q\s*=\s*/i, "") : "1";
22
25
  // Get both the full tag and base language code
23
- const baseTag = tag?.split("-")[0];
26
+ const baseTag = tag.split("-")[0];
24
27
  return {
25
28
  fullTag: tag,
26
29
  baseTag,
27
- q: Number(q),
30
+ q: qvaluePattern.test(q) ? Number(q) : 0,
28
31
  };
29
32
  })
33
+ // Skip ranges that are not acceptable (q=0) or have an invalid weight
34
+ .filter((lang) => lang.q > 0)
30
35
  .sort((a, b) => b.q - a.q);
31
36
  for (const lang of languages) {
32
37
  const fullLocale = toLocale(lang.fullTag);
@@ -42,4 +47,41 @@ export function extractLocaleFromHeader(request) {
42
47
  }
43
48
  return undefined;
44
49
  }
50
+ /**
51
+ * Splits on a separator that is not inside a quoted string, since parameter
52
+ * values may be quoted strings that contain `,` or `;`.
53
+ *
54
+ * @param {string} value
55
+ * @param {string} separator
56
+ * @returns {string[]}
57
+ */
58
+ function splitOutsideQuotes(value, separator) {
59
+ /** @type {string[]} */
60
+ const parts = [];
61
+ let start = 0;
62
+ // Index of the quote that opened the current quoted string, or -1
63
+ let quoteStart = -1;
64
+ for (let i = 0; i < value.length; i++) {
65
+ const char = value[i];
66
+ if (quoteStart !== -1 && char === "\\") {
67
+ i++;
68
+ }
69
+ else if (char === '"') {
70
+ quoteStart = quoteStart === -1 ? i : -1;
71
+ }
72
+ else if (quoteStart === -1 && char === separator) {
73
+ parts.push(value.slice(start, i));
74
+ start = i + 1;
75
+ }
76
+ }
77
+ if (quoteStart === -1) {
78
+ parts.push(value.slice(start));
79
+ return parts;
80
+ }
81
+ // A quote that is never closed would swallow everything after it, so only
82
+ // the text from that quote on is split as if it weren't quoted
83
+ const [rest = "", ...tail] = value.slice(quoteStart).split(separator);
84
+ parts.push(value.slice(start, quoteStart) + rest, ...tail);
85
+ return parts;
86
+ }
45
87
  //# sourceMappingURL=extract-locale-from-header.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"extract-locale-from-header.js","sourceRoot":"","sources":["../../../src/compiler/runtime/extract-locale-from-header.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAO;IAC9C,MAAM,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACpE,IAAI,oBAAoB,EAAE,CAAC;QAC1B,yEAAyE;QACzE,MAAM,SAAS,GAAG,oBAAoB;aACpC,KAAK,CAAC,GAAG,CAAC;aACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACb,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAChD,+CAA+C;YAC/C,MAAM,OAAO,GAAG,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACnC,OAAO;gBACN,OAAO,EAAE,GAAG;gBACZ,OAAO;gBACP,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;aACZ,CAAC;QACH,CAAC,CAAC;aACD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAE5B,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC9B,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC1C,IAAI,UAAU,EAAE,CAAC;gBAChB,OAAO,UAAU,CAAC;YACnB,CAAC;YAED,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC1C,IAAI,UAAU,EAAE,CAAC;gBAChB,OAAO,UAAU,CAAC;YACnB,CAAC;QACF,CAAC;QAED,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,OAAO,SAAS,CAAC;AAClB,CAAC","sourcesContent":["import { toLocale } from \"./check-locale.js\";\n\n/**\n * Extracts a locale from the accept-language header.\n *\n * Use the function on the server to extract the locale\n * from the accept-language header that is sent by the client.\n *\n * @example\n * const locale = extractLocaleFromHeader(request);\n *\n * @param {Request} request - The request object to extract the locale from.\n * @returns {Locale | undefined} The negotiated preferred language.\n */\nexport function extractLocaleFromHeader(request) {\n\tconst acceptLanguageHeader = request.headers.get(\"accept-language\");\n\tif (acceptLanguageHeader) {\n\t\t// Parse language preferences with their q-values and base language codes\n\t\tconst languages = acceptLanguageHeader\n\t\t\t.split(\",\")\n\t\t\t.map((lang) => {\n\t\t\t\tconst [tag, q = \"1\"] = lang.trim().split(\";q=\");\n\t\t\t\t// Get both the full tag and base language code\n\t\t\t\tconst baseTag = tag?.split(\"-\")[0];\n\t\t\t\treturn {\n\t\t\t\t\tfullTag: tag,\n\t\t\t\t\tbaseTag,\n\t\t\t\t\tq: Number(q),\n\t\t\t\t};\n\t\t\t})\n\t\t\t.sort((a, b) => b.q - a.q);\n\n\t\tfor (const lang of languages) {\n\t\t\tconst fullLocale = toLocale(lang.fullTag);\n\t\t\tif (fullLocale) {\n\t\t\t\treturn fullLocale;\n\t\t\t}\n\n\t\t\tconst baseLocale = toLocale(lang.baseTag);\n\t\t\tif (baseLocale) {\n\t\t\t\treturn baseLocale;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\treturn undefined;\n}\n"]}
1
+ {"version":3,"file":"extract-locale-from-header.js","sourceRoot":"","sources":["../../../src/compiler/runtime/extract-locale-from-header.js"],"names":[],"mappings":"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","sourcesContent":["import { toLocale } from \"./check-locale.js\";\n\n// qvalue grammar from https://www.rfc-editor.org/rfc/rfc9110#section-12.4.2\nconst qvaluePattern = /^(?:0(?:\\.\\d{0,3})?|1(?:\\.0{0,3})?)$/;\n\n/**\n * Extracts a locale from the accept-language header.\n *\n * Use the function on the server to extract the locale\n * from the accept-language header that is sent by the client.\n *\n * @example\n * const locale = extractLocaleFromHeader(request);\n *\n * @param {Request} request - The request object to extract the locale from.\n * @returns {Locale | undefined} The negotiated preferred language.\n */\nexport function extractLocaleFromHeader(request) {\n\tconst acceptLanguageHeader = request.headers.get(\"accept-language\");\n\tif (acceptLanguageHeader) {\n\t\t// Parse language preferences with their q-values and base language codes\n\t\tconst languages = splitOutsideQuotes(acceptLanguageHeader, \",\")\n\t\t\t.map((lang) => {\n\t\t\t\tconst [tag = \"\", ...params] = splitOutsideQuotes(lang, \";\").map((p) =>\n\t\t\t\t\tp.trim()\n\t\t\t\t);\n\t\t\t\tconst weight = params.find((param) => /^q(?:\\s|=|$)/i.test(param));\n\t\t\t\tconst q = weight ? weight.replace(/^q\\s*=\\s*/i, \"\") : \"1\";\n\t\t\t\t// Get both the full tag and base language code\n\t\t\t\tconst baseTag = tag.split(\"-\")[0];\n\t\t\t\treturn {\n\t\t\t\t\tfullTag: tag,\n\t\t\t\t\tbaseTag,\n\t\t\t\t\tq: qvaluePattern.test(q) ? Number(q) : 0,\n\t\t\t\t};\n\t\t\t})\n\t\t\t// Skip ranges that are not acceptable (q=0) or have an invalid weight\n\t\t\t.filter((lang) => lang.q > 0)\n\t\t\t.sort((a, b) => b.q - a.q);\n\n\t\tfor (const lang of languages) {\n\t\t\tconst fullLocale = toLocale(lang.fullTag);\n\t\t\tif (fullLocale) {\n\t\t\t\treturn fullLocale;\n\t\t\t}\n\n\t\t\tconst baseLocale = toLocale(lang.baseTag);\n\t\t\tif (baseLocale) {\n\t\t\t\treturn baseLocale;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Splits on a separator that is not inside a quoted string, since parameter\n * values may be quoted strings that contain `,` or `;`.\n *\n * @param {string} value\n * @param {string} separator\n * @returns {string[]}\n */\nfunction splitOutsideQuotes(value, separator) {\n\t/** @type {string[]} */\n\tconst parts = [];\n\tlet start = 0;\n\t// Index of the quote that opened the current quoted string, or -1\n\tlet quoteStart = -1;\n\tfor (let i = 0; i < value.length; i++) {\n\t\tconst char = value[i];\n\t\tif (quoteStart !== -1 && char === \"\\\\\") {\n\t\t\ti++;\n\t\t} else if (char === '\"') {\n\t\t\tquoteStart = quoteStart === -1 ? i : -1;\n\t\t} else if (quoteStart === -1 && char === separator) {\n\t\t\tparts.push(value.slice(start, i));\n\t\t\tstart = i + 1;\n\t\t}\n\t}\n\tif (quoteStart === -1) {\n\t\tparts.push(value.slice(start));\n\t\treturn parts;\n\t}\n\t// A quote that is never closed would swallow everything after it, so only\n\t// the text from that quote on is split as if it weren't quoted\n\tconst [rest = \"\", ...tail] = value.slice(quoteStart).split(separator);\n\tparts.push(value.slice(start, quoteStart) + rest, ...tail);\n\treturn parts;\n}\n"]}
@@ -65,4 +65,62 @@ test("canonicalizes matched locale from Accept-Language header", async () => {
65
65
  });
66
66
  expect(runtime.extractLocaleFromHeader(request)).toBe("pt-BR");
67
67
  });
68
+ // https://www.rfc-editor.org/rfc/rfc9110#section-12.4.2
69
+ test.each([
70
+ // q=0 means "not acceptable"
71
+ { header: "fr;q=0", expected: undefined },
72
+ { header: "fr;q=0.000, nl", expected: undefined },
73
+ // whitespace, uppercase Q and other parameters, with the weight still applied
74
+ { header: "fr; q=0.5, de;q=0.4", expected: "fr" },
75
+ { header: "fr; q=0.5, de;q=0.8", expected: "de" },
76
+ { header: "fr ; q = 0.5, de;q=0.4", expected: "fr" },
77
+ { header: "fr ; q = 0.5, de;q=0.8", expected: "de" },
78
+ { header: "fr;Q=0.9, de;q=0.8", expected: "fr" },
79
+ { header: "fr;Q=0.5, de;q=0.8", expected: "de" },
80
+ { header: "fr;level=1;q=0.9, de;q=0.8", expected: "fr" },
81
+ { header: "fr;level=1;q=0.5, de;q=0.8", expected: "de" },
82
+ // delimiters inside quoted parameter values
83
+ { header: 'fr;note="x;q=1;y";q=0, de;q=0.5', expected: "de" },
84
+ { header: 'fr;note="x;q=0;y";q=0.9, de;q=0.5', expected: "fr" },
85
+ { header: 'fr;note="a,b";q=0.5, de;q=0.8', expected: "de" },
86
+ { header: 'fr;note="a\\";q=0";q=0.9, de;q=0.5', expected: "fr" },
87
+ // an unclosed quote neither hides later ranges nor changes earlier ones
88
+ { header: 'en", fr', expected: "fr" },
89
+ { header: 'fr";q=0.9, de', expected: "de" },
90
+ { header: 'fr;note="a,b";q=0, de;q=0.5, en"', expected: "de" },
91
+ { header: 'fr;note="a,b";q=2, de;q=0.5, en"', expected: "de" },
92
+ { header: 'nl;note="x,fr,y";q=0, de;q=0.5, en"', expected: "de" },
93
+ // weights outside the qvalue grammar
94
+ { header: "fr;q=abc, de;q=0.5", expected: "de" },
95
+ { header: "fr;q=2, de;q=0.5", expected: "de" },
96
+ { header: "fr;q=-1", expected: undefined },
97
+ { header: "fr;q=0.1234", expected: undefined },
98
+ { header: "fr;q=1.001", expected: undefined },
99
+ { header: "fr;q=1.0000", expected: undefined },
100
+ { header: "fr;q=.5, de;q=0.4", expected: "de" },
101
+ { header: "fr;q, de;q=0.5", expected: "de" },
102
+ { header: "de;q 0,en;q=0.5", expected: "en" },
103
+ { header: "fr;q 0.5, de;q=0.4", expected: "de" },
104
+ { header: "fr;q=, de;q=0.5", expected: "de" },
105
+ // valid weights and ties
106
+ { header: "fr;q=1.000, de;q=0.5", expected: "fr" },
107
+ { header: "fr;q=1., de;q=0.5", expected: "fr" },
108
+ { header: "fr;q=0.001", expected: "fr" },
109
+ { header: "fr;q=0.8, de;q=0.8", expected: "fr" },
110
+ ])("applies Accept-Language quality values in $header", async ({ header, expected }) => {
111
+ const runtime = await createParaglide({
112
+ blob: await newProject({
113
+ settings: {
114
+ baseLocale: "en",
115
+ locales: ["en", "fr", "de"],
116
+ },
117
+ }),
118
+ });
119
+ const request = new Request("http://example.com", {
120
+ headers: {
121
+ "Accept-Language": header,
122
+ },
123
+ });
124
+ expect(runtime.extractLocaleFromHeader(request)).toBe(expected);
125
+ });
68
126
  //# sourceMappingURL=extract-locale-from-header.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"extract-locale-from-header.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/extract-locale-from-header.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,IAAI,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;IAC3E,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;aAC3B;SACD,CAAC;KACF,CAAC,CAAC;IAEH,sBAAsB;IACtB,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,oBAAoB,EAAE;QACjD,OAAO,EAAE;YACR,iBAAiB,EAAE,+BAA+B;SAClD;KACD,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACxD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE1B,qBAAqB;IACrB,MAAM,QAAQ,GAAG,IAAI,OAAO,CAAC,oBAAoB,EAAE;QAClD,OAAO,EAAE;YACR,iBAAiB,EAAE,+BAA+B;SAClD;KACD,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,OAAO,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAC1D,+EAA+E;IAC/E,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE3B,WAAW;IACX,MAAM,QAAQ,GAAG,IAAI,OAAO,CAAC,oBAAoB,EAAE;QAClD,OAAO,EAAE;YACR,iBAAiB,EAAE,mBAAmB;SACtC;KACD,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,OAAO,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAC1D,MAAM,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,CAAC;AACjC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;IAC5E,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;aAC3B;SACD,CAAC;KACF,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACxD,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,CAAC;AAChC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;IAC3E,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC;aACxB;SACD,CAAC;KACF,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,oBAAoB,EAAE;QACjD,OAAO,EAAE;YACR,iBAAiB,EAAE,iBAAiB;SACpC;KACD,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAChE,CAAC,CAAC,CAAC","sourcesContent":["import { test, expect } from \"vitest\";\nimport { createParaglide } from \"../create-paraglide.js\";\nimport { newProject } from \"@inlang/sdk\";\n\ntest(\"returns the preferred locale from Accept-Language header\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\", \"de\"],\n\t\t\t},\n\t\t}),\n\t});\n\n\t// simple direct match\n\tconst request = new Request(\"http://example.com\", {\n\t\theaders: {\n\t\t\t\"Accept-Language\": \"en-US;q=0.8,fr;q=0.9,de;q=0.6\",\n\t\t},\n\t});\n\tconst locale = runtime.extractLocaleFromHeader(request);\n\texpect(locale).toBe(\"fr\");\n\n\t// language tag match\n\tconst request2 = new Request(\"http://example.com\", {\n\t\theaders: {\n\t\t\t\"Accept-Language\": \"en-US;q=0.8,nl;q=0.9,de;q=0.7\",\n\t\t},\n\t});\n\tconst locale2 = runtime.extractLocaleFromHeader(request2);\n\t// Since \"nl\" isn't in our locales, and \"de\" has the highest q-value after \"nl\"\n\texpect(locale2).toBe(\"en\");\n\n\t// no match\n\tconst request3 = new Request(\"http://example.com\", {\n\t\theaders: {\n\t\t\t\"Accept-Language\": \"nl;q=0.9,es;q=0.6\",\n\t\t},\n\t});\n\tconst locale3 = runtime.extractLocaleFromHeader(request3);\n\texpect(locale3).toBeUndefined();\n});\n\ntest(\"returns undefined if no Accept-Language header is present\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\", \"de\"],\n\t\t\t},\n\t\t}),\n\t});\n\n\tconst request = new Request(\"http://example.com\");\n\tconst locale = runtime.extractLocaleFromHeader(request);\n\texpect(locale).toBeUndefined();\n});\n\ntest(\"canonicalizes matched locale from Accept-Language header\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"pt-BR\"],\n\t\t\t},\n\t\t}),\n\t});\n\n\tconst request = new Request(\"http://example.com\", {\n\t\theaders: {\n\t\t\t\"Accept-Language\": \"pt-br, en;q=0.8\",\n\t\t},\n\t});\n\n\texpect(runtime.extractLocaleFromHeader(request)).toBe(\"pt-BR\");\n});\n"]}
1
+ {"version":3,"file":"extract-locale-from-header.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/extract-locale-from-header.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,IAAI,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;IAC3E,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;aAC3B;SACD,CAAC;KACF,CAAC,CAAC;IAEH,sBAAsB;IACtB,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,oBAAoB,EAAE;QACjD,OAAO,EAAE;YACR,iBAAiB,EAAE,+BAA+B;SAClD;KACD,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACxD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE1B,qBAAqB;IACrB,MAAM,QAAQ,GAAG,IAAI,OAAO,CAAC,oBAAoB,EAAE;QAClD,OAAO,EAAE;YACR,iBAAiB,EAAE,+BAA+B;SAClD;KACD,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,OAAO,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAC1D,+EAA+E;IAC/E,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE3B,WAAW;IACX,MAAM,QAAQ,GAAG,IAAI,OAAO,CAAC,oBAAoB,EAAE;QAClD,OAAO,EAAE;YACR,iBAAiB,EAAE,mBAAmB;SACtC;KACD,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,OAAO,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAC1D,MAAM,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,CAAC;AACjC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;IAC5E,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;aAC3B;SACD,CAAC;KACF,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACxD,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,CAAC;AAChC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;IAC3E,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC;aACxB;SACD,CAAC;KACF,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,oBAAoB,EAAE;QACjD,OAAO,EAAE;YACR,iBAAiB,EAAE,iBAAiB;SACpC;KACD,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAChE,CAAC,CAAC,CAAC;AAEH,wDAAwD;AACxD,IAAI,CAAC,IAAI,CAAC;IACT,6BAA6B;IAC7B,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE;IACzC,EAAE,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,SAAS,EAAE;IACjD,8EAA8E;IAC9E,EAAE,MAAM,EAAE,qBAAqB,EAAE,QAAQ,EAAE,IAAI,EAAE;IACjD,EAAE,MAAM,EAAE,qBAAqB,EAAE,QAAQ,EAAE,IAAI,EAAE;IACjD,EAAE,MAAM,EAAE,wBAAwB,EAAE,QAAQ,EAAE,IAAI,EAAE;IACpD,EAAE,MAAM,EAAE,wBAAwB,EAAE,QAAQ,EAAE,IAAI,EAAE;IACpD,EAAE,MAAM,EAAE,oBAAoB,EAAE,QAAQ,EAAE,IAAI,EAAE;IAChD,EAAE,MAAM,EAAE,oBAAoB,EAAE,QAAQ,EAAE,IAAI,EAAE;IAChD,EAAE,MAAM,EAAE,4BAA4B,EAAE,QAAQ,EAAE,IAAI,EAAE;IACxD,EAAE,MAAM,EAAE,4BAA4B,EAAE,QAAQ,EAAE,IAAI,EAAE;IACxD,4CAA4C;IAC5C,EAAE,MAAM,EAAE,iCAAiC,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC7D,EAAE,MAAM,EAAE,mCAAmC,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC/D,EAAE,MAAM,EAAE,+BAA+B,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC3D,EAAE,MAAM,EAAE,oCAAoC,EAAE,QAAQ,EAAE,IAAI,EAAE;IAChE,wEAAwE;IACxE,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE;IACrC,EAAE,MAAM,EAAE,eAAe,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC3C,EAAE,MAAM,EAAE,kCAAkC,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC9D,EAAE,MAAM,EAAE,kCAAkC,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC9D,EAAE,MAAM,EAAE,qCAAqC,EAAE,QAAQ,EAAE,IAAI,EAAE;IACjE,qCAAqC;IACrC,EAAE,MAAM,EAAE,oBAAoB,EAAE,QAAQ,EAAE,IAAI,EAAE;IAChD,EAAE,MAAM,EAAE,kBAAkB,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC9C,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE;IAC1C,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE;IAC9C,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE;IAC7C,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE;IAC9C,EAAE,MAAM,EAAE,mBAAmB,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC/C,EAAE,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC5C,EAAE,MAAM,EAAE,iBAAiB,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC7C,EAAE,MAAM,EAAE,oBAAoB,EAAE,QAAQ,EAAE,IAAI,EAAE;IAChD,EAAE,MAAM,EAAE,iBAAiB,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC7C,yBAAyB;IACzB,EAAE,MAAM,EAAE,sBAAsB,EAAE,QAAQ,EAAE,IAAI,EAAE;IAClD,EAAE,MAAM,EAAE,mBAAmB,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC/C,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,EAAE;IACxC,EAAE,MAAM,EAAE,oBAAoB,EAAE,QAAQ,EAAE,IAAI,EAAE;CAChD,CAAC,CACD,mDAAmD,EACnD,KAAK,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE;IAC9B,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC;QACrC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;aAC3B;SACD,CAAC;KACF,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,oBAAoB,EAAE;QACjD,OAAO,EAAE;YACR,iBAAiB,EAAE,MAAM;SACzB;KACD,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACjE,CAAC,CACD,CAAC","sourcesContent":["import { test, expect } from \"vitest\";\nimport { createParaglide } from \"../create-paraglide.js\";\nimport { newProject } from \"@inlang/sdk\";\n\ntest(\"returns the preferred locale from Accept-Language header\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\", \"de\"],\n\t\t\t},\n\t\t}),\n\t});\n\n\t// simple direct match\n\tconst request = new Request(\"http://example.com\", {\n\t\theaders: {\n\t\t\t\"Accept-Language\": \"en-US;q=0.8,fr;q=0.9,de;q=0.6\",\n\t\t},\n\t});\n\tconst locale = runtime.extractLocaleFromHeader(request);\n\texpect(locale).toBe(\"fr\");\n\n\t// language tag match\n\tconst request2 = new Request(\"http://example.com\", {\n\t\theaders: {\n\t\t\t\"Accept-Language\": \"en-US;q=0.8,nl;q=0.9,de;q=0.7\",\n\t\t},\n\t});\n\tconst locale2 = runtime.extractLocaleFromHeader(request2);\n\t// Since \"nl\" isn't in our locales, and \"de\" has the highest q-value after \"nl\"\n\texpect(locale2).toBe(\"en\");\n\n\t// no match\n\tconst request3 = new Request(\"http://example.com\", {\n\t\theaders: {\n\t\t\t\"Accept-Language\": \"nl;q=0.9,es;q=0.6\",\n\t\t},\n\t});\n\tconst locale3 = runtime.extractLocaleFromHeader(request3);\n\texpect(locale3).toBeUndefined();\n});\n\ntest(\"returns undefined if no Accept-Language header is present\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\", \"de\"],\n\t\t\t},\n\t\t}),\n\t});\n\n\tconst request = new Request(\"http://example.com\");\n\tconst locale = runtime.extractLocaleFromHeader(request);\n\texpect(locale).toBeUndefined();\n});\n\ntest(\"canonicalizes matched locale from Accept-Language header\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"pt-BR\"],\n\t\t\t},\n\t\t}),\n\t});\n\n\tconst request = new Request(\"http://example.com\", {\n\t\theaders: {\n\t\t\t\"Accept-Language\": \"pt-br, en;q=0.8\",\n\t\t},\n\t});\n\n\texpect(runtime.extractLocaleFromHeader(request)).toBe(\"pt-BR\");\n});\n\n// https://www.rfc-editor.org/rfc/rfc9110#section-12.4.2\ntest.each([\n\t// q=0 means \"not acceptable\"\n\t{ header: \"fr;q=0\", expected: undefined },\n\t{ header: \"fr;q=0.000, nl\", expected: undefined },\n\t// whitespace, uppercase Q and other parameters, with the weight still applied\n\t{ header: \"fr; q=0.5, de;q=0.4\", expected: \"fr\" },\n\t{ header: \"fr; q=0.5, de;q=0.8\", expected: \"de\" },\n\t{ header: \"fr ; q = 0.5, de;q=0.4\", expected: \"fr\" },\n\t{ header: \"fr ; q = 0.5, de;q=0.8\", expected: \"de\" },\n\t{ header: \"fr;Q=0.9, de;q=0.8\", expected: \"fr\" },\n\t{ header: \"fr;Q=0.5, de;q=0.8\", expected: \"de\" },\n\t{ header: \"fr;level=1;q=0.9, de;q=0.8\", expected: \"fr\" },\n\t{ header: \"fr;level=1;q=0.5, de;q=0.8\", expected: \"de\" },\n\t// delimiters inside quoted parameter values\n\t{ header: 'fr;note=\"x;q=1;y\";q=0, de;q=0.5', expected: \"de\" },\n\t{ header: 'fr;note=\"x;q=0;y\";q=0.9, de;q=0.5', expected: \"fr\" },\n\t{ header: 'fr;note=\"a,b\";q=0.5, de;q=0.8', expected: \"de\" },\n\t{ header: 'fr;note=\"a\\\\\";q=0\";q=0.9, de;q=0.5', expected: \"fr\" },\n\t// an unclosed quote neither hides later ranges nor changes earlier ones\n\t{ header: 'en\", fr', expected: \"fr\" },\n\t{ header: 'fr\";q=0.9, de', expected: \"de\" },\n\t{ header: 'fr;note=\"a,b\";q=0, de;q=0.5, en\"', expected: \"de\" },\n\t{ header: 'fr;note=\"a,b\";q=2, de;q=0.5, en\"', expected: \"de\" },\n\t{ header: 'nl;note=\"x,fr,y\";q=0, de;q=0.5, en\"', expected: \"de\" },\n\t// weights outside the qvalue grammar\n\t{ header: \"fr;q=abc, de;q=0.5\", expected: \"de\" },\n\t{ header: \"fr;q=2, de;q=0.5\", expected: \"de\" },\n\t{ header: \"fr;q=-1\", expected: undefined },\n\t{ header: \"fr;q=0.1234\", expected: undefined },\n\t{ header: \"fr;q=1.001\", expected: undefined },\n\t{ header: \"fr;q=1.0000\", expected: undefined },\n\t{ header: \"fr;q=.5, de;q=0.4\", expected: \"de\" },\n\t{ header: \"fr;q, de;q=0.5\", expected: \"de\" },\n\t{ header: \"de;q 0,en;q=0.5\", expected: \"en\" },\n\t{ header: \"fr;q 0.5, de;q=0.4\", expected: \"de\" },\n\t{ header: \"fr;q=, de;q=0.5\", expected: \"de\" },\n\t// valid weights and ties\n\t{ header: \"fr;q=1.000, de;q=0.5\", expected: \"fr\" },\n\t{ header: \"fr;q=1., de;q=0.5\", expected: \"fr\" },\n\t{ header: \"fr;q=0.001\", expected: \"fr\" },\n\t{ header: \"fr;q=0.8, de;q=0.8\", expected: \"fr\" },\n])(\n\t\"applies Accept-Language quality values in $header\",\n\tasync ({ header, expected }) => {\n\t\tconst runtime = await createParaglide({\n\t\t\tblob: await newProject({\n\t\t\t\tsettings: {\n\t\t\t\t\tbaseLocale: \"en\",\n\t\t\t\t\tlocales: [\"en\", \"fr\", \"de\"],\n\t\t\t\t},\n\t\t\t}),\n\t\t});\n\n\t\tconst request = new Request(\"http://example.com\", {\n\t\t\theaders: {\n\t\t\t\t\"Accept-Language\": header,\n\t\t\t},\n\t\t});\n\n\t\texpect(runtime.extractLocaleFromHeader(request)).toBe(expected);\n\t}\n);\n"]}
@@ -114,7 +114,13 @@ function resolveLocaleWithStrategies(strategyToUse, urlForUrlStrategy) {
114
114
  else if (TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED &&
115
115
  strat === "localStorage" &&
116
116
  !isServer) {
117
- locale = localStorage.getItem(localStorageKey) ?? undefined;
117
+ try {
118
+ locale = localStorage.getItem(localStorageKey) ?? undefined;
119
+ }
120
+ catch {
121
+ // localStorage throws when storage access is denied
122
+ locale = undefined;
123
+ }
118
124
  }
119
125
  else if (isCustomStrategy(strat) && customClientStrategies.has(strat)) {
120
126
  const handler = customClientStrategies.get(strat);
@@ -1 +1 @@
1
- {"version":3,"file":"get-locale.js","sourceRoot":"","sources":["../../../src/compiler/runtime/get-locale.js"],"names":[],"mappings":"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","sourcesContent":["import { assertIsLocale, toLocale } from \"./check-locale.js\";\nimport { extractLocaleFromCookie } from \"./extract-locale-from-cookie.js\";\nimport { extractLocaleFromNavigator } from \"./extract-locale-from-navigator.js\";\nimport { extractLocaleFromUrl } from \"./extract-locale-from-url.js\";\nimport { getStrategyForUrl } from \"./route-strategy.js\";\nimport { setLocale } from \"./set-locale.js\";\nimport { customClientStrategies, isCustomStrategy } from \"./strategy.js\";\nimport {\n\tbaseLocale,\n\tisServer,\n\tlocalStorageKey,\n\tserverAsyncLocalStorage,\n\texperimentalStaticLocale,\n\tstrategy,\n\tTREE_SHAKE_COOKIE_STRATEGY_USED,\n\tTREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED,\n\tTREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED,\n\tTREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED,\n\tTREE_SHAKE_URL_STRATEGY_USED,\n} from \"./variables.js\";\n\n/**\n * This is a fallback to get started with a custom\n * strategy and avoid type errors.\n *\n * The implementation is overwritten\n * by `overwriteGetLocale()` and `defineSetLocale()`.\n *\n * @type {Locale | undefined}\n */\nlet _locale;\n\nlet localeInitiallySet = false;\n\n/**\n * Get the current locale.\n *\n * The locale is resolved using your configured strategies (URL, cookie, localStorage, etc.)\n * in the order they are defined. In SSR contexts, the locale is retrieved from AsyncLocalStorage\n * which is set by the `paraglideMiddleware()`.\n *\n * @see https://paraglidejs.com/strategy - Configure locale detection strategies\n *\n * @example\n * if (getLocale() === 'de') {\n * console.log('Germany 🇩🇪');\n * } else if (getLocale() === 'nl') {\n * console.log('Netherlands 🇳🇱');\n * }\n *\n * @returns {Locale} The current locale.\n */\nexport let getLocale = () => {\n\tif (experimentalStaticLocale !== undefined) {\n\t\treturn experimentalStaticLocale;\n\t}\n\n\t// if running in a server-side rendering context\n\t// retrieve the locale from the async local storage\n\tif (serverAsyncLocalStorage) {\n\t\tconst locale = serverAsyncLocalStorage?.getStore()?.locale;\n\t\tif (locale) {\n\t\t\treturn locale;\n\t\t}\n\t}\n\n\tlet strategyToUse = strategy;\n\tif (!isServer && typeof window !== \"undefined\" && window.location?.href) {\n\t\tstrategyToUse = getStrategyForUrl(window.location.href);\n\t}\n\n\tconst resolved = resolveLocaleWithStrategies(\n\t\tstrategyToUse,\n\t\ttypeof window !== \"undefined\" ? window.location?.href : undefined\n\t);\n\tif (resolved) {\n\t\tif (!localeInitiallySet) {\n\t\t\t_locale = resolved;\n\t\t\t// https://github.com/opral/inlang-paraglide-js/issues/455\n\t\t\tlocaleInitiallySet = true;\n\t\t\tsetLocale(resolved, { reload: false });\n\t\t}\n\t\treturn resolved;\n\t}\n\n\tthrow new Error(\n\t\t\"No locale found. Read the docs https://paraglidejs.com/errors#no-locale-found\"\n\t);\n};\n\n/**\n * Resolve locale for a given URL using route-aware strategies.\n *\n * @param {string | URL} url\n * @returns {Locale}\n */\nexport function getLocaleForUrl(url) {\n\tif (experimentalStaticLocale !== undefined) {\n\t\treturn experimentalStaticLocale;\n\t}\n\n\tconst strategyToUse = getStrategyForUrl(url);\n\tconst resolved = resolveLocaleWithStrategies(\n\t\tstrategyToUse,\n\t\ttypeof url === \"string\" ? url : url.href\n\t);\n\tif (resolved) {\n\t\treturn resolved;\n\t}\n\n\tthrow new Error(\n\t\t\"No locale found. Read the docs https://paraglidejs.com/errors#no-locale-found\"\n\t);\n}\n\n/**\n * @param {typeof strategy} strategyToUse\n * @param {string | undefined} urlForUrlStrategy\n * @returns {Locale | undefined}\n */\nfunction resolveLocaleWithStrategies(strategyToUse, urlForUrlStrategy) {\n\t/** @type {string | undefined} */\n\tlet locale;\n\n\tfor (const strat of strategyToUse) {\n\t\tif (TREE_SHAKE_COOKIE_STRATEGY_USED && strat === \"cookie\") {\n\t\t\tlocale = extractLocaleFromCookie();\n\t\t} else if (strat === \"baseLocale\") {\n\t\t\tlocale = baseLocale;\n\t\t} else if (\n\t\t\tTREE_SHAKE_URL_STRATEGY_USED &&\n\t\t\tstrat === \"url\" &&\n\t\t\t!isServer &&\n\t\t\ttypeof urlForUrlStrategy === \"string\"\n\t\t) {\n\t\t\tlocale = extractLocaleFromUrl(urlForUrlStrategy);\n\t\t} else if (\n\t\t\tTREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED &&\n\t\t\tstrat === \"globalVariable\" &&\n\t\t\t_locale !== undefined\n\t\t) {\n\t\t\tlocale = _locale;\n\t\t} else if (\n\t\t\tTREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED &&\n\t\t\tstrat === \"preferredLanguage\" &&\n\t\t\t!isServer\n\t\t) {\n\t\t\tlocale = extractLocaleFromNavigator();\n\t\t} else if (\n\t\t\tTREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED &&\n\t\t\tstrat === \"localStorage\" &&\n\t\t\t!isServer\n\t\t) {\n\t\t\tlocale = localStorage.getItem(localStorageKey) ?? undefined;\n\t\t} else if (isCustomStrategy(strat) && customClientStrategies.has(strat)) {\n\t\t\tconst handler = customClientStrategies.get(strat);\n\t\t\tif (handler) {\n\t\t\t\tconst result = handler.getLocale();\n\t\t\t\t// Handle both sync and async results - skip async in sync getLocale\n\t\t\t\tif (result instanceof Promise) {\n\t\t\t\t\t// Can't await in sync function, skip async strategies\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (result !== undefined) {\n\t\t\t\t\treturn assertIsLocale(result);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst matchedLocale = toLocale(locale);\n\t\tif (matchedLocale) {\n\t\t\treturn matchedLocale;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Overwrite the `getLocale()` function.\n *\n * Use this function to overwrite how the locale is resolved. This is useful\n * for custom locale resolution or advanced use cases like SSG with concurrent rendering.\n *\n * @see https://paraglidejs.com/strategy\n *\n * @example\n * overwriteGetLocale(() => {\n * return Cookies.get('locale') ?? baseLocale\n * });\n *\n * @param {() => Locale} fn - The new implementation for `getLocale()`.\n */\nexport const overwriteGetLocale = (fn) => {\n\tgetLocale = fn;\n};\n"]}
1
+ {"version":3,"file":"get-locale.js","sourceRoot":"","sources":["../../../src/compiler/runtime/get-locale.js"],"names":[],"mappings":"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","sourcesContent":["import { assertIsLocale, toLocale } from \"./check-locale.js\";\nimport { extractLocaleFromCookie } from \"./extract-locale-from-cookie.js\";\nimport { extractLocaleFromNavigator } from \"./extract-locale-from-navigator.js\";\nimport { extractLocaleFromUrl } from \"./extract-locale-from-url.js\";\nimport { getStrategyForUrl } from \"./route-strategy.js\";\nimport { setLocale } from \"./set-locale.js\";\nimport { customClientStrategies, isCustomStrategy } from \"./strategy.js\";\nimport {\n\tbaseLocale,\n\tisServer,\n\tlocalStorageKey,\n\tserverAsyncLocalStorage,\n\texperimentalStaticLocale,\n\tstrategy,\n\tTREE_SHAKE_COOKIE_STRATEGY_USED,\n\tTREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED,\n\tTREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED,\n\tTREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED,\n\tTREE_SHAKE_URL_STRATEGY_USED,\n} from \"./variables.js\";\n\n/**\n * This is a fallback to get started with a custom\n * strategy and avoid type errors.\n *\n * The implementation is overwritten\n * by `overwriteGetLocale()` and `defineSetLocale()`.\n *\n * @type {Locale | undefined}\n */\nlet _locale;\n\nlet localeInitiallySet = false;\n\n/**\n * Get the current locale.\n *\n * The locale is resolved using your configured strategies (URL, cookie, localStorage, etc.)\n * in the order they are defined. In SSR contexts, the locale is retrieved from AsyncLocalStorage\n * which is set by the `paraglideMiddleware()`.\n *\n * @see https://paraglidejs.com/strategy - Configure locale detection strategies\n *\n * @example\n * if (getLocale() === 'de') {\n * console.log('Germany 🇩🇪');\n * } else if (getLocale() === 'nl') {\n * console.log('Netherlands 🇳🇱');\n * }\n *\n * @returns {Locale} The current locale.\n */\nexport let getLocale = () => {\n\tif (experimentalStaticLocale !== undefined) {\n\t\treturn experimentalStaticLocale;\n\t}\n\n\t// if running in a server-side rendering context\n\t// retrieve the locale from the async local storage\n\tif (serverAsyncLocalStorage) {\n\t\tconst locale = serverAsyncLocalStorage?.getStore()?.locale;\n\t\tif (locale) {\n\t\t\treturn locale;\n\t\t}\n\t}\n\n\tlet strategyToUse = strategy;\n\tif (!isServer && typeof window !== \"undefined\" && window.location?.href) {\n\t\tstrategyToUse = getStrategyForUrl(window.location.href);\n\t}\n\n\tconst resolved = resolveLocaleWithStrategies(\n\t\tstrategyToUse,\n\t\ttypeof window !== \"undefined\" ? window.location?.href : undefined\n\t);\n\tif (resolved) {\n\t\tif (!localeInitiallySet) {\n\t\t\t_locale = resolved;\n\t\t\t// https://github.com/opral/inlang-paraglide-js/issues/455\n\t\t\tlocaleInitiallySet = true;\n\t\t\tsetLocale(resolved, { reload: false });\n\t\t}\n\t\treturn resolved;\n\t}\n\n\tthrow new Error(\n\t\t\"No locale found. Read the docs https://paraglidejs.com/errors#no-locale-found\"\n\t);\n};\n\n/**\n * Resolve locale for a given URL using route-aware strategies.\n *\n * @param {string | URL} url\n * @returns {Locale}\n */\nexport function getLocaleForUrl(url) {\n\tif (experimentalStaticLocale !== undefined) {\n\t\treturn experimentalStaticLocale;\n\t}\n\n\tconst strategyToUse = getStrategyForUrl(url);\n\tconst resolved = resolveLocaleWithStrategies(\n\t\tstrategyToUse,\n\t\ttypeof url === \"string\" ? url : url.href\n\t);\n\tif (resolved) {\n\t\treturn resolved;\n\t}\n\n\tthrow new Error(\n\t\t\"No locale found. Read the docs https://paraglidejs.com/errors#no-locale-found\"\n\t);\n}\n\n/**\n * @param {typeof strategy} strategyToUse\n * @param {string | undefined} urlForUrlStrategy\n * @returns {Locale | undefined}\n */\nfunction resolveLocaleWithStrategies(strategyToUse, urlForUrlStrategy) {\n\t/** @type {string | undefined} */\n\tlet locale;\n\n\tfor (const strat of strategyToUse) {\n\t\tif (TREE_SHAKE_COOKIE_STRATEGY_USED && strat === \"cookie\") {\n\t\t\tlocale = extractLocaleFromCookie();\n\t\t} else if (strat === \"baseLocale\") {\n\t\t\tlocale = baseLocale;\n\t\t} else if (\n\t\t\tTREE_SHAKE_URL_STRATEGY_USED &&\n\t\t\tstrat === \"url\" &&\n\t\t\t!isServer &&\n\t\t\ttypeof urlForUrlStrategy === \"string\"\n\t\t) {\n\t\t\tlocale = extractLocaleFromUrl(urlForUrlStrategy);\n\t\t} else if (\n\t\t\tTREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED &&\n\t\t\tstrat === \"globalVariable\" &&\n\t\t\t_locale !== undefined\n\t\t) {\n\t\t\tlocale = _locale;\n\t\t} else if (\n\t\t\tTREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED &&\n\t\t\tstrat === \"preferredLanguage\" &&\n\t\t\t!isServer\n\t\t) {\n\t\t\tlocale = extractLocaleFromNavigator();\n\t\t} else if (\n\t\t\tTREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED &&\n\t\t\tstrat === \"localStorage\" &&\n\t\t\t!isServer\n\t\t) {\n\t\t\ttry {\n\t\t\t\tlocale = localStorage.getItem(localStorageKey) ?? undefined;\n\t\t\t} catch {\n\t\t\t\t// localStorage throws when storage access is denied\n\t\t\t\tlocale = undefined;\n\t\t\t}\n\t\t} else if (isCustomStrategy(strat) && customClientStrategies.has(strat)) {\n\t\t\tconst handler = customClientStrategies.get(strat);\n\t\t\tif (handler) {\n\t\t\t\tconst result = handler.getLocale();\n\t\t\t\t// Handle both sync and async results - skip async in sync getLocale\n\t\t\t\tif (result instanceof Promise) {\n\t\t\t\t\t// Can't await in sync function, skip async strategies\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (result !== undefined) {\n\t\t\t\t\treturn assertIsLocale(result);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst matchedLocale = toLocale(locale);\n\t\tif (matchedLocale) {\n\t\t\treturn matchedLocale;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Overwrite the `getLocale()` function.\n *\n * Use this function to overwrite how the locale is resolved. This is useful\n * for custom locale resolution or advanced use cases like SSG with concurrent rendering.\n *\n * @see https://paraglidejs.com/strategy\n *\n * @example\n * overwriteGetLocale(() => {\n * return Cookies.get('locale') ?? baseLocale\n * });\n *\n * @param {() => Locale} fn - The new implementation for `getLocale()`.\n */\nexport const overwriteGetLocale = (fn) => {\n\tgetLocale = fn;\n};\n"]}
@@ -361,4 +361,99 @@ test("custom strategy integrates with locale initialization", async () => {
361
361
  // The locale should be set internally after first resolution
362
362
  expect(setLocaleCalled).toBe(true);
363
363
  });
364
+ // sandboxed iframes without allow-same-origin have an opaque origin and throw
365
+ // a SecurityError on document.cookie and localStorage access
366
+ test("resolves the url locale when document.cookie throws", async () => {
367
+ const runtime = await createParaglide({
368
+ blob: await newProject({
369
+ settings: {
370
+ baseLocale: "en",
371
+ locales: ["en", "de"],
372
+ },
373
+ }),
374
+ strategy: ["url", "cookie"],
375
+ isServer: "false",
376
+ cookieName: "PARAGLIDE_LOCALE",
377
+ urlPatterns: [
378
+ {
379
+ pattern: "https://example.com/:path*",
380
+ localized: [
381
+ ["en", "https://example.com/en/:path*"],
382
+ ["de", "https://example.com/de/:path*"],
383
+ ],
384
+ },
385
+ ],
386
+ });
387
+ globalThis.window = {
388
+ // @ts-expect-error - global variable definition
389
+ location: {
390
+ hostname: "example.com",
391
+ href: "https://example.com/de/page",
392
+ },
393
+ };
394
+ // @ts-expect-error - global variable definition
395
+ globalThis.document = {
396
+ get cookie() {
397
+ throw new DOMException("The operation is insecure.", "SecurityError");
398
+ },
399
+ set cookie(_value) {
400
+ throw new DOMException("The operation is insecure.", "SecurityError");
401
+ },
402
+ };
403
+ // the first resolution also writes the cookie
404
+ expect(runtime.getLocale()).toBe("de");
405
+ });
406
+ test("falls back to the next strategy when document.cookie throws", async () => {
407
+ const runtime = await createParaglide({
408
+ blob: await newProject({
409
+ settings: {
410
+ baseLocale: "en",
411
+ locales: ["en", "de"],
412
+ },
413
+ }),
414
+ strategy: ["cookie", "baseLocale"],
415
+ isServer: "false",
416
+ cookieName: "PARAGLIDE_LOCALE",
417
+ });
418
+ // @ts-expect-error - global variable definition
419
+ globalThis.window = {};
420
+ // @ts-expect-error - global variable definition
421
+ globalThis.document = {
422
+ get cookie() {
423
+ throw new DOMException("The operation is insecure.", "SecurityError");
424
+ },
425
+ set cookie(_value) {
426
+ throw new DOMException("The operation is insecure.", "SecurityError");
427
+ },
428
+ };
429
+ expect(runtime.getLocale()).toBe("en");
430
+ });
431
+ test("falls back to the next strategy when localStorage throws", async () => {
432
+ const originalLocalStorage = globalThis.localStorage;
433
+ const runtime = await createParaglide({
434
+ blob: await newProject({
435
+ settings: {
436
+ baseLocale: "en",
437
+ locales: ["en", "de"],
438
+ },
439
+ }),
440
+ strategy: ["localStorage", "baseLocale"],
441
+ isServer: "false",
442
+ });
443
+ // @ts-expect-error - global variable definition
444
+ globalThis.window = {};
445
+ // browsers throw when the localStorage property itself is read
446
+ Object.defineProperty(globalThis, "localStorage", {
447
+ get() {
448
+ throw new DOMException("The operation is insecure.", "SecurityError");
449
+ },
450
+ configurable: true,
451
+ });
452
+ expect(runtime.getLocale()).toBe("en");
453
+ Object.defineProperty(globalThis, "localStorage", {
454
+ value: originalLocalStorage,
455
+ configurable: true,
456
+ writable: true,
457
+ });
458
+ });
364
459
  //# sourceMappingURL=get-locale.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-locale.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/get-locale.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { newProject } from \"@inlang/sdk\";\nimport { expect, test, vi } from \"vitest\";\nimport { createParaglide } from \"../create-paraglide.js\";\n\ntest(\"matching by strategy works\", async () => {\n\tconst baseLocale = \"en\";\n\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale,\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\", \"baseLocale\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {};\n\tglobalThis.document.cookie = \"OTHER_COOKIE=blaba;\";\n\n\tconst locale = runtime.getLocale();\n\texpect(locale).toBe(baseLocale);\n});\n\ntest(\"experimentalStaticLocale overrides runtime strategies\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\", \"baseLocale\"],\n\t\texperimentalStaticLocale: JSON.stringify(\"de\"),\n\t});\n\n\texpect(runtime.getLocale()).toBe(\"de\");\n});\n\ntest(\"invalid experimentalStaticLocale throws during runtime creation\", async () => {\n\tawait expect(\n\t\tcreateParaglide({\n\t\t\tblob: await newProject({\n\t\t\t\tsettings: {\n\t\t\t\t\tbaseLocale: \"en\",\n\t\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t\t},\n\t\t\t}),\n\t\t\tstrategy: [\"cookie\", \"baseLocale\"],\n\t\t\texperimentalStaticLocale: JSON.stringify(\"fr\"),\n\t\t})\n\t).rejects.toThrow(\"Invalid locale: fr. Expected one of: en, de\");\n});\n\ntest(\"throws if variable is used without baseLocale as fallback strategy\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"globalVariable\"],\n\t});\n\n\texpect(() => runtime.getLocale()).toThrow();\n\n\truntime.setLocale(\"de\");\n\n\texpect(runtime.getLocale()).toBe(\"de\");\n});\n\ntest(\"retrieves the locale for a url pattern\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"url\"],\n\t\tisServer: \"false\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.:tld/:path*\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/:path*\"],\n\t\t\t\t\t[\"de\", \"https://example.de/:path*\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tglobalThis.window = { location: { href: \"https://example.com/page\" } } as any;\n\n\texpect(runtime.getLocale()).toBe(\"en\");\n\n\tglobalThis.window = { location: { href: \"https://example.de/page\" } } as any;\n\n\texpect(runtime.getLocale()).toBe(\"de\");\n});\n\ntest(\"url pattern strategy doesn't throw during SSR\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"url\", \"baseLocale\"],\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.:tld/:path*\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/:path*\"],\n\t\t\t\t\t[\"de\", \"https://example.de/:path*\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\texpect(() => runtime.getLocale()).not.toThrow();\n});\n\ntest(\"doesn't throw for an old cookie locale\", async () => {\n\tconst baseLocale = \"en\";\n\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\", \"baseLocale\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {};\n\tglobalThis.document.cookie = \"PARAGLIDE_LOCALE=fr;\";\n\n\tconst locale = runtime.getLocale();\n\texpect(locale).toBe(baseLocale);\n});\n\ntest(\"returns the preferred locale from navigator.languages\", async () => {\n\tconst originalNavigator = globalThis.navigator;\n\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tisServer: \"false\",\n\t\tstrategy: [\"preferredLanguage\"],\n\t});\n\n\t// Mock navigator.languages\n\tObject.defineProperty(globalThis, \"navigator\", {\n\t\tvalue: {\n\t\t\tlanguages: [\"fr-FR\", \"en-US\", \"de\"],\n\t\t},\n\t\tconfigurable: true,\n\t});\n\n\texpect(runtime.getLocale()).toBe(\"fr\");\n\n\t// Restore original navigator\n\tObject.defineProperty(globalThis, \"navigator\", {\n\t\tvalue: originalNavigator,\n\t\tconfigurable: true,\n\t});\n});\n\ntest(\"returns the locale from local storage\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"localStorage\"],\n\t\tlocalStorageKey: \"PARAGLIDE_LOCALE\",\n\t\tisServer: \"false\",\n\t});\n\n\t// @ts-expect-error - global variable definition\n\tglobalThis.localStorage = {\n\t\tsetItem: vi.fn(),\n\t\tgetItem: vi.fn(() => \"de\"),\n\t};\n\n\texpect(runtime.getLocale()).toBe(\"de\");\n});\n\ntest(\"initially sets the locale after resolving it for the first time\", async () => {\n\t// Create runtime with multiple strategies\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"url\", \"cookie\"],\n\t\tisServer: \"false\",\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path*\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path*\"],\n\t\t\t\t\t[\"de\", \"https://example.com/de/:path*\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path*\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\t// Setup global objects for URL strategy\n\tglobalThis.window = {\n\t\t// @ts-expect-error - global variable definition\n\t\tlocation: {\n\t\t\thostname: \"example.com\",\n\t\t\thref: \"https://example.com/de/page\",\n\t\t},\n\t};\n\n\t// Create a spy on the internal _locale variable to verify it was set\n\t// We can do this by checking if setLocale works without explicitly setting it first\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = { cookie: \"\" };\n\n\t// First call to getLocale should resolve and set the locale\n\texpect(runtime.getLocale()).toBe(\"de\");\n\t// Cookie should be set, proving that the locale was initially set\n\texpect(globalThis.document.cookie).toBe(\n\t\t\"PARAGLIDE_LOCALE=de; path=/; max-age=34560000\"\n\t);\n\texpect(globalThis.window.location.href).toBe(\"https://example.com/de/page\");\n});\n\ntest(\"returns locale from custom strategy\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"custom-client\", \"baseLocale\"],\n\t\tisServer: \"false\",\n\t});\n\n\truntime.defineCustomClientStrategy(\"custom-client\", {\n\t\tgetLocale: () => \"fr\",\n\t\tsetLocale: () => {},\n\t});\n\n\tconst locale = runtime.getLocale();\n\texpect(locale).toBe(\"fr\");\n});\n\ntest(\"falls back to next strategy when custom strategy returns undefined\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"custom-undefined\", \"baseLocale\"],\n\t\tisServer: \"false\",\n\t});\n\n\truntime.defineCustomClientStrategy(\"custom-undefined\", {\n\t\tgetLocale: () => undefined,\n\t\tsetLocale: () => {},\n\t});\n\n\tconst locale = runtime.getLocale();\n\texpect(locale).toBe(\"en\"); // Should fall back to baseLocale\n});\n\ntest(\"throws error if custom strategy returns invalid locale\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"custom-invalid\", \"baseLocale\"],\n\t\tisServer: \"false\",\n\t});\n\n\truntime.defineCustomClientStrategy(\"custom-invalid\", {\n\t\tgetLocale: () => \"invalid-locale\",\n\t\tsetLocale: () => {},\n\t});\n\n\texpect(() => runtime.getLocale()).toThrow();\n});\n\ntest(\"custom strategy takes precedence over built-in strategies in getLocale\", async () => {\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = { cookie: \"PARAGLIDE_LOCALE=fr\" };\n\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"custom-priority\", \"cookie\", \"baseLocale\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\tisServer: \"false\",\n\t});\n\n\truntime.defineCustomClientStrategy(\"custom-priority\", {\n\t\tgetLocale: () => \"de\",\n\t\tsetLocale: () => {},\n\t});\n\n\tconst locale = runtime.getLocale();\n\texpect(locale).toBe(\"de\"); // Should use custom strategy, not cookie\n});\n\ntest(\"multiple custom strategies work in order in getLocale\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"custom-first\", \"custom-second\", \"baseLocale\"],\n\t\tisServer: \"false\",\n\t});\n\n\t// Define two custom strategies\n\truntime.defineCustomClientStrategy(\"custom-first\", {\n\t\tgetLocale: () => undefined, // This one returns undefined\n\t\tsetLocale: () => {},\n\t});\n\n\truntime.defineCustomClientStrategy(\"custom-second\", {\n\t\tgetLocale: () => \"fr\", // This one returns a locale\n\t\tsetLocale: () => {},\n\t});\n\n\tconst locale = runtime.getLocale();\n\texpect(locale).toBe(\"fr\"); // Should use second custom strategy\n});\n\ntest(\"custom strategy works with session storage simulation\", async () => {\n\t// Simulate a custom strategy that reads from session storage\n\tlet sessionData = \"de\";\n\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"custom-session\", \"baseLocale\"],\n\t\tisServer: \"false\",\n\t});\n\n\truntime.defineCustomClientStrategy(\"custom-session\", {\n\t\tgetLocale: () => sessionData,\n\t\tsetLocale: (locale) => {\n\t\t\tsessionData = locale;\n\t\t},\n\t});\n\n\tconst locale = runtime.getLocale();\n\texpect(locale).toBe(\"de\");\n});\n\ntest(\"custom strategy integrates with locale initialization\", async () => {\n\t// @ts-expect-error - global variable definition\n\tglobalThis.window = {};\n\n\t// Define a custom strategy that tracks if it was called\n\tlet getLocaleCalled = false;\n\tlet setLocaleCalled = false;\n\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"custom-tracking\"],\n\t\tisServer: \"false\",\n\t});\n\n\truntime.defineCustomClientStrategy(\"custom-tracking\", {\n\t\tgetLocale: () => {\n\t\t\tgetLocaleCalled = true;\n\t\t\treturn \"fr\";\n\t\t},\n\t\tsetLocale: () => {\n\t\t\tsetLocaleCalled = true;\n\t\t},\n\t});\n\n\t// First call should trigger the custom strategy\n\tconst locale = runtime.getLocale();\n\texpect(locale).toBe(\"fr\");\n\texpect(getLocaleCalled).toBe(true);\n\n\t// Setting the locale should also call the custom strategy\n\truntime.setLocale(\"de\");\n\n\t// The locale should be set internally after first resolution\n\texpect(setLocaleCalled).toBe(true);\n});\n"]}
1
+ {"version":3,"file":"get-locale.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/get-locale.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { newProject } from \"@inlang/sdk\";\nimport { expect, test, vi } from \"vitest\";\nimport { createParaglide } from \"../create-paraglide.js\";\n\ntest(\"matching by strategy works\", async () => {\n\tconst baseLocale = \"en\";\n\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale,\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\", \"baseLocale\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {};\n\tglobalThis.document.cookie = \"OTHER_COOKIE=blaba;\";\n\n\tconst locale = runtime.getLocale();\n\texpect(locale).toBe(baseLocale);\n});\n\ntest(\"experimentalStaticLocale overrides runtime strategies\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\", \"baseLocale\"],\n\t\texperimentalStaticLocale: JSON.stringify(\"de\"),\n\t});\n\n\texpect(runtime.getLocale()).toBe(\"de\");\n});\n\ntest(\"invalid experimentalStaticLocale throws during runtime creation\", async () => {\n\tawait expect(\n\t\tcreateParaglide({\n\t\t\tblob: await newProject({\n\t\t\t\tsettings: {\n\t\t\t\t\tbaseLocale: \"en\",\n\t\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t\t},\n\t\t\t}),\n\t\t\tstrategy: [\"cookie\", \"baseLocale\"],\n\t\t\texperimentalStaticLocale: JSON.stringify(\"fr\"),\n\t\t})\n\t).rejects.toThrow(\"Invalid locale: fr. Expected one of: en, de\");\n});\n\ntest(\"throws if variable is used without baseLocale as fallback strategy\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"globalVariable\"],\n\t});\n\n\texpect(() => runtime.getLocale()).toThrow();\n\n\truntime.setLocale(\"de\");\n\n\texpect(runtime.getLocale()).toBe(\"de\");\n});\n\ntest(\"retrieves the locale for a url pattern\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"url\"],\n\t\tisServer: \"false\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.:tld/:path*\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/:path*\"],\n\t\t\t\t\t[\"de\", \"https://example.de/:path*\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tglobalThis.window = { location: { href: \"https://example.com/page\" } } as any;\n\n\texpect(runtime.getLocale()).toBe(\"en\");\n\n\tglobalThis.window = { location: { href: \"https://example.de/page\" } } as any;\n\n\texpect(runtime.getLocale()).toBe(\"de\");\n});\n\ntest(\"url pattern strategy doesn't throw during SSR\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"url\", \"baseLocale\"],\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.:tld/:path*\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/:path*\"],\n\t\t\t\t\t[\"de\", \"https://example.de/:path*\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\texpect(() => runtime.getLocale()).not.toThrow();\n});\n\ntest(\"doesn't throw for an old cookie locale\", async () => {\n\tconst baseLocale = \"en\";\n\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\", \"baseLocale\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {};\n\tglobalThis.document.cookie = \"PARAGLIDE_LOCALE=fr;\";\n\n\tconst locale = runtime.getLocale();\n\texpect(locale).toBe(baseLocale);\n});\n\ntest(\"returns the preferred locale from navigator.languages\", async () => {\n\tconst originalNavigator = globalThis.navigator;\n\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tisServer: \"false\",\n\t\tstrategy: [\"preferredLanguage\"],\n\t});\n\n\t// Mock navigator.languages\n\tObject.defineProperty(globalThis, \"navigator\", {\n\t\tvalue: {\n\t\t\tlanguages: [\"fr-FR\", \"en-US\", \"de\"],\n\t\t},\n\t\tconfigurable: true,\n\t});\n\n\texpect(runtime.getLocale()).toBe(\"fr\");\n\n\t// Restore original navigator\n\tObject.defineProperty(globalThis, \"navigator\", {\n\t\tvalue: originalNavigator,\n\t\tconfigurable: true,\n\t});\n});\n\ntest(\"returns the locale from local storage\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"localStorage\"],\n\t\tlocalStorageKey: \"PARAGLIDE_LOCALE\",\n\t\tisServer: \"false\",\n\t});\n\n\t// @ts-expect-error - global variable definition\n\tglobalThis.localStorage = {\n\t\tsetItem: vi.fn(),\n\t\tgetItem: vi.fn(() => \"de\"),\n\t};\n\n\texpect(runtime.getLocale()).toBe(\"de\");\n});\n\ntest(\"initially sets the locale after resolving it for the first time\", async () => {\n\t// Create runtime with multiple strategies\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"url\", \"cookie\"],\n\t\tisServer: \"false\",\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path*\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path*\"],\n\t\t\t\t\t[\"de\", \"https://example.com/de/:path*\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path*\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\t// Setup global objects for URL strategy\n\tglobalThis.window = {\n\t\t// @ts-expect-error - global variable definition\n\t\tlocation: {\n\t\t\thostname: \"example.com\",\n\t\t\thref: \"https://example.com/de/page\",\n\t\t},\n\t};\n\n\t// Create a spy on the internal _locale variable to verify it was set\n\t// We can do this by checking if setLocale works without explicitly setting it first\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = { cookie: \"\" };\n\n\t// First call to getLocale should resolve and set the locale\n\texpect(runtime.getLocale()).toBe(\"de\");\n\t// Cookie should be set, proving that the locale was initially set\n\texpect(globalThis.document.cookie).toBe(\n\t\t\"PARAGLIDE_LOCALE=de; path=/; max-age=34560000\"\n\t);\n\texpect(globalThis.window.location.href).toBe(\"https://example.com/de/page\");\n});\n\ntest(\"returns locale from custom strategy\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"custom-client\", \"baseLocale\"],\n\t\tisServer: \"false\",\n\t});\n\n\truntime.defineCustomClientStrategy(\"custom-client\", {\n\t\tgetLocale: () => \"fr\",\n\t\tsetLocale: () => {},\n\t});\n\n\tconst locale = runtime.getLocale();\n\texpect(locale).toBe(\"fr\");\n});\n\ntest(\"falls back to next strategy when custom strategy returns undefined\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"custom-undefined\", \"baseLocale\"],\n\t\tisServer: \"false\",\n\t});\n\n\truntime.defineCustomClientStrategy(\"custom-undefined\", {\n\t\tgetLocale: () => undefined,\n\t\tsetLocale: () => {},\n\t});\n\n\tconst locale = runtime.getLocale();\n\texpect(locale).toBe(\"en\"); // Should fall back to baseLocale\n});\n\ntest(\"throws error if custom strategy returns invalid locale\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"custom-invalid\", \"baseLocale\"],\n\t\tisServer: \"false\",\n\t});\n\n\truntime.defineCustomClientStrategy(\"custom-invalid\", {\n\t\tgetLocale: () => \"invalid-locale\",\n\t\tsetLocale: () => {},\n\t});\n\n\texpect(() => runtime.getLocale()).toThrow();\n});\n\ntest(\"custom strategy takes precedence over built-in strategies in getLocale\", async () => {\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = { cookie: \"PARAGLIDE_LOCALE=fr\" };\n\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"custom-priority\", \"cookie\", \"baseLocale\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\tisServer: \"false\",\n\t});\n\n\truntime.defineCustomClientStrategy(\"custom-priority\", {\n\t\tgetLocale: () => \"de\",\n\t\tsetLocale: () => {},\n\t});\n\n\tconst locale = runtime.getLocale();\n\texpect(locale).toBe(\"de\"); // Should use custom strategy, not cookie\n});\n\ntest(\"multiple custom strategies work in order in getLocale\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"custom-first\", \"custom-second\", \"baseLocale\"],\n\t\tisServer: \"false\",\n\t});\n\n\t// Define two custom strategies\n\truntime.defineCustomClientStrategy(\"custom-first\", {\n\t\tgetLocale: () => undefined, // This one returns undefined\n\t\tsetLocale: () => {},\n\t});\n\n\truntime.defineCustomClientStrategy(\"custom-second\", {\n\t\tgetLocale: () => \"fr\", // This one returns a locale\n\t\tsetLocale: () => {},\n\t});\n\n\tconst locale = runtime.getLocale();\n\texpect(locale).toBe(\"fr\"); // Should use second custom strategy\n});\n\ntest(\"custom strategy works with session storage simulation\", async () => {\n\t// Simulate a custom strategy that reads from session storage\n\tlet sessionData = \"de\";\n\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"custom-session\", \"baseLocale\"],\n\t\tisServer: \"false\",\n\t});\n\n\truntime.defineCustomClientStrategy(\"custom-session\", {\n\t\tgetLocale: () => sessionData,\n\t\tsetLocale: (locale) => {\n\t\t\tsessionData = locale;\n\t\t},\n\t});\n\n\tconst locale = runtime.getLocale();\n\texpect(locale).toBe(\"de\");\n});\n\ntest(\"custom strategy integrates with locale initialization\", async () => {\n\t// @ts-expect-error - global variable definition\n\tglobalThis.window = {};\n\n\t// Define a custom strategy that tracks if it was called\n\tlet getLocaleCalled = false;\n\tlet setLocaleCalled = false;\n\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"custom-tracking\"],\n\t\tisServer: \"false\",\n\t});\n\n\truntime.defineCustomClientStrategy(\"custom-tracking\", {\n\t\tgetLocale: () => {\n\t\t\tgetLocaleCalled = true;\n\t\t\treturn \"fr\";\n\t\t},\n\t\tsetLocale: () => {\n\t\t\tsetLocaleCalled = true;\n\t\t},\n\t});\n\n\t// First call should trigger the custom strategy\n\tconst locale = runtime.getLocale();\n\texpect(locale).toBe(\"fr\");\n\texpect(getLocaleCalled).toBe(true);\n\n\t// Setting the locale should also call the custom strategy\n\truntime.setLocale(\"de\");\n\n\t// The locale should be set internally after first resolution\n\texpect(setLocaleCalled).toBe(true);\n});\n\n// sandboxed iframes without allow-same-origin have an opaque origin and throw\n// a SecurityError on document.cookie and localStorage access\ntest(\"resolves the url locale when document.cookie throws\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"url\", \"cookie\"],\n\t\tisServer: \"false\",\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path*\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path*\"],\n\t\t\t\t\t[\"de\", \"https://example.com/de/:path*\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tglobalThis.window = {\n\t\t// @ts-expect-error - global variable definition\n\t\tlocation: {\n\t\t\thostname: \"example.com\",\n\t\t\thref: \"https://example.com/de/page\",\n\t\t},\n\t};\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {\n\t\tget cookie(): string {\n\t\t\tthrow new DOMException(\"The operation is insecure.\", \"SecurityError\");\n\t\t},\n\t\tset cookie(_value) {\n\t\t\tthrow new DOMException(\"The operation is insecure.\", \"SecurityError\");\n\t\t},\n\t};\n\n\t// the first resolution also writes the cookie\n\texpect(runtime.getLocale()).toBe(\"de\");\n});\n\ntest(\"falls back to the next strategy when document.cookie throws\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\", \"baseLocale\"],\n\t\tisServer: \"false\",\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\t// @ts-expect-error - global variable definition\n\tglobalThis.window = {};\n\t// @ts-expect-error - global variable definition\n\tglobalThis.document = {\n\t\tget cookie(): string {\n\t\t\tthrow new DOMException(\"The operation is insecure.\", \"SecurityError\");\n\t\t},\n\t\tset cookie(_value) {\n\t\t\tthrow new DOMException(\"The operation is insecure.\", \"SecurityError\");\n\t\t},\n\t};\n\n\texpect(runtime.getLocale()).toBe(\"en\");\n});\n\ntest(\"falls back to the next strategy when localStorage throws\", async () => {\n\tconst originalLocalStorage = globalThis.localStorage;\n\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"localStorage\", \"baseLocale\"],\n\t\tisServer: \"false\",\n\t});\n\n\t// @ts-expect-error - global variable definition\n\tglobalThis.window = {};\n\t// browsers throw when the localStorage property itself is read\n\tObject.defineProperty(globalThis, \"localStorage\", {\n\t\tget() {\n\t\t\tthrow new DOMException(\"The operation is insecure.\", \"SecurityError\");\n\t\t},\n\t\tconfigurable: true,\n\t});\n\n\texpect(runtime.getLocale()).toBe(\"en\");\n\n\tObject.defineProperty(globalThis, \"localStorage\", {\n\t\tvalue: originalLocalStorage,\n\t\tconfigurable: true,\n\t\twritable: true,\n\t});\n});\n"]}
@@ -28,6 +28,10 @@
28
28
  * // Cross-origin links remain absolute
29
29
  * localizeHref("https://other-site.com/about")
30
30
  * // => "https://other-site.com/de/about"
31
+ *
32
+ * // Protocol-relative links stay protocol-relative
33
+ * localizeHref("//other-site.com/about")
34
+ * // => "//other-site.com/de/about"
31
35
  * ```
32
36
  *
33
37
  * For server-side URL localization (e.g., in middleware), use `localizeUrl()`
@@ -73,6 +77,10 @@ export declare function localizeHref(href: string, options?: {
73
77
  * // Cross-origin links remain absolute
74
78
  * deLocalizeHref("https://example.com/de/about")
75
79
  * // => "https://example.com/about"
80
+ *
81
+ * // Protocol-relative links stay protocol-relative
82
+ * deLocalizeHref("//example.com/de/about")
83
+ * // => "//example.com/about"
76
84
  * ```
77
85
  *
78
86
  * For server-side URL de-localization (e.g., in middleware), use `deLocalizeUrl()`