Merge branch 'feat/poc' into feat/datacenter

This commit is contained in:
JimmFly
2023-01-12 16:38:54 +08:00
31 changed files with 170 additions and 173 deletions

View File

@@ -7,7 +7,7 @@ import {
StyledCloseButton, StyledCloseButton,
} from './styles'; } from './styles';
import CloseIcon from '@mui/icons-material/Close'; import CloseIcon from '@mui/icons-material/Close';
import { getWarningMessage, shouldShowWarning } from './utils'; import { useWarningMessage, shouldShowWarning } from './utils';
import EditorOptionMenu from './header-right-items/EditorOptionMenu'; import EditorOptionMenu from './header-right-items/EditorOptionMenu';
import TrashButtonGroup from './header-right-items/TrashButtonGroup'; import TrashButtonGroup from './header-right-items/TrashButtonGroup';
import ThemeModeSwitch from './header-right-items/theme-mode-switch'; import ThemeModeSwitch from './header-right-items/theme-mode-switch';
@@ -22,7 +22,7 @@ const BrowserWarning = ({
}) => { }) => {
return ( return (
<StyledBrowserWarning show={show}> <StyledBrowserWarning show={show}>
{getWarningMessage()} {useWarningMessage()}
<StyledCloseButton onClick={onClose}> <StyledCloseButton onClick={onClose}>
<CloseIcon /> <CloseIcon />
</StyledCloseButton> </StyledCloseButton>

View File

@@ -25,7 +25,7 @@ export const QuickSearchButton = ({
const { triggerQuickSearchModal } = useModal(); const { triggerQuickSearchModal } = useModal();
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<Tooltip content={t('Switch to')} placement="bottom"> <Tooltip content={t('Jump to')} placement="bottom">
<StyledIconButtonWithAnimate <StyledIconButtonWithAnimate
data-testid="header-quickSearchButton" data-testid="header-quickSearchButton"
{...props} {...props}

View File

@@ -1,4 +1,5 @@
import getIsMobile from '@/utils/get-is-mobile'; import getIsMobile from '@/utils/get-is-mobile';
import { Trans, useTranslation } from '@affine/i18n';
// Inspire by https://stackoverflow.com/a/4900484/8415727 // Inspire by https://stackoverflow.com/a/4900484/8415727
const getChromeVersion = () => { const getChromeVersion = () => {
const raw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./); const raw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);
@@ -19,20 +20,20 @@ export const shouldShowWarning = () => {
); );
}; };
export const getWarningMessage = () => { export const useWarningMessage = () => {
const { t } = useTranslation();
if (!getIsChrome()) { if (!getIsChrome()) {
return ( return (
<span> <span>
We recommend the <strong>Chrome</strong> browser for optimal experience. <Trans i18nKey="recommendBrowser">
We recommend the <strong>Chrome</strong> browser for optimal
experience.
</Trans>
</span> </span>
); );
} }
if (getChromeVersion() < minimumChromeVersion) { if (getChromeVersion() < minimumChromeVersion) {
return ( return <span>{t('upgradeBrowser')}</span>;
<span>
Please upgrade to the latest version of Chrome for the best experience.
</span>
);
} }
return ''; return '';
}; };

View File

@@ -1,5 +1,6 @@
import { styled } from '@/styles'; import { styled } from '@/styles';
import Loading from './Loading'; import Loading from './Loading';
import { useTranslation } from '@affine/i18n';
// Used for the full page loading // Used for the full page loading
const StyledLoadingContainer = styled('div')(() => { const StyledLoadingContainer = styled('div')(() => {
@@ -17,12 +18,13 @@ const StyledLoadingContainer = styled('div')(() => {
}; };
}); });
export const PageLoading = ({ text = 'Loading...' }: { text?: string }) => { export const PageLoading = ({ text }: { text?: string }) => {
const { t } = useTranslation();
return ( return (
<StyledLoadingContainer> <StyledLoadingContainer>
<div className="wrapper"> <div className="wrapper">
<Loading /> <Loading />
<h1>{text}</h1> <h1>{text ? text : t('Loading')}</h1>
</div> </div>
</StyledLoadingContainer> </StyledLoadingContainer>
); );

View File

@@ -3,9 +3,10 @@ import { styled } from '@/styles';
import { Button } from '@/ui/button'; import { Button } from '@/ui/button';
// import { useModal } from '@/providers/GlobalModalProvider'; // import { useModal } from '@/providers/GlobalModalProvider';
import { GoogleIcon, StayLogOutIcon } from './Icons'; import { GoogleIcon, StayLogOutIcon } from './Icons';
import { useTranslation } from '@affine/i18n';
export const GoogleLoginButton = () => { export const GoogleLoginButton = () => {
// const { triggerLoginModal } = useModal(); // const { triggerLoginModal } = useModal();
const { t } = useTranslation();
return ( return (
<StyledGoogleButton <StyledGoogleButton
onClick={() => { onClick={() => {
@@ -24,8 +25,10 @@ export const GoogleLoginButton = () => {
<GoogleIcon /> <GoogleIcon />
</IconWrapper> </IconWrapper>
<TextWrapper> <TextWrapper>
<Title>Continue with Google</Title> <Title>{t('Continue with Google')}</Title>
<Description>Set up an AFFiNE account to sync data</Description> <Description>
{t('Set up an AFFiNE account to sync data')}
</Description>
</TextWrapper> </TextWrapper>
</ButtonWrapper> </ButtonWrapper>
</StyledGoogleButton> </StyledGoogleButton>
@@ -33,6 +36,7 @@ export const GoogleLoginButton = () => {
}; };
export const StayLogOutButton = () => { export const StayLogOutButton = () => {
const { t } = useTranslation();
return ( return (
<StyledStayLogOutButton> <StyledStayLogOutButton>
<ButtonWrapper> <ButtonWrapper>
@@ -40,8 +44,8 @@ export const StayLogOutButton = () => {
<StayLogOutIcon /> <StayLogOutIcon />
</IconWrapper> </IconWrapper>
<TextWrapper> <TextWrapper>
<Title>Stay logged out</Title> <Title>{t('Stay logged out')}</Title>
<Description>All changes are saved locally</Description> <Description>{t('All changes are saved locally')}</Description>
</TextWrapper> </TextWrapper>
</ButtonWrapper> </ButtonWrapper>
</StyledStayLogOutButton> </StyledStayLogOutButton>

View File

@@ -3,13 +3,14 @@ import { styled } from '@/styles';
import { Modal, ModalWrapper, ModalCloseButton } from '@/ui/modal'; import { Modal, ModalWrapper, ModalCloseButton } from '@/ui/modal';
import { TextButton } from '@/ui/button'; import { TextButton } from '@/ui/button';
import { GoogleLoginButton, StayLogOutButton } from './LoginOptionButton'; import { GoogleLoginButton, StayLogOutButton } from './LoginOptionButton';
import { useTranslation } from '@affine/i18n';
interface LoginModalProps { interface LoginModalProps {
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;
} }
export const LoginModal = ({ open, onClose }: LoginModalProps) => { export const LoginModal = ({ open, onClose }: LoginModalProps) => {
const { t } = useTranslation();
return ( return (
<Modal open={open} onClose={onClose} data-testid="login-modal"> <Modal open={open} onClose={onClose} data-testid="login-modal">
<ModalWrapper width={620} height={334}> <ModalWrapper width={620} height={334}>
@@ -23,12 +24,12 @@ export const LoginModal = ({ open, onClose }: LoginModalProps) => {
/> />
</Header> </Header>
<Content> <Content>
<ContentTitle>Currently not logged in</ContentTitle> <ContentTitle>{t('NotLoggedIn')}</ContentTitle>
<GoogleLoginButton /> <GoogleLoginButton />
<StayLogOutButton /> <StayLogOutButton />
</Content> </Content>
<Footer> <Footer>
<TextButton icon={<StyledResetIcon />}>Clear local data</TextButton> <TextButton icon={<StyledResetIcon />}>{t('ClearData')}</TextButton>
</Footer> </Footer>
</ModalWrapper> </ModalWrapper>
</Modal> </Modal>

View File

@@ -3,8 +3,10 @@ import Modal, { ModalCloseButton, ModalWrapper } from '@/ui/modal';
import getIsMobile from '@/utils/get-is-mobile'; import getIsMobile from '@/utils/get-is-mobile';
import { StyledButton, StyledContent, StyledTitle } from './styles'; import { StyledButton, StyledContent, StyledTitle } from './styles';
import bg from './bg.png'; import bg from './bg.png';
import { useTranslation } from '@affine/i18n';
export const MobileModal = () => { export const MobileModal = () => {
const [showModal, setShowModal] = useState(getIsMobile()); const [showModal, setShowModal] = useState(getIsMobile());
const { t } = useTranslation();
return ( return (
<Modal <Modal
open={showModal} open={showModal}
@@ -25,20 +27,17 @@ export const MobileModal = () => {
}} }}
/> />
<StyledTitle>Ooops!</StyledTitle> <StyledTitle>{t('Ooops!')}</StyledTitle>
<StyledContent> <StyledContent>
<p>Looks like you are browsing on a mobile device.</p> <p>{t('mobile device')}</p>
<p> <p>{t('mobile device description')}</p>
We are still working on mobile support and recommend you use a
desktop device.
</p>
</StyledContent> </StyledContent>
<StyledButton <StyledButton
onClick={() => { onClick={() => {
setShowModal(false); setShowModal(false);
}} }}
> >
Got it {t('Got it')}
</StyledButton> </StyledButton>
</ModalWrapper> </ModalWrapper>
</Modal> </Modal>

View File

@@ -1,6 +1,9 @@
import React from 'react'; import React from 'react';
import { Empty } from '@/ui/empty'; import { Empty } from '@/ui/empty';
export const PageListEmpty = () => { import { useTranslation } from '@affine/i18n';
export const PageListEmpty = (props: { listType?: string }) => {
const { listType } = props;
const { t } = useTranslation();
return ( return (
<div style={{ textAlign: 'center' }}> <div style={{ textAlign: 'center' }}>
<Empty <Empty
@@ -8,8 +11,10 @@ export const PageListEmpty = () => {
height={300} height={300}
sx={{ marginTop: '100px', marginBottom: '30px' }} sx={{ marginTop: '100px', marginBottom: '30px' }}
/> />
<p>Tips: Click Add to Favourites/Trash and the page will appear here.</p> {listType === 'all' && <p>{t('emptyAllPages')}</p>}
<p>(Designer is grappling with designing)</p> {listType === 'favorite' && <p>{t('emptyFavourite')}</p>}
{listType === 'trash' && <p>{t('emptyTrash')}</p>}
<p>{t('still designed')}</p>
</div> </div>
); );
}; };

View File

@@ -68,17 +68,19 @@ export const PageList = ({
showFavoriteTag = false, showFavoriteTag = false,
isTrash = false, isTrash = false,
isPublic = false, isPublic = false,
listType,
}: { }: {
pageList: PageMeta[]; pageList: PageMeta[];
showFavoriteTag?: boolean; showFavoriteTag?: boolean;
isTrash?: boolean; isTrash?: boolean;
isPublic?: boolean; isPublic?: boolean;
listType?: 'all' | 'trash' | 'favorite';
}) => { }) => {
const router = useRouter(); const router = useRouter();
const { currentWorkspace } = useAppState(); const { currentWorkspace } = useAppState();
const { t } = useTranslation(); const { t } = useTranslation();
if (pageList.length === 0) { if (pageList.length === 0) {
return <Empty />; return <Empty listType={listType} />;
} }
return ( return (

View File

@@ -9,6 +9,7 @@ import { SearchIcon } from '@blocksuite/icons';
import { StyledInputContent, StyledLabel } from './style'; import { StyledInputContent, StyledLabel } from './style';
import { Command } from 'cmdk'; import { Command } from 'cmdk';
import { useAppState } from '@/providers/app-state-provider'; import { useAppState } from '@/providers/app-state-provider';
import { useTranslation } from '@affine/i18n';
export const Input = (props: { export const Input = (props: {
query: string; query: string;
setQuery: Dispatch<SetStateAction<string>>; setQuery: Dispatch<SetStateAction<string>>;
@@ -18,7 +19,7 @@ export const Input = (props: {
const [inputValue, setInputValue] = useState(''); const [inputValue, setInputValue] = useState('');
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const { currentWorkspace } = useAppState(); const { currentWorkspace } = useAppState();
const { t } = useTranslation();
useEffect(() => { useEffect(() => {
inputRef.current?.addEventListener( inputRef.current?.addEventListener(
'blur', 'blur',
@@ -78,8 +79,10 @@ export const Input = (props: {
}} }}
placeholder={ placeholder={
currentWorkspace?.isPublish currentWorkspace?.isPublish
? `Search in ${currentWorkspace?.blocksuiteWorkspace?.meta.name}` ? t('Quick search placeholder2', {
: 'Quick Search...' workspace: currentWorkspace?.blocksuiteWorkspace?.meta.name,
})
: t('Quick search placeholder')
} }
/> />
</StyledInputContent> </StyledInputContent>

View File

@@ -79,7 +79,7 @@ export const Results = (props: {
</StyledNotFound> </StyledNotFound>
) )
) : ( ) : (
<Command.Group heading={t('Switch to')}> <Command.Group heading={t('Jump to')}>
{List.map(link => { {List.map(link => {
return ( return (
<Command.Item <Command.Item

View File

@@ -14,6 +14,7 @@ import { WorkspaceAvatar } from '@/components/workspace-avatar';
import { useAppState } from '@/providers/app-state-provider'; import { useAppState } from '@/providers/app-state-provider';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useConfirm } from '@/providers/ConfirmProvider'; import { useConfirm } from '@/providers/ConfirmProvider';
import { useTranslation } from '@affine/i18n';
interface WorkspaceModalProps { interface WorkspaceModalProps {
open: boolean; open: boolean;
@@ -26,6 +27,7 @@ export const WorkspaceModal = ({ open, onClose }: WorkspaceModalProps) => {
const { workspaceList, currentWorkspace, login, user, logout } = const { workspaceList, currentWorkspace, login, user, logout } =
useAppState(); useAppState();
const router = useRouter(); const router = useRouter();
const { t } = useTranslation();
return ( return (
<div> <div>
<Modal open={open} onClose={onClose}> <Modal open={open} onClose={onClose}>
@@ -34,7 +36,7 @@ export const WorkspaceModal = ({ open, onClose }: WorkspaceModalProps) => {
style={{ padding: '10px', display: 'flex', flexDirection: 'column' }} style={{ padding: '10px', display: 'flex', flexDirection: 'column' }}
> >
<Header> <Header>
<ContentTitle>My Workspaces</ContentTitle> <ContentTitle>{t('My Workspaces')}</ContentTitle>
{/* <LanguageMenu /> */} {/* <LanguageMenu /> */}
<ModalCloseButton <ModalCloseButton
top={6} top={6}
@@ -123,13 +125,13 @@ export const WorkspaceModal = ({ open, onClose }: WorkspaceModalProps) => {
marginRight: '10px', marginRight: '10px',
}} }}
/> />
Create Or Import {t('Create Or Import')}
</Button> </Button>
</li> </li>
</WorkspaceList> </WorkspaceList>
<p style={{ fontSize: '14px', color: '#ccc', margin: '12px 0' }}> <p style={{ fontSize: '14px', color: '#ccc', margin: '12px 0' }}>
Tips:Workspace is your virtual space to capture, create and plan {t('Tips')}
as just one person or together as a team. {t('Workspace description')}
</p> </p>
</Content> </Content>
<Footer> <Footer>
@@ -137,10 +139,10 @@ export const WorkspaceModal = ({ open, onClose }: WorkspaceModalProps) => {
<Button <Button
onClick={async () => { onClick={async () => {
await login(); await login();
toast('Login success'); toast(t('login success'));
}} }}
> >
Sign in AFFiNE Cloud {t('Sign in')}
</Button> </Button>
) : ( ) : (
<Button <Button
@@ -161,7 +163,7 @@ export const WorkspaceModal = ({ open, onClose }: WorkspaceModalProps) => {
}); });
}} }}
> >
Sign out of AFFiNE Cloud {t('Sign out')}
</Button> </Button>
)} )}
</Footer> </Footer>

View File

@@ -1,6 +1,6 @@
import { styled } from '@/styles'; import { styled } from '@/styles';
import { WorkspaceUnit } from '@affine/datacenter'; import { WorkspaceUnit } from '@affine/datacenter';
import { Trans } from '@affine/i18n';
export const ExportPageTitleContainer = styled('div')(() => { export const ExportPageTitleContainer = styled('div')(() => {
return { return {
display: 'flex', display: 'flex',
@@ -12,8 +12,13 @@ export const ExportPageTitleContainer = styled('div')(() => {
export const ExportPage = ({ workspace }: { workspace: WorkspaceUnit }) => { export const ExportPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
return ( return (
<ExportPageTitleContainer> <ExportPageTitleContainer>
Export Workspace{' '} <Trans i18nKey="Export Workspace">
<code style={{ margin: '0 10px' }}>{workspace.name}</code> Is Comming Export Workspace
<code style={{ margin: '0 10px' }}>
{{ workspace: workspace.name }}
</code>
Is Comming
</Trans>
</ExportPageTitleContainer> </ExportPageTitleContainer>
); );
}; };

View File

@@ -12,11 +12,11 @@ import { toast } from '@/ui/toast';
// import { useAppState } from '@/providers/app-state-provider3'; // import { useAppState } from '@/providers/app-state-provider3';
import { WorkspaceUnit } from '@affine/datacenter'; import { WorkspaceUnit } from '@affine/datacenter';
import { useWorkspaceHelper } from '@/hooks/use-workspace-helper'; import { useWorkspaceHelper } from '@/hooks/use-workspace-helper';
import { useTranslation } from '@affine/i18n';
export const PublishPage = ({ workspace }: { workspace: WorkspaceUnit }) => { export const PublishPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
const shareUrl = window.location.host + '/public-workspace/' + workspace.id; const shareUrl = window.location.host + '/public-workspace/' + workspace.id;
const { publishWorkspace, enableWorkspace } = useWorkspaceHelper(); const { publishWorkspace, enableWorkspace } = useWorkspaceHelper();
const { t } = useTranslation();
const togglePublic = async (flag: boolean) => { const togglePublic = async (flag: boolean) => {
await publishWorkspace(workspace.id.toString(), flag); await publishWorkspace(workspace.id.toString(), flag);
}; };
@@ -37,22 +37,21 @@ export const PublishPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
{workspace?.published ? ( {workspace?.published ? (
<> <>
<StyledPublishExplanation> <StyledPublishExplanation>
Publishing to web requires AFFiNE Cloud service . {t('Publishing')}
</StyledPublishExplanation> </StyledPublishExplanation>
<StyledSettingH2>Share with link</StyledSettingH2> <StyledSettingH2>{t('Share with link')}</StyledSettingH2>
<StyledPublishCopyContainer> <StyledPublishCopyContainer>
<Input width={500} value={shareUrl} disabled={true}></Input> <Input width={500} value={shareUrl} disabled={true}></Input>
<StyledCopyButtonContainer> <StyledCopyButtonContainer>
<Button onClick={copyUrl} type="primary" shape="circle"> <Button onClick={copyUrl} type="primary" shape="circle">
Copy Link {t('Copy Link')}
</Button> </Button>
</StyledCopyButtonContainer> </StyledCopyButtonContainer>
</StyledPublishCopyContainer> </StyledPublishCopyContainer>
</> </>
) : ( ) : (
<StyledPublishExplanation> <StyledPublishExplanation>
After publishing to the web, everyone can view the content of {'Publishing Description'}
this workspace through the link.
</StyledPublishExplanation> </StyledPublishExplanation>
)} )}
</StyledPublishContent> </StyledPublishContent>
@@ -64,7 +63,7 @@ export const PublishPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
type="primary" type="primary"
shape="circle" shape="circle"
> >
Stop publishing {t('Stop publishing')}
</Button> </Button>
) : ( ) : (
<Button <Button
@@ -74,7 +73,7 @@ export const PublishPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
type="primary" type="primary"
shape="circle" shape="circle"
> >
Publish to web {t('Publish to web')}
</Button> </Button>
)} )}
</div> </div>
@@ -82,7 +81,7 @@ export const PublishPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
<StyledPublishContent> <StyledPublishContent>
<> <>
<StyledPublishExplanation> <StyledPublishExplanation>
Publishing to web requires AFFiNE Cloud service. {t('Publishing')}
</StyledPublishExplanation> </StyledPublishExplanation>
<StyledPublishCopyContainer> <StyledPublishCopyContainer>
@@ -93,7 +92,7 @@ export const PublishPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
type="primary" type="primary"
shape="circle" shape="circle"
> >
Enable AFFiNE Cloud {t('Enable AFFiNE Cloud')}
</Button> </Button>
</StyledPublishCopyContainer> </StyledPublishCopyContainer>
</> </>

View File

@@ -8,17 +8,19 @@ import { Button } from '@/ui/button';
import { Menu, MenuItem } from '@/ui/menu'; import { Menu, MenuItem } from '@/ui/menu';
import { WorkspaceUnit } from '@affine/datacenter'; import { WorkspaceUnit } from '@affine/datacenter';
import { useWorkspaceHelper } from '@/hooks/use-workspace-helper'; import { useWorkspaceHelper } from '@/hooks/use-workspace-helper';
import { Trans, useTranslation } from '@affine/i18n';
export const SyncPage = ({ workspace }: { workspace: WorkspaceUnit }) => { export const SyncPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
const { enableWorkspace } = useWorkspaceHelper(); const { enableWorkspace } = useWorkspaceHelper();
const { t } = useTranslation();
return ( return (
<div> <div>
<StyledPublishContent> <StyledPublishContent>
{workspace?.provider === 'local' ? ( {workspace?.provider === 'local' ? (
<> <>
<StyledPublishExplanation> <StyledPublishExplanation>
{workspace.name ?? 'Affine'} is Local Workspace. All data is {t('Sync Description', {
stored on the current device. You can enable AFFiNE Cloud for this workspaceName: workspace.name ?? 'Affine',
workspace to keep data in sync with the cloud. })}
</StyledPublishExplanation> </StyledPublishExplanation>
<StyledPublishCopyContainer> <StyledPublishCopyContainer>
@@ -29,15 +31,18 @@ export const SyncPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
type="primary" type="primary"
shape="circle" shape="circle"
> >
Enable AFFiNE Cloud {t('Enable AFFiNE Cloud')}
</Button> </Button>
</StyledPublishCopyContainer> </StyledPublishCopyContainer>
</> </>
) : ( ) : (
<> <>
<StyledPublishExplanation> <StyledPublishExplanation>
<code>{workspace.name ?? 'Affine'}</code> is Cloud Workspace. All <Trans i18nKey="Sync Description2">
data will be synchronized and saved to the AFFiNE <code>{{ workspaceName: workspace.name ?? 'Affine' }}</code>
is Cloud Workspace. All data will be synchronised and saved to
the AFFiNE
</Trans>
</StyledPublishExplanation> </StyledPublishExplanation>
<StyledPublishCopyContainer> <StyledPublishCopyContainer>
<Menu <Menu
@@ -49,7 +54,7 @@ export const SyncPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
}} }}
icon={<DownloadIcon />} icon={<DownloadIcon />}
> >
Download core data to device {t('Download data to device', { CoreOrAll: 'core' })}
</MenuItem> </MenuItem>
<MenuItem <MenuItem
onClick={() => { onClick={() => {
@@ -57,14 +62,16 @@ export const SyncPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
}} }}
icon={<DownloadIcon />} icon={<DownloadIcon />}
> >
Download all data to device {t('Download data to device', { CoreOrAll: 'all' })}
</MenuItem> </MenuItem>
</> </>
} }
placement="bottom-end" placement="bottom-end"
disablePortal={true} disablePortal={true}
> >
<Button>Download all data to device</Button> <Button>
{t('Download data to device', { CoreOrAll: 'all' })}
</Button>
</Menu> </Menu>
</StyledPublishCopyContainer> </StyledPublishCopyContainer>
</> </>

View File

@@ -16,6 +16,7 @@ import { Upload } from '@/components/file-upload';
import { WorkspaceAvatar } from '@/components/workspace-avatar'; import { WorkspaceAvatar } from '@/components/workspace-avatar';
import { WorkspaceUnit } from '@affine/datacenter'; import { WorkspaceUnit } from '@affine/datacenter';
import { useWorkspaceHelper } from '@/hooks/use-workspace-helper'; import { useWorkspaceHelper } from '@/hooks/use-workspace-helper';
import { useTranslation } from '@affine/i18n';
export const GeneralPage = ({ workspace }: { workspace: WorkspaceUnit }) => { export const GeneralPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
const [showDelete, setShowDelete] = useState<boolean>(false); const [showDelete, setShowDelete] = useState<boolean>(false);
const [showLeave, setShowLeave] = useState<boolean>(false); const [showLeave, setShowLeave] = useState<boolean>(false);
@@ -23,6 +24,8 @@ export const GeneralPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
const [workspaceName, setWorkspaceName] = useState<string>(workspace.name); const [workspaceName, setWorkspaceName] = useState<string>(workspace.name);
const { currentWorkspace } = useAppState(); const { currentWorkspace } = useAppState();
const { updateWorkspace } = useWorkspaceHelper(); const { updateWorkspace } = useWorkspaceHelper();
const { t } = useTranslation();
const isOwner = true; const isOwner = true;
const handleChangeWorkSpaceName = (newName: string) => { const handleChangeWorkSpaceName = (newName: string) => {
setWorkspaceName(newName); setWorkspaceName(newName);
@@ -56,7 +59,7 @@ export const GeneralPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
return workspace ? ( return workspace ? (
<div> <div>
<StyledSettingH2 marginTop={56}>Workspace Icon</StyledSettingH2> <StyledSettingH2 marginTop={56}>{t('Workspace Icon')}</StyledSettingH2>
<StyledSettingAvatarContent> <StyledSettingAvatarContent>
<div <div
style={{ style={{
@@ -74,16 +77,16 @@ export const GeneralPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
accept="image/gif,image/jpeg,image/jpg,image/png,image/svg" accept="image/gif,image/jpeg,image/jpg,image/png,image/svg"
fileChange={fileChange} fileChange={fileChange}
> >
<Button loading={uploading}>Upload</Button> <Button loading={uploading}>{t('Upload')}</Button>
</Upload> </Upload>
{/* TODO: add upload logic */} {/* TODO: add upload logic */}
</StyledSettingAvatarContent> </StyledSettingAvatarContent>
<StyledSettingH2 marginTop={20}>Workspace Name</StyledSettingH2> <StyledSettingH2 marginTop={20}>{t('Workspace Name')}</StyledSettingH2>
<StyledSettingInputContainer> <StyledSettingInputContainer>
<Input <Input
width={327} width={327}
value={workspaceName} value={workspaceName}
placeholder="Workspace Name" placeholder={t('Workspace Name')}
maxLength={14} maxLength={14}
minLength={1} minLength={1}
onChange={handleChangeWorkSpaceName} onChange={handleChangeWorkSpaceName}
@@ -97,7 +100,7 @@ export const GeneralPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
</TextButton> </TextButton>
</StyledSettingInputContainer> </StyledSettingInputContainer>
<StyledSettingH2 marginTop={20}>Workspace Type</StyledSettingH2> <StyledSettingH2 marginTop={20}>{t('Workspace Type')}</StyledSettingH2>
<StyledSettingInputContainer> <StyledSettingInputContainer>
<code>{workspace.provider} </code> <code>{workspace.provider} </code>
</StyledSettingInputContainer> </StyledSettingInputContainer>
@@ -105,7 +108,7 @@ export const GeneralPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
{isOwner ? ( {isOwner ? (
<> <>
<Button type="danger" shape="circle" onClick={handleClickDelete}> <Button type="danger" shape="circle" onClick={handleClickDelete}>
Delete Workspace {t('Delete Workspace')}
</Button> </Button>
<WorkspaceDelete <WorkspaceDelete
open={showDelete} open={showDelete}
@@ -116,7 +119,7 @@ export const GeneralPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
) : ( ) : (
<> <>
<Button type="danger" shape="circle" onClick={handleClickLeave}> <Button type="danger" shape="circle" onClick={handleClickLeave}>
Leave Workspace {t('Leave Workspace')}
</Button> </Button>
<WorkspaceLeave <WorkspaceLeave
open={showLeave} open={showLeave}

View File

@@ -18,7 +18,7 @@ import {
// Workspace, // Workspace,
} from '@/hooks/mock-data/mock'; } from '@/hooks/mock-data/mock';
import { WorkspaceUnit } from '@affine/datacenter'; import { WorkspaceUnit } from '@affine/datacenter';
import { Trans, useTranslation } from '@affine/i18n';
interface WorkspaceDeleteProps { interface WorkspaceDeleteProps {
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;
@@ -31,6 +31,7 @@ export const WorkspaceDelete = ({
workspace, workspace,
}: WorkspaceDeleteProps) => { }: WorkspaceDeleteProps) => {
const [deleteStr, setDeleteStr] = useState<string>(''); const [deleteStr, setDeleteStr] = useState<string>('');
const { t } = useTranslation();
const router = useRouter(); const router = useRouter();
const handlerInputChange = (workspaceName: string) => { const handlerInputChange = (workspaceName: string) => {
@@ -55,31 +56,40 @@ export const WorkspaceDelete = ({
<Modal open={open} onClose={onClose}> <Modal open={open} onClose={onClose}>
<StyledModalWrapper> <StyledModalWrapper>
<ModalCloseButton onClick={onClose} /> <ModalCloseButton onClick={onClose} />
<StyledModalHeader>Delete Workspace</StyledModalHeader> <StyledModalHeader>{t('Delete Workspace')}</StyledModalHeader>
{workspace.provider === 'local' ? ( {workspace.provider === 'local' ? (
<StyledTextContent> <StyledTextContent>
Deleting ( <Trans i18nKey="Delete Workspace Description">
<StyledWorkspaceName>{workspace.name}</StyledWorkspaceName>) cannot Deleting (
be undone, please proceed with caution. along with all its content. <StyledWorkspaceName>
{{ workspace: workspace.name }}
</StyledWorkspaceName>
) cannot be undone, please proceed with caution. along with all
its content.
</Trans>
</StyledTextContent> </StyledTextContent>
) : ( ) : (
<StyledTextContent> <StyledTextContent>
Deleting ( <Trans i18nKey="Delete Workspace Description2">
<StyledWorkspaceName>{workspace.name}</StyledWorkspaceName>) will Deleting (
delete both local and cloud data, this operation cannot be undone, <StyledWorkspaceName>
please proceed with caution. {{ workspace: workspace.name }}
</StyledWorkspaceName>
) will delete both local and cloud data, this operation cannot be
undone, please proceed with caution.
</Trans>
</StyledTextContent> </StyledTextContent>
)} )}
<StyledInputContent> <StyledInputContent>
<Input <Input
onChange={handlerInputChange} onChange={handlerInputChange}
placeholder="Please type “Delete” to confirm" placeholder={t('Delete Workspace placeholder')}
value={deleteStr} value={deleteStr}
></Input> ></Input>
</StyledInputContent> </StyledInputContent>
<StyledButtonContent> <StyledButtonContent>
<Button shape="circle" onClick={onClose}> <Button shape="circle" onClick={onClose}>
Cancel {t('Cancel')}
</Button> </Button>
<Button <Button
disabled={deleteStr.toLowerCase() !== 'delete'} disabled={deleteStr.toLowerCase() !== 'delete'}
@@ -88,7 +98,7 @@ export const WorkspaceDelete = ({
shape="circle" shape="circle"
style={{ marginLeft: '24px' }} style={{ marginLeft: '24px' }}
> >
Delete {t('Delete')}
</Button> </Button>
</StyledButtonContent> </StyledButtonContent>
</StyledModalWrapper> </StyledModalWrapper>

View File

@@ -7,6 +7,7 @@ import {
} from './style'; } from './style';
import { ModalCloseButton } from '@/ui/modal'; import { ModalCloseButton } from '@/ui/modal';
import { Button } from '@/ui/button'; import { Button } from '@/ui/button';
import { useTranslation } from '@affine/i18n';
// import { getDataCenter } from '@affine/datacenter'; // import { getDataCenter } from '@affine/datacenter';
// import { useAppState } from '@/providers/app-state-provider'; // import { useAppState } from '@/providers/app-state-provider';
@@ -22,6 +23,7 @@ export const WorkspaceLeave = ({
onClose, onClose,
workspaceId, workspaceId,
}: WorkspaceDeleteProps) => { }: WorkspaceDeleteProps) => {
const { t } = useTranslation();
console.log('workspaceId: ', workspaceId); console.log('workspaceId: ', workspaceId);
// const router = useRouter(); // const router = useRouter();
// const { refreshWorkspacesMeta } = useAppState(); // const { refreshWorkspacesMeta } = useAppState();
@@ -37,14 +39,13 @@ export const WorkspaceLeave = ({
<Modal open={open} onClose={onClose}> <Modal open={open} onClose={onClose}>
<StyledModalWrapper> <StyledModalWrapper>
<ModalCloseButton onClick={onClose} /> <ModalCloseButton onClick={onClose} />
<StyledModalHeader>Leave Workspace</StyledModalHeader> <StyledModalHeader>{t('Leave Workspace')}</StyledModalHeader>
<StyledTextContent> <StyledTextContent>
After you leave, you will not be able to access all the contents of {t('Leave Workspace Description')}
this workspace.
</StyledTextContent> </StyledTextContent>
<StyledButtonContent> <StyledButtonContent>
<Button shape="circle" onClick={onClose}> <Button shape="circle" onClick={onClose}>
Cancel {t('Cancel')}
</Button> </Button>
<Button <Button
onClick={handleLeave} onClick={handleLeave}
@@ -52,7 +53,7 @@ export const WorkspaceLeave = ({
shape="circle" shape="circle"
style={{ marginLeft: '24px' }} style={{ marginLeft: '24px' }}
> >
Leave {t('Leave')}
</Button> </Button>
</StyledButtonContent> </StyledButtonContent>
</StyledModalWrapper> </StyledModalWrapper>

View File

@@ -7,6 +7,7 @@ import { useState } from 'react';
import { Avatar } from '@mui/material'; import { Avatar } from '@mui/material';
import useMembers from '@/hooks/use-members'; import useMembers from '@/hooks/use-members';
import { User } from '@affine/datacenter'; import { User } from '@affine/datacenter';
import { useTranslation } from '@affine/i18n';
interface LoginModalProps { interface LoginModalProps {
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;
@@ -53,6 +54,7 @@ export const InviteMemberModal = ({
const [showTip, setShowTip] = useState<boolean>(false); const [showTip, setShowTip] = useState<boolean>(false);
const [userData, setUserData] = useState<User | null>(null); const [userData, setUserData] = useState<User | null>(null);
const { inviteMember, getUserByEmail } = useMembers(); const { inviteMember, getUserByEmail } = useMembers();
const { t } = useTranslation();
const inputChange = (value: string) => { const inputChange = (value: string) => {
setShowMember(true); setShowMember(true);
if (gmailReg.test(value)) { if (gmailReg.test(value)) {
@@ -82,7 +84,7 @@ export const InviteMemberModal = ({
/> />
</Header> </Header>
<Content> <Content>
<ContentTitle>Invite members</ContentTitle> <ContentTitle>{t('Invite Members')}</ContentTitle>
<InviteBox> <InviteBox>
<Input <Input
width={360} width={360}
@@ -91,12 +93,12 @@ export const InviteMemberModal = ({
onBlur={() => { onBlur={() => {
setShowMember(false); setShowMember(false);
}} }}
placeholder="Search mail (Gmail support only)" placeholder={t('Invite placeholder')}
></Input> ></Input>
{showMember ? ( {showMember ? (
<Members> <Members>
{showTip ? ( {showTip ? (
<NoFind>Non-Gmail is not supported</NoFind> <NoFind>{t('Non-Gmail')}</NoFind>
) : ( ) : (
<Member> <Member>
{userData?.avatar ? ( {userData?.avatar ? (
@@ -125,7 +127,7 @@ export const InviteMemberModal = ({
onInviteSuccess(); onInviteSuccess();
}} }}
> >
Invite {t('Invite')}
</Button> </Button>
</Footer> </Footer>
</ModalWrapper> </ModalWrapper>

View File

@@ -152,7 +152,7 @@ export const WorkSpaceSliderBar = () => {
<StyledListItem active={router.asPath === paths.setting}> <StyledListItem active={router.asPath === paths.setting}>
<StyledLink href={{ pathname: paths.setting }}> <StyledLink href={{ pathname: paths.setting }}>
<SettingsIcon /> <SettingsIcon />
Settings {t('Settings')}
</StyledLink> </StyledLink>
</StyledListItem> </StyledListItem>

View File

@@ -14,6 +14,7 @@ const All = () => {
<PageList <PageList
pageList={pageList.filter(p => !p.trash)} pageList={pageList.filter(p => !p.trash)}
showFavoriteTag={true} showFavoriteTag={true}
listType="all"
/> />
</> </>
); );

View File

@@ -13,7 +13,10 @@ export const Favorite = () => {
<PageListHeader icon={<FavouritesIcon />}> <PageListHeader icon={<FavouritesIcon />}>
{t('Favourites')} {t('Favourites')}
</PageListHeader> </PageListHeader>
<PageList pageList={pageList.filter(p => p.favorite && !p.trash)} /> <PageList
pageList={pageList.filter(p => p.favorite && !p.trash)}
listType="favorite"
/>
</> </>
); );
}; };

View File

@@ -11,7 +11,11 @@ export const Trash = () => {
return ( return (
<> <>
<PageListHeader icon={<TrashIcon />}>{t('Trash')}</PageListHeader> <PageListHeader icon={<TrashIcon />}>{t('Trash')}</PageListHeader>
<PageList pageList={pageList.filter(p => p.trash)} isTrash={true} /> <PageList
pageList={pageList.filter(p => p.trash)}
isTrash={true}
listType="trash"
/>
</> </>
); );
}; };

View File

@@ -15,6 +15,7 @@ declare module 'react-i18next' {
// custom namespace type if you changed it // custom namespace type if you changed it
// defaultNS: 'ns1'; // defaultNS: 'ns1';
// custom resources type // custom resources type
allowObjectInHTMLChildren: true;
resources: { resources: {
en: typeof en_US; en: typeof en_US;
}; };

View File

@@ -1 +0,0 @@
{}

View File

@@ -1,11 +1,8 @@
{ {
"Quick search": "Quick search", "Quick search": "Quick search",
"Quick search placeholder": "Quick Search...",
"Quick search placeholder2": "Search in {{workspace}}",
"All pages": "All pages", "All pages": "All pages",
"Favourites": "Favourites", "Favourites": "Favourites",
"No item": "No item", "No item": "No item",
"Settings": "Settings",
"Import": "Import", "Import": "Import",
"Trash": "Trash", "Trash": "Trash",
"New Page": "New Page", "New Page": "New Page",
@@ -14,13 +11,11 @@
"Find results": "Find {{number}} results", "Find results": "Find {{number}} results",
"Collapse sidebar": "Collapse sidebar", "Collapse sidebar": "Collapse sidebar",
"Expand sidebar": "Expand sidebar", "Expand sidebar": "Expand sidebar",
"Removed from Favourites": "Removed from Favourites",
"Remove from favourites": "Remove from favourites",
"Added to Favourites": "Added to Favourites", "Added to Favourites": "Added to Favourites",
"Add to favourites": "Add to favourites", "Add to favourites": "Add to favourites",
"Paper": "Paper", "Paper": "Paper",
"Edgeless": "Edgeless", "Edgeless": "Edgeless",
"Switch to": "Switch to", "Jump to": "Jump to",
"Convert to ": "Convert to ", "Convert to ": "Convert to ",
"Page": "Page", "Page": "Page",
"Export": "Export", "Export": "Export",
@@ -37,7 +32,6 @@
"Delete page?": "Delete page?", "Delete page?": "Delete page?",
"Delete permanently?": "Delete permanently?", "Delete permanently?": "Delete permanently?",
"will be moved to Trash": "{{title}} will be moved to Trash", "will be moved to Trash": "{{title}} will be moved to Trash",
"Once deleted, you can't undo this action.": "Once deleted, you can't undo this action.",
"Moved to Trash": "Moved to Trash", "Moved to Trash": "Moved to Trash",
"Permanently deleted": "Permanently deleted", "Permanently deleted": "Permanently deleted",
"restored": "{{title}} restored", "restored": "{{title}} restored",
@@ -57,7 +51,6 @@
"Strikethrough": "Strikethrough", "Strikethrough": "Strikethrough",
"Inline code": "Inline code", "Inline code": "Inline code",
"Code block": "Code block", "Code block": "Code block",
"Link": "Hyperlink (with selected text)",
"Body text": "Body text", "Body text": "Body text",
"Heading": "Heading {{number}}", "Heading": "Heading {{number}}",
"Increase indent": "Increase indent", "Increase indent": "Increase indent",
@@ -65,6 +58,11 @@
"Markdown Syntax": "Markdown Syntax", "Markdown Syntax": "Markdown Syntax",
"Divider": "Divider", "Divider": "Divider",
"404 - Page Not Found": "404 - Page Not Found", "404 - Page Not Found": "404 - Page Not Found",
"Once deleted, you can't undo this action": {
"": "Once deleted, you can't undo this action."
},
"Remove from favourites": "Remove from favourites",
"Removed from Favourites": "Removed from Favourites",
"New Workspace": "New Workspace", "New Workspace": "New Workspace",
"Workspace description": "Workspace is your virtual space to capture, create and plan as just one person or together as a team.", "Workspace description": "Workspace is your virtual space to capture, create and plan as just one person or together as a team.",
"Create": "Create", "Create": "Create",
@@ -79,6 +77,10 @@
"TrashButtonGroupTitle": "Permanently delete", "TrashButtonGroupTitle": "Permanently delete",
"TrashButtonGroupDescription": "Once deleted, you can't undo this action. Do you confirm?", "TrashButtonGroupDescription": "Once deleted, you can't undo this action. Do you confirm?",
"Delete permanently": "Delete permanently", "Delete permanently": "Delete permanently",
"Link": "Hyperlink (with selected text)",
"Quick search placeholder": "Quick Search...",
"Quick search placeholder2": "Search in {{workspace}}",
"Settings": "Settings",
"recommendBrowser": " We recommend the <1>Chrome</1> browser for optimal experience.", "recommendBrowser": " We recommend the <1>Chrome</1> browser for optimal experience.",
"upgradeBrowser": "Please upgrade to the latest version of Chrome for the best experience.", "upgradeBrowser": "Please upgrade to the latest version of Chrome for the best experience.",
"Invite Members": "Invite Members", "Invite Members": "Invite Members",
@@ -104,19 +106,15 @@
"Create Or Import": "Create Or Import", "Create Or Import": "Create Or Import",
"Tips": "Tips: ", "Tips": "Tips: ",
"login success": "Login success", "login success": "Login success",
"Sign in": "Sign in AFFiNE Cloud",
"Sign out": "Sign out of AFFiNE Cloud", "Sign out": "Sign out of AFFiNE Cloud",
"Delete Workspace": "Delete Workspace", "Delete Workspace": "Delete Workspace",
"Delete Workspace Description": "Deleting (<1>{{workspace}}</1>) cannot be undone, please proceed with caution. along with all its content.",
"Delete Workspace Description2": "Deleting (<1>{{workspace}}</1>) will delete both local and cloud data, this operation cannot be undone, please proceed with caution.", "Delete Workspace Description2": "Deleting (<1>{{workspace}}</1>) will delete both local and cloud data, this operation cannot be undone, please proceed with caution.",
"Delete Workspace placeholder": "Please type “Delete” to confirm", "Delete Workspace placeholder": "Please type “Delete” to confirm",
"Leave Workspace": "Leave Workspace", "Leave Workspace": "Leave Workspace",
"Leave Workspace Description": "After you leave, you will not be able to access all the contents of this workspace.",
"Leave": "Leave", "Leave": "Leave",
"Workspace Icon": "Workspace Icon", "Workspace Icon": "Workspace Icon",
"Workspace Name": "Workspace Name", "Workspace Name": "Workspace Name",
"Workspace Type": "Workspace Type", "Workspace Type": "Workspace Type",
"Export Workspace": "Export Workspace <1>{{workspace}}</1> Is Coming",
"Users": "Users", "Users": "Users",
"Access level": "Access level", "Access level": "Access level",
"Pending": "Pending", "Pending": "Pending",
@@ -132,12 +130,16 @@
"Publishing Description": "After publishing to the web, everyone can view the content of this workspace through the link.", "Publishing Description": "After publishing to the web, everyone can view the content of this workspace through the link.",
"Stop publishing": "Stop publishing", "Stop publishing": "Stop publishing",
"Publish to web": "Publish to web", "Publish to web": "Publish to web",
"Sync Description": "{{workspaceName}} is Local Workspace. All data is stored on the current device. You can enable AFFiNE Cloud for this workspace to keep data in sync with the cloud.",
"Sync Description2": "<1>{{workspaceName}}</1> is Cloud Workspace. All data will be synchronized and saved to the AFFiNE",
"Download data to device": "Download {{CoreOrAll}} data to device", "Download data to device": "Download {{CoreOrAll}} data to device",
"General": "General", "General": "General",
"Sync": "Sync", "Sync": "Sync",
"Collaboration": "Collaboration", "Collaboration": "Collaboration",
"Publish": "Publish", "Publish": "Publish",
"Workspace Settings": "Workspace Settings" "Workspace Settings": "Workspace Settings",
"Export Workspace": "Export Workspace <1>{{workspace}}</1> is coming soon",
"Leave Workspace Description": "After you leave, you will no longer be able to access the contents of this workspace.",
"Sign in": "Sign in to AFFiNE Cloud",
"Sync Description": "{{workspaceName}} is a Local Workspace. All data is stored on the current device. You can enable AFFiNE Cloud for this workspace to keep data in sync with the cloud.",
"Sync Description2": "<1>{{workspaceName}}</1> is a Cloud Workspace. All data will be synchronised and saved to AFFiNE Cloud.",
"Delete Workspace Description": "Deleting (<1>{{workspace}}</1>) cannot be undone, please proceed with caution. All contents will be lost."
} }

View File

@@ -1 +0,0 @@
{}

View File

@@ -1,16 +1,11 @@
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
// Run `pnpm run download-resources` to regenerate. // Run `pnpm run download-resources` to regenerate.
// To overwrite this, please overwrite download.ts // To overwrite this, please overwrite download.ts script.
import en from './en.json'; import en from './en.json';
import zh_Hans from './zh-Hans.json';
import zh_Hant from './zh-Hant.json';
import sr from './sr.json';
import fr from './fr.json';
import bn from './bn.json';
export const LOCALES = [ export const LOCALES = [
{ {
id: 1000016008, id: 1000040001,
name: 'English', name: 'English',
tag: 'en', tag: 'en',
originalName: 'English', originalName: 'English',
@@ -19,54 +14,4 @@ export const LOCALES = [
completeRate: 1, completeRate: 1,
res: en, res: en,
}, },
{
id: 1000016009,
name: 'Simplified Chinese',
tag: 'zh-Hans',
originalName: '简体中文',
flagEmoji: '🇨🇳',
base: false,
completeRate: 1,
res: zh_Hans,
},
{
id: 1000016012,
name: 'Traditional Chinese',
tag: 'zh-Hant',
originalName: '繁體中文',
flagEmoji: '🇭🇰',
base: false,
completeRate: 1,
res: zh_Hant,
},
{
id: 1000034005,
name: 'Serbian',
tag: 'sr',
originalName: 'српски',
flagEmoji: '🇷🇸',
base: false,
completeRate: 0.9166666666666666,
res: sr,
},
{
id: 1000034008,
name: 'French',
tag: 'fr',
originalName: 'français',
flagEmoji: '🇫🇷',
base: false,
completeRate: 1,
res: fr,
},
{
id: 1000034010,
name: 'Bangla',
tag: 'bn',
originalName: 'বাংলা',
flagEmoji: '🇧🇩',
base: false,
completeRate: 0.7083333333333334,
res: bn,
},
] as const; ] as const;

View File

@@ -1 +0,0 @@
{}

View File

@@ -1 +0,0 @@
{}

View File

@@ -1 +0,0 @@
{}