diff --git a/spx-gui/src/components/project/ProjectCreate.vue b/spx-gui/src/components/project/ProjectCreate.vue index 155d36f1d..c1e332a57 100644 --- a/spx-gui/src/components/project/ProjectCreate.vue +++ b/spx-gui/src/components/project/ProjectCreate.vue @@ -4,10 +4,10 @@ - + {{ _t({ en: 'Cancel', zh: '取消' }) }} - + {{ _t({ en: 'Create', zh: '创建' }) }} @@ -17,9 +17,9 @@ -../project \ No newline at end of file diff --git a/spx-gui/src/utils/exception.ts b/spx-gui/src/utils/exception.ts index 795a1dc5b..16808b9ea 100644 --- a/spx-gui/src/utils/exception.ts +++ b/spx-gui/src/utils/exception.ts @@ -4,7 +4,7 @@ import { useMessage } from 'naive-ui' import { useI18n } from './i18n' -import type { LocaleMessage, FunctionLocaleMessage } from './i18n' +import type { LocaleMessage } from './i18n' /** * Exceptions are like errors, while slightly different: @@ -28,6 +28,11 @@ export class DefaultException extends Exception { } } +/** + * Cancelled is a special exception, it stands for a "cancel operation" because of user ineraction. + * Like other exceptions, it breaks normal flows, while it is supposed to be ignored by all user-feedback components, + * so the user will not be notified of cancelled exceptions. + */ export class Cancelled extends Exception { name = 'Cancelled' userMessage = null @@ -36,43 +41,41 @@ export class Cancelled extends Exception { } } -export function cancel(): never { - throw new Cancelled() -} - -const failedMessage: FunctionLocaleMessage<[summary: string, reason: string | null]> = { - en: (summary, reason) => (reason ? `${summary} (${reason})` : summary), - zh: (summary, reason) => (reason ? `${summary}(${reason})` : summary) -} +const failedMessage = (summary: string, reason: string | null) => ({ + en: reason ? `${summary} (${reason})` : summary, + zh: reason ? `${summary}(${reason})` : summary +}) -export function useMessageHandle Promise>( - action: F, +export function useMessageHandle( + action: (...args: Args) => Promise, failureSummaryMessage: LocaleMessage, - successMessage?: LocaleMessage -): F { + successMessage?: LocaleMessage | ((ret: Ret) => LocaleMessage) +): (...args: Args) => Promise { const m = useMessage() const { t } = useI18n() - return (() => { - return action().then( + return ((...args: Args) => { + return action(...args).then( (ret) => { if (successMessage != null) { - m.success(() => t(successMessage)) + const successText = t(typeof successMessage === 'function' ? successMessage(ret) : successMessage) + m.success(() => successText) } return ret }, (e) => { - if (e instanceof Cancelled) return - let reasonMessage: LocaleMessage | null = null - if (e instanceof Exception && e.userMessage != null) { - reasonMessage = e.userMessage + if (!(e instanceof Cancelled)) { + let reasonMessage: LocaleMessage | null = null + if (e instanceof Exception && e.userMessage != null) { + reasonMessage = e.userMessage + } + const result = t(failedMessage(t(failureSummaryMessage), t(reasonMessage))) + m.error(() => result) } - const result = t(failedMessage, t(failureSummaryMessage), t(reasonMessage)) - m.error(() => result) throw e } ) - }) as F + }) } // TODO: helpers for in-place feedback diff --git a/spx-gui/src/utils/i18n/README.md b/spx-gui/src/utils/i18n/README.md index a3ba3833a..0a732eb5e 100644 --- a/spx-gui/src/utils/i18n/README.md +++ b/spx-gui/src/utils/i18n/README.md @@ -41,17 +41,17 @@ const { t } = useI18n() const signoutText = t({ en: 'Sign out', zh: '登出' }) ``` -### Function Locale Message +### Locale Message Functions -Function-locale-messages are messages that extra information are needed when translating. For example: +Locale-message-functions are functions that return locale message. It is useful when extra information is needed when constructing locale messages. For example: ```ts -const projectSummaryMessage: FunctionLocaleMessage<[num: number]> = { - en: num => `You have ${num} project${num > 1 ? 's' : ''}`, - zh: num => `你有 ${num} 个项目` -} +const projectSummaryMessage = (num: number) => ({ + en: `You have ${num} project${num > 1 ? 's' : ''}`, + zh: `你有 ${num} 个项目` +}) -const projectSummary = t(projectSummaryMessage, 3) // "You have 3 projects" / "你有 3 个项目" +const projectSummary = t(projectSummaryMessage(3)) // "You have 3 projects" / "你有 3 个项目" ``` It's like [interpolations](https://vue-i18n.intlify.dev/guide/essentials/syntax.html#interpolations) in vue-i18n, but simpler & more powerful. @@ -75,15 +75,3 @@ const helloMessage = { const resultMessage = mapMessage(helloMessage, hello => hello + ' foo') console.log(t(resultMessage)) // "Hello foo" / "你好 foo" ``` - -We can also use `mapMessage` with function-locale-messages: - -```ts -const projectSummaryMessage: FunctionLocaleMessage<[num: number]> = { - en: num => `You have ${num} project${num > 1 ? 's' : ''}`, - zh: num => `你有 ${num} 个项目` -} - -const resultMessage = mapMessage(projectSummaryMessage, f => f(3)) -console.log(t(resultMessage)) // "You have 3 projects" / "你有 3 个项目" -``` \ No newline at end of file diff --git a/spx-gui/src/utils/i18n/index.ts b/spx-gui/src/utils/i18n/index.ts index caf6848c2..d009a390f 100644 --- a/spx-gui/src/utils/i18n/index.ts +++ b/spx-gui/src/utils/i18n/index.ts @@ -11,8 +11,6 @@ export type Translated = string export type LocaleMessage = Record -export type FunctionLocaleMessage = Record Translated> - export interface I18nConfig { /** Initial lang */ lang: Lang @@ -43,17 +41,9 @@ export class I18n implements ObjectPlugin<[]> { /** Translate */ t(message: LocaleMessage): Translated t(message: LocaleMessage | null): Translated | null - t(message: FunctionLocaleMessage, ...args: Args): Translated - t( - message: FunctionLocaleMessage | null, - ...args: Args - ): Translated | null - t(message: LocaleMessage | FunctionLocaleMessage | null, ...args: unknown[]) { + t(message: LocaleMessage | null) { if (message == null) return null const val = message[this.lang.value] - if (typeof val === 'function') { - return (val as any)(...args) - } return val } @@ -74,7 +64,7 @@ export function useI18n() { return i18n } -export function mapMessage, T>( +export function mapMessage( message: M, process: (value: M[keyof M], lang: Lang) => T ): Record {