v3.accords-library.com/translations/translations.ts

217 lines
6.2 KiB
TypeScript
Raw Normal View History

2024-02-17 22:11:17 +00:00
import { cache } from "src/utils/cachedPayload";
2024-01-29 22:37:55 +00:00
import en from "./en.json";
2024-02-01 20:00:55 +00:00
import fr from "./fr.json";
2024-02-17 22:11:17 +00:00
import ja from "./ja.json";
2024-02-01 20:00:55 +00:00
2024-02-17 22:11:17 +00:00
import acceptLanguage from "accept-language";
2024-02-26 02:08:59 +00:00
import { KeysTypes } from "src/shared/payload/payload-sdk";
2024-01-29 22:37:55 +00:00
type WordingKeys = keyof typeof en;
2024-02-01 20:00:55 +00:00
const translationFiles: Record<string, Record<WordingKeys, string>> = {
en,
fr,
2024-02-17 22:11:17 +00:00
ja,
2024-02-01 20:00:55 +00:00
};
2024-01-29 22:37:55 +00:00
2024-01-29 04:02:58 +00:00
export const getI18n = async (locale: string) => {
2024-02-01 20:00:55 +00:00
const translations = translationFiles[locale];
2024-01-29 04:02:58 +00:00
2024-01-29 22:37:55 +00:00
const formatWithValues = (
templateName: string,
template: string,
values: Record<string, any>
): string => {
Object.entries(values).forEach(([key, value]) => {
if (
!template.match(new RegExp(`{{ ${key}\\+|{{ ${key}\\?|{{ ${key} }}`))
) {
console.warn(
"Value",
key,
"has been provided but is not present in template",
templateName
);
return;
}
if (typeof value === "number") {
// Find "plural" tokens
const matches = [
...template.matchAll(
new RegExp(`{{ ${key}\\+,[\\w\\s=>{},]+ }}`, "g")
),
].map(limitMatchToBalanceCurlyBraces);
const handlePlural = (match: string): string => {
match = match.substring(3, match.length - 3);
const options = match.split(",").splice(1);
for (const option of options) {
const optionCondition = option.split("{")[0];
if (!optionCondition) continue;
let optionValue = option.substring(optionCondition.length + 1);
if (!optionValue) continue;
optionValue = optionValue.substring(0, optionValue.length - 1);
if (option.startsWith("=")) {
const optionConditionValue = Number.parseInt(
optionCondition.substring(1)
);
if (value === optionConditionValue) {
return optionValue;
}
} else if (option.startsWith(">")) {
const optionConditionValue = Number.parseInt(
optionCondition.substring(1)
);
if (value > optionConditionValue) {
return optionValue;
}
} else if (option.startsWith("<")) {
const optionConditionValue = Number.parseInt(
optionCondition.substring(1)
);
if (value < optionConditionValue) {
return optionValue;
}
}
}
return "";
};
matches.forEach((match) => {
template = template.replace(match, handlePlural(match));
});
}
// Find "conditional" tokens
const matches = [
...template.matchAll(new RegExp(`{{ ${key}\\?,[\\w\\s{},]+ }}`, "g")),
].map(limitMatchToBalanceCurlyBraces);
const handleConditional = (match: string): string => {
match = match.substring(3, match.length - 3);
const options = match.split(",").splice(1);
if (value !== undefined && value !== null && value !== "") {
return options[0] ?? "";
}
return options[1] ?? "";
};
matches.forEach((match) => {
template = template.replace(match, handleConditional(match));
});
// Find "variable" tokens
let prettyValue = value;
if (typeof prettyValue === "number") {
prettyValue = prettyValue.toLocaleString(locale);
}
template = template.replaceAll(`{{ ${key} }}`, prettyValue);
});
return template;
};
2024-02-26 02:08:59 +00:00
const getLocalizedMatch = <T extends { language: string }>(
options: T[],
fallback: Omit<T, "language">
): Omit<T, "language"> & { language?: string } =>
options.find(({ language }) => language === locale) ??
options.find(({ language }) => language === defaultLocale) ?? {
...fallback,
};
const getLocalizedKey = (
keyType: KeysTypes,
keyId: string,
format: "short" | "default"
) => {
const category = cache.keys.find(
({ id, type }) => id === keyId && type === keyType
);
if (!category) {
return "UNKNOWN";
}
if (!category.translations) {
return category.name;
}
const translation = getLocalizedMatch(category.translations, {
name: category.name,
short: category.name,
});
return format === "default" ? translation.name : translation.short;
};
2024-01-29 04:02:58 +00:00
return {
2024-01-29 22:37:55 +00:00
t: (key: WordingKeys, values: Record<string, any> = {}): string => {
2024-02-01 20:00:55 +00:00
if (translations && key in translations) {
2024-01-29 22:37:55 +00:00
return formatWithValues(key, translations[key]!, values);
2024-01-29 04:02:58 +00:00
}
2024-01-29 22:37:55 +00:00
return `«${key}»`;
2024-01-29 04:02:58 +00:00
},
2024-01-29 22:37:55 +00:00
getLocalizedUrl: (url: string): string => `/${locale}${url}`,
2024-02-26 02:08:59 +00:00
getLocalizedMatch,
formatCategory: (
id: string,
format: "short" | "default" = "default"
): string => getLocalizedKey(KeysTypes.Categories, id, format),
formatContentType: (id: string): string =>
getLocalizedKey(KeysTypes.Contents, id, "default"),
formatRecorder: (recorderId: string): string => {
const result = cache.recorders.find(({ id }) => id === recorderId);
if (!result) {
return "UNKNOWN";
}
return result.username;
},
formatLocale: (code: string): string =>
cache.locales.find(({ id }) => id === code)?.name ?? code,
2024-01-29 04:02:58 +00:00
};
};
2024-01-29 22:37:55 +00:00
const limitMatchToBalanceCurlyBraces = (
matchArray: RegExpMatchArray
): string => {
// Cut match as soon as curly braces are balanced.
const match = matchArray[0];
let curlyCount = 2;
let index = 2;
while (index < match.length && curlyCount > 0) {
if (match[index] === "{") {
curlyCount++;
}
if (match[index] === "}") {
curlyCount--;
}
index++;
}
return match.substring(0, index);
};
2024-02-01 20:00:55 +00:00
2024-02-17 22:11:17 +00:00
export type Locale = string;
2024-02-01 20:00:55 +00:00
export const defaultLocale: Locale = "en";
export const getCurrentLocale = (pathname: string): Locale | undefined => {
2024-02-17 22:11:17 +00:00
for (const locale of cache.locales) {
2024-02-26 02:08:59 +00:00
if (pathname.startsWith(`/${locale.id}`)) {
return locale.id;
2024-02-01 20:00:55 +00:00
}
}
return undefined;
};
2024-02-17 22:11:17 +00:00
export const getBestAcceptedLanguage = (
request: Request
): Locale | undefined => {
2024-02-26 02:08:59 +00:00
acceptLanguage.languages(cache.locales.map(({ id }) => id));
2024-02-01 20:00:55 +00:00
2024-02-17 22:11:17 +00:00
return (
(acceptLanguage.get(
request.headers.get("Accept-Language")
) as Locale | null) ?? undefined
);
2024-02-01 20:00:55 +00:00
};