/
LocaleProvider.tsx
103 lines (91 loc) · 4.17 KB
/
LocaleProvider.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import * as React from "react";
import { TranslationsObject, Storage } from "../Storage";
import substitute, { SubstituteParams } from "../helpers/substitude";
export interface LocaleProviderContextValue {
registerCategory: (categoryName: string, translations: { [ k: string ]: TranslationsObject }) => void;
translate: (category: string, value: string, params?: SubstituteParams) => string;
setLocale: (nextLocale: string) => void;
availableLocales: Array<string>;
currentLocale: string;
baseLocale: string;
}
export const LocaleProviderContextDefaultValue: LocaleProviderContextValue = {
registerCategory: () => undefined,
translate: () => undefined,
setLocale: () => undefined,
availableLocales: [],
currentLocale: "",
baseLocale: "",
};
export const LocaleProviderContext = React.createContext<LocaleProviderContextValue>(LocaleProviderContextDefaultValue);
export interface LocaleProviderProps {
onMissingTranslation?: (params: { currentLocale: string; category: string, value: string }) => string;
onSameTranslation?: (params: { currentLocale: string; category: string, value: string }) => string;
onLocaleChanged?: (currentLocale: string) => void;
commonTranslations?: { [ k: string ]: TranslationsObject };
availableLocales: Array<string>;
defaultLocale?: string;
baseLocale: string;
storage?: Storage;
}
export const LocaleProvider: React.FC<LocaleProviderProps> = (props) => {
const [ currentLocale, setLocale ] = React.useState(props.defaultLocale || props.baseLocale);
const storage = React.useMemo(() => props.storage || new Storage(), [ props.storage ]);
const registerCategory: LocaleProviderContextValue["registerCategory"] = React.useCallback(
(categoryName, translations) => {
Object.keys(translations).forEach(
(locale) => storage.appendOrWrite(locale, { [ categoryName ]: translations[ locale ] })
)
},
[]
);
const translate: LocaleProviderContextValue["translate"] = React.useCallback((category, value, params) => {
if (currentLocale === props.baseLocale) {
return params ? substitute(value, params) : value;
}
let translation: string;
try {
translation = storage.readRecord(currentLocale, category, value);
} catch (error) {
return props.onMissingTranslation
? props.onMissingTranslation({ value, category, currentLocale })
: error.message;
}
if (props.onSameTranslation && translation === value) {
return props.onSameTranslation({ value, category, currentLocale });
}
return params ? substitute(translation, params) : translation;
}, [
props.baseLocale, storage.readRecord, props.onMissingTranslation, props.onSameTranslation, currentLocale
]);
React.useEffect(() => {
Object.keys(props.commonTranslations)
.filter((locale) => !storage.hasRecord(currentLocale, props.commonTranslations[ locale ]))
.forEach((locale) => storage.writeNewRecord(currentLocale, props.commonTranslations[ locale ]));
}, [ props.commonTranslations ]);
React.useEffect(() => {
storage.currentLocale = currentLocale;
props.onLocaleChanged && props.onLocaleChanged(currentLocale);
}, [ currentLocale ]);
const context: LocaleProviderContextValue = {
registerCategory,
setLocale,
currentLocale,
translate,
availableLocales: props.availableLocales,
baseLocale: props.baseLocale,
};
return <LocaleProviderContext.Provider value={context}>{props.children}</LocaleProviderContext.Provider>;
};
export interface LocaleProviderComponentProps {
localeProvider: LocaleProviderContextValue;
}
export function withLocaleProvider<P extends LocaleProviderComponentProps>(Component: React.ComponentType<P>)
: React.FC<Omit<P, keyof LocaleProviderComponentProps>> {
return (props: P) => (
<LocaleProviderContext.Consumer>
{(context) => <Component localeProvider={context} {...props} />}
</LocaleProviderContext.Consumer>
);
}
LocaleProvider.displayName = "LocaleProvider";