Translate subtitle langs in player
This commit is contained in:
parent
4f1f6f0383
commit
3dfa849402
10 changed files with 73 additions and 32 deletions
|
@ -7,8 +7,8 @@ import { getCompleteLocale, ServerConfig } from '../../../../../shared'
|
||||||
import { About } from '../../../../../shared/models/server/about.model'
|
import { About } from '../../../../../shared/models/server/about.model'
|
||||||
import { environment } from '../../../environments/environment'
|
import { environment } from '../../../environments/environment'
|
||||||
import { VideoConstant } from '../../../../../shared/models/videos'
|
import { VideoConstant } from '../../../../../shared/models/videos'
|
||||||
import { isDefaultLocale } from '../../../../../shared/models/i18n'
|
import { isDefaultLocale, peertubeTranslate } from '../../../../../shared/models/i18n'
|
||||||
import { getDevLocale, isOnDevLocale, peertubeTranslate } from '@app/shared/i18n/i18n-utils'
|
import { getDevLocale, isOnDevLocale } from '@app/shared/i18n/i18n-utils'
|
||||||
import { sortBy } from '@app/shared/misc/utils'
|
import { sortBy } from '@app/shared/misc/utils'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
import { environment } from '../../../environments/environment'
|
import { environment } from '../../../environments/environment'
|
||||||
|
|
||||||
function peertubeTranslate (str: string, translations: { [ id: string ]: string }) {
|
|
||||||
return translations[str] ? translations[str] : str
|
|
||||||
}
|
|
||||||
|
|
||||||
function isOnDevLocale () {
|
function isOnDevLocale () {
|
||||||
return environment.production === false && window.location.search === '?lang=fr'
|
return environment.production === false && window.location.search === '?lang=fr'
|
||||||
}
|
}
|
||||||
|
@ -14,6 +10,5 @@ function getDevLocale () {
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getDevLocale,
|
getDevLocale,
|
||||||
isOnDevLocale,
|
isOnDevLocale
|
||||||
peertubeTranslate
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,44 @@
|
||||||
import { catchError, map } from 'rxjs/operators'
|
import { catchError, map, switchMap } from 'rxjs/operators'
|
||||||
import { HttpClient } from '@angular/common/http'
|
import { HttpClient } from '@angular/common/http'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { forkJoin, Observable, of } from 'rxjs'
|
import { forkJoin, Observable, of } from 'rxjs'
|
||||||
import { ResultList } from '../../../../../shared'
|
import { peertubeTranslate, ResultList } from '../../../../../shared'
|
||||||
import { RestExtractor, RestService } from '../rest'
|
import { RestExtractor, RestService } from '../rest'
|
||||||
import { VideoService } from '@app/shared/video/video.service'
|
import { VideoService } from '@app/shared/video/video.service'
|
||||||
import { objectToFormData, sortBy } from '@app/shared/misc/utils'
|
import { objectToFormData, sortBy } from '@app/shared/misc/utils'
|
||||||
import { VideoCaptionEdit } from '@app/shared/video-caption/video-caption-edit.model'
|
import { VideoCaptionEdit } from '@app/shared/video-caption/video-caption-edit.model'
|
||||||
import { VideoCaption } from '../../../../../shared/models/videos/caption/video-caption.model'
|
import { VideoCaption } from '../../../../../shared/models/videos/caption/video-caption.model'
|
||||||
|
import { ServerService } from '@app/core'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class VideoCaptionService {
|
export class VideoCaptionService {
|
||||||
constructor (
|
constructor (
|
||||||
private authHttp: HttpClient,
|
private authHttp: HttpClient,
|
||||||
|
private serverService: ServerService,
|
||||||
private restService: RestService,
|
private restService: RestService,
|
||||||
private restExtractor: RestExtractor
|
private restExtractor: RestExtractor
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
listCaptions (videoId: number | string): Observable<ResultList<VideoCaption>> {
|
listCaptions (videoId: number | string): Observable<ResultList<VideoCaption>> {
|
||||||
return this.authHttp.get<ResultList<VideoCaption>>(VideoService.BASE_VIDEO_URL + videoId + '/captions')
|
return this.authHttp.get<ResultList<VideoCaption>>(VideoService.BASE_VIDEO_URL + videoId + '/captions')
|
||||||
.pipe(map(res => {
|
.pipe(
|
||||||
sortBy(res.data, 'language', 'label')
|
switchMap(captionsResult => {
|
||||||
|
return this.serverService.localeObservable
|
||||||
|
.pipe(map(translations => ({ captionsResult, translations })))
|
||||||
|
}),
|
||||||
|
map(({ captionsResult, translations }) => {
|
||||||
|
for (const c of captionsResult.data) {
|
||||||
|
c.language.label = peertubeTranslate(c.language.label, translations)
|
||||||
|
}
|
||||||
|
|
||||||
return res
|
return captionsResult
|
||||||
}))
|
}),
|
||||||
|
map(captionsResult => {
|
||||||
|
sortBy(captionsResult.data, 'language', 'label')
|
||||||
|
|
||||||
|
return captionsResult
|
||||||
|
})
|
||||||
|
)
|
||||||
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { catchError, map, switchMap } from 'rxjs/operators'
|
||||||
import { HttpClient, HttpParams } from '@angular/common/http'
|
import { HttpClient, HttpParams } from '@angular/common/http'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { Observable } from 'rxjs'
|
import { Observable } from 'rxjs'
|
||||||
import { VideoImport } from '../../../../../shared'
|
import { peertubeTranslate, VideoImport } from '../../../../../shared'
|
||||||
import { environment } from '../../../environments/environment'
|
import { environment } from '../../../environments/environment'
|
||||||
import { RestExtractor, RestService } from '../rest'
|
import { RestExtractor, RestService } from '../rest'
|
||||||
import { VideoImportCreate, VideoUpdate } from '../../../../../shared/models/videos'
|
import { VideoImportCreate, VideoUpdate } from '../../../../../shared/models/videos'
|
||||||
|
@ -12,7 +12,6 @@ import { UserService } from '@app/shared/users/user.service'
|
||||||
import { SortMeta } from 'primeng/components/common/sortmeta'
|
import { SortMeta } from 'primeng/components/common/sortmeta'
|
||||||
import { RestPagination } from '@app/shared/rest'
|
import { RestPagination } from '@app/shared/rest'
|
||||||
import { ServerService } from '@app/core'
|
import { ServerService } from '@app/core'
|
||||||
import { peertubeTranslate } from '@app/shared/i18n/i18n-utils'
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class VideoImportService {
|
export class VideoImportService {
|
||||||
|
|
|
@ -3,9 +3,8 @@ import { Video as VideoServerModel, VideoPrivacy, VideoState } from '../../../..
|
||||||
import { Avatar } from '../../../../../shared/models/avatars/avatar.model'
|
import { Avatar } from '../../../../../shared/models/avatars/avatar.model'
|
||||||
import { VideoConstant } from '../../../../../shared/models/videos/video-constant.model'
|
import { VideoConstant } from '../../../../../shared/models/videos/video-constant.model'
|
||||||
import { getAbsoluteAPIUrl } from '../misc/utils'
|
import { getAbsoluteAPIUrl } from '../misc/utils'
|
||||||
import { ServerConfig } from '../../../../../shared/models'
|
import { peertubeTranslate, ServerConfig } from '../../../../../shared/models'
|
||||||
import { Actor } from '@app/shared/actor/actor.model'
|
import { Actor } from '@app/shared/actor/actor.model'
|
||||||
import { peertubeTranslate } from '@app/shared/i18n/i18n-utils'
|
|
||||||
import { VideoScheduleUpdate } from '../../../../../shared/models/videos/video-schedule-update.model'
|
import { VideoScheduleUpdate } from '../../../../../shared/models/videos/video-schedule-update.model'
|
||||||
|
|
||||||
export class Video implements VideoServerModel {
|
export class Video implements VideoServerModel {
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { VideoDownloadComponent } from './modal/video-download.component'
|
||||||
import { VideoReportComponent } from './modal/video-report.component'
|
import { VideoReportComponent } from './modal/video-report.component'
|
||||||
import { VideoShareComponent } from './modal/video-share.component'
|
import { VideoShareComponent } from './modal/video-share.component'
|
||||||
import { VideoBlacklistComponent } from './modal/video-blacklist.component'
|
import { VideoBlacklistComponent } from './modal/video-blacklist.component'
|
||||||
import { addContextMenu, getVideojsOptions, loadLocale } from '../../../assets/player/peertube-player'
|
import { addContextMenu, getVideojsOptions, loadLocaleInVideoJS } from '../../../assets/player/peertube-player'
|
||||||
import { ServerService } from '@app/core'
|
import { ServerService } from '@app/core'
|
||||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
import { environment } from '../../../environments/environment'
|
import { environment } from '../../../environments/environment'
|
||||||
|
@ -411,7 +411,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
})
|
})
|
||||||
|
|
||||||
if (this.videojsLocaleLoaded === false) {
|
if (this.videojsLocaleLoaded === false) {
|
||||||
await loadLocale(environment.apiUrl, videojs, isOnDevLocale() ? getDevLocale() : this.localeId)
|
await loadLocaleInVideoJS(environment.apiUrl, videojs, isOnDevLocale() ? getDevLocale() : this.localeId)
|
||||||
this.videojsLocaleLoaded = true
|
this.videojsLocaleLoaded = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -174,18 +174,42 @@ function addContextMenu (player: any, videoEmbedUrl: string) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadLocale (serverUrl: string, videojs: any, locale: string) {
|
function loadLocaleInVideoJS (serverUrl: string, videojs: any, locale: string) {
|
||||||
|
const path = getLocalePath(serverUrl, locale)
|
||||||
|
// It is the default locale, nothing to translate
|
||||||
|
if (!path) return Promise.resolve(undefined)
|
||||||
|
|
||||||
const completeLocale = getCompleteLocale(locale)
|
const completeLocale = getCompleteLocale(locale)
|
||||||
|
|
||||||
if (!is18nLocale(completeLocale) || isDefaultLocale(completeLocale)) return Promise.resolve(undefined)
|
return fetch(path + '/player.json')
|
||||||
|
|
||||||
return fetch(serverUrl + '/client/locales/' + completeLocale + '/player.json')
|
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(json => videojs.addLanguage(getShortLocale(completeLocale), json))
|
.then(json => videojs.addLanguage(getShortLocale(completeLocale), json))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getServerTranslations (serverUrl: string, locale: string) {
|
||||||
|
const path = getLocalePath(serverUrl, locale)
|
||||||
|
// It is the default locale, nothing to translate
|
||||||
|
if (!path) return Promise.resolve(undefined)
|
||||||
|
|
||||||
|
return fetch(path + '/server.json')
|
||||||
|
.then(res => res.json())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ############################################################################
|
||||||
|
|
||||||
export {
|
export {
|
||||||
loadLocale,
|
getServerTranslations,
|
||||||
|
loadLocaleInVideoJS,
|
||||||
getVideojsOptions,
|
getVideojsOptions,
|
||||||
addContextMenu
|
addContextMenu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ############################################################################
|
||||||
|
|
||||||
|
function getLocalePath (serverUrl: string, locale: string) {
|
||||||
|
const completeLocale = getCompleteLocale(locale)
|
||||||
|
|
||||||
|
if (!is18nLocale(completeLocale) || isDefaultLocale(completeLocale)) return undefined
|
||||||
|
|
||||||
|
return serverUrl + '/client/locales/' + completeLocale
|
||||||
|
}
|
||||||
|
|
|
@ -20,8 +20,8 @@ import 'whatwg-fetch'
|
||||||
import * as vjs from 'video.js'
|
import * as vjs from 'video.js'
|
||||||
import * as Channel from 'jschannel'
|
import * as Channel from 'jschannel'
|
||||||
|
|
||||||
import { ResultList, VideoDetails } from '../../../../shared'
|
import { peertubeTranslate, ResultList, VideoDetails } from '../../../../shared'
|
||||||
import { addContextMenu, getVideojsOptions, loadLocale } from '../../assets/player/peertube-player'
|
import { addContextMenu, getServerTranslations, getVideojsOptions, loadLocaleInVideoJS } from '../../assets/player/peertube-player'
|
||||||
import { PeerTubeResolution } from '../player/definitions'
|
import { PeerTubeResolution } from '../player/definitions'
|
||||||
import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
|
import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
|
||||||
import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model'
|
import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model'
|
||||||
|
@ -257,8 +257,9 @@ class PeerTubeEmbed {
|
||||||
const lastPart = urlParts[ urlParts.length - 1 ]
|
const lastPart = urlParts[ urlParts.length - 1 ]
|
||||||
const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[ 0 ]
|
const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[ 0 ]
|
||||||
|
|
||||||
await loadLocale(window.location.origin, vjs, navigator.language)
|
const [ , serverTranslations, videoResponse, captionsResponse ] = await Promise.all([
|
||||||
const [ videoResponse, captionsResponse ] = await Promise.all([
|
loadLocaleInVideoJS(window.location.origin, vjs, navigator.language),
|
||||||
|
getServerTranslations(window.location.origin, navigator.language),
|
||||||
this.loadVideoInfo(videoId),
|
this.loadVideoInfo(videoId),
|
||||||
this.loadVideoCaptions(videoId)
|
this.loadVideoCaptions(videoId)
|
||||||
])
|
])
|
||||||
|
@ -274,7 +275,7 @@ class PeerTubeEmbed {
|
||||||
if (captionsResponse.ok) {
|
if (captionsResponse.ok) {
|
||||||
const { data } = (await captionsResponse.json()) as ResultList<VideoCaption>
|
const { data } = (await captionsResponse.json()) as ResultList<VideoCaption>
|
||||||
videoCaptions = data.map(c => ({
|
videoCaptions = data.map(c => ({
|
||||||
label: c.language.label,
|
label: peertubeTranslate(c.language.label, serverTranslations),
|
||||||
language: c.language.id,
|
language: c.language.id,
|
||||||
src: window.location.origin + c.captionPath
|
src: window.location.origin + c.captionPath
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -30,8 +30,12 @@ post_build_hook
|
||||||
|
|
||||||
# Don't build other languages if --light arg is provided
|
# Don't build other languages if --light arg is provided
|
||||||
if [ -z ${1+x} ] || [ "$1" != "--light" ]; then
|
if [ -z ${1+x} ] || [ "$1" != "--light" ]; then
|
||||||
# Supported languages
|
if [ ! -z ${1+x} ] && [ "$1" == "--light-fr" ]; then
|
||||||
languages=("fr_FR" "eu_ES" "ca_ES" "cs_CZ" "eo" "zh_Hant_TW" "de_DE" "es_ES" "oc")
|
languages=("fr_FR")
|
||||||
|
else
|
||||||
|
# Supported languages
|
||||||
|
languages=("fr_FR" "eu_ES" "ca_ES" "cs_CZ" "eo" "zh_Hant_TW" "de_DE" "es_ES" "oc")
|
||||||
|
fi
|
||||||
|
|
||||||
for lang in "${languages[@]}"; do
|
for lang in "${languages[@]}"; do
|
||||||
# TODO: remove when the project will use runtime translations
|
# TODO: remove when the project will use runtime translations
|
||||||
|
|
|
@ -36,6 +36,10 @@ export function isDefaultLocale (locale: string) {
|
||||||
return getCompleteLocale(locale) === getCompleteLocale(getDefaultLocale())
|
return getCompleteLocale(locale) === getCompleteLocale(getDefaultLocale())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function peertubeTranslate (str: string, translations?: { [ id: string ]: string }) {
|
||||||
|
return translations && translations[str] ? translations[str] : str
|
||||||
|
}
|
||||||
|
|
||||||
const possiblePaths = POSSIBLE_LOCALES.map(l => '/' + l)
|
const possiblePaths = POSSIBLE_LOCALES.map(l => '/' + l)
|
||||||
export function is18nPath (path: string) {
|
export function is18nPath (path: string) {
|
||||||
return possiblePaths.indexOf(path) !== -1
|
return possiblePaths.indexOf(path) !== -1
|
||||||
|
|
Loading…
Reference in a new issue