aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src
diff options
context:
space:
mode:
Diffstat (limited to 'client/src')
-rw-r--r--client/src/app/app.component.ts7
-rw-r--r--client/src/app/app.module.ts20
-rw-r--r--client/src/app/core/routing/redirect.service.ts4
-rw-r--r--client/src/app/shared/misc/utils.ts2
-rw-r--r--client/src/app/shared/shared.module.ts4
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.html66
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.ts41
-rw-r--r--client/src/app/videos/video-list/video-local.component.ts20
-rw-r--r--client/src/app/videos/video-list/video-recently-added.component.ts20
-rw-r--r--client/src/app/videos/video-list/video-search.component.ts21
-rw-r--r--client/src/app/videos/video-list/video-trending.component.ts20
-rw-r--r--client/src/locale/source/messages_en_US.xml354
-rw-r--r--client/src/locale/target/messages_fr.xml191
13 files changed, 685 insertions, 85 deletions
diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts
index 0bd127063..6087dbf80 100644
--- a/client/src/app/app.component.ts
+++ b/client/src/app/app.component.ts
@@ -1,8 +1,9 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { DomSanitizer, SafeHtml } from '@angular/platform-browser' 2import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
3import { GuardsCheckStart, Router, NavigationEnd } from '@angular/router' 3import { GuardsCheckStart, NavigationEnd, Router } from '@angular/router'
4import { AuthService, RedirectService, ServerService } from '@app/core' 4import { AuthService, RedirectService, ServerService } from '@app/core'
5import { isInSmallView } from '@app/shared/misc/utils' 5import { isInSmallView } from '@app/shared/misc/utils'
6import { is18nPath } from '../../../shared/models/i18n'
6 7
7@Component({ 8@Component({
8 selector: 'my-app', 9 selector: 'my-app',
@@ -33,7 +34,7 @@ export class AppComponent implements OnInit {
33 private serverService: ServerService, 34 private serverService: ServerService,
34 private domSanitizer: DomSanitizer, 35 private domSanitizer: DomSanitizer,
35 private redirectService: RedirectService 36 private redirectService: RedirectService
36 ) {} 37 ) { }
37 38
38 get serverVersion () { 39 get serverVersion () {
39 return this.serverService.getConfig().serverVersion 40 return this.serverService.getConfig().serverVersion
@@ -53,7 +54,7 @@ export class AppComponent implements OnInit {
53 this.router.events.subscribe(e => { 54 this.router.events.subscribe(e => {
54 if (e instanceof NavigationEnd) { 55 if (e instanceof NavigationEnd) {
55 const pathname = window.location.pathname 56 const pathname = window.location.pathname
56 if (!pathname || pathname === '/') { 57 if (!pathname || pathname === '/' || is18nPath(pathname)) {
57 this.redirectService.redirectToHomepage() 58 this.redirectService.redirectToHomepage()
58 } 59 }
59 } 60 }
diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts
index cf533629f..44552021f 100644
--- a/client/src/app/app.module.ts
+++ b/client/src/app/app.module.ts
@@ -1,4 +1,4 @@
1import { NgModule } from '@angular/core' 1import { LOCALE_ID, NgModule, TRANSLATIONS, TRANSLATIONS_FORMAT } from '@angular/core'
2import { BrowserModule } from '@angular/platform-browser' 2import { BrowserModule } from '@angular/platform-browser'
3import { AboutModule } from '@app/about' 3import { AboutModule } from '@app/about'
4import { ServerService } from '@app/core' 4import { ServerService } from '@app/core'
@@ -16,6 +16,7 @@ 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'
19 20
20export function metaFactory (serverService: ServerService): MetaLoader { 21export function metaFactory (serverService: ServerService): MetaLoader {
21 return new MetaStaticLoader({ 22 return new MetaStaticLoader({
@@ -61,6 +62,21 @@ export function metaFactory (serverService: ServerService): MetaLoader {
61 62
62 AppRoutingModule // Put it after all the module because it has the 404 route 63 AppRoutingModule // Put it after all the module because it has the 404 route
63 ], 64 ],
64 providers: [ ] 65 providers: [
66 {
67 provide: TRANSLATIONS,
68 useFactory: (locale) => {
69 const fileLocale = buildFileLocale(locale)
70
71 // Default locale, nothing to translate
72 const defaultFileLocale = buildFileLocale(getDefaultLocale())
73 if (fileLocale === defaultFileLocale) return ''
74
75 return require(`raw-loader!../locale/target/messages_${fileLocale}.xml`)
76 },
77 deps: [ LOCALE_ID ]
78 },
79 { provide: TRANSLATIONS_FORMAT, useValue: 'xlf' }
80 ]
65}) 81})
66export class AppModule {} 82export class AppModule {}
diff --git a/client/src/app/core/routing/redirect.service.ts b/client/src/app/core/routing/redirect.service.ts
index 844f184b4..b7803cce2 100644
--- a/client/src/app/core/routing/redirect.service.ts
+++ b/client/src/app/core/routing/redirect.service.ts
@@ -31,7 +31,7 @@ export class RedirectService {
31 redirectToHomepage () { 31 redirectToHomepage () {
32 console.log('Redirecting to %s...', RedirectService.DEFAULT_ROUTE) 32 console.log('Redirecting to %s...', RedirectService.DEFAULT_ROUTE)
33 33
34 this.router.navigate([ RedirectService.DEFAULT_ROUTE ], { replaceUrl: true }) 34 this.router.navigate([ RedirectService.DEFAULT_ROUTE ], { skipLocationChange: true })
35 .catch(() => { 35 .catch(() => {
36 console.error( 36 console.error(
37 'Cannot navigate to %s, resetting default route to %s.', 37 'Cannot navigate to %s, resetting default route to %s.',
@@ -40,7 +40,7 @@ export class RedirectService {
40 ) 40 )
41 41
42 RedirectService.DEFAULT_ROUTE = RedirectService.INIT_DEFAULT_ROUTE 42 RedirectService.DEFAULT_ROUTE = RedirectService.INIT_DEFAULT_ROUTE
43 return this.router.navigate([ RedirectService.DEFAULT_ROUTE ], { replaceUrl: true }) 43 return this.router.navigate([ RedirectService.DEFAULT_ROUTE ], { skipLocationChange: true })
44 }) 44 })
45 45
46 } 46 }
diff --git a/client/src/app/shared/misc/utils.ts b/client/src/app/shared/misc/utils.ts
index 11933e90b..2219ac802 100644
--- a/client/src/app/shared/misc/utils.ts
+++ b/client/src/app/shared/misc/utils.ts
@@ -98,7 +98,7 @@ function lineFeedToHtml (obj: object, keyToNormalize: string) {
98 98
99// Try to cache a little bit window.innerWidth 99// Try to cache a little bit window.innerWidth
100let windowInnerWidth = window.innerWidth 100let windowInnerWidth = window.innerWidth
101// setInterval(() => windowInnerWidth = window.innerWidth, 500) 101setInterval(() => windowInnerWidth = window.innerWidth, 500)
102 102
103function isInSmallView () { 103function isInSmallView () {
104 return windowInnerWidth < 600 104 return windowInnerWidth < 600
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts
index 20019e47a..fba099401 100644
--- a/client/src/app/shared/shared.module.ts
+++ b/client/src/app/shared/shared.module.ts
@@ -33,6 +33,7 @@ import { VideoThumbnailComponent } from './video/video-thumbnail.component'
33import { VideoService } from './video/video.service' 33import { VideoService } from './video/video.service'
34import { AccountService } from '@app/shared/account/account.service' 34import { AccountService } from '@app/shared/account/account.service'
35import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' 35import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
36import { I18n } from '@ngx-translate/i18n-polyfill'
36 37
37@NgModule({ 38@NgModule({
38 imports: [ 39 imports: [
@@ -108,7 +109,8 @@ import { VideoChannelService } from '@app/shared/video-channel/video-channel.ser
108 VideoService, 109 VideoService,
109 AccountService, 110 AccountService,
110 MarkdownService, 111 MarkdownService,
111 VideoChannelService 112 VideoChannelService,
113 I18n
112 ] 114 ]
113}) 115})
114export class SharedModule { } 116export class SharedModule { }
diff --git a/client/src/app/videos/+video-watch/video-watch.component.html b/client/src/app/videos/+video-watch/video-watch.component.html
index 583a97562..202a12fb0 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.html
+++ b/client/src/app/videos/+video-watch/video-watch.component.html
@@ -3,7 +3,7 @@
3 <div [hidden]="videoNotFound" id="video-element-wrapper"> 3 <div [hidden]="videoNotFound" id="video-element-wrapper">
4 </div> 4 </div>
5 5
6 <div *ngIf="videoNotFound" id="video-not-found">Video not found :'(</div> 6 <div i18n *ngIf="videoNotFound" id="video-not-found">Video not found :'(</div>
7 7
8 <!-- Video information --> 8 <!-- Video information -->
9 <div *ngIf="video" class="margin-content video-bottom"> 9 <div *ngIf="video" class="margin-content video-bottom">
@@ -12,21 +12,21 @@
12 <div> 12 <div>
13 <div class="video-info-name">{{ video.name }}</div> 13 <div class="video-info-name">{{ video.name }}</div>
14 14
15 <div class="video-info-date-views"> 15 <div i18n class="video-info-date-views">
16 {{ video.publishedAt | myFromNow }} - {{ video.views | myNumberFormatter }} views 16 {{ video.publishedAt | myFromNow }} - {{ video.views | myNumberFormatter }} views
17 </div> 17 </div>
18 18
19 <div class="video-info-channel"> 19 <div class="video-info-channel">
20 <a [routerLink]="[ '/video-channels', video.channel.id ]" title="Go the channel page"> 20 <a [routerLink]="[ '/video-channels', video.channel.id ]" i18n-title title="Go the channel page">
21 {{ video.channel.displayName }} 21 {{ video.channel.displayName }}
22 </a> 22 </a>
23 <!-- Here will be the subscribe button --> 23 <!-- Here will be the subscribe button -->
24 <my-help helpType="custom" customHtml="You can subscribe to this account via any ActivityPub-capable fediverse instance. For instance with Mastodon or Pleroma you can type in the search box <strong>@{{video.account.displayName}}@{{video.account.host}}</strong> and subscribe there. Subscription as a PeerTube user is being worked on in <a href='https://github.com/Chocobozzz/PeerTube/issues/470'>#470</a>."></my-help> 24 <my-help helpType="custom" i18n-customHtml customHtml="You can subscribe to this account via any ActivityPub-capable fediverse instance. For instance with Mastodon or Pleroma you can type in the search box <strong>@{{video.account.displayName}}@{{video.account.host}}</strong> and subscribe there. Subscription as a PeerTube user is being worked on in <a href='https://github.com/Chocobozzz/PeerTube/issues/470'>#470</a>."></my-help>
25 </div> 25 </div>
26 26
27 <div class="video-info-by"> 27 <div class="video-info-by">
28 <a [routerLink]="[ '/accounts', video.by ]" title="Go the account page"> 28 <a [routerLink]="[ '/accounts', video.by ]" i18n-title title="Go the account page">
29 <span>By {{ video.by }}</span> 29 <span i18n>By {{ video.by }}</span>
30 <img [src]="video.accountAvatarUrl" alt="Account avatar" /> 30 <img [src]="video.accountAvatarUrl" alt="Account avatar" />
31 </a> 31 </a>
32 </div> 32 </div>
@@ -38,24 +38,24 @@
38 *ngIf="isUserLoggedIn()" [ngClass]="{ 'activated': userRating === 'like' }" (click)="setLike()" 38 *ngIf="isUserLoggedIn()" [ngClass]="{ 'activated': userRating === 'like' }" (click)="setLike()"
39 class="action-button action-button-like" 39 class="action-button action-button-like"
40 > 40 >
41 <span class="icon icon-like" title="Like this video" ></span> 41 <span class="icon icon-like" i18n-title title="Like this video" ></span>
42 </div> 42 </div>
43 43
44 <div 44 <div
45 *ngIf="isUserLoggedIn()" [ngClass]="{ 'activated': userRating === 'dislike' }" (click)="setDislike()" 45 *ngIf="isUserLoggedIn()" [ngClass]="{ 'activated': userRating === 'dislike' }" (click)="setDislike()"
46 class="action-button action-button-dislike" 46 class="action-button action-button-dislike"
47 > 47 >
48 <span class="icon icon-dislike" title="Dislike this video"></span> 48 <span class="icon icon-dislike" i18n-title title="Dislike this video"></span>
49 </div> 49 </div>
50 50
51 <div *ngIf="video.support" (click)="showSupportModal()" class="action-button action-button-support"> 51 <div *ngIf="video.support" (click)="showSupportModal()" class="action-button action-button-support">
52 <span class="icon icon-support"></span> 52 <span class="icon icon-support"></span>
53 <span class="icon-text">Support</span> 53 <span class="icon-text" i18n>Support</span>
54 </div> 54 </div>
55 55
56 <div (click)="showShareModal()" class="action-button action-button-share"> 56 <div (click)="showShareModal()" class="action-button action-button-share">
57 <span class="icon icon-share"></span> 57 <span class="icon icon-share"></span>
58 <span class="icon-text">Share</span> 58 <span class="icon-text" i18n>Share</span>
59 </div> 59 </div>
60 60
61 <div class="action-more" dropdown dropup="true" placement="right"> 61 <div class="action-more" dropdown dropup="true" placement="right">
@@ -65,32 +65,32 @@
65 65
66 <ul *dropdownMenu class="dropdown-menu" id="more-menu" role="menu" aria-labelledby="single-button"> 66 <ul *dropdownMenu class="dropdown-menu" id="more-menu" role="menu" aria-labelledby="single-button">
67 <li role="menuitem"> 67 <li role="menuitem">
68 <a class="dropdown-item" title="Download the video" href="#" (click)="showDownloadModal($event)"> 68 <a class="dropdown-item" i18n-title title="Download the video" href="#" (click)="showDownloadModal($event)">
69 <span class="icon icon-download"></span> Download 69 <span class="icon icon-download"></span> <ng-container i18n>Download</ng-container>
70 </a> 70 </a>
71 </li> 71 </li>
72 72
73 <li *ngIf="isUserLoggedIn()" role="menuitem"> 73 <li *ngIf="isUserLoggedIn()" role="menuitem">
74 <a class="dropdown-item" title="Report this video" href="#" (click)="showReportModal($event)"> 74 <a class="dropdown-item" i18n-title title="Report this video" href="#" (click)="showReportModal($event)">
75 <span class="icon icon-alert"></span> Report 75 <span class="icon icon-alert"></span> <ng-container i18n>Report</ng-container>
76 </a> 76 </a>
77 </li> 77 </li>
78 78
79 <li *ngIf="isVideoBlacklistable()" role="menuitem"> 79 <li *ngIf="isVideoBlacklistable()" role="menuitem">
80 <a class="dropdown-item" title="Blacklist this video" href="#" (click)="blacklistVideo($event)"> 80 <a class="dropdown-item" i18n-title title="Blacklist this video" href="#" (click)="blacklistVideo($event)">
81 <span class="icon icon-blacklist"></span> Blacklist 81 <span class="icon icon-blacklist"></span> <ng-container i18n>Blacklist</ng-container>
82 </a> 82 </a>
83 </li> 83 </li>
84 84
85 <li *ngIf="isVideoUpdatable()" role="menuitem"> 85 <li *ngIf="isVideoUpdatable()" role="menuitem">
86 <a class="dropdown-item" title="Update this video" href="#" [routerLink]="[ '/videos/update', video.uuid ]"> 86 <a class="dropdown-item" i18n-title title="Update this video" href="#" [routerLink]="[ '/videos/update', video.uuid ]">
87 <span class="icon icon-edit"></span> Update 87 <span class="icon icon-edit"></span> <ng-container i18n>Update</ng-container>
88 </a> 88 </a>
89 </li> 89 </li>
90 90
91 <li *ngIf="isVideoRemovable()" role="menuitem"> 91 <li *ngIf="isVideoRemovable()" role="menuitem">
92 <a class="dropdown-item" title="Delete this video" href="#" (click)="removeVideo($event)"> 92 <a class="dropdown-item" i18n-title title="Delete this video" href="#" (click)="removeVideo($event)">
93 <span class="icon icon-blacklist"></span> Delete 93 <span class="icon icon-blacklist"></span> <ng-container i18n>Delete</ng-container>
94 </a> 94 </a>
95 </li> 95 </li>
96 </ul> 96 </ul>
@@ -109,20 +109,20 @@
109 <div class="video-info-description-html" [innerHTML]="videoHTMLDescription"></div> 109 <div class="video-info-description-html" [innerHTML]="videoHTMLDescription"></div>
110 110
111 <div class="video-info-description-more" *ngIf="completeDescriptionShown === false && video.description?.length >= 250" (click)="showMoreDescription()"> 111 <div class="video-info-description-more" *ngIf="completeDescriptionShown === false && video.description?.length >= 250" (click)="showMoreDescription()">
112 Show more 112 <ng-container i18n>Show more</ng-container>
113 <span *ngIf="descriptionLoading === false" class="glyphicon glyphicon-menu-down"></span> 113 <span *ngIf="descriptionLoading === false" class="glyphicon glyphicon-menu-down"></span>
114 <my-loader class="description-loading" [loading]="descriptionLoading"></my-loader> 114 <my-loader class="description-loading" [loading]="descriptionLoading"></my-loader>
115 </div> 115 </div>
116 116
117 <div *ngIf="completeDescriptionShown === true" (click)="showLessDescription()" class="video-info-description-more"> 117 <div *ngIf="completeDescriptionShown === true" (click)="showLessDescription()" class="video-info-description-more">
118 Show less 118 <ng-container i18n>Show less</ng-container>
119 <span *ngIf="descriptionLoading === false" class="glyphicon glyphicon-menu-up"></span> 119 <span *ngIf="descriptionLoading === false" class="glyphicon glyphicon-menu-up"></span>
120 </div> 120 </div>
121 </div> 121 </div>
122 122
123 <div class="video-attributes"> 123 <div class="video-attributes">
124 <div class="video-attribute"> 124 <div class="video-attribute">
125 <span class="video-attribute-label"> 125 <span i18n class="video-attribute-label">
126 Privacy 126 Privacy
127 </span> 127 </span>
128 <span class="video-attribute-value"> 128 <span class="video-attribute-value">
@@ -131,7 +131,7 @@
131 </div> 131 </div>
132 132
133 <div class="video-attribute"> 133 <div class="video-attribute">
134 <span class="video-attribute-label"> 134 <span i18n class="video-attribute-label">
135 Category 135 Category
136 </span> 136 </span>
137 <span class="video-attribute-value"> 137 <span class="video-attribute-value">
@@ -140,7 +140,7 @@
140 </div> 140 </div>
141 141
142 <div class="video-attribute"> 142 <div class="video-attribute">
143 <span class="video-attribute-label"> 143 <span i18n class="video-attribute-label">
144 Licence 144 Licence
145 </span> 145 </span>
146 <span class="video-attribute-value"> 146 <span class="video-attribute-value">
@@ -149,7 +149,7 @@
149 </div> 149 </div>
150 150
151 <div class="video-attribute"> 151 <div class="video-attribute">
152 <span class="video-attribute-label"> 152 <span i18n class="video-attribute-label">
153 Language 153 Language
154 </span> 154 </span>
155 <span class="video-attribute-value"> 155 <span class="video-attribute-value">
@@ -158,7 +158,7 @@
158 </div> 158 </div>
159 159
160 <div class="video-attribute"> 160 <div class="video-attribute">
161 <span class="video-attribute-label"> 161 <span i18n class="video-attribute-label">
162 Tags 162 Tags
163 </span> 163 </span>
164 164
@@ -172,7 +172,7 @@
172 </div> 172 </div>
173 173
174 <div class="other-videos"> 174 <div class="other-videos">
175 <div class="title-page title-page-single"> 175 <div i18n class="title-page title-page-single">
176 Other videos 176 Other videos
177 </div> 177 </div>
178 178
@@ -184,13 +184,15 @@
184 184
185 185
186 <div class="privacy-concerns" *ngIf="hasAlreadyAcceptedPrivacyConcern === false"> 186 <div class="privacy-concerns" *ngIf="hasAlreadyAcceptedPrivacyConcern === false">
187 <strong>Friendly Reminder:</strong> 187 <strong i18n>Friendly Reminder:</strong>
188 <div class="privacy-concerns-text"> 188 <div class="privacy-concerns-text">
189 The sharing system used by this video implies that some technical information about your system (such as a public IP address) can be accessed publicly. 189 <ng-container i18n>
190 <a title="Get more information" target="_blank" rel="noopener noreferrer" href="/about#p2p-privacy">More information</a> 190 The sharing system used by this video implies that some technical information about your system (such as a public IP address) can be accessed publicly.
191 </ng-container>
192 <a i18n i18n-title title="Get more information" target="_blank" rel="noopener noreferrer" href="/about#p2p-privacy">More information</a>
191 </div> 193 </div>
192 194
193 <div class="privacy-concerns-okay" (click)="acceptedPrivacyConcern()"> 195 <div i18n class="privacy-concerns-okay" (click)="acceptedPrivacyConcern()">
194 OK 196 OK
195 </div> 197 </div>
196 </div> 198 </div>
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 ad572ef58..f3b4f7a2b 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/videos/+video-watch/video-watch.component.ts
@@ -23,6 +23,7 @@ import { VideoReportComponent } from './modal/video-report.component'
23import { VideoShareComponent } from './modal/video-share.component' 23import { VideoShareComponent } from './modal/video-share.component'
24import { getVideojsOptions } from '../../../assets/player/peertube-player' 24import { getVideojsOptions } from '../../../assets/player/peertube-player'
25import { ServerService } from '@app/core' 25import { ServerService } from '@app/core'
26import { I18n } from '@ngx-translate/i18n-polyfill'
26 27
27@Component({ 28@Component({
28 selector: 'my-video-watch', 29 selector: 'my-video-watch',
@@ -70,7 +71,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
70 private notificationsService: NotificationsService, 71 private notificationsService: NotificationsService,
71 private markdownService: MarkdownService, 72 private markdownService: MarkdownService,
72 private zone: NgZone, 73 private zone: NgZone,
73 private redirectService: RedirectService 74 private redirectService: RedirectService,
75 private i18n: I18n
74 ) {} 76 ) {}
75 77
76 get user () { 78 get user () {
@@ -153,17 +155,20 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
153 async blacklistVideo (event: Event) { 155 async blacklistVideo (event: Event) {
154 event.preventDefault() 156 event.preventDefault()
155 157
156 const res = await this.confirmService.confirm('Do you really want to blacklist this video?', 'Blacklist') 158 const res = await this.confirmService.confirm(this.i18n('Do you really want to blacklist this video?'), this.i18n('Blacklist'))
157 if (res === false) return 159 if (res === false) return
158 160
159 this.videoBlacklistService.blacklistVideo(this.video.id) 161 this.videoBlacklistService.blacklistVideo(this.video.id)
160 .subscribe( 162 .subscribe(
161 status => { 163 status => {
162 this.notificationsService.success('Success', `Video ${this.video.name} had been blacklisted.`) 164 this.notificationsService.success(
165 this.i18n('Success'),
166 this.i18n('Video {{ videoName }} had been blacklisted.', { videoName: this.video.name })
167 )
163 this.redirectService.redirectToHomepage() 168 this.redirectService.redirectToHomepage()
164 }, 169 },
165 170
166 error => this.notificationsService.error('Error', error.message) 171 error => this.notificationsService.error(this.i18n('Error'), error.message)
167 ) 172 )
168 } 173 }
169 174
@@ -198,7 +203,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
198 203
199 error => { 204 error => {
200 this.descriptionLoading = false 205 this.descriptionLoading = false
201 this.notificationsService.error('Error', error.message) 206 this.notificationsService.error(this.i18n('Error'), error.message)
202 } 207 }
203 ) 208 )
204 } 209 }
@@ -252,19 +257,22 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
252 async removeVideo (event: Event) { 257 async removeVideo (event: Event) {
253 event.preventDefault() 258 event.preventDefault()
254 259
255 const res = await this.confirmService.confirm('Do you really want to delete this video?', 'Delete') 260 const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this video?'), this.i18n('Delete'))
256 if (res === false) return 261 if (res === false) return
257 262
258 this.videoService.removeVideo(this.video.id) 263 this.videoService.removeVideo(this.video.id)
259 .subscribe( 264 .subscribe(
260 status => { 265 status => {
261 this.notificationsService.success('Success', `Video ${this.video.name} deleted.`) 266 this.notificationsService.success(
267 this.i18n('Success'),
268 this.i18n('Video {{ videoName }} deleted.', { videoName: this.video.name })
269 )
262 270
263 // Go back to the video-list. 271 // Go back to the video-list.
264 this.redirectService.redirectToHomepage() 272 this.redirectService.redirectToHomepage()
265 }, 273 },
266 274
267 error => this.notificationsService.error('Error', error.message) 275 error => this.notificationsService.error(this.i18n('Error'), error.message)
268 ) 276 )
269 } 277 }
270 278
@@ -288,7 +296,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
288 } 296 }
289 297
290 private setVideoLikesBarTooltipText () { 298 private setVideoLikesBarTooltipText () {
291 this.likesBarTooltipText = `${this.video.likes} likes / ${this.video.dislikes} dislikes` 299 this.likesBarTooltipText = this.i18n(
300 '{{ likesNumber }} likes / {{ dislikesNumber }} dislikes',
301 { likesNumber: this.video.likes, dislikes: this.video.dislikes }
302 )
292 } 303 }
293 304
294 private handleError (err: any) { 305 private handleError (err: any) {
@@ -298,12 +309,12 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
298 let message = '' 309 let message = ''
299 310
300 if (errorMessage.indexOf('http error') !== -1) { 311 if (errorMessage.indexOf('http error') !== -1) {
301 message = 'Cannot fetch video from server, maybe down.' 312 message = this.i18n('Cannot fetch video from server, maybe down.')
302 } else { 313 } else {
303 message = errorMessage 314 message = errorMessage
304 } 315 }
305 316
306 this.notificationsService.error('Error', message) 317 this.notificationsService.error(this.i18n('Error'), message)
307 } 318 }
308 319
309 private checkUserRating () { 320 private checkUserRating () {
@@ -318,7 +329,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
318 } 329 }
319 }, 330 },
320 331
321 err => this.notificationsService.error('Error', err.message) 332 err => this.notificationsService.error(this.i18n('Error'), err.message)
322 ) 333 )
323 } 334 }
324 335
@@ -333,8 +344,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
333 344
334 if (this.video.isVideoNSFWForUser(this.user, this.serverService.getConfig())) { 345 if (this.video.isVideoNSFWForUser(this.user, this.serverService.getConfig())) {
335 const res = await this.confirmService.confirm( 346 const res = await this.confirmService.confirm(
336 'This video contains mature or explicit content. Are you sure you want to watch it?', 347 this.i18n('This video contains mature or explicit content. Are you sure you want to watch it?'),
337 'Mature or explicit content' 348 this.i18n('Mature or explicit content')
338 ) 349 )
339 if (res === false) return this.redirectService.redirectToHomepage() 350 if (res === false) return this.redirectService.redirectToHomepage()
340 } 351 }
@@ -399,7 +410,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
399 this.updateVideoRating(this.userRating, nextRating) 410 this.updateVideoRating(this.userRating, nextRating)
400 this.userRating = nextRating 411 this.userRating = nextRating
401 }, 412 },
402 err => this.notificationsService.error('Error', err.message) 413 err => this.notificationsService.error(this.i18n('Error'), err.message)
403 ) 414 )
404 } 415 }
405 416
diff --git a/client/src/app/videos/video-list/video-local.component.ts b/client/src/app/videos/video-list/video-local.component.ts
index abab7504f..03568b618 100644
--- a/client/src/app/videos/video-list/video-local.component.ts
+++ b/client/src/app/videos/video-list/video-local.component.ts
@@ -8,6 +8,7 @@ import { AbstractVideoList } from '../../shared/video/abstract-video-list'
8import { VideoSortField } from '../../shared/video/sort-field.type' 8import { VideoSortField } from '../../shared/video/sort-field.type'
9import { VideoService } from '../../shared/video/video.service' 9import { VideoService } from '../../shared/video/video.service'
10import { VideoFilter } from '../../../../../shared/models/videos/video-query.type' 10import { VideoFilter } from '../../../../../shared/models/videos/video-query.type'
11import { I18n } from '@ngx-translate/i18n-polyfill'
11 12
12@Component({ 13@Component({
13 selector: 'my-videos-local', 14 selector: 'my-videos-local',
@@ -15,18 +16,23 @@ import { VideoFilter } from '../../../../../shared/models/videos/video-query.typ
15 templateUrl: '../../shared/video/abstract-video-list.html' 16 templateUrl: '../../shared/video/abstract-video-list.html'
16}) 17})
17export class VideoLocalComponent extends AbstractVideoList implements OnInit, OnDestroy { 18export class VideoLocalComponent extends AbstractVideoList implements OnInit, OnDestroy {
18 titlePage = 'Local videos' 19 titlePage: string
19 currentRoute = '/videos/local' 20 currentRoute = '/videos/local'
20 sort = '-publishedAt' as VideoSortField 21 sort = '-publishedAt' as VideoSortField
21 filter: VideoFilter = 'local' 22 filter: VideoFilter = 'local'
22 23
23 constructor (protected router: Router, 24 constructor (
24 protected route: ActivatedRoute, 25 protected router: Router,
25 protected notificationsService: NotificationsService, 26 protected route: ActivatedRoute,
26 protected authService: AuthService, 27 protected notificationsService: NotificationsService,
27 protected location: Location, 28 protected authService: AuthService,
28 private videoService: VideoService) { 29 protected location: Location,
30 private videoService: VideoService,
31 private i18n: I18n
32 ) {
29 super() 33 super()
34
35 this.titlePage = i18n('Local videos')
30 } 36 }
31 37
32 ngOnInit () { 38 ngOnInit () {
diff --git a/client/src/app/videos/video-list/video-recently-added.component.ts b/client/src/app/videos/video-list/video-recently-added.component.ts
index d064d9628..5768d9fe0 100644
--- a/client/src/app/videos/video-list/video-recently-added.component.ts
+++ b/client/src/app/videos/video-list/video-recently-added.component.ts
@@ -7,6 +7,7 @@ import { AuthService } from '../../core/auth'
7import { AbstractVideoList } from '../../shared/video/abstract-video-list' 7import { AbstractVideoList } from '../../shared/video/abstract-video-list'
8import { VideoSortField } from '../../shared/video/sort-field.type' 8import { VideoSortField } from '../../shared/video/sort-field.type'
9import { VideoService } from '../../shared/video/video.service' 9import { VideoService } from '../../shared/video/video.service'
10import { I18n } from '@ngx-translate/i18n-polyfill'
10 11
11@Component({ 12@Component({
12 selector: 'my-videos-recently-added', 13 selector: 'my-videos-recently-added',
@@ -14,17 +15,22 @@ import { VideoService } from '../../shared/video/video.service'
14 templateUrl: '../../shared/video/abstract-video-list.html' 15 templateUrl: '../../shared/video/abstract-video-list.html'
15}) 16})
16export class VideoRecentlyAddedComponent extends AbstractVideoList implements OnInit, OnDestroy { 17export class VideoRecentlyAddedComponent extends AbstractVideoList implements OnInit, OnDestroy {
17 titlePage = 'Recently added' 18 titlePage: string
18 currentRoute = '/videos/recently-added' 19 currentRoute = '/videos/recently-added'
19 sort: VideoSortField = '-publishedAt' 20 sort: VideoSortField = '-publishedAt'
20 21
21 constructor (protected router: Router, 22 constructor (
22 protected route: ActivatedRoute, 23 protected router: Router,
23 protected location: Location, 24 protected route: ActivatedRoute,
24 protected notificationsService: NotificationsService, 25 protected location: Location,
25 protected authService: AuthService, 26 protected notificationsService: NotificationsService,
26 private videoService: VideoService) { 27 protected authService: AuthService,
28 private videoService: VideoService,
29 private i18n: I18n
30 ) {
27 super() 31 super()
32
33 this.titlePage = i18n('Recently added')
28 } 34 }
29 35
30 ngOnInit () { 36 ngOnInit () {
diff --git a/client/src/app/videos/video-list/video-search.component.ts b/client/src/app/videos/video-list/video-search.component.ts
index aab896d84..35566a7bd 100644
--- a/client/src/app/videos/video-list/video-search.component.ts
+++ b/client/src/app/videos/video-list/video-search.component.ts
@@ -8,6 +8,7 @@ import { Subscription } from 'rxjs'
8import { AuthService } from '../../core/auth' 8import { AuthService } from '../../core/auth'
9import { AbstractVideoList } from '../../shared/video/abstract-video-list' 9import { AbstractVideoList } from '../../shared/video/abstract-video-list'
10import { VideoService } from '../../shared/video/video.service' 10import { VideoService } from '../../shared/video/video.service'
11import { I18n } from '@ngx-translate/i18n-polyfill'
11 12
12@Component({ 13@Component({
13 selector: 'my-videos-search', 14 selector: 'my-videos-search',
@@ -15,7 +16,7 @@ import { VideoService } from '../../shared/video/video.service'
15 templateUrl: '../../shared/video/abstract-video-list.html' 16 templateUrl: '../../shared/video/abstract-video-list.html'
16}) 17})
17export class VideoSearchComponent extends AbstractVideoList implements OnInit, OnDestroy { 18export class VideoSearchComponent extends AbstractVideoList implements OnInit, OnDestroy {
18 titlePage = 'Search' 19 titlePage: string
19 currentRoute = '/videos/search' 20 currentRoute = '/videos/search'
20 loadOnInit = false 21 loadOnInit = false
21 22
@@ -24,15 +25,19 @@ export class VideoSearchComponent extends AbstractVideoList implements OnInit, O
24 } 25 }
25 private subActivatedRoute: Subscription 26 private subActivatedRoute: Subscription
26 27
27 constructor (protected router: Router, 28 constructor (
28 protected route: ActivatedRoute, 29 protected router: Router,
29 protected notificationsService: NotificationsService, 30 protected route: ActivatedRoute,
30 protected authService: AuthService, 31 protected notificationsService: NotificationsService,
31 protected location: Location, 32 protected authService: AuthService,
32 private videoService: VideoService, 33 protected location: Location,
33 private redirectService: RedirectService 34 private videoService: VideoService,
35 private redirectService: RedirectService,
36 private i18n: I18n
34 ) { 37 ) {
35 super() 38 super()
39
40 this.titlePage = i18n('Search')
36 } 41 }
37 42
38 ngOnInit () { 43 ngOnInit () {
diff --git a/client/src/app/videos/video-list/video-trending.component.ts b/client/src/app/videos/video-list/video-trending.component.ts
index ea65070f9..760470e8c 100644
--- a/client/src/app/videos/video-list/video-trending.component.ts
+++ b/client/src/app/videos/video-list/video-trending.component.ts
@@ -7,6 +7,7 @@ import { AuthService } from '../../core/auth'
7import { AbstractVideoList } from '../../shared/video/abstract-video-list' 7import { AbstractVideoList } from '../../shared/video/abstract-video-list'
8import { VideoSortField } from '../../shared/video/sort-field.type' 8import { VideoSortField } from '../../shared/video/sort-field.type'
9import { VideoService } from '../../shared/video/video.service' 9import { VideoService } from '../../shared/video/video.service'
10import { I18n } from '@ngx-translate/i18n-polyfill'
10 11
11@Component({ 12@Component({
12 selector: 'my-videos-trending', 13 selector: 'my-videos-trending',
@@ -14,17 +15,22 @@ import { VideoService } from '../../shared/video/video.service'
14 templateUrl: '../../shared/video/abstract-video-list.html' 15 templateUrl: '../../shared/video/abstract-video-list.html'
15}) 16})
16export class VideoTrendingComponent extends AbstractVideoList implements OnInit, OnDestroy { 17export class VideoTrendingComponent extends AbstractVideoList implements OnInit, OnDestroy {
17 titlePage = 'Trending' 18 titlePage: string
18 currentRoute = '/videos/trending' 19 currentRoute = '/videos/trending'
19 defaultSort: VideoSortField = '-views' 20 defaultSort: VideoSortField = '-views'
20 21
21 constructor (protected router: Router, 22 constructor (
22 protected route: ActivatedRoute, 23 protected router: Router,
23 protected notificationsService: NotificationsService, 24 protected route: ActivatedRoute,
24 protected authService: AuthService, 25 protected notificationsService: NotificationsService,
25 protected location: Location, 26 protected authService: AuthService,
26 private videoService: VideoService) { 27 protected location: Location,
28 private videoService: VideoService,
29 private i18n: I18n
30 ) {
27 super() 31 super()
32
33 this.titlePage = i18n('Trending')
28 } 34 }
29 35
30 ngOnInit () { 36 ngOnInit () {
diff --git a/client/src/locale/source/messages_en_US.xml b/client/src/locale/source/messages_en_US.xml
new file mode 100644
index 000000000..6c355a97f
--- /dev/null
+++ b/client/src/locale/source/messages_en_US.xml
@@ -0,0 +1,354 @@
1<?xml version="1.0" encoding="UTF-8" ?>
2<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
3 <file source-language="en-US" datatype="plaintext" original="ng2.template">
4 <body>
5 <trans-unit id="298cb43759c99e11e2ca5f92c768a145ddaa323f" datatype="html">
6 <source>
7 My public profile
8 </source>
9 <context-group purpose="location">
10 <context context-type="sourcefile">app/menu/menu.component.ts</context>
11 <context context-type="linenumber">17</context>
12 </context-group>
13 </trans-unit><trans-unit id="5f60990802486b7906b422d80aace6a1b19dcc02" datatype="html">
14 <source>Video not found :&apos;(</source>
15 <context-group purpose="location">
16 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
17 <context context-type="linenumber">6</context>
18 </context-group>
19 </trans-unit><trans-unit id="643ab402461b1169eebbe2ed790e12a9a83551aa" datatype="html">
20 <source>
21 &lt;x id="INTERPOLATION" equiv-text="{{ video.publishedAt | myFromNow }}"/&gt; - &lt;x id="INTERPOLATION_1" equiv-text="{{ video.views | myNumberFormatter }}"/&gt; views
22 </source>
23 <context-group purpose="location">
24 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
25 <context context-type="linenumber">15</context>
26 </context-group>
27 </trans-unit><trans-unit id="5cb397241041f7ad70997806227bafcdf7eb1b33" datatype="html">
28 <source>Go the channel page</source>
29 <context-group purpose="location">
30 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
31 <context context-type="linenumber">20</context>
32 </context-group>
33 </trans-unit><trans-unit id="912f005563d20191efc188dccedd35a7c4e6b396" datatype="html">
34 <source>You can subscribe to this account via any ActivityPub-capable fediverse instance. For instance with Mastodon or Pleroma you can type in the search box &lt;strong&gt;@&lt;x id="INTERPOLATION" equiv-text="{{video.account.displayName}}"/&gt;@&lt;x id="INTERPOLATION_1" equiv-text="{{video.account.host}}"/&gt;&lt;/strong&gt; and subscribe there. Subscription as a PeerTube user is being worked on in &lt;a href=&apos;https://github.com/Chocobozzz/PeerTube/issues/470&apos;&gt;#470&lt;/a&gt;.</source>
35 <context-group purpose="location">
36 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
37 <context context-type="linenumber">24</context>
38 </context-group>
39 </trans-unit><trans-unit id="ccc07df383b7a32be3e2e105faa5488caf261c1c" datatype="html">
40 <source>By &lt;x id="INTERPOLATION" equiv-text="{{ video.by }}"/&gt;</source>
41 <context-group purpose="location">
42 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
43 <context context-type="linenumber">29</context>
44 </context-group>
45 </trans-unit><trans-unit id="e88300c71e0cb0f346d5a72eb37c920f2aadae8a" datatype="html">
46 <source>Go the account page</source>
47 <context-group purpose="location">
48 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
49 <context context-type="linenumber">28</context>
50 </context-group>
51 </trans-unit><trans-unit id="82b59049f3f89d900c98da9319e156dd513e3ced" datatype="html">
52 <source>Like this video</source>
53 <context-group purpose="location">
54 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
55 <context context-type="linenumber">41</context>
56 </context-group>
57 </trans-unit><trans-unit id="623698f075025b2b2fc2e0c59fd95f4f4662a509" datatype="html">
58 <source>Dislike this video</source>
59 <context-group purpose="location">
60 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
61 <context context-type="linenumber">48</context>
62 </context-group>
63 </trans-unit><trans-unit id="b5629d298ff1a69b8db19a4ba2995c76b52da604" datatype="html">
64 <source>Support</source>
65 <context-group purpose="location">
66 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
67 <context context-type="linenumber">53</context>
68 </context-group>
69 </trans-unit><trans-unit id="0bd8b27f60a1f098a53e06328426d818e3508ff9" datatype="html">
70 <source>Share</source>
71 <context-group purpose="location">
72 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
73 <context context-type="linenumber">58</context>
74 </context-group>
75 </trans-unit><trans-unit id="dc75033a5238fdc4f462212c847a45ba8018a3fd" datatype="html">
76 <source>Download</source>
77 <context-group purpose="location">
78 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
79 <context context-type="linenumber">69</context>
80 </context-group>
81 </trans-unit><trans-unit id="144fff5c40b85414d59e644d8dee7cfefba925a2" datatype="html">
82 <source>Download the video</source>
83 <context-group purpose="location">
84 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
85 <context context-type="linenumber">68</context>
86 </context-group>
87 </trans-unit><trans-unit id="f72992030f134408b675152c397f9d0ec00f3b2a" datatype="html">
88 <source>Report</source>
89 <context-group purpose="location">
90 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
91 <context context-type="linenumber">75</context>
92 </context-group>
93 </trans-unit><trans-unit id="2f4894617d9c44010f87473e583bd4604b7d6ecf" datatype="html">
94 <source>Report this video</source>
95 <context-group purpose="location">
96 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
97 <context context-type="linenumber">74</context>
98 </context-group>
99 </trans-unit><trans-unit id="007ab5fa2aae8a7372307d3fc45a2dbcb11ffd61" datatype="html">
100 <source>Blacklist</source>
101 <context-group purpose="location">
102 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
103 <context context-type="linenumber">81</context>
104 </context-group>
105 </trans-unit><trans-unit id="803c6317abd2dbafcc93226c4e273c62932e3037" datatype="html">
106 <source>Blacklist this video</source>
107 <context-group purpose="location">
108 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
109 <context context-type="linenumber">80</context>
110 </context-group>
111 </trans-unit><trans-unit id="047f50bc5b5d17b5bec0196355953e1a5c590ddb" datatype="html">
112 <source>Update</source>
113 <context-group purpose="location">
114 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
115 <context context-type="linenumber">87</context>
116 </context-group>
117 </trans-unit><trans-unit id="cd27f761b923a5bdb16ba9844da632edd878f1b1" datatype="html">
118 <source>Update this video</source>
119 <context-group purpose="location">
120 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
121 <context context-type="linenumber">86</context>
122 </context-group>
123 </trans-unit><trans-unit id="826b25211922a1b46436589233cb6f1a163d89b7" datatype="html">
124 <source>Delete</source>
125 <context-group purpose="location">
126 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
127 <context context-type="linenumber">93</context>
128 </context-group>
129 </trans-unit><trans-unit id="3dbfdc68f83d91cb360172eb65578cae94e7cbe5" datatype="html">
130 <source>Delete this video</source>
131 <context-group purpose="location">
132 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
133 <context context-type="linenumber">92</context>
134 </context-group>
135 </trans-unit><trans-unit id="f0c5f6f270e70cbe063b5368fcf48f9afc1abd9b" datatype="html">
136 <source>Show more</source>
137 <context-group purpose="location">
138 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
139 <context context-type="linenumber">112</context>
140 </context-group>
141 </trans-unit><trans-unit id="5403a767248e304199592271bba3366d2ca3f903" datatype="html">
142 <source>Show less</source>
143 <context-group purpose="location">
144 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
145 <context context-type="linenumber">118</context>
146 </context-group>
147 </trans-unit><trans-unit id="8057a9b7f9e908ff350edfd71417b96c174e5911" datatype="html">
148 <source>
149 Privacy
150 </source>
151 <context-group purpose="location">
152 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
153 <context context-type="linenumber">125</context>
154 </context-group>
155 </trans-unit><trans-unit id="bd407eca607a8905a26a9e30c9d0cd70f4465db8" datatype="html">
156 <source>
157 Category
158 </source>
159 <context-group purpose="location">
160 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
161 <context context-type="linenumber">134</context>
162 </context-group>
163 </trans-unit><trans-unit id="af5072bd79ea3cd767ab74a6622d2eee791b3832" datatype="html">
164 <source>
165 Licence
166 </source>
167 <context-group purpose="location">
168 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
169 <context context-type="linenumber">143</context>
170 </context-group>
171 </trans-unit><trans-unit id="a911eee019174741b0aec6fcf3fbd5752fab3e67" datatype="html">
172 <source>
173 Language
174 </source>
175 <context-group purpose="location">
176 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
177 <context context-type="linenumber">152</context>
178 </context-group>
179 </trans-unit><trans-unit id="ecf7007c2842cc26a7b91d08d48c7a4f5f749fb3" datatype="html">
180 <source>
181 Tags
182 </source>
183 <context-group purpose="location">
184 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
185 <context context-type="linenumber">161</context>
186 </context-group>
187 </trans-unit><trans-unit id="7ce8b0d7cc34d4c1ef4a21e990b0a001337bedd1" datatype="html">
188 <source>
189 Other videos
190 </source>
191 <context-group purpose="location">
192 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
193 <context context-type="linenumber">175</context>
194 </context-group>
195 </trans-unit><trans-unit id="fb779d2b25c4d0ffa7d52c823a240717e8c1fe6c" datatype="html">
196 <source>Friendly Reminder:</source>
197 <context-group purpose="location">
198 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
199 <context context-type="linenumber">187</context>
200 </context-group>
201 </trans-unit><trans-unit id="4c2fca29fd9d7e85abe85a206958a4226f403be2" datatype="html">
202 <source>
203 The sharing system used by this video implies that some technical information about your system (such as a public IP address) can be accessed publicly.
204 </source>
205 <context-group purpose="location">
206 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
207 <context context-type="linenumber">189</context>
208 </context-group>
209 </trans-unit><trans-unit id="e60c11e1b1dfbbeda577364b8de39ded2d796c5e" datatype="html">
210 <source>More information</source>
211 <context-group purpose="location">
212 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
213 <context context-type="linenumber">192</context>
214 </context-group>
215 </trans-unit><trans-unit id="bd499ca7913bb5408fd139a4cb4f863852d5f318" datatype="html">
216 <source>Get more information</source>
217 <context-group purpose="location">
218 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
219 <context context-type="linenumber">192</context>
220 </context-group>
221 </trans-unit><trans-unit id="20fc98888baf65b5ba9fe9622dc036fa8dec6a5f" datatype="html">
222 <source>
223 OK
224 </source>
225 <context-group purpose="location">
226 <context context-type="sourcefile">app/videos/+video-watch/video-watch.component.ts</context>
227 <context context-type="linenumber">195</context>
228 </context-group>
229 </trans-unit>
230 <trans-unit id="23b2c2f4dd69e29c3bff00469e259dcb01de5633" datatype="html">
231 <source>Do you really want to blacklist this video?</source>
232 <context-group purpose="location">
233 <context context-type="sourcefile">src/app/videos/+video-watch/video-watch.component.ts</context>
234 <context context-type="linenumber">1</context>
235 </context-group>
236 </trans-unit>
237 <trans-unit id="1e035e6ccfab771cad4226b2ad230cb0d4a88cba" datatype="html">
238 <source>Success</source>
239 <context-group purpose="location">
240 <context context-type="sourcefile">src/app/videos/+video-watch/video-watch.component.ts</context>
241 <context context-type="linenumber">1</context>
242 </context-group>
243 <context-group purpose="location">
244 <context context-type="sourcefile">src/app/videos/+video-watch/video-watch.component.ts</context>
245 <context context-type="linenumber">1</context>
246 </context-group>
247 </trans-unit>
248 <trans-unit id="085d56464b75ae5c1e370f5290e4c4cf23961a61" datatype="html">
249 <source>Video &lt;x id="INTERPOLATION" equiv-text="{{ videoName }}"/&gt; had been blacklisted.</source>
250 <context-group purpose="location">
251 <context context-type="sourcefile">src/app/videos/+video-watch/video-watch.component.ts</context>
252 <context context-type="linenumber">1</context>
253 </context-group>
254 </trans-unit>
255 <trans-unit id="6080b77234e92ad41bb52653b239c4c4f851317d" datatype="html">
256 <source>Error</source>
257 <context-group purpose="location">
258 <context context-type="sourcefile">src/app/videos/+video-watch/video-watch.component.ts</context>
259 <context context-type="linenumber">1</context>
260 </context-group>
261 <context-group purpose="location">
262 <context context-type="sourcefile">src/app/videos/+video-watch/video-watch.component.ts</context>
263 <context context-type="linenumber">1</context>
264 </context-group>
265 <context-group purpose="location">
266 <context context-type="sourcefile">src/app/videos/+video-watch/video-watch.component.ts</context>
267 <context context-type="linenumber">1</context>
268 </context-group>
269 <context-group purpose="location">
270 <context context-type="sourcefile">src/app/videos/+video-watch/video-watch.component.ts</context>
271 <context context-type="linenumber">1</context>
272 </context-group>
273 <context-group purpose="location">
274 <context context-type="sourcefile">src/app/videos/+video-watch/video-watch.component.ts</context>
275 <context context-type="linenumber">1</context>
276 </context-group>
277 <context-group purpose="location">
278 <context context-type="sourcefile">src/app/videos/+video-watch/video-watch.component.ts</context>
279 <context context-type="linenumber">1</context>
280 </context-group>
281 </trans-unit>
282 <trans-unit id="f1abd89c9280323209e939fa9c30f6e5cda20c95" datatype="html">
283 <source>Do you really want to delete this video?</source>
284 <context-group purpose="location">
285 <context context-type="sourcefile">src/app/videos/+video-watch/video-watch.component.ts</context>
286 <context context-type="linenumber">1</context>
287 </context-group>
288 </trans-unit>
289 <trans-unit id="007c1d7080cf6da1ac264b23705246f0c53e3114" datatype="html">
290 <source>Video &lt;x id="INTERPOLATION" equiv-text="{{ videoName }}"/&gt; deleted.</source>
291 <context-group purpose="location">
292 <context context-type="sourcefile">src/app/videos/+video-watch/video-watch.component.ts</context>
293 <context context-type="linenumber">1</context>
294 </context-group>
295 </trans-unit>
296 <trans-unit id="cf9a064824f2fa3f01fd5544ad21032e33e60dca" datatype="html">
297 <source>&lt;x id="INTERPOLATION" equiv-text="{{ likesNumber }}"/&gt; likes / &lt;x id="INTERPOLATION_1" equiv-text="{{ dislikesNumber }}"/&gt; dislikes</source>
298 <context-group purpose="location">
299 <context context-type="sourcefile">src/app/videos/+video-watch/video-watch.component.ts</context>
300 <context context-type="linenumber">1</context>
301 </context-group>
302 </trans-unit>
303 <trans-unit id="4a400b174208188dcb46f2c23f4af9accfabaa3f" datatype="html">
304 <source>Cannot fetch video from server, maybe down.</source>
305 <context-group purpose="location">
306 <context context-type="sourcefile">src/app/videos/+video-watch/video-watch.component.ts</context>
307 <context context-type="linenumber">1</context>
308 </context-group>
309 </trans-unit>
310 <trans-unit id="ed013c2c29216501c688e9cb5f3a1c9fd9147b71" datatype="html">
311 <source>This video contains mature or explicit content. Are you sure you want to watch it?</source>
312 <context-group purpose="location">
313 <context context-type="sourcefile">src/app/videos/+video-watch/video-watch.component.ts</context>
314 <context context-type="linenumber">1</context>
315 </context-group>
316 </trans-unit>
317 <trans-unit id="5ba3d522e4146eefcbd5c222247c1e2423d27cd8" datatype="html">
318 <source>Mature or explicit content</source>
319 <context-group purpose="location">
320 <context context-type="sourcefile">src/app/videos/+video-watch/video-watch.component.ts</context>
321 <context context-type="linenumber">1</context>
322 </context-group>
323 </trans-unit>
324 <trans-unit id="b6307f83d9f43bff8d5129a7888e89964ddc3f7f" datatype="html">
325 <source>Local videos</source>
326 <context-group purpose="location">
327 <context context-type="sourcefile">src/app/videos/video-list/video-local.component.ts</context>
328 <context context-type="linenumber">1</context>
329 </context-group>
330 </trans-unit>
331 <trans-unit id="8d20c5f5dd30acbe71316544dab774393fd9c3c1" datatype="html">
332 <source>Recently added</source>
333 <context-group purpose="location">
334 <context context-type="sourcefile">src/app/videos/video-list/video-recently-added.component.ts</context>
335 <context context-type="linenumber">1</context>
336 </context-group>
337 </trans-unit>
338 <trans-unit id="7e892ba15f2c6c17e83510e273b3e10fc32ea016" datatype="html">
339 <source>Search</source>
340 <context-group purpose="location">
341 <context context-type="sourcefile">src/app/videos/video-list/video-search.component.ts</context>
342 <context context-type="linenumber">1</context>
343 </context-group>
344 </trans-unit>
345 <trans-unit id="b6b7986bc3721ac483baf20bc9a320529075c807" datatype="html">
346 <source>Trending</source>
347 <context-group purpose="location">
348 <context context-type="sourcefile">src/app/videos/video-list/video-trending.component.ts</context>
349 <context context-type="linenumber">1</context>
350 </context-group>
351 </trans-unit>
352 </body>
353 </file>
354</xliff>
diff --git a/client/src/locale/target/messages_fr.xml b/client/src/locale/target/messages_fr.xml
new file mode 100644
index 000000000..3a55922ba
--- /dev/null
+++ b/client/src/locale/target/messages_fr.xml
@@ -0,0 +1,191 @@
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="298cb43759c99e11e2ca5f92c768a145ddaa323f">
7 <source>
8 My public profile
9 </source>
10 <target>Mon profile public</target>
11 <context-group name="null">
12 <context context-type="linenumber">17</context>
13 </context-group>
14 </trans-unit>
15 <trans-unit id="5f60990802486b7906b422d80aace6a1b19dcc02">
16 <source>Video not found :'(</source>
17 <target>Vidéo non trouvée :'(</target>
18 <context-group name="null">
19 <context context-type="linenumber">6</context>
20 </context-group>
21 </trans-unit>
22 <trans-unit id="643ab402461b1169eebbe2ed790e12a9a83551aa">
23 <source>
24 <x id="INTERPOLATION" equiv-text="{{ video.publishedAt | myFromNow }}"/> - <x id="INTERPOLATION_1" equiv-text="{{ video.views | myNumberFormatter }}"/> views
25 </source>
26 <target>
27 <x id="INTERPOLATION" equiv-text="{{ video.publishedAt | myFromNow }}"/> - <x id="INTERPOLATION_1" equiv-text="{{ video.views | myNumberFormatter }}"/> vues </target>
28 <context-group name="null">
29 <context context-type="linenumber">15</context>
30 </context-group>
31 </trans-unit>
32 <trans-unit id="912f005563d20191efc188dccedd35a7c4e6b396">
33 <source>You can subscribe to this account via any ActivityPub-capable fediverse instance. For instance with Mastodon or Pleroma you can type in the search box &lt;strong&gt;@<x id="INTERPOLATION" equiv-text="{{video.account.displayName}}"/>@<x id="INTERPOLATION_1" equiv-text="{{video.account.host}}"/>&lt;/strong&gt; and subscribe there. Subscription as a PeerTube user is being worked on in &lt;a href='https://github.com/Chocobozzz/PeerTube/issues/470'&gt;#470&lt;/a&gt;.</source>
34 <target>You can subscribe to this account via any ActivityPub-capable fediverse instance. For instance with Mastodon or Pleroma you can type in the search box &lt;strong&gt;@<x id="INTERPOLATION" equiv-text="{{video.account.displayName}}"/>@<x id="INTERPOLATION_1" equiv-text="{{video.account.host}}"/>&lt;/strong&gt; and subscribe there. Subscription as a PeerTube user is being worked on in &lt;a href='https://github.com/Chocobozzz/PeerTube/issues/470'&gt;#470&lt;/a&gt;.</target>
35 <context-group name="null">
36 <context context-type="linenumber">24</context>
37 </context-group>
38 </trans-unit>
39 <trans-unit id="ccc07df383b7a32be3e2e105faa5488caf261c1c">
40 <source>By <x id="INTERPOLATION" equiv-text="{{ video.by }}"/></source>
41 <target>Par <x id="INTERPOLATION" equiv-text="{{ video.by }}"/></target>
42 <context-group name="null">
43 <context context-type="linenumber">29</context>
44 </context-group>
45 </trans-unit>
46 <trans-unit id="e88300c71e0cb0f346d5a72eb37c920f2aadae8a">
47 <source>Go the account page</source>
48 <target>Aller sur la page du compte</target>
49 <context-group name="null">
50 <context context-type="linenumber">28</context>
51 </context-group>
52 </trans-unit>
53 <trans-unit id="82b59049f3f89d900c98da9319e156dd513e3ced">
54 <source>Like this video</source>
55 <target>J'aime cette vidéo</target>
56 <context-group name="null">
57 <context context-type="linenumber">41</context>
58 </context-group>
59 </trans-unit>
60 <trans-unit id="623698f075025b2b2fc2e0c59fd95f4f4662a509">
61 <source>Dislike this video</source>
62 <target>Je n'aime pas cette vidéo</target>
63 <context-group name="null">
64 <context context-type="linenumber">48</context>
65 </context-group>
66 </trans-unit>
67 <trans-unit id="b5629d298ff1a69b8db19a4ba2995c76b52da604">
68 <source>Support</source>
69 <target>Supporter</target>
70 <context-group name="null">
71 <context context-type="linenumber">53</context>
72 </context-group>
73 </trans-unit>
74 <trans-unit id="0bd8b27f60a1f098a53e06328426d818e3508ff9">
75 <source>Share</source>
76 <target>Partager</target>
77 <context-group name="null">
78 <context context-type="linenumber">58</context>
79 </context-group>
80 </trans-unit>
81 <trans-unit id="dc75033a5238fdc4f462212c847a45ba8018a3fd">
82 <source>Download</source>
83 <target>Télécharger</target>
84 <context-group name="null">
85 <context context-type="linenumber">69</context>
86 </context-group>
87 </trans-unit>
88 <trans-unit id="144fff5c40b85414d59e644d8dee7cfefba925a2">
89 <source>Download the video</source>
90 <target>Télécharger la vidéo</target>
91 <context-group name="null">
92 <context context-type="linenumber">68</context>
93 </context-group>
94 </trans-unit>
95 <trans-unit id="f72992030f134408b675152c397f9d0ec00f3b2a">
96 <source>Report</source>
97 <target>Signaler</target>
98 <context-group name="null">
99 <context context-type="linenumber">75</context>
100 </context-group>
101 </trans-unit>
102 <trans-unit id="2f4894617d9c44010f87473e583bd4604b7d6ecf">
103 <source>Report this video</source>
104 <target>Signaler cette vidéo</target>
105 <context-group name="null">
106 <context context-type="linenumber">74</context>
107 </context-group>
108 </trans-unit>
109 <trans-unit id="007ab5fa2aae8a7372307d3fc45a2dbcb11ffd61">
110 <source>Blacklist</source>
111 <target>Blacklister</target>
112 <context-group name="null">
113 <context context-type="linenumber">81</context>
114 </context-group>
115 </trans-unit>
116 <trans-unit id="803c6317abd2dbafcc93226c4e273c62932e3037">
117 <source>Blacklist this video</source>
118 <target>Blacklister cette vidéo</target>
119 <context-group name="null">
120 <context context-type="linenumber">80</context>
121 </context-group>
122 </trans-unit>
123 <trans-unit id="047f50bc5b5d17b5bec0196355953e1a5c590ddb">
124 <source>Update</source>
125 <target>Mettre à jour</target>
126 <context-group name="null">
127 <context context-type="linenumber">87</context>
128 </context-group>
129 </trans-unit>
130 <trans-unit id="cd27f761b923a5bdb16ba9844da632edd878f1b1">
131 <source>Update this video</source>
132 <target>Mettre à jour cette vidéo</target>
133 <context-group name="null">
134 <context context-type="linenumber">86</context>
135 </context-group>
136 </trans-unit>
137 <trans-unit id="826b25211922a1b46436589233cb6f1a163d89b7">
138 <source>Delete</source>
139 <target>Supprimer</target>
140 <context-group name="null">
141 <context context-type="linenumber">93</context>
142 </context-group>
143 </trans-unit>
144 <trans-unit id="3dbfdc68f83d91cb360172eb65578cae94e7cbe5">
145 <source>Delete this video</source>
146 <target>Supprimer cette vidéo</target>
147 <context-group name="null">
148 <context context-type="linenumber">92</context>
149 </context-group>
150 </trans-unit>
151 <trans-unit id="f0c5f6f270e70cbe063b5368fcf48f9afc1abd9b">
152 <source>Show more</source>
153 <target>Montrer plus</target>
154 <context-group name="null">
155 <context context-type="linenumber">112</context>
156 </context-group>
157 </trans-unit>
158 <trans-unit id="5403a767248e304199592271bba3366d2ca3f903">
159 <source>Show less</source>
160 <target>Montrer moins</target>
161 <context-group name="null">
162 <context context-type="linenumber">118</context>
163 </context-group>
164 </trans-unit>
165 <trans-unit id="8057a9b7f9e908ff350edfd71417b96c174e5911">
166 <source>
167 Privacy
168 </source>
169 <target>Visibilité</target>
170 <context-group name="null">
171 <context context-type="linenumber">125</context>
172 </context-group>
173 </trans-unit>
174 <trans-unit id="bd407eca607a8905a26a9e30c9d0cd70f4465db8">
175 <source>
176 Category
177 </source>
178 <target>Catégorie</target>
179 <context-group name="null">
180 <context context-type="linenumber">134</context>
181 </context-group>
182 </trans-unit>
183 <trans-unit id="b6b7986bc3721ac483baf20bc9a320529075c807">
184 <source>Trending</source>
185 <target>Tendances</target>
186 <context-group name="null">
187 <context context-type="linenumber">1</context>
188 </context-group>
189 </trans-unit>
190 </body>
191 </file></xliff> \ No newline at end of file