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> <Label>
{t('auth:name', { {t('core:name', {
postProcess: 'capitalizeFirst', postProcess: 'capitalizeFirst',
})} })}
</Label> </Label>
@ -549,7 +549,7 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => {
}} }}
> >
<Label> <Label>
{t('auth:name', { {t('core:name', {
postProcess: 'capitalizeFirst', postProcess: 'capitalizeFirst',
})} })}
</Label> </Label>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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