feat: localization (#196)
* Started adding locale translations :) * Added some more translations * Working on translating even more pages * More translations * Added test default locale retrieval * replace `intl.formatMessage` with custom `t` hook * add more translations * improve title syntax * add more translations * translate admin config page * translated error messages * add language selecter * minor fixes * improve language handling * add upcoming languages * add `crowdin.yml` * run formatter --------- Co-authored-by: Steve Tautonico <stautonico@gmail.com>
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
import moment from "moment";
|
||||
|
||||
export const getExpirationPreview = (
|
||||
name: string,
|
||||
messages: {
|
||||
neverExpires: string;
|
||||
expiresOn: string;
|
||||
},
|
||||
form: {
|
||||
values: {
|
||||
never_expires?: boolean;
|
||||
@@ -13,7 +16,7 @@ export const getExpirationPreview = (
|
||||
const value = form.values.never_expires
|
||||
? "never"
|
||||
: form.values.expiration_num + form.values.expiration_unit;
|
||||
if (value === "never") return `This ${name} will never expire.`;
|
||||
if (value === "never") return messages.neverExpires;
|
||||
|
||||
const expirationDate = moment()
|
||||
.add(
|
||||
@@ -22,5 +25,8 @@ export const getExpirationPreview = (
|
||||
)
|
||||
.toDate();
|
||||
|
||||
return `This ${name} will expire on ${moment(expirationDate).format("LLL")}`;
|
||||
return messages.expiresOn.replace(
|
||||
"{expiration}",
|
||||
moment(expirationDate).format("LLL")
|
||||
);
|
||||
};
|
||||
|
||||
42
frontend/src/utils/i18n.util.ts
Normal file
42
frontend/src/utils/i18n.util.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { setCookie } from "cookies-next";
|
||||
import { LOCALES } from "../i18n/locales";
|
||||
|
||||
const getLocaleByCode = (code: string) => {
|
||||
return Object.values(LOCALES).find((l) => l.code === code) ?? LOCALES.ENGLISH;
|
||||
};
|
||||
|
||||
// Parse the Accept-Language header and return the first supported language
|
||||
const getLanguageFromAcceptHeader = (acceptLanguage?: string) => {
|
||||
if (!acceptLanguage) return "en";
|
||||
|
||||
const languages = acceptLanguage.split(",").map((l) => l.split(";")[0]);
|
||||
const supportedLanguages = Object.values(LOCALES).map((l) => l.code);
|
||||
|
||||
for (const language of languages) {
|
||||
// Try to match the full language code first, then the language code without the region
|
||||
if (supportedLanguages.includes(language)) {
|
||||
return language;
|
||||
} else if (supportedLanguages.includes(language.split("-")[0])) {
|
||||
return language.split("-")[0];
|
||||
}
|
||||
}
|
||||
return "en";
|
||||
};
|
||||
|
||||
const isLanguageSupported = (code: string) => {
|
||||
return Object.values(LOCALES).some((l) => l.code === code);
|
||||
};
|
||||
|
||||
const setLanguageCookie = (code: string) => {
|
||||
setCookie("language", code, {
|
||||
sameSite: "lax",
|
||||
expires: new Date(new Date().setFullYear(new Date().getFullYear() + 1)),
|
||||
});
|
||||
};
|
||||
|
||||
export default {
|
||||
getLocaleByCode,
|
||||
getLanguageFromAcceptHeader,
|
||||
isLanguageSupported,
|
||||
setLanguageCookie,
|
||||
};
|
||||
@@ -1,6 +1,5 @@
|
||||
export const configVariableToFriendlyName = (variable: string) => {
|
||||
const splitted = variable.split(/(?=[A-Z])/).join(" ");
|
||||
return splitted.charAt(0).toUpperCase() + splitted.slice(1);
|
||||
export const camelToKebab = (camelCaseString: string) => {
|
||||
return camelCaseString.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
||||
};
|
||||
|
||||
export const capitalizeFirstLetter = (string: string) => {
|
||||
|
||||
35
frontend/src/utils/userPreferences.util.ts
Normal file
35
frontend/src/utils/userPreferences.util.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
const defaultPreferences = [
|
||||
{
|
||||
key: "colorScheme",
|
||||
value: "system",
|
||||
},
|
||||
{
|
||||
key: "locale",
|
||||
value: "system",
|
||||
},
|
||||
];
|
||||
|
||||
const get = (key: string) => {
|
||||
if (typeof window !== "undefined") {
|
||||
const preferences = JSON.parse(localStorage.getItem("preferences") ?? "{}");
|
||||
return (
|
||||
preferences[key] ??
|
||||
defaultPreferences.find((p) => p.key == key)?.value ??
|
||||
null
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const set = (key: string, value: string) => {
|
||||
if (typeof window !== "undefined") {
|
||||
const preferences = JSON.parse(localStorage.getItem("preferences") ?? "{}");
|
||||
preferences[key] = value;
|
||||
localStorage.setItem("preferences", JSON.stringify(preferences));
|
||||
}
|
||||
};
|
||||
const userPreferences = {
|
||||
get,
|
||||
set,
|
||||
};
|
||||
|
||||
export default userPreferences;
|
||||
Reference in New Issue
Block a user