mirror of https://github.com/OpenVidu/openvidu.git
ov-components: Refactored translate service
parent
7336f4f609
commit
28a0574d99
|
@ -3,7 +3,7 @@ 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 { AvailableLangs, LangOption } from '../../../models/lang.model';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
/**
|
||||
|
@ -42,20 +42,20 @@ export class LangSelectorComponent implements OnInit, OnDestroy {
|
|||
|
||||
ngOnInit(): void {
|
||||
this.subscribeToLangSelected();
|
||||
this.languages = this.translateService.getLanguagesInfo();
|
||||
this.languages = this.translateService.getAvailableLanguages();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.langSub?.unsubscribe();
|
||||
}
|
||||
|
||||
onLangSelected(lang: string) {
|
||||
this.translateService.setLanguage(lang);
|
||||
onLangSelected(lang: AvailableLangs) {
|
||||
this.translateService.setCurrentLanguage(lang);
|
||||
this.storageSrv.setLang(lang);
|
||||
}
|
||||
|
||||
subscribeToLangSelected() {
|
||||
this.langSub = this.translateService.langSelectedObs.subscribe((lang) => {
|
||||
this.langSub = this.translateService.selectedLanguageOption$.subscribe((lang) => {
|
||||
this.langSelected = lang;
|
||||
this.onLangChanged.emit(lang);
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@ import { CaptionsLangOption } from '../../models/caption.model';
|
|||
// import { CaptionService } from '../../services/caption/caption.service';
|
||||
import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service';
|
||||
import { TranslateService } from '../../services/translate/translate.service';
|
||||
import { LangOption } from '../../models/lang.model';
|
||||
import { AvailableLangs, LangOption } from '../../models/lang.model';
|
||||
import { StorageService } from '../../services/storage/storage.service';
|
||||
|
||||
/**
|
||||
|
@ -244,7 +244,7 @@ export class LangDirective implements OnDestroy {
|
|||
/**
|
||||
* @ignore
|
||||
*/
|
||||
@Input() set lang(value: string) {
|
||||
@Input() set lang(value: AvailableLangs) {
|
||||
this.update(value);
|
||||
}
|
||||
|
||||
|
@ -273,8 +273,8 @@ export class LangDirective implements OnDestroy {
|
|||
/**
|
||||
* @ignore
|
||||
*/
|
||||
update(value: string) {
|
||||
this.translateService.setLanguage(value);
|
||||
update(value: AvailableLangs) {
|
||||
this.translateService.setCurrentLanguage(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -343,7 +343,7 @@ export class LangOptionsDirective implements OnDestroy {
|
|||
* @ignore
|
||||
*/
|
||||
update(value: LangOption[] | undefined) {
|
||||
this.translateService.setLanguageOptions(value);
|
||||
this.translateService.updateLanguageOptions(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
export type AvailableLangs = 'en' | 'es' | 'de' | 'fr' | 'cn' | 'hi' | 'it' | 'ja' | 'nl' | 'pt';
|
||||
|
||||
export type AdditionalTranslationsType = Record<AvailableLangs, Record<string, any>>;
|
||||
|
||||
export interface LangOption {
|
||||
name: string;
|
||||
lang: string;
|
||||
lang: AvailableLangs;
|
||||
}
|
||||
|
|
|
@ -63,7 +63,6 @@ import { OpenViduComponentsDirectiveModule } from './directives/template/openvid
|
|||
import { AppMaterialModule } from './openvidu-components-angular.material.module';
|
||||
import { VirtualBackgroundService } from './services/virtual-background/virtual-background.service';
|
||||
import { BroadcastingService } from './services/broadcasting/broadcasting.service';
|
||||
import { TranslateService } from './services/translate/translate.service';
|
||||
import { GlobalConfigService } from './services/config/global-config.service';
|
||||
import { OpenViduComponentsConfigService } from './services/config/directive-config.service';
|
||||
|
||||
|
@ -119,6 +118,7 @@ const privateComponents = [
|
|||
RemoteParticipantTracksPipe,
|
||||
DurationFromSecondsPipe,
|
||||
TrackPublishedTypesPipe,
|
||||
TranslatePipe,
|
||||
OpenViduComponentsDirectiveModule,
|
||||
ApiDirectiveModule
|
||||
],
|
||||
|
@ -150,7 +150,6 @@ const privateComponents = [
|
|||
PlatformService,
|
||||
RecordingService,
|
||||
StorageService,
|
||||
TranslateService,
|
||||
VirtualBackgroundService,
|
||||
provideHttpClient(withInterceptorsFromDi())
|
||||
]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Injectable, Inject, Injector, Type, Optional } from '@angular/core';
|
||||
import { Injectable, Inject, Injector, Optional } from '@angular/core';
|
||||
import { LayoutService } from '../layout/layout.service';
|
||||
import { OpenViduComponentsConfig } from '../../config/openvidu-components-angular.config';
|
||||
|
||||
|
|
|
@ -10,18 +10,27 @@ 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 { LangOption } from '../../models/lang.model';
|
||||
import { AdditionalTranslationsType, AvailableLangs, LangOption } from '../../models/lang.model';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Service responsible for managing translations for the application.
|
||||
* This service provides methods to add additional translations and to translate keys into the currently selected language.
|
||||
*
|
||||
* The pipe {@link TranslatePipe} is used to translate keys in the templates.
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class TranslateService {
|
||||
private availableLanguages = { en, es, de, fr, cn, hi, it, ja, nl, pt };
|
||||
private langOptions: LangOption[] = [
|
||||
// Maps language codes to their respective translations
|
||||
private translationsByLanguage: Record<AvailableLangs, any> = { en, es, de, fr, cn, hi, it, ja, nl, pt };
|
||||
|
||||
// Stores additional translations provided by the application
|
||||
private additionalTranslations: Record<AvailableLangs, any> | {} = {};
|
||||
|
||||
// List of available language options with their display names and language codes
|
||||
private languageOptions: LangOption[] = [
|
||||
{ name: 'English', lang: 'en' },
|
||||
{ name: 'Español', lang: 'es' },
|
||||
{ name: 'Deutsch', lang: 'de' },
|
||||
|
@ -33,68 +42,152 @@ export class TranslateService {
|
|||
{ name: 'Dutch', lang: 'nl' },
|
||||
{ name: 'Português', lang: 'pt' }
|
||||
];
|
||||
private currentLang: any;
|
||||
langSelected: LangOption;
|
||||
langSelectedObs: Observable<LangOption>;
|
||||
private _langSelected: BehaviorSubject<LangOption> = new BehaviorSubject<LangOption>({ name: 'English', lang: 'en' });
|
||||
|
||||
// The currently active translations for the selected language
|
||||
private activeTranslations: any;
|
||||
|
||||
// The currently selected language option
|
||||
private selectedLanguageOption: LangOption;
|
||||
|
||||
// BehaviorSubject to manage the currently selected language option
|
||||
private _selectedLanguageSubject: BehaviorSubject<LangOption> = new BehaviorSubject<LangOption>({ name: 'English', lang: 'en' });
|
||||
|
||||
// Observable that emits changes to the selected language option
|
||||
selectedLanguageOption$: Observable<LangOption>;
|
||||
|
||||
constructor(private storageService: StorageService) {
|
||||
this.langSelectedObs = this._langSelected.asObservable();
|
||||
this.updateLangSelected();
|
||||
this.selectedLanguageOption$ = this._selectedLanguageSubject.asObservable();
|
||||
this.refreshSelectedLanguage();
|
||||
}
|
||||
|
||||
async setLanguage(lang: string) {
|
||||
const matchingLang = this.langOptions.find((l) => l.lang === lang);
|
||||
/**
|
||||
* Adds multiple translations to the additional translations storage.
|
||||
* @param translations - A record where each key is a language code and the value is an object of translations for that language.
|
||||
*/
|
||||
addTranslations(translations: Partial<AdditionalTranslationsType>): void {
|
||||
this.additionalTranslations = translations;
|
||||
}
|
||||
|
||||
if (matchingLang) {
|
||||
this.currentLang = await this.getLangData(lang);
|
||||
this.langSelected = matchingLang;
|
||||
this._langSelected.next(this.langSelected);
|
||||
/**
|
||||
* Sets the current language based on the provided language code.
|
||||
* Updates the selected language and emits the change.
|
||||
* @param lang - The language code to set.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
async setCurrentLanguage(lang: AvailableLangs): Promise<void> {
|
||||
// Find the language option that matches the provided language code
|
||||
const selectedLanguageOption = this.languageOptions.find((option) => option.lang === lang);
|
||||
|
||||
if (selectedLanguageOption) {
|
||||
// Fetch the language data and update the current language
|
||||
this.activeTranslations = await this.fetchLanguageData(lang);
|
||||
this.selectedLanguageOption = selectedLanguageOption;
|
||||
this._selectedLanguageSubject.next(this.selectedLanguageOption);
|
||||
// Notify subscribers of the language change
|
||||
this._selectedLanguageSubject.next(this.selectedLanguageOption);
|
||||
}
|
||||
}
|
||||
|
||||
setLanguageOptions(options: LangOption[] | undefined) {
|
||||
/**
|
||||
* Updates the available language options.
|
||||
* @param options - The new language options to set.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
updateLanguageOptions(options?: LangOption[]): void {
|
||||
if (options && options.length > 0) {
|
||||
this.langOptions = options;
|
||||
this.updateLangSelected();
|
||||
this.languageOptions = options;
|
||||
this.refreshSelectedLanguage();
|
||||
}
|
||||
}
|
||||
|
||||
getLangSelected(): LangOption {
|
||||
return this.langSelected;
|
||||
/**
|
||||
* Retrieves the currently selected language option.
|
||||
* @returns The currently selected language option.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
getSelectedLanguage(): LangOption {
|
||||
return this.selectedLanguageOption;
|
||||
}
|
||||
|
||||
getLanguagesInfo(): LangOption[] {
|
||||
return this.langOptions;
|
||||
/**
|
||||
* Retrieves the list of all available language options.
|
||||
* @returns An array of available language options.
|
||||
*/
|
||||
getAvailableLanguages(): LangOption[] {
|
||||
return this.languageOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a given key into the current language.
|
||||
*
|
||||
* This method first attempts to find the translation in the official translations.
|
||||
* If the translation is not found, it then looks for the translation in the additional translations registered by the app.
|
||||
*
|
||||
* @param key - The key to be translated.
|
||||
* @returns The translated string if found, otherwise an empty string.
|
||||
*/
|
||||
translate(key: string): string {
|
||||
let result = this.currentLang;
|
||||
// Attempt to find the translation in the official translations
|
||||
let translation = this.findTranslation(this.activeTranslations, key);
|
||||
|
||||
key.split('.').forEach((prop) => {
|
||||
if (!translation) {
|
||||
// If not found, look for the translation in the additional translations
|
||||
const additionalLangTranslations = this.additionalTranslations[this.selectedLanguageOption.lang];
|
||||
translation = this.findTranslation(additionalLangTranslations, key);
|
||||
}
|
||||
|
||||
return translation || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns a translation string from a nested translations source object based on a dot-separated key.
|
||||
*
|
||||
* @param translationsSource - The source object containing nested translation strings.
|
||||
* @param key - A dot-separated string representing the path to the desired translation.
|
||||
* @returns The translation string if found, otherwise `undefined`.
|
||||
*/
|
||||
private findTranslation(translationsSource: any, key: string): string | undefined {
|
||||
let translation = translationsSource;
|
||||
|
||||
// Traverse the object tree based on the key structure
|
||||
key.split('.').forEach((nestedKey) => {
|
||||
try {
|
||||
result = result[prop];
|
||||
|
||||
translation = translation[nestedKey];
|
||||
} catch (error) {
|
||||
return '';
|
||||
}
|
||||
});
|
||||
return result;
|
||||
|
||||
return translation;
|
||||
}
|
||||
|
||||
private async updateLangSelected() {
|
||||
const storageLang = this.storageService.getLang();
|
||||
const langOpt = this.langOptions.find((opt) => opt.lang === storageLang);
|
||||
if (storageLang && langOpt) {
|
||||
this.langSelected = langOpt;
|
||||
/**
|
||||
* Updates the currently selected language based on the stored language setting.
|
||||
*/
|
||||
private async refreshSelectedLanguage() {
|
||||
const storedLang = this.storageService.getLang();
|
||||
const matchingOption = this.languageOptions.find((option) => option.lang === storedLang);
|
||||
|
||||
if (storedLang && matchingOption) {
|
||||
this.selectedLanguageOption = matchingOption;
|
||||
} else {
|
||||
this.langSelected = this.langOptions[0];
|
||||
// Default to the first language option if no language is found in storage
|
||||
this.selectedLanguageOption = this.languageOptions[0];
|
||||
}
|
||||
this.currentLang = await this.getLangData(this.langSelected.lang);
|
||||
this._langSelected.next(this.langSelected);
|
||||
this.activeTranslations = await this.fetchLanguageData(this.selectedLanguageOption.lang);
|
||||
this._selectedLanguageSubject.next(this.selectedLanguageOption);
|
||||
}
|
||||
|
||||
private async getLangData(lang: string): Promise<void> {
|
||||
if (!(lang in this.availableLanguages)) {
|
||||
/**
|
||||
* Fetches the language data from the source based on the provided language code.
|
||||
* @param lang - The language code to fetch data for.
|
||||
* @returns The language data associated with the provided language code.
|
||||
*/
|
||||
private async fetchLanguageData(lang: AvailableLangs): Promise<any> {
|
||||
if (!(lang in this.translationsByLanguage)) {
|
||||
// Language not found in default languages options
|
||||
// Try to find it in the assets/lang directory
|
||||
try {
|
||||
|
@ -102,9 +195,10 @@ export class TranslateService {
|
|||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error(`Not found ${lang}.json in assets/lang`, error);
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
return this.availableLanguages[lang];
|
||||
return this.translationsByLanguage[lang];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,10 +38,12 @@ export * from './lib/models/room.model';
|
|||
export * from './lib/models/toolbar.model';
|
||||
export * from './lib/models/logger.model'
|
||||
export * from './lib/models/storage.model';
|
||||
export * from './lib/models/lang.model';
|
||||
export * from './lib/openvidu-components-angular.module';
|
||||
// Pipes
|
||||
export * from './lib/pipes/participant.pipe';
|
||||
export * from './lib/pipes/recording.pipe';
|
||||
export * from './lib/pipes/translate.pipe';
|
||||
// Services
|
||||
export * from './lib/services/action/action.service';
|
||||
export * from './lib/services/broadcasting/broadcasting.service';
|
||||
|
@ -54,5 +56,6 @@ export * from './lib/services/recording/recording.service';
|
|||
export * from './lib/services/config/global-config.service';
|
||||
export * from './lib/services/logger/logger.service';
|
||||
export * from './lib/services/storage/storage.service';
|
||||
export * from './lib/services/translate/translate.service';
|
||||
|
||||
export * from 'livekit-client';
|
||||
|
|
Loading…
Reference in New Issue