feat: add en language
This commit is contained in:
		@@ -24,7 +24,7 @@
 | 
			
		||||
      work correctly both with client-side routing and a non-root public URL.
 | 
			
		||||
      Learn how to configure a non-root public URL by running `npm run build`.
 | 
			
		||||
    -->
 | 
			
		||||
    <title>React App</title>
 | 
			
		||||
    <title>VORKOUT</title>
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <noscript>You need to enable JavaScript to run this app.</noscript>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import { Drawer } from 'antd';
 | 
			
		||||
import { useEffect, useState } from 'react';
 | 
			
		||||
import { Avatar, Typography } from 'antd';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
 | 
			
		||||
interface ContentDrawerProps {
 | 
			
		||||
  open: boolean;
 | 
			
		||||
@@ -15,6 +16,7 @@ export default function ContentDrawer({
 | 
			
		||||
  children,
 | 
			
		||||
  type,
 | 
			
		||||
}: ContentDrawerProps) {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const [width, setWidth] = useState<number | string>('30%');
 | 
			
		||||
 | 
			
		||||
  const calculateWidths = () => {
 | 
			
		||||
@@ -123,7 +125,7 @@ export default function ContentDrawer({
 | 
			
		||||
          fontSize: '20px',
 | 
			
		||||
        }}
 | 
			
		||||
      >
 | 
			
		||||
        Новая учетная запись
 | 
			
		||||
        {t('newAccount')}
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div
 | 
			
		||||
@@ -133,6 +135,7 @@ export default function ContentDrawer({
 | 
			
		||||
          height: '24px',
 | 
			
		||||
          width: '24px',
 | 
			
		||||
        }}
 | 
			
		||||
        onClick={closeDrawer}
 | 
			
		||||
      >
 | 
			
		||||
        <img
 | 
			
		||||
          src="./icons/drawer/delete.svg"
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ import {
 | 
			
		||||
  UploadProps,
 | 
			
		||||
} from 'antd';
 | 
			
		||||
import { useState } from 'react';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
 | 
			
		||||
const { Option } = Select;
 | 
			
		||||
 | 
			
		||||
@@ -24,6 +25,7 @@ const getBase64 = (file: FileType): Promise<string> =>
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
export default function UserCreate() {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const [previewOpen, setPreviewOpen] = useState(false);
 | 
			
		||||
  const [previewImage, setPreviewImage] = useState('');
 | 
			
		||||
 | 
			
		||||
@@ -64,7 +66,9 @@ export default function UserCreate() {
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <span style={{ fontSize: '14px', color: '#8c8c8c' }}>Выбрать фото</span>
 | 
			
		||||
      <span style={{ fontSize: '14px', color: '#8c8c8c' }}>
 | 
			
		||||
        {t('selectPhoto')}
 | 
			
		||||
      </span>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
@@ -135,54 +139,54 @@ export default function UserCreate() {
 | 
			
		||||
        style={{ flex: 1, display: 'flex', flexDirection: 'column' }}
 | 
			
		||||
      >
 | 
			
		||||
        <Form.Item
 | 
			
		||||
          label="Имя"
 | 
			
		||||
          label={t('name')}
 | 
			
		||||
          name="name"
 | 
			
		||||
          rules={[{ required: true, message: 'Введите имя' }]}
 | 
			
		||||
          rules={[{ required: true, message: t('nameMessage') }]}
 | 
			
		||||
        >
 | 
			
		||||
          <Input />
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <Form.Item
 | 
			
		||||
          label="Логин"
 | 
			
		||||
          label={t('login')}
 | 
			
		||||
          name="login"
 | 
			
		||||
          rules={[{ required: true, message: 'Введите логин' }]}
 | 
			
		||||
          rules={[{ required: true, message: t('loginMessage') }]}
 | 
			
		||||
        >
 | 
			
		||||
          <Input />
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <Form.Item
 | 
			
		||||
          label="Пароль"
 | 
			
		||||
          label={t('password')}
 | 
			
		||||
          name="password"
 | 
			
		||||
          rules={[{ required: true, message: 'Введите пароль' }]}
 | 
			
		||||
          rules={[{ required: true, message: t('passwordMessage') }]}
 | 
			
		||||
        >
 | 
			
		||||
          <Input.Password />
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <Form.Item
 | 
			
		||||
          label="E-mail"
 | 
			
		||||
          label={t('email')}
 | 
			
		||||
          name="email"
 | 
			
		||||
          rules={[
 | 
			
		||||
            { required: true, message: 'Введите имейл' },
 | 
			
		||||
            { type: 'email', message: 'Некорректный имейл' },
 | 
			
		||||
            { required: true, message: t('emailMessage') },
 | 
			
		||||
            { type: 'email', message: t('emailErrorMessage') },
 | 
			
		||||
          ]}
 | 
			
		||||
        >
 | 
			
		||||
          <Input />
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <Form.Item
 | 
			
		||||
          label="Привязка"
 | 
			
		||||
          label={t('tenant')}
 | 
			
		||||
          name="tenant"
 | 
			
		||||
          rules={[{ required: true, message: 'Введите привязку' }]}
 | 
			
		||||
          rules={[{ required: true, message: t('tenantMessage') }]}
 | 
			
		||||
        >
 | 
			
		||||
          <Input />
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <Form.Item
 | 
			
		||||
          label="Роль"
 | 
			
		||||
          label={t('role')}
 | 
			
		||||
          name="role"
 | 
			
		||||
          rules={[{ required: true, message: 'Выберите роль' }]}
 | 
			
		||||
          rules={[{ required: true, message: t('roleMessage') }]}
 | 
			
		||||
        >
 | 
			
		||||
          <Select placeholder="Выберите роль">
 | 
			
		||||
          <Select placeholder={t('roleMessage')}>
 | 
			
		||||
            <Option value="Директор магазина">Директор магазина</Option>
 | 
			
		||||
            <Option value="Менеджер">Менеджер</Option>
 | 
			
		||||
            <Option value="Кассир">Кассир</Option>
 | 
			
		||||
@@ -190,11 +194,11 @@ export default function UserCreate() {
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <Form.Item
 | 
			
		||||
          label="Статус"
 | 
			
		||||
          label={t('status')}
 | 
			
		||||
          name="status"
 | 
			
		||||
          rules={[{ required: true, message: 'Выберите статус' }]}
 | 
			
		||||
          rules={[{ required: true, message: t('statusMessage') }]}
 | 
			
		||||
        >
 | 
			
		||||
          <Select placeholder="Выберите статус">
 | 
			
		||||
          <Select placeholder={t('statusMessage')}>
 | 
			
		||||
            <Option value="ACTIVE">Активен</Option>
 | 
			
		||||
            <Option value="DISABLED">Неактивен</Option>
 | 
			
		||||
            <Option value="BLOCKED">Заблокирован</Option>
 | 
			
		||||
@@ -216,7 +220,7 @@ export default function UserCreate() {
 | 
			
		||||
              alt="save"
 | 
			
		||||
              style={{ height: '18px', width: '18px' }}
 | 
			
		||||
            />{' '}
 | 
			
		||||
            Добавить аккаунт
 | 
			
		||||
            {t('addAccount')}
 | 
			
		||||
          </Button>
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
      </Form>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,10 @@
 | 
			
		||||
import { Button, Form, Input, Select } from 'antd';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
 | 
			
		||||
const { Option } = Select;
 | 
			
		||||
 | 
			
		||||
export default function UserEdit() {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  return (
 | 
			
		||||
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
 | 
			
		||||
      <Form
 | 
			
		||||
@@ -21,54 +23,54 @@ export default function UserEdit() {
 | 
			
		||||
        style={{ flex: 1, display: 'flex', flexDirection: 'column' }}
 | 
			
		||||
      >
 | 
			
		||||
        <Form.Item
 | 
			
		||||
          label="Имя"
 | 
			
		||||
          label={t('name')}
 | 
			
		||||
          name="name"
 | 
			
		||||
          rules={[{ required: true, message: 'Введите имя' }]}
 | 
			
		||||
          rules={[{ required: true, message: t('nameMessage') }]}
 | 
			
		||||
        >
 | 
			
		||||
          <Input />
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <Form.Item
 | 
			
		||||
          label="Логин"
 | 
			
		||||
          label={t('login')}
 | 
			
		||||
          name="login"
 | 
			
		||||
          rules={[{ required: true, message: 'Введите логин' }]}
 | 
			
		||||
          rules={[{ required: true, message: t('loginMessage') }]}
 | 
			
		||||
        >
 | 
			
		||||
          <Input />
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <Form.Item
 | 
			
		||||
          label="Пароль (последний ввод)"
 | 
			
		||||
          label={t('password')}
 | 
			
		||||
          name="password"
 | 
			
		||||
          rules={[{ required: true, message: 'Введите пароль' }]}
 | 
			
		||||
          rules={[{ required: true, message: t('passwordMessage') }]}
 | 
			
		||||
        >
 | 
			
		||||
          <Input.Password />
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <Form.Item
 | 
			
		||||
          label="E-mail"
 | 
			
		||||
          label={t('email')}
 | 
			
		||||
          name="email"
 | 
			
		||||
          rules={[
 | 
			
		||||
            { required: true, message: 'Введите имейл' },
 | 
			
		||||
            { type: 'email', message: 'Некорректный имейл' },
 | 
			
		||||
            { required: true, message: t('emailMessage') },
 | 
			
		||||
            { type: 'email', message: t('emailErrorMessage') },
 | 
			
		||||
          ]}
 | 
			
		||||
        >
 | 
			
		||||
          <Input />
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <Form.Item
 | 
			
		||||
          label="Привязка"
 | 
			
		||||
          label={t('tenant')}
 | 
			
		||||
          name="tenant"
 | 
			
		||||
          rules={[{ required: true, message: 'Введите привязку' }]}
 | 
			
		||||
          rules={[{ required: true, message: t('tenantMessage') }]}
 | 
			
		||||
        >
 | 
			
		||||
          <Input />
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <Form.Item
 | 
			
		||||
          label="Роль"
 | 
			
		||||
          label={t('role')}
 | 
			
		||||
          name="role"
 | 
			
		||||
          rules={[{ required: true, message: 'Выберите роль' }]}
 | 
			
		||||
          rules={[{ required: true, message: t('roleMessage') }]}
 | 
			
		||||
        >
 | 
			
		||||
          <Select placeholder="Выберите роль">
 | 
			
		||||
          <Select placeholder={t('roleMessage')}>
 | 
			
		||||
            <Option value="Директор магазина">Директор магазина</Option>
 | 
			
		||||
            <Option value="Менеджер">Менеджер</Option>
 | 
			
		||||
            <Option value="Кассир">Кассир</Option>
 | 
			
		||||
@@ -76,11 +78,11 @@ export default function UserEdit() {
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <Form.Item
 | 
			
		||||
          label="Статус"
 | 
			
		||||
          label={t('status')}
 | 
			
		||||
          name="status"
 | 
			
		||||
          rules={[{ required: true, message: 'Выберите статус' }]}
 | 
			
		||||
          rules={[{ required: true, message: t('statusMessage') }]}
 | 
			
		||||
        >
 | 
			
		||||
          <Select placeholder="Выберите статус">
 | 
			
		||||
          <Select placeholder={t('statusMessage')}>
 | 
			
		||||
            <Option value="ACTIVE">Активен</Option>
 | 
			
		||||
            <Option value="DISABLED">Неактивен</Option>
 | 
			
		||||
            <Option value="BLOCKED">Заблокирован</Option>
 | 
			
		||||
@@ -102,7 +104,7 @@ export default function UserEdit() {
 | 
			
		||||
              alt="save"
 | 
			
		||||
              style={{ height: '18px', width: '18px' }}
 | 
			
		||||
            />{' '}
 | 
			
		||||
            Сохранить изменения
 | 
			
		||||
            {t('save')}
 | 
			
		||||
          </Button>
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
      </Form>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								client/src/config/AppWrapper.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								client/src/config/AppWrapper.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
import './i18n';
 | 
			
		||||
import { ConfigProvider } from 'antd';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
 | 
			
		||||
import { theme } from './customTheme';
 | 
			
		||||
 | 
			
		||||
import en from 'antd/locale/en_US';
 | 
			
		||||
import ru from 'antd/locale/ru_RU';
 | 
			
		||||
 | 
			
		||||
const antdLocales = {
 | 
			
		||||
  en: en,
 | 
			
		||||
  ru: ru,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default function AppWrapper({ children }: any) {
 | 
			
		||||
  const { i18n } = useTranslation();
 | 
			
		||||
  const currentLang = i18n.language.split('-')[0] as 'en' | 'ru';
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <ConfigProvider locale={antdLocales[currentLang]} theme={theme}>
 | 
			
		||||
      {children}
 | 
			
		||||
    </ConfigProvider>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										74
									
								
								client/src/config/i18n.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								client/src/config/i18n.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
			
		||||
import i18n from 'i18next';
 | 
			
		||||
import { initReactI18next } from 'react-i18next';
 | 
			
		||||
import LanguageDetector from 'i18next-browser-languagedetector';
 | 
			
		||||
 | 
			
		||||
i18n
 | 
			
		||||
  .use(LanguageDetector)
 | 
			
		||||
  .use(initReactI18next)
 | 
			
		||||
  .init({
 | 
			
		||||
    fallbackLng: 'en',
 | 
			
		||||
    supportedLngs: ['en', 'ru'],
 | 
			
		||||
    interpolation: { escapeValue: false },
 | 
			
		||||
    resources: {
 | 
			
		||||
      en: {
 | 
			
		||||
        translation: {
 | 
			
		||||
          accounts: 'Accounts',
 | 
			
		||||
          processDiagrams: 'Process diagrams',
 | 
			
		||||
          runningProcesses: 'Running processes',
 | 
			
		||||
          settings: 'Settings',
 | 
			
		||||
          eventsList: 'Events list',
 | 
			
		||||
          configuration: 'Configuration',
 | 
			
		||||
          selectPhoto: 'Select photo',
 | 
			
		||||
          name: 'Name',
 | 
			
		||||
          login: 'Login',
 | 
			
		||||
          password: 'Password',
 | 
			
		||||
          email: 'Email',
 | 
			
		||||
          tenant: 'Tenant',
 | 
			
		||||
          role: 'Role',
 | 
			
		||||
          status: 'Status',
 | 
			
		||||
          nameMessage: 'Enter name',
 | 
			
		||||
          loginMessage: 'Enter login',
 | 
			
		||||
          passwordMessage: 'Enter password',
 | 
			
		||||
          emailMessage: 'Enter email',
 | 
			
		||||
          emailErrorMessage: 'Incorrect email',
 | 
			
		||||
          tenantMessage: 'Enter tenant',
 | 
			
		||||
          roleMessage: 'Choose role',
 | 
			
		||||
          statusMessage: 'Choose status',
 | 
			
		||||
          addAccount: 'Add account',
 | 
			
		||||
          save: 'Save changes',
 | 
			
		||||
          newAccount: 'New account',
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      ru: {
 | 
			
		||||
        translation: {
 | 
			
		||||
          accounts: 'Учетные записи',
 | 
			
		||||
          processDiagrams: 'Схемы процессов',
 | 
			
		||||
          runningProcesses: 'Запущенные процессы',
 | 
			
		||||
          settings: 'Настройки',
 | 
			
		||||
          eventsList: 'Справочкин событий',
 | 
			
		||||
          configuration: 'Конфигурация',
 | 
			
		||||
          selectPhoto: 'Выбрать фото',
 | 
			
		||||
          name: 'Имя',
 | 
			
		||||
          login: 'Логин',
 | 
			
		||||
          password: 'Пароль',
 | 
			
		||||
          email: 'Имейл',
 | 
			
		||||
          tenant: 'Привязка',
 | 
			
		||||
          role: 'Роль',
 | 
			
		||||
          status: 'Статус',
 | 
			
		||||
          nameMessage: 'Введите имя',
 | 
			
		||||
          loginMessage: 'Введите логин',
 | 
			
		||||
          passwordMessage: 'Введите пароль',
 | 
			
		||||
          emailMessage: 'Введите имейл',
 | 
			
		||||
          emailErrorMessage: 'Некорректный имейл',
 | 
			
		||||
          tenantMessage: 'Введите привязку',
 | 
			
		||||
          roleMessage: 'Выберите роль',
 | 
			
		||||
          statusMessage: 'Выберите статус',
 | 
			
		||||
          addAccount: 'Добавить аккаунт',
 | 
			
		||||
          save: 'Сохранить изменения',
 | 
			
		||||
          newAccount: 'Новая учетная запись',
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
export default i18n;
 | 
			
		||||
@@ -1,9 +1,11 @@
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
import Header from '../components/Header';
 | 
			
		||||
 | 
			
		||||
export default function ConfigurationPage() {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <Header title="Конфигурация" />
 | 
			
		||||
      <Header title={t('configuration')} />
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,11 @@
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
import Header from '../components/Header';
 | 
			
		||||
 | 
			
		||||
export default function EventsListPage() {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <Header title="Справочник событий" />
 | 
			
		||||
      <Header title={t('eventsList')} />
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,11 @@
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
import Header from '../components/Header';
 | 
			
		||||
 | 
			
		||||
export default function ProcessDiagramPage() {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <Header title="Схемы процессов" />
 | 
			
		||||
      <Header title={t('processDiagrams')} />
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,11 @@
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
import Header from '../components/Header';
 | 
			
		||||
 | 
			
		||||
export default function RunningProcessesPage() {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <Header title="Запущенные процессы" />
 | 
			
		||||
      <Header title={t('runningProcesses')} />
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user