mirror of
https://github.com/deiucanta/chatpad.git
synced 2026-03-11 09:04:31 +00:00
Add i18n module + translations
This commit is contained in:
parent
a085dae2a8
commit
445a310cc2
11 changed files with 156 additions and 66 deletions
|
|
@ -112,6 +112,46 @@
|
|||
"openAIVersionSaved": {
|
||||
"title": "saved",
|
||||
"message": "Your OpenAI Version has been saved."
|
||||
}
|
||||
},
|
||||
"newChatFromPrompt": "New Chat From Prompt",
|
||||
"copied": "Copied",
|
||||
"copy": "Copy",
|
||||
"words": "words",
|
||||
"editPrompt": "Edit Prompt",
|
||||
"promptUpdated": {
|
||||
"title": "saved",
|
||||
"message": "Prompt updated"
|
||||
},
|
||||
"editChat": "Edit Chat",
|
||||
"chatUpdated": {
|
||||
"title": "saved",
|
||||
"message": "Chat updated"
|
||||
},
|
||||
"name": "Name",
|
||||
"deleteAllData": "Delete All Data",
|
||||
"confirmDeleteAllData": "Are you sure you want to delete your data?",
|
||||
"messages": "Messages",
|
||||
"exportingData" : {
|
||||
"title": "Exporting Data",
|
||||
"message": "Your data is being exported."
|
||||
},
|
||||
"exportData": "Export Data",
|
||||
"importingData" : {
|
||||
"title": "Import Data",
|
||||
"message": "Your data is being imported."
|
||||
},
|
||||
"importData": "Import Data",
|
||||
"selectedFileError": {
|
||||
"title": "Error",
|
||||
"message": "The file you selected is invalid"
|
||||
},
|
||||
"edit": "Edit",
|
||||
"promptDeleted": "Prompt deleted.",
|
||||
"removePromptError": {
|
||||
"title": "Error",
|
||||
"message": "Can't remove prompt. Please refresh the page and try again."
|
||||
},
|
||||
"deletePrompt": "Delete Prompt",
|
||||
"confirmDeletePrompt": "Are you sure you want to delete this prompt?"
|
||||
}
|
||||
}
|
||||
|
|
@ -112,6 +112,46 @@
|
|||
"openAIVersionSaved": {
|
||||
"title": "Sauvegardé",
|
||||
"message": "Votre version OpenAI a été enregistrée."
|
||||
}
|
||||
},
|
||||
"newChatFromPrompt": "Nouvelle conversattion à partir du prompt",
|
||||
"copied": "Copié !",
|
||||
"copy": "Copier",
|
||||
"words": "mots",
|
||||
"editPrompt": "Modifier le prompt",
|
||||
"promptUpdated": {
|
||||
"title": "Sauvegardé",
|
||||
"message": "Prompt enregistré"
|
||||
},
|
||||
"editChat": "Modifier la conversation",
|
||||
"chatUpdated": {
|
||||
"title": "Sauvegardé",
|
||||
"message": "Conversation enregistrée"
|
||||
},
|
||||
"name": "Nom",
|
||||
"deleteAllData": "Supprimer toutes les données",
|
||||
"confirmDeleteAllData": "Êtes-vous sûr de vouloir effacer toutes vos données ?",
|
||||
"messages": "Messages",
|
||||
"exportingData" : {
|
||||
"title": "Exportation de données",
|
||||
"message": "Vos données sont en cours d'exportation."
|
||||
},
|
||||
"exportData": "Exporter les données",
|
||||
"importingData" : {
|
||||
"title": "Importation de données",
|
||||
"message": "Vos données sont en cours d'importation."
|
||||
},
|
||||
"importData": "Importer les données",
|
||||
"selectedFileError": {
|
||||
"title": "Erreur",
|
||||
"message": "Le fichier sélectionné n'est pas valide"
|
||||
},
|
||||
"edit": "Modifier",
|
||||
"promptDeleted": "Prompt supprimée.",
|
||||
"removePromptError": {
|
||||
"title": "Erreur",
|
||||
"message": "Impossible de supprimer le prompt. Veuillez rafraîchir la page et réessayer."
|
||||
},
|
||||
"deletePrompt": "Supprimer le prompt",
|
||||
"confirmDeletePrompt": "Êtes-vous sûr de vouloir supprimer ce prompt ?"
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,8 @@ import { useChatId } from "../hooks/useChatId";
|
|||
import { DeleteChatModal } from "./DeleteChatModal";
|
||||
import { EditChatModal } from "./EditChatModal";
|
||||
import { MainLink } from "./MainLink";
|
||||
import '../i18n'
|
||||
import {t} from "i18next";
|
||||
|
||||
export function Chats({ search }: { search: string }) {
|
||||
const chatId = useChatId();
|
||||
|
|
@ -55,10 +57,10 @@ export function Chats({ search }: { search: string }) {
|
|||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<EditChatModal chat={chat}>
|
||||
<Menu.Item>Edit</Menu.Item>
|
||||
<Menu.Item>{t('edit')}</Menu.Item>
|
||||
</EditChatModal>
|
||||
<DeleteChatModal chat={chat}>
|
||||
<Menu.Item>Delete</Menu.Item>
|
||||
<Menu.Item>{t('delete')}</Menu.Item>
|
||||
</DeleteChatModal>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import { cloneElement, ReactElement } from "react";
|
|||
import { db } from "../db";
|
||||
import { DeleteAllDataModal } from "./DeleteAllDataModal";
|
||||
import { DeleteChatsModal } from "./DeleteChatsModal";
|
||||
import '../i18n'
|
||||
import {t} from "i18next";
|
||||
|
||||
export function DatabaseModal({ children }: { children: ReactElement }) {
|
||||
const [opened, { open, close }] = useDisclosure(false);
|
||||
|
|
@ -27,7 +29,7 @@ export function DatabaseModal({ children }: { children: ReactElement }) {
|
|||
<Modal
|
||||
opened={opened}
|
||||
onClose={close}
|
||||
title="Database"
|
||||
title={t('database')}
|
||||
size="lg"
|
||||
withinPortal
|
||||
keepMounted
|
||||
|
|
@ -45,7 +47,7 @@ export function DatabaseModal({ children }: { children: ReactElement }) {
|
|||
tt="uppercase"
|
||||
fw={700}
|
||||
>
|
||||
Chats
|
||||
{t('chats')}
|
||||
</Text>
|
||||
</Card>
|
||||
<Card withBorder sx={{ flex: 1, marginLeft: -1 }}>
|
||||
|
|
@ -59,7 +61,7 @@ export function DatabaseModal({ children }: { children: ReactElement }) {
|
|||
tt="uppercase"
|
||||
fw={700}
|
||||
>
|
||||
Messages
|
||||
{t('messages')}
|
||||
</Text>
|
||||
</Card>
|
||||
<Card withBorder sx={{ flex: 1, marginLeft: -1 }}>
|
||||
|
|
@ -73,7 +75,7 @@ export function DatabaseModal({ children }: { children: ReactElement }) {
|
|||
tt="uppercase"
|
||||
fw={700}
|
||||
>
|
||||
Prompts
|
||||
{t('prompts')}
|
||||
</Text>
|
||||
</Card>
|
||||
</Flex>
|
||||
|
|
@ -89,12 +91,12 @@ export function DatabaseModal({ children }: { children: ReactElement }) {
|
|||
"application/json"
|
||||
);
|
||||
notifications.show({
|
||||
title: "Exporting Data",
|
||||
message: "Your data is being exported.",
|
||||
title: t('exportingData.title'),
|
||||
message: t('exportingData.message'),
|
||||
});
|
||||
}}
|
||||
>
|
||||
Export Data
|
||||
{t('exportData')}
|
||||
</Button>
|
||||
<input
|
||||
id="file-upload-btn"
|
||||
|
|
@ -110,15 +112,15 @@ export function DatabaseModal({ children }: { children: ReactElement }) {
|
|||
})
|
||||
.then(() => {
|
||||
notifications.show({
|
||||
title: "Importing data",
|
||||
message: "Your data is being imported.",
|
||||
title: t('importingData.title'),
|
||||
message: t('importingData.message'),
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
notifications.show({
|
||||
title: "Error",
|
||||
title: t('selectedFileError.title'),
|
||||
color: "red",
|
||||
message: "The file you selected is invalid",
|
||||
message: t('selectedFileError.message'),
|
||||
});
|
||||
});
|
||||
}}
|
||||
|
|
@ -131,7 +133,7 @@ export function DatabaseModal({ children }: { children: ReactElement }) {
|
|||
variant="default"
|
||||
leftIcon={<IconDatabaseImport size={20} />}
|
||||
>
|
||||
Import Data
|
||||
{t('importData')}
|
||||
</Button>
|
||||
</Group>
|
||||
<Group>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import { Button, Modal, Stack, Text } from "@mantine/core";
|
|||
import { useDisclosure } from "@mantine/hooks";
|
||||
import { IconTrash } from "@tabler/icons-react";
|
||||
import { db } from "../db";
|
||||
import '../i18n'
|
||||
import {t} from "i18next";
|
||||
|
||||
export function DeleteAllDataModal({ onOpen }: { onOpen: () => void }) {
|
||||
const [opened, { open, close }] = useDisclosure(false, { onOpen });
|
||||
|
|
@ -14,17 +16,17 @@ export function DeleteAllDataModal({ onOpen }: { onOpen: () => void }) {
|
|||
color="red"
|
||||
leftIcon={<IconTrash size={20} />}
|
||||
>
|
||||
Delete All Data
|
||||
{t('deleteAllData')}
|
||||
</Button>
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={close}
|
||||
title="Delete All Data"
|
||||
title={t('deleteAllData')}
|
||||
size="md"
|
||||
withinPortal
|
||||
>
|
||||
<Stack>
|
||||
<Text size="sm">Are you sure you want to delete your data?</Text>
|
||||
<Text size="sm">{t('confirmDeleteAllData')}</Text>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
await db.delete();
|
||||
|
|
@ -33,7 +35,7 @@ export function DeleteAllDataModal({ onOpen }: { onOpen: () => void }) {
|
|||
}}
|
||||
color="red"
|
||||
>
|
||||
Delete
|
||||
{t('delete')}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Modal>
|
||||
|
|
|
|||
|
|
@ -15,18 +15,18 @@ export function DeleteChatsModal({ onOpen }: { onOpen: () => void }) {
|
|||
variant="outline"
|
||||
color="red"
|
||||
leftIcon={<IconTrash size={20} />}
|
||||
>"
|
||||
{t("deleteChats")}
|
||||
>
|
||||
{t('deleteChats')}
|
||||
</Button>
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={close}
|
||||
title={t("deleteChats")}
|
||||
title={t('deleteChats')}
|
||||
size="md"
|
||||
withinPortal
|
||||
>
|
||||
<Stack>
|
||||
<Text size="sm">{t("confirmDeleteChats")}</Text>
|
||||
<Text size="sm">{t('confirmDeleteChats')}</Text>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
await db.chats.clear();
|
||||
|
|
@ -36,7 +36,7 @@ export function DeleteChatsModal({ onOpen }: { onOpen: () => void }) {
|
|||
}}
|
||||
color="red"
|
||||
>
|
||||
{t("delete")}
|
||||
{t('delete')}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Modal>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import { useEffect, useState } from "react";
|
|||
import { db, Prompt } from "../db";
|
||||
import { useApiKey } from "../hooks/useApiKey";
|
||||
import { useChatId } from "../hooks/useChatId";
|
||||
import '../i18n'
|
||||
import {t} from "i18next";
|
||||
import * as tty from "tty";
|
||||
|
||||
export function DeletePromptModal({ prompt }: { prompt: Prompt }) {
|
||||
const [opened, { open, close }] = useDisclosure(false);
|
||||
|
|
@ -23,7 +26,7 @@ export function DeletePromptModal({ prompt }: { prompt: Prompt }) {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Modal opened={opened} onClose={close} title="Delete Prompt" size="md">
|
||||
<Modal opened={opened} onClose={close} title={t('deletePrompt')} size="md">
|
||||
<form
|
||||
onSubmit={async (event) => {
|
||||
try {
|
||||
|
|
@ -33,22 +36,21 @@ export function DeletePromptModal({ prompt }: { prompt: Prompt }) {
|
|||
close();
|
||||
|
||||
notifications.show({
|
||||
title: "Deleted",
|
||||
message: "Chat deleted.",
|
||||
title: t('deleted'),
|
||||
message: t('promptDeleted'),
|
||||
});
|
||||
} catch (error: any) {
|
||||
if (error.toJSON().message === "Network Error") {
|
||||
notifications.show({
|
||||
title: "Error",
|
||||
title: t('networkError.title'),
|
||||
color: "red",
|
||||
message: "No internet connection.",
|
||||
message: t('networkError.message'),
|
||||
});
|
||||
} else {
|
||||
notifications.show({
|
||||
title: "Error",
|
||||
title: t('removePromptError.title'),
|
||||
color: "red",
|
||||
message:
|
||||
"Can't remove chat. Please refresh the page and try again.",
|
||||
message: t('removePromptError.message'),
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
|
|
@ -57,14 +59,14 @@ export function DeletePromptModal({ prompt }: { prompt: Prompt }) {
|
|||
}}
|
||||
>
|
||||
<Stack>
|
||||
<Text size="sm">Are you sure you want to delete this prompt?</Text>
|
||||
<Text size="sm">{t('confirmDeletePrompt')}</Text>
|
||||
<Button type="submit" color="red" loading={submitting}>
|
||||
Delete
|
||||
{t('delete')}
|
||||
</Button>
|
||||
</Stack>
|
||||
</form>
|
||||
</Modal>
|
||||
<Tooltip label="Delete Prompt">
|
||||
<Tooltip label={t('deletePrompt')}>
|
||||
<ActionIcon color="red" size="lg" onClick={open}>
|
||||
<IconTrash size={20} />
|
||||
</ActionIcon>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ import { useDisclosure } from "@mantine/hooks";
|
|||
import { notifications } from "@mantine/notifications";
|
||||
import { cloneElement, ReactElement, useEffect, useState } from "react";
|
||||
import { Chat, db } from "../db";
|
||||
import '../i18n'
|
||||
import {t} from "i18next";
|
||||
|
||||
export function EditChatModal({
|
||||
chat,
|
||||
|
|
@ -22,7 +24,7 @@ export function EditChatModal({
|
|||
return (
|
||||
<>
|
||||
{cloneElement(children, { onClick: open })}
|
||||
<Modal opened={opened} onClose={close} title="Edit Chat" withinPortal>
|
||||
<Modal opened={opened} onClose={close} title={t('editChat')} withinPortal>
|
||||
<form
|
||||
onSubmit={async (event) => {
|
||||
try {
|
||||
|
|
@ -32,22 +34,22 @@ export function EditChatModal({
|
|||
chat.description = value;
|
||||
});
|
||||
notifications.show({
|
||||
title: "Saved",
|
||||
message: "",
|
||||
title: t('chatUpdated.title'),
|
||||
message: t('chatUpdated.message'),
|
||||
});
|
||||
close();
|
||||
} catch (error: any) {
|
||||
if (error.toJSON().message === "Network Error") {
|
||||
notifications.show({
|
||||
title: "Error",
|
||||
title: t('networkError.title'),
|
||||
color: "red",
|
||||
message: "No internet connection.",
|
||||
message: t('networkError.message'),
|
||||
});
|
||||
}
|
||||
const message = error.response?.data?.error?.message;
|
||||
if (message) {
|
||||
notifications.show({
|
||||
title: "Error",
|
||||
title: t('error.title'),
|
||||
color: "red",
|
||||
message,
|
||||
});
|
||||
|
|
@ -59,14 +61,14 @@ export function EditChatModal({
|
|||
>
|
||||
<Stack>
|
||||
<TextInput
|
||||
label="Name"
|
||||
label={t('name')}
|
||||
value={value}
|
||||
onChange={(event) => setValue(event.currentTarget.value)}
|
||||
formNoValidate
|
||||
data-autofocus
|
||||
/>
|
||||
<Button type="submit" loading={submitting}>
|
||||
Save
|
||||
{t('save')}
|
||||
</Button>
|
||||
</Stack>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import { notifications } from "@mantine/notifications";
|
|||
import { IconPencil } from "@tabler/icons-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { db, Prompt } from "../db";
|
||||
import '../i18n'
|
||||
import {t} from "i18next";
|
||||
|
||||
export function EditPromptModal({ prompt }: { prompt: Prompt }) {
|
||||
const [opened, { open, close }] = useDisclosure(false);
|
||||
|
|
@ -26,7 +28,7 @@ export function EditPromptModal({ prompt }: { prompt: Prompt }) {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Modal opened={opened} onClose={close} title="Edit Prompt" size="lg">
|
||||
<Modal opened={opened} onClose={close} title={t('editPrompt')} size="lg">
|
||||
<form
|
||||
onSubmit={async (event) => {
|
||||
try {
|
||||
|
|
@ -37,21 +39,21 @@ export function EditPromptModal({ prompt }: { prompt: Prompt }) {
|
|||
chat.content = value;
|
||||
});
|
||||
notifications.show({
|
||||
title: "Saved",
|
||||
message: "Prompt updated",
|
||||
title: t('promptUpdated.title'),
|
||||
message: t('promptUpdated.message'),
|
||||
});
|
||||
} catch (error: any) {
|
||||
if (error.toJSON().message === "Network Error") {
|
||||
notifications.show({
|
||||
title: "Error",
|
||||
title: t('networkError.title'),
|
||||
color: "red",
|
||||
message: "No internet connection.",
|
||||
message: t('networkError.message'),
|
||||
});
|
||||
}
|
||||
const message = error.response?.data?.error?.message;
|
||||
if (message) {
|
||||
notifications.show({
|
||||
title: "Error",
|
||||
title: t('error.title'),
|
||||
color: "red",
|
||||
message,
|
||||
});
|
||||
|
|
@ -63,14 +65,14 @@ export function EditPromptModal({ prompt }: { prompt: Prompt }) {
|
|||
>
|
||||
<Stack>
|
||||
<TextInput
|
||||
label="Title"
|
||||
label={t('title')}
|
||||
value={title}
|
||||
onChange={(event) => setTitle(event.currentTarget.value)}
|
||||
formNoValidate
|
||||
data-autofocus
|
||||
/>
|
||||
<Textarea
|
||||
label="Content"
|
||||
label={t('content')}
|
||||
autosize
|
||||
minRows={5}
|
||||
maxRows={10}
|
||||
|
|
@ -78,12 +80,12 @@ export function EditPromptModal({ prompt }: { prompt: Prompt }) {
|
|||
onChange={(event) => setValue(event.currentTarget.value)}
|
||||
/>
|
||||
<Button type="submit" loading={submitting}>
|
||||
Save
|
||||
{t('save')}
|
||||
</Button>
|
||||
</Stack>
|
||||
</form>
|
||||
</Modal>
|
||||
<Tooltip label="Edit Prompt">
|
||||
<Tooltip label={t('editPrompt')}>
|
||||
<ActionIcon size="lg" onClick={open}>
|
||||
<IconPencil size={20} />
|
||||
</ActionIcon>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import "../styles/markdown.scss";
|
|||
import { CreatePromptModal } from "./CreatePromptModal";
|
||||
import { LogoIcon } from "./Logo";
|
||||
import { ScrollIntoView } from "./ScrollIntoView";
|
||||
import '../i18n'
|
||||
import {t} from "i18next";
|
||||
|
||||
export function MessageItem({ message }: { message: Message }) {
|
||||
const clipboard = useClipboard({ timeout: 500 });
|
||||
|
|
@ -55,7 +57,7 @@ export function MessageItem({ message }: { message: Message }) {
|
|||
<CopyButton value={String(props.children)}>
|
||||
{({ copied, copy }) => (
|
||||
<Tooltip
|
||||
label={copied ? "Copied" : "Copy"}
|
||||
label={copied ? t('copied') : t('copy')}
|
||||
position="left"
|
||||
>
|
||||
<ActionIcon
|
||||
|
|
@ -74,7 +76,7 @@ export function MessageItem({ message }: { message: Message }) {
|
|||
{message.role === "assistant" && (
|
||||
<Box>
|
||||
<Text size="sm" color="dimmed">
|
||||
{wordCount} words
|
||||
{wordCount} {t('words')}
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
|
|
@ -83,18 +85,13 @@ export function MessageItem({ message }: { message: Message }) {
|
|||
<CreatePromptModal content={message.content} />
|
||||
<CopyButton value={message.content}>
|
||||
{({ copied, copy }) => (
|
||||
<Tooltip label={copied ? "Copied" : "Copy"} position="left">
|
||||
<Tooltip label={copied ? t('copied') : t('copy')} position="left">
|
||||
<ActionIcon onClick={copy}>
|
||||
<IconCopy opacity={0.5} size={20} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
)}
|
||||
</CopyButton>
|
||||
{/* <Tooltip label={`${wordCount} words`} position="left">
|
||||
<ActionIcon>
|
||||
<IconInfoCircle opacity={0.5} size={20} />
|
||||
</ActionIcon>
|
||||
</Tooltip> */}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import { db } from "../db";
|
|||
import { createChatCompletion } from "../utils/openai";
|
||||
import { DeletePromptModal } from "./DeletePromptModal";
|
||||
import { EditPromptModal } from "./EditPromptModal";
|
||||
import '../i18n'
|
||||
import {t} from "i18next";
|
||||
|
||||
export function Prompts({
|
||||
onPlay,
|
||||
|
|
@ -80,7 +82,7 @@ export function Prompts({
|
|||
</Text>
|
||||
</Box>
|
||||
<Group spacing="none">
|
||||
<Tooltip label="New Chat From Prompt">
|
||||
<Tooltip label={t('newChatFromPrompt')}>
|
||||
<ActionIcon
|
||||
size="lg"
|
||||
onClick={async () => {
|
||||
|
|
@ -88,7 +90,7 @@ export function Prompts({
|
|||
const id = nanoid();
|
||||
await db.chats.add({
|
||||
id,
|
||||
description: "New Chat",
|
||||
description: t('newChat'),
|
||||
totalTokens: 0,
|
||||
createdAt: new Date(),
|
||||
});
|
||||
|
|
@ -105,8 +107,7 @@ export function Prompts({
|
|||
const result = await createChatCompletion(apiKey, [
|
||||
{
|
||||
role: "system",
|
||||
content:
|
||||
"You are ChatGPT, a large language model trained by OpenAI.",
|
||||
content: t('youAreChatGPT'),
|
||||
},
|
||||
{ role: "user", content: prompt.content },
|
||||
]);
|
||||
|
|
|
|||
Loading…
Reference in a new issue