Add translations

This commit is contained in:
Nicola Benaglia 2025-05-10 11:22:15 +02:00
parent f1d62fe11b
commit 5f602442e4
8 changed files with 104 additions and 69 deletions

View File

@ -5,6 +5,7 @@
"backup_account": "backup account", "backup_account": "backup account",
"backup_wallet": "backup wallet", "backup_wallet": "backup wallet",
"cancel": "cancel", "cancel": "cancel",
"cancel_invitation": "cancel invitation",
"change": "change", "change": "change",
"change_language": "change language", "change_language": "change language",
"choose": "choose", "choose": "choose",
@ -48,8 +49,11 @@
"publish": "publish fee" "publish": "publish fee"
}, },
"general_settings": "general settings", "general_settings": "general settings",
"invite_list": "invite list",
"last_height": "last height", "last_height": "last height",
"list": {
"invite": "invite list",
"join_request": "join request list"
},
"loading": "loading...", "loading": "loading...",
"loading_posts": "loading posts... please wait.", "loading_posts": "loading posts... please wait.",
"message_us": "please message us on Telegram or Discord if you need 4 QORT to start chatting without any limitations", "message_us": "please message us on Telegram or Discord if you need 4 QORT to start chatting without any limitations",

View File

@ -31,6 +31,7 @@
"type": "group type" "type": "group type"
}, },
"invitation_expiry": "invitation Expiry Time", "invitation_expiry": "invitation Expiry Time",
"invitees_list": "invitees list",
"join_requests": "join requests", "join_requests": "join requests",
"question": { "question": {
"perform_transaction": "would you like to perform a {{action}} transaction?", "perform_transaction": "would you like to perform a {{action}} transaction?",
@ -72,10 +73,13 @@
"group_join_label": "joined group {{name}}: success!", "group_join_label": "joined group {{name}}: success!",
"group_join_request": "requested to join Group {{group_name}}: awaiting confirmation", "group_join_request": "requested to join Group {{group_name}}: awaiting confirmation",
"group_join_outcome": "requested to join Group {{group_name}}: success!", "group_join_outcome": "requested to join Group {{group_name}}: success!",
"invitation_cancellation": "successfully canceled invitation. It may take a couple of minutes for the changes to propagate",
"invitation_request": "accepted join request: awaiting confirmation",
"loading_threads": "loading threads... please wait.", "loading_threads": "loading threads... please wait.",
"post_creation": "successfully created post. It may take some time for the publish to propagate", "post_creation": "successfully created post. It may take some time for the publish to propagate",
"thread_creation": "successfully created thread. It may take some time for the publish to propagate", "thread_creation": "successfully created thread. It may take some time for the publish to propagate",
"unbanned_user": "successfully unbanned user. It may take a couple of minutes for the changes to propagate" "unbanned_user": "successfully unbanned user. It may take a couple of minutes for the changes to propagate",
"user_joined": "user successfully joined!"
} }
} }
} }

View File

@ -9,20 +9,12 @@ import {
Typography, Typography,
useTheme, useTheme,
} from '@mui/material'; } from '@mui/material';
import React, { import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import { ChatGroup } from '../Chat/ChatGroup'; import { ChatGroup } from '../Chat/ChatGroup';
import { CreateCommonSecret } from '../Chat/CreateCommonSecret'; import { CreateCommonSecret } from '../Chat/CreateCommonSecret';
import { base64ToUint8Array } from '../../qdn/encryption/group-encryption'; import { base64ToUint8Array } from '../../qdn/encryption/group-encryption';
import { uint8ArrayToObject } from '../../backgroundFunctions/encryption'; import { uint8ArrayToObject } from '../../backgroundFunctions/encryption';
import CampaignIcon from '@mui/icons-material/Campaign';
import { AddGroup } from './AddGroup'; import { AddGroup } from './AddGroup';
import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline';
import CreateIcon from '@mui/icons-material/Create'; import CreateIcon from '@mui/icons-material/Create';
import { import {
AuthenticatedContainerInnerRight, AuthenticatedContainerInnerRight,
@ -52,7 +44,6 @@ import {
import { RequestQueueWithPromise } from '../../utils/queue/queue'; import { RequestQueueWithPromise } from '../../utils/queue/queue';
import { WebSocketActive } from './WebsocketActive'; import { WebSocketActive } from './WebsocketActive';
import { useMessageQueue } from '../../MessageQueueContext'; import { useMessageQueue } from '../../MessageQueueContext';
import { ContextMenu } from '../ContextMenu';
import { HomeDesktop } from './HomeDesktop'; import { HomeDesktop } from './HomeDesktop';
import { IconWrapper } from '../Desktop/DesktopFooter'; import { IconWrapper } from '../Desktop/DesktopFooter';
import { DesktopHeader } from '../Desktop/DesktopHeader'; import { DesktopHeader } from '../Desktop/DesktopHeader';
@ -63,7 +54,6 @@ import { HubsIcon } from '../../assets/Icons/HubsIcon';
import { MessagingIcon } from '../../assets/Icons/MessagingIcon'; import { MessagingIcon } from '../../assets/Icons/MessagingIcon';
import { formatEmailDate } from './QMailMessages'; import { formatEmailDate } from './QMailMessages';
import { AdminSpace } from '../Chat/AdminSpace'; import { AdminSpace } from '../Chat/AdminSpace';
import { import {
addressInfoControllerAtom, addressInfoControllerAtom,
groupAnnouncementsAtom, groupAnnouncementsAtom,
@ -77,9 +67,6 @@ import {
timestampEnterDataAtom, timestampEnterDataAtom,
} from '../../atoms/global'; } from '../../atoms/global';
import { sortArrayByTimestampAndGroupName } from '../../utils/time'; import { sortArrayByTimestampAndGroupName } from '../../utils/time';
import PersonOffIcon from '@mui/icons-material/PersonOff';
import LockIcon from '@mui/icons-material/Lock';
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
import { BlockedUsersModal } from './BlockedUsersModal'; import { BlockedUsersModal } from './BlockedUsersModal';
import { WalletsAppWrapper } from './WalletsAppWrapper'; import { WalletsAppWrapper } from './WalletsAppWrapper';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -92,6 +79,7 @@ export const getPublishesFromAdmins = async (admins: string[], groupId) => {
groupId groupId
}&exactmatchnames=true&limit=0&reverse=true&${queryString}&prefix=true`; }&exactmatchnames=true&limit=0&reverse=true&${queryString}&prefix=true`;
const response = await fetch(url); const response = await fetch(url);
if (!response.ok) { if (!response.ok) {
throw new Error('network error'); throw new Error('network error');
} }
@ -100,9 +88,11 @@ export const getPublishesFromAdmins = async (admins: string[], groupId) => {
const filterId = adminData.filter( const filterId = adminData.filter(
(data: any) => data.identifier === `symmetric-qchat-group-${groupId}` (data: any) => data.identifier === `symmetric-qchat-group-${groupId}`
); );
if (filterId?.length === 0) { if (filterId?.length === 0) {
return false; return false;
} }
const sortedData = filterId.sort((a: any, b: any) => { const sortedData = filterId.sort((a: any, b: any) => {
// Get the most recent date for both a and b // Get the most recent date for both a and b
const dateA = a.updated ? new Date(a.updated) : new Date(a.created); const dateA = a.updated ? new Date(a.updated) : new Date(a.created);
@ -114,24 +104,18 @@ export const getPublishesFromAdmins = async (admins: string[], groupId) => {
return sortedData[0]; return sortedData[0];
}; };
interface GroupProps { interface GroupProps {
myAddress: string;
isFocused: boolean;
userInfo: any;
balance: number; balance: number;
isFocused: boolean;
myAddress: string;
userInfo: any;
} }
export const timeDifferenceForNotificationChats = 900000; export const timeDifferenceForNotificationChats = 900000;
export const requestQueueMemberNames = new RequestQueueWithPromise(5); export const requestQueueMemberNames = new RequestQueueWithPromise(5);
export const requestQueueAdminMemberNames = new RequestQueueWithPromise(5); export const requestQueueAdminMemberNames = new RequestQueueWithPromise(5);
// const audio = new Audio(chrome.runtime?.getURL("msg-not1.wav"));
export const getGroupAdminsAddress = async (groupNumber: number) => { export const getGroupAdminsAddress = async (groupNumber: number) => {
// const validApi = await findUsableApi();
const response = await fetch( const response = await fetch(
`${getBaseApiReact()}/groups/members/${groupNumber}?limit=0&onlyAdmins=true` `${getBaseApiReact()}/groups/members/${groupNumber}?limit=0&onlyAdmins=true`
); );
@ -422,27 +406,24 @@ export const Group = ({
const [chatMode, setChatMode] = useState('groups'); const [chatMode, setChatMode] = useState('groups');
const [newChat, setNewChat] = useState(false); const [newChat, setNewChat] = useState(false);
const [openSnack, setOpenSnack] = React.useState(false); const [openSnack, setOpenSnack] = useState(false);
const [infoSnack, setInfoSnack] = React.useState(null); const [infoSnack, setInfoSnack] = useState(null);
const [isLoadingNotifyAdmin, setIsLoadingNotifyAdmin] = React.useState(false); const [isLoadingNotifyAdmin, setIsLoadingNotifyAdmin] = useState(false);
const [isLoadingGroups, setIsLoadingGroups] = React.useState(true); const [isLoadingGroups, setIsLoadingGroups] = useState(true);
const [isLoadingGroup, setIsLoadingGroup] = React.useState(false); const [isLoadingGroup, setIsLoadingGroup] = useState(false);
const [firstSecretKeyInCreation, setFirstSecretKeyInCreation] = const [firstSecretKeyInCreation, setFirstSecretKeyInCreation] =
React.useState(false); useState(false);
const [groupSection, setGroupSection] = React.useState('home'); const [groupSection, setGroupSection] = useState('home');
const [groupAnnouncements, setGroupAnnouncements] = useAtom( const [groupAnnouncements, setGroupAnnouncements] = useAtom(
groupAnnouncementsAtom groupAnnouncementsAtom
); );
const [defaultThread, setDefaultThread] = useState(null);
const [defaultThread, setDefaultThread] = React.useState(null); const [isOpenDrawer, setIsOpenDrawer] = useState(false);
const [isOpenDrawer, setIsOpenDrawer] = React.useState(false);
const setIsOpenBlockedUserModal = useSetAtom(isOpenBlockedModalAtom); const setIsOpenBlockedUserModal = useSetAtom(isOpenBlockedModalAtom);
const [hideCommonKeyPopup, setHideCommonKeyPopup] = useState(false);
const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false); const [isLoadingGroupMessage, setIsLoadingGroupMessage] = useState('');
const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState(''); const [drawerMode, setDrawerMode] = useState('groups');
const [drawerMode, setDrawerMode] = React.useState('groups');
const setMutedGroups = useSetAtom(mutedGroupsAtom); const setMutedGroups = useSetAtom(mutedGroupsAtom);
const [mobileViewMode, setMobileViewMode] = useState('home'); const [mobileViewMode, setMobileViewMode] = useState('home');
const [mobileViewModeKeepOpen, setMobileViewModeKeepOpen] = useState(''); const [mobileViewModeKeepOpen, setMobileViewModeKeepOpen] = useState('');
const isFocusedRef = useRef(true); const isFocusedRef = useRef(true);
@ -531,7 +512,10 @@ export const Group = ({
rej(response.error); rej(response.error);
}) })
.catch((error) => { .catch((error) => {
rej(error.message || 'An error occurred'); rej(
error.message ||
t('core:message.error.generic', { postProcess: 'capitalize' })
);
}); });
}); });
} catch (error) { } catch (error) {
@ -557,7 +541,10 @@ export const Group = ({
rej(response.error); rej(response.error);
}) })
.catch((error) => { .catch((error) => {
rej(error.message || 'An error occurred'); rej(
error.message ||
t('core:message.error.generic', { postProcess: 'capitalize' })
);
}); });
}); });
} catch (error) { } catch (error) {
@ -586,7 +573,10 @@ export const Group = ({
rej(response.error); rej(response.error);
}) })
.catch((error) => { .catch((error) => {
rej(error.message || 'An error occurred'); rej(
error.message ||
t('core:message.error.generic', { postProcess: 'capitalize' })
);
}); });
}); });
} catch (error) { } catch (error) {
@ -1106,7 +1096,10 @@ export const Group = ({
rej(response.error); rej(response.error);
}) })
.catch((error) => { .catch((error) => {
rej(error.message || 'An error occurred'); rej(
error.message ||
t('core:message.error.generic', { postProcess: 'capitalize' })
);
}); });
}); });
setInfoSnack({ setInfoSnack({

View File

@ -140,6 +140,7 @@ export const ListOfGroupPromotions = () => {
let data: any[] = []; let data: any[] = [];
const uniqueGroupIds = new Set(); const uniqueGroupIds = new Set();
const oneWeekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000; const oneWeekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
const getPromos = responseData?.map(async (promo: any) => { const getPromos = responseData?.map(async (promo: any) => {
if (promo?.size < 200 && promo.created > oneWeekAgo) { if (promo?.size < 200 && promo.created > oneWeekAgo) {
const name = await requestQueuePromos.enqueue(async () => { const name = await requestQueuePromos.enqueue(async () => {
@ -241,9 +242,12 @@ export const ListOfGroupPromotions = () => {
rej(response.error); rej(response.error);
}) })
.catch((error) => { .catch((error) => {
rej(error.message || 'An error occurred'); rej(
error.message ||
t('core:message.error.generic', { postProcess: 'capitalize' })
);
}); });
}); // TODO translate });
setInfoSnack({ setInfoSnack({
type: 'success', type: 'success',
message: message:

View File

@ -61,7 +61,7 @@ export const ListOfInvites = ({
const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open
const [isLoadingCancelInvite, setIsLoadingCancelInvite] = useState(false); const [isLoadingCancelInvite, setIsLoadingCancelInvite] = useState(false);
const { t } = useTranslation(['core', 'group']); const { t } = useTranslation(['core', 'group']);
const listRef = useRef(); const listRef = useRef(null);
const getInvites = async (groupId) => { const getInvites = async (groupId) => {
try { try {
@ -91,7 +91,6 @@ export const ListOfInvites = ({
const handleCancelInvitation = async (address) => { const handleCancelInvitation = async (address) => {
try { try {
// TODO translate
const fee = await getFee('CANCEL_GROUP_INVITE'); const fee = await getFee('CANCEL_GROUP_INVITE');
await show({ await show({
@ -101,7 +100,9 @@ export const ListOfInvites = ({
}), }),
publishFee: fee.fee + ' QORT', publishFee: fee.fee + ' QORT',
}); });
setIsLoadingCancelInvite(true); setIsLoadingCancelInvite(true);
await new Promise((res, rej) => { await new Promise((res, rej) => {
window window
.sendMessage('cancelInvitationToGroup', { .sendMessage('cancelInvitationToGroup', {
@ -112,8 +113,9 @@ export const ListOfInvites = ({
if (!response?.error) { if (!response?.error) {
setInfoSnack({ setInfoSnack({
type: 'success', type: 'success',
message: message: t('group:message.success.invitation_cancellation', {
'Successfully canceled invitation. It may take a couple of minutes for the changes to propagate', postProcess: 'capitalize',
}),
}); });
setOpenSnack(true); setOpenSnack(true);
handlePopoverClose(); handlePopoverClose();
@ -131,7 +133,11 @@ export const ListOfInvites = ({
.catch((error) => { .catch((error) => {
setInfoSnack({ setInfoSnack({
type: 'error', type: 'error',
message: error.message || 'An error occurred', message:
error.message ||
t('core:message.error.generic', {
postProcess: 'capitalize',
}),
}); });
setOpenSnack(true); setOpenSnack(true);
rej(error); rej(error);
@ -189,7 +195,9 @@ export const ListOfInvites = ({
variant="contained" variant="contained"
onClick={() => handleCancelInvitation(member?.invitee)} onClick={() => handleCancelInvitation(member?.invitee)}
> >
Cancel Invitation {t('core:action.cancel_invitation', {
postProcess: 'capitalize',
})}
</LoadingButton> </LoadingButton>
</Box> </Box>
</Popover> </Popover>
@ -207,6 +215,7 @@ export const ListOfInvites = ({
} }
/> />
</ListItemAvatar> </ListItemAvatar>
<ListItemText primary={member?.name || member?.invitee} /> <ListItemText primary={member?.name || member?.invitee} />
</ListItemButton> </ListItemButton>
</ListItem> </ListItem>
@ -218,7 +227,11 @@ export const ListOfInvites = ({
return ( return (
<div> <div>
<p>Invitees list</p> <p>
{t('group:invitees_list', {
postProcess: 'capitalize',
})}
</p>
<div <div
style={{ style={{
display: 'flex', display: 'flex',

View File

@ -15,11 +15,12 @@ import {
List, List,
} from 'react-virtualized'; } from 'react-virtualized';
import { getNameInfo } from './Group'; import { getNameInfo } from './Group';
import { getBaseApi, getFee } from '../../background'; import { getFee } from '../../background';
import { LoadingButton } from '@mui/lab'; import { LoadingButton } from '@mui/lab';
import { getBaseApiReact } from '../../App'; import { getBaseApiReact } from '../../App';
import { txListAtom } from '../../atoms/global'; import { txListAtom } from '../../atoms/global';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import { useTranslation } from 'react-i18next';
export const getMemberInvites = async (groupNumber) => { export const getMemberInvites = async (groupNumber) => {
const response = await fetch( const response = await fetch(
@ -59,11 +60,11 @@ export const ListOfJoinRequests = ({
}) => { }) => {
const [invites, setInvites] = useState([]); const [invites, setInvites] = useState([]);
const [txList, setTxList] = useAtom(txListAtom); const [txList, setTxList] = useAtom(txListAtom);
const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to 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 const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open
const listRef = useRef(); const listRef = useRef(null);
const [isLoadingAccept, setIsLoadingAccept] = useState(false); const [isLoadingAccept, setIsLoadingAccept] = useState(false);
const { t } = useTranslation(['core', 'group']);
const getInvites = async (groupId) => { const getInvites = async (groupId) => {
try { try {
@ -93,7 +94,8 @@ export const ListOfJoinRequests = ({
const handleAcceptJoinRequest = async (address) => { const handleAcceptJoinRequest = async (address) => {
try { try {
const fee = await getFee('GROUP_INVITE'); // TODO translate const fee = await getFee('GROUP_INVITE');
await show({ await show({
message: t('group:question.perform_transaction', { message: t('group:question.perform_transaction', {
action: 'GROUP_INVITE', action: 'GROUP_INVITE',
@ -101,7 +103,9 @@ export const ListOfJoinRequests = ({
}), }),
publishFee: fee.fee + ' QORT', publishFee: fee.fee + ' QORT',
}); });
setIsLoadingAccept(true); setIsLoadingAccept(true);
await new Promise((res, rej) => { await new Promise((res, rej) => {
window window
.sendMessage('inviteToGroup', { .sendMessage('inviteToGroup', {
@ -114,19 +118,23 @@ export const ListOfJoinRequests = ({
setIsLoadingAccept(false); setIsLoadingAccept(false);
setInfoSnack({ setInfoSnack({
type: 'success', type: 'success',
message: message: t('group:message.success,group_join', {
'Successfully accepted join request. It may take a couple of minutes for the changes to propagate', postProcess: 'capitalize',
}),
}); });
setOpenSnack(true); setOpenSnack(true);
handlePopoverClose(); handlePopoverClose();
res(response); res(response);
setTxList((prev) => [ setTxList((prev) => [
{ {
...response, ...response,
type: 'join-request-accept', type: 'join-request-accept',
label: `Accepted join request: awaiting confirmation`, label: t('group:message.success,invitation_request', {
labelDone: `User successfully joined!`, postProcess: 'capitalize',
}),
labelDone: t('group:message.success,user_joined', {
postProcess: 'capitalize',
}),
done: false, done: false,
groupId, groupId,
qortalAddress: address, qortalAddress: address,
@ -147,13 +155,16 @@ export const ListOfJoinRequests = ({
.catch((error) => { .catch((error) => {
setInfoSnack({ setInfoSnack({
type: 'error', type: 'error',
message: error?.message || 'An error occurred', message:
error?.message ||
t('core:message.error.generic', { postProcess: 'capitalize' }),
}); });
setOpenSnack(true); setOpenSnack(true);
rej(error); rej(error);
}); });
}); });
} catch (error) { } catch (error) {
console.log(error);
} finally { } finally {
setIsLoadingAccept(false); setIsLoadingAccept(false);
} }
@ -161,13 +172,15 @@ export const ListOfJoinRequests = ({
const rowRenderer = ({ index, key, parent, style }) => { const rowRenderer = ({ index, key, parent, style }) => {
const member = invites[index]; const member = invites[index];
const findJoinRequsetInTxList = txList?.find( const findJoinRequestInTxList = txList?.find(
(tx) => (tx) =>
tx?.groupId === groupId && tx?.groupId === groupId &&
tx?.qortalAddress === member?.joiner && tx?.qortalAddress === member?.joiner &&
tx?.type === 'join-request-accept' tx?.type === 'join-request-accept'
); );
if (findJoinRequsetInTxList) return null;
if (findJoinRequestInTxList) return null;
return ( return (
<CellMeasurer <CellMeasurer
key={key} key={key}
@ -210,10 +223,11 @@ export const ListOfJoinRequests = ({
variant="contained" variant="contained"
onClick={() => handleAcceptJoinRequest(member?.joiner)} onClick={() => handleAcceptJoinRequest(member?.joiner)}
> >
Accept {t('core:action.accept', { postProcess: 'capitalize' })}
</LoadingButton> </LoadingButton>
</Box> </Box>
</Popover> </Popover>
<ListItemButton <ListItemButton
onClick={(event) => handlePopoverOpen(event, index)} onClick={(event) => handlePopoverOpen(event, index)}
> >
@ -238,7 +252,7 @@ export const ListOfJoinRequests = ({
return ( return (
<div> <div>
<p>Join request list</p> <p>{t('core:list.join_request', { postProcess: 'capitalize' })}</p>
<div <div
style={{ style={{
position: 'relative', position: 'relative',

View File

@ -133,7 +133,10 @@ export const Settings = ({ open, setOpen, rawWallet }) => {
rej(response.error); rej(response.error);
}) })
.catch((error) => { .catch((error) => {
rej(error.message || 'An error occurred'); rej(
error.message ||
t('core:message.error.generic', { postProcess: 'capitalize' })
);
}); });
}); });
} catch (error) { } catch (error) {

View File

@ -264,7 +264,7 @@ export const UserListOfInvites = ({
}} }}
> >
<p> <p>
{t('core:invite_list', { {t('core:list.invite', {
postProcess: 'capitalize', postProcess: 'capitalize',
})} })}
</p> </p>