Parcourir la source

feat(sounds) use audio files based on locale (#14104)

factor2
Mihaela Dumitru il y a 1 an
Parent
révision
8504b4b5bb
Aucun compte lié à l'adresse e-mail de l'auteur

+ 16
- 3
react/features/base/media/components/native/Audio.ts Voir le fichier

@@ -1,7 +1,7 @@
1 1
 import Sound from 'react-native-sound';
2 2
 
3 3
 import logger from '../../logger';
4
-import AbstractAudio from '../AbstractAudio';
4
+import AbstractAudio, { IProps } from '../AbstractAudio';
5 5
 
6 6
 /**
7 7
  * The React Native/mobile {@link Component} which is similar to Web's
@@ -31,12 +31,25 @@ export default class Audio extends AbstractAudio {
31 31
         }
32 32
     }
33 33
 
34
+    /**
35
+     * Implements React's {@link Component#componentDidUpdate()}.
36
+     *
37
+     * @inheritdoc
38
+     */
39
+    async componentDidUpdate(prevProps: IProps): Promise<void> {
40
+        // source is different !! call didunmount and call didmount
41
+        if (prevProps.src !== this.props.src) {
42
+            await this.componentWillUnmount();
43
+            await this.componentDidMount();
44
+        }
45
+    }
46
+
34 47
     /**
35 48
      * Will load the sound, after the component did mount.
36 49
      *
37 50
      * @returns {void}
38 51
      */
39
-    componentDidMount() {
52
+    async componentDidMount() {
40 53
         this._sound
41 54
             = this.props.src
42 55
                 ? new Sound(
@@ -50,7 +63,7 @@ export default class Audio extends AbstractAudio {
50 63
      *
51 64
      * @returns {void}
52 65
      */
53
-    componentWillUnmount() {
66
+    async componentWillUnmount() {
54 67
         if (this._sound) {
55 68
             this._sound.release();
56 69
             this._sound = null;

+ 9
- 0
react/features/base/media/constants.ts Voir le fichier

@@ -38,6 +38,15 @@ export const SCREENSHARE_MUTISM_AUTHORITY = {
38 38
     USER: 1 << 2
39 39
 };
40 40
 
41
+/**
42
+ * The languages supported for audio files.
43
+ */
44
+export enum AudioSupportedLanguage {
45
+    en = 'en',
46
+    fr = 'fr',
47
+    frCA = 'frCA'
48
+}
49
+
41 50
 /**
42 51
  * The types of authorities which may mute/unmute the local video.
43 52
  *

+ 18
- 1
react/features/base/media/functions.ts Voir le fichier

@@ -2,7 +2,7 @@ import { IStateful } from '../app/types';
2 2
 import { toState } from '../redux/functions';
3 3
 import { getPropertyValue } from '../settings/functions';
4 4
 
5
-import { VIDEO_MUTISM_AUTHORITY } from './constants';
5
+import { AudioSupportedLanguage, VIDEO_MUTISM_AUTHORITY } from './constants';
6 6
 
7 7
 
8 8
 // XXX The configurations/preferences/settings startWithAudioMuted and startWithVideoMuted were introduced for
@@ -127,3 +127,20 @@ export function shouldRenderVideoTrack(
127 127
             && !videoTrack.muted
128 128
             && (!waitForVideoStarted || videoTrack.videoStarted));
129 129
 }
130
+
131
+/**
132
+ * Computes the localized sound file source.
133
+ *
134
+ * @param {string} file - The default file source.
135
+ * @param {string} language - The language to use for localization.
136
+ * @returns {string}
137
+ */
138
+export const getSoundFileSrc = (file: string, language: string): string => {
139
+    if (!AudioSupportedLanguage[language as keyof typeof AudioSupportedLanguage]
140
+        || language === AudioSupportedLanguage.en) {
141
+        return file;
142
+    }
143
+    const fileTokens = file.split('.');
144
+
145
+    return `${fileTokens[0]}_${language}.${fileTokens[1]}`;
146
+};

+ 56
- 0
react/features/base/sounds/middleware.any.ts Voir le fichier

@@ -1,5 +1,11 @@
1
+import i18next from 'i18next';
2
+
3
+import { registerE2eeAudioFiles } from '../../../features/e2ee/functions';
4
+import { registerRecordingAudioFiles } from '../../../features/recording/functions';
1 5
 import { IStore } from '../../app/types';
6
+import { AudioSupportedLanguage } from '../media/constants';
2 7
 import MiddlewareRegistry from '../redux/MiddlewareRegistry';
8
+import StateListenerRegistry from '../redux/StateListenerRegistry';
3 9
 
4 10
 import { PLAY_SOUND, STOP_SOUND } from './actionTypes';
5 11
 import logger from './logger';
@@ -71,3 +77,53 @@ function _stopSound({ getState }: IStore, soundId: string) {
71 77
         logger.warn(`STOP_SOUND: no sound found for id: ${soundId}`);
72 78
     }
73 79
 }
80
+
81
+/**
82
+ * Returns whether the language is supported for audio messages.
83
+ *
84
+ * @param {string} language - The requested language.
85
+ * @returns {boolean}
86
+ */
87
+function isLanguageSupported(language: string): Boolean {
88
+    return Boolean(AudioSupportedLanguage[language as keyof typeof AudioSupportedLanguage]);
89
+}
90
+
91
+/**
92
+ * Checking if it's necessary to reload the translated files.
93
+ *
94
+ * @param {string} language - The next language.
95
+ * @param {string} prevLanguage - The previous language.
96
+ * @returns {boolean}
97
+ */
98
+function shouldReloadAudioFiles(language: string, prevLanguage: string): Boolean {
99
+    const isNextLanguageSupported = isLanguageSupported(language);
100
+    const isPrevLanguageSupported = isLanguageSupported(prevLanguage);
101
+
102
+    return (
103
+
104
+        // From an unsupported language (which defaulted to English) to a supported language (that isn't English).
105
+        isNextLanguageSupported && language !== AudioSupportedLanguage.en && !isPrevLanguageSupported
106
+    ) || (
107
+
108
+        // From a supported language (that wasn't English) to English.
109
+        !isNextLanguageSupported && isPrevLanguageSupported && prevLanguage !== AudioSupportedLanguage.en
110
+    ) || (
111
+
112
+        // From a supported language to another.
113
+        isNextLanguageSupported && isPrevLanguageSupported
114
+    );
115
+}
116
+
117
+/**
118
+ * Set up state change listener for language.
119
+ */
120
+StateListenerRegistry.register(
121
+    () => i18next.language,
122
+    (language, { dispatch }, prevLanguage): void => {
123
+
124
+        if (language !== prevLanguage && shouldReloadAudioFiles(language, prevLanguage)) {
125
+            registerE2eeAudioFiles(dispatch, true);
126
+            registerRecordingAudioFiles(dispatch, true);
127
+        }
128
+    }
129
+);

+ 47
- 2
react/features/e2ee/functions.ts Voir le fichier

@@ -1,10 +1,23 @@
1
-import { IReduxState } from '../app/types';
1
+import i18next from 'i18next';
2
+
3
+import { IReduxState, IStore } from '../app/types';
2 4
 import { IStateful } from '../base/app/types';
5
+import { getSoundFileSrc } from '../base/media/functions';
3 6
 import { getParticipantById, getParticipantCount, getParticipantCountWithFake } from '../base/participants/functions';
4 7
 import { toState } from '../base/redux/functions';
8
+import { registerSound, unregisterSound } from '../base/sounds/actions';
5 9
 
10
+import {
11
+    E2EE_OFF_SOUND_ID,
12
+    E2EE_ON_SOUND_ID,
13
+    MAX_MODE_LIMIT,
14
+    MAX_MODE_THRESHOLD
15
+} from './constants';
16
+import {
17
+    E2EE_OFF_SOUND_FILE,
18
+    E2EE_ON_SOUND_FILE
19
+} from './sounds';
6 20
 
7
-import { MAX_MODE_LIMIT, MAX_MODE_THRESHOLD } from './constants';
8 21
 
9 22
 /**
10 23
  * Gets the value of a specific React {@code Component} prop of the currently
@@ -73,3 +86,35 @@ export function displayVerification(state: IReduxState, pId: string) {
73 86
         && participant?.e2eeVerificationAvailable
74 87
         && participant?.e2eeVerified === undefined);
75 88
 }
89
+
90
+/**
91
+ * Unregisters the audio files based on locale.
92
+ *
93
+ * @param {Dispatch<any>} dispatch - The redux dispatch function.
94
+ * @returns {void}
95
+ */
96
+export function unregisterE2eeAudioFiles(dispatch: IStore['dispatch']) {
97
+    dispatch(unregisterSound(E2EE_OFF_SOUND_ID));
98
+    dispatch(unregisterSound(E2EE_ON_SOUND_ID));
99
+}
100
+
101
+/**
102
+ * Registers the audio files based on locale.
103
+ *
104
+ * @param {Dispatch<any>} dispatch - The redux dispatch function.
105
+ * @param {boolean|undefined} shouldUnregister - Whether the sounds should be unregistered.
106
+ * @returns {void}
107
+ */
108
+export function registerE2eeAudioFiles(dispatch: IStore['dispatch'], shouldUnregister?: boolean) {
109
+    const language = i18next.language;
110
+
111
+    shouldUnregister && unregisterE2eeAudioFiles(dispatch);
112
+
113
+    dispatch(registerSound(
114
+        E2EE_OFF_SOUND_ID,
115
+        getSoundFileSrc(E2EE_OFF_SOUND_FILE, language)));
116
+
117
+    dispatch(registerSound(
118
+        E2EE_ON_SOUND_ID,
119
+        getSoundFileSrc(E2EE_ON_SOUND_FILE, language)));
120
+}

+ 9
- 14
react/features/e2ee/middleware.ts Voir le fichier

@@ -1,4 +1,3 @@
1
-
2 1
 import { IStore } from '../app/types';
3 2
 import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app/actionTypes';
4 3
 import { CONFERENCE_JOINED } from '../base/conference/actionTypes';
@@ -14,16 +13,19 @@ import {
14 13
 } from '../base/participants/functions';
15 14
 import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
16 15
 import StateListenerRegistry from '../base/redux/StateListenerRegistry';
17
-import { playSound, registerSound, unregisterSound } from '../base/sounds/actions';
16
+import { playSound } from '../base/sounds/actions';
18 17
 
19 18
 import { PARTICIPANT_VERIFIED, SET_MEDIA_ENCRYPTION_KEY, START_VERIFICATION, TOGGLE_E2EE } from './actionTypes';
20 19
 import { setE2EEMaxMode, toggleE2EE } from './actions';
21 20
 import ParticipantVerificationDialog from './components/ParticipantVerificationDialog';
22 21
 import { E2EE_OFF_SOUND_ID, E2EE_ON_SOUND_ID, MAX_MODE } from './constants';
23
-import { isMaxModeReached, isMaxModeThresholdReached } from './functions';
22
+import {
23
+    isMaxModeReached,
24
+    isMaxModeThresholdReached,
25
+    registerE2eeAudioFiles,
26
+    unregisterE2eeAudioFiles
27
+} from './functions';
24 28
 import logger from './logger';
25
-import { E2EE_OFF_SOUND_FILE, E2EE_ON_SOUND_FILE } from './sounds';
26
-
27 29
 
28 30
 /**
29 31
  * Middleware that captures actions related to E2EE.
@@ -36,18 +38,11 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
36 38
 
37 39
     switch (action.type) {
38 40
     case APP_WILL_MOUNT:
39
-        dispatch(registerSound(
40
-            E2EE_OFF_SOUND_ID,
41
-            E2EE_OFF_SOUND_FILE));
42
-
43
-        dispatch(registerSound(
44
-            E2EE_ON_SOUND_ID,
45
-            E2EE_ON_SOUND_FILE));
41
+        registerE2eeAudioFiles(dispatch);
46 42
         break;
47 43
 
48 44
     case APP_WILL_UNMOUNT:
49
-        dispatch(unregisterSound(E2EE_OFF_SOUND_ID));
50
-        dispatch(unregisterSound(E2EE_ON_SOUND_ID));
45
+        unregisterE2eeAudioFiles(dispatch);
51 46
         break;
52 47
 
53 48
     case CONFERENCE_JOINED:

+ 63
- 2
react/features/recording/functions.ts Voir le fichier

@@ -1,19 +1,36 @@
1
-import { IReduxState } from '../app/types';
1
+import i18next from 'i18next';
2
+
3
+import { IReduxState, IStore } from '../app/types';
2 4
 import { isMobileBrowser } from '../base/environment/utils';
3 5
 import { isJwtFeatureEnabled } from '../base/jwt/functions';
4 6
 import { JitsiRecordingConstants, browser } from '../base/lib-jitsi-meet';
7
+import { getSoundFileSrc } from '../base/media/functions';
5 8
 import {
6 9
     getLocalParticipant,
7 10
     getRemoteParticipants,
8 11
     isLocalParticipantModerator
9 12
 } from '../base/participants/functions';
13
+import { registerSound, unregisterSound } from '../base/sounds/actions';
10 14
 import { isInBreakoutRoom } from '../breakout-rooms/functions';
11 15
 import { isEnabled as isDropboxEnabled } from '../dropbox/functions';
12 16
 import { extractFqnFromPath } from '../dynamic-branding/functions.any';
13 17
 
14 18
 import LocalRecordingManager from './components/Recording/LocalRecordingManager';
15
-import { RECORDING_STATUS_PRIORITIES, RECORDING_TYPES } from './constants';
19
+import {
20
+    LIVE_STREAMING_OFF_SOUND_ID,
21
+    LIVE_STREAMING_ON_SOUND_ID,
22
+    RECORDING_OFF_SOUND_ID,
23
+    RECORDING_ON_SOUND_ID,
24
+    RECORDING_STATUS_PRIORITIES,
25
+    RECORDING_TYPES
26
+} from './constants';
16 27
 import logger from './logger';
28
+import {
29
+    LIVE_STREAMING_OFF_SOUND_FILE,
30
+    LIVE_STREAMING_ON_SOUND_FILE,
31
+    RECORDING_OFF_SOUND_FILE,
32
+    RECORDING_ON_SOUND_FILE
33
+} from './sounds';
17 34
 
18 35
 /**
19 36
  * Searches in the passed in redux state for an active recording session of the
@@ -278,3 +295,47 @@ function isRemoteParticipantRecordingLocally(state: IReduxState) {
278 295
 
279 296
     return false;
280 297
 }
298
+
299
+/**
300
+ * Unregisters the audio files based on locale.
301
+ *
302
+ * @param {Dispatch<any>} dispatch - The redux dispatch function.
303
+ * @returns {void}
304
+ */
305
+export function unregisterRecordingAudioFiles(dispatch: IStore['dispatch']) {
306
+    dispatch(unregisterSound(LIVE_STREAMING_OFF_SOUND_FILE));
307
+    dispatch(unregisterSound(LIVE_STREAMING_ON_SOUND_FILE));
308
+    dispatch(unregisterSound(RECORDING_OFF_SOUND_FILE));
309
+    dispatch(unregisterSound(RECORDING_ON_SOUND_FILE));
310
+}
311
+
312
+/**
313
+ * Registers the audio files based on locale.
314
+ *
315
+ * @param {Dispatch<any>} dispatch - The redux dispatch function.
316
+ * @param {boolean|undefined} shouldUnregister - Whether the sounds should be unregistered.
317
+ * @returns {void}
318
+ */
319
+export function registerRecordingAudioFiles(dispatch: IStore['dispatch'], shouldUnregister?: boolean) {
320
+    const language = i18next.language;
321
+
322
+    if (shouldUnregister) {
323
+        unregisterRecordingAudioFiles(dispatch);
324
+    }
325
+
326
+    dispatch(registerSound(
327
+        LIVE_STREAMING_OFF_SOUND_ID,
328
+        getSoundFileSrc(LIVE_STREAMING_OFF_SOUND_FILE, language)));
329
+
330
+    dispatch(registerSound(
331
+        LIVE_STREAMING_ON_SOUND_ID,
332
+        getSoundFileSrc(LIVE_STREAMING_ON_SOUND_FILE, language)));
333
+
334
+    dispatch(registerSound(
335
+        RECORDING_OFF_SOUND_ID,
336
+        getSoundFileSrc(RECORDING_OFF_SOUND_FILE, language)));
337
+
338
+    dispatch(registerSound(
339
+        RECORDING_ON_SOUND_ID,
340
+        getSoundFileSrc(RECORDING_ON_SOUND_FILE, language)));
341
+}

+ 6
- 29
react/features/recording/middleware.ts Voir le fichier

@@ -15,9 +15,7 @@ import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
15 15
 import StateListenerRegistry from '../base/redux/StateListenerRegistry';
16 16
 import {
17 17
     playSound,
18
-    registerSound,
19
-    stopSound,
20
-    unregisterSound
18
+    stopSound
21 19
 } from '../base/sounds/actions';
22 20
 import { TRACK_ADDED } from '../base/tracks/actionTypes';
23 21
 import { showErrorNotification, showNotification } from '../notifications/actions';
@@ -44,15 +42,11 @@ import {
44 42
 } from './constants';
45 43
 import {
46 44
     getResourceId,
47
-    getSessionById
45
+    getSessionById,
46
+    registerRecordingAudioFiles,
47
+    unregisterRecordingAudioFiles
48 48
 } from './functions';
49 49
 import logger from './logger';
50
-import {
51
-    LIVE_STREAMING_OFF_SOUND_FILE,
52
-    LIVE_STREAMING_ON_SOUND_FILE,
53
-    RECORDING_OFF_SOUND_FILE,
54
-    RECORDING_ON_SOUND_FILE
55
-} from './sounds';
56 50
 
57 51
 /**
58 52
  * StateListenerRegistry provides a reliable way to detect the leaving of a
@@ -85,29 +79,12 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => async action =>
85 79
 
86 80
     switch (action.type) {
87 81
     case APP_WILL_MOUNT:
88
-        dispatch(registerSound(
89
-            LIVE_STREAMING_OFF_SOUND_ID,
90
-            LIVE_STREAMING_OFF_SOUND_FILE));
91
-
92
-        dispatch(registerSound(
93
-            LIVE_STREAMING_ON_SOUND_ID,
94
-            LIVE_STREAMING_ON_SOUND_FILE));
95
-
96
-        dispatch(registerSound(
97
-            RECORDING_OFF_SOUND_ID,
98
-            RECORDING_OFF_SOUND_FILE));
99
-
100
-        dispatch(registerSound(
101
-            RECORDING_ON_SOUND_ID,
102
-            RECORDING_ON_SOUND_FILE));
82
+        registerRecordingAudioFiles(dispatch);
103 83
 
104 84
         break;
105 85
 
106 86
     case APP_WILL_UNMOUNT:
107
-        dispatch(unregisterSound(LIVE_STREAMING_OFF_SOUND_ID));
108
-        dispatch(unregisterSound(LIVE_STREAMING_ON_SOUND_ID));
109
-        dispatch(unregisterSound(RECORDING_OFF_SOUND_ID));
110
-        dispatch(unregisterSound(RECORDING_ON_SOUND_ID));
87
+        unregisterRecordingAudioFiles(dispatch);
111 88
 
112 89
         break;
113 90
 

BIN
sounds/e2eeOff_fr.mp3 Voir le fichier


BIN
sounds/e2eeOff_fr.opus Voir le fichier


BIN
sounds/e2eeOff_frCA.mp3 Voir le fichier


BIN
sounds/e2eeOff_frCA.opus Voir le fichier


BIN
sounds/e2eeOn_fr.mp3 Voir le fichier


BIN
sounds/e2eeOn_fr.opus Voir le fichier


BIN
sounds/e2eeOn_frCA.mp3 Voir le fichier


BIN
sounds/e2eeOn_frCA.opus Voir le fichier


BIN
sounds/liveStreamingOff_fr.mp3 Voir le fichier


BIN
sounds/liveStreamingOff_fr.opus Voir le fichier


BIN
sounds/liveStreamingOff_frCA.mp3 Voir le fichier


BIN
sounds/liveStreamingOff_frCA.opus Voir le fichier


BIN
sounds/liveStreamingOn_fr.mp3 Voir le fichier


BIN
sounds/liveStreamingOn_fr.opus Voir le fichier


BIN
sounds/liveStreamingOn_frCA.mp3 Voir le fichier


BIN
sounds/liveStreamingOn_frCA.opus Voir le fichier


BIN
sounds/recordingOff_fr.mp3 Voir le fichier


BIN
sounds/recordingOff_fr.opus Voir le fichier


BIN
sounds/recordingOff_frCA.mp3 Voir le fichier


BIN
sounds/recordingOff_frCA.opus Voir le fichier


BIN
sounds/recordingOn_fr.mp3 Voir le fichier


BIN
sounds/recordingOn_fr.opus Voir le fichier


BIN
sounds/recordingOn_frCA.mp3 Voir le fichier


BIN
sounds/recordingOn_frCA.opus Voir le fichier


Chargement…
Annuler
Enregistrer