feat(translation) enable cc translation (#12046)
* feat(translation) enable cc translation * Refactor translation for ListItem. * fix language file sorting * fix translation order * change import order
This commit is contained in:
parent
e8de9b4d66
commit
b4f98e7386
|
@ -1194,7 +1194,7 @@
|
||||||
"silence": "Silence",
|
"silence": "Silence",
|
||||||
"speakerStats": "Speaker stats",
|
"speakerStats": "Speaker stats",
|
||||||
"startScreenSharing": "Start screen sharing",
|
"startScreenSharing": "Start screen sharing",
|
||||||
"startSubtitles": "Start subtitles",
|
"startSubtitles": "Subtitles • {{language}}",
|
||||||
"stopAudioSharing": "Stop audio sharing",
|
"stopAudioSharing": "Stop audio sharing",
|
||||||
"stopScreenSharing": "Stop screen sharing",
|
"stopScreenSharing": "Stop screen sharing",
|
||||||
"stopSharedVideo": "Stop video",
|
"stopSharedVideo": "Stop video",
|
||||||
|
@ -1217,6 +1217,8 @@
|
||||||
"pending": "Preparing to transcribe the meeting...",
|
"pending": "Preparing to transcribe the meeting...",
|
||||||
"start": "Start showing subtitles",
|
"start": "Start showing subtitles",
|
||||||
"stop": "Stop showing subtitles",
|
"stop": "Stop showing subtitles",
|
||||||
|
"subtitles": "Subtitles",
|
||||||
|
"subtitlesOff": "Off",
|
||||||
"tr": "TR"
|
"tr": "TR"
|
||||||
},
|
},
|
||||||
"userMedia": {
|
"userMedia": {
|
||||||
|
|
|
@ -36,6 +36,23 @@ const COUNTRIES = _.merge({}, COUNTRIES_RESOURCES, COUNTRIES_RESOURCES_OVERRIDES
|
||||||
*/
|
*/
|
||||||
export const LANGUAGES: Array<string> = Object.keys(LANGUAGES_RESOURCES);
|
export const LANGUAGES: Array<string> = Object.keys(LANGUAGES_RESOURCES);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The languages for the top section of the translation language list.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @type {Array<string>}
|
||||||
|
*/
|
||||||
|
export const TRANSLATION_LANGUAGES_HEAD: Array<string> = [ 'en' ];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The languages to explude from the translation language list.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @type {Array<string>}
|
||||||
|
*/
|
||||||
|
export const TRANSLATION_LANGUAGES_EXCLUDE: Array<string>
|
||||||
|
= [ 'enGB', 'esUS', 'frCA', 'hsb', 'kab', 'ptBR', 'zhCN', 'zhTW' ];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default language.
|
* The default language.
|
||||||
*
|
*
|
||||||
|
|
|
@ -3,4 +3,5 @@ export * from './functions';
|
||||||
|
|
||||||
// TODO Eventually (e.g. when the non-React Web app is rewritten into React), it
|
// TODO Eventually (e.g. when the non-React Web app is rewritten into React), it
|
||||||
// should not be necessary to export i18next.
|
// should not be necessary to export i18next.
|
||||||
export { default as i18next, DEFAULT_LANGUAGE, LANGUAGES } from './i18next';
|
export { default as i18next, DEFAULT_LANGUAGE,
|
||||||
|
LANGUAGES, TRANSLATION_LANGUAGES_HEAD, TRANSLATION_LANGUAGES_EXCLUDE } from './i18next';
|
||||||
|
|
|
@ -112,6 +112,8 @@ export default class AbstractButton<P: Props, S: *> extends Component<P, S> {
|
||||||
*/
|
*/
|
||||||
accessibilityLabel: string;
|
accessibilityLabel: string;
|
||||||
|
|
||||||
|
labelProps: Object;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The icon of this button.
|
* The icon of this button.
|
||||||
*
|
*
|
||||||
|
@ -326,6 +328,7 @@ export default class AbstractButton<P: Props, S: *> extends Component<P, S> {
|
||||||
elementAfter: this._getElementAfter(),
|
elementAfter: this._getElementAfter(),
|
||||||
icon: this._getIcon(),
|
icon: this._getIcon(),
|
||||||
label: this._getLabel(),
|
label: this._getLabel(),
|
||||||
|
labelProps: this.labelProps,
|
||||||
styles: this._getStyles(),
|
styles: this._getStyles(),
|
||||||
toggled: this._isToggled(),
|
toggled: this._isToggled(),
|
||||||
tooltip: this._getTooltip()
|
tooltip: this._getTooltip()
|
||||||
|
|
|
@ -148,7 +148,7 @@ export default class AbstractToolboxItem<P : Props> extends Component<P> {
|
||||||
* @returns {?string}
|
* @returns {?string}
|
||||||
*/
|
*/
|
||||||
get label(): ?string {
|
get label(): ?string {
|
||||||
return this._maybeTranslateAttribute(this.props.label);
|
return this._maybeTranslateAttribute(this.props.label, this.props.labelProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -178,12 +178,18 @@ export default class AbstractToolboxItem<P : Props> extends Component<P> {
|
||||||
* function is available.
|
* function is available.
|
||||||
*
|
*
|
||||||
* @param {string} text - What needs translating.
|
* @param {string} text - What needs translating.
|
||||||
|
* @param {string} textProps - Additional properties for translation text.
|
||||||
* @private
|
* @private
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
_maybeTranslateAttribute(text) {
|
_maybeTranslateAttribute(text, textProps) {
|
||||||
const { t } = this.props;
|
const { t } = this.props;
|
||||||
|
|
||||||
|
if (textProps) {
|
||||||
|
|
||||||
|
return typeof t === 'function' ? t(text, textProps) : `${text} ${textProps}`;
|
||||||
|
}
|
||||||
|
|
||||||
return typeof t === 'function' ? t(text) : text;
|
return typeof t === 'function' ? t(text) : text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,18 @@ export const REMOVE_TRANSCRIPT_MESSAGE = 'REMOVE_TRANSCRIPT_MESSAGE';
|
||||||
*/
|
*/
|
||||||
export const UPDATE_TRANSCRIPT_MESSAGE = 'UPDATE_TRANSCRIPT_MESSAGE';
|
export const UPDATE_TRANSCRIPT_MESSAGE = 'UPDATE_TRANSCRIPT_MESSAGE';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of (redux) action which indicates that a transcript with an
|
||||||
|
* given message_id to be added or updated is received.
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* type: UPDATE_TRANSLATION_LANGUAGE,
|
||||||
|
* transcriptMessageID: string,
|
||||||
|
* newTranscriptMessage: Object
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export const UPDATE_TRANSLATION_LANGUAGE = 'UPDATE_TRANSLATION_LANGUAGE';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of (redux) action which indicates that the user pressed the
|
* The type of (redux) action which indicates that the user pressed the
|
||||||
* ClosedCaption button, to either enable or disable subtitles based on the
|
* ClosedCaption button, to either enable or disable subtitles based on the
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
|
import { toggleDialog } from '../base/dialog';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ENDPOINT_MESSAGE_RECEIVED,
|
ENDPOINT_MESSAGE_RECEIVED,
|
||||||
REMOVE_TRANSCRIPT_MESSAGE,
|
REMOVE_TRANSCRIPT_MESSAGE,
|
||||||
TOGGLE_REQUESTING_SUBTITLES,
|
TOGGLE_REQUESTING_SUBTITLES,
|
||||||
SET_REQUESTING_SUBTITLES,
|
SET_REQUESTING_SUBTITLES,
|
||||||
UPDATE_TRANSCRIPT_MESSAGE
|
UPDATE_TRANSCRIPT_MESSAGE,
|
||||||
|
UPDATE_TRANSLATION_LANGUAGE
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
|
import LanguageSelectorDialogWeb from './components/LanguageSelectorDialog.web';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signals that a participant sent an endpoint message on the data channel.
|
* Signals that a participant sent an endpoint message on the data channel.
|
||||||
|
@ -92,3 +96,31 @@ export function setRequestingSubtitles(enabled: boolean) {
|
||||||
enabled
|
enabled
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals that the local user has selected language for the translation.
|
||||||
|
*
|
||||||
|
* @param {boolean} value - The selected language for translation.
|
||||||
|
* @returns {{
|
||||||
|
* type: UPDATE_TRANSLATION_LANGUAGE
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
export function updateTranslationLanguage(value) {
|
||||||
|
return {
|
||||||
|
type: UPDATE_TRANSLATION_LANGUAGE,
|
||||||
|
value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals that the local user has toggled the LanguageSelector button.
|
||||||
|
*
|
||||||
|
* @returns {{
|
||||||
|
* type: UPDATE_TRANSLATION_LANGUAGE
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
export function toggleLangugeSelectorDialog() {
|
||||||
|
return function(dispatch: (Object) => Object) {
|
||||||
|
dispatch(toggleDialog(LanguageSelectorDialogWeb));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { isLocalParticipantModerator } from '../../base/participants';
|
||||||
import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
|
import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
|
||||||
import { maybeShowPremiumFeatureDialog } from '../../jaas/actions';
|
import { maybeShowPremiumFeatureDialog } from '../../jaas/actions';
|
||||||
import { FEATURES } from '../../jaas/constants';
|
import { FEATURES } from '../../jaas/constants';
|
||||||
import { toggleRequestingSubtitles } from '../actions';
|
|
||||||
|
|
||||||
export type AbstractProps = AbstractButtonProps & {
|
export type AbstractProps = AbstractButtonProps & {
|
||||||
|
|
||||||
|
@ -22,7 +21,12 @@ export type AbstractProps = AbstractButtonProps & {
|
||||||
/**
|
/**
|
||||||
* Whether the local participant is currently requesting subtitles.
|
* Whether the local participant is currently requesting subtitles.
|
||||||
*/
|
*/
|
||||||
_requestingSubtitles: Boolean
|
_requestingSubtitles: Boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selected language for subtitle.
|
||||||
|
*/
|
||||||
|
_subtitles: String
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,6 +34,18 @@ export type AbstractProps = AbstractButtonProps & {
|
||||||
*/
|
*/
|
||||||
export class AbstractClosedCaptionButton
|
export class AbstractClosedCaptionButton
|
||||||
extends AbstractButton<AbstractProps, *> {
|
extends AbstractButton<AbstractProps, *> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to be implemented by subclasses, which should be used
|
||||||
|
* to handle the closed caption button being clicked / pressed.
|
||||||
|
*
|
||||||
|
* @protected
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_handleClickOpenLanguageSelector() {
|
||||||
|
// To be implemented by subclass.
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles clicking / pressing the button.
|
* Handles clicking / pressing the button.
|
||||||
*
|
*
|
||||||
|
@ -45,11 +61,10 @@ export class AbstractClosedCaptionButton
|
||||||
'requesting_subtitles': Boolean(_requestingSubtitles)
|
'requesting_subtitles': Boolean(_requestingSubtitles)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(FEATURES.RECORDING));
|
const dialogShown = await dispatch(maybeShowPremiumFeatureDialog(FEATURES.RECORDING));
|
||||||
|
|
||||||
if (!dialogShown) {
|
if (!dialogShown) {
|
||||||
dispatch(toggleRequestingSubtitles());
|
this._handleClickOpenLanguageSelector();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,11 +101,12 @@ export class AbstractClosedCaptionButton
|
||||||
* @private
|
* @private
|
||||||
* @returns {{
|
* @returns {{
|
||||||
* _requestingSubtitles: boolean,
|
* _requestingSubtitles: boolean,
|
||||||
|
* _language: string,
|
||||||
* visible: boolean
|
* visible: boolean
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
export function _abstractMapStateToProps(state: Object, ownProps: Object) {
|
export function _abstractMapStateToProps(state: Object, ownProps: Object) {
|
||||||
const { _requestingSubtitles } = state['features/subtitles'];
|
const { _requestingSubtitles, _language } = state['features/subtitles'];
|
||||||
const { transcription } = state['features/base/config'];
|
const { transcription } = state['features/base/config'];
|
||||||
const { isTranscribing } = state['features/transcribing'];
|
const { isTranscribing } = state['features/transcribing'];
|
||||||
|
|
||||||
|
@ -101,6 +117,7 @@ export function _abstractMapStateToProps(state: Object, ownProps: Object) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_requestingSubtitles,
|
_requestingSubtitles,
|
||||||
|
_language,
|
||||||
visible
|
visible
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../base/i18n';
|
||||||
import { IconClosedCaption } from '../../base/icons';
|
import { IconClosedCaption } from '../../base/icons';
|
||||||
import { connect } from '../../base/redux';
|
import { connect } from '../../base/redux';
|
||||||
|
import { toggleLangugeSelectorDialog } from '../actions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AbstractClosedCaptionButton,
|
AbstractClosedCaptionButton,
|
||||||
|
@ -14,12 +15,24 @@ import {
|
||||||
*/
|
*/
|
||||||
class ClosedCaptionButton
|
class ClosedCaptionButton
|
||||||
extends AbstractClosedCaptionButton {
|
extends AbstractClosedCaptionButton {
|
||||||
|
|
||||||
accessibilityLabel = 'toolbar.accessibilityLabel.cc';
|
accessibilityLabel = 'toolbar.accessibilityLabel.cc';
|
||||||
icon = IconClosedCaption;
|
icon = IconClosedCaption;
|
||||||
tooltip = 'transcribing.ccButtonTooltip';
|
tooltip = 'transcribing.ccButtonTooltip';
|
||||||
label = 'toolbar.startSubtitles';
|
label = 'toolbar.startSubtitles';
|
||||||
toggledLabel = 'toolbar.stopSubtitles';
|
labelProps = {
|
||||||
|
language: this.props.t(this.props._language)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle language selection dialog.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_handleClickOpenLanguageSelector() {
|
||||||
|
const { dispatch } = this.props;
|
||||||
|
|
||||||
|
dispatch(toggleLangugeSelectorDialog());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default translate(connect(_abstractMapStateToProps)(ClosedCaptionButton));
|
export default translate(connect(_abstractMapStateToProps)(ClosedCaptionButton));
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { makeStyles } from '@material-ui/styles';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
|
||||||
|
import LanguageListItem from './LanguageListItem';
|
||||||
|
|
||||||
|
interface ILanguageListProps {
|
||||||
|
items: Array<LanguageItem>,
|
||||||
|
onLanguageSelected: (lang: string) => void;
|
||||||
|
selectedLanguage: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = makeStyles(() => {
|
||||||
|
return {
|
||||||
|
itemsContainer: {
|
||||||
|
display: 'flex',
|
||||||
|
flexFlow: 'column'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
interface LanguageItem {
|
||||||
|
id: string,
|
||||||
|
lang: string,
|
||||||
|
selected: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that renders the security options dialog.
|
||||||
|
*
|
||||||
|
* @returns {React$Element<any>}
|
||||||
|
*/
|
||||||
|
const LanguageList = ({
|
||||||
|
items,
|
||||||
|
onLanguageSelected
|
||||||
|
}: ILanguageListProps) => {
|
||||||
|
const styles = useStyles();
|
||||||
|
const listItems = items.map(item => (<LanguageListItem
|
||||||
|
key = { item.id }
|
||||||
|
lang = { item.lang }
|
||||||
|
onLanguageSelected = { onLanguageSelected }
|
||||||
|
selected = { item.selected } />));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className = { styles.itemsContainer }>{listItems}</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LanguageList;
|
|
@ -0,0 +1,78 @@
|
||||||
|
// @ts-ignore
|
||||||
|
import { makeStyles } from '@material-ui/styles';
|
||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { WithTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
// eslint-disable-next-line import/order
|
||||||
|
import { translate } from '../../base/i18n';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
import { Icon } from '../../base/icons/components';
|
||||||
|
import { IconCheck } from '../../base/icons/svg/index';
|
||||||
|
import { Theme } from '../../base/ui/types';
|
||||||
|
|
||||||
|
interface ILanguageListItemProps extends WithTranslation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the button should be full width.
|
||||||
|
*/
|
||||||
|
lang: string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click callback.
|
||||||
|
*/
|
||||||
|
onLanguageSelected: (lang: string) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The id of the button.
|
||||||
|
*/
|
||||||
|
selected?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => {
|
||||||
|
return {
|
||||||
|
itemContainer: {
|
||||||
|
display: 'flex',
|
||||||
|
color: theme.palette.text01,
|
||||||
|
alignItems: 'center',
|
||||||
|
fontSize: '14px'
|
||||||
|
},
|
||||||
|
iconWrapper: {
|
||||||
|
margin: '4px 10px',
|
||||||
|
width: '22px',
|
||||||
|
height: '22px'
|
||||||
|
},
|
||||||
|
activeItemContainer: {
|
||||||
|
fontWeight: 700
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that renders the language list item.
|
||||||
|
*
|
||||||
|
* @returns {React$Element<any>}
|
||||||
|
*/
|
||||||
|
|
||||||
|
const LanguageListItem = ({
|
||||||
|
t,
|
||||||
|
lang,
|
||||||
|
selected,
|
||||||
|
onLanguageSelected
|
||||||
|
}: ILanguageListItemProps) => {
|
||||||
|
const styles = useStyles();
|
||||||
|
const onLanguageSelectedWrapper = useCallback(() => onLanguageSelected(lang), [ lang ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className = { `${styles.itemContainer} ${selected ? styles.activeItemContainer : ''}` }
|
||||||
|
onClick = { onLanguageSelectedWrapper }>
|
||||||
|
<span className = { styles.iconWrapper }>{ selected
|
||||||
|
&& <Icon src = { IconCheck } /> }</span>
|
||||||
|
{ t(lang) }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default translate(LanguageListItem);
|
|
@ -0,0 +1,100 @@
|
||||||
|
/* eslint-disable lines-around-comment */
|
||||||
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
import { Dialog } from '../../base/dialog';
|
||||||
|
// @ts-ignore
|
||||||
|
import { LANGUAGES, TRANSLATION_LANGUAGES_HEAD, TRANSLATION_LANGUAGES_EXCLUDE } from '../../base/i18n';
|
||||||
|
// @ts-ignore
|
||||||
|
import { connect } from '../../base/redux';
|
||||||
|
// @ts-ignore
|
||||||
|
import { updateTranslationLanguage, setRequestingSubtitles, toggleLangugeSelectorDialog } from '../actions';
|
||||||
|
|
||||||
|
import LanguageList from './LanguageList';
|
||||||
|
|
||||||
|
interface ILanguageSelectorDialogProps {
|
||||||
|
_language: string,
|
||||||
|
t: Function,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that renders the subtitle language selector dialog.
|
||||||
|
*
|
||||||
|
* @returns {React$Element<any>}
|
||||||
|
*/
|
||||||
|
const LanguageSelectorDialog = ({ _language }: ILanguageSelectorDialogProps) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const off = 'transcribing.subtitlesOff';
|
||||||
|
const [ language, setLanguage ] = useState(off);
|
||||||
|
|
||||||
|
const importantLanguages = TRANSLATION_LANGUAGES_HEAD.map((lang: string) => `languages:${lang}`);
|
||||||
|
const fixedItems = [ off, ...importantLanguages ];
|
||||||
|
|
||||||
|
const languages = LANGUAGES
|
||||||
|
.filter((lang: string) => !TRANSLATION_LANGUAGES_EXCLUDE.includes(lang))
|
||||||
|
.map((lang: string) => `languages:${lang}`)
|
||||||
|
.filter((lang: string) => !(lang === language || importantLanguages.includes(lang)));
|
||||||
|
|
||||||
|
const listItems = (fixedItems.includes(language)
|
||||||
|
? [ ...fixedItems, ...languages ]
|
||||||
|
: [ ...fixedItems, language, ...languages ])
|
||||||
|
.map((lang, index) => {
|
||||||
|
return {
|
||||||
|
id: lang + index,
|
||||||
|
lang,
|
||||||
|
selected: lang === language
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
_language ? setLanguage(_language) : setLanguage(off);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onLanguageSelected = useCallback((e: string) => {
|
||||||
|
setLanguage(e);
|
||||||
|
dispatch(updateTranslationLanguage(e));
|
||||||
|
dispatch(setRequestingSubtitles(e !== off));
|
||||||
|
dispatch(toggleLangugeSelectorDialog());
|
||||||
|
}, [ _language ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
hideCancelButton = { true }
|
||||||
|
submitDisabled = { true }
|
||||||
|
titleKey = 'transcribing.subtitles'
|
||||||
|
width = { 'small' }>
|
||||||
|
<LanguageList
|
||||||
|
items = { listItems }
|
||||||
|
onLanguageSelected = { onLanguageSelected }
|
||||||
|
selectedLanguage = { language } />
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps (parts of) the Redux state to the associated props for the
|
||||||
|
* {@code LanguageSelectorDialog} component.
|
||||||
|
*
|
||||||
|
* @param {Object} state - The Redux state.
|
||||||
|
* @private
|
||||||
|
* @returns {Props}
|
||||||
|
*/
|
||||||
|
function mapStateToProps(state: any) {
|
||||||
|
const {
|
||||||
|
conference
|
||||||
|
} = state['features/base/conference'];
|
||||||
|
|
||||||
|
const {
|
||||||
|
_language
|
||||||
|
} = state['features/subtitles'];
|
||||||
|
|
||||||
|
return {
|
||||||
|
_conference: conference,
|
||||||
|
_language
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = {};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(LanguageSelectorDialog);
|
|
@ -55,9 +55,10 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
return _endpointMessageReceived(store, next, action);
|
return _endpointMessageReceived(store, next, action);
|
||||||
|
|
||||||
case TOGGLE_REQUESTING_SUBTITLES:
|
case TOGGLE_REQUESTING_SUBTITLES:
|
||||||
_requestingSubtitlesToggled(store);
|
_requestingSubtitlesChange(store);
|
||||||
break;
|
break;
|
||||||
case SET_REQUESTING_SUBTITLES:
|
case SET_REQUESTING_SUBTITLES:
|
||||||
|
_requestingSubtitlesChange(store);
|
||||||
_requestingSubtitlesSet(store, action.enabled);
|
_requestingSubtitlesSet(store, action.enabled);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -171,14 +172,22 @@ function _endpointMessageReceived({ dispatch, getState }, next, action) {
|
||||||
* @private
|
* @private
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
function _requestingSubtitlesToggled({ getState }) {
|
function _requestingSubtitlesChange({ getState }) {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const { _requestingSubtitles } = state['features/subtitles'];
|
const { _language } = state['features/subtitles'];
|
||||||
const { conference } = state['features/base/conference'];
|
const { conference } = state['features/base/conference'];
|
||||||
|
|
||||||
|
const requestingSubtitles = _language !== 'transcribing.subtitlesOff';
|
||||||
|
|
||||||
conference.setLocalParticipantProperty(
|
conference.setLocalParticipantProperty(
|
||||||
P_NAME_REQUESTING_TRANSCRIPTION,
|
P_NAME_REQUESTING_TRANSCRIPTION,
|
||||||
!_requestingSubtitles);
|
requestingSubtitles);
|
||||||
|
|
||||||
|
if (requestingSubtitles) {
|
||||||
|
conference.setLocalParticipantProperty(
|
||||||
|
P_NAME_TRANSLATION_LANGUAGE,
|
||||||
|
_language.replace('languages:', ''));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { ReducerRegistry } from '../base/redux';
|
import { ReducerRegistry } from '../base/redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
REMOVE_TRANSCRIPT_MESSAGE, TOGGLE_REQUESTING_SUBTITLES,
|
REMOVE_TRANSCRIPT_MESSAGE,
|
||||||
SET_REQUESTING_SUBTITLES, UPDATE_TRANSCRIPT_MESSAGE
|
SET_REQUESTING_SUBTITLES, UPDATE_TRANSCRIPT_MESSAGE, UPDATE_TRANSLATION_LANGUAGE
|
||||||
} from './actionTypes';
|
} from './actionTypes';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,7 +10,8 @@ import {
|
||||||
*/
|
*/
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
_transcriptMessages: new Map(),
|
_transcriptMessages: new Map(),
|
||||||
_requestingSubtitles: false
|
_requestingSubtitles: false,
|
||||||
|
_language: 'transcribing.subtitlesOff'
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,11 +25,10 @@ ReducerRegistry.register('features/subtitles', (
|
||||||
return _removeTranscriptMessage(state, action);
|
return _removeTranscriptMessage(state, action);
|
||||||
case UPDATE_TRANSCRIPT_MESSAGE:
|
case UPDATE_TRANSCRIPT_MESSAGE:
|
||||||
return _updateTranscriptMessage(state, action);
|
return _updateTranscriptMessage(state, action);
|
||||||
|
case UPDATE_TRANSLATION_LANGUAGE:
|
||||||
case TOGGLE_REQUESTING_SUBTITLES:
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
_requestingSubtitles: !state._requestingSubtitles
|
_language: action.value
|
||||||
};
|
};
|
||||||
case SET_REQUESTING_SUBTITLES:
|
case SET_REQUESTING_SUBTITLES:
|
||||||
return {
|
return {
|
||||||
|
|
Loading…
Reference in New Issue