Wβ
All docs
π
Sign Up/Sign In
inlang.com/m/gerre34r/library-inlang-paraglideJs
Public Link
Apr 14, 2025, 1:02:15 PM - complete - 123.2 kB
Starting URLs:
https://inlang.com/m/gerre34r/library-inlang-paraglideJs
## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs  #### Sherlock - VS Code extension App Visualize, edit & lint translated strings at a glance via Inline Decorations & Hover Support, and extract new strings with a single click. --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/comparison  #### Sherlock - VS Code extension App Visualize, edit & lint translated strings at a glance via Inline Decorations & Hover Support, and extract new strings with a single click. --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/benchmark ## Benchmark This benchmark compares the transfer size of different i18n libraries and their implementations. The goal is to understand how the size of the library changes with different configurations, such as the number of locales, messages per page, and namespace size. If you are looking for a feature comparison, check out the comparison table. π‘ **Tip**: Paraglide JS has not reached its final optimizations yet. Tickets like #88 per locale builds or #354 pruning server side rendered messages are yet to be implemented. Pull requests are welcome! ### What is Being Tested The benchmark creates a static website for each configuration (library variant, number of locales, messages per page, and namespace size). Each website is loaded in a headless browser, and the total transfer size is measured. * **Number of Locales**: How does an i18n library scale with the number of locales? * **Number of used Messages**: How does an i18n library scale with the number of messages that are used on a given page? * **Library Implementation Variants**: Testing different implementation approaches: * **Paraglide**: * `default`: Standard implementation * `experimental-`: Experimental implementation with per-locale splitting * **i18next**: * `http-backend`: Using HTTP backend for loading translations * **Namespace Size**: Testing how the total available messages in a namespace affects bundle size ### Library modes Each library is tested in different modes: * **Paraglide**: * **default**: Out of the box Paraglide JS with no additional compiler options. * : Mode with a compiler option that is being tested. * **i18next**: * **default**: The default i18next implementation source. * **http-backend**: i18next implementation using HTTP backend for loading translations on dmeand source. ### Limitations **Choosing the number of messages and namespace varies between projects** Some teams use per component namespacing while other teams have one namespace for their entire project. In cal.com's case, every component that uses i18n loads at least 3000 messages per locale. To the point of the problem: Avoiding manual chunking of messages into namespaces is the benefit of Paraglide JS. The bundler tree-shakes all unused messages, making namespaces redundant. ## Contributing Contributions to improve the benchmark are welcome. 1. adjust the build matrix in `build.config.ts` 2. run `pnpm run bench` to build the benchmark 3. run `pnpm run preview` to preview the results --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/changelog ## 2.0.11 ### Patch Changes * de0439f: Add domain property to cookie options. paraglideVitePlugin({ project: './project.inlang', outdir: "./src/paraglide", + cookieDomain: 'example.com' }), ## 2.0.10 ### Patch Changes * d16e853: fix: \[2.0.8\] Adds extra \*/ in generated .js files https://github.com/opral/inlang-paraglide-js/issues/493 ## 2.0.9 ### Patch Changes * Updated dependencies \[bd2c366\] * @inlang/sdk@2.4.7 ## 2.0.8 ### Patch Changes * 5258af0: fix: compiling message bundles with case sensitive ids for the locale module output https://github.com/opral/inlang-paraglide-js/issues/490 Case sensitive ids led to duplicate exports in the locale module output. This has been fixed by adjusting the `toSafeModuleId()` used by the compiler internally to append a number of uppercase characters to de-duplicate the ids. toSafeModuleId("helloworld") "helloworld" toSafeModuleId("helloWorld") - "helloworld" + "helloworld1" ## 2.0.7 ### Patch Changes * 48931f5: make `output-structure: locale-modules` the default for dev builds https://github.com/opral/inlang-paraglide-js/issues/486 * Updated dependencies \[49a7880\] * @inlang/sdk@2.4.6 ## 2.0.6 ### Patch Changes * 3fa27c0: fix: duplicate (case sensitive) message keys leading to compile error when using `output-structure: locale-modules`. closes https://github.com/opral/inlang-paraglide-js/issues/487 * 02c2d34: improve: compiler should log warnings when plugins can not be imported * Updated dependencies \[083ff1f\] * @inlang/sdk@2.4.5 ## 2.0.5 ### Patch Changes * 698b9a9: add `cookieMaxAge` option to compiler and runtime Closes https://github.com/opral/inlang-paraglide-js/issues/483 * Introduced `cookieMaxAge` option to `CompilerOptions`, allowing configuration of cookie expiration time. * Adjusted tests to verify `max-age` in cookies. ## 2.0.4 ### Patch Changes * @inlang/sdk@2.4.4 ## 2.0.3 ### Patch Changes * cf404e0: improve: handle duplicate inputs https://github.com/opral/inlang-paraglide-js/issues/479 ## 2.0.2 ### Patch Changes * a6c43ea: fix: error handling in paraglideMiddleware breaks SvelteKit features that rely on errors being thrown ## 2.0.1 ### Patch Changes * b906c0c: fix: window undefined bug in webpack * @inlang/sdk@2.4.3 ## Paraglide JS 2.0 π Paraglide JS 2.0 had three main goals which have all been achieved: 1. Use the new inlang SDK v2 which supports variants #201. 2. Unify the API across any framework #217. 3. Support any i18n strategy (cookie, url, domain, session, etc). * π **Variants (pluralization) are now supported** docs * π **No more adapters or providers are needed** (!) * π£οΈ **Any strategy (url, cookie, local storage) is now supported** docs In addition, Paraglide JS 2.0 comes with: * π **Nested message keys** are now supported (most requested feature!) * β¨ **Auto-imports** when writing `m.` (no more manual `import * as m`) * π **Arbitrary key names** including emojis via `m["π"]()` * π **Incremental migration** to Paraglide JS is now possible * ποΈ **Multi-tenancy support** for domain-based routing * π§ **Exposing the compiler API** for advanced workflows * π£οΈ **Configurable routing strategies** (URL, cookie, domain, etc) * π§ͺ **Experimental per-locale splitting** for decreasing bundle sizes * π **Framework-agnostic server middleware** for SSR (SvelteKit, Next.js, etc) ### Highlights #### Interactive benchmark Check out the benchmark to see how Paraglide JS compares to other libraries like i18next.  #### No more adapters are needed - @inlang/paraglide-sveltekit - @inlang/paraglide-next + // No more adapters are needed #### π Framework-Agnostic Server Middleware Docs are here * **New**: Universal `paraglideMiddleware()` works in any SSR framework * **Built-in**: Automatic locale redirects when user preference detected // SvelteKit example - same pattern works for Next.js, Astro, etc. import { paraglideMiddleware } from "./paraglide/server.js"; export const handle = ({ event, resolve }) => { return paraglideMiddleware(event.request, () => resolve(event)); }; #### π£οΈ Configurable Routing Strategies Read more about strategies on the docs. Literally anything is now possible. URL-based, domain-based, path-based, cookie-based, etc. paraglide({ strategy: [ "url", "cookie", "preferredLanguage", "..." ], }), #### π§ Exposing the compiler API * **New**: Direct compiler access for advanced workflows * **Why**: Enable CI/CD pipelines and custom tooling * **Benefit**: Full control over compilation timing/caching // New programmatic API import { compile } from "@inlang/paraglide-js"; await compile({ project: "./project.inlang", outdir: "./src/paraglide", }); #### π Arbitrary key names * **New**: Arbitrary key names including emojis via \`m"π" * **Why**: Enable nesting of messages * **Benefit**: More expressive and fun translations // Paraglide 1.0 m.some_message(); // β Works m.nested.message(); // π₯ Error // Paraglide 2.0 m.some_message(); // β Works m["nested.message"](); // β Works // Even emojis are supported m["π"](); #### π Incrementally migrating to Paraglide JS Paraglide JS 2.0 can load multiple translation file formats. As such, you can incrementally migrate to Paraglide JS with existing translation file formats. // In this example, Paraglide JS compiles i18next translation files // which enables using both i18next and Paraglide JS. import i18next from "i18next"; import { m } from "./paraglide/messages.js"; console.log(i18next.t("greeting", { name: "World" })); console.log(m.greeting({ name: "World" })); #### ποΈ Multi-tenancy support Paraglide JS 2.0 supports multi-tenant applications. Read more about it here. # Domain-based with sub-locale customer1.fr/about β French (default) customer1.fr/en/about β English version # Domain-based with root locale customer2.com/about β English (default) customer2.com/fr/about β French version # Path-based for any domain example.com/en/about β English example.com/fr/about β French app.example.com/en/about β English app.example.com/fr/about β French ### Migrating breaking changes If problems arise, please refer to the framework-specific getting started guide: * SvelteKit * Next.js * Astro * Vite * React Router #### `LanguageTag` got renamed to `locale` To align with industry standards, we renamed `LanguageTag` to `locale`. -languageTag() +getLocale() -setLanguageTag() +setLocale() -availableLanguageTags +locales #### Remove adapters `@inlang/paraglide-*` from your dependencies // package.json { "dependencies": { - "@inlang/paraglide-sveltekit": "^1.0.0", - "@inlang/paraglide-next": "^1.0.0", - "@inlang/paraglide-astro": "^1.0.0", - "@inlang/paraglide-vite": "^1.0.0", + "@inlang/paraglide-js": "^2.0.0", } } ``` ### Replace adapter bundler plugins ```diff // vite.config.js -import { paraglide } from "@inlang/paraglide-vite"; +import { paraglideVitePlugin } from "@inlang/paraglide-js"; export default defineConfig({ plugins: [ - paraglide({ + paraglideVitePlugin({ project: "./project.inlang", outdir: "./src/paraglide", }), ], }); #### Remove Paraglide Providers Paraglide JS 2.0 no longer requires providers. // app.tsx, layout.svelte, etc. -import { ParaglideProvider } from "@inlang/paraglide-{framework}"; function App() { return ( - <ParaglideProvider> <YourApp /> - </ParaglideProvider> ) } #### Shorten key names longer than 255 characters Paraglide JS 2.0 build output now defaults to message-modules to improve tree-shaking. Some filesystem's limitations require key names to be shorter than 255 characters. Upvote #423 to remove this limitation. - m.this_is_a_very_long_key_name_that_should_be_shortened() + m.shortened_key_name() #### Changing the locale requires a reload Changing the locale works via `setLocale()` in any framework now. If you used an adapter in v1 like the SvelteKit one, this behavior is new. The new behaviour leads to a page reload. The reload is a deliberate design decision. Reloading the site eliminates the need for providers, adapters, and API differences between frameworks. Furthermore, optimizations like per-locale splitting is expected to be easier to implement. Read https://github.com/opral/inlang-paraglide-js/issues/438#issuecomment-2703733096 for more information. -<a href="/de">Deutsch</a> +<button onclick="setLocale('de')">Deutsch</button> #### Lint rules were deprecated Remove lint rules from your project modules. We want to re-introduce lint rules in a better form in the future. Please upvote the #239 lix validation rules proposal. modules: [ - "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@latest/dist/index.js", - "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@latest/dist/index.js" ... ] #### `localizeHref()` is now required Some Paraglide adapters used AST transforms to automatically transform `<a>` into localized links. That led to many bugs and edge cases. The AST transforms were removed for v2. -<a href="/page"></a> +<a href={localizeHref("/page")} ## 2.0.0-beta.31 (released as 2.0.0) * feat: New API: `createParaglide()` The function allows a no build step or before build step access to Paraglide's compiled APIs. const project = await fs.readFile("./project.inlang"); const paraglide = await createParaglideModule({ project, compilerOptions: { strategy: ["url"], }, }); // Use runtime functions paraglide.localizeUrl("https://example.com", { locale: "de" }); // Use server middleware app.use(paraglide.paraglideMiddleware()); * fix: `getLocale` returns correct value on SvelteKit server #461 * fix: Prevent redirect loops by normalizing URLs with trailing slashes #408 * fix: Support for explicit port numbers in URL patterns * improve: Better error handling in server middleware ## 2.0.0-beta.30 * improve: if no url pattern matches, `localizeUrl()` and `deLocalizeUrl()` will return the input url unchanged instead of throwing an error #452 * improve: make AsyncLocalStorage tree-shakable by moving `disableAsyncLocalStorage` into the compiler options #424 - serverMiddleware(req, resolve, { disableAsyncLocalStorage: true }) + serverMiddleware(req, resolve) paraglideVitePlugin({ // ... + disableAsyncLocalStorage: true }) * improve: allow fall through. enables partially localized patterns and thereby eases adoption. ## 2.0.0-beta.29 * fix #455 setLocale and getLocale call each other in a loop * fix #454 Adapt config to the new localized param of beta.28 ## 2.0.0-beta.28 ### BREAKING update to the URLPattern API https://github.com/opral/monorepo/pull/3485 The `localizedNamedGroups` and `deLocalizedNamedGroups` API has been replaced with a tuple-based `localized` array to: * enable translated pathnames in any combination * make the API more intuitive #### Migration Guide: **Refer to the updated documentation here.** Before { "pattern": "https://:domain(.*)/:locale(de|fr)?/:path*", "deLocalizedNamedGroups": { "locale": null }, "localizedNamedGroups": { "en": { "locale": null }, "fr": { "locale": "fr" }, "de": { "locale": "de" } } } After { "pattern": "https://:domain(.*)/:path*", "localized": [ ["fr", "https://:domain(.*)/fr/:path*"], ["de", "https://:domain(.*)/de/:path*"] ["en", "https://:domain(.*)/:path*"], ] } ### other changes * improve: fallback to `typeof window` in vite #445 * make `setLocale()` set all strategies. Setting all strategies aligns with user expectations and ensures that server APIs can receive the cookie of the client, for example. #439 * new `generateStaticLocalizedUrls()` API #443 const localizedUrls = generateStaticLocalizedUrls([ "/example", "/page/blog", "/123/hello" ]) console.log(localizedUrls.map(url => url.pathnames)) >> /de/example >> /fr/example >> ... ## 2.0.0-beta.27 * fix wrong matching in API requests #427 Paraglide JS is no longer extracting the locale from API requests for the `url` strategy because that can lead to unwanted re-directs. To get the right locale in API requests, at least add the `baseLocale` strategy to your options. -strategy: ["url"] +strategy: ["url", "cookie", "baseLocale"] * consolidated `message-modules` output into a single file #434 to severaly improve scalability * `experimentalMiddlewareLocaleSplitting` option https://github.com/opral/inlang-paraglide-js/issues/425#issuecomment-2692351073 * fix setLocale() triggers re-loads if the same locale is set * fix serverMiddleware() throws when cookie contains invalid locale * add `localStorage` strategy #431 * fix url strategy with optional locale always resoles base locale #436 ## 2.0.0-beta.26 * replace `node:crypto` with the Web Crypto API https://github.com/opral/inlang-paraglide-js/issues/424 ## 2.0.0-beta.25 * added optional localized groups * keeps hashes, etc. in the URL when localizing https://github.com/opral/monorepo/pull/3452 * fixes the multi-variant return ## 2.0.0-beta.24 * changes the redirect status from `302` to `307` https://github.com/opral/inlang-paraglide-js/issues/416 * adds a `cleanOutdir` option which defaults to true https://github.com/opral/inlang-paraglide-js/issues/420 * adds a `machine-translate` command if desired on init ## 2.0.0-beta.23 Renames and splits the `serverMiddleware()` into a dedicated `server.js` file to avoid bundler issues. - import { serverMiddleware } from "./paraglide/runtime.js"; + import { paraglideMiddleware } from "./paraglide/server.js"; ## 2.0.0-beta.22 * fix `serverMiddleware()` only imports async\_hooks on the server * add `serverMiddleware(req, resolve, { disableAsyncLocalStorage: true })` to disable async local storage * fix `serverMiddleware()` throws in next js when `url` strategy is not used https://github.com/opral/inlang-paraglide-js/issues/411#issuecomment-2683530533 ## 2.0.0-beta.21 * compile arbitrary message keys https://github.com/opral/inlang-paraglide-js/issues/201#issuecomment-2680006131 * only polyfills `URLPattern` if needed https://github.com/opral/inlang-paraglide-js/issues/381 * don't include `url` strategy by default https://github.com/opral/inlang-paraglide-js/issues/405 * fixes webpack watch mode https://github.com/opral/inlang-paraglide-js/issues/406 ## 2.0.0-beta.20 * automatic re-directs in `serverMiddleware()` https://github.com/opral/inlang-paraglide-js/issues/201#issuecomment-2675823651 * various bug fixes and improvements ## 2.0.0-beta.19 NO MORE ADAPTERS NEEDED. If you have code from an adapter, remove it and follow the examples in the documentation. https://inlang.com/m/gerre34r/library-inlang-paraglideJs/sveltekit -@inlang/paraglide-sveltekit -@inlang/paraglide-next -@inlang/paraglide-astro * introduced `serverMiddleware()` https://github.com/opral/inlang-paraglide-js/issues/201#issuecomment-2673375348 * rename `defineGetLocale()` and `defineSetLocale()` to `overwriteGetLocale()` and `overwriteSetLocale()` https://github.com/opral/inlang-paraglide-js/issues/382 * enables `import { m } from "./paraglide/messages.js"` for auto imports https://github.com/opral/inlang-paraglide-js/issues/345 * adds the `strategy` compiler option to the cli https://github.com/opral/inlang-paraglide-js/issues/316 ## 2.0.0-beta.18 Added URLPatterns as a replacement for the beta 17 pathnames API. The URLPattern API is extremly powerful. You can express base paths, translated pathnames, domain based localization, and even multi-tenancy. Read the docs here and make PRs to improve the documentation. await compile({ - strategy: ["pathname"], + strategy: ["url"], }) The `localizePath()` API had to be replaced by a new `localizeHref()` API. Please give feedback on the new API in #380 - <a href={localizePath("/about")}>About</a> + <a href={localizeHref("/about")}>About</a> ## 2.0.0-beta.17 Add support for `pathnames` API https://github.com/opral/inlang-paraglide-js/issues/359 You can now create whatever pathname pattern you want. The syntax is provided by https://github.com/pillarjs/path-to-regexp. await compile({ strategy: ["pathname", "cookie", "baseLocale"], + pathnames: { + "{*path}": { + "de": "/de{/*path}", + "en": "/en{/*path}", } } }) Add support for `pathnameBase` https://github.com/opral/inlang-paraglide-js/issues/362 await compile({ strategy: ["pathname", "cookie", "baseLocale"], + pathnameBase: "/base" }) ## 2.0.0-beta.16 New `strategy` API. See https://github.com/opral/inlang-paraglide-js/issues/346. You can now define your own stragegy for getting and setting a locale. await compile({ + strategy: ["pathname", "cookie", "baseLocale"] }) ## 2.0.0-beta.14 Fixes windows path normalization https://github.com/opral/monorepo/pull/3374 ## 2.0.0-beta.13 * flat compiler options await compile({ - compilerOptions: { - emitPrettierIgnore: false, - }, + emitPrettierIgnore: false, }) * removed `experimentalEmitTs`. the overhead of maintaing two syntaxes is too large https://github.com/opral/inlang-paraglide-js/issues/329 await compile({ - experimentalEmitTs: true, }) ## 2.0.0-beta.11 * improve: compiler awaits ongoing compilations before starting a new one * update dependency that fixes https://github.com/opral/inlang-paraglide-js/issues/320 ## 2.0.0-beta.10 * remove `available` prefix from locale APIs alltogether https://github.com/opral/inlang-paraglide-js/issues/201#issuecomment-2596202820 ## 2.0.0-beta.9 * Expose compiler on the index -import { compile } from "@inlang/paraglide-js/compiler"; +import { compile } from "@inlang/paraglide-js"; * expose paraglide compile args + import { type CompileArgs } from "@inlang/paraglide-js"; ## 2.0.0-beta.8 * rename `runtime.locales` to `runtime.availableLocales` to align with v1 API and avoid ambiguity https://github.com/opral/inlang-paraglide-js/issues/314 * remove legacy `languageTag` APIs https://github.com/opral/inlang-paraglide-js/issues/315 ## 2.0.0-beta.7 * fixed windows path problems * increased performance of the compiler by removing a redundant setTimeout * increased performance of the compiler by removing prettier * `experimentalEmitTs` flag ## 2.0.0-beta.3 ### Patch Changes * remove `fast-glob` as dependency in favor of node's built-in `fs.glob` (a new API in node 22) ## 2.0.0 ### Major changes * Upgrade to @inlang/sdk v2 * Support for variants (pluralization, gendering, A/B test, etc.) ### Minor Changes * 1d62451: remove `dedent` dependency in CLI * 5d906bd: refactor: remove posthog-node dependency Posthog node has been replaced for a fetch call. Removing 3 (posthog + 2 transitive dependencies). * 855a71c: adds `experimentalEmitTsDeclarations` compiler option https://github.com/opral/inlang-paraglide-js/issues/288 await compile({ // ... options: { + experimentalEmitTsDeclarations: true } }) Projects can now select if TypeScript declaration file should be emitted. The need for the `allowJs: true` option in TypeScript configs becomes redundant at the cost of slower compilation times (https://github.com/opral/inlang-paraglide-js/issues/238). * 346c21b: maintenance: remove path prop from tsconfig -"paths": { - "~/*": ["./src/*"] -} So not worth it "nice to have" "but it's better DX" thing. Breaks path resolving in JS. Vitest needed a vite config to resolve the paths because only TS knew how to resolve thep paths. Etc. Etc. Etc. * fb06546: adds `emitGitIgnore` and `emitPrettierIgnore` compiler options Closes https://github.com/opral/inlang-paraglide-js/issues/189 await compile({ // ... options: { + emitPrettierIgnore: false + emitGitIgnore: false } }) * e2b9e24: feat: expose compiler as library closes https://github.com/opral/inlang-paraglide-js/issues/206 The Paraglide compiler is now exposed as a library. This allows you to use and extend the compiler however you need. import { compile } from "@inlang/paraglide-js/compiler"; await compile({ path: "/path/to/project.inlang", outdir: "/path/to/output", }); * 44ac447: maintenance: remove vite in favor of tsc to build paraglide js lib Closes https://github.com/opral/inlang-paraglide-js/issues/208 - "build": "vite build", + "build": "tsc", Paraglide JS used vite to build the library. This change removes vite in favor of tsc to build the library. This change is made to simplify the build process and to make it easier to maintain the library in the future. ## 1.11.2 ### Patch Changes * 59c8b11: Fix Ninja recommendation and adoption if working directory is not the repo root ## 1.11.1 ### Patch Changes * 14d80b3: Removed the "Which tech-stack are you using?" prompt from the `init` command as it was not providing any real value. All it did was link you to the appropriate documentation. From now on we rely on the docuemntation site to guide people to the correct documenation for their framework. * SvelteKit: https://inlang.com/m/dxnzrydw/paraglide-sveltekit-i18n * NextJs: https://inlang.com/m/osslbuzt/paraglide-next-i18n * Astro: https://inlang.com/m/iljlwzfs/paraglide-astro-i18n * SolidStart: https://inlang.com/m/n860p17j/paraglide-solidstart-i18n * Vite: https://github.com/opral/monorepo/tree/main/inlang/packages/paraglide/paraglide-vite ## 1.11.0 ### Minor Changes * e37eabf: - renamed packages `@inlang/cross-sell-X` to `@inlang/recommend-X` be more descriptive * refactor recommendation view in Sherlock VS Code extension * introduce new `shouldRecommend` function to `@inlang/recommend-sherlock` & `@inlang/recommend-ninja` ## 1.10.1 ### Patch Changes * c5d145d: use types from SDK for error handling ## 1.10.0 ### Minor Changes * 33662e6: Gracefully handle errors in the Inlang Project. Only crash on errors that are fatal to paraglide specifically ## 1.9.1 ### Patch Changes * b8573fa: Improved error-reporting ## 1.9.0 ### Minor Changes * eb941fe: Prompt about adding the Ninja Github Action for translation-linting during `paraglide-js init`. Also exposes it over the internal API. ### Patch Changes * 9566348: Better handling of `tsconfig` files that `extends` another config. ## 1.8.0 ### Minor Changes * 21ab0a0: Add an output mode where each message is it's own file. This enables treeshaking in less capable bundlers. Currently this is only available via the programmatic API. ### Patch Changes * 21ab0a0: performance improvements ## 1.7.3 ### Patch Changes * 32cbe48: Improve `bestMatch` reliability ## 1.7.2 ### Patch Changes * 6105a50: No longer log "Exiting the Watcher" when stopping the watching process, as it was causing annoying CMD popups on windows ## 1.7.1 ### Patch Changes * 4d24188: `paraglide-js init` now uses the `createNewProject` API from `@inlang/sdk` for creating new projects. This resulits in higher reliability. ## 1.7.0 ### Minor Changes * 0774c1a: Expose CLI programmatically to enable framework-specific init clis ## 1.6.2 ### Patch Changes * cee4692: Use index accesses instead of `.at` function for better compatability with legacy browsers * 4b631aa: Update invalid type-declarations * 3c7a87c: Fixes a race-condition when creating the messages directory during `paraglide-js init` * ab1fe48: When initialising and a single project is available, it no longer suggests and empty string as the project path ## 1.6.1 ### Patch Changes * fa6aa31: Update internal adapter-utilities * dee5aa6: Add `--silent` option to `paraglide-js compile` command that will only log errors ## 1.6.0 ### Minor Changes * 462325b: Paraglide now ships with internal utility functions that handle common adapter tasks such as language-negotiation. These aren't public facing, but the version bump is required to make sure adapter-packages resolve the correct version of paragldie. ## 1.5.0 ### Minor Changes * 2428451: `paraglide-js init` now finds more existing projects ## 1.4.0 ### Minor Changes * d47b2aa: Generate empty `messages/{lang}.js` files if no messages are present. This way the "shape" of the generated output is always the same regardless of messages ### Patch Changes * 192fdec: prompt the user for the `outdir` during `paraglide-js init` * 0b7c82e: Inline package-version at build time to be more robust ## 1.3.7 ### Patch Changes * 1cc9cbc: Run compiler after `paraglide-js init` so that initial files are present * 5401f95: Add `--outdir` flag to the generated CLI commands ## 1.3.6 ### Patch Changes * 526b0ba: `paraglide-js init` now prompts for which languages should be supported * 26d2ba1: No longer add `identical-pattern` lint rule by default ## 1.3.5 ### Patch Changes * 7dce581: fix `openRepository` crash in non-git environments ## 1.3.4 ### Patch Changes * 2a42b6e: bump `@lix-js/client` dependency ## 1.3.3 ### Patch Changes * 11f0e18: Update dependency * 10e3c28: fix JSDoc annotations on message aliases ## 1.3.2 ### Patch Changes * 45975c0: Fail gracefully if adding `.vscode` folder fails ## 1.3.1 ### Patch Changes * afaaffa: Bundle `@inlang/recommend-sherlock`. This is used to promot _once_ during `paraglide-js init` to ask if you want to install the Sherlock vscode extension ## 1.3.0 ### Minor Changes * 4970afc: paraglide deprecate aliases * 4837297: File locking for concurrent message updates through the load/store plugin api Auto-generated human-IDs and aliases - only with experimental: { aliases: true } ### Patch Changes * @inlang/recommend-sherlock@0.0.2 ## 1.2.9 ### Patch Changes * a99e35fee: use `@inlang/recommend-sherlock` during vscode extension initialization * Updated dependencies \[a99e35fee\] * @inlang/recommend-sherlock@0.0.2 ## 1.2.8 ### Patch Changes * b0f1e908b: Prompt user for tech-stack when running `paraglide-js init` & recommend Adapters ## 1.2.7 ### Patch Changes * f6ec6cdc9: bump `@inlang/sdk` dependency ## 1.2.6 ### Patch Changes * 960f8fb70: rename the vscode extension to "Sherlock" ## 1.2.5 ### Patch Changes * 00f181ad3: fix broken dependency ## 1.2.4 ### Patch Changes * Updated dependencies \[244442698\] * @inlang/language-tag@1.5.0 ## 1.2.3 ### Patch Changes * 4c26fa70a: bump dependencies ## 1.2.2 ### Patch Changes * 613ef9877: fix: Bump `@lix-js/client` dependency ## 1.2.1 ### Patch Changes * 74dc1f8c6: Update dependencies * 4ae6295d0: Detect when `paraglide-js init` is being run inside the VsCode terminal and skip the VsCode question if so * Updated dependencies \[74ac11c47\] * @inlang/language-tag@1.4.0 ## 1.2.0 ### Minor Changes * 0f0e8496d: Throw runtime error if `languageTag()` returns a non-language tag value ## 1.1.1 ### Patch Changes * 7ea9753fb: Improve onboarding message * 4277232db: fix: better formatting of messageID fallbacks ## 1.1.0 ### Minor Changes * cd29edb11: bumbing fixed env var dependecy issue affected packages ## 1.0.0 Bump Version to 1.0 as no more breaking changes are expected. ## 1.0.0-prerelease.26 Hotfix: Bundle SDK ## 1.0.0-prerelease.25 Update dependencies ## 1.0.0-prerelease.24 feat: Support language Fallbacks according to BCP 47 specification ## 1.0.0-prerelease.23 Update dependencies ## 1.0.0-prerelease.22 Update dependencies ## 1.0.0-prerelease.21 feat: Handle variables that have invalid JS identifiers as names. fix: Better text escaping in the compiler. fix: Compiler now fails reliably when a message ID is an invalid JS identifier. ## 1.0.0-prerelease.20 Paraglide now checks if the messages have actually changed before recompiling. This should improve reliability and performance. ## 1.0.0-prerelease.19 fix: Fix inlang/internal#195 ## 1.0.0-prerelease.18 `paraglide-js init` now adds `@inlang/message-lint-rule-valid-js-identifier` by default. ## 1.0.0-prerelease.17 `paraglide-js init` now adds `paraglide-js compile` to the postinstall script by default. This sidesteps numerous linting issues when using paraglide in CI environments. ## 1.0.0-prerelease.16 Fix `paraglide-js compile` hanging for a couple seconds after successful compilation ## 1.0.0-prerelease.15 Fix crash when using `npx @inlang/paraglide-js init` and selecting vscode. ## 1.0.0-prerelease.14 Added `--watch` flag to the `paraglide-js compile` command. This will keep the process alive and recompile whenever messages are changed. paraglide-js compile --project ./project.inlang --watch ## 1.0.0-prerelease.13 `./paraglide/runtime.js` now exports a function called `isAvailableLanguageTag`. This is the recommended way to check if something is a valid language tag, while maintaining type safety. //Pseudo code import { isAvailableLanguageTag } from "./paraglide/runtime"; if (isAvailableLanguageTag(params.lang)) { return renderSite(params.lang); } else { return 404; } ## 1.0.0-prerelease.12 \[Internal Change\] Expose the compiler so that bundler plugins can call it programmatically instead of going through the CLI. ## 1.0.0-prerelease.11 `onSetLanguageTag` no longer throws when called multiple times. Newer callbacks will overwrite old ones. Developers still should not call `onSetLanguageTag` multiple times, this is needed for HMR to work reliably. Big thanks to @KraXen72 for helping us find this bug. ## 1.0.0-prerelease.10 Add an optional options argument to message functions, to allow forcing a languageTag regardless of which languageTag is currently set. import * as m from "./paraglide/messages"; const msg = m.hello({ name: "John" }, { languageTag: "de" }); ## 1.0.0-prerelease.9 The `paraglide-js init` command now uses the inlang message format 2.0 which is human readable. ## 1.0.0-prerelease.8 IMPROVE: Paraglide now splits messages into different resource files as a step towards splitting messages by language via a bundler plugin. import * as en from "./paraglide/messages/en"; import * as de from "./paraglide/messages/de"; en.hello(); de.hello(); ## 1.0.0-prerelease.7 FIX: type error https://github.com/opral/monorepo/pull/1610#issuecomment-1801768825 export const currentLanguageTag = (params) => { + /** @type {Record<string, string>} */ const variants = { en: `The current language tag is "${params.languageTag}".`, de: `Der aktuelle Sprachtag ist "${params.languageTag}".`, } return variants[languageTag()] ?? "currentLanguageTag" } ## 1.0.0-prerelease.6 BREAKING: Paraglide JS now compiles into source code, see https://github.com/opral/monorepo/issues/1607. What you need to change: 1. Remove `--namespace` from the compile command 2. Replace imports from paraglide to point to the directory in ther source code: -import { setLanguageTag, languageTag } from '@inlang/paraglide-js/sveltekit-example'; +import { setLanguageTag, languageTag } from '../../paraglide-js/runtime'; -import * as m from "@inlang/paraglide-js/sveltekit-example/messages" +import * as m from "../../paraglide-js/messages" ## 1.0.0-prerelease.5 improve: `paraglide-js init` now adds the Visual Studio Code extension (Sherlock) if vscode is used ## 1.0.0-prerelease.4 add: `paraglide-js init` command which simplifies the setup process ## 1.0.0-prerelease.3 fix: https://github.com/opral/monorepo/issues/1478 ## 1.0.0-prerelease.1 ### fix: Jetbrains based editors not detecting `@inlang/paraglide-js/{namespace}/messages` imports The bug has been fixed by moving `./*/messages` above the less specifc `./*` export. "exports": { "./*/messages": "./dist/compiled-output/*/messages.js", "./*": "./dist/compiled-output/*/runtime.js" }, --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/vanilla-js-ts ## Vanilla JavaScript/TypeScript Setup This guide walks you through setting up Paraglide JS in a vanilla JavaScript or TypeScript project without any framework. ## Installation First, initialize a Paraglide JS project. This will set up the necessary configuration and create example message files. npx @inlang/paraglide-js@latest init ## Compiling your first messages npx @inlang/paraglide-js compile --project ./project.inlang --outdir ./src/paraglide This command generates TypeScript or JavaScript files in your `src/paraglide` directory, which you can then import into your application code. ## Using messages // Import your message functions import { m } from "./src/paraglide/messages.js"; import { getLocale, setLocale } from "./src/paraglide/runtime.js"; // Use a message (with parameters if needed) console.log(m.greeting({ name: "World" })); // "Hello World!" // Change the locale setLocale("de"); console.log(m.greeting({ name: "Welt" })); // "Hallo Welt!" // Get the current locale console.log(getLocale()); // "de" ## Next steps 1. Use a bundler plugin if applicable. 2. Read the basics documentation for more information on how to use Paraglide's messages, parameters, and locale management. --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/vite ## Paraglide JS Vite example This example shows how to use Paraglide with Vite. Any frontend framework that works with Vite is automatically supported. Paraglide JS does not require framework specific code. If you are looking for metaframework features, check out Paraglide JS on the server. The source code for the vite example is here. ## Getting started npx @inlang/paraglide-js@latest init Add the vite plugin to your `vite.config.ts`: import { defineConfig } from "vite"; +import { paraglideVitePlugin } from "@inlang/paraglide-js"; export default defineConfig({ plugins: [ + paraglideVitePlugin({ + project: "./project.inlang", + outdir: "./src/paraglide", + }), ], }); ## Usage See the basics documentation for more information on how to use Paraglide's messages, parameters, and locale management. --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/sveltekit  This example shows how to use Paraglide with SvelteKit.The source code can be found here. | Feature | Supported | | --- | --- | | CSR | β | | SSR | β | | SSG | β | | URLPattern | β | | Any Strategy | β | ## Getting started ### Install paraglide js npx @inlang/paraglide-js@latest init ### Add the `paraglideVitePlugin()` to `vite.config.js`. You can define strategy however you need. import { sveltekit } from '@sveltejs/kit/vite'; import { defineConfig } from 'vite'; +import { paraglideVitePlugin } from '@inlang/paraglide-js'; export default defineConfig({ plugins: [ sveltekit(), + paraglideVitePlugin({ + project: './project.inlang', + outdir: './src/lib/paraglide', + strategy: ['url', 'cookie', 'baseLocale'], + }) ] }); ### Add `%lang%` to `src/app.html`. See https://svelte.dev/docs/kit/accessibility#The-lang-attribute for more information. <!doctype html> -<html lang="en"> +<html lang="%lang%"> ... </html> ### Add the `paraglideMiddleware()` to `src/hooks.server.ts` import type { Handle } from '@sveltejs/kit'; import { paraglideMiddleware } from '$lib/paraglide/server'; // creating a handle to use the paraglide middleware const paraglideHandle: Handle = ({ event, resolve }) => paraglideMiddleware(event.request, ({ request: localizedRequest, locale }) => { event.request = localizedRequest; return resolve(event, { transformPageChunk: ({ html }) => { return html.replace('%lang%', locale); } }); }); export const handle: Handle = paraglideHandle; ### Add a reroute hook in `src/hooks.ts` IMPORTANT: The `reroute()` function must be exported from the `src/hooks.ts` file, not `src/hooks.server.ts`. import type { Reroute } from '@sveltejs/kit'; import { deLocalizeUrl } from '$lib/paraglide/runtime'; export const reroute: Reroute = (request) => { return deLocalizeUrl(request.url).pathname; }; ## Usage See the basics documentation for more information on how to use Paraglide's messages, parameters, and locale management. ## Static site generation (SSG) Enable pre-renderering by adding the following line to `routes/+layout.ts`: // routes/+layout.ts +export const prerender = true; Then add "invisble" anchor tags in `routes/+layout.svelte` to generate all pages during build time. SvelteKit crawls the anchor tags during the build and is, thereby, able to generate all pages statically. <script> import { page } from '$app/state'; + import { locales, localizeHref } from '$lib/paraglide/runtime'; </script> <slot></slot> +<div style="display:none"> + {#each locales as locale} + <a href={localizeHref(page.url.pathname, { locale })}>{locale}</a> + {/each} +</div> ## Troubleshooting ### Disabling AsyncLocalStorage in serverless environments If you're deploying to SvelteKit's Edge adapter like Vercel Edge or Cloudflare Pages, you can disable AsyncLocalStorage to avoid issues with Node.js dependencies not available in those environments: β οΈ Only use this option in serverless environments where each request gets its own isolated runtime context. Using it in multi-request server environments could lead to data leakage between concurrent requests. export default defineConfig({ plugins: [ sveltekit(), paraglideVitePlugin({ project: './project.inlang', outdir: './src/lib/paraglide', + disableAsyncLocalStorage: true }) ] }); ### No locale OR different locale when calling messages outside of .server.ts files If you call messages on the server outside of load functions or hooks, you might run into issues with the locale not being set correctly. This can happen if you call messages outside of a request context. // hello.ts import { m } from './paraglide/messages.js'; // π₯ there is no url in this context to retrieve // the locale from. console.log(m.hello()); --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/astro  This example demonstrates how to use Paraglide JS with Astro in SSR mode. The source code can be found here. | Feature | Supported | | --- | --- | | CSR | β | | SSR | β | | SSG | β | | URLPattern | β | | Any Strategy | β | You can integrate Paraglide JS yourself to achieve SSG. PR with an example is welcome. ## Setup ### 1\. If you have not initialized Paraglide JS yet, run: npx @inlang/paraglide-js@latest init ### 2\. Add the vite plugin to the `astro.config.mjs` file and set `output` to `server`: import { defineConfig } from "astro/config"; +import { paraglideVitePlugin } from "@inlang/paraglide-js"; +import node from "@astrojs/node"; export default defineConfig({ // ... other + vite: { + plugins: [ + paraglideVitePlugin({ + project: "./project.inlang", + outdir: "./src/paraglide", + }), + ], }, + output: "server", + adapter: node({ mode: "standalone" }), }); ### 3\. Create or add the paraglide js server middleware to the `src/middleware.ts` file: import { paraglideMiddleware } from "./paraglide/server.js"; export const onRequest = defineMiddleware((context, next) => { + return paraglideMiddleware(context.request, () => next()); }); You can read more about about Astro's middleware here. ## Usage See the basics documentation for more information on how to use Paraglide's messages, parameters, and locale management. ## Disabling AsyncLocalStorage in serverless environments You can disable async local storage in serverless environments by using the `disableAsyncLocalStorage` option. This is only safe in serverless environments where each request gets its own isolated runtime context. Using it in multi-request server environments could lead to data leakage between concurrent requests. vite: { plugins: [ paraglideVitePlugin({ project: "./project.inlang", outdir: "./src/paraglide", + disableAsyncLocalStorage: true, }), ], }, --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/react-router ## React Router v7 (framework) example This example shows how to use Paraglide with React Router v7. The source code can be found here. ## Getting started 1. Init Paraglide JS npx @inlang/paraglide-js@latest init 2. Add the vite plugin to your `vite.config.ts`: import { reactRouter } from "@react-router/dev/vite"; import { defineConfig } from "vite"; +import { paraglideVitePlugin } from "@inlang/paraglide-js"; export default defineConfig({ plugins: [ reactRouter(), + paraglideVitePlugin({ + project: "./project.inlang", + outdir: "./app/paraglide", + }), ], }); 3. Done :) Run the app and start translating. See the basics documentation for information on how to use Paraglide's messages, parameters, and locale management. ## Server side rendering If you use React Router v7 with SSR you will need to add the following code: The setup will be even easier when React Router receives middlewares which is expected to arrive in early 2025 https://github.com/remix-run/react-router/issues/12695 In `root.tsx`: import { assertIsLocale, baseLocale, getLocale, isLocale, overwriteGetLocale, } from "./paraglide/runtime"; +export function loader(args: Route.LoaderArgs) { + return { // detect the locale from the path. if no locale is found, the baseLocale is used. // e.g. /de will set the locale to "de" + locale: isLocale(args.params.locale) ? args.params.locale : baseLocale, + }; } // server-side rendering needs to be scoped to each request // react context is used to scope the locale to each request // and getLocale() is overwritten to read from the react context +const LocaleContextSSR = createContext(baseLocale); +if (import.meta.env.SSR) { + overwriteGetLocale(() => assertIsLocale(useContext(LocaleContextSSR))); +} export default function App(props: Route.ComponentProps) { return ( // use the locale + <LocaleContextSSR.Provider value={props.loaderData.locale}> <Outlet /> + </LocaleContextSSR.Provider> ); } In `routes.ts`: import { type RouteConfig, index, prefix, route, } from "@react-router/dev/routes"; export default [ // prefixing each path with an optional :locale // optional to match a path with no locale `/page` // or with a locale `/en/page` // // * make sure that the pattern you define here matches // * with the urlPatterns of paraglide JS if you use // * the `url` strategy + ...prefix(":locale?", [ index("routes/home.tsx"), route("about", "routes/about.tsx"), + ]), ] satisfies RouteConfig; --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/next-js  #### Sherlock - VS Code extension App Visualize, edit & lint translated strings at a glance via Inline Decorations & Hover Support, and extract new strings with a single click. --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/other-frameworks  #### Sherlock - VS Code extension App Visualize, edit & lint translated strings at a glance via Inline Decorations & Hover Support, and extract new strings with a single click. --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/basics ## Basics ## Adding and removing locales To add a new locale, add it to the `locales` array in `<project0name>.inlang/settings.json` file. // project.inlang/settings.json { "baseLocale": "en", + "locales": ["en", "de"] } ## Adding and editing messages This section assumes you use the inlang message format plugin that is setup by default in Paraglide JS. Messages are stored in `messages/{locale}.json` as key-value pairs. You can add parameters with curly braces. // messages/en.json { + "greeting": "Hello {name}!" } ## Importing messages After compiling your project, you'll have access to all your messages through the generated `messages.js` file: // Import all messages at once import { m } from "./paraglide/messages.js"; // Use a message console.log(m.hello_world()); // "Hello World!" ## Using parameters For messages with parameters, simply pass an object with the parameter values: // messages/en.json // { "greeting": "Hello {name}!" } import { m } from "./paraglide/messages.js"; // Pass parameters as an object console.log(m.greeting({ name: "Samuel" })); // "Hello Samuel!" ## Forcing a locale You can override the locale by passing a locale option as the second parameter: This is particularly useful in server-side contexts where you might need to render content in multiple languages regardless of the user's current locale. import { m } from "./paraglide/messages.js"; // Force the message to be in German console.log(m.greeting({ name: "Samuel" }, { locale: "de" })); // "Hallo Samuel!" ## Setting the locale To change the current locale, use the `setLocale` function: import { setLocale } from "./paraglide/runtime.js"; // Change locale to German setLocale("de"); ### Disabling reloading By default, `setLocale()` triggers a full page reload. This is a deliberate design decision that: * Enables a small, efficient runtime without complex state management * Makes Paraglide work in any framework without requiring framework-specific adapters * Follows the pattern used by major websites like YouTube, as language switching is an infrequent action that doesn't justify the complexity of a no-reload approach If you need to change the locale without a page reload, you can pass `{ reload: false }` as the second parameter, but then you'll need to handle UI updates yourself. // Change locale without reloading the page setLocale("de", { reload: false }); ## Getting the current locale To get the current locale, use the `getLocale` function: import { getLocale } from "./paraglide/runtime.js"; console.log(getLocale()); // "de" ## Routing The `localizeHref` function can be used to generate URLs with the current locale: <a href={localizeHref("/blog")}>Blog</a> Important: If you route to a different locale, ensure a reload happens afterwards. See https://inlang.com/m/gerre34r/library-inlang-paraglideJs/errors#switching-locales-via-links-doesnt-work ## Choosing your strategy You likely want to use one of the built-in strategies. Visit the strategy documentation to learn more. ## Dynamically calling messages You can dynamically call messages by specifying what messages you expect beforehand. Specifying the messages beforehand preserves tree-shaking. import { m } from "./paraglide/messages.js"; const messages = { greeting: m.greeting, goodbye: m.goodbye, }; let messageKey = "greeting"; console.log(messages[messageKey]()); // "Hello World!" ## Advanced usage * Choosing your strategy * Server-side rendering * Multi-tenancy --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/strategy ## Strategy Paraglide JS comes with various strategies to determine the locale out of the box. The strategy is defined with the `strategy` option. The priority is determined by the order of the strategies in the array. The first strategy that returns a locale will be used. In the example below, the `cookie` strategy first determines the locale. If no cookie is found, the `baseLocale` is used. compile({ project: "./project.inlang", outdir: "./src/paraglide", + strategy: ["cookie", "baseLocale"] }) ## Built-in strategies ### cookie The cookie strategy determines the locale from a cookie. compile({ project: "./project.inlang", outdir: "./src/paraglide", + strategy: ["cookie"] }) ### baseLocale Returns the `baseLocale` defined in the settings. It is useful as a fallback strategy if no other strategy returns a locale, for example, if a cookie has not been set yet. compile({ project: "./project.inlang", outdir: "./src/paraglide", + strategy: ["cookie", "baseLocale"] }) ### globalVariable Uses a global variable to determine the locale. This strategy is only useful in testing environments or to get started quickly. Setting a global variable can lead to cross-request issues in server-side environments, and the locale is not persisted between page reloads in client-side environments. compile({ project: "./project.inlang", outdir: "./src/paraglide", + strategy: ["globalVariable"] }) ### preferredLanguage Automatically detects the user's preferred language from browser settings or HTTP headers. * On the client: Uses navigator.languages * On the server: Uses the Accept-Language header compile({ project: "./project.inlang", outdir: "./src/paraglide", + strategy: ["preferredLanguage", "baseLocale"] }) The strategy attempts to match locale in order of user preference: 1. First try exact matches (e.g., "en-US" if supported) 2. Falls back to base language codes (e.g., "en") For example: * If user prefers `fr-FR,fr;q=0.9,en;q=0.7` and your app supports `["en", "fr"]`, it will use `fr` * If user prefers `en-US` and your app only supports `["en", "de"]`, it will use `en` ### localStorage Determine the locale from the user's local storage. compile({ project: "./project.inlang", outdir: "./src/paraglide", + strategy: ["localStorage"] }) ### url Determine the locale from the URL (pathname, domain, etc). compile({ project: "./project.inlang", outdir: "./src/paraglide", + strategy: ["url", "cookie"] }) The URL-based strategy uses the web standard URLPattern to match and localize URLs. Use https://urlpattern.com/ to test your URL patterns. #### Locale prefixing https://example.com/about https://example.com/de/about compile({ project: "./project.inlang", outdir: "./src/paraglide", strategy: ["url", "cookie"], urlPatterns: [ { pattern: "/:path(.*)?", localized: [ ["de", "/de/:path(.*)?"], // β make sure to match the least specific path last ["en", "/:path(.*)?"], ], }, ], }); #### Translated pathnames For pathnames where you want to localize the structure and path segments of the URL, you can use different patterns for each locale. This approach enables language-specific routes like `/about` in English and `/ueber-uns` in German. https://example.com/about https://example.com/ueber-uns Here's a simple example with translated path segments: compile({ project: "./project.inlang", outdir: "./src/paraglide", strategy: ["url", "cookie"], urlPatterns: [ // Specific translated routes { pattern: "/about", localized: [ ["en", "/about"], ["de", "/ueber-uns"], ], }, { pattern: "/products/:id", localized: [ ["en", "/products/:id"], ["de", "/produkte/:id"], ], }, // Wildcard pattern for untranslated routes // This allows you to incrementally translate routes as needed { pattern: "/:path(.*)?", localized: [ ["en", "/:path(.*)?"], ["de", "/:path(.*)?"], ], }, ], }); #### Domain-based localization https://example.com/about https://de.example.com/about compile({ project: "./project.inlang", outdir: "./src/paraglide", strategy: ["url", "cookie"], urlPatterns: [ // Include the localhost domain as otherwise the pattern will // always match and the path won't be localized { pattern: 'http://localhost::port?/:path(.*)?', localized: [ ["en", 'http://localhost::port?/en/:path(.*)?'], ["de", 'http://localhost::port?/de/:path(.*)?'] ], }, // production pattern which uses subdomains like de.example.com { pattern: "https://example.com/:path(.*)?", localized: [ ["en", "https://example.com/:path(.*)?"], ["de", "https://de.example.com/:path(.*)?"], ], }, ], }); #### Adding a base path You can add a base path to your URL patterns to support localized URLs with a common base path. For example, with the base path set to "shop": * `runtime.localizeHref("/about")` will return `/shop/en/about` * `runtime.deLocalizeHref("/about")` will return `/shop/about` When using a base path, it's important to make it optional using the `{basepath/}?` syntax with curly braces and the `?` modifier. This ensures that paths without the base path will still be properly matched and have the base path added during localization. compile({ project: "./project.inlang", outdir: "./src/paraglide", strategy: ["url", "cookie"], urlPatterns: [ { pattern: "/{shop/}?:path(.*)?", localized: [ ["en", "/{shop/}?en/:path(.*)?"], ["de", "/{shop/}?de/:path(.*)?"], ], }, ], }); This configuration enables: | Original URL | Localized URL (EN) | Localized URL (DE) | Notes | | --- | --- | --- | --- | | `/about` | `/shop/en/about` | `/shop/de/about` | Path without base path gets base path added | | `/shop/about` | `/shop/en/about` | `/shop/de/about` | Path with base path gets properly localized | The curly braces `{}` with the `?` modifier ensure that the group is treated as optional, allowing both URLs with and without the base path to be matched and properly localized. #### Making URL patterns unavailable in specific locales You can configure certain URL patterns to be unavailable in specific locales by redirecting them to a 404 page or any other designated error page. This is useful when some content or features should only be accessible in certain languages. https://example.com/specific-path // Available in English https://example.com/de/404 // Redirected to 404 in German To implement this, map the pattern to your 404 page URL for the locales where the content should be unavailable: compile({ project: "./project.inlang", outdir: "./src/paraglide", strategy: ["url", "cookie"], urlPatterns: [ // 404 page definition. // // π‘ make sure to define the 404 pattern // before a catch all pattern { pattern: "/404", localized: [ ["en", "/404"], ["de", "/de/404"], // defining paths for locales that should not // be caught by the catch all pattern // // this will be matched first and the catch all // pattern will not be triggered and a redirect // from /de/unavailable to /de/404 will be triggered ["de", "/de/unavailable"] ], }, // Path that's only available in English { pattern: "/specific-path", localized: [ ["en", "/specific-path"], // Normal path in English ["de", "/de/404"], // Redirects to 404 in German ], }, // Catch-all pattern for other routes { pattern: "/:path(.*)?", localized: [ ["en", "/:path(.*)?"], ["de", "/de/:path(.*)?"], ], }, ], }); When a user tries to access `/specific-path` in German, they will be redirected to `/de/404` instead. This approach allows you to: * Make certain content available only in specific languages * Create locale-specific restrictions for particular routes * Implement gradual rollouts of features by language * Handle legacy URLs that might only exist in certain locales Note that other paths will still work normally through the catch-all pattern, so only the specifically configured paths will be unavailable. #### Troubleshooting URL patterns When working with URL patterns, there are a few important considerations to keep in mind: ##### Excluding paths is not supported URLPattern does not support negative lookahead regex patterns. The decision to not support negative lookaheads is likely related to ReDoS (Regular Expression Denial of Service) attacks. Read this blog post or the CVE on GitHub. ##### Pattern order matters URL patterns are evaluated in the order they appear in the `urlPatterns` array. The first pattern that matches a URL will be used. This means that more specific patterns should come before more general patterns. urlPatterns: [ // β INCORRECT ORDER: The wildcard pattern will match everything, // so the specific pattern will never be reached { pattern: "https://example.com/:path(.*)?", // This will match ANY path localized: [ ["en", "https://example.com/:path(.*)?"], ["de", "https://example.com/de/:path(.*)?"], ], }, { pattern: "https://example.com/blog/:id", // This will never be reached localized: [ ["en", "https://example.com/blog/:id"], ["de", "https://example.com/de/blog/:id"], ], }, ] // β CORRECT ORDER: Specific patterns first, then more general patterns urlPatterns: [ { pattern: "https://example.com/blog/:id", // Specific pattern first localized: [ ["en", "https://example.com/blog/:id"], ["de", "https://example.com/de/blog/:id"], ], }, { pattern: "https://example.com/:path(.*)?", // General pattern last localized: [ ["en", "https://example.com/:path(.*)?"], ["de", "https://example.com/de/:path(.*)?"], ], }, ] ##### Localized pattern order matters too Within each pattern's `localized` array, the order of locale patterns also matters. When localizing a URL, the first matching pattern for the target locale will be used. Similarly, when delocalizing a URL, patterns are checked in order. This is especially important for path-based localization where one locale has a prefix (like `/de/`) and another doesn't. In these cases, put the more specific pattern (with prefix) first. // β INCORRECT ORDER: The first pattern is too general { pattern: "https://example.com/:path(.*)?", localized: [ ["en", "https://example.com/:path(.*)?"], // This will match ANY path ["en", "https://example.com/en/blog/:id"], // This specific pattern will never be reached ], } // β CORRECT ORDER: Specific patterns first, then more general patterns { pattern: "https://example.com/:path(.*)?", localized: [ ["en", "https://example.com/en/blog/:id"], // Specific pattern first ["en", "https://example.com/:path(.*)?"], // General pattern last ], } // β INCORRECT ORDER FOR DELOCALIZATION: Generic pattern first will cause problems { pattern: "/:path(.*)?", localized: [ ["en", "/:path(.*)?"], // Generic pattern will match everything including "/de/about" ["de", "/de/:path(.*)?"], // Pattern with prefix won't be reached for delocalization ], } // β CORRECT ORDER: More specific patterns with prefixes should come first { pattern: "/:path(.*)?", localized: [ ["de", "/de/:path(.*)?"], // Specific pattern with prefix first ["en", "/:path(.*)?"], // Generic pattern last ], } ##### Example: Multi-tenant application with specific routes For a multi-tenant application with specific routes, proper pattern ordering is crucial: compile({ project: "./project.inlang", outdir: "./src/paraglide", strategy: ["url", "cookie"], urlPatterns: [ // Specific product routes first { pattern: "https://:tenant.example.com/products/:id", localized: [ ["en", "https://:tenant.example.com/products/:id"], ["de", "https://:tenant.example.com/produkte/:id"], ["fr", "https://:tenant.example.com/produits/:id"], ], }, // Specific category routes next { pattern: "https://:tenant.example.com/categories/:name", localized: [ ["en", "https://:tenant.example.com/categories/:name"], ["de", "https://:tenant.example.com/kategorien/:name"], ["fr", "https://:tenant.example.com/categories/:name"], ], }, // General wildcard pattern last { pattern: "https://:tenant.example.com/:path(.*)?", localized: [ ["en", "https://:tenant.example.com/:path(.*)?"], ["de", "https://:tenant.example.com/de/:path(.*)?"], ["fr", "https://:tenant.example.com/fr/:path(.*)?"], ], }, ], }); With this configuration: 1. Product URLs like `https://acme.example.com/products/123` will use the specific product pattern 2. Category URLs like `https://acme.example.com/categories/electronics` will use the specific category pattern 3. All other URLs will fall back to the general pattern ## Write your own strategy Write your own cookie, http header, or i18n routing based locale strategy to integrate Paraglide into any framework or app. Only two APIs are needed to define this behaviour and adapt Paraglide JS to your requirements: * `overwriteGetLocale` defines the `getLocale()` function that messages use to determine the locale * `overwriteSetLocale` defines the `setLocale()` function that apps call to change the locale Because the client and server have separate Paraglide runtimes, you will need to define these behaviours separately on the client and server. The steps are usually the same, irrespective of the strategy and framework you use: 1. Use `overwriteGetLocale()` function that reads the locale from a cookie, HTTP header, or i18n routing. 2. Handle any side effects of changing the locale and trigger a re-render in your application via `overwriteSetLocale()` (for many apps, this may only be required on the client side). _Read the architecture documentation to learn more about's Paraglide's inner workings._ To dynamically resolve the locale, pass a function that returns the locale to `getLocale()`. You can use this to get the locale from the `documentElement.lang` attribute, a cookie, a locale route, or any other source. import { m } from "./paraglide/messages.js"; import { overwriteGetLocale } from "./paraglide/runtime.js"; overwriteGetLocale(() => document.documentElement.lang /** en */); m.orange_dog_wheel(); // Hello world! On the server, you might determine the locale from a cookie, a locale route, a http header, or anything else. When calling `overwriteGetLocale()` on the server, you need to be mindful of race conditions caused when multiple requests come in at the same time with different locales. To avoid this, use `AsyncLocaleStorage` in Node, or its equivalent for other server-side JS runtimes. import { m } from "./paraglide/messages.js"; import {overwriteGetLocale, baseLocale } from "./paraglide/runtime.js"; import { AsyncLocalStorage } from "node:async_hooks"; const localeStorage = new AsyncLocalStorage(); overwriteGetLocale(() => { //any calls to getLocale() in the async local storage context will return the stored locale return localeStorage.getStore() ?? baseLocale; }); export function onRequest(request, next) { const locale = detectLocale(request); //parse the locale from headers, cookies, etc. // set the async locale storage for the current request // to the detected locale and let the request continue // in that context return localeStorage.run(locale, async () => await next()); } --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/variants ## Variants Variants enable pluralization, gendering, A/B testing, and more. They are a powerful feature of inlang that allows you to create different versions of a message based on conditions. ## Matching The message below will match the following conditions: | Platform | User Gender | Message | | --- | --- | --- | | android | male | {username} has to download the app on his phone from the Google Play Store. | | ios | female | {username} has to download the app on her iPhone from the App Store. | | \* | \* | The person has to download the app. | The example below uses the inlang message format plugin for illustrative purposes. The syntax may differ depending on the plugin you are using. { "jojo_mountain_day": [{ "match": { "platform=android, userGender=male": "{username} has to download the app on his phone from the Google Play Store.", "platform=ios, userGender=female": "{username} has to download the app on her iPhone from the App Store.", "platform=*, userGender=*": "The person has to download the app." } }] } ## Pluralization You can define a variable in your message and then use it in the selector. Paraglide uses `Intl.PluralRules` under the hood to determine the plural form. | Inputs | Condition | Message | | --- | --- | --- | | count=1 | countPlural=one | There is one cat. | | count>1 | countPlural=other | There are many cats. | Read the \`local countPlural = count: plural\` syntax as "create a local variable \`countPlural\` that equals \`plural(count)\`". { "some_happy_cat": [{ "declarations": ["input count", "local countPlural = count: plural"], "selectors": ["countPlural"], "match": { "countPlural=one": "There is one cat.", "countPlural=other": "There are many cats.", }, }] } --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/file-formats ## Message file formats You can use any message syntax and file formats with Paraglide JS via inlang plugins. By default, Paraglide JS uses the inlang-message-format plugin, but you can use any other plugin that suits your needs. Mixing & matching is also possible. graph LR; subgraph "Pipeline" A\[Inlang Project\] -->|Opened by| B\[Paraglide Compiler\] B -->|Compiles| C\[Code\] end A <-.->|Imports & Exports| D\[Inlang Plugin\] D <-.->|Reads & Writes| E\[Translation File\] ## Available plugins All plugins can be found on https://inlang.com/c/plugins. Here are some popular plugins: * Inlang Message Format * JSON * i18next ## Installing a plugin Add the link of the plugin to the `modules` in the `settings.json` file. Refer to the documentation of the plugin for the linkΒ and installation guide. { "baseLocale": "en", "locales": ["en", "de"], "modules": [ "other plugins...", + "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js" ] } ## Using multiple plugins You can use multiple plugins in your project. { "baseLocale": "en", "locales": ["en", "de"], "modules": [ "other plugins...", + "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js" + "https://cdn.jsdelivr.net/npm/@inlang/plugin-i18next@latest/dist/index.js" ] } ## Implementation Details We learned the hard way that a binary `.inlang` file is needed to make localization simple. Unfortunately, git can't store binary files without losing the benefits of version control. Hence, for now, unpacking `.inlang` files into directories and creating an in-memory sqlite on each load is the way to go. ## Good-to-know: `.inlang` files The long-term vision is to use `.inlang` files directly without depending on external message files and plugins. --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/multi-tenancy ## Multi-Tenancy https://customer1.com/about -> de https://customer2.de/en/about -> en https://customer3.com/fr/about -> fr Multi-tenancy in i18n refers to serving different localization configurations. This is particularly useful when: * You have multiple customer domains that need different default languages * You're running a SaaS platform where each customer has their own subdomain * Different sections of your app need different localization strategies For example, you might want: * `customer1.fr` to default to French with English as an option * `customer2.com` to default to English with French as an option * All other domains to use path-based localization (`/en/`, `/fr/`) Multi-tenancy allows you to handle all these cases with a single configuration. ## Use Cases * **Regional Businesses**: Different domains for different markets (e.g., `.fr` for France, `.de` for Germany) * **White-Label Solutions**: Each client gets their own domain with specific language preferences * **Enterprise Applications**: Different departments or subsidiaries need different language defaults ## Configuration URLPatterns can be used to configure multi-tenancy in your application. Here is an example configuration: await compile({ project: "./project.inlang", strategy: ["url"], urlPatterns: [ // 1) customer1.fr => root locale is fr, sub-locale is /en/ { pattern: "https://customer1.fr/:path(.*)?", localized: [ ["fr", "https://customer1.fr/:path(.*)?"], ["en", "https://customer1.fr/en/:path(.*)?"], ], }, // 2) customer2.com => root locale is en, sub-locale is /fr/ { pattern: "https://customer2.com/:path(.*)?", localized: [ ["en", "https://customer2.com/:path(.*)?"], ["fr", "https://customer2.com/fr/:path(.*)?"], ], }, // 3) Any other domain => path-based for en/fr { pattern: "https://:domain(.*)/:path(.*)?", localized: [ ["en", "https://:domain(.*)/en/:path(.*)?"], ["fr", "https://:domain(.*)/fr/:path(.*)?"], ], }, ], }); ### Customer 1 * Localizing French to French: localizeHref("https://customer1.fr/about", { locale: "fr" }) // Output: "https://customer1.fr/about" * Localizing from French to English: localizeHref("https://customer1.fr/about", { locale: "en" }) // Output: "https://customer1.fr/en/about" * De-localizing English: deLocalizeHref("https://customer1.fr/en/about"); // Output: "https://customer1.fr/about" ### Customer 2 * Localizing English to English: localizeHref("https://customer2.com/about", { locale: "en" }) // Output: "https://customer2.com/about" * Localizing from English to French: localizeHref("https://customer2.com/about", { locale: "fr" }) // Output: "https://customer2.com/fr/about" * De-localizing French: deLocalizeHref("https://customer2.com/fr/about"); // Output: "https://customer2.com/about" ### Other Domains For any other domain, the path-based localization will be used for English and French. localizeHref("https://example.com/about", { locale: "fr" }) // Output: "https://example.com/fr/about" localizeHref("https://example.com/fr/about", { locale: "en" }) // Output: "https://example.com/en/about" ## Disabling Specific Locales for Tenants In multi-tenant applications, you might need to restrict certain locales for specific tenants. For example: * Customer A only supports English and German * Customer B only supports French and Spanish * Customer C supports all locales You can implement this by redirecting unsupported locales to a 404 page or any other error page using the `null` or 404 redirect pattern: await compile({ project: "./project.inlang", strategy: ["url"], urlPatterns: [ // Define 404 pages for each tenant { pattern: "https://customer1.com/404", localized: [ ["en", "https://customer1.com/404"], ["de", "https://customer1.com/de/404"], // No fr pattern - will use the en pattern as fallback ], }, // Customer1 - only supports en and de { pattern: "https://customer1.com/:path(.*)?", localized: [ ["en", "https://customer1.com/:path(.*)?"], ["de", "https://customer1.com/de/:path(.*)?"], ["fr", "https://customer1.com/404"], // Redirect fr to 404 ["es", "https://customer1.com/404"], // Redirect es to 404 ], }, // Customer2 - only supports fr and es { pattern: "https://customer2.com/:path(.*)?", localized: [ ["fr", "https://customer2.com/:path(.*)?"], ["es", "https://customer2.com/es/:path(.*)?"], ["en", "https://customer2.com/404"], // Redirect en to 404 ["de", "https://customer2.com/404"], // Redirect de to 404 ], }, // Customer3 - supports all locales { pattern: "https://customer3.com/:path(.*)?", localized: [ ["en", "https://customer3.com/:path(.*)?"], ["de", "https://customer3.com/de/:path(.*)?"], ["fr", "https://customer3.com/fr/:path(.*)?"], ["es", "https://customer3.com/es/:path(.*)?"], ], }, ], }); When a user tries to access a URL with an unsupported locale for a specific tenant, they will be redirected to the 404 page: // Customer1 doesn't support French localizeHref("https://customer1.com/about", { locale: "fr" }) // Output: "https://customer1.com/404" // Customer2 doesn't support German localizeHref("https://customer2.com/about", { locale: "de" }) // Output: "https://customer2.com/404" // Customer3 supports all locales localizeHref("https://customer3.com/about", { locale: "fr" }) // Output: "https://customer3.com/fr/about" This approach allows you to: 1. Define tenant-specific locale support 2. Gracefully handle unsupported locale requests 3. Maintain a consistent user experience across your multi-tenant application ## Tenant-Specific Locale Switchers When implementing a language/locale switcher in a multi-tenant application, it's essential to only display the locales that are supported by the current tenant. This prevents users from clicking on links that would lead to 404 pages or unsupported content. ### Filtering Available Locales You should filter the available locales based on the current tenant before rendering your locale switcher: // Example of filtering locales for a language switcher function getTenantSupportedLocales(hostname) { // Determine which tenant we're on based on hostname if (hostname.includes('customer1.com')) { // Customer1 only supports English and German return ['en', 'de']; } else if (hostname.includes('customer2.com')) { // Customer2 only supports French and Spanish return ['fr', 'es']; } else if (hostname.includes('customer3.com')) { // Customer3 supports all locales return ['en', 'de', 'fr', 'es']; } } // In your language switcher component function LanguageSwitcher() { const hostname = window.location.hostname; const supportedLocales = getTenantSupportedLocales(hostname); return ( <div className="language-switcher"> {supportedLocales.map(locale => ( <a key={locale} href={localizeHref(window.location.href, { locale })} > {locale.toUpperCase()} </a> ))} </div> ); } --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/server-side-rendering ## Server Side Rendering (SSR) / Static Site Generation (SSG) Paraglide JS provides first-class support for server-side rendering (SSR) and static site generation (SSG) through the `paraglideMiddleware()`. If you just want to use Paraglide JS on the server, in CLI apps, etc, without SSR/SSG, refer to the vanilla JS/TS docs. ## Using `paraglideMiddleware()` The `paraglideMiddleware()` handles request-scoped locale management automatically: import { paraglideMiddleware } from './paraglide/server.js'; app.get("*", async (request) => { return paraglideMiddleware(request, async ({ request, locale }) => { // Your request handling logic here return Response(html(request)); }); }); Key features: * Automatically manages async local storage context * Handles URL localization/delocalization * Ensures locale state isolation between requests ### Automatic re-directs The `paraglideMiddleware()` automatically re-directs requests to the appropriate localized URL. For example, assume that the cookie strategy preceeds the url strategy. If a request from a client is made where the client's locale cookie is set to `de`, the `paraglideMiddleware()` will re-direct the request from `https://example.com/page` to `https://example.com/de/seite`. await compile({ project: "./project.inlang", outdir: "./src/paraglide", + strategy: ['cookie', 'url'], }) If the automatic redirects are not desired, you can increase the precedence of the `url` strategy: await compile({ project: "./project.inlang", outdir: "./src/paraglide", - strategy: ['cookie', 'url'], + strategy: ['url', 'cookie'], }) ### Disabling Async Local Storage You can use `disableAsyncLocalStorage: true` to disable the use of Node.js' AsyncLocalStorage. **This is only safe** in environments that do not share request context between concurrent requests. **Examples of safe environments:** * Cloudflare Workers * Vercel Edge Functions * AWS Lambda (single-request mode) * Other isolated runtime contexts paraglideVitePlugin({ // ... disableAsyncLocalStorage: true // β οΈ Use with caution }) #### Best Practices 1. **Always enable AsyncLocalStorage** for: * Traditional Node.js servers * Docker containers handling multiple requests * Any environment with request pooling 2. **Test isolation** by making parallel requests: // Test that locale doesn't leak between requests await Promise.all([ fetch('/fr/about'), fetch('/de/contact') ]) 3. Monitor for these warning signs: * Random locale switches during load testing * Incorrect URL origins in logs * Session data appearing in wrong requests ### Serverless Environments For edge functions and serverless platforms, you can use per-request overrides because each request is isolated: // Cloudflare Worker example export default { async fetch(request: Request) { // Determine locale from request const locale = getLocaleFromURL(request.url); // Override per-request overwriteGetLocale(() => locale); overwriteGetUrlOrigin(() => new URL(request.url).origin); return handleRequest(request); } }; // Next.js Edge Middleware import { NextResponse } from 'next/server'; export function middleware(request) { const locale = getLocaleFromCookie(request); overwriteGetLocale(() => locale); overwriteGetUrlOrigin(() => request.nextUrl.origin); return NextResponse.next(); } ## Server Side Rendering (SSR) Setting up the `paraglideMiddleware()` automatically enables server-side rendering (SSR) for your application. ## Static Site Generation (SSG) Your framework of choice (e.g. Next.js, SvelteKit, etc.) needs to know the localized URLs of your pages to generate them during build time. https://example.com/about +https://example.com/de/about +https://example.com/fr/about Several possibilities exist to communicate these URLs to your framework: ### Site crawling (invisible anchor tags) Some static site generators crawl your site during build time by following anchor tags to discover all pages. You can leverage this behavior to ensure all localized URLs of your pages are generated: 1. Add invisible anchor tags in the root layout of your application for each locale 2. Ensure the `paraglideMiddleware()` is called _You can adapt the example beneath to any other framework_ import { locales, localizeHref } from "./paraglide/runtime.js"; // in the root layout function Layout({ children }) { return ( <div>{children}</div> // add invisible anchor tags for the currently visible page in each locale <div style="display: none"> {locales.map((locale) => ( <a href={localizeHref(`/about`, { locale })}></a> ))} </div> ) } The rendered HTML of the page will include the invisible anchor tags, ensuring they are generated during build time. The framwork will crawl the HTML and follow the anchor tags to discover all pages. <div> <p>My Cool Website </div> +<div style="display: none"> + <a href="/de/about"></a> + <a href="/fr/about"></a> +</div> ### Programmatic Discovery If invisible anchor tags are not an option, some frameworks provide APIs to discover the localized URLs during build time. Figure out which API your framework provides and adapt the example above accordingly. * **Next.js** has generateStaticParams() API to discover all localized URLs. * **Astro** has getStaticPaths() ## Troubleshooting ### `getLocale()` returns a different locale than expected This can happen if `getLocale()` is called outside of the scope of `paraglideMiddleware()`. The `paraglideMiddleware()` ensures that the locale is set correctly for each request. If you call `getLocale()` outside of the scope of `paraglideMiddleware()`, you will get the locale of the server which is not the expected locale. app.get("*", async (request) => { // β don't call `getLocale()` outside of `paraglideMiddleware` const locale = getLocale() return paraglideMiddleware(request, async ({ request, locale }) => { // β call `getLocale()` inside of `paraglideMiddleware` const locale = getLocale() // Your request handling logic here return Response(html(request)); }); }); --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/compiling-messages ## Compiling Messages There are three ways to invoke the Paraglide JS compiler: 1. Via the Paraglide CLI 2. Via a bundler plugin 3. Programatically Bundler plugins are the recommend approach. They are more flexible and can be integrated into your build pipeline. ## Via the Paraglide CLI To compile your messages via the CLI, run the following command: npx @inlang/paraglide-js compile --project ./project.inlang --outdir ./src/paraglide Use `--help` to see all available options: npx @inlang/paraglide-js compile --help ## Via a bundler plugin Paraglide JS exports bundler plugins via the `paraglide<Bundler>Plugin()` functions. * `paraglideRollupPlugin` * `paraglideWebpackPlugin` * `paraglideVitePlugin` * `paraglideRspackPlugin` * `paraglideRolldownPlugin` * `paraglideEsbuildPlugin` * ... and more plugins supported by unplugin ### Vite example import { defineConfig } from "vite"; import { paraglideVitePlugin } from "@inlang/paraglide-js"; export default defineConfig({ plugins: [ paraglideVitePlugin({ project: "./project.inlang", outdir: "./src/paraglide", }), ], }); ## Programatically The Paraglide compiler can be invoked programatically via the `compile` function. import { compile } from "@inlang/paraglide-js"; await compile({ project: "./project.inlang", outdir: "./src/paraglide", }); If you need/want to extend the compiler, you can use the lower level `compileProject` function. import { compileProject } from "@inlang/paraglide-js"; import { loadProjectFromDirectory } from "@inlang/sdk"; const inlangProject = await loadProjectFromDirectory({ path: "./project.inlang", }); const output = await compileProject({ project: inlangProject, }); console.log(output); --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/incremental-migration ## Incremental migration Paraglide JS can load any translation file formats. As such, you can incrementally migrate to Paraglide JS. * Continue using your existing translation files * Mix and match different formats (JSON, i18next, etc.) * Migrate your codebase piece by piece This example uses i18next translation files but is applicable to any format. ## Add the i18next plugin Find a list of available plugins here. For this example, the i18next plugin is used. Update the `project.inlang` settings to include the i18next plugin: { "baseLocale": "en", "locales": ["en", "de", "fr"], "modules": [ "https://cdn.jsdelivr.net/npm/@inlang/message-format-plugin@latest/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/plugin-i18next@latest/dist/index.js" ], + "plugin.inlang.i18next": { + "pathPattern": "./locales/{locale}.json" + } } ### Using i18n libraries together Now you can use both i18next and Paraglide JS in your code: // Existing i18next code import i18next from 'i18next'; import { m } from './paraglide/messages.js'; console.log(i18next.t('greeting', { name: 'World' })); console.log(m.greeting({ name: 'World' })); --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/architecture ## Architecture Paraglide uses a compiler to generate JS functions from your messages. We call these "message functions". Message Functions are fully typed and TypeScript compatible using JSDoc. They are exported individually from the `messages.js` file making them tree-shakable. When called, they return a translated string. Message functions aren't reactive in any way, if you want a translation in another language you will need to re-call them. This design avoids many edge cases with reactivity, lazy-loading, and namespacing that other i18n libraries have to work around. flowchart TD INLANG\_PROJECT\[INLANG PROJECT\] COMPILER\[COMPILER\] subgraph RUNTIME\[runtime.js\] GET\_LOCALE\["getLocale()"\] SET\_LOCALE\["setLocale()"\] STRATEGY end subgraph MESSAGES\[messages.js\] M\["m.hello\_world()"\] end COMPILER -->|Opens| INLANG\_PROJECT M --> GET\_LOCALE MESSAGES --> COMPILER RUNTIME --> COMPILER APP\[Your App\] --> M MESSAGE\["'Hello World!'"\] -->|renders| APP\[Your App\] APP --> SET\_LOCALE GET\_LOCALE --> STRATEGY SET\_LOCALE --> STRATEGY classDef plainText stroke-width:0,fill-opacity:0,color:black; class X plainText Paraglide consists of four main parts: | Part | Description | | --- | --- | | **Compiler** | Compiles messages into tree-shakable message functions | | **Messages** | The compiled tree-shakable message functions | | **Runtime** | A runtime that resolves the locale based on the strategy | | **Strategy** | The strategy to detect the locale of a user | --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/compiler-options ## CompilerOptions > **CompilerOptions**: `object` Defined in: compiler-options.ts:19 ### Type declaration #### additionalFiles? > `optional` **additionalFiles**: `Record`<`string`, `string`\> The `additionalFiles` option is an array of paths to additional files that should be copied to the output directory. ##### Example await compile({ project: "./project.inlang", outdir: "./src/paraglide", additionalFiles: [ + "my-file.js": "console.log('hello')" ] }) The output will look like this: - outdir/ - messages/ + - my-file.js - messages.js - runtime.js #### cleanOutdir? > `optional` **cleanOutdir**: `boolean` Whether to clean the output directory before writing the new files. ##### Default true #### cookieDomain? > `optional` **cookieDomain**: `string` The host to which the cookie will be sent. If null, this defaults to the host portion of the current document location and the cookie is not available on subdomains. Otherwise, subdomains are always included. ##### Default window.location.hostname #### cookieMaxAge? > `optional` **cookieMaxAge**: `number` The max-age in seconds of the cookie until it expires. ##### Default 60 * 60 * 24 * 400 #### cookieName? > `optional` **cookieName**: `string` The name of the cookie to use for the cookie strategy. ##### Default 'PARAGLIDE_LOCALE' #### disableAsyncLocalStorage? > `optional` **disableAsyncLocalStorage**: `boolean` Replaces AsyncLocalStorage with a synchronous implementation. β οΈ WARNING: This should ONLY be used in serverless environments like Cloudflare Workers. Disabling AsyncLocalStorage in traditional server environments risks cross-request pollution where state from one request could leak into another concurrent request. #### emitGitIgnore? > `optional` **emitGitIgnore**: `boolean` If `emitGitIgnore` is set to `true` a `.gitignore` file will be emitted in the output directory. Defaults to `true`. - outdir/ - messages/ + - .gitignore - messages.js - runtime.js ##### Default true #### emitPrettierIgnore? > `optional` **emitPrettierIgnore**: `boolean` If `emitPrettierIgnore` is set to `true` a `.prettierignore` file will be emitted in the output directory. Defaults to `true`. - outdir/ - messages/ + - .prettierignore - messages.js - runtime.js ##### Default true #### experimentalMiddlewareLocaleSplitting? > `optional` **experimentalMiddlewareLocaleSplitting**: `boolean` Whether or not to use experimental middleware locale splitting. β οΈ This feature is experimental and only works in SSR/SSG environment without client-side routing. Do not rely on this feature for production. This feature is part of the exploration of per locale splitting. The issue is ongoing and can be followed here #88. * The client bundle will tree-shake all messages (have close to 0kb JS). * The server middleware will inject the used messages into the HTML. * The client will re-trieve the messages from the injected HTML. ##### Default false #### fs? > `optional` **fs**: `any` The file system to use. Defaults to `await import('node:fs')`. Useful for testing the paraglide compiler by mocking the fs. > `optional` **includeEslintDisableComment**: `boolean` Whether to include an eslint-disable comment at the top of each .js file. ##### Default true #### isServer? > `optional` **isServer**: `string` Tree-shaking flag if the code is running on the server. Dependent on the bundler, this flag must be adapted to enable tree-shaking. ##### Example // vite isServer: "import.meta.env.SSR" ##### Default typeof window === "undefined" #### localStorageKey? > `optional` **localStorageKey**: `string` The name of the localStorage key to use for the localStorage strategy. ##### Default 'PARAGLIDE_LOCALE' #### outdir > **outdir**: `string` The path to the output directory. ##### Example await compile({ project: "./project.inlang", + outdir: "./src/paraglide" }) #### outputStructure? > `optional` **outputStructure**: `"locale-modules"` | `"message-modules"` The `outputStructure` defines how modules are structured in the output. * `message-modules` - Each module is a message. This is the default. * `locale-modules` - Each module is a locale. It is recommended to use `locale-modules` for development and `message-modules` for production. Bundlers speed up the dev mode by bypassing bundling which can lead to many http requests during the dev mode with `message-modules`. See https://github.com/opral/inlang-paraglide-js/issues/486. **`message-modules`** Messages have their own module which eases tree-shaking for bundlers. - outdir/ - messages/ + - blue_elephant_tree/ + - index.js + - en.js + - fr.js + - sky_phone_bottle/ + - index.js + - en.js + - fr.js - ... - messages.js - runtime.js **`locale-modules`** Messages are bundled in a per locale module. Bundlers often struggle with tree-shaking this structure, which can lead to more inefficient tree-shaking and larger bundle sizes compared to `message-modules`. The benefit are substantially fewer files which is needed in large projects. - outdir/ - messages/ + - de.js + - en.js + - fr.js - ... - messages.js - runtime.js ##### Default "message-modules" #### project > **project**: `string` The path to the inlang project. ##### Example await compile({ + project: "./project.inlang", outdir: "./src/paraglide" }) #### strategy? > `optional` **strategy**: `Runtime`\[`"strategy"`\] The strategy to use for getting the locale. The order of the strategy defines the precedence of matches. For example, in `['url', 'cookie', 'baseLocale']`, the locale will be first tried to be detected in the url, then in a cookie, and finally fallback to the base locale. The default ensures that the browser takes a cookie approach, server-side takes the globalVariable (because cookie is unavailable), whereas both fallback to the base locale if not available. ##### Default ["cookie", "globalVariable", "baseLocale"] #### urlPatterns? > `optional` **urlPatterns**: `Runtime`\[`"urlPatterns"`\] https://inlang.com/m/gerre34r/library-inlang-paraglideJs/strategy#url * * * ## defaultCompilerOptions > `const` **defaultCompilerOptions**: `object` Defined in: compiler-options.ts:3 ### Type declaration #### cleanOutdir > `readonly` **cleanOutdir**: `true` = `true` #### cookieDomain > `readonly` **cookieDomain**: `""` = `""` #### cookieMaxAge > `readonly` **cookieMaxAge**: `number` #### cookieName > `readonly` **cookieName**: `"PARAGLIDE_LOCALE"` = `"PARAGLIDE_LOCALE"` #### disableAsyncLocalStorage > `readonly` **disableAsyncLocalStorage**: `false` = `false` #### emitGitIgnore > `readonly` **emitGitIgnore**: `true` = `true` #### emitPrettierIgnore > `readonly` **emitPrettierIgnore**: `true` = `true` #### experimentalMiddlewareLocaleSplitting > `readonly` **experimentalMiddlewareLocaleSplitting**: `false` = `false` > `readonly` **includeEslintDisableComment**: `true` = `true` #### isServer > `readonly` **isServer**: `"typeof window === 'undefined'"` = `"typeof window === 'undefined'"` #### localStorageKey > `readonly` **localStorageKey**: `"PARAGLIDE_LOCALE"` = `"PARAGLIDE_LOCALE"` #### outputStructure > `readonly` **outputStructure**: `"message-modules"` = `"message-modules"` #### strategy > `readonly` **strategy**: \[`"cookie"`, `"globalVariable"`, `"baseLocale"`\] --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/runtime ## Locale > **Locale**: `any` Defined in: runtime/ambient.d.ts:10 * * * ## ParaglideAsyncLocalStorage > **ParaglideAsyncLocalStorage**<>: `object` Defined in: runtime/variables.js:54 ### Type Parameters ### Type declaration #### run() > **run**: (`store`, `cb`) => `any` ##### Parameters ###### store ###### locale? `Locale` ###### messageCalls? `Set`<`string`\> ###### origin? `string` ###### cb `any` ##### Returns `any` #### getStore() ##### Returns `undefined` | { `locale`: `Locale`; `messageCalls`: `Set`<`string`\>; `origin`: `string`; } * * * ## baseLocale > `const` **baseLocale**: `"en"` = `"en"` Defined in: runtime/variables.js:9 The project's base locale. ### Example if (locale === baseLocale) { // do something } * * * ## cookieMaxAge > `const` **cookieMaxAge**: `number` Defined in: runtime/variables.js:25 * * * ## cookieName > `const` **cookieName**: `string` = `"<cookie-name>"` Defined in: runtime/variables.js:22 * * * ## disableAsyncLocalStorage > `const` **disableAsyncLocalStorage**: `false` = `false` Defined in: runtime/variables.js:67 * * * ## experimentalMiddlewareLocaleSplitting > `const` **experimentalMiddlewareLocaleSplitting**: `false` = `false` Defined in: runtime/variables.js:69 * * * ## isServer > `const` **isServer**: `boolean` Defined in: runtime/variables.js:71 * * * ## locales > `const` **locales**: readonly \[`"en"`, `"de"`\] Defined in: runtime/variables.js:19 The project's locales that have been specified in the settings. ### Example if (locales.includes(userSelectedLocale) === false) { throw new Error('Locale is not available'); } * * * ## serverAsyncLocalStorage > **serverAsyncLocalStorage**: `undefined` | `ParaglideAsyncLocalStorage` = `undefined` Defined in: runtime/variables.js:65 Server side async local storage that is set by `serverMiddleware()`. The variable is used to retrieve the locale and origin in a server-side rendering context without effecting other requests. * * * ## strategy > `const` **strategy**: (`"cookie"` | `"baseLocale"` | `"globalVariable"` | `"url"` | `"preferredLanguage"` | `"localStorage"`)\[\] Defined in: runtime/variables.js:36 * * * ## urlPatterns > `const` **urlPatterns**: `object`\[\] = `[]` Defined in: runtime/variables.js:43 The used URL patterns. ### Type declaration #### localized > **localized**: \[`any`, `string`\]\[\] #### pattern > **pattern**: `string` * * * ## assertIsLocale() > **assertIsLocale**(`input`): `any` Defined in: runtime/assert-is-locale.js:11 Asserts that the input is a locale. ### Parameters #### input `any` The input to check. ### Returns `any` The input if it is a locale. ### Throws If the input is not a locale. * * * ## deLocalizeHref() > **deLocalizeHref**(`href`): `string` Defined in: runtime/localize-href.js:104 High-level URL de-localization function optimized for client-side UI usage. This is a convenience wrapper around `deLocalizeUrl()` that provides features needed in the UI: * Accepts relative paths (e.g., "/de/about") * Returns relative paths when possible * Handles string input/output instead of URL objects ### Parameters #### href `string` The href to de-localize (can be relative or absolute) ### Returns `string` The de-localized href, relative if input was relative ### Example // In a React/Vue/Svelte component const LocaleSwitcher = ({ href }) => { // Remove locale prefix before switching const baseHref = deLocalizeHref(href); return locales.map(locale => <a href={localizeHref(baseHref, { locale })}> Switch to {locale} </a> ); }; // Examples: deLocalizeHref("/de/about") // => "/about" deLocalizeHref("/fr/store") // => "/store" // Cross-origin links remain absolute deLocalizeHref("https://example.com/de/about") // => "https://example.com/about" For server-side URL de-localization (e.g., in middleware), use `deLocalizeUrl()` which provides more precise control over URL handling. ### See deLocalizeUrl - For low-level URL de-localization in server contexts * * * ## deLocalizeUrl() > **deLocalizeUrl**(`url`): `URL` Defined in: runtime/localize-url.js:184 Low-level URL de-localization function, primarily used in server contexts. This function is designed for server-side usage where you need precise control over URL de-localization, such as in middleware or request handlers. It works with URL objects and always returns absolute URLs. For client-side UI components, use `deLocalizeHref()` instead, which provides a more convenient API with relative paths. ### Parameters #### url The URL to de-localize. If string, must be absolute. `string` | `URL` ### Returns `URL` The de-localized URL, always absolute ### Examples // Server middleware example app.use((req, res, next) => { const url = new URL(req.url, `${req.protocol}://${req.headers.host}`); const baseUrl = deLocalizeUrl(url); // Store the base URL for later use req.baseUrl = baseUrl; next(); }); // Using with URL patterns const url = new URL("https://example.com/de/about"); deLocalizeUrl(url); // => URL("https://example.com/about") // Using with domain-based localization const url = new URL("https://de.example.com/store"); deLocalizeUrl(url); // => URL("https://example.com/store") * * * > **extractLocaleFromCookie**(): `undefined` | `string` Defined in: runtime/extract-locale-from-cookie.js:12 Extracts a cookie from the document. Will return undefined if the docuement is not available or if the cookie is not set. The `document` object is not available in server-side rendering, so this function should not be called in that context. ### Returns `undefined` | `string` * * * > **extractLocaleFromRequest**(`request`): `any` Defined in: runtime/extract-locale-from-request.js:28 Extracts a locale from a request. Use the function on the server to extract the locale from a request. The function goes through the strategies in the order they are defined. If a strategy returns an invalid locale, it will fall back to the next strategy. ### Parameters #### request `Request` ### Returns `any` ### Example const locale = extractLocaleFromRequest(request); * * * > **extractLocaleFromUrl**(`url`): `any` Defined in: runtime/extract-locale-from-url.js:15 Extracts the locale from a given URL using native URLPattern. ### Parameters #### url The full URL from which to extract the locale. `string` | `URL` ### Returns `any` The extracted locale, or undefined if no locale is found. * * * ## generateStaticLocalizedUrls() > **generateStaticLocalizedUrls**(`urls`): `URL`\[\] Defined in: runtime/generate-static-localized-urls.js:31 Generates a list of localized URLs for all provided URLs. This is useful for SSG (Static Site Generation) and sitemap generation. NextJS and other frameworks use this function for SSG. ### Parameters #### urls (`string` | `URL`)\[\] List of URLs to generate localized versions for. Can be absolute URLs or paths. ### Returns `URL`\[\] List of localized URLs as URL objects ### Example const urls = generateStaticLocalizedUrls([ "https://example.com/about", "https://example.com/blog", ]); urls[0].href // => "https://example.com/about" urls[1].href // => "https://example.com/blog" urls[2].href // => "https://example.com/de/about" urls[3].href // => "https://example.com/de/blog" ... * * * ## getLocale() > **getLocale**(): `any` Defined in: runtime/get-locale.js:44 Get the current locale. ### Returns `any` ### Example if (getLocale() === 'de') { console.log('Germany π©πͺ'); } else if (getLocale() === 'nl') { console.log('Netherlands π³π±'); } * * * ## getUrlOrigin() > **getUrlOrigin**(): `string` Defined in: runtime/get-url-origin.js:12 The origin of the current URL. Defaults to "http://y.com" in non-browser environments. If this behavior is not desired, the implementation can be overwritten by `overwriteGetUrlOrigin()`. ### Returns `string` * * * ## isLocale() > **isLocale**(`locale`): `locale is any` Defined in: runtime/is-locale.js:16 Check if something is an available locale. ### Parameters #### locale `any` ### Returns `locale is any` ### Example if (isLocale(params.locale)) { setLocale(params.locale); } else { setLocale('en'); } * * * ## localizeHref() > **localizeHref**(`href`, `options`?): `string` Defined in: runtime/localize-href.js:43 High-level URL localization function optimized for client-side UI usage. This is a convenience wrapper around `localizeUrl()` that provides features needed in UI: * Accepts relative paths (e.g., "/about") * Returns relative paths when possible * Automatically detects current locale if not specified * Handles string input/output instead of URL objects ### Parameters #### href `string` The href to localize (can be relative or absolute) #### options? Options for localization ##### locale? `string` Target locale. If not provided, uses `getLocale()` ### Returns `string` The localized href, relative if input was relative ### Example // In a React/Vue/Svelte component const NavLink = ({ href }) => { // Automatically uses current locale, keeps path relative return <a href={localizeHref(href)}>...</a>; }; // Examples: localizeHref("/about") // => "/de/about" (if current locale is "de") localizeHref("/store", { locale: "fr" }) // => "/fr/store" (explicit locale) // Cross-origin links remain absolute localizeHref("https://other-site.com/about") // => "https://other-site.com/de/about" For server-side URL localization (e.g., in middleware), use `localizeUrl()` which provides more precise control over URL handling. * * * ## localizeUrl() > **localizeUrl**(`url`, `options`?): `URL` Defined in: runtime/localize-url.js:53 Lower-level URL localization function, primarily used in server contexts. This function is designed for server-side usage where you need precise control over URL localization, such as in middleware or request handlers. It works with URL objects and always returns absolute URLs. For client-side UI components, use `localizeHref()` instead, which provides a more convenient API with relative paths and automatic locale detection. ### Parameters #### url The URL to localize. If string, must be absolute. `string` | `URL` #### options? Options for localization ##### locale? `string` Target locale. If not provided, uses getLocale() ### Returns `URL` The localized URL, always absolute ### Examples // Server middleware example app.use((req, res, next) => { const url = new URL(req.url, `${req.protocol}://${req.headers.host}`); const localized = localizeUrl(url, { locale: "de" }); if (localized.href !== url.href) { return res.redirect(localized.href); } next(); }); // Using with URL patterns const url = new URL("https://example.com/about"); localizeUrl(url, { locale: "de" }); // => URL("https://example.com/de/about") // Using with domain-based localization const url = new URL("https://example.com/store"); localizeUrl(url, { locale: "de" }); // => URL("https://de.example.com/store") * * * ## overwriteGetLocale() > **overwriteGetLocale**(`fn`): `void` Defined in: runtime/get-locale.js:147 Overwrite the `getLocale()` function. Use this function to overwrite how the locale is resolved. For example, you can resolve the locale from the browser's preferred language, a cookie, env variable, or a user's preference. ### Parameters #### fn () => `any` ### Returns `void` ### Example overwriteGetLocale(() => { // resolve the locale from a cookie. fallback to the base locale. return Cookies.get('locale') ?? baseLocale } * * * ## overwriteGetUrlOrigin() > **overwriteGetUrlOrigin**(`fn`): `void` Defined in: runtime/get-url-origin.js:29 Overwrite the getUrlOrigin function. Use this function in server environments to define how the URL origin is resolved. ### Parameters #### fn () => `string` ### Returns `void` * * * ## overwriteServerAsyncLocalStorage() > **overwriteServerAsyncLocalStorage**(`value`): `void` Defined in: runtime/variables.js:83 Sets the server side async local storage. The function is needed because the `runtime.js` file must define the `serverAsyncLocalStorage` variable to avoid a circular import between `runtime.js` and `server.js` files. ### Parameters #### value `undefined` | `ParaglideAsyncLocalStorage` ### Returns `void` * * * ## overwriteSetLocale() > **overwriteSetLocale**(`fn`): `void` Defined in: runtime/set-locale.js:128 Overwrite the `setLocale()` function. Use this function to overwrite how the locale is set. For example, modify a cookie, env variable, or a user's preference. ### Parameters #### fn (`newLocale`) => `void` ### Returns `void` ### Example overwriteSetLocale((newLocale) => { // set the locale in a cookie return Cookies.set('locale', newLocale) }); * * * ## setLocale() > **setLocale**(`newLocale`, `options`?): `void` Defined in: runtime/set-locale.js:32 Set the locale. Set locale reloads the site by default on the client. Reloading can be disabled by passing `reload: false` as an option. If reloading is disabled, you need to ensure that the UI is updated to reflect the new locale. ### Parameters #### newLocale `any` #### options? ##### reload? `boolean` ### Returns `void` ### Examples setLocale('en'); setLocale('en', { reload: false }); * * * ## trackMessageCall() > **trackMessageCall**(`safeModuleId`, `locale`): `void` Defined in: runtime/track-message-call.js:7 ### Parameters #### safeModuleId `string` #### locale `any` ### Returns `void` --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/server ## paraglideMiddleware() > **paraglideMiddleware**<`T`\>(`request`, `resolve`): `Promise`<`Response`\> Defined in: server/middleware.js:64 Server middleware that handles locale-based routing and request processing. This middleware performs several key functions: 1. Determines the locale for the incoming request using configured strategies 2. Handles URL localization and redirects (only for document requests) 3. Maintains locale state using AsyncLocalStorage to prevent request interference When URL strategy is used: * The locale is extracted from the URL for all request types * If URL doesn't match the determined locale, redirects to localized URL (only for document requests) * De-localizes URLs before passing to server (e.g., `/fr/about` β `/about`) ### Type Parameters β’ **T** The return type of the resolve function ### Parameters #### request `Request` The incoming request object #### resolve (`args`) => `T` | `Promise`<`T`\> Function to handle the request ### Returns `Promise`<`Response`\> ### Examples // Basic usage in metaframeworks like NextJS, SvelteKit, Astro, Nuxt, etc. export const handle = async ({ event, resolve }) => { return serverMiddleware(event.request, ({ request, locale }) => { // let the framework further resolve the request return resolve(request); }); }; // Usage in a framework like Express JS or Hono app.use(async (req, res, next) => { const result = await serverMiddleware(req, ({ request, locale }) => { // If a redirect happens this won't be called return next(request); }); }); // Usage in serverless environments like Cloudflare Workers // β οΈ WARNING: This should ONLY be used in serverless environments like Cloudflare Workers. // Disabling AsyncLocalStorage in traditional server environments risks cross-request pollution where state from // one request could leak into another concurrent request. export default { fetch: async (request) => { return serverMiddleware( request, ({ request, locale }) => handleRequest(request, locale), { disableAsyncLocalStorage: true } ); } }; --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/errors ## Errors ## No locale found Paraglide JS was not able to resolve a locale. This can happen if: 1. Your strategy array is empty. -strategy: [] +strategy: ["cookie", "baseLocale"] 2. You are using `overwriteGetLocale()` and `overwriteSetLocale()` but forgot to call them at the root/entrypoint of your app. import { overwriteGetLocale, overwriteSetLocale } from "./paraglide/runtime.js"; // Call the overwrites before your app starts rendering. overwriteGetLocale(() => "en"); overwriteSetLocale((locale) => console.log(`Set locale to ${locale}`)); // Your app rendering entrypoint. export default function App() { return ( <div> <p>Hello world</p> </div> ); } 3. You are using the `url` strategy but call messages outside of a request context. strategy: ["url"] // hello.ts import { m } from "./paraglide/messages.js"; // π₯ there is no url in this context to retrieve // the locale from. console.log(m.hello()); Make sure to call messages within a request context that is set by the paraglideMiddleware: Dependent on your framework, what runs in a request context can differ. In SvelteKit, for example, you can use the \`load\` function in your routes to ensure that messages are called within a request context. // hello.ts import { m } from "./paraglide/messages.js"; app.use(paraglideMiddleware); // β this will work app.get("/", (req, res) => { console.log(m.hello()); }); 4. You make API requests and only have `strategy: ["url"]` set. Paraglide JS will only extract the locale from a URL if the request is a document request, indicated by Sec-Fetch-Dest: document to distinguish it from API requests. Add `cookie` or `baseLocale` to your strategy array to ensure that the locale is always resolved in API requests as well. -strategy: ["url"] +strategy: ["url", "cookie", "baseLocale"] ## Switching locales via links doesn't work Use `setLocale()` to switch locales. If your application uses client-side routing, the UI will not update if you use a localized href to switch the locale. You need to force a reload or navigate to the new locale. Issue #472 discusses possibilities to handle this in a framework-agnostic way. import { setLocale } from "./paraglide/runtime.js"; // β this will work setLocale("de"); // β this will not work in client side routing <a href={localizeHref("/page", { locale: "de" })}>Deutsch</a> // π your framework might expose a reload attribute which // would make locale switching via links work <a href={localizeHref("/page", { locale: "de" })} reload={true} >Deutsch</a> --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/next  #### Sherlock - VS Code extension App Visualize, edit & lint translated strings at a glance via Inline Decorations & Hover Support, and extract new strings with a single click. --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/strategy.md  #### Sherlock - VS Code extension App Visualize, edit & lint translated strings at a glance via Inline Decorations & Hover Support, and extract new strings with a single click. --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/runtime/type.md#runtime  #### Sherlock - VS Code extension App Visualize, edit & lint translated strings at a glance via Inline Decorations & Hover Support, and extract new strings with a single click. --- ## Page: https://inlang.com/m/gerre34r/library-inlang-paraglideJs/-internal-.md#locale  #### Sherlock - VS Code extension App Visualize, edit & lint translated strings at a glance via Inline Decorations & Hover Support, and extract new strings with a single click.