Support I18n (#243)

* feat: support i18n

* feat: wip support i18n

* feat: complete space translation

* feat: complete page translation

* feat: update space translation

* feat: update workspace translation

* feat: update group translation

* feat: update workspace translation

* feat: update page translation

* feat: update user translation

* chore: update pnpm-lock

* feat: add query translation

* refactor: merge to single file

* chore: remove necessary code

* feat: save language to BE

* fix: only load current language

* feat: save language to locale column

* fix: cleanups

* add language menu to preferences page

* new translations

* translate editor

* Translate editor placeholders

* translate space selection component

---------

Co-authored-by: Philip Okugbe <phil@docmost.com>
Co-authored-by: Philip Okugbe <16838612+Philipinho@users.noreply.github.com>
This commit is contained in:
lleohao
2025-01-04 21:17:17 +08:00
committed by GitHub
parent 290b7d9d94
commit 670ee64179
119 changed files with 1672 additions and 649 deletions
@@ -1,20 +1,23 @@
import {Group, Table, Text, Badge, Menu, ActionIcon} from "@mantine/core";
import { Group, Table, Text, Badge, Menu, ActionIcon } from "@mantine/core";
import {
useGroupMembersQuery,
useRemoveGroupMemberMutation,
} from "@/features/group/queries/group-query";
import {useParams} from "react-router-dom";
import { useParams } from "react-router-dom";
import React from "react";
import {IconDots} from "@tabler/icons-react";
import {modals} from "@mantine/modals";
import {CustomAvatar} from "@/components/ui/custom-avatar.tsx";
import { IconDots } from "@tabler/icons-react";
import { modals } from "@mantine/modals";
import { CustomAvatar } from "@/components/ui/custom-avatar.tsx";
import useUserRole from "@/hooks/use-user-role.tsx";
import { useTranslation } from "react-i18next";
import { IUser } from "@/features/user/types/user.types.ts";
export default function GroupMembersList() {
const {groupId} = useParams();
const {data, isLoading} = useGroupMembersQuery(groupId);
const { t } = useTranslation();
const { groupId } = useParams();
const { data, isLoading } = useGroupMembersQuery(groupId);
const removeGroupMember = useRemoveGroupMemberMutation();
const {isAdmin} = useUserRole();
const { isAdmin } = useUserRole();
const onRemove = async (userId: string) => {
const memberToRemove = {
@@ -26,16 +29,17 @@ export default function GroupMembersList() {
const openRemoveModal = (userId: string) =>
modals.openConfirmModal({
title: "Remove group member",
title: t("Remove group member"),
children: (
<Text size="sm">
Are you sure you want to remove this user from the group? The user
will lose access to resources this group has access to.
{t(
"Are you sure you want to remove this user from the group? The user will lose access to resources this group has access to.",
)}
</Text>
),
centered: true,
labels: {confirm: "Delete", cancel: "Cancel"},
confirmProps: {color: "red"},
labels: { confirm: t("Delete"), cancel: t("Cancel") },
confirmProps: { color: "red" },
onConfirm: () => onRemove(userId),
});
@@ -46,18 +50,21 @@ export default function GroupMembersList() {
<Table verticalSpacing="sm">
<Table.Thead>
<Table.Tr>
<Table.Th>User</Table.Th>
<Table.Th>Status</Table.Th>
<Table.Th>{t("User")}</Table.Th>
<Table.Th>{t("Status")}</Table.Th>
<Table.Th></Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{data?.items.map((user, index) => (
{data?.items.map((user: IUser, index: number) => (
<Table.Tr key={index}>
<Table.Td>
<Group gap="sm">
<CustomAvatar avatarUrl={user.avatarUrl} name={user.name}/>
<CustomAvatar
avatarUrl={user.avatarUrl}
name={user.name}
/>
<div>
<Text fz="sm" fw={500}>
{user.name}
@@ -68,11 +75,9 @@ export default function GroupMembersList() {
</div>
</Group>
</Table.Td>
<Table.Td>
<Badge variant="light">Active</Badge>
<Badge variant="light">{t("Active")}</Badge>
</Table.Td>
<Table.Td>
{isAdmin && (
<Menu
@@ -85,13 +90,12 @@ export default function GroupMembersList() {
>
<Menu.Target>
<ActionIcon variant="subtle" c="gray">
<IconDots size={20} stroke={2}/>
<IconDots size={20} stroke={2} />
</ActionIcon>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item onClick={() => openRemoveModal(user.id)}>
Remove group member
{t("Remove group member")}
</Menu.Item>
</Menu.Dropdown>
</Menu>