From 58479167ce63c965c734f8606ebec1b52c02c6c4 Mon Sep 17 00:00:00 2001 From: Carlos Santos <4a.santos@gmail.com> Date: Wed, 21 Jun 2023 13:37:43 +0200 Subject: [PATCH] openvidu-components: Allowed override lang options with a directive --- .../e2e/webcomponent-app/app.js | 9 +++ .../e2e/webcomponent.test.ts | 44 ++++++++++++ .../lang-selector.component.html | 2 +- .../lang-selector/lang-selector.component.ts | 20 ++++-- .../videoconference.component.ts | 1 + .../directives/api/api.directive.module.ts | 3 + .../api/videoconference.directive.ts | 67 +++++++++++++++++++ .../services/translate/translate.service.ts | 48 ++++++++++--- .../src/app/openvidu-call/call.component.html | 5 +- .../openvidu-webcomponent.component.html | 1 + .../openvidu-webcomponent.component.ts | 62 +++++++++++++---- 11 files changed, 235 insertions(+), 27 deletions(-) diff --git a/openvidu-components-angular/e2e/webcomponent-app/app.js b/openvidu-components-angular/e2e/webcomponent-app/app.js index 71335ec18d..90df8eb1da 100644 --- a/openvidu-components-angular/e2e/webcomponent-app/app.js +++ b/openvidu-components-angular/e2e/webcomponent-app/app.js @@ -3,6 +3,7 @@ import monkeyPatchMediaDevices from './utils/media-devices.js'; var MINIMAL; var LANG; var CAPTIONS_LANG; +var CUSTOM_LANG_OPTIONS; var CUSTOM_CAPTIONS_LANG_OPTIONS; var PREJOIN; var VIDEO_MUTED; @@ -52,6 +53,8 @@ $(document).ready(() => { MINIMAL = url.searchParams.get('minimal') === null ? false : url.searchParams.get('minimal') === 'true'; LANG = url.searchParams.get('lang') || 'en'; CAPTIONS_LANG = url.searchParams.get('captionsLang') || 'en-US'; + CUSTOM_LANG_OPTIONS = + url.searchParams.get('langOptions') === null ? false : url.searchParams.get('langOptions') === 'true'; CUSTOM_CAPTIONS_LANG_OPTIONS = url.searchParams.get('captionsLangOptions') === null ? false : url.searchParams.get('captionsLangOptions') === 'true'; PARTICIPANT_NAME = url.searchParams.get('participantName') || 'TEST_USER'; @@ -216,6 +219,12 @@ async function joinSession(sessionName, participantName) { webComponent.minimal = MINIMAL; webComponent.lang = LANG; webComponent.captionsLang = CAPTIONS_LANG; + if (CUSTOM_LANG_OPTIONS) { + webComponent.langOptions = [ + { name: 'Esp', lang: 'es' }, + { name: 'Eng', lang: 'en' } + ]; + } if (CUSTOM_CAPTIONS_LANG_OPTIONS) { webComponent.captionsLangOptions = [ { name: 'Esp', lang: 'es-ES' }, diff --git a/openvidu-components-angular/e2e/webcomponent.test.ts b/openvidu-components-angular/e2e/webcomponent.test.ts index acc9b3edc2..842c059c99 100644 --- a/openvidu-components-angular/e2e/webcomponent.test.ts +++ b/openvidu-components-angular/e2e/webcomponent.test.ts @@ -109,6 +109,50 @@ describe('Testing API Directives', () => { expect(await element.getText()).equal('Unirme ahora'); }); + it('should override the LANG OPTIONS', async () => { + await browser.get(`${url}&prejoin=true&langOptions=true`); + + + await utils.checkPrejoinIsPresent(); + await utils.waitForElement('.lang-button'); + await utils.clickOn('.lang-button'); + await browser.sleep(500); + expect(await utils.getNumberOfElements('.lang-menu-opt')).equals(2); + + await utils.clickOn('.lang-menu-opt'); + await browser.sleep(500); + + await utils.clickOn('#join-button'); + + await utils.checkSessionIsPresent(); + + // Checking if toolbar is present + await utils.checkToolbarIsPresent(); + + // Open more options menu + await utils.clickOn('#more-options-btn'); + + await browser.sleep(500); + + // Checking if button panel is present + await utils.waitForElement('.mat-menu-content'); + expect(await utils.isPresent('.mat-menu-content')).to.be.true; + + // Checking if captions button is present + await utils.waitForElement('#toolbar-settings-btn'); + expect(await utils.isPresent('#toolbar-settings-btn')).to.be.true; + await utils.clickOn('#toolbar-settings-btn'); + + await utils.waitForElement('#settings-container'); + await utils.waitForElement('.lang-button'); + await utils.clickOn('.lang-button'); + + await browser.sleep(500); + + expect(await utils.getNumberOfElements('.lang-menu-opt')).equals(2); + + }); + it('should show the PREJOIN page', async () => { await browser.get(`${url}&prejoin=true`); diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.html b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.html index f9be138962..ae26d5c50f 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.html +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.html @@ -3,7 +3,7 @@ expand_more - \ No newline at end of file diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.ts index 2fd453de83..1e620de639 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/lang-selector/lang-selector.component.ts @@ -1,9 +1,10 @@ -import { AfterViewInit, Component, OnInit, Output, ViewChild, EventEmitter } from '@angular/core'; +import { AfterViewInit, Component, OnInit, Output, ViewChild, EventEmitter, OnDestroy } from '@angular/core'; import { MatMenuTrigger } from '@angular/material/menu'; import { MatSelect } from '@angular/material/select'; import { StorageService } from '../../../services/storage/storage.service'; import { TranslateService } from '../../../services/translate/translate.service'; import { LangOption } from '../../../models/lang.model'; +import { Subscription } from 'rxjs'; /** * @internal @@ -13,11 +14,13 @@ import { LangOption } from '../../../models/lang.model'; templateUrl: './lang-selector.component.html', styleUrls: ['./lang-selector.component.css'] }) -export class LangSelectorComponent implements OnInit, AfterViewInit { +export class LangSelectorComponent implements OnInit, AfterViewInit, OnDestroy { @Output() onLangSelectorClicked = new EventEmitter(); langSelected: LangOption | undefined; languages: LangOption[] = []; + private langSub: Subscription; + /** * @ignore */ @@ -31,8 +34,12 @@ export class LangSelectorComponent implements OnInit, AfterViewInit { constructor(private translateService: TranslateService, private storageSrv: StorageService) {} ngOnInit(): void { + this.subscribeToLangSelected(); this.languages = this.translateService.getLanguagesInfo(); - this.langSelected = this.translateService.getLangSelected(); + } + + ngOnDestroy(): void { + this.langSub?.unsubscribe(); } ngAfterViewInit() { @@ -47,6 +54,11 @@ export class LangSelectorComponent implements OnInit, AfterViewInit { onLangSelected(lang: string) { this.translateService.setLanguage(lang); this.storageSrv.setLang(lang); - this.langSelected = this.translateService.getLangSelected(); + } + + subscribeToLangSelected() { + this.langSub = this.translateService.langSelectedObs.subscribe((lang) => { + this.langSelected = lang; + }); } } diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.ts index 133d2ad451..1240eee5ad 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.ts @@ -56,6 +56,7 @@ import { LangOption } from '../../models/lang.model'; * | :----------------------------: | :-------: | :---------------------------------------------: | * | **minimal** | `boolean` | {@link MinimalDirective} | * | **lang** | `string` | {@link LangDirective} | + * | **langOptions** | `LangOption []` | {@link LangOptionsDirective} | * | **captionsLang** | `string` | {@link CaptionsLangDirective} | * | **captionsLangOptions** | `CaptionsLangOption []` | {@link CaptionsLangOptionsDirective} | * | **prejoin** | `boolean` | {@link PrejoinDirective} | diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/api.directive.module.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/api.directive.module.ts index 8464e38571..08d78ae024 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/api.directive.module.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/api.directive.module.ts @@ -27,6 +27,7 @@ import { AudioMutedDirective, CaptionsLangDirective, CaptionsLangOptionsDirective, + LangOptionsDirective, LangDirective, MinimalDirective, ParticipantNameDirective, @@ -38,6 +39,7 @@ import { declarations: [ MinimalDirective, LangDirective, + LangOptionsDirective, CaptionsLangOptionsDirective, CaptionsLangDirective, PrejoinDirective, @@ -73,6 +75,7 @@ import { exports: [ MinimalDirective, LangDirective, + LangOptionsDirective, CaptionsLangOptionsDirective, CaptionsLangDirective, PrejoinDirective, diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/videoconference.directive.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/videoconference.directive.ts index 96103720c1..453aafba6e 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/videoconference.directive.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/api/videoconference.directive.ts @@ -3,6 +3,7 @@ import { CaptionsLangOption } from '../../models/caption.model'; import { CaptionService } from '../../services/caption/caption.service'; import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service'; import { TranslateService } from '../../services/translate/translate.service'; +import { LangOption } from '../../models/lang.model'; /** @@ -116,6 +117,72 @@ export class LangDirective implements OnDestroy { } } +/** + * The **langOptions** directive allows to set the application language options. + * It will override the application languages provided by default. + * This propety is an array of objects which must comply with the {@link LangOption} interface. + * + * It is only available for {@link VideoconferenceComponent}. + * + * Default: ``` + * [ + * { name: 'English', lang: 'en' }, + * { name: 'Español', lang: 'es' }, + * { name: 'Deutsch', lang: 'de' }, + * { name: 'Français', lang: 'fr' }, + * { name: '中国', lang: 'cn' }, + * { name: 'हिन्दी', lang: 'hi' }, + * { name: 'Italiano', lang: 'it' }, + * { name: 'やまと', lang: 'ja' }, + * { name: 'Dutch', lang: 'nl' }, + * { name: 'Português', lang: 'pt' } + * ]``` + * + * Note: If you want to add a new language, you must add a new object with the name and the language code (e.g. `{ name: 'Custom', lang: 'cus' }`) + * and then add the language file in the `assets/lang` folder with the name `cus.json`. + * + * + * @example + * + */ +@Directive({ + selector: 'ov-videoconference[langOptions]' +}) +export class LangOptionsDirective implements OnDestroy { + /** + * @ignore + */ + @Input() set langOptions(value: LangOption []) { + this.update(value); + } + + /** + * @ignore + */ + constructor(public elementRef: ElementRef, private translateService: TranslateService) {} + + /** + * @ignore + */ + ngOnDestroy(): void { + this.clear(); + } + + /** + * @ignore + */ + clear() { + this.update(undefined); + } + + /** + * @ignore + */ + update(value: LangOption [] | undefined) { + this.translateService.setLanguageOptions(value); + } +} + /** * The **captionsLang** directive allows specify the deafult language that OpenVidu will try to recognise. * diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/translate/translate.service.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/translate/translate.service.ts index c2b6e49fe6..b4304e3f93 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/translate/translate.service.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/translate/translate.service.ts @@ -10,7 +10,7 @@ import * as ja from '../../lang/ja.json'; import * as nl from '../../lang/nl.json'; import * as pt from '../../lang/pt.json'; import { StorageService } from '../storage/storage.service'; -import { BehaviorSubject, Observable, Subject } from 'rxjs'; +import { BehaviorSubject, Observable } from 'rxjs'; import { LangOption } from '../../models/lang.model'; /** @@ -36,20 +36,24 @@ export class TranslateService { private currentLang: any; langSelected: LangOption | undefined; langSelectedObs: Observable; - private _langSelected: BehaviorSubject = new BehaviorSubject(undefined); + private _langSelected: BehaviorSubject = new BehaviorSubject(undefined); constructor(private storageService: StorageService) { - const iso = this.storageService.getLang() || 'en'; - this.langSelected = this.langOptions.find((l) => l.lang === iso) || this.langOptions[0]; - this.currentLang = this.availableLanguages[this.langSelected.lang]; this.langSelectedObs = this._langSelected.asObservable(); - this._langSelected.next(this.langSelected); + this.updateLangSelected(); + } + + setLanguageOptions(options: LangOption[] | undefined) { + if (options && options.length > 0) { + this.langOptions = options; + this.updateLangSelected(); + } } - setLanguage(lang: string) { + async setLanguage(lang: string) { const matchingLang = this.langOptions.find((l) => l.lang === lang); if (matchingLang) { - this.currentLang = this.availableLanguages[lang]; + this.currentLang = await this.getLangData(lang); this.langSelected = matchingLang; this._langSelected.next(this.langSelected); } @@ -75,4 +79,32 @@ export class TranslateService { }); return result; } + + private async updateLangSelected() { + const storageLang = this.storageService.getLang(); + const langOpt = this.langOptions.find((opt) => opt.lang === storageLang); + if (storageLang && langOpt) { + this.langSelected = langOpt; + } else { + this.langSelected = this.langOptions[0]; + } + this.currentLang = await this.getLangData(this.langSelected.lang); + this._langSelected.next(this.langSelected); + + } + + private async getLangData(lang: string): Promise { + if (!(lang in this.availableLanguages)) { + // Language not found in default languages options + // Try to find it in the assets/lang directory + try { + const response = await fetch(`assets/lang/${lang}.json`); + return await response.json(); + } catch (error) { + console.error(`Not found ${lang}.json in assets/lang`, error); + } + } else { + return this.availableLanguages[lang]; + } + } } diff --git a/openvidu-components-angular/src/app/openvidu-call/call.component.html b/openvidu-components-angular/src/app/openvidu-call/call.component.html index 7118e137c9..0c5057c8c3 100644 --- a/openvidu-components-angular/src/app/openvidu-call/call.component.html +++ b/openvidu-components-angular/src/app/openvidu-call/call.component.html @@ -1,9 +1,10 @@ + */ + @Input() set langOptions(value: string | LangOption[]) { + this._langOptions = this.castToArray(value); + } + /** * The captionsLangOptions attribute sets the language options for the captions. * It will override the languages provided by default. @@ -188,19 +226,19 @@ export class OpenviduWebComponentComponent implements OnInit { * * Default: ``` * [ - * { name: 'English', ISO: 'en-US' }, - * { name: 'Español', ISO: 'es-ES' }, - * { name: 'Deutsch', ISO: 'de-DE' }, - * { name: 'Français', ISO: 'fr-FR' }, - * { name: '中国', ISO: 'zh-CN' }, - * { name: 'हिन्दी', ISO: 'hi-IN' }, - * { name: 'Italiano', ISO: 'it-IT' }, - * { name: 'やまと', ISO: 'jp-JP' }, - * { name: 'Português', ISO: 'pt-PT' } + * { name: 'English', lang: 'en-US' }, + * { name: 'Español', lang: 'es-ES' }, + * { name: 'Deutsch', lang: 'de-DE' }, + * { name: 'Français', lang: 'fr-FR' }, + * { name: '中国', lang: 'zh-CN' }, + * { name: 'हिन्दी', lang: 'hi-IN' }, + * { name: 'Italiano', lang: 'it-IT' }, + * { name: 'やまと', lang: 'jp-JP' }, + * { name: 'Português', lang: 'pt-PT' } * ]``` * * @example - * + * */ @Input() set captionsLangOptions(value: string | CaptionsLangOption[]) { this._captionsLangOptions = this.castToArray(value); @@ -888,7 +926,7 @@ export class OpenviduWebComponentComponent implements OnInit { return value; } else { throw new Error( - 'Parameter has not a valid type. The parameters must to be string or CaptionsLangOptions [] [{name:string, ISO: string}].' + 'Parameter has not a valid type. The parameters must to be string or CaptionsLangOptions [] [{name:string, lang: string}].' ); } }