From 5684243215afcec2cf453568dad418455aea76d8 Mon Sep 17 00:00:00 2001 From: AmitChauhan63390 Date: Sat, 7 Dec 2024 22:20:17 +0530 Subject: [PATCH 1/3] langs --- package.json | 6 +- public/locales/ar.json | 0 public/locales/en.json | 13 + public/locales/es.json | 13 + public/locales/ja.json | 13 + public/locales/zh.json | 13 + src/App.tsx | 29 ++- src/components/molecules/NavBar.tsx | 370 +++++++++++++++++++++------- src/i18n.ts | 22 ++ src/index.tsx | 1 + src/pages/Login.tsx | 256 ++++++++++--------- 11 files changed, 508 insertions(+), 228 deletions(-) create mode 100644 public/locales/ar.json create mode 100644 public/locales/en.json create mode 100644 public/locales/es.json create mode 100644 public/locales/ja.json create mode 100644 public/locales/zh.json create mode 100644 src/i18n.ts diff --git a/package.json b/package.json index b6b73537..8b9d5d66 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,9 @@ "fortawesome": "^0.0.1-security", "google-auth-library": "^9.14.1", "googleapis": "^144.0.0", + "i18next": "^24.0.2", + "i18next-browser-languagedetector": "^8.0.0", + "i18next-http-backend": "^3.0.1", "ioredis": "^5.4.1", "joi": "^17.6.0", "jsonwebtoken": "^9.0.2", @@ -56,6 +59,7 @@ "react": "^18.0.0", "react-dom": "^18.0.0", "react-highlight": "0.15.0", + "react-i18next": "^15.1.3", "react-router-dom": "^6.26.1", "react-simple-code-editor": "^0.11.2", "react-transition-group": "^4.4.2", @@ -110,4 +114,4 @@ "ts-node": "^10.4.0", "vite": "^5.4.10" } -} \ No newline at end of file +} diff --git a/public/locales/ar.json b/public/locales/ar.json new file mode 100644 index 00000000..e69de29b diff --git a/public/locales/en.json b/public/locales/en.json new file mode 100644 index 00000000..23281300 --- /dev/null +++ b/public/locales/en.json @@ -0,0 +1,13 @@ +{ + "login": { + "title": "Welcome Back!", + "email": "Email", + "password": "Password", + "button": "Login", + "loading": "Loading", + "register_prompt": "Don't have an account?", + "register_link": "Register", + "welcome_notification": "Welcome to Maxun!", + "error_notification": "Login Failed. Please try again." + } + } \ No newline at end of file diff --git a/public/locales/es.json b/public/locales/es.json new file mode 100644 index 00000000..00589622 --- /dev/null +++ b/public/locales/es.json @@ -0,0 +1,13 @@ +{ + "app": { + "name": "Maxun", + "version": "beta" + }, + "login": { + "title": "¡Bienvenido de nuevo!", + "email": "Correo electrónico", + "password": "Contraseña", + "button": "Iniciar sesión", + "register_prompt": "¿No tienes una cuenta? Regístrate" + } + } \ No newline at end of file diff --git a/public/locales/ja.json b/public/locales/ja.json new file mode 100644 index 00000000..80a594eb --- /dev/null +++ b/public/locales/ja.json @@ -0,0 +1,13 @@ +{ + "app": { + "name": "Maxun", + "version": "beta" + }, + "login": { + "title": "おかえりなさい!", + "email": "メールアドレス", + "password": "パスワード", + "button": "ログイン", + "register_prompt": "アカウントをお持ちでない方は、新規登録" + } + } \ No newline at end of file diff --git a/public/locales/zh.json b/public/locales/zh.json new file mode 100644 index 00000000..7fa8bb60 --- /dev/null +++ b/public/locales/zh.json @@ -0,0 +1,13 @@ +{ + "app": { + "name": "Maxun", + "version": "beta" + }, + "login": { + "title": "欢迎回来!", + "email": "电子邮件", + "password": "密码", + "button": "登录", + "register_prompt": "没有账号?注册" + } + } \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index c37de9ea..02dff134 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,8 +1,10 @@ -import React from 'react'; -import { Routes, Route } from 'react-router-dom'; +import React from "react"; +import { Routes, Route } from "react-router-dom"; import { ThemeProvider, createTheme } from "@mui/material/styles"; import { GlobalInfoProvider } from "./context/globalInfo"; import { PageWrapper } from "./pages/PageWrappper"; +import i18n from "./i18n"; + const theme = createTheme({ palette: { @@ -20,14 +22,14 @@ const theme = createTheme({ }, containedPrimary: { // Styles for 'contained' variant with 'primary' color - '&:hover': { + "&:hover": { backgroundColor: "#ff66d9", }, }, outlined: { // Apply white background for all 'outlined' variant buttons backgroundColor: "#ffffff", - '&:hover': { + "&:hover": { backgroundColor: "#f0f0f0", // Optional lighter background on hover }, }, @@ -36,7 +38,7 @@ const theme = createTheme({ MuiLink: { styleOverrides: { root: { - '&:hover': { + "&:hover": { color: "#ff00c3", }, }, @@ -63,7 +65,7 @@ const theme = createTheme({ standardInfo: { backgroundColor: "#fce1f4", color: "#ff00c3", - '& .MuiAlert-icon': { + "& .MuiAlert-icon": { color: "#ff00c3", }, }, @@ -72,7 +74,7 @@ const theme = createTheme({ MuiAlertTitle: { styleOverrides: { root: { - '& .MuiAlert-icon': { + "& .MuiAlert-icon": { color: "#ffffff", }, }, @@ -81,15 +83,16 @@ const theme = createTheme({ }, }); - function App() { return ( - - - } /> - - + + + + } /> + + + ); } diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 4c0b7296..b068aa3a 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -1,134 +1,322 @@ -import React, { useState, useContext } from 'react'; -import axios from 'axios'; +import React, { useState, useContext } from "react"; +import axios from "axios"; import styled from "styled-components"; import { stopRecording } from "../../api/recording"; import { useGlobalInfoStore } from "../../context/globalInfo"; -import { IconButton, Menu, MenuItem, Typography, Avatar, Chip, } from "@mui/material"; +import { IconButton, Menu, MenuItem, Typography, Chip } from "@mui/material"; import { AccountCircle, Logout, Clear } from "@mui/icons-material"; -import { useNavigate } from 'react-router-dom'; -import { AuthContext } from '../../context/auth'; -import { SaveRecording } from '../molecules/SaveRecording'; -import DiscordIcon from '../atoms/DiscordIcon'; -import { apiUrl } from '../../apiConfig'; +import { useNavigate } from "react-router-dom"; +import { AuthContext } from "../../context/auth"; +import { SaveRecording } from "../molecules/SaveRecording"; +import DiscordIcon from "../atoms/DiscordIcon"; +import { apiUrl } from "../../apiConfig"; import MaxunLogo from "../../assets/maxunlogo.png"; +import { useTranslation } from "react-i18next"; // Import useTranslation hook interface NavBarProps { recordingName: string; isRecording: boolean; } -export const NavBar: React.FC = ({ recordingName, isRecording }) => { - const { notify, browserId, setBrowserId, recordingUrl } = useGlobalInfoStore(); +export const NavBar: React.FC = ({ + recordingName, + isRecording, +}) => { + const { notify, browserId, setBrowserId } = useGlobalInfoStore(); const { state, dispatch } = useContext(AuthContext); const { user } = state; const navigate = useNavigate(); + const { t, i18n } = useTranslation(); // Get translation function and i18n methods const [anchorEl, setAnchorEl] = useState(null); + const [langAnchorEl, setLangAnchorEl] = useState(null); const handleMenuOpen = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); }; + const handleLangMenuOpen = (event: React.MouseEvent) => { + setLangAnchorEl(event.currentTarget); + }; + const handleMenuClose = () => { setAnchorEl(null); + setLangAnchorEl(null); }; const logout = async () => { - dispatch({ type: 'LOGOUT' }); - window.localStorage.removeItem('user'); + dispatch({ type: "LOGOUT" }); + window.localStorage.removeItem("user"); const { data } = await axios.get(`${apiUrl}/auth/logout`); - notify('success', data.message); - navigate('/login'); + notify("success", data.message); + navigate("/login"); }; const goToMainMenu = async () => { if (browserId) { await stopRecording(browserId); - notify('warning', 'Current Recording was terminated'); + notify("warning", "Current Recording was terminated"); setBrowserId(null); } - navigate('/'); + navigate("/"); + }; + + const changeLanguage = (lang: string) => { + i18n.changeLanguage(lang); // Change language dynamically + localStorage.setItem("language", lang); // Persist language to localStorage }; return ( -
- -
Maxun
- +
+ +
+ Maxun +
+
- { - user ? ( -
- {!isRecording ? ( - <> - + {!isRecording ? ( + <> + + + + + + + {user.email} + + + { + handleMenuClose(); + logout(); }} > - - - - - - {user.email} - - - { handleMenuClose(); logout(); }}> - Logout - - - - ) : ( - <> - - - Discard - - - - )} -
- ) : "" - } + {t("logout")} + + + {/* Language dropdown */} + + ) : ( + <> + + + {t("discard")} + + + + )} + + {t("language")} + + + { + changeLanguage("en"); + handleMenuClose(); + }} + > + English + + { + changeLanguage("es"); + handleMenuClose(); + }} + > + Español + + { + changeLanguage("ja"); + handleMenuClose(); + }} + > + 日本語 + + { + changeLanguage("ar"); + handleMenuClose(); + }} + > + العربية + + { + changeLanguage("zh"); + handleMenuClose(); + }} + > + 中文 + + +
+ ) : ( + <> + {t("language")} + + + { + changeLanguage("en"); + handleMenuClose(); + }} + > + English + + { + changeLanguage("es"); + handleMenuClose(); + }} + > + Español + + { + changeLanguage("ja"); + handleMenuClose(); + }} + > + 日本語 + + { + changeLanguage("ar"); + handleMenuClose(); + }} + > + العربية + + { + changeLanguage("zh"); + handleMenuClose(); + }} + > + 中文 + + + )} + +
); }; @@ -136,7 +324,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => const NavBarWrapper = styled.div` grid-area: navbar; background-color: white; - padding:5px; + padding: 5px; display: flex; justify-content: space-between; border-bottom: 1px solid #e0e0e0; diff --git a/src/i18n.ts b/src/i18n.ts new file mode 100644 index 00000000..f318cd5c --- /dev/null +++ b/src/i18n.ts @@ -0,0 +1,22 @@ +import i18n from 'i18next'; +import { initReactI18next } from 'react-i18next'; +import Backend from 'i18next-http-backend'; +import LanguageDetector from 'i18next-browser-languagedetector'; + +i18n + .use(Backend) + .use(LanguageDetector) + .use(initReactI18next) + .init({ + fallbackLng: 'en', + debug: import.meta.env.DEV, + supportedLngs: ['en', 'es', 'ja', 'zh', 'ar'], + interpolation: { + escapeValue: false, // React already escapes + }, + backend: { + loadPath: '/locales/{{lng}}.json', + }, + }); + +export default i18n; \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx index 8c14f60a..96f914ff 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,6 +3,7 @@ import ReactDOM from 'react-dom/client'; import './index.css'; import { BrowserRouter } from 'react-router-dom'; import App from './App'; +import i18n from "./i18n" const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index 87f90b53..3d70bfc2 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -1,134 +1,144 @@ -import axios from "axios"; -import { useState, useContext, useEffect, FormEvent } from "react"; -import { useNavigate, Link } from "react-router-dom"; -import { AuthContext } from "../context/auth"; -import { Box, Typography, TextField, Button, CircularProgress, Grid } from "@mui/material"; -import { useGlobalInfoStore } from "../context/globalInfo"; +import axios from "axios"; +import { useState, useContext, useEffect, FormEvent } from "react"; +import { useNavigate, Link } from "react-router-dom"; +import { AuthContext } from "../context/auth"; +import { Box, Typography, TextField, Button, CircularProgress, Grid } from "@mui/material"; +import { useGlobalInfoStore } from "../context/globalInfo"; import { apiUrl } from "../apiConfig"; +import { useTranslation } from 'react-i18next'; +import i18n from '../i18n'; // Add this import const Login = () => { - const [form, setForm] = useState({ - email: "", - password: "", - }); - const [loading, setLoading] = useState(false); - const { notify } = useGlobalInfoStore(); - const { email, password } = form; + const { t } = useTranslation(); + console.log(i18n) // Add translation hook + console.log(t) + const [form, setForm] = useState({ + email: "", + password: "", + }); + const [loading, setLoading] = useState(false); + const { notify } = useGlobalInfoStore(); + const { email, password } = form; - const { state, dispatch } = useContext(AuthContext); - const { user } = state; + const { state, dispatch } = useContext(AuthContext); + const { user } = state; - const navigate = useNavigate(); + const navigate = useNavigate(); - useEffect(() => { - if (user) { - navigate("/"); - } - }, [user, navigate]); + useEffect(() => { + if (user) { + navigate("/"); + } + }, [user, navigate]); - const handleChange = (e: any) => { - const { name, value } = e.target; - setForm({ ...form, [name]: value }); - }; + const handleChange = (e: any) => { + const { name, value } = e.target; + setForm({ ...form, [name]: value }); + }; - const submitForm = async (e: any) => { - e.preventDefault(); - setLoading(true); - try { - const { data } = await axios.post(`${apiUrl}/auth/login`, { - email, - password, - }); - dispatch({ type: "LOGIN", payload: data }); - notify("success", "Welcome to Maxun!"); - window.localStorage.setItem("user", JSON.stringify(data)); - navigate("/"); - } catch (err) { - notify("error", "Login Failed. Please try again."); - setLoading(false); - } - }; + const submitForm = async (e: any) => { + e.preventDefault(); + setLoading(true); + try { + const { data } = await axios.post(`${apiUrl}/auth/login`, { + email, + password, + }); + dispatch({ type: "LOGIN", payload: data }); + notify("success", t('login.welcome_notification')); // Translated notification + window.localStorage.setItem("user", JSON.stringify(data)); + navigate("/"); + } catch (err) { + notify("error", t('login.error_notification')); // Translated error + setLoading(false); + } + }; - return ( - - - - logo - - Welcome Back! - - - - - - Don’t have an account?{" "} - - Register - - - - - - ); + // Language switcher function + const changeLanguage = (lng: string) => { + i18n.changeLanguage(lng); + }; + + return ( + + {/* Language Switcher Buttons */} + + + logo + + {t('login.title')} + + + + + + {t('login.register_prompt')}{" "} + + {t('login.register_link')} + + + + + ); }; -export default Login; +export default Login; \ No newline at end of file From 077234212af3e920e78699d25a1acedc789da28b Mon Sep 17 00:00:00 2001 From: AmitChauhan63390 Date: Tue, 10 Dec 2024 20:40:59 +0530 Subject: [PATCH 2/3] language support --- public/locales/ar.json | 0 public/locales/de.json | 50 +++++++++++++ public/locales/en.json | 40 +++++++++++ public/locales/ja.json | 55 +++++++++++--- src/components/molecules/NavBar.tsx | 9 +++ src/components/molecules/RecordingsTable.tsx | 76 +++++++++++--------- src/components/organisms/MainMenu.tsx | 16 +++-- src/i18n.ts | 2 +- src/pages/Login.tsx | 8 +-- src/pages/Register.tsx | 20 ++++-- 10 files changed, 215 insertions(+), 61 deletions(-) delete mode 100644 public/locales/ar.json create mode 100644 public/locales/de.json diff --git a/public/locales/ar.json b/public/locales/ar.json deleted file mode 100644 index e69de29b..00000000 diff --git a/public/locales/de.json b/public/locales/de.json new file mode 100644 index 00000000..d12b2489 --- /dev/null +++ b/public/locales/de.json @@ -0,0 +1,50 @@ +{ + "login": { + "title": "Willkommen zurück!", + "email": "E-Mail", + "password": "Passwort", + "button": "Einloggen", + "loading": "Lädt", + "register_prompt": "Noch keinen Account?", + "register_link": "Registrieren", + "welcome_notification": "Willkommen bei Maxun!", + "error_notification": "Anmeldung fehlgeschlagen. Bitte versuchen Sie es erneut." + }, + "register": { + "title": "Konto registrieren", + "email": "E-Mail", + "password": "Passwort", + "button": "Registrieren", + "loading": "Lädt", + "register_prompt": "Bereits ein Konto?", + "login_link": "Einloggen", + "welcome_notification": "Willkommen bei Maxun!", + "error_notification": "Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut." + }, + "recordingtable": { + "run": "Ausführen", + "name": "Name", + "schedule": "Zeitplan", + "integrate": "Integrieren", + "settings": "Einstellungen", + "options": "Optionen", + "heading": "Meine Roboter", + "new": "Roboter erstellen", + "modal": { + "title": "Geben Sie die URL ein", + "label": "URL", + "button": "Aufnahme starten" + }, + "edit": "Bearbeiten", + "delete": "Löschen", + "duplicate": "Duplizieren" + }, + "mainmenu": { + "recordings": "Roboter", + "runs": "Ausführungen", + "proxy": "Proxy", + "apikey": "API-Schlüssel", + "feedback": "Maxun Cloud beitreten", + "apidocs": "API-Dokumentation" + } +} diff --git a/public/locales/en.json b/public/locales/en.json index 23281300..897e1729 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -9,5 +9,45 @@ "register_link": "Register", "welcome_notification": "Welcome to Maxun!", "error_notification": "Login Failed. Please try again." + }, + "register": { + "title": "Register Account", + "email": "Email", + "password": "Password", + "button": "Register", + "loading": "Loading", + "register_prompt": "Already have an account?", + "login_link": "Login", + "welcome_notification": "Welcome to Maxun!", + "error_notification": "Registeration Failed. Please try again." + }, + "recordingtable":{ + "run": "Run", + "name": "Name", + "schedule": "Schedule", + "integrate": "Integrate", + "settings": "Settings", + "options": "Options", + "heading":"My Robots", + "new":"Create Robot", + "modal":{ + "title":"Enter the URL", + "label":"URL", + "button":"Start Recording" + }, + "edit":"Edit", + "delete":"Delete", + "duplicate":"Duplicate", + "search":"Search Robots..." + + }, + "mainmenu":{ + "recordings": "Robots", + "runs": "Runs", + "proxy": "Proxy", + "apikey": "API Key", + "feedback":"Join Maxun Cloud", + "apidocs":"API Docs" + } } \ No newline at end of file diff --git a/public/locales/ja.json b/public/locales/ja.json index 80a594eb..88686e62 100644 --- a/public/locales/ja.json +++ b/public/locales/ja.json @@ -1,13 +1,50 @@ { - "app": { - "name": "Maxun", - "version": "beta" - }, - "login": { - "title": "おかえりなさい!", + "login": { + "title": "お帰りなさい!", "email": "メールアドレス", "password": "パスワード", "button": "ログイン", - "register_prompt": "アカウントをお持ちでない方は、新規登録" - } - } \ No newline at end of file + "loading": "読み込み中", + "register_prompt": "アカウントをお持ちでないですか?", + "register_link": "登録する", + "welcome_notification": "Maxunへようこそ!", + "error_notification": "ログインに失敗しました。もう一度お試しください。" + }, + "register": { + "title": "アカウントを登録する", + "email": "メールアドレス", + "password": "パスワード", + "button": "登録する", + "loading": "読み込み中", + "register_prompt": "既にアカウントをお持ちですか?", + "login_link": "ログイン", + "welcome_notification": "Maxunへようこそ!", + "error_notification": "登録に失敗しました。もう一度お試しください。" + }, + "recordingtable": { + "run": "実行", + "name": "名前", + "schedule": "スケジュール", + "integrate": "統合", + "settings": "設定", + "options": "オプション", + "heading": "私のロボット", + "new": "ロボットを作成", + "modal": { + "title": "URLを入力してください", + "label": "URL", + "button": "録画を開始" + }, + "edit": "編集", + "delete": "削除", + "duplicate": "複製" + }, + "mainmenu": { + "recordings": "ロボット", + "runs": "実行", + "proxy": "プロキシ", + "apikey": "APIキー", + "feedback": "Maxunクラウドに参加する", + "apidocs": "APIドキュメント" + } +} diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index b068aa3a..958492e6 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -245,6 +245,15 @@ export const NavBar: React.FC = ({ > 中文 + { + changeLanguage("de"); + handleMenuClose(); + }} + > + German + + ) : ( diff --git a/src/components/molecules/RecordingsTable.tsx b/src/components/molecules/RecordingsTable.tsx index e9f0aebc..e298445f 100644 --- a/src/components/molecules/RecordingsTable.tsx +++ b/src/components/molecules/RecordingsTable.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { useTranslation } from 'react-i18next'; import Paper from '@mui/material/Paper'; import Table from '@mui/material/Table'; import TableBody from '@mui/material/TableBody'; @@ -19,6 +20,7 @@ import { useNavigate } from 'react-router-dom'; import { stopRecording } from "../../api/recording"; import { GenericModal } from '../atoms/GenericModal'; + /** TODO: * 1. allow editing existing robot after persisting browser steps */ @@ -31,30 +33,9 @@ interface Column { format?: (value: string) => string; } -const columns: readonly Column[] = [ - { id: 'interpret', label: 'Run', minWidth: 80 }, - { id: 'name', label: 'Name', minWidth: 80 }, - { - id: 'schedule', - label: 'Schedule', - minWidth: 80, - }, - { - id: 'integrate', - label: 'Integrate', - minWidth: 80, - }, - { - id: 'settings', - label: 'Settings', - minWidth: 80, - }, - { - id: 'options', - label: 'Options', - minWidth: 80, - }, -]; + + + interface Data { id: string; @@ -76,12 +57,38 @@ interface RecordingsTableProps { } export const RecordingsTable = ({ handleEditRecording, handleRunRecording, handleScheduleRecording, handleIntegrateRecording, handleSettingsRecording, handleEditRobot, handleDuplicateRobot }: RecordingsTableProps) => { + const {t} = useTranslation(); const [page, setPage] = React.useState(0); const [rowsPerPage, setRowsPerPage] = React.useState(10); const [rows, setRows] = React.useState([]); const [isModalOpen, setModalOpen] = React.useState(false); const [searchTerm, setSearchTerm] = React.useState(''); + const columns: readonly Column[] = [ + { id: 'interpret', label: t('recordingtable.run'), minWidth: 80 }, + { id: 'name', label: t('recordingtable.name'), minWidth: 80 }, + { + id: 'schedule', + label: t('recordingtable.schedule'), + minWidth: 80, + }, + { + id: 'integrate', + label: t('recordingtable.integrate'), + minWidth: 80, + }, + { + id: 'settings', + label: t('recordingtable.settings'), + minWidth: 80, + }, + { + id: 'options', + label: t('recordingtable.options'), + minWidth: 80, + }, + ]; + const { notify, setRecordings, browserId, setBrowserId, recordingUrl, setRecordingUrl, recordingName, setRecordingName, recordingId, setRecordingId } = useGlobalInfoStore(); const navigate = useNavigate(); @@ -154,16 +161,17 @@ export const RecordingsTable = ({ handleEditRecording, handleRunRecording, handl + return ( - My Robots + {t('recordingtable.heading')} - Create Robot + {t('recordingtable.new')} @@ -300,9 +308,9 @@ export const RecordingsTable = ({ handleEditRecording, handleRunRecording, handl /> setModalOpen(false)} modalStyle={modalStyle}>
- Enter URL To Extract Data + {t('recordingtable.modal.title')} - Start Training Robot + {t('recordingtable.modal.button')}
@@ -400,6 +408,8 @@ const OptionsButton = ({ handleEdit, handleDelete, handleDuplicate }: OptionsBut setAnchorEl(null); }; + const {t} = useTranslation(); + return ( <> - Edit + {t('recordingtable.edit')} { handleDelete(); handleClose(); }}> - Delete + {t('recordingtable.delete')} { handleDuplicate(); handleClose(); }}> - Duplicate + {t('recordingtable.duplicate')} diff --git a/src/components/organisms/MainMenu.tsx b/src/components/organisms/MainMenu.tsx index dadb6731..4143ae9f 100644 --- a/src/components/organisms/MainMenu.tsx +++ b/src/components/organisms/MainMenu.tsx @@ -5,6 +5,9 @@ import Box from '@mui/material/Box'; import { Paper, Button } from "@mui/material"; import { AutoAwesome, FormatListBulleted, VpnKey, Usb, CloudQueue, Code } from "@mui/icons-material"; import { apiUrl } from "../../apiConfig"; +import { useTranslation } from 'react-i18next'; +import i18n from '../../i18n'; + interface MainMenuProps { value: string; @@ -12,6 +15,7 @@ interface MainMenuProps { } export const MainMenu = ({ value = 'recordings', handleChangeContent }: MainMenuProps) => { + const {t} = useTranslation(); const handleChange = (event: React.SyntheticEvent, newValue: string) => { handleChangeContent(newValue); @@ -47,7 +51,7 @@ export const MainMenu = ({ value = 'recordings', handleChangeContent }: MainMenu fontSize: 'medium', }} value="recordings" - label="Robots" + label={t('mainmenu.recordings')} icon={} iconPosition="start" /> @@ -58,7 +62,7 @@ export const MainMenu = ({ value = 'recordings', handleChangeContent }: MainMenu fontSize: 'medium', }} value="runs" - label="Runs" + label={t('mainmenu.runs')} icon={} iconPosition="start" /> @@ -69,7 +73,7 @@ export const MainMenu = ({ value = 'recordings', handleChangeContent }: MainMenu fontSize: 'medium', }} value="proxy" - label="Proxy" + label={t('mainmenu.proxy')} icon={} iconPosition="start" /> @@ -80,7 +84,7 @@ export const MainMenu = ({ value = 'recordings', handleChangeContent }: MainMenu fontSize: 'medium', }} value="apikey" - label="API Key" + label={t('mainmenu.apikey')} icon={} iconPosition="start" /> @@ -88,10 +92,10 @@ export const MainMenu = ({ value = 'recordings', handleChangeContent }: MainMenu
diff --git a/src/i18n.ts b/src/i18n.ts index f318cd5c..c5e84364 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -10,7 +10,7 @@ i18n .init({ fallbackLng: 'en', debug: import.meta.env.DEV, - supportedLngs: ['en', 'es', 'ja', 'zh', 'ar'], + supportedLngs: ['en', 'es', 'ja', 'zh','de'], interpolation: { escapeValue: false, // React already escapes }, diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index 3d70bfc2..3c8e08c4 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -6,11 +6,11 @@ import { Box, Typography, TextField, Button, CircularProgress, Grid } from "@mui import { useGlobalInfoStore } from "../context/globalInfo"; import { apiUrl } from "../apiConfig"; import { useTranslation } from 'react-i18next'; -import i18n from '../i18n'; // Add this import +import i18n from '../i18n'; const Login = () => { const { t } = useTranslation(); - console.log(i18n) // Add translation hook + console.log(i18n) console.log(t) const [form, setForm] = useState({ email: "", @@ -55,9 +55,7 @@ const Login = () => { }; // Language switcher function - const changeLanguage = (lng: string) => { - i18n.changeLanguage(lng); - }; + return ( { + const {t} = useTranslation(); const [form, setForm] = useState({ email: "", password: "", @@ -40,11 +45,11 @@ const Register = () => { password, }); dispatch({ type: "LOGIN", payload: data }); - notify("success", "Registration Successful!"); + notify("success", t('register.welcome_notification')); window.localStorage.setItem("user", JSON.stringify(data)); navigate("/"); } catch (error:any) { - notify("error", error.response.data || "Registration Failed. Please try again."); + notify("error", error.response.data || t('register.error_notification')); setLoading(false); } }; @@ -82,7 +87,7 @@ const Register = () => { { /> { Loading ) : ( - "Register" + t('register.button') )} - Already have an account?{" "} + {t('register.register_prompt')}{" "} - Login + + {t('register.login_link')} From 5663abe63eaec093a22299a87b007c206617d4aa Mon Sep 17 00:00:00 2001 From: AmitChauhan63390 Date: Tue, 10 Dec 2024 21:45:09 +0530 Subject: [PATCH 3/3] little progress and icon changed --- public/locales/en.json | 13 +++++ public/locales/ja.json | 13 ++++- src/components/molecules/NavBar.tsx | 8 ++- src/components/molecules/RunsTable.tsx | 76 ++++++++++++++------------ src/pages/Register.tsx | 2 +- 5 files changed, 72 insertions(+), 40 deletions(-) diff --git a/public/locales/en.json b/public/locales/en.json index 897e1729..b6396893 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -49,5 +49,18 @@ "feedback":"Join Maxun Cloud", "apidocs":"API Docs" + }, + "runstable":{ + "runs":"All Runs", + "runStatus":"Status", + "runName":"Name", + "startedAt":"Started At", + "finishedAt":"Finished At", + "delete":"Delete", + "settings":"Settings", + "search":"Search Runs..." + + + } } \ No newline at end of file diff --git a/public/locales/ja.json b/public/locales/ja.json index 88686e62..881fd131 100644 --- a/public/locales/ja.json +++ b/public/locales/ja.json @@ -37,7 +37,8 @@ }, "edit": "編集", "delete": "削除", - "duplicate": "複製" + "duplicate": "複製", + "search": "ロボットを検索..." }, "mainmenu": { "recordings": "ロボット", @@ -46,5 +47,15 @@ "apikey": "APIキー", "feedback": "Maxunクラウドに参加する", "apidocs": "APIドキュメント" + }, + "runstable": { + "runs": "すべての実行", + "runStatus": "ステータス", + "runName": "名前", + "startedAt": "開始日時", + "finishedAt": "終了日時", + "delete": "削除", + "settings": "設定", + "search": "実行を検索..." } } diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 958492e6..ba607fe4 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -4,7 +4,7 @@ import styled from "styled-components"; import { stopRecording } from "../../api/recording"; import { useGlobalInfoStore } from "../../context/globalInfo"; import { IconButton, Menu, MenuItem, Typography, Chip } from "@mui/material"; -import { AccountCircle, Logout, Clear } from "@mui/icons-material"; +import { AccountCircle, Logout, Clear, Language } from "@mui/icons-material"; import { useNavigate } from "react-router-dom"; import { AuthContext } from "../../context/auth"; import { SaveRecording } from "../molecules/SaveRecording"; @@ -159,7 +159,7 @@ export const NavBar: React.FC = ({ {t("logout")} - {/* Language dropdown */} + ) : ( <> @@ -190,7 +190,9 @@ export const NavBar: React.FC = ({ marginRight: "10px", }} > - {t("language")} + + +
string; } -export const columns: readonly Column[] = [ - { id: 'runStatus', label: 'Status', minWidth: 80 }, - { id: 'name', label: 'Robot Name', minWidth: 80 }, - { id: 'startedAt', label: 'Started at', minWidth: 80 }, - { id: 'finishedAt', label: 'Finished at', minWidth: 80 }, - { id: 'settings', label: 'Settings', minWidth: 80 }, - { id: 'delete', label: 'Delete', minWidth: 80 }, -]; - -export interface Data { +interface Data { id: number; status: string; name: string; @@ -58,15 +61,25 @@ interface RunsTableProps { runningRecordingName: string; } -export const RunsTable = ( - { currentInterpretationLog, abortRunHandler, runId, runningRecordingName }: RunsTableProps) => { +export const RunsTable: React.FC = ({ + currentInterpretationLog, + abortRunHandler, + runId, + runningRecordingName +}) => { + const { t } = useTranslation(); + + // Update column labels using translation if needed + const translatedColumns = columns.map(column => ({ + ...column, + label: t(`runstable.${column.id}`, column.label) + })); + const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(10); const [rows, setRows] = useState([]); - const [searchTerm, setSearchTerm] = useState(''); - const { notify, rerenderRuns, setRerenderRuns } = useGlobalInfoStore(); const handleChangePage = (event: unknown, newPage: number) => { @@ -86,16 +99,13 @@ export const RunsTable = ( const fetchRuns = async () => { const runs = await getStoredRuns(); if (runs) { - const parsedRows: Data[] = []; - runs.map((run: any, index) => { - parsedRows.push({ - id: index, - ...run, - }); - }); + const parsedRows: Data[] = runs.map((run: any, index: number) => ({ + id: index, + ...run, + })); setRows(parsedRows); } else { - notify('error', 'No runs found. Please try again.') + notify('error', 'No runs found. Please try again.'); } }; @@ -104,7 +114,7 @@ export const RunsTable = ( fetchRuns(); setRerenderRuns(false); } - }, [rerenderRuns]); + }, [rerenderRuns, rows.length, setRerenderRuns]); const handleDelete = () => { setRows([]); @@ -112,7 +122,6 @@ export const RunsTable = ( fetchRuns(); }; - // Filter rows based on search term const filteredRows = rows.filter((row) => row.name.toLowerCase().includes(searchTerm.toLowerCase()) @@ -120,7 +129,6 @@ export const RunsTable = ( // Group filtered rows by robot meta id const groupedRows = filteredRows.reduce((acc, row) => { - if (!acc[row.robotMetaId]) { acc[row.robotMetaId] = []; } @@ -132,11 +140,11 @@ export const RunsTable = ( - All Runs + {t('runstable.runs', 'Runs')} ( }> - {data[data.length - 1].name} - - {columns.map((column) => ( + {translatedColumns.map((column) => ( ); -}; +}; \ No newline at end of file diff --git a/src/pages/Register.tsx b/src/pages/Register.tsx index 57ec676a..b1fb23e2 100644 --- a/src/pages/Register.tsx +++ b/src/pages/Register.tsx @@ -83,7 +83,7 @@ const Register = () => { > logo - Create an Account + {t('register.title')}