Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/app/localize.tsx
1496 views
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { ConfigProvider as AntdConfigProvider } from "antd";
7
import type { Locale as AntdLocale } from "antd/lib/locale";
8
import enUS from "antd/locale/en_US";
9
import { isEmpty } from "lodash";
10
import { createContext, useContext, useState } from "react";
11
import { IntlProvider } from "react-intl";
12
import useAsyncEffect from "use-async-effect";
13
14
type OnErrorFn = (typeof IntlProvider.defaultProps)["onError"];
15
16
import { Loading } from "@cocalc/frontend/components";
17
import {
18
DEFAULT_LOCALE,
19
loadLocaleMessages,
20
Locale,
21
LOCALIZATIONS,
22
Messages,
23
sanitizeLocale,
24
} from "@cocalc/frontend/i18n";
25
import { unreachable } from "@cocalc/util/misc";
26
import { useAntdStyleProvider } from "./context";
27
import { LOCALIZE_DEFAULT_ELEMENTS } from "./localize-default-elements";
28
29
interface LanguageContextInterface {
30
setLocale: (language: string) => void;
31
locale: Locale;
32
}
33
34
export const LocalizationContext = createContext<LanguageContextInterface>({
35
locale: DEFAULT_LOCALE,
36
setLocale: () => {
37
console.warn("LanguageContext.changeLanguage not implemented");
38
},
39
});
40
41
// This configures AntD (locale+style) and react-intl
42
export function Localize({ children }: { children: React.ReactNode }) {
43
const [locale, setLocale] = useState<Locale>(DEFAULT_LOCALE);
44
const [antdLoc, setAntdLoc] = useState<AntdLocale | undefined>(undefined);
45
const [messages, setMessages] = useState<Messages | undefined>(undefined);
46
const { antdTheme } = useAntdStyleProvider();
47
48
useAsyncEffect(async () => {
49
setMessages(await loadLocaleMessages(locale));
50
}, [locale]);
51
52
useAsyncEffect(async () => {
53
setAntdLoc(await loadAntdLocale(locale));
54
}, [locale]);
55
56
function renderApp() {
57
// NOTE: the locale will be set from the other_settings, on the "page".
58
// So, for the default (english) we always have to render it, and then, maybe, a locale is set...
59
if (locale === DEFAULT_LOCALE) {
60
// we are explicitly returning as any since ts is suddenly complaining about a potential bigint
61
return children as any;
62
} else {
63
if (isEmpty(messages)) {
64
return (
65
<Loading
66
theme="medium"
67
delay={1000}
68
text={`Loading support for ${LOCALIZATIONS[locale].name}…`}
69
/>
70
);
71
} else {
72
return children as any;
73
}
74
}
75
}
76
77
function onError(err: Parameters<OnErrorFn>[0]): ReturnType<OnErrorFn> {
78
if (process.env.NODE_ENV !== "production") {
79
console.log(err.message);
80
}
81
}
82
83
return (
84
<LocalizationContext.Provider
85
value={{
86
setLocale: (locale: unknown) => setLocale(sanitizeLocale(locale)),
87
locale,
88
}}
89
>
90
<AntdConfigProvider theme={antdTheme} locale={antdLoc}>
91
<IntlProvider
92
locale={locale}
93
messages={messages}
94
defaultLocale={DEFAULT_LOCALE}
95
onError={onError}
96
defaultRichTextElements={LOCALIZE_DEFAULT_ELEMENTS}
97
>
98
{renderApp()}
99
</IntlProvider>
100
</AntdConfigProvider>
101
</LocalizationContext.Provider>
102
);
103
}
104
105
export function useLocalizationCtx() {
106
return useContext(LocalizationContext);
107
}
108
109
function loadAntdLocale(locale: Locale): Promise<AntdLocale> {
110
return (() => {
111
switch (locale) {
112
case "en":
113
// English is "baked in", because it is the default. Other languages are splitted up...
114
return enUS;
115
case "de":
116
// DEV: all those imports needs to be explicit full strings, and point to the pkg to resolve
117
return import("antd/locale/de_DE");
118
case "zh":
119
return import("antd/locale/zh_CN");
120
case "es":
121
return import("antd/locale/es_ES");
122
case "nl":
123
return import("antd/locale/nl_NL");
124
case "ru":
125
return import("antd/locale/ru_RU");
126
case "fr":
127
return import("antd/locale/fr_FR");
128
case "it":
129
return import("antd/locale/it_IT");
130
case "ja":
131
return import("antd/locale/ja_JP");
132
case "pt":
133
return import("antd/locale/pt_PT");
134
case "ko":
135
return import("antd/locale/ko_KR");
136
case "pl":
137
return import("antd/locale/pl_PL");
138
case "tr":
139
return import("antd/locale/tr_TR");
140
case "he":
141
return import("antd/locale/he_IL");
142
case "hi":
143
return import("antd/locale/hi_IN");
144
case "hu":
145
return import("antd/locale/hu_HU");
146
case "ar":
147
return import("antd/locale/ar_EG");
148
default:
149
unreachable(locale);
150
throw new Error(`Unknown locale '${locale}.`);
151
}
152
})() as any as Promise<AntdLocale>;
153
}
154
155