feat(i18n): static type on i18n (#2225)

This commit is contained in:
Himself65
2023-05-04 00:35:09 -05:00
committed by GitHub
parent 66c3b09c67
commit 3d43e61087
80 changed files with 585 additions and 444 deletions

1
.gitignore vendored
View File

@@ -57,6 +57,7 @@ Thumbs.db
.next .next
out/ out/
storybook-static storybook-static
i18n_generated.ts
/test-results/ /test-results/
/playwright-report/ /playwright-report/

21
.i18n-codegen.json Normal file
View File

@@ -0,0 +1,21 @@
{
"$schema": "./node_modules/@magic-works/i18n-codegen/schema.json",
"version": 1,
"list": [
{
"input": "./packages/i18n/src/resources/en.json",
"output": "./packages/i18n/src/i18n_generated",
"parser": {
"type": "i18next",
"contextSeparator": "$",
"pluralSeparator": "_"
},
"generator": {
"type": "i18next/react-hooks",
"hooks": "useAFFiNEI18N",
"emitTS": true,
"shouldUnescape": true
}
}
]
}

View File

@@ -2,10 +2,12 @@
import { createRequire } from 'node:module'; import { createRequire } from 'node:module';
import path from 'node:path'; import path from 'node:path';
import { runCli } from '@magic-works/i18n-codegen';
import { PerfseePlugin } from '@perfsee/webpack'; import { PerfseePlugin } from '@perfsee/webpack';
import { withSentryConfig } from '@sentry/nextjs'; import { withSentryConfig } from '@sentry/nextjs';
import SentryWebpackPlugin from '@sentry/webpack-plugin'; import SentryWebpackPlugin from '@sentry/webpack-plugin';
import debugLocal from 'next-debug-local'; import debugLocal from 'next-debug-local';
import { fileURLToPath } from 'url';
import { blockSuiteFeatureFlags, buildFlags } from './preset.config.mjs'; import { blockSuiteFeatureFlags, buildFlags } from './preset.config.mjs';
import { getCommitHash, getGitVersion } from './scripts/gitInfo.mjs'; import { getCommitHash, getGitVersion } from './scripts/gitInfo.mjs';
@@ -17,6 +19,21 @@ const withVanillaExtract = createVanillaExtractPlugin();
console.info('Build Flags', buildFlags); console.info('Build Flags', buildFlags);
console.info('Editor Flags', blockSuiteFeatureFlags); console.info('Editor Flags', blockSuiteFeatureFlags);
if (process.env.NODE_ENV !== 'development') {
await runCli(
{
config: fileURLToPath(
new URL('../../.i18n-codegen.json', import.meta.url)
),
watch: false,
},
error => {
console.error(error);
process.exit(1);
}
);
}
const enableDebugLocal = path.isAbsolute(process.env.LOCAL_BLOCK_SUITE ?? ''); const enableDebugLocal = path.isAbsolute(process.env.LOCAL_BLOCK_SUITE ?? '');
if (enableDebugLocal) { if (enableDebugLocal) {

View File

@@ -1,5 +1,5 @@
import { IconButton, Modal, ModalWrapper } from '@affine/component'; import { IconButton, Modal, ModalWrapper } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CloseIcon } from '@blocksuite/icons'; import { CloseIcon } from '@blocksuite/icons';
import type React from 'react'; import type React from 'react';
@@ -17,7 +17,7 @@ export const EnableAffineCloudModal: React.FC<EnableAffineCloudModalProps> = ({
open, open,
onClose, onClose,
}) => { }) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
const user = useCurrentUser(); const user = useCurrentUser();
return ( return (
@@ -33,8 +33,8 @@ export const EnableAffineCloudModal: React.FC<EnableAffineCloudModalProps> = ({
</IconButton> </IconButton>
</Header> </Header>
<Content> <Content>
<ContentTitle>{t('Enable AFFiNE Cloud')}?</ContentTitle> <ContentTitle>{t['Enable AFFiNE Cloud']()}?</ContentTitle>
<StyleTips>{t('Enable AFFiNE Cloud Description')}</StyleTips> <StyleTips>{t['Enable AFFiNE Cloud Description']()}</StyleTips>
{/* <StyleTips>{t('Retain cached cloud data')}</StyleTips> */} {/* <StyleTips>{t('Retain cached cloud data')}</StyleTips> */}
<div> <div>
<StyleButton <StyleButton
@@ -45,7 +45,7 @@ export const EnableAffineCloudModal: React.FC<EnableAffineCloudModalProps> = ({
onConfirm(); onConfirm();
}} }}
> >
{user ? t('Enable') : t('Sign in and Enable')} {user ? t.Enable() : t['Sign in and Enable']()}
</StyleButton> </StyleButton>
<StyleButton <StyleButton
shape="round" shape="round"
@@ -53,7 +53,7 @@ export const EnableAffineCloudModal: React.FC<EnableAffineCloudModalProps> = ({
onClose(); onClose();
}} }}
> >
{t('Not now')} {t['Not now']()}
</StyleButton> </StyleButton>
</div> </div>
</Content> </Content>

View File

@@ -1,5 +1,5 @@
import { MenuItem } from '@affine/component'; import { MenuItem } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CopyIcon } from '@blocksuite/icons'; import { CopyIcon } from '@blocksuite/icons';
import { useCallback } from 'react'; import { useCallback } from 'react';
@@ -8,11 +8,11 @@ import { toast } from '../../../utils';
import type { CommonMenuItemProps } from './types'; import type { CommonMenuItemProps } from './types';
export const CopyLink = ({ onItemClick, onSelect }: CommonMenuItemProps) => { export const CopyLink = ({ onItemClick, onSelect }: CommonMenuItemProps) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
const copyUrl = useCallback(() => { const copyUrl = useCallback(() => {
navigator.clipboard.writeText(window.location.href); navigator.clipboard.writeText(window.location.href);
toast(t('Copied link to clipboard')); toast(t['Copied link to clipboard']());
}, [t]); }, [t]);
return ( return (
@@ -25,7 +25,7 @@ export const CopyLink = ({ onItemClick, onSelect }: CommonMenuItemProps) => {
}} }}
icon={<CopyIcon />} icon={<CopyIcon />}
> >
{t('Copy Link')} {t['Copy Link']()}
</MenuItem> </MenuItem>
); );
}; };

View File

@@ -1,7 +1,7 @@
import { MenuItem, styled } from '@affine/component'; import { MenuItem, styled } from '@affine/component';
import type { PublicLinkDisableProps } from '@affine/component/share-menu'; import type { PublicLinkDisableProps } from '@affine/component/share-menu';
import { PublicLinkDisableModal } from '@affine/component/share-menu'; import { PublicLinkDisableModal } from '@affine/component/share-menu';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { ShareIcon } from '@blocksuite/icons'; import { ShareIcon } from '@blocksuite/icons';
import type { CommonMenuItemProps } from './types'; import type { CommonMenuItemProps } from './types';
@@ -29,7 +29,7 @@ export const DisablePublicSharing = ({
onItemClick, onItemClick,
testId, testId,
}: CommonMenuItemProps) => { }: CommonMenuItemProps) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
return ( return (
<> <>
<StyledMenuItem <StyledMenuItem
@@ -41,7 +41,7 @@ export const DisablePublicSharing = ({
style={{ color: 'red' }} style={{ color: 'red' }}
icon={<ShareIcon />} icon={<ShareIcon />}
> >
{t('Disable Public Sharing')} {t['Disable Public Sharing']()}
</StyledMenuItem> </StyledMenuItem>
</> </>
); );

View File

@@ -1,5 +1,5 @@
import { Menu, MenuItem } from '@affine/component'; import { Menu, MenuItem } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { ContentParser } from '@blocksuite/blocks/content-parser'; import { ContentParser } from '@blocksuite/blocks/content-parser';
import { import {
ArrowRightSmallIcon, ArrowRightSmallIcon,
@@ -15,7 +15,7 @@ export const Export = ({
onSelect, onSelect,
onItemClick, onItemClick,
}: CommonMenuItemProps<{ type: 'markdown' | 'html' }>) => { }: CommonMenuItemProps<{ type: 'markdown' | 'html' }>) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
const contentParserRef = useRef<ContentParser>(); const contentParserRef = useRef<ContentParser>();
return ( return (
<Menu <Menu
@@ -37,7 +37,7 @@ export const Export = ({
}} }}
icon={<ExportToHtmlIcon />} icon={<ExportToHtmlIcon />}
> >
{t('Export to HTML')} {t['Export to HTML']()}
</MenuItem> </MenuItem>
<MenuItem <MenuItem
data-testid="export-to-markdown" data-testid="export-to-markdown"
@@ -52,7 +52,7 @@ export const Export = ({
}} }}
icon={<ExportToMarkdownIcon />} icon={<ExportToMarkdownIcon />}
> >
{t('Export to Markdown')} {t['Export to Markdown']()}
</MenuItem> </MenuItem>
</> </>
} }
@@ -66,7 +66,7 @@ export const Export = ({
onItemClick?.(); onItemClick?.();
}} }}
> >
{t('Export')} {t.Export()}
</MenuItem> </MenuItem>
</Menu> </Menu>
); );

View File

@@ -1,5 +1,5 @@
import { MenuItem } from '@affine/component'; import { MenuItem } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { ArrowRightSmallIcon, MoveToIcon } from '@blocksuite/icons'; import { ArrowRightSmallIcon, MoveToIcon } from '@blocksuite/icons';
import type { PageMeta } from '@blocksuite/store'; import type { PageMeta } from '@blocksuite/store';
import { useRef, useState } from 'react'; import { useRef, useState } from 'react';
@@ -27,7 +27,7 @@ export const MoveTo = ({
onSelect, onSelect,
onItemClick, onItemClick,
}: MoveToProps) => { }: MoveToProps) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
const ref = useRef<HTMLButtonElement>(null); const ref = useRef<HTMLButtonElement>(null);
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null); const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
const open = anchorEl !== null; const open = anchorEl !== null;
@@ -44,7 +44,7 @@ export const MoveTo = ({
endIcon={<ArrowRightSmallIcon />} endIcon={<ArrowRightSmallIcon />}
data-testid="move-to-menu-item" data-testid="move-to-menu-item"
> >
{t('Move to')} {t['Move to']()}
</MenuItem> </MenuItem>
<PinboardMenu <PinboardMenu
anchorEl={anchorEl} anchorEl={anchorEl}

View File

@@ -1,6 +1,6 @@
import type { ConfirmProps } from '@affine/component'; import type { ConfirmProps } from '@affine/component';
import { Confirm, MenuItem } from '@affine/component'; import { Confirm, MenuItem } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { DeleteTemporarilyIcon } from '@blocksuite/icons'; import { DeleteTemporarilyIcon } from '@blocksuite/icons';
import type { PageMeta } from '@blocksuite/store'; import type { PageMeta } from '@blocksuite/store';
@@ -11,7 +11,7 @@ export const MoveToTrash = ({
onItemClick, onItemClick,
testId, testId,
}: CommonMenuItemProps) => { }: CommonMenuItemProps) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
return ( return (
<> <>
@@ -23,7 +23,7 @@ export const MoveToTrash = ({
}} }}
icon={<DeleteTemporarilyIcon />} icon={<DeleteTemporarilyIcon />}
> >
{t('Move to Trash')} {t['Move to Trash']()}
</MenuItem> </MenuItem>
</> </>
); );
@@ -35,15 +35,15 @@ const ConfirmModal = ({
}: { }: {
meta: PageMeta; meta: PageMeta;
} & ConfirmProps) => { } & ConfirmProps) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
return ( return (
<Confirm <Confirm
title={t('Delete page?')} title={t['Delete page?']()}
content={t('will be moved to Trash', { content={t['will be moved to Trash']({
title: meta.title || 'Untitled', title: meta.title || 'Untitled',
})} })}
confirmText={t('Delete')} confirmText={t.Delete()}
confirmType="danger" confirmType="danger"
{...confirmModalProps} {...confirmModalProps}
/> />

View File

@@ -1,5 +1,5 @@
import { FlexWrapper } from '@affine/component'; import { FlexWrapper } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { EdgelessIcon, PageIcon } from '@blocksuite/icons'; import { EdgelessIcon, PageIcon } from '@blocksuite/icons';
import type { PageMeta } from '@blocksuite/store'; import type { PageMeta } from '@blocksuite/store';
import { useAtomValue } from 'jotai'; import { useAtomValue } from 'jotai';
@@ -16,14 +16,14 @@ export const SearchContent = ({
results: PageMeta[]; results: PageMeta[];
onClick?: (dropId: string) => void; onClick?: (dropId: string) => void;
}) => { }) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
const record = useAtomValue(workspacePreferredModeAtom); const record = useAtomValue(workspacePreferredModeAtom);
if (results.length) { if (results.length) {
return ( return (
<> <>
<StyledMenuSubTitle> <StyledMenuSubTitle>
{t('Find results', { number: results.length })} {t['Find results']({ number: `${results.length}` })}
</StyledMenuSubTitle> </StyledMenuSubTitle>
{results.map(meta => { {results.map(meta => {
return ( return (
@@ -45,7 +45,7 @@ export const SearchContent = ({
return ( return (
<> <>
<StyledMenuSubTitle>{t('Find 0 result')}</StyledMenuSubTitle> <StyledMenuSubTitle>{t['Find 0 result']()}</StyledMenuSubTitle>
<FlexWrapper <FlexWrapper
alignItems="center" alignItems="center"
justifyContent="center" justifyContent="center"

View File

@@ -1,6 +1,6 @@
import type { PureMenuProps } from '@affine/component'; import type { PureMenuProps } from '@affine/component';
import { Input, PureMenu, TreeView } from '@affine/component'; import { Input, PureMenu, TreeView } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { RemoveIcon, SearchIcon } from '@blocksuite/icons'; import { RemoveIcon, SearchIcon } from '@blocksuite/icons';
import type { PageMeta } from '@blocksuite/store'; import type { PageMeta } from '@blocksuite/store';
import React, { useCallback, useMemo, useState } from 'react'; import React, { useCallback, useMemo, useState } from 'react';
@@ -40,7 +40,7 @@ export const PinboardMenu = ({
() => propsMetas.filter(m => m.id !== currentMeta.id), () => propsMetas.filter(m => m.id !== currentMeta.id),
[currentMeta.id, propsMetas] [currentMeta.id, propsMetas]
); );
const { t } = useTranslation(); const t = useAFFiNEI18N();
const [query, setQuery] = useState(''); const [query, setQuery] = useState('');
const isSearching = query.length > 0; const isSearching = query.length > 0;
@@ -92,7 +92,7 @@ export const PinboardMenu = ({
<Input <Input
value={query} value={query}
onChange={setQuery} onChange={setQuery}
placeholder={t('Move page to...')} placeholder={t['Move page to...']()}
height={32} height={32}
noBorder={true} noBorder={true}
onClick={e => e.stopPropagation()} onClick={e => e.stopPropagation()}
@@ -121,9 +121,9 @@ export const PinboardMenu = ({
}} }}
> >
<RemoveIcon /> <RemoveIcon />
{t('Remove from Pinboard')} {t['Remove from Pivots']()}
</StyledPinboard> </StyledPinboard>
<p>{t('RFP')}</p> <p>{t['RFP']()}</p>
</StyledMenuFooter> </StyledMenuFooter>
)} )}
</PureMenu> </PureMenu>

View File

@@ -1,12 +1,12 @@
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { StyledPinboard } from '../styles'; import { StyledPinboard } from '../styles';
export const EmptyItem = () => { export const EmptyItem = () => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
return ( return (
<StyledPinboard disable={true} textWrap={true}> <StyledPinboard disable={true} textWrap={true}>
{t('Organize pages to build knowledge')} {t['Organize pages to build knowledge']()}
</StyledPinboard> </StyledPinboard>
); );
}; };

View File

@@ -1,5 +1,5 @@
import { MenuItem, MuiClickAwayListener, PureMenu } from '@affine/component'; import { MenuItem, MuiClickAwayListener, PureMenu } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { import {
MoreVerticalIcon, MoreVerticalIcon,
MoveToIcon, MoveToIcon,
@@ -39,7 +39,7 @@ export const OperationButton = ({
onMenuClose, onMenuClose,
onRename, onRename,
}: OperationButtonProps) => { }: OperationButtonProps) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
const timer = useRef<ReturnType<typeof setTimeout>>(); const timer = useRef<ReturnType<typeof setTimeout>>();
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
@@ -100,7 +100,7 @@ export const OperationButton = ({
}} }}
icon={<PlusIcon />} icon={<PlusIcon />}
> >
{t('Add a subpage inside')} {t['Add a subpage inside']()}
</MenuItem> </MenuItem>
{!isRoot && ( {!isRoot && (
<MenuItem <MenuItem
@@ -111,7 +111,7 @@ export const OperationButton = ({
}} }}
icon={<MoveToIcon />} icon={<MoveToIcon />}
> >
{t('Move to')} {t['Move to']()}
</MenuItem> </MenuItem>
)} )}
{!isRoot && ( {!isRoot && (
@@ -124,7 +124,7 @@ export const OperationButton = ({
}} }}
icon={<PenIcon />} icon={<PenIcon />}
> >
{t('Rename')} {t['Rename']()}
</MenuItem> </MenuItem>
)} )}
{!isRoot && ( {!isRoot && (
@@ -154,7 +154,7 @@ export const OperationButton = ({
open={confirmModalOpen} open={confirmModalOpen}
meta={currentMeta} meta={currentMeta}
onConfirm={() => { onConfirm={() => {
toast(t('Moved to Trash')); toast(t['Moved to Trash']());
removeToTrash(currentMeta.id); removeToTrash(currentMeta.id);
onDelete(); onDelete();
}} }}

View File

@@ -1,7 +1,7 @@
import { Tooltip } from '@affine/component'; import { Tooltip } from '@affine/component';
import { appSidebarOpenAtom } from '@affine/component/app-sidebar'; import { appSidebarOpenAtom } from '@affine/component/app-sidebar';
import { getEnvironment } from '@affine/env'; import { getEnvironment } from '@affine/env';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
@@ -21,7 +21,7 @@ export const SidebarSwitch = ({
}: SidebarSwitchProps) => { }: SidebarSwitchProps) => {
const [open, setOpen] = useAtom(appSidebarOpenAtom); const [open, setOpen] = useAtom(appSidebarOpenAtom);
const [tooltipVisible, setTooltipVisible] = useState(false); const [tooltipVisible, setTooltipVisible] = useState(false);
const { t } = useTranslation(); const t = useAFFiNEI18N();
const checkIsMac = () => { const checkIsMac = () => {
const env = getEnvironment(); const env = getEnvironment();
return env.isBrowser && env.isMacOs; return env.isBrowser && env.isMacOs;
@@ -34,7 +34,7 @@ export const SidebarSwitch = ({
}, []); }, []);
tooltipContent = tooltipContent =
tooltipContent || (open ? t('Collapse sidebar') : t('Expand sidebar')); tooltipContent || (open ? t['Collapse sidebar']() : t['Expand sidebar']());
return ( return (
<Tooltip <Tooltip

View File

@@ -1,5 +1,5 @@
import { IconButton, Modal, ModalWrapper } from '@affine/component'; import { IconButton, Modal, ModalWrapper } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CloseIcon } from '@blocksuite/icons'; import { CloseIcon } from '@blocksuite/icons';
import type React from 'react'; import type React from 'react';
@@ -15,7 +15,7 @@ export type TransformWorkspaceToAffineModalProps = {
export const TransformWorkspaceToAffineModal: React.FC< export const TransformWorkspaceToAffineModal: React.FC<
TransformWorkspaceToAffineModalProps TransformWorkspaceToAffineModalProps
> = ({ open, onClose, onConform }) => { > = ({ open, onClose, onConform }) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
const user = useCurrentUser(); const user = useCurrentUser();
return ( return (
@@ -35,8 +35,8 @@ export const TransformWorkspaceToAffineModal: React.FC<
</IconButton> </IconButton>
</Header> </Header>
<Content> <Content>
<ContentTitle>{t('Enable AFFiNE Cloud')}?</ContentTitle> <ContentTitle>{t['Enable AFFiNE Cloud']()}?</ContentTitle>
<StyleTips>{t('Enable AFFiNE Cloud Description')}</StyleTips> <StyleTips>{t['Enable AFFiNE Cloud Description']()}</StyleTips>
{/* <StyleTips>{t('Retain cached cloud data')}</StyleTips> */} {/* <StyleTips>{t('Retain cached cloud data')}</StyleTips> */}
<div> <div>
<StyleButton <StyleButton
@@ -61,7 +61,7 @@ export const TransformWorkspaceToAffineModal: React.FC<
// setLoading(false); // setLoading(false);
}} }}
> >
{user ? t('Enable') : t('Sign in and Enable')} {user ? t['Enable']() : t['Sign in and Enable']()}
</StyleButton> </StyleButton>
<StyleButton <StyleButton
shape="round" shape="round"
@@ -69,7 +69,7 @@ export const TransformWorkspaceToAffineModal: React.FC<
onClose(); onClose();
}} }}
> >
{t('Not now')} {t['Not now']()}
</StyleButton> </StyleButton>
</div> </div>
</Content> </Content>

View File

@@ -1,4 +1,4 @@
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { SettingPanel, WorkspaceRegistry } from '@affine/workspace/type'; import type { SettingPanel, WorkspaceRegistry } from '@affine/workspace/type';
import { settingPanel, WorkspaceFlavour } from '@affine/workspace/type'; import { settingPanel, WorkspaceFlavour } from '@affine/workspace/type';
import type { MouseEvent } from 'react'; import type { MouseEvent } from 'react';
@@ -39,6 +39,7 @@ export type WorkspaceSettingDetailProps = {
export type PanelProps = WorkspaceSettingDetailProps; export type PanelProps = WorkspaceSettingDetailProps;
type Name = 'General' | 'Sync' | 'Collaboration' | 'Publish' | 'Export';
const panelMap = { const panelMap = {
[settingPanel.General]: { [settingPanel.General]: {
name: 'General', name: 'General',
@@ -63,7 +64,7 @@ const panelMap = {
}, },
} satisfies { } satisfies {
[Key in SettingPanel]: { [Key in SettingPanel]: {
name: string; name: Name;
enable?: (flavour: WorkspaceFlavour) => boolean; enable?: (flavour: WorkspaceFlavour) => boolean;
ui: React.FC<PanelProps>; ui: React.FC<PanelProps>;
}; };
@@ -96,7 +97,7 @@ export const WorkspaceSettingDetail: React.FC<
if (!(currentTab in panelMap)) { if (!(currentTab in panelMap)) {
throw new Error('Invalid activeTab: ' + currentTab); throw new Error('Invalid activeTab: ' + currentTab);
} }
const { t } = useTranslation(); const t = useAFFiNEI18N();
const workspaceId = workspace.id; const workspaceId = workspace.id;
useEffect(() => { useEffect(() => {
if (isAffine && isOwner) { if (isAffine && isOwner) {
@@ -148,7 +149,7 @@ export const WorkspaceSettingDetail: React.FC<
data-tab-key={key} data-tab-key={key}
onClick={handleTabClick} onClick={handleTabClick}
> >
{t(value.name)} {t[value.name]()}
</WorkspaceSettingTagItem> </WorkspaceSettingTagItem>
); );
})} })}

View File

@@ -1,6 +1,6 @@
import { Button, IconButton, Menu, MenuItem, Wrapper } from '@affine/component'; import { Button, IconButton, Menu, MenuItem, Wrapper } from '@affine/component';
import { config } from '@affine/env'; import { config } from '@affine/env';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { PermissionType } from '@affine/workspace/affine/api'; import { PermissionType } from '@affine/workspace/affine/api';
import type { AffineWorkspace, LocalWorkspace } from '@affine/workspace/type'; import type { AffineWorkspace, LocalWorkspace } from '@affine/workspace/type';
import { WorkspaceFlavour } from '@affine/workspace/type'; import { WorkspaceFlavour } from '@affine/workspace/type';
@@ -41,7 +41,7 @@ const AffineRemoteCollaborationPanel: React.FC<
} }
> = ({ workspace }) => { > = ({ workspace }) => {
const [isInviteModalShow, setIsInviteModalShow] = useState(false); const [isInviteModalShow, setIsInviteModalShow] = useState(false);
const { t } = useTranslation(); const t = useAFFiNEI18N();
const { members, removeMember } = useMembers(workspace.id); const { members, removeMember } = useMembers(workspace.id);
return ( return (
<> <>
@@ -49,11 +49,11 @@ const AffineRemoteCollaborationPanel: React.FC<
<ul> <ul>
<StyledMemberTitleContainer> <StyledMemberTitleContainer>
<StyledMemberNameContainer> <StyledMemberNameContainer>
{t('Users')} ( {t['Users']()} (
<span data-testid="member-length">{members.length}</span>) <span data-testid="member-length">{members.length}</span>)
</StyledMemberNameContainer> </StyledMemberNameContainer>
<StyledMemberRoleContainer> <StyledMemberRoleContainer>
{t('Access level')} {t['Access level']()}
</StyledMemberRoleContainer> </StyledMemberRoleContainer>
<div style={{ width: '24px', paddingRight: '48px' }}></div> <div style={{ width: '24px', paddingRight: '48px' }}></div>
</StyledMemberTitleContainer> </StyledMemberTitleContainer>
@@ -91,9 +91,9 @@ const AffineRemoteCollaborationPanel: React.FC<
<StyledMemberRoleContainer> <StyledMemberRoleContainer>
{member.accepted {member.accepted
? member.type !== PermissionType.Owner ? member.type !== PermissionType.Owner
? t('Member') ? t['Member']()
: t('Owner') : t['Owner']()
: t('Pending')} : t['Pending']()}
</StyledMemberRoleContainer> </StyledMemberRoleContainer>
{member.type === PermissionType.Owner ? ( {member.type === PermissionType.Owner ? (
<StyledMoreVerticalDiv /> <StyledMoreVerticalDiv />
@@ -109,14 +109,14 @@ const AffineRemoteCollaborationPanel: React.FC<
// @ts-ignore // @ts-ignore
await removeMember(member.id); await removeMember(member.id);
toast( toast(
t('Member has been removed', { t['Member has been removed']({
name: user.name, name: user.name,
}) })
); );
}} }}
icon={<DeleteTemporarilyIcon />} icon={<DeleteTemporarilyIcon />}
> >
{t('Remove from workspace')} {t['Remove from workspace']()}
</MenuItem> </MenuItem>
</> </>
} }
@@ -145,7 +145,7 @@ const AffineRemoteCollaborationPanel: React.FC<
data-testid="invite-members" data-testid="invite-members"
shape="circle" shape="circle"
> >
{t('Invite Members')} {t['Invite Members']()}
</Button> </Button>
</StyledMemberButtonContainer> </StyledMemberButtonContainer>
</StyledMemberContainer> </StyledMemberContainer>
@@ -168,11 +168,11 @@ const LocalCollaborationPanel: React.FC<
workspace: LocalWorkspace; workspace: LocalWorkspace;
} }
> = ({ workspace, onTransferWorkspace }) => { > = ({ workspace, onTransferWorkspace }) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
return ( return (
<> <>
<Wrapper marginBottom="42px">{t('Collaboration Description')}</Wrapper> <Wrapper marginBottom="42px">{t['Collaboration Description']()}</Wrapper>
<Button <Button
data-testid="local-workspace-enable-cloud-button" data-testid="local-workspace-enable-cloud-button"
@@ -182,7 +182,7 @@ const LocalCollaborationPanel: React.FC<
setOpen(true); setOpen(true);
}} }}
> >
{t('Enable AFFiNE Cloud')} {t['Enable AFFiNE Cloud']()}
</Button> </Button>
{config.enableLegacyCloud ? ( {config.enableLegacyCloud ? (
<TransformWorkspaceToAffineModal <TransformWorkspaceToAffineModal

View File

@@ -7,7 +7,7 @@ import {
MuiAvatar, MuiAvatar,
styled, styled,
} from '@affine/component'; } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { EmailIcon } from '@blocksuite/icons'; import { EmailIcon } from '@blocksuite/icons';
import type React from 'react'; import type React from 'react';
import { Suspense, useCallback, useState } from 'react'; import { Suspense, useCallback, useState } from 'react';
@@ -60,7 +60,7 @@ export const InviteMemberModal = ({
const { inviteMember } = useMembers(workspaceId); const { inviteMember } = useMembers(workspaceId);
const [email, setEmail] = useState<string>(''); const [email, setEmail] = useState<string>('');
const [showMemberPreview, setShowMemberPreview] = useState(false); const [showMemberPreview, setShowMemberPreview] = useState(false);
const { t } = useTranslation(); const t = useAFFiNEI18N();
const inputChange = useCallback((value: string) => { const inputChange = useCallback((value: string) => {
setEmail(value); setEmail(value);
}, []); }, []);
@@ -77,7 +77,7 @@ export const InviteMemberModal = ({
/> />
</Header> </Header>
<Content> <Content>
<ContentTitle>{t('Invite Members')}</ContentTitle> <ContentTitle>{t['Invite Members']()}</ContentTitle>
<InviteBox> <InviteBox>
<Input <Input
data-testid="invite-member-input" data-testid="invite-member-input"
@@ -90,7 +90,7 @@ export const InviteMemberModal = ({
onBlur={useCallback(() => { onBlur={useCallback(() => {
setShowMemberPreview(false); setShowMemberPreview(false);
}, [])} }, [])}
placeholder={t('Invite placeholder')} placeholder={t['Invite placeholder']()}
/> />
{showMemberPreview && gmailReg.test(email) && ( {showMemberPreview && gmailReg.test(email) && (
<Suspense fallback="loading..."> <Suspense fallback="loading...">
@@ -112,7 +112,7 @@ export const InviteMemberModal = ({
onInviteSuccess(); onInviteSuccess();
}} }}
> >
{t('Invite')} {t['Invite']()}
</Button> </Button>
</Footer> </Footer>
</ModalWrapper> </ModalWrapper>

View File

@@ -1,11 +1,11 @@
import { Button, Wrapper } from '@affine/component'; import { Button, Wrapper } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
export const ExportPanel = () => { export const ExportPanel = () => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
return ( return (
<> <>
<Wrapper marginBottom="42px"> {t('Export Description')}</Wrapper> <Wrapper marginBottom="42px"> {t['Export Description']()}</Wrapper>
<Button <Button
type="light" type="light"
shape="circle" shape="circle"
@@ -14,7 +14,7 @@ export const ExportPanel = () => {
window.apis.openSaveDBFileDialog(); window.apis.openSaveDBFileDialog();
}} }}
> >
{t('Export AFFiNE backup file')} {t['Export AFFiNE backup file']()}
</Button> </Button>
</> </>
); );

View File

@@ -1,5 +1,6 @@
import { Button, Input, Modal, ModalCloseButton } from '@affine/component'; import { Button, Input, Modal, ModalCloseButton } from '@affine/component';
import { Trans, useTranslation } from '@affine/i18n'; import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { WorkspaceFlavour } from '@affine/workspace/type'; import { WorkspaceFlavour } from '@affine/workspace/type';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name'; import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
@@ -33,11 +34,11 @@ export const WorkspaceDeleteModal = ({
); );
const [deleteStr, setDeleteStr] = useState<string>(''); const [deleteStr, setDeleteStr] = useState<string>('');
const allowDelete = deleteStr === workspaceName; const allowDelete = deleteStr === workspaceName;
const { t } = useTranslation(); const t = useAFFiNEI18N();
const handleDelete = useCallback(() => { const handleDelete = useCallback(() => {
onDeleteWorkspace().then(() => { onDeleteWorkspace().then(() => {
toast(t('Successfully deleted'), { toast(t['Successfully deleted'](), {
portal: document.body, portal: document.body,
}); });
}); });
@@ -47,7 +48,7 @@ export const WorkspaceDeleteModal = ({
<Modal open={open} onClose={onClose}> <Modal open={open} onClose={onClose}>
<StyledModalWrapper> <StyledModalWrapper>
<ModalCloseButton onClick={onClose} /> <ModalCloseButton onClick={onClose} />
<StyledModalHeader>{t('Delete Workspace')}?</StyledModalHeader> <StyledModalHeader>{t['Delete Workspace']()}?</StyledModalHeader>
{workspace.flavour === WorkspaceFlavour.LOCAL ? ( {workspace.flavour === WorkspaceFlavour.LOCAL ? (
<StyledTextContent> <StyledTextContent>
<Trans i18nKey="Delete Workspace Description"> <Trans i18nKey="Delete Workspace Description">
@@ -80,7 +81,7 @@ export const WorkspaceDeleteModal = ({
}} }}
onChange={setDeleteStr} onChange={setDeleteStr}
data-testid="delete-workspace-input" data-testid="delete-workspace-input"
placeholder={t('Placeholder of delete workspace')} placeholder={t['Placeholder of delete workspace']()}
value={deleteStr} value={deleteStr}
width={315} width={315}
height={42} height={42}
@@ -88,7 +89,7 @@ export const WorkspaceDeleteModal = ({
</StyledInputContent> </StyledInputContent>
<StyledButtonContent> <StyledButtonContent>
<Button shape="circle" onClick={onClose}> <Button shape="circle" onClick={onClose}>
{t('Cancel')} {t['Cancel']()}
</Button> </Button>
<Button <Button
data-testid="delete-workspace-confirm-button" data-testid="delete-workspace-confirm-button"
@@ -98,7 +99,7 @@ export const WorkspaceDeleteModal = ({
shape="circle" shape="circle"
style={{ marginLeft: '24px' }} style={{ marginLeft: '24px' }}
> >
{t('Delete')} {t['Delete']()}
</Button> </Button>
</StyledButtonContent> </StyledButtonContent>
</StyledModalWrapper> </StyledModalWrapper>

View File

@@ -1,6 +1,6 @@
import { Button, FlexWrapper, MuiFade } from '@affine/component'; import { Button, FlexWrapper, MuiFade } from '@affine/component';
import { WorkspaceAvatar } from '@affine/component/workspace-avatar'; import { WorkspaceAvatar } from '@affine/component/workspace-avatar';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { WorkspaceFlavour } from '@affine/workspace/type'; import { WorkspaceFlavour } from '@affine/workspace/type';
import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-block-suite-workspace-avatar-url'; import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-block-suite-workspace-avatar-url';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name'; import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
@@ -38,7 +38,7 @@ export const GeneralPanel: React.FC<PanelProps> = ({
const [input, setInput] = useState<string>(name); const [input, setInput] = useState<string>(name);
const isOwner = useIsWorkspaceOwner(workspace); const isOwner = useIsWorkspaceOwner(workspace);
const [showEditInput, setShowEditInput] = useState(false); const [showEditInput, setShowEditInput] = useState(false);
const { t } = useTranslation(); const t = useAFFiNEI18N();
const handleUpdateWorkspaceName = (name: string) => { const handleUpdateWorkspaceName = (name: string) => {
setName(name); setName(name);
@@ -50,7 +50,7 @@ export const GeneralPanel: React.FC<PanelProps> = ({
return ( return (
<> <>
<StyledRow> <StyledRow>
<StyledSettingKey>{t('Workspace Avatar')}</StyledSettingKey> <StyledSettingKey>{t['Workspace Avatar']()}</StyledSettingKey>
<StyledAvatar disabled={!isOwner}> <StyledAvatar disabled={!isOwner}>
{isOwner ? ( {isOwner ? (
<Upload <Upload
@@ -72,7 +72,7 @@ export const GeneralPanel: React.FC<PanelProps> = ({
</StyledRow> </StyledRow>
<StyledRow> <StyledRow>
<StyledSettingKey>{t('Workspace Name')}</StyledSettingKey> <StyledSettingKey>{t['Workspace Name']()}</StyledSettingKey>
<div style={{ position: 'relative' }}> <div style={{ position: 'relative' }}>
<MuiFade in={!showEditInput}> <MuiFade in={!showEditInput}>
@@ -84,7 +84,7 @@ export const GeneralPanel: React.FC<PanelProps> = ({
setShowEditInput(true); setShowEditInput(true);
}} }}
> >
{t('Edit')} {t['Edit']()}
</StyledEditButton> </StyledEditButton>
)} )}
</FlexWrapper> </FlexWrapper>
@@ -97,7 +97,7 @@ export const GeneralPanel: React.FC<PanelProps> = ({
width={284} width={284}
height={38} height={38}
value={input} value={input}
placeholder={t('Workspace Name')} placeholder={t['Workspace Name']()}
maxLength={50} maxLength={50}
minLength={0} minLength={0}
onChange={newName => { onChange={newName => {
@@ -114,7 +114,7 @@ export const GeneralPanel: React.FC<PanelProps> = ({
setShowEditInput(false); setShowEditInput(false);
}} }}
> >
{t('Confirm')} {t['Confirm']()}
</Button> </Button>
<Button <Button
type="default" type="default"
@@ -125,7 +125,7 @@ export const GeneralPanel: React.FC<PanelProps> = ({
setShowEditInput(false); setShowEditInput(false);
}} }}
> >
{t('Cancel')} {t['Cancel']()}
</Button> </Button>
</FlexWrapper> </FlexWrapper>
</MuiFade> </MuiFade>
@@ -165,29 +165,29 @@ export const GeneralPanel: React.FC<PanelProps> = ({
} }
}} }}
> >
<StyledSettingKey>{t('Workspace Type')}</StyledSettingKey> <StyledSettingKey>{t['Workspace Type']()}</StyledSettingKey>
{isOwner ? ( {isOwner ? (
workspace.flavour === WorkspaceFlavour.LOCAL ? ( workspace.flavour === WorkspaceFlavour.LOCAL ? (
<StyledWorkspaceInfo> <StyledWorkspaceInfo>
<LocalWorkspaceIcon /> <LocalWorkspaceIcon />
<span>{t('Local Workspace')}</span> <span>{t['Local Workspace']()}</span>
</StyledWorkspaceInfo> </StyledWorkspaceInfo>
) : ( ) : (
<StyledWorkspaceInfo> <StyledWorkspaceInfo>
<CloudWorkspaceIcon /> <CloudWorkspaceIcon />
<span>{t('Cloud Workspace')}</span> <span>{t['Cloud Workspace']()}</span>
</StyledWorkspaceInfo> </StyledWorkspaceInfo>
) )
) : ( ) : (
<StyledWorkspaceInfo> <StyledWorkspaceInfo>
<JoinedWorkspaceIcon /> <JoinedWorkspaceIcon />
<span>{t('Joined Workspace')}</span> <span>{t['Joined Workspace']()}</span>
</StyledWorkspaceInfo> </StyledWorkspaceInfo>
)} )}
</StyledRow> </StyledRow>
<StyledRow> <StyledRow>
<StyledSettingKey> {t('Delete Workspace')}</StyledSettingKey> <StyledSettingKey> {t['Delete Workspace']()}</StyledSettingKey>
{isOwner ? ( {isOwner ? (
<> <>
<Button <Button
@@ -199,7 +199,7 @@ export const GeneralPanel: React.FC<PanelProps> = ({
setShowDelete(true); setShowDelete(true);
}} }}
> >
{t('Delete Workspace')} {t['Delete Workspace']()}
</Button> </Button>
<WorkspaceDeleteModal <WorkspaceDeleteModal
onDeleteWorkspace={onDeleteWorkspace} onDeleteWorkspace={onDeleteWorkspace}
@@ -219,7 +219,7 @@ export const GeneralPanel: React.FC<PanelProps> = ({
setShowLeave(true); setShowLeave(true);
}} }}
> >
{t('Leave Workspace')} {t['Leave Workspace']()}
</Button> </Button>
<WorkspaceLeave <WorkspaceLeave
open={showLeave} open={showLeave}

View File

@@ -1,7 +1,7 @@
import { Modal } from '@affine/component'; import { Modal } from '@affine/component';
import { ModalCloseButton } from '@affine/component'; import { ModalCloseButton } from '@affine/component';
import { Button } from '@affine/component'; import { Button } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { import {
StyledButtonContent, StyledButtonContent,
@@ -17,7 +17,7 @@ interface WorkspaceDeleteProps {
export const WorkspaceLeave = ({ open, onClose }: WorkspaceDeleteProps) => { export const WorkspaceLeave = ({ open, onClose }: WorkspaceDeleteProps) => {
// const { leaveWorkSpace } = useWorkspaceHelper(); // const { leaveWorkSpace } = useWorkspaceHelper();
const { t } = useTranslation(); const t = useAFFiNEI18N();
const handleLeave = async () => { const handleLeave = async () => {
// await leaveWorkSpace(); // await leaveWorkSpace();
onClose(); onClose();
@@ -27,13 +27,13 @@ export const WorkspaceLeave = ({ open, onClose }: WorkspaceDeleteProps) => {
<Modal open={open} onClose={onClose}> <Modal open={open} onClose={onClose}>
<StyledModalWrapper> <StyledModalWrapper>
<ModalCloseButton onClick={onClose} /> <ModalCloseButton onClick={onClose} />
<StyledModalHeader>{t('Leave Workspace')}</StyledModalHeader> <StyledModalHeader>{t['Leave Workspace']()}</StyledModalHeader>
<StyledTextContent> <StyledTextContent>
{t('Leave Workspace Description')} {t['Leave Workspace Description']()}
</StyledTextContent> </StyledTextContent>
<StyledButtonContent> <StyledButtonContent>
<Button shape="circle" onClick={onClose}> <Button shape="circle" onClick={onClose}>
{t('Cancel')} {t['Cancel']()}
</Button> </Button>
<Button <Button
onClick={handleLeave} onClick={handleLeave}
@@ -41,7 +41,7 @@ export const WorkspaceLeave = ({ open, onClose }: WorkspaceDeleteProps) => {
shape="circle" shape="circle"
style={{ marginLeft: '24px' }} style={{ marginLeft: '24px' }}
> >
{t('Leave')} {t['Leave']()}
</Button> </Button>
</StyledButtonContent> </StyledButtonContent>
</StyledModalWrapper> </StyledModalWrapper>

View File

@@ -6,7 +6,7 @@ import {
Wrapper, Wrapper,
} from '@affine/component'; } from '@affine/component';
import { config } from '@affine/env'; import { config } from '@affine/env';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { AffineWorkspace, LocalWorkspace } from '@affine/workspace/type'; import type { AffineWorkspace, LocalWorkspace } from '@affine/workspace/type';
import { WorkspaceFlavour } from '@affine/workspace/type'; import { WorkspaceFlavour } from '@affine/workspace/type';
import { Box } from '@mui/material'; import { Box } from '@mui/material';
@@ -41,20 +41,20 @@ const PublishPanelAffine: React.FC<PublishPanelAffineProps> = ({
); );
}, []); }, []);
const shareUrl = origin + '/public-workspace/' + workspace.id; const shareUrl = origin + '/public-workspace/' + workspace.id;
const { t } = useTranslation(); const t = useAFFiNEI18N();
const publishWorkspace = useToggleWorkspacePublish(workspace); const publishWorkspace = useToggleWorkspacePublish(workspace);
const copyUrl = useCallback(() => { const copyUrl = useCallback(() => {
navigator.clipboard.writeText(shareUrl); navigator.clipboard.writeText(shareUrl);
toast(t('Copied link to clipboard')); toast(t['Copied link to clipboard']());
}, [shareUrl, t]); }, [shareUrl, t]);
if (workspace.public) { if (workspace.public) {
return ( return (
<> <>
<Wrapper marginBottom="42px">{t('Published Description')}</Wrapper> <Wrapper marginBottom="42px">{t['Published Description']()}</Wrapper>
<Wrapper marginBottom="12px"> <Wrapper marginBottom="12px">
<Content weight="500">{t('Share with link')}</Content> <Content weight="500">{t['Share with link']()}</Content>
</Wrapper> </Wrapper>
<FlexWrapper> <FlexWrapper>
<Input <Input
@@ -69,7 +69,7 @@ const PublishPanelAffine: React.FC<PublishPanelAffineProps> = ({
shape="circle" shape="circle"
style={{ marginLeft: '24px' }} style={{ marginLeft: '24px' }}
> >
{t('Copy Link')} {t['Copy Link']()}
</Button> </Button>
</FlexWrapper> </FlexWrapper>
<Button <Button
@@ -81,14 +81,14 @@ const PublishPanelAffine: React.FC<PublishPanelAffineProps> = ({
shape="circle" shape="circle"
style={{ marginTop: '38px' }} style={{ marginTop: '38px' }}
> >
{t('Stop publishing')} {t['Stop publishing']()}
</Button> </Button>
</> </>
); );
} }
return ( return (
<> <>
<Wrapper marginBottom="42px">{t('Publishing Description')}</Wrapper> <Wrapper marginBottom="42px">{t['Publishing Description']()}</Wrapper>
<Button <Button
data-testid="publish-to-web-button" data-testid="publish-to-web-button"
onClick={async () => { onClick={async () => {
@@ -97,7 +97,7 @@ const PublishPanelAffine: React.FC<PublishPanelAffineProps> = ({
type="light" type="light"
shape="circle" shape="circle"
> >
{t('Publish to web')} {t['Publish to web']()}
</Button> </Button>
</> </>
); );
@@ -111,7 +111,7 @@ const PublishPanelLocal: React.FC<PublishPanelLocalProps> = ({
workspace, workspace,
onTransferWorkspace, onTransferWorkspace,
}) => { }) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
return ( return (
<> <>
@@ -120,7 +120,7 @@ const PublishPanelLocal: React.FC<PublishPanelLocalProps> = ({
marginBottom: '42px', marginBottom: '42px',
}} }}
> >
{t('Publishing')} {t['Publishing']()}
</Box> </Box>
{/* TmpDisableAffineCloudModal */} {/* TmpDisableAffineCloudModal */}
@@ -132,7 +132,7 @@ const PublishPanelLocal: React.FC<PublishPanelLocalProps> = ({
setOpen(true); setOpen(true);
}} }}
> >
{t('Enable AFFiNE Cloud')} {t['Enable AFFiNE Cloud']()}
</Button> </Button>
{config.enableLegacyCloud ? ( {config.enableLegacyCloud ? (
<EnableAffineCloudModal <EnableAffineCloudModal

View File

@@ -1,5 +1,6 @@
import { Content, FlexWrapper, styled } from '@affine/component'; import { Content, FlexWrapper, styled } from '@affine/component';
import { Trans, useTranslation } from '@affine/i18n'; import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { WorkspaceFlavour } from '@affine/workspace/type'; import { WorkspaceFlavour } from '@affine/workspace/type';
import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-block-suite-workspace-avatar-url'; import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-block-suite-workspace-avatar-url';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name'; import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
@@ -25,7 +26,7 @@ export const SyncPanel: React.FC<PanelProps> = ({ workspace }) => {
workspace.blockSuiteWorkspace workspace.blockSuiteWorkspace
); );
const user = useCurrentUser(); const user = useCurrentUser();
const { t } = useTranslation(); const t = useAFFiNEI18N();
return ( return (
<> <>
<FlexWrapper alignItems="center" style={{ marginBottom: '12px' }}> <FlexWrapper alignItems="center" style={{ marginBottom: '12px' }}>
@@ -37,7 +38,7 @@ export const SyncPanel: React.FC<PanelProps> = ({ workspace }) => {
/> />
<StyledWorkspaceName>{name}</StyledWorkspaceName> <StyledWorkspaceName>{name}</StyledWorkspaceName>
&nbsp; &nbsp;
<Content weight={500}>{t('is a Cloud Workspace')}</Content> <Content weight={500}>{t['is a Cloud Workspace']()}</Content>
</FlexWrapper> </FlexWrapper>
<Trans i18nKey="Cloud Workspace Description"> <Trans i18nKey="Cloud Workspace Description">
All data will be synchronised and saved to the AFFiNE account All data will be synchronised and saved to the AFFiNE account

View File

@@ -1,22 +1,22 @@
import { Empty } from '@affine/component'; import { Empty } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import React from 'react'; import React from 'react';
export const PageListEmpty = (props: { listType?: string }) => { export const PageListEmpty = (props: { listType?: string }) => {
const { listType } = props; const { listType } = props;
const { t } = useTranslation(); const t = useAFFiNEI18N();
const getEmptyDescription = () => { const getEmptyDescription = () => {
if (listType === 'all') { if (listType === 'all') {
return t('emptyAllPages'); return t['emptyAllPages']();
} }
if (listType === 'favorite') { if (listType === 'favorite') {
return t('emptyFavorite'); return t['emptyFavorite']();
} }
if (listType === 'trash') { if (listType === 'trash') {
return t('emptyTrash'); return t['emptyTrash']();
} }
if (listType === 'shared') { if (listType === 'shared') {
return t('emptySharedPages'); return t['emptySharedPages']();
} }
}; };

View File

@@ -6,7 +6,7 @@ import {
MenuItem, MenuItem,
Tooltip, Tooltip,
} from '@affine/component'; } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { import {
DeletePermanentlyIcon, DeletePermanentlyIcon,
FavoritedIcon, FavoritedIcon,
@@ -44,7 +44,7 @@ export const OperationCell: React.FC<OperationCellProps> = ({
onToggleTrashPage, onToggleTrashPage,
}) => { }) => {
const { id, favorite, isPublic } = pageMeta; const { id, favorite, isPublic } = pageMeta;
const { t } = useTranslation(); const t = useAFFiNEI18N();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [openDisableShared, setOpenDisableShared] = useState(false); const [openDisableShared, setOpenDisableShared] = useState(false);
@@ -65,7 +65,7 @@ export const OperationCell: React.FC<OperationCellProps> = ({
onClick={() => { onClick={() => {
onToggleFavoritePage(id); onToggleFavoritePage(id);
toast( toast(
favorite ? t('Removed from Favorites') : t('Added to Favorites') favorite ? t['Removed from Favorites']() : t['Added to Favorites']()
); );
}} }}
icon={ icon={
@@ -76,7 +76,7 @@ export const OperationCell: React.FC<OperationCellProps> = ({
) )
} }
> >
{favorite ? t('Remove from favorites') : t('Add to Favorites')} {favorite ? t['Remove from favorites']() : t['Add to Favorites']()}
</MenuItem> </MenuItem>
{!environment.isDesktop && ( {!environment.isDesktop && (
<MenuItem <MenuItem
@@ -85,7 +85,7 @@ export const OperationCell: React.FC<OperationCellProps> = ({
}} }}
icon={<OpenInNewIcon />} icon={<OpenInNewIcon />}
> >
{t('Open in new tab')} {t['Open in new tab']()}
</MenuItem> </MenuItem>
)} )}
{!pageMeta.isRootPinboard && ( {!pageMeta.isRootPinboard && (
@@ -117,7 +117,7 @@ export const OperationCell: React.FC<OperationCellProps> = ({
meta={pageMeta} meta={pageMeta}
onConfirm={() => { onConfirm={() => {
onToggleTrashPage(id, true); onToggleTrashPage(id, true);
toast(t('Deleted')); toast(t['Moved to Trash']());
setOpen(false); setOpen(false);
}} }}
onClose={() => { onClose={() => {
@@ -151,22 +151,22 @@ export const TrashOperationCell: React.FC<TrashOperationCellProps> = ({
onRestorePage, onRestorePage,
}) => { }) => {
const { id, title } = pageMeta; const { id, title } = pageMeta;
const { t } = useTranslation(); const t = useAFFiNEI18N();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
return ( return (
<FlexWrapper> <FlexWrapper>
<Tooltip content={t('Restore it')} placement="top-start"> <Tooltip content={t['Restore it']()} placement="top-start">
<IconButton <IconButton
style={{ marginRight: '12px' }} style={{ marginRight: '12px' }}
onClick={() => { onClick={() => {
onRestorePage(id); onRestorePage(id);
toast(t('restored', { title: title || 'Untitled' })); toast(t['restored']({ title: title || 'Untitled' }));
}} }}
> >
<ResetIcon /> <ResetIcon />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<Tooltip content={t('Delete permanently')} placement="top-start"> <Tooltip content={t['Delete permanently']()} placement="top-start">
<IconButton <IconButton
onClick={() => { onClick={() => {
setOpen(true); setOpen(true);
@@ -176,14 +176,14 @@ export const TrashOperationCell: React.FC<TrashOperationCellProps> = ({
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<Confirm <Confirm
title={t('Delete permanently?')} title={t['Delete permanently?']()}
content={t("Once deleted, you can't undo this action.")} content={t['TrashButtonGroupDescription']()}
confirmText={t('Delete')} confirmText={t['Delete']()}
confirmType="danger" confirmType="danger"
open={open} open={open}
onConfirm={() => { onConfirm={() => {
onPermanentlyDeletePage(id); onPermanentlyDeletePage(id);
toast(t('Permanently deleted')); toast(t['Permanently deleted']());
setOpen(false); setOpen(false);
}} }}
onClose={() => { onClose={() => {

View File

@@ -8,7 +8,7 @@ import {
TableRow, TableRow,
Tooltip, Tooltip,
} from '@affine/component'; } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { import {
EdgelessIcon, EdgelessIcon,
FavoritedIcon, FavoritedIcon,
@@ -47,10 +47,10 @@ const FavoriteTag: React.FC<FavoriteTagProps> = ({
pageMeta: { favorite }, pageMeta: { favorite },
onClick, onClick,
}) => { }) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
return ( return (
<Tooltip <Tooltip
content={favorite ? t('Favorited') : t('Favorite')} content={favorite ? t['Favorited']() : t['Favorite']()}
placement="top-start" placement="top-start"
> >
<IconButton <IconButton
@@ -59,7 +59,7 @@ const FavoriteTag: React.FC<FavoriteTagProps> = ({
e.stopPropagation(); e.stopPropagation();
onClick(); onClick();
toast( toast(
favorite ? t('Removed from Favorites') : t('Added to Favorites') favorite ? t['Removed from Favorites']() : t['Added to Favorites']()
); );
}} }}
style={{ style={{
@@ -106,7 +106,7 @@ export const PageList: React.FC<PageListProps> = ({
const helper = usePageMetaHelper(blockSuiteWorkspace); const helper = usePageMetaHelper(blockSuiteWorkspace);
const { removeToTrash, restoreFromTrash } = const { removeToTrash, restoreFromTrash } =
useBlockSuiteMetaHelper(blockSuiteWorkspace); useBlockSuiteMetaHelper(blockSuiteWorkspace);
const { t } = useTranslation(); const t = useAFFiNEI18N();
const theme = useTheme(); const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.up('sm')); const matches = useMediaQuery(theme.breakpoints.up('sm'));
const isTrash = listType === 'trash'; const isTrash = listType === 'trash';
@@ -130,14 +130,14 @@ export const PageList: React.FC<PageListProps> = ({
<TableRow> <TableRow>
{matches && ( {matches && (
<> <>
<TableCell proportion={0.5}>{t('Title')}</TableCell> <TableCell proportion={0.5}>{t['Title']()}</TableCell>
<TableCell proportion={0.2}>{t('Created')}</TableCell> <TableCell proportion={0.2}>{t['Created']()}</TableCell>
<TableCell proportion={0.2}> <TableCell proportion={0.2}>
{isTrash {isTrash
? t('Moved to Trash') ? t['Moved to Trash']()
: isShared : isShared
? 'Shared' ? 'Shared'
: t('Updated')} : t['Updated']()}
</TableCell> </TableCell>
<TableCell proportion={0.1}></TableCell> <TableCell proportion={0.1}></TableCell>
</> </>
@@ -164,7 +164,7 @@ export const PageList: React.FC<PageListProps> = ({
<PageIcon /> <PageIcon />
)} )}
<Content ellipsis={true} color="inherit"> <Content ellipsis={true} color="inherit">
{pageMeta.title || t('Untitled')} {pageMeta.title || t['Untitled']()}
</Content> </Content>
</StyledTitleLink> </StyledTitleLink>
{listType && !isTrash && ( {listType && !isTrash && (

View File

@@ -1,6 +1,6 @@
// fixme(himself65): refactor this file // fixme(himself65): refactor this file
import { FlexWrapper, IconButton, Menu, MenuItem } from '@affine/component'; import { FlexWrapper, IconButton, Menu, MenuItem } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { import {
EdgelessIcon, EdgelessIcon,
FavoritedIcon, FavoritedIcon,
@@ -57,7 +57,7 @@ const CommonMenu = () => {
); );
}; };
const PageMenu = () => { const PageMenu = () => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
// fixme(himself65): remove these hooks ASAP // fixme(himself65): remove these hooks ASAP
const [workspace] = useCurrentWorkspace(); const [workspace] = useCurrentWorkspace();
const [pageId] = useCurrentPageId(); const [pageId] = useCurrentPageId();
@@ -83,7 +83,9 @@ const PageMenu = () => {
onClick={() => { onClick={() => {
setPageMeta(pageId, { favorite: !favorite }); setPageMeta(pageId, { favorite: !favorite });
toast( toast(
favorite ? t('Removed from Favorites') : t('Added to Favorites') favorite
? t['Removed from Favorites']()
: t['Added to Favorites']()
); );
}} }}
icon={ icon={
@@ -94,7 +96,7 @@ const PageMenu = () => {
) )
} }
> >
{favorite ? t('Remove from favorites') : t('Add to Favorites')} {favorite ? t['Remove from favorites']() : t['Add to Favorites']()}
</MenuItem> </MenuItem>
<MenuItem <MenuItem
icon={mode === 'page' ? <EdgelessIcon /> : <PageIcon />} icon={mode === 'page' ? <EdgelessIcon /> : <PageIcon />}
@@ -106,8 +108,8 @@ const PageMenu = () => {
})); }));
}} }}
> >
{t('Convert to ')} {t['Convert to ']()}
{mode === 'page' ? t('Edgeless') : t('Page')} {mode === 'page' ? t['Edgeless']() : t['Page']()}
</MenuItem> </MenuItem>
<Export /> <Export />
{!pageMeta.isRootPinboard && ( {!pageMeta.isRootPinboard && (
@@ -153,7 +155,7 @@ const PageMenu = () => {
meta={pageMeta} meta={pageMeta}
onConfirm={() => { onConfirm={() => {
removeToTrash(pageMeta.id); removeToTrash(pageMeta.id);
toast(t('Moved to Trash')); toast(t['Moved to Trash']());
}} }}
onCancel={() => { onCancel={() => {
setOpenConfirm(false); setOpenConfirm(false);

View File

@@ -1,12 +1,12 @@
import { Button, displayFlex, Menu, MenuItem, styled } from '@affine/component'; import { Button, displayFlex, Menu, MenuItem, styled } from '@affine/component';
import { LOCALES } from '@affine/i18n'; import { LOCALES } from '@affine/i18n';
import { useTranslation } from '@affine/i18n'; import { useI18N } from '@affine/i18n';
import { ArrowDownSmallIcon, PublishIcon } from '@blocksuite/icons'; import { ArrowDownSmallIcon, PublishIcon } from '@blocksuite/icons';
import type { FC, ReactElement } from 'react'; import type { FC, ReactElement } from 'react';
import { useCallback } from 'react'; import { useCallback } from 'react';
const LanguageMenuContent: FC = () => { const LanguageMenuContent: FC = () => {
const { i18n } = useTranslation(); const i18n = useI18N();
const changeLanguage = useCallback( const changeLanguage = useCallback(
(event: string) => { (event: string) => {
i18n.changeLanguage(event); i18n.changeLanguage(event);
@@ -32,7 +32,7 @@ const LanguageMenuContent: FC = () => {
); );
}; };
export const LanguageMenu: React.FC = () => { export const LanguageMenu: React.FC = () => {
const { i18n } = useTranslation(); const i18n = useI18N();
const currentLanguage = LOCALES.find(item => item.tag === i18n.language); const currentLanguage = LOCALES.find(item => item.tag === i18n.language);

View File

@@ -1,6 +1,6 @@
import { displayFlex, IconButton, styled, Tooltip } from '@affine/component'; import { displayFlex, IconButton, styled, Tooltip } from '@affine/component';
import { config } from '@affine/env'; import { config } from '@affine/env';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { import {
getLoginStorage, getLoginStorage,
setLoginStorage, setLoginStorage,
@@ -79,7 +79,7 @@ export const SyncUser = () => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const { t } = useTranslation(); const t = useAFFiNEI18N();
const transformWorkspace = useTransformWorkspace(); const transformWorkspace = useTransformWorkspace();
if (!config.enableLegacyCloud) { if (!config.enableLegacyCloud) {
@@ -89,7 +89,7 @@ export const SyncUser = () => {
if (status === 'offline') { if (status === 'offline') {
return ( return (
<Tooltip <Tooltip
content={t('Please make sure you are online')} content={t['Please make sure you are online']()}
placement="bottom-end" placement="bottom-end"
> >
<IconWrapper> <IconWrapper>
@@ -103,7 +103,7 @@ export const SyncUser = () => {
return ( return (
<> <>
<Tooltip <Tooltip
content={t('Saved then enable AFFiNE Cloud')} content={t['Saved then enable AFFiNE Cloud']()}
placement="bottom-end" placement="bottom-end"
> >
<IconButton <IconButton
@@ -156,7 +156,7 @@ export const SyncUser = () => {
} }
return ( return (
<Tooltip content={t('Synced with AFFiNE Cloud')} placement="bottom-end"> <Tooltip content={t['Synced with AFFiNE Cloud']()} placement="bottom-end">
<IconWrapper> <IconWrapper>
<CloudWorkspaceIcon /> <CloudWorkspaceIcon />
</IconWrapper> </IconWrapper>

View File

@@ -1,5 +1,5 @@
import { Button, Confirm } from '@affine/component'; import { Button, Confirm } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { assertExists } from '@blocksuite/store'; import { assertExists } from '@blocksuite/store';
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
@@ -20,7 +20,7 @@ export const TrashButtonGroup = () => {
meta => meta.id === pageId meta => meta.id === pageId
); );
assertExists(pageMeta); assertExists(pageMeta);
const { t } = useTranslation(); const t = useAFFiNEI18N();
const router = useRouter(); const router = useRouter();
const { restoreFromTrash } = useBlockSuiteMetaHelper(blockSuiteWorkspace); const { restoreFromTrash } = useBlockSuiteMetaHelper(blockSuiteWorkspace);
@@ -36,7 +36,7 @@ export const TrashButtonGroup = () => {
restoreFromTrash(pageId); restoreFromTrash(pageId);
}} }}
> >
{t('Restore it')} {t['Restore it']()}
</Button> </Button>
<Button <Button
bold={true} bold={true}
@@ -46,12 +46,12 @@ export const TrashButtonGroup = () => {
setOpen(true); setOpen(true);
}} }}
> >
{t('Delete permanently')} {t['Delete permanently']()}
</Button> </Button>
<Confirm <Confirm
title={t('TrashButtonGroupTitle')} title={t['TrashButtonGroupTitle']()}
content={t('TrashButtonGroupDescription')} content={t['TrashButtonGroupDescription']()}
confirmText={t('Delete')} confirmText={t['Delete']()}
confirmType="danger" confirmType="danger"
open={open} open={open}
onConfirm={() => { onConfirm={() => {

View File

@@ -1,5 +1,5 @@
import { appSidebarOpenAtom } from '@affine/component/app-sidebar'; import { appSidebarOpenAtom } from '@affine/component/app-sidebar';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { WorkspaceFlavour } from '@affine/workspace/type'; import { WorkspaceFlavour } from '@affine/workspace/type';
import { CloseIcon } from '@blocksuite/icons'; import { CloseIcon } from '@blocksuite/icons';
import type { Page } from '@blocksuite/store'; import type { Page } from '@blocksuite/store';
@@ -133,7 +133,7 @@ export const Header = forwardRef<
setShowWarning(shouldShowWarning()); setShowWarning(shouldShowWarning());
}, []); }, []);
const [open] = useAtom(appSidebarOpenAtom); const [open] = useAtom(appSidebarOpenAtom);
const { t } = useTranslation(); const t = useAFFiNEI18N();
return ( return (
<StyledHeaderContainer <StyledHeaderContainer
@@ -156,7 +156,7 @@ export const Header = forwardRef<
<Suspense> <Suspense>
<SidebarSwitch <SidebarSwitch
visible={!open} visible={!open}
tooltipContent={t('Expand sidebar')} tooltipContent={t['Expand sidebar']()}
data-testid="sliderBar-arrowButton-expand" data-testid="sliderBar-arrowButton-expand"
/> />
</Suspense> </Suspense>

View File

@@ -1,5 +1,6 @@
import { getEnvironment } from '@affine/env'; import { getEnvironment } from '@affine/env';
import { Trans, useTranslation } from '@affine/i18n'; import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type React from 'react'; import type React from 'react';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
@@ -23,7 +24,7 @@ export const shouldShowWarning = () => {
}; };
export const OSWarningMessage: React.FC = () => { export const OSWarningMessage: React.FC = () => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
const [notChrome, setNotChrome] = useState(false); const [notChrome, setNotChrome] = useState(false);
const [notGoodVersion, setNotGoodVersion] = useState(false); const [notGoodVersion, setNotGoodVersion] = useState(false);
useEffect(() => { useEffect(() => {
@@ -44,7 +45,7 @@ export const OSWarningMessage: React.FC = () => {
</span> </span>
); );
} else if (notGoodVersion) { } else if (notGoodVersion) {
return <span>{t('upgradeBrowser')}</span>; return <span>{t['upgradeBrowser']()}</span>;
} }
return null; return null;
}; };

View File

@@ -6,7 +6,7 @@ import {
ModalWrapper, ModalWrapper,
styled, styled,
} from '@affine/component'; } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { KeyboardEvent } from 'react'; import type { KeyboardEvent } from 'react';
import { useCallback, useRef, useState } from 'react'; import { useCallback, useRef, useState } from 'react';
@@ -35,7 +35,7 @@ export const CreateWorkspaceModal = ({
}, },
[handleCreateWorkspace, workspaceName] [handleCreateWorkspace, workspaceName]
); );
const { t } = useTranslation(); const t = useAFFiNEI18N();
return ( return (
<Modal open={open} onClose={onClose}> <Modal open={open} onClose={onClose}>
<ModalWrapper width={560} height={342} style={{ padding: '10px' }}> <ModalWrapper width={560} height={342} style={{ padding: '10px' }}>
@@ -49,8 +49,8 @@ export const CreateWorkspaceModal = ({
/> />
</Header> </Header>
<Content> <Content>
<ContentTitle>{t('New Workspace')}</ContentTitle> <ContentTitle>{t['New Workspace']()}</ContentTitle>
<p>{t('Workspace description')}</p> <p>{t['Workspace description']()}</p>
<Input <Input
ref={ref => { ref={ref => {
if (ref) { if (ref) {
@@ -59,7 +59,7 @@ export const CreateWorkspaceModal = ({
}} }}
data-testid="create-workspace-input" data-testid="create-workspace-input"
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
placeholder={t('Set a Workspace name')} placeholder={t['Set a Workspace name']()}
maxLength={15} maxLength={15}
minLength={0} minLength={0}
onChange={value => { onChange={value => {
@@ -86,7 +86,7 @@ export const CreateWorkspaceModal = ({
handleCreateWorkspace(); handleCreateWorkspace();
}} }}
> >
{t('Create')} {t['Create']()}
</Button> </Button>
</Content> </Content>
</ModalWrapper> </ModalWrapper>

View File

@@ -1,6 +1,6 @@
import { Button } from '@affine/component'; import { Button } from '@affine/component';
import { styled } from '@affine/component'; import { styled } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { ChangeEvent } from 'react'; import type { ChangeEvent } from 'react';
import type React from 'react'; import type React from 'react';
import { useRef } from 'react'; import { useRef } from 'react';
@@ -17,7 +17,7 @@ export const Upload: React.FC<UploadProps> = ({
children, children,
...props ...props
}) => { }) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
const input_ref = useRef<HTMLInputElement>(null); const input_ref = useRef<HTMLInputElement>(null);
const _chooseFile = () => { const _chooseFile = () => {
if (input_ref.current) { if (input_ref.current) {
@@ -37,7 +37,7 @@ export const Upload: React.FC<UploadProps> = ({
}; };
return ( return (
<UploadStyle onClick={_chooseFile}> <UploadStyle onClick={_chooseFile}>
{children ?? <Button>{t('Upload')}</Button>} {children ?? <Button>{t['Upload']()}</Button>}
<input <input
ref={input_ref} ref={input_ref}
type="file" type="file"

View File

@@ -2,7 +2,7 @@ import { FlexWrapper } from '@affine/component';
import { IconButton } from '@affine/component'; import { IconButton } from '@affine/component';
import { Tooltip } from '@affine/component'; import { Tooltip } from '@affine/component';
import { config } from '@affine/env'; import { config } from '@affine/env';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { AccessTokenMessage } from '@affine/workspace/affine/login'; import type { AccessTokenMessage } from '@affine/workspace/affine/login';
import { CloudWorkspaceIcon, SignOutIcon } from '@blocksuite/icons'; import { CloudWorkspaceIcon, SignOutIcon } from '@blocksuite/icons';
import { useSetAtom } from 'jotai'; import { useSetAtom } from 'jotai';
@@ -21,7 +21,7 @@ export type FooterProps = {
}; };
export const Footer: React.FC<FooterProps> = ({ user, onLogin, onLogout }) => { export const Footer: React.FC<FooterProps> = ({ user, onLogin, onLogout }) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
const setOpen = useSetAtom(openDisableCloudAlertModalAtom); const setOpen = useSetAtom(openDisableCloudAlertModalAtom);
return ( return (
<StyledFooter data-testid="workspace-list-modal-footer"> <StyledFooter data-testid="workspace-list-modal-footer">
@@ -38,7 +38,7 @@ export const Footer: React.FC<FooterProps> = ({ user, onLogin, onLogout }) => {
<p>{user.email}</p> <p>{user.email}</p>
</StyleUserInfo> </StyleUserInfo>
</FlexWrapper> </FlexWrapper>
<Tooltip content={t('Sign out')} disablePortal={true}> <Tooltip content={t['Sign out']()} disablePortal={true}>
<IconButton <IconButton
data-testid="workspace-list-modal-sign-out" data-testid="workspace-list-modal-sign-out"
onClick={() => { onClick={() => {
@@ -69,7 +69,7 @@ export const Footer: React.FC<FooterProps> = ({ user, onLogin, onLogout }) => {
} }
}} }}
> >
{t('Sign in')} {t['Sign in']()}
</StyledSignInButton> </StyledSignInButton>
)} )}
</StyledFooter> </StyledFooter>

View File

@@ -1,5 +1,5 @@
import { MuiFade, Tooltip } from '@affine/component'; import { MuiFade, Tooltip } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CloseIcon, NewIcon } from '@blocksuite/icons'; import { CloseIcon, NewIcon } from '@blocksuite/icons';
import { lazy, Suspense, useState } from 'react'; import { lazy, Suspense, useState } from 'react';
@@ -27,7 +27,7 @@ export const HelpIsland = ({
const [spread, setShowSpread] = useState(false); const [spread, setShowSpread] = useState(false);
// const { triggerShortcutsModal, triggerContactModal } = useModal(); // const { triggerShortcutsModal, triggerContactModal } = useModal();
// const blockHub = useGlobalState(store => store.blockHub); // const blockHub = useGlobalState(store => store.blockHub);
const { t } = useTranslation(); const t = useAFFiNEI18N();
// //
// useEffect(() => { // useEffect(() => {
// blockHub?.blockHubStatusUpdated.on(status => { // blockHub?.blockHubStatusUpdated.on(status => {
@@ -58,7 +58,7 @@ export const HelpIsland = ({
style={{ height: spread ? `${showList.length * 44}px` : 0 }} style={{ height: spread ? `${showList.length * 44}px` : 0 }}
> >
{showList.includes('whatNew') && ( {showList.includes('whatNew') && (
<Tooltip content={t("Discover what's new!")} placement="left-end"> <Tooltip content={t["Discover what's new!"]()} placement="left-end">
<StyledIconWrapper <StyledIconWrapper
data-testid="right-bottom-change-log-icon" data-testid="right-bottom-change-log-icon"
onClick={() => { onClick={() => {
@@ -73,7 +73,7 @@ export const HelpIsland = ({
</Tooltip> </Tooltip>
)} )}
{showList.includes('contact') && ( {showList.includes('contact') && (
<Tooltip content={t('Contact Us')} placement="left-end"> <Tooltip content={t['Contact Us']()} placement="left-end">
<StyledIconWrapper <StyledIconWrapper
data-testid="right-bottom-contact-us-icon" data-testid="right-bottom-contact-us-icon"
onClick={() => { onClick={() => {
@@ -86,7 +86,7 @@ export const HelpIsland = ({
</Tooltip> </Tooltip>
)} )}
{showList.includes('shortcuts') && ( {showList.includes('shortcuts') && (
<Tooltip content={t('Keyboard Shortcuts')} placement="left-end"> <Tooltip content={t['Keyboard Shortcuts']()} placement="left-end">
<StyledIconWrapper <StyledIconWrapper
data-testid="shortcuts-icon" data-testid="shortcuts-icon"
onClick={() => { onClick={() => {
@@ -100,7 +100,7 @@ export const HelpIsland = ({
)} )}
</StyledAnimateWrapper> </StyledAnimateWrapper>
<Tooltip content={t('Help and Feedback')} placement="left-end"> <Tooltip content={t['Help and Feedback']()} placement="left-end">
<MuiFade in={!spread} data-testid="faq-icon"> <MuiFade in={!spread} data-testid="faq-icon">
<StyledTriggerWrapper> <StyledTriggerWrapper>
<HelpIcon /> <HelpIcon />

View File

@@ -1,6 +1,6 @@
import { styled } from '@affine/component'; import { styled } from '@affine/component';
import { AffineLoading } from '@affine/component/affine-loading'; import { AffineLoading } from '@affine/component/affine-loading';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { memo, Suspense } from 'react'; import { memo, Suspense } from 'react';
export const Loading = memo(function Loading() { export const Loading = memo(function Loading() {
@@ -36,11 +36,11 @@ const StyledLoadingContainer = styled('div')(() => {
}); });
export const PageLoading = ({ text }: { text?: string }) => { export const PageLoading = ({ text }: { text?: string }) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
return ( return (
<StyledLoadingContainer> <StyledLoadingContainer>
<Loading /> <Loading />
<h1>{text ? text : t('Loading')}</h1> <h1>{text ? text : t['Loading']()}</h1>
</StyledLoadingContainer> </StyledLoadingContainer>
); );
}; };

View File

@@ -1,5 +1,5 @@
import { initPage } from '@affine/env/blocksuite'; import { initPage } from '@affine/env/blocksuite';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { PageBlockModel } from '@blocksuite/blocks'; import type { PageBlockModel } from '@blocksuite/blocks';
import { PlusIcon } from '@blocksuite/icons'; import { PlusIcon } from '@blocksuite/icons';
import { assertEquals, nanoid } from '@blocksuite/store'; import { assertEquals, nanoid } from '@blocksuite/store';
@@ -27,7 +27,7 @@ export const Footer: React.FC<FooterProps> = ({
router, router,
}) => { }) => {
const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace); const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace);
const { t } = useTranslation(); const t = useAFFiNEI18N();
const { jumpToPage } = useRouterHelper(router); const { jumpToPage } = useRouterHelper(router);
const MAX_QUERY_SHOW_LENGTH = 20; const MAX_QUERY_SHOW_LENGTH = 20;
const normalizedQuery = const normalizedQuery =
@@ -60,9 +60,9 @@ export const Footer: React.FC<FooterProps> = ({
<StyledModalFooterContent> <StyledModalFooterContent>
<PlusIcon /> <PlusIcon />
{query ? ( {query ? (
<span>{t('New Keyword Page', { query: normalizedQuery })}</span> <span>{t['New Keyword Page']({ query: normalizedQuery })}</span>
) : ( ) : (
<span>{t('New Page')}</span> <span>{t['New Page']()}</span>
)} )}
</StyledModalFooterContent> </StyledModalFooterContent>
</Command.Item> </Command.Item>

View File

@@ -1,4 +1,4 @@
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { EdgelessIcon, PageIcon } from '@blocksuite/icons'; import { EdgelessIcon, PageIcon } from '@blocksuite/icons';
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
import { Command } from 'cmdk'; import { Command } from 'cmdk';
@@ -41,7 +41,7 @@ export const PublishedResults: FC<PublishedResultsProps> = ({
// router.push('/404'); // router.push('/404');
// }); // });
// }, [router, dataCenter, setPublishWorkspaceName]); // }, [router, dataCenter, setPublishWorkspaceName]);
const { t } = useTranslation(); const t = useAFFiNEI18N();
useEffect(() => { useEffect(() => {
setResults(blockSuiteWorkspace.search(query)); setResults(blockSuiteWorkspace.search(query));
//Save the Map<BlockId, PageId> obtained from the search as state //Save the Map<BlockId, PageId> obtained from the search as state
@@ -57,7 +57,7 @@ export const PublishedResults: FC<PublishedResultsProps> = ({
{query ? ( {query ? (
resultsPageMeta.length ? ( resultsPageMeta.length ? (
<Command.Group <Command.Group
heading={t('Find results', { number: resultsPageMeta.length })} heading={t['Find results']({ number: `${resultsPageMeta.length}` })}
> >
{resultsPageMeta.map(result => { {resultsPageMeta.map(result => {
return ( return (
@@ -85,7 +85,7 @@ export const PublishedResults: FC<PublishedResultsProps> = ({
</Command.Group> </Command.Group>
) : ( ) : (
<StyledNotFound> <StyledNotFound>
<span>{t('Find 0 result')}</span> <span>{t['Find 0 result']()}</span>
<Image <Image
src="/imgs/no-result.svg" src="/imgs/no-result.svg"
alt="no result" alt="no result"

View File

@@ -1,5 +1,5 @@
import { UNTITLED_WORKSPACE_NAME } from '@affine/env'; import { UNTITLED_WORKSPACE_NAME } from '@affine/env';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { EdgelessIcon, PageIcon } from '@blocksuite/icons'; import { EdgelessIcon, PageIcon } from '@blocksuite/icons';
import { assertExists } from '@blocksuite/store'; import { assertExists } from '@blocksuite/store';
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
@@ -36,7 +36,7 @@ export const Results: FC<ResultsProps> = ({
const List = useSwitchToConfig(blockSuiteWorkspace.id); const List = useSwitchToConfig(blockSuiteWorkspace.id);
const recentlyViewed = useRecentlyViewed(); const recentlyViewed = useRecentlyViewed();
const { t } = useTranslation(); const t = useAFFiNEI18N();
const { jumpToPage } = useRouterHelper(router); const { jumpToPage } = useRouterHelper(router);
const results = blockSuiteWorkspace.search(query); const results = blockSuiteWorkspace.search(query);
@@ -61,7 +61,7 @@ export const Results: FC<ResultsProps> = ({
return ( return (
<> <>
{recentlyViewedItem.length > 0 && ( {recentlyViewedItem.length > 0 && (
<Command.Group heading={t('Recent')}> <Command.Group heading={t['Recent']()}>
{recentlyViewedItem.map(recent => { {recentlyViewedItem.map(recent => {
const page = pageList.find(page => recent.id === page.id); const page = pageList.find(page => recent.id === page.id);
assertExists(page); assertExists(page);
@@ -87,7 +87,7 @@ export const Results: FC<ResultsProps> = ({
})} })}
</Command.Group> </Command.Group>
)} )}
<Command.Group heading={t('Jump to')}> <Command.Group heading={t['Jump to']()}>
{List.map(link => { {List.map(link => {
return ( return (
<Command.Item <Command.Item
@@ -112,7 +112,7 @@ export const Results: FC<ResultsProps> = ({
if (!resultsPageMeta.length) { if (!resultsPageMeta.length) {
return ( return (
<StyledNotFound> <StyledNotFound>
<span>{t('Find 0 result')}</span> <span>{t['Find 0 result']()}</span>
<Image <Image
src="/imgs/no-result.svg" src="/imgs/no-result.svg"
alt="no result" alt="no result"
@@ -124,7 +124,7 @@ export const Results: FC<ResultsProps> = ({
} }
return ( return (
<Command.Group <Command.Group
heading={t('Find results', { number: resultsPageMeta.length })} heading={t['Find results']({ number: `${resultsPageMeta.length}` })}
> >
{resultsPageMeta.map(result => { {resultsPageMeta.map(result => {
return ( return (

View File

@@ -1,4 +1,4 @@
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { import {
DeleteTemporarilyIcon, DeleteTemporarilyIcon,
FavoriteIcon, FavoriteIcon,
@@ -16,26 +16,26 @@ export const useSwitchToConfig = (
href: string; href: string;
icon: FC<SVGProps<SVGSVGElement>>; icon: FC<SVGProps<SVGSVGElement>>;
}[] => { }[] => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
return useMemo( return useMemo(
() => [ () => [
{ {
title: t('All pages'), title: t['All pages'](),
href: pathGenerator.all(workspaceId), href: pathGenerator.all(workspaceId),
icon: FolderIcon, icon: FolderIcon,
}, },
{ {
title: t('Favorites'), title: t['Favorites'](),
href: pathGenerator.favorite(workspaceId), href: pathGenerator.favorite(workspaceId),
icon: FavoriteIcon, icon: FavoriteIcon,
}, },
{ {
title: t('Workspace Settings'), title: t['Workspace Settings'](),
href: pathGenerator.setting(workspaceId), href: pathGenerator.setting(workspaceId),
icon: SettingsIcon, icon: SettingsIcon,
}, },
{ {
title: t('Trash'), title: t['Trash'](),
href: pathGenerator.trash(workspaceId), href: pathGenerator.trash(workspaceId),
icon: DeleteTemporarilyIcon, icon: DeleteTemporarilyIcon,
}, },

View File

@@ -1,6 +1,6 @@
import { Modal, ModalWrapper } from '@affine/component'; import { Modal, ModalWrapper } from '@affine/component';
import { getEnvironment } from '@affine/env'; import { getEnvironment } from '@affine/env';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { Command } from 'cmdk'; import { Command } from 'cmdk';
import type { NextRouter } from 'next/router'; import type { NextRouter } from 'next/router';
import type React from 'react'; import type React from 'react';
@@ -45,7 +45,7 @@ export const QuickSearchModal: React.FC<QuickSearchModalProps> = ({
router, router,
blockSuiteWorkspace, blockSuiteWorkspace,
}) => { }) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const [loading, startTransition] = useTransition(); const [loading, startTransition] = useTransition();
const [query, _setQuery] = useState(''); const [query, _setQuery] = useState('');
@@ -145,10 +145,10 @@ export const QuickSearchModal: React.FC<QuickSearchModalProps> = ({
}} }}
placeholder={ placeholder={
isPublicWorkspace isPublicWorkspace
? t('Quick search placeholder2', { ? t['Quick search placeholder2']({
workspace: publishWorkspaceName, workspace: publishWorkspaceName,
}) })
: t('Quick search placeholder') : t['Quick search placeholder']()
} }
/> />
<StyledShortcut>{isMac() ? '⌘ + K' : 'Ctrl + K'}</StyledShortcut> <StyledShortcut>{isMac() ? '⌘ + K' : 'Ctrl + K'}</StyledShortcut>

View File

@@ -1,5 +1,5 @@
import { IconButton, Tooltip, TreeView } from '@affine/component'; import { IconButton, Tooltip, TreeView } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { import {
ArrowRightSmallIcon, ArrowRightSmallIcon,
CollapseIcon, CollapseIcon,
@@ -35,7 +35,7 @@ export const NavigationPath = ({
}) => { }) => {
const metas = useBlockSuitePageMeta(blockSuiteWorkspace); const metas = useBlockSuitePageMeta(blockSuiteWorkspace);
const router = useRouter(); const router = useRouter();
const { t } = useTranslation(); const t = useAFFiNEI18N();
const [openExtend, setOpenExtend] = useState(false); const [openExtend, setOpenExtend] = useState(false);
const pageId = propsPageId ?? router.query.pageId; const pageId = propsPageId ?? router.query.pageId;
@@ -59,7 +59,7 @@ export const NavigationPath = ({
<> <>
<StyledNavigationPathContainer data-testid="navigation-path"> <StyledNavigationPathContainer data-testid="navigation-path">
{openExtend ? ( {openExtend ? (
<span>{t('Navigation Path')}</span> <span>{t['Navigation Path']()}</span>
) : ( ) : (
pathData.path.map((meta, index) => { pathData.path.map((meta, index) => {
const isLast = index === pathData.path.length - 1; const isLast = index === pathData.path.length - 1;
@@ -96,7 +96,9 @@ export const NavigationPath = ({
)} )}
<Tooltip <Tooltip
content={ content={
openExtend ? t('Back to Quick Search') : t('View Navigation Path') openExtend
? t['Back to Quick Search']()
: t['View Navigation Path']()
} }
placement="top" placement="top"
disablePortal={true} disablePortal={true}

View File

@@ -1,90 +1,91 @@
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
interface ShortcutTip { interface ShortcutTip {
[x: string]: string; [x: string]: string;
} }
export const useMacKeyboardShortcuts = (): ShortcutTip => { export const useMacKeyboardShortcuts = (): ShortcutTip => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
return { return {
[t('Undo')]: '⌘+Z', [t['Undo']()]: '⌘+Z',
[t('Redo')]: '⌘+⇧+Z', [t['Redo']()]: '⌘+⇧+Z',
[t('Bold')]: '⌘+B', [t['Bold']()]: '⌘+B',
[t('Italic')]: '⌘+I', [t['Italic']()]: '⌘+I',
[t('Underline')]: '⌘+U', [t['Underline']()]: '⌘+U',
[t('Strikethrough')]: '⌘+⇧+S', [t['Strikethrough']()]: '⌘+⇧+S',
[t('Inline code')]: ' ⌘+E', [t['Inline code']()]: ' ⌘+E',
[t('Code block')]: '⌘+⌥+C', [t['Code block']()]: '⌘+⌥+C',
[t('Link')]: '⌘+K', [t['Link']()]: '⌘+K',
[t('Quick search')]: '⌘+K', [t['Quick search']()]: '⌘+K',
[t('Body text')]: '⌘+⌥+0', [t['Body text']()]: '⌘+⌥+0',
[t('Heading', { number: '1' })]: '⌘+⌥+1', [t['Heading']({ number: '1' })]: '⌘+⌥+1',
[t('Heading', { number: '2' })]: '⌘+⌥+2', [t['Heading']({ number: '2' })]: '⌘+⌥+2',
[t('Heading', { number: '3' })]: '⌘+⌥+3', [t['Heading']({ number: '3' })]: '⌘+⌥+3',
[t('Heading', { number: '4' })]: '⌘+⌥+4', [t['Heading']({ number: '4' })]: '⌘+⌥+4',
[t('Heading', { number: '5' })]: '⌘+⌥+5', [t['Heading']({ number: '5' })]: '⌘+⌥+5',
[t('Heading', { number: '6' })]: '⌘+⌥+6', [t['Heading']({ number: '6' })]: '⌘+⌥+6',
[t('Increase indent')]: 'Tab', [t['Increase indent']()]: 'Tab',
[t('Reduce indent')]: '⇧+Tab', [t['Reduce indent']()]: '⇧+Tab',
}; };
}; };
export const useMacMarkdownShortcuts = (): ShortcutTip => { export const useMacMarkdownShortcuts = (): ShortcutTip => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
return { return {
[t('Bold')]: '**Text** ', [t['Bold']()]: '**Text** ',
[t('Italic')]: '*Text* ', [t['Italic']()]: '*Text* ',
[t('Underline')]: '~Text~ ', [t['Underline']()]: '~Text~ ',
[t('Strikethrough')]: '~~Text~~ ', [t['Strikethrough']()]: '~~Text~~ ',
[t('Divider')]: '***', [t['Divider']()]: '***',
[t('Inline code')]: '`Text` ', [t['Inline code']()]: '`Text` ',
[t('Code block')]: '``` Space', [t['Code block']()]: '``` Space',
[t('Heading', { number: '1' })]: '# Text', [t['Heading']({ number: '1' })]: '# Text',
[t('Heading', { number: '2' })]: '## Text', [t['Heading']({ number: '2' })]: '## Text',
[t('Heading', { number: '3' })]: '### Text', [t['Heading']({ number: '3' })]: '### Text',
[t('Heading', { number: '4' })]: '#### Text', [t['Heading']({ number: '4' })]: '#### Text',
[t('Heading', { number: '5' })]: '##### Text', [t['Heading']({ number: '5' })]: '##### Text',
[t('Heading', { number: '6' })]: '###### Text', [t['Heading']({ number: '6' })]: '###### Text',
}; };
}; };
export const useWindowsKeyboardShortcuts = (): ShortcutTip => { export const useWindowsKeyboardShortcuts = (): ShortcutTip => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
return { return {
[t('Undo')]: 'Ctrl+Z', [t['Undo']()]: 'Ctrl+Z',
[t('Redo')]: 'Ctrl+Y', [t['Redo']()]: 'Ctrl+Y',
[t('Bold')]: 'Ctrl+B', [t['Bold']()]: 'Ctrl+B',
[t('Italic')]: 'Ctrl+I', [t['Italic']()]: 'Ctrl+I',
[t('Underline')]: 'Ctrl+U', [t['Underline']()]: 'Ctrl+U',
[t('Strikethrough')]: 'Ctrl+Shift+S', [t['Strikethrough']()]: 'Ctrl+Shift+S',
[t('Inline code')]: ' Ctrl+E', [t['Inline code']()]: ' Ctrl+E',
[t('Code block')]: 'Ctrl+Alt+C', [t['Code block']()]: 'Ctrl+Alt+C',
[t('Link')]: 'Ctrl+K', [t['Link']()]: 'Ctrl+K',
[t('Quick search')]: 'Ctrl+K', [t['Quick search']()]: 'Ctrl+K',
[t('Body text')]: 'Ctrl+Shift+0', [t['Body text']()]: 'Ctrl+Shift+0',
[t('Heading', { number: '1' })]: 'Ctrl+Shift+1', [t['Heading']({ number: '1' })]: 'Ctrl+Shift+1',
[t('Heading', { number: '2' })]: 'Ctrl+Shift+2', [t['Heading']({ number: '2' })]: 'Ctrl+Shift+2',
[t('Heading', { number: '3' })]: 'Ctrl+Shift+3', [t['Heading']({ number: '3' })]: 'Ctrl+Shift+3',
[t('Heading', { number: '4' })]: 'Ctrl+Shift+4', [t['Heading']({ number: '4' })]: 'Ctrl+Shift+4',
[t('Heading', { number: '5' })]: 'Ctrl+Shift+5', [t['Heading']({ number: '5' })]: 'Ctrl+Shift+5',
[t('Heading', { number: '6' })]: 'Ctrl+Shift+6', [t['Heading']({ number: '6' })]: 'Ctrl+Shift+6',
[t('Increase indent')]: 'Tab', [t['Increase indent']()]: 'Tab',
[t('Reduce indent')]: 'Shift+Tab', [t['Reduce indent']()]: 'Shift+Tab',
}; };
}; };
export const useWinMarkdownShortcuts = (): ShortcutTip => { export const useWinMarkdownShortcuts = (): ShortcutTip => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
return { return {
[t('Bold')]: '**Text** ', [t['Bold']()]: '**Text** ',
[t('Italic')]: '*Text* ', [t['Italic']()]: '*Text* ',
[t('Underline')]: '~Text~ ', [t['Underline']()]: '~Text~ ',
[t('Strikethrough')]: '~~Text~~ ', [t['Strikethrough']()]: '~~Text~~ ',
[t('Divider')]: '***', [t['Divider']()]: '***',
[t('Inline code')]: '`Text` ', [t['Inline code']()]: '`Text` ',
[t('Code block')]: '``` Text', [t['Code block']()]: '``` Text',
[t('Heading', { number: '1' })]: '# Text', [t['Heading']({ number: '1' })]: '# Text',
[t('Heading', { number: '2' })]: '## Text', [t['Heading']({ number: '2' })]: '## Text',
[t('Heading', { number: '3' })]: '### Text', [t['Heading']({ number: '3' })]: '### Text',
[t('Heading', { number: '4' })]: '#### Text', [t['Heading']({ number: '4' })]: '#### Text',
[t('Heading', { number: '5' })]: '##### Text', [t['Heading']({ number: '5' })]: '##### Text',
[t('Heading', { number: '6' })]: '###### Text', [t['Heading']({ number: '6' })]: '###### Text',
}; };
}; };

View File

@@ -4,7 +4,7 @@ import {
MuiSlide, MuiSlide,
} from '@affine/component'; } from '@affine/component';
import { getEnvironment } from '@affine/env'; import { getEnvironment } from '@affine/env';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { import {
@@ -32,7 +32,7 @@ const checkIsMac = () => {
}; };
export const ShortcutsModal = ({ open, onClose }: ModalProps) => { export const ShortcutsModal = ({ open, onClose }: ModalProps) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
const macMarkdownShortcuts = useMacMarkdownShortcuts(); const macMarkdownShortcuts = useMacMarkdownShortcuts();
const winMarkdownShortcuts = useWinMarkdownShortcuts(); const winMarkdownShortcuts = useWinMarkdownShortcuts();
const macKeyboardShortcuts = useMacKeyboardShortcuts(); const macKeyboardShortcuts = useMacKeyboardShortcuts();
@@ -59,7 +59,7 @@ export const ShortcutsModal = ({ open, onClose }: ModalProps) => {
<StyledModalHeader> <StyledModalHeader>
<StyledTitle> <StyledTitle>
<KeyboardIcon /> <KeyboardIcon />
{t('Shortcuts')} {t['Shortcuts']()}
</StyledTitle> </StyledTitle>
<ModalCloseButton <ModalCloseButton
@@ -73,7 +73,7 @@ export const ShortcutsModal = ({ open, onClose }: ModalProps) => {
/> />
</StyledModalHeader> </StyledModalHeader>
<StyledSubTitle style={{ marginTop: 0 }}> <StyledSubTitle style={{ marginTop: 0 }}>
{t('Keyboard Shortcuts')} {t['Keyboard Shortcuts']()}
</StyledSubTitle> </StyledSubTitle>
{Object.entries(keyboardShortcuts).map(([title, shortcuts]) => { {Object.entries(keyboardShortcuts).map(([title, shortcuts]) => {
return ( return (
@@ -83,7 +83,7 @@ export const ShortcutsModal = ({ open, onClose }: ModalProps) => {
</StyledListItem> </StyledListItem>
); );
})} })}
<StyledSubTitle>{t('Markdown Syntax')}</StyledSubTitle> <StyledSubTitle>{t['Markdown Syntax']()}</StyledSubTitle>
{Object.entries(markdownShortcuts).map(([title, shortcuts]) => { {Object.entries(markdownShortcuts).map(([title, shortcuts]) => {
return ( return (
<StyledListItem key={title}> <StyledListItem key={title}>

View File

@@ -5,7 +5,7 @@ import {
Tooltip, Tooltip,
} from '@affine/component'; } from '@affine/component';
import { WorkspaceList } from '@affine/component/workspace-list'; import { WorkspaceList } from '@affine/component/workspace-list';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { AccessTokenMessage } from '@affine/workspace/affine/login'; import type { AccessTokenMessage } from '@affine/workspace/affine/login';
import type { AffineWorkspace, LocalWorkspace } from '@affine/workspace/type'; import type { AffineWorkspace, LocalWorkspace } from '@affine/workspace/type';
import { WorkspaceFlavour } from '@affine/workspace/type'; import { WorkspaceFlavour } from '@affine/workspace/type';
@@ -57,7 +57,7 @@ export const WorkspaceListModal = ({
currentWorkspaceId, currentWorkspaceId,
onMoveWorkspace, onMoveWorkspace,
}: WorkspaceModalProps) => { }: WorkspaceModalProps) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
return ( return (
<Modal open={open} onClose={onClose}> <Modal open={open} onClose={onClose}>
@@ -71,9 +71,9 @@ export const WorkspaceListModal = ({
> >
<StyledModalHeader> <StyledModalHeader>
<StyledModalHeaderLeft> <StyledModalHeaderLeft>
<StyledModalTitle>{t('My Workspaces')}</StyledModalTitle> <StyledModalTitle>{t['My Workspaces']()}</StyledModalTitle>
<Tooltip <Tooltip
content={t('Workspace description')} content={t['Workspace description']()}
placement="top-start" placement="top-start"
disablePortal={true} disablePortal={true}
> >
@@ -124,8 +124,8 @@ export const WorkspaceListModal = ({
</StyleWorkspaceAdd> </StyleWorkspaceAdd>
<StyleWorkspaceInfo> <StyleWorkspaceInfo>
<StyleWorkspaceTitle>{t('New Workspace')}</StyleWorkspaceTitle> <StyleWorkspaceTitle>{t['New Workspace']()}</StyleWorkspaceTitle>
<p>{t('Create Or Import')}</p> <p>{t['Create Or Import']()}</p>
</StyleWorkspaceInfo> </StyleWorkspaceInfo>
</StyledCreateWorkspaceCard> </StyledCreateWorkspaceCard>
</StyledModalContent> </StyledModalContent>

View File

@@ -1,12 +1,12 @@
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { StyledCollapseItem } from '../shared-styles'; import { StyledCollapseItem } from '../shared-styles';
export const EmptyItem = () => { export const EmptyItem = () => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
return ( return (
<StyledCollapseItem disable={true} textWrap={true}> <StyledCollapseItem disable={true} textWrap={true}>
{t('Favorite pages for easy access')} {t['Favorite pages for easy access']()}
</StyledCollapseItem> </StyledCollapseItem>
); );
}; };

View File

@@ -1,4 +1,4 @@
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { ArrowDownSmallIcon, FavoriteIcon } from '@blocksuite/icons'; import { ArrowDownSmallIcon, FavoriteIcon } from '@blocksuite/icons';
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
@@ -26,7 +26,7 @@ export const Favorite = ({
const [showSubFavorite, setOpenSubFavorite] = useState(true); const [showSubFavorite, setOpenSubFavorite] = useState(true);
const { t } = useTranslation(); const t = useAFFiNEI18N();
return ( return (
<> <>
@@ -50,7 +50,7 @@ export const Favorite = ({
}} }}
> >
<FavoriteIcon /> <FavoriteIcon />
{t('Favorites')} {t['Favorites']()}
</StyledLink> </StyledLink>
</StyledListItem> </StyledListItem>
<FavoriteList <FavoriteList

View File

@@ -4,7 +4,7 @@ import {
ResizeIndicator, ResizeIndicator,
} from '@affine/component/app-sidebar'; } from '@affine/component/app-sidebar';
import { config } from '@affine/env'; import { config } from '@affine/env';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { WorkspaceFlavour } from '@affine/workspace/type'; import { WorkspaceFlavour } from '@affine/workspace/type';
import { import {
DeleteTemporarilyIcon, DeleteTemporarilyIcon,
@@ -67,7 +67,7 @@ export const RootAppSidebar = ({
}: RootAppSidebarProps): ReactElement => { }: RootAppSidebarProps): ReactElement => {
const currentWorkspaceId = currentWorkspace?.id || null; const currentWorkspaceId = currentWorkspace?.id || null;
const blockSuiteWorkspace = currentWorkspace?.blockSuiteWorkspace; const blockSuiteWorkspace = currentWorkspace?.blockSuiteWorkspace;
const { t } = useTranslation(); const t = useAFFiNEI18N();
const [isScrollAtTop, setIsScrollAtTop] = useState(true); const [isScrollAtTop, setIsScrollAtTop] = useState(true);
const onClickNewPage = useCallback(async () => { const onClickNewPage = useCallback(async () => {
const page = await createPage(); const page = await createPage();
@@ -89,7 +89,7 @@ export const RootAppSidebar = ({
data-testid="new-page-button" data-testid="new-page-button"
onClick={onClickNewPage} onClick={onClickNewPage}
> >
<PlusIcon /> {t('New Page')} <PlusIcon /> {t['New Page']()}
</StyledNewPageButton> </StyledNewPageButton>
} }
> >
@@ -106,7 +106,7 @@ export const RootAppSidebar = ({
}, [onOpenQuickSearchModal])} }, [onOpenQuickSearchModal])}
> >
<SearchIcon /> <SearchIcon />
{t('Quick search')} {t['Quick search']()}
</StyledListItem> </StyledListItem>
<StyledListItem <StyledListItem
active={ active={
@@ -125,7 +125,7 @@ export const RootAppSidebar = ({
}} }}
> >
<SettingsIcon /> <SettingsIcon />
<div>{t('Workspace Settings')}</div> <div>{t['Workspace Settings']()}</div>
</StyledLink> </StyledLink>
</StyledListItem> </StyledListItem>
<StyledListItem <StyledListItem
@@ -140,7 +140,7 @@ export const RootAppSidebar = ({
}} }}
> >
<FolderIcon /> <FolderIcon />
<span data-testid="all-pages">{t('All pages')}</span> <span data-testid="all-pages">{t['All pages']()}</span>
</StyledLink> </StyledLink>
</StyledListItem> </StyledListItem>
<StyledScrollWrapper <StyledScrollWrapper
@@ -190,7 +190,7 @@ export const RootAppSidebar = ({
}} }}
> >
<ShareIcon /> <ShareIcon />
<span data-testid="shared-pages">{t('Shared Pages')}</span> <span data-testid="shared-pages">{t['Shared Pages']()}</span>
</StyledLink> </StyledLink>
</StyledListItem> </StyledListItem>
))} ))}
@@ -205,7 +205,7 @@ export const RootAppSidebar = ({
pathname: currentWorkspaceId && paths.trash(currentWorkspaceId), pathname: currentWorkspaceId && paths.trash(currentWorkspaceId),
}} }}
> >
<DeleteTemporarilyIcon /> {t('Trash')} <DeleteTemporarilyIcon /> {t['Trash']()}
</StyledLink> </StyledLink>
</StyledListItem> </StyledListItem>
</StyledSliderBarInnerWrapper> </StyledSliderBarInnerWrapper>

View File

@@ -1,7 +1,8 @@
import { DebugLogger } from '@affine/debug'; import { DebugLogger } from '@affine/debug';
import { DEFAULT_HELLO_WORLD_PAGE_ID } from '@affine/env'; import { DEFAULT_HELLO_WORLD_PAGE_ID } from '@affine/env';
import { initPage } from '@affine/env/blocksuite'; import { initPage } from '@affine/env/blocksuite';
import { setUpLanguage, useTranslation } from '@affine/i18n'; import { setUpLanguage, useI18N } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { createAffineGlobalChannel } from '@affine/workspace/affine/sync'; import { createAffineGlobalChannel } from '@affine/workspace/affine/sync';
import { import {
rootCurrentPageIdAtom, rootCurrentPageIdAtom,
@@ -147,22 +148,23 @@ export const CurrentWorkspaceContext = ({
useRouterWithWorkspaceIdDefense(router); useRouterWithWorkspaceIdDefense(router);
const metadata = useAtomValue(rootWorkspacesMetadataAtom); const metadata = useAtomValue(rootWorkspacesMetadataAtom);
const exist = metadata.find(m => m.id === workspaceId); const exist = metadata.find(m => m.id === workspaceId);
const { t } = useTranslation(); const t = useAFFiNEI18N();
if (!router.isReady) { if (!router.isReady) {
return <PageLoading text={t('Router is Loading')} />; return <PageLoading text={t['Router is Loading']()} />;
} }
if (!workspaceId) { if (!workspaceId) {
return <PageLoading text={t('Finding Workspace ID')} />; return <PageLoading text={t['Finding Workspace ID']()} />;
} }
if (!exist) { if (!exist) {
return <PageLoading text={t('Workspace Not Found')} />; return <PageLoading text={t['Workspace Not Found']()} />;
} }
return <>{children}</>; return <>{children}</>;
}; };
export const WorkspaceLayout: FC<PropsWithChildren> = export const WorkspaceLayout: FC<PropsWithChildren> =
function WorkspacesSuspense({ children }) { function WorkspacesSuspense({ children }) {
const { i18n, t } = useTranslation(); const i18n = useI18N();
const t = useAFFiNEI18N();
useEffect(() => { useEffect(() => {
document.documentElement.lang = i18n.language; document.documentElement.lang = i18n.language;
// todo(himself65): this is a hack, we should use a better way to set the language // todo(himself65): this is a hack, we should use a better way to set the language
@@ -239,7 +241,7 @@ export const WorkspaceLayout: FC<PropsWithChildren> =
</AllWorkspaceContext> </AllWorkspaceContext>
<CurrentWorkspaceContext> <CurrentWorkspaceContext>
<Suspense <Suspense
fallback={<PageLoading text={t('Finding Current Workspace')} />} fallback={<PageLoading text={t['Finding Current Workspace']()} />}
> >
<Provider> <Provider>
<WorkspaceLayoutInner>{children}</WorkspaceLayoutInner> <WorkspaceLayoutInner>{children}</WorkspaceLayoutInner>
@@ -256,7 +258,7 @@ export const WorkspaceLayoutInner: FC<PropsWithChildren> = ({ children }) => {
const currentPageId = useAtomValue(rootCurrentPageIdAtom); const currentPageId = useAtomValue(rootCurrentPageIdAtom);
const router = useRouter(); const router = useRouter();
const { jumpToPage } = useRouterHelper(router); const { jumpToPage } = useRouterHelper(router);
const { t } = useTranslation(); const t = useAFFiNEI18N();
useEffect(() => { useEffect(() => {
logger.info('currentWorkspace: ', currentWorkspace); logger.info('currentWorkspace: ', currentWorkspace);
@@ -371,7 +373,7 @@ export const WorkspaceLayoutInner: FC<PropsWithChildren> = ({ children }) => {
/> />
<MainContainerWrapper> <MainContainerWrapper>
<MainContainer className="main-container"> <MainContainer className="main-container">
<Suspense fallback={<PageLoading text={t('Page is Loading')} />}> <Suspense fallback={<PageLoading text={t['Page is Loading']()} />}>
{children} {children}
</Suspense> </Suspense>
<StyledToolWrapper> <StyledToolWrapper>

View File

@@ -1,5 +1,5 @@
import { Button, displayFlex, styled } from '@affine/component'; import { Button, displayFlex, styled } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import Head from 'next/head'; import Head from 'next/head';
import Image from 'next/legacy/image'; import Image from 'next/legacy/image';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
@@ -24,20 +24,20 @@ export const StyledContainer = styled('div')(() => {
}); });
export const NotfoundPage = () => { export const NotfoundPage = () => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
const router = useRouter(); const router = useRouter();
return ( return (
<StyledContainer data-testid="notFound"> <StyledContainer data-testid="notFound">
<Image alt="404" src="/imgs/invite-error.svg" width={360} height={270} /> <Image alt="404" src="/imgs/invite-error.svg" width={360} height={270} />
<p>{t('404 - Page Not Found')}</p> <p>{t['404 - Page Not Found']()}</p>
<Button <Button
shape="round" shape="round"
onClick={() => { onClick={() => {
router.push('/'); router.push('/');
}} }}
> >
{t('Back Home')} {t['Back Home']()}
</Button> </Button>
</StyledContainer> </StyledContainer>
); );

View File

@@ -1,5 +1,5 @@
import { DebugLogger } from '@affine/debug'; import { DebugLogger } from '@affine/debug';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { Suspense, useEffect } from 'react'; import { Suspense, useEffect } from 'react';
@@ -63,9 +63,9 @@ const IndexPageInner = () => {
}; };
const IndexPage: NextPage = () => { const IndexPage: NextPage = () => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
return ( return (
<Suspense fallback={<PageLoading text={t('Loading All Workspaces')} />}> <Suspense fallback={<PageLoading text={t['Loading All Workspaces']()} />}>
<IndexPageInner /> <IndexPageInner />
</Suspense> </Suspense>
); );

View File

@@ -1,6 +1,6 @@
import { Breadcrumbs, displayFlex, styled } from '@affine/component'; import { Breadcrumbs, displayFlex, styled } from '@affine/component';
import { initPage } from '@affine/env/blocksuite'; import { initPage } from '@affine/env/blocksuite';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { PageIcon } from '@blocksuite/icons'; import { PageIcon } from '@blocksuite/icons';
import { assertExists } from '@blocksuite/store'; import { assertExists } from '@blocksuite/store';
import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-block-suite-workspace-avatar-url'; import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-block-suite-workspace-avatar-url';
@@ -72,7 +72,7 @@ const PublicWorkspaceDetailPageInner = (): ReactElement => {
[blockSuiteWorkspace.id, openPage] [blockSuiteWorkspace.id, openPage]
), ),
}); });
const { t } = useTranslation(); const t = useAFFiNEI18N();
const [name] = useBlockSuiteWorkspaceName(blockSuiteWorkspace); const [name] = useBlockSuiteWorkspaceName(blockSuiteWorkspace);
const [avatar] = useBlockSuiteWorkspaceAvatarUrl(blockSuiteWorkspace); const [avatar] = useBlockSuiteWorkspaceAvatarUrl(blockSuiteWorkspace);
const pageTitle = blockSuiteWorkspace.meta.getPageMeta(pageId)?.title; const pageTitle = blockSuiteWorkspace.meta.getPageMeta(pageId)?.title;
@@ -101,7 +101,7 @@ const PublicWorkspaceDetailPageInner = (): ReactElement => {
href={`/public-workspace/${blockSuiteWorkspace.id}/${pageId}`} href={`/public-workspace/${blockSuiteWorkspace.id}/${pageId}`}
> >
<PageIcon fontSize={24} /> <PageIcon fontSize={24} />
<span>{pageTitle ? pageTitle : t('Untitled')}</span> <span>{pageTitle ? pageTitle : t['Untitled']()}</span>
</StyledBreadcrumbs> </StyledBreadcrumbs>
</Breadcrumbs> </Breadcrumbs>
</NavContainer> </NavContainer>

View File

@@ -1,6 +1,6 @@
import type { BlockSuiteFeatureFlags } from '@affine/env'; import type { BlockSuiteFeatureFlags } from '@affine/env';
import { config } from '@affine/env'; import { config } from '@affine/env';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { rootCurrentPageIdAtom } from '@affine/workspace/atom'; import { rootCurrentPageIdAtom } from '@affine/workspace/atom';
import { WorkspaceFlavour } from '@affine/workspace/type'; import { WorkspaceFlavour } from '@affine/workspace/type';
import { assertExists } from '@blocksuite/store'; import { assertExists } from '@blocksuite/store';
@@ -41,7 +41,7 @@ const WorkspaceDetail: React.FC = () => {
const { openPage } = useRouterHelper(router); const { openPage } = useRouterHelper(router);
const currentPageId = useAtomValue(rootCurrentPageIdAtom); const currentPageId = useAtomValue(rootCurrentPageIdAtom);
const [currentWorkspace] = useCurrentWorkspace(); const [currentWorkspace] = useCurrentWorkspace();
const { t } = useTranslation(); const t = useAFFiNEI18N();
assertExists(currentWorkspace); assertExists(currentWorkspace);
const blockSuiteWorkspace = currentWorkspace.blockSuiteWorkspace; const blockSuiteWorkspace = currentWorkspace.blockSuiteWorkspace;
const { setPageMeta, getPageMeta } = usePageMetaHelper(blockSuiteWorkspace); const { setPageMeta, getPageMeta } = usePageMetaHelper(blockSuiteWorkspace);
@@ -86,7 +86,7 @@ const WorkspaceDetail: React.FC = () => {
} }
}, [currentWorkspace]); }, [currentWorkspace]);
if (!currentPageId) { if (!currentPageId) {
return <PageLoading text={t('Loading Page')} />; return <PageLoading text={t['Loading Page']()} />;
} }
if (currentWorkspace.flavour === WorkspaceFlavour.AFFINE) { if (currentWorkspace.flavour === WorkspaceFlavour.AFFINE) {
const PageDetail = WorkspacePlugins[currentWorkspace.flavour].UI.PageDetail; const PageDetail = WorkspacePlugins[currentWorkspace.flavour].UI.PageDetail;
@@ -112,16 +112,16 @@ const WorkspaceDetailPage: NextPageWithLayout = () => {
const router = useRouter(); const router = useRouter();
const currentWorkspace = useAtomValue(rootCurrentWorkspaceAtom); const currentWorkspace = useAtomValue(rootCurrentWorkspaceAtom);
const currentPageId = useAtomValue(rootCurrentPageIdAtom); const currentPageId = useAtomValue(rootCurrentPageIdAtom);
const { t } = useTranslation(); const t = useAFFiNEI18N();
useRouterAndWorkspaceWithPageIdDefense(router); useRouterAndWorkspaceWithPageIdDefense(router);
const page = useBlockSuiteWorkspacePage( const page = useBlockSuiteWorkspacePage(
currentWorkspace.blockSuiteWorkspace, currentWorkspace.blockSuiteWorkspace,
currentPageId currentPageId
); );
if (!router.isReady) { if (!router.isReady) {
return <PageLoading text={t('Router is Loading')} />; return <PageLoading text={t['Router is Loading']()} />;
} else if (!currentPageId || !page) { } else if (!currentPageId || !page) {
return <PageLoading text={t('Page is Loading')} />; return <PageLoading text={t['Page is Loading']()} />;
} }
return <WorkspaceDetail />; return <WorkspaceDetail />;
}; };

View File

@@ -1,4 +1,4 @@
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { WorkspaceFlavour } from '@affine/workspace/type'; import { WorkspaceFlavour } from '@affine/workspace/type';
import { FolderIcon } from '@blocksuite/icons'; import { FolderIcon } from '@blocksuite/icons';
import { assertExists } from '@blocksuite/store'; import { assertExists } from '@blocksuite/store';
@@ -23,7 +23,7 @@ const AllPage: NextPageWithLayout = () => {
const router = useRouter(); const router = useRouter();
const { jumpToPage } = useRouterHelper(router); const { jumpToPage } = useRouterHelper(router);
const [currentWorkspace] = useCurrentWorkspace(); const [currentWorkspace] = useCurrentWorkspace();
const { t } = useTranslation(); const t = useAFFiNEI18N();
useSyncRouterWithCurrentWorkspaceId(router); useSyncRouterWithCurrentWorkspaceId(router);
const onClickPage = useCallback( const onClickPage = useCallback(
(pageId: string, newTab?: boolean) => { (pageId: string, newTab?: boolean) => {
@@ -47,7 +47,7 @@ const AllPage: NextPageWithLayout = () => {
return ( return (
<> <>
<Head> <Head>
<title>{t('All Pages')} - AFFiNE</title> <title>{t['All pages']()} - AFFiNE</title>
</Head> </Head>
<WorkspaceTitle <WorkspaceTitle
workspace={currentWorkspace} workspace={currentWorkspace}
@@ -56,7 +56,7 @@ const AllPage: NextPageWithLayout = () => {
isPublic={false} isPublic={false}
icon={<FolderIcon />} icon={<FolderIcon />}
> >
{t('All pages')} {t['All pages']()}
</WorkspaceTitle> </WorkspaceTitle>
<PageList <PageList
onOpenPage={onClickPage} onOpenPage={onClickPage}
@@ -69,7 +69,7 @@ const AllPage: NextPageWithLayout = () => {
return ( return (
<> <>
<Head> <Head>
<title>{t('All Pages')} - AFFiNE</title> <title>{t['All pages']()} - AFFiNE</title>
</Head> </Head>
<WorkspaceTitle <WorkspaceTitle
workspace={currentWorkspace} workspace={currentWorkspace}
@@ -78,7 +78,7 @@ const AllPage: NextPageWithLayout = () => {
isPublic={false} isPublic={false}
icon={<FolderIcon />} icon={<FolderIcon />}
> >
{t('All pages')} {t['All pages']()}
</WorkspaceTitle> </WorkspaceTitle>
<PageList <PageList
onOpenPage={onClickPage} onOpenPage={onClickPage}

View File

@@ -1,4 +1,4 @@
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { FavoriteIcon } from '@blocksuite/icons'; import { FavoriteIcon } from '@blocksuite/icons';
import { assertExists } from '@blocksuite/store'; import { assertExists } from '@blocksuite/store';
import Head from 'next/head'; import Head from 'next/head';
@@ -18,7 +18,7 @@ const FavouritePage: NextPageWithLayout = () => {
const router = useRouter(); const router = useRouter();
const { jumpToPage } = useRouterHelper(router); const { jumpToPage } = useRouterHelper(router);
const [currentWorkspace] = useCurrentWorkspace(); const [currentWorkspace] = useCurrentWorkspace();
const { t } = useTranslation(); const t = useAFFiNEI18N();
useSyncRouterWithCurrentWorkspaceId(router); useSyncRouterWithCurrentWorkspaceId(router);
const onClickPage = useCallback( const onClickPage = useCallback(
(pageId: string, newTab?: boolean) => { (pageId: string, newTab?: boolean) => {
@@ -39,7 +39,7 @@ const FavouritePage: NextPageWithLayout = () => {
return ( return (
<> <>
<Head> <Head>
<title>{t('Favorites')} - AFFiNE</title> <title>{t['Favorites']()} - AFFiNE</title>
</Head> </Head>
<WorkspaceTitle <WorkspaceTitle
workspace={currentWorkspace} workspace={currentWorkspace}
@@ -48,7 +48,7 @@ const FavouritePage: NextPageWithLayout = () => {
isPublic={false} isPublic={false}
icon={<FavoriteIcon />} icon={<FavoriteIcon />}
> >
{t('Favorites')} {t['Favorites']()}
</WorkspaceTitle> </WorkspaceTitle>
<PageList <PageList
blockSuiteWorkspace={blockSuiteWorkspace} blockSuiteWorkspace={blockSuiteWorkspace}

View File

@@ -1,4 +1,4 @@
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { atomWithSyncStorage } from '@affine/jotai'; import { atomWithSyncStorage } from '@affine/jotai';
import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom'; import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
import type { SettingPanel } from '@affine/workspace/type'; import type { SettingPanel } from '@affine/workspace/type';
@@ -35,7 +35,7 @@ const SettingPage: NextPageWithLayout = () => {
const router = useRouter(); const router = useRouter();
const workspaceIds = useAtomValue(rootWorkspacesMetadataAtom); const workspaceIds = useAtomValue(rootWorkspacesMetadataAtom);
const [currentWorkspace] = useCurrentWorkspace(); const [currentWorkspace] = useCurrentWorkspace();
const { t } = useTranslation(); const t = useAFFiNEI18N();
useSyncRouterWithCurrentWorkspaceId(router); useSyncRouterWithCurrentWorkspaceId(router);
const [currentTab, setCurrentTab] = useAtom(settingPanelAtom); const [currentTab, setCurrentTab] = useAtom(settingPanelAtom);
useEffect(() => {}); useEffect(() => {});
@@ -101,7 +101,7 @@ const SettingPage: NextPageWithLayout = () => {
assertExists(currentWorkspace); assertExists(currentWorkspace);
const workspaceId = currentWorkspace.id; const workspaceId = currentWorkspace.id;
if (workspaceIds.length === 1 && workspaceId === workspaceIds[0].id) { if (workspaceIds.length === 1 && workspaceId === workspaceIds[0].id) {
toast(t('You cannot delete the last workspace')); toast(t['You cannot delete the last workspace']());
throw new Error('You cannot delete the last workspace'); throw new Error('You cannot delete the last workspace');
} }
return helper.deleteWorkspace(workspaceId); return helper.deleteWorkspace(workspaceId);
@@ -119,7 +119,7 @@ const SettingPage: NextPageWithLayout = () => {
return ( return (
<> <>
<Head> <Head>
<title>{t('Settings')} - AFFiNE</title> <title>{t['Settings']()} - AFFiNE</title>
</Head> </Head>
<WorkspaceTitle <WorkspaceTitle
workspace={currentWorkspace} workspace={currentWorkspace}
@@ -128,7 +128,7 @@ const SettingPage: NextPageWithLayout = () => {
isPublic={false} isPublic={false}
icon={<SettingsIcon />} icon={<SettingsIcon />}
> >
{t('Workspace Settings')} {t['Workspace Settings']()}
</WorkspaceTitle> </WorkspaceTitle>
<Setting <Setting
onTransformWorkspace={onTransformWorkspace} onTransformWorkspace={onTransformWorkspace}
@@ -145,7 +145,7 @@ const SettingPage: NextPageWithLayout = () => {
return ( return (
<> <>
<Head> <Head>
<title>{t('Settings')} - AFFiNE</title> <title>{t['Settings']()} - AFFiNE</title>
</Head> </Head>
<WorkspaceTitle <WorkspaceTitle
workspace={currentWorkspace} workspace={currentWorkspace}
@@ -154,7 +154,7 @@ const SettingPage: NextPageWithLayout = () => {
isPublic={false} isPublic={false}
icon={<SettingsIcon />} icon={<SettingsIcon />}
> >
{t('Workspace Settings')} {t['Workspace Settings']()}
</WorkspaceTitle> </WorkspaceTitle>
<Setting <Setting
onTransformWorkspace={onTransformWorkspace} onTransformWorkspace={onTransformWorkspace}

View File

@@ -1,4 +1,4 @@
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { ShareIcon } from '@blocksuite/icons'; import { ShareIcon } from '@blocksuite/icons';
import { assertExists } from '@blocksuite/store'; import { assertExists } from '@blocksuite/store';
import Head from 'next/head'; import Head from 'next/head';
@@ -18,7 +18,7 @@ const SharedPages: NextPageWithLayout = () => {
const router = useRouter(); const router = useRouter();
const { jumpToPage } = useRouterHelper(router); const { jumpToPage } = useRouterHelper(router);
const [currentWorkspace] = useCurrentWorkspace(); const [currentWorkspace] = useCurrentWorkspace();
const { t } = useTranslation(); const t = useAFFiNEI18N();
useSyncRouterWithCurrentWorkspaceId(router); useSyncRouterWithCurrentWorkspaceId(router);
const onClickPage = useCallback( const onClickPage = useCallback(
(pageId: string, newTab?: boolean) => { (pageId: string, newTab?: boolean) => {
@@ -39,7 +39,7 @@ const SharedPages: NextPageWithLayout = () => {
return ( return (
<> <>
<Head> <Head>
<title>{t('Shared Pages')} - AFFiNE</title> <title>{t['Shared Pages']()} - AFFiNE</title>
</Head> </Head>
<WorkspaceTitle <WorkspaceTitle
workspace={currentWorkspace} workspace={currentWorkspace}
@@ -48,7 +48,7 @@ const SharedPages: NextPageWithLayout = () => {
isPublic={false} isPublic={false}
icon={<ShareIcon />} icon={<ShareIcon />}
> >
{t('Shared Pages')} {t['Shared Pages']()}
</WorkspaceTitle> </WorkspaceTitle>
<PageList <PageList
blockSuiteWorkspace={blockSuiteWorkspace} blockSuiteWorkspace={blockSuiteWorkspace}

View File

@@ -1,4 +1,4 @@
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { DeleteTemporarilyIcon } from '@blocksuite/icons'; import { DeleteTemporarilyIcon } from '@blocksuite/icons';
import { assertExists } from '@blocksuite/store'; import { assertExists } from '@blocksuite/store';
import Head from 'next/head'; import Head from 'next/head';
@@ -18,7 +18,7 @@ const TrashPage: NextPageWithLayout = () => {
const router = useRouter(); const router = useRouter();
const { jumpToPage } = useRouterHelper(router); const { jumpToPage } = useRouterHelper(router);
const [currentWorkspace] = useCurrentWorkspace(); const [currentWorkspace] = useCurrentWorkspace();
const { t } = useTranslation(); const t = useAFFiNEI18N();
useSyncRouterWithCurrentWorkspaceId(router); useSyncRouterWithCurrentWorkspaceId(router);
const onClickPage = useCallback( const onClickPage = useCallback(
(pageId: string, newTab?: boolean) => { (pageId: string, newTab?: boolean) => {
@@ -42,7 +42,7 @@ const TrashPage: NextPageWithLayout = () => {
return ( return (
<> <>
<Head> <Head>
<title>{t('Trash')} - AFFiNE</title> <title>{t['Trash']()} - AFFiNE</title>
</Head> </Head>
<WorkspaceTitle <WorkspaceTitle
workspace={currentWorkspace} workspace={currentWorkspace}
@@ -51,7 +51,7 @@ const TrashPage: NextPageWithLayout = () => {
isPublic={false} isPublic={false}
icon={<DeleteTemporarilyIcon />} icon={<DeleteTemporarilyIcon />}
> >
{t('Trash')} {t['Trash']()}
</WorkspaceTitle> </WorkspaceTitle>
<PageList <PageList
blockSuiteWorkspace={blockSuiteWorkspace} blockSuiteWorkspace={blockSuiteWorkspace}

View File

@@ -30,7 +30,7 @@
"test:unit": "vitest --run", "test:unit": "vitest --run",
"test:unit:ui": "vitest --ui", "test:unit:ui": "vitest --ui",
"test:unit:coverage": "vitest run --coverage", "test:unit:coverage": "vitest run --coverage",
"postinstall": "husky install", "postinstall": "i18n-codegen gen && husky install",
"notify": "node scripts/notify.mjs" "notify": "node scripts/notify.mjs"
}, },
"lint-staged": { "lint-staged": {
@@ -44,6 +44,7 @@
"@commitlint/config-conventional": "^17.6.1", "@commitlint/config-conventional": "^17.6.1",
"@faker-js/faker": "^7.6.0", "@faker-js/faker": "^7.6.0",
"@istanbuljs/schema": "^0.1.3", "@istanbuljs/schema": "^0.1.3",
"@magic-works/i18n-codegen": "^0.5.0",
"@perfsee/sdk": "^1.6.0", "@perfsee/sdk": "^1.6.0",
"@playwright/test": "^1.33.0", "@playwright/test": "^1.33.0",
"@testing-library/react": "^14.0.0", "@testing-library/react": "^14.0.0",

View File

@@ -5,6 +5,7 @@ import * as path from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import * as p from '@clack/prompts'; import * as p from '@clack/prompts';
import { runCli } from '@magic-works/i18n-codegen';
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);
@@ -80,6 +81,14 @@ if (dev.server === 'local') {
console.log('You might need setup OctoBase dev server first.'); console.log('You might need setup OctoBase dev server first.');
} }
runCli(
{
watch: true,
cwd: root,
},
console.error
);
spawn('yarn', ['dev'], { spawn('yarn', ['dev'], {
env, env,
cwd, cwd,

View File

@@ -1,9 +1,23 @@
import { runCli } from '@magic-works/i18n-codegen';
import type { StorybookConfig } from '@storybook/react-vite'; import type { StorybookConfig } from '@storybook/react-vite';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { mergeConfig } from 'vite'; import { mergeConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths'; import tsconfigPaths from 'vite-tsconfig-paths';
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin'; import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';
runCli(
{
config: fileURLToPath(
new URL('../../../.i18n-codegen.json', import.meta.url)
),
watch: false,
},
error => {
console.error(error);
process.exit(1);
}
);
export default { export default {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
staticDirs: ['../../../apps/web/public'], staticDirs: ['../../../apps/web/public'],

View File

@@ -1,4 +1,4 @@
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CloseIcon, NewIcon } from '@blocksuite/icons'; import { CloseIcon, NewIcon } from '@blocksuite/icons';
import clsx from 'clsx'; import clsx from 'clsx';
import { useState } from 'react'; import { useState } from 'react';
@@ -20,7 +20,7 @@ type ChangeLogProps = {
export const ChangeLog = (props: ChangeLogProps) => { export const ChangeLog = (props: ChangeLogProps) => {
const { onCloseWhatsNew } = props; const { onCloseWhatsNew } = props;
const [isClose, setIsClose] = useState(false); const [isClose, setIsClose] = useState(false);
const { t } = useTranslation(); const t = useAFFiNEI18N();
const handleClose = () => { const handleClose = () => {
setIsClose(true); setIsClose(true);
onCloseWhatsNew(); onCloseWhatsNew();
@@ -47,7 +47,7 @@ export const ChangeLog = (props: ChangeLogProps) => {
}} }}
> >
<NewIcon className={iconStyle} /> <NewIcon className={iconStyle} />
{t("Discover what's new!")} {t["Discover what's new!"]()}
</div> </div>
<IconButton <IconButton
className={iconButtonStyle} className={iconButtonStyle}

View File

@@ -4,7 +4,7 @@ import {
ModalCloseButton, ModalCloseButton,
ModalWrapper, ModalWrapper,
} from '@affine/component'; } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { import {
DiscordIcon, DiscordIcon,
@@ -64,17 +64,17 @@ export const ContactModal = ({
onClose, onClose,
logoSrc, logoSrc,
}: ContactModalProps): JSX.Element => { }: ContactModalProps): JSX.Element => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
const topLinkList = [ const topLinkList = [
{ {
icon: <LogoIcon />, icon: <LogoIcon />,
title: t('Official Website'), title: t['Official Website'](),
subTitle: 'AFFiNE.pro', subTitle: 'AFFiNE.pro',
link: 'https://affine.pro', link: 'https://affine.pro',
}, },
{ {
icon: <DocIcon />, icon: <DocIcon />,
title: t('Check Our Docs'), title: t['Check Our Docs'](),
subTitle: 'Open Source', subTitle: 'Open Source',
link: 'https://community.affine.pro', link: 'https://community.affine.pro',
}, },
@@ -109,7 +109,7 @@ export const ContactModal = ({
})} })}
</FlexWrapper> </FlexWrapper>
<StyledSubTitle> <StyledSubTitle>
{t('Get in touch! Join our communities.')} {t['Get in touch! Join our communities.']()}
</StyledSubTitle> </StyledSubTitle>
<FlexWrapper justifyContent="center"> <FlexWrapper justifyContent="center">
{linkList.map(({ icon, title, link }) => { {linkList.map(({ icon, title, link }) => {

View File

@@ -1,4 +1,4 @@
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { ContentParser } from '@blocksuite/blocks/content-parser'; import { ContentParser } from '@blocksuite/blocks/content-parser';
import { ExportToHtmlIcon, ExportToMarkdownIcon } from '@blocksuite/icons'; import { ExportToHtmlIcon, ExportToMarkdownIcon } from '@blocksuite/icons';
import type { FC } from 'react'; import type { FC } from 'react';
@@ -16,11 +16,11 @@ import type { ShareMenuProps } from './ShareMenu';
export const Export: FC<ShareMenuProps> = props => { export const Export: FC<ShareMenuProps> = props => {
const contentParserRef = useRef<ContentParser>(); const contentParserRef = useRef<ContentParser>();
const { t } = useTranslation(); const t = useAFFiNEI18N();
return ( return (
<div className={menuItemStyle}> <div className={menuItemStyle}>
<div className={descriptionStyle}> <div className={descriptionStyle}>
{t('Export Shared Pages Description')} {t['Export Shared Pages Description']()}
</div> </div>
<div className={actionsStyle}> <div className={actionsStyle}>
<Button <Button
@@ -33,7 +33,7 @@ export const Export: FC<ShareMenuProps> = props => {
}} }}
> >
<ExportToHtmlIcon className={svgStyle} /> <ExportToHtmlIcon className={svgStyle} />
{t('Export to HTML')} {t['Export to HTML']()}
</Button> </Button>
<Button <Button
className={exportButtonStyle} className={exportButtonStyle}
@@ -45,7 +45,7 @@ export const Export: FC<ShareMenuProps> = props => {
}} }}
> >
<ExportToMarkdownIcon className={svgStyle} /> <ExportToMarkdownIcon className={svgStyle} />
{t('Export to Markdown')} {t['Export to Markdown']()}
</Button> </Button>
</div> </div>
</div> </div>

View File

@@ -1,5 +1,6 @@
import { prefixUrl } from '@affine/env'; import { prefixUrl } from '@affine/env';
import { Trans, useTranslation } from '@affine/i18n'; import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { LocalWorkspace } from '@affine/workspace/type'; import type { LocalWorkspace } from '@affine/workspace/type';
import { WorkspaceFlavour } from '@affine/workspace/type'; import { WorkspaceFlavour } from '@affine/workspace/type';
import { useBlockSuiteWorkspacePageIsPublic } from '@toeverything/hooks/use-block-suite-workspace-page-is-public'; import { useBlockSuiteWorkspacePageIsPublic } from '@toeverything/hooks/use-block-suite-workspace-page-is-public';
@@ -23,17 +24,17 @@ import {
} from './styles'; } from './styles';
export const LocalSharePage: FC<ShareMenuProps> = props => { export const LocalSharePage: FC<ShareMenuProps> = props => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
return ( return (
<div className={menuItemStyle}> <div className={menuItemStyle}>
<div className={descriptionStyle}>{t('Shared Pages Description')}</div> <div className={descriptionStyle}>{t['Shared Pages Description']()}</div>
<StyledButton <StyledButton
data-testid="share-menu-enable-affine-cloud-button" data-testid="share-menu-enable-affine-cloud-button"
onClick={() => { onClick={() => {
props.onEnableAffineCloud(props.workspace as LocalWorkspace); props.onEnableAffineCloud(props.workspace as LocalWorkspace);
}} }}
> >
{t('Enable AFFiNE Cloud')} {t['Enable AFFiNE Cloud']()}
</StyledButton> </StyledButton>
</div> </div>
); );
@@ -44,7 +45,7 @@ export const AffineSharePage: FC<ShareMenuProps> = props => {
props.currentPage props.currentPage
); );
const [showDisable, setShowDisable] = useState(false); const [showDisable, setShowDisable] = useState(false);
const { t } = useTranslation(); const t = useAFFiNEI18N();
const sharingUrl = useMemo(() => { const sharingUrl = useMemo(() => {
return `${prefixUrl}public-workspace/${props.workspace.id}/${props.currentPage.id}`; return `${prefixUrl}public-workspace/${props.workspace.id}/${props.currentPage.id}`;
}, [props.workspace.id, props.currentPage.id]); }, [props.workspace.id, props.currentPage.id]);
@@ -53,13 +54,13 @@ export const AffineSharePage: FC<ShareMenuProps> = props => {
}, [setIsPublic]); }, [setIsPublic]);
const onClickCopyLink = useCallback(() => { const onClickCopyLink = useCallback(() => {
navigator.clipboard.writeText(sharingUrl); navigator.clipboard.writeText(sharingUrl);
toast(t('Copied link to clipboard')); toast(t['Copied link to clipboard']());
}, [sharingUrl, t]); }, [sharingUrl, t]);
return ( return (
<div className={menuItemStyle}> <div className={menuItemStyle}>
<div className={descriptionStyle}> <div className={descriptionStyle}>
{t('Create Shared Link Description')} {t['Create Shared Link Description']()}
</div> </div>
<div className={inputButtonRowStyle}> <div className={inputButtonRowStyle}>
<StyledInput <StyledInput
@@ -72,7 +73,7 @@ export const AffineSharePage: FC<ShareMenuProps> = props => {
data-testid="affine-share-create-link" data-testid="affine-share-create-link"
onClick={onClickCreateLink} onClick={onClickCreateLink}
> >
{t('Create')} {t['Create']()}
</StyledButton> </StyledButton>
)} )}
{isPublic && ( {isPublic && (
@@ -80,7 +81,7 @@ export const AffineSharePage: FC<ShareMenuProps> = props => {
data-testid="affine-share-copy-link" data-testid="affine-share-copy-link"
onClick={onClickCopyLink} onClick={onClickCopyLink}
> >
{t('Copy Link')} {t['Copy Link']()}
</StyledButton> </StyledButton>
)} )}
</div> </div>
@@ -100,7 +101,7 @@ export const AffineSharePage: FC<ShareMenuProps> = props => {
{isPublic && ( {isPublic && (
<> <>
<StyledDisableButton onClick={() => setShowDisable(true)}> <StyledDisableButton onClick={() => setShowDisable(true)}>
{t('Disable Public Link')} {t['Disable Public Link']()}
</StyledDisableButton> </StyledDisableButton>
<PublicLinkDisableModal <PublicLinkDisableModal
page={props.currentPage} page={props.currentPage}

View File

@@ -1,4 +1,4 @@
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { AffineWorkspace, LocalWorkspace } from '@affine/workspace/type'; import type { AffineWorkspace, LocalWorkspace } from '@affine/workspace/type';
import { WorkspaceFlavour } from '@affine/workspace/type'; import { WorkspaceFlavour } from '@affine/workspace/type';
import type { FC } from 'react'; import type { FC } from 'react';
@@ -8,11 +8,11 @@ import type { ShareMenuProps } from './ShareMenu';
import { StyledButton } from './styles'; import { StyledButton } from './styles';
const ShareLocalWorkspace: FC<ShareMenuProps<LocalWorkspace>> = props => { const ShareLocalWorkspace: FC<ShareMenuProps<LocalWorkspace>> = props => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
return ( return (
<div className={menuItemStyle}> <div className={menuItemStyle}>
<div className={descriptionStyle}> <div className={descriptionStyle}>
{t('Share Menu Public Workspace Description1')} {t['Share Menu Public Workspace Description1']()}
</div> </div>
<StyledButton <StyledButton
data-testid="share-menu-enable-affine-cloud-button" data-testid="share-menu-enable-affine-cloud-button"
@@ -20,7 +20,7 @@ const ShareLocalWorkspace: FC<ShareMenuProps<LocalWorkspace>> = props => {
props.onOpenWorkspaceSettings(props.workspace); props.onOpenWorkspaceSettings(props.workspace);
}} }}
> >
{t('Open Workspace Settings')} {t['Open Workspace Settings']()}
</StyledButton> </StyledButton>
</div> </div>
); );
@@ -28,13 +28,13 @@ const ShareLocalWorkspace: FC<ShareMenuProps<LocalWorkspace>> = props => {
const ShareAffineWorkspace: FC<ShareMenuProps<AffineWorkspace>> = props => { const ShareAffineWorkspace: FC<ShareMenuProps<AffineWorkspace>> = props => {
const isPublicWorkspace = props.workspace.public; const isPublicWorkspace = props.workspace.public;
const { t } = useTranslation(); const t = useAFFiNEI18N();
return ( return (
<div className={menuItemStyle}> <div className={menuItemStyle}>
<div className={descriptionStyle}> <div className={descriptionStyle}>
{isPublicWorkspace {isPublicWorkspace
? t('Share Menu Public Workspace Description2') ? t['Share Menu Public Workspace Description2']()
: t('Share Menu Public Workspace Description1')} : t['Share Menu Public Workspace Description1']()}
</div> </div>
<StyledButton <StyledButton
data-testid="share-menu-publish-to-web-button" data-testid="share-menu-publish-to-web-button"
@@ -42,7 +42,7 @@ const ShareAffineWorkspace: FC<ShareMenuProps<AffineWorkspace>> = props => {
props.onOpenWorkspaceSettings(props.workspace); props.onOpenWorkspaceSettings(props.workspace);
}} }}
> >
{t('Open Workspace Settings')} {t['Open Workspace Settings']()}
</StyledButton> </StyledButton>
</div> </div>
); );

View File

@@ -1,4 +1,4 @@
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { Page } from '@blocksuite/store'; import type { Page } from '@blocksuite/store';
import { useBlockSuiteWorkspacePageIsPublic } from '@toeverything/hooks/use-block-suite-workspace-page-is-public'; import { useBlockSuiteWorkspacePageIsPublic } from '@toeverything/hooks/use-block-suite-workspace-page-is-public';
import { useCallback } from 'react'; import { useCallback } from 'react';
@@ -24,7 +24,7 @@ export const PublicLinkDisableModal = ({
open, open,
onClose, onClose,
}: PublicLinkDisableProps) => { }: PublicLinkDisableProps) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
const [, setIsPublic] = useBlockSuiteWorkspacePageIsPublic(page); const [, setIsPublic] = useBlockSuiteWorkspacePageIsPublic(page);
const handleDisable = useCallback(() => { const handleDisable = useCallback(() => {
setIsPublic(false); setIsPublic(false);
@@ -37,20 +37,20 @@ export const PublicLinkDisableModal = ({
<Modal open={open} onClose={onClose}> <Modal open={open} onClose={onClose}>
<StyledModalWrapper> <StyledModalWrapper>
<ModalCloseButton onClick={onClose} top={12} right={12} /> <ModalCloseButton onClick={onClose} top={12} right={12} />
<StyledModalHeader>{t('Disable Public Link ?')}</StyledModalHeader> <StyledModalHeader>{t['Disable Public Link ?']()}</StyledModalHeader>
<StyledTextContent> <StyledTextContent>
{t('Disable Public Link Description')} {t['Disable Public Link Description']()}
</StyledTextContent> </StyledTextContent>
<StyledButtonContent> <StyledButtonContent>
<StyledButton onClick={onClose}>{t('Cancel')}</StyledButton> <StyledButton onClick={onClose}>{t['Cancel']()}</StyledButton>
<StyledDangerButton <StyledDangerButton
data-testid="disable-public-link-confirm-button" data-testid="disable-public-link-confirm-button"
onClick={handleDisable} onClick={handleDisable}
style={{ marginLeft: '24px' }} style={{ marginLeft: '24px' }}
> >
{t('Disable')} {t['Disable']()}
</StyledDangerButton> </StyledDangerButton>
</StyledButtonContent> </StyledButtonContent>
</StyledModalWrapper> </StyledModalWrapper>

View File

@@ -1,4 +1,4 @@
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { PermissionType } from '@affine/workspace/affine/api'; import { PermissionType } from '@affine/workspace/affine/api';
import type { AffineWorkspace, LocalWorkspace } from '@affine/workspace/type'; import type { AffineWorkspace, LocalWorkspace } from '@affine/workspace/type';
import { WorkspaceFlavour } from '@affine/workspace/type'; import { WorkspaceFlavour } from '@affine/workspace/type';
@@ -46,7 +46,7 @@ const PublishIcon = () => {
}; };
const WorkspaceType: FC<WorkspaceTypeProps> = ({ workspace }) => { const WorkspaceType: FC<WorkspaceTypeProps> = ({ workspace }) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
let isOwner = true; let isOwner = true;
if (workspace.flavour === WorkspaceFlavour.AFFINE) { if (workspace.flavour === WorkspaceFlavour.AFFINE) {
isOwner = workspace.permission === PermissionType.Owner; isOwner = workspace.permission === PermissionType.Owner;
@@ -56,22 +56,22 @@ const WorkspaceType: FC<WorkspaceTypeProps> = ({ workspace }) => {
if (workspace.flavour === WorkspaceFlavour.LOCAL) { if (workspace.flavour === WorkspaceFlavour.LOCAL) {
return ( return (
<p title={t('Local Workspace')}> <p title={t['Local Workspace']()}>
<LocalWorkspaceIcon /> <LocalWorkspaceIcon />
<span>{t('Local Workspace')}</span> <span>{t['Local Workspace']()}</span>
</p> </p>
); );
} }
return isOwner ? ( return isOwner ? (
<p title={t('Cloud Workspace')}> <p title={t['Cloud Workspace']()}>
<CloudWorkspaceIcon /> <CloudWorkspaceIcon />
<span>{t('Cloud Workspace')}</span> <span>{t['Cloud Workspace']()}</span>
</p> </p>
) : ( ) : (
<p title={t('Joined Workspace')}> <p title={t['Joined Workspace']()}>
<JoinedWorkspaceIcon /> <JoinedWorkspaceIcon />
<span>{t('Joined Workspace')}</span> <span>{t['Joined Workspace']()}</span>
</p> </p>
); );
}; };
@@ -89,7 +89,7 @@ export const WorkspaceCard: FC<WorkspaceCardProps> = ({
onSettingClick, onSettingClick,
currentWorkspaceId, currentWorkspaceId,
}) => { }) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
const [name] = useBlockSuiteWorkspaceName(workspace.blockSuiteWorkspace); const [name] = useBlockSuiteWorkspaceName(workspace.blockSuiteWorkspace);
return ( return (
@@ -106,15 +106,15 @@ export const WorkspaceCard: FC<WorkspaceCardProps> = ({
<StyleWorkspaceTitle>{name}</StyleWorkspaceTitle> <StyleWorkspaceTitle>{name}</StyleWorkspaceTitle>
<WorkspaceType workspace={workspace} /> <WorkspaceType workspace={workspace} />
{workspace.flavour === WorkspaceFlavour.LOCAL && ( {workspace.flavour === WorkspaceFlavour.LOCAL && (
<p title={t('Available Offline')}> <p title={t['Available Offline']()}>
<LocalDataIcon /> <LocalDataIcon />
<span>{t('Available Offline')}</span> <span>{t['Available Offline']()}</span>
</p> </p>
)} )}
{workspace.flavour === WorkspaceFlavour.AFFINE && workspace.public && ( {workspace.flavour === WorkspaceFlavour.AFFINE && workspace.public && (
<p title={t('Published to Web')}> <p title={t['Published to Web']()}>
<PublishIcon /> <PublishIcon />
<span>{t('Published to Web')}</span> <span>{t['Published to Web']()}</span>
</p> </p>
)} )}
</StyleWorkspaceInfo> </StyleWorkspaceInfo>

View File

@@ -1,4 +1,4 @@
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { Button } from '../button'; import { Button } from '../button';
import type { ModalProps } from '../modal'; import type { ModalProps } from '../modal';
@@ -38,7 +38,7 @@ export const Confirm = ({
cancelButtonTestId = '', cancelButtonTestId = '',
confirmButtonTestId = '', confirmButtonTestId = '',
}: ConfirmProps) => { }: ConfirmProps) => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
return ( return (
<Modal open={open} disablePortal={false}> <Modal open={open} disablePortal={false}>
<StyledModalWrapper> <StyledModalWrapper>
@@ -60,7 +60,7 @@ export const Confirm = ({
style={{ marginRight: '24px' }} style={{ marginRight: '24px' }}
data-testid={cancelButtonTestId} data-testid={cancelButtonTestId}
> >
{cancelText === 'Cancel' ? t('Cancel') : cancelText} {cancelText === 'Cancel' ? t['Cancel']() : cancelText}
</Button> </Button>
<Button <Button
type={confirmType} type={confirmType}
@@ -102,7 +102,7 @@ export const Confirm = ({
}} }}
data-testid={cancelButtonTestId} data-testid={cancelButtonTestId}
> >
{cancelText === 'Cancel' ? t('Cancel') : cancelText} {cancelText === 'Cancel' ? t['Cancel']() : cancelText}
</Button> </Button>
</StyledColumnButtonWrapper> </StyledColumnButtonWrapper>
)} )}

View File

@@ -6,7 +6,7 @@
- Replace literal text with translation keys - Replace literal text with translation keys
```tsx ```tsx
import { useTranslation } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
// src/resources/en.json // src/resources/en.json
// { // {
@@ -15,7 +15,7 @@ import { useTranslation } from '@affine/i18n';
// }; // };
const App = () => { const App = () => {
const { t } = useTranslation(); const t = useAFFiNEI18N();
const changeLanguage = (language: string) => { const changeLanguage = (language: string) => {
i18n.changeLanguage(language); i18n.changeLanguage(language);
@@ -25,12 +25,8 @@ const App = () => {
<div> <div>
<div>{t('Text')}</div> <div>{t('Text')}</div>
<button onClick={() => changeLanguage('en')}> <button onClick={() => changeLanguage('en')}>{t('Switch to language', { language: 'en' })}</button>
{t('Switch to language', { language: 'en' })} <button onClick={() => changeLanguage('zh-Hans')}>{t('Switch to language', { language: 'zh-Hans' })}</button>
</button>
<button onClick={() => changeLanguage('zh-Hans')}>
{t('Switch to language', { language: 'zh-Hans' })}
</button>
</div> </div>
); );
}; };

View File

@@ -4,7 +4,8 @@
"type": "module", "type": "module",
"main": "src/index.ts", "main": "src/index.ts",
"exports": { "exports": {
".": "./src/index.ts" ".": "./src/index.ts",
"./hooks": "./src/i18n_generated"
}, },
"publishConfig": { "publishConfig": {
"main": "dist/src/index.js", "main": "dist/src/index.js",

View File

@@ -4,7 +4,7 @@ import {
I18nextProvider, I18nextProvider,
initReactI18next, initReactI18next,
Trans, Trans,
useTranslation, useTranslation as useRootTranslation,
} from 'react-i18next'; } from 'react-i18next';
import { LOCALES } from './resources'; import { LOCALES } from './resources';
@@ -38,7 +38,11 @@ declare module 'react-i18next' {
const STORAGE_KEY = 'i18n_lng'; const STORAGE_KEY = 'i18n_lng';
export { I18nextProvider, LOCALES, Trans, useTranslation }; export { I18nextProvider, LOCALES, Trans };
export function useI18N() {
const { i18n } = useRootTranslation();
return i18n;
}
const resources = LOCALES.reduce<Resource>( const resources = LOCALES.reduce<Resource>(
(acc, { tag, res }) => ({ ...acc, [tag]: { translation: res } }), (acc, { tag, res }) => ({ ...acc, [tag]: { translation: res } }),

View File

@@ -8,6 +8,8 @@
"It takes up more space on your device": { "It takes up more space on your device": {
"": "It takes up more space on your device." "": "It takes up more space on your device."
}, },
"Synced with AFFiNE Cloud": "Synced with AFFiNE Cloud",
"Successfully deleted": "Successfully deleted",
"Export AFFiNE backup file": "Export AFFiNE backup file", "Export AFFiNE backup file": "Export AFFiNE backup file",
"Saved then enable AFFiNE Cloud": "All changes are saved locally, click to enable AFFiNE Cloud.", "Saved then enable AFFiNE Cloud": "All changes are saved locally, click to enable AFFiNE Cloud.",
"Not now": "Not now", "Not now": "Not now",
@@ -95,6 +97,7 @@
"Quick search placeholder": "Quick Search...", "Quick search placeholder": "Quick Search...",
"Quick search placeholder2": "Search in {{workspace}}", "Quick search placeholder2": "Search in {{workspace}}",
"Settings": "Settings", "Settings": "Settings",
"Recent": "Recent",
"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",

19
scripts/setup/i18n.ts Normal file
View File

@@ -0,0 +1,19 @@
import { fileURLToPath } from 'node:url';
import { runCli } from '@magic-works/i18n-codegen';
import { beforeAll } from 'vitest';
beforeAll(async () => {
await runCli(
{
watch: false,
cwd: fileURLToPath(
new URL('../../../.i18n-codegen.json', import.meta.url)
),
},
error => {
console.error(error);
process.exit(1);
}
);
});

View File

@@ -22,6 +22,7 @@
"@affine/component/*": ["./packages/component/src/components/*/index"], "@affine/component/*": ["./packages/component/src/components/*/index"],
"@affine/templates/*": ["./packages/templates/src/*"], "@affine/templates/*": ["./packages/templates/src/*"],
"@affine/i18n": ["./packages/i18n/src"], "@affine/i18n": ["./packages/i18n/src"],
"@affine/i18n/hooks": ["./packages/i18n/src/i18n_generated"],
"@affine/debug": ["./packages/debug"], "@affine/debug": ["./packages/debug"],
"@affine/jotai": ["./packages/jotai"], "@affine/jotai": ["./packages/jotai"],
"@affine/env": ["./packages/env"], "@affine/env": ["./packages/env"],

View File

@@ -5,5 +5,5 @@
"moduleResolution": "Node", "moduleResolution": "Node",
"allowSyntheticDefaultImports": true "allowSyntheticDefaultImports": true
}, },
"include": ["vitest.config.ts"] "include": ["vitest.config.ts", "scripts"]
} }

View File

@@ -19,6 +19,7 @@ export default defineConfig({
test: { test: {
setupFiles: [ setupFiles: [
resolve(rootDir, './scripts/setup/lit.ts'), resolve(rootDir, './scripts/setup/lit.ts'),
resolve(rootDir, './scripts/setup/i18n.ts'),
resolve(rootDir, './scripts/setup/search.ts'), resolve(rootDir, './scripts/setup/search.ts'),
resolve(rootDir, './scripts/setup/lottie-web.ts'), resolve(rootDir, './scripts/setup/lottie-web.ts'),
], ],

View File

@@ -5075,6 +5075,21 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@magic-works/i18n-codegen@npm:^0.5.0":
version: 0.5.0
resolution: "@magic-works/i18n-codegen@npm:0.5.0"
dependencies:
chokidar: ^3.5.3
i18next-translation-parser: ^1.0.1
yargs: ^17.7.0
peerDependencies:
typescript: ^4.8 || ^5
bin:
i18n-codegen: dist/cli.js
checksum: 6b730f322dbf90b55256ca818546252b43b9c86e051d3de697f09b64b12b09812af468b185f64546f0cb53ef07815b91dfaa9d0ed2de0b3a58fb2f81a4dc67a9
languageName: node
linkType: hard
"@malept/cross-spawn-promise@npm:^1.0.0, @malept/cross-spawn-promise@npm:^1.1.0": "@malept/cross-spawn-promise@npm:^1.0.0, @malept/cross-spawn-promise@npm:^1.1.0":
version: 1.1.1 version: 1.1.1
resolution: "@malept/cross-spawn-promise@npm:1.1.1" resolution: "@malept/cross-spawn-promise@npm:1.1.1"
@@ -9431,6 +9446,7 @@ __metadata:
"@commitlint/config-conventional": ^17.6.1 "@commitlint/config-conventional": ^17.6.1
"@faker-js/faker": ^7.6.0 "@faker-js/faker": ^7.6.0
"@istanbuljs/schema": ^0.1.3 "@istanbuljs/schema": ^0.1.3
"@magic-works/i18n-codegen": ^0.5.0
"@perfsee/sdk": ^1.6.0 "@perfsee/sdk": ^1.6.0
"@playwright/test": ^1.33.0 "@playwright/test": ^1.33.0
"@testing-library/react": ^14.0.0 "@testing-library/react": ^14.0.0
@@ -15536,6 +15552,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"html-parse-stringify2@github:locize/html-parse-stringify2":
version: 2.0.1
resolution: "html-parse-stringify2@https://github.com/locize/html-parse-stringify2.git#commit=d463109433b2c49c74a081044f54b2a6a1ccad7c"
dependencies:
void-elements: ^2.0.1
checksum: 4416c04f907ec14e4dac96b32094dd98a8633264f1d8b1128243c20d0e720dc79dfd5ccaac5ee4d80b7f845e676a248bfbe323fe86c096ea020c0e33d754d0b5
languageName: node
linkType: hard
"html-parse-stringify@npm:^3.0.1": "html-parse-stringify@npm:^3.0.1":
version: 3.0.1 version: 3.0.1
resolution: "html-parse-stringify@npm:3.0.1" resolution: "html-parse-stringify@npm:3.0.1"
@@ -15703,6 +15728,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"i18next-translation-parser@npm:^1.0.1":
version: 1.0.1
resolution: "i18next-translation-parser@npm:1.0.1"
dependencies:
html-parse-stringify2: "github:locize/html-parse-stringify2"
checksum: c208e0335a7fc3f3e298811d4d512c86bfdf5e5d7048081ed1a1f41940e775efa7c330b8147fde97e46b0876d02c30d9b9213c672198add39215599cd019b802
languageName: node
linkType: hard
"i18next@npm:^22.4.15": "i18next@npm:^22.4.15":
version: 22.4.15 version: 22.4.15
resolution: "i18next@npm:22.4.15" resolution: "i18next@npm:22.4.15"
@@ -24739,6 +24773,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"void-elements@npm:^2.0.1":
version: 2.0.1
resolution: "void-elements@npm:2.0.1"
checksum: 700c07ba9cfa2dff88bb23974b3173118f9ad8107143db9e5d753552be15cf93380954d4e7f7d7bc80e7306c35c3a7fb83ab0ce4d4dcc18abf90ca8b31452126
languageName: node
linkType: hard
"vscode-oniguruma@npm:^1.7.0": "vscode-oniguruma@npm:^1.7.0":
version: 1.7.0 version: 1.7.0
resolution: "vscode-oniguruma@npm:1.7.0" resolution: "vscode-oniguruma@npm:1.7.0"
@@ -25437,7 +25478,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"yargs@npm:^17.0.0, yargs@npm:^17.0.1, yargs@npm:^17.3.1, yargs@npm:^17.7.1": "yargs@npm:^17.0.0, yargs@npm:^17.0.1, yargs@npm:^17.3.1, yargs@npm:^17.7.0, yargs@npm:^17.7.1":
version: 17.7.2 version: 17.7.2
resolution: "yargs@npm:17.7.2" resolution: "yargs@npm:17.7.2"
dependencies: dependencies: