From 7ce44a74a3b052190cfacd4bd5ee6b92cfc620ac Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 6 Jun 2018 16:46:42 +0200 Subject: Add server localization --- client/src/app/core/server/server.service.ts | 76 +++++++++++++++------- client/src/app/shared/i18n/i18n-utils.ts | 7 ++ client/src/app/shared/video/video-details.model.ts | 4 +- client/src/app/shared/video/video.model.ts | 8 ++- client/src/app/shared/video/video.service.ts | 56 +++++++++------- 5 files changed, 102 insertions(+), 49 deletions(-) create mode 100644 client/src/app/shared/i18n/i18n-utils.ts (limited to 'client/src/app') diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts index ccae5a151..56d33339e 100644 --- a/client/src/app/core/server/server.service.ts +++ b/client/src/app/core/server/server.service.ts @@ -1,17 +1,20 @@ -import { tap } from 'rxjs/operators' +import { map, share, switchMap, tap } from 'rxjs/operators' import { HttpClient } from '@angular/common/http' -import { Injectable } from '@angular/core' +import { Inject, Injectable, LOCALE_ID } from '@angular/core' import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage' -import { ReplaySubject } from 'rxjs' +import { Observable, ReplaySubject } from 'rxjs' import { ServerConfig } from '../../../../../shared' import { About } from '../../../../../shared/models/server/about.model' import { environment } from '../../../environments/environment' import { VideoConstant, VideoPrivacy } from '../../../../../shared/models/videos' +import { buildFileLocale, getDefaultLocale } from '../../../../../shared/models/i18n' +import { peertubeTranslate } from '@app/shared/i18n/i18n-utils' @Injectable() export class ServerService { private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config/' private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/' + private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/' private static CONFIG_LOCAL_STORAGE_KEY = 'server-config' configLoaded = new ReplaySubject(1) @@ -19,6 +22,7 @@ export class ServerService { videoCategoriesLoaded = new ReplaySubject(1) videoLicencesLoaded = new ReplaySubject(1) videoLanguagesLoaded = new ReplaySubject(1) + localeObservable: Observable private config: ServerConfig = { instance: { @@ -64,8 +68,12 @@ export class ServerService { private videoLanguages: Array> = [] private videoPrivacies: Array> = [] - constructor (private http: HttpClient) { + constructor ( + private http: HttpClient, + @Inject(LOCALE_ID) private localeId: string + ) { this.loadConfigLocally() + this.loadServerLocale() } loadConfig () { @@ -124,26 +132,46 @@ export class ServerService { notifier: ReplaySubject, sort = false ) { - return this.http.get(ServerService.BASE_VIDEO_URL + attributeName) - .subscribe(data => { - Object.keys(data) - .forEach(dataKey => { - hashToPopulate.push({ - id: dataKey, - label: data[dataKey] - }) - }) - - if (sort === true) { - hashToPopulate.sort((a, b) => { - if (a.label < b.label) return -1 - if (a.label === b.label) return 0 - return 1 - }) - } - - notifier.next(true) - }) + this.localeObservable + .pipe( + switchMap(translations => { + return this.http.get(ServerService.BASE_VIDEO_URL + attributeName) + .pipe(map(data => ({ data, translations }))) + }) + ) + .subscribe(({ data, translations }) => { + Object.keys(data) + .forEach(dataKey => { + const label = data[ dataKey ] + + hashToPopulate.push({ + id: dataKey, + label: peertubeTranslate(label, translations) + }) + }) + + if (sort === true) { + hashToPopulate.sort((a, b) => { + if (a.label < b.label) return -1 + if (a.label === b.label) return 0 + return 1 + }) + } + + notifier.next(true) + }) + } + + private loadServerLocale () { + const fileLocale = buildFileLocale(environment.production === true ? this.localeId : 'fr') + + // Default locale, nothing to translate + const defaultFileLocale = buildFileLocale(getDefaultLocale()) + if (fileLocale === defaultFileLocale) return {} + + this.localeObservable = this.http + .get(ServerService.BASE_LOCALE_URL + fileLocale + '/server.json') + .pipe(share()) } private saveConfigLocally (config: ServerConfig) { diff --git a/client/src/app/shared/i18n/i18n-utils.ts b/client/src/app/shared/i18n/i18n-utils.ts new file mode 100644 index 000000000..c1de51b7b --- /dev/null +++ b/client/src/app/shared/i18n/i18n-utils.ts @@ -0,0 +1,7 @@ +function peertubeTranslate (str: string, translations: { [ id: string ]: string }) { + return translations[str] ? translations[str] : str +} + +export { + peertubeTranslate +} diff --git a/client/src/app/shared/video/video-details.model.ts b/client/src/app/shared/video/video-details.model.ts index 5fc55fca6..19c350ab3 100644 --- a/client/src/app/shared/video/video-details.model.ts +++ b/client/src/app/shared/video/video-details.model.ts @@ -15,8 +15,8 @@ export class VideoDetails extends Video implements VideoDetailsServerModel { likesPercent: number dislikesPercent: number - constructor (hash: VideoDetailsServerModel) { - super(hash) + constructor (hash: VideoDetailsServerModel, translations = {}) { + super(hash, translations) this.descriptionPath = hash.descriptionPath this.files = hash.files diff --git a/client/src/app/shared/video/video.model.ts b/client/src/app/shared/video/video.model.ts index 48d562f9c..d37dc2c3e 100644 --- a/client/src/app/shared/video/video.model.ts +++ b/client/src/app/shared/video/video.model.ts @@ -5,6 +5,7 @@ import { VideoConstant } from '../../../../../shared/models/videos/video.model' import { getAbsoluteAPIUrl } from '../misc/utils' import { ServerConfig } from '../../../../../shared/models' import { Actor } from '@app/shared/actor/actor.model' +import { peertubeTranslate } from '@app/shared/i18n/i18n-utils' export class Video implements VideoServerModel { by: string @@ -68,7 +69,7 @@ export class Video implements VideoServerModel { minutes.toString() + ':' + secondsPadding + seconds.toString() } - constructor (hash: VideoServerModel) { + constructor (hash: VideoServerModel, translations = {}) { const absoluteAPIUrl = getAbsoluteAPIUrl() this.createdAt = new Date(hash.createdAt.toString()) @@ -98,6 +99,11 @@ export class Video implements VideoServerModel { this.by = Actor.CREATE_BY_STRING(hash.account.name, hash.account.host) this.accountAvatarUrl = Actor.GET_ACTOR_AVATAR_URL(this.account) + + this.category.label = peertubeTranslate(this.category.label, translations) + this.licence.label = peertubeTranslate(this.licence.label, translations) + this.language.label = peertubeTranslate(this.language.label, translations) + this.privacy.label = peertubeTranslate(this.privacy.label, translations) } isVideoNSFWForUser (user: User, serverConfig: ServerConfig) { diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts index d1e32faeb..c607b7d6a 100644 --- a/client/src/app/shared/video/video.service.ts +++ b/client/src/app/shared/video/video.service.ts @@ -1,4 +1,4 @@ -import { catchError, map } from 'rxjs/operators' +import { catchError, map, switchMap } from 'rxjs/operators' import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http' import { Injectable } from '@angular/core' import { Observable } from 'rxjs' @@ -24,6 +24,7 @@ import { Account } from '@app/shared/account/account.model' import { AccountService } from '@app/shared/account/account.service' import { VideoChannel } from '../../../../../shared/models/videos' import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' +import { ServerService } from '@app/core' @Injectable() export class VideoService { @@ -33,7 +34,8 @@ export class VideoService { constructor ( private authHttp: HttpClient, private restExtractor: RestExtractor, - private restService: RestService + private restService: RestService, + private serverService: ServerService ) {} getVideoViewUrl (uuid: string) { @@ -41,9 +43,13 @@ export class VideoService { } getVideo (uuid: string): Observable { - return this.authHttp.get(VideoService.BASE_VIDEO_URL + uuid) + return this.serverService.localeObservable .pipe( - map(videoHash => new VideoDetails(videoHash)), + switchMap(translations => { + return this.authHttp.get(VideoService.BASE_VIDEO_URL + uuid) + .pipe(map(videoHash => ({ videoHash, translations }))) + }), + map(({ videoHash, translations }) => new VideoDetails(videoHash, translations)), catchError(res => this.restExtractor.handleError(res)) ) } @@ -102,9 +108,10 @@ export class VideoService { let params = new HttpParams() params = this.restService.addRestGetParams(params, pagination, sort) - return this.authHttp.get(UserService.BASE_USERS_URL + '/me/videos', { params }) + return this.authHttp + .get>(UserService.BASE_USERS_URL + '/me/videos', { params }) .pipe( - map(this.extractVideos), + switchMap(res => this.extractVideos(res)), catchError(res => this.restExtractor.handleError(res)) ) } @@ -120,9 +127,9 @@ export class VideoService { params = this.restService.addRestGetParams(params, pagination, sort) return this.authHttp - .get(AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/videos', { params }) + .get>(AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/videos', { params }) .pipe( - map(this.extractVideos), + switchMap(res => this.extractVideos(res)), catchError(res => this.restExtractor.handleError(res)) ) } @@ -138,9 +145,9 @@ export class VideoService { params = this.restService.addRestGetParams(params, pagination, sort) return this.authHttp - .get(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.uuid + '/videos', { params }) + .get>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.uuid + '/videos', { params }) .pipe( - map(this.extractVideos), + switchMap(res => this.extractVideos(res)), catchError(res => this.restExtractor.handleError(res)) ) } @@ -160,9 +167,9 @@ export class VideoService { } return this.authHttp - .get(VideoService.BASE_VIDEO_URL, { params }) + .get>(VideoService.BASE_VIDEO_URL, { params }) .pipe( - map(this.extractVideos), + switchMap(res => this.extractVideos(res)), catchError(res => this.restExtractor.handleError(res)) ) } @@ -230,7 +237,7 @@ export class VideoService { return this.authHttp .get>(url, { params }) .pipe( - map(this.extractVideos), + switchMap(res => this.extractVideos(res)), catchError(res => this.restExtractor.handleError(res)) ) } @@ -287,14 +294,19 @@ export class VideoService { } private extractVideos (result: ResultList) { - const videosJson = result.data - const totalVideos = result.total - const videos = [] - - for (const videoJson of videosJson) { - videos.push(new Video(videoJson)) - } - - return { videos, totalVideos } + return this.serverService.localeObservable + .pipe( + map(translations => { + const videosJson = result.data + const totalVideos = result.total + const videos: Video[] = [] + + for (const videoJson of videosJson) { + videos.push(new Video(videoJson, translations)) + } + + return { videos, totalVideos } + }) + ) } } -- cgit v1.2.3