Add i18n module + translations

This commit is contained in:
Jordi Marzo 2023-05-13 22:58:11 +02:00
parent a085dae2a8
commit 445a310cc2
11 changed files with 156 additions and 66 deletions

View file

@ -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?"
}
}

View file

@ -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 ?"
}
}

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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 },
]);