adds three-letter language picker

This commit is contained in:
Sara
2023-10-13 23:35:19 +02:00
parent 3555cc3a3c
commit 47fc52af11
8 changed files with 593 additions and 78 deletions

View File

@@ -0,0 +1,496 @@
import Script from "next/script";
// type Script = 'Latn' | 'Ethi' | 'Arab' | 'Beng' | 'Cyrl' | 'Taml' | 'Hant' | 'Hans' | 'Grek' | 'Gujr' | 'Hebr'| 'Deva'| 'Armn' | 'Jpan' | 'Knda' | 'Geor';
type LanguageOption = {
value: string | undefined;
name: string;
script?: string;
};
const supportedLanguages: LanguageOption[] = [
{ value: "afr", name: "Afrikaans", script: "Latn" },
{
value: "amh",
name: "Amharic",
script: "Ethi",
},
{
value: "arb",
name: "Modern Standard Arabic",
script: "Arab",
},
{
value: "ary",
name: "Moroccan Arabic",
script: "Arab",
},
{
value: "arz",
name: "Egyptian Arabic",
script: "Arab",
},
{
value: "asm",
name: "Assamese",
script: "Beng",
},
{
value: "azj",
name: "North Azerbaijani",
script: "Latn",
},
{
value: "bel",
name: "Belarusian",
script: "Cyrl",
},
{
value: "ben",
name: "Bengali",
script: "Beng",
},
{
value: "bos",
name: "Bosnian",
script: "Latn",
},
{
value: "bul",
name: "Bulgarian",
script: "Cyrl",
},
{
value: "cat",
name: "Catalan",
script: "Latn",
},
{
value: "ceb",
name: "Cebuano",
script: "Latn",
},
{
value: "ces",
name: "Czech",
script: "Latn",
},
{
value: "ckb",
name: "Central Kurdish",
script: "Arab",
},
{
value: "cmn",
name: "Mandarin Chinese",
script: "Hans",
},
{
value: "cmn_Ha",
name: "Mandarin Chinese",
script: "Hant",
},
{
value: "cym",
name: "Welsh",
script: "Latn",
},
{
value: "dan",
name: "Danish",
script: "Latn",
},
{
value: "deu",
name: "German",
script: "Latn",
},
{
value: "ell",
name: "Greek",
script: "Grek",
},
{
value: "eng",
name: "English",
script: "Latn",
},
{
value: "est",
name: "Estonian",
script: "Latn",
},
{
value: "eus",
name: "Basque",
script: "Latn",
},
{
value: "fin",
name: "Finnish",
script: "Latn",
},
{
value: "fra",
name: "French",
script: "Latn",
},
{
value: "gaz",
name: "West Central Oromo",
script: "Latn",
},
{
value: "gle",
name: "Irish",
script: "Latn",
},
{
value: "glg",
name: "Galician",
script: "Latn",
},
{
value: "guj",
name: "Gujarati",
script: "Gujr",
},
{
value: "heb",
name: "Hebrew",
script: "Hebr",
},
{
value: "hin",
name: "Hindi",
script: "Deva",
},
{
value: "hrv",
name: "Croatian",
script: "Latn",
},
{
value: "hun",
name: "Hungarian",
script: "Latn",
},
{
value: "hye",
name: "Armenian",
script: "Armn",
},
{
value: "ibo",
name: "Igbo",
script: "Latn",
},
{
value: "ind",
name: "Indonesian",
script: "Latn",
},
{
value: "isl",
name: "Icelandic",
script: "Latn",
},
{
value: "ita",
name: "Italian",
script: "Latn",
},
{
value: "jav",
name: "Javanese",
script: "Latn",
},
{
value: "jpn",
name: "Japanese",
script: "Jpan",
},
{
value: "kan",
name: "Kannada",
script: "Knda",
},
{
value: "kat",
name: "Georgian",
script: "Geor",
},
{
value: "kaz",
name: "Kazakh",
script: "Cyrl",
},
{
value: "khk",
name: "Halh Mongolian",
script: "Cyrl",
},
{
value: "khm",
name: "Khmer",
script: "Khmr",
},
{
value: "kir",
name: "Kyrgyz",
script: "Cyrl",
},
{
value: "kor",
name: "Korean",
script: "Kore",
},
{
value: "lao",
name: "Lao",
script: "Laoo",
},
{
value: "lit",
name: "Lithuanian",
script: "Latn",
},
{
value: "lug",
name: "Ganda",
script: "Latn",
},
{
value: "luo",
name: "Luo",
script: "Latn",
},
{
value: "lvs",
name: "Standard Latvian",
script: "Latn",
},
{
value: "mai",
name: "Maithili",
script: "Deva",
},
{
value: "mal",
name: "Malayalam",
script: "Mlym",
},
{
value: "mar",
name: "Marathi",
script: "Deva",
},
{
value: "mkd",
name: "Macedonian",
script: "Cyrl",
},
{
value: "mlt",
name: "Maltese",
script: "Latn",
},
{
value: "mni",
name: "Meitei",
script: "Beng",
},
{
value: "mya",
name: "Burmese",
script: "Mymr",
},
{
value: "nld",
name: "Dutch",
script: "Latn",
},
{
value: "nno",
name: "Norwegian Nynorsk",
script: "Latn",
},
{
value: "nob",
name: "Norwegian Bokmål",
script: "Latn",
},
{
value: "npi",
name: "Nepali",
script: "Deva",
},
{
value: "nya",
name: "Nyanja",
script: "Latn",
},
{
value: "ory",
name: "Odia",
script: "Orya",
},
{
value: "pan",
name: "Punjabi",
script: "Guru",
},
{
value: "pbt",
name: "Southern Pashto",
script: "Arab",
},
{
value: "pes",
name: "Western Persian",
script: "Arab",
},
{
value: "pol",
name: "Polish",
script: "Latn",
},
{
value: "por",
name: "Portuguese",
script: "Latn",
},
{
value: "ron",
name: "Romanian",
script: "Latn",
},
{
value: "rus",
name: "Russian",
script: "Cyrl",
},
{
value: "slk",
name: "Slovak",
script: "Latn",
},
{
value: "slv",
name: "Slovenian",
script: "Latn",
},
{
value: "sna",
name: "Shona",
script: "Latn",
},
{
value: "snd",
name: "Sindhi",
script: "Arab",
},
{
value: "som",
name: "Somali",
script: "Latn",
},
{
value: "spa",
name: "Spanish",
script: "Latn",
},
{
value: "srp",
name: "Serbian",
script: "Cyrl",
},
{
value: "swe",
name: "Swedish",
script: "Latn",
},
{
value: "swh",
name: "Swahili",
script: "Latn",
},
{
value: "tam",
name: "Tamil",
script: "Taml",
},
{
value: "tel",
name: "Telugu",
script: "Telu",
},
{
value: "tgk",
name: "Tajik",
script: "Cyrl",
},
{
value: "tgl",
name: "Tagalog",
script: "Latn",
},
{
value: "tha",
name: "Thai",
script: "Thai",
},
{
value: "tur",
name: "Turkish",
script: "Latn",
},
{
value: "ukr",
name: "Ukrainian",
script: "Cyrl",
},
{
value: "urd",
name: "Urdu",
script: "Arab",
},
{
value: "uzn",
name: "Northern Uzbek",
script: "Latn",
},
{
value: "vie",
name: "Vietnamese",
script: "Latn",
},
{
value: "yor",
name: "Yoruba",
script: "Latn",
},
{
value: "yue",
name: "Cantonese",
script: "Hant",
},
{
value: "zsm",
name: "Standard Malay",
script: "Latn",
},
{
value: "zul",
name: "Zulu",
script: "Latn",
},
];
const supportedLatinLanguages = supportedLanguages.filter(
(lan) => lan.script == "Latn",
);
supportedLatinLanguages.push({ value: undefined, name: "None" });
export { supportedLatinLanguages };
export default supportedLanguages;

View File

@@ -41,14 +41,7 @@ const TranscriptRecord = (details: TranscriptDetails) => {
const webRTC = useWebRTC(stream, details.params.transcriptId, api);
const webSockets = useWebSockets(details.params.transcriptId);
const {
loading,
permissionOk,
permissionDenied,
audioDevices,
requestPermission,
getAudioStream,
} = useAudioDevice();
const { audioDevices, getAudioStream } = useAudioDevice();
const [hasRecorded, setHasRecorded] = useState(false);
const [transcriptStarted, setTranscriptStarted] = useState(false);
@@ -115,7 +108,10 @@ const TranscriptRecord = (details: TranscriptDetails) => {
you start recording.
</div>
) : (
<LiveTrancription text={webSockets.transcriptText} />
<LiveTrancription
text={webSockets.transcriptText}
translateText={webSockets.translateText}
/>
)}
</div>
</div>

View File

@@ -25,7 +25,7 @@ const useCreateTranscript = (): CreateTranscript => {
const requestParameters: V1TranscriptsCreateRequest = {
createTranscript: {
name: params.name || "Weekly All-Hands", // Default
targetLanguage: params.targetLanguage || "en", // Default
targetLanguage: params.targetLanguage || "eng", // Default
},
};

View File

@@ -1,14 +1,19 @@
type LiveTranscriptionProps = {
text: string;
translateText: string;
};
export default function LiveTrancription(props: LiveTranscriptionProps) {
return (
<div className="text-center p-4">
<p className="text-lg md:text-xl font-bold line-clamp-4">
{/* Nous allons prendre quelques appels téléphoniques et répondre à quelques questions */}
{props.translateText ? props.translateText : props.text}
</p>
{props.translateText && (
<p className="text-base md:textlg font-bold line-clamp-4">
{props.text}
</p>
)}
</div>
);
}

View File

@@ -1,20 +1,15 @@
"use client";
import React, { useEffect, useState } from "react";
import useWebRTC from "../useWebRTC";
import useTranscript from "../useTranscript";
import { useWebSockets } from "../useWebSockets";
import useAudioDevice from "../useAudioDevice";
import "../../styles/button.css";
import { Topic } from "../webSocketTypes";
import getApi from "../../lib/getApi";
import About from "../../(aboutAndPrivacy)/about";
import Privacy from "../../(aboutAndPrivacy)/privacy";
import { lockWakeState, releaseWakeState } from "../../lib/wakeLock";
import { useRouter } from "next/navigation";
import createTranscript from "../createTranscript";
import { GetTranscript } from "../../api";
import { Router } from "next/router";
import useCreateTranscript from "../createTranscript";
import SelectSearch from "react-select-search";
import { supportedLatinLanguages } from "../../supportedLanguages";
import "react-select-search/style.css";
const TranscriptCreate = () => {
// const transcript = useTranscript(stream, api);
@@ -27,6 +22,10 @@ const TranscriptCreate = () => {
};
const [targetLanguage, setTargetLanguage] = useState<string>();
const onLanguageChange = (newval) => {
typeof newval === "string" && setTargetLanguage(newval);
};
const createTranscript = useCreateTranscript();
const send = () => {
@@ -38,54 +37,61 @@ const TranscriptCreate = () => {
router.push(`/transcripts/${createTranscript.response.id}/record`);
}, [createTranscript.response]);
const {
loading,
permissionOk,
permissionDenied,
audioDevices,
requestPermission,
getAudioStream,
} = useAudioDevice();
const { loading, permissionOk, permissionDenied, requestPermission } =
useAudioDevice();
return (
<>
<div></div>
<div className="max-h-full overflow-auto">
<div className="grid grid-cols-1 lg:grid-cols-2 grid-rows-mobile-inner lg:grid-rows-1 gap-2 lg:gap-4 h-full">
<section className="flex flex-col w-full h-full items-center justify-evenly p-4 md:px-6 md:py-8">
<div>
<div className="flex flex-col max-w-xl items-center justify-center">
<h1 className="text-2xl font-bold mb-2">
Welcome to reflector.media
</h1>
<p>
Reflector is a transcription and summarization pipeline that
transforms audio into knowledge. The output is meeting minutes
and topic summaries enabling topic-specific analyses stored in
your systems of record. This is accomplished on your
infrastructure without 3rd parties keeping your data
private, secure, and organized.
transforms audio into knowledge. The output is meeting minutes and
topic summaries enabling topic-specific analyses stored in your
systems of record. This is accomplished on your infrastructure
without 3rd parties keeping your data private, secure, and
organized.
</p>
<About buttonText="Learn more" />
</div>
</section>
<section className="rounded-xl md:bg-blue-200 flex flex-col justify-start p-6">
<h2 className="text-2xl font-bold mt-4 mb-2"> Try Reflector</h2>
<label className="mb-3">
<p>What is this meeting about ?</p>
<input type="text" onChange={nameChange} />
<button onClick={() => setTargetLanguage("fr")}>Language</button>
<h2 className="text-2xl font-bold mt-4 mb-2">
Audio Permissions
</h2>
</label>
<label className="mb-3">
<p>Do you need live translation ?</p>
<SelectSearch
search
options={supportedLatinLanguages}
value={targetLanguage}
onChange={onLanguageChange}
placeholder="Choose your language"
/>
</label>
{loading ? (
<p className="text-center">Checking permission...</p>
<p className="">Checking permission...</p>
) : permissionOk ? (
<> Microphone permission granted </>
) : (
<>
<p className="text-center">
<p className="">
In order to use Reflector, we kindly request permission to
access your microphone during meetings and events.
<br />
<Privacy buttonText="Privacy policy" />
<br />
{permissionDenied
? "Permission to use your microphone was denied, please change the permission setting in your browser and refresh this page."
: "Please grant permission to continue."}
{permissionDenied &&
"Permission to use your microphone was denied, please change the permission setting in your browser and refresh this page."}
</p>
<button
className="mt-4 bg-blue-400 hover:bg-blue-500 focus-visible:bg-blue-500 text-white font-bold py-2 px-4 rounded m-auto"
@@ -96,11 +102,9 @@ const TranscriptCreate = () => {
</button>
</>
)}
</div>
<button onClick={send} disabled={!permissionOk}>
{createTranscript.loading ? "loading" : "Send"}
</button>
</div>
</section>
</div>
</>

View File

@@ -5,6 +5,7 @@ import { useRouter } from "next/navigation";
type UseWebSockets = {
transcriptText: string;
translateText: string;
topics: Topic[];
finalSummary: FinalSummary;
status: Status;
@@ -12,7 +13,9 @@ type UseWebSockets = {
export const useWebSockets = (transcriptId: string | null): UseWebSockets => {
const [transcriptText, setTranscriptText] = useState<string>("");
const [translateText, setTranslateText] = useState<string>("");
const [textQueue, setTextQueue] = useState<string[]>([]);
const [translationQueue, setTranslationQueue] = useState<string[]>([]);
const [isProcessing, setIsProcessing] = useState(false);
const [topics, setTopics] = useState<Topic[]>([]);
const [finalSummary, setFinalSummary] = useState<FinalSummary>({
@@ -30,6 +33,8 @@ export const useWebSockets = (transcriptId: string | null): UseWebSockets => {
setIsProcessing(true);
const text = textQueue[0];
setTranscriptText(text);
setTranslateText(translationQueue[0]);
console.log("displaying " + translateText);
const WPM_READING = 200 + textQueue.length * 10; // words per minute to read
const wordCount = text.split(/\s+/).length;
@@ -38,6 +43,7 @@ export const useWebSockets = (transcriptId: string | null): UseWebSockets => {
setTimeout(() => {
setIsProcessing(false);
setTextQueue((prevQueue) => prevQueue.slice(1));
setTranslationQueue((prevQueue) => prevQueue.slice(1));
}, delay);
}, [textQueue, isProcessing]);
@@ -158,11 +164,13 @@ export const useWebSockets = (transcriptId: string | null): UseWebSockets => {
switch (message.event) {
case "TRANSCRIPT":
const newText = (message.data.text ?? "").trim();
const newTranslation = (message.data.translation ?? "").trim();
if (!newText) break;
console.debug("TRANSCRIPT event:", newText);
setTextQueue((prevQueue) => [...prevQueue, newText]);
setTranslationQueue((prevQueue) => [...prevQueue, newTranslation]);
break;
case "TOPIC":
@@ -233,5 +241,5 @@ export const useWebSockets = (transcriptId: string | null): UseWebSockets => {
};
}, [transcriptId]);
return { transcriptText, topics, finalSummary, status };
return { transcriptText, translateText, topics, finalSummary, status };
};

View File

@@ -27,6 +27,7 @@
"react-dropdown": "^1.11.0",
"react-markdown": "^9.0.0",
"react-qr-code": "^2.0.12",
"react-select-search": "^4.1.7",
"sass": "^1.63.6",
"simple-peer": "^9.11.1",
"superagent": "^8.0.9",

View File

@@ -2088,6 +2088,11 @@ react-qr-code@^2.0.12:
prop-types "^15.8.1"
qr.js "0.0.0"
react-select-search@^4.1.7:
version "4.1.7"
resolved "https://registry.yarnpkg.com/react-select-search/-/react-select-search-4.1.7.tgz#5662729b9052282bde52e1352006d495d9c5ed6e"
integrity sha512-pU7ONAdK+bmz2tbhBWYQv9m5mnXOn8yImuiy+5UhimIG80d5iKv3nSYJIjJWjDbdrrdoXiCRwQm8xbA8llTjmQ==
react@^18.2.0:
version "18.2.0"
resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz"