Skip to content

Commit

Permalink
feat: settings support multi lang
Browse files Browse the repository at this point in the history
  • Loading branch information
张欢 committed Sep 18, 2024
1 parent 0ad3cf6 commit 97f2f07
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 34 deletions.
8 changes: 5 additions & 3 deletions src/app/[locale]/(protected)/dashboard/settings/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { redirect } from "next/navigation";
import { getTranslations } from "next-intl/server";

import { getCurrentUser } from "@/lib/session";
import { constructMetadata } from "@/lib/utils";
Expand All @@ -8,20 +9,21 @@ import { UserNameForm } from "@/components/forms/user-name-form";
import { UserRoleForm } from "@/components/forms/user-role-form";

export const metadata = constructMetadata({
title: "Settings – FFlow Next",
title: "Settings – FFlow Next",
description: "Configure your account and website settings.",
});

export default async function SettingsPage() {
const user = await getCurrentUser();
const t = await getTranslations("Settings");

if (!user?.id) redirect("/login");

return (
<>
<DashboardHeader
heading="Settings"
text="Manage account and website settings."
heading={t("heading")}
text={t("subheading")}
/>
<div className="divide-y divide-muted pb-10">
<UserNameForm user={{ id: user.id, name: user.name || "" }} />
Expand Down
19 changes: 11 additions & 8 deletions src/components/dashboard/delete-account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import { Button } from "@/components/ui/button";
import { SectionColumns } from "@/components/dashboard/section-columns";
import { useDeleteAccountModal } from "@/components/modals/delete-account-modal";
import { Icons } from "@/components/shared/icons";
import { useTranslations } from 'next-intl';

export function DeleteAccountSection() {
const t = useTranslations('DeleteAccountSection');
const { setShowDeleteAccountModal, DeleteAccountModal } =
useDeleteAccountModal();

Expand All @@ -16,27 +18,28 @@ export function DeleteAccountSection() {
<>
<DeleteAccountModal />
<SectionColumns
title="Delete Account"
description="This is a danger zone - Be careful !"
title={t('title')}
description={t('description')}
>
<div className="flex flex-col gap-4 rounded-xl border border-red-400 p-4 dark:border-red-900">
<div className="flex flex-col gap-2">
<div className="flex items-center gap-2">
<span className="text-[15px] font-medium">Are you sure ?</span>
<span className="text-[15px] font-medium">{t('areYouSure')}</span>

{userPaidPlan ? (
<div className="flex items-center gap-1 rounded-md bg-red-600/10 p-1 pr-2 text-xs font-medium text-red-600 dark:bg-red-500/10 dark:text-red-500">
<div className="m-0.5 rounded-full bg-red-600 p-[3px]">
<Icons.close size={10} className="text-background" />
</div>
Active Subscription
{t('activeSubscription')}
</div>
) : null}
</div>
<div className="text-balance text-sm text-muted-foreground">
Permanently delete your {siteConfig.name} account
{userPaidPlan ? " and your subscription" : ""}. This action cannot
be undone - please proceed with caution.
{t('deleteWarning', {
siteName: siteConfig.name,
subscription: userPaidPlan ? t(' and your subscription') : ''
})}
</div>
</div>
<div className="flex items-center gap-2">
Expand All @@ -46,7 +49,7 @@ export function DeleteAccountSection() {
onClick={() => setShowDeleteAccountModal(true)}
>
<Icons.trash className="mr-2 size-4" />
<span>Delete Account</span>
<span>{t('deleteButton')}</span>
</Button>
</div>
</div>
Expand Down
20 changes: 11 additions & 9 deletions src/components/forms/user-name-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { User } from "@/types";
import { useSession } from "next-auth/react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { useTranslations } from 'next-intl'; // 导入 useTranslations 钩子

import { userNameSchema } from "@/lib/validations/user";
import { Button } from "@/components/ui/button";
Expand All @@ -20,6 +21,7 @@ interface UserNameFormProps {
}

export function UserNameForm({ user }: UserNameFormProps) {
const t = useTranslations('UserNameForm'); // 获取翻译函数
const { update } = useSession();
const [updated, setUpdated] = useState(false);
const [isPending, startTransition] = useTransition();
Expand All @@ -45,33 +47,34 @@ export function UserNameForm({ user }: UserNameFormProps) {
const { status } = await updateUserNameWithId(data);

if (status !== "success") {
toast.error("Something went wrong.", {
description: "Your name was not updated. Please try again.",
toast.error(t('errorTitle'), {
description: t('errorDescription'),
});
} else {
await update();
setUpdated(false);
toast.success("Your name has been updated.");
toast.success(t('successDescription'));
}
});
});

return (
<form onSubmit={onSubmit}>
<SectionColumns
title="Your Name"
description="Please enter a display name you are comfortable with."
title={t('nameLabel')}
description={t('nameDescription')}
>
<div className="flex w-full items-center gap-2">
<Label className="sr-only" htmlFor="name">
Name
{t('nameLabel')}
</Label>
<Input
id="name"
className="flex-1"
size={32}
{...register("name")}
onChange={(e) => checkUpdate(e.target.value)}
placeholder={t('namePlaceholder')}
/>
<Button
type="submit"
Expand All @@ -83,8 +86,7 @@ export function UserNameForm({ user }: UserNameFormProps) {
<Icons.spinner className="size-4 animate-spin" />
) : (
<p>
Save
<span className="hidden sm:inline-flex">&nbsp;Changes</span>
{t('saveChanges')}
</p>
)}
</Button>
Expand All @@ -95,7 +97,7 @@ export function UserNameForm({ user }: UserNameFormProps) {
{errors.name.message}
</p>
)}
<p className="text-[13px] text-muted-foreground">Max 32 characters</p>
<p className="text-[13px] text-muted-foreground">{t('nameDescription')}</p>
</div>
</SectionColumns>
</form>
Expand Down
22 changes: 11 additions & 11 deletions src/components/forms/user-role-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useSession } from "next-auth/react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import { useTranslations } from 'next-intl'; // 导入 useTranslations 钩子

import { userRoleSchema } from "@/lib/validations/user";
import { Button } from "@/components/ui/button";
Expand All @@ -34,6 +35,7 @@ interface UserNameFormProps {
}

export function UserRoleForm({ user }: UserNameFormProps) {
const t = useTranslations('UserRoleForm'); // 获取翻译函数
const { update } = useSession();
const [updated, setUpdated] = useState(false);
const [isPending, startTransition] = useTransition();
Expand All @@ -54,13 +56,13 @@ export function UserRoleForm({ user }: UserNameFormProps) {
const { status } = await updateUserRoleWithId(data);

if (status !== "success") {
toast.error("Something went wrong.", {
description: "Your role was not updated. Please try again.",
toast.error(t('errorTitle'), {
description: t('errorDescription'),
});
} else {
await update();
setUpdated(false);
toast.success("Your role has been updated.");
toast.success(t('successDescription'));
}
});
};
Expand All @@ -69,18 +71,17 @@ export function UserRoleForm({ user }: UserNameFormProps) {
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<SectionColumns
title="Your Role"
description="Select the role what you want for test the app."
title={t('title')}
description={t('description')}
>
<div className="flex w-full items-center gap-2">
<FormField
control={form.control}
name="role"
render={({ field }) => (
<FormItem className="w-full space-y-0">
<FormLabel className="sr-only">Role</FormLabel>
<FormLabel className="sr-only">{t('roleLabel')}</FormLabel>
<Select
// TODO:(FIX) Option value not update. Use useState for the moment
onValueChange={(value: UserRole) => {
setUpdated(user.role !== value);
setRole(value);
Expand All @@ -91,7 +92,7 @@ export function UserRoleForm({ user }: UserNameFormProps) {
>
<FormControl>
<SelectTrigger className="w-full">
<SelectValue placeholder="Select a role" />
<SelectValue placeholder={t('selectPlaceholder')} />
</SelectTrigger>
</FormControl>
<SelectContent>
Expand All @@ -116,15 +117,14 @@ export function UserRoleForm({ user }: UserNameFormProps) {
<Icons.spinner className="size-4 animate-spin" />
) : (
<p>
Save
<span className="hidden sm:inline-flex">&nbsp;Changes</span>
{t('saveChanges')}
</p>
)}
</Button>
</div>
<div className="flex flex-col justify-between p-1">
<p className="text-[13px] text-muted-foreground">
Remove this field on real production
{t('removeInProduction')}
</p>
</div>
</SectionColumns>
Expand Down
32 changes: 32 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -268,5 +268,37 @@
"login": "Login",
"signUp": "Sign Up",
"admin": "Admin"
},
"Settings": {
"heading": "Settings",
"subheading": "Manage account and website settings."
},
"UserNameForm": {
"errorTitle": "Something went wrong",
"errorDescription": "Your name could not be updated. Please try again later.",
"successDescription": "Your name has been updated.",
"nameLabel": "Name",
"namePlaceholder": "Enter your name",
"nameDescription": "This is your public display name. It can be your real name or a pseudonym.",
"saveChanges": "Save changes"
},
"UserRoleForm": {
"title": "Your Role",
"description": "Select the role you want to test the app with.",
"roleLabel": "Role",
"selectPlaceholder": "Select a role",
"saveChanges": "Save Changes",
"errorTitle": "Something went wrong",
"errorDescription": "Your role was not updated. Please try again.",
"successDescription": "Your role has been updated.",
"removeInProduction": "Remove this field in real production"
},
"DeleteAccountSection": {
"title": "Delete Account",
"description": "This is a danger zone - Be careful !",
"areYouSure": "Are you sure ?",
"activeSubscription": "Active Subscription",
"deleteWarning": "Permanently delete your {siteName} account. This action cannot be undone, please proceed with caution.",
"deleteButton": "Delete Account"
}
}
38 changes: 35 additions & 3 deletions src/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@
},
"ordersPage": {
"heading": "",
"subheading": "查看并管理您的最新订单",
"subheading": "看并管理您的最新订单",
"emptyTitle": "暂无订单",
"emptyDescription": "您还没有任何订单。开始订购产品吧。",
"buyProducts": "购买产品"
Expand Down Expand Up @@ -160,7 +160,7 @@
"3": "采用 FFlow Next 后,我们团队的生产力飞速提升。强烈推荐!",
"4": "我对 FFlow Next 的代码质量和清晰文档印象深刻。向团队致敬!",
"5": "FFlow Next 为我提供了必要的工具,以高效地管理用户数据。非常感谢!",
"6": "FFlow Next 在我作为营销经理的角色中发挥了无价的作用。通过 Stripe 的无缝集成,我能够启动具有内置支付功能的定向营销活动,使我们能够更有效地货币化我们的产品和服务。",
"6": "FFlow Next 在我作为营销经理的角色中发挥了无价的作用。通过 Stripe 的无缝集成,我能够启动具有内置支付功能的定向营活动,使我们能够更有效地货币化我们的产品和服务。",
"7": "感谢 FFlow Next, 我能够在创纪录的时间内创建现代和吸引人的用户界面。入门套件为构建时尚和直观的界面提供了坚实的基础, 使我能够更专注于工作的创造性方面。"
}
},
Expand Down Expand Up @@ -221,7 +221,7 @@
},
"InfoLanding": {
"Empower your projects": {
"title": "为您的项目赋能",
"title": "为的项目赋能",
"description": "通过我们的开源 SaaS 平台释放项目的全部潜力。无缝协作,轻松创新,无限扩展。",
"list": {
"Collaborative": {
Expand Down Expand Up @@ -268,5 +268,37 @@
"login": "登录",
"signUp": "注册",
"admin": "管理员"
},
"Settings": {
"heading": "设置",
"subheading": "管理账户和网站设置。"
},
"UserNameForm": {
"errorTitle": "出错了",
"errorDescription": "您的姓名无法更新。请稍后再试。",
"successDescription": "您的姓名已更新。",
"nameLabel": "姓名",
"namePlaceholder": "输入您的姓名",
"nameDescription": "这是您的公开显示姓名。它可以是您的真实姓名或昵称。",
"saveChanges": "保存更改"
},
"UserRoleForm": {
"title": "您的角色",
"description": "选择您想要用于测试应用的角色。",
"roleLabel": "角色",
"selectPlaceholder": "选择一个角色",
"saveChanges": "保存更改",
"errorTitle": "出错了",
"errorDescription": "您的角色未能更新。请重试。",
"successDescription": "您的角色已更新。",
"removeInProduction": "在实际生产环境中移除此字段"
},
"DeleteAccountSection": {
"title": "删除账户",
"description": "这是危险操作 - 请小心操作!",
"areYouSure": "您确定吗?",
"activeSubscription": "活跃订阅",
"deleteWarning": "永久删除您的 {siteName} 账户。此操作无法撤销,请谨慎操作。",
"deleteButton": "删除账户"
}
}

0 comments on commit 97f2f07

Please sign in to comment.