From a70d77c17e5e99ad4f262f707e16f36e79de69a3 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Wed, 23 Apr 2025 23:57:55 +0200 Subject: [PATCH 01/33] Add group namespace --- i18n.js | 2 +- public/locales/en/core.json | 3 +++ public/locales/en/group.json | 34 ++++++++++++++++++++++++ src/components/Group/Forum/GroupMail.tsx | 30 ++++++++++++--------- src/components/Group/Forum/NewThread.tsx | 22 +++++++++------ 5 files changed, 69 insertions(+), 22 deletions(-) create mode 100644 public/locales/en/group.json diff --git a/i18n.js b/i18n.js index 78df3c1..376f28d 100644 --- a/i18n.js +++ b/i18n.js @@ -42,7 +42,7 @@ i18n escapeValue: false, }, lng: navigator.language, - ns: ['auth', 'core', 'tutorial'], + ns: ['auth', 'core', 'group', 'tutorial'], supportedLngs: ['en', 'it', 'es', 'fr', 'de', 'ru'], }); diff --git a/public/locales/en/core.json b/public/locales/en/core.json index 8988c4a..c2089a9 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -18,6 +18,9 @@ "loading": "loading...", "logout": "logout", "minting_status": "minting status", + "page": { + "last": "last" + }, "payment_notification": "payment notification", "price": "price", "q_mail": "q-mail", diff --git a/public/locales/en/group.json b/public/locales/en/group.json new file mode 100644 index 0000000..453c1f6 --- /dev/null +++ b/public/locales/en/group.json @@ -0,0 +1,34 @@ +{ + "provide_thread": "please provide a thread title", + "result": { + "cannot": { + "access_name": "Cannot send a message without a access to your name", + "group_info": "Cannot access group information" + } + }, + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "", + "": "" +} diff --git a/src/components/Group/Forum/GroupMail.tsx b/src/components/Group/Forum/GroupMail.tsx index 3c8972f..0de9921 100644 --- a/src/components/Group/Forum/GroupMail.tsx +++ b/src/components/Group/Forum/GroupMail.tsx @@ -1,5 +1,4 @@ import React, { - FC, useCallback, useEffect, useMemo, @@ -17,7 +16,6 @@ import { ComposeIcon, ComposeP, GroupContainer, - GroupNameP, InstanceFooter, InstanceListContainer, InstanceListContainerRow, @@ -58,10 +56,12 @@ import { executeEvent } from '../../../utils/events'; import RefreshIcon from '@mui/icons-material/Refresh'; import { getArbitraryEndpointReact, getBaseApiReact } from '../../../App'; import { addDataPublishesFunc, getDataPublishesFunc } from '../Group'; +import { useTranslation } from 'react-i18next'; const filterOptions = ['Recently active', 'Newest', 'Oldest']; export const threadIdentifier = 'DOCUMENT'; + export const GroupMail = ({ selectedGroup, userInfo, @@ -82,6 +82,7 @@ export const GroupMail = ({ const anchorElInstanceFilter = useRef(null); const [tempPublishedList, setTempPublishedList] = useState([]); const dataPublishes = useRef({}); + const { t } = useTranslation(['core']); const [isLoading, setIsLoading] = useState(false); const groupIdRef = useRef(null); @@ -627,9 +628,9 @@ export const GroupMail = ({ @@ -682,6 +683,7 @@ export const GroupMail = ({ }} /> + {combinedListTempAndReal.map((thread) => { @@ -754,8 +756,8 @@ export const GroupMail = ({ {filterMode === 'Recently active' && (
@@ -776,16 +778,16 @@ export const GroupMail = ({ }, 300); }} sx={{ - position: 'absolute', - bottom: '2px', - right: '2px', - borderRadius: '5px', + alignItems: 'center', backgroundColor: '#27282c', + borderRadius: '5px', + bottom: '2px', + cursor: 'pointer', display: 'flex', gap: '10px', - alignItems: 'center', padding: '5px', - cursor: 'pointer', + position: 'absolute', + right: '2px', '&:hover': { background: 'rgba(255, 255, 255, 0.60)', }, @@ -795,9 +797,11 @@ export const GroupMail = ({ sx={{ color: 'white', fontSize: '12px', - }} // TODO translate + }} > - Last page + {t('core:page.last', { + postProcess: 'capitalize', + })} { console.log(error); } }; + export const NewThread = ({ groupInfo, members, @@ -143,8 +143,8 @@ export const NewThread = ({ setPostReply, isPrivate, }: NewMessageProps) => { + const { t } = useTranslation(['core', 'group']); const { show } = React.useContext(MyContext); - const [isOpen, setIsOpen] = useState(false); const [value, setValue] = useState(''); const [isSending, setIsSending] = useState(false); @@ -183,21 +183,27 @@ export const NewThread = ({ const missingFields: string[] = []; if (!isMessage && !threadTitle) { - errorMsg = 'Please provide a thread title'; + errorMsg = t('group:provide_thread', { + postProcess: 'capitalize', + }); } if (!name) { - errorMsg = 'Cannot send a message without a access to your name'; + errorMsg = t('group:result.cannot.access_name', { + postProcess: 'capitalize', + }); } if (!groupInfo) { - errorMsg = 'Cannot access group information'; - } // TODO translate + errorMsg = t('group:result.cannot.group_info', { + postProcess: 'capitalize', + }); + } // if (!description) missingFields.push('subject') if (missingFields.length > 0) { const missingFieldsString = missingFields.join(', '); const errMsg = `Missing: ${missingFieldsString}`; - errorMsg = errMsg; + errorMsg = errMsg; // TODO translate } if (errorMsg) { From d627c6a1427ad5c1f79811b1e6d695fc07fbabbc Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 24 Apr 2025 09:12:33 +0200 Subject: [PATCH 02/33] Add language selector --- i18n.js | 11 ++- src/App.tsx | 2 + src/ExtStates/NotAuthenticated.tsx | 2 + src/components/Apps/AppsHomeDesktop.tsx | 2 + src/components/DesktopSideBar.tsx | 2 + src/components/Language/LanguageSelector.tsx | 73 ++++++++++++++++++++ src/components/Theme/ThemeSelector.tsx | 2 +- 7 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 src/components/Language/LanguageSelector.tsx diff --git a/i18n.js b/i18n.js index 376f28d..12d8a9d 100644 --- a/i18n.js +++ b/i18n.js @@ -19,6 +19,15 @@ const capitalize = { }, }; +export const supportedLanguages = { + de: { name: 'Deutsch', flag: '🇩🇪' }, + en: { name: 'English', flag: '🇬🇧' }, + es: { name: 'Español', flag: '🇪🇸' }, + fr: { name: 'Français', flag: '🇫🇷' }, + it: { name: 'Italiano', flag: '🇮🇹' }, + ru: { name: 'Русский', flag: '🇷🇺' }, +}; + i18n .use(HttpApi) .use(LanguageDetector) @@ -43,7 +52,7 @@ i18n }, lng: navigator.language, ns: ['auth', 'core', 'group', 'tutorial'], - supportedLngs: ['en', 'it', 'es', 'fr', 'de', 'ru'], + supportedLngs: Object.keys(supportedLanguages), }); export default i18n; diff --git a/src/App.tsx b/src/App.tsx index ba57d99..cfefb34 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -137,6 +137,7 @@ import { GeneralNotifications } from './components/GeneralNotifications'; import { PdfViewer } from './common/PdfViewer'; import ThemeSelector from './components/Theme/ThemeSelector.tsx'; import { useTranslation } from 'react-i18next'; +import LanguageSelector from './components/Language/LanguageSelector.tsx'; type extStates = | 'not-authenticated' @@ -3704,6 +3705,7 @@ function App() { /> )} + ); diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index 28892a9..2574f96 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -31,6 +31,7 @@ import { GlobalContext } from '../App'; import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip'; import ThemeSelector from '../components/Theme/ThemeSelector'; import { useTranslation } from 'react-i18next'; +import LanguageSelector from '../components/Language/LanguageSelector'; const manifestData = { version: '0.5.3', @@ -1097,6 +1098,7 @@ export const NotAuthenticated = ({ /> + ); diff --git a/src/components/Apps/AppsHomeDesktop.tsx b/src/components/Apps/AppsHomeDesktop.tsx index d525c26..c77865c 100644 --- a/src/components/Apps/AppsHomeDesktop.tsx +++ b/src/components/Apps/AppsHomeDesktop.tsx @@ -15,6 +15,7 @@ import { extractComponents } from '../Chat/MessageDisplay'; import ArrowOutwardIcon from '@mui/icons-material/ArrowOutward'; import { AppsPrivate } from './AppsPrivate'; import ThemeSelector from '../Theme/ThemeSelector'; +import LanguageSelector from '../Language/LanguageSelector'; export const AppsHomeDesktop = ({ setMode, @@ -157,6 +158,7 @@ export const AppsHomeDesktop = ({ /> + ); diff --git a/src/components/DesktopSideBar.tsx b/src/components/DesktopSideBar.tsx index b25d206..53937ae 100644 --- a/src/components/DesktopSideBar.tsx +++ b/src/components/DesktopSideBar.tsx @@ -8,6 +8,7 @@ import { enabledDevModeAtom } from '../atoms/global'; import { AppsIcon } from '../assets/Icons/AppsIcon'; import ThemeSelector from './Theme/ThemeSelector'; import { CoreSyncStatus } from './CoreSyncStatus'; +import LanguageSelector from './Language/LanguageSelector'; export const DesktopSideBar = ({ goToHome, @@ -139,6 +140,7 @@ export const DesktopSideBar = ({ )} + ); diff --git a/src/components/Language/LanguageSelector.tsx b/src/components/Language/LanguageSelector.tsx new file mode 100644 index 0000000..b89836d --- /dev/null +++ b/src/components/Language/LanguageSelector.tsx @@ -0,0 +1,73 @@ +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { supportedLanguages } from '../../../i18n'; +import { Tooltip } from '@mui/material'; + +const LanguageSelector = () => { + const { i18n } = useTranslation(); + const [showSelect, setShowSelect] = useState(false); + + const handleChange = (e) => { + const newLang = e.target.value; + i18n.changeLanguage(newLang); + setShowSelect(false); + }; + + const currentLang = i18n.language; + const { name, flag } = + supportedLanguages[currentLang] || supportedLanguages['en']; + + return ( +
+ + {showSelect ? ( + + ) : ( + + )} + +
+ ); +}; + +export default LanguageSelector; diff --git a/src/components/Theme/ThemeSelector.tsx b/src/components/Theme/ThemeSelector.tsx index 42b75c0..b962e1f 100644 --- a/src/components/Theme/ThemeSelector.tsx +++ b/src/components/Theme/ThemeSelector.tsx @@ -14,7 +14,7 @@ const ThemeSelector = () => { bottom: '1%', display: 'flex', gap: '12px', - left: '1.5vh', + left: '1.2vh', position: 'absolute', }} > From 501f35fa70a5b225cfd91f9891de26cf2d7f87e5 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 24 Apr 2025 09:25:59 +0200 Subject: [PATCH 03/33] Refactor structure --- public/locales/en/core.json | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/public/locales/en/core.json b/public/locales/en/core.json index c2089a9..ef134a7 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -1,9 +1,16 @@ { - "add": "add", - "cancel": "cancel", - "choose": "choose", - "close": "close", - "continue": "continue", + "action": { + "add": "add", + "cancel": "cancel", + "change": "change", + "choose": "choose", + "close": "close", + "continue": "continue", + "edit": "edit", + "export": "export", + "import": "import", + "logout": "logout" + }, "core": { "block_height": "block height", "information": "core information", @@ -11,12 +18,8 @@ "version": "core version" }, "description": "description", - "edit": "edit", - "export": "export", - "import": "import", "last_height": "last height", "loading": "loading...", - "logout": "logout", "minting_status": "minting status", "page": { "last": "last" From 7d45d54216c9a2cb079f77c0ffb9978e2a02f0eb Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 24 Apr 2025 09:26:31 +0200 Subject: [PATCH 04/33] Refactor structure of translation core file --- src/App.tsx | 4 ++-- src/ExtStates/NotAuthenticated.tsx | 16 ++++++++++------ src/components/QMailStatus.tsx | 1 + src/components/Save/Save.tsx | 4 ++-- src/components/Tutorials/Tutorials.tsx | 4 ++-- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index cfefb34..0d0460a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1557,7 +1557,7 @@ function App() { textTransform: 'uppercase', }} > - {t('core:logout')} + {t('core:action.logout')} } placement="left" @@ -3585,7 +3585,7 @@ function App() { I have read this request - + // TODO translate } /> )} diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index 2574f96..338a657 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -817,7 +817,7 @@ export const NotAuthenticated = ({ }} variant="contained" > - {t('core:choose', { postProcess: 'capitalize' })} + {t('core:action.choose', { postProcess: 'capitalize' })} @@ -876,7 +876,9 @@ export const NotAuthenticated = ({ }} variant="contained" > - {t('core:choose', { postProcess: 'capitalize' })} + {t('core:action.choose', { + postProcess: 'capitalize', + })} )} @@ -954,7 +958,7 @@ export const NotAuthenticated = ({ }} autoFocus > - {t('core:close', { postProcess: 'capitalize' })} + {t('core:action.close', { postProcess: 'capitalize' })} )} @@ -1076,7 +1080,7 @@ export const NotAuthenticated = ({ setShowSelectApiKey(false); }} > - {t('core:close', { postProcess: 'capitalize' })} + {t('core:action.close', { postProcess: 'capitalize' })} diff --git a/src/components/QMailStatus.tsx b/src/components/QMailStatus.tsx index dd6f9be..b733261 100644 --- a/src/components/QMailStatus.tsx +++ b/src/components/QMailStatus.tsx @@ -62,6 +62,7 @@ export const QMailStatus = () => { color: theme.palette.text.primary, fontSize: '14px', fontWeight: 700, + textTransform: 'uppercase', }} > {t('core:q_mail', { diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index f0fbc4f..3835c3e 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -569,7 +569,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { } }} > - {t('core:import', { + {t('core:action.import', { postProcess: 'capitalize', })} @@ -594,7 +594,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { } }} > - {t('core:export', { + {t('core:action.export', { postProcess: 'capitalize', })} diff --git a/src/components/Tutorials/Tutorials.tsx b/src/components/Tutorials/Tutorials.tsx index 06274e7..522324f 100644 --- a/src/components/Tutorials/Tutorials.tsx +++ b/src/components/Tutorials/Tutorials.tsx @@ -91,7 +91,7 @@ export const Tutorials = () => { @@ -138,7 +138,7 @@ export const Tutorials = () => { From 6b1e15a58df4b17304d547997714cc7a2777fbf1 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 24 Apr 2025 20:15:13 +0200 Subject: [PATCH 05/33] Add some translations --- public/locales/en/core.json | 17 +++++- src/components/Group/Forum/NewThread.tsx | 1 + .../Group/Forum/ShowMessageWithoutModal.tsx | 9 ++- src/components/Group/Forum/Thread.tsx | 59 ++++++++++--------- 4 files changed, 51 insertions(+), 35 deletions(-) diff --git a/public/locales/en/core.json b/public/locales/en/core.json index ef134a7..d2caa8f 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -18,14 +18,25 @@ "version": "core version" }, "description": "description", + "page": { + "last": "last", + "first": "first", + "previous": "previous" + }, + "downloading_qdn": "downloading from QDN", + "edit": "edit", + "export": "export", + "import": "import", "last_height": "last height", "loading": "loading...", + "loading_posts": "loading posts... please wait.", + "logout": "logout", "minting_status": "minting status", - "page": { - "last": "last" - }, + "next": "next", "payment_notification": "payment notification", "price": "price", + "refetch_page": "refetch page", + "return_to_thread": "return to Threads", "q_mail": "q-mail", "result": { "error": { diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index 3909686..f83aed9 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -193,6 +193,7 @@ export const NewThread = ({ postProcess: 'capitalize', }); } + if (!groupInfo) { errorMsg = t('group:result.cannot.group_info', { postProcess: 'capitalize', diff --git a/src/components/Group/Forum/ShowMessageWithoutModal.tsx b/src/components/Group/Forum/ShowMessageWithoutModal.tsx index 7a4e594..914b9c5 100644 --- a/src/components/Group/Forum/ShowMessageWithoutModal.tsx +++ b/src/components/Group/Forum/ShowMessageWithoutModal.tsx @@ -3,7 +3,6 @@ import { Avatar, Box, IconButton } from '@mui/material'; import DOMPurify from 'dompurify'; import FormatQuoteIcon from '@mui/icons-material/FormatQuote'; import MoreSVG from '../../../assets/svgs/More.svg'; - import { MoreImg, MoreP, @@ -38,16 +37,16 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => { > @@ -67,6 +66,7 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => { {message?.name?.charAt(0)} + { > {message?.name} + {formatTimestampForum(message?.created)} @@ -205,6 +206,7 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => { > {message?.reply?.name?.charAt(0)} + { + diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index 2bf2318..2d44b98 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -1,20 +1,11 @@ import React, { - FC, useCallback, useEffect, useMemo, useRef, useState, } from 'react'; -import { - Avatar, - Box, - Button, - ButtonBase, - IconButton, - Skeleton, - Typography, -} from '@mui/material'; +import { Avatar, Box, Button, ButtonBase, Typography } from '@mui/material'; import { ShowMessage } from './ShowMessageWithoutModal'; import { ComposeP, @@ -51,8 +42,12 @@ import { RequestQueueWithPromise } from '../../../utils/queue/queue'; import { CustomLoader } from '../../../common/CustomLoader'; import { WrapperUserAction } from '../../WrapperUserAction'; import { formatTimestampForum } from '../../../utils/time'; +import { useTranslation } from 'react-i18next'; + const requestQueueSaveToLocal = new RequestQueueWithPromise(1); + const requestQueueDownloadPost = new RequestQueueWithPromise(3); + interface ThreadProps { currentThread: any; groupInfo: any; @@ -120,6 +115,7 @@ export const Thread = ({ const [isLoading, setIsLoading] = useState(true); const [postReply, setPostReply] = useState(null); const [hasLastPage, setHasLastPage] = useState(false); + const { t } = useTranslation(['core']); // Update: Use a new ref for the scrollable container const threadContainerRef = useRef(null); @@ -251,6 +247,7 @@ export const Thread = ({ 'Content-Type': 'application/json', }, }); + const responseData = await response.json(); let fullArrayMsg = [...responseData]; @@ -431,6 +428,7 @@ export const Thread = ({ } const newArray = responseData.slice(0, findMessage).reverse(); let fullArrayMsg = [...messages]; + for (const message of newArray) { try { const responseDataMessage = await getEncryptedResource({ @@ -468,7 +466,6 @@ export const Thread = ({ setMessages(fullArrayMsg); } catch (error) { console.log(error); - } finally { } }, [messages] @@ -565,20 +562,20 @@ export const Thread = ({ return ( - Return to Threads + + {t('core:return_to_thread', { postProcess: 'capitalize' })} + {/* Conditionally render the scroll buttons */} {showScrollButton && @@ -658,6 +657,7 @@ export const Thread = ({ > {currentThread?.threadData?.title} + - First + {t(core:page.first', { postProcess: 'capitalize' })} + @@ -925,7 +926,7 @@ export const Thread = ({ color: 'white', }} > - Downloading from QDN + {t('core:downloading_qdn', { postProcess: 'capitalize' })} @@ -959,7 +960,7 @@ export const Thread = ({ color: 'white', }} > - Refetch page + {t('core:refetch_page', { postProcess: 'capitalize' })} @@ -997,7 +998,7 @@ export const Thread = ({ disabled={!hasFirstPage} variant="contained" > - First + {t(core:page.first', { postProcess: 'capitalize' })} @@ -1063,7 +1064,7 @@ export const Thread = ({ From 02ac18767514b7903a4172094dbb30ea94545e6a Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 24 Apr 2025 20:32:00 +0200 Subject: [PATCH 06/33] Add translations to app --- public/locales/en/core.json | 24 ++++++++----- src/App.tsx | 51 +++++++++++++++++++-------- src/components/Group/AddGroup.tsx | 15 +++++--- src/components/Group/Forum/Thread.tsx | 4 +-- 4 files changed, 64 insertions(+), 30 deletions(-) diff --git a/public/locales/en/core.json b/public/locales/en/core.json index d2caa8f..f2191be 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -1,15 +1,20 @@ { "action": { "add": "add", + "accept": "accept", "cancel": "cancel", "change": "change", "choose": "choose", "close": "close", "continue": "continue", + "continue_logout": "continue to logout", + "decline": "decline", "edit": "edit", "export": "export", "import": "import", - "logout": "logout" + "logout": "logout", + "refetch_page": "refetch page", + "return_to_thread": "return to threads" }, "core": { "block_height": "block height", @@ -18,25 +23,23 @@ "version": "core version" }, "description": "description", + "fee": { + "payment": "payment fee", + "publish": "publish fee" + }, "page": { "last": "last", "first": "first", "previous": "previous" }, "downloading_qdn": "downloading from QDN", - "edit": "edit", - "export": "export", - "import": "import", "last_height": "last height", "loading": "loading...", "loading_posts": "loading posts... please wait.", - "logout": "logout", "minting_status": "minting status", "next": "next", "payment_notification": "payment notification", "price": "price", - "refetch_page": "refetch page", - "return_to_thread": "return to Threads", "q_mail": "q-mail", "result": { "error": { @@ -51,9 +54,14 @@ "synchronizing": "synchronizing" }, "success": { - "publish_qdn": "successfully published to QDN" + "group_creation": "successfully created group. It may take a couple of minutes for the changes to propagate", + "publish_qdn": "successfully published to QDN", + "request_read": "I have read this request" } }, + "question": { + "perform_create_group": "would you like to perform an CREATE_GROUP transaction?" + }, "save_options": { "no_pinned_changes": "you currently do not have any changes to your pinned apps", "overwrite_changes": "the app was unable to download your existing QDN-saved pinned apps. Would you like to overwrite those changes?", diff --git a/src/App.tsx b/src/App.tsx index 0d0460a..e74f168 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2079,7 +2079,6 @@ function App() { /> )} - {isShowQortalRequest && !isMainWindow && ( <> @@ -2362,7 +2361,6 @@ function App() { {sendPaymentError} )} - {extState === 'web-app-request-payment' && !isMainWindow && ( <> @@ -3176,8 +3174,9 @@ function App() { > Close - + // TODO translate )} + {countdown && ( {message?.paymentFee && ( - payment fee: {message.paymentFee} + {t('core:fee.payment', { + postProcess: 'capitalize', + })} + : {message.paymentFee} )} {message?.publishFee && ( - publish fee: {message.publishFee} + {t('core:fee.publish', { + postProcess: 'capitalize', + })} + : {message.publishFee} )} @@ -3247,7 +3252,9 @@ function App() { onClick={onOk} autoFocus > - accept + {t('core:action.accept', { + postProcess: 'capitalize', + })} @@ -3285,7 +3294,9 @@ function App() { @@ -3304,14 +3315,18 @@ function App() { @@ -3582,10 +3597,12 @@ function App() { label={ - I have read this request + {t('core:result.success.request_read', { + postProcess: 'capitalize', + })} - // TODO translate + } /> )} @@ -3593,8 +3610,8 @@ function App() { @@ -3630,7 +3647,9 @@ function App() { onOkQortalRequestExtension('accepted'); }} > - accept + {t('core:action.accept', { + postProcess: 'capitalize', + })} onCancelQortalRequestExtension()} > - decline + {t('core:action.decline', { + postProcess: 'capitalize', + })} {sendPaymentError} diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index 91552cb..2358d3d 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -28,6 +28,7 @@ import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { getFee } from '../../background'; import { MyContext } from '../../App'; import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; +import { useTranslation } from 'react-i18next'; export const Label = styled('label')` display: block; @@ -84,6 +85,7 @@ export const AddGroup = ({ address, open, setOpen }) => { setMaxBlock(event.target.value as string); }; + const { t } = useTranslation(['core']); const theme = useTheme(); const handleCreateGroup = async () => { @@ -91,9 +93,11 @@ export const AddGroup = ({ address, open, setOpen }) => { if (!name) throw new Error('Please provide a name'); if (!description) throw new Error('Please provide a description'); - const fee = await getFee('CREATE_GROUP'); // TODO translate + const fee = await getFee('CREATE_GROUP'); await show({ - message: 'Would you like to perform an CREATE_GROUP transaction?', + message: t('core:question.perform_create_group', { + postProcess: 'capitalize', + }), publishFee: fee.fee + ' QORT', }); @@ -111,10 +115,11 @@ export const AddGroup = ({ address, open, setOpen }) => { if (!response?.error) { setInfoSnack({ type: 'success', - message: - 'Successfully created group. It may take a couple of minutes for the changes to propagate', + message: t('core:result.success.group_creation', { + postProcess: 'capitalize', + }), }); - setOpenSnack(true); + setOpenSnack(true); // TODO translate setTxList((prev) => [ { ...response, diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index 2d44b98..476db83 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -608,7 +608,7 @@ export const Thread = ({ > - {t('core:return_to_thread', { postProcess: 'capitalize' })} + {t('core:action.return_to_thread', { postProcess: 'capitalize' })} {/* Conditionally render the scroll buttons */} @@ -960,7 +960,7 @@ export const Thread = ({ color: 'white', }} > - {t('core:refetch_page', { postProcess: 'capitalize' })} + {t('core:action.refetch_page', { postProcess: 'capitalize' })} From b0f0ec57a98a11b3492c18bdecf0d1157931d520 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 24 Apr 2025 21:33:04 +0200 Subject: [PATCH 07/33] Add translations to App --- public/locales/en/core.json | 7 ++-- src/App.tsx | 65 ++++++++++++++++--------------------- 2 files changed, 33 insertions(+), 39 deletions(-) diff --git a/public/locales/en/core.json b/public/locales/en/core.json index f2191be..bb56264 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -2,6 +2,8 @@ "action": { "add": "add", "accept": "accept", + "backup_account": "backup account", + "backup_wallet": "backup wallet", "cancel": "cancel", "change": "change", "choose": "choose", @@ -55,8 +57,10 @@ }, "success": { "group_creation": "successfully created group. It may take a couple of minutes for the changes to propagate", + "order_submitted": "your buy order was submitted", "publish_qdn": "successfully published to QDN", - "request_read": "I have read this request" + "request_read": "I have read this request", + "transfer": "the transfer was succesful!" } }, "question": { @@ -88,7 +92,6 @@ "tutorial": "tutorial", "user_lookup": "user lookup", "wallet": { - "backup_wallet": "backup wallet", "wallet": "wallet", "wallet_other": "wallets" }, diff --git a/src/App.tsx b/src/App.tsx index e74f168..38b8337 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -257,14 +257,7 @@ export const getBaseApiReact = (customApi?: string) => { return groupApi; } }; -// export const getArbitraryEndpointReact = () => { -// if (globalApiKey) { -// return `/arbitrary/resources/search`; -// } else { -// return `/arbitrary/resources/searchsimple`; -// } -// }; export const getArbitraryEndpointReact = () => { if (globalApiKey) { return `/arbitrary/resources/searchsimple`; @@ -538,7 +531,7 @@ function App() { console.error( 'Failed to get API key:', error?.message || 'An error occurred' - ); + ); // TODO translate }) .finally(() => { window @@ -593,26 +586,6 @@ function App() { isFocusedRef.current = isFocused; }, [isFocused]); - // const checkIfUserHasLocalNode = useCallback(async () => { - // try { - // const url = `http://127.0.0.1:12391/admin/status`; - // const response = await fetch(url, { - // method: "GET", - // headers: { - // "Content-Type": "application/json", - // }, - // }); - // const data = await response.json(); - // if (data?.isSynchronizing === false && data?.syncPercent === 100) { - // setHasLocalNode(true); - // } - // } catch (error) {} - // }, []); - - // useEffect(() => { - // checkIfUserHasLocalNode(); - // }, [extState]); - const address = useMemo(() => { if (!rawWallet?.address0) return ''; return rawWallet.address0; @@ -1050,7 +1023,7 @@ function App() { await showUnsavedChanges({ message: 'Your settings have changed. If you logout you will lose your changes. Click on the save button in the header to keep your changed settings.', - }); + }); // TODO translate } else if (extState === 'authenticated') { await showUnsavedChanges({ message: 'Are you sure you would like to logout?', @@ -1354,19 +1327,24 @@ function App() { )} + {authenticatedMode === 'ltc' ? ( <> + + {rawWallet?.ltcAddress?.slice(0, 6)}... {rawWallet?.ltcAddress?.slice(-4)} + + {ltcBalanceLoading && ( )} @@ -1388,6 +1366,7 @@ function App() { > {ltcBalance} LTC + + + {userInfo?.name} + + {rawWallet?.address0?.slice(0, 6)}... @@ -1912,7 +1895,7 @@ function App() { textTransform: 'uppercase', }} > - {t('core:backup_wallet')} + {t('core:action.backup_wallet')} } placement="left" @@ -3090,7 +3073,9 @@ function App() { }); }} > - Backup Account + {t('core:action.backup_account', { + postProcess: 'capitalize', + })} )} @@ -3118,7 +3103,9 @@ function App() { lineHeight: '15px', }} > - The transfer was succesful! + {t('core:result.success.transfer', { + postProcess: 'capitalize', + })} - Continue + {t('core:action.continue', { postProcess: 'capitalize' })} )} @@ -3141,7 +3128,9 @@ function App() { lineHeight: '15px', }} > - The transfer was succesful! + {t('core:result.success.transfer', { + postProcess: 'capitalize', + })} - Continue + {t('core:action.continue', { postProcess: 'capitalize' })} )} @@ -3164,7 +3153,9 @@ function App() { lineHeight: '15px', }} > - Your buy order was submitted + {t('core:result.success.order_submitted', { + postProcess: 'capitalize', + })} - Close + {t('core:action.close', { postProcess: 'capitalize' })} // TODO translate )} From 230cc25a3b00bfaa730aa9fe9c32adbb396b3004 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Thu, 24 Apr 2025 21:45:42 +0200 Subject: [PATCH 08/33] Add translation to group --- public/locales/en/core.json | 3 +-- public/locales/en/group.json | 9 +++++++-- src/components/Group/Forum/GroupMail.tsx | 4 +++- src/components/Group/Forum/Thread.tsx | 2 +- src/components/Group/Group.tsx | 4 ++-- src/components/Group/GroupInvites.tsx | 16 ++++++++-------- 6 files changed, 22 insertions(+), 16 deletions(-) diff --git a/public/locales/en/core.json b/public/locales/en/core.json index bb56264..576f952 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -15,8 +15,7 @@ "export": "export", "import": "import", "logout": "logout", - "refetch_page": "refetch page", - "return_to_thread": "return to threads" + "refetch_page": "refetch page" }, "core": { "block_height": "block height", diff --git a/public/locales/en/group.json b/public/locales/en/group.json index 453c1f6..21d11c4 100644 --- a/public/locales/en/group.json +++ b/public/locales/en/group.json @@ -1,12 +1,17 @@ { + "action": { + "return_to_thread": "return to threads" + }, + "group_invites": "group invites", "provide_thread": "please provide a thread title", "result": { "cannot": { "access_name": "Cannot send a message without a access to your name", "group_info": "Cannot access group information" - } + }, + "loading_threads": "loading threads... please wait." }, - "": "", + "": "", "": "", "": "", diff --git a/src/components/Group/Forum/GroupMail.tsx b/src/components/Group/Forum/GroupMail.tsx index 0de9921..c575cf1 100644 --- a/src/components/Group/Forum/GroupMail.tsx +++ b/src/components/Group/Forum/GroupMail.tsx @@ -121,7 +121,9 @@ export const GroupMail = ({ }); setTempPublishedList(tempData); } - } catch (error) {} + } catch (error) { + console.log(error); + } }; const getEncryptedResource = async ( diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index 476db83..1a4075e 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -608,7 +608,7 @@ export const Thread = ({ > - {t('core:action.return_to_thread', { postProcess: 'capitalize' })} + {t('group:action.return_to_thread', { postProcess: 'capitalize' })} {/* Conditionally render the scroll buttons */} diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index a4629ce..f0eff94 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -55,8 +55,6 @@ import { RequestQueueWithPromise } from '../../utils/queue/queue'; import { WebSocketActive } from './WebsocketActive'; import { useMessageQueue } from '../../MessageQueueContext'; import { ContextMenu } from '../ContextMenu'; -import { ReturnIcon } from '../../assets/Icons/ReturnIcon'; -import { ExitIcon } from '../../assets/Icons/ExitIcon'; import { HomeDesktop } from './HomeDesktop'; import { IconWrapper } from '../Desktop/DesktopFooter'; import { DesktopHeader } from '../Desktop/DesktopHeader'; @@ -80,6 +78,7 @@ import LockIcon from '@mui/icons-material/Lock'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; import { BlockedUsersModal } from './BlockedUsersModal'; import { WalletsAppWrapper } from './WalletsAppWrapper'; +import { useTranslation } from 'react-i18next'; export const getPublishesFromAdmins = async (admins: string[], groupId) => { const queryString = admins.map((name) => `name=${name}`).join('&'); @@ -450,6 +449,7 @@ export const Group = ({ const [isOpenSideViewGroups, setIsOpenSideViewGroups] = useState(false); const [isForceShowCreationKeyPopup, setIsForceShowCreationKeyPopup] = useState(false); + const { t } = useTranslation(['core', 'group']); const [groupsProperties, setGroupsProperties] = useRecoilState(groupsPropertiesAtom); diff --git a/src/components/Group/GroupInvites.tsx b/src/components/Group/GroupInvites.tsx index ecbf88a..c753fdb 100644 --- a/src/components/Group/GroupInvites.tsx +++ b/src/components/Group/GroupInvites.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import { useEffect, useState } from 'react'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import ListItemButton from '@mui/material/ListItemButton'; @@ -12,14 +12,13 @@ import { CustomLoader } from '../../common/CustomLoader'; import { getBaseApiReact } from '../../App'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import { useTranslation } from 'react-i18next'; export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { - const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState( - [] - ); - const [isExpanded, setIsExpanded] = React.useState(false); + const [groupsWithJoinRequests, setGroupsWithJoinRequests] = useState([]); + const [isExpanded, setIsExpanded] = useState(false); - const [loading, setLoading] = React.useState(true); + const [loading, setLoading] = useState(true); const getJoinRequests = async () => { try { @@ -38,9 +37,10 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { } }; + const { t } = useTranslation(['core', 'group']); const theme = useTheme(); - React.useEffect(() => { + useEffect(() => { if (myAddress) { getJoinRequests(); } @@ -71,7 +71,7 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { fontSize: '1rem', }} // TODO translate > - Group Invites{' '} + {t('group:group_invites', { postProcess: 'capitalize' })}{' '} {groupsWithJoinRequests?.length > 0 && ` (${groupsWithJoinRequests?.length})`} From 340fe551d6e1901ebefba341497e164a1f3e847c Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 25 Apr 2025 09:05:32 +0200 Subject: [PATCH 09/33] Add translation to language selector --- public/locales/en/core.json | 1 + src/components/Group/Forum/Thread.tsx | 16 ++++++----- src/components/Language/LanguageSelector.tsx | 29 +++++++++++--------- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/public/locales/en/core.json b/public/locales/en/core.json index 576f952..1b6529f 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -6,6 +6,7 @@ "backup_wallet": "backup wallet", "cancel": "cancel", "change": "change", + "change_language": "change language", "choose": "choose", "close": "close", "continue": "continue", diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index 1a4075e..9472443 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -608,7 +608,9 @@ export const Thread = ({ > - {t('group:action.return_to_thread', { postProcess: 'capitalize' })} + {t('group:action.return_to_thread', { + postProcess: 'capitalize', + })} {/* Conditionally render the scroll buttons */} @@ -685,7 +687,7 @@ export const Thread = ({ disabled={!hasFirstPage} variant="contained" > - {t(core:page.first', { postProcess: 'capitalize' })} + {t('core:page.first', { postProcess: 'capitalize' })} @@ -998,7 +1000,7 @@ export const Thread = ({ disabled={!hasFirstPage} variant="contained" > - {t(core:page.first', { postProcess: 'capitalize' })} + {t('core:page.first', { postProcess: 'capitalize' })} diff --git a/src/components/Language/LanguageSelector.tsx b/src/components/Language/LanguageSelector.tsx index b89836d..6241347 100644 --- a/src/components/Language/LanguageSelector.tsx +++ b/src/components/Language/LanguageSelector.tsx @@ -1,11 +1,12 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { supportedLanguages } from '../../../i18n'; -import { Tooltip } from '@mui/material'; +import { Tooltip, useTheme } from '@mui/material'; const LanguageSelector = () => { - const { i18n } = useTranslation(); + const { i18n, t } = useTranslation(['core']); const [showSelect, setShowSelect] = useState(false); + const theme = useTheme(); const handleChange = (e) => { const newLang = e.target.value; @@ -28,19 +29,21 @@ const LanguageSelector = () => { }} > {showSelect ? ( setName(e.target.value)} /> @@ -303,10 +326,16 @@ export const AddGroup = ({ address, open, setOpen }) => { gap: '5px', }} > - + setDescription(e.target.value)} /> @@ -318,7 +347,13 @@ export const AddGroup = ({ address, open, setOpen }) => { gap: '5px', }} > - + + @@ -341,7 +382,11 @@ export const AddGroup = ({ address, open, setOpen }) => { }} onClick={() => setOpenAdvance((prev) => !prev)} > - Advanced options + + {t('group:advanced_options', { + postProcess: 'capitalize', + })} + {openAdvance ? : } @@ -355,8 +400,9 @@ export const AddGroup = ({ address, open, setOpen }) => { }} > { }} > { }} > @@ -449,7 +552,9 @@ export const AddGroup = ({ address, open, setOpen }) => { color="primary" onClick={handleCreateGroup} > - Create Group + {t('group.action.create', { + postProcess: 'capitalize', + })} diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index f83aed9..6f0c466 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -183,19 +183,19 @@ export const NewThread = ({ const missingFields: string[] = []; if (!isMessage && !threadTitle) { - errorMsg = t('group:provide_thread', { + errorMsg = t('group:question.provide_thread', { postProcess: 'capitalize', }); } if (!name) { - errorMsg = t('group:result.cannot.access_name', { + errorMsg = t('group:result.error.access_name', { postProcess: 'capitalize', }); } if (!groupInfo) { - errorMsg = t('group:result.cannot.group_info', { + errorMsg = t('group:result.error.group_info', { postProcess: 'capitalize', }); } From 7f2102e5e09c37d120d58f4bd6b5a06d5df37ffa Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 07:41:07 +0200 Subject: [PATCH 15/33] Add missing translation --- public/locales/en/group.json | 6 ++++-- src/components/Group/AddGroup.tsx | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/public/locales/en/group.json b/public/locales/en/group.json index 3bc248e..4333ff6 100644 --- a/public/locales/en/group.json +++ b/public/locales/en/group.json @@ -25,8 +25,10 @@ }, "result": { "error": { - "access_name": "Cannot send a message without a access to your name", - "group_info": "Cannot access group information" + "access_name": "cannot send a message without a access to your name", + "description_required": "please provide a description", + "group_info": "cannot access group information", + "name_required": "please provide a name" }, "loading_threads": "loading threads... please wait.", "success": { diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index 6b45d00..a82dfb1 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -98,8 +98,18 @@ export const AddGroup = ({ address, open, setOpen }) => { const handleCreateGroup = async () => { try { - if (!name) throw new Error('Please provide a name'); - if (!description) throw new Error('Please provide a description'); + if (!name) + throw new Error( + t('group:result.error.name_required', { + postProcess: 'capitalize', + }) + ); + if (!description) + throw new Error( + t('group:result.error.description_required', { + postProcess: 'capitalize', + }) + ); const fee = await getFee('CREATE_GROUP'); @@ -128,7 +138,7 @@ export const AddGroup = ({ address, open, setOpen }) => { postProcess: 'capitalize', }), }); - setOpenSnack(true); // TODO translate + setOpenSnack(true); setTxList((prev) => [ { ...response, From 8a653d9c155b4222feef14cfd54fa2f8a0dafd58 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 07:50:16 +0200 Subject: [PATCH 16/33] Add translations --- public/locales/en/auth.json | 5 +++++ src/ExtStates/NotAuthenticated.tsx | 23 ++++++----------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/public/locales/en/auth.json b/public/locales/en/auth.json index 26cab11..ebc4fa8 100644 --- a/public/locales/en/auth.json +++ b/public/locales/en/auth.json @@ -29,6 +29,11 @@ "password": "password", "password_confirmation": "confirm password", "return_to_list": "return to list", + "tips": { + "digital_id": "your wallet is like your digital ID on Qortal, and is how you will login to the Qortal User Interface. It holds your public address and the Qortal name you will eventually choose. Every transaction you make is linked to your ID, and this is where you manage all your QORT and other tradeable cryptocurrencies on Qortal.", + "new_account": "creating an account means creating a new wallet and digital ID to start using Qortal. Once you have made your account, you can start doing things like obtaining some QORT, buying a name and avatar, publishing videos and blogs, and much more.", + "new_users": "new users start here!" + }, "wallet": { "password_confirmation": "confirm wallet password", "password": "wallet password" diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index 338a657..2e3d598 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -511,14 +511,8 @@ export const NotAuthenticated = ({ fontSize: '16px', }} > - Your wallet is like your digital ID on Qortal, and is how you - will login to the Qortal User Interface. It holds your public - address and the Qortal name you will eventually choose. Every - transaction you make is linked to your ID, and this is where you - manage all your QORT and other tradeable cryptocurrencies on - Qortal. - {' '} - // TODO translate + {t('auth:tips.digital_id', { postProcess: 'capitalize' })} + } > @@ -548,9 +542,8 @@ export const NotAuthenticated = ({ fontSize: '18px', }} > - New users start here! - {' '} - // TODO translate + {t('auth:tips.new_users', { postProcess: 'capitalize' })} + - Creating an account means creating a new wallet and digital ID - to start using Qortal. Once you have made your account, you can - start doing things like obtaining some QORT, buying a name and - avatar, publishing videos and blogs, and much more. - {' '} - // TODO translate + {t('auth:tips.new_account', { postProcess: 'capitalize' })} + } > From 629aa8f3f59b0005f0b38b385be456c0c184b08f Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 07:59:26 +0200 Subject: [PATCH 17/33] Remove commented code --- src/components/Group/ThingsToDoInitial.tsx | 52 ---------------------- 1 file changed, 52 deletions(-) diff --git a/src/components/Group/ThingsToDoInitial.tsx b/src/components/Group/ThingsToDoInitial.tsx index 6c315cd..84c806b 100644 --- a/src/components/Group/ThingsToDoInitial.tsx +++ b/src/components/Group/ThingsToDoInitial.tsx @@ -141,21 +141,6 @@ export const ThingsToDoInitial = ({ outline: '1px solid rgba(9, 182, 232, 1)', }} /> - {/* */} @@ -163,15 +148,6 @@ export const ThingsToDoInitial = ({ sx={{ marginBottom: '20px', }} - // secondaryAction={ - // - // - // - // } disablePadding > - {/* - - - - - - - - */} )} From 0e3c18b21a919f48834ad8f921ecdd5dbb51a552 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 08:00:19 +0200 Subject: [PATCH 18/33] Remove unused function --- src/components/Group/Settings.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index fd2f9c2..f5a952c 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -11,13 +11,6 @@ import { Box, FormControlLabel, Switch, styled, useTheme } from '@mui/material'; import { enabledDevModeAtom } from '../../atoms/global'; import { useRecoilState } from 'recoil'; -function a11yProps(index: number) { - return { - id: `simple-tab-${index}`, - 'aria-controls': `simple-tabpanel-${index}`, - }; -} - const LocalNodeSwitch = styled(Switch)(({ theme }) => ({ padding: 8, '& .MuiSwitch-track': { From 2327efa537ba63c878028e077e7e1b688b6cf9aa Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 08:00:29 +0200 Subject: [PATCH 19/33] Format code --- src/components/Group/WalletsAppWrapper.tsx | 2 +- src/components/Group/WebsocketActive.tsx | 1 - src/components/Group/useBlockUsers.tsx | 8 +++++++- src/components/Group/useHandleUserInfo.tsx | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/Group/WalletsAppWrapper.tsx b/src/components/Group/WalletsAppWrapper.tsx index 111e216..c569425 100644 --- a/src/components/Group/WalletsAppWrapper.tsx +++ b/src/components/Group/WalletsAppWrapper.tsx @@ -88,7 +88,7 @@ export const WalletsAppWrapper = () => { justifyContent: 'space-between', }} > - Q-Wallets // TODO translate + Q-Wallets { directs: sortedDirects, }) .catch((error) => { - // TODO translate console.error( 'Failed to handle active group data from socket:', error.message || 'An error occurred' diff --git a/src/components/Group/useBlockUsers.tsx b/src/components/Group/useBlockUsers.tsx index 178c358..4165260 100644 --- a/src/components/Group/useBlockUsers.tsx +++ b/src/components/Group/useBlockUsers.tsx @@ -17,7 +17,7 @@ export const useBlockedAddresses = () => { if (userBlockedRef.current[address]) return true; return false; } catch (error) { - //error + console.log(error); } }, []); @@ -42,10 +42,13 @@ export const useBlockedAddresses = () => { console.error('Failed qortalRequest', error); }); }); + const blockedUsers = {}; + response?.forEach((item) => { blockedUsers[item] = true; }); + userBlockedRef.current = blockedUsers; const response2 = await new Promise((res, rej) => { @@ -66,10 +69,13 @@ export const useBlockedAddresses = () => { console.error('Failed qortalRequest', error); }); }); + const blockedUsers2 = {}; + response2?.forEach((item) => { blockedUsers2[item] = true; }); + userNamesBlockedRef.current = blockedUsers2; } catch (error) { console.error(error); diff --git a/src/components/Group/useHandleUserInfo.tsx b/src/components/Group/useHandleUserInfo.tsx index 622e737..da7ee13 100644 --- a/src/components/Group/useHandleUserInfo.tsx +++ b/src/components/Group/useHandleUserInfo.tsx @@ -22,7 +22,7 @@ export const useHandleUserInfo = () => { }; return data?.level; } catch (error) { - //error + console.log(error); } }, []); From db211ee2b9940ede5e5b897eec0689c0b635ecd2 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 09:10:06 +0200 Subject: [PATCH 20/33] Add translatios to addGroup --- public/locales/en/core.json | 1 + public/locales/en/group.json | 9 +++-- src/components/Group/AddGroup.tsx | 2 ++ src/components/Group/AddGroupList.tsx | 42 +++++++++++++++++------- src/components/Group/Forum/GroupMail.tsx | 4 ++- 5 files changed, 43 insertions(+), 15 deletions(-) diff --git a/public/locales/en/core.json b/public/locales/en/core.json index c640b7b..5b75a16 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -15,6 +15,7 @@ "edit": "edit", "export": "export", "import": "import", + "join": "join", "logout": "logout", "refetch_page": "refetch page" }, diff --git a/public/locales/en/group.json b/public/locales/en/group.json index 4333ff6..766b506 100644 --- a/public/locales/en/group.json +++ b/public/locales/en/group.json @@ -2,6 +2,7 @@ "action": { "create_group": "create group", "find_group": "find group", + "join_group": "join group", "return_to_thread": "return to threads" }, "advanced_options": "advanced options", @@ -21,6 +22,7 @@ }, "question": { "create_group": "would you like to perform an CREATE_GROUP transaction?", + "join_group": "would you like to perform an JOIN_GROUP transaction?", "provide_thread": "please provide a thread title" }, "result": { @@ -30,11 +32,14 @@ "group_info": "cannot access group information", "name_required": "please provide a name" }, - "loading_threads": "loading threads... please wait.", "success": { "group_creation": "successfully created group. It may take a couple of minutes for the changes to propagate", "group_creation_name": "created group {{group_name}}: awaiting confirmation", - "group_creation_label": "created group {{name}}: success!" + "group_creation_label": "created group {{name}}: success!", + "join_creation": "successfully requested to join group. It may take a couple of minutes for the changes to propagate", + "group_join_name": "joined group {{group_name}}: awaiting confirmation", + "group_join_label": "joined group {{name}}: success!", + "loading_threads": "loading threads... please wait." } } } diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index a82dfb1..2bdf81a 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -350,6 +350,7 @@ export const AddGroup = ({ address, open, setOpen }) => { onChange={(e) => setDescription(e.target.value)} /> + { + { const { memberGroups, show, setTxList } = useContext(MyContext); - + const { t } = useTranslation(['core', 'group']); const [groups, setGroups] = useState([]); const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open @@ -101,12 +102,17 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { const handleJoinGroup = async (group, isOpen) => { try { const groupId = group.groupId; - const fee = await getFee('JOIN_GROUP'); // TODO translate + + const fee = await getFee('JOIN_GROUP'); + await show({ - message: 'Would you like to perform an JOIN_GROUP transaction?', + message: t('group:question.join_group', { + postProcess: 'capitalize', + }), publishFee: fee.fee + ' QORT', }); setIsLoading(true); + await new Promise((res, rej) => { window .sendMessage('joinGroup', { @@ -116,8 +122,9 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { if (!response?.error) { setInfoSnack({ type: 'success', - message: - 'Successfully requested to join group. It may take a couple of minutes for the changes to propagate', + message: t('group:result.success.join_group', { + postProcess: 'capitalize', + }), }); if (isOpen) { @@ -125,8 +132,14 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { { ...response, type: 'joined-group', - label: `Joined Group ${group?.groupName}: awaiting confirmation`, - labelDone: `Joined Group ${group?.groupName}: success!`, + label: t('group:result.success.group_join_label', { + group_name: group?.groupName, + postProcess: 'capitalize', + }), + labelDone: t('group:result.success.group_join_label', { + group_name: group?.groupName, + postProcess: 'capitalize', + }), done: false, groupId, }, @@ -215,7 +228,10 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { padding: '10px', }} > - Join {group?.groupName} + + {t('core:action.join', { postProcess: 'capitalize' })}{' '} + {group?.groupName} + {group?.isOpen === false && 'This is a closed/private group, so you will need to wait until an admin accepts your request'} @@ -226,7 +242,9 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { variant="contained" onClick={() => handleJoinGroup(group, group?.isOpen)} > - Join group + {t('group:action.join_group', { + postProcess: 'capitalize', + })} diff --git a/src/components/Group/Forum/GroupMail.tsx b/src/components/Group/Forum/GroupMail.tsx index c575cf1..985644e 100644 --- a/src/components/Group/Forum/GroupMail.tsx +++ b/src/components/Group/Forum/GroupMail.tsx @@ -834,7 +834,9 @@ export const GroupMail = ({ From c734b8016cc37210d4ccd7998a9963d223770b76 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Wed, 23 Apr 2025 08:55:50 +0200 Subject: [PATCH 21/33] Simplify selector with a unique icon --- src/components/Theme/ThemeSelector.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Theme/ThemeSelector.tsx b/src/components/Theme/ThemeSelector.tsx index b962e1f..bab403c 100644 --- a/src/components/Theme/ThemeSelector.tsx +++ b/src/components/Theme/ThemeSelector.tsx @@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next'; const ThemeSelector = () => { const { t } = useTranslation(['core']); + const { themeMode, toggleTheme } = useThemeContext(); return ( From 9a5970731aed419d482fe5edb530bcf590110eb9 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 26 Apr 2025 12:33:55 +0300 Subject: [PATCH 22/33] remove full screen on double click --- src/App.tsx | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 38b8337..0f2d4b1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -102,7 +102,6 @@ import { useRecoilState, useResetRecoilState, useSetRecoilState } from 'recoil'; import { canSaveSettingToQdnAtom, enabledDevModeAtom, - fullScreenAtom, groupsPropertiesAtom, hasSettingsChangedAtom, isDisabledEditorEnterAtom, @@ -115,7 +114,6 @@ import { settingsQDNLastUpdatedAtom, sortablePinnedAppsAtom, } from './atoms/global'; -import { useAppFullScreen } from './useAppFullscreen'; import { NotAuthenticated } from './ExtStates/NotAuthenticated'; import { handleGetFileFromIndexedDB } from './utils/indexedDB'; import { Wallets } from './Wallets'; @@ -401,12 +399,10 @@ function App() { const qortalRequestCheckbox1Ref = useRef(null); useRetrieveDataLocalStorage(userInfo?.address); useQortalGetSaveSettings(userInfo?.name, extState === 'authenticated'); - const [fullScreen, setFullScreen] = useRecoilState(fullScreenAtom); const [isEnabledDevMode, setIsEnabledDevMode] = useRecoilState(enabledDevModeAtom); const setIsDisabledEditorEnter = useSetRecoilState(isDisabledEditorEnterAtom); const [isOpenMinting, setIsOpenMinting] = useState(false); - const { toggleFullScreen } = useAppFullScreen(setFullScreen); const generatorRef = useRef(null); const exportSeedphrase = () => { @@ -450,24 +446,6 @@ function App() { } }, [extState, walletToBeDownloaded, shownTutorialsInitiated]); - useEffect(() => { - // Attach a global event listener for double-click - const handleDoubleClick = () => { - toggleFullScreen(); - }; - - // Add the event listener to the root HTML document - document.documentElement.addEventListener('dblclick', handleDoubleClick); - - // Clean up the event listener on unmount - return () => { - document.documentElement.removeEventListener( - 'dblclick', - handleDoubleClick - ); - }; - }, [toggleFullScreen]); - //resets for recoil const resetAtomSortablePinnedAppsAtom = useResetRecoilState( sortablePinnedAppsAtom From a81910cf3d9b3d93324b7df34a516f983f262df0 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 26 Apr 2025 12:44:52 +0300 Subject: [PATCH 23/33] save theme to localstorage for persistance --- src/components/Theme/ThemeContext.tsx | 38 +++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/components/Theme/ThemeContext.tsx b/src/components/Theme/ThemeContext.tsx index 5a5abb8..d2e7048 100644 --- a/src/components/Theme/ThemeContext.tsx +++ b/src/components/Theme/ThemeContext.tsx @@ -1,4 +1,11 @@ -import { createContext, useContext, useState, useMemo } from 'react'; +import { + createContext, + useContext, + useState, + useMemo, + useEffect, + useCallback, +} from 'react'; import { ThemeProvider as MuiThemeProvider } from '@mui/material/styles'; import { darkTheme } from '../../styles/theme-dark'; import { lightTheme } from '../../styles/theme-light'; @@ -17,9 +24,36 @@ export const ThemeProvider = ({ children }: { children: React.ReactNode }) => { ); const toggleTheme = () => { - setThemeMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light')); + setThemeMode((prevMode) => { + const newMode = prevMode === 'light' ? 'dark' : 'light'; + + const themeProperties = { + mode: newMode, + }; + + localStorage.setItem('saved_ui_theme', JSON.stringify(themeProperties)); + + return newMode; + }); }; + const getSavedTheme = useCallback(async () => { + try { + const themeProperties = JSON.parse( + localStorage.getItem(`saved_ui_theme`) || '{}' + ); + + const theme = themeProperties?.mode || 'light'; + setThemeMode(theme); + } catch (error) { + console.log('error', error); + } + }, []); + + useEffect(() => { + getSavedTheme(); + }, [getSavedTheme]); + return ( {children} From 5030723285c0621e125ab19cc09ccc398e7b9e00 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 26 Apr 2025 10:34:15 +0300 Subject: [PATCH 24/33] added the option to change the wallet's password --- public/locales/de/auth.json | 8 +- public/locales/en/auth.json | 8 +- public/locales/es/auth.json | 8 +- public/locales/fr/auth.json | 8 +- public/locales/it/auth.json | 8 +- public/locales/ru/auth.json | 8 +- src/App.tsx | 136 +------------- src/ExtStates/NotAuthenticated.tsx | 2 +- src/components/Auth/DownloadWallet.tsx | 248 +++++++++++++++++++++++++ src/components/Explore/Explore.tsx | 2 +- 10 files changed, 302 insertions(+), 134 deletions(-) create mode 100644 src/components/Auth/DownloadWallet.tsx diff --git a/public/locales/de/auth.json b/public/locales/de/auth.json index fc83d42..5b14183 100644 --- a/public/locales/de/auth.json +++ b/public/locales/de/auth.json @@ -31,7 +31,13 @@ "return_to_list": "zurück zur Liste", "wallet": { "password_confirmation": "Wallet-Passwort bestätigen", - "password": "Wallet-Passwort" + "password": "Wallet-Passwort", + "keep_password": "aktuelles Passwort beibehalten", + "new_password": "neues Passwort", + "error": { + "missing_new_password": "bitte neues Passwort eingeben", + "missing_password": "bitte Passwort eingeben" + } }, "welcome": "willkommen bei" } diff --git a/public/locales/en/auth.json b/public/locales/en/auth.json index ebc4fa8..6591afe 100644 --- a/public/locales/en/auth.json +++ b/public/locales/en/auth.json @@ -36,7 +36,13 @@ }, "wallet": { "password_confirmation": "confirm wallet password", - "password": "wallet password" + "password": "wallet password", + "keep_password": "keep current password", + "new_password": "new password", + "error": { + "missing_new_password": "please enter a new password", + "missing_password": "please enter your password" + } }, "welcome": "welcome to" } diff --git a/public/locales/es/auth.json b/public/locales/es/auth.json index 3c62c24..5d4b8b6 100644 --- a/public/locales/es/auth.json +++ b/public/locales/es/auth.json @@ -31,7 +31,13 @@ "return_to_list": "volver a la lista", "wallet": { "password_confirmation": "confirmar contraseña del monedero", - "password": "contraseña del monedero" + "password": "contraseña del monedero", + "keep_password": "mantener la contraseña actual", + "new_password": "nueva contraseña", + "error": { + "missing_new_password": "por favor ingresa una nueva contraseña", + "missing_password": "por favor ingresa tu contraseña" + } }, "welcome": "bienvenido a" } diff --git a/public/locales/fr/auth.json b/public/locales/fr/auth.json index 06d08d5..f314d3e 100644 --- a/public/locales/fr/auth.json +++ b/public/locales/fr/auth.json @@ -31,7 +31,13 @@ "return_to_list": "retour à la liste", "wallet": { "password_confirmation": "confirmer le mot de passe du portefeuille", - "password": "mot de passe du portefeuille" + "password": "mot de passe du portefeuille", + "keep_password": "garder le mot de passe actuel", + "new_password": "nouveau mot de passe", + "error": { + "missing_new_password": "veuillez entrer un nouveau mot de passe", + "missing_password": "veuillez entrer votre mot de passe" + } }, "welcome": "bienvenue sur" } diff --git a/public/locales/it/auth.json b/public/locales/it/auth.json index 345c154..ad00833 100644 --- a/public/locales/it/auth.json +++ b/public/locales/it/auth.json @@ -30,7 +30,13 @@ "password": "password", "wallet": { "password_confirmation": "conferma la password del wallet", - "password": "password del wallet" + "password": "password del wallet", + "keep_password": "mantieni la password attuale", + "new_password": "nuova password", + "error": { + "missing_new_password": "per favore inserisci una nuova password", + "missing_password": "per favore inserisci la tua password" + } }, "return_to_list": "ritorna alla lista", "welcome": "benvenuto in" diff --git a/public/locales/ru/auth.json b/public/locales/ru/auth.json index 3ae2327..797220b 100644 --- a/public/locales/ru/auth.json +++ b/public/locales/ru/auth.json @@ -31,7 +31,13 @@ "return_to_list": "вернуться к списку", "wallet": { "password_confirmation": "подтвердите пароль кошелька", - "password": "пароль кошелька" + "password": "пароль кошелька", + "keep_password": "сохранить текущий пароль", + "new_password": "новый пароль", + "error": { + "missing_new_password": "пожалуйста, введите новый пароль", + "missing_password": "пожалуйста, введите ваш пароль" + } }, "welcome": "добро пожаловать в" } diff --git a/src/App.tsx b/src/App.tsx index 0f2d4b1..fe375b8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -136,6 +136,7 @@ import { PdfViewer } from './common/PdfViewer'; import ThemeSelector from './components/Theme/ThemeSelector.tsx'; import { useTranslation } from 'react-i18next'; import LanguageSelector from './components/Language/LanguageSelector.tsx'; +import { DownloadWallet } from './components/Auth/DownloadWallet.tsx'; type extStates = | 'not-authenticated' @@ -871,27 +872,6 @@ function App() { } }, [authenticatedMode]); - const confirmPasswordToDownload = async () => { - try { - setWalletToBeDownloadedError(''); - if (!walletToBeDownloadedPassword) { - setSendPaymentError('Please enter your password'); - return; - } - setIsLoading(true); - await new Promise((res) => { - setTimeout(() => { - res(); - }, 250); - }); - const res = await saveWalletFunc(walletToBeDownloadedPassword); - } catch (error: any) { - setWalletToBeDownloadedError(error?.message); - } finally { - setIsLoading(false); - } - }; - const saveFileToDiskFunc = async () => { try { await saveFileToDisk( @@ -1638,7 +1618,7 @@ function App() { textTransform: 'uppercase', }} > - {t('core:wallet_other')} + {t('core:wallet.wallet_other')} } placement="left" @@ -2652,110 +2632,14 @@ function App() { )} {extState === 'download-wallet' && ( - <> - - - - - - - -
- -
- - - - - - {t('auth:download_account', { postProcess: 'capitalize' })} - - - - - - {!walletToBeDownloaded && ( - <> - - {t('auth:wallet.password_confirmation', { - postProcess: 'capitalize', - })} - - - - - - setWalletToBeDownloadedPassword(e.target.value) - } - /> - - - - - {t('auth:password_confirmation', { - postProcess: 'capitalize', - })} - - {walletToBeDownloadedError} - - )} - - {walletToBeDownloaded && ( - <> - { - await saveFileToDiskFunc(); - await showInfo({ - message: t('auth:keep_secure', { - postProcess: 'capitalize', - }), - }); - }} - > - {t('auth:download_account', { - postProcess: 'capitalize', - })} - - - )} - + )} {extState === 'create-wallet' && ( <> diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index 2e3d598..1f58360 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -693,7 +693,7 @@ export const NotAuthenticated = ({ visibility: importedApiKey ? 'visible' : 'hidden', }} > - {t('auth:apikey.key', { postProcess: 'capitalize' })}: $ + {t('auth:apikey.key', { postProcess: 'capitalize' })}:{' '} {importedApiKey} diff --git a/src/components/Auth/DownloadWallet.tsx b/src/components/Auth/DownloadWallet.tsx new file mode 100644 index 0000000..12b46f4 --- /dev/null +++ b/src/components/Auth/DownloadWallet.tsx @@ -0,0 +1,248 @@ +import { Box, Checkbox, FormControlLabel, Typography } from '@mui/material'; +import { Spacer } from '../../common/Spacer'; +import { Return } from '../../assets/Icons/Return'; +import { CustomButton, CustomLabel, TextP } from '../../styles/App-styles'; +import { PasswordField } from '../PasswordField/PasswordField'; +import { ErrorText } from '../ErrorText/ErrorText'; +import Logo1Dark from '../../assets/svgs/Logo1Dark.svg'; +import { useTranslation } from 'react-i18next'; +import { saveFileToDisk } from '../../utils/generateWallet/generateWallet'; +import { useState } from 'react'; +import { decryptStoredWallet } from '../../utils/decryptWallet'; +import PhraseWallet from '../../utils/generateWallet/phrase-wallet'; +import { crypto, walletVersion } from '../../constants/decryptWallet'; + +export const DownloadWallet = ({ + returnToMain, + setIsLoading, + showInfo, + rawWallet, + setWalletToBeDownloaded, + walletToBeDownloaded, +}) => { + const [walletToBeDownloadedPassword, setWalletToBeDownloadedPassword] = + useState(''); + const [newPassword, setNewPassword] = useState(''); + const [keepCurrentPassword, setKeepCurrentPassword] = useState(true); + + const [walletToBeDownloadedError, setWalletToBeDownloadedError] = + useState(''); + + const { t } = useTranslation(['auth']); + + const saveFileToDiskFunc = async () => { + try { + await saveFileToDisk( + walletToBeDownloaded.wallet, + walletToBeDownloaded.qortAddress + ); + } catch (error: any) { + setWalletToBeDownloadedError(error?.message); + } + }; + + const saveWalletFunc = async (password: string, newPassword) => { + let wallet = structuredClone(rawWallet); + + const res = await decryptStoredWallet(password, wallet); + const wallet2 = new PhraseWallet(res, wallet?.version || walletVersion); + const passwordToUse = newPassword || password; + wallet = await wallet2.generateSaveWalletData( + passwordToUse, + crypto.kdfThreads, + () => {} + ); + + setWalletToBeDownloaded({ + wallet, + qortAddress: rawWallet.address0, + }); + return { + wallet, + qortAddress: rawWallet.address0, + }; + }; + + const confirmPasswordToDownload = async () => { + try { + setWalletToBeDownloadedError(''); + if (!keepCurrentPassword && !newPassword) { + setWalletToBeDownloadedError( + t('auth:wallet.error.missing_new_password', { + postProcess: 'capitalize', + }) + ); + return; + } + if (!walletToBeDownloadedPassword) { + setWalletToBeDownloadedError( + t('auth:wallet.error.missing_password', { postProcess: 'capitalize' }) + ); + return; + } + setIsLoading(true); + await new Promise((res) => { + setTimeout(() => { + res(); + }, 250); + }); + const newPasswordForWallet = !keepCurrentPassword ? newPassword : null; + const res = await saveWalletFunc( + walletToBeDownloadedPassword, + newPasswordForWallet + ); + } catch (error: any) { + setWalletToBeDownloadedError(error?.message); + } finally { + setIsLoading(false); + } + }; + + return ( + <> + + + + + + + +
+ +
+ + + + + + {t('auth:download_account', { postProcess: 'capitalize' })} + + + + + + {!walletToBeDownloaded && ( + <> + + {t('auth:wallet.password_confirmation', { + postProcess: 'capitalize', + })} + + + + + setWalletToBeDownloadedPassword(e.target.value)} + /> + + + + setKeepCurrentPassword(e.target.checked)} + checked={keepCurrentPassword} + edge="start" + tabIndex={-1} + disableRipple + /> + } + label={ + + + {t('auth:wallet.keep_password', { + postProcess: 'capitalize', + })} + + + } + /> + + {!keepCurrentPassword && ( + <> + + {t('auth:wallet.new_password', { + postProcess: 'capitalize', + })} + + + + setNewPassword(e.target.value)} + /> + + + )} + + + {t('auth:password_confirmation', { + postProcess: 'capitalize', + })} + + + {walletToBeDownloadedError} + + )} + + {walletToBeDownloaded && ( + <> + { + await saveFileToDiskFunc(); + await showInfo({ + message: t('auth:keep_secure', { + postProcess: 'capitalize', + }), + }); + }} + > + {t('auth:download_account', { + postProcess: 'capitalize', + })} + + + )} + + ); +}; diff --git a/src/components/Explore/Explore.tsx b/src/components/Explore/Explore.tsx index ae1988e..266fdd5 100644 --- a/src/components/Explore/Explore.tsx +++ b/src/components/Explore/Explore.tsx @@ -124,7 +124,7 @@ export const Explore = ({ setDesktopViewMode }) => { fontSize: '1rem', }} > - {t('core:wallet_other', { postProcess: 'capitalize' })} + {t('core:wallet.wallet_other', { postProcess: 'capitalize' })}
From 53facb9f2cf4d61ddaa472b28ecdc293f09f4461 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 15:39:45 +0200 Subject: [PATCH 25/33] Detect click outside of the component (manage no language selection) --- src/components/Language/LanguageSelector.tsx | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/components/Language/LanguageSelector.tsx b/src/components/Language/LanguageSelector.tsx index 6241347..751c0a9 100644 --- a/src/components/Language/LanguageSelector.tsx +++ b/src/components/Language/LanguageSelector.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { supportedLanguages } from '../../../i18n'; import { Tooltip, useTheme } from '@mui/material'; @@ -7,6 +7,7 @@ const LanguageSelector = () => { const { i18n, t } = useTranslation(['core']); const [showSelect, setShowSelect] = useState(false); const theme = useTheme(); + const selectorRef = useRef(null); const handleChange = (e) => { const newLang = e.target.value; @@ -18,8 +19,23 @@ const LanguageSelector = () => { const { name, flag } = supportedLanguages[currentLang] || supportedLanguages['en']; + // Detect clicks outside the component + useEffect(() => { + const handleClickOutside = (event) => { + if (selectorRef.current && !selectorRef.current.contains(event.target)) { + setShowSelect(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + return (
{ }} value={currentLang} onChange={handleChange} - onBlur={() => setShowSelect(false)} + autoFocus > {Object.entries(supportedLanguages).map(([code, { name }]) => (
)} @@ -2341,18 +2342,23 @@ export const Group = ({ > {' '} - You are not part of the encrypted group of members. Wait - until an admin re-encrypts the keys. + {t('group:message.generic.not_part_group', { + postProcess: 'capitalize', + })} - Only unencrypted messages will be displayed. + {t('group:message.generic.only_encrypted', { + postProcess: 'capitalize', + })} - Try notifying an admin from the list of admins below: + {t('group:message.generic.notify_admins', { + postProcess: 'capitalize', + })} {adminsWithNames.map((admin) => { @@ -2372,7 +2378,9 @@ export const Group = ({ variant="contained" onClick={() => notifyAdmin(admin)} > - Notify + {t('core:action.notify', { + postProcess: 'capitalize', + })} ); @@ -2592,14 +2600,19 @@ export const Group = ({ open={isLoadingGroup} info={{ message: - isLoadingGroupMessage || 'Setting up group... please wait.', + isLoadingGroupMessage || + t('group:message.generic.setting_group', { + postProcess: 'capitalize', + }), }} /> diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx index 3835c3e..af81863 100644 --- a/src/components/Save/Save.tsx +++ b/src/components/Save/Save.tsx @@ -169,7 +169,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { .catch((error) => { rej( error.message || - t('core:result.error.generic', { postProcess: 'capitalize' }) + t('core:message.error.generic', { postProcess: 'capitalize' }) ); }); }); @@ -178,7 +178,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { setSettingsQdnLastUpdated(Date.now()); setInfoSnack({ type: 'success', - message: t('core:result.success.publish_qdn', { + message: t('core:message.success.publish_qdn', { postProcess: 'capitalize', }), }); @@ -191,7 +191,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => { type: 'error', message: error?.message || - t('core:result.error.save_qdn', { + t('core:message.error.save_qdn', { postProcess: 'capitalize', }), }); From d89a98ec4f89a74cb393f4782c7e3d8842493927 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 16:45:31 +0200 Subject: [PATCH 28/33] Translate inviteMember --- public/locales/en/core.json | 1 + public/locales/en/group.json | 4 +++ src/components/Group/InviteMember.tsx | 43 ++++++++++++++++----------- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/public/locales/en/core.json b/public/locales/en/core.json index 407571c..5ba27e8 100644 --- a/public/locales/en/core.json +++ b/public/locales/en/core.json @@ -15,6 +15,7 @@ "edit": "edit", "export": "export", "import": "import", + "invite": "invite", "join": "join", "logout": "logout", "notify": "notify" diff --git a/public/locales/en/group.json b/public/locales/en/group.json index 220aa9f..2514468 100644 --- a/public/locales/en/group.json +++ b/public/locales/en/group.json @@ -3,6 +3,7 @@ "create_group": "create group", "find_group": "find group", "join_group": "join group", + "invite_member": "invite member", "refetch_page": "refetch page", "return_to_thread": "return to threads" }, @@ -21,8 +22,10 @@ "open": "open (public)", "type": "group type" }, + "invitation_expiry": "invitation Expiry Time", "question": { "create_group": "would you like to perform an CREATE_GROUP transaction?", + "group_invite": "would you like to perform a GROUP_INVITE transaction?", "join_group": "would you like to perform an JOIN_GROUP transaction?", "provide_thread": "please provide a thread title" }, @@ -45,6 +48,7 @@ "group_creation": "successfully created group. It may take a couple of minutes for the changes to propagate", "group_creation_name": "created group {{group_name}}: awaiting confirmation", "group_creation_label": "created group {{name}}: success!", + "group_invite": "successfully invited {{value}}. It may take a couple of minutes for the changes to propagate", "join_creation": "successfully requested to join group. It may take a couple of minutes for the changes to propagate", "group_join_name": "joined group {{group_name}}: awaiting confirmation", "group_join_label": "joined group {{name}}: success!", diff --git a/src/components/Group/InviteMember.tsx b/src/components/Group/InviteMember.tsx index de3dc20..db01b59 100644 --- a/src/components/Group/InviteMember.tsx +++ b/src/components/Group/InviteMember.tsx @@ -4,16 +4,21 @@ import { useState } from 'react'; import { Spacer } from '../../common/Spacer'; import { Label } from './AddGroup'; import { getFee } from '../../background'; +import { useTranslation } from 'react-i18next'; export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { const [value, setValue] = useState(''); const [expiryTime, setExpiryTime] = useState('259200'); const [isLoadingInvite, setIsLoadingInvite] = useState(false); + const { t } = useTranslation(['core', 'group']); + const inviteMember = async () => { try { const fee = await getFee('GROUP_INVITE'); await show({ - message: 'Would you like to perform a GROUP_INVITE transaction?', + message: t('group:question.group_invite', { + postProcess: 'capitalize', + }), publishFee: fee.fee + ' QORT', }); setIsLoadingInvite(true); @@ -27,10 +32,12 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { }) .then((response) => { if (!response?.error) { - // TODO translate setInfoSnack({ type: 'success', - message: `Successfully invited ${value}. It may take a couple of minutes for the changes to propagate`, + message: t('group:message.success.group_invite', { + value: value, + postProcess: 'capitalize', + }), }); setOpenSnack(true); res(response); @@ -72,7 +79,7 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { flexDirection: 'column', }} > - Invite member + {t('group:action.invite_member', { postProcess: 'capitalize' })} { onChange={(e) => setValue(e.target.value)} /> - + { loading={isLoadingInvite} onClick={inviteMember} > - Invite + {t('core:action.invite', { postProcess: 'capitalize' })} ); From ea6b9bf93d6856dc02f15b2098ab9392db9f1bf5 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 16:53:17 +0200 Subject: [PATCH 29/33] Translate groupJoinRequests --- public/locales/en/group.json | 3 +++ src/components/Group/GroupInvites.tsx | 11 +++++++--- src/components/Group/GroupJoinRequests.tsx | 24 ++++++++++++---------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/public/locales/en/group.json b/public/locales/en/group.json index 2514468..d59e6db 100644 --- a/public/locales/en/group.json +++ b/public/locales/en/group.json @@ -23,6 +23,7 @@ "type": "group type" }, "invitation_expiry": "invitation Expiry Time", + "join_requests": "join requests", "question": { "create_group": "would you like to perform an CREATE_GROUP transaction?", "group_invite": "would you like to perform a GROUP_INVITE transaction?", @@ -32,6 +33,8 @@ "message": { "generic": { "encryption_key": "the group's first common encryption key is in the process of creation. Please wait a few minutes for it to be retrieved by the network. Checking every 2 minutes...", + "group_invited_you": "{{group}} has invited you", + "no_display": "nothing to display", "no_selection": "no group selected", "not_part_group": "you are not part of the encrypted group of members. Wait until an admin re-encrypts the keys.", "only_encrypted": "only unencrypted messages will be displayed.", diff --git a/src/components/Group/GroupInvites.tsx b/src/components/Group/GroupInvites.tsx index c753fdb..9883911 100644 --- a/src/components/Group/GroupInvites.tsx +++ b/src/components/Group/GroupInvites.tsx @@ -69,7 +69,7 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { {t('group:group_invites', { postProcess: 'capitalize' })}{' '} {groupsWithJoinRequests?.length > 0 && @@ -130,7 +130,9 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { fontWeight: 400, }} > - Nothing to display + {t('group:message.generic.no_display', { + postProcess: 'capitalize', + })} )} @@ -177,7 +179,10 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { fontWeight: 400, }, }} - primary={`${group?.groupName} has invited you`} + primary={t('group:message.generic.group_invited_you', { + group: group?.groupName, + postProcess: 'capitalize', + })} /> diff --git a/src/components/Group/GroupJoinRequests.tsx b/src/components/Group/GroupJoinRequests.tsx index 4bc6b21..c7bb32e 100644 --- a/src/components/Group/GroupJoinRequests.tsx +++ b/src/components/Group/GroupJoinRequests.tsx @@ -14,6 +14,7 @@ import { myGroupsWhereIAmAdminAtom } from '../../atoms/global'; import { useSetRecoilState } from 'recoil'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import { useTranslation } from 'react-i18next'; export const requestQueueGroupJoinRequests = new RequestQueueWithPromise(2); export const GroupJoinRequests = ({ @@ -27,7 +28,7 @@ export const GroupJoinRequests = ({ setDesktopViewMode, }) => { const [isExpanded, setIsExpanded] = React.useState(false); - + const { t } = useTranslation(['core', 'group']); const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState( [] ); @@ -139,9 +140,9 @@ export const GroupJoinRequests = ({ - Join Requests{' '} + {t('group:join_requests', { postProcess: 'capitalize' })}{' '} {filteredJoinRequests?.filter((group) => group?.data?.length > 0) ?.length > 0 && ` (${filteredJoinRequests?.filter((group) => group?.data?.length > 0)?.length})`} @@ -163,14 +164,13 @@ export const GroupJoinRequests = ({ {loading && filteredJoinRequests.length === 0 && ( @@ -204,18 +204,20 @@ export const GroupJoinRequests = ({ color: 'rgba(255, 255, 255, 0.2)', }} > - Nothing to display + {t('group:message.generic.no_display', { + postProcess: 'capitalize', + })} )} {filteredJoinRequests?.map((group) => { From 5daf76ace60e9354d570f8fa4b96585ff2b099e0 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 16:58:02 +0200 Subject: [PATCH 30/33] Translate listOfBans --- public/locales/en/group.json | 6 +++++- src/components/Group/ListOfBans.tsx | 17 ++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/public/locales/en/group.json b/public/locales/en/group.json index d59e6db..345c6d0 100644 --- a/public/locales/en/group.json +++ b/public/locales/en/group.json @@ -1,5 +1,6 @@ { "action": { + "cancel_ban": "cancel ban", "create_group": "create group", "find_group": "find group", "join_group": "join group", @@ -9,6 +10,7 @@ }, "advanced_options": "advanced options", "approval_threshold": "group Approval Threshold (number / percentage of Admins that must approve a transaction)", + "ban_list": "ban list", "block_delay": { "minimum": "minimum Block delay for Group Transaction Approvals", "maximum": "maximum Block delay for Group Transaction Approvals" @@ -25,6 +27,7 @@ "invitation_expiry": "invitation Expiry Time", "join_requests": "join requests", "question": { + "cancel_ban": "would you like to perform a CANCEL_GROUP_BAN transaction?", "create_group": "would you like to perform an CREATE_GROUP transaction?", "group_invite": "would you like to perform a GROUP_INVITE transaction?", "join_group": "would you like to perform an JOIN_GROUP transaction?", @@ -55,7 +58,8 @@ "join_creation": "successfully requested to join group. It may take a couple of minutes for the changes to propagate", "group_join_name": "joined group {{group_name}}: awaiting confirmation", "group_join_label": "joined group {{name}}: success!", - "loading_threads": "loading threads... please wait." + "loading_threads": "loading threads... please wait.", + "unbanned_user": "successfully unbanned user. It may take a couple of minutes for the changes to propagate" } } } diff --git a/src/components/Group/ListOfBans.tsx b/src/components/Group/ListOfBans.tsx index 850ce13..0c522e0 100644 --- a/src/components/Group/ListOfBans.tsx +++ b/src/components/Group/ListOfBans.tsx @@ -18,6 +18,7 @@ import { getNameInfo } from './Group'; import { getFee } from '../../background'; import { LoadingButton } from '@mui/lab'; import { getBaseApiReact } from '../../App'; +import { useTranslation } from 'react-i18next'; export const getMemberInvites = async (groupNumber) => { const response = await fetch( @@ -55,6 +56,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open const listRef = useRef(); const [isLoadingUnban, setIsLoadingUnban] = useState(false); + const { t } = useTranslation(['core', 'group']); const getInvites = async (groupId) => { try { @@ -87,7 +89,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { // TODO translate const fee = await getFee('CANCEL_GROUP_BAN'); await show({ - message: 'Would you like to perform a CANCEL_GROUP_BAN transaction?', + message: t('group:question.cancel_ban', { postProcess: 'capitalize' }), publishFee: fee.fee + ' QORT', }); setIsLoadingUnban(true); @@ -103,8 +105,9 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { setIsLoadingUnban(false); setInfoSnack({ type: 'success', - message: - 'Successfully unbanned user. It may take a couple of minutes for the changes to propagate', + message: t('group:message.success.unbanned_user', { + postProcess: 'capitalize', + }), }); handlePopoverClose(); setOpenSnack(true); @@ -127,6 +130,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { }); }); } catch (error) { + console.log(error); } finally { setIsLoadingUnban(false); } @@ -177,10 +181,13 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { variant="contained" onClick={() => handleCancelBan(member?.offender)} > - Cancel Ban + {t('group:action.cancel_ban', { + postProcess: 'capitalize', + })} + handlePopoverOpen(event, index)} > @@ -205,7 +212,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { return (
-

Ban list

+

{t('group:ban_list', { postProcess: 'capitalize' })}

Date: Sat, 26 Apr 2025 16:59:39 +0200 Subject: [PATCH 31/33] Import react components --- src/components/Group/Settings.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/Group/Settings.tsx b/src/components/Group/Settings.tsx index f5a952c..4af646b 100644 --- a/src/components/Group/Settings.tsx +++ b/src/components/Group/Settings.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import { forwardRef, Fragment, ReactElement, Ref, useEffect } from 'react'; import Dialog from '@mui/material/Dialog'; import AppBar from '@mui/material/AppBar'; import Toolbar from '@mui/material/Toolbar'; @@ -44,11 +44,11 @@ const LocalNodeSwitch = styled(Switch)(({ theme }) => ({ }, })); -const Transition = React.forwardRef(function Transition( +const Transition = forwardRef(function Transition( props: TransitionProps & { - children: React.ReactElement; + children: ReactElement; }, - ref: React.Ref + ref: Ref ) { return ; }); @@ -111,12 +111,12 @@ export const Settings = ({ address, open, setOpen }) => { } }; - React.useEffect(() => { + useEffect(() => { getUserSettings(); }, []); return ( - + { )} - + ); }; From fa63ac493eaf130bf776f77f384cf7053fabafb6 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 17:00:20 +0200 Subject: [PATCH 32/33] Remove comment --- src/components/Group/ListOfBans.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Group/ListOfBans.tsx b/src/components/Group/ListOfBans.tsx index 0c522e0..355f1d8 100644 --- a/src/components/Group/ListOfBans.tsx +++ b/src/components/Group/ListOfBans.tsx @@ -86,7 +86,6 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { const handleCancelBan = async (address) => { try { - // TODO translate const fee = await getFee('CANCEL_GROUP_BAN'); await show({ message: t('group:question.cancel_ban', { postProcess: 'capitalize' }), From cf998ed04cc916853f4b071df3876e42fe7d7715 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 26 Apr 2025 17:06:23 +0200 Subject: [PATCH 33/33] Update italian translation --- public/locales/it/auth.json | 37 ++++++++------- public/locales/it/core.json | 92 ++++++++++++++++++++++++++---------- public/locales/it/group.json | 65 +++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 42 deletions(-) create mode 100644 public/locales/it/group.json diff --git a/public/locales/it/auth.json b/public/locales/it/auth.json index ad00833..513378f 100644 --- a/public/locales/it/auth.json +++ b/public/locales/it/auth.json @@ -6,38 +6,43 @@ }, "advanced_users": "per utenti avanzati", "apikey": { - "alternative": "alternativa: seleziona un file", - "change": "cambia la chiave API", - "enter": "inserisci la chiave API", - "import": "importa chiave API", + "alternative": "alternativa: selezione file", + "change": "cambia APIkey", + "enter": "inserisci APIkey", + "import": "importa APIkey", "key": "chiave API", - "select_valid": "selezione una chiave API valida" + "select_valid": "seleziona una APIkey valida" }, - "authenticate": "autenticazione", + "authenticate": "autentica", "build_version": "versione build", - "create_account": "crea un account", + "create_account": "crea account", "download_account": "scarica account", - "keep_secure": "metti al sicuro il file del tuo account", + "keep_secure": "mantieni sicuro il file del tuo account", "node": { - "choose": "scegli un nodo custom", - "custom_many": "nodi custom", - "use_custom": "use nodo custom", + "choose": "scegli nodo personalizzato", + "custom_many": "nodi personalizzati", + "use_custom": "usa nodo personalizzato", "use_local": "usa nodo locale", - "using": "nodo in uso", + "using": "utilizzo nodo", "using_public": "utilizzo nodo pubblico" }, - "password_confirmation": "confirma la password", "password": "password", + "password_confirmation": "conferma password", + "return_to_list": "torna alla lista", + "tips": { + "digital_id": "il tuo wallet è come la tua identità digitale su Qortal ed è il modo in cui accederai all'interfaccia utente di Qortal. Contiene il tuo indirizzo pubblico e il nome Qortal che sceglierai. Ogni transazione che esegui è collegata alla tua identità ed è qui che gestisci tutti i tuoi QORT e altre criptovalute scambiabili su Qortal.", + "new_account": "creare un account significa creare un nuovo wallet e un'identità digitale per iniziare a usare Qortal. Una volta creato l'account, potrai iniziare a ottenere QORT, acquistare un nome e un avatar, pubblicare video e blog, e molto altro.", + "new_users": "i nuovi utenti iniziano qui!" + }, "wallet": { - "password_confirmation": "conferma la password del wallet", + "password_confirmation": "conferma password del wallet", "password": "password del wallet", - "keep_password": "mantieni la password attuale", + "keep_password": "mantieni password corrente", "new_password": "nuova password", "error": { "missing_new_password": "per favore inserisci una nuova password", "missing_password": "per favore inserisci la tua password" } }, - "return_to_list": "ritorna alla lista", "welcome": "benvenuto in" } diff --git a/public/locales/it/core.json b/public/locales/it/core.json index 8a6c4a2..8319b22 100644 --- a/public/locales/it/core.json +++ b/public/locales/it/core.json @@ -1,71 +1,111 @@ { - "add": "aggiungi", - "cancel": "annulla", - "choose": "scegli", - "close": "chiudi", - "continue": "continua", + "action": { + "add": "aggiungi", + "accept": "accetta", + "backup_account": "backup account", + "backup_wallet": "backup wallet", + "cancel": "annulla", + "change": "cambia", + "change_language": "cambia lingua", + "choose": "scegli", + "close": "chiudi", + "continue": "continua", + "continue_logout": "continua con il logout", + "decline": "rifiuta", + "edit": "modifica", + "export": "esporta", + "import": "importa", + "invite": "invita", + "join": "unisciti", + "logout": "esci", + "notify": "notifica" + }, "core": { - "block_height": "altezza del blocco", + "block_height": "altezza blocco", "information": "informazioni core", "peers": "peer connessi", "version": "versione core" }, + "count": { + "none": "nessuno", + "one": "uno" + }, "description": "descrizione", - "edit": "modifica", - "export": "esporta", - "import": "importa", + "fee": { + "payment": "commissione di pagamento", + "publish": "commissione di pubblicazione" + }, + "page": { + "last": "ultimo", + "first": "primo", + "next": "successivo", + "previous": "precedente" + }, + "downloading_qdn": "scaricamento da QDN", "last_height": "ultima altezza", "loading": "caricamento...", - "logout": "disconnetti", - "minting_status": "stato del conio", + "loading_posts": "caricamento post... attendere prego.", + "message_us": "per favore scrivici su Telegram o Discord se hai bisogno di 4 QORT per iniziare a chattare senza limitazioni", + "minting_status": "stato minting", + "new_user": "sei un nuovo utente?", "payment_notification": "notifica di pagamento", "price": "prezzo", "q_mail": "q-mail", - "result": { + "message": { "error": { "generic": "si è verificato un errore", "incorrect_password": "password errata", "save_qdn": "impossibile salvare su QDN" }, "status": { - "minting": "(conio in corso)", - "not_minting": "(conio non attivo)", + "minting": "(minting)", + "not_minting": "(non minting)", "synchronized": "sincronizzato", "synchronizing": "sincronizzazione in corso" }, "success": { - "publish_qdn": "pubblicato con successo su QDN" + "order_submitted": "il tuo ordine di acquisto è stato inviato", + "publish_qdn": "pubblicato su QDN con successo", + "request_read": "ho letto questa richiesta", + "transfer": "il trasferimento è stato effettuato con successo!" } }, "save_options": { "no_pinned_changes": "attualmente non hai modifiche alle tue app appuntate", - "overwrite_changes": "l'app non è riuscita a scaricare le tue app appuntate salvate su QDN. Vuoi sovrascrivere queste modifiche?", + "overwrite_changes": "l'app non è riuscita a scaricare le tue app appuntate salvate su QDN. Vuoi sovrascrivere le modifiche?", "overwrite_qdn": "sovrascrivi su QDN", - "publish_qdn": "vuoi pubblicare le tue impostazioni su QDN (crittografate)?", - "qdn": "usa il salvataggio QDN", - "register_name": "hai bisogno di un nome Qortal registrato per salvare le tue app appuntate su QDN.", + "publish_qdn": "vuoi pubblicare le tue impostazioni su QDN (crittografato)?", + "qdn": "usa il salvataggio su QDN", + "register_name": "devi avere un nome Qortal registrato per salvare le tue app appuntate su QDN.", "reset_pinned": "non ti piacciono le modifiche locali attuali? Vuoi ripristinare le app appuntate predefinite?", - "reset_qdn": "non ti piacciono le modifiche locali attuali? Vuoi ripristinare le tue app appuntate salvate su QDN?", - "revert_default": "ripristina predefinite", + "reset_qdn": "non ti piacciono le modifiche locali attuali? Vuoi ripristinare le app appuntate salvate su QDN?", + "revert_default": "ripristina predefinito", "revert_qdn": "ripristina da QDN", "save_qdn": "salva su QDN", "save": "salva", - "settings": "stai utilizzando il metodo esporta/importa per salvare le impostazioni.", + "settings": "stai usando il metodo di esportazione/importazione per salvare le impostazioni.", "unsaved_changes": "hai modifiche non salvate alle tue app appuntate. Salvale su QDN." }, "settings": "impostazioni", - "supply": "offerta", + "supply": "disponibilità", "theme": { "dark": "modalità scura", "light": "modalità chiara" }, + "time": { + "day_one": "{{count}} giorno", + "day_other": "{{count}} giorni", + "hour_one": "{{count}} ora", + "hour_other": "{{count}} ore", + "minute_one": "{{count}} minuto", + "minute_other": "{{count}} minuti" + }, "title": "titolo", "tutorial": "tutorial", "user_lookup": "ricerca utente", "wallet": { - "backup_wallet": "backup portafoglio", - "wallet": "portafoglio", - "wallet_other": "portafogli" + "wallet": "wallet", + "wallet_other": "wallet" }, "welcome": "benvenuto" } diff --git a/public/locales/it/group.json b/public/locales/it/group.json new file mode 100644 index 0000000..862496d --- /dev/null +++ b/public/locales/it/group.json @@ -0,0 +1,65 @@ +{ + "action": { + "cancel_ban": "annulla ban", + "create_group": "crea gruppo", + "find_group": "trova gruppo", + "join_group": "unisciti al gruppo", + "invite_member": "invita membro", + "refetch_page": "ricarica pagina", + "return_to_thread": "torna ai thread" + }, + "advanced_options": "opzioni avanzate", + "approval_threshold": "soglia di Approvazione del gruppo (numero/percentuale di Admin che devono approvare una transazione)", + "ban_list": "lista ban", + "block_delay": { + "minimum": "ritardo minimo dei blocchi per Approvazione di Transazioni di Gruppo", + "maximum": "ritardo massimo dei blocchi per Approvazione di Transazioni di Gruppo" + }, + "group": { + "closed": "chiuso (privato) - gli utenti necessitano di permesso per unirsi", + "description": "descrizione del gruppo", + "invites": "inviti del gruppo", + "management": "gestione del gruppo", + "name": "nome del gruppo", + "open": "aperto (pubblico)", + "type": "tipo di gruppo" + }, + "invitation_expiry": "tempo di scadenza dell'invito", + "join_requests": "richieste di adesione", + "question": { + "cancel_ban": "vuoi eseguire una transazione CANCEL_GROUP_BAN?", + "create_group": "vuoi eseguire una transazione CREATE_GROUP?", + "group_invite": "vuoi eseguire una transazione GROUP_INVITE?", + "join_group": "vuoi eseguire una transazione JOIN_GROUP?", + "provide_thread": "per favore fornisci un titolo per il thread" + }, + "message": { + "generic": { + "encryption_key": "la prima chiave di cifratura comune del gruppo è in fase di creazione. Attendere alcuni minuti affinché venga recuperata dalla rete. Controllo ogni 2 minuti...", + "group_invited_you": "{{group}} ti ha invitato", + "no_display": "niente da visualizzare", + "no_selection": "nessun gruppo selezionato", + "not_part_group": "non fai parte del gruppo cifrato dei membri. Attendi che un amministratore ri-codifichi le chiavi.", + "only_encrypted": "verranno visualizzati solo i messaggi non cifrati.", + "setting_group": "configurazione del gruppo... attendere prego." + }, + "error": { + "access_name": "impossibile inviare un messaggio senza accesso al tuo nome", + "description_required": "per favore fornisci una descrizione", + "group_info": "impossibile accedere alle informazioni del gruppo", + "name_required": "per favore fornisci un nome", + "notify_admins": "prova a notificare un admin dalla lista di amministratori qui sotto:" + }, + "success": { + "group_creation": "gruppo creato con successo. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino", + "group_creation_name": "gruppo {{group_name}} creato: in attesa di conferma", + "group_creation_label": "gruppo {{name}} creato: successo!", + "group_invite": "invito inviato con successo a {{value}}. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino", + "join_creation": "richiesta di adesione al gruppo inviata con successo. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino", + "group_join_name": "entrato nel gruppo {{group_name}}: in attesa di conferma", + "group_join_label": "entrato nel gruppo {{name}}: successo!", + "loading_threads": "caricamento thread... attendere prego.", + "unbanned_user": "utente sbannato con successo. Potrebbero volerci alcuni minuti affinché le modifiche si propaghino" + } + } +}