mirror of
https://github.com/docmost/docmost.git
synced 2025-11-16 23:11:09 +10:00
feat: wip support i18n
This commit is contained in:
@ -98,7 +98,10 @@
|
|||||||
"Active": "Active",
|
"Active": "Active",
|
||||||
"Add members": "Add members",
|
"Add members": "Add members",
|
||||||
"Search for users": "Search for users",
|
"Search for users": "Search for users",
|
||||||
"No user found": "No user found"
|
"No user found": "No user found",
|
||||||
|
"Add groups": "Add groups",
|
||||||
|
"Search for groups": "Search for groups",
|
||||||
|
"No group found": "No group found"
|
||||||
},
|
},
|
||||||
"space": {
|
"space": {
|
||||||
"Spaces": "Spaces",
|
"Spaces": "Spaces",
|
||||||
|
|||||||
@ -5,6 +5,20 @@
|
|||||||
"No pages yet": "No pages yet",
|
"No pages yet": "No pages yet",
|
||||||
"Failed to load page. An error occurred.": "Failed to load page. An error occurred."
|
"Failed to load page. An error occurred.": "Failed to load page. An error occurred."
|
||||||
},
|
},
|
||||||
|
"role": {
|
||||||
|
"Full access": "Full access",
|
||||||
|
"Has full access to space settings and pages.": "Has full access to space settings and pages.",
|
||||||
|
"Can edit": "Can edit",
|
||||||
|
"Can create and edit pages in space.": "Can create and edit pages in space.",
|
||||||
|
"Can view": "Can view",
|
||||||
|
"Can view pages in space but not edit.": "Can view pages in space but not edit.",
|
||||||
|
"Owner": "Owner",
|
||||||
|
"Can manage workspace": "Can manage workspace",
|
||||||
|
"Admin": "Admin",
|
||||||
|
"Can manage workspace but cannot delete it": "Can manage workspace but cannot delete it",
|
||||||
|
"Member": "Member",
|
||||||
|
"Can become members of groups and spaces in workspace": "Can become members of groups and spaces in workspace"
|
||||||
|
},
|
||||||
"layout": {
|
"layout": {
|
||||||
"Home": "Home",
|
"Home": "Home",
|
||||||
"Workspace": "Workspace",
|
"Workspace": "Workspace",
|
||||||
|
|||||||
@ -98,7 +98,10 @@
|
|||||||
"Active": "活跃",
|
"Active": "活跃",
|
||||||
"Add members": "添加成员",
|
"Add members": "添加成员",
|
||||||
"Search for users": "搜索用户",
|
"Search for users": "搜索用户",
|
||||||
"No user found": "未找到用户"
|
"No user found": "未找到用户",
|
||||||
|
"Add groups": "添加群组",
|
||||||
|
"Search for groups": "搜索群组",
|
||||||
|
"No group found": "未找到群组"
|
||||||
},
|
},
|
||||||
"space": {
|
"space": {
|
||||||
"Spaces": "空间",
|
"Spaces": "空间",
|
||||||
|
|||||||
@ -5,6 +5,20 @@
|
|||||||
"No pages yet": "暂无页面",
|
"No pages yet": "暂无页面",
|
||||||
"Failed to load page. An error occurred.": "加载页面失败。发生错误。"
|
"Failed to load page. An error occurred.": "加载页面失败。发生错误。"
|
||||||
},
|
},
|
||||||
|
"role": {
|
||||||
|
"Full access": "完全访问",
|
||||||
|
"Has full access to space settings and pages": "具有对空间设置和页面的完全访问权限",
|
||||||
|
"Can edit": "可以编辑",
|
||||||
|
"Can create and edit pages in space": "可以在空间中创建和编辑页面",
|
||||||
|
"Can view": "可以查看",
|
||||||
|
"Can view pages in space but not edit": "可以查看空间中的页面但不能编辑",
|
||||||
|
"Owner": "所有者",
|
||||||
|
"Can manage workspace": "可以管理工作区",
|
||||||
|
"Admin": "管理员",
|
||||||
|
"Can manage workspace but cannot delete it": "可以管理工作区但不能删除它",
|
||||||
|
"Member": "成员",
|
||||||
|
"Can become members of groups and spaces in workspace": "可以成为工作区中群组和空间的成员"
|
||||||
|
},
|
||||||
"layout": {
|
"layout": {
|
||||||
"Home": "首页",
|
"Home": "首页",
|
||||||
"Workspace": "工作区",
|
"Workspace": "工作区",
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import React, { forwardRef } from "react";
|
|||||||
import { IconCheck, IconChevronDown } from "@tabler/icons-react";
|
import { IconCheck, IconChevronDown } from "@tabler/icons-react";
|
||||||
import { Group, Text, Menu, Button } from "@mantine/core";
|
import { Group, Text, Menu, Button } from "@mantine/core";
|
||||||
import { IRoleData } from "@/lib/types.ts";
|
import { IRoleData } from "@/lib/types.ts";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
interface RoleButtonProps extends React.ComponentPropsWithoutRef<"button"> {
|
interface RoleButtonProps extends React.ComponentPropsWithoutRef<"button"> {
|
||||||
name: string;
|
name: string;
|
||||||
@ -36,10 +37,14 @@ export default function RoleSelectMenu({
|
|||||||
onChange,
|
onChange,
|
||||||
disabled,
|
disabled,
|
||||||
}: RoleMenuProps) {
|
}: RoleMenuProps) {
|
||||||
|
const { t } = useTranslation("translation", {
|
||||||
|
keyPrefix: "role",
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu withArrow>
|
<Menu withArrow>
|
||||||
<Menu.Target>
|
<Menu.Target>
|
||||||
<RoleButton name={roleName} disabled={disabled} />
|
<RoleButton name={t(roleName)} disabled={disabled} />
|
||||||
</Menu.Target>
|
</Menu.Target>
|
||||||
|
|
||||||
<Menu.Dropdown>
|
<Menu.Dropdown>
|
||||||
@ -50,9 +55,9 @@ export default function RoleSelectMenu({
|
|||||||
>
|
>
|
||||||
<Group flex="1" gap="xs">
|
<Group flex="1" gap="xs">
|
||||||
<div>
|
<div>
|
||||||
<Text size="sm">{item.label}</Text>
|
<Text size="sm">{t(item.label)}</Text>
|
||||||
<Text size="xs" opacity={0.65}>
|
<Text size="xs" opacity={0.65}>
|
||||||
{item.description}
|
{t(item.description)}
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
{item.label === roleName && <IconCheck size={20} />}
|
{item.label === roleName && <IconCheck size={20} />}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { Group, MultiSelect, MultiSelectProps, Text } from "@mantine/core";
|
|||||||
import { useGetGroupsQuery } from "@/features/group/queries/group-query.ts";
|
import { useGetGroupsQuery } from "@/features/group/queries/group-query.ts";
|
||||||
import { IGroup } from "@/features/group/types/group.types.ts";
|
import { IGroup } from "@/features/group/types/group.types.ts";
|
||||||
import { IconUsersGroup } from "@tabler/icons-react";
|
import { IconUsersGroup } from "@tabler/icons-react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
interface MultiGroupSelectProps {
|
interface MultiGroupSelectProps {
|
||||||
onChange: (value: string[]) => void;
|
onChange: (value: string[]) => void;
|
||||||
@ -29,6 +30,9 @@ export function MultiGroupSelect({
|
|||||||
description,
|
description,
|
||||||
mt,
|
mt,
|
||||||
}: MultiGroupSelectProps) {
|
}: MultiGroupSelectProps) {
|
||||||
|
const { t } = useTranslation("settings", {
|
||||||
|
keyPrefix: "workspace.group",
|
||||||
|
});
|
||||||
const [searchValue, setSearchValue] = useState("");
|
const [searchValue, setSearchValue] = useState("");
|
||||||
const [debouncedQuery] = useDebouncedValue(searchValue, 500);
|
const [debouncedQuery] = useDebouncedValue(searchValue, 500);
|
||||||
const { data: groups, isLoading } = useGetGroupsQuery({
|
const { data: groups, isLoading } = useGetGroupsQuery({
|
||||||
@ -64,8 +68,8 @@ export function MultiGroupSelect({
|
|||||||
hidePickedOptions
|
hidePickedOptions
|
||||||
maxDropdownHeight={300}
|
maxDropdownHeight={300}
|
||||||
description={description}
|
description={description}
|
||||||
label={label || "Add groups"}
|
label={label || t("Add groups")}
|
||||||
placeholder="Search for groups"
|
placeholder={t("Search for groups")}
|
||||||
mt={mt}
|
mt={mt}
|
||||||
searchable
|
searchable
|
||||||
searchValue={searchValue}
|
searchValue={searchValue}
|
||||||
@ -73,7 +77,7 @@ export function MultiGroupSelect({
|
|||||||
clearable
|
clearable
|
||||||
variant="filled"
|
variant="filled"
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
nothingFoundMessage="No group found"
|
nothingFoundMessage={t("No group found")}
|
||||||
maxValues={50}
|
maxValues={50}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -9,7 +9,7 @@ export const spaceRoleData: IRoleData[] = [
|
|||||||
{
|
{
|
||||||
label: "Can edit",
|
label: "Can edit",
|
||||||
value: SpaceRole.WRITER,
|
value: SpaceRole.WRITER,
|
||||||
description: "Can create and edit pages in space.",
|
description: "Can create and edit pages in space",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Can view",
|
label: "Can view",
|
||||||
|
|||||||
@ -11,9 +11,7 @@ interface Props {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
export function WorkspaceInviteForm({ onClose }: Props) {
|
export function WorkspaceInviteForm({ onClose }: Props) {
|
||||||
const { t } = useTranslation("settings", {
|
const { t } = useTranslation(["settings", "translation"]);
|
||||||
keyPrefix: "workspace.member",
|
|
||||||
});
|
|
||||||
const [emails, setEmails] = useState<string[]>([]);
|
const [emails, setEmails] = useState<string[]>([]);
|
||||||
const [role, setRole] = useState<string | null>(UserRole.MEMBER);
|
const [role, setRole] = useState<string | null>(UserRole.MEMBER);
|
||||||
const [groupIds, setGroupIds] = useState<string[]>([]);
|
const [groupIds, setGroupIds] = useState<string[]>([]);
|
||||||
@ -49,10 +47,10 @@ export function WorkspaceInviteForm({ onClose }: Props) {
|
|||||||
<TagsInput
|
<TagsInput
|
||||||
mt="sm"
|
mt="sm"
|
||||||
description={t(
|
description={t(
|
||||||
"Enter valid email addresses separated by comma or space max_50",
|
"workspace.member.Enter valid email addresses separated by comma or space max_50",
|
||||||
)}
|
)}
|
||||||
label={t("Invite by email")}
|
label={t("workspace.member.Invite by email")}
|
||||||
placeholder={t("enter valid emails addresses")}
|
placeholder={t("workspace.member.enter valid emails addresses")}
|
||||||
variant="filled"
|
variant="filled"
|
||||||
splitChars={[",", " "]}
|
splitChars={[",", " "]}
|
||||||
maxDropdownHeight={200}
|
maxDropdownHeight={200}
|
||||||
@ -62,11 +60,19 @@ export function WorkspaceInviteForm({ onClose }: Props) {
|
|||||||
|
|
||||||
<Select
|
<Select
|
||||||
mt="sm"
|
mt="sm"
|
||||||
description={t("Select role to assign to all invited members")}
|
description={t(
|
||||||
label={t("Select role")}
|
"workspace.member.Select role to assign to all invited members",
|
||||||
placeholder={t("Choose a role")}
|
)}
|
||||||
|
label={t("workspace.member.Select role")}
|
||||||
|
placeholder={t("workspace.member.Choose a role")}
|
||||||
variant="filled"
|
variant="filled"
|
||||||
data={userRoleData.filter((role) => role.value !== UserRole.OWNER)}
|
data={userRoleData
|
||||||
|
.filter((role) => role.value !== UserRole.OWNER)
|
||||||
|
.map((role) => ({
|
||||||
|
...role,
|
||||||
|
label: t(`role.${role.label}`, { ns: "translation" }),
|
||||||
|
description: t(`role.${role.description}`, { ns: "translation" }),
|
||||||
|
}))}
|
||||||
defaultValue={UserRole.MEMBER}
|
defaultValue={UserRole.MEMBER}
|
||||||
allowDeselect={false}
|
allowDeselect={false}
|
||||||
checkIconPosition="right"
|
checkIconPosition="right"
|
||||||
@ -76,9 +82,9 @@ export function WorkspaceInviteForm({ onClose }: Props) {
|
|||||||
<MultiGroupSelect
|
<MultiGroupSelect
|
||||||
mt="sm"
|
mt="sm"
|
||||||
description={t(
|
description={t(
|
||||||
"Invited members will be granted access to spaces the groups can access",
|
"workspace.member.Invited members will be granted access to spaces the groups can access",
|
||||||
)}
|
)}
|
||||||
label={t("Add to groups")}
|
label={t("workspace.member.Add to groups")}
|
||||||
onChange={handleGroupSelect}
|
onChange={handleGroupSelect}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -87,7 +93,7 @@ export function WorkspaceInviteForm({ onClose }: Props) {
|
|||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
loading={createInvitationMutation.isPending}
|
loading={createInvitationMutation.isPending}
|
||||||
>
|
>
|
||||||
{t("Send invitation")}
|
{t("workspace.member.Send invitation")}
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -14,7 +14,7 @@ export const userRoleData: IRoleData[] = [
|
|||||||
{
|
{
|
||||||
label: "Member",
|
label: "Member",
|
||||||
value: UserRole.MEMBER,
|
value: UserRole.MEMBER,
|
||||||
description: "Can become members of groups and spaces in workspace.",
|
description: "Can become members of groups and spaces in workspace",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user