frontend: apply more i18n (#2639)

This commit is contained in:
tobitege
2024-06-26 05:54:35 +02:00
committed by GitHub
parent fcbf1f8242
commit 94d24b71d7
6 changed files with 103 additions and 18 deletions

View File

@@ -62,7 +62,7 @@ function ChatInput({ disabled = false, onSendMessage }: ChatInputProps) {
? "cursor-not-allowed border-neutral-400 text-neutral-400"
: "hover:bg-neutral-500 ",
)}
aria-label="Send message"
aria-label={t(I18nKey.CHAT_INTERFACE$TOOLTIP_SEND_MESSAGE)}
>
<VscArrowUp />
</button>

View File

@@ -2,8 +2,10 @@ import React, { useState } from "react";
import Markdown from "react-markdown";
import { FaClipboard } from "react-icons/fa";
import { twMerge } from "tailwind-merge";
import { useTranslation } from "react-i18next";
import { code } from "../markdown/code";
import toast from "#/utils/toast";
import { I18nKey } from "#/i18n/declaration";
interface MessageProps {
message: Message;
@@ -18,14 +20,18 @@ function ChatMessage({ message }: MessageProps) {
message.sender === "user" ? "bg-neutral-700 self-end" : "bg-neutral-500",
);
const { t } = useTranslation();
const copyToClipboard = () => {
navigator.clipboard
.writeText(message.content)
.then(() => {
toast.info("Message copied to clipboard!");
toast.info(t(I18nKey.CHAT_INTERFACE$CHAT_MESSAGE_COPIED));
})
.catch((error) => {
toast.error("copy-error", `Failed to copy message: ${error}`);
.catch(() => {
toast.error(
"copy-error",
t(I18nKey.CHAT_INTERFACE$CHAT_MESSAGE_COPY_FAILED),
);
});
};
@@ -40,7 +46,7 @@ function ChatMessage({ message }: MessageProps) {
<button
onClick={copyToClipboard}
className="absolute top-1 right-1 p-1 bg-neutral-600 rounded hover:bg-neutral-500 transition-opacity opacity-75 hover:opacity-100"
aria-label="Copy message"
aria-label={t(I18nKey.CHAT_INTERFACE$TOOLTIP_COPY_MESSAGE)}
type="button"
>
<FaClipboard />

View File

@@ -7,6 +7,7 @@ import {
} from "react-icons/io";
import { useDispatch, useSelector } from "react-redux";
import { IoFileTray } from "react-icons/io5";
import { useTranslation } from "react-i18next";
import { twMerge } from "tailwind-merge";
import AgentState from "#/types/AgentState";
import { setRefreshID } from "#/state/codeSlice";
@@ -15,6 +16,7 @@ import IconButton from "../IconButton";
import ExplorerTree from "./ExplorerTree";
import toast from "#/utils/toast";
import { RootState } from "#/store";
import { I18nKey } from "#/i18n/declaration";
interface ExplorerActionsProps {
onRefresh: () => void;
@@ -92,7 +94,7 @@ function FileExplorer() {
const { curAgentState } = useSelector((state: RootState) => state.agent);
const fileInputRef = React.useRef<HTMLInputElement | null>(null);
const dispatch = useDispatch();
const { t } = useTranslation();
const selectFileInput = () => {
fileInputRef.current?.click(); // Trigger the file browser
};
@@ -113,7 +115,7 @@ function FileExplorer() {
await uploadFiles(toAdd);
await refreshWorkspace();
} catch (error) {
toast.error("ws", "Error uploading file");
toast.error("ws", t(I18nKey.EXPLORER$UPLOAD_ERROR_MESSAGE));
}
};
@@ -158,7 +160,9 @@ function FileExplorer() {
className="z-10 absolute flex flex-col justify-center items-center bg-black top-0 bottom-0 left-0 right-0 opacity-65"
>
<IoFileTray size={32} />
<p className="font-bold text-xl">Drop Files Here</p>
<p className="font-bold text-xl">
{t(I18nKey.EXPLORER$LABEL_DROP_FILES)}
</p>
</div>
)}
<div
@@ -176,7 +180,7 @@ function FileExplorer() {
>
{!isHidden && (
<div className="ml-1 text-neutral-300 font-bold text-sm">
Workspace
{t(I18nKey.EXPLORER$LABEL_WORKSPACE)}
</div>
)}
<ExplorerActions

View File

@@ -58,7 +58,7 @@ function SettingsModal({ isOpen, onOpenChange }: SettingsProps) {
setModels(await fetchModels());
setAgents(await fetchAgents());
} catch (error) {
toast.error("settings", "Failed to fetch models and agents");
toast.error("settings", t(I18nKey.CONFIGURATION$ERROR_FETCH_MODELS));
} finally {
setLoading(false);
}

View File

@@ -268,6 +268,46 @@
"en": "Please stop the agent before editing these settings.",
"de": "Bitte beenden Sie den Agenten vor der Bearbeitung der Einstellungen."
},
"CONFIGURATION$ERROR_FETCH_MODELS": {
"en": "Failed to fetch models and agents",
"zh-CN": "获取模型和智能体失败",
"de": "Fehler beim Abrufen der Modelle und Agenten"
},
"SESSION$SERVER_CONNECTED_MESSAGE": {
"en": "Connected to server",
"zh-CN": "已连接到服务器",
"de": "Verbindung zum Server hergestellt"
},
"SESSION$SESSION_HANDLING_ERROR_MESSAGE": {
"en": "Error handling message",
"zh-CN": "处理消息时发生错误",
"de": "Fehler beim Verarbeiten der Nachricht"
},
"SESSION$SESSION_CONNECTION_ERROR_MESSAGE": {
"en": "Error connecting to session",
"zh-CN": "连接到会话时发生错误",
"de": "Verbindung zur Sitzung fehlgeschlagen"
},
"SESSION$SOCKET_NOT_INITIALIZED_ERROR_MESSAGE": {
"en": "Socket not initialized",
"zh-CN": "Socket 未初始化",
"de": "Socket nicht initialisiert"
},
"EXPLORER$UPLOAD_ERROR_MESSAGE": {
"en": "Error uploading file",
"zh-CN": "上传文件时发生错误",
"de": "Fehler beim Hochladen der Datei"
},
"EXPLORER$LABEL_DROP_FILES": {
"en": "Drop files here",
"zh-CN": "将文件拖到这里",
"de": "Dateien hier ablegen"
},
"EXPLORER$LABEL_WORKSPACE": {
"en": "Workspace",
"zh-CN": "工作区",
"de": "Arbeitsbereich"
},
"LOAD_SESSION$MODAL_TITLE": {
"en": "Return to existing session?",
"de": "Zurück zu vorhandener Sitzung?",
@@ -394,6 +434,26 @@
"ar": "إرسال",
"fr": "Envoyer"
},
"CHAT_INTERFACE$CHAT_MESSAGE_COPIED": {
"en": "Message copied to clipboard",
"zh-CN": "消息已复制到剪贴板",
"de": "Nachricht in die Zwischenablage kopiert"
},
"CHAT_INTERFACE$CHAT_MESSAGE_COPY_FAILED": {
"en": "Failed to copy message to clipboard",
"zh-CN": "复制消息到剪贴板失败",
"de": "Nachricht konnte nicht in die Zwischenablage kopiert werden"
},
"CHAT_INTERFACE$TOOLTIP_COPY_MESSAGE": {
"en": "Copy message",
"zh-CN": "复制消息",
"de": "Nachricht kopieren"
},
"CHAT_INTERFACE$TOOLTIP_SEND_MESSAGE": {
"en": "Send message",
"zh-CN": "发送消息",
"de": "Nachricht senden"
},
"CHAT_INTERFACE$INITIAL_MESSAGE": {
"en": "Hi! I'm OpenDevin, an AI Software Engineer. What would you like to build with me today?",
"zh-CN": "你好!我是 OpenDevin一名 AI 软件工程师。今天想和我一起编写什么程序呢?",

View File

@@ -1,8 +1,12 @@
import i18next from "i18next";
import toast from "#/utils/toast";
import { handleAssistantMessage } from "./actions";
import { getToken, setToken, clearToken } from "./auth";
import ActionType from "#/types/ActionType";
import { getSettings } from "./settings";
import { I18nKey } from "#/i18n/declaration";
const translate = (key: I18nKey) => i18next.t(key);
class Session {
private static _socket: WebSocket | null = null;
@@ -66,10 +70,12 @@ class Session {
private static _setupSocket(): void {
if (!Session._socket) {
throw new Error("Socket is not initialized.");
throw new Error(
translate(I18nKey.SESSION$SOCKET_NOT_INITIALIZED_ERROR_MESSAGE),
);
}
Session._socket.onopen = (e) => {
toast.success("ws", "Connected to server.");
toast.success("ws", translate(I18nKey.SESSION$SERVER_CONNECTED_MESSAGE));
Session._connecting = false;
Session._initializeAgent();
Session.callbacks.open?.forEach((callback) => {
@@ -84,7 +90,10 @@ class Session {
Session._history.push(data);
} catch (err) {
// TODO: report the error
toast.error("ws", "Error handing message.");
toast.error(
"ws",
translate(I18nKey.SESSION$SESSION_HANDLING_ERROR_MESSAGE),
);
return;
}
if (data.error && data.error_code === 401) {
@@ -101,8 +110,10 @@ class Session {
};
Session._socket.onerror = () => {
const msg = "Connection failed. Retry...";
toast.error("ws", msg);
toast.error(
"ws",
translate(I18nKey.SESSION$SESSION_CONNECTION_ERROR_MESSAGE),
);
};
Session._socket.onclose = () => {
@@ -135,15 +146,19 @@ class Session {
return;
}
if (!Session.isConnected()) {
throw new Error("Not connected to server.");
throw new Error(
translate(I18nKey.SESSION$SESSION_CONNECTION_ERROR_MESSAGE),
);
}
if (Session.isConnected()) {
Session._socket?.send(message);
Session._history.push(JSON.parse(message));
} else {
const msg = "Connection failed. Retry...";
toast.error("ws", msg);
toast.error(
"ws",
translate(I18nKey.SESSION$SESSION_CONNECTION_ERROR_MESSAGE),
);
}
}