Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2164 [UI/UX]: More Localization #2794

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions frontend/src/components/SettingsButton/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import paths from "@/utils/paths";
import { ArrowUUpLeft, Wrench } from "@phosphor-icons/react";
import { Link } from "react-router-dom";
import { useMatch } from "react-router-dom";
import { useTranslation } from "react-i18next";

export default function SettingsButton() {
const isInSettings = !!useMatch("/settings/*");
const { user } = useUser();
const { t } = useTranslation();

if (user && user?.role === "default") return null;

Expand All @@ -18,7 +20,7 @@ export default function SettingsButton() {
className="transition-all duration-300 p-2 rounded-full bg-theme-sidebar-footer-icon hover:bg-theme-sidebar-footer-icon-hover"
aria-label="Home"
data-tooltip-id="footer-item"
data-tooltip-content="Back to workspaces"
data-tooltip-content={t("settings.back-to-workspaces")}
>
<ArrowUUpLeft
className="h-5 w-5"
Expand All @@ -37,7 +39,7 @@ export default function SettingsButton() {
// className="transition-all duration-300 p-2 rounded-full bg-sidebar-button hover:bg-menu-item-selected-gradient hover:border-slate-100 hover:border-opacity-50 border-transparent border"
aria-label="Settings"
data-tooltip-id="footer-item"
data-tooltip-content="Open settings"
data-tooltip-content={t("settings.open")}
>
<Wrench
className="h-5 w-5"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { useEffect, useRef, useState } from "react";
import { useParams } from "react-router-dom";
import truncate from "truncate";
import { useTranslation } from "react-i18next";

const THREAD_CALLOUT_DETAIL_WIDTH = 26;
export default function ThreadItem({
Expand All @@ -27,6 +28,7 @@ export default function ThreadItem({
const { slug } = useParams();
const optionsContainer = useRef(null);
const [showOptions, setShowOptions] = useState(false);
const { t } = useTranslation();
const linkTo = !thread.slug
? paths.workspace.chat(slug)
: paths.workspace.thread(slug, thread.slug);
Expand Down Expand Up @@ -126,7 +128,7 @@ export default function ThreadItem({
type="button"
className="border-none"
onClick={() => setShowOptions(!showOptions)}
aria-label="Thread options"
aria-label={t("threads.options")}
>
<DotsThree
className="text-slate-300 light:text-theme-text-secondary hover:text-white hover:light:text-theme-text-primary"
Expand All @@ -153,6 +155,7 @@ export default function ThreadItem({

function OptionsMenu({ containerRef, workspace, thread, onRemove, close }) {
const menuRef = useRef(null);
const { t } = useTranslation();

// Ref menu options
const outsideClick = (e) => {
Expand Down Expand Up @@ -188,7 +191,7 @@ function OptionsMenu({ containerRef, workspace, thread, onRemove, close }) {

const renameThread = async () => {
const name = window
.prompt("What would you like to rename this thread to?")
.prompt(t("threads.rename_question"))
?.trim();
if (!name || name.length === 0) {
close();
Expand All @@ -213,19 +216,14 @@ function OptionsMenu({ containerRef, workspace, thread, onRemove, close }) {
};

const handleDelete = async () => {
if (
!window.confirm(
"Are you sure you want to delete this thread? All of its chats will be deleted. You cannot undo this."
)
)
return;
if (!window.confirm(t("threads.delete.question"))) return;
const success = await Workspace.threads.delete(workspace.slug, thread.slug);
if (!success) {
showToast("Thread could not be deleted!", "error", { clear: true });
showToast(t("threads.delete.failure"), "error", { clear: true });
return;
}
if (success) {
showToast("Thread deleted successfully!", "success", { clear: true });
showToast(t("threads.delete.success"), "success", { clear: true });
onRemove(thread.id);
return;
}
Expand All @@ -242,15 +240,15 @@ function OptionsMenu({ containerRef, workspace, thread, onRemove, close }) {
className="w-full rounded-md flex items-center p-2 gap-x-2 hover:bg-slate-500/20 text-slate-300 light:text-theme-text-primary"
>
<PencilSimple size={18} />
<p className="text-sm">Rename</p>
<p className="text-sm">{t("threads.rename")}</p>
</button>
<button
onClick={handleDelete}
type="button"
className="w-full rounded-md flex items-center p-2 gap-x-2 hover:bg-red-500/20 text-slate-300 light:text-theme-text-primary hover:text-red-100"
>
<Trash size={18} />
<p className="text-sm">Delete Thread</p>
<p className="text-sm">{t("threads.delete.delete")}</p>
</button>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Plus, CircleNotch, Trash } from "@phosphor-icons/react";
import { useEffect, useState } from "react";
import ThreadItem from "./ThreadItem";
import { useParams } from "react-router-dom";
import { useTranslation } from "react-i18next";
export const THREAD_RENAME_EVENT = "renameThread";

export default function ThreadContainer({ workspace }) {
Expand Down Expand Up @@ -153,6 +154,8 @@ export default function ThreadContainer({ workspace }) {

function NewThreadButton({ workspace }) {
const [loading, setLoading] = useState(false);
const { t } = useTranslation();

const onClick = async () => {
setLoading(true);
const { thread, error } = await Workspace.threads.new(workspace.slug);
Expand Down Expand Up @@ -190,11 +193,11 @@ function NewThreadButton({ workspace }) {

{loading ? (
<p className="text-left text-white light:text-theme-text-primary text-sm">
Starting Thread...
{t("threads.start")}
</p>
) : (
<p className="text-left text-white light:text-theme-text-primary text-sm">
New Thread
{t("threads.new")}
</p>
)}
</div>
Expand All @@ -203,6 +206,7 @@ function NewThreadButton({ workspace }) {
}

function DeleteAllThreadButton({ ctrlPressed, threads, onDelete }) {
const { t } = useTranslation();
if (!ctrlPressed || threads.filter((t) => t.deleted).length === 0)
return null;
return (
Expand All @@ -220,7 +224,7 @@ function DeleteAllThreadButton({ ctrlPressed, threads, onDelete }) {
/>
</div>
<p className="text-white light:text-theme-text-secondary text-left text-sm group-hover:text-red-400">
Delete Selected
{t("threads.delete.selected")}
</p>
</div>
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
} from "@phosphor-icons/react";
import ConfluenceLogo from "@/media/dataConnectors/confluence.png";
import { toPercentString } from "@/utils/numbers";
import { useTranslation } from "react-i18next";

function combineLikeSources(sources) {
const combined = {};
Expand All @@ -36,9 +37,10 @@ function combineLikeSources(sources) {
}

export default function Citations({ sources = [] }) {
if (sources.length === 0) return null;
const [open, setOpen] = useState(false);
const [selectedSource, setSelectedSource] = useState(null);
const { t } = useTranslation();
if (sources.length === 0) return null;

return (
<div className="flex flex-col mt-4 justify-left">
Expand All @@ -48,7 +50,7 @@ export default function Citations({ sources = [] }) {
open ? "pb-2" : ""
} hover:text-white/75 hover:light:text-black/75 transition-all duration-300`}
>
{open ? "Hide Citations" : "Show Citations"}
{open ? t("citations.hide") : t("citations.show")}
<CaretRight
className={`w-3.5 h-3.5 inline-block ml-1 transform transition-transform duration-300 ${
open ? "rotate-90" : ""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React, { useState, useEffect, useRef } from "react";
import { Trash, DotsThreeVertical, TreeView } from "@phosphor-icons/react";
import { useTranslation } from "react-i18next";

function ActionMenu({ chatId, forkThread, isEditing, role }) {
const [open, setOpen] = useState(false);
const menuRef = useRef(null);
const { t } = useTranslation();

const toggleMenu = () => setOpen(!open);

Expand Down Expand Up @@ -40,8 +42,8 @@ function ActionMenu({ chatId, forkThread, isEditing, role }) {
onClick={toggleMenu}
className="border-none text-[var(--theme-sidebar-footer-icon-fill)] hover:text-[var(--theme-sidebar-footer-icon-fill)] transition-colors duration-200"
data-tooltip-id="action-menu"
data-tooltip-content="More actions"
aria-label="More actions"
data-tooltip-content={t("general.message.more-actions")}
aria-label="general.message.more-actions"
>
<DotsThreeVertical size={24} weight="bold" />
</button>
Expand All @@ -52,14 +54,14 @@ function ActionMenu({ chatId, forkThread, isEditing, role }) {
className="border-none rounded-t-lg flex items-center text-white gap-x-2 hover:bg-theme-action-menu-item-hover py-1.5 px-2 transition-colors duration-200 w-full text-left"
>
<TreeView size={18} />
<span className="text-sm">Fork</span>
<span className="text-sm">{t("general.message.fork")}</span>
</button>
<button
onClick={handleDelete}
className="border-none flex rounded-b-lg items-center text-white gap-x-2 hover:bg-theme-action-menu-item-hover py-1.5 px-2 transition-colors duration-200 w-full text-left"
>
<Trash size={18} />
<span className="text-sm">Delete</span>
<span className="text-sm">{t("general.message.delete")}</span>
</button>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useState, useEffect } from "react";
import { Trash } from "@phosphor-icons/react";
import Workspace from "@/models/workspace";
import { useTranslation } from "react-i18next";

const DELETE_EVENT = "delete-message";

Expand Down Expand Up @@ -39,6 +40,7 @@ export function useWatchDeleteMessage({ chatId = null, role = "user" }) {
}

export function DeleteMessage({ chatId, isEditing, role }) {
const { t } = useTranslation();
if (!chatId || isEditing || role === "user") return null;

function emitDeleteEvent() {
Expand All @@ -52,7 +54,7 @@ export function DeleteMessage({ chatId, isEditing, role }) {
role="menuitem"
>
<Trash size={21} weight="fill" />
<p>Delete</p>
<p>{t("general.message.delete")}</p>
</button>
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Pencil } from "@phosphor-icons/react";
import { useState, useEffect, useRef } from "react";
import { useTranslation } from "react-i18next";

const EDIT_EVENT = "toggle-message-edit";

Expand Down Expand Up @@ -29,6 +30,7 @@ export function useEditMessage({ chatId, role }) {
}

export function EditMessageAction({ chatId = null, role, isEditing }) {
const { t } = useTranslation();
function handleEditClick() {
window.dispatchEvent(
new CustomEvent(EDIT_EVENT, { detail: { chatId, role } })
Expand All @@ -45,11 +47,13 @@ export function EditMessageAction({ chatId = null, role, isEditing }) {
<button
onClick={handleEditClick}
data-tooltip-id="edit-input-text"
data-tooltip-content={`Edit ${
role === "user" ? "Prompt" : "Response"
} `}
data-tooltip-content={t(
`general.message.edit.${role === "user" ? "prompt" : "response"}`
)}
className="border-none text-zinc-300"
aria-label={`Edit ${role === "user" ? "Prompt" : "Response"}`}
aria-label={t(
`general.message.edit.${role === "user" ? "prompt" : "response"}`
)}
>
<Pencil
color="var(--theme-sidebar-footer-icon-fill)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { useEffect, useState, useRef } from "react";
import { SpeakerHigh, PauseCircle, CircleNotch } from "@phosphor-icons/react";
import Workspace from "@/models/workspace";
import showToast from "@/utils/toast";
import { useTranslation } from "react-i18next";

export default function AsyncTTSMessage({ slug, chatId }) {
const playerRef = useRef(null);
const [speaking, setSpeaking] = useState(false);
const [loading, setLoading] = useState(false);
const [audioSrc, setAudioSrc] = useState(null);
const { t } = useTranslation();

function speakMessage() {
if (speaking) {
Expand Down Expand Up @@ -58,10 +60,14 @@ export default function AsyncTTSMessage({ slug, chatId }) {
onClick={speakMessage}
data-tooltip-id="message-to-speech"
data-tooltip-content={
speaking ? "Pause TTS speech of message" : "TTS Speak message"
speaking
? t("chat-history.tts.pause-tts")
: t("chat-history.tts.start-tts")
}
className="border-none text-[var(--theme-sidebar-footer-icon-fill)]"
aria-label={speaking ? "Pause speech" : "Speak message"}
aria-label={
speaking ? t("chat-history.tts.pause") : t("chat-history.tts.start")
}
>
{speaking ? (
<PauseCircle size={18} className="mb-1" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import React, { useEffect, useState } from "react";
import { SpeakerHigh, PauseCircle } from "@phosphor-icons/react";
import { useTranslation } from "react-i18next";

export default function NativeTTSMessage({ message }) {
const [speaking, setSpeaking] = useState(false);
const [supported, setSupported] = useState(false);
const { t } = useTranslation();

useEffect(() => {
setSupported("speechSynthesis" in window);
}, []);
Expand Down Expand Up @@ -38,10 +41,14 @@ export default function NativeTTSMessage({ message }) {
onClick={speakMessage}
data-tooltip-id="message-to-speech"
data-tooltip-content={
speaking ? "Pause TTS speech of message" : "TTS Speak message"
speaking
? t("chat-history.tts.pause-tts")
: t("chat-history.tts.start-tts")
}
className="border-none text-[var(--theme-sidebar-footer-icon-fill)]"
aria-label={speaking ? "Pause speech" : "Speak message"}
aria-label={
speaking ? t("chat-history.tts.pause") : t("chat-history.tts.start")
}
>
{speaking ? (
<PauseCircle size={18} className="mb-1" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { useEffect, useState, useRef } from "react";
import { SpeakerHigh, PauseCircle, CircleNotch } from "@phosphor-icons/react";
import PiperTTSClient from "@/utils/piperTTS";
import { useTranslation } from "react-i18next";

export default function PiperTTS({ voiceId = null, message }) {
const playerRef = useRef(null);
const [speaking, setSpeaking] = useState(false);
const [loading, setLoading] = useState(false);
const [audioSrc, setAudioSrc] = useState(null);
const { t } = useTranslation();

async function speakMessage(e) {
e.preventDefault();
Expand Down Expand Up @@ -55,10 +57,14 @@ export default function PiperTTS({ voiceId = null, message }) {
disabled={loading}
data-tooltip-id="message-to-speech"
data-tooltip-content={
speaking ? "Pause TTS speech of message" : "TTS Speak message"
speaking
? t("chat-history.tts.pause-tts")
: t("chat-history.tts.start-tts")
}
className="border-none text-[var(--theme-sidebar-footer-icon-fill)]"
aria-label={speaking ? "Pause speech" : "Speak message"}
aria-label={
speaking ? t("chat-history.tts.pause") : t("chat-history.tts.start")
}
>
{speaking ? (
<PauseCircle size={18} className="mb-1" />
Expand Down
Loading