diff options
author | Chocobozzz <me@florianbigard.com> | 2018-06-06 16:46:42 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-06-06 16:48:41 +0200 |
commit | 7ce44a74a3b052190cfacd4bd5ee6b92cfc620ac (patch) | |
tree | 6f178426c165f9136eb08354efa4a06c24725f87 /client/src/app | |
parent | f07d6385b4b46c3254898292a8a53ed415b8d49b (diff) | |
download | PeerTube-7ce44a74a3b052190cfacd4bd5ee6b92cfc620ac.tar.gz PeerTube-7ce44a74a3b052190cfacd4bd5ee6b92cfc620ac.tar.zst PeerTube-7ce44a74a3b052190cfacd4bd5ee6b92cfc620ac.zip |
Add server localization
Diffstat (limited to 'client/src/app')
-rw-r--r-- | client/src/app/core/server/server.service.ts | 76 | ||||
-rw-r--r-- | client/src/app/shared/i18n/i18n-utils.ts | 7 | ||||
-rw-r--r-- | client/src/app/shared/video/video-details.model.ts | 4 | ||||
-rw-r--r-- | client/src/app/shared/video/video.model.ts | 8 | ||||
-rw-r--r-- | client/src/app/shared/video/video.service.ts | 56 |
5 files changed, 102 insertions, 49 deletions
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 @@ | |||
1 | import { tap } from 'rxjs/operators' | 1 | import { map, share, switchMap, tap } from 'rxjs/operators' |
2 | import { HttpClient } from '@angular/common/http' | 2 | import { HttpClient } from '@angular/common/http' |
3 | import { Injectable } from '@angular/core' | 3 | import { Inject, Injectable, LOCALE_ID } from '@angular/core' |
4 | import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage' | 4 | import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage' |
5 | import { ReplaySubject } from 'rxjs' | 5 | import { Observable, ReplaySubject } from 'rxjs' |
6 | import { ServerConfig } from '../../../../../shared' | 6 | import { ServerConfig } from '../../../../../shared' |
7 | import { About } from '../../../../../shared/models/server/about.model' | 7 | import { About } from '../../../../../shared/models/server/about.model' |
8 | import { environment } from '../../../environments/environment' | 8 | import { environment } from '../../../environments/environment' |
9 | import { VideoConstant, VideoPrivacy } from '../../../../../shared/models/videos' | 9 | import { VideoConstant, VideoPrivacy } from '../../../../../shared/models/videos' |
10 | import { buildFileLocale, getDefaultLocale } from '../../../../../shared/models/i18n' | ||
11 | import { peertubeTranslate } from '@app/shared/i18n/i18n-utils' | ||
10 | 12 | ||
11 | @Injectable() | 13 | @Injectable() |
12 | export class ServerService { | 14 | export class ServerService { |
13 | private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config/' | 15 | private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config/' |
14 | private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/' | 16 | private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/' |
17 | private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/' | ||
15 | private static CONFIG_LOCAL_STORAGE_KEY = 'server-config' | 18 | private static CONFIG_LOCAL_STORAGE_KEY = 'server-config' |
16 | 19 | ||
17 | configLoaded = new ReplaySubject<boolean>(1) | 20 | configLoaded = new ReplaySubject<boolean>(1) |
@@ -19,6 +22,7 @@ export class ServerService { | |||
19 | videoCategoriesLoaded = new ReplaySubject<boolean>(1) | 22 | videoCategoriesLoaded = new ReplaySubject<boolean>(1) |
20 | videoLicencesLoaded = new ReplaySubject<boolean>(1) | 23 | videoLicencesLoaded = new ReplaySubject<boolean>(1) |
21 | videoLanguagesLoaded = new ReplaySubject<boolean>(1) | 24 | videoLanguagesLoaded = new ReplaySubject<boolean>(1) |
25 | localeObservable: Observable<any> | ||
22 | 26 | ||
23 | private config: ServerConfig = { | 27 | private config: ServerConfig = { |
24 | instance: { | 28 | instance: { |
@@ -64,8 +68,12 @@ export class ServerService { | |||
64 | private videoLanguages: Array<VideoConstant<string>> = [] | 68 | private videoLanguages: Array<VideoConstant<string>> = [] |
65 | private videoPrivacies: Array<VideoConstant<VideoPrivacy>> = [] | 69 | private videoPrivacies: Array<VideoConstant<VideoPrivacy>> = [] |
66 | 70 | ||
67 | constructor (private http: HttpClient) { | 71 | constructor ( |
72 | private http: HttpClient, | ||
73 | @Inject(LOCALE_ID) private localeId: string | ||
74 | ) { | ||
68 | this.loadConfigLocally() | 75 | this.loadConfigLocally() |
76 | this.loadServerLocale() | ||
69 | } | 77 | } |
70 | 78 | ||
71 | loadConfig () { | 79 | loadConfig () { |
@@ -124,26 +132,46 @@ export class ServerService { | |||
124 | notifier: ReplaySubject<boolean>, | 132 | notifier: ReplaySubject<boolean>, |
125 | sort = false | 133 | sort = false |
126 | ) { | 134 | ) { |
127 | return this.http.get(ServerService.BASE_VIDEO_URL + attributeName) | 135 | this.localeObservable |
128 | .subscribe(data => { | 136 | .pipe( |
129 | Object.keys(data) | 137 | switchMap(translations => { |
130 | .forEach(dataKey => { | 138 | return this.http.get(ServerService.BASE_VIDEO_URL + attributeName) |
131 | hashToPopulate.push({ | 139 | .pipe(map(data => ({ data, translations }))) |
132 | id: dataKey, | 140 | }) |
133 | label: data[dataKey] | 141 | ) |
134 | }) | 142 | .subscribe(({ data, translations }) => { |
135 | }) | 143 | Object.keys(data) |
136 | 144 | .forEach(dataKey => { | |
137 | if (sort === true) { | 145 | const label = data[ dataKey ] |
138 | hashToPopulate.sort((a, b) => { | 146 | |
139 | if (a.label < b.label) return -1 | 147 | hashToPopulate.push({ |
140 | if (a.label === b.label) return 0 | 148 | id: dataKey, |
141 | return 1 | 149 | label: peertubeTranslate(label, translations) |
142 | }) | 150 | }) |
143 | } | 151 | }) |
144 | 152 | ||
145 | notifier.next(true) | 153 | if (sort === true) { |
146 | }) | 154 | hashToPopulate.sort((a, b) => { |
155 | if (a.label < b.label) return -1 | ||
156 | if (a.label === b.label) return 0 | ||
157 | return 1 | ||
158 | }) | ||
159 | } | ||
160 | |||
161 | notifier.next(true) | ||
162 | }) | ||
163 | } | ||
164 | |||
165 | private loadServerLocale () { | ||
166 | const fileLocale = buildFileLocale(environment.production === true ? this.localeId : 'fr') | ||
167 | |||
168 | // Default locale, nothing to translate | ||
169 | const defaultFileLocale = buildFileLocale(getDefaultLocale()) | ||
170 | if (fileLocale === defaultFileLocale) return {} | ||
171 | |||
172 | this.localeObservable = this.http | ||
173 | .get(ServerService.BASE_LOCALE_URL + fileLocale + '/server.json') | ||
174 | .pipe(share()) | ||
147 | } | 175 | } |
148 | 176 | ||
149 | private saveConfigLocally (config: ServerConfig) { | 177 | 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 @@ | |||
1 | function peertubeTranslate (str: string, translations: { [ id: string ]: string }) { | ||
2 | return translations[str] ? translations[str] : str | ||
3 | } | ||
4 | |||
5 | export { | ||
6 | peertubeTranslate | ||
7 | } | ||
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 { | |||
15 | likesPercent: number | 15 | likesPercent: number |
16 | dislikesPercent: number | 16 | dislikesPercent: number |
17 | 17 | ||
18 | constructor (hash: VideoDetailsServerModel) { | 18 | constructor (hash: VideoDetailsServerModel, translations = {}) { |
19 | super(hash) | 19 | super(hash, translations) |
20 | 20 | ||
21 | this.descriptionPath = hash.descriptionPath | 21 | this.descriptionPath = hash.descriptionPath |
22 | this.files = hash.files | 22 | 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' | |||
5 | import { getAbsoluteAPIUrl } from '../misc/utils' | 5 | import { getAbsoluteAPIUrl } from '../misc/utils' |
6 | import { ServerConfig } from '../../../../../shared/models' | 6 | import { ServerConfig } from '../../../../../shared/models' |
7 | import { Actor } from '@app/shared/actor/actor.model' | 7 | import { Actor } from '@app/shared/actor/actor.model' |
8 | import { peertubeTranslate } from '@app/shared/i18n/i18n-utils' | ||
8 | 9 | ||
9 | export class Video implements VideoServerModel { | 10 | export class Video implements VideoServerModel { |
10 | by: string | 11 | by: string |
@@ -68,7 +69,7 @@ export class Video implements VideoServerModel { | |||
68 | minutes.toString() + ':' + secondsPadding + seconds.toString() | 69 | minutes.toString() + ':' + secondsPadding + seconds.toString() |
69 | } | 70 | } |
70 | 71 | ||
71 | constructor (hash: VideoServerModel) { | 72 | constructor (hash: VideoServerModel, translations = {}) { |
72 | const absoluteAPIUrl = getAbsoluteAPIUrl() | 73 | const absoluteAPIUrl = getAbsoluteAPIUrl() |
73 | 74 | ||
74 | this.createdAt = new Date(hash.createdAt.toString()) | 75 | this.createdAt = new Date(hash.createdAt.toString()) |
@@ -98,6 +99,11 @@ export class Video implements VideoServerModel { | |||
98 | 99 | ||
99 | this.by = Actor.CREATE_BY_STRING(hash.account.name, hash.account.host) | 100 | this.by = Actor.CREATE_BY_STRING(hash.account.name, hash.account.host) |
100 | this.accountAvatarUrl = Actor.GET_ACTOR_AVATAR_URL(this.account) | 101 | this.accountAvatarUrl = Actor.GET_ACTOR_AVATAR_URL(this.account) |
102 | |||
103 | this.category.label = peertubeTranslate(this.category.label, translations) | ||
104 | this.licence.label = peertubeTranslate(this.licence.label, translations) | ||
105 | this.language.label = peertubeTranslate(this.language.label, translations) | ||
106 | this.privacy.label = peertubeTranslate(this.privacy.label, translations) | ||
101 | } | 107 | } |
102 | 108 | ||
103 | isVideoNSFWForUser (user: User, serverConfig: ServerConfig) { | 109 | 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 @@ | |||
1 | import { catchError, map } from 'rxjs/operators' | 1 | import { catchError, map, switchMap } from 'rxjs/operators' |
2 | import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http' | 2 | import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http' |
3 | import { Injectable } from '@angular/core' | 3 | import { Injectable } from '@angular/core' |
4 | import { Observable } from 'rxjs' | 4 | import { Observable } from 'rxjs' |
@@ -24,6 +24,7 @@ import { Account } from '@app/shared/account/account.model' | |||
24 | import { AccountService } from '@app/shared/account/account.service' | 24 | import { AccountService } from '@app/shared/account/account.service' |
25 | import { VideoChannel } from '../../../../../shared/models/videos' | 25 | import { VideoChannel } from '../../../../../shared/models/videos' |
26 | import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' | 26 | import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' |
27 | import { ServerService } from '@app/core' | ||
27 | 28 | ||
28 | @Injectable() | 29 | @Injectable() |
29 | export class VideoService { | 30 | export class VideoService { |
@@ -33,7 +34,8 @@ export class VideoService { | |||
33 | constructor ( | 34 | constructor ( |
34 | private authHttp: HttpClient, | 35 | private authHttp: HttpClient, |
35 | private restExtractor: RestExtractor, | 36 | private restExtractor: RestExtractor, |
36 | private restService: RestService | 37 | private restService: RestService, |
38 | private serverService: ServerService | ||
37 | ) {} | 39 | ) {} |
38 | 40 | ||
39 | getVideoViewUrl (uuid: string) { | 41 | getVideoViewUrl (uuid: string) { |
@@ -41,9 +43,13 @@ export class VideoService { | |||
41 | } | 43 | } |
42 | 44 | ||
43 | getVideo (uuid: string): Observable<VideoDetails> { | 45 | getVideo (uuid: string): Observable<VideoDetails> { |
44 | return this.authHttp.get<VideoDetailsServerModel>(VideoService.BASE_VIDEO_URL + uuid) | 46 | return this.serverService.localeObservable |
45 | .pipe( | 47 | .pipe( |
46 | map(videoHash => new VideoDetails(videoHash)), | 48 | switchMap(translations => { |
49 | return this.authHttp.get<VideoDetailsServerModel>(VideoService.BASE_VIDEO_URL + uuid) | ||
50 | .pipe(map(videoHash => ({ videoHash, translations }))) | ||
51 | }), | ||
52 | map(({ videoHash, translations }) => new VideoDetails(videoHash, translations)), | ||
47 | catchError(res => this.restExtractor.handleError(res)) | 53 | catchError(res => this.restExtractor.handleError(res)) |
48 | ) | 54 | ) |
49 | } | 55 | } |
@@ -102,9 +108,10 @@ export class VideoService { | |||
102 | let params = new HttpParams() | 108 | let params = new HttpParams() |
103 | params = this.restService.addRestGetParams(params, pagination, sort) | 109 | params = this.restService.addRestGetParams(params, pagination, sort) |
104 | 110 | ||
105 | return this.authHttp.get(UserService.BASE_USERS_URL + '/me/videos', { params }) | 111 | return this.authHttp |
112 | .get<ResultList<Video>>(UserService.BASE_USERS_URL + '/me/videos', { params }) | ||
106 | .pipe( | 113 | .pipe( |
107 | map(this.extractVideos), | 114 | switchMap(res => this.extractVideos(res)), |
108 | catchError(res => this.restExtractor.handleError(res)) | 115 | catchError(res => this.restExtractor.handleError(res)) |
109 | ) | 116 | ) |
110 | } | 117 | } |
@@ -120,9 +127,9 @@ export class VideoService { | |||
120 | params = this.restService.addRestGetParams(params, pagination, sort) | 127 | params = this.restService.addRestGetParams(params, pagination, sort) |
121 | 128 | ||
122 | return this.authHttp | 129 | return this.authHttp |
123 | .get(AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/videos', { params }) | 130 | .get<ResultList<Video>>(AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/videos', { params }) |
124 | .pipe( | 131 | .pipe( |
125 | map(this.extractVideos), | 132 | switchMap(res => this.extractVideos(res)), |
126 | catchError(res => this.restExtractor.handleError(res)) | 133 | catchError(res => this.restExtractor.handleError(res)) |
127 | ) | 134 | ) |
128 | } | 135 | } |
@@ -138,9 +145,9 @@ export class VideoService { | |||
138 | params = this.restService.addRestGetParams(params, pagination, sort) | 145 | params = this.restService.addRestGetParams(params, pagination, sort) |
139 | 146 | ||
140 | return this.authHttp | 147 | return this.authHttp |
141 | .get(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.uuid + '/videos', { params }) | 148 | .get<ResultList<Video>>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.uuid + '/videos', { params }) |
142 | .pipe( | 149 | .pipe( |
143 | map(this.extractVideos), | 150 | switchMap(res => this.extractVideos(res)), |
144 | catchError(res => this.restExtractor.handleError(res)) | 151 | catchError(res => this.restExtractor.handleError(res)) |
145 | ) | 152 | ) |
146 | } | 153 | } |
@@ -160,9 +167,9 @@ export class VideoService { | |||
160 | } | 167 | } |
161 | 168 | ||
162 | return this.authHttp | 169 | return this.authHttp |
163 | .get(VideoService.BASE_VIDEO_URL, { params }) | 170 | .get<ResultList<Video>>(VideoService.BASE_VIDEO_URL, { params }) |
164 | .pipe( | 171 | .pipe( |
165 | map(this.extractVideos), | 172 | switchMap(res => this.extractVideos(res)), |
166 | catchError(res => this.restExtractor.handleError(res)) | 173 | catchError(res => this.restExtractor.handleError(res)) |
167 | ) | 174 | ) |
168 | } | 175 | } |
@@ -230,7 +237,7 @@ export class VideoService { | |||
230 | return this.authHttp | 237 | return this.authHttp |
231 | .get<ResultList<VideoServerModel>>(url, { params }) | 238 | .get<ResultList<VideoServerModel>>(url, { params }) |
232 | .pipe( | 239 | .pipe( |
233 | map(this.extractVideos), | 240 | switchMap(res => this.extractVideos(res)), |
234 | catchError(res => this.restExtractor.handleError(res)) | 241 | catchError(res => this.restExtractor.handleError(res)) |
235 | ) | 242 | ) |
236 | } | 243 | } |
@@ -287,14 +294,19 @@ export class VideoService { | |||
287 | } | 294 | } |
288 | 295 | ||
289 | private extractVideos (result: ResultList<VideoServerModel>) { | 296 | private extractVideos (result: ResultList<VideoServerModel>) { |
290 | const videosJson = result.data | 297 | return this.serverService.localeObservable |
291 | const totalVideos = result.total | 298 | .pipe( |
292 | const videos = [] | 299 | map(translations => { |
293 | 300 | const videosJson = result.data | |
294 | for (const videoJson of videosJson) { | 301 | const totalVideos = result.total |
295 | videos.push(new Video(videoJson)) | 302 | const videos: Video[] = [] |
296 | } | 303 | |
297 | 304 | for (const videoJson of videosJson) { | |
298 | return { videos, totalVideos } | 305 | videos.push(new Video(videoJson, translations)) |
306 | } | ||
307 | |||
308 | return { videos, totalVideos } | ||
309 | }) | ||
310 | ) | ||
299 | } | 311 | } |
300 | } | 312 | } |