Commit 3fc11377 authored by BBeretta's avatar BBeretta

Merge branch 'optimize/language-translation' into 'feat/language-translation'

optimize some implementation, including paths of components and so on

See merge request !373
parents e811fd11 b59fdac1
Pipeline #27208 passed with stages
in 6 minutes and 55 seconds
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
const LanguageSelector: React.FC = () => {
const { i18n } = useTranslation();
const onClickLanguageChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
const language = e.target.value;
i18n.changeLanguage(language);
};
useEffect(() => {
//Adding language state as a dependency to force re-render when the language changes
}, [i18n.language]);
return (
<select value={i18n.language} onChange={onClickLanguageChange}>
<option value="cn">Chinese</option>
<option value="en">English</option>
<option value="fr">French</option>
<option value="jp">Japanese</option>
<option value="br">Brazilian</option>
<option value="pt">Portuguese</option>
<option value="es">Spanish</option>
</select>
);
};
export default LanguageSelector;
......@@ -18,7 +18,6 @@ import "u-reset.css";
import "overlayscrollbars/overlayscrollbars.css";
import "@/styles/core.scss";
import "@/styles/inject.scss";
import "./i18n";
import { ProConfigProvider } from "@ant-design/pro-provider";
import { App, ConfigProvider } from "antd";
......@@ -28,7 +27,7 @@ import ReactDOM from "react-dom/client";
import { theme } from "@/ui/theme";
import { LanguageProvider } from "./Language/LanguageContext";
import { I18NProvider } from "./ui/I18N";
import { NeosRouter } from "./ui/NeosRouter";
const root = ReactDOM.createRoot(
......@@ -36,7 +35,7 @@ const root = ReactDOM.createRoot(
);
root.render(
<LanguageProvider>
<I18NProvider>
<ConfigProvider theme={theme} locale={zhCN}>
<App>
<ProConfigProvider dark>
......@@ -44,5 +43,5 @@ root.render(
</ProConfigProvider>
</App>
</ConfigProvider>
</LanguageProvider>,
</I18NProvider>,
);
......@@ -2,8 +2,8 @@
import { easings } from "@react-spring/web";
import { isMe } from "@/stores";
import { matConfig } from "@/ui/Shared";
import { matConfig } from "../../css";
import type { AttackFunc } from "./types";
import { asyncStart } from "./utils";
......
import { ygopro } from "@/api";
import { isMe } from "@/stores";
import { matConfig } from "@/ui/Shared";
import { matConfig } from "../../css";
import type { MoveFunc } from "./types";
import { asyncStart } from "./utils";
......
......@@ -2,8 +2,8 @@ import { easings } from "@react-spring/web";
import { ygopro } from "@/api";
import { isMe } from "@/stores";
import { matConfig } from "@/ui/Shared";
import { matConfig } from "../../css";
import type { MoveFunc } from "./types";
import { asyncStart } from "./utils";
......
import { ygopro } from "@/api";
import { cardStore, isMe } from "@/stores";
import { matConfig } from "@/ui/Shared";
import { matConfig } from "../../css";
import type { MoveFunc } from "./types";
import { asyncStart } from "./utils";
......
import { ygopro } from "@/api";
import { isMe } from "@/stores";
import { matConfig } from "@/ui/Shared";
import { matConfig } from "../../css";
import type { MoveFunc } from "./types";
import { asyncStart } from "./utils";
......
import React, { createContext, useContext, useState } from "react";
interface LanguageContextType {
interface I18NContextType {
language: string;
changeLanguage: (newLanguage: string) => void;
}
const LanguageContext = createContext<LanguageContextType | undefined>(
undefined,
);
const I18NContext = createContext<I18NContextType | undefined>(undefined);
export const LanguageProvider: React.FC<{ children: React.ReactNode }> = ({
export const I18NProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const [language, setLanguage] = useState<string>("cn"); // default language
......@@ -19,16 +17,16 @@ export const LanguageProvider: React.FC<{ children: React.ReactNode }> = ({
};
return (
<LanguageContext.Provider value={{ language, changeLanguage }}>
<I18NContext.Provider value={{ language, changeLanguage }}>
{children}
</LanguageContext.Provider>
</I18NContext.Provider>
);
};
export const useLanguage = (): LanguageContextType => {
const context = useContext(LanguageContext);
export const useI18N = (): I18NContextType => {
const context = useContext(I18NContext);
if (!context) {
throw new Error("useLanguage must be used within a LanguageProvider");
throw new Error("useI18N must be used within a I18NProvider");
}
return context;
};
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { Select } from "@/ui/Shared";
export const I18NSelector: React.FC = () => {
const { i18n } = useTranslation();
const onClickLanguageChange = (language: any) => {
i18n.changeLanguage(language);
};
useEffect(() => {
// Adding language state as a dependency to force re-render
// when the language changes
}, [i18n.language]);
return (
<Select
value={i18n.language}
onChange={onClickLanguageChange}
options={[
{ value: "cn", label: "简体中文" },
{ value: "en", label: "English" },
{ value: "fr", label: "Français" },
{ value: "jp", label: "日本語" },
{ value: "br", label: "português do Brasil" },
{ value: "pt", label: "português" },
{ value: "es", label: "Castellano" },
]}
/>
);
};
......@@ -4,10 +4,10 @@
"Match": "Match",
"DeckBuilding": "Montagem de baralho",
"PersonalCenter": "Centro pessoal",
"MengkaCommunity": "Comunidade Mengka",
"MyCardCommunity": "Comunidade Mengka",
"DuelDatabase": "Base de dados de duelos",
"LogOut": "Sair",
"LoginToMengka": "Entrar no Mengka",
"Login": "Entrar no Mengka",
"Fullscreen": "Tela cheia"
},
"Start": {
......
......@@ -4,10 +4,10 @@
"Match": "匹配",
"DeckBuilding": "组卡",
"PersonalCenter": "个人中心",
"MengkaCommunity": "萌卡社区",
"MyCardCommunity": "萌卡社区",
"DuelDatabase": "决斗数据库",
"LogOut": "退出登录",
"LoginToMengka": "登录萌卡",
"Login": "登录萌卡",
"Fullscreen": "全屏"
},
"Start": {
......
......@@ -4,10 +4,10 @@
"Match": "Match",
"DeckBuilding": "Deck Building",
"PersonalCenter": "Personal Center",
"MengkaCommunity": "Mengka Community",
"MyCardCommunity": "Mengka Community",
"DuelDatabase": "Duel Database",
"LogOut": "Log out",
"LoginToMengka": "Login to Mengka",
"Login": "Login to Mengka",
"Fullscreen": "Fullscreen"
},
"Start": {
......
......@@ -4,10 +4,10 @@
"Match": "Correspondance",
"DeckBuilding": "Construction de Deck",
"PersonalCenter": "Centre personnel",
"MengkaCommunity": "Communauté Mengka",
"MyCardCommunity": "Communauté Mengka",
"DuelDatabase": "Base de données de duels",
"LogOut": "Déconnexion",
"LoginToMengka": "Connexion à Mengka",
"Login": "Connexion à Mengka",
"Fullscreen": "Plein écran"
},
"Start": {
......
......@@ -4,10 +4,10 @@
"Match": "マッチ",
"DeckBuilding": "デッキ構築",
"PersonalCenter": "個人センター",
"MengkaCommunity": "萌卡コミュニティ",
"MyCardCommunity": "萌卡コミュニティ",
"DuelDatabase": "デュエルデータベース",
"LogOut": "ログアウト",
"LoginToMengka": "萌卡にログインする",
"Login": "萌卡にログインする",
"Fullscreen": "フルスクリーン"
},
"Start": {
......
......@@ -4,10 +4,10 @@
"Match": "Match",
"DeckBuilding": "Montagem de baralho",
"PersonalCenter": "Centro pessoal",
"MengkaCommunity": "Comunidade Mengka",
"MyCardCommunity": "Comunidade Mengka",
"DuelDatabase": "Base de dados de duelos",
"LogOut": "Terminar sessão",
"LoginToMengka": "Iniciar sessão no Mengka",
"Login": "Iniciar sessão no Mengka",
"Fullscreen": "Ecrã completo"
},
"Start": {
......
......@@ -4,10 +4,10 @@
"Match": "Emparejamiento",
"DeckBuilding": "Construcción de Mazo",
"PersonalCenter": "Centro personal",
"MengkaCommunity": "Comunidad Mengka",
"MyCardCommunity": "Comunidad Mengka",
"DuelDatabase": "Base de datos de duelos",
"LogOut": "Cerrar sesión",
"LoginToMengka": "Iniciar sesión en Mengka",
"Login": "Iniciar sesión en Mengka",
"Fullscreen": "Pantalla completa"
},
"Start": {
......
export * from "./I18NContext";
export * from "./I18NSelector";
import i18next from "i18next";
import { initReactI18next } from "react-i18next";
import translationBrazilian from "./Translation/Brazilian/translation.json";
import translationChinese from "./Translation/Chinese/translation.json";
//Import all translation files
import translationEnglish from "./Translation/English/translation.json";
import translationFrench from "./Translation/French/translation.json";
import translationJapanese from "./Translation/Japanese/translation.json";
import translationPortuguese from "./Translation/Portuguese/translation.json";
import translationSpanish from "./Translation/Spanish/translation.json";
//---Using translation
// const resources = {
// en: {
// translation: translationEnglish,
// },
// es: {
// translation: translationSpanish,
// },
// fr: {
// translation: translationFrench,
// },
// }
/* Import all translation files */
import translationBrazilian from "./Source/Brazilian/translation.json";
import translationChinese from "./Source/Chinese/translation.json";
import translationEnglish from "./Source/English/translation.json";
import translationFrench from "./Source/French/translation.json";
import translationJapanese from "./Source/Japanese/translation.json";
import translationPortuguese from "./Source/Portuguese/translation.json";
import translationSpanish from "./Source/Spanish/translation.json";
//---Using different namespaces
const resources = {
cn: {
Header: translationChinese.Header,
......
......@@ -17,9 +17,9 @@ import {
removeCookie,
} from "@/api";
import { useConfig } from "@/config";
import LanguageSelector from "@/Language/LanguageSelector";
import { accountStore } from "@/stores";
import { I18NSelector } from "../I18N";
import styles from "./index.module.scss";
import {
getLoginStatus,
......@@ -60,7 +60,7 @@ const HeaderBtn: React.FC<
};
export const Component = () => {
const { t } = useTranslation("Header");
const { t: i18n } = useTranslation("Header");
// 捕获SSO登录
const routerLocation = useLocation();
......@@ -99,15 +99,15 @@ export const Component = () => {
/>
</a>
<HeaderBtn to="/">{t("HomePage")}</HeaderBtn>
<HeaderBtn to="/">{i18n("HomePage")}</HeaderBtn>
<HeaderBtn to="/match" disabled={!logined}>
{t("Match")}
{i18n("Match")}
</HeaderBtn>
<HeaderBtn to="/build" disabled={!logined}>
{t("DeckBuilding")}
{i18n("DeckBuilding")}
</HeaderBtn>
<span style={{ flexGrow: 1 }} />
<LanguageSelector />
<I18NSelector />
<span className={styles.profile}>
<Dropdown
arrow
......@@ -116,14 +116,14 @@ export const Component = () => {
{
label: (
<a href={NeosConfig.profileUrl} target="_blank">
{t("PersonalCenter")}
{i18n("PersonalCenter")}
</a>
),
},
{
label: (
<a href="https://ygobbs.com" target="_blank">
{t("MengkaCommunity")}
{i18n("MyCardCommunity")}
</a>
),
},
......@@ -133,16 +133,16 @@ export const Component = () => {
href="https://mycard.moe/ygopro/arena/#/"
target="_blank"
>
{t("DuelDatabase")}
{i18n("DuelDatabase")}
</a>
),
},
{
label: logined ? t("LogOut") : t("LoginToMengka"),
label: logined ? i18n("LogOut") : i18n("Login"),
onClick: logined ? onLogout : onLogin,
},
{
label: t("Fullscreen"),
label: i18n("Fullscreen"),
onClick: () => document.documentElement.requestFullscreen(),
danger: true,
},
......
......@@ -19,7 +19,7 @@ import {
} from "@/api";
import { useConfig } from "@/config";
import { accountStore, deckStore, resetUniverse, roomStore } from "@/stores";
import { Background, IconFont, Select } from "@/ui/Shared";
import { Background, IconFont, ScrollableArea, Select } from "@/ui/Shared";
import {
CustomRoomContent,
......@@ -52,7 +52,7 @@ export const Component: React.FC = () => {
const [entertainMatchLoading, setEntertainMatchLoading] = useState(false); // 娱乐匹配的loading状态
const [watchLoading, setWatchLoading] = useState(false); // 观战模式的loading状态
const navigate = useNavigate();
const { t } = useTranslation("Match");
const { t: i18n } = useTranslation("Match");
// 匹配
const onMatch = async (arena: "athletic" | "entertain") => {
......@@ -224,7 +224,7 @@ export const Component: React.FC = () => {
<div className={styles.wrap}>
<Space size={16}>
<Select
title={t("Deck")}
title={i18n("Deck")}
showSearch
value={deckName}
style={{ width: 200 }}
......@@ -248,13 +248,13 @@ export const Component: React.FC = () => {
onClick={() => navigate("/build")}
size="large"
>
{t("DeckEdit")}
{i18n("DeckEdit")}
</Button>
</Space>
<div className={styles["mode-select"]}>
<Mode
title={t("MCCompetitiveMatchmakingTitle")}
desc={t("MCCompetitiveMatchmakingDesc")}
title={i18n("MCCompetitiveMatchmakingTitle")}
desc={i18n("MCCompetitiveMatchmakingDesc")}
icon={
athleticMatchLoading ? (
<LoadingOutlined />
......@@ -265,8 +265,8 @@ export const Component: React.FC = () => {
onClick={onCompetitiveMatch}
/>
<Mode
title={t("MCCasualMatchmakingTitle")}
desc={t("MCCasualMatchmakingDesc")}
title={i18n("MCCasualMatchmakingTitle")}
desc={i18n("MCCasualMatchmakingDesc")}
icon={
entertainMatchLoading ? (
<LoadingOutlined />
......@@ -277,20 +277,20 @@ export const Component: React.FC = () => {
onClick={onEntertainMatch}
/>
<Mode
title={t("MCCustomRoomTitle")}
desc={t("MCCustomRoomDesc")}
title={i18n("MCCustomRoomTitle")}
desc={i18n("MCCustomRoomDesc")}
icon={<BulbOutlined />}
onClick={onMCCustomRoom}
/>
<Mode
title={t("MCSpectatorListTitle")}
desc={t("MCSpectatorListDesc")}
title={i18n("MCSpectatorListTitle")}
desc={i18n("MCSpectatorListDesc")}
icon={watchLoading ? <LoadingOutlined /> : <PlayCircleFilled />}
onClick={onMCWatch}
/>
<Mode
title={t("SinglePlayerModeTitle")}
desc={t("SinglePlayerModeDesc")}
title={i18n("SinglePlayerModeTitle")}
desc={i18n("SinglePlayerModeDesc")}
icon={
singleLoading ? (
<LoadingOutlined />
......@@ -301,18 +301,18 @@ export const Component: React.FC = () => {
onClick={onAIMatch}
/>
<Mode
title={t("CustomRoomTitle")}
desc={t("CustomRoomDesc")}
title={i18n("CustomRoomTitle")}
desc={i18n("CustomRoomDesc")}
icon={<SettingFilled />}
onClick={onCustomRoom}
/>
<Mode
title={t("ReplayTitle")}
desc={t("ReplayDesc")}
title={i18n("ReplayTitle")}
desc={i18n("ReplayDesc")}
icon={<IconFont type="icon-record" size={24} />}
onClick={replayOpen}
/>
<Mode title={t("WIPTitle")} desc={t("WIPDesc")} icon={null} />
<Mode title={i18n("WIPTitle")} desc={i18n("WIPDesc")} icon={null} />
</div>
</div>
</div>
......@@ -330,8 +330,10 @@ const Mode: React.FC<{
onClick?: () => void;
}> = ({ title, desc, icon, onClick }) => (
<div className={styles.mode} onClick={onClick}>
<ScrollableArea maxHeight="15rem">
<div className={styles.icon}>{icon}</div>
<div className={styles.title}>{title}</div>
<div className={styles.desc}>{desc}</div>
</ScrollableArea>
</div>
);
......@@ -4,10 +4,10 @@ import classNames from "classnames";
import styles from "./index.module.scss";
export const Select: React.FC<
React.ComponentProps<typeof AntdSelect> & { title: string }
React.ComponentProps<typeof AntdSelect> & { title?: string }
> = ({ title, className, dropdownStyle, ...rest }) => (
<div className={styles["custom-select"]}>
<span className={styles.prefix}>{title}</span>
{title && <span className={styles.prefix}>{title}</span>}
<AntdSelect
className={classNames(styles.select, className)}
size="large"
......
......@@ -2,7 +2,6 @@ export * from "./Background";
export * from "./CardEffectText";
export * from "./Chain";
export * from "./chatHook";
export * from "./css";
export * from "./DeckCard";
export * from "./DeckZone";
export * from "./IconFont";
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment