Merge pull request #969 from ManishMadan2882/main

Internationalisation with i18next
This commit is contained in:
Alex
2024-05-29 11:23:45 +01:00
committed by GitHub
20 changed files with 474 additions and 294 deletions

View File

@@ -9,13 +9,14 @@ import {
import { selectSourceDocs } from '../preferences/preferenceSlice';
import Exit from '../assets/exit.svg';
import Trash from '../assets/trash.svg';
import { useTranslation } from 'react-i18next';
const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com';
const embeddingsName =
import.meta.env.VITE_EMBEDDINGS_NAME ||
'huggingface_sentence-transformers/all-mpnet-base-v2';
const APIKeys: React.FC = () => {
const { t } = useTranslation();
const [isCreateModalOpen, setCreateModal] = React.useState(false);
const [isSaveKeyModalOpen, setSaveKeyModal] = React.useState(false);
const [newKey, setNewKey] = React.useState('');
@@ -97,7 +98,7 @@ const APIKeys: React.FC = () => {
onClick={() => setCreateModal(true)}
className="rounded-full bg-purple-30 px-4 py-3 text-white hover:bg-[#6F3FD1]"
>
Create new
{t('settings.apiKeys.createNew')}
</button>
</div>
{isCreateModalOpen && (
@@ -117,11 +118,15 @@ const APIKeys: React.FC = () => {
<table className="block w-max table-auto content-center justify-center rounded-xl border text-center dark:border-chinese-silver dark:text-bright-gray">
<thead>
<tr>
<th className="border-r p-4 md:w-[244px]">Name</th>
<th className="w-[244px] border-r px-4 py-2">
Source document
<th className="border-r p-4 md:w-[244px]">
{t('settings.apiKeys.name')}
</th>
<th className="w-[244px] border-r px-4 py-2">
{t('settings.apiKeys.sourceDoc')}
</th>
<th className="w-[244px] border-r px-4 py-2">
{t('settings.apiKeys.key')}
</th>
<th className="w-[244px] border-r px-4 py-2">API Key</th>
<th className="px-4 py-2"></th>
</tr>
</thead>
@@ -216,7 +221,7 @@ const CreateAPIKeyModal: React.FC<CreateAPIKeyModalProps> = ({
};
})
: [];
const { t } = useTranslation();
return (
<div className="fixed top-0 left-0 z-30 flex h-screen w-screen items-center justify-center bg-gray-alpha bg-opacity-50">
<div className="relative w-11/12 rounded-2xl bg-white p-10 dark:bg-outer-space sm:w-[512px]">
@@ -225,12 +230,12 @@ const CreateAPIKeyModal: React.FC<CreateAPIKeyModalProps> = ({
</button>
<div className="mb-6">
<span className="text-xl text-jet dark:text-bright-gray">
Create New API Key
{t('modals.createAPIKey.label')}
</span>
</div>
<div className="relative mt-5 mb-4">
<span className="absolute left-2 -top-2 bg-white px-2 text-xs text-gray-4000 dark:bg-outer-space dark:text-silver">
API Key Name
{t('modals.createAPIKey.apiKeyName')}
</span>
<input
type="text"
@@ -241,7 +246,7 @@ const CreateAPIKeyModal: React.FC<CreateAPIKeyModalProps> = ({
</div>
<div className="my-4">
<Dropdown
placeholder="Source document"
placeholder={t('modals.createAPIKey.sourceDoc')}
selectedValue={sourcePath}
onSelect={(selection: { label: string; value: string }) =>
setSourcePath(selection)
@@ -255,7 +260,7 @@ const CreateAPIKeyModal: React.FC<CreateAPIKeyModalProps> = ({
<Dropdown
options={activePrompts}
selectedValue={prompt ? prompt.name : null}
placeholder="Select active prompt"
placeholder={t('modals.createAPIKey.prompt')}
onSelect={(value: { name: string; id: string; type: string }) =>
setPrompt(value)
}
@@ -264,7 +269,7 @@ const CreateAPIKeyModal: React.FC<CreateAPIKeyModalProps> = ({
</div>
<div className="my-4">
<p className="mb-2 ml-2 font-bold text-jet dark:text-bright-gray">
Chunks processed per query
{t('modals.createAPIKey.chunks')}
</p>
<Dropdown
options={chunkOptions}
@@ -287,7 +292,7 @@ const CreateAPIKeyModal: React.FC<CreateAPIKeyModalProps> = ({
}
className="float-right mt-4 rounded-full bg-purple-30 px-5 py-2 text-sm text-white hover:bg-[#6F3FD1] disabled:opacity-50"
>
Create
{t('modals.createAPIKey.create')}
</button>
</div>
</div>
@@ -296,6 +301,7 @@ const CreateAPIKeyModal: React.FC<CreateAPIKeyModalProps> = ({
const SaveAPIKeyModal: React.FC<SaveAPIKeyModalProps> = ({ apiKey, close }) => {
const [isCopied, setIsCopied] = React.useState(false);
const { t } = useTranslation();
const handleCopyKey = () => {
navigator.clipboard.writeText(apiKey);
setIsCopied(true);
@@ -306,9 +312,12 @@ const SaveAPIKeyModal: React.FC<SaveAPIKeyModalProps> = ({ apiKey, close }) => {
<button className="absolute top-3 right-4 m-2 w-3" onClick={close}>
<img className="filter dark:invert" src={Exit} />
</button>
<h1 className="my-0 text-xl font-medium">Please save your Key</h1>
<h1 className="my-0 text-xl font-medium">
{' '}
{t('modals.saveKey.note')}
</h1>
<h3 className="text-sm font-normal text-outer-space">
This is the only time your key will be shown.
{t('modals.saveKey.disclaimer')}
</h3>
<div className="flex justify-between py-2">
<div>
@@ -319,14 +328,14 @@ const SaveAPIKeyModal: React.FC<SaveAPIKeyModalProps> = ({ apiKey, close }) => {
className="my-1 h-10 w-20 rounded-full border border-solid border-purple-30 p-2 text-sm text-purple-30 hover:bg-purple-30 hover:text-white"
onClick={handleCopyKey}
>
{isCopied ? 'Copied' : 'Copy'}
{isCopied ? t('modals.saveKey.copied') : t('modals.saveKey.copy')}
</button>
</div>
<button
onClick={close}
className="rounded-full bg-philippine-yellow px-4 py-3 font-medium text-black hover:bg-[#E6B91A]"
>
I saved the Key
{t('modals.saveKey.confirm')}
</button>
</div>
</div>

View File

@@ -1,10 +1,12 @@
import { DocumentsProps } from '../models/misc';
import Trash from '../assets/trash.svg';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
const Documents: React.FC<DocumentsProps> = ({
documents,
handleDeleteDocument,
}) => {
const { t } = useTranslation();
return (
<div className="mt-8">
<div className="flex flex-col">
@@ -12,10 +14,18 @@ const Documents: React.FC<DocumentsProps> = ({
<table className="block w-max table-auto content-center justify-center rounded-xl border text-center dark:border-chinese-silver dark:text-bright-gray">
<thead>
<tr>
<th className="border-r p-4 md:w-[244px]">Document Name</th>
<th className="w-[244px] border-r px-4 py-2">Vector Date</th>
<th className="w-[244px] border-r px-4 py-2">Token usage</th>
<th className="w-[244px] border-r px-4 py-2">Type</th>
<th className="border-r p-4 md:w-[244px]">
{t('settings.documents.name')}
</th>
<th className="w-[244px] border-r px-4 py-2">
{t('settings.documents.date')}
</th>
<th className="w-[244px] border-r px-4 py-2">
{t('settings.documents.tokenUsage')}
</th>
<th className="w-[244px] border-r px-4 py-2">
{t('settings.documents.type')}
</th>
<th className="px-4 py-2"></th>
</tr>
</thead>

View File

@@ -2,6 +2,7 @@ import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import Prompts from './Prompts';
import { useDarkTheme } from '../hooks';
import { useTranslation } from 'react-i18next';
import Dropdown from '../components/Dropdown';
import {
selectPrompt,
@@ -16,8 +17,22 @@ import {
const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com';
const General: React.FC = () => {
const {
t,
i18n: { changeLanguage, language },
} = useTranslation();
const themes = ['Light', 'Dark'];
const languages = ['English'];
const languageOptions = [
{
label: 'English',
value: 'en',
},
{
label: 'Spanish',
value: 'es',
},
];
const chunks = ['0', '2', '4', '6', '8', '10'];
const token_limits = new Map([
[0, 'None'],
@@ -37,7 +52,12 @@ const General: React.FC = () => {
isDarkTheme ? 'Dark' : 'Light',
);
const dispatch = useDispatch();
const [selectedLanguage, setSelectedLanguage] = React.useState(languages[0]);
const locale = localStorage.getItem('docsgpt-locale');
const [selectedLanguage, setSelectedLanguage] = React.useState(
locale
? languageOptions.find((option) => option.value === locale)
: languageOptions[0],
);
const selectedPrompt = useSelector(selectPrompt);
React.useEffect(() => {
@@ -59,7 +79,9 @@ const General: React.FC = () => {
return (
<div className="mt-[59px]">
<div className="mb-5">
<p className="font-bold text-jet dark:text-bright-gray">Select Theme</p>
<p className="font-bold text-jet dark:text-bright-gray">
{t('settings.general.selectTheme')}
</p>
<Dropdown
options={themes}
selectedValue={selectedTheme}
@@ -73,13 +95,17 @@ const General: React.FC = () => {
/>
</div>
<div className="mb-5">
<p className="font-bold text-jet dark:text-bright-gray">
Select Language
<p className="mb-2 font-bold text-jet dark:text-bright-gray">
{t('settings.general.selectLanguage')}
</p>
<Dropdown
options={languages}
selectedValue={selectedLanguage}
onSelect={setSelectedLanguage}
options={languageOptions}
selectedValue={selectedLanguage ?? languageOptions[0]}
onSelect={(selectedOption: { label: string; value: string }) => {
setSelectedLanguage(selectedOption);
changeLanguage(selectedOption.value);
localStorage.setItem('docsgpt-locale', selectedOption.value);
}}
size="w-56"
rounded="3xl"
border="border"
@@ -87,7 +113,7 @@ const General: React.FC = () => {
</div>
<div className="mb-5">
<p className="font-bold text-jet dark:text-bright-gray">
Chunks processed per query
{t('settings.general.chunks')}
</p>
<Dropdown
options={chunks}
@@ -136,13 +162,15 @@ const General: React.FC = () => {
</div>
<div className="w-56">
<p className="font-bold text-jet dark:text-bright-gray">
Delete all conversations
{t('settings.general.deleteAllLabel')}
</p>
<button
className="mt-2 flex w-full cursor-pointer items-center justify-between rounded-3xl border border-solid border-red-500 px-5 py-3 text-red-500 hover:bg-red-500 hover:text-white"
onClick={() => dispatch(setModalStateDeleteConv('ACTIVE'))}
>
<span className="overflow-hidden text-ellipsis ">Delete all</span>
<span className="overflow-hidden text-ellipsis ">
{t('settings.general.deleteAllBtn')}
</span>
</button>
</div>
</div>

View File

@@ -2,9 +2,8 @@ import React from 'react';
import { PromptProps, ActiveState } from '../models/misc';
import Dropdown from '../components/Dropdown';
import PromptsModal from '../preferences/PromptsModal';
import { useTranslation } from 'react-i18next';
const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com';
const Prompts: React.FC<PromptProps> = ({
prompts,
selectedPrompt,
@@ -34,7 +33,10 @@ const Prompts: React.FC<PromptProps> = ({
});
const [modalType, setModalType] = React.useState<'ADD' | 'EDIT'>('ADD');
const [modalState, setModalState] = React.useState<ActiveState>('INACTIVE');
const {
t,
i18n: { changeLanguage, language },
} = useTranslation();
const handleAddPrompt = async () => {
try {
const response = await fetch(`${apiHost}/api/create_prompt`, {
@@ -158,7 +160,9 @@ const Prompts: React.FC<PromptProps> = ({
<div>
<div className="flex flex-row items-center gap-8">
<div>
<p className="font-semibold dark:text-bright-gray">Active Prompt</p>
<p className="font-semibold dark:text-bright-gray">
{t('settings.general.prompt')}
</p>
<Dropdown
options={prompts}
selectedValue={selectedPrompt.name}
@@ -193,7 +197,7 @@ const Prompts: React.FC<PromptProps> = ({
setModalState('ACTIVE');
}}
>
Add new
{t('settings.general.addNew')}
</button>
</div>
</div>

View File

@@ -11,12 +11,18 @@ import {
import { Doc } from '../preferences/preferenceApi';
import ArrowLeft from '../assets/arrow-left.svg';
import ArrowRight from '../assets/arrow-right.svg';
import { useTranslation } from 'react-i18next';
const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com';
const Settings: React.FC = () => {
const dispatch = useDispatch();
const tabs = ['General', 'Documents', 'API Keys'];
const { t } = useTranslation();
const tabs = [
t('settings.general.label'),
t('settings.documents.label'),
t('settings.apiKeys.label'),
];
const [activeTab, setActiveTab] = React.useState('General');
const [widgetScreenshot, setWidgetScreenshot] = React.useState<File | null>(
null,
@@ -45,7 +51,7 @@ const Settings: React.FC = () => {
return (
<div className="wa p-4 pt-20 md:p-12">
<p className="text-2xl font-bold text-eerie-black dark:text-bright-gray">
Settings
{t('settings.label')}
</p>
<div className="mt-6 flex flex-row items-center space-x-4 overflow-x-auto md:space-x-8 ">
<div className="md:hidden">
@@ -100,9 +106,9 @@ const Settings: React.FC = () => {
function renderActiveTab() {
switch (activeTab) {
case 'General':
case t('settings.general.label'):
return <General />;
case 'Documents':
case t('settings.documents.label'):
return (
<Documents
documents={documents}
@@ -116,7 +122,7 @@ const Settings: React.FC = () => {
onWidgetScreenshotChange={updateWidgetScreenshot} // Add this line
/>
);
case 'API Keys':
case t('settings.apiKeys.label'):
return <APIKeys />;
default:
return null;