working translation
This commit is contained in:
@ -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!"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -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 />,
|
||||||
|
|||||||
@ -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`.
|
||||||
|
|||||||
21
frontend/app/src/config/translation.ts
Normal file
21
frontend/app/src/config/translation.ts
Normal 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;
|
||||||
53
frontend/app/src/config/translationMap.json
Normal file
53
frontend/app/src/config/translationMap.json
Normal 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": "Вход не удался!"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user