From 7ee2c135e1c76761cc332a8f63616f6c95c8a3b0 Mon Sep 17 00:00:00 2001 From: hyrious Date: Fri, 18 Jun 2021 09:53:50 +0800 Subject: [PATCH] feat(i18n): add translations of titles (#750) --- .../src/AppRoutes/AppRouteContainer.tsx | 2 +- desktop/renderer-app/src/AppRoutes/index.tsx | 67 ++++++++--------- desktop/renderer-app/src/route-config.ts | 44 +++++------ desktop/renderer-app/src/tasks/Init-ui.tsx | 73 ++++++++++++++----- packages/flat-i18n/locales/en.json | 24 +++++- packages/flat-i18n/locales/zh-CN.json | 24 +++++- .../src/AppRoutes/AppRouteContainer.tsx | 6 +- web/flat-web/src/AppRoutes/route-pages.ts | 4 +- 8 files changed, 159 insertions(+), 85 deletions(-) diff --git a/desktop/renderer-app/src/AppRoutes/AppRouteContainer.tsx b/desktop/renderer-app/src/AppRoutes/AppRouteContainer.tsx index a1f37c12251..3d38e1b49bb 100644 --- a/desktop/renderer-app/src/AppRoutes/AppRouteContainer.tsx +++ b/desktop/renderer-app/src/AppRoutes/AppRouteContainer.tsx @@ -1,6 +1,6 @@ +import { ErrorPage } from "flat-components"; import React from "react"; import { RouteComponentProps } from "react-router-dom"; -import { ErrorPage } from "flat-components"; import { ipcAsyncByMainWindow } from "../utils/ipc"; export interface AppRouteContainerProps { diff --git a/desktop/renderer-app/src/AppRoutes/index.tsx b/desktop/renderer-app/src/AppRoutes/index.tsx index a311465176c..9f2d22e220e 100644 --- a/desktop/renderer-app/src/AppRoutes/index.tsx +++ b/desktop/renderer-app/src/AppRoutes/index.tsx @@ -1,44 +1,35 @@ -import React from "react"; +import React, { FC } from "react"; +import { useTranslation } from "react-i18next"; import { HashRouter, Route, Switch } from "react-router-dom"; -import { message } from "antd"; import { LastLocationProvider } from "react-router-last-location"; import { RouteConfig, routeConfig } from "../route-config"; import { AppRouteContainer } from "./AppRouteContainer"; -import { I18nextProvider } from "react-i18next"; -import { i18n } from "../utils/i18n"; -export class AppRoutes extends React.Component { - public componentDidCatch(error: any): void { - void message.error(`网页加载发生错误:${error}`); - } - - public render(): React.ReactElement { - return ( - - - - - {Object.keys(routeConfig).map(((name: keyof RouteConfig) => { - const { path, component, title } = routeConfig[name]; - return ( - ( - - )} +export const AppRoutes: FC = () => { + const { t } = useTranslation(); + return ( + + + + {Object.keys(routeConfig).map(((name: keyof RouteConfig) => { + const { path, component, title } = routeConfig[name]; + return ( + ( + - ); - }) as (name: string) => React.ReactElement)} - - - - - ); - } -} + )} + /> + ); + }) as (name: string) => React.ReactElement)} + + + + ); +}; diff --git a/desktop/renderer-app/src/route-config.ts b/desktop/renderer-app/src/route-config.ts index 5514fab9d58..0210a4337d2 100644 --- a/desktop/renderer-app/src/route-config.ts +++ b/desktop/renderer-app/src/route-config.ts @@ -50,112 +50,112 @@ export enum RouteNameType { export const routeConfig = { [RouteNameType.SplashPage]: { - title: "Flat", + title: "SplashPage", path: "/", component: SplashPage, }, [RouteNameType.LoginPage]: { - title: "Flat", + title: "LoginPage", path: "/login/", component: LoginPage, }, [RouteNameType.HomePage]: { - title: "Flat", + title: "HomePage", path: "/user/", component: HomePage, }, [RouteNameType.SmallClassPage]: { - title: "小班课", + title: "SmallClassPage", path: "/classroom/SmallClass/:roomUUID/:ownerUUID/", component: SmallClassPage, }, [RouteNameType.OneToOnePage]: { - title: "一对一", + title: "OneToOnePage", path: "/classroom/OneToOne/:roomUUID/:ownerUUID/", component: OneToOnePage, }, [RouteNameType.BigClassPage]: { - title: "大班课", + title: "BigClassPage", path: "/classroom/BigClass/:roomUUID/:ownerUUID/", component: BigClassPage, }, [RouteNameType.RoomDetailPage]: { - title: "房间详情", + title: "RoomDetailPage", path: "/user/room/:roomUUID/:periodicUUID?/", component: RoomDetailPage, }, [RouteNameType.UserScheduledPage]: { - title: "预定房间", + title: "UserScheduledPage", path: "/user/scheduled/", component: UserScheduledPage, }, [RouteNameType.ScheduleRoomDetailPage]: { - title: "周期性房间详情", + title: "ScheduleRoomDetailPage", path: "/user/scheduled/info/:periodicUUID", component: ScheduleRoomDetailPage, }, [RouteNameType.PeriodicRoomDetailPage]: { - title: "周期性房间详情", + title: "PeriodicRoomDetailPage", path: "/user/periodic/info/:periodicUUID", component: PeriodicRoomDetailPage, }, [RouteNameType.ReplayPage]: { - title: "房间回放", + title: "ReplayPage", path: "/replay/:roomType/:roomUUID/:ownerUUID/", component: ReplayPage, }, [RouteNameType.ModifyOrdinaryRoomPage]: { - title: "修改房间", + title: "ModifyOrdinaryRoomPage", path: "/modify/:roomUUID/:periodicUUID?/", component: ModifyOrdinaryRoomPage, }, [RouteNameType.ModifyPeriodicRoomPage]: { - title: "修改周期性房间", + title: "ModifyPeriodicRoomPage", path: "/modify/periodic/room/:periodicUUID/", component: ModifyPeriodicRoomPage, }, [RouteNameType.CloudStoragePage]: { - title: "Flat", + title: "CloudStoragePage", path: "/pan/", component: CloudStoragePage, }, [RouteNameType.SystemCheckPage]: { - title: "系统检测", + title: "SystemCheckPage", path: "/device/system/", component: SystemCheckPage, }, [RouteNameType.CameraCheckPage]: { - title: "摄像头检测", + title: "CameraCheckPage", path: "/device/camera/", component: CameraCheckPage, }, [RouteNameType.SpeakerCheckPage]: { - title: "扬声器检测", + title: "SpeakerCheckPage", path: "/device/speaker/", component: SpeakerCheckPage, }, [RouteNameType.MicrophoneCheckPage]: { - title: "麦克风检测", + title: "MicrophoneCheckPage", path: "/device/microphone/", component: MicrophoneCheckPage, }, [RouteNameType.GeneralSettingPage]: { - title: "常规设置", + title: "GeneralSettingPage", path: "/general-settings/", component: GeneralSettingPage, }, [RouteNameType.HotKeySettingPage]: { - title: "热键设置", + title: "HotKeySettingPage", path: "/hotkey/", component: HotKeySettingPage, }, [RouteNameType.FeedbackPage]: { - title: "吐个槽", + title: "FeedbackPage", path: "/feedback/", component: FeedbackPage, }, [RouteNameType.AboutPage]: { - title: "关于我们", + title: "AboutPage", path: "/about/", component: AboutPage, }, diff --git a/desktop/renderer-app/src/tasks/Init-ui.tsx b/desktop/renderer-app/src/tasks/Init-ui.tsx index 256e9873727..c4d4af23add 100644 --- a/desktop/renderer-app/src/tasks/Init-ui.tsx +++ b/desktop/renderer-app/src/tasks/Init-ui.tsx @@ -1,33 +1,70 @@ -import React from "react"; -import ReactDOM from "react-dom"; +import "flat-components/build/style.css"; +import "../theme.less"; + import { ConfigProvider } from "antd"; +import enUS from "antd/lib/locale/en_US"; import zhCN from "antd/lib/locale/zh_CN"; + +import React, { useEffect, useMemo } from "react"; +import ReactDOM from "react-dom"; + +import { I18nextProvider } from "react-i18next"; +import { useUpdate } from "react-use"; + +import { i18n } from "../utils/i18n"; import { AppRoutes } from "../AppRoutes"; import { StoreProvider } from "../components/StoreProvider"; -import "../theme.less"; -import "flat-components/build/style.css"; - /** configure right after import */ import { configure } from "mobx"; configure({ isolateGlobalState: true, }); -const initUI = (): void => { - ReactDOM.render( - trigger?.parentElement || document.body} - > - - - - , - document.getElementById("root") as HTMLElement, +const App: React.FC = () => { + const forceUpdate = useUpdate(); + + const antdLocale = useMemo( + () => (i18n.language.startsWith("zh") ? zhCN : enUS), + // eslint-disable-next-line react-hooks/exhaustive-deps + [i18n.language], ); + + useEffect(() => { + const onLangChanged = (): void => { + forceUpdate(); + }; + + i18n.on("languageChanged", onLangChanged); + + return () => { + i18n.off("languageChanged", onLangChanged); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( + + + + + + + + ); +}; + +function getPopupContainer(trigger?: HTMLElement): HTMLElement { + return trigger?.parentElement || document.body; +} + +const initUI = (): void => { + ReactDOM.render(, document.getElementById("root")); }; export default initUI; diff --git a/packages/flat-i18n/locales/en.json b/packages/flat-i18n/locales/en.json index 38a3a765376..482d455bd3d 100644 --- a/packages/flat-i18n/locales/en.json +++ b/packages/flat-i18n/locales/en.json @@ -167,5 +167,27 @@ "new-version-tips": "Found a new version: {{version}}, please update to the latest version for a better product experience", "update-failed-tips": "Update failed, please reopen the program", "update-now": "Update immediately", - "version-updates": "New version update" + "version-updates": "New version update", + "title-SplashPage": "Flat", + "title-LoginPage": "Flat", + "title-HomePage": "Flat", + "title-SmallClassPage": "Small Class", + "title-OneToOnePage": "One To One", + "title-BigClassPage": "Big Class", + "title-RoomDetailPage": "Room Detail", + "title-UserScheduledPage": "Schedule Room", + "title-ScheduleRoomDetailPage": "Room Detail", + "title-PeriodicRoomDetailPage": "Room Detail", + "title-ReplayPage": "Replay", + "title-ModifyOrdinaryRoomPage": "Edit Room", + "title-ModifyPeriodicRoomPage": "Edit Periodic Room", + "title-CloudStoragePage": "Flat", + "title-SystemCheckPage": "System Check", + "title-CameraCheckPage": "Camera Check", + "title-SpeakerCheckPage": "Speaker Check", + "title-MicrophoneCheckPage": "Microphone Check", + "title-GeneralSettingPage": "General Settings", + "title-HotKeySettingPage": "HotKey Settings", + "title-FeedbackPage": "Feedback", + "title-AboutPage": "About" } diff --git a/packages/flat-i18n/locales/zh-CN.json b/packages/flat-i18n/locales/zh-CN.json index 4ca31577493..c826bb92a11 100644 --- a/packages/flat-i18n/locales/zh-CN.json +++ b/packages/flat-i18n/locales/zh-CN.json @@ -167,5 +167,27 @@ "downloading": "下载中", "update-failed-tips": "更新失败,请重新打开程序", "update-now": "立即更新", - "new-version-tips": "发现新版本:{{version}}, 请更新到最新版本获取更好的产品体验" + "new-version-tips": "发现新版本:{{version}}, 请更新到最新版本获取更好的产品体验", + "title-SplashPage": "Flat", + "title-LoginPage": "Flat", + "title-HomePage": "Flat", + "title-SmallClassPage": "小班课", + "title-OneToOnePage": "一对一", + "title-BigClassPage": "大班课", + "title-RoomDetailPage": "房间详情", + "title-UserScheduledPage": "预定房间", + "title-ScheduleRoomDetailPage": "周期性房间详情", + "title-PeriodicRoomDetailPage": "周期性房间详情", + "title-ReplayPage": "房间回放", + "title-ModifyOrdinaryRoomPage": "修改房间", + "title-ModifyPeriodicRoomPage": "修改周期性房间", + "title-CloudStoragePage": "Flat", + "title-SystemCheckPage": "系统检测", + "title-CameraCheckPage": "摄像头检测", + "title-SpeakerCheckPage": "扬声器检测", + "title-MicrophoneCheckPage": "麦克风检测", + "title-GeneralSettingPage": "常规设置", + "title-HotKeySettingPage": "热键设置", + "title-FeedbackPage": "吐个槽", + "title-AboutPage": "关于我们" } diff --git a/web/flat-web/src/AppRoutes/AppRouteContainer.tsx b/web/flat-web/src/AppRoutes/AppRouteContainer.tsx index cdee3a60e60..6d35b5867d8 100644 --- a/web/flat-web/src/AppRoutes/AppRouteContainer.tsx +++ b/web/flat-web/src/AppRoutes/AppRouteContainer.tsx @@ -1,5 +1,6 @@ import loadable from "@loadable/component"; import React, { ComponentType, FC, useContext, useEffect } from "react"; +import { useTranslation } from "react-i18next"; import { RouteComponentProps } from "react-router-dom"; import { useIsomorphicLayoutEffect } from "react-use"; import { PageStoreContext } from "../components/StoreProvider"; @@ -20,17 +21,18 @@ export const AppRouteContainer: FC = ({ routeProps, }) => { const pageStore = useContext(PageStoreContext); + const { t } = useTranslation(); useIsomorphicLayoutEffect(() => { pageStore.setName(name); }, [name, pageStore]); useEffect(() => { - document.title = title; + document.title = t("title-" + title); // clear selection window.getSelection()?.removeAllRanges(); - }, [title]); + }, [t, title]); return ; }; diff --git a/web/flat-web/src/AppRoutes/route-pages.ts b/web/flat-web/src/AppRoutes/route-pages.ts index de321c256e8..456091e72d7 100644 --- a/web/flat-web/src/AppRoutes/route-pages.ts +++ b/web/flat-web/src/AppRoutes/route-pages.ts @@ -17,11 +17,11 @@ export type RoutePages = { */ export const routePages: RoutePages = { [RouteNameType.LoginPage]: { - title: "Flat Login", + title: "LoginPage", component: () => import("../pages/LoginPage"), }, [RouteNameType.HomePage]: { - title: "Flat", + title: "HomePage", hasHeader: true, component: () => import("../pages/HomePage"), },