Little i18n refractoring
authorChocobozzz <me@florianbigard.com>
Wed, 6 Jun 2018 15:37:13 +0000 (17:37 +0200)
committerChocobozzz <me@florianbigard.com>
Wed, 6 Jun 2018 15:37:13 +0000 (17:37 +0200)
client/src/app/app.module.ts
client/src/app/core/server/server.service.ts
client/src/app/shared/i18n/i18n-utils.ts
client/src/app/shared/video/video.service.ts
client/src/app/videos/+video-watch/video-watch.component.ts
client/src/assets/player/peertube-player.ts
client/src/main.ts
scripts/i18n/xliff2json.ts
server/controllers/client.ts
shared/models/i18n/i18n.ts

index e60a74cc022b57ee9f410e03005c459094e28757..51e35437824629f8ca399ff78ed99a4e97507007 100644 (file)
@@ -16,8 +16,8 @@ import { MenuComponent } from './menu'
 import { SharedModule } from './shared'
 import { SignupModule } from './signup'
 import { VideosModule } from './videos'
-import { buildFileLocale, getDefaultLocale } from '../../../shared/models/i18n'
-import { environment } from '../environments/environment'
+import { buildFileLocale, getCompleteLocale, getDefaultLocale, isDefaultLocale } from '../../../shared/models/i18n'
+import { getDevLocale, isOnDevLocale } from '@app/shared/i18n/i18n-utils'
 
 export function metaFactory (serverService: ServerService): MetaLoader {
   return new MetaStaticLoader({
@@ -67,17 +67,17 @@ export function metaFactory (serverService: ServerService): MetaLoader {
     {
       provide: TRANSLATIONS,
       useFactory: (locale) => {
-        // On dev mode, test locales
-        if (environment.production === false && window.location.search === '?lang=fr') {
-          return require(`raw-loader!../locale/target/angular_fr.xml`)
+        // On dev mode, test localization
+        if (isOnDevLocale()) {
+          locale = getDevLocale()
+          return require(`raw-loader!../locale/target/angular_${locale}.xml`)
         }
 
-        const fileLocale = buildFileLocale(locale)
-
         // Default locale, nothing to translate
-        const defaultFileLocale = buildFileLocale(getDefaultLocale())
-        if (fileLocale === defaultFileLocale) return ''
+        const completeLocale = getCompleteLocale(locale)
+        if (isDefaultLocale(completeLocale)) return ''
 
+        const fileLocale = buildFileLocale(locale)
         return require(`raw-loader!../locale/target/angular_${fileLocale}.xml`)
       },
       deps: [ LOCALE_ID ]
index 56d33339ebdf0e663df98506efa92ce0ac7c04e9..74363e6a1bac7eedeb7627c3319ae7d3f977d147 100644 (file)
@@ -2,13 +2,13 @@ import { map, share, switchMap, tap } from 'rxjs/operators'
 import { HttpClient } from '@angular/common/http'
 import { Inject, Injectable, LOCALE_ID } from '@angular/core'
 import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage'
-import { Observable, ReplaySubject } from 'rxjs'
-import { ServerConfig } from '../../../../../shared'
+import { Observable, ReplaySubject, of } from 'rxjs'
+import { getCompleteLocale, ServerConfig } from '../../../../../shared'
 import { About } from '../../../../../shared/models/server/about.model'
 import { environment } from '../../../environments/environment'
 import { VideoConstant, VideoPrivacy } from '../../../../../shared/models/videos'
-import { buildFileLocale, getDefaultLocale } from '../../../../../shared/models/i18n'
-import { peertubeTranslate } from '@app/shared/i18n/i18n-utils'
+import { isDefaultLocale } from '../../../../../shared/models/i18n'
+import { getDevLocale, isOnDevLocale, peertubeTranslate } from '@app/shared/i18n/i18n-utils'
 
 @Injectable()
 export class ServerService {
@@ -72,8 +72,8 @@ export class ServerService {
     private http: HttpClient,
     @Inject(LOCALE_ID) private localeId: string
   ) {
-    this.loadConfigLocally()
     this.loadServerLocale()
+    this.loadConfigLocally()
   }
 
   loadConfig () {
@@ -163,14 +163,16 @@ export class ServerService {
   }
 
   private loadServerLocale () {
-    const fileLocale = buildFileLocale(environment.production === true ? this.localeId : 'fr')
+    const completeLocale = isOnDevLocale() ? getDevLocale() : getCompleteLocale(this.localeId)
 
     // Default locale, nothing to translate
-    const defaultFileLocale = buildFileLocale(getDefaultLocale())
-    if (fileLocale === defaultFileLocale) return {}
+    if (isDefaultLocale(completeLocale)) {
+      this.localeObservable = of({}).pipe(share())
+      return
+    }
 
     this.localeObservable = this.http
-                                  .get(ServerService.BASE_LOCALE_URL + fileLocale + '/server.json')
+                                  .get(ServerService.BASE_LOCALE_URL + completeLocale + '/server.json')
                                   .pipe(share())
   }
 
index c1de51b7bd64950abd82a6ce43eebcf457c501e8..37180b9300c7ea0a2cbafe4de658bb2682dfa109 100644 (file)
@@ -1,7 +1,19 @@
+import { environment } from '../../../environments/environment'
+
 function peertubeTranslate (str: string, translations: { [ id: string ]: string }) {
   return translations[str] ? translations[str] : str
 }
 
+function isOnDevLocale () {
+  return environment.production === false && window.location.search === '?lang=fr'
+}
+
+function getDevLocale () {
+  return 'fr'
+}
+
 export {
+  getDevLocale,
+  isOnDevLocale,
   peertubeTranslate
 }
index c607b7d6a70ae0d69fb0d38a81718a2351da22d8..58cb52efcbc6a8239b8afe9437a565c094610cfd 100644 (file)
@@ -46,8 +46,8 @@ export class VideoService {
     return this.serverService.localeObservable
                .pipe(
                  switchMap(translations => {
-                  return this.authHttp.get<VideoDetailsServerModel>(VideoService.BASE_VIDEO_URL + uuid)
-                     .pipe(map(videoHash => ({ videoHash, translations })))
+                   return this.authHttp.get<VideoDetailsServerModel>(VideoService.BASE_VIDEO_URL + uuid)
+                              .pipe(map(videoHash => ({ videoHash, translations })))
                  }),
                  map(({ videoHash, translations }) => new VideoDetails(videoHash, translations)),
                  catchError(res => this.restExtractor.handleError(res))
index d3e16c4cfb92c4fa5cb20b0c2ad6974901330dab..4a67d456e164957e4847ea5b61fd28625a9fb8f2 100644 (file)
@@ -25,6 +25,7 @@ import { getVideojsOptions, loadLocale, addContextMenu } from '../../../assets/p
 import { ServerService } from '@app/core'
 import { I18n } from '@ngx-translate/i18n-polyfill'
 import { environment } from '../../../environments/environment'
+import { getDevLocale, isOnDevLocale } from '@app/shared/i18n/i18n-utils'
 
 @Component({
   selector: 'my-video-watch',
@@ -377,7 +378,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
     })
 
     if (this.videojsLocaleLoaded === false) {
-      await loadLocale(environment.apiUrl, videojs, environment.production === true ? this.localeId : 'fr')
+      await loadLocale(environment.apiUrl, videojs, isOnDevLocale() ? getDevLocale() : this.localeId)
       this.videojsLocaleLoaded = true
     }
 
index b604097fa664a0746bd2433d8e2ae04d2b4b1d76..9e37b75d235aa9182eb4bf8ea7ffd70ee366a153 100644 (file)
@@ -12,7 +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'
+import { getCompleteLocale, is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n'
 
 // Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
 videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed'
@@ -141,11 +141,13 @@ function addContextMenu (player: any, videoEmbedUrl: string) {
 }
 
 function loadLocale (serverUrl: string, videojs: any, locale: string) {
-  if (!is18nLocale(locale) || isDefaultLocale(locale)) return undefined
+  const completeLocale = getCompleteLocale(locale)
 
-  return fetch(serverUrl + '/client/locales/' + locale + '/player.json')
+  if (!is18nLocale(completeLocale) || isDefaultLocale(completeLocale)) return Promise.resolve(undefined)
+
+  return fetch(serverUrl + '/client/locales/' + completeLocale + '/player.json')
     .then(res => res.json())
-    .then(json => videojs.addLanguage(locale, json))
+    .then(json => videojs.addLanguage(completeLocale, json))
 }
 
 export {
index 19f45a3e309bf46b9c0963841a63487a4ac6d901..061be17deec6ea3bc5441c57124481122c09e15f 100644 (file)
@@ -5,14 +5,17 @@ import { AppModule } from './app/app.module'
 import { environment } from './environments/environment'
 
 import { hmrBootstrap } from './hmr'
+import { getDevLocale, isOnDevLocale } from '@app/shared/i18n/i18n-utils'
 
 let providers = []
 if (environment.production) {
   enableProdMode()
 }
 
-if (environment.production === false && window.location.search === '?lang=fr') {
-  const translations = require(`raw-loader!./locale/target/angular_fr.xml`)
+// Template translation, should be in the bootstrap step
+if (isOnDevLocale()) {
+  const locale = getDevLocale()
+  const translations = require(`raw-loader!./locale/target/angular_${locale}.xml`)
 
   providers = [
     { provide: TRANSLATIONS, useValue: translations },
index fa5a71d659133430aa7b5f4052fd701f4f5d94ef..c607395615399100759c75e27ab2a004a5a7b507 100755 (executable)
@@ -1,7 +1,7 @@
 import * as xliff12ToJs from 'xliff/xliff12ToJs'
 import { unlink, readFileSync, writeFile } from 'fs'
 import { join } from 'path'
-import { buildFileLocale, I18N_LOCALES, isDefaultLocale } from '../../shared/models/i18n/i18n'
+import { buildFileLocale, I18N_LOCALES, isDefaultLocale, LOCALE_FILES } from '../../shared/models/i18n/i18n'
 import { eachSeries } from 'async'
 
 const sources: string[] = []
@@ -9,7 +9,7 @@ const availableLocales = Object.keys(I18N_LOCALES)
                                .filter(l => isDefaultLocale(l) === false)
                                .map(l => buildFileLocale(l))
 
-for (const file of [ 'server', 'player' ]) {
+for (const file of LOCALE_FILES) {
   for (const locale of availableLocales) {
     sources.push(join(__dirname, '../../../client/src/locale/target/', `${file}_${locale}.xml`))
   }
index ec78a4bbc0cbcf25e1376d3fd8038ffafff81f1d..385757fa6bb45729433b142458f918e9a815589e 100644 (file)
@@ -3,18 +3,12 @@ import * as express from 'express'
 import { join } from 'path'
 import * as validator from 'validator'
 import { escapeHTML, readFileBufferPromise, root } from '../helpers/core-utils'
-import {
-  ACCEPT_HEADERS,
-  CONFIG,
-  EMBED_SIZE,
-  OPENGRAPH_AND_OEMBED_COMMENT,
-  STATIC_MAX_AGE,
-  STATIC_PATHS
-} from '../initializers'
+import { ACCEPT_HEADERS, CONFIG, EMBED_SIZE, OPENGRAPH_AND_OEMBED_COMMENT, STATIC_MAX_AGE, STATIC_PATHS } from '../initializers'
 import { asyncMiddleware } from '../middlewares'
 import { VideoModel } from '../models/video/video'
 import { VideoPrivacy } from '../../shared/models/videos'
-import { I18N_LOCALES, is18nLocale, getDefaultLocale } from '../../shared/models'
+import { buildFileLocale, getCompleteLocale, getDefaultLocale, is18nLocale } from '../../shared/models'
+import { LOCALE_FILES } from '../../shared/models/i18n/i18n'
 
 const clientsRouter = express.Router()
 
@@ -51,8 +45,10 @@ clientsRouter.use('/client/locales/:locale/:file.json', function (req, res) {
   const locale = req.params.locale
   const file = req.params.file
 
-  if (is18nLocale(locale) && [ 'player', 'server' ].indexOf(file) !== -1) {
-    return res.sendFile(join(__dirname, `../../../client/dist/locale/${file}_${locale}.json`))
+  if (is18nLocale(locale) && LOCALE_FILES.indexOf(file) !== -1) {
+    const completeLocale = getCompleteLocale(locale)
+    const completeFileLocale = buildFileLocale(completeLocale)
+    return res.sendFile(join(__dirname, `../../../client/dist/locale/${file}_${completeFileLocale}.json`))
   }
 
   return res.sendStatus(404)
@@ -88,12 +84,12 @@ function getIndexPath (req: express.Request, paramLang?: string) {
   if (paramLang && is18nLocale(paramLang)) {
     lang = paramLang
   } else {
-    // lang = req.acceptsLanguages(Object.keys(I18N_LOCALES)) || getDefaultLocale()
+    // lang = req.acceptsLanguages(POSSIBLE_LOCALES) || getDefaultLocale()
     // Disable auto language for now
     lang = getDefaultLocale()
   }
 
-  return join(__dirname, '../../../client/dist/' + lang + '/index.html')
+  return join(__dirname, '../../../client/dist/' + buildFileLocale(lang) + '/index.html')
 }
 
 function addOpenGraphAndOEmbedTags (htmlStringPage: string, video: VideoModel) {
index 4d50bc36ea73edca5e4130c1766ec08655bf01a5..be14201507484a34d717194afbced4cccdb19067 100644 (file)
@@ -1,34 +1,45 @@
+export const LOCALE_FILES = [ 'player', 'server' ]
+
 export const I18N_LOCALES = {
   'en-US': 'English (US)',
   fr: 'French'
 }
 
+const I18N_LOCALE_ALIAS = {
+  'en': 'en-US'
+}
+
+export const POSSIBLE_LOCALES = Object.keys(I18N_LOCALES)
+                                      .concat(Object.keys(I18N_LOCALE_ALIAS))
+
+const possiblePaths = POSSIBLE_LOCALES.map(l => '/' + l)
+
 export function getDefaultLocale () {
   return 'en-US'
 }
 
 export function isDefaultLocale (locale: string) {
-  return locale === getDefaultLocale()
+  return getCompleteLocale(locale) === getCompleteLocale(getDefaultLocale())
 }
 
-const possiblePaths = Object.keys(I18N_LOCALES).map(l => '/' + l)
 export function is18nPath (path: string) {
   return possiblePaths.indexOf(path) !== -1
 }
 
-const possibleLanguages = Object.keys(I18N_LOCALES)
 export function is18nLocale (locale: string) {
-  return possibleLanguages.indexOf(locale) !== -1
+  return POSSIBLE_LOCALES.indexOf(locale) !== -1
+}
+
+export function getCompleteLocale (locale: string) {
+  if (!locale) return locale
+
+  if (I18N_LOCALE_ALIAS[locale]) return I18N_LOCALE_ALIAS[locale]
+
+  return locale
 }
 
-// Only use in dev mode, so relax
-// In production, the locale always match with a I18N_LANGUAGES key
 export function buildFileLocale (locale: string) {
-  if (!is18nLocale(locale)) {
-    // Some working examples for development purpose
-    if (locale.split('-')[ 0 ] === 'en') return 'en_US'
-    else if (locale === 'fr') return 'fr'
-  }
+  const completeLocale = getCompleteLocale(locale)
 
-  return locale.replace('-', '_')
+  return completeLocale.replace('-', '_')
 }