@inlang/paraglide-js
Version:
[](https://www.npmjs.com/package/@inlang/paraglide-js) [ => {
const runtime = await createParaglide({
blob: await newProject({
settings: {
baseLocale: "en",
locales: ["en", "fr"],
},
}),
strategy: ["cookie"],
cookieName: "PARAGLIDE_LOCALE",
});
const request = new Request("http://example.com", {
headers: {
cookie: `PARAGLIDE_LOCALE=fr`,
},
});
const locale = runtime.extractLocaleFromRequest(request);
expect(locale).toBe("fr");
});
test("returns the locale from a cookie header without whitespace after semicolons", async () => {
const runtime = await createParaglide({
blob: await newProject({
settings: {
baseLocale: "en",
locales: ["en", "fr"],
},
}),
strategy: ["cookie"],
cookieName: "PARAGLIDE_LOCALE",
});
const request = new Request("http://example.com", {
headers: {
cookie: `PARAGLIDE_LOCALE=fr;session=abc`,
},
});
const locale = runtime.extractLocaleFromRequest(request);
expect(locale).toBe("fr");
});
test("returns the locale from the pathname for document requests", async () => {
const runtime = await createParaglide({
blob: await newProject({
settings: {
baseLocale: "en",
locales: ["en"],
},
}),
strategy: ["url", "baseLocale"],
urlPatterns: [
{
pattern: "https://example.com/:path(.*)",
localized: [["en", "https://example.com/en/:path(.*)"]],
},
],
});
const request = new Request("https://example.com/en/home", {
headers: {
"Sec-Fetch-Dest": "document",
},
});
const locale = runtime.extractLocaleFromRequest(request);
expect(locale).toBe("en");
});
test("uses the provided public url for url strategy matching", async () => {
const runtime = await createParaglide({
blob: await newProject({
settings: {
baseLocale: "en",
locales: ["en", "fr"],
},
}),
strategy: ["url", "baseLocale"],
urlPatterns: [
{
pattern: "https://example.com/:path(.*)?",
localized: [
["en", "https://example.com/en/:path(.*)?"],
["fr", "https://example.com/fr/:path(.*)?"],
],
},
],
});
const request = new Request("http://internal.example.com/en/home", {
headers: {
"Sec-Fetch-Dest": "document",
},
});
const locale = runtime.extractLocaleFromRequest(request, {
effectiveRequestUrl: "https://example.com/fr/home",
});
expect(locale).toBe("fr");
});
test("resolves relative effectiveRequestUrl strings against request.url", async () => {
const runtime = await createParaglide({
blob: await newProject({
settings: {
baseLocale: "en",
locales: ["en", "fr"],
},
}),
strategy: ["url", "baseLocale"],
urlPatterns: [
{
pattern: "https://example.com/:path(.*)?",
localized: [
["en", "https://example.com/en/:path(.*)?"],
["fr", "https://example.com/fr/:path(.*)?"],
],
},
],
});
const request = new Request("https://example.com/en/home", {
headers: {
"Sec-Fetch-Dest": "document",
},
});
const locale = runtime.extractLocaleFromRequest(request, {
effectiveRequestUrl: "/fr/home",
});
expect(locale).toBe("fr");
});
test("returns the baseLocale if no other strategy matches", async () => {
const runtime = await createParaglide({
blob: await newProject({
settings: {
baseLocale: "en",
locales: ["en"],
},
}),
strategy: ["baseLocale"],
});
const request = new Request("http://example.com");
const locale = runtime.extractLocaleFromRequest(request);
expect(locale).toBe("en");
});
test("throws an error if no locale is found", async () => {
const runtime = await createParaglide({
blob: await newProject({
settings: {
baseLocale: "en",
locales: ["en"],
},
}),
strategy: ["cookie"],
});
const request = new Request("http://example.com");
expect(() => runtime.extractLocaleFromRequest(request)).toThrow("No locale found. There is an error in your strategy. Try adding 'baseLocale' as the very last strategy.");
});
test("returns the preferred locale from Accept-Language header", async () => {
const runtime = await createParaglide({
blob: await newProject({
settings: {
baseLocale: "en",
locales: ["en", "fr", "de"],
},
}),
strategy: ["preferredLanguage", "baseLocale"],
});
// simple direct match
const request = new Request("http://example.com", {
headers: {
"Accept-Language": "en-US;q=0.8,fr;q=0.9,de;q=0.6",
},
});
const locale = runtime.extractLocaleFromRequest(request);
expect(locale).toBe("fr");
// language tag match
const request2 = new Request("http://example.com", {
headers: {
"Accept-Language": "en-US;q=0.8,nl;q=0.9,de;q=0.7",
},
});
const locale2 = runtime.extractLocaleFromRequest(request2);
// Since "nl" isn't in our locales, and "de" has the highest q-value after "nl"
expect(locale2).toBe("en");
// no match
const request3 = new Request("http://example.com", {
headers: {
"Accept-Language": "nl;q=0.9,es;q=0.6",
},
});
const locale3 = runtime.extractLocaleFromRequest(request3);
expect(locale3).toBe("en");
});
test("should fall back to next strategy when cookie contains invalid locale", async () => {
const runtime = await createParaglide({
blob: await newProject({
settings: {
baseLocale: "en",
locales: ["en", "fr"],
},
}),
strategy: ["cookie", "baseLocale"],
cookieName: "PARAGLIDE_LOCALE",
});
// Request with an invalid locale in cookie
const request = new Request("http://example.com", {
headers: {
cookie: `PARAGLIDE_LOCALE=invalid_locale`,
},
});
// Should fall back to baseLocale
expect(runtime.extractLocaleFromRequest(request)).toBe("en");
});
test("skips over localStorage strategy as it is not supported on the server", async () => {
const runtime = await createParaglide({
blob: await newProject({
settings: {
baseLocale: "en",
locales: ["en", "fr"],
},
}),
strategy: ["localStorage", "baseLocale"],
});
const request = new Request("http://example.com");
// expecting baseLocale
expect(runtime.extractLocaleFromRequest(request)).toBe("en");
});
test("resolves the locale from the url for all request types", async () => {
const runtime = await createParaglide({
blob: await newProject({
settings: {
baseLocale: "en",
locales: ["en", "fr"],
},
}),
strategy: ["url", "baseLocale"],
urlPatterns: [
{
pattern: "https://example.com/:path(.*)",
localized: [
["en", "https://example.com/en/:path(.*)"],
["fr", "https://example.com/fr/:path(.*)"],
],
},
],
});
// Non-document request should still use URL strategy
const request = new Request("https://example.com/fr/home", {
headers: {
"Sec-Fetch-Dest": "something",
},
});
const locale = runtime.extractLocaleFromRequest(request);
expect(locale).toBe("fr");
});
test("cookie strategy precedes URL strategy for API requests with wildcards", async () => {
const runtime = await createParaglide({
blob: await newProject({
settings: {
baseLocale: "en",
locales: ["en", "fr", "de"],
},
}),
strategy: ["cookie", "url", "baseLocale"],
cookieName: "PARAGLIDE_LOCALE",
urlPatterns: [
{
pattern: "https://example.com/:path(.*)",
localized: [
["fr", "https://example.com/fr/:path(.*)"],
["de", "https://example.com/de/:path(.*)"],
["en", "https://example.com/:path(.*)"],
],
},
],
});
// API request with cookie should use cookie locale
const apiRequestWithCookie = new Request("https://example.com/api/data", {
headers: {
"Sec-Fetch-Dest": "empty",
cookie: "PARAGLIDE_LOCALE=de",
},
});
const apiLocale = runtime.extractLocaleFromRequest(apiRequestWithCookie);
expect(apiLocale).toBe("de");
// API request without cookie should fall back to URL strategy
const apiRequestNoMatch = new Request("https://example.com/api/data", {
headers: {
"Sec-Fetch-Dest": "empty",
},
});
const fallbackLocale = runtime.extractLocaleFromRequest(apiRequestNoMatch);
expect(fallbackLocale).toBe("en");
});
test("routeStrategies can override strategy order for matching routes", async () => {
const runtime = await createParaglide({
blob: await newProject({
settings: {
baseLocale: "en",
locales: ["en", "de"],
},
}),
strategy: ["url", "cookie", "baseLocale"],
cookieName: "PARAGLIDE_LOCALE",
routeStrategies: [
{
match: "/dashboard/:path(.*)?",
strategy: ["cookie", "baseLocale"],
},
],
});
const request = new Request("https://example.com/dashboard", {
headers: {
cookie: "PARAGLIDE_LOCALE=de",
},
});
expect(runtime.extractLocaleFromRequest(request)).toBe("de");
});
// https://github.com/opral/inlang-paraglide-js/issues/436
test("preferredLanguage precedence over url", async () => {
const runtime = await createParaglide({
blob: await newProject({
settings: {
baseLocale: "en",
locales: ["en", "de"],
},
}),
strategy: ["url", "preferredLanguage"],
urlPatterns: [
{
pattern: ":protocol://:domain(.*)::port??/:path(.*)?",
localized: [
["de", ":protocol://:domain(.*)::port?/de/:path(.*)?"],
["en", ":protocol://:domain(.*)::port?/en/:path(.*)?"],
],
},
],
});
// no locale in url, should use preferredLanguage
const request = new Request("https://example.com/home", {
headers: {
"Sec-Fetch-Dest": "document",
"Accept-Language": "de;q=0.9,en;q=0.8",
},
});
const locale = runtime.extractLocaleFromRequest(request);
expect(locale).toBe("de");
});
test("sync version no longer supports custom strategies", async () => {
const runtime = await createParaglide({
blob: await newProject({
settings: {
baseLocale: "en",
locales: ["en", "fr", "de"],
},
}),
strategy: ["custom-header", "baseLocale"],
});
// Define a custom strategy that extracts locale from a custom header
runtime.defineCustomServerStrategy("custom-header", {
getLocale: (request) => request?.headers.get("X-Custom-Locale") ?? undefined,
});
const request = new Request("http://example.com", {
headers: {
"X-Custom-Locale": "fr",
},
});
// Sync version skips custom strategies and falls back to baseLocale
const locale = runtime.extractLocaleFromRequest(request);
expect(locale).toBe("en"); // baseLocale fallback
});
test("sync version skips custom strategy and falls back to next built-in strategy", async () => {
const runtime = await createParaglide({
blob: await newProject({
settings: {
baseLocale: "en",
locales: ["en", "fr"],
},
}),
strategy: ["custom-fallback", "baseLocale"],
});
runtime.defineCustomServerStrategy("custom-fallback", {
getLocale: () => undefined,
});
const request = new Request("http://example.com");
const locale = runtime.extractLocaleFromRequest(request);
expect(locale).toBe("en"); // Should fall back to baseLocale (skipping custom strategy)
});
test("sync version uses built-in strategies instead of custom strategies", async () => {
const runtime = await createParaglide({
blob: await newProject({
settings: {
baseLocale: "en",
locales: ["en", "fr", "de"],
},
}),
strategy: ["custom-priority", "cookie", "baseLocale"],
cookieName: "PARAGLIDE_LOCALE",
});
runtime.defineCustomServerStrategy("custom-priority", {
getLocale: () => "de",
});
const request = new Request("http://example.com", {
headers: {
cookie: "PARAGLIDE_LOCALE=fr", // Cookie has different locale
},
});
const locale = runtime.extractLocaleFromRequest(request);
expect(locale).toBe("fr"); // Should use cookie since custom strategy is skipped in sync version
});
test("sync version skips all custom strategies and uses baseLocale", async () => {
const runtime = await createParaglide({
blob: await newProject({
settings: {
baseLocale: "en",
locales: ["en", "fr", "de"],
},
}),
strategy: ["custom-first", "custom-second", "baseLocale"],
});
runtime.defineCustomServerStrategy("custom-first", {
getLocale: () => undefined,
});
runtime.defineCustomServerStrategy("custom-second", {
getLocale: () => "fr",
});
const request = new Request("http://example.com");
const locale = runtime.extractLocaleFromRequest(request);
expect(locale).toBe("en"); // Should skip all custom strategies and use baseLocale
});
//# sourceMappingURL=extract-locale-from-request.test.js.map