diff options
author | Chocobozzz <me@florianbigard.com> | 2018-08-16 10:48:35 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-08-16 10:48:35 +0200 |
commit | 3dfa84940273619ae00f11a5f419a5e4876b2f53 (patch) | |
tree | bd31beeb985a9696af90e15ff6b767c4a0da03d9 | |
parent | 4f1f6f038389ce9cdf0c77dfccdc63efc6948101 (diff) | |
download | PeerTube-3dfa84940273619ae00f11a5f419a5e4876b2f53.tar.gz PeerTube-3dfa84940273619ae00f11a5f419a5e4876b2f53.tar.zst PeerTube-3dfa84940273619ae00f11a5f419a5e4876b2f53.zip |
Translate subtitle langs in player
-rw-r--r-- | client/src/app/core/server/server.service.ts | 4 | ||||
-rw-r--r-- | client/src/app/shared/i18n/i18n-utils.ts | 7 | ||||
-rw-r--r-- | client/src/app/shared/video-caption/video-caption.service.ts | 27 | ||||
-rw-r--r-- | client/src/app/shared/video-import/video-import.service.ts | 3 | ||||
-rw-r--r-- | client/src/app/shared/video/video.model.ts | 3 | ||||
-rw-r--r-- | client/src/app/videos/+video-watch/video-watch.component.ts | 4 | ||||
-rw-r--r-- | client/src/assets/player/peertube-player.ts | 34 | ||||
-rw-r--r-- | client/src/standalone/videos/embed.ts | 11 | ||||
-rwxr-xr-x | scripts/build/client.sh | 8 | ||||
-rw-r--r-- | shared/models/i18n/i18n.ts | 4 |
10 files changed, 73 insertions, 32 deletions
diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts index 52b50cbe8..6d886d2dd 100644 --- a/client/src/app/core/server/server.service.ts +++ b/client/src/app/core/server/server.service.ts | |||
@@ -7,8 +7,8 @@ import { getCompleteLocale, 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 } from '../../../../../shared/models/videos' | 9 | import { VideoConstant } from '../../../../../shared/models/videos' |
10 | import { isDefaultLocale } from '../../../../../shared/models/i18n' | 10 | import { isDefaultLocale, peertubeTranslate } from '../../../../../shared/models/i18n' |
11 | import { getDevLocale, isOnDevLocale, peertubeTranslate } from '@app/shared/i18n/i18n-utils' | 11 | import { getDevLocale, isOnDevLocale } from '@app/shared/i18n/i18n-utils' |
12 | import { sortBy } from '@app/shared/misc/utils' | 12 | import { sortBy } from '@app/shared/misc/utils' |
13 | 13 | ||
14 | @Injectable() | 14 | @Injectable() |
diff --git a/client/src/app/shared/i18n/i18n-utils.ts b/client/src/app/shared/i18n/i18n-utils.ts index 37180b930..1838dc752 100644 --- a/client/src/app/shared/i18n/i18n-utils.ts +++ b/client/src/app/shared/i18n/i18n-utils.ts | |||
@@ -1,9 +1,5 @@ | |||
1 | import { environment } from '../../../environments/environment' | 1 | import { environment } from '../../../environments/environment' |
2 | 2 | ||
3 | function peertubeTranslate (str: string, translations: { [ id: string ]: string }) { | ||
4 | return translations[str] ? translations[str] : str | ||
5 | } | ||
6 | |||
7 | function isOnDevLocale () { | 3 | function isOnDevLocale () { |
8 | return environment.production === false && window.location.search === '?lang=fr' | 4 | return environment.production === false && window.location.search === '?lang=fr' |
9 | } | 5 | } |
@@ -14,6 +10,5 @@ function getDevLocale () { | |||
14 | 10 | ||
15 | export { | 11 | export { |
16 | getDevLocale, | 12 | getDevLocale, |
17 | isOnDevLocale, | 13 | isOnDevLocale |
18 | peertubeTranslate | ||
19 | } | 14 | } |
diff --git a/client/src/app/shared/video-caption/video-caption.service.ts b/client/src/app/shared/video-caption/video-caption.service.ts index 9c29bc052..994882451 100644 --- a/client/src/app/shared/video-caption/video-caption.service.ts +++ b/client/src/app/shared/video-caption/video-caption.service.ts | |||
@@ -1,29 +1,44 @@ | |||
1 | import { catchError, map } from 'rxjs/operators' | 1 | import { catchError, map, switchMap } 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 { Injectable } from '@angular/core' |
4 | import { forkJoin, Observable, of } from 'rxjs' | 4 | import { forkJoin, Observable, of } from 'rxjs' |
5 | import { ResultList } from '../../../../../shared' | 5 | import { peertubeTranslate, ResultList } from '../../../../../shared' |
6 | import { RestExtractor, RestService } from '../rest' | 6 | import { RestExtractor, RestService } from '../rest' |
7 | import { VideoService } from '@app/shared/video/video.service' | 7 | import { VideoService } from '@app/shared/video/video.service' |
8 | import { objectToFormData, sortBy } from '@app/shared/misc/utils' | 8 | import { objectToFormData, sortBy } from '@app/shared/misc/utils' |
9 | import { VideoCaptionEdit } from '@app/shared/video-caption/video-caption-edit.model' | 9 | import { VideoCaptionEdit } from '@app/shared/video-caption/video-caption-edit.model' |
10 | import { VideoCaption } from '../../../../../shared/models/videos/caption/video-caption.model' | 10 | import { VideoCaption } from '../../../../../shared/models/videos/caption/video-caption.model' |
11 | import { ServerService } from '@app/core' | ||
11 | 12 | ||
12 | @Injectable() | 13 | @Injectable() |
13 | export class VideoCaptionService { | 14 | export class VideoCaptionService { |
14 | constructor ( | 15 | constructor ( |
15 | private authHttp: HttpClient, | 16 | private authHttp: HttpClient, |
17 | private serverService: ServerService, | ||
16 | private restService: RestService, | 18 | private restService: RestService, |
17 | private restExtractor: RestExtractor | 19 | private restExtractor: RestExtractor |
18 | ) {} | 20 | ) {} |
19 | 21 | ||
20 | listCaptions (videoId: number | string): Observable<ResultList<VideoCaption>> { | 22 | listCaptions (videoId: number | string): Observable<ResultList<VideoCaption>> { |
21 | return this.authHttp.get<ResultList<VideoCaption>>(VideoService.BASE_VIDEO_URL + videoId + '/captions') | 23 | return this.authHttp.get<ResultList<VideoCaption>>(VideoService.BASE_VIDEO_URL + videoId + '/captions') |
22 | .pipe(map(res => { | 24 | .pipe( |
23 | sortBy(res.data, 'language', 'label') | 25 | switchMap(captionsResult => { |
26 | return this.serverService.localeObservable | ||
27 | .pipe(map(translations => ({ captionsResult, translations }))) | ||
28 | }), | ||
29 | map(({ captionsResult, translations }) => { | ||
30 | for (const c of captionsResult.data) { | ||
31 | c.language.label = peertubeTranslate(c.language.label, translations) | ||
32 | } | ||
33 | |||
34 | return captionsResult | ||
35 | }), | ||
36 | map(captionsResult => { | ||
37 | sortBy(captionsResult.data, 'language', 'label') | ||
24 | 38 | ||
25 | return res | 39 | return captionsResult |
26 | })) | 40 | }) |
41 | ) | ||
27 | .pipe(catchError(res => this.restExtractor.handleError(res))) | 42 | .pipe(catchError(res => this.restExtractor.handleError(res))) |
28 | } | 43 | } |
29 | 44 | ||
diff --git a/client/src/app/shared/video-import/video-import.service.ts b/client/src/app/shared/video-import/video-import.service.ts index fc34dbf2d..7ae66ddfc 100644 --- a/client/src/app/shared/video-import/video-import.service.ts +++ b/client/src/app/shared/video-import/video-import.service.ts | |||
@@ -2,7 +2,7 @@ import { catchError, map, switchMap } from 'rxjs/operators' | |||
2 | import { HttpClient, HttpParams } from '@angular/common/http' | 2 | import { HttpClient, HttpParams } 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' |
5 | import { VideoImport } from '../../../../../shared' | 5 | import { peertubeTranslate, VideoImport } from '../../../../../shared' |
6 | import { environment } from '../../../environments/environment' | 6 | import { environment } from '../../../environments/environment' |
7 | import { RestExtractor, RestService } from '../rest' | 7 | import { RestExtractor, RestService } from '../rest' |
8 | import { VideoImportCreate, VideoUpdate } from '../../../../../shared/models/videos' | 8 | import { VideoImportCreate, VideoUpdate } from '../../../../../shared/models/videos' |
@@ -12,7 +12,6 @@ import { UserService } from '@app/shared/users/user.service' | |||
12 | import { SortMeta } from 'primeng/components/common/sortmeta' | 12 | import { SortMeta } from 'primeng/components/common/sortmeta' |
13 | import { RestPagination } from '@app/shared/rest' | 13 | import { RestPagination } from '@app/shared/rest' |
14 | import { ServerService } from '@app/core' | 14 | import { ServerService } from '@app/core' |
15 | import { peertubeTranslate } from '@app/shared/i18n/i18n-utils' | ||
16 | 15 | ||
17 | @Injectable() | 16 | @Injectable() |
18 | export class VideoImportService { | 17 | export class VideoImportService { |
diff --git a/client/src/app/shared/video/video.model.ts b/client/src/app/shared/video/video.model.ts index ec0afcccb..df8253301 100644 --- a/client/src/app/shared/video/video.model.ts +++ b/client/src/app/shared/video/video.model.ts | |||
@@ -3,9 +3,8 @@ import { Video as VideoServerModel, VideoPrivacy, VideoState } from '../../../.. | |||
3 | import { Avatar } from '../../../../../shared/models/avatars/avatar.model' | 3 | import { Avatar } from '../../../../../shared/models/avatars/avatar.model' |
4 | import { VideoConstant } from '../../../../../shared/models/videos/video-constant.model' | 4 | import { VideoConstant } from '../../../../../shared/models/videos/video-constant.model' |
5 | import { getAbsoluteAPIUrl } from '../misc/utils' | 5 | import { getAbsoluteAPIUrl } from '../misc/utils' |
6 | import { ServerConfig } from '../../../../../shared/models' | 6 | import { peertubeTranslate, 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' | ||
9 | import { VideoScheduleUpdate } from '../../../../../shared/models/videos/video-schedule-update.model' | 8 | import { VideoScheduleUpdate } from '../../../../../shared/models/videos/video-schedule-update.model' |
10 | 9 | ||
11 | export class Video implements VideoServerModel { | 10 | export class Video implements VideoServerModel { |
diff --git a/client/src/app/videos/+video-watch/video-watch.component.ts b/client/src/app/videos/+video-watch/video-watch.component.ts index d8230f172..5fba1b12d 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts | |||
@@ -22,7 +22,7 @@ import { VideoDownloadComponent } from './modal/video-download.component' | |||
22 | import { VideoReportComponent } from './modal/video-report.component' | 22 | import { VideoReportComponent } from './modal/video-report.component' |
23 | import { VideoShareComponent } from './modal/video-share.component' | 23 | import { VideoShareComponent } from './modal/video-share.component' |
24 | import { VideoBlacklistComponent } from './modal/video-blacklist.component' | 24 | import { VideoBlacklistComponent } from './modal/video-blacklist.component' |
25 | import { addContextMenu, getVideojsOptions, loadLocale } from '../../../assets/player/peertube-player' | 25 | import { addContextMenu, getVideojsOptions, loadLocaleInVideoJS } from '../../../assets/player/peertube-player' |
26 | import { ServerService } from '@app/core' | 26 | import { ServerService } from '@app/core' |
27 | import { I18n } from '@ngx-translate/i18n-polyfill' | 27 | import { I18n } from '@ngx-translate/i18n-polyfill' |
28 | import { environment } from '../../../environments/environment' | 28 | import { environment } from '../../../environments/environment' |
@@ -411,7 +411,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
411 | }) | 411 | }) |
412 | 412 | ||
413 | if (this.videojsLocaleLoaded === false) { | 413 | if (this.videojsLocaleLoaded === false) { |
414 | await loadLocale(environment.apiUrl, videojs, isOnDevLocale() ? getDevLocale() : this.localeId) | 414 | await loadLocaleInVideoJS(environment.apiUrl, videojs, isOnDevLocale() ? getDevLocale() : this.localeId) |
415 | this.videojsLocaleLoaded = true | 415 | this.videojsLocaleLoaded = true |
416 | } | 416 | } |
417 | 417 | ||
diff --git a/client/src/assets/player/peertube-player.ts b/client/src/assets/player/peertube-player.ts index 1fca6a7d2..1b1ec7a68 100644 --- a/client/src/assets/player/peertube-player.ts +++ b/client/src/assets/player/peertube-player.ts | |||
@@ -174,18 +174,42 @@ function addContextMenu (player: any, videoEmbedUrl: string) { | |||
174 | }) | 174 | }) |
175 | } | 175 | } |
176 | 176 | ||
177 | function loadLocale (serverUrl: string, videojs: any, locale: string) { | 177 | function loadLocaleInVideoJS (serverUrl: string, videojs: any, locale: string) { |
178 | const completeLocale = getCompleteLocale(locale) | 178 | const path = getLocalePath(serverUrl, locale) |
179 | // It is the default locale, nothing to translate | ||
180 | if (!path) return Promise.resolve(undefined) | ||
179 | 181 | ||
180 | if (!is18nLocale(completeLocale) || isDefaultLocale(completeLocale)) return Promise.resolve(undefined) | 182 | const completeLocale = getCompleteLocale(locale) |
181 | 183 | ||
182 | return fetch(serverUrl + '/client/locales/' + completeLocale + '/player.json') | 184 | return fetch(path + '/player.json') |
183 | .then(res => res.json()) | 185 | .then(res => res.json()) |
184 | .then(json => videojs.addLanguage(getShortLocale(completeLocale), json)) | 186 | .then(json => videojs.addLanguage(getShortLocale(completeLocale), json)) |
185 | } | 187 | } |
186 | 188 | ||
189 | function getServerTranslations (serverUrl: string, locale: string) { | ||
190 | const path = getLocalePath(serverUrl, locale) | ||
191 | // It is the default locale, nothing to translate | ||
192 | if (!path) return Promise.resolve(undefined) | ||
193 | |||
194 | return fetch(path + '/server.json') | ||
195 | .then(res => res.json()) | ||
196 | } | ||
197 | |||
198 | // ############################################################################ | ||
199 | |||
187 | export { | 200 | export { |
188 | loadLocale, | 201 | getServerTranslations, |
202 | loadLocaleInVideoJS, | ||
189 | getVideojsOptions, | 203 | getVideojsOptions, |
190 | addContextMenu | 204 | addContextMenu |
191 | } | 205 | } |
206 | |||
207 | // ############################################################################ | ||
208 | |||
209 | function getLocalePath (serverUrl: string, locale: string) { | ||
210 | const completeLocale = getCompleteLocale(locale) | ||
211 | |||
212 | if (!is18nLocale(completeLocale) || isDefaultLocale(completeLocale)) return undefined | ||
213 | |||
214 | return serverUrl + '/client/locales/' + completeLocale | ||
215 | } | ||
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index 2aabb5fe8..ea3436c7c 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts | |||
@@ -20,8 +20,8 @@ import 'whatwg-fetch' | |||
20 | import * as vjs from 'video.js' | 20 | import * as vjs from 'video.js' |
21 | import * as Channel from 'jschannel' | 21 | import * as Channel from 'jschannel' |
22 | 22 | ||
23 | import { ResultList, VideoDetails } from '../../../../shared' | 23 | import { peertubeTranslate, ResultList, VideoDetails } from '../../../../shared' |
24 | import { addContextMenu, getVideojsOptions, loadLocale } from '../../assets/player/peertube-player' | 24 | import { addContextMenu, getServerTranslations, getVideojsOptions, loadLocaleInVideoJS } from '../../assets/player/peertube-player' |
25 | import { PeerTubeResolution } from '../player/definitions' | 25 | import { PeerTubeResolution } from '../player/definitions' |
26 | import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' | 26 | import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' |
27 | import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model' | 27 | import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model' |
@@ -257,8 +257,9 @@ class PeerTubeEmbed { | |||
257 | const lastPart = urlParts[ urlParts.length - 1 ] | 257 | const lastPart = urlParts[ urlParts.length - 1 ] |
258 | const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[ 0 ] | 258 | const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[ 0 ] |
259 | 259 | ||
260 | await loadLocale(window.location.origin, vjs, navigator.language) | 260 | const [ , serverTranslations, videoResponse, captionsResponse ] = await Promise.all([ |
261 | const [ videoResponse, captionsResponse ] = await Promise.all([ | 261 | loadLocaleInVideoJS(window.location.origin, vjs, navigator.language), |
262 | getServerTranslations(window.location.origin, navigator.language), | ||
262 | this.loadVideoInfo(videoId), | 263 | this.loadVideoInfo(videoId), |
263 | this.loadVideoCaptions(videoId) | 264 | this.loadVideoCaptions(videoId) |
264 | ]) | 265 | ]) |
@@ -274,7 +275,7 @@ class PeerTubeEmbed { | |||
274 | if (captionsResponse.ok) { | 275 | if (captionsResponse.ok) { |
275 | const { data } = (await captionsResponse.json()) as ResultList<VideoCaption> | 276 | const { data } = (await captionsResponse.json()) as ResultList<VideoCaption> |
276 | videoCaptions = data.map(c => ({ | 277 | videoCaptions = data.map(c => ({ |
277 | label: c.language.label, | 278 | label: peertubeTranslate(c.language.label, serverTranslations), |
278 | language: c.language.id, | 279 | language: c.language.id, |
279 | src: window.location.origin + c.captionPath | 280 | src: window.location.origin + c.captionPath |
280 | })) | 281 | })) |
diff --git a/scripts/build/client.sh b/scripts/build/client.sh index 080454d07..3d1d0234e 100755 --- a/scripts/build/client.sh +++ b/scripts/build/client.sh | |||
@@ -30,8 +30,12 @@ post_build_hook | |||
30 | 30 | ||
31 | # Don't build other languages if --light arg is provided | 31 | # Don't build other languages if --light arg is provided |
32 | if [ -z ${1+x} ] || [ "$1" != "--light" ]; then | 32 | if [ -z ${1+x} ] || [ "$1" != "--light" ]; then |
33 | # Supported languages | 33 | if [ ! -z ${1+x} ] && [ "$1" == "--light-fr" ]; then |
34 | languages=("fr_FR" "eu_ES" "ca_ES" "cs_CZ" "eo" "zh_Hant_TW" "de_DE" "es_ES" "oc") | 34 | languages=("fr_FR") |
35 | else | ||
36 | # Supported languages | ||
37 | languages=("fr_FR" "eu_ES" "ca_ES" "cs_CZ" "eo" "zh_Hant_TW" "de_DE" "es_ES" "oc") | ||
38 | fi | ||
35 | 39 | ||
36 | for lang in "${languages[@]}"; do | 40 | for lang in "${languages[@]}"; do |
37 | # TODO: remove when the project will use runtime translations | 41 | # TODO: remove when the project will use runtime translations |
diff --git a/shared/models/i18n/i18n.ts b/shared/models/i18n/i18n.ts index 2530a1927..c5de972ac 100644 --- a/shared/models/i18n/i18n.ts +++ b/shared/models/i18n/i18n.ts | |||
@@ -36,6 +36,10 @@ export function isDefaultLocale (locale: string) { | |||
36 | return getCompleteLocale(locale) === getCompleteLocale(getDefaultLocale()) | 36 | return getCompleteLocale(locale) === getCompleteLocale(getDefaultLocale()) |
37 | } | 37 | } |
38 | 38 | ||
39 | export function peertubeTranslate (str: string, translations?: { [ id: string ]: string }) { | ||
40 | return translations && translations[str] ? translations[str] : str | ||
41 | } | ||
42 | |||
39 | const possiblePaths = POSSIBLE_LOCALES.map(l => '/' + l) | 43 | const possiblePaths = POSSIBLE_LOCALES.map(l => '/' + l) |
40 | export function is18nPath (path: string) { | 44 | export function is18nPath (path: string) { |
41 | return possiblePaths.indexOf(path) !== -1 | 45 | return possiblePaths.indexOf(path) !== -1 |