✨(frontend) chose transcription’s language in settings
Add a key feature allowing users to choose the language of their transcription via a setting. The default value is set to French, the most commonly used language across our user base. Users can still select English or “Automatic,” which re-enables automatic language detection if no default is configured on the microservice.
This commit is contained in:
committed by
aleb_the_flash
parent
19f8c96e9d
commit
049a9079c4
@@ -13,7 +13,11 @@ import {
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { ConnectionState, RoomEvent } from 'livekit-client'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RecordingStatus, recordingStore } from '@/stores/recording'
|
||||
import {
|
||||
RecordingLanguage,
|
||||
RecordingStatus,
|
||||
recordingStore,
|
||||
} from '@/stores/recording'
|
||||
import { FeatureFlags } from '@/features/analytics/enums'
|
||||
import {
|
||||
NotificationType,
|
||||
@@ -31,6 +35,12 @@ import { LoginButton } from '@/components/LoginButton'
|
||||
import { HStack, VStack } from '@/styled-system/jsx'
|
||||
import { Checkbox } from '@/primitives/Checkbox.tsx'
|
||||
|
||||
import {
|
||||
useSettingsDialog,
|
||||
SettingsDialogExtendedKey,
|
||||
useTranscriptionLanguageOptions,
|
||||
} from '@/features/settings'
|
||||
|
||||
export const TranscriptSidePanel = () => {
|
||||
const { data } = useConfig()
|
||||
|
||||
@@ -45,6 +55,9 @@ export const TranscriptSidePanel = () => {
|
||||
const recordingSnap = useSnapshot(recordingStore)
|
||||
|
||||
const { notifyParticipants } = useNotifyParticipants()
|
||||
const languageOptions = useTranscriptionLanguageOptions()
|
||||
|
||||
const { openSettingsDialog } = useSettingsDialog()
|
||||
|
||||
const hasTranscriptAccess = useHasRecordingAccess(
|
||||
RecordingMode.Transcript,
|
||||
@@ -115,7 +128,9 @@ export const TranscriptSidePanel = () => {
|
||||
: RecordingMode.Transcript
|
||||
|
||||
const recordingOptions = {
|
||||
language: 'fr', // fix hardcoded language
|
||||
...(recordingSnap.language != RecordingLanguage.AUTOMATIC && {
|
||||
language: recordingSnap.language,
|
||||
}),
|
||||
...(includeScreenRecording && { transcribe: true }),
|
||||
}
|
||||
|
||||
@@ -459,9 +474,27 @@ export const TranscriptSidePanel = () => {
|
||||
<div
|
||||
className={css({
|
||||
flex: 5,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '0.25rem',
|
||||
})}
|
||||
>
|
||||
<Text variant="sm">{t('details.language')}</Text>
|
||||
<Text variant="sm">
|
||||
<Button
|
||||
variant="text"
|
||||
size="xs"
|
||||
onPress={() =>
|
||||
openSettingsDialog(SettingsDialogExtendedKey.TRANSCRIPTION)
|
||||
}
|
||||
>
|
||||
{
|
||||
languageOptions.find(
|
||||
(option) => option.key == recordingSnap.language
|
||||
)?.label
|
||||
}
|
||||
</Button>
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import { NotificationsTab } from './tabs/NotificationsTab'
|
||||
import { GeneralTab } from './tabs/GeneralTab'
|
||||
import { AudioTab } from './tabs/AudioTab'
|
||||
import { VideoTab } from './tabs/VideoTab'
|
||||
import { TranscriptionTab } from './tabs/TranscriptionTab'
|
||||
import { useRef } from 'react'
|
||||
import { useMediaQuery } from '@/features/rooms/livekit/hooks/useMediaQuery'
|
||||
import { SettingsDialogExtendedKey } from '@/features/settings/type'
|
||||
@@ -100,6 +101,11 @@ export const SettingsDialogExtended = (props: SettingsDialogExtended) => {
|
||||
{isWideScreen &&
|
||||
t(`tabs.${SettingsDialogExtendedKey.NOTIFICATIONS}`)}
|
||||
</Tab>
|
||||
<Tab icon highlight id={SettingsDialogExtendedKey.TRANSCRIPTION}>
|
||||
<span className="material-symbols">speech_to_text</span>
|
||||
{isWideScreen &&
|
||||
t(`tabs.${SettingsDialogExtendedKey.TRANSCRIPTION}`)}
|
||||
</Tab>
|
||||
</TabList>
|
||||
</div>
|
||||
<div className={tabPanelContainerStyle}>
|
||||
@@ -111,6 +117,8 @@ export const SettingsDialogExtended = (props: SettingsDialogExtended) => {
|
||||
<VideoTab id={SettingsDialogExtendedKey.VIDEO} />
|
||||
<GeneralTab id={SettingsDialogExtendedKey.GENERAL} />
|
||||
<NotificationsTab id={SettingsDialogExtendedKey.NOTIFICATIONS} />
|
||||
{/* Transcription tab won't be accessible if the tab is not active in the tab list */}
|
||||
<TranscriptionTab id={SettingsDialogExtendedKey.TRANSCRIPTION} />
|
||||
</div>
|
||||
</Tabs>
|
||||
</Dialog>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { TabPanel, TabPanelProps } from '@/primitives/Tabs'
|
||||
import { Field, H } from '@/primitives'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RecordingLanguage, recordingStore } from '@/stores/recording'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { useTranscriptionLanguageOptions } from '../../hook/useTranscriptionLanguageOptions'
|
||||
|
||||
export type TranscriptionTabProps = Pick<TabPanelProps, 'id'>
|
||||
|
||||
export const TranscriptionTab = ({ id }: TranscriptionTabProps) => {
|
||||
const { t } = useTranslation('settings', { keyPrefix: 'transcription' })
|
||||
const recordingSnap = useSnapshot(recordingStore)
|
||||
|
||||
const languageOptions = useTranscriptionLanguageOptions()
|
||||
|
||||
return (
|
||||
<TabPanel padding={'md'} flex id={id}>
|
||||
<H lvl={2}>{t('heading')}</H>
|
||||
<Field
|
||||
type="select"
|
||||
label={t('language.label')}
|
||||
items={languageOptions}
|
||||
selectedKey={recordingSnap.language}
|
||||
onSelectionChange={(lang) => {
|
||||
recordingStore.language = lang as RecordingLanguage
|
||||
}}
|
||||
/>
|
||||
</TabPanel>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { useMemo } from 'react'
|
||||
import { RecordingLanguage } from '@/stores/recording'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export const useTranscriptionLanguageOptions = () => {
|
||||
const { t } = useTranslation('settings', { keyPrefix: 'transcription' })
|
||||
|
||||
return useMemo(
|
||||
() => [
|
||||
{
|
||||
key: RecordingLanguage.FRENCH,
|
||||
value: RecordingLanguage.FRENCH,
|
||||
label: t('language.options.french'),
|
||||
},
|
||||
{
|
||||
key: RecordingLanguage.ENGLISH,
|
||||
value: RecordingLanguage.ENGLISH,
|
||||
label: t('language.options.english'),
|
||||
},
|
||||
{
|
||||
key: RecordingLanguage.AUTOMATIC,
|
||||
value: RecordingLanguage.AUTOMATIC,
|
||||
label: t('language.options.auto'),
|
||||
},
|
||||
],
|
||||
[t]
|
||||
)
|
||||
}
|
||||
@@ -1,2 +1,7 @@
|
||||
export { SettingsButton } from './components/SettingsButton'
|
||||
export { SettingsDialog } from './components/SettingsDialog'
|
||||
|
||||
export { useTranscriptionLanguageOptions } from './hook/useTranscriptionLanguageOptions'
|
||||
export { useSettingsDialog } from './hook/useSettingsDialog'
|
||||
|
||||
export { SettingsDialogExtendedKey } from './type.ts'
|
||||
|
||||
@@ -4,4 +4,5 @@ export enum SettingsDialogExtendedKey {
|
||||
VIDEO = 'video',
|
||||
GENERAL = 'general',
|
||||
NOTIFICATIONS = 'notifications',
|
||||
TRANSCRIPTION = 'transcription',
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@
|
||||
"receiver": "Das Transkript wird an den Organisator und die Mitorganisatoren gesendet.",
|
||||
"destination": "Ein neues Dokument wird erstellt auf",
|
||||
"destinationUnknown": "Ein neues Dokument wird erstellt",
|
||||
"language": "Meeting-Sprachen: Französisch (fr)",
|
||||
"language": "Meeting-Sprache:",
|
||||
"recording": "Auch eine Aufzeichnung starten"
|
||||
},
|
||||
"button": {
|
||||
|
||||
@@ -68,6 +68,17 @@
|
||||
},
|
||||
"permissionsRequired": "Berechtigungen erforderlich"
|
||||
},
|
||||
"transcription": {
|
||||
"heading": "Transkription",
|
||||
"language": {
|
||||
"label": "Besprechungssprache",
|
||||
"options": {
|
||||
"french": "Französisch (fr)",
|
||||
"english": "Englisch (en)",
|
||||
"auto": "Automatisch"
|
||||
}
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
"heading": "Tonbenachrichtigungen",
|
||||
"label": "Tonbenachrichtigungen für",
|
||||
@@ -100,6 +111,7 @@
|
||||
"audio": "Audio",
|
||||
"video": "Video",
|
||||
"general": "Allgemein",
|
||||
"notifications": "Benachrichtigungen"
|
||||
"notifications": "Benachrichtigungen",
|
||||
"transcription": "Transkription"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@
|
||||
"receiver": "The transcript will be sent to the host and co-hosts.",
|
||||
"destination": "A new document will be created on",
|
||||
"destinationUnknown": "A new document will be created",
|
||||
"language": "Meeting language: French (fr)",
|
||||
"language": "Meeting language:",
|
||||
"recording": "Also start a recording"
|
||||
},
|
||||
"button": {
|
||||
|
||||
@@ -68,6 +68,17 @@
|
||||
},
|
||||
"permissionsRequired": "Permissions required"
|
||||
},
|
||||
"transcription": {
|
||||
"heading": "Transcription",
|
||||
"language": {
|
||||
"label": "Meeting language",
|
||||
"options": {
|
||||
"french": "French (fr)",
|
||||
"english": "English (en)",
|
||||
"auto": "Automatic"
|
||||
}
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
"heading": "Sound notifications",
|
||||
"label": "sound notifications for",
|
||||
@@ -100,6 +111,7 @@
|
||||
"audio": "Audio",
|
||||
"video": "Video",
|
||||
"general": "General",
|
||||
"notifications": "Notifications"
|
||||
"notifications": "Notifications",
|
||||
"transcription": "Transcription"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@
|
||||
"receiver": "La transcription sera envoyée à l'organisateur et aux coorganisateurs.",
|
||||
"destination": "Un nouveau document sera créé sur",
|
||||
"destinationUnknown": "Un nouveau document sera créé",
|
||||
"language": "Langues de la réunion : Français (fr)",
|
||||
"language": "Langue de la réunion :",
|
||||
"recording": "Démarrer aussi un enregistrement"
|
||||
},
|
||||
"button": {
|
||||
|
||||
@@ -68,6 +68,17 @@
|
||||
},
|
||||
"permissionsRequired": "Autorisations nécessaires"
|
||||
},
|
||||
"transcription": {
|
||||
"heading": "Transcription",
|
||||
"language": {
|
||||
"label": "Langue de la réunion",
|
||||
"options": {
|
||||
"french": "Français (fr)",
|
||||
"english": "Anglais (en)",
|
||||
"auto": "Automatique"
|
||||
}
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
"heading": "Notifications sonores",
|
||||
"label": "la notification sonore pour",
|
||||
@@ -100,6 +111,7 @@
|
||||
"audio": "Audio",
|
||||
"video": "Vidéo",
|
||||
"general": "Général",
|
||||
"notifications": "Notifications"
|
||||
"notifications": "Notifications",
|
||||
"transcription": "Transcription"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@
|
||||
"receiver": "Het transcript wordt verzonden naar de organisator en medeorganisatoren.",
|
||||
"destination": "Er wordt een nieuw document aangemaakt op",
|
||||
"destinationUnknown": "Een nieuw document wordt aangemaakt",
|
||||
"language": "Vergadertalen: Frans (fr)",
|
||||
"language": "Vergadertalen:",
|
||||
"recording": "Start ook een opname"
|
||||
},
|
||||
"button": {
|
||||
|
||||
@@ -68,6 +68,17 @@
|
||||
},
|
||||
"permissionsRequired": "Machtigingen vereist"
|
||||
},
|
||||
"transcription": {
|
||||
"heading": "Transcriptie",
|
||||
"language": {
|
||||
"label": "Vergadertaal",
|
||||
"options": {
|
||||
"french": "Frans (fr)",
|
||||
"english": "Engels (en)",
|
||||
"auto": "Automatisch"
|
||||
}
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
"heading": "Geluidsmeldingen",
|
||||
"label": "Geluidsmeldingen voor",
|
||||
@@ -100,6 +111,7 @@
|
||||
"audio": "Audio",
|
||||
"video": "Video",
|
||||
"general": "Algemeen",
|
||||
"notifications": "Meldingen"
|
||||
"notifications": "Meldingen",
|
||||
"transcription": "Transcriptie"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { proxy } from 'valtio'
|
||||
|
||||
export enum RecordingLanguage {
|
||||
ENGLISH = 'en',
|
||||
FRENCH = 'fr',
|
||||
AUTOMATIC = 'auto',
|
||||
}
|
||||
|
||||
export enum RecordingStatus {
|
||||
TRANSCRIPT_STARTING,
|
||||
TRANSCRIPT_STARTED,
|
||||
@@ -13,8 +19,10 @@ export enum RecordingStatus {
|
||||
|
||||
type State = {
|
||||
status: RecordingStatus
|
||||
language: RecordingLanguage
|
||||
}
|
||||
|
||||
export const recordingStore = proxy<State>({
|
||||
status: RecordingStatus.STOPPED,
|
||||
language: RecordingLanguage.FRENCH,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user