From 7765f4a19d46143f86c1dfd44b505b11a305f059 Mon Sep 17 00:00:00 2001 From: Sebastian Weise Date: Thu, 7 Dec 2023 21:04:57 +0100 Subject: [PATCH 01/28] Nextjs 14 upgrade && i18n fix --- apps/unsaged/Dockerfile | 2 +- .../unsaged/app/[locale]/NextIntlProvider.tsx | 34 +++ apps/unsaged/app/[locale]/layout.tsx | 69 +++++ apps/unsaged/app/{ => [locale]}/page.tsx | 0 apps/unsaged/app/layout.tsx | 48 +-- .../ChatZone/Screens/Chat/ChatInput.tsx | 4 +- .../ChatZone/Screens/Chat/ChatMessage.tsx | 4 +- .../ChatZone/Screens/Chat/Regenerate.tsx | 4 +- .../ChatZone/Screens/Settings/Settings.tsx | 4 +- .../Screens/Conversations/Conversations.tsx | 4 +- .../components/ClearConversations.tsx | 4 +- .../components/ConversationsSettings.tsx | 4 +- .../components/Screens/Prompts/Prompts.tsx | 4 +- .../Prompts/components/PromptModal.tsx | 4 +- .../Screens/SystemPrompts/SystemPrompts.tsx | 4 +- .../components/SystemPromptEditModal.tsx | 4 +- .../components/SystemPromptModal.tsx | 4 +- .../components/frequency-penalty.tsx | 4 +- .../ModelSettings/components/max-tokens.tsx | 4 +- .../ModelSettings/components/model.tsx | 4 +- .../components/presence-penalty.tsx | 4 +- .../Screens/ModelSettings/components/seed.tsx | 4 +- .../ModelSettings/components/stop-input.tsx | 4 +- .../components/system-prompt.tsx | 4 +- .../ModelSettings/components/temperature.tsx | 4 +- .../ModelSettings/components/top-k.tsx | 4 +- .../ModelSettings/components/top-p.tsx | 4 +- .../Home/components/Settings/Import.tsx | 4 +- .../unsaged/components/Markdown/CodeBlock.tsx | 4 +- apps/unsaged/components/common/Search.tsx | 4 +- .../components/common/Sidebar/Sidebar.tsx | 4 +- apps/unsaged/i18n.ts | 10 + apps/unsaged/messages/de.json | 63 ++++ apps/unsaged/messages/en.json | 64 ++++ apps/unsaged/middleware.ts | 79 +++-- apps/unsaged/next-i18next.config.js | 34 --- apps/unsaged/next.config.js | 5 +- apps/unsaged/package.json | 9 +- apps/unsaged/playwright.config.ts | 24 ++ apps/unsaged/services/errorService.ts | 30 +- apps/unsaged/tests/main.spec.ts | 74 +++++ package.json | 1 + pnpm-lock.yaml | 285 ++++++++++-------- turbo.json | 3 +- 44 files changed, 646 insertions(+), 292 deletions(-) create mode 100644 apps/unsaged/app/[locale]/NextIntlProvider.tsx create mode 100644 apps/unsaged/app/[locale]/layout.tsx rename apps/unsaged/app/{ => [locale]}/page.tsx (100%) create mode 100644 apps/unsaged/i18n.ts create mode 100644 apps/unsaged/messages/de.json create mode 100644 apps/unsaged/messages/en.json delete mode 100644 apps/unsaged/next-i18next.config.js create mode 100644 apps/unsaged/playwright.config.ts create mode 100644 apps/unsaged/tests/main.spec.ts diff --git a/apps/unsaged/Dockerfile b/apps/unsaged/Dockerfile index 439f6185..af28d9e9 100644 --- a/apps/unsaged/Dockerfile +++ b/apps/unsaged/Dockerfile @@ -41,7 +41,7 @@ COPY --from=dependencies /unsaged/node_modules ./node_modules COPY --from=build /unsaged/apps/unsaged/.next ./apps/unsaged/.next COPY --from=build /unsaged/apps/unsaged/public ./apps/unsaged/public COPY --from=build /unsaged/apps/unsaged/next.config.js ./apps/unsaged/next.config.js -COPY --from=build /unsaged/apps/unsaged/next-i18next.config.js ./apps/unsaged/next-i18next.config.js +COPY --from=build /unsaged/apps/unsaged/next-i18next.config.js ./apps/unsaged/i18n.ts COPY --from=build /unsaged/apps/unsaged/package.json ./apps/unsaged/package.json # Expose the port the app will run on diff --git a/apps/unsaged/app/[locale]/NextIntlProvider.tsx b/apps/unsaged/app/[locale]/NextIntlProvider.tsx new file mode 100644 index 00000000..2767a542 --- /dev/null +++ b/apps/unsaged/app/[locale]/NextIntlProvider.tsx @@ -0,0 +1,34 @@ +"use client"; + +import { ReactNode } from "react"; +import { NextIntlClientProvider, AbstractIntlMessages } from "next-intl"; + +type Props = { + messages: AbstractIntlMessages; + locale: string; + children: ReactNode; + now: Date; + timeZone: string; +}; + +export default function NextIntlProvider({ + messages, + locale, + children, + now, + timeZone, +}: Props) { + return ( + {text}, + }} + now={now} + timeZone={timeZone} + > + {children} + + ); +} diff --git a/apps/unsaged/app/[locale]/layout.tsx b/apps/unsaged/app/[locale]/layout.tsx new file mode 100644 index 00000000..74398fef --- /dev/null +++ b/apps/unsaged/app/[locale]/layout.tsx @@ -0,0 +1,69 @@ +import { DEFAULT_DESCRIPTION, DEFAULT_TITLE } from '@/utils/app/const'; + +import { Analytics } from '@vercel/analytics/react'; +import { AxiomWebVitals } from 'next-axiom'; + +import { Metadata, Viewport } from 'next'; + +import { ThemeProvider } from '@/components/common/ui/theme-provider'; + +import '@/styles/globals.css'; +import NextIntlProvider from './NextIntlProvider'; +import { notFound } from 'next/navigation'; +import { ReactNode } from 'react'; + +type Props = { + children: ReactNode; + params: { locale: string }; +}; + +export const metadata: Metadata = { + title: DEFAULT_TITLE, + description: DEFAULT_DESCRIPTION, +}; + +export const viewport: Viewport = { + height: 'device-height', + width: 'device-width', + initialScale: 1, +} + +export function generateStaticParams() { + return [{ locale: "en" }]; +} + +export default async function LocaleLayout({ + children, + params: { locale } +}: Props) { + let messages; + try { + messages = (await import(`../../messages/${locale}.json`)).default; + } catch (error) { + notFound(); + } + + return ( + + + + + + + {children} + + + + + ); +} diff --git a/apps/unsaged/app/page.tsx b/apps/unsaged/app/[locale]/page.tsx similarity index 100% rename from apps/unsaged/app/page.tsx rename to apps/unsaged/app/[locale]/page.tsx diff --git a/apps/unsaged/app/layout.tsx b/apps/unsaged/app/layout.tsx index 60ef6cb3..baf8fa62 100644 --- a/apps/unsaged/app/layout.tsx +++ b/apps/unsaged/app/layout.tsx @@ -1,42 +1,12 @@ -import { DEFAULT_DESCRIPTION, DEFAULT_TITLE } from '@/utils/app/const'; +import { ReactNode } from 'react'; -import { Analytics } from '@vercel/analytics/react'; -import { AxiomWebVitals } from 'next-axiom'; - -import { Metadata } from 'next'; - -import { ThemeProvider } from '@/components/common/ui/theme-provider'; - -import '@/styles/globals.css'; - -export const metadata: Metadata = { - title: DEFAULT_TITLE, - description: DEFAULT_DESCRIPTION, - viewport: - 'height=device-height, width=device-width, initial-scale=1, user-scalable=no', +type Props = { + children: ReactNode; }; -export default function RootLayout({ - // Layouts must accept a children prop. - // This will be populated with nested layouts or pages - children, -}: { - children: React.ReactNode; -}) { - return ( - - - - - - {children} - - - - ); -} +// Even though this component is just passing its children through, the presence +// of this file fixes an issue in Next.js 13.3.0 where link clicks that switch +// the locale would otherwise be ignored. +export default function RootLayout({ children }: Props) { + return children; +} \ No newline at end of file diff --git a/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/ChatInput.tsx b/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/ChatInput.tsx index 3e3092c5..85e3938f 100644 --- a/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/ChatInput.tsx +++ b/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/ChatInput.tsx @@ -14,7 +14,7 @@ import { useState, } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import { Conversation, Message } from '@/types/chat'; import { Prompt } from '@/types/prompt'; @@ -44,7 +44,7 @@ export const ChatInput = ({ textareaRef, showScrollDownButton, }: Props) => { - const { t } = useTranslation('chat'); + const t = useTranslations('chat'); const { state: { selectedConversation, messageIsStreaming, prompts }, diff --git a/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/ChatMessage.tsx b/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/ChatMessage.tsx index 699cacfb..b24d1375 100644 --- a/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/ChatMessage.tsx +++ b/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/ChatMessage.tsx @@ -8,7 +8,7 @@ import { } from '@tabler/icons-react'; import { FC, memo, useContext, useEffect, useRef, useState } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import { storageDeleteMessages } from '@/utils/app/storage/messages'; @@ -35,7 +35,7 @@ export interface Props { export const ChatMessage: FC = memo( ({ message, messageIndex, onEdit }) => { - const { t } = useTranslation('chat'); + const t = useTranslations('chat'); const { state: { diff --git a/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/Regenerate.tsx b/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/Regenerate.tsx index d36c3e48..188f944a 100644 --- a/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/Regenerate.tsx +++ b/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/Regenerate.tsx @@ -1,14 +1,14 @@ import { IconRefresh } from '@tabler/icons-react'; import { FC } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; interface Props { onRegenerate: () => void; } export const Regenerate: FC = ({ onRegenerate }) => { - const { t } = useTranslation('chat'); + const t = useTranslations('chat'); return (
diff --git a/apps/unsaged/components/Home/components/ChatZone/Screens/Settings/Settings.tsx b/apps/unsaged/components/Home/components/ChatZone/Screens/Settings/Settings.tsx index 7739c53c..505b069a 100644 --- a/apps/unsaged/components/Home/components/ChatZone/Screens/Settings/Settings.tsx +++ b/apps/unsaged/components/Home/components/ChatZone/Screens/Settings/Settings.tsx @@ -1,7 +1,7 @@ import { IconDeviceFloppy, IconX } from '@tabler/icons-react'; import { useContext, useEffect } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import { useCreateReducer } from '@/hooks/useCreateReducer'; @@ -19,7 +19,7 @@ import SettingsContext from './Settings.context'; import { SettingsInitialState, initialState } from './Settings.state'; export const Settings = () => { - const { t } = useTranslation('settings'); + const t = useTranslations('settings'); const settingsContextValue = useCreateReducer({ initialState, diff --git a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/Conversations.tsx b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/Conversations.tsx index c9d58eee..36543b64 100644 --- a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/Conversations.tsx +++ b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/Conversations.tsx @@ -1,7 +1,7 @@ import { IconFolderPlus, IconMistOff } from '@tabler/icons-react'; import { useContext, useEffect } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import { useCreateReducer } from '@/hooks/useCreateReducer'; @@ -42,7 +42,7 @@ import { ConversationsInitialState, initialState } from './Conversations.state'; import { v4 as uuidv4 } from 'uuid'; export const Conversations = () => { - const { t } = useTranslation('conversations'); + const t = useTranslations('conversations'); const conversationsContextValue = useCreateReducer( { diff --git a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/components/ClearConversations.tsx b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/components/ClearConversations.tsx index 83670646..e1fe9805 100644 --- a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/components/ClearConversations.tsx +++ b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/components/ClearConversations.tsx @@ -1,7 +1,7 @@ import { IconCheck, IconTrash, IconX } from '@tabler/icons-react'; import { FC, useState } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import { SidebarButton } from '@/components/common/Sidebar/SidebarButton'; @@ -12,7 +12,7 @@ interface Props { export const ClearConversations: FC = ({ onClearConversations }) => { const [isConfirming, setIsConfirming] = useState(false); - const { t } = useTranslation('sidebar'); + const t = useTranslations('sidebar'); const handleClearConversations = () => { onClearConversations(); diff --git a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/components/ConversationsSettings.tsx b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/components/ConversationsSettings.tsx index 25c1b37d..0f2ce932 100644 --- a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/components/ConversationsSettings.tsx +++ b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/components/ConversationsSettings.tsx @@ -1,7 +1,7 @@ import { IconFileExport, IconLogout, IconSettings } from '@tabler/icons-react'; import { useContext } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import { Import } from '@/components/Home/components/Settings/Import'; import HomeContext from '@/components/Home/home.context'; @@ -11,7 +11,7 @@ import ConversationsContext from '../Conversations.context'; import { ClearConversations } from './ClearConversations'; export const ConversationsSettings = () => { - const { t } = useTranslation('sidebar'); + const t = useTranslations('sidebar'); const { state: { database, conversations }, diff --git a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Prompts/Prompts.tsx b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Prompts/Prompts.tsx index 84a616fd..fcc659a5 100644 --- a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Prompts/Prompts.tsx +++ b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Prompts/Prompts.tsx @@ -1,6 +1,6 @@ import { IconFolderPlus, IconMistOff, IconPlus } from '@tabler/icons-react'; import { useContext, useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; +import { useTranslations } from 'next-intl'; import { useCreateReducer } from '@/hooks/useCreateReducer'; @@ -25,7 +25,7 @@ import { PromptsInitialState, initialState } from './Prompts.state'; import { v4 as uuidv4 } from 'uuid'; const Prompts = () => { - const { t } = useTranslation('promptbar'); + const t = useTranslations('promptbar'); const promptBarContextValue = useCreateReducer({ initialState, diff --git a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Prompts/components/PromptModal.tsx b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Prompts/components/PromptModal.tsx index 1baf2f93..6df1886e 100644 --- a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Prompts/components/PromptModal.tsx +++ b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Prompts/components/PromptModal.tsx @@ -1,6 +1,6 @@ import { FC, KeyboardEvent, useEffect, useRef, useState } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import { Prompt } from '@/types/prompt'; @@ -13,7 +13,7 @@ interface Props { } export const PromptModal: FC = ({ prompt, onClose, onUpdatePrompt }) => { - const { t } = useTranslation('promptbar'); + const t = useTranslations('promptbar'); const [name, setName] = useState(prompt.name); const [description, setDescription] = useState(prompt.description); const [content, setContent] = useState(prompt.content); diff --git a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/SystemPrompts/SystemPrompts.tsx b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/SystemPrompts/SystemPrompts.tsx index b9a0f33f..e5838f1b 100644 --- a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/SystemPrompts/SystemPrompts.tsx +++ b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/SystemPrompts/SystemPrompts.tsx @@ -1,6 +1,6 @@ import { IconFolderPlus, IconMistOff } from '@tabler/icons-react'; import { useContext, useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; +import { useTranslations } from 'next-intl'; import { useCreateReducer } from '@/hooks/useCreateReducer'; @@ -26,7 +26,7 @@ import { SystemPromptsInitialState, initialState } from './SystemPrompts.state'; import { v4 as uuidv4 } from 'uuid'; const SystemPrompts = () => { - const { t } = useTranslation('systemPrompts'); + const t = useTranslations('systemPrompts'); const systemPromptsContextValue = useCreateReducer( { diff --git a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/SystemPrompts/components/SystemPromptEditModal.tsx b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/SystemPrompts/components/SystemPromptEditModal.tsx index 366d9926..d7ea8651 100644 --- a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/SystemPrompts/components/SystemPromptEditModal.tsx +++ b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/SystemPrompts/components/SystemPromptEditModal.tsx @@ -7,7 +7,7 @@ import { useState, } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import { SystemPrompt } from '@/types/system-prompt'; @@ -24,7 +24,7 @@ interface Props { export const SystemPromptEditModal: FC = ({ systemPrompt, onClose }) => { const { handleUpdateSystemPrompt } = useContext(SystemPromptsContext); - const { t } = useTranslation('systemPrompt'); + const t = useTranslations('systemPrompt'); const [name, setName] = useState(systemPrompt.name); const [content, setContent] = useState(systemPrompt.content); diff --git a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/SystemPrompts/components/SystemPromptModal.tsx b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/SystemPrompts/components/SystemPromptModal.tsx index 1baf2f93..6df1886e 100644 --- a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/SystemPrompts/components/SystemPromptModal.tsx +++ b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/SystemPrompts/components/SystemPromptModal.tsx @@ -1,6 +1,6 @@ import { FC, KeyboardEvent, useEffect, useRef, useState } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import { Prompt } from '@/types/prompt'; @@ -13,7 +13,7 @@ interface Props { } export const PromptModal: FC = ({ prompt, onClose, onUpdatePrompt }) => { - const { t } = useTranslation('promptbar'); + const t = useTranslations('promptbar'); const [name, setName] = useState(prompt.name); const [description, setDescription] = useState(prompt.description); const [content, setContent] = useState(prompt.content); diff --git a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/frequency-penalty.tsx b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/frequency-penalty.tsx index 2b80bf59..06b3d61f 100644 --- a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/frequency-penalty.tsx +++ b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/frequency-penalty.tsx @@ -1,6 +1,6 @@ import { useContext, useEffect, useMemo, useState } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import HomeContext from '@/components/Home/home.context'; import { PrimaryLabel } from '@/components/common/Labels/PrimaryLabel'; @@ -8,7 +8,7 @@ import { Slider } from '@/components/common/ui/slider'; import { Switch } from '@/components/common/ui/switch'; export const RepeatPenaltySlider = () => { - const { t } = useTranslation('chat'); + const t = useTranslations('chat'); const { state: { selectedConversation }, handleUpdateConversationParams, diff --git a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/max-tokens.tsx b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/max-tokens.tsx index 07b89a1e..e8a4b36c 100644 --- a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/max-tokens.tsx +++ b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/max-tokens.tsx @@ -1,6 +1,6 @@ import { useContext, useEffect, useMemo, useState } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import { PossibleAiModels } from '@/types/ai-models'; @@ -10,7 +10,7 @@ import { Slider } from '@/components/common/ui/slider'; import { Switch } from '@/components/common/ui/switch'; export const MaxTokensSlider = () => { - const { t } = useTranslation('chat'); + const t = useTranslations('chat'); const { state: { selectedConversation }, handleUpdateConversationParams, diff --git a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/model.tsx b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/model.tsx index 2bc58455..c8f67eff 100644 --- a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/model.tsx +++ b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/model.tsx @@ -1,5 +1,5 @@ import { useContext, useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; +import { useTranslations } from 'next-intl'; import { AiModel } from '@/types/ai-models'; @@ -43,7 +43,7 @@ export const ModelSelect = () => { value: selectedModel, }); }; - const { t } = useTranslation('modelSettings'); + const t = useTranslations('modelSettings'); return (
diff --git a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/presence-penalty.tsx b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/presence-penalty.tsx index fd786380..741853a8 100644 --- a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/presence-penalty.tsx +++ b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/presence-penalty.tsx @@ -1,6 +1,6 @@ import { useContext, useEffect, useMemo, useState } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import HomeContext from '@/components/Home/home.context'; import { PrimaryLabel } from '@/components/common/Labels/PrimaryLabel'; @@ -8,7 +8,7 @@ import { Slider } from '@/components/common/ui/slider'; import { Switch } from '@/components/common/ui/switch'; export const PresencePenaltySlider = () => { - const { t } = useTranslation('chat'); + const t = useTranslations('chat'); const { state: { selectedConversation }, handleUpdateConversationParams, diff --git a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/seed.tsx b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/seed.tsx index caf31aec..661c9e0c 100644 --- a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/seed.tsx +++ b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/seed.tsx @@ -1,6 +1,6 @@ import { useContext, useEffect, useMemo, useState } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import HomeContext from '@/components/Home/home.context'; import { PrimaryLabel } from '@/components/common/Labels/PrimaryLabel'; @@ -8,7 +8,7 @@ import { Input } from '@/components/common/ui/input'; import { Switch } from '@/components/common/ui/switch'; export const SeedInput = () => { - const { t } = useTranslation('chat'); + const t = useTranslations('chat'); const { state: { selectedConversation }, handleUpdateConversationParams, diff --git a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/stop-input.tsx b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/stop-input.tsx index d9611232..8bd4c765 100644 --- a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/stop-input.tsx +++ b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/stop-input.tsx @@ -1,13 +1,13 @@ import { useContext, useEffect, useMemo, useState } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import HomeContext from '@/components/Home/home.context'; import { PrimaryLabel } from '@/components/common/Labels/PrimaryLabel'; import { Input } from '@/components/common/ui/input'; export const StopInput = () => { - const { t } = useTranslation('chat'); + const t = useTranslations('chat'); const { state: { selectedConversation }, handleUpdateConversationParams, diff --git a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/system-prompt.tsx b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/system-prompt.tsx index 24629a7f..dd976429 100644 --- a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/system-prompt.tsx +++ b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/system-prompt.tsx @@ -1,5 +1,5 @@ import { useCallback, useContext, useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; +import { useTranslations } from 'next-intl'; import { PossibleAiModels } from '@/types/ai-models'; import { SystemPrompt } from '@/types/system-prompt'; @@ -111,7 +111,7 @@ export const SystemPromptSelect = () => { }); }; - const { t } = useTranslation('modelSettings'); + const t = useTranslations('modelSettings'); return (
diff --git a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/temperature.tsx b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/temperature.tsx index a2a30039..2ecb08a7 100644 --- a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/temperature.tsx +++ b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/temperature.tsx @@ -1,6 +1,6 @@ import { useContext, useEffect, useMemo, useState } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import { getModelDefaults } from '@/utils/app/settings/model-defaults'; @@ -10,7 +10,7 @@ import { Slider } from '@/components/common/ui/slider'; import { Switch } from '@/components/common/ui/switch'; export const TemperatureSlider = () => { - const { t } = useTranslation('chat'); + const t = useTranslations('chat'); const { state: { selectedConversation }, handleUpdateConversationParams, diff --git a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/top-k.tsx b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/top-k.tsx index 7613cd07..059d1f98 100644 --- a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/top-k.tsx +++ b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/top-k.tsx @@ -1,13 +1,13 @@ import { useContext, useEffect, useState } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import HomeContext from '@/components/Home/home.context'; import { PrimaryLabel } from '@/components/common/Labels/PrimaryLabel'; import { Input } from '@/components/common/ui/input'; export const TopKInput = () => { - const { t } = useTranslation('chat'); + const t = useTranslations('chat'); const { state: { selectedConversation }, handleUpdateConversationParams, diff --git a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/top-p.tsx b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/top-p.tsx index 4e8cd146..f8cb45db 100644 --- a/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/top-p.tsx +++ b/apps/unsaged/components/Home/components/SecondaryMenu/components/Menu/components/Screens/ModelSettings/components/top-p.tsx @@ -1,6 +1,6 @@ import { useContext, useEffect, useMemo, useState } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import { PossibleAiModels } from '@/types/ai-models'; @@ -10,7 +10,7 @@ import { Slider } from '@/components/common/ui/slider'; import { Switch } from '@/components/common/ui/switch'; export const TopPSlider = () => { - const { t } = useTranslation('chat'); + const t = useTranslations('chat'); const { state: { selectedConversation }, handleUpdateConversationParams, diff --git a/apps/unsaged/components/Home/components/Settings/Import.tsx b/apps/unsaged/components/Home/components/Settings/Import.tsx index a4368f9c..e783a73c 100644 --- a/apps/unsaged/components/Home/components/Settings/Import.tsx +++ b/apps/unsaged/components/Home/components/Settings/Import.tsx @@ -1,7 +1,7 @@ import { IconFileImport } from '@tabler/icons-react'; import { FC, useContext } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import { SupportedExportFormats } from '@/types/export'; import { SystemPrompt } from '@/types/system-prompt'; @@ -18,7 +18,7 @@ interface Props { } export const Import: FC = ({ onImport }) => { - const { t } = useTranslation('sidebar'); + const t = useTranslations('sidebar'); const { state: { systemPrompts }, diff --git a/apps/unsaged/components/Markdown/CodeBlock.tsx b/apps/unsaged/components/Markdown/CodeBlock.tsx index 06bc3d57..4b9253bc 100644 --- a/apps/unsaged/components/Markdown/CodeBlock.tsx +++ b/apps/unsaged/components/Markdown/CodeBlock.tsx @@ -3,7 +3,7 @@ import { FC, memo, useState } from 'react'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import { generateRandomString, @@ -16,7 +16,7 @@ interface Props { } export const CodeBlock: FC = memo(({ language, value }) => { - const { t } = useTranslation('markdown'); + const t = useTranslations('markdown'); const [isCopied, setIsCopied] = useState(false); const copyToClipboard = () => { diff --git a/apps/unsaged/components/common/Search.tsx b/apps/unsaged/components/common/Search.tsx index 85159fa2..1f3701ec 100644 --- a/apps/unsaged/components/common/Search.tsx +++ b/apps/unsaged/components/common/Search.tsx @@ -1,7 +1,7 @@ import { IconX } from '@tabler/icons-react'; import { FC } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; interface Props { placeholder: string; @@ -9,7 +9,7 @@ interface Props { onSearch: (searchTerm: string) => void; } const Search: FC = ({ placeholder, searchTerm, onSearch }) => { - const { t } = useTranslation('sidebar'); + const t = useTranslations('sidebar'); const handleSearchChange = (e: React.ChangeEvent) => { onSearch(e.target.value); diff --git a/apps/unsaged/components/common/Sidebar/Sidebar.tsx b/apps/unsaged/components/common/Sidebar/Sidebar.tsx index bcbca7f9..e0c40102 100644 --- a/apps/unsaged/components/common/Sidebar/Sidebar.tsx +++ b/apps/unsaged/components/common/Sidebar/Sidebar.tsx @@ -1,6 +1,6 @@ import { IconFolderPlus, IconMistOff, IconPlus } from '@tabler/icons-react'; import { ReactNode } from 'react'; -import { useTranslation } from 'react-i18next'; +import { useTranslations } from 'next-intl'; import { CloseSidebarButton, @@ -40,7 +40,7 @@ const Sidebar = ({ handleCreateFolder, handleDrop, }: Props) => { - const { t } = useTranslation('promptbar'); + const t = useTranslations('promptbar'); const allowDrop = (e: any) => { e.preventDefault(); diff --git a/apps/unsaged/i18n.ts b/apps/unsaged/i18n.ts new file mode 100644 index 00000000..25bff52a --- /dev/null +++ b/apps/unsaged/i18n.ts @@ -0,0 +1,10 @@ +import { getRequestConfig } from 'next-intl/server'; + +export default getRequestConfig(async ({ locale }) => ({ + messages: ( + await (locale === 'en' + ? // When using Turbopack, this will enable HMR for `en` + import('./messages/en.json') + : import(`./messages/${locale}.json`)) + ).default +})); \ No newline at end of file diff --git a/apps/unsaged/messages/de.json b/apps/unsaged/messages/de.json new file mode 100644 index 00000000..6b8e014c --- /dev/null +++ b/apps/unsaged/messages/de.json @@ -0,0 +1,63 @@ +{ + "chat": { + "OpenAI API Key Required": "OpenAI API-Schlüssel erforderlich", + "Please set your OpenAI API key in the bottom left of the sidebar.": "Bitte trage deinen OpenAI API-Schlüssel in der linken unteren Ecke der Seitenleiste ein.", + "Stop Generating": "Generieren stoppen", + "Prompt limit is {{maxLength}} characters": "Das Eingabelimit liegt bei {{maxLength}} Zeichen", + "System Prompt": "Systemaufforderung", + "You are ChatGPT, a large language model trained by OpenAI. Follow the user's instructions carefully. Respond using markdown.": "Du bist ChatGPT, ein großes Sprachmodell, das von OpenAI trainiert wurde. Befolge die Anweisungen des Benutzers sorgfältig. Antworte in Markdown-Format.", + "Enter a prompt": "Gib eine Anweisung ein", + "Regenerate response": "Antwort erneut generieren", + "Sorry, there was an error.": "Entschuldigung, es ist ein Fehler aufgetreten.", + "Model": "Modell", + "Conversation": "Konversation", + "OR": "ODER", + "Loading...": "Laden...", + "Type a message...": "Schreibe eine Nachricht...", + "Error fetching models.": "Fehler beim Abrufen der Sprachmodelle.", + "AI": "KI", + "You": "Du", + "Cancel": "Cancel", + "Save & Submit": "Save & Submit", + "Make sure your OpenAI API key is set in the bottom left of the sidebar.": "Stelle sicher, dass dein OpenAI API-Schlüssel in der unteren linken Ecke der Seitenleiste eingetragen ist.", + "If you completed this step, OpenAI may be experiencing issues.": "Wenn dies der Fall ist, könnte OpenAI möglicherweise momentan Probleme haben.", + "click if using a .env.local file": "click if using a .env.local file", + "Message limit is {{maxLength}} characters. You have entered {{valueLength}} characters.": "Das Nachrichtenlimit beträgt {{maxLength}} Zeichen. Du hast bereits {{valueLength}} Zeichen eingegeben.", + "Please enter a message": "Bitte gib eine Nachricht ein", + "unSAGED is an advanced chat kit for interacting with AI models.": "unSAGED ist ein fortschrittliches Chatbot-Toolkit für OpenAI's Chat-Modelle, das darauf abzielt, die Benutzeroberfläche und Funktionalität von ChatGPT nachzuahmen.", + "Are you sure you want to clear all messages?": "Bist du sicher, dass du alle Nachrichten löschen möchtest?", + "Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.": "Höhere Werte wie 0,8 machen die Ausgabe zufälliger, während niedrigere Werte wie 0,2 sie fokussierter und deterministischer machen werden." + }, + "markdown": { + "Copy code": "Code kopieren", + "Copied!": "Kopiert!", + "Enter file name": "Dateinamen eingeben" + }, + "promptbar": { + "New prompt": "New prompt", + "New folder": "New folder", + "No prompts.": "No prompts.", + "Search prompts...": "Search prompts...", + "Name": "Name", + "Description": "Description", + "A description for your prompt.": "A description for your prompt.", + "Prompt": "Prompt", + "Prompt content. Use {{}} to denote a variable. Ex: {{name}} is a {{adjective}} {{noun}}": "Prompt content. Use {{}} to denote a variable. Ex: {{name}} is a {{adjective}} {{noun}}", + "Save": "Save" + }, + "settings": { + "Dark mode": "Dark Mode", + "Light mode": "Light Mode" + }, + "sidebar": { + "New folder": "Neuer Ordner", + "New chat": "Neue Konversation", + "No conversations.": "Keine Konversationen.", + "Search conversations...": "Konversationen suchen...", + "OpenAI API Key": "OpenAI API-Schlüssel", + "Import data": "Konversationen importieren", + "Are you sure?": "Bist du sicher?", + "Clear conversations": "Konversationen löschen", + "Export data": "Konversationen exportieren" + } +} diff --git a/apps/unsaged/messages/en.json b/apps/unsaged/messages/en.json new file mode 100644 index 00000000..287eb794 --- /dev/null +++ b/apps/unsaged/messages/en.json @@ -0,0 +1,64 @@ +{ + "chat": { + "OpenAI API Key Required": "OpenAI API-Schlüssel erforderlich", + "Please set your OpenAI API key in the bottom left of the sidebar.": "Bitte trage deinen OpenAI API-Schlüssel in der linken unteren Ecke der Seitenleiste ein.", + "Stop Generating": "Generieren stoppen", + "Prompt limit is {{maxLength}} characters": "Das Eingabelimit liegt bei {{maxLength}} Zeichen", + "System Prompt": "Systemaufforderung", + "You are ChatGPT, a large language model trained by OpenAI. Follow the user's instructions carefully. Respond using markdown.": "Du bist ChatGPT, ein großes Sprachmodell, das von OpenAI trainiert wurde. Befolge die Anweisungen des Benutzers sorgfältig. Antworte in Markdown-Format.", + "Enter a prompt": "Gib eine Anweisung ein", + "Regenerate response": "Antwort erneut generieren", + "Sorry, there was an error.": "Entschuldigung, es ist ein Fehler aufgetreten.", + "Model": "Modell", + "Conversation": "Konversation", + "OR": "ODER", + "Loading...": "Laden...", + "Type a message...": "Schreibe eine Nachricht...", + "Error fetching models.": "Fehler beim Abrufen der Sprachmodelle.", + "AI": "KI", + "You": "Du", + "Cancel": "Cancel", + "Save & Submit": "Save & Submit", + "Make sure your OpenAI API key is set in the bottom left of the sidebar.": "Stelle sicher, dass dein OpenAI API-Schlüssel in der unteren linken Ecke der Seitenleiste eingetragen ist.", + "If you completed this step, OpenAI may be experiencing issues.": "Wenn dies der Fall ist, könnte OpenAI möglicherweise momentan Probleme haben.", + "click if using a .env.local file": "click if using a .env.local file", + "Message limit is {{maxLength}} characters. You have entered {{valueLength}} characters.": "Das Nachrichtenlimit beträgt {{maxLength}} Zeichen. Du hast bereits {{valueLength}} Zeichen eingegeben.", + "Please enter a message": "Bitte gib eine Nachricht ein", + "unSAGED is an advanced chat kit for interacting with AI models.": "unSAGED ist ein fortschrittliches Chatbot-Toolkit für OpenAI's Chat-Modelle, das darauf abzielt, die Benutzeroberfläche und Funktionalität von ChatGPT nachzuahmen.", + "Are you sure you want to clear all messages?": "Bist du sicher, dass du alle Nachrichten löschen möchtest?", + "Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.": "Höhere Werte wie 0,8 machen die Ausgabe zufälliger, während niedrigere Werte wie 0,2 sie fokussierter und deterministischer machen werden." + }, + "markdown": { + "Copy code": "Code kopieren", + "Copied!": "Kopiert!", + "Enter file name": "Dateinamen eingeben" + }, + "promptbar": { + "New prompt": "New prompt", + "New folder": "New folder", + "No prompts.": "No prompts.", + "Search prompts...": "Search prompts...", + "Name": "Name", + "Description": "Description", + "A description for your prompt.": "A description for your prompt.", + "Prompt": "Prompt", + "Prompt content. Use {{}} to denote a variable. Ex: {{name}} is a {{adjective}} {{noun}}": "Prompt content. Use {{}} to denote a variable. Ex: {{name}} is a {{adjective}} {{noun}}", + "Save": "Save" + }, + "settings": { + "Dark mode": "Dark Mode", + "Light mode": "Light Mode" + }, + "sidebar": { + "New folder": "Neuer Ordner", + "New chat": "Neue Konversation", + "No conversations.": "Keine Konversationen.", + "Search conversations...": "Konversationen suchen...", + "OpenAI API Key": "OpenAI API-Schlüssel", + "Import data": "Konversationen importieren", + "Are you sure?": "Bist du sicher?", + "Clear conversations": "Konversationen löschen", + "Export data": "Konversationen exportieren" + } + } + \ No newline at end of file diff --git a/apps/unsaged/middleware.ts b/apps/unsaged/middleware.ts index 73c90549..2bf75ee1 100644 --- a/apps/unsaged/middleware.ts +++ b/apps/unsaged/middleware.ts @@ -1,7 +1,15 @@ import { withAuth } from 'next-auth/middleware'; +import createMiddleware from 'next-intl/middleware'; import { get } from '@vercel/edge-config'; import { dockerEnvVarFix } from './utils/app/docker/envFix'; +import { NextRequest } from 'next/server'; + +export const locales = ["en", "de"] as const; + +const publicPages = [ + '/', +]; const getSecret = () => { return dockerEnvVarFix(process.env.NEXTAUTH_SECRET); @@ -16,28 +24,57 @@ const getEmailPatterns = async () => { return patternsString ? patternsString.split(',') : []; }; -export default withAuth({ - callbacks: { - async authorized({ token }) { - if (!token?.email) { - return false; - } else { - const patterns = await getEmailPatterns(); - if (patterns.length === 0) { - return true; // No patterns specified, allow access - } +const intlMiddleware = createMiddleware({ + locales: locales, + localePrefix: 'as-needed', + defaultLocale: 'en' +}); - const email = token.email.toLowerCase(); - for (const pattern of patterns) { - const regex = new RegExp(pattern.trim()); - if (email.match(regex)) { - return true; // Email matches one of the patterns, allow access +const authMiddleware = withAuth( + (req) => intlMiddleware(req), + { + callbacks: { + async authorized({ token }) { + if (!token?.email) { + return false; + } else { + const patterns = await getEmailPatterns(); + if (patterns.length === 0) { + return true; // No patterns specified, allow access + } + + const email = token.email.toLowerCase(); + for (const pattern of patterns) { + const regex = new RegExp(pattern.trim()); + if (email.match(regex)) { + return true; // Email matches one of the patterns, allow access + } } - } - return false; // Email does not match any of the patterns, deny access - } + return false; // Email does not match any of the patterns, deny access + } + }, }, - }, - secret: getSecret(), -}); + secret: getSecret(), + }); + +export default function middleware(req: NextRequest) { + const publicPathnameRegex = RegExp( + `^(/(${locales.join('|')}))?(${publicPages + .flatMap((p) => (p === '/' ? ['', '/'] : p)) + .join('|')})/?$`, + 'i' + ); + const isPublicPage = publicPathnameRegex.test(req.nextUrl.pathname); + + if (isPublicPage) { + return intlMiddleware(req); + } else { + return (authMiddleware as any)(req); + } +} + +export const config = { + // Skip all paths that should not be internationalized + matcher: ['/((?!api|_next|.*\\..*).*)'] +}; \ No newline at end of file diff --git a/apps/unsaged/next-i18next.config.js b/apps/unsaged/next-i18next.config.js deleted file mode 100644 index 2e015ea8..00000000 --- a/apps/unsaged/next-i18next.config.js +++ /dev/null @@ -1,34 +0,0 @@ -module.exports = { - i18n: { - localeDetection: false, - defaultLocale: 'en', - locales: [ - "bn", - "de", - "en", - "es", - "fr", - "he", - "id", - "it", - "ja", - "ko", - "pl", - "pt", - "ru", - "ro", - "sv", - "te", - "vi", - "zh", - "ar", - "tr", - "ca", - "fi", - ], - }, - localePath: - typeof window === 'undefined' - ? require('path').resolve('./public/locales') - : '/public/locales', -}; diff --git a/apps/unsaged/next.config.js b/apps/unsaged/next.config.js index ded37984..171e5c7c 100644 --- a/apps/unsaged/next.config.js +++ b/apps/unsaged/next.config.js @@ -1,8 +1,7 @@ -const { i18n } = require('./next-i18next.config'); +const withNextIntl = require('next-intl/plugin')(); /** @type {import('next').NextConfig} */ const nextConfig = { - i18n, reactStrictMode: true, webpack(config, { isServer, dev }) { config.experiments = { @@ -15,4 +14,4 @@ const nextConfig = { }, }; -module.exports = nextConfig; +module.exports = withNextIntl(nextConfig); diff --git a/apps/unsaged/package.json b/apps/unsaged/package.json index fbc6b903..38d4d835 100644 --- a/apps/unsaged/package.json +++ b/apps/unsaged/package.json @@ -8,7 +8,7 @@ "start": "next start", "lint": "next lint", "format": "prettier --write .", - "test": "vitest", + "test": "playwright test", "coverage": "vitest run --coverage" }, "dependencies": { @@ -31,14 +31,13 @@ "date-fns": "^2.30.0", "encoding": "^0.1.13", "eventsource-parser": "^0.1.0", - "i18next": "^22.5.1", "js-yaml": "^4.1.0", "jsonwebtoken": "^9.0.2", "katex": "^0.16.9", - "next": "^13.5.6", + "next": "latest", "next-auth": "^4.24.5", "next-axiom": "^1.1.1", - "next-i18next": "^13.3.0", + "next-intl": "^3.3.1", "next-themes": "^0.2.1", "nextra": "^2.13.2", "nextra-theme-docs": "^2.13.2", @@ -47,7 +46,6 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-hot-toast": "^2.4.1", - "react-i18next": "^12.3.1", "react-markdown": "^8.0.7", "react-syntax-highlighter": "^15.5.0", "reflect-metadata": "^0.1.13", @@ -60,6 +58,7 @@ "uuid": "^9.0.1" }, "devDependencies": { + "@playwright/test": "^1.33.0", "@mozilla/readability": "^0.4.4", "@tailwindcss/typography": "^0.5.10", "@trivago/prettier-plugin-sort-imports": "^4.3.0", diff --git a/apps/unsaged/playwright.config.ts b/apps/unsaged/playwright.config.ts new file mode 100644 index 00000000..9c9611ec --- /dev/null +++ b/apps/unsaged/playwright.config.ts @@ -0,0 +1,24 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import type { PlaywrightTestConfig } from '@playwright/test'; +import { devices } from '@playwright/test'; + +// Use a distinct port on CI to avoid conflicts during concurrent tests +const PORT = process.env.CI ? 3002 : 3000; + +const config: PlaywrightTestConfig = { + retries: process.env.CI ? 1 : 0, + testDir: './tests', + projects: [ + { + name: 'chromium', + use: devices['Desktop Chrome'] + } + ], + webServer: { + command: `PORT=${PORT} pnpm start`, + port: PORT, + reuseExistingServer: true + } +}; + +export default config; \ No newline at end of file diff --git a/apps/unsaged/services/errorService.ts b/apps/unsaged/services/errorService.ts index e22eb60b..59872757 100644 --- a/apps/unsaged/services/errorService.ts +++ b/apps/unsaged/services/errorService.ts @@ -1,11 +1,11 @@ import { useMemo } from 'react'; -import { useTranslation } from 'next-i18next'; +import { useTranslations } from 'next-intl'; import { ErrorMessage } from '@/types/error'; const useErrorService = () => { - const { t } = useTranslation('chat'); + const t = useTranslations('chat'); return { getModelsError: useMemo( @@ -13,19 +13,19 @@ const useErrorService = () => { return !error ? null : ({ - title: t('Error fetching models.'), - code: error.status || 'unknown', - messageLines: error.statusText - ? [error.statusText] - : [ - t( - 'Make sure your OpenAI API key is set in the bottom left of the sidebar.', - ), - t( - 'If you completed this step, OpenAI may be experiencing issues.', - ), - ], - } as ErrorMessage); + title: t('Error fetching models.'), + code: error.status || 'unknown', + messageLines: error.statusText + ? [error.statusText] + : [ + t( + 'Make sure your OpenAI API key is set in the bottom left of the sidebar.', + ), + t( + 'If you completed this step, OpenAI may be experiencing issues.', + ), + ], + } as ErrorMessage); }, [t], ), diff --git a/apps/unsaged/tests/main.spec.ts b/apps/unsaged/tests/main.spec.ts new file mode 100644 index 00000000..814dba15 --- /dev/null +++ b/apps/unsaged/tests/main.spec.ts @@ -0,0 +1,74 @@ +import { test as it, expect } from '@playwright/test'; + +it('handles i18n routing', async ({ page }) => { + await page.goto('/'); + await expect(page).toHaveURL('/en'); + + // A cookie remembers the last locale + await page.goto('/de'); + await page.goto('/'); + await expect(page).toHaveURL('/de'); + await page + .getByRole('combobox', { name: 'Sprache ändern' }) + .selectOption({ label: 'Englisch' }); + + await expect(page).toHaveURL('/en'); + page.getByRole('heading', { name: 'next-intl example' }); +}); + +it('handles not found pages', async ({ page }) => { + await page.goto('/unknown'); + page.getByRole('heading', { name: 'Page not found' }); + + await page.goto('/de/unknown'); + page.getByRole('heading', { name: 'Seite nicht gefunden' }); +}); + +it("handles not found pages for routes that don't match the middleware", async ({ + page +}) => { + await page.goto('/test.png'); + page.getByRole('heading', { name: 'This page could not be found.' }); + await page.goto('/api/hello'); + page.getByRole('heading', { name: 'This page could not be found.' }); +}); + +it('sets caching headers', async ({ request }) => { + for (const pathname of ['/en', '/de']) { + expect((await request.get(pathname)).headers()['cache-control']).toBe( + 's-maxage=31536000, stale-while-revalidate' + ); + } +}); + +it('can be used to configure metadata', async ({ page }) => { + await page.goto('/en'); + await expect(page).toHaveTitle('next-intl example'); + + await page.goto('/de'); + await expect(page).toHaveTitle('next-intl Beispiel'); +}); + +it('can be used to localize the page', async ({ page }) => { + await page.goto('/en'); + page.getByRole('heading', { name: 'next-intl example' }); + + await page.goto('/de'); + page.getByRole('heading', { name: 'next-intl Beispiel' }); +}); + +it('sets a cookie', async ({ page }) => { + const response = await page.goto('/en'); + const value = await response?.headerValue('set-cookie'); + expect(value).toContain('NEXT_LOCALE=en;'); + expect(value).toContain('Path=/;'); + expect(value).toContain('SameSite=strict'); + expect(value).toContain('Max-Age=31536000;'); + expect(value).toContain('Expires='); +}); + +it('serves a robots.txt', async ({ page }) => { + const response = await page.goto('/robots.txt'); + const body = await response?.body(); + expect(body?.toString()).toEqual('User-Agent: *\nAllow: *\n'); +}); \ No newline at end of file diff --git a/package.json b/package.json index cb0ee157..e3ffd2e8 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "build": "turbo run build", "dev": "turbo run dev", "lint": "turbo run lint", + "test": "turbo run test", "format": "prettier --write \"**/*.{ts,tsx,md}\"" }, "workspaces": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 843c7283..ec266b53 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,9 +135,6 @@ importers: eventsource-parser: specifier: ^0.1.0 version: 0.1.0 - i18next: - specifier: ^22.5.1 - version: 22.5.1 js-yaml: specifier: ^4.1.0 version: 4.1.0 @@ -148,26 +145,26 @@ importers: specifier: ^0.16.9 version: 0.16.9 next: - specifier: ^13.5.6 - version: 13.5.6(react-dom@18.2.0)(react@18.2.0) + specifier: latest + version: 14.0.3(react-dom@18.2.0)(react@18.2.0) next-auth: specifier: ^4.24.5 - version: 4.24.5(next@13.5.6)(nodemailer@6.9.7)(react-dom@18.2.0)(react@18.2.0) + version: 4.24.5(next@14.0.3)(nodemailer@6.9.7)(react-dom@18.2.0)(react@18.2.0) next-axiom: specifier: ^1.1.1 - version: 1.1.1(next@13.5.6)(react@18.2.0) - next-i18next: - specifier: ^13.3.0 - version: 13.3.0(i18next@22.5.1)(next@13.5.6)(react-i18next@12.3.1)(react@18.2.0) + version: 1.1.1(next@14.0.3)(react@18.2.0) + next-intl: + specifier: ^3.3.1 + version: 3.3.1(next@14.0.3)(react@18.2.0) next-themes: specifier: ^0.2.1 - version: 0.2.1(next@13.5.6)(react-dom@18.2.0)(react@18.2.0) + version: 0.2.1(next@14.0.3)(react-dom@18.2.0)(react@18.2.0) nextra: specifier: ^2.13.2 - version: 2.13.2(next@13.5.6)(react-dom@18.2.0)(react@18.2.0) + version: 2.13.2(next@14.0.3)(react-dom@18.2.0)(react@18.2.0) nextra-theme-docs: specifier: ^2.13.2 - version: 2.13.2(next@13.5.6)(nextra@2.13.2)(react-dom@18.2.0)(react@18.2.0) + version: 2.13.2(next@14.0.3)(nextra@2.13.2)(react-dom@18.2.0)(react@18.2.0) nodemailer: specifier: ^6.9.7 version: 6.9.7 @@ -183,9 +180,6 @@ importers: react-hot-toast: specifier: ^2.4.1 version: 2.4.1(csstype@3.1.2)(react-dom@18.2.0)(react@18.2.0) - react-i18next: - specifier: ^12.3.1 - version: 12.3.1(i18next@22.5.1)(react-dom@18.2.0)(react@18.2.0) react-markdown: specifier: ^8.0.7 version: 8.0.7(@types/react@18.2.43)(react@18.2.0) @@ -220,6 +214,9 @@ importers: '@mozilla/readability': specifier: ^0.4.4 version: 0.4.4 + '@playwright/test': + specifier: ^1.33.0 + version: 1.40.1 '@tailwindcss/typography': specifier: ^0.5.10 version: 0.5.10(tailwindcss@3.3.5) @@ -2908,6 +2905,59 @@ packages: resolution: {integrity: sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==} dev: false + /@formatjs/ecma402-abstract@1.11.4: + resolution: {integrity: sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==} + dependencies: + '@formatjs/intl-localematcher': 0.2.25 + tslib: 2.6.2 + dev: false + + /@formatjs/ecma402-abstract@1.18.0: + resolution: {integrity: sha512-PEVLoa3zBevWSCZzPIM/lvPCi8P5l4G+NXQMc/CjEiaCWgyHieUoo0nM7Bs0n/NbuQ6JpXEolivQ9pKSBHaDlA==} + dependencies: + '@formatjs/intl-localematcher': 0.5.2 + tslib: 2.6.2 + dev: false + + /@formatjs/fast-memoize@1.2.1: + resolution: {integrity: sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==} + dependencies: + tslib: 2.6.2 + dev: false + + /@formatjs/icu-messageformat-parser@2.1.0: + resolution: {integrity: sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==} + dependencies: + '@formatjs/ecma402-abstract': 1.11.4 + '@formatjs/icu-skeleton-parser': 1.3.6 + tslib: 2.6.2 + dev: false + + /@formatjs/icu-skeleton-parser@1.3.6: + resolution: {integrity: sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==} + dependencies: + '@formatjs/ecma402-abstract': 1.11.4 + tslib: 2.6.2 + dev: false + + /@formatjs/intl-localematcher@0.2.25: + resolution: {integrity: sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==} + dependencies: + tslib: 2.6.2 + dev: false + + /@formatjs/intl-localematcher@0.2.32: + resolution: {integrity: sha512-k/MEBstff4sttohyEpXxCmC3MqbUn9VvHGlZ8fauLzkbwXmVrEeyzS+4uhrvAk9DWU9/7otYWxyDox4nT/KVLQ==} + dependencies: + tslib: 2.6.2 + dev: false + + /@formatjs/intl-localematcher@0.5.2: + resolution: {integrity: sha512-txaaE2fiBMagLrR4jYhxzFO6wEdEG4TPMqrzBAcbr4HFUYzH/YC+lg6OIzKCHm8WgDdyQevxbAAV1OgcXctuGw==} + dependencies: + tslib: 2.6.2 + dev: false + /@hapi/hoek@9.3.0: resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} @@ -3223,11 +3273,11 @@ packages: next-auth: ^4.18.7 dependencies: '@supabase/supabase-js': 2.38.5 - next-auth: 4.24.5(next@13.5.6)(nodemailer@6.9.7)(react-dom@18.2.0)(react@18.2.0) + next-auth: 4.24.5(next@14.0.3)(nodemailer@6.9.7)(react-dom@18.2.0)(react@18.2.0) dev: false - /@next/env@13.5.6: - resolution: {integrity: sha512-Yac/bV5sBGkkEXmAX5FWPS9Mmo2rthrOPRQQNfycJPkjUAUclomCPH7QFVCDQ4Mp2k2K1SSM6m0zrxYrOwtFQw==} + /@next/env@14.0.3: + resolution: {integrity: sha512-7xRqh9nMvP5xrW4/+L0jgRRX+HoNRGnfJpD+5Wq6/13j3dsdzxO3BCXn7D3hMqsDb+vjZnJq+vI7+EtgrYZTeA==} dev: false /@next/eslint-plugin-next@14.0.3: @@ -3236,8 +3286,8 @@ packages: glob: 7.1.7 dev: false - /@next/swc-darwin-arm64@13.5.6: - resolution: {integrity: sha512-5nvXMzKtZfvcu4BhtV0KH1oGv4XEW+B+jOfmBdpFI3C7FrB/MfujRpWYSBBO64+qbW8pkZiSyQv9eiwnn5VIQA==} + /@next/swc-darwin-arm64@14.0.3: + resolution: {integrity: sha512-64JbSvi3nbbcEtyitNn2LEDS/hcleAFpHdykpcnrstITFlzFgB/bW0ER5/SJJwUPj+ZPY+z3e+1jAfcczRLVGw==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] @@ -3245,8 +3295,8 @@ packages: dev: false optional: true - /@next/swc-darwin-x64@13.5.6: - resolution: {integrity: sha512-6cgBfxg98oOCSr4BckWjLLgiVwlL3vlLj8hXg2b+nDgm4bC/qVXXLfpLB9FHdoDu4057hzywbxKvmYGmi7yUzA==} + /@next/swc-darwin-x64@14.0.3: + resolution: {integrity: sha512-RkTf+KbAD0SgYdVn1XzqE/+sIxYGB7NLMZRn9I4Z24afrhUpVJx6L8hsRnIwxz3ERE2NFURNliPjJ2QNfnWicQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] @@ -3254,8 +3304,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-gnu@13.5.6: - resolution: {integrity: sha512-txagBbj1e1w47YQjcKgSU4rRVQ7uF29YpnlHV5xuVUsgCUf2FmyfJ3CPjZUvpIeXCJAoMCFAoGnbtX86BK7+sg==} + /@next/swc-linux-arm64-gnu@14.0.3: + resolution: {integrity: sha512-3tBWGgz7M9RKLO6sPWC6c4pAw4geujSwQ7q7Si4d6bo0l6cLs4tmO+lnSwFp1Tm3lxwfMk0SgkJT7EdwYSJvcg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -3263,8 +3313,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-musl@13.5.6: - resolution: {integrity: sha512-cGd+H8amifT86ZldVJtAKDxUqeFyLWW+v2NlBULnLAdWsiuuN8TuhVBt8ZNpCqcAuoruoSWynvMWixTFcroq+Q==} + /@next/swc-linux-arm64-musl@14.0.3: + resolution: {integrity: sha512-v0v8Kb8j8T23jvVUWZeA2D8+izWspeyeDGNaT2/mTHWp7+37fiNfL8bmBWiOmeumXkacM/AB0XOUQvEbncSnHA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -3272,8 +3322,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-gnu@13.5.6: - resolution: {integrity: sha512-Mc2b4xiIWKXIhBy2NBTwOxGD3nHLmq4keFk+d4/WL5fMsB8XdJRdtUlL87SqVCTSaf1BRuQQf1HvXZcy+rq3Nw==} + /@next/swc-linux-x64-gnu@14.0.3: + resolution: {integrity: sha512-VM1aE1tJKLBwMGtyBR21yy+STfl0MapMQnNrXkxeyLs0GFv/kZqXS5Jw/TQ3TSUnbv0QPDf/X8sDXuMtSgG6eg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -3281,8 +3331,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-musl@13.5.6: - resolution: {integrity: sha512-CFHvP9Qz98NruJiUnCe61O6GveKKHpJLloXbDSWRhqhkJdZD2zU5hG+gtVJR//tyW897izuHpM6Gtf6+sNgJPQ==} + /@next/swc-linux-x64-musl@14.0.3: + resolution: {integrity: sha512-64EnmKy18MYFL5CzLaSuUn561hbO1Gk16jM/KHznYP3iCIfF9e3yULtHaMy0D8zbHfxset9LTOv6cuYKJgcOxg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -3290,8 +3340,8 @@ packages: dev: false optional: true - /@next/swc-win32-arm64-msvc@13.5.6: - resolution: {integrity: sha512-aFv1ejfkbS7PUa1qVPwzDHjQWQtknzAZWGTKYIAaS4NMtBlk3VyA6AYn593pqNanlicewqyl2jUhQAaFV/qXsg==} + /@next/swc-win32-arm64-msvc@14.0.3: + resolution: {integrity: sha512-WRDp8QrmsL1bbGtsh5GqQ/KWulmrnMBgbnb+59qNTW1kVi1nG/2ndZLkcbs2GX7NpFLlToLRMWSQXmPzQm4tog==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] @@ -3299,8 +3349,8 @@ packages: dev: false optional: true - /@next/swc-win32-ia32-msvc@13.5.6: - resolution: {integrity: sha512-XqqpHgEIlBHvzwG8sp/JXMFkLAfGLqkbVsyN+/Ih1mR8INb6YCc2x/Mbwi6hsAgUnqQztz8cvEbHJUbSl7RHDg==} + /@next/swc-win32-ia32-msvc@14.0.3: + resolution: {integrity: sha512-EKffQeqCrj+t6qFFhIFTRoqb2QwX1mU7iTOvMyLbYw3QtqTw9sMwjykyiMlZlrfm2a4fA84+/aeW+PMg1MjuTg==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] @@ -3308,8 +3358,8 @@ packages: dev: false optional: true - /@next/swc-win32-x64-msvc@13.5.6: - resolution: {integrity: sha512-Cqfe1YmOS7k+5mGu92nl5ULkzpKuxJrP3+4AEuPmrpFZ3BHxTY3TnHmU1On3bFmFFs6FbTcdF58CCUProGpIGQ==} + /@next/swc-win32-x64-msvc@14.0.3: + resolution: {integrity: sha512-ERhKPSJ1vQrPiwrs15Pjz/rvDHZmkmvbf/BjPN/UCOI++ODftT0GtasDPi0j+y6PPJi5HsXw+dpRaXUaw4vjuQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -3339,6 +3389,14 @@ packages: resolution: {integrity: sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==} dev: false + /@playwright/test@1.40.1: + resolution: {integrity: sha512-EaaawMTOeEItCRvfmkI9v6rBkF1svM8wjl/YPRrg2N2Wmp+4qJYkWtJsbew1szfKKDm6fPLy4YAanBhIlf9dWw==} + engines: {node: '>=16'} + hasBin: true + dependencies: + playwright: 1.40.1 + dev: true + /@pnpm/config.env-replace@1.1.0: resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} engines: {node: '>=12.22.0'} @@ -8341,6 +8399,14 @@ packages: /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + /fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + /fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -9000,12 +9066,6 @@ packages: terser: 5.24.0 dev: false - /html-parse-stringify@3.0.1: - resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} - dependencies: - void-elements: 3.1.0 - dev: false - /html-tags@3.3.1: resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} engines: {node: '>=8'} @@ -9169,16 +9229,6 @@ packages: ms: 2.1.3 dev: false - /i18next-fs-backend@2.3.0: - resolution: {integrity: sha512-N0SS2WojoVIh2x/QkajSps8RPKzXqryZsQh12VoFY4cLZgkD+62EPY2fY+ZjkNADu8xA5I5EadQQXa8TXBKN3w==} - dev: false - - /i18next@22.5.1: - resolution: {integrity: sha512-8TGPgM3pAD+VRsMtUMNknRz3kzqwp/gPALrWMsDnmC1mKqJwpWyooQRLMcbTwq8z8YwSmuj+ZYvc+xCuEpkssA==} - dependencies: - '@babel/runtime': 7.23.4 - dev: false - /iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -9296,6 +9346,15 @@ packages: resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==} dev: false + /intl-messageformat@9.13.0: + resolution: {integrity: sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==} + dependencies: + '@formatjs/ecma402-abstract': 1.11.4 + '@formatjs/fast-memoize': 1.2.1 + '@formatjs/icu-messageformat-parser': 2.1.0 + tslib: 2.6.2 + dev: false + /invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} dependencies: @@ -11586,7 +11645,7 @@ packages: /neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - /next-auth@4.24.5(next@13.5.6)(nodemailer@6.9.7)(react-dom@18.2.0)(react@18.2.0): + /next-auth@4.24.5(next@14.0.3)(nodemailer@6.9.7)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-3RafV3XbfIKk6rF6GlLE4/KxjTcuMCifqrmD+98ejFq73SRoj2rmzoca8u764977lH/Q7jo6Xu6yM+Re1Mz/Og==} peerDependencies: next: ^12.2.5 || ^13 || ^14 @@ -11601,7 +11660,7 @@ packages: '@panva/hkdf': 1.1.1 cookie: 0.5.0 jose: 4.15.4 - next: 13.5.6(react-dom@18.2.0)(react@18.2.0) + next: 14.0.3(react-dom@18.2.0)(react@18.2.0) nodemailer: 6.9.7 oauth: 0.9.15 openid-client: 5.6.1 @@ -11612,37 +11671,30 @@ packages: uuid: 8.3.2 dev: false - /next-axiom@1.1.1(next@13.5.6)(react@18.2.0): + /next-axiom@1.1.1(next@14.0.3)(react@18.2.0): resolution: {integrity: sha512-0r/TJ+/zetD+uDc7B+2E7WpC86hEtQ1U+DuWYrP/JNmUz+ZdPFbrZgzOSqaZ6TwYbXP56VVlPfYwq1YsKHTHYQ==} engines: {node: '>=18'} peerDependencies: next: '>=13.4' react: '>=18.0.0' dependencies: - next: 13.5.6(react-dom@18.2.0)(react@18.2.0) + next: 14.0.3(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 remeda: 1.29.0 whatwg-fetch: 3.6.19 dev: false - /next-i18next@13.3.0(i18next@22.5.1)(next@13.5.6)(react-i18next@12.3.1)(react@18.2.0): - resolution: {integrity: sha512-X4kgi51BCOoGdKbv87eZ8OU7ICQDg5IP+T5fNjqDY3os9ea0OKTY4YpAiVFiwcI9XimcUmSPbKO4a9jFUyYSgg==} - engines: {node: '>=14'} + /next-intl@3.3.1(next@14.0.3)(react@18.2.0): + resolution: {integrity: sha512-/NXy0txAZihat2dkuTrrLWgQUkuJTIu7up1R+xXZbCj4mJX+1OkoRnt/BhhszqcOW6CkmfYfkAG8q7LoI5cOUw==} peerDependencies: - i18next: ^22.0.6 - next: '>= 12.0.0' - react: '>= 17.0.2' - react-i18next: ^12.2.0 + next: ^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - '@babel/runtime': 7.23.4 - '@types/hoist-non-react-statics': 3.3.5 - core-js: 3.33.3 - hoist-non-react-statics: 3.3.2 - i18next: 22.5.1 - i18next-fs-backend: 2.3.0 - next: 13.5.6(react-dom@18.2.0)(react@18.2.0) + '@formatjs/intl-localematcher': 0.2.32 + negotiator: 0.6.3 + next: 14.0.3(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 - react-i18next: 12.3.1(i18next@22.5.1)(react-dom@18.2.0)(react@18.2.0) + use-intl: 3.3.1(react@18.2.0) dev: false /next-mdx-remote@4.4.1(react-dom@18.2.0)(react@18.2.0): @@ -11662,26 +11714,26 @@ packages: - supports-color dev: false - /next-seo@6.4.0(next@13.5.6)(react-dom@18.2.0)(react@18.2.0): + /next-seo@6.4.0(next@14.0.3)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-XQFxkOL2hw0YE+P100HbI3EAvcludlHPxuzMgaIjKb7kPK0CvjGvLFjd9hszZFEDc5oiQkGFA8+cuWcnip7eYA==} peerDependencies: next: ^8.1.1-canary.54 || >=9.0.0 react: '>=16.0.0' react-dom: '>=16.0.0' dependencies: - next: 13.5.6(react-dom@18.2.0)(react@18.2.0) + next: 14.0.3(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /next-themes@0.2.1(next@13.5.6)(react-dom@18.2.0)(react@18.2.0): + /next-themes@0.2.1(next@14.0.3)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==} peerDependencies: next: '*' react: '*' react-dom: '*' dependencies: - next: 13.5.6(react-dom@18.2.0)(react@18.2.0) + next: 14.0.3(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false @@ -11690,9 +11742,9 @@ packages: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} dev: false - /next@13.5.6(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-Y2wTcTbO4WwEsVb4A8VSnOsG1I9ok+h74q0ZdxkwM3EODqrs4pasq7O0iUxbcS9VtWMicG7f3+HAj0r1+NtKSw==} - engines: {node: '>=16.14.0'} + /next@14.0.3(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-AbYdRNfImBr3XGtvnwOxq8ekVCwbFTv/UJoLwmaX89nk9i051AEY4/HAWzU0YpaTDw8IofUpmuIlvzWF13jxIw==} + engines: {node: '>=18.17.0'} hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 @@ -11705,7 +11757,7 @@ packages: sass: optional: true dependencies: - '@next/env': 13.5.6 + '@next/env': 14.0.3 '@swc/helpers': 0.5.2 busboy: 1.6.0 caniuse-lite: 1.0.30001564 @@ -11715,21 +11767,21 @@ packages: styled-jsx: 5.1.1(react@18.2.0) watchpack: 2.4.0 optionalDependencies: - '@next/swc-darwin-arm64': 13.5.6 - '@next/swc-darwin-x64': 13.5.6 - '@next/swc-linux-arm64-gnu': 13.5.6 - '@next/swc-linux-arm64-musl': 13.5.6 - '@next/swc-linux-x64-gnu': 13.5.6 - '@next/swc-linux-x64-musl': 13.5.6 - '@next/swc-win32-arm64-msvc': 13.5.6 - '@next/swc-win32-ia32-msvc': 13.5.6 - '@next/swc-win32-x64-msvc': 13.5.6 + '@next/swc-darwin-arm64': 14.0.3 + '@next/swc-darwin-x64': 14.0.3 + '@next/swc-linux-arm64-gnu': 14.0.3 + '@next/swc-linux-arm64-musl': 14.0.3 + '@next/swc-linux-x64-gnu': 14.0.3 + '@next/swc-linux-x64-musl': 14.0.3 + '@next/swc-win32-arm64-msvc': 14.0.3 + '@next/swc-win32-ia32-msvc': 14.0.3 + '@next/swc-win32-x64-msvc': 14.0.3 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros dev: false - /nextra-theme-docs@2.13.2(next@13.5.6)(nextra@2.13.2)(react-dom@18.2.0)(react@18.2.0): + /nextra-theme-docs@2.13.2(next@14.0.3)(nextra@2.13.2)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-yE4umXaImp1/kf/sFciPj2+EFrNSwd9Db26hi98sIIiujzGf3+9eUgAz45vF9CwBw50FSXxm1QGRcY+slQ4xQQ==} peerDependencies: next: '>=9.5.3' @@ -11746,17 +11798,17 @@ packages: git-url-parse: 13.1.1 intersection-observer: 0.12.2 match-sorter: 6.3.1 - next: 13.5.6(react-dom@18.2.0)(react@18.2.0) - next-seo: 6.4.0(next@13.5.6)(react-dom@18.2.0)(react@18.2.0) - next-themes: 0.2.1(next@13.5.6)(react-dom@18.2.0)(react@18.2.0) - nextra: 2.13.2(next@13.5.6)(react-dom@18.2.0)(react@18.2.0) + next: 14.0.3(react-dom@18.2.0)(react@18.2.0) + next-seo: 6.4.0(next@14.0.3)(react-dom@18.2.0)(react@18.2.0) + next-themes: 0.2.1(next@14.0.3)(react-dom@18.2.0)(react@18.2.0) + nextra: 2.13.2(next@14.0.3)(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) scroll-into-view-if-needed: 3.1.0 zod: 3.22.4 dev: false - /nextra@2.13.2(next@13.5.6)(react-dom@18.2.0)(react@18.2.0): + /nextra@2.13.2(next@14.0.3)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-pIgOSXNUqTz1laxV4ChFZOU7lzJAoDHHaBPj8L09PuxrLKqU1BU/iZtXAG6bQeKCx8EPdBsoXxEuENnL9QGnGA==} engines: {node: '>=16'} peerDependencies: @@ -11776,7 +11828,7 @@ packages: gray-matter: 4.0.3 katex: 0.16.9 lodash.get: 4.4.2 - next: 13.5.6(react-dom@18.2.0)(react@18.2.0) + next: 14.0.3(react-dom@18.2.0)(react@18.2.0) next-mdx-remote: 4.4.1(react-dom@18.2.0)(react@18.2.0) p-limit: 3.1.0 react: 18.2.0 @@ -12391,6 +12443,22 @@ packages: find-up: 3.0.0 dev: false + /playwright-core@1.40.1: + resolution: {integrity: sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ==} + engines: {node: '>=16'} + hasBin: true + dev: true + + /playwright@1.40.1: + resolution: {integrity: sha512-2eHI7IioIpQ0bS1Ovg/HszsN/XKNwEG1kbzSDDmADpclKc7CyqkHw7Mg2JCz/bbCxg25QUPcjksoMW7JcIFQmw==} + engines: {node: '>=16'} + hasBin: true + dependencies: + playwright-core: 1.40.1 + optionalDependencies: + fsevents: 2.3.2 + dev: true + /postcss-calc@8.2.4(postcss@8.4.31): resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==} peerDependencies: @@ -13574,26 +13642,6 @@ packages: - csstype dev: false - /react-i18next@12.3.1(i18next@22.5.1)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-5v8E2XjZDFzK7K87eSwC7AJcAkcLt5xYZ4+yTPDAW1i7C93oOY1dnr4BaQM7un4Hm+GmghuiPvevWwlca5PwDA==} - peerDependencies: - i18next: '>= 19.0.0' - react: '>= 16.8.0' - react-dom: '*' - react-native: '*' - peerDependenciesMeta: - react-dom: - optional: true - react-native: - optional: true - dependencies: - '@babel/runtime': 7.23.4 - html-parse-stringify: 3.0.1 - i18next: 22.5.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: false - /react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -15950,11 +15998,6 @@ packages: - terser dev: true - /void-elements@3.1.0: - resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} - engines: {node: '>=0.10.0'} - dev: false - /vscode-oniguruma@1.7.0: resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} dev: false diff --git a/turbo.json b/turbo.json index e95b0d98..24536d9d 100644 --- a/turbo.json +++ b/turbo.json @@ -53,6 +53,7 @@ "cache": false, "persistent": true }, - "lint": {} + "lint": {}, + "test": {} } } From 3bf1adaf2965babaa5254285280cc879400684e6 Mon Sep 17 00:00:00 2001 From: Sebastian Weise Date: Thu, 7 Dec 2023 22:20:58 +0100 Subject: [PATCH 02/28] Translation update --- .../ChatZone/Screens/Chat/ChatInput.tsx | 25 +-- .../ChatZone/Screens/Chat/ChatMessage.tsx | 6 +- .../ChatZone/Screens/Chat/Regenerate.tsx | 4 +- .../ChatZone/Screens/Settings/Settings.tsx | 7 +- .../Screens/Conversations/Conversations.tsx | 10 +- .../components/ClearConversations.tsx | 6 +- .../components/ConversationsSettings.tsx | 6 - .../components/Screens/Prompts/Prompts.tsx | 6 +- .../Prompts/components/PromptModal.tsx | 14 +- .../Screens/SystemPrompts/SystemPrompts.tsx | 2 +- apps/unsaged/components/common/Search.tsx | 2 +- .../components/common/Sidebar/Sidebar.tsx | 6 +- apps/unsaged/messages/de.json | 28 +++ apps/unsaged/messages/en.json | 2 + package.json | 8 +- pnpm-lock.yaml | 188 ++++++++++-------- 16 files changed, 178 insertions(+), 142 deletions(-) diff --git a/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/ChatInput.tsx b/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/ChatInput.tsx index 85e3938f..f5de6e3c 100644 --- a/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/ChatInput.tsx +++ b/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/ChatInput.tsx @@ -74,8 +74,7 @@ export const ChatInput = ({ if (maxLength && value.length > maxLength) { alert( - t( - `Message limit is {{maxLength}} characters. You have entered {{valueLength}} characters.`, + t('messageLimit', { maxLength, valueLength: value.length }, ), ); @@ -94,7 +93,7 @@ export const ChatInput = ({ } if (!content) { - alert(t('Please enter a message')); + alert(t('enterMessage')); return; } @@ -239,9 +238,8 @@ export const ChatInput = ({ if (textareaRef && textareaRef.current) { textareaRef.current.style.height = 'inherit'; textareaRef.current.style.height = `${textareaRef.current?.scrollHeight}px`; - textareaRef.current.style.overflow = `${ - textareaRef?.current?.scrollHeight > 400 ? 'auto' : 'hidden' - }`; + textareaRef.current.style.overflow = `${textareaRef?.current?.scrollHeight > 400 ? 'auto' : 'hidden' + }`; } }, [content, textareaRef]); @@ -281,7 +279,7 @@ export const ChatInput = ({ dark:bg-theme-dark dark:text-white md:mt-2" onClick={handleStopConversation} > - {t('Stop Generating')} + {t('stopGenerating')} )} @@ -295,7 +293,7 @@ export const ChatInput = ({ dark:bg-theme-dark dark:text-white md:mt-2" onClick={handleRegenerate} > - {t('Regenerate response')} + {t('regenerateResponse')} )} @@ -327,14 +325,13 @@ export const ChatInput = ({ resize: 'none', bottom: `${textareaRef?.current?.scrollHeight}px`, maxHeight: '400px', - overflow: `${ - textareaRef.current && textareaRef.current.scrollHeight > 400 - ? 'auto' - : 'hidden' - }`, + overflow: `${textareaRef.current && textareaRef.current.scrollHeight > 400 + ? 'auto' + : 'hidden' + }`, }} placeholder={ - t('Start typing, type / to select a template...') || '' + t('startTyping') } value={content} rows={1} diff --git a/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/ChatMessage.tsx b/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/ChatMessage.tsx index b24d1375..5b6dcefd 100644 --- a/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/ChatMessage.tsx +++ b/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/ChatMessage.tsx @@ -35,7 +35,7 @@ export interface Props { export const ChatMessage: FC = memo( ({ message, messageIndex, onEdit }) => { - const t = useTranslations('chat'); + const t = useTranslations(); const { state: { @@ -193,7 +193,7 @@ export const ChatMessage: FC = memo( onClick={handleEditMessage} disabled={messageContent.trim().length <= 0} > - {t('Save & Submit')} + {t('saveSubmit')}
diff --git a/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/Regenerate.tsx b/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/Regenerate.tsx index 188f944a..d24625ae 100644 --- a/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/Regenerate.tsx +++ b/apps/unsaged/components/Home/components/ChatZone/Screens/Chat/Regenerate.tsx @@ -12,14 +12,14 @@ export const Regenerate: FC = ({ onRegenerate }) => { return (
- {t('Sorry, there was an error.')} + {t('error')}
); diff --git a/apps/unsaged/components/Home/components/ChatZone/Screens/Settings/Settings.tsx b/apps/unsaged/components/Home/components/ChatZone/Screens/Settings/Settings.tsx index 505b069a..7420939d 100644 --- a/apps/unsaged/components/Home/components/ChatZone/Screens/Settings/Settings.tsx +++ b/apps/unsaged/components/Home/components/ChatZone/Screens/Settings/Settings.tsx @@ -1,13 +1,10 @@ -import { IconDeviceFloppy, IconX } from '@tabler/icons-react'; +import { IconX } from '@tabler/icons-react'; import { useContext, useEffect } from 'react'; -import { useTranslations } from 'next-intl'; - import { useCreateReducer } from '@/hooks/useCreateReducer'; import { setSavedSetting, - setSavedSettings, } from '@/utils/app/storage/local/settings'; import { Setting, SettingsSection } from '@/types/settings'; @@ -19,8 +16,6 @@ import SettingsContext from './Settings.context'; import { SettingsInitialState, initialState } from './Settings.state'; export const Settings = () => { - const t = useTranslations('settings'); - const settingsContextValue = useCreateReducer({ initialState, }); diff --git a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/Conversations.tsx b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/Conversations.tsx index 36543b64..d8d5dbb8 100644 --- a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/Conversations.tsx +++ b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/Conversations.tsx @@ -42,7 +42,7 @@ import { ConversationsInitialState, initialState } from './Conversations.state'; import { v4 as uuidv4 } from 'uuid'; export const Conversations = () => { - const t = useTranslations('conversations'); + const t = useTranslations('chat'); const conversationsContextValue = useCreateReducer( { @@ -258,7 +258,7 @@ export const Conversations = () => { const doSearch = (term: string) => chatDispatch({ field: 'searchTerm', value: term }); - const createFolder = () => handleCreateFolder(t('New folder'), 'chat'); + const createFolder = () => handleCreateFolder(t('newFolder'), 'chat'); const allowDrop = (e: any) => { e.preventDefault(); @@ -289,7 +289,7 @@ export const Conversations = () => { doSearch(''); }} > - {t('New conversation')} + {t('newConversation')} @@ -297,7 +297,7 @@ export const Conversations = () => {
@@ -325,7 +325,7 @@ export const Conversations = () => { ) : (
- {t('No data.')} + {t('noData')}.
)}
diff --git a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/components/ClearConversations.tsx b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/components/ClearConversations.tsx index e1fe9805..8b77c340 100644 --- a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/components/ClearConversations.tsx +++ b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/components/ClearConversations.tsx @@ -12,7 +12,7 @@ interface Props { export const ClearConversations: FC = ({ onClearConversations }) => { const [isConfirming, setIsConfirming] = useState(false); - const t = useTranslations('sidebar'); + const t = useTranslations(); const handleClearConversations = () => { onClearConversations(); @@ -24,7 +24,7 @@ export const ClearConversations: FC = ({ onClearConversations }) => {
- {t('Are you sure?')} + {t('sureQ')}
@@ -49,7 +49,7 @@ export const ClearConversations: FC = ({ onClearConversations }) => {
) : ( } onClick={() => setIsConfirming(true)} /> diff --git a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/components/ConversationsSettings.tsx b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/components/ConversationsSettings.tsx index 0f2ce932..8f9372c7 100644 --- a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/components/ConversationsSettings.tsx +++ b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Conversations/components/ConversationsSettings.tsx @@ -1,18 +1,12 @@ -import { IconFileExport, IconLogout, IconSettings } from '@tabler/icons-react'; import { useContext } from 'react'; -import { useTranslations } from 'next-intl'; - import { Import } from '@/components/Home/components/Settings/Import'; import HomeContext from '@/components/Home/home.context'; -import { SidebarButton } from '@/components/common/Sidebar/SidebarButton'; import ConversationsContext from '../Conversations.context'; import { ClearConversations } from './ClearConversations'; export const ConversationsSettings = () => { - const t = useTranslations('sidebar'); - const { state: { database, conversations }, } = useContext(HomeContext); diff --git a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Prompts/Prompts.tsx b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Prompts/Prompts.tsx index fcc659a5..d190ed07 100644 --- a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Prompts/Prompts.tsx +++ b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Prompts/Prompts.tsx @@ -127,7 +127,7 @@ const Prompts = () => { const doSearch = (term: string) => promptDispatch({ field: 'searchTerm', value: term }); - const createFolder = () => handleCreateFolder(t('New folder'), 'prompt'); + const createFolder = () => handleCreateFolder(t('newFolder'), 'prompt'); return ( { doSearch(''); }} > - {t('New message template')} + {t('newMessageTemplate')} @@ -153,7 +153,7 @@ const Prompts = () => { diff --git a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Prompts/components/PromptModal.tsx b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Prompts/components/PromptModal.tsx index 6df1886e..69da485b 100644 --- a/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Prompts/components/PromptModal.tsx +++ b/apps/unsaged/components/Home/components/PrimaryMenu/components/Menu/components/Screens/Prompts/components/PromptModal.tsx @@ -69,7 +69,7 @@ export const PromptModal: FC = ({ prompt, onClose, onUpdatePrompt }) => { role="dialog" >
- {t('Name')} + {t('name')}
= ({ prompt, onClose, onUpdatePrompt }) => { />
- {t('Description')} + {t('description')}