refactor(i18n): new hook api (#7273)

# NEW HOOK API

`useI18n`: same as `useAFFiNEI18N`, with additional APIs

```ts
import { useI18n } from '@affine/i18n'

const i18n = useI18n()
i18n['hello world']() -> 你好世界
```

# NEW GLOBAL i18n Instance

`I18n`: use i18n capabilities outside of React

```ts
import { I18n } from '@affine/i18n'

I18n['hello world']() -> 你好世界
```

# NEW TYPES

`I18nKeys` -> all i18n keys

`I18nString` -> An i18n message (key&options)
transfer and store i18n text outside of React
```ts
const msg: I18nString = {
  key: 'helloworld',
  options: {
    arg1: '123'
  }
}

I18n.t(msg) -> 你好世界123
```

before:

```ts
registerCommand('open-page', {
  name: t('command.open-page')
  // ^- translation happens here,
})
```

after:

```ts
registerCommand('open-page', {
  name: { key: 'command.open-page' }
  // ^- store I18nString here, translate when the command render to UI
})
```
This commit is contained in:
EYHN
2024-06-20 02:19:41 +00:00
parent 5b0f56399c
commit 7c0a686cd9
193 changed files with 553 additions and 575 deletions
@@ -5,7 +5,7 @@ import { useBlockSuiteDocMeta } from '@affine/core/hooks/use-block-suite-page-me
import { FavoriteItemsAdapter } from '@affine/core/modules/properties';
import { ShareDocsService } from '@affine/core/modules/share-doc';
import { PublicPageMode } from '@affine/graphql';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useI18n } from '@affine/i18n';
import type { DocMeta } from '@blocksuite/store';
import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
import { useCallback, useEffect, useMemo } from 'react';
@@ -30,7 +30,7 @@ export const useAllPageListConfig = () => {
[pageMetas]
);
const favAdapter = useService(FavoriteItemsAdapter);
const t = useAFFiNEI18N();
const t = useI18n();
const favoriteItems = useLiveData(favAdapter.favorites$);
const isActive = useCallback(
@@ -2,7 +2,7 @@ import { notify, useConfirmModal } from '@affine/component';
import { authAtom } from '@affine/core/atoms';
import { AuthService } from '@affine/core/modules/cloud';
import { WorkspaceSubPath } from '@affine/core/shared';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useI18n } from '@affine/i18n';
import type { Workspace } from '@toeverything/infra';
import {
useLiveData,
@@ -28,7 +28,7 @@ interface ConfirmEnableCloudOptions {
type ConfirmEnableArgs = [Workspace, ConfirmEnableCloudOptions | undefined];
export const useEnableCloud = () => {
const t = useAFFiNEI18N();
const t = useI18n();
const loginStatus = useLiveData(useService(AuthService).session.status$);
const setAuthAtom = useSetAtom(authAtom);
const { openConfirmModal, closeConfirmModal } = useConfirmModal();
@@ -5,7 +5,7 @@ import {
} from '@affine/component/global-loading';
import { mixpanel } from '@affine/core/utils';
import { apis } from '@affine/electron-api';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useI18n } from '@affine/i18n';
import type { PageRootService, RootBlockModel } from '@blocksuite/blocks';
import { HtmlTransformer, MarkdownTransformer } from '@blocksuite/blocks';
import type { Doc } from '@blocksuite/store';
@@ -59,7 +59,7 @@ async function exportHandler({ page, type }: ExportHandlerOptions) {
export const useExportPage = (page: Doc) => {
const pushGlobalLoadingEvent = useSetAtom(pushGlobalLoadingEventAtom);
const resolveGlobalLoadingEvent = useSetAtom(resolveGlobalLoadingEventAtom);
const t = useAFFiNEI18N();
const t = useI18n();
const onClickHandler = useCallback(
async (type: ExportType) => {
@@ -2,7 +2,7 @@ import { toast } from '@affine/component';
import { useDocMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta';
import { CollectionService } from '@affine/core/modules/collection';
import { FavoriteItemsAdapter } from '@affine/core/modules/properties';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useI18n } from '@affine/i18n';
import type {
Active,
DragEndEvent,
@@ -152,7 +152,7 @@ export function resolveDragEndIntent(
export type GlobalDragEndIntent = ReturnType<typeof resolveDragEndIntent>;
export const useGlobalDNDHelper = () => {
const t = useAFFiNEI18N();
const t = useI18n();
const currentWorkspace = useService(WorkspaceService).workspace;
const favAdapter = useService(FavoriteItemsAdapter);
const workspace = currentWorkspace.docCollection;
@@ -6,7 +6,7 @@ import {
publishPageMutation,
revokePublicPageMutation,
} from '@affine/graphql';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { type I18nKeys, useI18n } from '@affine/i18n';
import { SingleSelectSelectSolidIcon } from '@blocksuite/icons/rc';
import type { DocMode, Workspace } from '@toeverything/infra';
import { cssVar } from '@toeverything/theme';
@@ -15,12 +15,6 @@ import { useCallback, useMemo } from 'react';
import { useMutation } from '../use-mutation';
import { useQuery } from '../use-query';
type NoParametersKeys<T> = {
[K in keyof T]: T[K] extends () => any ? K : never;
}[keyof T];
type i18nKey = NoParametersKeys<ReturnType<typeof useAFFiNEI18N>>;
type NotificationKey =
| 'enableSuccessTitle'
| 'enableSuccessMessage'
@@ -34,7 +28,7 @@ type NotificationKey =
| 'disableErrorTitle'
| 'disableErrorMessage';
const notificationToI18nKey: Record<NotificationKey, i18nKey> = {
const notificationToI18nKey = {
enableSuccessTitle:
'com.affine.share-menu.create-public-link.notification.success.title',
enableSuccessMessage:
@@ -57,7 +51,7 @@ const notificationToI18nKey: Record<NotificationKey, i18nKey> = {
'com.affine.share-menu.disable-publish-link.notification.fail.title',
disableErrorMessage:
'com.affine.share-menu.disable-publish-link.notification.fail.message',
};
} satisfies Record<NotificationKey, I18nKeys>;
export function useIsSharedPage(
workspaceId: string,
@@ -69,7 +63,7 @@ export function useIsSharedPage(
currentShareMode: DocMode;
enableShare: (mode: DocMode) => void;
} {
const t = useAFFiNEI18N();
const t = useI18n();
const { data, mutate } = useQuery({
query: getWorkspacePublicPagesQuery,
variables: {
@@ -1,9 +1,9 @@
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { LOCALES, useI18N } from '@affine/i18n';
import { LOCALES, useI18n } from '@affine/i18n';
import { useEffect, useMemo } from 'react';
export function useLanguageHelper() {
const i18n = useI18N();
const i18n = useI18n();
const currentLanguage = useMemo(
() => LOCALES.find(item => item.tag === i18n.language),
[i18n.language]
@@ -7,7 +7,7 @@ import { useDocMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta';
import { FavoriteItemsAdapter } from '@affine/core/modules/properties';
import { mixpanel } from '@affine/core/utils';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useI18n } from '@affine/i18n';
import { assertExists } from '@blocksuite/global/utils';
import { EdgelessIcon, HistoryIcon, PageIcon } from '@blocksuite/icons/rc';
import {
@@ -28,7 +28,7 @@ export function useRegisterBlocksuiteEditorCommands() {
const doc = useService(DocService).doc;
const docId = doc.id;
const mode = useLiveData(doc.mode$);
const t = useAFFiNEI18N();
const t = useI18n();
const workspace = useService(WorkspaceService).workspace;
const docCollection = workspace.docCollection;
const { getDocMeta } = useDocMetaHelper(docCollection);
@@ -2,7 +2,7 @@ import { notify } from '@affine/component';
import { getAffineCloudBaseUrl } from '@affine/core/modules/cloud/services/fetch';
import { WorkbenchService } from '@affine/core/modules/workbench';
import { mixpanel } from '@affine/core/utils';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useI18n } from '@affine/i18n';
import { useLiveData, useService } from '@toeverything/infra';
import { useCallback, useMemo } from 'react';
@@ -47,7 +47,7 @@ export const useSharingUrl = ({
pageId,
urlType,
}: UseSharingUrl) => {
const t = useAFFiNEI18N();
const t = useI18n();
const sharingUrl = useGenerateUrl({ workspaceId, pageId, urlType });
const onClickCopyLink = useCallback(() => {
@@ -1,4 +1,4 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useI18n } from '@affine/i18n';
import { useCallback, useMemo } from 'react';
type KeyboardShortcutsI18NKeys =
@@ -45,9 +45,9 @@ type KeyboardShortcutsI18NKeys =
| 'divider'
| 'copy-private-link';
// TODO(550): remove this hook after 'useAFFiNEI18N' support scoped i18n
// TODO(550): remove this hook after 'useI18n' support scoped i18n
const useKeyboardShortcutsI18N = () => {
const t = useAFFiNEI18N();
const t = useI18n();
return useCallback(
(key: KeyboardShortcutsI18NKeys) =>
t[`com.affine.keyboardShortcuts.${key}`](),
@@ -55,9 +55,9 @@ const useKeyboardShortcutsI18N = () => {
);
};
// TODO(550): remove this hook after 'useAFFiNEI18N' support scoped i18n
// TODO(550): remove this hook after 'useI18n' support scoped i18n
const useHeadingKeyboardShortcutsI18N = () => {
const t = useAFFiNEI18N();
const t = useI18n();
return useCallback(
(number: string) => t['com.affine.keyboardShortcuts.heading']({ number }),
[t]
@@ -280,7 +280,7 @@ export const useWinMarkdownShortcuts = (): ShortcutMap => {
};
export const useMarkdownShortcuts = (): ShortcutsInfo => {
const t = useAFFiNEI18N();
const t = useI18n();
const macMarkdownShortcuts = useMacMarkdownShortcuts();
const winMarkdownShortcuts = useWinMarkdownShortcuts();
@@ -292,7 +292,7 @@ export const useMarkdownShortcuts = (): ShortcutsInfo => {
};
export const usePageShortcuts = (): ShortcutsInfo => {
const t = useAFFiNEI18N();
const t = useI18n();
const macPageShortcuts = useMacPageKeyboardShortcuts();
const winPageShortcuts = useWinPageKeyboardShortcuts();
@@ -304,7 +304,7 @@ export const usePageShortcuts = (): ShortcutsInfo => {
};
export const useEdgelessShortcuts = (): ShortcutsInfo => {
const t = useAFFiNEI18N();
const t = useI18n();
const macEdgelessShortcuts = useMacEdgelessKeyboardShortcuts();
const winEdgelessShortcuts = useWinEdgelessKeyboardShortcuts();
@@ -316,7 +316,7 @@ export const useEdgelessShortcuts = (): ShortcutsInfo => {
};
export const useGeneralShortcuts = (): ShortcutsInfo => {
const t = useAFFiNEI18N();
const t = useI18n();
const macGeneralShortcuts = useMacGeneralKeyboardShortcuts();
const winGeneralShortcuts = useWinGeneralKeyboardShortcuts();
@@ -1,5 +1,5 @@
import { toast } from '@affine/component';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useI18n } from '@affine/i18n';
import type { DocCollection } from '@blocksuite/store';
import { useAtom } from 'jotai';
import { useCallback } from 'react';
@@ -8,7 +8,7 @@ import { trashModalAtom } from '../../atoms/trash-modal';
import { useBlockSuiteMetaHelper } from './use-block-suite-meta-helper';
export function useTrashModalHelper(docCollection: DocCollection) {
const t = useAFFiNEI18N();
const t = useI18n();
const [trashModal, setTrashModal] = useAtom(trashModalAtom);
const { pageIds } = trashModal;
const { removeToTrash } = useBlockSuiteMetaHelper(docCollection);
@@ -1,4 +1,4 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useI18n } from '@affine/i18n';
import type { AffineEditorContainer } from '@blocksuite/presets';
import { useService, WorkspaceService } from '@toeverything/infra';
import { useStore } from 'jotai';
@@ -58,7 +58,7 @@ function registerCMDKCommand(
export function useRegisterWorkspaceCommands() {
const store = useStore();
const t = useAFFiNEI18N();
const t = useI18n();
const theme = useTheme();
const currentWorkspace = useService(WorkspaceService).workspace;
const languageHelper = useLanguageHelper();