Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/frontend/app/localize.tsx
Views: 687
/*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, useRef, useState } from "react";10import { IntlProvider } from "react-intl";11import useAsyncEffect from "use-async-effect";1213type OnErrorFn = (typeof IntlProvider.defaultProps)["onError"];1415import { Loading, Paragraph, Text } 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";2627interface LanguageContextInterface {28setLocale: (language: string) => void;29locale: Locale;30}3132export const LocalizationContext = createContext<LanguageContextInterface>({33locale: DEFAULT_LOCALE,34setLocale: () => {35console.warn("LanguageContext.changeLanguage not implemented");36},37});3839// This configures AntD (locale+style) and react-intl40export function Localize({ children }: { children: React.ReactNode }) {41const [locale, setLocale] = useState<Locale>(DEFAULT_LOCALE);42const [antdLoc, setAntdLoc] = useState<AntdLocale | undefined>(undefined);43const [messages, setMessages] = useState<Messages | undefined>(undefined);44const { antdTheme } = useAntdStyleProvider();45const uniqueKey = useRef<{ [tag: string]: number }>({});4647// Note: this is e.g. necessary to render text in a modal, where some caching happens, apparently48function getKey(tag: string): number {49const n = (uniqueKey.current[tag] ?? 0) + 1;50uniqueKey.current[tag] = n;51return n;52}5354useAsyncEffect(async () => {55setMessages(await loadLocaleMessages(locale));56}, [locale]);5758useAsyncEffect(async () => {59setAntdLoc(await loadAntdLocale(locale));60}, [locale]);6162function renderApp() {63// NOTE: the locale will be set from the other_settings, on the "page".64// So, for the default (english) we always have to render it, and then, maybe, a locale is set...65if (locale === DEFAULT_LOCALE) {66return children;67} else {68if (isEmpty(messages)) {69return (70<Loading71theme="medium"72delay={1000}73text={`Loading support for ${LOCALIZATIONS[locale].name}…`}74/>75);76} else {77return children;78}79}80}8182function onError(err: Parameters<OnErrorFn>[0]): ReturnType<OnErrorFn> {83if (process.env.NODE_ENV !== "production") {84console.log(err.message);85}86}8788return (89<LocalizationContext.Provider90value={{91setLocale: (locale: unknown) => setLocale(sanitizeLocale(locale)),92locale,93}}94>95<AntdConfigProvider theme={antdTheme} locale={antdLoc}>96<IntlProvider97locale={locale}98messages={messages}99defaultLocale={DEFAULT_LOCALE}100onError={onError}101defaultRichTextElements={{102strong: (ch) => (103<Text strong key={getKey("strong")}>104{ch}105</Text>106),107b: (ch) => (108<Text strong key={getKey("b")}>109{ch}110</Text>111),112i: (ch) => (113<Text italic key={getKey("i")}>114{ch}115</Text>116),117p: (ch) => <Paragraph key={getKey("p")}>{ch}</Paragraph>,118code: (ch) => (119<Text code key={getKey("code")}>120{ch}121</Text>122),123ul: (e) => <ul key={getKey("ul")}>{e}</ul>,124ol: (e) => <ol key={getKey("ol")}>{e}</ol>,125li: (e) => <li key={getKey("li")}>{e}</li>,126}}127>128{renderApp()}129</IntlProvider>130</AntdConfigProvider>131</LocalizationContext.Provider>132);133}134135export function useLocalizationCtx() {136return useContext(LocalizationContext);137}138139function loadAntdLocale(locale: Locale): Promise<AntdLocale> {140return (() => {141switch (locale) {142case "en":143// English is "baked in", because it is the default. Other languages are splitted up...144return enUS;145case "de":146// DEV: all those imports needs to be explicit full strings, and point to the pkg to resolve147return import("antd/locale/de_DE");148case "zh":149return import("antd/locale/zh_CN");150case "es":151return import("antd/locale/es_ES");152case "nl":153return import("antd/locale/nl_NL");154case "ru":155return import("antd/locale/ru_RU");156case "fr":157return import("antd/locale/fr_FR");158case "it":159return import("antd/locale/it_IT");160case "ja":161return import("antd/locale/ja_JP");162case "pt":163return import("antd/locale/pt_PT");164case "ko":165return import("antd/locale/ko_KR");166case "pl":167return import("antd/locale/pl_PL");168case "tr":169return import("antd/locale/tr_TR");170case "he":171return import("antd/locale/he_IL");172case "hi":173return import("antd/locale/hi_IN");174case "hu":175return import("antd/locale/hu_HU");176case "ar":177return import("antd/locale/ar_EG");178default:179unreachable(locale);180throw new Error(`Unknown locale '${locale}.`);181}182})() as any as Promise<AntdLocale>;183}184185186