2023-02-20 16:26:30 +01:00
|
|
|
import React, {
|
|
|
|
|
createContext,
|
|
|
|
|
PropsWithChildren,
|
|
|
|
|
useContext,
|
2023-09-26 11:38:22 +02:00
|
|
|
useEffect,
|
2023-02-20 16:26:30 +01:00
|
|
|
useMemo,
|
|
|
|
|
} from "react";
|
2023-05-04 15:04:47 +02:00
|
|
|
import * as enUS from ":/locales/en-US.json";
|
|
|
|
|
import * as frFR from ":/locales/fr-FR.json";
|
|
|
|
|
import { PartialNested } from ":/types";
|
|
|
|
|
import { Locales } from ":/components/Provider/Locales";
|
2024-01-05 11:55:16 +01:00
|
|
|
import { ToastProvider } from ":/components/Toast/ToastProvider";
|
2024-01-18 14:38:48 +01:00
|
|
|
import { ModalProvider } from ":/components/Modal/ModalProvider";
|
2023-02-20 16:26:30 +01:00
|
|
|
|
|
|
|
|
type TranslationSet = PartialNested<typeof enUS>;
|
|
|
|
|
|
|
|
|
|
const CunninghamContext = createContext<
|
|
|
|
|
| undefined
|
|
|
|
|
| {
|
|
|
|
|
t: (key: string, vars?: Record<string, string | number>) => string;
|
2023-06-26 18:55:13 +02:00
|
|
|
currentLocale: string;
|
2023-02-20 16:26:30 +01:00
|
|
|
}
|
|
|
|
|
>(undefined);
|
|
|
|
|
|
|
|
|
|
export const useCunningham = () => {
|
|
|
|
|
const context = useContext(CunninghamContext);
|
|
|
|
|
if (context === undefined) {
|
|
|
|
|
throw new Error("useCunningham must be used within a CunninghamProvider.");
|
|
|
|
|
}
|
|
|
|
|
return context;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
interface Props extends PropsWithChildren {
|
|
|
|
|
customLocales?: Record<string, TranslationSet>;
|
|
|
|
|
currentLocale?: string;
|
2023-09-26 11:38:22 +02:00
|
|
|
theme?: string;
|
2024-04-04 16:59:12 +02:00
|
|
|
modalParentSelector?: () => HTMLElement;
|
2023-02-20 16:26:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const DEFAULT_LOCALE = Locales.enUS;
|
2023-09-26 11:38:22 +02:00
|
|
|
export const DEFAULT_THEME = "default";
|
2023-02-20 16:26:30 +01:00
|
|
|
export const SUPPORTED_LOCALES = Object.values(Locales);
|
2023-09-26 11:38:22 +02:00
|
|
|
const THEME_CLASSNAME_PREFIX = "cunningham-theme--";
|
2023-02-20 16:26:30 +01:00
|
|
|
|
|
|
|
|
const findTranslation = (
|
|
|
|
|
key: string,
|
2023-07-18 15:43:56 +02:00
|
|
|
locale: TranslationSet,
|
2023-02-20 16:26:30 +01:00
|
|
|
): string | undefined => {
|
|
|
|
|
const [namespace, ...keys] = key.split(".");
|
|
|
|
|
return keys.reduce((acc, subKey) => acc[subKey], (locale as any)[namespace]);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const CunninghamProvider = ({
|
|
|
|
|
currentLocale = DEFAULT_LOCALE,
|
|
|
|
|
customLocales,
|
2023-09-26 11:38:22 +02:00
|
|
|
theme = DEFAULT_THEME,
|
2024-04-04 16:59:12 +02:00
|
|
|
modalParentSelector,
|
2023-02-20 16:26:30 +01:00
|
|
|
children,
|
|
|
|
|
}: Props) => {
|
|
|
|
|
const locales: Record<string, TranslationSet> = useMemo(
|
|
|
|
|
() => ({
|
|
|
|
|
[DEFAULT_LOCALE]: enUS,
|
|
|
|
|
"fr-FR": frFR,
|
|
|
|
|
...customLocales,
|
|
|
|
|
}),
|
2023-07-18 15:43:56 +02:00
|
|
|
[customLocales],
|
2023-02-20 16:26:30 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const locale = useMemo(() => {
|
2023-06-26 18:55:13 +02:00
|
|
|
return (locales[currentLocale] && currentLocale) || DEFAULT_LOCALE;
|
2023-02-20 16:26:30 +01:00
|
|
|
}, [currentLocale, locales]);
|
|
|
|
|
|
|
|
|
|
const context = useMemo(
|
|
|
|
|
() => ({
|
|
|
|
|
t: (key: string, vars?: Record<string, string | number>) => {
|
|
|
|
|
let message: string =
|
2023-06-26 18:55:13 +02:00
|
|
|
findTranslation(key, locales[locale]) ??
|
2023-02-20 16:26:30 +01:00
|
|
|
findTranslation(key, locales[DEFAULT_LOCALE]) ??
|
|
|
|
|
key;
|
|
|
|
|
|
|
|
|
|
// Replace vars in message from vars in form of {varName}.
|
|
|
|
|
if (vars) {
|
|
|
|
|
Object.keys(vars).forEach((varName) => {
|
|
|
|
|
message = message?.replace(`{${varName}}`, "" + vars[varName]);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return message;
|
|
|
|
|
},
|
2023-06-26 18:55:13 +02:00
|
|
|
currentLocale: locale,
|
2023-02-20 16:26:30 +01:00
|
|
|
}),
|
2023-07-18 15:43:56 +02:00
|
|
|
[currentLocale, locales],
|
2023-02-20 16:26:30 +01:00
|
|
|
);
|
|
|
|
|
|
2023-09-26 11:38:22 +02:00
|
|
|
useEffect(() => {
|
|
|
|
|
const root = document.querySelector(":root")!;
|
|
|
|
|
root.classList.forEach((className) => {
|
|
|
|
|
if (className.startsWith(THEME_CLASSNAME_PREFIX)) {
|
|
|
|
|
root.classList.remove(className);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
root.classList.add(THEME_CLASSNAME_PREFIX + theme);
|
|
|
|
|
}, [theme]);
|
|
|
|
|
|
2023-02-20 16:26:30 +01:00
|
|
|
return (
|
|
|
|
|
<CunninghamContext.Provider value={context}>
|
2024-04-04 16:59:12 +02:00
|
|
|
<ModalProvider modalParentSelector={modalParentSelector}>
|
2024-04-04 15:01:29 +02:00
|
|
|
<div className="c__app">
|
|
|
|
|
<ToastProvider>{children}</ToastProvider>
|
|
|
|
|
</div>
|
2024-01-18 14:38:48 +01:00
|
|
|
</ModalProvider>
|
2023-02-20 16:26:30 +01:00
|
|
|
</CunninghamContext.Provider>
|
|
|
|
|
);
|
|
|
|
|
};
|