working translation

This commit is contained in:
2024-04-10 20:20:40 +03:00
parent 5c8d6d9d42
commit 8b8124d58d
9 changed files with 137 additions and 31 deletions

View File

@ -10,6 +10,7 @@ import {
} from "../slice/AuthApi"; } from "../slice/AuthApi";
import { MessageContext } from "../App"; import { MessageContext } from "../App";
import { store, updateToken, updateUser } from "../config/store"; import { store, updateToken, updateUser } from "../config/store";
import tr from "../config/translation";
const AuthModal = (props: { const AuthModal = (props: {
open: boolean; open: boolean;
@ -43,7 +44,7 @@ const AuthModal = (props: {
}) })
.then(() => refetch()) .then(() => refetch())
.then(() => props.setOpen(false)) .then(() => props.setOpen(false))
.catch(() => messageApi.error("Login failed!")); .catch(() => messageApi.error(tr("Login failed!")));
}; };
const submitRegisterForm = (formData: { const submitRegisterForm = (formData: {
@ -55,17 +56,17 @@ const AuthModal = (props: {
registerUser(formData) registerUser(formData)
.unwrap() .unwrap()
.then(() => props.setOpen(false)) .then(() => props.setOpen(false))
.catch(() => messageApi.error("Registration failed!")); .catch(() => messageApi.error(tr("Registration failed!")));
}; };
const items: MenuProps["items"] = [ const items: MenuProps["items"] = [
{ {
label: "Log In", label: tr("Log in"),
key: "login", key: "login",
icon: <KeyOutlined />, icon: <KeyOutlined />,
}, },
{ {
label: "Register", label: tr("Register"),
key: "register", key: "register",
icon: <UserAddOutlined />, icon: <UserAddOutlined />,
}, },
@ -79,7 +80,7 @@ const AuthModal = (props: {
current === "register" && registerForm.submit(); current === "register" && registerForm.submit();
current === "login" && loginForm.submit(); current === "login" && loginForm.submit();
}} }}
okText={current === "login" ? "Log In" : "Register"} okText={current === "login" ? tr("Log in") : tr("Register")}
confirmLoading={isLoggingIn} confirmLoading={isLoggingIn}
> >
<Spin spinning={isLoggingIn || isRegistering}> <Spin spinning={isLoggingIn || isRegistering}>
@ -99,26 +100,26 @@ const AuthModal = (props: {
> >
<Form.Item <Form.Item
name="username" name="username"
label="Username" label={tr("Username")}
rules={[ rules={[
{ {
required: true, required: true,
message: "Please input your Username!", message: tr("Please input your Username!"),
}, },
]} ]}
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item name="name" label="Display name"> <Form.Item name="name" label={tr("Display name")}>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="password" name="password"
label="Password" label={tr("Password")}
rules={[ rules={[
{ {
required: true, required: true,
message: "Please input your password!", message: tr("Please input your password!"),
}, },
]} ]}
> >
@ -126,11 +127,11 @@ const AuthModal = (props: {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="password2" name="password2"
label="Repeat password" label={tr("Repeat password")}
rules={[ rules={[
{ {
required: true, required: true,
message: "Please confirm your password!", message: tr("Please confirm your Password!"),
}, },
({ getFieldValue }) => ({ ({ getFieldValue }) => ({
validator(_, value) { validator(_, value) {
@ -139,7 +140,7 @@ const AuthModal = (props: {
} }
return Promise.reject( return Promise.reject(
new Error( new Error(
"The new password that you entered do not match!" tr("The new password that you entered do not match!")
) )
); );
}, },
@ -158,11 +159,11 @@ const AuthModal = (props: {
> >
<Form.Item <Form.Item
name="username" name="username"
label="Username" label={tr("Username")}
rules={[ rules={[
{ {
required: true, required: true,
message: "Please input your Username!", message: tr("Please input your Username!"),
}, },
]} ]}
> >
@ -170,11 +171,11 @@ const AuthModal = (props: {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="password" name="password"
label="Password" label={tr("Password")}
rules={[ rules={[
{ {
required: true, required: true,
message: "Please input your Password!", message: tr("Please input your Password!"),
}, },
]} ]}
> >

View File

@ -5,6 +5,7 @@ import AuthModal from "./AuthModal";
import "./styles.css"; import "./styles.css";
import { StorePrototype, logOut, store } from "../config/store"; import { StorePrototype, logOut, store } from "../config/store";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import tr from "../config/translation";
const { Header } = Layout; const { Header } = Layout;
@ -18,9 +19,9 @@ const HeaderComponent = () => {
(state: StorePrototype) => state.auth.user (state: StorePrototype) => state.auth.user
); );
const userMenuItems: MenuProps["userMenuItems"] = [ const userMenuItems: MenuProps["items"] = [
{ {
label: "Log out", label: tr("Log out"),
key: "logout", key: "logout",
icon: <LogoutOutlined />, icon: <LogoutOutlined />,
onClick: () => store.dispatch(logOut()), onClick: () => store.dispatch(logOut()),
@ -39,7 +40,7 @@ const HeaderComponent = () => {
{user.username} {user.username}
</Popover> </Popover>
) : ( ) : (
"Log In" tr("Log in")
), ),
key: "login", key: "login",
icon: <UserOutlined />, icon: <UserOutlined />,

View File

@ -17,9 +17,18 @@ const initialAuthDataState: AuthDataType = {
user: null, user: null,
}; };
export type SettingsType = {
language: string | null;
};
const initialSettingsState: SettingsType = {
language: null,
};
export type StorePrototype = { export type StorePrototype = {
AuthApi: ReducerType; AuthApi: ReducerType;
auth: AuthDataType; auth: AuthDataType;
settings: SettingsType;
}; };
export const updateToken = createAction<string>("auth/updateToken"); export const updateToken = createAction<string>("auth/updateToken");
@ -27,6 +36,9 @@ export const getLocalToken = createAction("auth/getLocalToken");
export const updateUser = createAction<User>("auth/updateUser"); export const updateUser = createAction<User>("auth/updateUser");
export const logOut = createAction("auth/logOut"); export const logOut = createAction("auth/logOut");
export const setLanguage = createAction<string>("settings/setLanguage");
export const loadLanguage = createAction("settings/loadLanguage");
export const store = configureStore({ export const store = configureStore({
reducer: { reducer: {
// Add the generated reducer as a specific top-level slice // Add the generated reducer as a specific top-level slice
@ -51,6 +63,20 @@ export const store = configureStore({
state.user = null; state.user = null;
}); });
}), }),
settings: createReducer(initialSettingsState, (builder) => {
builder.addCase(setLanguage, (state, action) => {
state.language = action.payload || "en";
localStorage.setItem("language", action.payload || "en");
});
builder.addCase(loadLanguage, (state) => {
const language: string | null = localStorage.getItem("language");
if (language) {
state.language = language;
} else {
state.language = "en";
}
});
}),
}, },
// Adding the api middleware enables caching, invalidation, polling, // Adding the api middleware enables caching, invalidation, polling,
// and other useful features of `rtk-query`. // and other useful features of `rtk-query`.

View File

@ -0,0 +1,21 @@
import trMapJson from "./translationMap.json";
import { store } from "./store";
const trMap: unknown = trMapJson;
const tr = (phrase: string): string => {
const currentLanguage = store.getState().settings.language;
if (!currentLanguage || currentLanguage === "en") {
return phrase;
}
return (
((trMap &&
typeof trMap === "object" &&
trMap[phrase as keyof object] &&
trMap[phrase as keyof object][
currentLanguage as keyof object
]) as string) || phrase
);
};
export default tr;

View File

@ -0,0 +1,53 @@
{
"Queuing has never been so simple": {
"ru": "Организация очередей еще никогда не была настолько простой"
},
"Log in": {
"ru": "Войти"
},
"Log out": {
"ru": "Выйти"
},
"Register": {
"ru": "Зарегистрироваться"
},
"Username": {
"ru": "Логин"
},
"Password": {
"ru": "Пароль"
},
"Display name": {
"ru": "Отображаемое имя"
},
"Repeat password": {
"ru": "Повторите пароль"
},
"Cancel": {
"ru": "Отмена"
},
"Join a queue": {
"ru": "Присоединиться к очереди"
},
"Take a tour": {
"ru": "Взглянуть на функционал"
},
"Please input your Username!": {
"ru": "Пожалуйста, введите ваш Логин!"
},
"Please input your Password!": {
"ru": "Пожалуйста, введите ваш Пароль!"
},
"Please confirm your password!": {
"ru": "Пожалуйста, подтвердите ваш пароль!"
},
"The new password that you entered do not match!": {
"ru": "Пароли не совпадают!"
},
"Registration failed!": {
"ru": "Регистрация не удалась!"
},
"Login failed!": {
"ru": "Вход не удался!"
}
}

View File

@ -1,10 +1,10 @@
import React from 'react'; import React from "react";
import ReactDOM from 'react-dom/client'; import ReactDOM from "react-dom/client";
import App from './App'; import App from "./App";
const root = ReactDOM.createRoot(document.getElementById('root')!); const root = ReactDOM.createRoot(document.getElementById("root")!);
root.render( root.render(
<React.StrictMode> <React.StrictMode>
<App /> <App />
</React.StrictMode>, </React.StrictMode>
); );

View File

@ -1,11 +1,11 @@
import React, { useEffect } from "react"; import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom"; import { BrowserRouter, Route, Routes } from "react-router-dom";
import MainPage from "./MainPage"; import MainPage from "./MainPage";
import { useGetUserQuery } from "../slice/AuthApi"; import { getLocalToken, loadLanguage, store } from "../config/store";
import { getLocalToken, store, updateUser } from "../config/store";
const AppRoutes = () => { const AppRoutes = () => {
store.dispatch(getLocalToken()); store.dispatch(getLocalToken());
store.dispatch(loadLanguage());
return ( return (
<BrowserRouter> <BrowserRouter>

View File

@ -2,15 +2,18 @@ import React from "react";
import "./styles.css"; import "./styles.css";
import logoSquare from "../../static/logo-square.png"; import logoSquare from "../../static/logo-square.png";
import { Button } from "antd"; import { Button } from "antd";
import tr from "../config/translation";
const MainPage = () => { const MainPage = () => {
return ( return (
<div className="card main"> <div className="card main">
<img src={logoSquare} alt="logo" className="image" /> <img src={logoSquare} alt="logo" className="image" />
<p style={{ fontSize: "2rem" }}>Queuing has never been so simple</p> <p style={{ fontSize: "2rem" }}>
{tr("Queuing has never been so simple")}
</p>
<div className="button-box"> <div className="button-box">
<Button>Join a queue</Button> <Button>{tr("Join a queue")}</Button>
<Button>Take a tour</Button> <Button>{tr("Take a tour")}</Button>
</div> </div>
</div> </div>
); );

View File

@ -5,6 +5,7 @@
} }
.main { .main {
font-family: "Comfortaa", sans-serif;
display: flex; display: flex;
flex-flow: column; flex-flow: column;
justify-items: center; justify-items: center;