fix(translation) translation button visibility for users, add missing languages
This commit is contained in:
parent
69e0a37529
commit
8162ae4dbe
10
config.js
10
config.js
|
@ -375,6 +375,14 @@ var config = {
|
||||||
// // Whether the feature should be enabled or not.
|
// // Whether the feature should be enabled or not.
|
||||||
// enabled: false,
|
// enabled: false,
|
||||||
|
|
||||||
|
// // Translation languages.
|
||||||
|
// // Available languages can be found in
|
||||||
|
// // ./src/react/features/transcribing/translation-languages.json.
|
||||||
|
// translationLanguages: ['en', 'es', 'fr', 'ro'],
|
||||||
|
|
||||||
|
// // Important languages to show on the top of the language list.
|
||||||
|
// translationLanguagesHead: ['en'],
|
||||||
|
|
||||||
// // If true transcriber will use the application language.
|
// // If true transcriber will use the application language.
|
||||||
// // The application language is either explicitly set by participants in their settings or automatically
|
// // The application language is either explicitly set by participants in their settings or automatically
|
||||||
// // detected based on the environment, e.g. if the app is opened in a chrome instance which
|
// // detected based on the environment, e.g. if the app is opened in a chrome instance which
|
||||||
|
@ -1349,6 +1357,8 @@ var config = {
|
||||||
*/
|
*/
|
||||||
mouseMoveCallbackInterval: 1000,
|
mouseMoveCallbackInterval: 1000,
|
||||||
|
|
||||||
|
hiddenDomain: 'tdomokos.jitsi.net',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Use this array to configure which notifications will be shown to the user
|
Use this array to configure which notifications will be shown to the user
|
||||||
The items correspond to the title or description key of that notification
|
The items correspond to the title or description key of that notification
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
{
|
||||||
|
"af": "Afrikaans",
|
||||||
|
"am": "Amharic",
|
||||||
|
"ar": "Arabic",
|
||||||
|
"az": "Azerbaijani",
|
||||||
|
"be": "Belarusian",
|
||||||
|
"bg": "Bulgarian",
|
||||||
|
"bn": "Bengali",
|
||||||
|
"bs": "Bosnian",
|
||||||
|
"ca": "Catalan",
|
||||||
|
"ceb": "Cebuano",
|
||||||
|
"co": "Corsican",
|
||||||
|
"cs": "Czech",
|
||||||
|
"cy": "Welsh",
|
||||||
|
"da": "Danish",
|
||||||
|
"de": "German",
|
||||||
|
"el": "Greek",
|
||||||
|
"en": "English",
|
||||||
|
"eo": "Esperanto",
|
||||||
|
"es": "Spanish",
|
||||||
|
"et": "Estonian",
|
||||||
|
"eu": "Basque",
|
||||||
|
"fa": "Persian",
|
||||||
|
"fi": "Finnish",
|
||||||
|
"fr": "French",
|
||||||
|
"fy": "Frisian",
|
||||||
|
"ga": "Irish",
|
||||||
|
"gd": "Scots Gaelic",
|
||||||
|
"gl": "Galician",
|
||||||
|
"gu": "Gujarati",
|
||||||
|
"ha": "Hausa",
|
||||||
|
"haw": "Hawaiian",
|
||||||
|
"he": "Hebrew",
|
||||||
|
"hi": "Hindi",
|
||||||
|
"hmn": "Hmong",
|
||||||
|
"hr": "Croatian",
|
||||||
|
"ht": "Haitian Creole",
|
||||||
|
"hu": "Hungarian",
|
||||||
|
"hy": "Armenian",
|
||||||
|
"id": "Indonesian",
|
||||||
|
"ig": "Igbo",
|
||||||
|
"is": "Icelandic",
|
||||||
|
"it": "Italian",
|
||||||
|
"ja": "Japanese",
|
||||||
|
"jv": "Javanese",
|
||||||
|
"ka": "Georgian",
|
||||||
|
"kk": "Kazakh",
|
||||||
|
"km": "Khmer",
|
||||||
|
"kn": "Kannada",
|
||||||
|
"ko": "Korean",
|
||||||
|
"ku": "Kurdish",
|
||||||
|
"ky": "Kyrgyz",
|
||||||
|
"la": "Latin",
|
||||||
|
"lb": "Luxembourgish",
|
||||||
|
"lo": "Lao",
|
||||||
|
"lt": "Lithuanian",
|
||||||
|
"lv": "Latvian",
|
||||||
|
"mg": "Malagasy",
|
||||||
|
"mi": "Maori",
|
||||||
|
"mk": "Macedonian",
|
||||||
|
"ml": "Malayalam",
|
||||||
|
"mn": "Mongolian",
|
||||||
|
"mr": "Marathi",
|
||||||
|
"ms": "Malay",
|
||||||
|
"mt": "Maltese",
|
||||||
|
"my": "Myanmar (Burmese)",
|
||||||
|
"ne": "Nepali",
|
||||||
|
"nl": "Dutch",
|
||||||
|
"no": "Norwegian",
|
||||||
|
"ny": "Nyanja (Chichewa)",
|
||||||
|
"or": "Odia (Oriya)",
|
||||||
|
"pa": "Punjabi",
|
||||||
|
"pl": "Polish",
|
||||||
|
"ps": "Pashto",
|
||||||
|
"pt": "Portuguese (Portugal, Brazil)",
|
||||||
|
"ro": "Romanian",
|
||||||
|
"ru": "Russian",
|
||||||
|
"rw": "Kinyarwanda",
|
||||||
|
"sd": "Sindhi",
|
||||||
|
"si": "Sinhala (Sinhalese)",
|
||||||
|
"sk": "Slovak",
|
||||||
|
"sl": "Slovenian",
|
||||||
|
"sm": "Samoan",
|
||||||
|
"sn": "Shona",
|
||||||
|
"so": "Somali",
|
||||||
|
"sq": "Albanian",
|
||||||
|
"sr": "Serbian",
|
||||||
|
"st": "Sesotho",
|
||||||
|
"su": "Sundanese",
|
||||||
|
"sv": "Swedish",
|
||||||
|
"sw": "Swahili",
|
||||||
|
"ta": "Tamil",
|
||||||
|
"te": "Telugu",
|
||||||
|
"tg": "Tajik",
|
||||||
|
"th": "Thai",
|
||||||
|
"tk": "Turkmen",
|
||||||
|
"tl": "Tagalog (Filipino)",
|
||||||
|
"tr": "Turkish",
|
||||||
|
"tt": "Tatar",
|
||||||
|
"ug": "Uyghur",
|
||||||
|
"uk": "Ukrainian",
|
||||||
|
"ur": "Urdu",
|
||||||
|
"uz": "Uzbek",
|
||||||
|
"vi": "Vietnamese",
|
||||||
|
"xh": "Xhosa",
|
||||||
|
"yi": "Yiddish",
|
||||||
|
"yo": "Yoruba",
|
||||||
|
"zh-CN": "Chinese (Simplified)",
|
||||||
|
"zh-TW": "Chinese (Traditional)",
|
||||||
|
"zu": "Zulu"
|
||||||
|
}
|
|
@ -464,6 +464,8 @@ export interface IConfig {
|
||||||
disableStartForAll?: boolean;
|
disableStartForAll?: boolean;
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
preferredLanguage?: string;
|
preferredLanguage?: string;
|
||||||
|
translationLanguages?: Array<string>;
|
||||||
|
translationLanguagesHead?: Array<string>;
|
||||||
useAppLanguage?: boolean;
|
useAppLanguage?: boolean;
|
||||||
};
|
};
|
||||||
useHostPageLocalStorage?: boolean;
|
useHostPageLocalStorage?: boolean;
|
||||||
|
|
|
@ -7,6 +7,7 @@ import _ from 'lodash';
|
||||||
|
|
||||||
import LANGUAGES_RESOURCES from '../../../../lang/languages.json';
|
import LANGUAGES_RESOURCES from '../../../../lang/languages.json';
|
||||||
import MAIN_RESOURCES from '../../../../lang/main.json';
|
import MAIN_RESOURCES from '../../../../lang/main.json';
|
||||||
|
import TRANSLATION_LANGUAGES_RESOURCES from '../../../../lang/translation-languages.json';
|
||||||
|
|
||||||
import { I18NEXT_INITIALIZED, LANGUAGE_CHANGED } from './actionTypes';
|
import { I18NEXT_INITIALIZED, LANGUAGE_CHANGED } from './actionTypes';
|
||||||
// eslint-disable-next-line lines-around-comment
|
// eslint-disable-next-line lines-around-comment
|
||||||
|
@ -36,22 +37,21 @@ 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.
|
* The available/supported translation languages.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @type {Array<string>}
|
||||||
|
*/
|
||||||
|
export const TRANSLATION_LANGUAGES: Array<string> = Object.keys(TRANSLATION_LANGUAGES_RESOURCES);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The available/supported translation languages head. (Languages displayed on the top ).
|
||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
* @type {Array<string>}
|
* @type {Array<string>}
|
||||||
*/
|
*/
|
||||||
export const TRANSLATION_LANGUAGES_HEAD: Array<string> = [ 'en' ];
|
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.
|
||||||
*
|
*
|
||||||
|
@ -77,7 +77,7 @@ const options = {
|
||||||
escapeValue: false // not needed for react as it escapes by default
|
escapeValue: false // not needed for react as it escapes by default
|
||||||
},
|
},
|
||||||
load: 'languageOnly',
|
load: 'languageOnly',
|
||||||
ns: [ 'main', 'languages', 'countries' ],
|
ns: [ 'main', 'languages', 'countries', 'translation-languages' ],
|
||||||
react: {
|
react: {
|
||||||
// re-render when a new resource bundle is added
|
// re-render when a new resource bundle is added
|
||||||
bindI18nStore: 'added',
|
bindI18nStore: 'added',
|
||||||
|
@ -109,6 +109,12 @@ i18next.addResourceBundle(
|
||||||
LANGUAGES_RESOURCES,
|
LANGUAGES_RESOURCES,
|
||||||
/* deep */ true,
|
/* deep */ true,
|
||||||
/* overwrite */ true);
|
/* overwrite */ true);
|
||||||
|
i18next.addResourceBundle(
|
||||||
|
DEFAULT_LANGUAGE,
|
||||||
|
'translation-languages',
|
||||||
|
TRANSLATION_LANGUAGES_RESOURCES,
|
||||||
|
/* deep */ true,
|
||||||
|
/* overwrite */ true);
|
||||||
i18next.addResourceBundle(
|
i18next.addResourceBundle(
|
||||||
DEFAULT_LANGUAGE,
|
DEFAULT_LANGUAGE,
|
||||||
'main',
|
'main',
|
||||||
|
|
|
@ -4,4 +4,4 @@ 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,
|
export { default as i18next, DEFAULT_LANGUAGE,
|
||||||
LANGUAGES, TRANSLATION_LANGUAGES_HEAD, TRANSLATION_LANGUAGES_EXCLUDE } from './i18next';
|
LANGUAGES, TRANSLATION_LANGUAGES, TRANSLATION_LANGUAGES_HEAD, i18n } from './i18next';
|
||||||
|
|
|
@ -11,7 +11,7 @@ export * from './actions.any';
|
||||||
* type: UPDATE_TRANSLATION_LANGUAGE
|
* type: UPDATE_TRANSLATION_LANGUAGE
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
export function toggleLangugeSelectorDialog() {
|
export function toggleLanguageSelectorDialog() {
|
||||||
return function(dispatch: (Object) => Object) {
|
return function(dispatch: (Object) => Object) {
|
||||||
dispatch(toggleDialog(LanguageSelectorDialogWeb));
|
dispatch(toggleDialog(LanguageSelectorDialogWeb));
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,7 +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 { toggleLanguageSelectorDialog } from '../actions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AbstractClosedCaptionButton,
|
AbstractClosedCaptionButton,
|
||||||
|
@ -20,7 +20,9 @@ class ClosedCaptionButton
|
||||||
tooltip = 'transcribing.ccButtonTooltip';
|
tooltip = 'transcribing.ccButtonTooltip';
|
||||||
label = 'toolbar.startSubtitles';
|
label = 'toolbar.startSubtitles';
|
||||||
labelProps = {
|
labelProps = {
|
||||||
language: this.props.t(this.props._language)
|
language: this.props.t(this.props._language),
|
||||||
|
languages: this.props.t(this.props.languages),
|
||||||
|
languagesHead: this.props.t(this.props.languagesHead)
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +33,7 @@ class ClosedCaptionButton
|
||||||
_handleClickOpenLanguageSelector() {
|
_handleClickOpenLanguageSelector() {
|
||||||
const { dispatch } = this.props;
|
const { dispatch } = this.props;
|
||||||
|
|
||||||
dispatch(toggleLangugeSelectorDialog());
|
dispatch(toggleLanguageSelectorDialog());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,11 @@ const useStyles = makeStyles()((theme: Theme) => {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
color: theme.palette.text01,
|
color: theme.palette.text01,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
fontSize: '14px'
|
fontSize: '14px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: theme.palette.ui04
|
||||||
|
}
|
||||||
},
|
},
|
||||||
iconWrapper: {
|
iconWrapper: {
|
||||||
margin: '4px 10px',
|
margin: '4px 10px',
|
||||||
|
|
|
@ -6,15 +6,17 @@ import { IState } from '../../app/types';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { Dialog } from '../../base/dialog';
|
import { Dialog } from '../../base/dialog';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { LANGUAGES, TRANSLATION_LANGUAGES_EXCLUDE, TRANSLATION_LANGUAGES_HEAD } from '../../base/i18n';
|
import { TRANSLATION_LANGUAGES, TRANSLATION_LANGUAGES_HEAD } from '../../base/i18n';
|
||||||
import { connect } from '../../base/redux/functions';
|
import { connect } from '../../base/redux/functions';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { setRequestingSubtitles, toggleLangugeSelectorDialog, updateTranslationLanguage } from '../actions';
|
import { setRequestingSubtitles, toggleLanguageSelectorDialog, updateTranslationLanguage } from '../actions';
|
||||||
|
|
||||||
import LanguageList from './LanguageList';
|
import LanguageList from './LanguageList';
|
||||||
|
|
||||||
interface ILanguageSelectorDialogProps {
|
interface ILanguageSelectorDialogProps {
|
||||||
_language: string;
|
_language: string;
|
||||||
|
_translationLanguages: Array<string>;
|
||||||
|
_translationLanguagesHead: Array<string>;
|
||||||
t: Function;
|
t: Function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,18 +25,21 @@ interface ILanguageSelectorDialogProps {
|
||||||
*
|
*
|
||||||
* @returns {React$Element<any>}
|
* @returns {React$Element<any>}
|
||||||
*/
|
*/
|
||||||
const LanguageSelectorDialog = ({ _language }: ILanguageSelectorDialogProps) => {
|
const LanguageSelectorDialog = ({ _language, _translationLanguages, _translationLanguagesHead }:
|
||||||
|
ILanguageSelectorDialogProps) => {
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const off = 'transcribing.subtitlesOff';
|
const off = 'transcribing.subtitlesOff';
|
||||||
const [ language, setLanguage ] = useState(off);
|
const [ language, setLanguage ] = useState(off);
|
||||||
|
|
||||||
const importantLanguages = TRANSLATION_LANGUAGES_HEAD.map((lang: string) => `languages:${lang}`);
|
const languagesHead = _translationLanguagesHead.map((lang: string) => `translation-languages:${lang}`);
|
||||||
const fixedItems = [ off, ...importantLanguages ];
|
// The off and the head languages are always on the top of the list. But once you are selecting
|
||||||
|
// a language from the translationLanguages, that language is moved under the fixedItems list,
|
||||||
const languages = LANGUAGES
|
// until a new languages is selected. FixedItems keep their positions.
|
||||||
.filter((lang: string) => !TRANSLATION_LANGUAGES_EXCLUDE.includes(lang))
|
const fixedItems = [ off, ...languagesHead ];
|
||||||
.map((lang: string) => `languages:${lang}`)
|
const languages = _translationLanguages
|
||||||
.filter((lang: string) => !(lang === language || importantLanguages.includes(lang)));
|
.map((lang: string) => `translation-languages:${lang}`)
|
||||||
|
.filter((lang: string) => !(lang === language || languagesHead.includes(lang)));
|
||||||
|
|
||||||
const listItems = (fixedItems.includes(language)
|
const listItems = (fixedItems.includes(language)
|
||||||
? [ ...fixedItems, ...languages ]
|
? [ ...fixedItems, ...languages ]
|
||||||
|
@ -55,7 +60,7 @@ const LanguageSelectorDialog = ({ _language }: ILanguageSelectorDialogProps) =>
|
||||||
setLanguage(e);
|
setLanguage(e);
|
||||||
dispatch(updateTranslationLanguage(e));
|
dispatch(updateTranslationLanguage(e));
|
||||||
dispatch(setRequestingSubtitles(e !== off));
|
dispatch(setRequestingSubtitles(e !== off));
|
||||||
dispatch(toggleLangugeSelectorDialog());
|
dispatch(toggleLanguageSelectorDialog());
|
||||||
}, [ _language ]);
|
}, [ _language ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -81,17 +86,18 @@ const LanguageSelectorDialog = ({ _language }: ILanguageSelectorDialogProps) =>
|
||||||
* @returns {Props}
|
* @returns {Props}
|
||||||
*/
|
*/
|
||||||
function mapStateToProps(state: IState) {
|
function mapStateToProps(state: IState) {
|
||||||
const {
|
const { conference } = state['features/base/conference'];
|
||||||
conference
|
const { _language } = state['features/subtitles'];
|
||||||
} = state['features/base/conference'];
|
const { transcription } = state['features/base/config'];
|
||||||
|
|
||||||
const {
|
const languages = transcription?.translationLanguages ?? TRANSLATION_LANGUAGES;
|
||||||
_language
|
const languagesHead = transcription?.translationLanguagesHead ?? TRANSLATION_LANGUAGES_HEAD;
|
||||||
} = state['features/subtitles'];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_conference: conference,
|
_conference: conference,
|
||||||
_language
|
_language,
|
||||||
|
_translationLanguages: languages,
|
||||||
|
_translationLanguagesHead: languagesHead
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
import i18next from 'i18next';
|
||||||
|
|
||||||
import { MiddlewareRegistry } from '../base/redux';
|
import { MiddlewareRegistry } from '../base/redux';
|
||||||
|
|
||||||
|
@ -115,7 +116,7 @@ function _endpointMessageReceived({ dispatch, getState }, next, action) {
|
||||||
newTranscriptMessage));
|
newTranscriptMessage));
|
||||||
|
|
||||||
} else if (json.type === JSON_TYPE_TRANSCRIPTION_RESULT
|
} else if (json.type === JSON_TYPE_TRANSCRIPTION_RESULT
|
||||||
&& !translationLanguage) {
|
&& i18next.language === translationLanguage) {
|
||||||
// Displays interim and final results without any translation if
|
// Displays interim and final results without any translation if
|
||||||
// translations are disabled.
|
// translations are disabled.
|
||||||
|
|
||||||
|
@ -186,7 +187,7 @@ function _requestingSubtitlesChange({ getState }) {
|
||||||
if (requestingSubtitles) {
|
if (requestingSubtitles) {
|
||||||
conference.setLocalParticipantProperty(
|
conference.setLocalParticipantProperty(
|
||||||
P_NAME_TRANSLATION_LANGUAGE,
|
P_NAME_TRANSLATION_LANGUAGE,
|
||||||
_language.replace('languages:', ''));
|
_language.replace('translation-languages:', ''));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue