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 23d74494c..d3e16c4cf 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/videos/+video-watch/video-watch.component.ts
@@ -1,5 +1,5 @@
import { catchError } from 'rxjs/operators'
-import { Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core'
+import { Component, ElementRef, LOCALE_ID, NgZone, OnDestroy, OnInit, ViewChild, Inject } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'
import { RedirectService } from '@app/core/routing/redirect.service'
import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage'
@@ -21,9 +21,10 @@ import { MarkdownService } from '../shared'
import { VideoDownloadComponent } from './modal/video-download.component'
import { VideoReportComponent } from './modal/video-report.component'
import { VideoShareComponent } from './modal/video-share.component'
-import { getVideojsOptions } from '../../../assets/player/peertube-player'
+import { getVideojsOptions, loadLocale, addContextMenu } from '../../../assets/player/peertube-player'
import { ServerService } from '@app/core'
import { I18n } from '@ngx-translate/i18n-polyfill'
+import { environment } from '../../../environments/environment'
@Component({
selector: 'my-video-watch',
@@ -54,6 +55,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
likesBarTooltipText = ''
hasAlreadyAcceptedPrivacyConcern = false
+ private videojsLocaleLoaded = false
private otherVideos: Video[] = []
private paramsSub: Subscription
@@ -72,7 +74,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
private markdownService: MarkdownService,
private zone: NgZone,
private redirectService: RedirectService,
- private i18n: I18n
+ private i18n: I18n,
+ @Inject(LOCALE_ID) private localeId: string
) {}
get user () {
@@ -365,7 +368,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
inactivityTimeout: 2500,
videoFiles: this.video.files,
playerElement: this.playerElement,
- videoEmbedUrl: this.video.embedUrl,
videoViewUrl: this.videoService.getVideoViewUrl(this.video.uuid),
videoDuration: this.video.duration,
enableHotkeys: true,
@@ -374,11 +376,18 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
startTime
})
+ if (this.videojsLocaleLoaded === false) {
+ await loadLocale(environment.apiUrl, videojs, environment.production === true ? this.localeId : 'fr')
+ this.videojsLocaleLoaded = true
+ }
+
const self = this
- this.zone.runOutsideAngular(() => {
+ this.zone.runOutsideAngular(async () => {
videojs(this.playerElement, videojsOptions, function () {
self.player = this
this.on('customError', (event, data) => self.handleError(data.err))
+
+ addContextMenu(self.player, self.video.embedUrl)
})
})
diff --git a/client/src/assets/player/peertube-link-button.ts b/client/src/assets/player/peertube-link-button.ts
index a13815d61..26f8b9d73 100644
--- a/client/src/assets/player/peertube-link-button.ts
+++ b/client/src/assets/player/peertube-link-button.ts
@@ -24,7 +24,7 @@ class PeerTubeLinkButton extends Button {
const el = videojsUntyped.dom.createEl('a', {
href: buildVideoLink(),
innerHTML: 'PeerTube',
- title: 'Go to the video page',
+ title: this.player_.localize('Go to the video page'),
className: 'vjs-peertube-link',
target: '_blank'
})
diff --git a/client/src/assets/player/peertube-player.ts b/client/src/assets/player/peertube-player.ts
index d204b9703..b604097fa 100644
--- a/client/src/assets/player/peertube-player.ts
+++ b/client/src/assets/player/peertube-player.ts
@@ -12,6 +12,7 @@ import './peertube-videojs-plugin'
import './peertube-load-progress-bar'
import { videojsUntyped } from './peertube-videojs-typings'
import { buildVideoEmbed, buildVideoLink, copyToClipboard } from './utils'
+import { is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n'
// Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed'
@@ -20,7 +21,6 @@ function getVideojsOptions (options: {
autoplay: boolean,
playerElement: HTMLVideoElement,
videoViewUrl: string,
- videoEmbedUrl: string,
videoDuration: number,
videoFiles: VideoFile[],
enableHotkeys: boolean,
@@ -43,29 +43,6 @@ function getVideojsOptions (options: {
videoViewUrl: options.videoViewUrl,
videoDuration: options.videoDuration,
startTime: options.startTime
- },
- contextmenuUI: {
- content: [
- {
- label: 'Copy the video URL',
- listener: function () {
- copyToClipboard(buildVideoLink())
- }
- },
- {
- label: 'Copy the video URL at the current time',
- listener: function () {
- const player = this
- copyToClipboard(buildVideoLink(player.currentTime()))
- }
- },
- {
- label: 'Copy embed code',
- listener: () => {
- copyToClipboard(buildVideoEmbed(options.videoEmbedUrl))
- }
- }
- ]
}
},
controlBar: {
@@ -135,4 +112,44 @@ function getControlBarChildren (options: {
return children
}
-export { getVideojsOptions }
+function addContextMenu (player: any, videoEmbedUrl: string) {
+ console.log(videoEmbedUrl)
+
+ player.contextmenuUI({
+ content: [
+ {
+ label: player.localize('Copy the video URL'),
+ listener: function () {
+ copyToClipboard(buildVideoLink())
+ }
+ },
+ {
+ label: player.localize('Copy the video URL at the current time'),
+ listener: function () {
+ const player = this
+ copyToClipboard(buildVideoLink(player.currentTime()))
+ }
+ },
+ {
+ label: player.localize('Copy embed code'),
+ listener: () => {
+ copyToClipboard(buildVideoEmbed(videoEmbedUrl))
+ }
+ }
+ ]
+ })
+}
+
+function loadLocale (serverUrl: string, videojs: any, locale: string) {
+ if (!is18nLocale(locale) || isDefaultLocale(locale)) return undefined
+
+ return fetch(serverUrl + '/client/locales/' + locale + '/player.json')
+ .then(res => res.json())
+ .then(json => videojs.addLanguage(locale, json))
+}
+
+export {
+ loadLocale,
+ getVideojsOptions,
+ addContextMenu
+}
diff --git a/client/src/assets/player/peertube-videojs-plugin.ts b/client/src/assets/player/peertube-videojs-plugin.ts
index 79df42a53..68e98f170 100644
--- a/client/src/assets/player/peertube-videojs-plugin.ts
+++ b/client/src/assets/player/peertube-videojs-plugin.ts
@@ -4,15 +4,7 @@ import { VideoFile } from '../../../../shared/models/videos/video.model'
import { renderVideo } from './video-renderer'
import './settings-menu-button'
import { PeertubePluginOptions, VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings'
-import {
- getAverageBandwidth,
- getStoredMute,
- getStoredVolume,
- isMobile,
- saveAverageBandwidth,
- saveMuteInStore,
- saveVolumeInStore
-} from './utils'
+import { getAverageBandwidth, getStoredMute, getStoredVolume, saveAverageBandwidth, saveMuteInStore, saveVolumeInStore } from './utils'
import minBy from 'lodash-es/minBy'
import maxBy from 'lodash-es/maxBy'
import * as CacheChunkStore from 'cache-chunk-store'
diff --git a/client/src/assets/player/resolution-menu-button.ts b/client/src/assets/player/resolution-menu-button.ts
index 2efc8de69..d317a5efc 100644
--- a/client/src/assets/player/resolution-menu-button.ts
+++ b/client/src/assets/player/resolution-menu-button.ts
@@ -8,10 +8,7 @@ class ResolutionMenuButton extends MenuButton {
label: HTMLElement
constructor (player: videojs.Player, options) {
- options.label = 'Quality'
super(player, options)
-
- this.controlText_ = 'Quality'
this.player = player
player.peertube().on('videoFileUpdate', () => this.updateLabel())
@@ -51,7 +48,7 @@ class ResolutionMenuButton extends MenuButton {
this.player_,
{
id: -1,
- label: 'Auto',
+ label: this.player_.localize('Auto'),
src: null
}
))
@@ -77,4 +74,6 @@ class ResolutionMenuButton extends MenuButton {
return this.player_.peertube().getCurrentResolutionLabel()
}
}
+ResolutionMenuButton.prototype.controlText_ = 'Quality'
+
MenuButton.registerComponent('ResolutionMenuButton', ResolutionMenuButton)
diff --git a/client/src/assets/player/settings-menu-button.ts b/client/src/assets/player/settings-menu-button.ts
index bf6ac145a..b51c52506 100644
--- a/client/src/assets/player/settings-menu-button.ts
+++ b/client/src/assets/player/settings-menu-button.ts
@@ -275,7 +275,7 @@ class SettingsDialog extends Component {
}
-SettingsButton.prototype.controlText_ = 'Settings Button'
+SettingsButton.prototype.controlText_ = 'Settings'
Component.registerComponent('SettingsButton', SettingsButton)
Component.registerComponent('SettingsDialog', SettingsDialog)
diff --git a/client/src/assets/player/settings-menu-item.ts b/client/src/assets/player/settings-menu-item.ts
index 048c88533..f595fd459 100644
--- a/client/src/assets/player/settings-menu-item.ts
+++ b/client/src/assets/player/settings-menu-item.ts
@@ -132,7 +132,7 @@ class SettingsMenuItem extends MenuItem {
const button = this.subMenu.menu.addChild('MenuItem', {}, 0)
button.name_ = 'BackButton'
button.addClass('vjs-back-button')
- button.el_.innerHTML = this.subMenu.controlText_
+ button.el_.innerHTML = this.player_.localize(this.subMenu.controlText_)
}
/**
@@ -201,7 +201,7 @@ class SettingsMenuItem extends MenuItem {
saveUpdateLabel.call(this.subMenu)
}
- this.settingsSubMenuTitleEl_.innerHTML = this.subMenu.controlText_
+ this.settingsSubMenuTitleEl_.innerHTML = this.player_.localize(this.subMenu.controlText_)
this.settingsSubMenuEl_.appendChild(this.subMenu.menu.el_)
this.panelChildEl.appendChild(this.settingsSubMenuEl_)
this.update()
diff --git a/client/src/assets/player/utils.ts b/client/src/assets/player/utils.ts
index 487b3a1be..ce7aaea2a 100644
--- a/client/src/assets/player/utils.ts
+++ b/client/src/assets/player/utils.ts
@@ -1,3 +1,5 @@
+import { is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n'
+
function toTitleCase (str: string) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
diff --git a/client/src/assets/player/webtorrent-info-button.ts b/client/src/assets/player/webtorrent-info-button.ts
index baeb22b64..10945c665 100644
--- a/client/src/assets/player/webtorrent-info-button.ts
+++ b/client/src/assets/player/webtorrent-info-button.ts
@@ -60,13 +60,8 @@ class WebtorrentInfoButton extends Button {
className: 'peers-number',
textContent: 'HTTP'
})
- const subDivFallbackText = videojsUntyped.dom.createEl('span', {
- className: 'peers-text',
- textContent: ' fallback'
- })
subDivHttp.appendChild(subDivHttpText)
- subDivHttp.appendChild(subDivFallbackText)
div.appendChild(subDivHttp)
this.player_.peertube().on('torrentInfo', (event, data) => {
@@ -89,7 +84,7 @@ class WebtorrentInfoButton extends Button {
uploadSpeedUnit.textContent = ' ' + uploadSpeed[ 1 ]
peersNumber.textContent = numPeers
- peersText.textContent = ' peers'
+ peersText.textContent = ' ' + this.player_.localize('peers')
subDivHttp.className = 'vjs-peertube-hidden'
subDivWebtorrent.className = 'vjs-peertube-displayed'
diff --git a/client/src/locale/source/player_en_US.xml b/client/src/locale/source/player_en_US.xml
new file mode 100644
index 000000000..5bb6afdf7
--- /dev/null
+++ b/client/src/locale/source/player_en_US.xml
@@ -0,0 +1,378 @@
+
+
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+ undefined
+
+
+
+
\ No newline at end of file
diff --git a/client/src/locale/source/videojs_en_US.json b/client/src/locale/source/videojs_en_US.json
new file mode 100644
index 000000000..92caaa683
--- /dev/null
+++ b/client/src/locale/source/videojs_en_US.json
@@ -0,0 +1,85 @@
+{
+ "Audio Player": "Audio Player",
+ "Video Player": "Video Player",
+ "Play": "Play",
+ "Pause": "Pause",
+ "Replay": "Replay",
+ "Current Time": "Current Time",
+ "Duration": "Duration",
+ "Remaining Time": "Remaining Time",
+ "Stream Type": "Stream Type",
+ "LIVE": "LIVE",
+ "Loaded": "Loaded",
+ "Progress": "Progress",
+ "Progress Bar": "Progress Bar",
+ "progress bar timing: currentTime={1} duration={2}": "{1} of {2}",
+ "Fullscreen": "Fullscreen",
+ "Non-Fullscreen": "Non-Fullscreen",
+ "Mute": "Mute",
+ "Unmute": "Unmute",
+ "Playback Rate": "Playback Rate",
+ "Subtitles": "Subtitles",
+ "subtitles off": "subtitles off",
+ "Captions": "Captions",
+ "captions off": "captions off",
+ "Chapters": "Chapters",
+ "Descriptions": "Descriptions",
+ "descriptions off": "descriptions off",
+ "Audio Track": "Audio Track",
+ "Volume Level": "Volume Level",
+ "You aborted the media playback": "You aborted the media playback",
+ "A network error caused the media download to fail part-way.": "A network error caused the media download to fail part-way.",
+ "The media could not be loaded, either because the server or network failed or because the format is not supported.": "The media could not be loaded, either because the server or network failed or because the format is not supported.",
+ "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.",
+ "No compatible source was found for this media.": "No compatible source was found for this media.",
+ "The media is encrypted and we do not have the keys to decrypt it.": "The media is encrypted and we do not have the keys to decrypt it.",
+ "Play Video": "Play Video",
+ "Close": "Close",
+ "Close Modal Dialog": "Close Modal Dialog",
+ "Modal Window": "Modal Window",
+ "This is a modal window": "This is a modal window",
+ "This modal can be closed by pressing the Escape key or activating the close button.": "This modal can be closed by pressing the Escape key or activating the close button.",
+ ", opens captions settings dialog": ", opens captions settings dialog",
+ ", opens subtitles settings dialog": ", opens subtitles settings dialog",
+ ", opens descriptions settings dialog": ", opens descriptions settings dialog",
+ ", selected": ", selected",
+ "captions settings": "captions settings",
+ "subtitles settings": "subititles settings",
+ "descriptions settings": "descriptions settings",
+ "Text": "Text",
+ "White": "White",
+ "Black": "Black",
+ "Red": "Red",
+ "Green": "Green",
+ "Blue": "Blue",
+ "Yellow": "Yellow",
+ "Magenta": "Magenta",
+ "Cyan": "Cyan",
+ "Background": "Background",
+ "Window": "Window",
+ "Transparent": "Transparent",
+ "Semi-Transparent": "Semi-Transparent",
+ "Opaque": "Opaque",
+ "Font Size": "Font Size",
+ "Text Edge Style": "Text Edge Style",
+ "None": "None",
+ "Raised": "Raised",
+ "Depressed": "Depressed",
+ "Uniform": "Uniform",
+ "Dropshadow": "Dropshadow",
+ "Font Family": "Font Family",
+ "Proportional Sans-Serif": "Proportional Sans-Serif",
+ "Monospace Sans-Serif": "Monospace Sans-Serif",
+ "Proportional Serif": "Proportional Serif",
+ "Monospace Serif": "Monospace Serif",
+ "Casual": "Casual",
+ "Script": "Script",
+ "Small Caps": "Small Caps",
+ "Reset": "Reset",
+ "restore all settings to the default values": "restore all settings to the default values",
+ "Done": "Done",
+ "Caption Settings Dialog": "Caption Settings Dialog",
+ "Beginning of dialog window. Escape will cancel and close the window.": "Beginning of dialog window. Escape will cancel and close the window.",
+ "End of dialog window.": "End of dialog window.",
+ "{1} is loading.": "{1} is loading."
+}
diff --git a/client/src/locale/target/player_fr.json b/client/src/locale/target/player_fr.json
new file mode 100644
index 000000000..6c399fc3f
--- /dev/null
+++ b/client/src/locale/target/player_fr.json
@@ -0,0 +1 @@
+{"Audio Player":"Lecteur audio","Video Player":"Lecteur vidéo","Play":"Lecture","Pause":"Pause","Replay":"Revoir","Current Time":"Temps actuel","Duration":"Durée","Remaining Time":"Temps restant","Stream Type":"Type de flux","LIVE":"EN DIRECT","Loaded":"Chargé","Progress":"Progression","Progress Bar":"Barre de progression","progress bar timing: currentTime={1} duration={2}":"{1} de {2}","Fullscreen":"Plein écran","Non-Fullscreen":"Fenêtré","Mute":"Sourdine","Unmute":"Son activé","Playback Rate":"Vitesse de lecture","Subtitles":"Sous-titres","subtitles off":"Sous-titres désactivés","Captions":"Sous-titres transcrits","captions off":"Sous-titres transcrits désactivés","Chapters":"Chapitres","Descriptions":"Descriptions","descriptions off":"descriptions désactivées","Audio Track":"Piste audio","Volume Level":"Niveau de volume","You aborted the media playback":"Vous avez interrompu la lecture de la vidéo.","A network error caused the media download to fail part-way.":"Une erreur de réseau a interrompu le téléchargement de la vidéo.","The media could not be loaded, either because the server or network failed or because the format is not supported.":"Cette vidéo n'a pas pu être chargée, soit parce que le serveur ou le réseau a échoué ou parce que le format n'est pas reconnu.","The media playback was aborted due to a corruption problem or because the media used features your browser did not support.":"La lecture de la vidéo a été interrompue à cause d'un problème de corruption ou parce que la vidéo utilise des fonctionnalités non prises en charge par votre navigateur.","No compatible source was found for this media.":"Aucune source compatible n'a été trouvée pour cette vidéo.","The media is encrypted and we do not have the keys to decrypt it.":"Le média est chiffré et nous n'avons pas les clés pour le déchiffrer.","Play Video":"Lire la vidéo","Close":"Fermer","Close Modal Dialog":"Fermer la boîte de dialogue modale","Modal Window":"Fenêtre modale","This is a modal window":"Ceci est une fenêtre modale","This modal can be closed by pressing the Escape key or activating the close button.":"Ce modal peut être fermé en appuyant sur la touche Échap ou activer le bouton de fermeture.",", opens captions settings dialog":", ouvrir les paramètres des sous-titres transcrits",", opens subtitles settings dialog":", ouvrir les paramètres des sous-titres",", opens descriptions settings dialog":", ouvrir les paramètres des descriptions",", selected":", sélectionné","captions settings":"Paramètres des sous-titres transcrits","subtitles settings":"Paramètres des sous-titres","descriptions settings":"Paramètres des descriptions","Text":"Texte","White":"Blanc","Black":"Noir","Red":"Rouge","Green":"Vert","Blue":"Bleu","Yellow":"Jaune","Magenta":"Magenta","Cyan":"Cyan","Background":"Arrière-plan","Window":"Fenêtre","Transparent":"Transparent","Semi-Transparent":"Semi-transparent","Opaque":"Opaque","Font Size":"Taille des caractères","Text Edge Style":"Style des contours du texte","None":"Aucun","Raised":"Élevé","Depressed":"Enfoncé","Uniform":"Uniforme","Dropshadow":"Ombre portée","Font Family":"Familles de polices","Proportional Sans-Serif":"Polices à chasse variable sans empattement (Proportional Sans-Serif)","Monospace Sans-Serif":"Polices à chasse fixe sans empattement (Monospace Sans-Serif)","Proportional Serif":"Polices à chasse variable avec empattement (Proportional Serif)","Monospace Serif":"Polices à chasse fixe avec empattement (Monospace Serif)","Casual":"Manuscrite","Script":"Scripte","Small Caps":"Petites capitales","Reset":"Réinitialiser","restore all settings to the default values":"Restaurer tous les paramètres aux valeurs par défaut","Done":"Terminé","Caption Settings Dialog":"Boîte de dialogue des paramètres des sous-titres transcrits","Beginning of dialog window. Escape will cancel and close the window.":"Début de la fenêtre de dialogue. La touche d'échappement annulera et fermera la fenêtre.","End of dialog window.":"Fin de la fenêtre de dialogue.","{1} is loading.":"{1} est en train de charger","Quality":"Qualité","Auto":"Auto","Speed":"Vitesse","peers":"pairs","Go to the video page":"Aller sur la page de la vidéo","Settings":"Paramètres","Uses P2P, others may know you are watching this video.":"Utilise le P2P, d'autres personnes pourraient savoir que vous regardez cette vidéo.","Copy the video URL":"Copier le lien de la vidéo","Copy the video URL at the current time":"Copier le lien de la vidéo à partir de cette séquence","Copy embed code":"Copier le code d'intégration"}
\ No newline at end of file
diff --git a/client/src/locale/target/player_fr.xml b/client/src/locale/target/player_fr.xml
new file mode 100644
index 000000000..eafa4baff
--- /dev/null
+++ b/client/src/locale/target/player_fr.xml
@@ -0,0 +1,379 @@
+
+
+
+
+
+
+
+ Lecteur audio
+
+
+
+ Lecteur vidéo
+
+
+
+ Lecture
+
+
+
+ Pause
+
+
+
+ Revoir
+
+
+
+ Temps actuel
+
+
+
+ Durée
+
+
+
+ Temps restant
+
+
+
+ Type de flux
+
+
+
+ EN DIRECT
+
+
+
+ Chargé
+
+
+
+ Progression
+
+
+
+ Barre de progression
+
+
+
+ {1} de {2}
+
+
+
+ Plein écran
+
+
+
+ Fenêtré
+
+
+
+ Sourdine
+
+
+
+ Son activé
+
+
+
+ Vitesse de lecture
+
+
+
+ Sous-titres
+
+
+
+ Sous-titres désactivés
+
+
+
+ Sous-titres transcrits
+
+
+
+ Sous-titres transcrits désactivés
+
+
+
+ Chapitres
+
+
+
+ Descriptions
+
+
+
+ descriptions désactivées
+
+
+
+ Piste audio
+
+
+
+ Niveau de volume
+
+
+
+ Vous avez interrompu la lecture de la vidéo.
+
+
+
+ Une erreur de réseau a interrompu le téléchargement de la vidéo.
+
+
+
+ Cette vidéo n'a pas pu être chargée, soit parce que le serveur ou le réseau a échoué ou parce que le format n'est pas reconnu.
+
+
+
+ La lecture de la vidéo a été interrompue à cause d'un problème de corruption ou parce que la vidéo utilise des fonctionnalités non prises en charge par votre navigateur.
+
+
+
+ Aucune source compatible n'a été trouvée pour cette vidéo.
+
+
+
+ Le média est chiffré et nous n'avons pas les clés pour le déchiffrer.
+
+
+
+ Lire la vidéo
+
+
+
+ Fermer
+
+
+
+ Fermer la boîte de dialogue modale
+
+
+
+ Fenêtre modale
+
+
+
+ Ceci est une fenêtre modale
+
+
+
+ Ce modal peut être fermé en appuyant sur la touche Échap ou activer le bouton de fermeture.
+
+
+
+ , ouvrir les paramètres des sous-titres transcrits
+
+
+
+ , ouvrir les paramètres des sous-titres
+
+
+
+ , ouvrir les paramètres des descriptions
+
+
+
+ , sélectionné
+
+
+
+ Paramètres des sous-titres transcrits
+
+
+
+ Paramètres des sous-titres
+
+
+
+ Paramètres des descriptions
+
+
+
+ Texte
+
+
+
+ Blanc
+
+
+
+ Noir
+
+
+
+ Rouge
+
+
+
+ Vert
+
+
+
+ Bleu
+
+
+
+ Jaune
+
+
+
+ Magenta
+
+
+
+ Cyan
+
+
+
+ Arrière-plan
+
+
+
+ Fenêtre
+
+
+
+ Transparent
+
+
+
+ Semi-transparent
+
+
+
+ Opaque
+
+
+
+ Taille des caractères
+
+
+
+ Style des contours du texte
+
+
+
+ Aucun
+
+
+
+ Élevé
+
+
+
+ Enfoncé
+
+
+
+ Uniforme
+
+
+
+ Ombre portée
+
+
+
+ Familles de polices
+
+
+
+ Polices à chasse variable sans empattement (Proportional Sans-Serif)
+
+
+
+ Polices à chasse fixe sans empattement (Monospace Sans-Serif)
+
+
+
+ Polices à chasse variable avec empattement (Proportional Serif)
+
+
+
+ Polices à chasse fixe avec empattement (Monospace Serif)
+
+
+
+ Manuscrite
+
+
+
+ Scripte
+
+
+
+ Petites capitales
+
+
+
+ Réinitialiser
+
+
+
+ Restaurer tous les paramètres aux valeurs par défaut
+
+
+
+ Terminé
+
+
+
+ Boîte de dialogue des paramètres des sous-titres transcrits
+
+
+
+ Début de la fenêtre de dialogue. La touche d'échappement annulera et fermera la fenêtre.
+
+
+
+ Fin de la fenêtre de dialogue.
+
+
+
+ {1} est en train de charger
+
+
+
+ Qualité
+
+
+
+ Auto
+
+
+
+ Vitesse
+
+
+
+ pairs
+
+
+
+ Aller sur la page de la vidéo
+
+
+
+ Paramètres
+
+
+
+ Utilise le P2P, d'autres personnes pourraient savoir que vous regardez cette vidéo.
+
+
+
+ Copier le lien de la vidéo
+
+
+
+ Copier le lien de la vidéo à partir de cette séquence
+
+
+
+ Copier le code d'intégration
+
+
+
\ No newline at end of file
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts
index d603690ca..166013226 100644
--- a/client/src/standalone/videos/embed.ts
+++ b/client/src/standalone/videos/embed.ts
@@ -14,14 +14,14 @@ import 'core-js/es6/regexp'
import 'core-js/es6/map'
import 'core-js/es6/weak-map'
import 'core-js/es6/set'
-
// For google bot that uses Chrome 41 and does not understand fetch
import 'whatwg-fetch'
import * as videojs from 'video.js'
import { VideoDetails } from '../../../../shared'
-import { getVideojsOptions } from '../../assets/player/peertube-player'
+import { addContextMenu, getVideojsOptions, loadLocale } from '../../assets/player/peertube-player'
+import { environment } from '../../environments/environment'
function getVideoUrl (id: string) {
return window.location.origin + '/api/v1/videos/' + id
@@ -61,7 +61,8 @@ function videoFetchError (videoElement: HTMLVideoElement) {
const urlParts = window.location.href.split('/')
const videoId = urlParts[urlParts.length - 1]
-loadVideoInfo(videoId)
+loadLocale(environment.apiUrl, videojs, navigator.language)
+ .then(() => loadVideoInfo(videoId))
.then(async response => {
const videoContainerId = 'video-container'
const videoElement = document.getElementById(videoContainerId) as HTMLVideoElement
@@ -91,7 +92,6 @@ loadVideoInfo(videoId)
const videojsOptions = getVideojsOptions({
autoplay,
inactivityTimeout: 1500,
- videoEmbedUrl: window.location.origin + videoInfo.embedPath,
videoViewUrl: getVideoUrl(videoId) + '/views',
playerElement: videoElement,
videoFiles: videoInfo.files,
@@ -106,8 +106,10 @@ loadVideoInfo(videoId)
player.dock({
title: videoInfo.name,
- description: 'Uses P2P, others may know you are watching this video.'
+ description: player.localize('Uses P2P, others may know you are watching this video.')
})
+
+ addContextMenu(player, window.location.origin + videoInfo.embedPath)
})
})
.catch(err => console.error(err))
diff --git a/package.json b/package.json
index 21701e664..707579af3 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,8 @@
"danger:clean:prod": "scripty",
"danger:clean:modules": "scripty",
"i18n:generate": "scripty",
+ "i18n:xliff2json": "node ./dist/scripts/i18n/xliff2json.js",
+ "i18n:create-custom-files": "node ./dist/scripts/i18n/create-custom-files.js",
"reset-password": "node ./dist/scripts/reset-password.js",
"play": "scripty",
"dev": "scripty",
@@ -174,6 +176,7 @@
"tslint-config-standard": "^7.0.0",
"typescript": "^2.5.2",
"webtorrent": "^0.100.0",
+ "xliff": "^3.0.1",
"youtube-dl": "^1.12.2"
},
"scripty": {
diff --git a/scripts/i18n/create-custom-files.ts b/scripts/i18n/create-custom-files.ts
new file mode 100755
index 000000000..3895b3b9d
--- /dev/null
+++ b/scripts/i18n/create-custom-files.ts
@@ -0,0 +1,49 @@
+import * as jsToXliff12 from 'xliff/jsToXliff12'
+import { writeFile } from 'fs'
+import { join } from 'path'
+
+// First, the player
+const playerSource = join(__dirname, '../../../client/src/locale/source/videojs_en_US.json')
+const playerTarget = join(__dirname, '../../../client/src/locale/source/player_en_US.xml')
+
+const videojs = require(playerSource)
+const playerKeys = {
+ 'Quality': 'Quality',
+ 'Auto': 'Auto',
+ 'Speed': 'Speed',
+ 'peers': 'peers',
+ 'Go to the video page': 'Go to the video page',
+ 'Settings': 'Settings',
+ 'Uses P2P, others may know you are watching this video.': 'Uses P2P, others may know you are watching this video.',
+ 'Copy the video URL': 'Copy the video URL',
+ 'Copy the video URL at the current time': 'Copy the video URL at the current time',
+ 'Copy embed code': 'Copy embed code'
+}
+
+const obj = {
+ resources: {
+ namespace1: {}
+ }
+}
+
+for (const sourceObject of [ videojs, playerKeys ]) {
+ Object.keys(sourceObject).forEach(k => obj.resources.namespace1[ k ] = { source: sourceObject[ k ] })
+}
+
+jsToXliff12(obj, (err, res) => {
+ if (err) {
+ console.error(err)
+ process.exit(-1)
+ }
+
+ writeFile(playerTarget, res, err => {
+ if (err) {
+ console.error(err)
+ process.exit(-1)
+ }
+
+ process.exit(0)
+ })
+})
+
+// Then, the server strings
diff --git a/scripts/i18n/generate.sh b/scripts/i18n/generate.sh
index f8ad8a3c1..0a5b6dee1 100755
--- a/scripts/i18n/generate.sh
+++ b/scripts/i18n/generate.sh
@@ -9,4 +9,8 @@ npm run ngx-extractor -- --locale "en-US" -i 'src/**/*.ts' -f xlf -o src/locale/
# Zanata does not support inner elements in