aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-06-06 16:46:42 +0200
committerChocobozzz <me@florianbigard.com>2018-06-06 16:48:41 +0200
commit7ce44a74a3b052190cfacd4bd5ee6b92cfc620ac (patch)
tree6f178426c165f9136eb08354efa4a06c24725f87
parentf07d6385b4b46c3254898292a8a53ed415b8d49b (diff)
downloadPeerTube-7ce44a74a3b052190cfacd4bd5ee6b92cfc620ac.tar.gz
PeerTube-7ce44a74a3b052190cfacd4bd5ee6b92cfc620ac.tar.zst
PeerTube-7ce44a74a3b052190cfacd4bd5ee6b92cfc620ac.zip
Add server localization
-rw-r--r--client/src/app/core/server/server.service.ts76
-rw-r--r--client/src/app/shared/i18n/i18n-utils.ts7
-rw-r--r--client/src/app/shared/video/video-details.model.ts4
-rw-r--r--client/src/app/shared/video/video.model.ts8
-rw-r--r--client/src/app/shared/video/video.service.ts56
-rw-r--r--client/src/locale/source/server_en_US.xml878
-rw-r--r--client/src/locale/target/player_fr.xml379
-rw-r--r--client/src/locale/target/server_fr.json1
-rwxr-xr-xscripts/i18n/create-custom-files.ts70
-rwxr-xr-xscripts/i18n/xliff2json.ts62
-rw-r--r--server/controllers/client.ts7
-rw-r--r--server/initializers/constants.ts3
12 files changed, 1080 insertions, 471 deletions
diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts
index ccae5a151..56d33339e 100644
--- a/client/src/app/core/server/server.service.ts
+++ b/client/src/app/core/server/server.service.ts
@@ -1,17 +1,20 @@
1import { tap } from 'rxjs/operators' 1import { map, share, switchMap, tap } from 'rxjs/operators'
2import { HttpClient } from '@angular/common/http' 2import { HttpClient } from '@angular/common/http'
3import { Injectable } 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 { ReplaySubject } from 'rxjs' 5import { Observable, ReplaySubject } from 'rxjs'
6import { ServerConfig } from '../../../../../shared' 6import { 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'
11import { peertubeTranslate } from '@app/shared/i18n/i18n-utils'
10 12
11@Injectable() 13@Injectable()
12export class ServerService { 14export class ServerService {
13 private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config/' 15 private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config/'
14 private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/' 16 private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/'
17 private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/'
15 private static CONFIG_LOCAL_STORAGE_KEY = 'server-config' 18 private static CONFIG_LOCAL_STORAGE_KEY = 'server-config'
16 19
17 configLoaded = new ReplaySubject<boolean>(1) 20 configLoaded = new ReplaySubject<boolean>(1)
@@ -19,6 +22,7 @@ export class ServerService {
19 videoCategoriesLoaded = new ReplaySubject<boolean>(1) 22 videoCategoriesLoaded = new ReplaySubject<boolean>(1)
20 videoLicencesLoaded = new ReplaySubject<boolean>(1) 23 videoLicencesLoaded = new ReplaySubject<boolean>(1)
21 videoLanguagesLoaded = new ReplaySubject<boolean>(1) 24 videoLanguagesLoaded = new ReplaySubject<boolean>(1)
25 localeObservable: Observable<any>
22 26
23 private config: ServerConfig = { 27 private config: ServerConfig = {
24 instance: { 28 instance: {
@@ -64,8 +68,12 @@ export class ServerService {
64 private videoLanguages: Array<VideoConstant<string>> = [] 68 private videoLanguages: Array<VideoConstant<string>> = []
65 private videoPrivacies: Array<VideoConstant<VideoPrivacy>> = [] 69 private videoPrivacies: Array<VideoConstant<VideoPrivacy>> = []
66 70
67 constructor (private http: HttpClient) { 71 constructor (
72 private http: HttpClient,
73 @Inject(LOCALE_ID) private localeId: string
74 ) {
68 this.loadConfigLocally() 75 this.loadConfigLocally()
76 this.loadServerLocale()
69 } 77 }
70 78
71 loadConfig () { 79 loadConfig () {
@@ -124,26 +132,46 @@ export class ServerService {
124 notifier: ReplaySubject<boolean>, 132 notifier: ReplaySubject<boolean>,
125 sort = false 133 sort = false
126 ) { 134 ) {
127 return this.http.get(ServerService.BASE_VIDEO_URL + attributeName) 135 this.localeObservable
128 .subscribe(data => { 136 .pipe(
129 Object.keys(data) 137 switchMap(translations => {
130 .forEach(dataKey => { 138 return this.http.get(ServerService.BASE_VIDEO_URL + attributeName)
131 hashToPopulate.push({ 139 .pipe(map(data => ({ data, translations })))
132 id: dataKey, 140 })
133 label: data[dataKey] 141 )
134 }) 142 .subscribe(({ data, translations }) => {
135 }) 143 Object.keys(data)
136 144 .forEach(dataKey => {
137 if (sort === true) { 145 const label = data[ dataKey ]
138 hashToPopulate.sort((a, b) => { 146
139 if (a.label < b.label) return -1 147 hashToPopulate.push({
140 if (a.label === b.label) return 0 148 id: dataKey,
141 return 1 149 label: peertubeTranslate(label, translations)
142 }) 150 })
143 } 151 })
144 152
145 notifier.next(true) 153 if (sort === true) {
146 }) 154 hashToPopulate.sort((a, b) => {
155 if (a.label < b.label) return -1
156 if (a.label === b.label) return 0
157 return 1
158 })
159 }
160
161 notifier.next(true)
162 })
163 }
164
165 private loadServerLocale () {
166 const fileLocale = buildFileLocale(environment.production === true ? this.localeId : 'fr')
167
168 // Default locale, nothing to translate
169 const defaultFileLocale = buildFileLocale(getDefaultLocale())
170 if (fileLocale === defaultFileLocale) return {}
171
172 this.localeObservable = this.http
173 .get(ServerService.BASE_LOCALE_URL + fileLocale + '/server.json')
174 .pipe(share())
147 } 175 }
148 176
149 private saveConfigLocally (config: ServerConfig) { 177 private saveConfigLocally (config: ServerConfig) {
diff --git a/client/src/app/shared/i18n/i18n-utils.ts b/client/src/app/shared/i18n/i18n-utils.ts
new file mode 100644
index 000000000..c1de51b7b
--- /dev/null
+++ b/client/src/app/shared/i18n/i18n-utils.ts
@@ -0,0 +1,7 @@
1function peertubeTranslate (str: string, translations: { [ id: string ]: string }) {
2 return translations[str] ? translations[str] : str
3}
4
5export {
6 peertubeTranslate
7}
diff --git a/client/src/app/shared/video/video-details.model.ts b/client/src/app/shared/video/video-details.model.ts
index 5fc55fca6..19c350ab3 100644
--- a/client/src/app/shared/video/video-details.model.ts
+++ b/client/src/app/shared/video/video-details.model.ts
@@ -15,8 +15,8 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
15 likesPercent: number 15 likesPercent: number
16 dislikesPercent: number 16 dislikesPercent: number
17 17
18 constructor (hash: VideoDetailsServerModel) { 18 constructor (hash: VideoDetailsServerModel, translations = {}) {
19 super(hash) 19 super(hash, translations)
20 20
21 this.descriptionPath = hash.descriptionPath 21 this.descriptionPath = hash.descriptionPath
22 this.files = hash.files 22 this.files = hash.files
diff --git a/client/src/app/shared/video/video.model.ts b/client/src/app/shared/video/video.model.ts
index 48d562f9c..d37dc2c3e 100644
--- a/client/src/app/shared/video/video.model.ts
+++ b/client/src/app/shared/video/video.model.ts
@@ -5,6 +5,7 @@ import { VideoConstant } from '../../../../../shared/models/videos/video.model'
5import { getAbsoluteAPIUrl } from '../misc/utils' 5import { getAbsoluteAPIUrl } from '../misc/utils'
6import { ServerConfig } from '../../../../../shared/models' 6import { ServerConfig } from '../../../../../shared/models'
7import { Actor } from '@app/shared/actor/actor.model' 7import { Actor } from '@app/shared/actor/actor.model'
8import { peertubeTranslate } from '@app/shared/i18n/i18n-utils'
8 9
9export class Video implements VideoServerModel { 10export class Video implements VideoServerModel {
10 by: string 11 by: string
@@ -68,7 +69,7 @@ export class Video implements VideoServerModel {
68 minutes.toString() + ':' + secondsPadding + seconds.toString() 69 minutes.toString() + ':' + secondsPadding + seconds.toString()
69 } 70 }
70 71
71 constructor (hash: VideoServerModel) { 72 constructor (hash: VideoServerModel, translations = {}) {
72 const absoluteAPIUrl = getAbsoluteAPIUrl() 73 const absoluteAPIUrl = getAbsoluteAPIUrl()
73 74
74 this.createdAt = new Date(hash.createdAt.toString()) 75 this.createdAt = new Date(hash.createdAt.toString())
@@ -98,6 +99,11 @@ export class Video implements VideoServerModel {
98 99
99 this.by = Actor.CREATE_BY_STRING(hash.account.name, hash.account.host) 100 this.by = Actor.CREATE_BY_STRING(hash.account.name, hash.account.host)
100 this.accountAvatarUrl = Actor.GET_ACTOR_AVATAR_URL(this.account) 101 this.accountAvatarUrl = Actor.GET_ACTOR_AVATAR_URL(this.account)
102
103 this.category.label = peertubeTranslate(this.category.label, translations)
104 this.licence.label = peertubeTranslate(this.licence.label, translations)
105 this.language.label = peertubeTranslate(this.language.label, translations)
106 this.privacy.label = peertubeTranslate(this.privacy.label, translations)
101 } 107 }
102 108
103 isVideoNSFWForUser (user: User, serverConfig: ServerConfig) { 109 isVideoNSFWForUser (user: User, serverConfig: ServerConfig) {
diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts
index d1e32faeb..c607b7d6a 100644
--- a/client/src/app/shared/video/video.service.ts
+++ b/client/src/app/shared/video/video.service.ts
@@ -1,4 +1,4 @@
1import { catchError, map } from 'rxjs/operators' 1import { catchError, map, switchMap } from 'rxjs/operators'
2import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http' 2import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http'
3import { Injectable } from '@angular/core' 3import { Injectable } from '@angular/core'
4import { Observable } from 'rxjs' 4import { Observable } from 'rxjs'
@@ -24,6 +24,7 @@ import { Account } from '@app/shared/account/account.model'
24import { AccountService } from '@app/shared/account/account.service' 24import { AccountService } from '@app/shared/account/account.service'
25import { VideoChannel } from '../../../../../shared/models/videos' 25import { VideoChannel } from '../../../../../shared/models/videos'
26import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' 26import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
27import { ServerService } from '@app/core'
27 28
28@Injectable() 29@Injectable()
29export class VideoService { 30export class VideoService {
@@ -33,7 +34,8 @@ export class VideoService {
33 constructor ( 34 constructor (
34 private authHttp: HttpClient, 35 private authHttp: HttpClient,
35 private restExtractor: RestExtractor, 36 private restExtractor: RestExtractor,
36 private restService: RestService 37 private restService: RestService,
38 private serverService: ServerService
37 ) {} 39 ) {}
38 40
39 getVideoViewUrl (uuid: string) { 41 getVideoViewUrl (uuid: string) {
@@ -41,9 +43,13 @@ export class VideoService {
41 } 43 }
42 44
43 getVideo (uuid: string): Observable<VideoDetails> { 45 getVideo (uuid: string): Observable<VideoDetails> {
44 return this.authHttp.get<VideoDetailsServerModel>(VideoService.BASE_VIDEO_URL + uuid) 46 return this.serverService.localeObservable
45 .pipe( 47 .pipe(
46 map(videoHash => new VideoDetails(videoHash)), 48 switchMap(translations => {
49 return this.authHttp.get<VideoDetailsServerModel>(VideoService.BASE_VIDEO_URL + uuid)
50 .pipe(map(videoHash => ({ videoHash, translations })))
51 }),
52 map(({ videoHash, translations }) => new VideoDetails(videoHash, translations)),
47 catchError(res => this.restExtractor.handleError(res)) 53 catchError(res => this.restExtractor.handleError(res))
48 ) 54 )
49 } 55 }
@@ -102,9 +108,10 @@ export class VideoService {
102 let params = new HttpParams() 108 let params = new HttpParams()
103 params = this.restService.addRestGetParams(params, pagination, sort) 109 params = this.restService.addRestGetParams(params, pagination, sort)
104 110
105 return this.authHttp.get(UserService.BASE_USERS_URL + '/me/videos', { params }) 111 return this.authHttp
112 .get<ResultList<Video>>(UserService.BASE_USERS_URL + '/me/videos', { params })
106 .pipe( 113 .pipe(
107 map(this.extractVideos), 114 switchMap(res => this.extractVideos(res)),
108 catchError(res => this.restExtractor.handleError(res)) 115 catchError(res => this.restExtractor.handleError(res))
109 ) 116 )
110 } 117 }
@@ -120,9 +127,9 @@ export class VideoService {
120 params = this.restService.addRestGetParams(params, pagination, sort) 127 params = this.restService.addRestGetParams(params, pagination, sort)
121 128
122 return this.authHttp 129 return this.authHttp
123 .get(AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/videos', { params }) 130 .get<ResultList<Video>>(AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/videos', { params })
124 .pipe( 131 .pipe(
125 map(this.extractVideos), 132 switchMap(res => this.extractVideos(res)),
126 catchError(res => this.restExtractor.handleError(res)) 133 catchError(res => this.restExtractor.handleError(res))
127 ) 134 )
128 } 135 }
@@ -138,9 +145,9 @@ export class VideoService {
138 params = this.restService.addRestGetParams(params, pagination, sort) 145 params = this.restService.addRestGetParams(params, pagination, sort)
139 146
140 return this.authHttp 147 return this.authHttp
141 .get(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.uuid + '/videos', { params }) 148 .get<ResultList<Video>>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.uuid + '/videos', { params })
142 .pipe( 149 .pipe(
143 map(this.extractVideos), 150 switchMap(res => this.extractVideos(res)),
144 catchError(res => this.restExtractor.handleError(res)) 151 catchError(res => this.restExtractor.handleError(res))
145 ) 152 )
146 } 153 }
@@ -160,9 +167,9 @@ export class VideoService {
160 } 167 }
161 168
162 return this.authHttp 169 return this.authHttp
163 .get(VideoService.BASE_VIDEO_URL, { params }) 170 .get<ResultList<Video>>(VideoService.BASE_VIDEO_URL, { params })
164 .pipe( 171 .pipe(
165 map(this.extractVideos), 172 switchMap(res => this.extractVideos(res)),
166 catchError(res => this.restExtractor.handleError(res)) 173 catchError(res => this.restExtractor.handleError(res))
167 ) 174 )
168 } 175 }
@@ -230,7 +237,7 @@ export class VideoService {
230 return this.authHttp 237 return this.authHttp
231 .get<ResultList<VideoServerModel>>(url, { params }) 238 .get<ResultList<VideoServerModel>>(url, { params })
232 .pipe( 239 .pipe(
233 map(this.extractVideos), 240 switchMap(res => this.extractVideos(res)),
234 catchError(res => this.restExtractor.handleError(res)) 241 catchError(res => this.restExtractor.handleError(res))
235 ) 242 )
236 } 243 }
@@ -287,14 +294,19 @@ export class VideoService {
287 } 294 }
288 295
289 private extractVideos (result: ResultList<VideoServerModel>) { 296 private extractVideos (result: ResultList<VideoServerModel>) {
290 const videosJson = result.data 297 return this.serverService.localeObservable
291 const totalVideos = result.total 298 .pipe(
292 const videos = [] 299 map(translations => {
293 300 const videosJson = result.data
294 for (const videoJson of videosJson) { 301 const totalVideos = result.total
295 videos.push(new Video(videoJson)) 302 const videos: Video[] = []
296 } 303
297 304 for (const videoJson of videosJson) {
298 return { videos, totalVideos } 305 videos.push(new Video(videoJson, translations))
306 }
307
308 return { videos, totalVideos }
309 })
310 )
299 } 311 }
300} 312}
diff --git a/client/src/locale/source/server_en_US.xml b/client/src/locale/source/server_en_US.xml
new file mode 100644
index 000000000..dab91f98d
--- /dev/null
+++ b/client/src/locale/source/server_en_US.xml
@@ -0,0 +1,878 @@
1<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">
2 <file original="namespace1" datatype="plaintext" source-language="undefined" target-language="undefined">
3 <body>
4 <trans-unit id="Music">
5 <source>Music</source>
6 <target>undefined</target>
7 </trans-unit>
8 <trans-unit id="Films">
9 <source>Films</source>
10 <target>undefined</target>
11 </trans-unit>
12 <trans-unit id="Vehicles">
13 <source>Vehicles</source>
14 <target>undefined</target>
15 </trans-unit>
16 <trans-unit id="Art">
17 <source>Art</source>
18 <target>undefined</target>
19 </trans-unit>
20 <trans-unit id="Sports">
21 <source>Sports</source>
22 <target>undefined</target>
23 </trans-unit>
24 <trans-unit id="Travels">
25 <source>Travels</source>
26 <target>undefined</target>
27 </trans-unit>
28 <trans-unit id="Gaming">
29 <source>Gaming</source>
30 <target>undefined</target>
31 </trans-unit>
32 <trans-unit id="People">
33 <source>People</source>
34 <target>undefined</target>
35 </trans-unit>
36 <trans-unit id="Comedy">
37 <source>Comedy</source>
38 <target>undefined</target>
39 </trans-unit>
40 <trans-unit id="Entertainment">
41 <source>Entertainment</source>
42 <target>undefined</target>
43 </trans-unit>
44 <trans-unit id="News">
45 <source>News</source>
46 <target>undefined</target>
47 </trans-unit>
48 <trans-unit id="How To">
49 <source>How To</source>
50 <target>undefined</target>
51 </trans-unit>
52 <trans-unit id="Education">
53 <source>Education</source>
54 <target>undefined</target>
55 </trans-unit>
56 <trans-unit id="Activism">
57 <source>Activism</source>
58 <target>undefined</target>
59 </trans-unit>
60 <trans-unit id="Science &amp; Technology">
61 <source>Science &amp; Technology</source>
62 <target>undefined</target>
63 </trans-unit>
64 <trans-unit id="Animals">
65 <source>Animals</source>
66 <target>undefined</target>
67 </trans-unit>
68 <trans-unit id="Kids">
69 <source>Kids</source>
70 <target>undefined</target>
71 </trans-unit>
72 <trans-unit id="Food">
73 <source>Food</source>
74 <target>undefined</target>
75 </trans-unit>
76 <trans-unit id="Attribution">
77 <source>Attribution</source>
78 <target>undefined</target>
79 </trans-unit>
80 <trans-unit id="Attribution - Share Alike">
81 <source>Attribution - Share Alike</source>
82 <target>undefined</target>
83 </trans-unit>
84 <trans-unit id="Attribution - No Derivatives">
85 <source>Attribution - No Derivatives</source>
86 <target>undefined</target>
87 </trans-unit>
88 <trans-unit id="Attribution - Non Commercial">
89 <source>Attribution - Non Commercial</source>
90 <target>undefined</target>
91 </trans-unit>
92 <trans-unit id="Attribution - Non Commercial - Share Alike">
93 <source>Attribution - Non Commercial - Share Alike</source>
94 <target>undefined</target>
95 </trans-unit>
96 <trans-unit id="Attribution - Non Commercial - No Derivatives">
97 <source>Attribution - Non Commercial - No Derivatives</source>
98 <target>undefined</target>
99 </trans-unit>
100 <trans-unit id="Public Domain Dedication">
101 <source>Public Domain Dedication</source>
102 <target>undefined</target>
103 </trans-unit>
104 <trans-unit id="Public">
105 <source>Public</source>
106 <target>undefined</target>
107 </trans-unit>
108 <trans-unit id="Unlisted">
109 <source>Unlisted</source>
110 <target>undefined</target>
111 </trans-unit>
112 <trans-unit id="Private">
113 <source>Private</source>
114 <target>undefined</target>
115 </trans-unit>
116 <trans-unit id="Afar">
117 <source>Afar</source>
118 <target>undefined</target>
119 </trans-unit>
120 <trans-unit id="Abkhazian">
121 <source>Abkhazian</source>
122 <target>undefined</target>
123 </trans-unit>
124 <trans-unit id="Afrikaans">
125 <source>Afrikaans</source>
126 <target>undefined</target>
127 </trans-unit>
128 <trans-unit id="Akan">
129 <source>Akan</source>
130 <target>undefined</target>
131 </trans-unit>
132 <trans-unit id="Amharic">
133 <source>Amharic</source>
134 <target>undefined</target>
135 </trans-unit>
136 <trans-unit id="Arabic">
137 <source>Arabic</source>
138 <target>undefined</target>
139 </trans-unit>
140 <trans-unit id="Aragonese">
141 <source>Aragonese</source>
142 <target>undefined</target>
143 </trans-unit>
144 <trans-unit id="American Sign Language">
145 <source>American Sign Language</source>
146 <target>undefined</target>
147 </trans-unit>
148 <trans-unit id="Assamese">
149 <source>Assamese</source>
150 <target>undefined</target>
151 </trans-unit>
152 <trans-unit id="Avaric">
153 <source>Avaric</source>
154 <target>undefined</target>
155 </trans-unit>
156 <trans-unit id="Aymara">
157 <source>Aymara</source>
158 <target>undefined</target>
159 </trans-unit>
160 <trans-unit id="Azerbaijani">
161 <source>Azerbaijani</source>
162 <target>undefined</target>
163 </trans-unit>
164 <trans-unit id="Bashkir">
165 <source>Bashkir</source>
166 <target>undefined</target>
167 </trans-unit>
168 <trans-unit id="Bambara">
169 <source>Bambara</source>
170 <target>undefined</target>
171 </trans-unit>
172 <trans-unit id="Belarusian">
173 <source>Belarusian</source>
174 <target>undefined</target>
175 </trans-unit>
176 <trans-unit id="Bengali">
177 <source>Bengali</source>
178 <target>undefined</target>
179 </trans-unit>
180 <trans-unit id="British Sign Language">
181 <source>British Sign Language</source>
182 <target>undefined</target>
183 </trans-unit>
184 <trans-unit id="Bislama">
185 <source>Bislama</source>
186 <target>undefined</target>
187 </trans-unit>
188 <trans-unit id="Tibetan">
189 <source>Tibetan</source>
190 <target>undefined</target>
191 </trans-unit>
192 <trans-unit id="Bosnian">
193 <source>Bosnian</source>
194 <target>undefined</target>
195 </trans-unit>
196 <trans-unit id="Breton">
197 <source>Breton</source>
198 <target>undefined</target>
199 </trans-unit>
200 <trans-unit id="Bulgarian">
201 <source>Bulgarian</source>
202 <target>undefined</target>
203 </trans-unit>
204 <trans-unit id="Brazilian Sign Language">
205 <source>Brazilian Sign Language</source>
206 <target>undefined</target>
207 </trans-unit>
208 <trans-unit id="Catalan">
209 <source>Catalan</source>
210 <target>undefined</target>
211 </trans-unit>
212 <trans-unit id="Czech">
213 <source>Czech</source>
214 <target>undefined</target>
215 </trans-unit>
216 <trans-unit id="Chamorro">
217 <source>Chamorro</source>
218 <target>undefined</target>
219 </trans-unit>
220 <trans-unit id="Chechen">
221 <source>Chechen</source>
222 <target>undefined</target>
223 </trans-unit>
224 <trans-unit id="Chuvash">
225 <source>Chuvash</source>
226 <target>undefined</target>
227 </trans-unit>
228 <trans-unit id="Cornish">
229 <source>Cornish</source>
230 <target>undefined</target>
231 </trans-unit>
232 <trans-unit id="Corsican">
233 <source>Corsican</source>
234 <target>undefined</target>
235 </trans-unit>
236 <trans-unit id="Cree">
237 <source>Cree</source>
238 <target>undefined</target>
239 </trans-unit>
240 <trans-unit id="Czech Sign Language">
241 <source>Czech Sign Language</source>
242 <target>undefined</target>
243 </trans-unit>
244 <trans-unit id="Chinese Sign Language">
245 <source>Chinese Sign Language</source>
246 <target>undefined</target>
247 </trans-unit>
248 <trans-unit id="Welsh">
249 <source>Welsh</source>
250 <target>undefined</target>
251 </trans-unit>
252 <trans-unit id="Danish">
253 <source>Danish</source>
254 <target>undefined</target>
255 </trans-unit>
256 <trans-unit id="German">
257 <source>German</source>
258 <target>undefined</target>
259 </trans-unit>
260 <trans-unit id="Dhivehi">
261 <source>Dhivehi</source>
262 <target>undefined</target>
263 </trans-unit>
264 <trans-unit id="Danish Sign Language">
265 <source>Danish Sign Language</source>
266 <target>undefined</target>
267 </trans-unit>
268 <trans-unit id="Dzongkha">
269 <source>Dzongkha</source>
270 <target>undefined</target>
271 </trans-unit>
272 <trans-unit id="Modern Greek (1453-)">
273 <source>Modern Greek (1453-)</source>
274 <target>undefined</target>
275 </trans-unit>
276 <trans-unit id="English">
277 <source>English</source>
278 <target>undefined</target>
279 </trans-unit>
280 <trans-unit id="Estonian">
281 <source>Estonian</source>
282 <target>undefined</target>
283 </trans-unit>
284 <trans-unit id="Basque">
285 <source>Basque</source>
286 <target>undefined</target>
287 </trans-unit>
288 <trans-unit id="Ewe">
289 <source>Ewe</source>
290 <target>undefined</target>
291 </trans-unit>
292 <trans-unit id="Faroese">
293 <source>Faroese</source>
294 <target>undefined</target>
295 </trans-unit>
296 <trans-unit id="Persian">
297 <source>Persian</source>
298 <target>undefined</target>
299 </trans-unit>
300 <trans-unit id="Fijian">
301 <source>Fijian</source>
302 <target>undefined</target>
303 </trans-unit>
304 <trans-unit id="Finnish">
305 <source>Finnish</source>
306 <target>undefined</target>
307 </trans-unit>
308 <trans-unit id="French">
309 <source>French</source>
310 <target>undefined</target>
311 </trans-unit>
312 <trans-unit id="Western Frisian">
313 <source>Western Frisian</source>
314 <target>undefined</target>
315 </trans-unit>
316 <trans-unit id="French Sign Language">
317 <source>French Sign Language</source>
318 <target>undefined</target>
319 </trans-unit>
320 <trans-unit id="Fulah">
321 <source>Fulah</source>
322 <target>undefined</target>
323 </trans-unit>
324 <trans-unit id="Scottish Gaelic">
325 <source>Scottish Gaelic</source>
326 <target>undefined</target>
327 </trans-unit>
328 <trans-unit id="Irish">
329 <source>Irish</source>
330 <target>undefined</target>
331 </trans-unit>
332 <trans-unit id="Galician">
333 <source>Galician</source>
334 <target>undefined</target>
335 </trans-unit>
336 <trans-unit id="Manx">
337 <source>Manx</source>
338 <target>undefined</target>
339 </trans-unit>
340 <trans-unit id="Guarani">
341 <source>Guarani</source>
342 <target>undefined</target>
343 </trans-unit>
344 <trans-unit id="German Sign Language">
345 <source>German Sign Language</source>
346 <target>undefined</target>
347 </trans-unit>
348 <trans-unit id="Gujarati">
349 <source>Gujarati</source>
350 <target>undefined</target>
351 </trans-unit>
352 <trans-unit id="Haitian">
353 <source>Haitian</source>
354 <target>undefined</target>
355 </trans-unit>
356 <trans-unit id="Hausa">
357 <source>Hausa</source>
358 <target>undefined</target>
359 </trans-unit>
360 <trans-unit id="Serbo-Croatian">
361 <source>Serbo-Croatian</source>
362 <target>undefined</target>
363 </trans-unit>
364 <trans-unit id="Hebrew">
365 <source>Hebrew</source>
366 <target>undefined</target>
367 </trans-unit>
368 <trans-unit id="Herero">
369 <source>Herero</source>
370 <target>undefined</target>
371 </trans-unit>
372 <trans-unit id="Hindi">
373 <source>Hindi</source>
374 <target>undefined</target>
375 </trans-unit>
376 <trans-unit id="Hiri Motu">
377 <source>Hiri Motu</source>
378 <target>undefined</target>
379 </trans-unit>
380 <trans-unit id="Croatian">
381 <source>Croatian</source>
382 <target>undefined</target>
383 </trans-unit>
384 <trans-unit id="Hungarian">
385 <source>Hungarian</source>
386 <target>undefined</target>
387 </trans-unit>
388 <trans-unit id="Armenian">
389 <source>Armenian</source>
390 <target>undefined</target>
391 </trans-unit>
392 <trans-unit id="Igbo">
393 <source>Igbo</source>
394 <target>undefined</target>
395 </trans-unit>
396 <trans-unit id="Sichuan Yi">
397 <source>Sichuan Yi</source>
398 <target>undefined</target>
399 </trans-unit>
400 <trans-unit id="Inuktitut">
401 <source>Inuktitut</source>
402 <target>undefined</target>
403 </trans-unit>
404 <trans-unit id="Indonesian">
405 <source>Indonesian</source>
406 <target>undefined</target>
407 </trans-unit>
408 <trans-unit id="Inupiaq">
409 <source>Inupiaq</source>
410 <target>undefined</target>
411 </trans-unit>
412 <trans-unit id="Icelandic">
413 <source>Icelandic</source>
414 <target>undefined</target>
415 </trans-unit>
416 <trans-unit id="Italian">
417 <source>Italian</source>
418 <target>undefined</target>
419 </trans-unit>
420 <trans-unit id="Javanese">
421 <source>Javanese</source>
422 <target>undefined</target>
423 </trans-unit>
424 <trans-unit id="Japanese">
425 <source>Japanese</source>
426 <target>undefined</target>
427 </trans-unit>
428 <trans-unit id="Japanese Sign Language">
429 <source>Japanese Sign Language</source>
430 <target>undefined</target>
431 </trans-unit>
432 <trans-unit id="Kalaallisut">
433 <source>Kalaallisut</source>
434 <target>undefined</target>
435 </trans-unit>
436 <trans-unit id="Kannada">
437 <source>Kannada</source>
438 <target>undefined</target>
439 </trans-unit>
440 <trans-unit id="Kashmiri">
441 <source>Kashmiri</source>
442 <target>undefined</target>
443 </trans-unit>
444 <trans-unit id="Georgian">
445 <source>Georgian</source>
446 <target>undefined</target>
447 </trans-unit>
448 <trans-unit id="Kanuri">
449 <source>Kanuri</source>
450 <target>undefined</target>
451 </trans-unit>
452 <trans-unit id="Kazakh">
453 <source>Kazakh</source>
454 <target>undefined</target>
455 </trans-unit>
456 <trans-unit id="Khmer">
457 <source>Khmer</source>
458 <target>undefined</target>
459 </trans-unit>
460 <trans-unit id="Kikuyu">
461 <source>Kikuyu</source>
462 <target>undefined</target>
463 </trans-unit>
464 <trans-unit id="Kinyarwanda">
465 <source>Kinyarwanda</source>
466 <target>undefined</target>
467 </trans-unit>
468 <trans-unit id="Kirghiz">
469 <source>Kirghiz</source>
470 <target>undefined</target>
471 </trans-unit>
472 <trans-unit id="Komi">
473 <source>Komi</source>
474 <target>undefined</target>
475 </trans-unit>
476 <trans-unit id="Kongo">
477 <source>Kongo</source>
478 <target>undefined</target>
479 </trans-unit>
480 <trans-unit id="Korean">
481 <source>Korean</source>
482 <target>undefined</target>
483 </trans-unit>
484 <trans-unit id="Kuanyama">
485 <source>Kuanyama</source>
486 <target>undefined</target>
487 </trans-unit>
488 <trans-unit id="Kurdish">
489 <source>Kurdish</source>
490 <target>undefined</target>
491 </trans-unit>
492 <trans-unit id="Lao">
493 <source>Lao</source>
494 <target>undefined</target>
495 </trans-unit>
496 <trans-unit id="Latvian">
497 <source>Latvian</source>
498 <target>undefined</target>
499 </trans-unit>
500 <trans-unit id="Limburgan">
501 <source>Limburgan</source>
502 <target>undefined</target>
503 </trans-unit>
504 <trans-unit id="Lingala">
505 <source>Lingala</source>
506 <target>undefined</target>
507 </trans-unit>
508 <trans-unit id="Lithuanian">
509 <source>Lithuanian</source>
510 <target>undefined</target>
511 </trans-unit>
512 <trans-unit id="Luxembourgish">
513 <source>Luxembourgish</source>
514 <target>undefined</target>
515 </trans-unit>
516 <trans-unit id="Luba-Katanga">
517 <source>Luba-Katanga</source>
518 <target>undefined</target>
519 </trans-unit>
520 <trans-unit id="Ganda">
521 <source>Ganda</source>
522 <target>undefined</target>
523 </trans-unit>
524 <trans-unit id="Marshallese">
525 <source>Marshallese</source>
526 <target>undefined</target>
527 </trans-unit>
528 <trans-unit id="Malayalam">
529 <source>Malayalam</source>
530 <target>undefined</target>
531 </trans-unit>
532 <trans-unit id="Marathi">
533 <source>Marathi</source>
534 <target>undefined</target>
535 </trans-unit>
536 <trans-unit id="Macedonian">
537 <source>Macedonian</source>
538 <target>undefined</target>
539 </trans-unit>
540 <trans-unit id="Malagasy">
541 <source>Malagasy</source>
542 <target>undefined</target>
543 </trans-unit>
544 <trans-unit id="Maltese">
545 <source>Maltese</source>
546 <target>undefined</target>
547 </trans-unit>
548 <trans-unit id="Mongolian">
549 <source>Mongolian</source>
550 <target>undefined</target>
551 </trans-unit>
552 <trans-unit id="Maori">
553 <source>Maori</source>
554 <target>undefined</target>
555 </trans-unit>
556 <trans-unit id="Malay (macrolanguage)">
557 <source>Malay (macrolanguage)</source>
558 <target>undefined</target>
559 </trans-unit>
560 <trans-unit id="Burmese">
561 <source>Burmese</source>
562 <target>undefined</target>
563 </trans-unit>
564 <trans-unit id="Nauru">
565 <source>Nauru</source>
566 <target>undefined</target>
567 </trans-unit>
568 <trans-unit id="Navajo">
569 <source>Navajo</source>
570 <target>undefined</target>
571 </trans-unit>
572 <trans-unit id="South Ndebele">
573 <source>South Ndebele</source>
574 <target>undefined</target>
575 </trans-unit>
576 <trans-unit id="North Ndebele">
577 <source>North Ndebele</source>
578 <target>undefined</target>
579 </trans-unit>
580 <trans-unit id="Ndonga">
581 <source>Ndonga</source>
582 <target>undefined</target>
583 </trans-unit>
584 <trans-unit id="Nepali (macrolanguage)">
585 <source>Nepali (macrolanguage)</source>
586 <target>undefined</target>
587 </trans-unit>
588 <trans-unit id="Dutch">
589 <source>Dutch</source>
590 <target>undefined</target>
591 </trans-unit>
592 <trans-unit id="Norwegian Nynorsk">
593 <source>Norwegian Nynorsk</source>
594 <target>undefined</target>
595 </trans-unit>
596 <trans-unit id="Norwegian Bokmål">
597 <source>Norwegian Bokmål</source>
598 <target>undefined</target>
599 </trans-unit>
600 <trans-unit id="Norwegian">
601 <source>Norwegian</source>
602 <target>undefined</target>
603 </trans-unit>
604 <trans-unit id="Nyanja">
605 <source>Nyanja</source>
606 <target>undefined</target>
607 </trans-unit>
608 <trans-unit id="Occitan (post 1500)">
609 <source>Occitan (post 1500)</source>
610 <target>undefined</target>
611 </trans-unit>
612 <trans-unit id="Ojibwa">
613 <source>Ojibwa</source>
614 <target>undefined</target>
615 </trans-unit>
616 <trans-unit id="Oriya (macrolanguage)">
617 <source>Oriya (macrolanguage)</source>
618 <target>undefined</target>
619 </trans-unit>
620 <trans-unit id="Oromo">
621 <source>Oromo</source>
622 <target>undefined</target>
623 </trans-unit>
624 <trans-unit id="Ossetian">
625 <source>Ossetian</source>
626 <target>undefined</target>
627 </trans-unit>
628 <trans-unit id="Panjabi">
629 <source>Panjabi</source>
630 <target>undefined</target>
631 </trans-unit>
632 <trans-unit id="Pakistan Sign Language">
633 <source>Pakistan Sign Language</source>
634 <target>undefined</target>
635 </trans-unit>
636 <trans-unit id="Polish">
637 <source>Polish</source>
638 <target>undefined</target>
639 </trans-unit>
640 <trans-unit id="Portuguese">
641 <source>Portuguese</source>
642 <target>undefined</target>
643 </trans-unit>
644 <trans-unit id="Pushto">
645 <source>Pushto</source>
646 <target>undefined</target>
647 </trans-unit>
648 <trans-unit id="Quechua">
649 <source>Quechua</source>
650 <target>undefined</target>
651 </trans-unit>
652 <trans-unit id="Romansh">
653 <source>Romansh</source>
654 <target>undefined</target>
655 </trans-unit>
656 <trans-unit id="Romanian">
657 <source>Romanian</source>
658 <target>undefined</target>
659 </trans-unit>
660 <trans-unit id="Russian Sign Language">
661 <source>Russian Sign Language</source>
662 <target>undefined</target>
663 </trans-unit>
664 <trans-unit id="Rundi">
665 <source>Rundi</source>
666 <target>undefined</target>
667 </trans-unit>
668 <trans-unit id="Russian">
669 <source>Russian</source>
670 <target>undefined</target>
671 </trans-unit>
672 <trans-unit id="Sango">
673 <source>Sango</source>
674 <target>undefined</target>
675 </trans-unit>
676 <trans-unit id="Saudi Arabian Sign Language">
677 <source>Saudi Arabian Sign Language</source>
678 <target>undefined</target>
679 </trans-unit>
680 <trans-unit id="South African Sign Language">
681 <source>South African Sign Language</source>
682 <target>undefined</target>
683 </trans-unit>
684 <trans-unit id="Sinhala">
685 <source>Sinhala</source>
686 <target>undefined</target>
687 </trans-unit>
688 <trans-unit id="Slovak">
689 <source>Slovak</source>
690 <target>undefined</target>
691 </trans-unit>
692 <trans-unit id="Slovenian">
693 <source>Slovenian</source>
694 <target>undefined</target>
695 </trans-unit>
696 <trans-unit id="Northern Sami">
697 <source>Northern Sami</source>
698 <target>undefined</target>
699 </trans-unit>
700 <trans-unit id="Samoan">
701 <source>Samoan</source>
702 <target>undefined</target>
703 </trans-unit>
704 <trans-unit id="Shona">
705 <source>Shona</source>
706 <target>undefined</target>
707 </trans-unit>
708 <trans-unit id="Sindhi">
709 <source>Sindhi</source>
710 <target>undefined</target>
711 </trans-unit>
712 <trans-unit id="Somali">
713 <source>Somali</source>
714 <target>undefined</target>
715 </trans-unit>
716 <trans-unit id="Southern Sotho">
717 <source>Southern Sotho</source>
718 <target>undefined</target>
719 </trans-unit>
720 <trans-unit id="Spanish">
721 <source>Spanish</source>
722 <target>undefined</target>
723 </trans-unit>
724 <trans-unit id="Albanian">
725 <source>Albanian</source>
726 <target>undefined</target>
727 </trans-unit>
728 <trans-unit id="Sardinian">
729 <source>Sardinian</source>
730 <target>undefined</target>
731 </trans-unit>
732 <trans-unit id="Serbian">
733 <source>Serbian</source>
734 <target>undefined</target>
735 </trans-unit>
736 <trans-unit id="Swati">
737 <source>Swati</source>
738 <target>undefined</target>
739 </trans-unit>
740 <trans-unit id="Sundanese">
741 <source>Sundanese</source>
742 <target>undefined</target>
743 </trans-unit>
744 <trans-unit id="Swahili (macrolanguage)">
745 <source>Swahili (macrolanguage)</source>
746 <target>undefined</target>
747 </trans-unit>
748 <trans-unit id="Swedish">
749 <source>Swedish</source>
750 <target>undefined</target>
751 </trans-unit>
752 <trans-unit id="Swedish Sign Language">
753 <source>Swedish Sign Language</source>
754 <target>undefined</target>
755 </trans-unit>
756 <trans-unit id="Tahitian">
757 <source>Tahitian</source>
758 <target>undefined</target>
759 </trans-unit>
760 <trans-unit id="Tamil">
761 <source>Tamil</source>
762 <target>undefined</target>
763 </trans-unit>
764 <trans-unit id="Tatar">
765 <source>Tatar</source>
766 <target>undefined</target>
767 </trans-unit>
768 <trans-unit id="Telugu">
769 <source>Telugu</source>
770 <target>undefined</target>
771 </trans-unit>
772 <trans-unit id="Tajik">
773 <source>Tajik</source>
774 <target>undefined</target>
775 </trans-unit>
776 <trans-unit id="Tagalog">
777 <source>Tagalog</source>
778 <target>undefined</target>
779 </trans-unit>
780 <trans-unit id="Thai">
781 <source>Thai</source>
782 <target>undefined</target>
783 </trans-unit>
784 <trans-unit id="Tigrinya">
785 <source>Tigrinya</source>
786 <target>undefined</target>
787 </trans-unit>
788 <trans-unit id="Tonga (Tonga Islands)">
789 <source>Tonga (Tonga Islands)</source>
790 <target>undefined</target>
791 </trans-unit>
792 <trans-unit id="Tswana">
793 <source>Tswana</source>
794 <target>undefined</target>
795 </trans-unit>
796 <trans-unit id="Tsonga">
797 <source>Tsonga</source>
798 <target>undefined</target>
799 </trans-unit>
800 <trans-unit id="Turkmen">
801 <source>Turkmen</source>
802 <target>undefined</target>
803 </trans-unit>
804 <trans-unit id="Turkish">
805 <source>Turkish</source>
806 <target>undefined</target>
807 </trans-unit>
808 <trans-unit id="Twi">
809 <source>Twi</source>
810 <target>undefined</target>
811 </trans-unit>
812 <trans-unit id="Uighur">
813 <source>Uighur</source>
814 <target>undefined</target>
815 </trans-unit>
816 <trans-unit id="Ukrainian">
817 <source>Ukrainian</source>
818 <target>undefined</target>
819 </trans-unit>
820 <trans-unit id="Urdu">
821 <source>Urdu</source>
822 <target>undefined</target>
823 </trans-unit>
824 <trans-unit id="Uzbek">
825 <source>Uzbek</source>
826 <target>undefined</target>
827 </trans-unit>
828 <trans-unit id="Venda">
829 <source>Venda</source>
830 <target>undefined</target>
831 </trans-unit>
832 <trans-unit id="Vietnamese">
833 <source>Vietnamese</source>
834 <target>undefined</target>
835 </trans-unit>
836 <trans-unit id="Walloon">
837 <source>Walloon</source>
838 <target>undefined</target>
839 </trans-unit>
840 <trans-unit id="Wolof">
841 <source>Wolof</source>
842 <target>undefined</target>
843 </trans-unit>
844 <trans-unit id="Xhosa">
845 <source>Xhosa</source>
846 <target>undefined</target>
847 </trans-unit>
848 <trans-unit id="Yiddish">
849 <source>Yiddish</source>
850 <target>undefined</target>
851 </trans-unit>
852 <trans-unit id="Yoruba">
853 <source>Yoruba</source>
854 <target>undefined</target>
855 </trans-unit>
856 <trans-unit id="Zhuang">
857 <source>Zhuang</source>
858 <target>undefined</target>
859 </trans-unit>
860 <trans-unit id="Chinese">
861 <source>Chinese</source>
862 <target>undefined</target>
863 </trans-unit>
864 <trans-unit id="Zulu">
865 <source>Zulu</source>
866 <target>undefined</target>
867 </trans-unit>
868 <trans-unit id="Misc">
869 <source>Misc</source>
870 <target>undefined</target>
871 </trans-unit>
872 <trans-unit id="Unknown">
873 <source>Unknown</source>
874 <target>undefined</target>
875 </trans-unit>
876 </body>
877 </file>
878</xliff> \ No newline at end of file
diff --git a/client/src/locale/target/player_fr.xml b/client/src/locale/target/player_fr.xml
deleted file mode 100644
index eafa4baff..000000000
--- a/client/src/locale/target/player_fr.xml
+++ /dev/null
@@ -1,379 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--XLIFF document generated by Zanata. Visit http://zanata.org for more infomation.-->
3<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">
4 <file source-language="en-US" datatype="plaintext" original="" target-language="fr">
5 <body>
6 <trans-unit id="Audio Player">
7 <source>Audio Player</source>
8 <target>Lecteur audio</target>
9 </trans-unit>
10 <trans-unit id="Video Player">
11 <source>Video Player</source>
12 <target>Lecteur vidéo</target>
13 </trans-unit>
14 <trans-unit id="Play">
15 <source>Play</source>
16 <target>Lecture</target>
17 </trans-unit>
18 <trans-unit id="Pause">
19 <source>Pause</source>
20 <target>Pause</target>
21 </trans-unit>
22 <trans-unit id="Replay">
23 <source>Replay</source>
24 <target>Revoir</target>
25 </trans-unit>
26 <trans-unit id="Current Time">
27 <source>Current Time</source>
28 <target>Temps actuel</target>
29 </trans-unit>
30 <trans-unit id="Duration">
31 <source>Duration</source>
32 <target>Durée</target>
33 </trans-unit>
34 <trans-unit id="Remaining Time">
35 <source>Remaining Time</source>
36 <target>Temps restant</target>
37 </trans-unit>
38 <trans-unit id="Stream Type">
39 <source>Stream Type</source>
40 <target>Type de flux</target>
41 </trans-unit>
42 <trans-unit id="LIVE">
43 <source>LIVE</source>
44 <target>EN DIRECT</target>
45 </trans-unit>
46 <trans-unit id="Loaded">
47 <source>Loaded</source>
48 <target>Chargé</target>
49 </trans-unit>
50 <trans-unit id="Progress">
51 <source>Progress</source>
52 <target>Progression</target>
53 </trans-unit>
54 <trans-unit id="Progress Bar">
55 <source>Progress Bar</source>
56 <target>Barre de progression</target>
57 </trans-unit>
58 <trans-unit id="progress bar timing: currentTime={1} duration={2}">
59 <source>{1} of {2}</source>
60 <target>{1} de {2}</target>
61 </trans-unit>
62 <trans-unit id="Fullscreen">
63 <source>Fullscreen</source>
64 <target>Plein écran</target>
65 </trans-unit>
66 <trans-unit id="Non-Fullscreen">
67 <source>Non-Fullscreen</source>
68 <target>Fenêtré</target>
69 </trans-unit>
70 <trans-unit id="Mute">
71 <source>Mute</source>
72 <target>Sourdine</target>
73 </trans-unit>
74 <trans-unit id="Unmute">
75 <source>Unmute</source>
76 <target>Son activé</target>
77 </trans-unit>
78 <trans-unit id="Playback Rate">
79 <source>Playback Rate</source>
80 <target>Vitesse de lecture</target>
81 </trans-unit>
82 <trans-unit id="Subtitles">
83 <source>Subtitles</source>
84 <target>Sous-titres</target>
85 </trans-unit>
86 <trans-unit id="subtitles off">
87 <source>subtitles off</source>
88 <target>Sous-titres désactivés</target>
89 </trans-unit>
90 <trans-unit id="Captions">
91 <source>Captions</source>
92 <target>Sous-titres transcrits</target>
93 </trans-unit>
94 <trans-unit id="captions off">
95 <source>captions off</source>
96 <target>Sous-titres transcrits désactivés</target>
97 </trans-unit>
98 <trans-unit id="Chapters">
99 <source>Chapters</source>
100 <target>Chapitres</target>
101 </trans-unit>
102 <trans-unit id="Descriptions">
103 <source>Descriptions</source>
104 <target>Descriptions</target>
105 </trans-unit>
106 <trans-unit id="descriptions off">
107 <source>descriptions off</source>
108 <target>descriptions désactivées</target>
109 </trans-unit>
110 <trans-unit id="Audio Track">
111 <source>Audio Track</source>
112 <target>Piste audio</target>
113 </trans-unit>
114 <trans-unit id="Volume Level">
115 <source>Volume Level</source>
116 <target>Niveau de volume</target>
117 </trans-unit>
118 <trans-unit id="You aborted the media playback">
119 <source>You aborted the media playback</source>
120 <target>Vous avez interrompu la lecture de la vidéo.</target>
121 </trans-unit>
122 <trans-unit id="A network error caused the media download to fail part-way.">
123 <source>A network error caused the media download to fail part-way.</source>
124 <target>Une erreur de réseau a interrompu le téléchargement de la vidéo.</target>
125 </trans-unit>
126 <trans-unit id="The media could not be loaded, either because the server or network failed or because the format is not supported.">
127 <source>The media could not be loaded, either because the server or network failed or because the format is not supported.</source>
128 <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>
129 </trans-unit>
130 <trans-unit id="The media playback was aborted due to a corruption problem or because the media used features your browser did not support.">
131 <source>The media playback was aborted due to a corruption problem or because the media used features your browser did not support.</source>
132 <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>
133 </trans-unit>
134 <trans-unit id="No compatible source was found for this media.">
135 <source>No compatible source was found for this media.</source>
136 <target>Aucune source compatible n'a été trouvée pour cette vidéo.</target>
137 </trans-unit>
138 <trans-unit id="The media is encrypted and we do not have the keys to decrypt it.">
139 <source>The media is encrypted and we do not have the keys to decrypt it.</source>
140 <target>Le média est chiffré et nous n'avons pas les clés pour le déchiffrer.</target>
141 </trans-unit>
142 <trans-unit id="Play Video">
143 <source>Play Video</source>
144 <target>Lire la vidéo</target>
145 </trans-unit>
146 <trans-unit id="Close">
147 <source>Close</source>
148 <target>Fermer</target>
149 </trans-unit>
150 <trans-unit id="Close Modal Dialog">
151 <source>Close Modal Dialog</source>
152 <target>Fermer la boîte de dialogue modale</target>
153 </trans-unit>
154 <trans-unit id="Modal Window">
155 <source>Modal Window</source>
156 <target>Fenêtre modale</target>
157 </trans-unit>
158 <trans-unit id="This is a modal window">
159 <source>This is a modal window</source>
160 <target>Ceci est une fenêtre modale</target>
161 </trans-unit>
162 <trans-unit id="This modal can be closed by pressing the Escape key or activating the close button.">
163 <source>This modal can be closed by pressing the Escape key or activating the close button.</source>
164 <target>Ce modal peut être fermé en appuyant sur la touche Échap ou activer le bouton de fermeture.</target>
165 </trans-unit>
166 <trans-unit id=", opens captions settings dialog">
167 <source>, opens captions settings dialog</source>
168 <target>, ouvrir les paramètres des sous-titres transcrits</target>
169 </trans-unit>
170 <trans-unit id=", opens subtitles settings dialog">
171 <source>, opens subtitles settings dialog</source>
172 <target>, ouvrir les paramètres des sous-titres</target>
173 </trans-unit>
174 <trans-unit id=", opens descriptions settings dialog">
175 <source>, opens descriptions settings dialog</source>
176 <target>, ouvrir les paramètres des descriptions</target>
177 </trans-unit>
178 <trans-unit id=", selected">
179 <source>, selected</source>
180 <target>, sélectionné</target>
181 </trans-unit>
182 <trans-unit id="captions settings">
183 <source>captions settings</source>
184 <target>Paramètres des sous-titres transcrits</target>
185 </trans-unit>
186 <trans-unit id="subtitles settings">
187 <source>subititles settings</source>
188 <target>Paramètres des sous-titres</target>
189 </trans-unit>
190 <trans-unit id="descriptions settings">
191 <source>descriptions settings</source>
192 <target>Paramètres des descriptions</target>
193 </trans-unit>
194 <trans-unit id="Text">
195 <source>Text</source>
196 <target>Texte</target>
197 </trans-unit>
198 <trans-unit id="White">
199 <source>White</source>
200 <target>Blanc</target>
201 </trans-unit>
202 <trans-unit id="Black">
203 <source>Black</source>
204 <target>Noir</target>
205 </trans-unit>
206 <trans-unit id="Red">
207 <source>Red</source>
208 <target>Rouge</target>
209 </trans-unit>
210 <trans-unit id="Green">
211 <source>Green</source>
212 <target>Vert</target>
213 </trans-unit>
214 <trans-unit id="Blue">
215 <source>Blue</source>
216 <target>Bleu</target>
217 </trans-unit>
218 <trans-unit id="Yellow">
219 <source>Yellow</source>
220 <target>Jaune</target>
221 </trans-unit>
222 <trans-unit id="Magenta">
223 <source>Magenta</source>
224 <target>Magenta</target>
225 </trans-unit>
226 <trans-unit id="Cyan">
227 <source>Cyan</source>
228 <target>Cyan</target>
229 </trans-unit>
230 <trans-unit id="Background">
231 <source>Background</source>
232 <target>Arrière-plan</target>
233 </trans-unit>
234 <trans-unit id="Window">
235 <source>Window</source>
236 <target>Fenêtre</target>
237 </trans-unit>
238 <trans-unit id="Transparent">
239 <source>Transparent</source>
240 <target>Transparent</target>
241 </trans-unit>
242 <trans-unit id="Semi-Transparent">
243 <source>Semi-Transparent</source>
244 <target>Semi-transparent</target>
245 </trans-unit>
246 <trans-unit id="Opaque">
247 <source>Opaque</source>
248 <target>Opaque</target>
249 </trans-unit>
250 <trans-unit id="Font Size">
251 <source>Font Size</source>
252 <target>Taille des caractères</target>
253 </trans-unit>
254 <trans-unit id="Text Edge Style">
255 <source>Text Edge Style</source>
256 <target>Style des contours du texte</target>
257 </trans-unit>
258 <trans-unit id="None">
259 <source>None</source>
260 <target>Aucun</target>
261 </trans-unit>
262 <trans-unit id="Raised">
263 <source>Raised</source>
264 <target>Élevé</target>
265 </trans-unit>
266 <trans-unit id="Depressed">
267 <source>Depressed</source>
268 <target>Enfoncé</target>
269 </trans-unit>
270 <trans-unit id="Uniform">
271 <source>Uniform</source>
272 <target>Uniforme</target>
273 </trans-unit>
274 <trans-unit id="Dropshadow">
275 <source>Dropshadow</source>
276 <target>Ombre portée</target>
277 </trans-unit>
278 <trans-unit id="Font Family">
279 <source>Font Family</source>
280 <target>Familles de polices</target>
281 </trans-unit>
282 <trans-unit id="Proportional Sans-Serif">
283 <source>Proportional Sans-Serif</source>
284 <target>Polices à chasse variable sans empattement (Proportional Sans-Serif)</target>
285 </trans-unit>
286 <trans-unit id="Monospace Sans-Serif">
287 <source>Monospace Sans-Serif</source>
288 <target>Polices à chasse fixe sans empattement (Monospace Sans-Serif)</target>
289 </trans-unit>
290 <trans-unit id="Proportional Serif">
291 <source>Proportional Serif</source>
292 <target>Polices à chasse variable avec empattement (Proportional Serif)</target>
293 </trans-unit>
294 <trans-unit id="Monospace Serif">
295 <source>Monospace Serif</source>
296 <target>Polices à chasse fixe avec empattement (Monospace Serif)</target>
297 </trans-unit>
298 <trans-unit id="Casual">
299 <source>Casual</source>
300 <target>Manuscrite</target>
301 </trans-unit>
302 <trans-unit id="Script">
303 <source>Script</source>
304 <target>Scripte</target>
305 </trans-unit>
306 <trans-unit id="Small Caps">
307 <source>Small Caps</source>
308 <target>Petites capitales</target>
309 </trans-unit>
310 <trans-unit id="Reset">
311 <source>Reset</source>
312 <target>Réinitialiser</target>
313 </trans-unit>
314 <trans-unit id="restore all settings to the default values">
315 <source>restore all settings to the default values</source>
316 <target>Restaurer tous les paramètres aux valeurs par défaut</target>
317 </trans-unit>
318 <trans-unit id="Done">
319 <source>Done</source>
320 <target>Terminé</target>
321 </trans-unit>
322 <trans-unit id="Caption Settings Dialog">
323 <source>Caption Settings Dialog</source>
324 <target>Boîte de dialogue des paramètres des sous-titres transcrits</target>
325 </trans-unit>
326 <trans-unit id="Beginning of dialog window. Escape will cancel and close the window.">
327 <source>Beginning of dialog window. Escape will cancel and close the window.</source>
328 <target>Début de la fenêtre de dialogue. La touche d'échappement annulera et fermera la fenêtre.</target>
329 </trans-unit>
330 <trans-unit id="End of dialog window.">
331 <source>End of dialog window.</source>
332 <target>Fin de la fenêtre de dialogue.</target>
333 </trans-unit>
334 <trans-unit id="{1} is loading.">
335 <source>{1} is loading.</source>
336 <target>{1} est en train de charger</target>
337 </trans-unit>
338 <trans-unit id="Quality">
339 <source>Quality</source>
340 <target>Qualité</target>
341 </trans-unit>
342 <trans-unit id="Auto">
343 <source>Auto</source>
344 <target>Auto</target>
345 </trans-unit>
346 <trans-unit id="Speed">
347 <source>Speed</source>
348 <target>Vitesse</target>
349 </trans-unit>
350 <trans-unit id="peers">
351 <source>peers</source>
352 <target>pairs</target>
353 </trans-unit>
354 <trans-unit id="Go to the video page">
355 <source>Go to the video page</source>
356 <target>Aller sur la page de la vidéo</target>
357 </trans-unit>
358 <trans-unit id="Settings">
359 <source>Settings</source>
360 <target>Paramètres</target>
361 </trans-unit>
362 <trans-unit id="Uses P2P, others may know you are watching this video.">
363 <source>Uses P2P, others may know you are watching this video.</source>
364 <target>Utilise le P2P, d'autres personnes pourraient savoir que vous regardez cette vidéo.</target>
365 </trans-unit>
366 <trans-unit id="Copy the video URL">
367 <source>Copy the video URL</source>
368 <target>Copier le lien de la vidéo</target>
369 </trans-unit>
370 <trans-unit id="Copy the video URL at the current time">
371 <source>Copy the video URL at the current time</source>
372 <target>Copier le lien de la vidéo à partir de cette séquence</target>
373 </trans-unit>
374 <trans-unit id="Copy embed code">
375 <source>Copy embed code</source>
376 <target>Copier le code d'intégration</target>
377 </trans-unit>
378 </body>
379 </file></xliff> \ No newline at end of file
diff --git a/client/src/locale/target/server_fr.json b/client/src/locale/target/server_fr.json
new file mode 100644
index 000000000..43216adf4
--- /dev/null
+++ b/client/src/locale/target/server_fr.json
@@ -0,0 +1 @@
{"Music":"Musique","Films":"Films","Vehicles":"Transport","Art":"Art","Sports":"Sports","Travels":"Voyages","Gaming":"Jeux vidéos","People":"People","Comedy":"Humour","Entertainment":"Divertissement","News":"Actualités","How To":"Tutoriel","Education":"Éducation","Activism":"Activisme","Science & Technology":"Science & Technologie","Animals":"Animaux","Kids":"Enfants","Food":"Cuisine","Attribution":"Attribution","Attribution - Share Alike":"Attribution - Partage dans les mêmes conditions","Attribution - No Derivatives":"Attribution - Pas d'oeuvre dérivée","Attribution - Non Commercial":"Attribution - Utilisation non commerciale","Attribution - Non Commercial - Share Alike":"Attribution - Utilisation non commerciale - Partage dans les mêmes conditions","Attribution - Non Commercial - No Derivatives":"Attribution - Utilisation non commerciale - Pas d'oeuvre dérivée","Public Domain Dedication":"Domaine public","Public":"Publique","Unlisted":"Non listée","Private":"Privée","French":"Français","French Sign Language":"Langage des signes français","Misc":"Divers","Unknown":"Inconnu"} \ No newline at end of file
diff --git a/scripts/i18n/create-custom-files.ts b/scripts/i18n/create-custom-files.ts
index d8a87f291..3519afd47 100755
--- a/scripts/i18n/create-custom-files.ts
+++ b/scripts/i18n/create-custom-files.ts
@@ -1,12 +1,15 @@
1import * as jsToXliff12 from 'xliff/jsToXliff12' 1import * as jsToXliff12 from 'xliff/jsToXliff12'
2import { writeFile } from 'fs' 2import { writeFile } from 'fs'
3import { join } from 'path' 3import { join } from 'path'
4import { buildLanguages, VIDEO_CATEGORIES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../server/initializers/constants'
5import { values } from 'lodash'
4 6
5// First, the player 7type TranslationType = {
6const playerSource = join(__dirname, '../../../client/src/locale/source/videojs_en_US.json') 8 target: string
7const playerTarget = join(__dirname, '../../../client/src/locale/source/player_en_US.xml') 9 data: { [id: string]: string }
10}
8 11
9const videojs = require(playerSource) 12const videojs = require(join(__dirname, '../../../client/src/locale/source/videojs_en_US.json'))
10const playerKeys = { 13const playerKeys = {
11 'Quality': 'Quality', 14 'Quality': 'Quality',
12 'Auto': 'Auto', 15 'Auto': 'Auto',
@@ -19,36 +22,65 @@ const playerKeys = {
19 'Copy the video URL at the current time': 'Copy the video URL at the current time', 22 'Copy the video URL at the current time': 'Copy the video URL at the current time',
20 'Copy embed code': 'Copy embed code' 23 'Copy embed code': 'Copy embed code'
21} 24}
22 25const playerTranslations = {
23const obj = { 26 target: join(__dirname, '../../../client/src/locale/source/player_en_US.xml'),
24 resources: { 27 data: Object.assign({}, videojs, playerKeys)
25 namespace1: {}
26 }
27} 28}
28 29
29for (const sourceObject of [ videojs, playerKeys ]) { 30// Server keys
30 Object.keys(sourceObject).forEach(k => obj.resources.namespace1[ k ] = { source: sourceObject[ k ] }) 31const serverKeys: any = {}
32values(VIDEO_CATEGORIES)
33 .concat(values(VIDEO_LICENCES))
34 .concat(values(VIDEO_PRIVACIES))
35 .forEach(v => serverKeys[v] = v)
36
37// ISO 639 keys
38const languages = buildLanguages()
39Object.keys(languages).forEach(k => serverKeys[languages[k]] = languages[k])
40
41// More keys
42Object.assign(serverKeys, {
43 'Misc': 'Misc',
44 'Unknown': 'Unknown'
45})
46
47const serverTranslations = {
48 target: join(__dirname, '../../../client/src/locale/source/server_en_US.xml'),
49 data: serverKeys
31} 50}
32 51
33saveToXliffFile(playerTarget, obj, err => { 52saveToXliffFile(playerTranslations, err => {
34 if (err) { 53 if (err) return handleError(err)
35 console.error(err) 54
36 process.exit(-1) 55 saveToXliffFile(serverTranslations, err => {
37 } 56 if (err) return handleError(err)
38 57
39 process.exit(0) 58 process.exit(0)
59 })
40}) 60})
41 61
42// Then, the server strings 62// Then, the server strings
43 63
44function saveToXliffFile (targetPath: string, obj: any, cb: Function) { 64function saveToXliffFile (jsonTranslations: TranslationType, cb: Function) {
65 const obj = {
66 resources: {
67 namespace1: {}
68 }
69 }
70 Object.keys(jsonTranslations.data).forEach(k => obj.resources.namespace1[ k ] = { source: jsonTranslations.data[ k ] })
71
45 jsToXliff12(obj, (err, res) => { 72 jsToXliff12(obj, (err, res) => {
46 if (err) return cb(err) 73 if (err) return cb(err)
47 74
48 writeFile(playerTarget, res, err => { 75 writeFile(jsonTranslations.target, res, err => {
49 if (err) return cb(err) 76 if (err) return cb(err)
50 77
51 return cb(null) 78 return cb(null)
52 }) 79 })
53 }) 80 })
54} 81}
82
83function handleError (err: any) {
84 console.error(err)
85 process.exit(-1)
86} \ No newline at end of file
diff --git a/scripts/i18n/xliff2json.ts b/scripts/i18n/xliff2json.ts
index 34784ac11..fa5a71d65 100755
--- a/scripts/i18n/xliff2json.ts
+++ b/scripts/i18n/xliff2json.ts
@@ -1,33 +1,53 @@
1import * as xliff12ToJs from 'xliff/xliff12ToJs' 1import * as xliff12ToJs from 'xliff/xliff12ToJs'
2import { 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'
5import { eachSeries } from 'async'
4 6
5// First, the player 7const sources: string[] = []
6const playerSource = join(__dirname, '../../../client/src/locale/target/player_fr.xml') 8const availableLocales = Object.keys(I18N_LOCALES)
7const playerTarget = join(__dirname, '../../../client/src/locale/target/player_fr.json') 9 .filter(l => isDefaultLocale(l) === false)
10 .map(l => buildFileLocale(l))
8 11
9// Remove the two first lines our xliff module does not like 12for (const file of [ 'server', 'player' ]) {
10let playerFile = readFileSync(playerSource).toString() 13 for (const locale of availableLocales) {
11playerFile = removeFirstLine(playerFile) 14 sources.push(join(__dirname, '../../../client/src/locale/target/', `${file}_${locale}.xml`))
12playerFile = removeFirstLine(playerFile)
13
14xliff12ToJs(playerFile, (err, res) => {
15 if (err) {
16 console.error(err)
17 process.exit(-1)
18 } 15 }
16}
19 17
20 const json = createJSONString(res) 18eachSeries(sources, (source, cb) => {
21 writeFile(playerTarget, json, err => { 19 xliffFile2JSON(source, cb)
22 if (err) { 20}, err => {
23 console.error(err) 21 if (err) return handleError(err)
24 process.exit(-1)
25 }
26 22
27 process.exit(0) 23 process.exit(0)
28 })
29}) 24})
30 25
26function handleError (err: any) {
27 console.error(err)
28 process.exit(-1)
29}
30
31function xliffFile2JSON (filePath: string, cb) {
32 const fileTarget = filePath.replace('.xml', '.json')
33
34 // Remove the two first lines our xliff module does not like
35 let fileContent = readFileSync(filePath).toString()
36 fileContent = removeFirstLine(fileContent)
37 fileContent = removeFirstLine(fileContent)
38
39 xliff12ToJs(fileContent, (err, res) => {
40 if (err) return cb(err)
41
42 const json = createJSONString(res)
43 writeFile(fileTarget, json, err => {
44 if (err) return cb(err)
45
46 return unlink(filePath, cb)
47 })
48 })
49}
50
31function removeFirstLine (str: string) { 51function removeFirstLine (str: string) {
32 return str.substring(str.indexOf('\n') + 1) 52 return str.substring(str.indexOf('\n') + 1)
33} 53}
diff --git a/server/controllers/client.ts b/server/controllers/client.ts
index b153f6086..ec78a4bbc 100644
--- a/server/controllers/client.ts
+++ b/server/controllers/client.ts
@@ -48,8 +48,11 @@ clientsRouter.use('/client', express.static(distPath, { maxAge: STATIC_MAX_AGE }
48clientsRouter.use('/client/assets/images', express.static(assetsImagesPath, { maxAge: STATIC_MAX_AGE })) 48clientsRouter.use('/client/assets/images', express.static(assetsImagesPath, { maxAge: STATIC_MAX_AGE }))
49 49
50clientsRouter.use('/client/locales/:locale/:file.json', function (req, res) { 50clientsRouter.use('/client/locales/:locale/:file.json', function (req, res) {
51 if (req.params.locale === 'fr' && req.params.file === 'player') { 51 const locale = req.params.locale
52 return res.sendFile(join(__dirname, '../../../client/dist/locale/player_fr.json')) 52 const file = req.params.file
53
54 if (is18nLocale(locale) && [ 'player', 'server' ].indexOf(file) !== -1) {
55 return res.sendFile(join(__dirname, `../../../client/dist/locale/${file}_${locale}.json`))
53 } 56 }
54 57
55 return res.sendStatus(404) 58 return res.sendStatus(404)
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 26ee3db47..9b459c241 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -500,7 +500,8 @@ export {
500 STATIC_DOWNLOAD_PATHS, 500 STATIC_DOWNLOAD_PATHS,
501 RATES_LIMIT, 501 RATES_LIMIT,
502 JOB_COMPLETED_LIFETIME, 502 JOB_COMPLETED_LIFETIME,
503 VIDEO_VIEW_LIFETIME 503 VIDEO_VIEW_LIFETIME,
504 buildLanguages
504} 505}
505 506
506// --------------------------------------------------------------------------- 507// ---------------------------------------------------------------------------