chore: update zh i18n

This commit is contained in:
Steven 2023-09-30 09:03:52 +08:00
parent 7795b17fd1
commit 690e14e4ed
11 changed files with 113 additions and 46 deletions

View File

@ -11,7 +11,14 @@
"language": "Language", "language": "Language",
"search": "Search", "search": "Search",
"email": "Email", "email": "Email",
"password": "Password" "password": "Password",
"account": "Account"
},
"auth": {
"sign-in": "Sign in",
"sign-up": "Sign up",
"sign-out": "Sign out",
"create-your-account": "Create your account"
}, },
"analytics": { "analytics": {
"self": "Analytics", "self": "Analytics",
@ -38,5 +45,30 @@
"description": "Visible to everyone on the internet" "description": "Visible to everyone on the internet"
} }
} }
},
"user": {
"self": "User",
"nickname": "Nickname",
"email": "Email",
"role": "Role",
"profile": "Profile",
"action": {
"add-user": "Add user"
}
},
"settings": {
"self": "Setting",
"preference": {
"self": "Preference",
"color-theme": "Color theme"
},
"workspace": {
"self": "Workspace settings",
"custom-style": "Custom style",
"enable-user-signup": {
"self": "Enable user signup",
"description": "Once enabled, other users can signup."
}
}
} }
} }

View File

@ -11,7 +11,14 @@
"language": "语言", "language": "语言",
"search": "搜索", "search": "搜索",
"email": "邮箱", "email": "邮箱",
"password": "密码" "password": "密码",
"account": "账号"
},
"auth": {
"sign-in": "登录",
"sign-up": "注册",
"sign-out": "退出登录",
"create-your-account": "创建账号"
}, },
"analytics": { "analytics": {
"self": "分析", "self": "分析",
@ -38,5 +45,30 @@
"description": "对任何人可见" "description": "对任何人可见"
} }
} }
},
"user": {
"self": "用户",
"nickname": "昵称",
"email": "邮箱",
"role": "角色",
"profile": "账号",
"action": {
"add-user": "添加用户"
}
},
"settings": {
"self": "设置",
"preference": {
"self": "偏好设置",
"color-theme": "主题"
},
"workspace": {
"self": "系统设置",
"custom-style": "自定义样式",
"enable-user-signup": {
"self": "启用用户注册",
"description": "允许其他用户注册新账号"
}
}
} }
} }

View File

@ -1,5 +1,6 @@
import { Avatar } from "@mui/joy"; import { Avatar } from "@mui/joy";
import { useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import useWorkspaceStore from "@/stores/v1/workspace"; import useWorkspaceStore from "@/stores/v1/workspace";
import { PlanType } from "@/types/proto/api/v2/subscription_service"; import { PlanType } from "@/types/proto/api/v2/subscription_service";
@ -10,6 +11,7 @@ import Icon from "./Icon";
import Dropdown from "./common/Dropdown"; import Dropdown from "./common/Dropdown";
const Header: React.FC = () => { const Header: React.FC = () => {
const { t } = useTranslation();
const workspaceStore = useWorkspaceStore(); const workspaceStore = useWorkspaceStore();
const currentUser = useUserStore().getCurrentUser(); const currentUser = useUserStore().getCurrentUser();
const [showAboutDialog, setShowAboutDialog] = useState<boolean>(false); const [showAboutDialog, setShowAboutDialog] = useState<boolean>(false);
@ -52,27 +54,27 @@ const Header: React.FC = () => {
to="/setting/general" to="/setting/general"
className="w-full px-2 flex flex-row justify-start items-center text-left dark:text-gray-400 leading-8 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-800 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:opacity-60" className="w-full px-2 flex flex-row justify-start items-center text-left dark:text-gray-400 leading-8 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-800 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:opacity-60"
> >
<Icon.User className="w-4 h-auto mr-2" /> Profile <Icon.User className="w-4 h-auto mr-2" /> {t("user.profile")}
</Link> </Link>
{isAdmin && ( {isAdmin && (
<Link <Link
to="/setting/workspace" to="/setting/workspace"
className="w-full px-2 flex flex-row justify-start items-center text-left dark:text-gray-400 leading-8 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-800 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:opacity-60" className="w-full px-2 flex flex-row justify-start items-center text-left dark:text-gray-400 leading-8 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-800 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:opacity-60"
> >
<Icon.Settings className="w-4 h-auto mr-2" /> Setting <Icon.Settings className="w-4 h-auto mr-2" /> {t("settings.self")}
</Link> </Link>
)} )}
<button <button
className="w-full px-2 flex flex-row justify-start items-center text-left dark:text-gray-400 leading-8 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-800 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:opacity-60" className="w-full px-2 flex flex-row justify-start items-center text-left dark:text-gray-400 leading-8 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-800 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:opacity-60"
onClick={() => setShowAboutDialog(true)} onClick={() => setShowAboutDialog(true)}
> >
<Icon.Info className="w-4 h-auto mr-2" /> About <Icon.Info className="w-4 h-auto mr-2" /> {t("common.about")}
</button> </button>
<button <button
className="w-full px-2 flex flex-row justify-start items-center text-left dark:text-gray-400 leading-8 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-800 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:opacity-60" className="w-full px-2 flex flex-row justify-start items-center text-left dark:text-gray-400 leading-8 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-800 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:opacity-60"
onClick={() => handleSignOutButtonClick()} onClick={() => handleSignOutButtonClick()}
> >
<Icon.LogOut className="w-4 h-auto mr-2" /> Sign out <Icon.LogOut className="w-4 h-auto mr-2" /> {t("auth.sign-out")}
</button> </button>
</> </>
} }

View File

@ -15,7 +15,7 @@ const AccountSection: React.FC = () => {
return ( return (
<> <>
<div className="w-full flex flex-col justify-start items-start gap-y-2"> <div className="w-full flex flex-col justify-start items-start gap-y-2">
<p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">Account</p> <p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">{t("common.account")}</p>
<p className="flex flex-row justify-start items-center mt-2 dark:text-gray-400"> <p className="flex flex-row justify-start items-center mt-2 dark:text-gray-400">
<span className="text-xl">{currentUser.nickname}</span> <span className="text-xl">{currentUser.nickname}</span>
{isAdmin && <span className="ml-2 bg-blue-600 text-white px-2 leading-6 text-sm rounded-full">Admin</span>} {isAdmin && <span className="ml-2 bg-blue-600 text-white px-2 leading-6 text-sm rounded-full">Admin</span>}

View File

@ -1,12 +1,14 @@
import { Button, IconButton } from "@mui/joy"; import { Button, IconButton } from "@mui/joy";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
import useUserStore from "../../stores/v1/user"; import useUserStore from "../../stores/v1/user";
import { showCommonDialog } from "../Alert"; import { showCommonDialog } from "../Alert";
import CreateUserDialog from "../CreateUserDialog"; import CreateUserDialog from "../CreateUserDialog";
import Icon from "../Icon"; import Icon from "../Icon";
const MemberSection = () => { const MemberSection = () => {
const { t } = useTranslation();
const userStore = useUserStore(); const userStore = useUserStore();
const [showCreateUserDialog, setShowCreateUserDialog] = useState<boolean>(false); const [showCreateUserDialog, setShowCreateUserDialog] = useState<boolean>(false);
const [currentEditingUser, setCurrentEditingUser] = useState<User | undefined>(undefined); const [currentEditingUser, setCurrentEditingUser] = useState<User | undefined>(undefined);
@ -43,7 +45,7 @@ const MemberSection = () => {
<div className="w-full"> <div className="w-full">
<div className="sm:flex sm:items-center"> <div className="sm:flex sm:items-center">
<div className="sm:flex-auto"> <div className="sm:flex-auto">
<p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">Users</p> <p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">{t("user.self")}</p>
<p className="mt-2 text-sm text-gray-700 dark:text-gray-600"> <p className="mt-2 text-sm text-gray-700 dark:text-gray-600">
A list of all the users in your workspace including their nickname, email and role. A list of all the users in your workspace including their nickname, email and role.
</p> </p>
@ -57,7 +59,7 @@ const MemberSection = () => {
setCurrentEditingUser(undefined); setCurrentEditingUser(undefined);
}} }}
> >
Add user {t("user.action.add-user")}
</Button> </Button>
</div> </div>
</div> </div>
@ -68,20 +70,20 @@ const MemberSection = () => {
<thead> <thead>
<tr> <tr>
<th scope="col" className="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 dark:text-gray-500"> <th scope="col" className="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 dark:text-gray-500">
Nickname {t("user.nickname")}
</th> </th>
<th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-500"> <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-500">
Email {t("user.email")}
</th> </th>
<th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-500"> <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-500">
Role {t("user.role")}
</th> </th>
<th scope="col" className="relative py-3.5 pl-3 pr-4"> <th scope="col" className="relative py-3.5 pl-3 pr-4">
<span className="sr-only">Edit</span> <span className="sr-only">{t("common.edit")}</span>
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody className="divide-y divide-gray-200"> <tbody className="divide-y divide-gray-200 dark:divide-zinc-800">
{userList.map((user) => ( {userList.map((user) => (
<tr key={user.email}> <tr key={user.email}>
<td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm text-gray-900 dark:text-gray-500">{user.nickname}</td> <td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm text-gray-900 dark:text-gray-500">{user.nickname}</td>

View File

@ -1,6 +1,5 @@
import { Option, Select } from "@mui/joy"; import { Option, Select } from "@mui/joy";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { releaseGuard } from "@/helpers/utils";
import { UserSetting, UserSetting_ColorTheme, UserSetting_Locale } from "@/types/proto/api/v2/user_setting_service"; import { UserSetting, UserSetting_ColorTheme, UserSetting_Locale } from "@/types/proto/api/v2/user_setting_service";
import useUserStore from "../../stores/v1/user"; import useUserStore from "../../stores/v1/user";
import BetaBadge from "../BetaBadge"; import BetaBadge from "../BetaBadge";
@ -61,10 +60,10 @@ const PreferenceSection: React.FC = () => {
return ( return (
<> <>
<div className="w-full flex flex-col justify-start items-start gap-y-2"> <div className="w-full flex flex-col justify-start items-start gap-y-2">
<p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">Preference</p> <p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">{t("settings.preference.self")}</p>
<div className="w-full flex flex-row justify-between items-center"> <div className="w-full flex flex-row justify-between items-center">
<div className="flex flex-row justify-start items-center gap-x-1"> <div className="flex flex-row justify-start items-center gap-x-1">
<span className="dark:text-gray-400">Color Theme</span> <span className="dark:text-gray-400">{t("settings.preference.color-theme")}</span>
</div> </div>
<Select defaultValue={colorTheme} onChange={(_, value) => handleSelectColorTheme(value as UserSetting_ColorTheme)}> <Select defaultValue={colorTheme} onChange={(_, value) => handleSelectColorTheme(value as UserSetting_ColorTheme)}>
{colorThemeOptions.map((option) => { {colorThemeOptions.map((option) => {
@ -76,7 +75,6 @@ const PreferenceSection: React.FC = () => {
})} })}
</Select> </Select>
</div> </div>
{releaseGuard() && (
<div className="w-full flex flex-row justify-between items-center"> <div className="w-full flex flex-row justify-between items-center">
<div className="flex flex-row justify-start items-center gap-x-1"> <div className="flex flex-row justify-start items-center gap-x-1">
<span className="dark:text-gray-400">{t("common.language")}</span> <span className="dark:text-gray-400">{t("common.language")}</span>
@ -92,7 +90,6 @@ const PreferenceSection: React.FC = () => {
})} })}
</Select> </Select>
</div> </div>
)}
</div> </div>
</> </>
); );

View File

@ -2,11 +2,13 @@ import { Button, Checkbox, Textarea } from "@mui/joy";
import { isEqual } from "lodash-es"; import { isEqual } from "lodash-es";
import { useRef, useState } from "react"; import { useRef, useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
import { workspaceServiceClient } from "@/grpcweb"; import { workspaceServiceClient } from "@/grpcweb";
import useWorkspaceStore from "@/stores/v1/workspace"; import useWorkspaceStore from "@/stores/v1/workspace";
import { WorkspaceSetting } from "@/types/proto/api/v2/workspace_service"; import { WorkspaceSetting } from "@/types/proto/api/v2/workspace_service";
const WorkspaceSection: React.FC = () => { const WorkspaceSection: React.FC = () => {
const { t } = useTranslation();
const workspaceStore = useWorkspaceStore(); const workspaceStore = useWorkspaceStore();
const [workspaceSetting, setWorkspaceSetting] = useState<WorkspaceSetting>(workspaceStore.setting); const [workspaceSetting, setWorkspaceSetting] = useState<WorkspaceSetting>(workspaceStore.setting);
const originalWorkspaceSetting = useRef<WorkspaceSetting>(workspaceStore.setting); const originalWorkspaceSetting = useRef<WorkspaceSetting>(workspaceStore.setting);
@ -56,9 +58,9 @@ const WorkspaceSection: React.FC = () => {
return ( return (
<div className="w-full flex flex-col justify-start items-start space-y-4"> <div className="w-full flex flex-col justify-start items-start space-y-4">
<p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">Workspace settings</p> <p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">{t("settings.workspace.self")}</p>
<div className="w-full flex flex-col justify-start items-start"> <div className="w-full flex flex-col justify-start items-start">
<p className="mt-2 dark:text-gray-400">Custom style</p> <p className="mt-2 dark:text-gray-400">{t("settings.workspace.custom-style")}</p>
<Textarea <Textarea
className="w-full mt-2" className="w-full mt-2"
minRows={2} minRows={2}
@ -69,15 +71,15 @@ const WorkspaceSection: React.FC = () => {
</div> </div>
<div className="w-full flex flex-col justify-start items-start"> <div className="w-full flex flex-col justify-start items-start">
<Checkbox <Checkbox
label="Enable user signup" label={t("settings.workspace.enable-user-signup.self")}
checked={workspaceSetting.enableSignup} checked={workspaceSetting.enableSignup}
onChange={(event) => handleEnableSignUpChange(event.target.checked)} onChange={(event) => handleEnableSignUpChange(event.target.checked)}
/> />
<p className="mt-2 text-gray-500">Once enabled, other users can signup.</p> <p className="mt-2 text-gray-500">{t("settings.workspace.enable-user-signup.description")}</p>
</div> </div>
<div> <div>
<Button variant="outlined" color="neutral" disabled={!allowSave} onClick={handleSaveWorkspaceSetting}> <Button variant="outlined" color="neutral" disabled={!allowSave} onClick={handleSaveWorkspaceSetting}>
Save {t("common.save")}
</Button> </Button>
</div> </div>
</div> </div>

View File

@ -4,7 +4,7 @@ import { useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Outlet } from "react-router-dom"; import { Outlet } from "react-router-dom";
import useNavigateTo from "@/hooks/useNavigateTo"; import useNavigateTo from "@/hooks/useNavigateTo";
import { UserSetting_ColorTheme } from "@/types/proto/api/v2/user_setting_service"; import { UserSetting_ColorTheme, UserSetting_Locale } from "@/types/proto/api/v2/user_setting_service";
import Header from "../components/Header"; import Header from "../components/Header";
import useUserStore from "../stores/v1/user"; import useUserStore from "../stores/v1/user";
@ -34,7 +34,7 @@ const Root: React.FC = () => {
return; return;
} }
if (isEqual(currentUserSetting.locale, "LOCALE_ZH")) { if (isEqual(currentUserSetting.locale, UserSetting_Locale.LOCALE_ZH)) {
i18n.changeLanguage("zh"); i18n.changeLanguage("zh");
} else { } else {
i18n.changeLanguage("en"); i18n.changeLanguage("en");

View File

@ -71,7 +71,7 @@ const Home: React.FC = () => {
{loadingState.isLoading ? ( {loadingState.isLoading ? (
<div className="py-12 w-full flex flex-row justify-center items-center opacity-80 dark:text-gray-500"> <div className="py-12 w-full flex flex-row justify-center items-center opacity-80 dark:text-gray-500">
<Icon.Loader className="mr-2 w-5 h-auto animate-spin" /> <Icon.Loader className="mr-2 w-5 h-auto animate-spin" />
loading {t("common.loading")}
</div> </div>
) : orderedShortcutList.length === 0 ? ( ) : orderedShortcutList.length === 0 ? (
<div className="py-16 w-full flex flex-col justify-center items-center text-gray-400"> <div className="py-16 w-full flex flex-col justify-center items-center text-gray-400">

View File

@ -100,7 +100,7 @@ const SignIn: React.FC = () => {
disabled={actionBtnLoadingState.isLoading || !allowConfirm} disabled={actionBtnLoadingState.isLoading || !allowConfirm}
onClick={handleSigninBtnClick} onClick={handleSigninBtnClick}
> >
Sign in {t("auth.sign-in")}
</Button> </Button>
</div> </div>
</form> </form>
@ -108,7 +108,7 @@ const SignIn: React.FC = () => {
<p className="w-full mt-4 text-sm"> <p className="w-full mt-4 text-sm">
<span className="dark:text-gray-500">{"Don't have an account yet?"}</span> <span className="dark:text-gray-500">{"Don't have an account yet?"}</span>
<Link to="/auth/signup" className="cursor-pointer ml-2 text-blue-600 hover:underline"> <Link to="/auth/signup" className="cursor-pointer ml-2 text-blue-600 hover:underline">
Sign up {t("auth.sign-up")}
</Link> </Link>
</p> </p>
)} )}

View File

@ -81,7 +81,7 @@ const SignUp: React.FC = () => {
<img id="logo-img" src="/logo.png" className="w-12 h-auto mr-2 -mt-1" alt="logo" /> <img id="logo-img" src="/logo.png" className="w-12 h-auto mr-2 -mt-1" alt="logo" />
<span className="text-3xl opacity-80 dark:text-gray-500">Slash</span> <span className="text-3xl opacity-80 dark:text-gray-500">Slash</span>
</div> </div>
<p className="w-full text-2xl mt-6 dark:text-gray-500">Create your account</p> <p className="w-full text-2xl mt-6 dark:text-gray-500">{t("auth.create-your-account")}</p>
<form className="w-full mt-4" onSubmit={handleSignupBtnClick}> <form className="w-full mt-4" onSubmit={handleSignupBtnClick}>
<div className={`flex flex-col justify-start items-start w-full ${actionBtnLoadingState.isLoading ? "opacity-80" : ""}`}> <div className={`flex flex-col justify-start items-start w-full ${actionBtnLoadingState.isLoading ? "opacity-80" : ""}`}>
<div className="w-full flex flex-col mb-2"> <div className="w-full flex flex-col mb-2">
@ -112,14 +112,14 @@ const SignUp: React.FC = () => {
disabled={actionBtnLoadingState.isLoading || !allowConfirm} disabled={actionBtnLoadingState.isLoading || !allowConfirm}
onClick={handleSignupBtnClick} onClick={handleSignupBtnClick}
> >
Sign up {t("auth.sign-up")}
</Button> </Button>
</div> </div>
</form> </form>
<p className="w-full mt-4 text-sm"> <p className="w-full mt-4 text-sm">
<span className="dark:text-gray-500">{"Already has an account?"}</span> <span className="dark:text-gray-500">{"Already has an account?"}</span>
<Link to="/auth" className="cursor-pointer ml-2 text-blue-600 hover:underline"> <Link to="/auth" className="cursor-pointer ml-2 text-blue-600 hover:underline">
Sign in {t("auth.sign-in")}
</Link> </Link>
</p> </p>
</div> </div>