mirror of
https://github.com/logto-io/logto.git
synced 2025-04-14 23:11:31 -05:00
refactor(console): extract common i18n values (#1264)
This commit is contained in:
parent
05db12492c
commit
6c6e06474a
37 changed files with 134 additions and 222 deletions
|
@ -270,7 +270,7 @@ Notes:
|
|||
subtitle="3 steps"
|
||||
index={4}
|
||||
activeIndex={props.activeStepIndex}
|
||||
buttonText="general.done"
|
||||
buttonText="admin_console.general.done"
|
||||
onButtonClick={props.onComplete}
|
||||
>
|
||||
|
||||
|
|
|
@ -270,7 +270,7 @@ logtoClient.signOut(logtoException -> {
|
|||
subtitle="3 steps"
|
||||
index={4}
|
||||
activeIndex={props.activeStepIndex}
|
||||
buttonText="general.done"
|
||||
buttonText="admin_console.general.done"
|
||||
onButtonClick={props.onComplete}
|
||||
>
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@ await client.signOut()
|
|||
subtitle="3 steps"
|
||||
index={4}
|
||||
activeIndex={props.activeStepIndex}
|
||||
buttonText="general.done"
|
||||
buttonText="admin_console.general.done"
|
||||
onButtonClick={props.onComplete}
|
||||
>
|
||||
|
||||
|
|
|
@ -183,7 +183,7 @@ export default SignOutButton;
|
|||
subtitle="3 steps"
|
||||
index={4}
|
||||
activeIndex={props.activeStepIndex}
|
||||
buttonText="general.done"
|
||||
buttonText="admin_console.general.done"
|
||||
onButtonClick={props.onComplete}
|
||||
>
|
||||
|
||||
|
|
|
@ -178,7 +178,7 @@ const onClickSignOut = () => signOut('http://localhost:1234');
|
|||
subtitle="3 steps"
|
||||
index={4}
|
||||
activeIndex={props.activeStepIndex}
|
||||
buttonText="general.done"
|
||||
buttonText="admin_console.general.done"
|
||||
onButtonClick={props.onComplete}
|
||||
>
|
||||
|
||||
|
|
|
@ -178,7 +178,7 @@ const onClickSignOut = () => signOut('http://localhost:1234');
|
|||
subtitle="3 steps"
|
||||
index={4}
|
||||
activeIndex={props.activeStepIndex}
|
||||
buttonText="general.done"
|
||||
buttonText="admin_console.general.done"
|
||||
onButtonClick={props.onComplete}
|
||||
>
|
||||
|
||||
|
|
|
@ -24,10 +24,10 @@ export type ConfirmModalProps = {
|
|||
const ConfirmModal = ({
|
||||
children,
|
||||
className,
|
||||
title = 'form.confirm',
|
||||
title = 'general.reminder',
|
||||
confirmButtonType = 'danger',
|
||||
confirmButtonText = 'general.confirm',
|
||||
cancelButtonText = 'general.cancel',
|
||||
confirmButtonText = 'admin_console.general.confirm',
|
||||
cancelButtonText = 'admin_console.general.cancel',
|
||||
isOpen,
|
||||
onCancel,
|
||||
onConfirm,
|
||||
|
|
|
@ -13,16 +13,16 @@ type Props = {
|
|||
variant?: 'text' | 'contained' | 'border' | 'icon';
|
||||
};
|
||||
|
||||
type CopyState = TFuncKey<'translation', 'admin_console.copy'>;
|
||||
type CopyState = TFuncKey<'translation', 'admin_console.general'>;
|
||||
|
||||
const CopyToClipboard = ({ value, className, variant = 'contained' }: Props) => {
|
||||
const copyIconReference = useRef<HTMLDivElement>(null);
|
||||
const [copyState, setCopyState] = useState<CopyState>('pending');
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.copy' });
|
||||
const [copyState, setCopyState] = useState<CopyState>('copy');
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.general' });
|
||||
|
||||
useEffect(() => {
|
||||
copyIconReference.current?.addEventListener('mouseleave', () => {
|
||||
setCopyState('pending');
|
||||
setCopyState('copy');
|
||||
});
|
||||
}, []);
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ const FormField = ({ title, children, isRequired, className, tooltip }: Props) =
|
|||
</div>
|
||||
)}
|
||||
<Spacer />
|
||||
{isRequired && <div className={styles.required}>{t('admin_console.form.required')}</div>}
|
||||
{isRequired && <div className={styles.required}>{t('admin_console.general.required')}</div>}
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
|
|
|
@ -84,11 +84,11 @@ const MultiTextInput = ({ title, value, onChange, onKeyPress, error }: Props) =>
|
|||
</div>
|
||||
))}
|
||||
<div className={classNames(textButtonStyles.button, styles.addAnother)} onClick={handleAdd}>
|
||||
{t('admin_console.form.add_another')}
|
||||
{t('admin_console.general.add_another')}
|
||||
</div>
|
||||
<ConfirmModal
|
||||
isOpen={deleteFieldIndex !== undefined}
|
||||
confirmButtonText="admin_console.form.delete"
|
||||
confirmButtonText="admin_console.general.delete"
|
||||
onCancel={() => {
|
||||
setDeleteFieldIndex(undefined);
|
||||
}}
|
||||
|
@ -99,7 +99,7 @@ const MultiTextInput = ({ title, value, onChange, onKeyPress, error }: Props) =>
|
|||
}
|
||||
}}
|
||||
>
|
||||
{t('admin_console.form.deletion_confirmation', { title: t(title) })}
|
||||
{t('admin_console.general.deletion_confirmation', { title: t(title) })}
|
||||
</ConfirmModal>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -40,9 +40,14 @@ const Search = ({ defaultValue = '', isClearable = false, onSearch, onClearSearc
|
|||
onKeyPress={handleSearchKeyPress}
|
||||
/>
|
||||
</div>
|
||||
<Button title="general.search" onClick={handleClick} />
|
||||
<Button title="admin_console.general.search" onClick={handleClick} />
|
||||
{isClearable && (
|
||||
<Button size="small" type="plain" title="general.clear_result" onClick={onClearSearch} />
|
||||
<Button
|
||||
size="small"
|
||||
type="plain"
|
||||
title="admin_console.general.clear_result"
|
||||
onClick={onClearSearch}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -31,7 +31,7 @@ const TableError = ({ title, content, onRetry, columns }: Props) => {
|
|||
<div className={styles.content}>
|
||||
{content ?? t('admin_console.errors.unknown_server_error')}
|
||||
</div>
|
||||
{onRetry && <Button title="general.retry" onClick={onRetry} />}
|
||||
{onRetry && <Button title="admin_console.general.retry" onClick={onRetry} />}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -25,7 +25,9 @@ type Props = {
|
|||
};
|
||||
|
||||
const Transfer = ({ title, datasource, value = [], footer, onChange }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { t } = useTranslation(undefined, {
|
||||
keyPrefix: 'admin_console',
|
||||
});
|
||||
|
||||
const selectedItems = useMemo(() => {
|
||||
return value
|
||||
|
|
|
@ -30,7 +30,7 @@ const Step = ({
|
|||
subtitle,
|
||||
index,
|
||||
activeIndex,
|
||||
buttonText = 'general.next',
|
||||
buttonText = 'admin_console.general.next',
|
||||
buttonHtmlType = 'button',
|
||||
isLoading,
|
||||
onButtonClick,
|
||||
|
|
|
@ -118,7 +118,7 @@ const UriInputField = ({ appId, name, title, isSingle = false }: Props) => {
|
|||
className={styles.saveButton}
|
||||
disabled={!isDirty}
|
||||
isLoading={isSubmitting}
|
||||
title="general.save"
|
||||
title="admin_console.general.save"
|
||||
type="primary"
|
||||
onClick={handleSubmit(async () => onSubmit(value))}
|
||||
/>
|
||||
|
|
|
@ -42,19 +42,15 @@ const DeleteForm = ({ id, name, onClose }: Props) => {
|
|||
|
||||
return (
|
||||
<ModalLayout
|
||||
title="api_resource_details.reminder"
|
||||
title="general.reminder"
|
||||
footer={
|
||||
<>
|
||||
<Button
|
||||
type="outline"
|
||||
title="admin_console.api_resource_details.cancel"
|
||||
onClick={onClose}
|
||||
/>
|
||||
<Button type="outline" title="admin_console.general.cancel" onClick={onClose} />
|
||||
<Button
|
||||
disabled={inputMismatched}
|
||||
isLoading={loading}
|
||||
type="danger"
|
||||
title="admin_console.api_resource_details.delete"
|
||||
title="admin_console.general.delete"
|
||||
onClick={handleDelete}
|
||||
/>
|
||||
</>
|
||||
|
|
|
@ -77,7 +77,7 @@ const ApiResourceDetails = () => {
|
|||
.patch(`/api/resources/${data.id}`, { json: formData })
|
||||
.json<Resource>();
|
||||
void mutate(updatedApiResource);
|
||||
toast.success(t('api_resource_details.save_success'));
|
||||
toast.success(t('general.saved'));
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -104,7 +104,7 @@ const ApiResourceDetails = () => {
|
|||
<div className={styles.operations}>
|
||||
<ActionMenu
|
||||
buttonProps={{ icon: <More className={styles.moreIcon} />, size: 'large' }}
|
||||
title={t('api_resource_details.more_options')}
|
||||
title={t('general.more_options')}
|
||||
>
|
||||
<ActionMenuItem
|
||||
icon={<Delete />}
|
||||
|
@ -113,7 +113,7 @@ const ApiResourceDetails = () => {
|
|||
setIsDeleteFormOpen(true);
|
||||
}}
|
||||
>
|
||||
{t('api_resource_details.options_delete')}
|
||||
{t('general.delete')}
|
||||
</ActionMenuItem>
|
||||
</ActionMenu>
|
||||
<Modal
|
||||
|
@ -134,7 +134,7 @@ const ApiResourceDetails = () => {
|
|||
</Card>
|
||||
<Card className={classNames(styles.body, detailsStyles.body)}>
|
||||
<TabNav>
|
||||
<TabNavItem href={location.pathname}>{t('api_resource_details.settings')}</TabNavItem>
|
||||
<TabNavItem href={location.pathname}>{t('general.settings_nav')}</TabNavItem>
|
||||
</TabNav>
|
||||
<form className={classNames(styles.form, detailsStyles.body)} onSubmit={onSubmit}>
|
||||
<div className={styles.fields}>
|
||||
|
@ -166,7 +166,7 @@ const ApiResourceDetails = () => {
|
|||
isLoading={isSubmitting}
|
||||
htmlType="submit"
|
||||
type="primary"
|
||||
title="admin_console.api_resource_details.save_changes"
|
||||
title="admin_console.general.save_changes"
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -42,19 +42,15 @@ const DeleteForm = ({ id, name, onClose }: Props) => {
|
|||
|
||||
return (
|
||||
<ModalLayout
|
||||
title="application_details.reminder"
|
||||
title="general.reminder"
|
||||
footer={
|
||||
<>
|
||||
<Button
|
||||
type="outline"
|
||||
title="admin_console.application_details.cancel"
|
||||
onClick={onClose}
|
||||
/>
|
||||
<Button type="outline" title="admin_console.general.cancel" onClick={onClose} />
|
||||
<Button
|
||||
disabled={inputMismatched}
|
||||
isLoading={loading}
|
||||
type="danger"
|
||||
title="admin_console.application_details.delete"
|
||||
title="admin_console.general.delete"
|
||||
onClick={handleDelete}
|
||||
/>
|
||||
</>
|
||||
|
|
|
@ -94,7 +94,7 @@ const ApplicationDetails = () => {
|
|||
})
|
||||
.json<Application>();
|
||||
void mutate(updatedApplication);
|
||||
toast.success(t('application_details.save_success'));
|
||||
toast.success(t('general.saved'));
|
||||
});
|
||||
|
||||
const onCloseDrawer = () => {
|
||||
|
@ -138,7 +138,7 @@ const ApplicationDetails = () => {
|
|||
</Drawer>
|
||||
<ActionMenu
|
||||
buttonProps={{ icon: <More className={styles.moreIcon} />, size: 'large' }}
|
||||
title={t('application_details.more_options')}
|
||||
title={t('general.more_options')}
|
||||
>
|
||||
<ActionMenuItem
|
||||
icon={<Delete />}
|
||||
|
@ -147,7 +147,7 @@ const ApplicationDetails = () => {
|
|||
setIsDeleteFormOpen(true);
|
||||
}}
|
||||
>
|
||||
{t('application_details.options_delete')}
|
||||
{t('general.delete')}
|
||||
</ActionMenuItem>
|
||||
</ActionMenu>
|
||||
<Modal
|
||||
|
@ -168,7 +168,7 @@ const ApplicationDetails = () => {
|
|||
<Card className={classNames(styles.body, detailsStyles.body)}>
|
||||
<TabNav>
|
||||
<TabNavItem href={`/applications/${data.id}/settings`}>
|
||||
{t('application_details.settings')}
|
||||
{t('general.settings_nav')}
|
||||
</TabNavItem>
|
||||
<TabNavItem href={`/applications/${data.id}/advanced-settings`}>
|
||||
{t('application_details.advanced_settings')}
|
||||
|
@ -190,7 +190,7 @@ const ApplicationDetails = () => {
|
|||
htmlType="submit"
|
||||
type="primary"
|
||||
size="large"
|
||||
title="admin_console.application_details.save_changes"
|
||||
title="admin_console.general.save_changes"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -49,7 +49,7 @@ const GuideHeader = ({ appName, selectedSdk, isCompact = false, onClose }: Props
|
|||
subtitle="applications.guide.header_description"
|
||||
/>
|
||||
<Spacer />
|
||||
<Button type="plain" size="small" title="general.skip" onClick={onClose} />
|
||||
<Button type="plain" size="small" title="admin_console.general.skip" onClick={onClose} />
|
||||
<Button
|
||||
className={styles.getSampleButton}
|
||||
type="outline"
|
||||
|
|
|
@ -84,7 +84,7 @@ const SdkSelector = ({
|
|||
<div className={styles.buttonWrapper}>
|
||||
<Button
|
||||
type="outline"
|
||||
title="general.next"
|
||||
title="admin_console.general.next"
|
||||
size="large"
|
||||
onClick={() => {
|
||||
setIsFolded(true);
|
||||
|
|
|
@ -77,7 +77,7 @@ const ConnectorDetails = () => {
|
|||
json: { config: configJson },
|
||||
})
|
||||
.json<ConnectorDTO>();
|
||||
toast.success(t('connector_details.save_success'));
|
||||
toast.success(t('general.saved'));
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof SyntaxError) {
|
||||
setSaveError(t('connector_details.save_error_json_parse_error'));
|
||||
|
@ -168,7 +168,7 @@ const ConnectorDetails = () => {
|
|||
</Drawer>
|
||||
<ActionMenu
|
||||
buttonProps={{ icon: <More className={styles.moreIcon} />, size: 'large' }}
|
||||
title={t('connector_details.more_options')}
|
||||
title={t('general.more_options')}
|
||||
>
|
||||
{data.type !== ConnectorType.Social && (
|
||||
<ActionMenuItem
|
||||
|
@ -186,7 +186,7 @@ const ConnectorDetails = () => {
|
|||
</ActionMenuItem>
|
||||
)}
|
||||
<ActionMenuItem icon={<Delete />} type="danger" onClick={handleDelete}>
|
||||
{t('connector_details.options_delete')}
|
||||
{t('general.delete')}
|
||||
</ActionMenuItem>
|
||||
</ActionMenu>
|
||||
<CreateForm
|
||||
|
@ -203,7 +203,7 @@ const ConnectorDetails = () => {
|
|||
<Card className={classNames(styles.body, detailsStyles.body)}>
|
||||
<TabNav>
|
||||
<TabNavItem href={`/connectors/${connectorId ?? ''}`}>
|
||||
{t('connector_details.tab_settings')}
|
||||
{t('general.settings_nav')}
|
||||
</TabNavItem>
|
||||
</TabNav>
|
||||
<div className={styles.main}>
|
||||
|
@ -227,7 +227,7 @@ const ConnectorDetails = () => {
|
|||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
title="admin_console.connector_details.save_changes"
|
||||
title="admin_console.general.save_changes"
|
||||
isLoading={isSubmitting}
|
||||
onClick={handleSave}
|
||||
/>
|
||||
|
|
|
@ -32,7 +32,7 @@ const ConnectorName = ({ type, connectors, onClickSetup }: Props) => {
|
|||
<div className={styles.previewTitle}>
|
||||
<div>{t(connectorTitlePlaceHolder[type])}</div>
|
||||
{type !== ConnectorType.Social && (
|
||||
<Button title="admin_console.connectors.set_up" onClick={onClickSetup} />
|
||||
<Button title="admin_console.general.set_up" onClick={onClickSetup} />
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ const CreateForm = ({ onClose, isOpen: isFormOpen, type }: Props) => {
|
|||
title={cardTitle}
|
||||
footer={
|
||||
<Button
|
||||
title="admin_console.connectors.next"
|
||||
title="admin_console.general.next"
|
||||
type="primary"
|
||||
disabled={!activeConnectorId}
|
||||
onClick={() => {
|
||||
|
@ -108,7 +108,7 @@ const CreateForm = ({ onClose, isOpen: isFormOpen, type }: Props) => {
|
|||
value={id}
|
||||
isDisabled={connectors.every(({ enabled }) => enabled)}
|
||||
className={styles.connector}
|
||||
disabledLabel="connectors.added"
|
||||
disabledLabel="general.added"
|
||||
>
|
||||
<div className={styles.logo}>
|
||||
<img src={logo} />
|
||||
|
|
|
@ -69,7 +69,7 @@ const Guide = ({ connector, onClose }: Props) => {
|
|||
});
|
||||
|
||||
onClose();
|
||||
toast.success(t('connector_details.save_success'));
|
||||
toast.success(t('general.saved'));
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof SyntaxError) {
|
||||
toast.error(t('connector_details.save_error_json_parse_error'));
|
||||
|
|
|
@ -60,7 +60,7 @@ const useGetStartedMetadata = ({ checkDemoAppExists }: Props) => {
|
|||
title: 'get_started.card1_title',
|
||||
subtitle: 'get_started.card1_subtitle',
|
||||
icon: isLightMode ? CheckDemo : CheckDemoDark,
|
||||
buttonText: 'general.check_out',
|
||||
buttonText: 'admin_console.general.check_out',
|
||||
isComplete: settings?.checkDemo,
|
||||
isHidden: hideDemo,
|
||||
onClick: async () => {
|
||||
|
@ -73,7 +73,7 @@ const useGetStartedMetadata = ({ checkDemoAppExists }: Props) => {
|
|||
title: 'get_started.card2_title',
|
||||
subtitle: 'get_started.card2_subtitle',
|
||||
icon: isLightMode ? CreateApp : CreateAppDark,
|
||||
buttonText: 'general.create',
|
||||
buttonText: 'admin_console.general.create',
|
||||
isComplete: settings?.createApplication,
|
||||
onClick: () => {
|
||||
navigate('/applications/create');
|
||||
|
@ -84,7 +84,7 @@ const useGetStartedMetadata = ({ checkDemoAppExists }: Props) => {
|
|||
title: 'get_started.card3_title',
|
||||
subtitle: 'get_started.card3_subtitle',
|
||||
icon: isLightMode ? Customize : CustomizeDark,
|
||||
buttonText: 'general.customize',
|
||||
buttonText: 'admin_console.general.customize',
|
||||
isComplete: settings?.customizeSignInExperience,
|
||||
onClick: () => {
|
||||
navigate('/sign-in-experience');
|
||||
|
@ -95,7 +95,7 @@ const useGetStartedMetadata = ({ checkDemoAppExists }: Props) => {
|
|||
title: 'get_started.card4_title',
|
||||
subtitle: 'get_started.card4_subtitle',
|
||||
icon: isLightMode ? Passwordless : PasswordlessDark,
|
||||
buttonText: 'general.create',
|
||||
buttonText: 'admin_console.general.create',
|
||||
isComplete: settings?.configurePasswordless,
|
||||
onClick: () => {
|
||||
navigate('/connectors');
|
||||
|
@ -106,7 +106,7 @@ const useGetStartedMetadata = ({ checkDemoAppExists }: Props) => {
|
|||
title: 'get_started.card5_title',
|
||||
subtitle: 'get_started.card5_subtitle',
|
||||
icon: isLightMode ? OneClick : OneClickDark,
|
||||
buttonText: 'general.set_up',
|
||||
buttonText: 'admin_console.general.set_up',
|
||||
onClick: () => {
|
||||
navigate('/connectors/social');
|
||||
},
|
||||
|
@ -116,7 +116,7 @@ const useGetStartedMetadata = ({ checkDemoAppExists }: Props) => {
|
|||
title: 'get_started.card6_title',
|
||||
subtitle: 'get_started.card6_subtitle',
|
||||
icon: isLightMode ? FurtherReadings : FurtherReadingsDark,
|
||||
buttonText: 'general.check_out',
|
||||
buttonText: 'admin_console.general.check_out',
|
||||
isComplete: settings?.checkFurtherReadings,
|
||||
onClick: () => {
|
||||
void updateSettings({ checkFurtherReadings: true });
|
||||
|
|
|
@ -69,7 +69,6 @@ const GetStarted = () => {
|
|||
)
|
||||
)}
|
||||
<ConfirmModal
|
||||
title="get_started.confirm"
|
||||
isOpen={showConfirmModal}
|
||||
confirmButtonType="primary"
|
||||
confirmButtonText="admin_console.get_started.hide_this"
|
||||
|
|
|
@ -32,7 +32,7 @@ const Settings = () => {
|
|||
}
|
||||
|
||||
await update(formData);
|
||||
toast.success(t('settings.saved'));
|
||||
toast.success(t('general.saved'));
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -102,7 +102,7 @@ const Settings = () => {
|
|||
type="primary"
|
||||
htmlType="submit"
|
||||
size="large"
|
||||
title="general.save_changes"
|
||||
title="admin_console.general.save_changes"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -40,7 +40,7 @@ const ConnectorSetupWarning = ({ method }: Props) => {
|
|||
|
||||
return (
|
||||
<Alert
|
||||
action="admin_console.sign_in_exp.setup_warning.setup"
|
||||
action="admin_console.general.set_up"
|
||||
href={type === ConnectorType.Social ? '/connectors/social' : '/connectors'}
|
||||
>
|
||||
{t('sign_in_exp.setup_warning.no_connector', { context: type.toLowerCase() })}
|
||||
|
|
|
@ -83,7 +83,7 @@ const GuideModal = ({ isOpen, onClose }: Props) => {
|
|||
<div className={styles.separator} />
|
||||
<CardTitle size="small" title="sign_in_exp.title" subtitle="sign_in_exp.description" />
|
||||
<Spacer />
|
||||
<Button type="plain" size="small" title="general.skip" onClick={onClose} />
|
||||
<Button type="plain" size="small" title="admin_console.general.skip" onClick={onClose} />
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
{!preferences.experienceNoticeConfirmed && (
|
||||
|
@ -123,7 +123,7 @@ const GuideModal = ({ isOpen, onClose }: Props) => {
|
|||
isLoading={isSubmitting}
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
title="general.done"
|
||||
title="admin_console.general.done"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -63,7 +63,7 @@ const SignInExperience = () => {
|
|||
.json<SignInExperienceType>();
|
||||
void mutate(updatedData);
|
||||
await updateSettings({ customizeSignInExperience: true });
|
||||
toast.success(t('application_details.save_success'));
|
||||
toast.success(t('general.saved'));
|
||||
};
|
||||
|
||||
const onSubmit = handleSubmit(async (formData) => {
|
||||
|
@ -138,7 +138,7 @@ const SignInExperience = () => {
|
|||
type="primary"
|
||||
size="large"
|
||||
htmlType="submit"
|
||||
title="general.save_changes"
|
||||
title="admin_console.general.save_changes"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -35,7 +35,7 @@ const CreateSuccess = ({ username }: Props) => {
|
|||
await navigator.clipboard.writeText(
|
||||
`User username: ${username}\nInitial password: ${password}`
|
||||
);
|
||||
toast.success(t('copy.copied'));
|
||||
toast.success(t('general.copied'));
|
||||
};
|
||||
|
||||
if (!password) {
|
||||
|
@ -48,12 +48,8 @@ const CreateSuccess = ({ username }: Props) => {
|
|||
title="user_details.created_title"
|
||||
footer={
|
||||
<>
|
||||
<Button title="admin_console.user_details.created_button_close" onClick={handleClose} />
|
||||
<Button
|
||||
type="primary"
|
||||
title="admin_console.user_details.created_button_copy"
|
||||
onClick={handleCopy}
|
||||
/>
|
||||
<Button title="admin_console.general.close" onClick={handleClose} />
|
||||
<Button type="primary" title="admin_console.general.copy" onClick={handleCopy} />
|
||||
</>
|
||||
}
|
||||
className={styles.content}
|
||||
|
|
|
@ -35,18 +35,14 @@ const DeleteForm = ({ id, onClose }: Props) => {
|
|||
|
||||
return (
|
||||
<ModalLayout
|
||||
title="user_details.delete_title"
|
||||
title="general.reminder"
|
||||
footer={
|
||||
<>
|
||||
<Button
|
||||
type="outline"
|
||||
title="admin_console.user_details.delete_cancel"
|
||||
onClick={onClose}
|
||||
/>
|
||||
<Button type="outline" title="admin_console.general.cancel" onClick={onClose} />
|
||||
<Button
|
||||
disabled={loading}
|
||||
type="danger"
|
||||
title="admin_console.user_details.delete_confirm"
|
||||
title="admin_console.general.delete"
|
||||
onClick={handleDelete}
|
||||
/>
|
||||
</>
|
||||
|
|
|
@ -144,7 +144,7 @@ const UserConnectors = ({ userId, connectors, onDelete }: Props) => {
|
|||
)}
|
||||
<ConfirmModal
|
||||
isOpen={deletingConnector !== undefined}
|
||||
confirmButtonText="admin_console.form.delete"
|
||||
confirmButtonText="admin_console.general.delete"
|
||||
onCancel={() => {
|
||||
setDeletingConnector(undefined);
|
||||
}}
|
||||
|
|
|
@ -106,7 +106,7 @@ const UserDetails = () => {
|
|||
|
||||
const updatedUser = await api.patch(`/api/users/${data.id}`, { json: payload }).json<User>();
|
||||
void mutate(updatedUser);
|
||||
toast.success(t('user_details.saved'));
|
||||
toast.success(t('general.saved'));
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -142,7 +142,7 @@ const UserDetails = () => {
|
|||
<div>
|
||||
<ActionMenu
|
||||
buttonProps={{ icon: <More className={styles.moreIcon} />, size: 'large' }}
|
||||
title={t('user_details.more_options')}
|
||||
title={t('general.more_options')}
|
||||
>
|
||||
<ActionMenuItem
|
||||
icon={<Reset />}
|
||||
|
@ -160,7 +160,7 @@ const UserDetails = () => {
|
|||
setIsDeleteFormOpen(true);
|
||||
}}
|
||||
>
|
||||
{t('user_details.menu_delete')}
|
||||
{t('general.delete')}
|
||||
</ActionMenuItem>
|
||||
</ActionMenu>
|
||||
<ReactModal
|
||||
|
@ -191,7 +191,7 @@ const UserDetails = () => {
|
|||
</Card>
|
||||
<Card className={classNames(styles.body, detailsStyles.body)}>
|
||||
<TabNav>
|
||||
<TabNavItem href={`/users/${userId}`}>{t('user_details.tab_settings')}</TabNavItem>
|
||||
<TabNavItem href={`/users/${userId}`}>{t('general.settings_nav')}</TabNavItem>
|
||||
<TabNavItem href={`/users/${userId}/logs`}>{t('user_details.tab_logs')}</TabNavItem>
|
||||
</TabNav>
|
||||
{isLogs ? (
|
||||
|
@ -270,7 +270,7 @@ const UserDetails = () => {
|
|||
isLoading={isSubmitting}
|
||||
htmlType="submit"
|
||||
type="primary"
|
||||
title="admin_console.user_details.save_changes"
|
||||
title="admin_console.general.save_changes"
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -1,43 +1,42 @@
|
|||
/* eslint-disable max-lines */
|
||||
const translation = {
|
||||
general: {
|
||||
placeholder: 'Placeholder',
|
||||
skip: 'Skip',
|
||||
next: 'Next',
|
||||
retry: 'Try again',
|
||||
done: 'Done',
|
||||
search: 'Search',
|
||||
clear_result: 'Clear Results',
|
||||
save: 'Save',
|
||||
save_changes: 'Save Changes',
|
||||
loading: 'Loading...',
|
||||
redirecting: 'Redirecting...',
|
||||
added: 'Added',
|
||||
cancel: 'Cancel',
|
||||
confirm: 'Confirm',
|
||||
check_out: 'Check out',
|
||||
create: 'Create',
|
||||
set_up: 'Set up',
|
||||
customize: 'Customize',
|
||||
},
|
||||
admin_console: {
|
||||
title: 'Admin Console',
|
||||
sign_out: 'Sign Out',
|
||||
profile: 'Profile',
|
||||
admin_user: 'Admin',
|
||||
system_app: 'System',
|
||||
copy: {
|
||||
pending: 'Copy',
|
||||
general: {
|
||||
placeholder: 'Placeholder',
|
||||
skip: 'Skip',
|
||||
next: 'Next',
|
||||
retry: 'Try Again',
|
||||
done: 'Done',
|
||||
search: 'Search',
|
||||
clear_result: 'Clear Results',
|
||||
save: 'Save',
|
||||
save_changes: 'Save Changes',
|
||||
saved: 'Saved!',
|
||||
loading: 'Loading...',
|
||||
redirecting: 'Redirecting...',
|
||||
added: 'Added',
|
||||
cancel: 'Cancel',
|
||||
confirm: 'Confirm',
|
||||
check_out: 'Check Out',
|
||||
create: 'Create',
|
||||
set_up: 'Set Up',
|
||||
customize: 'Customize',
|
||||
reminder: 'Reminder',
|
||||
delete: 'Delete',
|
||||
more_options: 'MORE OPTIONS',
|
||||
close: 'Close',
|
||||
copy: 'Copy',
|
||||
copying: 'Copying',
|
||||
copied: 'Copied',
|
||||
},
|
||||
form: {
|
||||
required: 'Required',
|
||||
add_another: '+ Add Another',
|
||||
confirm: 'Reminder',
|
||||
cancel: 'Cancel',
|
||||
delete: 'Delete',
|
||||
deletion_confirmation: 'Are you sure you want to delete this {{title}}?',
|
||||
settings_nav: 'Settings',
|
||||
},
|
||||
errors: {
|
||||
something_went_wrong: 'Oops! Something went wrong.',
|
||||
|
@ -116,7 +115,6 @@ const translation = {
|
|||
application_details: {
|
||||
back_to_applications: 'Back to Applications',
|
||||
check_help_guide: 'Check Help Guide',
|
||||
settings: 'Settings',
|
||||
advanced_settings: 'Advanced settings',
|
||||
application_name: 'Application name',
|
||||
description: 'Description',
|
||||
|
@ -129,17 +127,10 @@ const translation = {
|
|||
refresh_token_expiration: 'Refresh Token expiration',
|
||||
token_endpoint: 'Token endpoint',
|
||||
user_info_endpoint: 'Userinfo endpoint',
|
||||
save_changes: 'Save Changes',
|
||||
more_options: 'MORE OPTIONS',
|
||||
options_delete: 'Delete',
|
||||
reminder: 'Reminder',
|
||||
delete_description:
|
||||
'This action cannot be undone. It will permanently delete the application. Please enter the application name <span>{{name}}</span> to confirm.',
|
||||
enter_your_application_name: 'Enter your application name',
|
||||
cancel: 'Cancel',
|
||||
delete: 'Delete',
|
||||
application_deleted: 'Application {{name}} has been successfully deleted.',
|
||||
save_success: 'Saved!',
|
||||
redirect_uri_required: 'You must enter at least one redirect URI',
|
||||
},
|
||||
api_resources: {
|
||||
|
@ -152,26 +143,16 @@ const translation = {
|
|||
},
|
||||
api_resource_details: {
|
||||
back_to_api_resources: 'Back to API resources',
|
||||
more_options: 'MORE OPTIONS',
|
||||
options_delete: 'Delete',
|
||||
settings: 'Settings',
|
||||
save_changes: 'Save Changes',
|
||||
token_expiration_time_in_seconds: 'Token expiration time (in seconds)',
|
||||
reminder: 'Reminder',
|
||||
delete_description:
|
||||
'This action cannot be undone. It will permanently delete the API resource. Please enter the api resource name <span>{{name}}</span> to confirm.',
|
||||
enter_your_api_resource_name: 'Enter your API resource name',
|
||||
cancel: 'Cancel',
|
||||
delete: 'Delete',
|
||||
api_resource_deleted: 'The API Resource {{name}} has been successfully deleted',
|
||||
save_success: 'Saved!',
|
||||
},
|
||||
connectors: {
|
||||
title: 'Connectors',
|
||||
subtitle: 'Setup connectors to enable passwordless and social sign in experience',
|
||||
create: 'Add Social Connector',
|
||||
added: 'Added',
|
||||
set_up: 'Set Up',
|
||||
tab_email_sms: 'Email and SMS connectors',
|
||||
tab_social: 'Social connectors',
|
||||
connector_name: 'Connector name',
|
||||
|
@ -180,7 +161,6 @@ const translation = {
|
|||
connector_status_in_use: 'In use',
|
||||
connector_status_not_in_use: 'Not in use',
|
||||
social_connector_eg: 'E.g., Google, Facebook, Github',
|
||||
next: 'Next',
|
||||
save_and_done: 'Save and Done',
|
||||
type: {
|
||||
email: 'Email connector',
|
||||
|
@ -207,11 +187,8 @@ const translation = {
|
|||
connector_details: {
|
||||
back_to_connectors: 'Back to Connectors',
|
||||
check_readme: 'Check README',
|
||||
tab_settings: 'Settings',
|
||||
save_changes: 'Save Changes',
|
||||
save_error_empty_config: 'Please enter config',
|
||||
save_error_json_parse_error: 'Please enter valid JSON',
|
||||
save_success: 'Saved!',
|
||||
send: 'Send',
|
||||
send_error_invalid_format: 'Invalid input',
|
||||
edit_config_label: 'Enter your json here',
|
||||
|
@ -219,11 +196,8 @@ const translation = {
|
|||
test_sms_sender: 'Test your SMS connector',
|
||||
test_message_sent: 'Test message sent!',
|
||||
test_sender_description: 'You will receive a message if your json is rightly configured',
|
||||
options: 'MORE OPTIONS',
|
||||
options_delete: 'Delete',
|
||||
options_change_email: 'Change email connector',
|
||||
options_change_sms: 'Change SMS connector',
|
||||
more_options: 'MORE OPTIONS',
|
||||
connector_deleted: 'The connector has been successfully deleted.',
|
||||
type_email: 'Email connector',
|
||||
type_sms: 'SMS connector',
|
||||
|
@ -236,7 +210,6 @@ const translation = {
|
|||
subtitle_part1: 'A few things you can do to quickly get value of Logto',
|
||||
subtitle_part2: 'I’m done with this set up',
|
||||
hide_this: 'Hide this',
|
||||
confirm: 'Reminder',
|
||||
confirm_message: 'Are you sure you want to hide this page? This action cannot be undone.',
|
||||
card1_title: 'Check out the demo',
|
||||
card1_subtitle: 'Try Logto sign-in experience now to see how it works',
|
||||
|
@ -273,14 +246,8 @@ const translation = {
|
|||
created_guide: 'You can send the following log in information to the user',
|
||||
created_username: 'Username:',
|
||||
created_password: 'Initial password:',
|
||||
created_button_close: 'Close',
|
||||
created_button_copy: 'Copy',
|
||||
more_options: 'MORE OPTIONS',
|
||||
menu_delete: 'Delete',
|
||||
delete_title: 'Reminder',
|
||||
delete_description: 'This action cannot be undone. It will permanently delete the user.',
|
||||
delete_cancel: 'Cancel',
|
||||
delete_confirm: 'Delete',
|
||||
deleted: 'The user has been successfully deleted',
|
||||
reset_password: {
|
||||
title: 'Reset password',
|
||||
|
@ -288,7 +255,6 @@ const translation = {
|
|||
reset_password: 'Reset password',
|
||||
reset_password_success: 'Password has been successfully reset',
|
||||
},
|
||||
tab_settings: 'Settings',
|
||||
tab_logs: 'User logs',
|
||||
field_email: 'Primary email',
|
||||
field_phone: 'Primary phone',
|
||||
|
@ -298,8 +264,6 @@ const translation = {
|
|||
field_custom_data: 'Custom data',
|
||||
field_connectors: 'Social connections',
|
||||
custom_data_invalid: 'Custom data must be a valid JSON',
|
||||
save_changes: 'Save Changes',
|
||||
saved: 'Saved!',
|
||||
connectors: {
|
||||
connectors: 'Connectors',
|
||||
user_id: 'User ID',
|
||||
|
@ -410,7 +374,6 @@ const translation = {
|
|||
},
|
||||
},
|
||||
setup_warning: {
|
||||
setup: 'Set Up',
|
||||
no_connector: '',
|
||||
no_connector_sms:
|
||||
'You haven’t set up a SMS connector yet. Your sign in experience won’t go live until you finish the settings first. ',
|
||||
|
@ -422,7 +385,6 @@ const translation = {
|
|||
'You’ve set up a few social connectors now. Make sure to add some to your sign in experience. Drag and drop to change the order.',
|
||||
},
|
||||
save_alert: {
|
||||
title: 'Reminder',
|
||||
description:
|
||||
'You are changing sign in methods. This will impact some of your users. Are you sure you want to do that?',
|
||||
before: 'Before',
|
||||
|
@ -455,7 +417,6 @@ const translation = {
|
|||
appearance_system: 'Sync with system',
|
||||
appearance_light: 'Light mode',
|
||||
appearance_dark: 'Dark mode',
|
||||
saved: 'Saved!',
|
||||
},
|
||||
dashboard: {
|
||||
title: 'Dashboard',
|
||||
|
|
|
@ -2,44 +2,43 @@
|
|||
import en from './en';
|
||||
|
||||
const translation = {
|
||||
general: {
|
||||
placeholder: '占位符',
|
||||
skip: '跳过',
|
||||
next: '下一步',
|
||||
retry: '重试',
|
||||
done: '完成',
|
||||
search: '搜索',
|
||||
clear_result: '清除结果',
|
||||
save: '保存',
|
||||
save_changes: '保存更改',
|
||||
loading: '读取中...',
|
||||
redirecting: '页面跳转中...',
|
||||
added: '已添加',
|
||||
cancel: '取消',
|
||||
confirm: '确认',
|
||||
check_out: '查看',
|
||||
create: '创建',
|
||||
set_up: '配置',
|
||||
customize: '自定义',
|
||||
},
|
||||
admin_console: {
|
||||
title: '管理控制台',
|
||||
sign_out: '退出登录',
|
||||
profile: '帐户管理',
|
||||
admin_user: '管理员',
|
||||
system_app: '系统应用',
|
||||
copy: {
|
||||
pending: '复制',
|
||||
general: {
|
||||
placeholder: '占位符',
|
||||
skip: '跳过',
|
||||
next: '下一步',
|
||||
retry: '重试',
|
||||
done: '完成',
|
||||
search: '搜索',
|
||||
clear_result: '清除结果',
|
||||
save: '保存',
|
||||
save_changes: '保存更改',
|
||||
saved: '保存成功!',
|
||||
loading: '读取中...',
|
||||
redirecting: '页面跳转中...',
|
||||
added: '已添加',
|
||||
cancel: '取消',
|
||||
confirm: '确认',
|
||||
check_out: '查看',
|
||||
create: '创建',
|
||||
set_up: '配置',
|
||||
customize: '自定义',
|
||||
reminder: '提示',
|
||||
delete: '删除',
|
||||
more_options: '更多选项',
|
||||
close: '关闭',
|
||||
copy: '复制',
|
||||
copying: '复制中',
|
||||
copied: '已复制',
|
||||
},
|
||||
form: {
|
||||
required: '必填',
|
||||
add_another: '+ 新增',
|
||||
confirm: '提示',
|
||||
cancel: '取消',
|
||||
delete: '删除',
|
||||
deletion_confirmation: '你确定要删除这个 {{title}} 吗?',
|
||||
settings_nav: '设置',
|
||||
},
|
||||
errors: {
|
||||
something_went_wrong: '哎呀,出错了!',
|
||||
|
@ -113,7 +112,6 @@ const translation = {
|
|||
application_details: {
|
||||
back_to_applications: '返回全部应用',
|
||||
check_help_guide: '查看帮助引导',
|
||||
settings: '设置',
|
||||
advanced_settings: '高级设置',
|
||||
application_name: '应用名称',
|
||||
description: '描述',
|
||||
|
@ -126,17 +124,10 @@ const translation = {
|
|||
refresh_token_expiration: 'Refresh Token 过期时间',
|
||||
token_endpoint: 'Token endpoint',
|
||||
user_info_endpoint: 'UserInfo endpoint',
|
||||
save_changes: '保存变更',
|
||||
more_options: '更多选项',
|
||||
options_delete: '删除',
|
||||
reminder: '提示',
|
||||
delete_description:
|
||||
'本操作会永久性地删除该应用,且不可撤销。输入 <span>{{name}}</span> 确认。',
|
||||
enter_your_application_name: '输入你的应用名称',
|
||||
cancel: '取消',
|
||||
delete: '删除',
|
||||
application_deleted: '应用 {{name}} 成功删除.',
|
||||
save_success: '已保存!',
|
||||
redirect_uri_required: '至少需要输入一个 Redirect URL',
|
||||
},
|
||||
api_resources: {
|
||||
|
@ -149,26 +140,16 @@ const translation = {
|
|||
},
|
||||
api_resource_details: {
|
||||
back_to_api_resources: '返回 API 资源',
|
||||
more_options: '更多选项',
|
||||
options_delete: '删除',
|
||||
settings: '设置',
|
||||
save_changes: '保存变更',
|
||||
token_expiration_time_in_seconds: 'Token 过期时间(秒)',
|
||||
reminder: '提示',
|
||||
delete_description:
|
||||
'本操作会永久性地删除该 API 资源,且不可撤销。输入 API 资源名称 <span>{{name}}</span> 确认。',
|
||||
enter_your_api_resource_name: '输入 API 资源名称',
|
||||
cancel: '取消',
|
||||
delete: '删除',
|
||||
api_resource_deleted: ' API 资源 {{name}} 已删除.',
|
||||
save_success: '已保存!',
|
||||
},
|
||||
connectors: {
|
||||
title: '连接器',
|
||||
subtitle: '设置连接器,开启无密码和社交登录',
|
||||
create: '添加社交连接器',
|
||||
added: '已添加',
|
||||
set_up: '设置',
|
||||
tab_email_sms: '短信和邮件连接器',
|
||||
tab_social: '社交连接器',
|
||||
connector_name: '连接器名称',
|
||||
|
@ -177,7 +158,6 @@ const translation = {
|
|||
connector_status_in_use: '使用中',
|
||||
connector_status_not_in_use: '未使用',
|
||||
social_connector_eg: '如: 微信登录,支付宝登录',
|
||||
next: '下一步',
|
||||
save_and_done: '保存并完成',
|
||||
type: {
|
||||
email: '邮件连接器',
|
||||
|
@ -204,11 +184,8 @@ const translation = {
|
|||
connector_details: {
|
||||
back_to_connectors: '返回连接器',
|
||||
check_readme: '查看 README',
|
||||
tab_settings: '设置',
|
||||
save_changes: '保存变更',
|
||||
save_error_empty_config: '请输入配置内容',
|
||||
save_error_json_parse_error: '请输入符合 JSON 格式的配置',
|
||||
save_success: '保存成功',
|
||||
send: '发送',
|
||||
send_error_invalid_format: '无效输入',
|
||||
edit_config_label: '请在此输入你的 JSON 配置',
|
||||
|
@ -216,11 +193,8 @@ const translation = {
|
|||
test_sms_sender: '测试你的短信连接器',
|
||||
test_message_sent: '测试信息已发送!',
|
||||
test_sender_description: '如果你的 JSON 配置正确,你会收到一条测试消息。',
|
||||
options: '更多选项',
|
||||
options_delete: '删除',
|
||||
options_change_email: '更换邮件连接器',
|
||||
options_change_sms: '更换短信连接器',
|
||||
more_options: '更多选项',
|
||||
connector_deleted: '成功删除连接器',
|
||||
type_email: '邮件连接器',
|
||||
type_sms: '短信连接器',
|
||||
|
@ -233,7 +207,6 @@ const translation = {
|
|||
subtitle_part1: '下列是一些可以快速上手的操作,通过这些,你可以更好地感受 Logto 的价值',
|
||||
subtitle_part2: '我已经完成了这些设置',
|
||||
hide_this: '隐藏引导',
|
||||
confirm: '提示',
|
||||
confirm_message: '你确认要隐藏该页面吗? 本操作将无法恢复。',
|
||||
card1_title: '看看 Demo',
|
||||
card1_subtitle: '来体验 Logto 登录吧',
|
||||
|
@ -267,14 +240,8 @@ const translation = {
|
|||
created_guide: '你可以将以下登录信息发送给用户',
|
||||
created_username: '用户名:',
|
||||
created_password: '初始密码:',
|
||||
created_button_close: '关闭',
|
||||
created_button_copy: '复制',
|
||||
more_options: '更多选项',
|
||||
menu_delete: '删除用户',
|
||||
delete_title: '提示',
|
||||
delete_description: '本操作将永久删除该用户,且无法撤销。',
|
||||
delete_cancel: '取消',
|
||||
delete_confirm: '删除',
|
||||
deleted: '用户已成功删除!',
|
||||
reset_password: {
|
||||
title: '重置密码',
|
||||
|
@ -282,7 +249,6 @@ const translation = {
|
|||
reset_password: '重置密码',
|
||||
reset_password_success: '密码已成功重置',
|
||||
},
|
||||
tab_settings: '设置',
|
||||
tab_logs: '用户日志',
|
||||
field_email: '主要邮箱',
|
||||
field_phone: '主要手机号码',
|
||||
|
@ -292,8 +258,6 @@ const translation = {
|
|||
field_custom_data: '自定义数据',
|
||||
field_connectors: '社交帐号',
|
||||
custom_data_invalid: '自定义数据必须是有效的 JSON',
|
||||
save_changes: '保存变更',
|
||||
saved: '保存成功!',
|
||||
connectors: {
|
||||
connectors: '连接器',
|
||||
user_id: '用户ID',
|
||||
|
@ -400,7 +364,6 @@ const translation = {
|
|||
},
|
||||
},
|
||||
setup_warning: {
|
||||
setup: '设置',
|
||||
no_connector: '',
|
||||
no_connector_sms: '你还没有设置 SMS 连接器。你需完成设置后登录体验才会生效。',
|
||||
no_connector_email: '你还没有设置 email 连接器。你需完成设置后登录体验才会生效。',
|
||||
|
@ -409,7 +372,6 @@ const translation = {
|
|||
'你已经成功设置了一些社会化连接器。确认添加一些到你的登录体验。你可以拖拽改变他们的顺序。',
|
||||
},
|
||||
save_alert: {
|
||||
title: '提示',
|
||||
description: '你正在修改登录方式,这可能会影响部分用户。是否继续保存修改?',
|
||||
before: '修改前',
|
||||
after: '修改后',
|
||||
|
@ -441,7 +403,6 @@ const translation = {
|
|||
appearance_system: '跟随系统',
|
||||
appearance_light: '浅色模式',
|
||||
appearance_dark: '深色模式',
|
||||
saved: '已保存!',
|
||||
},
|
||||
dashboard: {
|
||||
title: '仪表盘',
|
||||
|
|
Loading…
Add table
Reference in a new issue