aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/app.module.ts18
-rw-r--r--client/src/app/core/server/server.service.ts20
-rw-r--r--client/src/app/shared/i18n/i18n-utils.ts12
-rw-r--r--client/src/app/shared/video/video.service.ts4
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.ts3
-rw-r--r--client/src/assets/player/peertube-player.ts10
-rw-r--r--client/src/main.ts7
-rwxr-xr-xscripts/i18n/xliff2json.ts4
-rw-r--r--server/controllers/client.ts22
-rw-r--r--shared/models/i18n/i18n.ts35
10 files changed, 81 insertions, 54 deletions
diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts
index e60a74cc0..51e354378 100644
--- a/client/src/app/app.module.ts
+++ b/client/src/app/app.module.ts
@@ -16,8 +16,8 @@ import { MenuComponent } from './menu'
16import { SharedModule } from './shared' 16import { SharedModule } from './shared'
17import { SignupModule } from './signup' 17import { SignupModule } from './signup'
18import { VideosModule } from './videos' 18import { VideosModule } from './videos'
19import { buildFileLocale, getDefaultLocale } from '../../../shared/models/i18n' 19import { buildFileLocale, getCompleteLocale, getDefaultLocale, isDefaultLocale } from '../../../shared/models/i18n'
20import { environment } from '../environments/environment' 20import { getDevLocale, isOnDevLocale } from '@app/shared/i18n/i18n-utils'
21 21
22export function metaFactory (serverService: ServerService): MetaLoader { 22export function metaFactory (serverService: ServerService): MetaLoader {
23 return new MetaStaticLoader({ 23 return new MetaStaticLoader({
@@ -67,17 +67,17 @@ export function metaFactory (serverService: ServerService): MetaLoader {
67 { 67 {
68 provide: TRANSLATIONS, 68 provide: TRANSLATIONS,
69 useFactory: (locale) => { 69 useFactory: (locale) => {
70 // On dev mode, test locales 70 // On dev mode, test localization
71 if (environment.production === false && window.location.search === '?lang=fr') { 71 if (isOnDevLocale()) {
72 return require(`raw-loader!../locale/target/angular_fr.xml`) 72 locale = getDevLocale()
73 return require(`raw-loader!../locale/target/angular_${locale}.xml`)
73 } 74 }
74 75
75 const fileLocale = buildFileLocale(locale)
76
77 // Default locale, nothing to translate 76 // Default locale, nothing to translate
78 const defaultFileLocale = buildFileLocale(getDefaultLocale()) 77 const completeLocale = getCompleteLocale(locale)
79 if (fileLocale === defaultFileLocale) return '' 78 if (isDefaultLocale(completeLocale)) return ''
80 79
80 const fileLocale = buildFileLocale(locale)
81 return require(`raw-loader!../locale/target/angular_${fileLocale}.xml`) 81 return require(`raw-loader!../locale/target/angular_${fileLocale}.xml`)
82 }, 82 },
83 deps: [ LOCALE_ID ] 83 deps: [ LOCALE_ID ]
diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts
index 56d33339e..74363e6a1 100644
--- a/client/src/app/core/server/server.service.ts
+++ b/client/src/app/core/server/server.service.ts
@@ -2,13 +2,13 @@ import { map, share, switchMap, tap } from 'rxjs/operators'
2import { HttpClient } from '@angular/common/http' 2import { HttpClient } from '@angular/common/http'
3import { Inject, Injectable, LOCALE_ID } from '@angular/core' 3import { Inject, Injectable, LOCALE_ID } from '@angular/core'
4import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage' 4import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage'
5import { Observable, ReplaySubject } from 'rxjs' 5import { Observable, ReplaySubject, of } from 'rxjs'
6import { ServerConfig } from '../../../../../shared' 6import { getCompleteLocale, ServerConfig } from '../../../../../shared'
7import { About } from '../../../../../shared/models/server/about.model' 7import { About } from '../../../../../shared/models/server/about.model'
8import { environment } from '../../../environments/environment' 8import { environment } from '../../../environments/environment'
9import { VideoConstant, VideoPrivacy } from '../../../../../shared/models/videos' 9import { VideoConstant, VideoPrivacy } from '../../../../../shared/models/videos'
10import { buildFileLocale, getDefaultLocale } from '../../../../../shared/models/i18n' 10import { isDefaultLocale } from '../../../../../shared/models/i18n'
11import { peertubeTranslate } from '@app/shared/i18n/i18n-utils' 11import { getDevLocale, isOnDevLocale, peertubeTranslate } from '@app/shared/i18n/i18n-utils'
12 12
13@Injectable() 13@Injectable()
14export class ServerService { 14export class ServerService {
@@ -72,8 +72,8 @@ export class ServerService {
72 private http: HttpClient, 72 private http: HttpClient,
73 @Inject(LOCALE_ID) private localeId: string 73 @Inject(LOCALE_ID) private localeId: string
74 ) { 74 ) {
75 this.loadConfigLocally()
76 this.loadServerLocale() 75 this.loadServerLocale()
76 this.loadConfigLocally()
77 } 77 }
78 78
79 loadConfig () { 79 loadConfig () {
@@ -163,14 +163,16 @@ export class ServerService {
163 } 163 }
164 164
165 private loadServerLocale () { 165 private loadServerLocale () {
166 const fileLocale = buildFileLocale(environment.production === true ? this.localeId : 'fr') 166 const completeLocale = isOnDevLocale() ? getDevLocale() : getCompleteLocale(this.localeId)
167 167
168 // Default locale, nothing to translate 168 // Default locale, nothing to translate
169 const defaultFileLocale = buildFileLocale(getDefaultLocale()) 169 if (isDefaultLocale(completeLocale)) {
170 if (fileLocale === defaultFileLocale) return {} 170 this.localeObservable = of({}).pipe(share())
171 return
172 }
171 173
172 this.localeObservable = this.http 174 this.localeObservable = this.http
173 .get(ServerService.BASE_LOCALE_URL + fileLocale + '/server.json') 175 .get(ServerService.BASE_LOCALE_URL + completeLocale + '/server.json')
174 .pipe(share()) 176 .pipe(share())
175 } 177 }
176 178
diff --git a/client/src/app/shared/i18n/i18n-utils.ts b/client/src/app/shared/i18n/i18n-utils.ts
index c1de51b7b..37180b930 100644
--- a/client/src/app/shared/i18n/i18n-utils.ts
+++ b/client/src/app/shared/i18n/i18n-utils.ts
@@ -1,7 +1,19 @@
1import { environment } from '../../../environments/environment'
2
1function peertubeTranslate (str: string, translations: { [ id: string ]: string }) { 3function peertubeTranslate (str: string, translations: { [ id: string ]: string }) {
2 return translations[str] ? translations[str] : str 4 return translations[str] ? translations[str] : str
3} 5}
4 6
7function isOnDevLocale () {
8 return environment.production === false && window.location.search === '?lang=fr'
9}
10
11function getDevLocale () {
12 return 'fr'
13}
14
5export { 15export {
16 getDevLocale,
17 isOnDevLocale,
6 peertubeTranslate 18 peertubeTranslate
7} 19}
diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts
index c607b7d6a..58cb52efc 100644
--- a/client/src/app/shared/video/video.service.ts
+++ b/client/src/app/shared/video/video.service.ts
@@ -46,8 +46,8 @@ export class VideoService {
46 return this.serverService.localeObservable 46 return this.serverService.localeObservable
47 .pipe( 47 .pipe(
48 switchMap(translations => { 48 switchMap(translations => {
49 return this.authHttp.get<VideoDetailsServerModel>(VideoService.BASE_VIDEO_URL + uuid) 49 return this.authHttp.get<VideoDetailsServerModel>(VideoService.BASE_VIDEO_URL + uuid)
50 .pipe(map(videoHash => ({ videoHash, translations }))) 50 .pipe(map(videoHash => ({ videoHash, translations })))
51 }), 51 }),
52 map(({ videoHash, translations }) => new VideoDetails(videoHash, translations)), 52 map(({ videoHash, translations }) => new VideoDetails(videoHash, translations)),
53 catchError(res => this.restExtractor.handleError(res)) 53 catchError(res => this.restExtractor.handleError(res))
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 d3e16c4cf..4a67d456e 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/videos/+video-watch/video-watch.component.ts
@@ -25,6 +25,7 @@ import { getVideojsOptions, loadLocale, addContextMenu } from '../../../assets/p
25import { ServerService } from '@app/core' 25import { ServerService } from '@app/core'
26import { I18n } from '@ngx-translate/i18n-polyfill' 26import { I18n } from '@ngx-translate/i18n-polyfill'
27import { environment } from '../../../environments/environment' 27import { environment } from '../../../environments/environment'
28import { getDevLocale, isOnDevLocale } from '@app/shared/i18n/i18n-utils'
28 29
29@Component({ 30@Component({
30 selector: 'my-video-watch', 31 selector: 'my-video-watch',
@@ -377,7 +378,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
377 }) 378 })
378 379
379 if (this.videojsLocaleLoaded === false) { 380 if (this.videojsLocaleLoaded === false) {
380 await loadLocale(environment.apiUrl, videojs, environment.production === true ? this.localeId : 'fr') 381 await loadLocale(environment.apiUrl, videojs, isOnDevLocale() ? getDevLocale() : this.localeId)
381 this.videojsLocaleLoaded = true 382 this.videojsLocaleLoaded = true
382 } 383 }
383 384
diff --git a/client/src/assets/player/peertube-player.ts b/client/src/assets/player/peertube-player.ts
index b604097fa..9e37b75d2 100644
--- a/client/src/assets/player/peertube-player.ts
+++ b/client/src/assets/player/peertube-player.ts
@@ -12,7 +12,7 @@ import './peertube-videojs-plugin'
12import './peertube-load-progress-bar' 12import './peertube-load-progress-bar'
13import { videojsUntyped } from './peertube-videojs-typings' 13import { videojsUntyped } from './peertube-videojs-typings'
14import { buildVideoEmbed, buildVideoLink, copyToClipboard } from './utils' 14import { buildVideoEmbed, buildVideoLink, copyToClipboard } from './utils'
15import { is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n' 15import { getCompleteLocale, is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n'
16 16
17// Change 'Playback Rate' to 'Speed' (smaller for our settings menu) 17// Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
18videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed' 18videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed'
@@ -141,11 +141,13 @@ function addContextMenu (player: any, videoEmbedUrl: string) {
141} 141}
142 142
143function loadLocale (serverUrl: string, videojs: any, locale: string) { 143function loadLocale (serverUrl: string, videojs: any, locale: string) {
144 if (!is18nLocale(locale) || isDefaultLocale(locale)) return undefined 144 const completeLocale = getCompleteLocale(locale)
145 145
146 return fetch(serverUrl + '/client/locales/' + locale + '/player.json') 146 if (!is18nLocale(completeLocale) || isDefaultLocale(completeLocale)) return Promise.resolve(undefined)
147
148 return fetch(serverUrl + '/client/locales/' + completeLocale + '/player.json')
147 .then(res => res.json()) 149 .then(res => res.json())
148 .then(json => videojs.addLanguage(locale, json)) 150 .then(json => videojs.addLanguage(completeLocale, json))
149} 151}
150 152
151export { 153export {
diff --git a/client/src/main.ts b/client/src/main.ts
index 19f45a3e3..061be17de 100644
--- a/client/src/main.ts
+++ b/client/src/main.ts
@@ -5,14 +5,17 @@ import { AppModule } from './app/app.module'
5import { environment } from './environments/environment' 5import { environment } from './environments/environment'
6 6
7import { hmrBootstrap } from './hmr' 7import { hmrBootstrap } from './hmr'
8import { getDevLocale, isOnDevLocale } from '@app/shared/i18n/i18n-utils'
8 9
9let providers = [] 10let providers = []
10if (environment.production) { 11if (environment.production) {
11 enableProdMode() 12 enableProdMode()
12} 13}
13 14
14if (environment.production === false && window.location.search === '?lang=fr') { 15// Template translation, should be in the bootstrap step
15 const translations = require(`raw-loader!./locale/target/angular_fr.xml`) 16if (isOnDevLocale()) {
17 const locale = getDevLocale()
18 const translations = require(`raw-loader!./locale/target/angular_${locale}.xml`)
16 19
17 providers = [ 20 providers = [
18 { provide: TRANSLATIONS, useValue: translations }, 21 { provide: TRANSLATIONS, useValue: translations },
diff --git a/scripts/i18n/xliff2json.ts b/scripts/i18n/xliff2json.ts
index fa5a71d65..c60739561 100755
--- a/scripts/i18n/xliff2json.ts
+++ b/scripts/i18n/xliff2json.ts
@@ -1,7 +1,7 @@
1import * as xliff12ToJs from 'xliff/xliff12ToJs' 1import * as xliff12ToJs from 'xliff/xliff12ToJs'
2import { unlink, readFileSync, writeFile } from 'fs' 2import { unlink, readFileSync, writeFile } from 'fs'
3import { join } from 'path' 3import { join } from 'path'
4import { buildFileLocale, I18N_LOCALES, isDefaultLocale } from '../../shared/models/i18n/i18n' 4import { buildFileLocale, I18N_LOCALES, isDefaultLocale, LOCALE_FILES } from '../../shared/models/i18n/i18n'
5import { eachSeries } from 'async' 5import { eachSeries } from 'async'
6 6
7const sources: string[] = [] 7const sources: string[] = []
@@ -9,7 +9,7 @@ const availableLocales = Object.keys(I18N_LOCALES)
9 .filter(l => isDefaultLocale(l) === false) 9 .filter(l => isDefaultLocale(l) === false)
10 .map(l => buildFileLocale(l)) 10 .map(l => buildFileLocale(l))
11 11
12for (const file of [ 'server', 'player' ]) { 12for (const file of LOCALE_FILES) {
13 for (const locale of availableLocales) { 13 for (const locale of availableLocales) {
14 sources.push(join(__dirname, '../../../client/src/locale/target/', `${file}_${locale}.xml`)) 14 sources.push(join(__dirname, '../../../client/src/locale/target/', `${file}_${locale}.xml`))
15 } 15 }
diff --git a/server/controllers/client.ts b/server/controllers/client.ts
index ec78a4bbc..385757fa6 100644
--- a/server/controllers/client.ts
+++ b/server/controllers/client.ts
@@ -3,18 +3,12 @@ import * as express from 'express'
3import { join } from 'path' 3import { join } from 'path'
4import * as validator from 'validator' 4import * as validator from 'validator'
5import { escapeHTML, readFileBufferPromise, root } from '../helpers/core-utils' 5import { escapeHTML, readFileBufferPromise, root } from '../helpers/core-utils'
6import { 6import { ACCEPT_HEADERS, CONFIG, EMBED_SIZE, OPENGRAPH_AND_OEMBED_COMMENT, STATIC_MAX_AGE, STATIC_PATHS } from '../initializers'
7 ACCEPT_HEADERS,
8 CONFIG,
9 EMBED_SIZE,
10 OPENGRAPH_AND_OEMBED_COMMENT,
11 STATIC_MAX_AGE,
12 STATIC_PATHS
13} from '../initializers'
14import { asyncMiddleware } from '../middlewares' 7import { asyncMiddleware } from '../middlewares'
15import { VideoModel } from '../models/video/video' 8import { VideoModel } from '../models/video/video'
16import { VideoPrivacy } from '../../shared/models/videos' 9import { VideoPrivacy } from '../../shared/models/videos'
17import { I18N_LOCALES, is18nLocale, getDefaultLocale } from '../../shared/models' 10import { buildFileLocale, getCompleteLocale, getDefaultLocale, is18nLocale } from '../../shared/models'
11import { LOCALE_FILES } from '../../shared/models/i18n/i18n'
18 12
19const clientsRouter = express.Router() 13const clientsRouter = express.Router()
20 14
@@ -51,8 +45,10 @@ clientsRouter.use('/client/locales/:locale/:file.json', function (req, res) {
51 const locale = req.params.locale 45 const locale = req.params.locale
52 const file = req.params.file 46 const file = req.params.file
53 47
54 if (is18nLocale(locale) && [ 'player', 'server' ].indexOf(file) !== -1) { 48 if (is18nLocale(locale) && LOCALE_FILES.indexOf(file) !== -1) {
55 return res.sendFile(join(__dirname, `../../../client/dist/locale/${file}_${locale}.json`)) 49 const completeLocale = getCompleteLocale(locale)
50 const completeFileLocale = buildFileLocale(completeLocale)
51 return res.sendFile(join(__dirname, `../../../client/dist/locale/${file}_${completeFileLocale}.json`))
56 } 52 }
57 53
58 return res.sendStatus(404) 54 return res.sendStatus(404)
@@ -88,12 +84,12 @@ function getIndexPath (req: express.Request, paramLang?: string) {
88 if (paramLang && is18nLocale(paramLang)) { 84 if (paramLang && is18nLocale(paramLang)) {
89 lang = paramLang 85 lang = paramLang
90 } else { 86 } else {
91 // lang = req.acceptsLanguages(Object.keys(I18N_LOCALES)) || getDefaultLocale() 87 // lang = req.acceptsLanguages(POSSIBLE_LOCALES) || getDefaultLocale()
92 // Disable auto language for now 88 // Disable auto language for now
93 lang = getDefaultLocale() 89 lang = getDefaultLocale()
94 } 90 }
95 91
96 return join(__dirname, '../../../client/dist/' + lang + '/index.html') 92 return join(__dirname, '../../../client/dist/' + buildFileLocale(lang) + '/index.html')
97} 93}
98 94
99function addOpenGraphAndOEmbedTags (htmlStringPage: string, video: VideoModel) { 95function addOpenGraphAndOEmbedTags (htmlStringPage: string, video: VideoModel) {
diff --git a/shared/models/i18n/i18n.ts b/shared/models/i18n/i18n.ts
index 4d50bc36e..be1420150 100644
--- a/shared/models/i18n/i18n.ts
+++ b/shared/models/i18n/i18n.ts
@@ -1,34 +1,45 @@
1export const LOCALE_FILES = [ 'player', 'server' ]
2
1export const I18N_LOCALES = { 3export const I18N_LOCALES = {
2 'en-US': 'English (US)', 4 'en-US': 'English (US)',
3 fr: 'French' 5 fr: 'French'
4} 6}
5 7
8const I18N_LOCALE_ALIAS = {
9 'en': 'en-US'
10}
11
12export const POSSIBLE_LOCALES = Object.keys(I18N_LOCALES)
13 .concat(Object.keys(I18N_LOCALE_ALIAS))
14
15const possiblePaths = POSSIBLE_LOCALES.map(l => '/' + l)
16
6export function getDefaultLocale () { 17export function getDefaultLocale () {
7 return 'en-US' 18 return 'en-US'
8} 19}
9 20
10export function isDefaultLocale (locale: string) { 21export function isDefaultLocale (locale: string) {
11 return locale === getDefaultLocale() 22 return getCompleteLocale(locale) === getCompleteLocale(getDefaultLocale())
12} 23}
13 24
14const possiblePaths = Object.keys(I18N_LOCALES).map(l => '/' + l)
15export function is18nPath (path: string) { 25export function is18nPath (path: string) {
16 return possiblePaths.indexOf(path) !== -1 26 return possiblePaths.indexOf(path) !== -1
17} 27}
18 28
19const possibleLanguages = Object.keys(I18N_LOCALES)
20export function is18nLocale (locale: string) { 29export function is18nLocale (locale: string) {
21 return possibleLanguages.indexOf(locale) !== -1 30 return POSSIBLE_LOCALES.indexOf(locale) !== -1
31}
32
33export function getCompleteLocale (locale: string) {
34 if (!locale) return locale
35
36 if (I18N_LOCALE_ALIAS[locale]) return I18N_LOCALE_ALIAS[locale]
37
38 return locale
22} 39}
23 40
24// Only use in dev mode, so relax
25// In production, the locale always match with a I18N_LANGUAGES key
26export function buildFileLocale (locale: string) { 41export function buildFileLocale (locale: string) {
27 if (!is18nLocale(locale)) { 42 const completeLocale = getCompleteLocale(locale)
28 // Some working examples for development purpose
29 if (locale.split('-')[ 0 ] === 'en') return 'en_US'
30 else if (locale === 'fr') return 'fr'
31 }
32 43
33 return locale.replace('-', '_') 44 return completeLocale.replace('-', '_')
34} 45}