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

View File

@ -12,6 +12,7 @@ import { useState } from "react";
import { ExportFormat } from "@/features/page/types/page.types.ts";
import { notifications } from "@mantine/notifications";
import { exportSpace } from "@/features/space/services/space-service";
import { useTranslation } from "react-i18next";
interface ExportModalProps {
id: string;
@ -29,6 +30,7 @@ export default function ExportModal({
const [format, setFormat] = useState<ExportFormat>(ExportFormat.Markdown);
const [includeChildren, setIncludeChildren] = useState<boolean>(false);
const [includeAttachments, setIncludeAttachments] = useState<boolean>(true);
const { t } = useTranslation();
const handleExport = async () => {
try {
@ -73,7 +75,7 @@ export default function ExportModal({
<Modal.Body>
<Group justify="space-between" wrap="nowrap">
<div>
<Text size="md">Format</Text>
<Text size="md">{t("Format")}</Text>
</div>
<ExportFormatSelection format={format} onChange={handleChange} />
</Group>
@ -84,7 +86,7 @@ export default function ExportModal({
<Group justify="space-between" wrap="nowrap">
<div>
<Text size="md">Include subpages</Text>
<Text size="md">{t("Include subpages")}</Text>
</div>
<Switch
onChange={(event) =>
@ -102,7 +104,7 @@ export default function ExportModal({
<Group justify="space-between" wrap="nowrap">
<div>
<Text size="md">Include attachments</Text>
<Text size="md">{t("Include attachments")}</Text>
</div>
<Switch
onChange={(event) =>
@ -116,9 +118,9 @@ export default function ExportModal({
<Group justify="center" mt="md">
<Button onClick={onClose} variant="default">
Cancel
{t("Cancel")}
</Button>
<Button onClick={handleExport}>Export</Button>
<Button onClick={handleExport}>{t("Export")}</Button>
</Group>
</Modal.Body>
</Modal.Content>
@ -131,6 +133,8 @@ interface ExportFormatSelection {
onChange: (value: string) => void;
}
function ExportFormatSelection({ format, onChange }: ExportFormatSelection) {
const { t } = useTranslation();
return (
<Select
data={[
@ -143,7 +147,7 @@ function ExportFormatSelection({ format, onChange }: ExportFormatSelection) {
comboboxProps={{ width: "120" }}
allowDeselect={false}
withCheckIcon={false}
aria-label="Select export format"
aria-label={t("Select export format")}
/>
);
}

View File

@ -8,17 +8,19 @@ import {
} from '@mantine/core';
import {Link} from 'react-router-dom';
import PageListSkeleton from '@/components/ui/page-list-skeleton.tsx';
import {buildPageUrl} from '@/features/page/page.utils.ts';
import {formattedDate} from '@/lib/time.ts';
import {useRecentChangesQuery} from '@/features/page/queries/page-query.ts';
import {IconFileDescription} from '@tabler/icons-react';
import {getSpaceUrl} from '@/lib/config.ts';
import { buildPageUrl } from '@/features/page/page.utils.ts';
import { formattedDate } from '@/lib/time.ts';
import { useRecentChangesQuery } from '@/features/page/queries/page-query.ts';
import { IconFileDescription } from '@tabler/icons-react';
import { getSpaceUrl } from '@/lib/config.ts';
import { useTranslation } from "react-i18next";
interface Props {
spaceId?: string;
}
export default function RecentChanges({spaceId}: Props) {
const { t } = useTranslation();
const {data: pages, isLoading, isError} = useRecentChangesQuery(spaceId);
if (isLoading) {
@ -26,7 +28,7 @@ export default function RecentChanges({spaceId}: Props) {
}
if (isError) {
return <Text>Failed to fetch recent pages</Text>;
return <Text>{t("Failed to fetch recent pages")}</Text>;
}
return pages && pages.items.length > 0 ? (
@ -48,7 +50,7 @@ export default function RecentChanges({spaceId}: Props) {
)}
<Text fw={500} size="md" lineClamp={1}>
{page.title || 'Untitled'}
{page.title || t("Untitled")}
</Text>
</Group>
</UnstyledButton>
@ -78,7 +80,7 @@ export default function RecentChanges({spaceId}: Props) {
</Table.ScrollContainer>
) : (
<Text size="md" ta="center">
No pages yet
{t("No pages yet")}
</Text>
);
}