Add translations

This commit is contained in:
Nicola Benaglia 2025-05-10 12:12:00 +02:00
parent 88c7aed59a
commit 35d211c44e
3 changed files with 115 additions and 58 deletions

View File

@ -12,7 +12,10 @@
"join_group": "join group", "join_group": "join group",
"kick_member": "kick member from group", "kick_member": "kick member from group",
"invite_member": "invite member", "invite_member": "invite member",
"leave_group": "leave group",
"load_members": "load members with names",
"make_admin": "make an admin", "make_admin": "make an admin",
"manage_members": "manage members",
"refetch_page": "refetch page", "refetch_page": "refetch page",
"remove_admin": "remove as admin", "remove_admin": "remove as admin",
"return_to_thread": "return to threads" "return_to_thread": "return to threads"
@ -27,6 +30,7 @@
"group": { "group": {
"closed": "closed (private) - users need permission to join", "closed": "closed (private) - users need permission to join",
"description": "description of group", "description": "description of group",
"id": "group id",
"invites": "group invites", "invites": "group invites",
"management": "group management", "management": "group management",
"member_number": "number of members", "member_number": "number of members",
@ -36,11 +40,8 @@
}, },
"invitation_expiry": "invitation Expiry Time", "invitation_expiry": "invitation Expiry Time",
"invitees_list": "invitees list", "invitees_list": "invitees list",
"join_link": "join group link",
"join_requests": "join requests", "join_requests": "join requests",
"question": {
"perform_transaction": "would you like to perform a {{action}} transaction?",
"provide_thread": "please provide a thread title"
},
"message": { "message": {
"generic": { "generic": {
"already_in_group": "you are already in this group!", "already_in_group": "you are already in this group!",
@ -48,6 +49,7 @@
"descrypt_wallet": "decrypting wallet...", "descrypt_wallet": "decrypting wallet...",
"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...", "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", "group_invited_you": "{{group}} has invited you",
"loading_members": "loading member list with names... please wait.",
"no_display": "nothing to display", "no_display": "nothing to display",
"no_selection": "no group selected", "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.", "not_part_group": "you are not part of the encrypted group of members. Wait until an admin re-encrypts the keys.",
@ -79,6 +81,9 @@
"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!",
"group_kick": "successfully kicked member from group. It may take a couple of minutes for the changes to propagate", "group_kick": "successfully kicked member from group. It may take a couple of minutes for the changes to propagate",
"group_leave": "successfully requested to leave group. It may take a couple of minutes for the changes to propagate",
"group_leave_name": "left group {{group_name}}: awaiting confirmation",
"group_leave_label": "left group {{name}}: success!",
"group_member_admin": "successfully made member an admin. It may take a couple of minutes for the changes to propagate", "group_member_admin": "successfully made member an admin. It may take a couple of minutes for the changes to propagate",
"group_remove_member": "successfully removed member as an admin. It may take a couple of minutes for the changes to propagate", "group_remove_member": "successfully removed member as an admin. It may take a couple of minutes for the changes to propagate",
"invitation_cancellation": "successfully canceled invitation. It may take a couple of minutes for the changes to propagate", "invitation_cancellation": "successfully canceled invitation. It may take a couple of minutes for the changes to propagate",
@ -89,5 +94,10 @@
"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!" "user_joined": "user successfully joined!"
} }
} },
"question": {
"perform_transaction": "would you like to perform a {{action}} transaction?",
"provide_thread": "please provide a thread title"
},
"thread_posts": "new thread posts"
} }

View File

@ -1,4 +1,4 @@
import * as React from 'react'; import { useEffect, useState } from 'react';
import List from '@mui/material/List'; import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem'; import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton'; import ListItemButton from '@mui/material/ListItemButton';
@ -9,10 +9,12 @@ import { Box, Typography } from '@mui/material';
import { Spacer } from '../../common/Spacer'; import { Spacer } from '../../common/Spacer';
import { CustomLoader } from '../../common/CustomLoader'; import { CustomLoader } from '../../common/CustomLoader';
import VisibilityIcon from '@mui/icons-material/Visibility'; import VisibilityIcon from '@mui/icons-material/Visibility';
import { useTranslation } from 'react-i18next';
export const ListOfThreadPostsWatched = () => { export const ListOfThreadPostsWatched = () => {
const [posts, setPosts] = React.useState([]); const [posts, setPosts] = useState([]);
const [loading, setLoading] = React.useState(true); const [loading, setLoading] = useState(true);
const { t } = useTranslation(['core', 'group']);
const getPosts = async () => { const getPosts = async () => {
try { try {
@ -42,7 +44,10 @@ export const ListOfThreadPostsWatched = () => {
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) {
@ -52,7 +57,7 @@ export const ListOfThreadPostsWatched = () => {
} }
}; };
React.useEffect(() => { useEffect(() => {
getPosts(); getPosts();
}, []); }, []);
@ -73,14 +78,18 @@ export const ListOfThreadPostsWatched = () => {
width: '322px', width: '322px',
}} }}
> >
<Typography // TODO translate <Typography
sx={{ sx={{
fontSize: '13px', fontSize: '13px',
fontWeight: 600, fontWeight: 600,
}} }}
> >
New Thread Posts: {t('group:thread_posts', {
postProcess: 'capitalize',
})}
:
</Typography> </Typography>
<Spacer height="10px" /> <Spacer height="10px" />
</Box> </Box>
@ -98,9 +107,9 @@ export const ListOfThreadPostsWatched = () => {
{loading && posts.length === 0 && ( {loading && posts.length === 0 && (
<Box <Box
sx={{ sx={{
width: '100%',
display: 'flex', display: 'flex',
justifyContent: 'center', justifyContent: 'center',
width: '100%',
}} }}
> >
<CustomLoader /> <CustomLoader />
@ -109,11 +118,11 @@ export const ListOfThreadPostsWatched = () => {
{!loading && posts.length === 0 && ( {!loading && posts.length === 0 && (
<Box <Box
sx={{ sx={{
width: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
display: 'flex',
height: '100%', height: '100%',
justifyContent: 'center',
width: '100%',
}} }}
> >
<Typography <Typography
@ -123,7 +132,9 @@ export const ListOfThreadPostsWatched = () => {
color: 'rgba(255, 255, 255, 0.2)', color: 'rgba(255, 255, 255, 0.2)',
}} }}
> >
Nothing to display {t('group:message.generic.no_display', {
postProcess: 'capitalize',
})}
</Typography> </Typography>
</Box> </Box>
)} )}
@ -131,11 +142,11 @@ export const ListOfThreadPostsWatched = () => {
<List <List
className="scrollable-container" className="scrollable-container"
sx={{ sx={{
width: '100%',
maxWidth: 360,
bgcolor: 'background.paper', bgcolor: 'background.paper',
maxHeight: '300px', maxHeight: '300px',
maxWidth: 360,
overflow: 'auto', overflow: 'auto',
width: '100%',
}} }}
> >
{posts?.map((post) => { {posts?.map((post) => {

View File

@ -1,4 +1,14 @@
import * as React from 'react'; import {
forwardRef,
Fragment,
ReactElement,
Ref,
SyntheticEvent,
useCallback,
useContext,
useEffect,
useState,
} from 'react';
import Button from '@mui/material/Button'; import Button from '@mui/material/Button';
import Dialog from '@mui/material/Dialog'; import Dialog from '@mui/material/Dialog';
import AppBar from '@mui/material/AppBar'; import AppBar from '@mui/material/AppBar';
@ -25,6 +35,7 @@ import { Spacer } from '../../common/Spacer';
import InsertLinkIcon from '@mui/icons-material/InsertLink'; import InsertLinkIcon from '@mui/icons-material/InsertLink';
import { useSetAtom } from 'jotai'; import { useSetAtom } from 'jotai';
import { txListAtom } from '../../atoms/global'; import { txListAtom } from '../../atoms/global';
import { useTranslation } from 'react-i18next';
function a11yProps(index: number) { function a11yProps(index: number) {
return { return {
@ -33,37 +44,35 @@ function a11yProps(index: number) {
}; };
} }
const Transition = React.forwardRef(function Transition( const Transition = forwardRef(function Transition(
props: TransitionProps & { props: TransitionProps & {
children: React.ReactElement; children: ReactElement;
}, },
ref: React.Ref<unknown> ref: Ref<unknown>
) { ) {
return <Slide direction="up" ref={ref} {...props} />; return <Slide direction="up" ref={ref} {...props} />;
}); });
export const ManageMembers = ({ export const ManageMembers = ({
address,
open, open,
setOpen, setOpen,
selectedGroup, selectedGroup,
isAdmin, isAdmin,
isOwner, isOwner,
}) => { }) => {
const [membersWithNames, setMembersWithNames] = React.useState([]); const [membersWithNames, setMembersWithNames] = useState([]);
const [tab, setTab] = React.useState('create'); const [value, setValue] = useState(0);
const [value, setValue] = React.useState(0); const [openSnack, setOpenSnack] = useState(false);
const [openSnack, setOpenSnack] = React.useState(false); const [infoSnack, setInfoSnack] = useState(null);
const [infoSnack, setInfoSnack] = React.useState(null); const [isLoadingMembers, setIsLoadingMembers] = useState(false);
const [isLoadingMembers, setIsLoadingMembers] = React.useState(false); const [isLoadingLeave, setIsLoadingLeave] = useState(false);
const [isLoadingLeave, setIsLoadingLeave] = React.useState(false); const [groupInfo, setGroupInfo] = useState(null);
const [groupInfo, setGroupInfo] = React.useState(null); const handleChange = (event: SyntheticEvent, newValue: number) => {
const handleChange = (event: React.SyntheticEvent, newValue: number) => {
setValue(newValue); setValue(newValue);
}; };
const theme = useTheme(); const theme = useTheme();
const { show } = React.useContext(MyContext); const { t } = useTranslation(['core', 'group']);
const { show } = useContext(MyContext);
const setTxList = useSetAtom(txListAtom); const setTxList = useSetAtom(txListAtom);
const handleClose = () => { const handleClose = () => {
@ -73,7 +82,7 @@ export const ManageMembers = ({
const handleLeaveGroup = async () => { const handleLeaveGroup = async () => {
try { try {
setIsLoadingLeave(true); setIsLoadingLeave(true);
const fee = await getFee('LEAVE_GROUP'); // TODO translate const fee = await getFee('LEAVE_GROUP');
await show({ await show({
message: t('group:question.perform_transaction', { message: t('group:question.perform_transaction', {
action: 'LEAVE_GROUP', action: 'LEAVE_GROUP',
@ -93,8 +102,14 @@ export const ManageMembers = ({
{ {
...response, ...response,
type: 'leave-group', type: 'leave-group',
label: `Left Group ${selectedGroup?.groupName}: awaiting confirmation`, label: t('group:message.success.group_leave_name', {
labelDone: `Left Group ${selectedGroup?.groupName}: success!`, group_name: selectedGroup?.groupName,
postProcess: 'capitalize',
}),
labelDone: t('group:message.success.group_leave_label', {
group_name: selectedGroup?.groupName,
postProcess: 'capitalize',
}),
done: false, done: false,
groupId: selectedGroup?.groupId, groupId: selectedGroup?.groupId,
}, },
@ -103,8 +118,9 @@ export const ManageMembers = ({
res(response); res(response);
setInfoSnack({ setInfoSnack({
type: 'success', type: 'success',
message: message: t('group:message.success.group_leave', {
'Successfully requested to leave group. It may take a couple of minutes for the changes to propagate', postProcess: 'capitalize',
}),
}); });
setOpenSnack(true); setOpenSnack(true);
return; return;
@ -112,7 +128,10 @@ export const ManageMembers = ({
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) {
@ -122,7 +141,7 @@ export const ManageMembers = ({
} }
}; };
const getMembersWithNames = React.useCallback(async (groupId) => { const getMembersWithNames = useCallback(async (groupId) => {
try { try {
setIsLoadingMembers(true); setIsLoadingMembers(true);
const res = await getGroupMembers(groupId); const res = await getGroupMembers(groupId);
@ -153,7 +172,7 @@ export const ManageMembers = ({
} }
}; };
React.useEffect(() => { useEffect(() => {
if (selectedGroup?.groupId) { if (selectedGroup?.groupId) {
getMembers(selectedGroup?.groupId); getMembers(selectedGroup?.groupId);
getGroupInfo(selectedGroup?.groupId); getGroupInfo(selectedGroup?.groupId);
@ -164,7 +183,7 @@ export const ManageMembers = ({
setValue(4); setValue(4);
}; };
React.useEffect(() => { useEffect(() => {
subscribeToEvent('openGroupJoinRequest', openGroupJoinRequestFunc); subscribeToEvent('openGroupJoinRequest', openGroupJoinRequestFunc);
return () => { return () => {
@ -173,7 +192,7 @@ export const ManageMembers = ({
}, []); }, []);
return ( return (
<React.Fragment> <Fragment>
<Dialog <Dialog
fullScreen fullScreen
open={open} open={open}
@ -188,18 +207,20 @@ export const ManageMembers = ({
> >
<Toolbar> <Toolbar>
<Typography sx={{ ml: 2, flex: 1 }} variant="h4" component="div"> <Typography sx={{ ml: 2, flex: 1 }} variant="h4" component="div">
Manage Members {t('group:action.manage_members', { postProcess: 'capitalize' })}
</Typography> </Typography>
<IconButton <IconButton
edge="start"
color="inherit"
onClick={handleClose}
aria-label="close" aria-label="close"
color="inherit"
edge="start"
onClick={handleClose}
> >
<CloseIcon /> <CloseIcon />
</IconButton> </IconButton>
</Toolbar> </Toolbar>
</AppBar> </AppBar>
<Box <Box
sx={{ sx={{
bgcolor: theme.palette.background.default, bgcolor: theme.palette.background.default,
@ -288,12 +309,19 @@ export const ManageMembers = ({
}} }}
> >
<Box> <Box>
<Typography>GroupId: {groupInfo?.groupId}</Typography> <Typography>
{t('group:group.id', { postProcess: 'capitalize' })}:{' '}
<Typography>GroupName: {groupInfo?.groupName}</Typography> {groupInfo?.groupId}
</Typography>
<Typography> <Typography>
Number of members: {groupInfo?.memberCount} {t('group:group.name', { postProcess: 'capitalize' })}:{' '}
{groupInfo?.groupName}
</Typography>
<Typography>
{t('group:group.member_number', { postProcess: 'capitalize' })}:{' '}
{groupInfo?.memberCount}
</Typography> </Typography>
<ButtonBase <ButtonBase
@ -305,7 +333,11 @@ export const ManageMembers = ({
await navigator.clipboard.writeText(link); await navigator.clipboard.writeText(link);
}} }}
> >
<InsertLinkIcon /> <Typography>Join Group Link</Typography> <InsertLinkIcon />
<Typography>
{t('group:join_link', { postProcess: 'capitalize' })}
</Typography>
</ButtonBase> </ButtonBase>
</Box> </Box>
@ -319,10 +351,11 @@ export const ManageMembers = ({
variant="contained" variant="contained"
onClick={handleLeaveGroup} onClick={handleLeaveGroup}
> >
Leave Group {t('group:action.leave_group', { postProcess: 'capitalize' })}
</LoadingButton> </LoadingButton>
)} )}
</Card> </Card>
{value === 0 && ( {value === 0 && (
<Box <Box
sx={{ sx={{
@ -335,7 +368,7 @@ export const ManageMembers = ({
variant="contained" variant="contained"
onClick={() => getMembersWithNames(selectedGroup?.groupId)} onClick={() => getMembersWithNames(selectedGroup?.groupId)}
> >
Load members with names {t('group:action.load_members', { postProcess: 'capitalize' })}
</Button> </Button>
<Spacer height="10px" /> <Spacer height="10px" />
@ -351,6 +384,7 @@ export const ManageMembers = ({
/> />
</Box> </Box>
)} )}
{value === 1 && ( {value === 1 && (
<Box <Box
sx={{ sx={{
@ -430,10 +464,12 @@ export const ManageMembers = ({
<LoadingSnackbar <LoadingSnackbar
open={isLoadingMembers} open={isLoadingMembers}
info={{ info={{
message: 'Loading member list with names... please wait.', message: t('group:message.generic.loading_members', {
postProcess: 'capitalize',
}),
}} }}
/> />
</Dialog> </Dialog>
</React.Fragment> </Fragment>
); );
}; };