50 lines
1.5 KiB
TypeScript
50 lines
1.5 KiB
TypeScript
import React from 'react';
|
|
import { translate, DEFAULT_LOCALE, type LocaleCode } from './messages';
|
|
import { useLocale } from './LocaleContext';
|
|
|
|
type ReplacementValues = Record<string, string | number>;
|
|
|
|
export type TranslateFn = {
|
|
(key: string): string;
|
|
(key: string, fallback: string): string;
|
|
(key: string, replacements: ReplacementValues): string;
|
|
(key: string, replacements: ReplacementValues, fallback: string): string;
|
|
};
|
|
|
|
function resolveTranslation(locale: LocaleCode, key: string, fallback?: string): string {
|
|
return translate(locale, key) ?? translate(DEFAULT_LOCALE, key) ?? fallback ?? key;
|
|
}
|
|
|
|
function applyReplacements(value: string, replacements?: ReplacementValues): string {
|
|
if (!replacements) {
|
|
return value;
|
|
}
|
|
|
|
return Object.entries(replacements).reduce((acc, [token, replacement]) => {
|
|
const pattern = new RegExp(`\\{${token}\\}`, 'g');
|
|
return acc.replace(pattern, String(replacement));
|
|
}, value);
|
|
}
|
|
|
|
export function useTranslation() {
|
|
const { locale } = useLocale();
|
|
|
|
const t = React.useCallback<TranslateFn>((key: string, arg2?: ReplacementValues | string, arg3?: string) => {
|
|
let replacements: ReplacementValues | undefined;
|
|
let fallback: string | undefined;
|
|
|
|
if (typeof arg2 === 'string' || arg2 === undefined) {
|
|
fallback = arg2 ?? arg3;
|
|
} else {
|
|
replacements = arg2;
|
|
fallback = arg3;
|
|
}
|
|
|
|
const raw = resolveTranslation(locale, key, fallback);
|
|
|
|
return applyReplacements(raw, replacements);
|
|
}, [locale]);
|
|
|
|
return React.useMemo(() => ({ t, locale }), [t, locale]);
|
|
}
|