feat: add new languages to selection (#626)

* Add new languages to selection

* more translations
This commit is contained in:
Philip Okugbe
2025-01-11 22:11:31 +00:00
committed by GitHub
parent f7ac6bb4bb
commit f3dbf7cc5d
9 changed files with 54 additions and 39 deletions

View File

@ -226,7 +226,11 @@
"Select a group": "Select a group", "Select a group": "Select a group",
"Export all pages and attachments in this space.": "Export all pages and attachments in this space.", "Export all pages and attachments in this space.": "Export all pages and attachments in this space.",
"Delete space": "Delete space", "Delete space": "Delete space",
"Are you sure you want to delete this space?": "Are you sure you want to delete this space?",
"Delete this space with all its pages and data.": "Delete this space with all its pages and data.", "Delete this space with all its pages and data.": "Delete this space with all its pages and data.",
"All pages, comments, attachments and permissions in this space will be deleted irreversibly.": "All pages, comments, attachments and permissions in this space will be deleted irreversibly.",
"Confirm space name": "Confirm space name",
"Type the space name <b>{{spaceName}}</b> to confirm your action.": "Type the space name <b>{{spaceName}}</b> to confirm your action.",
"Format": "Format", "Format": "Format",
"Include subpages": "Include subpages", "Include subpages": "Include subpages",
"Include attachments": "Include attachments", "Include attachments": "Include attachments",
@ -331,5 +335,8 @@
"Multiple": "Multiple", "Multiple": "Multiple",
"Heading {{level}}": "Heading {{level}}", "Heading {{level}}": "Heading {{level}}",
"Toggle title": "Toggle title", "Toggle title": "Toggle title",
"Write anything. Enter \"/\" for commands": "Write anything. Enter \"/\" for commands" "Write anything. Enter \"/\" for commands": "Write anything. Enter \"/\" for commands",
"Names do not match": "Names do not match",
"Today, {{time}}": "Today, {{time}}",
"Yesterday, {{time}}": "Yesterday, {{time}}"
} }

View File

@ -64,8 +64,8 @@ import clojure from "highlight.js/lib/languages/clojure";
import fortran from "highlight.js/lib/languages/fortran"; import fortran from "highlight.js/lib/languages/fortran";
import haskell from "highlight.js/lib/languages/haskell"; import haskell from "highlight.js/lib/languages/haskell";
import scala from "highlight.js/lib/languages/scala"; import scala from "highlight.js/lib/languages/scala";
import i18n from "@/i18n.ts";
import { MarkdownClipboard } from "@/features/editor/extensions/markdown-clipboard.ts"; import { MarkdownClipboard } from "@/features/editor/extensions/markdown-clipboard.ts";
import i18n from "i18next";
const lowlight = createLowlight(common); const lowlight = createLowlight(common);
lowlight.register("mermaid", plaintext); lowlight.register("mermaid", plaintext);

View File

@ -1,27 +1,29 @@
import { Button, Divider, Group, Modal, Text, TextInput } from '@mantine/core'; import { Button, Divider, Group, Modal, Text, TextInput } from "@mantine/core";
import { useDisclosure } from '@mantine/hooks'; import { useDisclosure } from "@mantine/hooks";
import { useDeleteSpaceMutation } from '../queries/space-query'; import { useDeleteSpaceMutation } from "../queries/space-query";
import { useField } from '@mantine/form'; import { useField } from "@mantine/form";
import { ISpace } from '../types/space.types'; import { ISpace } from "../types/space.types";
import { useNavigate } from 'react-router-dom'; import { useNavigate } from "react-router-dom";
import APP_ROUTE from '@/lib/app-route'; import APP_ROUTE from "@/lib/app-route";
import { Trans, useTranslation } from "react-i18next";
interface DeleteSpaceModalProps { interface DeleteSpaceModalProps {
space: ISpace; space: ISpace;
} }
export default function DeleteSpaceModal({ space }: DeleteSpaceModalProps) { export default function DeleteSpaceModal({ space }: DeleteSpaceModalProps) {
const { t } = useTranslation();
const [opened, { open, close }] = useDisclosure(false); const [opened, { open, close }] = useDisclosure(false);
const deleteSpaceMutation = useDeleteSpaceMutation(); const deleteSpaceMutation = useDeleteSpaceMutation();
const navigate = useNavigate(); const navigate = useNavigate();
const confirmNameField = useField({ const confirmNameField = useField({
initialValue: '', initialValue: "",
validateOnChange: true, validateOnChange: true,
validate: (value) => validate: (value) =>
value.trim().toLowerCase() === space.name.trim().toLocaleLowerCase() value.trim().toLowerCase() === space.name.trim().toLocaleLowerCase()
? null ? null
: 'Names do not match', : t("Names do not match"),
}); });
const handleDelete = async () => { const handleDelete = async () => {
@ -38,46 +40,47 @@ export default function DeleteSpaceModal({ space }: DeleteSpaceModalProps) {
await deleteSpaceMutation.mutateAsync({ id: space.id, slug: space.slug }); await deleteSpaceMutation.mutateAsync({ id: space.id, slug: space.slug });
navigate(APP_ROUTE.HOME); navigate(APP_ROUTE.HOME);
} catch (error) { } catch (error) {
console.error('Failed to delete space', error); console.error("Failed to delete space", error);
} }
}; };
return ( return (
<> <>
<Button onClick={open} variant="light" color="red"> <Button onClick={open} variant="light" color="red">
Delete {t("Delete")}
</Button> </Button>
<Modal <Modal
opened={opened} opened={opened}
onClose={close} onClose={close}
title="Are you sure you want to delete this space?" title={t("Are you sure you want to delete this space?")}
> >
<Divider size="xs" mb="xs" /> <Divider size="xs" mb="xs" />
<Text> <Text>
All pages, comments, attachments and permissions in this space will be {t(
deleted irreversibly. "All pages, comments, attachments and permissions in this space will be deleted irreversibly.",
)}
</Text> </Text>
<Text mt="sm"> <Text mt="sm">
Type the space name{' '} <Trans
<Text span fw={500}> defaults="Type the space name <b>{{spaceName}}</b> to confirm your action."
'{space.name}' values={{ spaceName: space.name }}
</Text>{' '} components={{ b: <Text span fw={500} /> }}
to confirm your action. />
</Text> </Text>
<TextInput <TextInput
{...confirmNameField.getInputProps()} {...confirmNameField.getInputProps()}
variant="filled" variant="filled"
placeholder="Confirm space name" placeholder={t("Confirm space name")}
py="sm" py="sm"
data-autofocus data-autofocus
/> />
<Group justify="flex-end" mt="md"> <Group justify="flex-end" mt="md">
<Button onClick={close} variant="default"> <Button onClick={close} variant="default">
Cancel {t("Cancel")}
</Button> </Button>
<Button onClick={handleDelete} color="red"> <Button onClick={handleDelete} color="red">
Confirm {t("Confirm")}
</Button> </Button>
</Group> </Group>
</Modal> </Modal>

View File

@ -82,7 +82,7 @@ export function SpaceSidebar() {
classes.menu, classes.menu,
location.pathname.toLowerCase() === getSpaceUrl(spaceSlug) location.pathname.toLowerCase() === getSpaceUrl(spaceSlug)
? classes.activeButton ? classes.activeButton
: "" : "",
)} )}
> >
<div className={classes.menuItemInner}> <div className={classes.menuItemInner}>
@ -119,7 +119,7 @@ export function SpaceSidebar() {
{spaceAbility.can( {spaceAbility.can(
SpaceCaslAction.Manage, SpaceCaslAction.Manage,
SpaceCaslSubject.Page SpaceCaslSubject.Page,
) && ( ) && (
<UnstyledButton <UnstyledButton
className={classes.menu} className={classes.menu}
@ -146,7 +146,7 @@ export function SpaceSidebar() {
{spaceAbility.can( {spaceAbility.can(
SpaceCaslAction.Manage, SpaceCaslAction.Manage,
SpaceCaslSubject.Page SpaceCaslSubject.Page,
) && ( ) && (
<Group gap="xs"> <Group gap="xs">
<SpaceMenu spaceId={space.id} onSpaceSettings={openSettings} /> <SpaceMenu spaceId={space.id} onSpaceSettings={openSettings} />
@ -170,7 +170,7 @@ export function SpaceSidebar() {
spaceId={space.id} spaceId={space.id}
readOnly={spaceAbility.cannot( readOnly={spaceAbility.cannot(
SpaceCaslAction.Manage, SpaceCaslAction.Manage,
SpaceCaslSubject.Page SpaceCaslSubject.Page,
)} )}
/> />
</div> </div>
@ -230,7 +230,7 @@ function SpaceMenu({ spaceId, onSpaceSettings }: SpaceMenuProps) {
onClick={openExportModal} onClick={openExportModal}
leftSection={<IconFileExport size={16} />} leftSection={<IconFileExport size={16} />}
> >
Export space {t("Export space")}
</Menu.Item> </Menu.Item>
<Menu.Divider /> <Menu.Divider />

View File

@ -33,7 +33,7 @@ export default function SpaceDetails({ spaceId, readOnly }: SpaceDetailsProps) {
<Group justify="space-between" wrap="nowrap" gap="xl"> <Group justify="space-between" wrap="nowrap" gap="xl">
<div> <div>
<Text size="md">Export space</Text> <Text size="md">{t("Export space")}</Text>
<Text size="sm" c="dimmed"> <Text size="sm" c="dimmed">
{t("Export all pages and attachments in this space.")} {t("Export all pages and attachments in this space.")}
</Text> </Text>

View File

@ -1,20 +1,21 @@
import { IRoleData, SpaceRole } from "@/lib/types.ts"; import { IRoleData, SpaceRole } from "@/lib/types.ts";
import i18n from "i18next";
export const spaceRoleData: IRoleData[] = [ export const spaceRoleData: IRoleData[] = [
{ {
label: "Full access", label: i18n.t("Full access"),
value: SpaceRole.ADMIN, value: SpaceRole.ADMIN,
description: "Has full access to space settings and pages", description: i18n.t("Has full access to space settings and pages."),
}, },
{ {
label: "Can edit", label: i18n.t("Can edit"),
value: SpaceRole.WRITER, value: SpaceRole.WRITER,
description: "Can create and edit pages in space", description: i18n.t("Can create and edit pages in space."),
}, },
{ {
label: "Can view", label: i18n.t("Can view"),
value: SpaceRole.READER, value: SpaceRole.READER,
description: "Can view pages in space but not edit", description: i18n.t("Can view pages in space but not edit."),
}, },
]; ];

View File

@ -42,6 +42,9 @@ function LanguageSwitcher() {
label={t("Select language")} label={t("Select language")}
data={[ data={[
{ value: "en-US", label: "English (United States)" }, { value: "en-US", label: "English (United States)" },
{ value: "de-DE", label: "Deutsch (Germany)" },
{ value: "fr-FR", label: "Français (France)" },
{ value: "pt-BR", label: "Português (Brazilian)" },
{ value: "zh-CN", label: "中文 (简体)" }, { value: "zh-CN", label: "中文 (简体)" },
]} ]}
value={language} value={language}

View File

@ -1,5 +1,6 @@
import { formatDistanceStrict } from "date-fns"; import { formatDistanceStrict } from "date-fns";
import { format, isToday, isYesterday } from "date-fns"; import { format, isToday, isYesterday } from "date-fns";
import i18n from "i18next";
export function timeAgo(date: Date) { export function timeAgo(date: Date) {
return formatDistanceStrict(new Date(date), new Date(), { addSuffix: true }); return formatDistanceStrict(new Date(date), new Date(), { addSuffix: true });
@ -7,9 +8,9 @@ export function timeAgo(date: Date) {
export function formattedDate(date: Date) { export function formattedDate(date: Date) {
if (isToday(date)) { if (isToday(date)) {
return `Today, ${format(date, "h:mma")}`; return i18n.t("Today, {{time}}", { time: format(date, "h:mma") });
} else if (isYesterday(date)) { } else if (isYesterday(date)) {
return `Yesterday, ${format(date, "h:mma")}`; return i18n.t("Yesterday, {{time}}", { time: format(date, "h:mma") });
} else { } else {
return format(date, "MMM dd, yyyy, h:mma"); return format(date, "MMM dd, yyyy, h:mma");
} }

View File

@ -1,5 +1,5 @@
import SettingsTitle from "@/components/settings/settings-title.tsx"; import SettingsTitle from "@/components/settings/settings-title.tsx";
import AccountLanguage from "@/features/user/components/account-languate"; import AccountLanguage from "@/features/user/components/account-language.tsx";
import AccountTheme from "@/features/user/components/account-theme.tsx"; import AccountTheme from "@/features/user/components/account-theme.tsx";
import PageWidthPref from "@/features/user/components/page-width-pref.tsx"; import PageWidthPref from "@/features/user/components/page-width-pref.tsx";
import { Divider } from "@mantine/core"; import { Divider } from "@mantine/core";