Translate app

This commit is contained in:
Nicola Benaglia 2025-05-16 09:08:45 +02:00
parent 45e5e9b660
commit daa8a9145b
12 changed files with 352 additions and 109 deletions

View File

@ -357,7 +357,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
}}
>
<Label>
{t('auth:name', {
{t('core:name', {
postProcess: 'capitalizeFirst',
})}
</Label>
@ -549,7 +549,7 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => {
}}
>
<Label>
{t('auth:name', {
{t('core:name', {
postProcess: 'capitalizeFirst',
})}
</Label>

View File

@ -54,13 +54,23 @@ export const AppsDesktop = ({
const myApp = useMemo(() => {
return availableQapps.find(
(app) => app.name === myName && app.service === 'APP'
(app) =>
app.name === myName &&
app.service ===
t('core:app', {
postProcess: 'capitalizeAll',
})
);
}, [myName, availableQapps]);
const myWebsite = useMemo(() => {
return availableQapps.find(
(app) => app.name === myName && app.service === 'WEBSITE'
(app) =>
app.name === myName &&
app.service ===
t('core:website', {
postProcess: 'capitalizeAll',
})
);
}, [myName, availableQapps]);
@ -247,7 +257,6 @@ export const AppsDesktop = ({
setTabs((prev) => [...prev, newTab]);
setSelectedTab(newTab);
setMode('viewer');
setIsNewTabWindow(false);
};
@ -258,6 +267,7 @@ export const AppsDesktop = ({
unsubscribeFromEvent('addTab', addTabFunc);
};
}, [tabs]);
const setSelectedTabFunc = (e) => {
const data = e.detail?.data;
if (e.detail?.isDevMode) return;
@ -327,9 +337,9 @@ export const AppsDesktop = ({
return (
<AppsParent
sx={{
position: !show && 'fixed',
left: !show && '-200vw',
flexDirection: 'row',
left: !show && '-200vw',
position: !show && 'fixed',
}}
>
<Box
@ -450,6 +460,7 @@ export const AppsDesktop = ({
}}
>
<Spacer height="30px" />
<AppsHomeDesktop
myName={myName}
availableQapps={availableQapps}
@ -476,15 +487,18 @@ export const AppsDesktop = ({
{mode === 'appInfo-from-category' && !selectedTab && (
<AppInfo app={selectedAppInfo} myName={myName} />
)}
<AppsCategoryDesktop
availableQapps={availableQapps}
isShow={mode === 'category' && !selectedTab}
category={selectedCategory}
myName={myName}
/>
{mode === 'publish' && !selectedTab && (
<AppPublish names={myName ? [myName] : []} categories={categories} />
)}
{tabs.map((tab) => {
if (!iframeRefs.current[tab.tabId]) {
iframeRefs.current[tab.tabId] = React.createRef();

View File

@ -1,7 +1,6 @@
import React, { useEffect, useRef, useState } from 'react';
import { AppsDevModeHome } from './AppsDevModeHome';
import { Spacer } from '../../common/Spacer';
import {
executeEvent,
subscribeToEvent,
@ -10,7 +9,6 @@ import {
import { AppsParent } from './Apps-styles';
import AppViewerContainer from './AppViewerContainer';
import ShortUniqueId from 'short-unique-id';
import { Box, ButtonBase, useTheme } from '@mui/material';
import { HomeIcon } from '../../assets/Icons/HomeIcon';
import { Save } from '../Save/Save';
@ -137,7 +135,6 @@ export const AppsDevMode = ({
setTabs(copyTabs);
setSelectedTab(newTab);
setMode('viewer');
setIsNewTabWindow(false);
};
@ -260,6 +257,7 @@ export const AppsDevMode = ({
}
/>
</ButtonBase>
<ButtonBase
onClick={() => {
setDesktopViewMode('apps');
@ -282,6 +280,7 @@ export const AppsDevMode = ({
/>
</IconWrapper>
</ButtonBase>
<ButtonBase
onClick={() => {
setDesktopViewMode('chat');
@ -351,6 +350,7 @@ export const AppsDevMode = ({
}}
>
<Spacer height="30px" />
<AppsDevModeHome
myName={myName}
availableQapps={availableQapps}

View File

@ -1,14 +1,12 @@
import React, { useContext, useMemo, useState } from 'react';
import { useContext, useState } from 'react';
import {
AppCircle,
AppCircleContainer,
AppCircleLabel,
AppLibrarySubTitle,
AppsContainer,
AppsParent,
} from './Apps-styles';
import { Buffer } from 'buffer';
import {
Avatar,
Box,
@ -17,13 +15,11 @@ import {
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
Input,
} from '@mui/material';
import { Add } from '@mui/icons-material';
import { MyContext, getBaseApiReact } from '../../App';
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
import { executeEvent } from '../../utils/events';
import { Spacer } from '../../common/Spacer';
import { useModal } from '../../common/useModal';
@ -31,6 +27,8 @@ import { createEndpoint, isUsingLocal } from '../../background';
import { Label } from '../Group/AddGroup';
import ShortUniqueId from 'short-unique-id';
import swaggerSVG from '../../assets/svgs/swagger.svg';
import { useTranslation } from 'react-i18next';
const uid = new ShortUniqueId({ length: 8 });
export const AppsDevModeHome = ({
@ -43,7 +41,7 @@ export const AppsDevModeHome = ({
const [domain, setDomain] = useState('127.0.0.1');
const [port, setPort] = useState('');
const [selectedPreviewFile, setSelectedPreviewFile] = useState(null);
const { t } = useTranslation(['core', 'group']);
const { isShow, onCancel, onOk, show, message } = useModal();
const {
openSnackGlobal,
@ -61,6 +59,7 @@ export const AppsDevModeHome = ({
console.log('No file selected.');
}
};
const handleSelectDirectry = async (existingDirectoryPath) => {
const { buffer, directoryPath } =
await window.electron.selectAndZipDirectory(existingDirectoryPath);
@ -79,8 +78,7 @@ export const AppsDevModeHome = ({
setInfoSnackCustom({
type: 'error',
message:
'Please use your local node for dev mode! Logout and use Local node.',
message: '',
});
return;
}
@ -115,20 +113,21 @@ export const AppsDevModeHome = ({
const usingLocal = await isUsingLocal();
if (!usingLocal) {
setOpenSnackGlobal(true);
setInfoSnackCustom({
type: 'error',
message:
'Please use your local node for dev mode! Logout and use Local node.',
message: t('core:message.generic.devmode_local_node', {
postProcess: 'capitalizeFirst',
}),
});
return;
}
if (!myName) {
setOpenSnackGlobal(true);
setInfoSnackCustom({
type: 'error',
message: 'You need a name to use preview',
message: t('core:message.generic.name_preview', {
postProcess: 'capitalizeFirst',
}),
});
return;
}
@ -137,15 +136,16 @@ export const AppsDevModeHome = ({
if (!buffer) {
setOpenSnackGlobal(true);
setInfoSnackCustom({
type: 'error',
message: 'Please select a file',
message: t('core:message.generic.select_file', {
postProcess: 'capitalizeFirst',
}),
});
return;
}
const postBody = Buffer.from(buffer).toString('base64');
const postBody = Buffer.from(buffer).toString('base64');
const endpoint = await createEndpoint(
`/arbitrary/APP/${myName}/zip?preview=true`
);
@ -156,6 +156,7 @@ export const AppsDevModeHome = ({
},
body: postBody,
});
if (!response?.ok) throw new Error('Invalid zip');
const previewPath = await response.text();
if (tabId) {
@ -192,20 +193,21 @@ export const AppsDevModeHome = ({
const usingLocal = await isUsingLocal();
if (!usingLocal) {
setOpenSnackGlobal(true);
setInfoSnackCustom({
type: 'error',
message:
'Please use your local node for dev mode! Logout and use Local node.',
message: t('core:message.generic.devmode_local_node', {
postProcess: 'capitalizeFirst',
}),
});
return;
}
if (!myName) {
setOpenSnackGlobal(true);
setInfoSnackCustom({
type: 'error',
message: 'You need a name to use preview',
message: t('core:message.generic.name_preview', {
postProcess: 'capitalizeFirst',
}),
});
return;
}
@ -214,15 +216,16 @@ export const AppsDevModeHome = ({
if (!buffer) {
setOpenSnackGlobal(true);
setInfoSnackCustom({
type: 'error',
message: 'Please select a file',
message: t('core:message.generic.select_file', {
postProcess: 'capitalizeFirst',
}),
});
return;
}
const postBody = Buffer.from(buffer).toString('base64');
const postBody = Buffer.from(buffer).toString('base64');
const endpoint = await createEndpoint(
`/arbitrary/APP/${myName}/zip?preview=true`
);
@ -233,8 +236,15 @@ export const AppsDevModeHome = ({
},
body: postBody,
});
if (!response?.ok) throw new Error('Invalid zip');
if (!response?.ok)
throw new Error(
t('core:message.error.invalid_zip', {
postProcess: 'capitalizeFirst',
})
);
const previewPath = await response.text();
if (tabId) {
executeEvent('appsDevModeUpdateTab', {
data: {
@ -276,7 +286,7 @@ export const AppsDevModeHome = ({
fontSize: '30px',
}}
>
Dev Mode Apps
{t('core:devmode_apps', { postProcess: 'capitalizeFirst' })}
</AppLibrarySubTitle>
</AppsContainer>
@ -301,7 +311,9 @@ export const AppsDevModeHome = ({
<AppCircle>
<Add>+</Add>
</AppCircle>
<AppCircleLabel>Server</AppCircleLabel>
<AppCircleLabel>
{t('core:server', { postProcess: 'capitalizeFirst' })}
</AppCircleLabel>
</AppCircleContainer>
</ButtonBase>
@ -319,7 +331,9 @@ export const AppsDevModeHome = ({
<Add>+</Add>
</AppCircle>
<AppCircleLabel>Zip</AppCircleLabel>
<AppCircleLabel>
{t('core:zip', { postProcess: 'capitalizeFirst' })}
</AppCircleLabel>
</AppCircleContainer>
</ButtonBase>
@ -336,7 +350,9 @@ export const AppsDevModeHome = ({
<AppCircle>
<Add>+</Add>
</AppCircle>
<AppCircleLabel>Directory</AppCircleLabel>
<AppCircleLabel>
{t('core:directory', { postProcess: 'capitalizeFirst' })}
</AppCircleLabel>
</AppCircleContainer>
</ButtonBase>
@ -365,7 +381,9 @@ export const AppsDevModeHome = ({
objectFit: 'fill',
},
}}
alt="Q-Sandbox"
alt={t('core:q_apps.q_sandbox', {
postProcess: 'capitalizeFirst',
})}
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/Q-Sandbox/qortal_avatar?async=true`}
>
<img
@ -378,7 +396,11 @@ export const AppsDevModeHome = ({
</Avatar>
</AppCircle>
<AppCircleLabel>Q-Sandbox</AppCircleLabel>
<AppCircleLabel>
{t('core:q_apps.q_sandbox', {
postProcess: 'capitalizeFirst',
})}
</AppCircleLabel>
</AppCircleContainer>
</ButtonBase>
@ -407,7 +429,9 @@ export const AppsDevModeHome = ({
objectFit: 'fill',
},
}}
alt="API"
alt={t('core:api', {
postProcess: 'capitalizeAll',
})}
src={swaggerSVG}
>
<img
@ -420,7 +444,11 @@ export const AppsDevModeHome = ({
</Avatar>
</AppCircle>
<AppCircleLabel>API</AppCircleLabel>
<AppCircleLabel>
{t('core:api', {
postProcess: 'capitalizeAll',
})}
</AppCircleLabel>
</AppCircleContainer>
</ButtonBase>
</AppsContainer>
@ -437,7 +465,9 @@ export const AppsDevModeHome = ({
}}
>
<DialogTitle id="alert-dialog-title">
{'Add custom framework'}
{t('core:action.add_custom_framework', {
postProcess: 'capitalizeFirst',
})}
</DialogTitle>
<DialogContent>
@ -446,15 +476,22 @@ export const AppsDevModeHome = ({
display: 'flex',
flexDirection: 'column',
gap: '5px',
}} // TODO translate
}}
>
<Label>Domain</Label>
<Label>
{t('core:domain', {
postProcess: 'capitalizeFirst',
})}
</Label>
<Input
placeholder="Domain"
placeholder={t('core:domain', {
postProcess: 'capitalizeFirst',
})}
value={domain}
onChange={(e) => setDomain(e.target.value)}
/>
</Box>
<Box
sx={{
display: 'flex',
@ -463,9 +500,15 @@ export const AppsDevModeHome = ({
marginTop: '15px',
}}
>
<Label>Port</Label>
<Label>
{t('core:port', {
postProcess: 'capitalizeFirst',
})}
</Label>
<Input
placeholder="Port"
placeholder={t('core:port', {
postProcess: 'capitalizeFirst',
})}
value={port}
onChange={(e) => setPort(e.target.value)}
/>
@ -474,15 +517,20 @@ export const AppsDevModeHome = ({
<DialogActions>
<Button variant="contained" onClick={onCancel}>
Close
{t('core:action.close', {
postProcess: 'capitalizeFirst',
})}
</Button>
<Button
disabled={!domain || !port}
variant="contained"
onClick={() => onOk({ portVal: port, domainVal: domain })}
autoFocus
>
Add
{t('core:action.add', {
postProcess: 'capitalizeFirst',
})}
</Button>
</DialogActions>
</Dialog>

View File

@ -23,7 +23,6 @@ export const AppsDevModeNavBar = () => {
const [navigationController, setNavigationController] = useAtom(
navigationControllerAtom
);
const theme = useTheme();
const [isNewTabWindow, setIsNewTabWindow] = useState(false);
const tabsRef = useRef(null);

View File

@ -16,6 +16,7 @@ import ArrowOutwardIcon from '@mui/icons-material/ArrowOutward';
import { AppsPrivate } from './AppsPrivate';
import ThemeSelector from '../Theme/ThemeSelector';
import LanguageSelector from '../Language/LanguageSelector';
import { useTranslation } from 'react-i18next';
export const AppsHomeDesktop = ({
setMode,
@ -26,6 +27,7 @@ export const AppsHomeDesktop = ({
}) => {
const [qortalUrl, setQortalUrl] = useState('');
const theme = useTheme();
const { t } = useTranslation(['core', 'group']);
const openQortalUrl = () => {
try {
@ -41,6 +43,7 @@ export const AppsHomeDesktop = ({
console.log(error);
}
};
return (
<>
<AppsContainer
@ -51,9 +54,9 @@ export const AppsHomeDesktop = ({
<AppLibrarySubTitle
sx={{
fontSize: '30px',
}} // TODO translate
}}
>
Apps Dashboard
{t('core:apps_dashboard', { postProcess: 'capitalizeFirst' })}
</AppLibrarySubTitle>
</AppsContainer>
@ -66,14 +69,14 @@ export const AppsHomeDesktop = ({
>
<Box
sx={{
display: 'flex',
gap: '20px',
alignItems: 'center',
backgroundColor: theme.palette.background.paper,
padding: '7px',
borderRadius: '20px',
width: '100%',
display: 'flex',
gap: '20px',
maxWidth: '500px',
padding: '7px',
width: '100%',
}}
>
<Input
@ -143,7 +146,9 @@ export const AppsHomeDesktop = ({
<AddIcon />
</AppCircle>
<AppCircleLabel>Library</AppCircleLabel>
<AppCircleLabel>
{t('core:library', { postProcess: 'capitalizeFirst' })}
</AppCircleLabel>
</AppCircleContainer>
</ButtonBase>

View File

@ -41,6 +41,7 @@ import { Virtuoso } from 'react-virtuoso';
import { executeEvent } from '../../utils/events';
import { ComposeP, ShowMessageReturnButton } from '../Group/Forum/Mail-styles';
import { ReturnIcon } from '../../assets/Icons/ReturnIcon.tsx';
import { useTranslation } from 'react-i18next';
const officialAppList = [
'q-tube',
@ -104,6 +105,7 @@ export const AppsLibraryDesktop = ({
const [searchValue, setSearchValue] = useState('');
const virtuosoRef = useRef(null);
const theme = useTheme();
const { t } = useTranslation(['core', 'group']);
const officialApps = useMemo(() => {
return availableQapps.filter(
@ -210,9 +212,13 @@ export const AppsLibraryDesktop = ({
ml: 1,
paddingLeft: '12px',
}}
placeholder="Search for apps"
placeholder={t('core:action.search_apps', {
postProcess: 'capitalizeFirst',
})}
inputProps={{
'aria-label': 'Search for apps',
'aria-label': t('core:action.search_apps', {
postProcess: 'capitalizeFirst',
}),
fontSize: '16px',
fontWeight: 400,
}}
@ -273,10 +279,14 @@ export const AppsLibraryDesktop = ({
}}
onClick={() => {
executeEvent('navigateBack', {});
}} // TODO translate
}}
>
<ReturnIcon />
<ComposeP>Return to Apps Dashboard</ComposeP>
<ComposeP>
{t('core:action.return_apps_dashboard', {
postProcess: 'capitalizeFirst',
})}
</ComposeP>
</ShowMessageReturnButton>
<Spacer height="20px" />
@ -302,7 +312,11 @@ export const AppsLibraryDesktop = ({
</AppsWidthLimiter>
) : searchedList?.length === 0 && debouncedValue ? (
<AppsWidthLimiter>
<Typography>No results</Typography>
<Typography>
{t('core:message.generic.no_results', {
postProcess: 'capitalizeFirst',
})}
</Typography>
</AppsWidthLimiter>
) : (
<>
@ -311,7 +325,7 @@ export const AppsLibraryDesktop = ({
fontSize: '30px',
}}
>
Official Apps
{t('core:apps_official', { postProcess: 'capitalizeFirst' })}
</AppLibrarySubTitle>
<Spacer height="45px" />
@ -396,7 +410,13 @@ export const AppsLibraryDesktop = ({
textAlign: 'start',
}}
>
{hasPublishApp ? 'Update your app' : 'Publish your app'}
{hasPublishApp
? t('core:action.update_app', {
postProcess: 'capitalizeFirst',
})
: t('core:action.publish_app', {
postProcess: 'capitalizeFirst',
})}
</AppLibrarySubTitle>
<Spacer height="18px" />
@ -422,7 +442,13 @@ export const AppsLibraryDesktop = ({
}}
>
<PublishQAppCTAButton>
{hasPublishApp ? 'Update' : 'Publish'}
{hasPublishApp
? t('core:action.update', {
postProcess: 'capitalizeFirst',
})
: t('core:action.publish', {
postProcess: 'capitalizeFirst',
})}
</PublishQAppCTAButton>
<Spacer width="20px" />
@ -441,7 +467,9 @@ export const AppsLibraryDesktop = ({
fontSize: '30px',
}}
>
Categories
{t('core:category_other', {
postProcess: 'capitalizeFirst',
})}
</AppLibrarySubTitle>
<Spacer height="18px" />
@ -480,7 +508,7 @@ export const AppsLibraryDesktop = ({
},
}}
>
All
{t('core:all', { postProcess: 'capitalizeFirst' })}
</Box>
</ButtonBase>

View File

@ -32,6 +32,7 @@ import {
sortablePinnedAppsAtom,
} from '../../atoms/global';
import { useAtom, useSetAtom } from 'jotai';
import { useTranslation } from 'react-i18next';
export function saveToLocalStorage(key, subKey, newValue) {
try {
@ -75,7 +76,7 @@ export const AppsNavBarDesktop = ({ disableBack }) => {
);
const theme = useTheme();
const { t } = useTranslation(['core', 'group']);
const [isNewTabWindow, setIsNewTabWindow] = useState(false);
const tabsRef = useRef(null);
const [anchorEl, setAnchorEl] = useState(null);
@ -238,6 +239,7 @@ export const AppsNavBarDesktop = ({ disableBack }) => {
}}
/>
</ButtonBase>
<ButtonBase
onClick={(e) => {
if (!selectedTab) return;
@ -274,9 +276,9 @@ export const AppsNavBarDesktop = ({ disableBack }) => {
paper: {
sx: {
backgroundColor: theme.palette.background.default,
borderRadius: '5px',
color: theme.palette.text.primary,
width: '148px',
borderRadius: '5px',
},
},
}}
@ -375,9 +377,18 @@ export const AppsNavBarDesktop = ({ disableBack }) => {
: theme.palette.text.primary,
},
}}
primary={`${isSelectedAppPinned ? 'Unpin app' : 'Pin app'}`}
primary={`${
isSelectedAppPinned
? t('core:action.unpin_app', {
postProcess: 'capitalizeFirst',
})
: t('core:action.pin_app', {
postProcess: 'capitalizeFirst',
})
}}`}
/>
</MenuItem>
<MenuItem
onClick={() => {
if (selectedTab?.refreshFunc) {
@ -404,6 +415,7 @@ export const AppsNavBarDesktop = ({ disableBack }) => {
}}
/>
</ListItemIcon>
<ListItemText
sx={{
'& .MuiTypography-root': {
@ -447,7 +459,9 @@ export const AppsNavBarDesktop = ({ disableBack }) => {
color: theme.palette.text.primary,
},
}}
primary="Copy link"
primary={t('core:action.copy_link', {
postProcess: 'capitalizeFirst',
})}
/>
</MenuItem>
)}

View File

@ -36,6 +36,7 @@ import { fileToBase64 } from '../../utils/fileReading';
import { objectToBase64 } from '../../qdn/encryption/group-encryption';
import { getFee } from '../../background';
import { useAtom } from 'jotai';
import { useTranslation } from 'react-i18next';
const maxFileSize = 50 * 1024 * 1024; // 50MB
@ -62,6 +63,7 @@ export const AppsPrivate = ({ myName }) => {
const [memberGroups] = useAtom(memberGroupsAtom);
const theme = useTheme();
const { t } = useTranslation(['core', 'group']);
const myGroupsPrivate = useMemo(() => {
return memberGroups?.filter(
@ -98,9 +100,11 @@ export const AppsPrivate = ({ myName }) => {
errors.forEach((error) => {
if (error.code === 'file-too-large') {
console.error(
`File ${file.name} is too large. Max size allowed is ${
maxFileSize / (1024 * 1024)
} MB.`
t('core:message.error.file_too_large', {
filename: file.name,
size: maxFileSize / (1024 * 1024),
postProcess: 'capitalizeFirst',
})
);
}
});
@ -111,7 +115,6 @@ export const AppsPrivate = ({ myName }) => {
const addPrivateApp = async () => {
try {
if (privateAppValues?.groupId === 0) return;
await openApp(privateAppValues, true);
} catch (error) {
console.error(error);
@ -139,9 +142,28 @@ export const AppsPrivate = ({ myName }) => {
const publishPrivateApp = async () => {
try {
if (selectedGroup === 0) return;
if (!logo) throw new Error('Please select an image for a logo');
if (!myName) throw new Error('You need a Qortal name to publish');
if (!newPrivateAppValues?.name) throw new Error('Your app needs a name');
if (!logo)
throw new Error(
t('core:message.generic.select_image', {
postProcess: 'capitalizeFirst',
})
);
if (!myName)
throw new Error(
t('core:message.generic.name_publish', {
postProcess: 'capitalizeFirst',
})
);
if (!newPrivateAppValues?.name)
throw new Error(
t('core:message.error.app_need_name', {
postProcess: 'capitalizeFirst',
})
);
const base64Logo = await fileToBase64(logo);
const base64App = await fileToBase64(file);
const objectToSave = {
@ -160,16 +182,22 @@ export const AppsPrivate = ({ myName }) => {
if (decryptedData?.error) {
throw new Error(
decryptedData?.error || 'Unable to encrypt app. App not published'
decryptedData?.error ||
t('core:message.error.unable_encrypt_app', {
postProcess: 'capitalizeFirst',
})
);
}
const fee = await getFee('ARBITRARY');
await show({
message: 'Would you like to publish this app?',
message: t('core:save_options.publish_app', {
postProcess: 'capitalizeFirst',
}),
publishFee: fee.fee + ' QORT',
});
await new Promise((res, rej) => {
window
.sendMessage('publishOnQDN', {
@ -185,7 +213,12 @@ export const AppsPrivate = ({ myName }) => {
rej(response.error);
})
.catch((error) => {
rej(error.message || 'An error occurred');
rej(
error.message ||
t('core:message.error.generic', {
postProcess: 'capitalizeFirst',
})
);
});
});
@ -203,7 +236,11 @@ export const AppsPrivate = ({ myName }) => {
setOpenSnackGlobal(true);
setInfoSnackCustom({
type: 'error',
message: error?.message || 'Unable to publish app',
message:
error?.message ||
t('core:message.error.unable_publish_app', {
postProcess: 'capitalizeFirst',
}),
});
}
};
@ -241,6 +278,7 @@ export const AppsPrivate = ({ myName }) => {
<AppCircleLabel>Private</AppCircleLabel>
</AppCircleContainer>
</ButtonBase>
{isOpenPrivateModal && (
<Dialog
open={isOpenPrivateModal}
@ -312,10 +350,19 @@ export const AppsPrivate = ({ myName }) => {
display: 'flex',
flexDirection: 'column',
gap: '5px',
}} // TODO translate
}}
>
<Label>Select a group</Label>
<Label>Only private groups will be shown</Label>
<Label>
{t('group:action.select_group', {
postProcess: 'capitalizeFirst',
})}
</Label>
<Label>
{t('group:message.generic.only_private_groups', {
postProcess: 'capitalizeFirst',
})}
</Label>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
@ -330,7 +377,11 @@ export const AppsPrivate = ({ myName }) => {
});
}}
>
<MenuItem value={0}>No group selected</MenuItem>
<MenuItem value={0}>
{t('group:message.generic.no_selection', {
postProcess: 'capitalizeFirst',
})}
</MenuItem>
{myGroupsPrivate
?.filter((item) => !item?.isOpen)
@ -343,7 +394,9 @@ export const AppsPrivate = ({ myName }) => {
})}
</Select>
</Box>
<Spacer height="10px" />
<Box
sx={{
display: 'flex',
@ -352,7 +405,9 @@ export const AppsPrivate = ({ myName }) => {
marginTop: '15px',
}}
>
<Label>name</Label>
<Label>
{t('core:name', { postProcess: 'capitalizeFirst' })}
</Label>
<Input
placeholder="name"
value={privateAppValues?.name}
@ -366,6 +421,7 @@ export const AppsPrivate = ({ myName }) => {
}
/>
</Box>
<Box
sx={{
display: 'flex',
@ -374,9 +430,14 @@ export const AppsPrivate = ({ myName }) => {
marginTop: '15px',
}}
>
<Label>identifier</Label>
<Label>
{t('core:identifier', { postProcess: 'capitalizeFirst' })}
</Label>
<Input
placeholder="identifier"
placeholder={t('core:identifier', {
postProcess: 'capitalizeFirst',
})}
value={privateAppValues?.identifier}
onChange={(e) =>
setPrivateAppValues((prev) => {
@ -397,7 +458,7 @@ export const AppsPrivate = ({ myName }) => {
setIsOpenPrivateModal(false);
}}
>
Close
{t('core:action.close', { postProcess: 'capitalizeFirst' })}
</Button>
<Button
disabled={
@ -410,7 +471,7 @@ export const AppsPrivate = ({ myName }) => {
onClick={() => addPrivateApp()}
autoFocus
>
Access
{t('core:action.access', { postProcess: 'capitalizeFirst' })}
</Button>
</DialogActions>
</>
@ -424,7 +485,9 @@ export const AppsPrivate = ({ myName }) => {
fontSize: '14px',
}}
>
Select .zip file containing static content:{' '}
{t('core:message.generic.select_zip', {
postProcess: 'capitalizeFirst',
})}
</PublishQAppInfo>
<Spacer height="10px" />
@ -435,10 +498,11 @@ export const AppsPrivate = ({ myName }) => {
fontSize: '14px',
}}
>{`
50mb MB maximum`}</PublishQAppInfo>
50mb MB max`}</PublishQAppInfo>
{file && (
<>
<Spacer height="5px" />
<PublishQAppInfo>{`Selected: (${file?.name})`}</PublishQAppInfo>
</>
)}
@ -454,7 +518,13 @@ export const AppsPrivate = ({ myName }) => {
>
{' '}
<input {...getInputProps()} />
{file ? 'Change' : 'Choose'} File
{file
? t('core:action.change_file', {
postProcess: 'capitalizeFirst',
})
: t('core:action.choose_file', {
postProcess: 'capitalizeFirst',
})}
</PublishQAppChoseFile>
<Spacer height="20px" />
@ -466,10 +536,18 @@ export const AppsPrivate = ({ myName }) => {
gap: '5px',
}}
>
<Label>Select a group</Label>
<Label>
Only groups where you are an admin will be shown
{t('group:action.select_group', {
postProcess: 'capitalizeFirst',
})}
</Label>
<Label>
{t('group:amessage.generic.admin_only', {
postProcess: 'capitalizeFirst',
})}
</Label>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
@ -477,7 +555,11 @@ export const AppsPrivate = ({ myName }) => {
label="Groups where you are an admin"
onChange={(e) => setSelectedGroup(e.target.value)}
>
<MenuItem value={0}>No group selected</MenuItem>
<MenuItem value={0}>
{t('group:message.generic.no_selection', {
postProcess: 'capitalizeFirst',
})}
</MenuItem>
{myGroupsWhereIAmAdmin
?.filter((item) => !item?.isOpen)
.map((group) => {
@ -500,9 +582,13 @@ export const AppsPrivate = ({ myName }) => {
marginTop: '15px',
}}
>
<Label>identifier</Label>
<Label>
{t('core:identifier', { postProcess: 'capitalizeFirst' })}
</Label>
<Input
placeholder="identifier"
placeholder={t('core:identifier', {
postProcess: 'capitalizeFirst',
})}
value={newPrivateAppValues?.identifier}
onChange={(e) =>
setNewPrivateAppValues((prev) => {
@ -525,9 +611,14 @@ export const AppsPrivate = ({ myName }) => {
marginTop: '15px',
}}
>
<Label>App name</Label>
<Label>
{t('core:app_name', { postProcess: 'capitalizeFirst' })}
</Label>
<Input
placeholder="App name"
placeholder={t('core:app_name', {
postProcess: 'capitalizeFirst',
})}
value={newPrivateAppValues?.name}
onChange={(e) =>
setNewPrivateAppValues((prev) => {
@ -543,10 +634,15 @@ export const AppsPrivate = ({ myName }) => {
<Spacer height="10px" />
<ImageUploader onPick={(file) => setLogo(file)}>
<Button variant="contained">Choose logo</Button>
<Button variant="contained">
{t('core:action.choose_logo', {
postProcess: 'capitalizeFirst',
})}
</Button>
</ImageUploader>
{logo?.name}
<Spacer height="25px" />
</DialogContent>
@ -558,7 +654,7 @@ export const AppsPrivate = ({ myName }) => {
clearFields();
}}
>
Close
{t('core:action.close', { postProcess: 'capitalizeFirst' })}
</Button>
<Button
@ -572,7 +668,7 @@ export const AppsPrivate = ({ myName }) => {
onClick={() => publishPrivateApp()}
autoFocus
>
Publish
{t('core:action.publish', { postProcess: 'capitalizeFirst' })}
</Button>
</DialogActions>
</>

View File

@ -37,7 +37,6 @@
"your_accounts": "your saved accounts"
}
},
"name": "name",
"node": {
"choose": "choose custom node",
"custom_many": "custom nodes",

View File

@ -1,20 +1,26 @@
{
"action": {
"add": "add",
"add_custom_framework": "add custom framework",
"accept": "accept",
"access": "access",
"backup_account": "backup account",
"backup_wallet": "backup wallet",
"cancel": "cancel",
"cancel_invitation": "cancel invitation",
"change": "change",
"change_file": "change file",
"change_language": "change language",
"choose": "choose",
"choose_file": "choose file",
"close": "close",
"continue": "continue",
"continue_logout": "continue to logout",
"copy_link": "copy link",
"create_apps": "create apps",
"create_file": "create file",
"create_thread": "create thread",
"choose_logo": "choose a logo",
"choose_name": "choose a name",
"decline": "decline",
"decrypt": "decrypt",
@ -34,30 +40,44 @@
"notify": "notify",
"open": "open",
"pin": "pin",
"pin_app": "pin app",
"pin_from_dashboard": "pin from dashboard",
"post": "post",
"post_message": "post message",
"publish": "publish",
"publish_app": "publish your app",
"register_name": "register name",
"remove": "remove",
"return_apps_dashboard": "return to Apps Dashboard",
"save": "save",
"search_apps": "search for apps",
"select_app_type": "select App Type",
"select_category": "select Category",
"select_name_app": "select Name/App",
"start_minting": "start minting",
"unpin": "unpin",
"unpin_from_dashboard": "unpin from dashboard"
"unpin_app": "unpin app",
"unpin_from_dashboard": "unpin from dashboard",
"update": "update",
"update_app": "update your app"
},
"admin": "admin",
"all": "all",
"api": "API",
"app": "app",
"app_name": "app name",
"app_service_type": "app service type",
"apps_dashboard": "apps Dashboard",
"apps_official": "official Apps",
"category": "category",
"category_other": "categories",
"core": {
"block_height": "block height",
"information": "core information",
"peers": "connected peers",
"version": "core version"
},
"domain": "domain",
"ui": {
"version": "UI version"
},
@ -66,14 +86,18 @@
"one": "one"
},
"description": "description",
"devmode_apps": "dev Mode Apps",
"directory": "directory",
"downloading_qdn": "downloading from QDN",
"fee": {
"payment": "payment fee",
"publish": "publish fee"
},
"general_settings": "general settings",
"identifier": "identifier",
"last_height": "last height",
"level": "level",
"library": "library",
"list": {
"invite": "invite list",
"join_request": "join request list",
@ -85,31 +109,41 @@
"message": {
"error": {
"address_not_found": "your address was not found",
"app_need_name": "your app needs a name",
"file_too_large": "file {{ filename }} is too large. Max size allowed is {{ size }} MB.",
"generic": "an error occurred",
"incorrect_password": "incorrect password",
"invalid_zip": "invalid zip",
"minting_account_add": "unable to add minting account",
"minting_account_remove": "unable to remove minting account",
"missing_fields": "missing: {{ fields }}",
"publish_app": "unable to publish app",
"rating_option": "cannot find rating option",
"save_qdn": "unable to save to QDN",
"unable_encrypt_app": "unable to encrypt app. App not published'",
"unable_publish_app": "unable to publish app",
"unable_rate": "unable to rate"
},
"generic": {
"devmode_local_node": "please use your local node for dev mode! Logout and use Local node.",
"name_available": "{{ name }} is available",
"name_benefits": "benefits of a name",
"name_checking": "checking if name already exists",
"name_preview": "you need a name to use preview",
"name_publish": "you need a Qortal name to publish",
"name_rate": "you need a name to rate.",
"name_registration": "your balance is {{ balance }} QORT. A name registration requires a {{ fee }} QORT fee",
"name_unavailable": "{{ name }} is unavailable",
"no_description": "no description",
"no_notifications": "no new notifications",
"no_results": "no results",
"one_app_per_name": "note: Currently, only one App and Website is allowed per Name.",
"publish_data": "publish data to Qortal: anything from apps to videos. Fully decentralized!",
"publishing": "publishing... Please wait.",
"rating": "rating for {{ service }} {{ name }}",
"secure_ownership": "secure ownership of data published by your name. You can even sell your name, along with your data to a third party.",
"select_file": "please select a file",
"select_image": "please select an image for a logo",
"select_zip": "select .zip file containing static content:"
},
"question": {
@ -131,6 +165,7 @@
}
},
"minting_status": "minting status",
"name": "name",
"name_app": "name/App",
"none": "none",
"page": {
@ -140,10 +175,12 @@
"previous": "previous"
},
"payment_notification": "payment notification",
"port": "port",
"price": "price",
"q_apps": {
"about": "about this Q-App",
"q_mail": "q-mail"
"q_mail": "q-mail",
"q_sandbox": "q-Sandbox"
},
"save_options": {
"no_pinned_changes": "you currently do not have any changes to your pinned apps",
@ -162,6 +199,7 @@
"settings": "you are using the export/import way of saving settings.",
"unsaved_changes": " you have unsaved changes to your pinned apps. Save them to QDN."
},
"server": "server",
"settings": "settings",
"supply": "supply",
"tags": "tags",
@ -180,6 +218,7 @@
"title": "title",
"tutorial": "tutorial",
"user_lookup": "user lookup",
"zip": "zip",
"wallet": {
"wallet": "wallet",
"wallet_other": "wallets"

View File

@ -78,6 +78,7 @@
"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.",
"only_private_groups": "only private groups will be shown",
"private_key_copied": "private key copied",
"provide_message": "please provide a first message to the thread",
"secure_place": "keep your private key in a secure place. Do not share!",