Localize player

This commit is contained in:
Chocobozzz 2018-06-06 14:23:40 +02:00
parent 550a562cec
commit e945b184a0
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
23 changed files with 1049 additions and 61 deletions

View File

@ -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)
})
})

View File

@ -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'
})

View File

@ -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
}

View File

@ -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'

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -1,3 +1,5 @@
import { is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n'
function toTitleCase (str: string) {
return str.charAt(0).toUpperCase() + str.slice(1)
}

View File

@ -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'

View File

@ -0,0 +1,378 @@
<xliff xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd" xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file original="namespace1" datatype="plaintext" source-language="undefined" target-language="undefined">
<body>
<trans-unit id="Audio Player">
<source>Audio Player</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Video Player">
<source>Video Player</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Play">
<source>Play</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Pause">
<source>Pause</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Replay">
<source>Replay</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Current Time">
<source>Current Time</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Duration">
<source>Duration</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Remaining Time">
<source>Remaining Time</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Stream Type">
<source>Stream Type</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="LIVE">
<source>LIVE</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Loaded">
<source>Loaded</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Progress">
<source>Progress</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Progress Bar">
<source>Progress Bar</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="progress bar timing: currentTime={1} duration={2}">
<source>{1} of {2}</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Fullscreen">
<source>Fullscreen</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Non-Fullscreen">
<source>Non-Fullscreen</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Mute">
<source>Mute</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Unmute">
<source>Unmute</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Playback Rate">
<source>Playback Rate</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Subtitles">
<source>Subtitles</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="subtitles off">
<source>subtitles off</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Captions">
<source>Captions</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="captions off">
<source>captions off</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Chapters">
<source>Chapters</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Descriptions">
<source>Descriptions</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="descriptions off">
<source>descriptions off</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Audio Track">
<source>Audio Track</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Volume Level">
<source>Volume Level</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="You aborted the media playback">
<source>You aborted the media playback</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="A network error caused the media download to fail part-way.">
<source>A network error caused the media download to fail part-way.</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="The media could not be loaded, either because the server or network failed or because the format is not supported.">
<source>The media could not be loaded, either because the server or network failed or because the format is not supported.</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="The media playback was aborted due to a corruption problem or because the media used features your browser did not support.">
<source>The media playback was aborted due to a corruption problem or because the media used features your browser did not support.</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="No compatible source was found for this media.">
<source>No compatible source was found for this media.</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="The media is encrypted and we do not have the keys to decrypt it.">
<source>The media is encrypted and we do not have the keys to decrypt it.</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Play Video">
<source>Play Video</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Close">
<source>Close</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Close Modal Dialog">
<source>Close Modal Dialog</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Modal Window">
<source>Modal Window</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="This is a modal window">
<source>This is a modal window</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="This modal can be closed by pressing the Escape key or activating the close button.">
<source>This modal can be closed by pressing the Escape key or activating the close button.</source>
<target>undefined</target>
</trans-unit>
<trans-unit id=", opens captions settings dialog">
<source>, opens captions settings dialog</source>
<target>undefined</target>
</trans-unit>
<trans-unit id=", opens subtitles settings dialog">
<source>, opens subtitles settings dialog</source>
<target>undefined</target>
</trans-unit>
<trans-unit id=", opens descriptions settings dialog">
<source>, opens descriptions settings dialog</source>
<target>undefined</target>
</trans-unit>
<trans-unit id=", selected">
<source>, selected</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="captions settings">
<source>captions settings</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="subtitles settings">
<source>subititles settings</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="descriptions settings">
<source>descriptions settings</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Text">
<source>Text</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="White">
<source>White</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Black">
<source>Black</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Red">
<source>Red</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Green">
<source>Green</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Blue">
<source>Blue</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Yellow">
<source>Yellow</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Magenta">
<source>Magenta</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Cyan">
<source>Cyan</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Background">
<source>Background</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Window">
<source>Window</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Transparent">
<source>Transparent</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Semi-Transparent">
<source>Semi-Transparent</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Opaque">
<source>Opaque</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Font Size">
<source>Font Size</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Text Edge Style">
<source>Text Edge Style</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="None">
<source>None</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Raised">
<source>Raised</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Depressed">
<source>Depressed</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Uniform">
<source>Uniform</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Dropshadow">
<source>Dropshadow</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Font Family">
<source>Font Family</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Proportional Sans-Serif">
<source>Proportional Sans-Serif</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Monospace Sans-Serif">
<source>Monospace Sans-Serif</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Proportional Serif">
<source>Proportional Serif</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Monospace Serif">
<source>Monospace Serif</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Casual">
<source>Casual</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Script">
<source>Script</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Small Caps">
<source>Small Caps</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Reset">
<source>Reset</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="restore all settings to the default values">
<source>restore all settings to the default values</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Done">
<source>Done</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Caption Settings Dialog">
<source>Caption Settings Dialog</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Beginning of dialog window. Escape will cancel and close the window.">
<source>Beginning of dialog window. Escape will cancel and close the window.</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="End of dialog window.">
<source>End of dialog window.</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="{1} is loading.">
<source>{1} is loading.</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Quality">
<source>Quality</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Auto">
<source>Auto</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Speed">
<source>Speed</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="peers">
<source>peers</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Go to the video page">
<source>Go to the video page</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Settings">
<source>Settings</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Uses P2P, others may know you are watching this video.">
<source>Uses P2P, others may know you are watching this video.</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Copy the video URL">
<source>Copy the video URL</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Copy the video URL at the current time">
<source>Copy the video URL at the current time</source>
<target>undefined</target>
</trans-unit>
<trans-unit id="Copy embed code">
<source>Copy embed code</source>
<target>undefined</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -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."
}

View File

@ -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"}

View File

@ -0,0 +1,379 @@
<?xml version="1.0" encoding="utf-8"?>
<!--XLIFF document generated by Zanata. Visit http://zanata.org for more infomation.-->
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.1" xmlns:xyz="urn:appInfo:Items" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.1 http://www.oasis-open.org/committees/xliff/documents/xliff-core-1.1.xsd" version="1.1">
<file source-language="en-US" datatype="plaintext" original="" target-language="fr">
<body>
<trans-unit id="Audio Player">
<source>Audio Player</source>
<target>Lecteur audio</target>
</trans-unit>
<trans-unit id="Video Player">
<source>Video Player</source>
<target>Lecteur vidéo</target>
</trans-unit>
<trans-unit id="Play">
<source>Play</source>
<target>Lecture</target>
</trans-unit>
<trans-unit id="Pause">
<source>Pause</source>
<target>Pause</target>
</trans-unit>
<trans-unit id="Replay">
<source>Replay</source>
<target>Revoir</target>
</trans-unit>
<trans-unit id="Current Time">
<source>Current Time</source>
<target>Temps actuel</target>
</trans-unit>
<trans-unit id="Duration">
<source>Duration</source>
<target>Durée</target>
</trans-unit>
<trans-unit id="Remaining Time">
<source>Remaining Time</source>
<target>Temps restant</target>
</trans-unit>
<trans-unit id="Stream Type">
<source>Stream Type</source>
<target>Type de flux</target>
</trans-unit>
<trans-unit id="LIVE">
<source>LIVE</source>
<target>EN DIRECT</target>
</trans-unit>
<trans-unit id="Loaded">
<source>Loaded</source>
<target>Chargé</target>
</trans-unit>
<trans-unit id="Progress">
<source>Progress</source>
<target>Progression</target>
</trans-unit>
<trans-unit id="Progress Bar">
<source>Progress Bar</source>
<target>Barre de progression</target>
</trans-unit>
<trans-unit id="progress bar timing: currentTime={1} duration={2}">
<source>{1} of {2}</source>
<target>{1} de {2}</target>
</trans-unit>
<trans-unit id="Fullscreen">
<source>Fullscreen</source>
<target>Plein écran</target>
</trans-unit>
<trans-unit id="Non-Fullscreen">
<source>Non-Fullscreen</source>
<target>Fenêtré</target>
</trans-unit>
<trans-unit id="Mute">
<source>Mute</source>
<target>Sourdine</target>
</trans-unit>
<trans-unit id="Unmute">
<source>Unmute</source>
<target>Son activé</target>
</trans-unit>
<trans-unit id="Playback Rate">
<source>Playback Rate</source>
<target>Vitesse de lecture</target>
</trans-unit>
<trans-unit id="Subtitles">
<source>Subtitles</source>
<target>Sous-titres</target>
</trans-unit>
<trans-unit id="subtitles off">
<source>subtitles off</source>
<target>Sous-titres désactivés</target>
</trans-unit>
<trans-unit id="Captions">
<source>Captions</source>
<target>Sous-titres transcrits</target>
</trans-unit>
<trans-unit id="captions off">
<source>captions off</source>
<target>Sous-titres transcrits désactivés</target>
</trans-unit>
<trans-unit id="Chapters">
<source>Chapters</source>
<target>Chapitres</target>
</trans-unit>
<trans-unit id="Descriptions">
<source>Descriptions</source>
<target>Descriptions</target>
</trans-unit>
<trans-unit id="descriptions off">
<source>descriptions off</source>
<target>descriptions désactivées</target>
</trans-unit>
<trans-unit id="Audio Track">
<source>Audio Track</source>
<target>Piste audio</target>
</trans-unit>
<trans-unit id="Volume Level">
<source>Volume Level</source>
<target>Niveau de volume</target>
</trans-unit>
<trans-unit id="You aborted the media playback">
<source>You aborted the media playback</source>
<target>Vous avez interrompu la lecture de la vidéo.</target>
</trans-unit>
<trans-unit id="A network error caused the media download to fail part-way.">
<source>A network error caused the media download to fail part-way.</source>
<target>Une erreur de réseau a interrompu le téléchargement de la vidéo.</target>
</trans-unit>
<trans-unit id="The media could not be loaded, either because the server or network failed or because the format is not supported.">
<source>The media could not be loaded, either because the server or network failed or because the format is not supported.</source>
<target>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.</target>
</trans-unit>
<trans-unit id="The media playback was aborted due to a corruption problem or because the media used features your browser did not support.">
<source>The media playback was aborted due to a corruption problem or because the media used features your browser did not support.</source>
<target>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.</target>
</trans-unit>
<trans-unit id="No compatible source was found for this media.">
<source>No compatible source was found for this media.</source>
<target>Aucune source compatible n'a été trouvée pour cette vidéo.</target>
</trans-unit>
<trans-unit id="The media is encrypted and we do not have the keys to decrypt it.">
<source>The media is encrypted and we do not have the keys to decrypt it.</source>
<target>Le média est chiffré et nous n'avons pas les clés pour le déchiffrer.</target>
</trans-unit>
<trans-unit id="Play Video">
<source>Play Video</source>
<target>Lire la vidéo</target>
</trans-unit>
<trans-unit id="Close">
<source>Close</source>
<target>Fermer</target>
</trans-unit>
<trans-unit id="Close Modal Dialog">
<source>Close Modal Dialog</source>
<target>Fermer la boîte de dialogue modale</target>
</trans-unit>
<trans-unit id="Modal Window">
<source>Modal Window</source>
<target>Fenêtre modale</target>
</trans-unit>
<trans-unit id="This is a modal window">
<source>This is a modal window</source>
<target>Ceci est une fenêtre modale</target>
</trans-unit>
<trans-unit id="This modal can be closed by pressing the Escape key or activating the close button.">
<source>This modal can be closed by pressing the Escape key or activating the close button.</source>
<target>Ce modal peut être fermé en appuyant sur la touche Échap ou activer le bouton de fermeture.</target>
</trans-unit>
<trans-unit id=", opens captions settings dialog">
<source>, opens captions settings dialog</source>
<target>, ouvrir les paramètres des sous-titres transcrits</target>
</trans-unit>
<trans-unit id=", opens subtitles settings dialog">
<source>, opens subtitles settings dialog</source>
<target>, ouvrir les paramètres des sous-titres</target>
</trans-unit>
<trans-unit id=", opens descriptions settings dialog">
<source>, opens descriptions settings dialog</source>
<target>, ouvrir les paramètres des descriptions</target>
</trans-unit>
<trans-unit id=", selected">
<source>, selected</source>
<target>, sélectionné</target>
</trans-unit>
<trans-unit id="captions settings">
<source>captions settings</source>
<target>Paramètres des sous-titres transcrits</target>
</trans-unit>
<trans-unit id="subtitles settings">
<source>subititles settings</source>
<target>Paramètres des sous-titres</target>
</trans-unit>
<trans-unit id="descriptions settings">
<source>descriptions settings</source>
<target>Paramètres des descriptions</target>
</trans-unit>
<trans-unit id="Text">
<source>Text</source>
<target>Texte</target>
</trans-unit>
<trans-unit id="White">
<source>White</source>
<target>Blanc</target>
</trans-unit>
<trans-unit id="Black">
<source>Black</source>
<target>Noir</target>
</trans-unit>
<trans-unit id="Red">
<source>Red</source>
<target>Rouge</target>
</trans-unit>
<trans-unit id="Green">
<source>Green</source>
<target>Vert</target>
</trans-unit>
<trans-unit id="Blue">
<source>Blue</source>
<target>Bleu</target>
</trans-unit>
<trans-unit id="Yellow">
<source>Yellow</source>
<target>Jaune</target>
</trans-unit>
<trans-unit id="Magenta">
<source>Magenta</source>
<target>Magenta</target>
</trans-unit>
<trans-unit id="Cyan">
<source>Cyan</source>
<target>Cyan</target>
</trans-unit>
<trans-unit id="Background">
<source>Background</source>
<target>Arrière-plan</target>
</trans-unit>
<trans-unit id="Window">
<source>Window</source>
<target>Fenêtre</target>
</trans-unit>
<trans-unit id="Transparent">
<source>Transparent</source>
<target>Transparent</target>
</trans-unit>
<trans-unit id="Semi-Transparent">
<source>Semi-Transparent</source>
<target>Semi-transparent</target>
</trans-unit>
<trans-unit id="Opaque">
<source>Opaque</source>
<target>Opaque</target>
</trans-unit>
<trans-unit id="Font Size">
<source>Font Size</source>
<target>Taille des caractères</target>
</trans-unit>
<trans-unit id="Text Edge Style">
<source>Text Edge Style</source>
<target>Style des contours du texte</target>
</trans-unit>
<trans-unit id="None">
<source>None</source>
<target>Aucun</target>
</trans-unit>
<trans-unit id="Raised">
<source>Raised</source>
<target>Élevé</target>
</trans-unit>
<trans-unit id="Depressed">
<source>Depressed</source>
<target>Enfoncé</target>
</trans-unit>
<trans-unit id="Uniform">
<source>Uniform</source>
<target>Uniforme</target>
</trans-unit>
<trans-unit id="Dropshadow">
<source>Dropshadow</source>
<target>Ombre portée</target>
</trans-unit>
<trans-unit id="Font Family">
<source>Font Family</source>
<target>Familles de polices</target>
</trans-unit>
<trans-unit id="Proportional Sans-Serif">
<source>Proportional Sans-Serif</source>
<target>Polices à chasse variable sans empattement (Proportional Sans-Serif)</target>
</trans-unit>
<trans-unit id="Monospace Sans-Serif">
<source>Monospace Sans-Serif</source>
<target>Polices à chasse fixe sans empattement (Monospace Sans-Serif)</target>
</trans-unit>
<trans-unit id="Proportional Serif">
<source>Proportional Serif</source>
<target>Polices à chasse variable avec empattement (Proportional Serif)</target>
</trans-unit>
<trans-unit id="Monospace Serif">
<source>Monospace Serif</source>
<target>Polices à chasse fixe avec empattement (Monospace Serif)</target>
</trans-unit>
<trans-unit id="Casual">
<source>Casual</source>
<target>Manuscrite</target>
</trans-unit>
<trans-unit id="Script">
<source>Script</source>
<target>Scripte</target>
</trans-unit>
<trans-unit id="Small Caps">
<source>Small Caps</source>
<target>Petites capitales</target>
</trans-unit>
<trans-unit id="Reset">
<source>Reset</source>
<target>Réinitialiser</target>
</trans-unit>
<trans-unit id="restore all settings to the default values">
<source>restore all settings to the default values</source>
<target>Restaurer tous les paramètres aux valeurs par défaut</target>
</trans-unit>
<trans-unit id="Done">
<source>Done</source>
<target>Terminé</target>
</trans-unit>
<trans-unit id="Caption Settings Dialog">
<source>Caption Settings Dialog</source>
<target>Boîte de dialogue des paramètres des sous-titres transcrits</target>
</trans-unit>
<trans-unit id="Beginning of dialog window. Escape will cancel and close the window.">
<source>Beginning of dialog window. Escape will cancel and close the window.</source>
<target>Début de la fenêtre de dialogue. La touche d'échappement annulera et fermera la fenêtre.</target>
</trans-unit>
<trans-unit id="End of dialog window.">
<source>End of dialog window.</source>
<target>Fin de la fenêtre de dialogue.</target>
</trans-unit>
<trans-unit id="{1} is loading.">
<source>{1} is loading.</source>
<target>{1} est en train de charger</target>
</trans-unit>
<trans-unit id="Quality">
<source>Quality</source>
<target>Qualité</target>
</trans-unit>
<trans-unit id="Auto">
<source>Auto</source>
<target>Auto</target>
</trans-unit>
<trans-unit id="Speed">
<source>Speed</source>
<target>Vitesse</target>
</trans-unit>
<trans-unit id="peers">
<source>peers</source>
<target>pairs</target>
</trans-unit>
<trans-unit id="Go to the video page">
<source>Go to the video page</source>
<target>Aller sur la page de la vidéo</target>
</trans-unit>
<trans-unit id="Settings">
<source>Settings</source>
<target>Paramètres</target>
</trans-unit>
<trans-unit id="Uses P2P, others may know you are watching this video.">
<source>Uses P2P, others may know you are watching this video.</source>
<target>Utilise le P2P, d'autres personnes pourraient savoir que vous regardez cette vidéo.</target>
</trans-unit>
<trans-unit id="Copy the video URL">
<source>Copy the video URL</source>
<target>Copier le lien de la vidéo</target>
</trans-unit>
<trans-unit id="Copy the video URL at the current time">
<source>Copy the video URL at the current time</source>
<target>Copier le lien de la vidéo à partir de cette séquence</target>
</trans-unit>
<trans-unit id="Copy embed code">
<source>Copy embed code</source>
<target>Copier le code d'intégration</target>
</trans-unit>
</body>
</file></xliff>

View File

@ -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))

View File

@ -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": {

View File

@ -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

View File

@ -10,3 +10,7 @@ npm run ngx-extractor -- --locale "en-US" -i 'src/**/*.ts' -f xlf -o src/locale/
# This regex translate the Angular elements to special entities (that we will reconvert on pull)
#sed -i 's/<x id=\(.\+\?\)\/>/\&lt;x id=\1\/\&gt;/g' src/locale/source/messages_en_US.xml
perl -pi -e 's|<x id=(.+?)/>|&lt;x id=\1/&gt;|g' src/locale/source/messages_en_US.xml
# Add our strings too
cd ../
npm run i18n:create-custom-files

View File

@ -7,5 +7,7 @@ set -eu
#sed -i 's/\&lt;x id=\(.\+\?\)\/\&gt;/<x id=\1\/>/g' client/src/locale/target/*
for i in 1 2 3; do
perl -pi -e 's|&lt;x id=(.+?)/&gt;([^"])|<x id=\1/>\2|g' client/src/locale/target/*
perl -pi -e 's|&lt;x id=(.+?)/&gt;([^"])|<x id=\1/>\2|g' client/src/locale/target/*.xml
done
npm run i18n:xliff2json

42
scripts/i18n/xliff2json.ts Executable file
View File

@ -0,0 +1,42 @@
import * as xliff12ToJs from 'xliff/xliff12ToJs'
import { readFileSync, writeFile } from 'fs'
import { join } from 'path'
// First, the player
const playerSource = join(__dirname, '../../../client/src/locale/target/player_fr.xml')
const playerTarget = join(__dirname, '../../../client/src/locale/target/player_fr.json')
// Remove the two first lines our xliff module does not like
let playerFile = readFileSync(playerSource).toString()
playerFile = removeFirstLine(playerFile)
playerFile = removeFirstLine(playerFile)
xliff12ToJs(playerFile, (err, res) => {
if (err) {
console.error(err)
process.exit(-1)
}
const json = createJSONString(res)
writeFile(playerTarget, json, err => {
if (err) {
console.error(err)
process.exit(-1)
}
process.exit(0)
})
})
function removeFirstLine (str: string) {
return str.substring(str.indexOf('\n') + 1)
}
function createJSONString (obj: any) {
const res: any = {}
const strings = obj.resources['']
Object.keys(strings).forEach(k => res[k] = strings[k].target)
return JSON.stringify(res)
}

View File

@ -2,6 +2,11 @@
set -eu
# Copy locales
mkdir -p "./client/dist"
rm -r "./client/dist/locale"
cp -r "./client/src/locale/target" "./client/dist/locale"
NODE_ENV=test concurrently -k \
"npm run tsc -- --sourceMap && npm run nodemon -- --delay 2 --watch ./dist dist/server" \
"npm run tsc -- --sourceMap --preserveWatchOutput -w"

View File

@ -47,6 +47,14 @@ for (const staticClientFile of staticClientFiles) {
clientsRouter.use('/client', express.static(distPath, { maxAge: STATIC_MAX_AGE }))
clientsRouter.use('/client/assets/images', express.static(assetsImagesPath, { maxAge: STATIC_MAX_AGE }))
clientsRouter.use('/client/locales/:locale/:file.json', function (req, res) {
if (req.params.locale === 'fr' && req.params.file === 'player') {
return res.sendFile(join(__dirname, '../../../client/dist/locale/player_fr.json'))
}
return res.sendStatus(404)
})
// 404 for static files not found
clientsRouter.use('/client/*', (req: express.Request, res: express.Response, next: express.NextFunction) => {
res.sendStatus(404)

View File

@ -7,6 +7,10 @@ export function getDefaultLocale () {
return 'en-US'
}
export function isDefaultLocale (locale: string) {
return locale === getDefaultLocale()
}
const possiblePaths = Object.keys(I18N_LOCALES).map(l => '/' + l)
export function is18nPath (path: string) {
return possiblePaths.indexOf(path) !== -1

View File

@ -8402,6 +8402,18 @@ xhr2@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.1.4.tgz#7f87658847716db5026323812f818cadab387a5f"
xliff@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/xliff/-/xliff-3.0.1.tgz#ea0f5840011727aecbddf111e5c26d8590dcca9b"
dependencies:
xml-js "1.6.2"
xml-js@1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.2.tgz#4c4cb8413998f73701a202a1b8b2f17c985a72c5"
dependencies:
sax "^1.2.4"
xml-name-validator@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"