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