191 lines
5.8 KiB
Plaintext
191 lines
5.8 KiB
Plaintext
import { normalizeTheLocale, toCodes } from "../../i18n/index.js";
|
|
import { AstroError, AstroErrorData } from "../errors/index.js";
|
|
import { getParamsAndProps } from "./params-and-props.js";
|
|
import { ROUTE_DATA_SYMBOL } from "../constants.js";
|
|
const clientLocalsSymbol = Symbol.for("astro.locals");
|
|
const routeDataSymbol = Symbol.for(ROUTE_DATA_SYMBOL);
|
|
async function createRenderContext(options) {
|
|
const request = options.request;
|
|
const pathname = options.pathname ?? new URL(request.url).pathname;
|
|
const [params, props] = await getParamsAndProps({
|
|
mod: options.mod,
|
|
route: options.route,
|
|
routeCache: options.env.routeCache,
|
|
pathname,
|
|
logger: options.env.logger,
|
|
ssr: options.env.ssr
|
|
});
|
|
const context = {
|
|
...options,
|
|
pathname,
|
|
params,
|
|
props,
|
|
locales: options.locales,
|
|
routing: options.routing,
|
|
defaultLocale: options.defaultLocale
|
|
};
|
|
Object.defineProperty(context, "locals", {
|
|
enumerable: true,
|
|
get() {
|
|
return Reflect.get(request, clientLocalsSymbol);
|
|
},
|
|
set(val) {
|
|
if (typeof val !== "object") {
|
|
throw new AstroError(AstroErrorData.LocalsNotAnObject);
|
|
} else {
|
|
Reflect.set(request, clientLocalsSymbol, val);
|
|
}
|
|
}
|
|
});
|
|
return context;
|
|
}
|
|
function parseLocale(header) {
|
|
if (header === "*") {
|
|
return [{ locale: header, qualityValue: void 0 }];
|
|
}
|
|
const result = [];
|
|
const localeValues = header.split(",").map((str) => str.trim());
|
|
for (const localeValue of localeValues) {
|
|
const split = localeValue.split(";").map((str) => str.trim());
|
|
const localeName = split[0];
|
|
const qualityValue = split[1];
|
|
if (!split) {
|
|
continue;
|
|
}
|
|
if (qualityValue && qualityValue.startsWith("q=")) {
|
|
const qualityValueAsFloat = Number.parseFloat(qualityValue.slice("q=".length));
|
|
if (Number.isNaN(qualityValueAsFloat) || qualityValueAsFloat > 1) {
|
|
result.push({
|
|
locale: localeName,
|
|
qualityValue: void 0
|
|
});
|
|
} else {
|
|
result.push({
|
|
locale: localeName,
|
|
qualityValue: qualityValueAsFloat
|
|
});
|
|
}
|
|
} else {
|
|
result.push({
|
|
locale: localeName,
|
|
qualityValue: void 0
|
|
});
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function sortAndFilterLocales(browserLocaleList, locales) {
|
|
const normalizedLocales = toCodes(locales).map(normalizeTheLocale);
|
|
return browserLocaleList.filter((browserLocale) => {
|
|
if (browserLocale.locale !== "*") {
|
|
return normalizedLocales.includes(normalizeTheLocale(browserLocale.locale));
|
|
}
|
|
return true;
|
|
}).sort((a, b) => {
|
|
if (a.qualityValue && b.qualityValue) {
|
|
if (a.qualityValue > b.qualityValue) {
|
|
return -1;
|
|
} else if (a.qualityValue < b.qualityValue) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
});
|
|
}
|
|
function computePreferredLocale(request, locales) {
|
|
const acceptHeader = request.headers.get("Accept-Language");
|
|
let result = void 0;
|
|
if (acceptHeader) {
|
|
const browserLocaleList = sortAndFilterLocales(parseLocale(acceptHeader), locales);
|
|
const firstResult = browserLocaleList.at(0);
|
|
if (firstResult && firstResult.locale !== "*") {
|
|
for (const currentLocale of locales) {
|
|
if (typeof currentLocale === "string") {
|
|
if (normalizeTheLocale(currentLocale) === normalizeTheLocale(firstResult.locale)) {
|
|
result = currentLocale;
|
|
}
|
|
} else {
|
|
for (const currentCode of currentLocale.codes) {
|
|
if (normalizeTheLocale(currentCode) === normalizeTheLocale(firstResult.locale)) {
|
|
result = currentLocale.path;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function computePreferredLocaleList(request, locales) {
|
|
const acceptHeader = request.headers.get("Accept-Language");
|
|
let result = [];
|
|
if (acceptHeader) {
|
|
const browserLocaleList = sortAndFilterLocales(parseLocale(acceptHeader), locales);
|
|
if (browserLocaleList.length === 1 && browserLocaleList.at(0).locale === "*") {
|
|
return locales.map((locale) => {
|
|
if (typeof locale === "string") {
|
|
return locale;
|
|
} else {
|
|
return locale.codes.at(0);
|
|
}
|
|
});
|
|
} else if (browserLocaleList.length > 0) {
|
|
for (const browserLocale of browserLocaleList) {
|
|
for (const loopLocale of locales) {
|
|
if (typeof loopLocale === "string") {
|
|
if (normalizeTheLocale(loopLocale) === normalizeTheLocale(browserLocale.locale)) {
|
|
result.push(loopLocale);
|
|
}
|
|
} else {
|
|
for (const code of loopLocale.codes) {
|
|
if (code === browserLocale.locale) {
|
|
result.push(loopLocale.path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function computeCurrentLocale(request, locales, routingStrategy, defaultLocale) {
|
|
const routeData = Reflect.get(request, routeDataSymbol);
|
|
if (!routeData) {
|
|
return defaultLocale;
|
|
}
|
|
const pathname = routeData.pathname ?? new URL(request.url).pathname;
|
|
for (const segment of pathname.split("/").filter(Boolean)) {
|
|
for (const locale of locales) {
|
|
if (typeof locale === "string") {
|
|
if (!segment.includes(locale))
|
|
continue;
|
|
if (normalizeTheLocale(locale) === normalizeTheLocale(segment)) {
|
|
return locale;
|
|
}
|
|
} else {
|
|
if (locale.path === segment) {
|
|
return locale.codes.at(0);
|
|
} else {
|
|
for (const code of locale.codes) {
|
|
if (normalizeTheLocale(code) === normalizeTheLocale(segment)) {
|
|
return code;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (routingStrategy === "pathname-prefix-other-locales" || routingStrategy === "domains-prefix-other-locales") {
|
|
return defaultLocale;
|
|
}
|
|
return void 0;
|
|
}
|
|
export {
|
|
computeCurrentLocale,
|
|
computePreferredLocale,
|
|
computePreferredLocaleList,
|
|
createRenderContext,
|
|
parseLocale
|
|
};
|