aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app')
-rw-r--r--client/src/app/+accounts/account-about/account-about.component.html6
-rw-r--r--client/src/app/+accounts/account-about/account-about.component.ts6
-rw-r--r--client/src/app/+accounts/account-video-channels/account-video-channels.component.html4
-rw-r--r--client/src/app/+accounts/account-videos/account-videos.component.ts12
-rw-r--r--client/src/app/+accounts/accounts.component.html8
-rw-r--r--client/src/app/+admin/admin.component.html12
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html140
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts28
-rw-r--r--client/src/app/+admin/follows/followers-list/followers-list.component.html12
-rw-r--r--client/src/app/+admin/follows/followers-list/followers-list.component.ts6
-rw-r--r--client/src/app/+admin/follows/following-add/following-add.component.html6
-rw-r--r--client/src/app/+admin/follows/following-add/following-add.component.ts22
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.html8
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.ts18
-rw-r--r--client/src/app/+admin/follows/follows.component.html2
-rw-r--r--client/src/app/+admin/jobs/index.ts5
-rw-r--r--client/src/app/+admin/jobs/jobs-list/jobs-list.component.html12
-rw-r--r--client/src/app/+admin/jobs/jobs-list/jobs-list.component.ts6
-rw-r--r--client/src/app/+admin/users/shared/user.service.ts7
-rw-r--r--client/src/app/+admin/users/user-edit/user-create.component.ts21
-rw-r--r--client/src/app/+admin/users/user-edit/user-edit.component.html20
-rw-r--r--client/src/app/+admin/users/user-edit/user-update.component.ts11
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.html14
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.ts19
-rw-r--r--client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html14
-rw-r--r--client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts6
-rw-r--r--client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html16
-rw-r--r--client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.ts23
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.html8
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts8
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html6
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts6
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-settings.component.html14
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-settings.component.ts10
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.html17
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.ts8
-rw-r--r--client/src/app/+my-account/my-account-video-channels/my-account-video-channel-create.component.ts11
-rw-r--r--client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html10
-rw-r--r--client/src/app/+my-account/my-account-video-channels/my-account-video-channel-update.component.ts11
-rw-r--r--client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html6
-rw-r--r--client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.ts20
-rw-r--r--client/src/app/+my-account/my-account-videos/my-account-videos.component.html8
-rw-r--r--client/src/app/+my-account/my-account-videos/my-account-videos.component.ts22
-rw-r--r--client/src/app/+my-account/my-account.component.html6
-rw-r--r--client/src/app/+page-not-found/page-not-found.component.html2
-rw-r--r--client/src/app/+video-channels/video-channel-about/video-channel-about.component.html8
-rw-r--r--client/src/app/+video-channels/video-channel-about/video-channel-about.component.ts6
-rw-r--r--client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts12
-rw-r--r--client/src/app/+video-channels/video-channels.component.html10
-rw-r--r--client/src/app/about/about.component.html24
-rw-r--r--client/src/app/about/about.component.ts6
-rw-r--r--client/src/app/core/auth/auth.service.ts17
-rw-r--r--client/src/app/core/confirm/confirm.component.html2
-rw-r--r--client/src/app/core/confirm/confirm.component.ts8
-rw-r--r--client/src/app/header/header.component.html4
-rw-r--r--client/src/app/login/login.component.html28
-rw-r--r--client/src/app/login/login.component.ts25
-rw-r--r--client/src/app/menu/menu.component.html20
-rw-r--r--client/src/app/menu/menu.component.ts6
-rw-r--r--client/src/app/reset-password/reset-password.component.html12
-rw-r--r--client/src/app/reset-password/reset-password.component.ts8
-rw-r--r--client/src/app/shared/forms/markdown-textarea.component.html4
-rw-r--r--client/src/app/shared/forms/markdown-textarea.component.ts2
-rw-r--r--client/src/app/shared/guards/can-deactivate-guard.service.ts10
-rw-r--r--client/src/app/shared/misc/edit-button.component.html2
-rw-r--r--client/src/app/shared/misc/from-now.pipe.ts25
-rw-r--r--client/src/app/shared/misc/help.component.html1
-rw-r--r--client/src/app/shared/rest/rest-extractor.service.ts16
-rw-r--r--client/src/app/shared/video/abstract-video-list.html2
-rw-r--r--client/src/app/shared/video/abstract-video-list.ts4
-rw-r--r--client/src/app/shared/video/video-miniature.component.html2
-rw-r--r--client/src/app/shared/video/video-thumbnail.component.html2
-rw-r--r--client/src/app/signup/signup.component.html22
-rw-r--r--client/src/app/signup/signup.component.ts19
-rw-r--r--client/src/app/videos/+video-edit/shared/video-edit.component.html36
-rw-r--r--client/src/app/videos/+video-edit/shared/video-image.component.html2
-rw-r--r--client/src/app/videos/+video-edit/video-add.component.html14
-rw-r--r--client/src/app/videos/+video-edit/video-add.component.ts37
-rw-r--r--client/src/app/videos/+video-edit/video-update.component.html4
-rw-r--r--client/src/app/videos/+video-edit/video-update.component.ts10
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comment-add.component.html4
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comment-add.component.ts6
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comment.component.html6
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comments.component.html29
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comments.component.ts16
-rw-r--r--client/src/app/videos/+video-watch/modal/video-download.component.html10
-rw-r--r--client/src/app/videos/+video-watch/modal/video-report.component.html8
-rw-r--r--client/src/app/videos/+video-watch/modal/video-report.component.ts10
-rw-r--r--client/src/app/videos/+video-watch/modal/video-share.component.html10
-rw-r--r--client/src/app/videos/+video-watch/modal/video-share.component.ts8
-rw-r--r--client/src/app/videos/+video-watch/modal/video-support.component.html4
-rw-r--r--client/src/app/videos/video-list/video-local.component.ts4
-rw-r--r--client/src/app/videos/video-list/video-recently-added.component.ts4
-rw-r--r--client/src/app/videos/video-list/video-search.component.ts4
-rw-r--r--client/src/app/videos/video-list/video-trending.component.ts4
95 files changed, 672 insertions, 502 deletions
diff --git a/client/src/app/+accounts/account-about/account-about.component.html b/client/src/app/+accounts/account-about/account-about.component.html
index eae1cb509..b178f5cc8 100644
--- a/client/src/app/+accounts/account-about/account-about.component.html
+++ b/client/src/app/+accounts/account-about/account-about.component.html
@@ -1,12 +1,12 @@
1<div *ngIf="account" class="row"> 1<div *ngIf="account" class="row">
2 <div class="block col-md-6 col-sm-12"> 2 <div class="block col-md-6 col-sm-12">
3 <div class="small-title">Description</div> 3 <div i18n class="small-title">Description</div>
4 <div class="content">{{ getAccountDescription() }}</div> 4 <div class="content">{{ getAccountDescription() }}</div>
5 </div> 5 </div>
6 6
7 <div class="block col-md-6 col-sm-12"> 7 <div class="block col-md-6 col-sm-12">
8 <div class="small-title">Stats</div> 8 <div i18n class="small-title">Stats</div>
9 9
10 <div class="content">Joined {{ account.createdAt | date }}</div> 10 <div i18n class="content">Joined {{ account.createdAt | date }}</div>
11 </div> 11 </div>
12</div> \ No newline at end of file 12</div> \ No newline at end of file
diff --git a/client/src/app/+accounts/account-about/account-about.component.ts b/client/src/app/+accounts/account-about/account-about.component.ts
index f063df392..4086510ba 100644
--- a/client/src/app/+accounts/account-about/account-about.component.ts
+++ b/client/src/app/+accounts/account-about/account-about.component.ts
@@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'
2import { ActivatedRoute } from '@angular/router' 2import { ActivatedRoute } from '@angular/router'
3import { Account } from '@app/shared/account/account.model' 3import { Account } from '@app/shared/account/account.model'
4import { AccountService } from '@app/shared/account/account.service' 4import { AccountService } from '@app/shared/account/account.service'
5import { I18n } from '@ngx-translate/i18n-polyfill'
5 6
6@Component({ 7@Component({
7 selector: 'my-account-about', 8 selector: 'my-account-about',
@@ -12,7 +13,8 @@ export class AccountAboutComponent implements OnInit {
12 account: Account 13 account: Account
13 14
14 constructor ( 15 constructor (
15 protected route: ActivatedRoute, 16 private route: ActivatedRoute,
17 private i18n: I18n,
16 private accountService: AccountService 18 private accountService: AccountService
17 ) { } 19 ) { }
18 20
@@ -25,6 +27,6 @@ export class AccountAboutComponent implements OnInit {
25 getAccountDescription () { 27 getAccountDescription () {
26 if (this.account.description) return this.account.description 28 if (this.account.description) return this.account.description
27 29
28 return 'No description' 30 return this.i18n('No description')
29 } 31 }
30} 32}
diff --git a/client/src/app/+accounts/account-video-channels/account-video-channels.component.html b/client/src/app/+accounts/account-video-channels/account-video-channels.component.html
index d20b40c60..bcd3beaf0 100644
--- a/client/src/app/+accounts/account-video-channels/account-video-channels.component.html
+++ b/client/src/app/+accounts/account-video-channels/account-video-channels.component.html
@@ -1,11 +1,11 @@
1<div *ngIf="account" class="row"> 1<div *ngIf="account" class="row">
2 <a 2 <a
3 *ngFor="let videoChannel of videoChannels" [routerLink]="[ '/video-channels', videoChannel.uuid ]" 3 *ngFor="let videoChannel of videoChannels" [routerLink]="[ '/video-channels', videoChannel.uuid ]"
4 class="video-channel" title="See this video channel" 4 class="video-channel" i18n-title title="See this video channel"
5 > 5 >
6 <img [src]="videoChannel.avatarUrl" alt="Avatar" /> 6 <img [src]="videoChannel.avatarUrl" alt="Avatar" />
7 7
8 <div class="video-channel-display-name">{{ videoChannel.displayName }}</div> 8 <div class="video-channel-display-name">{{ videoChannel.displayName }}</div>
9 <div class="video-channel-followers">{{ videoChannel.followersCount }} subscribers</div> 9 <div i18n class="video-channel-followers">{{ videoChannel.followersCount }} subscribers</div>
10 </a> 10 </a>
11</div> \ No newline at end of file 11</div> \ No newline at end of file
diff --git a/client/src/app/+accounts/account-videos/account-videos.component.ts b/client/src/app/+accounts/account-videos/account-videos.component.ts
index eca9fb6b7..7b7629480 100644
--- a/client/src/app/+accounts/account-videos/account-videos.component.ts
+++ b/client/src/app/+accounts/account-videos/account-videos.component.ts
@@ -10,6 +10,7 @@ import { VideoService } from '../../shared/video/video.service'
10import { Account } from '@app/shared/account/account.model' 10import { Account } from '@app/shared/account/account.model'
11import { AccountService } from '@app/shared/account/account.service' 11import { AccountService } from '@app/shared/account/account.service'
12import { tap } from 'rxjs/operators' 12import { tap } from 'rxjs/operators'
13import { I18n } from '@ngx-translate/i18n-polyfill'
13 14
14@Component({ 15@Component({
15 selector: 'my-account-videos', 16 selector: 'my-account-videos',
@@ -20,7 +21,7 @@ import { tap } from 'rxjs/operators'
20 ] 21 ]
21}) 22})
22export class AccountVideosComponent extends AbstractVideoList implements OnInit, OnDestroy { 23export class AccountVideosComponent extends AbstractVideoList implements OnInit, OnDestroy {
23 titlePage = 'Published videos' 24 titlePage: string
24 marginContent = false // Disable margin 25 marginContent = false // Disable margin
25 currentRoute = '/account/videos' 26 currentRoute = '/account/videos'
26 loadOnInit = false 27 loadOnInit = false
@@ -34,10 +35,13 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit,
34 protected notificationsService: NotificationsService, 35 protected notificationsService: NotificationsService,
35 protected confirmService: ConfirmService, 36 protected confirmService: ConfirmService,
36 protected location: Location, 37 protected location: Location,
38 protected i18n: I18n,
37 private accountService: AccountService, 39 private accountService: AccountService,
38 private videoService: VideoService 40 private videoService: VideoService
39 ) { 41 ) {
40 super() 42 super()
43
44 this.titlePage = this.i18n('Published videos')
41 } 45 }
42 46
43 ngOnInit () { 47 ngOnInit () {
@@ -63,7 +67,11 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit,
63 67
64 return this.videoService 68 return this.videoService
65 .getAccountVideos(this.account, newPagination, this.sort) 69 .getAccountVideos(this.account, newPagination, this.sort)
66 .pipe(tap(({ totalVideos }) => this.titlePage = `Published ${totalVideos} videos`)) 70 .pipe(
71 tap(({ totalVideos }) => {
72 this.titlePage = this.i18n('Published {{ totalVideos }} videos', { totalVideos })
73 })
74 )
67 } 75 }
68 76
69 generateSyndicationList () { 77 generateSyndicationList () {
diff --git a/client/src/app/+accounts/accounts.component.html b/client/src/app/+accounts/accounts.component.html
index e872bda03..69f648269 100644
--- a/client/src/app/+accounts/accounts.component.html
+++ b/client/src/app/+accounts/accounts.component.html
@@ -9,16 +9,16 @@
9 <div class="actor-display-name">{{ account.displayName }}</div> 9 <div class="actor-display-name">{{ account.displayName }}</div>
10 <div class="actor-name">{{ account.nameWithHost }}</div> 10 <div class="actor-name">{{ account.nameWithHost }}</div>
11 </div> 11 </div>
12 <div class="actor-followers">{{ account.followersCount }} subscribers</div> 12 <div i18n class="actor-followers">{{ account.followersCount }} subscribers</div>
13 </div> 13 </div>
14 </div> 14 </div>
15 15
16 <div class="links"> 16 <div class="links">
17 <a routerLink="videos" routerLinkActive="active" class="title-page">Videos</a> 17 <a i18n routerLink="videos" routerLinkActive="active" class="title-page">Videos</a>
18 18
19 <a routerLink="video-channels" routerLinkActive="active" class="title-page">Video channels</a> 19 <a i18n routerLink="video-channels" routerLinkActive="active" class="title-page">Video channels</a>
20 20
21 <a routerLink="about" routerLinkActive="active" class="title-page">About</a> 21 <a i18n routerLink="about" routerLinkActive="active" class="title-page">About</a>
22 </div> 22 </div>
23 </div> 23 </div>
24 24
diff --git a/client/src/app/+admin/admin.component.html b/client/src/app/+admin/admin.component.html
index e4644498b..1b2b89c3a 100644
--- a/client/src/app/+admin/admin.component.html
+++ b/client/src/app/+admin/admin.component.html
@@ -1,26 +1,26 @@
1<div class="row"> 1<div class="row">
2 <div class="sub-menu"> 2 <div class="sub-menu">
3 <a *ngIf="hasUsersRight()" routerLink="/admin/users" routerLinkActive="active" class="title-page"> 3 <a i18n *ngIf="hasUsersRight()" routerLink="/admin/users" routerLinkActive="active" class="title-page">
4 Users 4 Users
5 </a> 5 </a>
6 6
7 <a *ngIf="hasServerFollowRight()" routerLink="/admin/follows" routerLinkActive="active" class="title-page"> 7 <a i18n *ngIf="hasServerFollowRight()" routerLink="/admin/follows" routerLinkActive="active" class="title-page">
8 Manage follows 8 Manage follows
9 </a> 9 </a>
10 10
11 <a *ngIf="hasVideoAbusesRight()" routerLink="/admin/video-abuses" routerLinkActive="active" class="title-page"> 11 <a i18n *ngIf="hasVideoAbusesRight()" routerLink="/admin/video-abuses" routerLinkActive="active" class="title-page">
12 Video abuses 12 Video abuses
13 </a> 13 </a>
14 14
15 <a *ngIf="hasVideoBlacklistRight()" routerLink="/admin/video-blacklist" routerLinkActive="active" class="title-page"> 15 <a i18n *ngIf="hasVideoBlacklistRight()" routerLink="/admin/video-blacklist" routerLinkActive="active" class="title-page">
16 Video blacklist 16 Video blacklist
17 </a> 17 </a>
18 18
19 <a *ngIf="hasJobsRight()" routerLink="/admin/jobs" routerLinkActive="active" class="title-page"> 19 <a i18n *ngIf="hasJobsRight()" routerLink="/admin/jobs" routerLinkActive="active" class="title-page">
20 Jobs 20 Jobs
21 </a> 21 </a>
22 22
23 <a *ngIf="hasConfigRight()" routerLink="/admin/config" routerLinkActive="active" class="title-page"> 23 <a i18n *ngIf="hasConfigRight()" routerLink="/admin/config" routerLinkActive="active" class="title-page">
24 Configuration 24 Configuration
25 </a> 25 </a>
26 </div> 26 </div>
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html
index 8a1e33c56..4263b7b5f 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html
+++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html
@@ -4,13 +4,13 @@
4 4
5 <tab heading="Basic configuration"> 5 <tab heading="Basic configuration">
6 6
7 <div class="inner-form-title">Instance</div> 7 <div i18n class="inner-form-title">Instance</div>
8 8
9 <div class="form-group"> 9 <div class="form-group">
10 <label for="instanceName">Name</label> 10 <label i18n for="instanceName">Name</label>
11 <input 11 <input
12 type="text" id="instanceName" 12 type="text" id="instanceName"
13 formControlName="instanceName" [ngClass]="{ 'input-error': formErrors['instanceName'] }" 13 formControlName="instanceName" [ngClass]="{ 'input-error': formErrors['instanceName'] }"
14 > 14 >
15 <div *ngIf="formErrors.instanceName" class="form-error"> 15 <div *ngIf="formErrors.instanceName" class="form-error">
16 {{ formErrors.instanceName }} 16 {{ formErrors.instanceName }}
@@ -18,10 +18,10 @@
18 </div> 18 </div>
19 19
20 <div class="form-group"> 20 <div class="form-group">
21 <label for="instanceShortDescription">Short description</label> 21 <label i18n for="instanceShortDescription">Short description</label>
22 <textarea 22 <textarea
23 id="instanceShortDescription" formControlName="instanceShortDescription" 23 id="instanceShortDescription" formControlName="instanceShortDescription"
24 [ngClass]="{ 'input-error': formErrors['instanceShortDescription'] }" 24 [ngClass]="{ 'input-error': formErrors['instanceShortDescription'] }"
25 ></textarea> 25 ></textarea>
26 <div *ngIf="formErrors.instanceShortDescription" class="form-error"> 26 <div *ngIf="formErrors.instanceShortDescription" class="form-error">
27 {{ formErrors.instanceShortDescription }} 27 {{ formErrors.instanceShortDescription }}
@@ -29,10 +29,10 @@
29 </div> 29 </div>
30 30
31 <div class="form-group"> 31 <div class="form-group">
32 <label for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help> 32 <label i18n for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help>
33 <my-markdown-textarea 33 <my-markdown-textarea
34 id="instanceDescription" formControlName="instanceDescription" textareaWidth="500px" [previewColumn]="true" 34 id="instanceDescription" formControlName="instanceDescription" textareaWidth="500px" [previewColumn]="true"
35 [classes]="{ 'input-error': formErrors['instanceDescription'] }" 35 [classes]="{ 'input-error': formErrors['instanceDescription'] }"
36 ></my-markdown-textarea> 36 ></my-markdown-textarea>
37 <div *ngIf="formErrors.instanceDescription" class="form-error"> 37 <div *ngIf="formErrors.instanceDescription" class="form-error">
38 {{ formErrors.instanceDescription }} 38 {{ formErrors.instanceDescription }}
@@ -40,10 +40,10 @@
40 </div> 40 </div>
41 41
42 <div class="form-group"> 42 <div class="form-group">
43 <label for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help> 43 <label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help>
44 <my-markdown-textarea 44 <my-markdown-textarea
45 id="instanceTerms" formControlName="instanceTerms" textareaWidth="500px" [previewColumn]="true" 45 id="instanceTerms" formControlName="instanceTerms" textareaWidth="500px" [previewColumn]="true"
46 [ngClass]="{ 'input-error': formErrors['instanceTerms'] }" 46 [ngClass]="{ 'input-error': formErrors['instanceTerms'] }"
47 ></my-markdown-textarea> 47 ></my-markdown-textarea>
48 <div *ngIf="formErrors.instanceTerms" class="form-error"> 48 <div *ngIf="formErrors.instanceTerms" class="form-error">
49 {{ formErrors.instanceTerms }} 49 {{ formErrors.instanceTerms }}
@@ -51,12 +51,12 @@
51 </div> 51 </div>
52 52
53 <div class="form-group"> 53 <div class="form-group">
54 <label for="instanceDefaultClientRoute">Default client route</label> 54 <label i18n for="instanceDefaultClientRoute">Default client route</label>
55 <div class="peertube-select-container"> 55 <div class="peertube-select-container">
56 <select id="instanceDefaultClientRoute" formControlName="instanceDefaultClientRoute"> 56 <select id="instanceDefaultClientRoute" formControlName="instanceDefaultClientRoute">
57 <option value="/videos/trending">Videos Trending</option> 57 <option i18n value="/videos/trending">Videos Trending</option>
58 <option value="/videos/recently-added">Videos Recently Added</option> 58 <option i18n value="/videos/recently-added">Videos Recently Added</option>
59 <option value="/videos/local">Local videos</option> 59 <option i18n value="/videos/local">Local videos</option>
60 </select> 60 </select>
61 </div> 61 </div>
62 <div *ngIf="formErrors.instanceDefaultClientRoute" class="form-error"> 62 <div *ngIf="formErrors.instanceDefaultClientRoute" class="form-error">
@@ -65,14 +65,17 @@
65 </div> 65 </div>
66 66
67 <div class="form-group"> 67 <div class="form-group">
68 <label for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label> 68 <label i18n for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label>
69 <my-help helpType="custom" customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."></my-help> 69 <my-help
70 helpType="custom" i18n-customHtml
71 customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."
72 ></my-help>
70 73
71 <div class="peertube-select-container"> 74 <div class="peertube-select-container">
72 <select id="instanceDefaultNSFWPolicy" formControlName="instanceDefaultNSFWPolicy"> 75 <select id="instanceDefaultNSFWPolicy" formControlName="instanceDefaultNSFWPolicy">
73 <option value="do_not_list">Do not list</option> 76 <option i18n value="do_not_list">Do not list</option>
74 <option value="blur">Blur thumbnails</option> 77 <option i18n value="blur">Blur thumbnails</option>
75 <option value="display">Display</option> 78 <option i18n value="display">Display</option>
76 </select> 79 </select>
77 </div> 80 </div>
78 <div *ngIf="formErrors.instanceDefaultNSFWPolicy" class="form-error"> 81 <div *ngIf="formErrors.instanceDefaultNSFWPolicy" class="form-error">
@@ -80,43 +83,43 @@
80 </div> 83 </div>
81 </div> 84 </div>
82 85
83 <div class="inner-form-title">Signup</div> 86 <div i18n class="inner-form-title">Signup</div>
84 87
85 <div class="form-group"> 88 <div class="form-group">
86 <input type="checkbox" id="signupEnabled" formControlName="signupEnabled"> 89 <input type="checkbox" id="signupEnabled" formControlName="signupEnabled">
87 90
88 <label for="signupEnabled"></label> 91 <label for="signupEnabled"></label>
89 <label for="signupEnabled">Signup enabled</label> 92 <label i18n for="signupEnabled">Signup enabled</label>
90 </div> 93 </div>
91 94
92 <div *ngIf="isSignupEnabled()" class="form-group"> 95 <div *ngIf="isSignupEnabled()" class="form-group">
93 <label for="signupLimit">Signup limit</label> 96 <label i18n for="signupLimit">Signup limit</label>
94 <input 97 <input
95 type="text" id="signupLimit" 98 type="text" id="signupLimit"
96 formControlName="signupLimit" [ngClass]="{ 'input-error': formErrors['signupLimit'] }" 99 formControlName="signupLimit" [ngClass]="{ 'input-error': formErrors['signupLimit'] }"
97 > 100 >
98 <div *ngIf="formErrors.signupLimit" class="form-error"> 101 <div *ngIf="formErrors.signupLimit" class="form-error">
99 {{ formErrors.signupLimit }} 102 {{ formErrors.signupLimit }}
100 </div> 103 </div>
101 </div> 104 </div>
102 105
103 <div class="inner-form-title">Administrator</div> 106 <div i18n class="inner-form-title">Administrator</div>
104 107
105 <div class="form-group"> 108 <div class="form-group">
106 <label for="adminEmail">Admin email</label> 109 <label i18n for="adminEmail">Admin email</label>
107 <input 110 <input
108 type="text" id="adminEmail" 111 type="text" id="adminEmail"
109 formControlName="adminEmail" [ngClass]="{ 'input-error': formErrors['adminEmail'] }" 112 formControlName="adminEmail" [ngClass]="{ 'input-error': formErrors['adminEmail'] }"
110 > 113 >
111 <div *ngIf="formErrors.adminEmail" class="form-error"> 114 <div *ngIf="formErrors.adminEmail" class="form-error">
112 {{ formErrors.adminEmail }} 115 {{ formErrors.adminEmail }}
113 </div> 116 </div>
114 </div> 117 </div>
115 118
116 <div class="inner-form-title">Users</div> 119 <div i18n class="inner-form-title">Users</div>
117 120
118 <div class="form-group"> 121 <div class="form-group">
119 <label for="userVideoQuota">User default video quota</label> 122 <label i18n for="userVideoQuota">User default video quota</label>
120 <div class="peertube-select-container"> 123 <div class="peertube-select-container">
121 <select id="userVideoQuota" formControlName="userVideoQuota"> 124 <select id="userVideoQuota" formControlName="userVideoQuota">
122 <option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value"> 125 <option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value">
@@ -132,14 +135,17 @@
132 135
133 <tab heading="Services"> 136 <tab heading="Services">
134 137
135 <div class="inner-form-title">Twitter</div> 138 <div i18n class="inner-form-title">Twitter</div>
136 139
137 <div class="form-group"> 140 <div class="form-group">
138 <label for="signupLimit">Your Twitter username</label> 141 <label i18n for="signupLimit">Your Twitter username</label>
139 <my-help helpType="custom" customHtml="Indicates the Twitter account for the website or platform on which the content was published."></my-help> 142 <my-help
143 helpType="custom" i18n-customHtml
144 customHtml="Indicates the Twitter account for the website or platform on which the content was published."
145 ></my-help>
140 <input 146 <input
141 type="text" id="servicesTwitterUsername" 147 type="text" id="servicesTwitterUsername"
142 formControlName="servicesTwitterUsername" [ngClass]="{ 'input-error': formErrors['servicesTwitterUsername'] }" 148 formControlName="servicesTwitterUsername" [ngClass]="{ 'input-error': formErrors['servicesTwitterUsername'] }"
143 > 149 >
144 <div *ngIf="formErrors.servicesTwitterUsername" class="form-error"> 150 <div *ngIf="formErrors.servicesTwitterUsername" class="form-error">
145 {{ formErrors.servicesTwitterUsername }} 151 {{ formErrors.servicesTwitterUsername }}
@@ -150,29 +156,32 @@
150 <input type="checkbox" id="servicesTwitterWhitelisted" formControlName="servicesTwitterWhitelisted"> 156 <input type="checkbox" id="servicesTwitterWhitelisted" formControlName="servicesTwitterWhitelisted">
151 157
152 <label for="servicesTwitterWhitelisted"></label> 158 <label for="servicesTwitterWhitelisted"></label>
153 <label for="servicesTwitterWhitelisted">Instance whitelisted by Twitter</label> 159 <label i18n for="servicesTwitterWhitelisted">Instance whitelisted by Twitter</label>
154 <my-help helpType="custom" customHtml="If your instance is whitelisted by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br /> 160 <my-help
161 helpType="custom" i18n-customHtml
162 customHtml="If your instance is whitelisted by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br />
155If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br /> 163If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br />
156Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on <a target='_blank' rel='noopener noreferrer' href='https://cards-dev.twitter.com/validator'>https://cards-dev.twitter.com/validator</a> to see if you instance is whitelisted."></my-help> 164Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on <a target='_blank' rel='noopener noreferrer' href='https://cards-dev.twitter.com/validator'>https://cards-dev.twitter.com/validator</a> to see if you instance is whitelisted."
165 ></my-help>
157 166
158 </div> 167 </div>
159 </tab> 168 </tab>
160 169
161 <tab heading="Advanced configuration"> 170 <tab heading="Advanced configuration">
162 171
163 <div class="inner-form-title">Transcoding</div> 172 <div i18n class="inner-form-title">Transcoding</div>
164 173
165 <div class="form-group"> 174 <div class="form-group">
166 <input type="checkbox" id="transcodingEnabled" formControlName="transcodingEnabled"> 175 <input type="checkbox" id="transcodingEnabled" formControlName="transcodingEnabled">
167 176
168 <label for="transcodingEnabled"></label> 177 <label for="transcodingEnabled"></label>
169 <label for="transcodingEnabled">Transcoding enabled</label> 178 <label i18n for="transcodingEnabled">Transcoding enabled</label>
170 </div> 179 </div>
171 180
172 <ng-template [ngIf]="isTranscodingEnabled()"> 181 <ng-template [ngIf]="isTranscodingEnabled()">
173 182
174 <div class="form-group"> 183 <div class="form-group">
175 <label for="transcodingThreads">Transcoding threads</label> 184 <label i18n for="transcodingThreads">Transcoding threads</label>
176 <div class="peertube-select-container"> 185 <div class="peertube-select-container">
177 <select id="transcodingThreads" formControlName="transcodingThreads"> 186 <select id="transcodingThreads" formControlName="transcodingThreads">
178 <option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value"> 187 <option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value">
@@ -191,33 +200,39 @@ Check this checkbox, save the configuration and test with a video URL of your in
191 [formControlName]="getResolutionKey(resolution)" 200 [formControlName]="getResolutionKey(resolution)"
192 > 201 >
193 <label [for]="getResolutionKey(resolution)"></label> 202 <label [for]="getResolutionKey(resolution)"></label>
194 <label [for]="getResolutionKey(resolution)">Resolution {{ resolution }} enabled</label> 203 <label i18n [for]="getResolutionKey(resolution)">Resolution {{ resolution }} enabled</label>
195 </div> 204 </div>
196 </ng-template> 205 </ng-template>
197 206
198 <div class="inner-form-title">Cache</div> 207 <div i18n class="inner-form-title">Cache</div>
199 208
200 <div class="form-group"> 209 <div class="form-group">
201 <label for="cachePreviewsSize">Preview cache size</label> 210 <label i18n for="cachePreviewsSize">Preview cache size</label>
202 <my-help helpType="custom" customHtml="Previews are not federated. We fetch them directly from the origin instance and cache them."></my-help> 211 <my-help
212 helpType="custom" i18n-customHtml
213 customHtml="Previews are not federated. We fetch them directly from the origin instance and cache them."
214 ></my-help>
203 215
204 <input 216 <input
205 type="text" id="cachePreviewsSize" 217 type="text" id="cachePreviewsSize"
206 formControlName="cachePreviewsSize" [ngClass]="{ 'input-error': formErrors['cachePreviewsSize'] }" 218 formControlName="cachePreviewsSize" [ngClass]="{ 'input-error': formErrors['cachePreviewsSize'] }"
207 > 219 >
208 <div *ngIf="formErrors.cachePreviewsSize" class="form-error"> 220 <div *ngIf="formErrors.cachePreviewsSize" class="form-error">
209 {{ formErrors.cachePreviewsSize }} 221 {{ formErrors.cachePreviewsSize }}
210 </div> 222 </div>
211 </div> 223 </div>
212 224
213 <div class="inner-form-title">Customizations</div> 225 <div i18n class="inner-form-title">Customizations</div>
214 226
215 <div class="form-group"> 227 <div class="form-group">
216 <label for="customizationJavascript">JavaScript</label> 228 <label i18n for="customizationJavascript">JavaScript</label>
217 <my-help helpType="custom" customHtml="Write directly JavaScript code.<br />Example: <pre>console.log('my instance is amazing');</pre>"></my-help> 229 <my-help
230 helpType="custom" i18n-customHtml
231 customHtml="Write directly JavaScript code.<br />Example: <pre>console.log('my instance is amazing');</pre>"
232 ></my-help>
218 <textarea 233 <textarea
219 id="customizationJavascript" formControlName="customizationJavascript" 234 id="customizationJavascript" formControlName="customizationJavascript"
220 [ngClass]="{ 'input-error': formErrors['customizationJavascript'] }" 235 [ngClass]="{ 'input-error': formErrors['customizationJavascript'] }"
221 ></textarea> 236 ></textarea>
222 <div *ngIf="formErrors.customizationJavascript" class="form-error"> 237 <div *ngIf="formErrors.customizationJavascript" class="form-error">
223 {{ formErrors.customizationJavascript }} 238 {{ formErrors.customizationJavascript }}
@@ -228,25 +243,26 @@ Check this checkbox, save the configuration and test with a video URL of your in
228 <label for="customizationCSS">CSS</label> 243 <label for="customizationCSS">CSS</label>
229 <my-help 244 <my-help
230 helpType="custom" 245 helpType="custom"
246 i18n-customHtml
231 customHtml=" 247 customHtml="
232 Write directly CSS code. Example:<br /> 248 Write directly CSS code. Example:<br />
233 <pre> 249 <pre>
234 body { 250 body {{ '{' }}
235 background-color: red; 251 background-color: red;
236 } 252 {{ '}' }}
237 </pre> 253 </pre>
238 254
239 Prepend with <em>#custom-css</em> to override styles. Example: 255 Prepend with <em>#custom-css</em> to override styles. Example:
240 <pre> 256 <pre>
241 #custom-css .logged-in-email { 257 #custom-css .logged-in-email {{ '{' }}
242 color: red; 258 color: red;
243 } 259 {{ '}' }}
244 </pre> 260 </pre>
245 " 261 "
246 ></my-help> 262 ></my-help>
247 <textarea 263 <textarea
248 id="customizationCSS" formControlName="customizationCSS" 264 id="customizationCSS" formControlName="customizationCSS"
249 [ngClass]="{ 'input-error': formErrors['customizationCSS'] }" 265 [ngClass]="{ 'input-error': formErrors['customizationCSS'] }"
250 ></textarea> 266 ></textarea>
251 <div *ngIf="formErrors.customizationCSS" class="form-error"> 267 <div *ngIf="formErrors.customizationCSS" class="form-error">
252 {{ formErrors.customizationCSS }} 268 {{ formErrors.customizationCSS }}
@@ -255,5 +271,5 @@ Check this checkbox, save the configuration and test with a video URL of your in
255 </tab> 271 </tab>
256 </tabset> 272 </tabset>
257 273
258 <input (click)="formValidated()" type="submit" value="Update configuration" [disabled]="!form.valid"> 274 <input (click)="formValidated()" type="submit" i18n-value value="Update configuration" [disabled]="!form.valid">
259</form> 275</form>
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
index a1e334a74..3f9205460 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
+++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
@@ -8,12 +8,15 @@ import { FormReactive, USER_VIDEO_QUOTA } from '@app/shared'
8import { 8import {
9 ADMIN_EMAIL, 9 ADMIN_EMAIL,
10 CACHE_PREVIEWS_SIZE, 10 CACHE_PREVIEWS_SIZE,
11 INSTANCE_NAME, INSTANCE_SHORT_DESCRIPTION, SERVICES_TWITTER_USERNAME, 11 INSTANCE_NAME,
12 INSTANCE_SHORT_DESCRIPTION,
13 SERVICES_TWITTER_USERNAME,
12 SIGNUP_LIMIT, 14 SIGNUP_LIMIT,
13 TRANSCODING_THREADS 15 TRANSCODING_THREADS
14} from '@app/shared/forms/form-validators/custom-config' 16} from '@app/shared/forms/form-validators/custom-config'
15import { NotificationsService } from 'angular2-notifications' 17import { NotificationsService } from 'angular2-notifications'
16import { CustomConfig } from '../../../../../../shared/models/server/custom-config.model' 18import { CustomConfig } from '../../../../../../shared/models/server/custom-config.model'
19import { I18n } from '@ngx-translate/i18n-polyfill'
17 20
18@Component({ 21@Component({
19 selector: 'my-edit-custom-config', 22 selector: 'my-edit-custom-config',
@@ -77,7 +80,8 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
77 private notificationsService: NotificationsService, 80 private notificationsService: NotificationsService,
78 private configService: ConfigService, 81 private configService: ConfigService,
79 private serverService: ServerService, 82 private serverService: ServerService,
80 private confirmService: ConfirmService 83 private confirmService: ConfirmService,
84 private i18n: I18n
81 ) { 85 ) {
82 super() 86 super()
83 } 87 }
@@ -133,7 +137,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
133 this.forceCheck() 137 this.forceCheck()
134 }, 138 },
135 139
136 err => this.notificationsService.error('Error', err.message) 140 err => this.notificationsService.error(this.i18n('Error'), err.message)
137 ) 141 )
138 } 142 }
139 143
@@ -156,11 +160,15 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
156 if (customizations.length !== 0) { 160 if (customizations.length !== 0) {
157 const customizationsText = customizations.join('/') 161 const customizationsText = customizations.join('/')
158 162
159 const message = `You set custom ${customizationsText}. ` + 163 // FIXME: i18n service does not support string concatenation
160 'This could lead to security issues or bugs if you do not understand it. ' + 164 const message = this.i18n('You set custom {{ customizationsText }}. ', { customizationsText }) +
161 'Are you sure you want to update the configuration?' 165 this.i18n('This could lead to security issues or bugs if you do not understand it. ') +
162 const label = `Please type "I understand the ${customizationsText} I set" to confirm.` 166 this.i18n('Are you sure you want to update the configuration?')
163 const expectedInputValue = `I understand the ${customizationsText} I set` 167 const label = this.i18n(
168 'Please type "I understand the {{ customizationsText }} I set" to confirm.',
169 { customizationsText }
170 )
171 const expectedInputValue = this.i18n('I understand the {{ customizationsText }} I set', { customizationsText})
164 172
165 const confirmRes = await this.confirmService.confirmWithInput(message, label, expectedInputValue) 173 const confirmRes = await this.confirmService.confirmWithInput(message, label, expectedInputValue)
166 if (confirmRes === false) return 174 if (confirmRes === false) return
@@ -223,10 +231,10 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
223 231
224 this.updateForm() 232 this.updateForm()
225 233
226 this.notificationsService.success('Success', 'Configuration updated.') 234 this.notificationsService.success(this.i18n('Success'), this.i18n('Configuration updated.'))
227 }, 235 },
228 236
229 err => this.notificationsService.error('Error', err.message) 237 err => this.notificationsService.error(this.i18n('Error'), err.message)
230 ) 238 )
231 } 239 }
232 240
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.html b/client/src/app/+admin/follows/followers-list/followers-list.component.html
index 85d2a2cf6..1a6ed616a 100644
--- a/client/src/app/+admin/follows/followers-list/followers-list.component.html
+++ b/client/src/app/+admin/follows/followers-list/followers-list.component.html
@@ -4,12 +4,12 @@
4> 4>
5 <ng-template pTemplate="header"> 5 <ng-template pTemplate="header">
6 <tr> 6 <tr>
7 <th style="width: 60px">ID</th> 7 <th i18n style="width: 60px">ID</th>
8 <th>Score</th> 8 <th i18n>Score</th>
9 <th>Name</th> 9 <th i18n>Name</th>
10 <th>Host</th> 10 <th i18n>Host</th>
11 <th>State</th> 11 <th i18n>State</th>
12 <th pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> 12 <th i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
13 </tr> 13 </tr>
14 </ng-template> 14 </ng-template>
15 15
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.ts b/client/src/app/+admin/follows/followers-list/followers-list.component.ts
index 69b3e5e58..96fb67588 100644
--- a/client/src/app/+admin/follows/followers-list/followers-list.component.ts
+++ b/client/src/app/+admin/follows/followers-list/followers-list.component.ts
@@ -5,6 +5,7 @@ import { SortMeta } from 'primeng/primeng'
5import { AccountFollow } from '../../../../../../shared/models/actors/follow.model' 5import { AccountFollow } from '../../../../../../shared/models/actors/follow.model'
6import { RestPagination, RestTable } from '../../../shared' 6import { RestPagination, RestTable } from '../../../shared'
7import { FollowService } from '../shared' 7import { FollowService } from '../shared'
8import { I18n } from '@ngx-translate/i18n-polyfill'
8 9
9@Component({ 10@Component({
10 selector: 'my-followers-list', 11 selector: 'my-followers-list',
@@ -20,7 +21,8 @@ export class FollowersListComponent extends RestTable implements OnInit {
20 21
21 constructor ( 22 constructor (
22 private notificationsService: NotificationsService, 23 private notificationsService: NotificationsService,
23 private followService: FollowService 24 private followService: FollowService,
25 private i18n: I18n
24 ) { 26 ) {
25 super() 27 super()
26 } 28 }
@@ -37,7 +39,7 @@ export class FollowersListComponent extends RestTable implements OnInit {
37 this.totalRecords = resultList.total 39 this.totalRecords = resultList.total
38 }, 40 },
39 41
40 err => this.notificationsService.error('Error', err.message) 42 err => this.notificationsService.error(this.i18n('Error'), err.message)
41 ) 43 )
42 } 44 }
43} 45}
diff --git a/client/src/app/+admin/follows/following-add/following-add.component.html b/client/src/app/+admin/follows/following-add/following-add.component.html
index 25bab9d0d..72635048c 100644
--- a/client/src/app/+admin/follows/following-add/following-add.component.html
+++ b/client/src/app/+admin/follows/following-add/following-add.component.html
@@ -2,7 +2,7 @@
2 2
3<form (ngSubmit)="addFollowing()"> 3<form (ngSubmit)="addFollowing()">
4 <div class="form-group"> 4 <div class="form-group">
5 <label for="hosts">1 host (without "http://") per line</label> 5 <label i18n for="hosts">1 host (without "http://") per line</label>
6 6
7 <textarea 7 <textarea
8 type="text" class="form-control" placeholder="example.com" id="hosts" name="hosts" 8 type="text" class="form-control" placeholder="example.com" id="hosts" name="hosts"
@@ -14,9 +14,9 @@
14 </div> 14 </div>
15 </div> 15 </div>
16 16
17 <div *ngIf="httpEnabled() === false" class="alert alert-warning"> 17 <div i18n *ngIf="httpEnabled() === false" class="alert alert-warning">
18 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. 18 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
19 </div> 19 </div>
20 20
21 <input type="submit" value="Add following" [disabled]="hostsError || !hostsString" class="btn btn-default"> 21 <input type="submit" i18n-value value="Add following" [disabled]="hostsError || !hostsString" class="btn btn-default">
22</form> 22</form>
diff --git a/client/src/app/+admin/follows/following-add/following-add.component.ts b/client/src/app/+admin/follows/following-add/following-add.component.ts
index c296c8852..f197c1fe9 100644
--- a/client/src/app/+admin/follows/following-add/following-add.component.ts
+++ b/client/src/app/+admin/follows/following-add/following-add.component.ts
@@ -4,6 +4,7 @@ import { NotificationsService } from 'angular2-notifications'
4import { ConfirmService } from '../../../core' 4import { ConfirmService } from '../../../core'
5import { validateHost } from '../../../shared' 5import { validateHost } from '../../../shared'
6import { FollowService } from '../shared' 6import { FollowService } from '../shared'
7import { I18n } from '@ngx-translate/i18n-polyfill'
7 8
8@Component({ 9@Component({
9 selector: 'my-following-add', 10 selector: 'my-following-add',
@@ -19,7 +20,8 @@ export class FollowingAddComponent {
19 private router: Router, 20 private router: Router,
20 private notificationsService: NotificationsService, 21 private notificationsService: NotificationsService,
21 private confirmService: ConfirmService, 22 private confirmService: ConfirmService,
22 private followService: FollowService 23 private followService: FollowService,
24 private i18n: I18n
23 ) {} 25 ) {}
24 26
25 httpEnabled () { 27 httpEnabled () {
@@ -34,7 +36,7 @@ export class FollowingAddComponent {
34 36
35 for (const host of hosts) { 37 for (const host of hosts) {
36 if (validateHost(host) === false) { 38 if (validateHost(host) === false) {
37 newHostsErrors.push(`${host} is not valid`) 39 newHostsErrors.push(this.i18n('{{ host }} is not valid', { host }))
38 } 40 }
39 } 41 }
40 42
@@ -48,26 +50,26 @@ export class FollowingAddComponent {
48 50
49 const hosts = this.getNotEmptyHosts() 51 const hosts = this.getNotEmptyHosts()
50 if (hosts.length === 0) { 52 if (hosts.length === 0) {
51 this.error = 'You need to specify hosts to follow.' 53 this.error = this.i18n('You need to specify hosts to follow.')
52 } 54 }
53 55
54 if (!this.isHostsUnique(hosts)) { 56 if (!this.isHostsUnique(hosts)) {
55 this.error = 'Hosts need to be unique.' 57 this.error = this.i18n('Hosts need to be unique.')
56 return 58 return
57 } 59 }
58 60
59 const confirmMessage = 'If you confirm, you will send a follow request to:<br /> - ' + hosts.join('<br /> - ') 61 const confirmMessage = this.i18n('If you confirm, you will send a follow request to:<br /> - ') + hosts.join('<br /> - ')
60 const res = await this.confirmService.confirm(confirmMessage, 'Follow new server(s)') 62 const res = await this.confirmService.confirm(confirmMessage, this.i18n('Follow new server(s)'))
61 if (res === false) return 63 if (res === false) return
62 64
63 this.followService.follow(hosts).subscribe( 65 this.followService.follow(hosts).subscribe(
64 () => { 66 () => {
65 this.notificationsService.success('Success', 'Follow request(s) sent!') 67 this.notificationsService.success(this.i18n('Success'), this.i18n('Follow request(s) sent!'))
66 68
67 setTimeout(() => this.router.navigate([ '/admin/follows/following-list' ]), 500) 69 setTimeout(() => this.router.navigate([ '/admin/follows/following-list' ]), 500)
68 }, 70 },
69 71
70 err => this.notificationsService.error('Error', err.message) 72 err => this.notificationsService.error(this.i18n('Error'), err.message)
71 ) 73 )
72 } 74 }
73 75
@@ -76,10 +78,8 @@ export class FollowingAddComponent {
76 } 78 }
77 79
78 private getNotEmptyHosts () { 80 private getNotEmptyHosts () {
79 const hosts = this.hostsString 81 return this.hostsString
80 .split('\n') 82 .split('\n')
81 .filter(host => host && host.length !== 0) // Eject empty hosts 83 .filter(host => host && host.length !== 0) // Eject empty hosts
82
83 return hosts
84 } 84 }
85} 85}
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.html b/client/src/app/+admin/follows/following-list/following-list.component.html
index 24981d3e9..e4a45e88c 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.html
+++ b/client/src/app/+admin/follows/following-list/following-list.component.html
@@ -4,10 +4,10 @@
4> 4>
5 <ng-template pTemplate="header"> 5 <ng-template pTemplate="header">
6 <tr> 6 <tr>
7 <th style="width: 60px">ID</th> 7 <th i18n style="width: 60px">ID</th>
8 <th>Host</th> 8 <th i18n>Host</th>
9 <th>State</th> 9 <th i18n>State</th>
10 <th pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> 10 <th i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
11 <th></th> 11 <th></th>
12 </tr> 12 </tr>
13 </ng-template> 13 </ng-template>
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.ts b/client/src/app/+admin/follows/following-list/following-list.component.ts
index 873a5d965..2fb818c90 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.ts
+++ b/client/src/app/+admin/follows/following-list/following-list.component.ts
@@ -5,6 +5,7 @@ import { AccountFollow } from '../../../../../../shared/models/actors/follow.mod
5import { ConfirmService } from '../../../core/confirm/confirm.service' 5import { ConfirmService } from '../../../core/confirm/confirm.service'
6import { RestPagination, RestTable } from '../../../shared' 6import { RestPagination, RestTable } from '../../../shared'
7import { FollowService } from '../shared' 7import { FollowService } from '../shared'
8import { I18n } from '@ngx-translate/i18n-polyfill'
8 9
9@Component({ 10@Component({
10 selector: 'my-followers-list', 11 selector: 'my-followers-list',
@@ -20,7 +21,8 @@ export class FollowingListComponent extends RestTable implements OnInit {
20 constructor ( 21 constructor (
21 private notificationsService: NotificationsService, 22 private notificationsService: NotificationsService,
22 private confirmService: ConfirmService, 23 private confirmService: ConfirmService,
23 private followService: FollowService 24 private followService: FollowService,
25 private i18n: I18n
24 ) { 26 ) {
25 super() 27 super()
26 } 28 }
@@ -30,16 +32,22 @@ export class FollowingListComponent extends RestTable implements OnInit {
30 } 32 }
31 33
32 async removeFollowing (follow: AccountFollow) { 34 async removeFollowing (follow: AccountFollow) {
33 const res = await this.confirmService.confirm(`Do you really want to unfollow ${follow.following.host}?`, 'Unfollow') 35 const res = await this.confirmService.confirm(
36 this.i18n('Do you really want to unfollow {{ host }}?', { host: follow.following.host }),
37 this.i18n('Unfollow')
38 )
34 if (res === false) return 39 if (res === false) return
35 40
36 this.followService.unfollow(follow).subscribe( 41 this.followService.unfollow(follow).subscribe(
37 () => { 42 () => {
38 this.notificationsService.success('Success', `You are not following ${follow.following.host} anymore.`) 43 this.notificationsService.success(
44 this.i18n('Success'),
45 this.i18n('You are not following {{ host }} anymore.', { host: follow.following.host })
46 )
39 this.loadData() 47 this.loadData()
40 }, 48 },
41 49
42 err => this.notificationsService.error('Error', err.message) 50 err => this.notificationsService.error(this.i18n('Error'), err.message)
43 ) 51 )
44 } 52 }
45 53
@@ -51,7 +59,7 @@ export class FollowingListComponent extends RestTable implements OnInit {
51 this.totalRecords = resultList.total 59 this.totalRecords = resultList.total
52 }, 60 },
53 61
54 err => this.notificationsService.error('Error', err.message) 62 err => this.notificationsService.error(this.i18n('Error'), err.message)
55 ) 63 )
56 } 64 }
57} 65}
diff --git a/client/src/app/+admin/follows/follows.component.html b/client/src/app/+admin/follows/follows.component.html
index 71e82059c..a8258bf70 100644
--- a/client/src/app/+admin/follows/follows.component.html
+++ b/client/src/app/+admin/follows/follows.component.html
@@ -1,5 +1,5 @@
1<div class="admin-sub-header"> 1<div class="admin-sub-header">
2 <div class="form-sub-title">Manage follows</div> 2 <div i18n class="form-sub-title">Manage follows</div>
3 3
4 <tabset #followsMenuTabs> 4 <tabset #followsMenuTabs>
5 <tab *ngFor="let link of links"> 5 <tab *ngFor="let link of links">
diff --git a/client/src/app/+admin/jobs/index.ts b/client/src/app/+admin/jobs/index.ts
index 7b5271956..c0e0cc95d 100644
--- a/client/src/app/+admin/jobs/index.ts
+++ b/client/src/app/+admin/jobs/index.ts
@@ -1 +1,4 @@
1export * from './' 1export * from './shared'
2export * from './jobs-list'
3export * from './job.routes'
4export * from './job.component'
diff --git a/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html b/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html
index 0cc4dc3e0..20c35cb5b 100644
--- a/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html
+++ b/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html
@@ -1,5 +1,5 @@
1<div class="admin-sub-header"> 1<div class="admin-sub-header">
2 <div class="form-sub-title">Jobs list</div> 2 <div i18n class="form-sub-title">Jobs list</div>
3 3
4 <div class="peertube-select-container"> 4 <div class="peertube-select-container">
5 <select [(ngModel)]="jobState" (ngModelChange)="onJobStateChanged()"> 5 <select [(ngModel)]="jobState" (ngModelChange)="onJobStateChanged()">
@@ -15,11 +15,11 @@
15 <ng-template pTemplate="header"> 15 <ng-template pTemplate="header">
16 <tr> 16 <tr>
17 <th style="width: 27px"></th> 17 <th style="width: 27px"></th>
18 <th style="width: 60px">ID</th> 18 <th i18n style="width: 60px">ID</th>
19 <th style="width: 210px">Type</th> 19 <th i18n style="width: 210px">Type</th>
20 <th style="width: 130px">State</th> 20 <th i18n style="width: 130px">State</th>
21 <th style="width: 250px" pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> 21 <th i18n style="width: 250px" pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
22 <th style="width: 250px">Updated</th> 22 <th i18n style="width: 250px">Updated</th>
23 </tr> 23 </tr>
24 </ng-template> 24 </ng-template>
25 25
diff --git a/client/src/app/+admin/jobs/jobs-list/jobs-list.component.ts b/client/src/app/+admin/jobs/jobs-list/jobs-list.component.ts
index ad2f05c6b..29dd9f31c 100644
--- a/client/src/app/+admin/jobs/jobs-list/jobs-list.component.ts
+++ b/client/src/app/+admin/jobs/jobs-list/jobs-list.component.ts
@@ -7,6 +7,7 @@ import { JobState } from '../../../../../../shared/models'
7import { RestPagination, RestTable } from '../../../shared' 7import { RestPagination, RestTable } from '../../../shared'
8import { RestExtractor } from '../../../shared/rest/rest-extractor.service' 8import { RestExtractor } from '../../../shared/rest/rest-extractor.service'
9import { JobService } from '../shared' 9import { JobService } from '../shared'
10import { I18n } from '@ngx-translate/i18n-polyfill'
10 11
11@Component({ 12@Component({
12 selector: 'my-jobs-list', 13 selector: 'my-jobs-list',
@@ -27,7 +28,8 @@ export class JobsListComponent extends RestTable implements OnInit {
27 constructor ( 28 constructor (
28 private notificationsService: NotificationsService, 29 private notificationsService: NotificationsService,
29 private restExtractor: RestExtractor, 30 private restExtractor: RestExtractor,
30 private jobsService: JobService 31 private jobsService: JobService,
32 private i18n: I18n
31 ) { 33 ) {
32 super() 34 super()
33 } 35 }
@@ -51,7 +53,7 @@ export class JobsListComponent extends RestTable implements OnInit {
51 this.totalRecords = resultList.total 53 this.totalRecords = resultList.total
52 }, 54 },
53 55
54 err => this.notificationsService.error('Error', err.message) 56 err => this.notificationsService.error(this.i18n('Error'), err.message)
55 ) 57 )
56 } 58 }
57 59
diff --git a/client/src/app/+admin/users/shared/user.service.ts b/client/src/app/+admin/users/shared/user.service.ts
index 578cd98c3..d8b00b862 100644
--- a/client/src/app/+admin/users/shared/user.service.ts
+++ b/client/src/app/+admin/users/shared/user.service.ts
@@ -7,6 +7,7 @@ import { Observable } from 'rxjs'
7import { ResultList, UserCreate, UserUpdate } from '../../../../../../shared' 7import { ResultList, UserCreate, UserUpdate } from '../../../../../../shared'
8import { environment } from '../../../../environments/environment' 8import { environment } from '../../../../environments/environment'
9import { RestExtractor, RestPagination, RestService, User } from '../../../shared' 9import { RestExtractor, RestPagination, RestService, User } from '../../../shared'
10import { I18n } from '@ngx-translate/i18n-polyfill'
10 11
11@Injectable() 12@Injectable()
12export class UserService { 13export class UserService {
@@ -16,9 +17,9 @@ export class UserService {
16 constructor ( 17 constructor (
17 private authHttp: HttpClient, 18 private authHttp: HttpClient,
18 private restService: RestService, 19 private restService: RestService,
19 private restExtractor: RestExtractor 20 private restExtractor: RestExtractor,
20 ) { 21 private i18n: I18n
21 } 22 ) { }
22 23
23 addUser (userCreate: UserCreate) { 24 addUser (userCreate: UserCreate) {
24 return this.authHttp.post(UserService.BASE_USERS_URL, userCreate) 25 return this.authHttp.post(UserService.BASE_USERS_URL, userCreate)
diff --git a/client/src/app/+admin/users/user-edit/user-create.component.ts b/client/src/app/+admin/users/user-edit/user-create.component.ts
index 2a9882cde..b91ffa115 100644
--- a/client/src/app/+admin/users/user-edit/user-create.component.ts
+++ b/client/src/app/+admin/users/user-edit/user-create.component.ts
@@ -1,20 +1,13 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { FormBuilder, FormGroup } from '@angular/forms' 2import { FormBuilder, FormGroup } from '@angular/forms'
3import { Router } from '@angular/router' 3import { Router } from '@angular/router'
4
5import { NotificationsService } from 'angular2-notifications' 4import { NotificationsService } from 'angular2-notifications'
6
7import { UserService } from '../shared' 5import { UserService } from '../shared'
8import { 6import { USER_EMAIL, USER_PASSWORD, USER_ROLE, USER_USERNAME, USER_VIDEO_QUOTA } from '../../../shared'
9 USER_USERNAME,
10 USER_EMAIL,
11 USER_PASSWORD,
12 USER_VIDEO_QUOTA,
13 USER_ROLE
14} from '../../../shared'
15import { ServerService } from '../../../core' 7import { ServerService } from '../../../core'
16import { UserCreate, UserRole } from '../../../../../../shared' 8import { UserCreate, UserRole } from '../../../../../../shared'
17import { UserEdit } from './user-edit' 9import { UserEdit } from './user-edit'
10import { I18n } from '@ngx-translate/i18n-polyfill'
18 11
19@Component({ 12@Component({
20 selector: 'my-user-create', 13 selector: 'my-user-create',
@@ -45,7 +38,8 @@ export class UserCreateComponent extends UserEdit implements OnInit {
45 private formBuilder: FormBuilder, 38 private formBuilder: FormBuilder,
46 private router: Router, 39 private router: Router,
47 private notificationsService: NotificationsService, 40 private notificationsService: NotificationsService,
48 private userService: UserService 41 private userService: UserService,
42 private i18n: I18n
49 ) { 43 ) {
50 super() 44 super()
51 } 45 }
@@ -76,7 +70,10 @@ export class UserCreateComponent extends UserEdit implements OnInit {
76 70
77 this.userService.addUser(userCreate).subscribe( 71 this.userService.addUser(userCreate).subscribe(
78 () => { 72 () => {
79 this.notificationsService.success('Success', `User ${userCreate.username} created.`) 73 this.notificationsService.success(
74 this.i18n('Success'),
75 this.i18n('User {{ username }} created.', { username: userCreate.username })
76 )
80 this.router.navigate([ '/admin/users/list' ]) 77 this.router.navigate([ '/admin/users/list' ])
81 }, 78 },
82 79
@@ -89,6 +86,6 @@ export class UserCreateComponent extends UserEdit implements OnInit {
89 } 86 }
90 87
91 getFormButtonTitle () { 88 getFormButtonTitle () {
92 return 'Create user' 89 return this.i18n('Create user')
93 } 90 }
94} 91}
diff --git a/client/src/app/+admin/users/user-edit/user-edit.component.html b/client/src/app/+admin/users/user-edit/user-edit.component.html
index a8c0ddadb..4626a40c9 100644
--- a/client/src/app/+admin/users/user-edit/user-edit.component.html
+++ b/client/src/app/+admin/users/user-edit/user-edit.component.html
@@ -1,13 +1,13 @@
1<div class="form-sub-title" *ngIf="isCreation() === true">Create user</div> 1<div i18n class="form-sub-title" *ngIf="isCreation() === true">Create user</div>
2<div class="form-sub-title" *ngIf="isCreation() === false">Edit user {{ username }}</div> 2<div i18n class="form-sub-title" *ngIf="isCreation() === false">Edit user {{ username }}</div>
3 3
4<div *ngIf="error" class="alert alert-danger">{{ error }}</div> 4<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
5 5
6<form role="form" (ngSubmit)="formValidated()" [formGroup]="form"> 6<form role="form" (ngSubmit)="formValidated()" [formGroup]="form">
7 <div class="form-group" *ngIf="isCreation()"> 7 <div class="form-group" *ngIf="isCreation()">
8 <label for="username">Username</label> 8 <label i18n for="username">Username</label>
9 <input 9 <input
10 type="text" id="username" placeholder="john" 10 type="text" id="username" i18n-placeholder placeholder="john"
11 formControlName="username" [ngClass]="{ 'input-error': formErrors['username'] }" 11 formControlName="username" [ngClass]="{ 'input-error': formErrors['username'] }"
12 > 12 >
13 <div *ngIf="formErrors.username" class="form-error"> 13 <div *ngIf="formErrors.username" class="form-error">
@@ -16,9 +16,9 @@
16 </div> 16 </div>
17 17
18 <div class="form-group"> 18 <div class="form-group">
19 <label for="email">Email</label> 19 <label i18n for="email">Email</label>
20 <input 20 <input
21 type="text" id="email" placeholder="mail@example.com" 21 type="text" id="email" i18n-placeholder placeholder="mail@example.com"
22 formControlName="email" [ngClass]="{ 'input-error': formErrors['email'] }" 22 formControlName="email" [ngClass]="{ 'input-error': formErrors['email'] }"
23 > 23 >
24 <div *ngIf="formErrors.email" class="form-error"> 24 <div *ngIf="formErrors.email" class="form-error">
@@ -27,7 +27,7 @@
27 </div> 27 </div>
28 28
29 <div class="form-group" *ngIf="isCreation()"> 29 <div class="form-group" *ngIf="isCreation()">
30 <label for="password">Password</label> 30 <label i18n for="password">Password</label>
31 <input 31 <input
32 type="password" id="password" 32 type="password" id="password"
33 formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }" 33 formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }"
@@ -38,7 +38,7 @@
38 </div> 38 </div>
39 39
40 <div class="form-group"> 40 <div class="form-group">
41 <label for="role">Role</label> 41 <label i18n for="role">Role</label>
42 <div class="peertube-select-container"> 42 <div class="peertube-select-container">
43 <select id="role" formControlName="role"> 43 <select id="role" formControlName="role">
44 <option *ngFor="let role of roles" [value]="role.value"> 44 <option *ngFor="let role of roles" [value]="role.value">
@@ -53,7 +53,7 @@
53 </div> 53 </div>
54 54
55 <div class="form-group"> 55 <div class="form-group">
56 <label for="videoQuota">Video quota</label> 56 <label i18n for="videoQuota">Video quota</label>
57 <div class="peertube-select-container"> 57 <div class="peertube-select-container">
58 <select id="videoQuota" formControlName="videoQuota"> 58 <select id="videoQuota" formControlName="videoQuota">
59 <option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value"> 59 <option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value">
@@ -62,7 +62,7 @@
62 </select> 62 </select>
63 </div> 63 </div>
64 64
65 <div class="transcoding-information" *ngIf="isTranscodingInformationDisplayed()"> 65 <div i18n class="transcoding-information" *ngIf="isTranscodingInformationDisplayed()">
66 Transcoding is enabled on server. The video quota only take in account <strong>original</strong> video. <br /> 66 Transcoding is enabled on server. The video quota only take in account <strong>original</strong> video. <br />
67 At most, this user could use ~ {{ computeQuotaWithTranscoding() | bytes: 0 }}. 67 At most, this user could use ~ {{ computeQuotaWithTranscoding() | bytes: 0 }}.
68 </div> 68 </div>
diff --git a/client/src/app/+admin/users/user-edit/user-update.component.ts b/client/src/app/+admin/users/user-edit/user-update.component.ts
index 3cde07c65..dca555706 100644
--- a/client/src/app/+admin/users/user-edit/user-update.component.ts
+++ b/client/src/app/+admin/users/user-edit/user-update.component.ts
@@ -8,6 +8,7 @@ import { User, USER_EMAIL, USER_ROLE, USER_VIDEO_QUOTA } from '../../../shared'
8import { ServerService } from '../../../core' 8import { ServerService } from '../../../core'
9import { UserEdit } from './user-edit' 9import { UserEdit } from './user-edit'
10import { UserUpdate } from '../../../../../../shared' 10import { UserUpdate } from '../../../../../../shared'
11import { I18n } from '@ngx-translate/i18n-polyfill'
11 12
12@Component({ 13@Component({
13 selector: 'my-user-update', 14 selector: 'my-user-update',
@@ -39,7 +40,8 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
39 private router: Router, 40 private router: Router,
40 private notificationsService: NotificationsService, 41 private notificationsService: NotificationsService,
41 private formBuilder: FormBuilder, 42 private formBuilder: FormBuilder,
42 private userService: UserService 43 private userService: UserService,
44 private i18n: I18n
43 ) { 45 ) {
44 super() 46 super()
45 } 47 }
@@ -81,7 +83,10 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
81 83
82 this.userService.updateUser(this.userId, userUpdate).subscribe( 84 this.userService.updateUser(this.userId, userUpdate).subscribe(
83 () => { 85 () => {
84 this.notificationsService.success('Success', `User ${this.username} updated.`) 86 this.notificationsService.success(
87 this.i18n('Success'),
88 this.i18n('User {{ username }} updated.', { username: this.username })
89 )
85 this.router.navigate([ '/admin/users/list' ]) 90 this.router.navigate([ '/admin/users/list' ])
86 }, 91 },
87 92
@@ -94,7 +99,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
94 } 99 }
95 100
96 getFormButtonTitle () { 101 getFormButtonTitle () {
97 return 'Update user' 102 return this.i18n('Update user')
98 } 103 }
99 104
100 private onUserFetched (userJson: User) { 105 private onUserFetched (userJson: User) {
diff --git a/client/src/app/+admin/users/user-list/user-list.component.html b/client/src/app/+admin/users/user-list/user-list.component.html
index 09a4ac1e7..166fafef0 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.html
+++ b/client/src/app/+admin/users/user-list/user-list.component.html
@@ -1,9 +1,9 @@
1<div class="admin-sub-header"> 1<div class="admin-sub-header">
2 <div class="form-sub-title">Users list</div> 2 <div i18n class="form-sub-title">Users list</div>
3 3
4 <a class="add-button" routerLink="/admin/users/create"> 4 <a class="add-button" routerLink="/admin/users/create">
5 <span class="icon icon-add"></span> 5 <span class="icon icon-add"></span>
6 Create user 6 <ng-container i18n>Create user</ng-container>
7 </a> 7 </a>
8</div> 8</div>
9 9
@@ -13,11 +13,11 @@
13> 13>
14 <ng-template pTemplate="header"> 14 <ng-template pTemplate="header">
15 <tr> 15 <tr>
16 <th pSortableColumn="username">Username <p-sortIcon field="username"></p-sortIcon></th> 16 <th i18n pSortableColumn="username">Username <p-sortIcon field="username"></p-sortIcon></th>
17 <th>Email</th> 17 <th i18n>Email</th>
18 <th>Video quota</th> 18 <th i18n>Video quota</th>
19 <th>Role</th> 19 <th i18n>Role</th>
20 <th pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> 20 <th i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
21 <th></th> 21 <th></th>
22 </tr> 22 </tr>
23 </ng-template> 23 </ng-template>
diff --git a/client/src/app/+admin/users/user-list/user-list.component.ts b/client/src/app/+admin/users/user-list/user-list.component.ts
index 2cc4d4349..b644fcf71 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.ts
+++ b/client/src/app/+admin/users/user-list/user-list.component.ts
@@ -1,11 +1,10 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2
3import { NotificationsService } from 'angular2-notifications' 2import { NotificationsService } from 'angular2-notifications'
4import { SortMeta } from 'primeng/components/common/sortmeta' 3import { SortMeta } from 'primeng/components/common/sortmeta'
5
6import { ConfirmService } from '../../../core' 4import { ConfirmService } from '../../../core'
7import { RestPagination, RestTable, User } from '../../../shared' 5import { RestPagination, RestTable, User } from '../../../shared'
8import { UserService } from '../shared' 6import { UserService } from '../shared'
7import { I18n } from '@ngx-translate/i18n-polyfill'
9 8
10@Component({ 9@Component({
11 selector: 'my-user-list', 10 selector: 'my-user-list',
@@ -22,7 +21,8 @@ export class UserListComponent extends RestTable implements OnInit {
22 constructor ( 21 constructor (
23 private notificationsService: NotificationsService, 22 private notificationsService: NotificationsService,
24 private confirmService: ConfirmService, 23 private confirmService: ConfirmService,
25 private userService: UserService 24 private userService: UserService,
25 private i18n: I18n
26 ) { 26 ) {
27 super() 27 super()
28 } 28 }
@@ -33,20 +33,23 @@ export class UserListComponent extends RestTable implements OnInit {
33 33
34 async removeUser (user: User) { 34 async removeUser (user: User) {
35 if (user.username === 'root') { 35 if (user.username === 'root') {
36 this.notificationsService.error('Error', 'You cannot delete root.') 36 this.notificationsService.error(this.i18n('Error'), this.i18n('You cannot delete root.'))
37 return 37 return
38 } 38 }
39 39
40 const res = await this.confirmService.confirm('Do you really want to delete this user?', 'Delete') 40 const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this user?'), this.i18n('Delete'))
41 if (res === false) return 41 if (res === false) return
42 42
43 this.userService.removeUser(user).subscribe( 43 this.userService.removeUser(user).subscribe(
44 () => { 44 () => {
45 this.notificationsService.success('Success', `User ${user.username} deleted.`) 45 this.notificationsService.success(
46 this.i18n('Success'),
47 this.i18n('User {{ username }} deleted.', { username: user.username })
48 )
46 this.loadData() 49 this.loadData()
47 }, 50 },
48 51
49 err => this.notificationsService.error('Error', err.message) 52 err => this.notificationsService.error(this.i18n('Error'), err.message)
50 ) 53 )
51 } 54 }
52 55
@@ -62,7 +65,7 @@ export class UserListComponent extends RestTable implements OnInit {
62 this.totalRecords = resultList.total 65 this.totalRecords = resultList.total
63 }, 66 },
64 67
65 err => this.notificationsService.error('Error', err.message) 68 err => this.notificationsService.error(this.i18n('Error'), err.message)
66 ) 69 )
67 } 70 }
68} 71}
diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html
index 5f70db991..8111e5f73 100644
--- a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html
+++ b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html
@@ -1,5 +1,5 @@
1<div class="admin-sub-header"> 1<div class="admin-sub-header">
2 <div class="form-sub-title">Video abuses list</div> 2 <div i18n class="form-sub-title">Video abuses list</div>
3</div> 3</div>
4 4
5<p-table 5<p-table
@@ -8,10 +8,10 @@
8> 8>
9 <ng-template pTemplate="header"> 9 <ng-template pTemplate="header">
10 <tr> 10 <tr>
11 <th>Reason</th> 11 <th i18n>Reason</th>
12 <th>Reporter</th> 12 <th i18n>Reporter</th>
13 <th pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> 13 <th i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
14 <th>Video</th> 14 <th i18n>Video</th>
15 </tr> 15 </tr>
16 </ng-template> 16 </ng-template>
17 17
@@ -19,13 +19,13 @@
19 <tr> 19 <tr>
20 <td>{{ videoAbuse.reason }}</td> 20 <td>{{ videoAbuse.reason }}</td>
21 <td> 21 <td>
22 <a [href]="videoAbuse.reporterAccount.url" title="Go to the account" target="_blank" rel="noopener noreferrer"> 22 <a [href]="videoAbuse.reporterAccount.url" i18n-title title="Go to the account" target="_blank" rel="noopener noreferrer">
23 {{ createByString(videoAbuse.reporterAccount) }} 23 {{ createByString(videoAbuse.reporterAccount) }}
24 </a> 24 </a>
25 </td> 25 </td>
26 <td>{{ videoAbuse.createdAt }}</td> 26 <td>{{ videoAbuse.createdAt }}</td>
27 <td> 27 <td>
28 <a [href]="videoAbuse.video.url" title="Go to the video" target="_blank" rel="noopener noreferrer"> 28 <a [href]="videoAbuse.video.url" i18n-title title="Go to the video" target="_blank" rel="noopener noreferrer">
29 {{ videoAbuse.video.name }} 29 {{ videoAbuse.video.name }}
30 </a> 30 </a>
31 </td> 31 </td>
diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts
index b650194b7..6ddebff7e 100644
--- a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts
+++ b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts
@@ -5,6 +5,7 @@ import { SortMeta } from 'primeng/components/common/sortmeta'
5import { VideoAbuse } from '../../../../../../shared' 5import { VideoAbuse } from '../../../../../../shared'
6 6
7import { RestPagination, RestTable, VideoAbuseService } from '../../../shared' 7import { RestPagination, RestTable, VideoAbuseService } from '../../../shared'
8import { I18n } from '@ngx-translate/i18n-polyfill'
8 9
9@Component({ 10@Component({
10 selector: 'my-video-abuse-list', 11 selector: 'my-video-abuse-list',
@@ -20,7 +21,8 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
20 21
21 constructor ( 22 constructor (
22 private notificationsService: NotificationsService, 23 private notificationsService: NotificationsService,
23 private videoAbuseService: VideoAbuseService 24 private videoAbuseService: VideoAbuseService,
25 private i18n: I18n
24 ) { 26 ) {
25 super() 27 super()
26 } 28 }
@@ -41,7 +43,7 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
41 this.totalRecords = resultList.total 43 this.totalRecords = resultList.total
42 }, 44 },
43 45
44 err => this.notificationsService.error('Error', err.message) 46 err => this.notificationsService.error(this.i18n('Error'), err.message)
45 ) 47 )
46 } 48 }
47} 49}
diff --git a/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html b/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html
index 0a0fcb762..04f0e3b5c 100644
--- a/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html
+++ b/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html
@@ -1,5 +1,5 @@
1<div class="admin-sub-header"> 1<div class="admin-sub-header">
2 <div class="form-sub-title">Blacklisted videos</div> 2 <div i18n class="form-sub-title">Blacklisted videos</div>
3</div> 3</div>
4 4
5<p-table 5<p-table
@@ -8,12 +8,12 @@
8> 8>
9 <ng-template pTemplate="header"> 9 <ng-template pTemplate="header">
10 <tr> 10 <tr>
11 <th pSortableColumn="name">Name <p-sortIcon field="name"></p-sortIcon></th> 11 <th i18n pSortableColumn="name">Name <p-sortIcon field="name"></p-sortIcon></th>
12 <th>Description</th> 12 <th i18n>Description</th>
13 <th pSortableColumn="views">Views <p-sortIcon field="views"></p-sortIcon></th> 13 <th i18n pSortableColumn="views">Views <p-sortIcon field="views"></p-sortIcon></th>
14 <th>NSFW</th> 14 <th i18n>NSFW</th>
15 <th>UUID</th> 15 <th i18n>UUID</th>
16 <th pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> 16 <th i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
17 <th></th> 17 <th></th>
18 </tr> 18 </tr>
19 </ng-template> 19 </ng-template>
@@ -27,7 +27,7 @@
27 <td>{{ videoBlacklist.uuid }}</td> 27 <td>{{ videoBlacklist.uuid }}</td>
28 <td>{{ videoBlacklist.createdAt }}</td> 28 <td>{{ videoBlacklist.createdAt }}</td>
29 <td class="action-cell"> 29 <td class="action-cell">
30 <my-delete-button label="Unblacklist" (click)="removeVideoFromBlacklist(videoBlacklist)"></my-delete-button> 30 <my-delete-button i18n-label label="Unblacklist" (click)="removeVideoFromBlacklist(videoBlacklist)"></my-delete-button>
31 </td> 31 </td>
32 </tr> 32 </tr>
33 </ng-template> 33 </ng-template>
diff --git a/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.ts b/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.ts
index 7210e677c..1864e5f65 100644
--- a/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.ts
+++ b/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.ts
@@ -1,11 +1,10 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { SortMeta } from 'primeng/components/common/sortmeta' 2import { SortMeta } from 'primeng/components/common/sortmeta'
3
4import { NotificationsService } from 'angular2-notifications' 3import { NotificationsService } from 'angular2-notifications'
5
6import { ConfirmService } from '../../../core' 4import { ConfirmService } from '../../../core'
7import { VideoBlacklistService, RestTable, RestPagination } from '../../../shared' 5import { RestPagination, RestTable, VideoBlacklistService } from '../../../shared'
8import { BlacklistedVideo } from '../../../../../../shared' 6import { BlacklistedVideo } from '../../../../../../shared'
7import { I18n } from '@ngx-translate/i18n-polyfill'
9 8
10@Component({ 9@Component({
11 selector: 'my-video-blacklist-list', 10 selector: 'my-video-blacklist-list',
@@ -22,7 +21,8 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
22 constructor ( 21 constructor (
23 private notificationsService: NotificationsService, 22 private notificationsService: NotificationsService,
24 private confirmService: ConfirmService, 23 private confirmService: ConfirmService,
25 private videoBlacklistService: VideoBlacklistService 24 private videoBlacklistService: VideoBlacklistService,
25 private i18n: I18n
26 ) { 26 ) {
27 super() 27 super()
28 } 28 }
@@ -32,18 +32,23 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
32 } 32 }
33 33
34 async removeVideoFromBlacklist (entry: BlacklistedVideo) { 34 async removeVideoFromBlacklist (entry: BlacklistedVideo) {
35 const confirmMessage = 'Do you really want to remove this video from the blacklist ? It will be available again in the videos list.' 35 const confirmMessage = this.i18n(
36 'Do you really want to remove this video from the blacklist ? It will be available again in the videos list.'
37 )
36 38
37 const res = await this.confirmService.confirm(confirmMessage, 'Unblacklist') 39 const res = await this.confirmService.confirm(confirmMessage, this.i18n('Unblacklist'))
38 if (res === false) return 40 if (res === false) return
39 41
40 this.videoBlacklistService.removeVideoFromBlacklist(entry.videoId).subscribe( 42 this.videoBlacklistService.removeVideoFromBlacklist(entry.videoId).subscribe(
41 () => { 43 () => {
42 this.notificationsService.success('Success', `Video ${entry.name} removed from the blacklist.`) 44 this.notificationsService.success(
45 this.i18n('Success'),
46 this.i18n('Video {{ name }} removed from the blacklist.', { name: entry.name })
47 )
43 this.loadData() 48 this.loadData()
44 }, 49 },
45 50
46 err => this.notificationsService.error('Error', err.message) 51 err => this.notificationsService.error(this.i18n('Error'), err.message)
47 ) 52 )
48 } 53 }
49 54
@@ -55,7 +60,7 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
55 this.totalRecords = resultList.total 60 this.totalRecords = resultList.total
56 }, 61 },
57 62
58 err => this.notificationsService.error('Error', err.message) 63 err => this.notificationsService.error(this.i18n('Error'), err.message)
59 ) 64 )
60 } 65 }
61} 66}
diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.html b/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.html
index b0e3cada4..767ef0336 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.html
+++ b/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.html
@@ -2,9 +2,9 @@
2 2
3<form role="form" (ngSubmit)="changePassword()" [formGroup]="form"> 3<form role="form" (ngSubmit)="changePassword()" [formGroup]="form">
4 4
5 <label for="new-password">Change password</label> 5 <label i18n for="new-password">Change password</label>
6 <input 6 <input
7 type="password" id="new-password" placeholder="New password" 7 type="password" id="new-password" i18n-placeholder placeholder="New password"
8 formControlName="new-password" [ngClass]="{ 'input-error': formErrors['new-password'] }" 8 formControlName="new-password" [ngClass]="{ 'input-error': formErrors['new-password'] }"
9 > 9 >
10 <div *ngIf="formErrors['new-password']" class="form-error"> 10 <div *ngIf="formErrors['new-password']" class="form-error">
@@ -12,9 +12,9 @@
12 </div> 12 </div>
13 13
14 <input 14 <input
15 type="password" id="new-confirmed-password" placeholder="Confirm new password" 15 type="password" id="new-confirmed-password" i18n-placeholder placeholder="Confirm new password"
16 formControlName="new-confirmed-password" 16 formControlName="new-confirmed-password"
17 > 17 >
18 18
19 <input type="submit" value="Change password" [disabled]="!form.valid"> 19 <input type="submit" i18n-value value="Change password" [disabled]="!form.valid">
20</form> 20</form>
diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts b/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts
index 80af668f9..1a88aa82e 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts
@@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'
2import { FormBuilder, FormGroup } from '@angular/forms' 2import { FormBuilder, FormGroup } from '@angular/forms'
3import { NotificationsService } from 'angular2-notifications' 3import { NotificationsService } from 'angular2-notifications'
4import { FormReactive, USER_PASSWORD, UserService } from '../../../shared' 4import { FormReactive, USER_PASSWORD, UserService } from '../../../shared'
5import { I18n } from '@ngx-translate/i18n-polyfill'
5 6
6@Component({ 7@Component({
7 selector: 'my-account-change-password', 8 selector: 'my-account-change-password',
@@ -24,7 +25,8 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On
24 constructor ( 25 constructor (
25 private formBuilder: FormBuilder, 26 private formBuilder: FormBuilder,
26 private notificationsService: NotificationsService, 27 private notificationsService: NotificationsService,
27 private userService: UserService 28 private userService: UserService,
29 private i18n: I18n
28 ) { 30 ) {
29 super() 31 super()
30 } 32 }
@@ -49,12 +51,12 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On
49 this.error = null 51 this.error = null
50 52
51 if (newPassword !== newConfirmedPassword) { 53 if (newPassword !== newConfirmedPassword) {
52 this.error = 'The new password and the confirmed password do not correspond.' 54 this.error = this.i18n('The new password and the confirmed password do not correspond.')
53 return 55 return
54 } 56 }
55 57
56 this.userService.changePassword(newPassword).subscribe( 58 this.userService.changePassword(newPassword).subscribe(
57 () => this.notificationsService.success('Success', 'Password updated.'), 59 () => this.notificationsService.success(this.i18n('Success'), this.i18n('Password updated.')),
58 60
59 err => this.error = err.message 61 err => this.error = err.message
60 ) 62 )
diff --git a/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html b/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html
index 306f3a12c..bbdce5b02 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html
+++ b/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.html
@@ -2,7 +2,7 @@
2 2
3<form role="form" (ngSubmit)="updateMyProfile()" [formGroup]="form"> 3<form role="form" (ngSubmit)="updateMyProfile()" [formGroup]="form">
4 4
5 <label for="display-name">Display name</label> 5 <label i18n for="display-name">Display name</label>
6 <input 6 <input
7 type="text" id="display-name" 7 type="text" id="display-name"
8 formControlName="display-name" [ngClass]="{ 'input-error': formErrors['display-name'] }" 8 formControlName="display-name" [ngClass]="{ 'input-error': formErrors['display-name'] }"
@@ -11,7 +11,7 @@
11 {{ formErrors['display-name'] }} 11 {{ formErrors['display-name'] }}
12 </div> 12 </div>
13 13
14 <label for="description">Description</label> 14 <label i18n for="description">Description</label>
15 <textarea 15 <textarea
16 id="description" formControlName="description" 16 id="description" formControlName="description"
17 [ngClass]="{ 'input-error': formErrors['description'] }" 17 [ngClass]="{ 'input-error': formErrors['description'] }"
@@ -20,5 +20,5 @@
20 {{ formErrors.description }} 20 {{ formErrors.description }}
21 </div> 21 </div>
22 22
23 <input type="submit" value="Update my profile" [disabled]="!form.valid"> 23 <input type="submit" i18n-value value="Update my profile" [disabled]="!form.valid">
24</form> 24</form>
diff --git a/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts b/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts
index 468be022c..35843ecd9 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts
@@ -3,6 +3,7 @@ import { FormBuilder, FormGroup } from '@angular/forms'
3import { NotificationsService } from 'angular2-notifications' 3import { NotificationsService } from 'angular2-notifications'
4import { FormReactive, USER_DESCRIPTION, USER_DISPLAY_NAME, UserService } from '../../../shared' 4import { FormReactive, USER_DESCRIPTION, USER_DISPLAY_NAME, UserService } from '../../../shared'
5import { User } from '@app/shared' 5import { User } from '@app/shared'
6import { I18n } from '@ngx-translate/i18n-polyfill'
6 7
7@Component({ 8@Component({
8 selector: 'my-account-profile', 9 selector: 'my-account-profile',
@@ -27,7 +28,8 @@ export class MyAccountProfileComponent extends FormReactive implements OnInit {
27 constructor ( 28 constructor (
28 private formBuilder: FormBuilder, 29 private formBuilder: FormBuilder,
29 private notificationsService: NotificationsService, 30 private notificationsService: NotificationsService,
30 private userService: UserService 31 private userService: UserService,
32 private i18n: I18n
31 ) { 33 ) {
32 super() 34 super()
33 } 35 }
@@ -56,7 +58,7 @@ export class MyAccountProfileComponent extends FormReactive implements OnInit {
56 this.user.account.displayName = displayName 58 this.user.account.displayName = displayName
57 this.user.account.description = description 59 this.user.account.description = description
58 60
59 this.notificationsService.success('Success', 'Profile updated.') 61 this.notificationsService.success(this.i18n('Success'), this.i18n('Profile updated.'))
60 }, 62 },
61 63
62 err => this.error = err.message 64 err => this.error = err.message
diff --git a/client/src/app/+my-account/my-account-settings/my-account-settings.component.html b/client/src/app/+my-account/my-account-settings/my-account-settings.component.html
index 0fcc7782e..056c2a7d7 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-settings.component.html
+++ b/client/src/app/+my-account/my-account-settings/my-account-settings.component.html
@@ -6,27 +6,27 @@
6 <div class="user-info-display-name">{{ user.account.displayName }}</div> 6 <div class="user-info-display-name">{{ user.account.displayName }}</div>
7 <div class="user-info-username">{{ user.username }}</div> 7 <div class="user-info-username">{{ user.username }}</div>
8 </div> 8 </div>
9 <div class="user-info-followers">{{ user.account?.followersCount }} subscribers</div> 9 <div i18n class="user-info-followers">{{ user.account?.followersCount }} subscribers</div>
10 </div> 10 </div>
11</div> 11</div>
12 12
13<div class="button-file"> 13<div class="button-file">
14 <span>Change your avatar</span> 14 <span i18n>Change your avatar</span>
15 <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="changeAvatar()" /> 15 <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="changeAvatar()" />
16</div> 16</div>
17<div class="file-max-size">(extensions: {{ avatarExtensions }}, max size: {{ maxAvatarSize | bytes }})</div> 17<div i18n class="file-max-size">(extensions: {{ avatarExtensions }}, max size: {{ maxAvatarSize | bytes }})</div>
18 18
19<div class="user-quota"> 19<div class="user-quota">
20 <span class="user-quota-label">Video quota:</span> {{ userVideoQuotaUsed | bytes: 0 }} / {{ userVideoQuota }} 20 <span i18n class="user-quota-label">Video quota:</span> {{ userVideoQuotaUsed | bytes: 0 }} / {{ userVideoQuota }}
21</div> 21</div>
22 22
23<ng-template [ngIf]="user && user.account"> 23<ng-template [ngIf]="user && user.account">
24 <div class="account-title">Profile</div> 24 <div i18n class="account-title">Profile</div>
25 <my-account-profile [user]="user"></my-account-profile> 25 <my-account-profile [user]="user"></my-account-profile>
26</ng-template> 26</ng-template>
27 27
28<div class="account-title">Password</div> 28<div i18n class="account-title">Password</div>
29<my-account-change-password></my-account-change-password> 29<my-account-change-password></my-account-change-password>
30 30
31<div class="account-title">Video settings</div> 31<div i18n class="account-title">Video settings</div>
32<my-account-video-settings [user]="user"></my-account-video-settings> 32<my-account-video-settings [user]="user"></my-account-video-settings>
diff --git a/client/src/app/+my-account/my-account-settings/my-account-settings.component.ts b/client/src/app/+my-account/my-account-settings/my-account-settings.component.ts
index 06d1138e7..44eddaa7c 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-settings.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-settings.component.ts
@@ -5,6 +5,7 @@ import { AuthService } from '../../core'
5import { ServerService } from '../../core/server' 5import { ServerService } from '../../core/server'
6import { User } from '../../shared' 6import { User } from '../../shared'
7import { UserService } from '../../shared/users' 7import { UserService } from '../../shared/users'
8import { I18n } from '@ngx-translate/i18n-polyfill'
8 9
9@Component({ 10@Component({
10 selector: 'my-account-settings', 11 selector: 'my-account-settings',
@@ -22,7 +23,8 @@ export class MyAccountSettingsComponent implements OnInit {
22 private userService: UserService, 23 private userService: UserService,
23 private authService: AuthService, 24 private authService: AuthService,
24 private serverService: ServerService, 25 private serverService: ServerService,
25 private notificationsService: NotificationsService 26 private notificationsService: NotificationsService,
27 private i18n: I18n
26 ) {} 28 ) {}
27 29
28 ngOnInit () { 30 ngOnInit () {
@@ -33,7 +35,7 @@ export class MyAccountSettingsComponent implements OnInit {
33 if (this.user.videoQuota !== -1) { 35 if (this.user.videoQuota !== -1) {
34 this.userVideoQuota = new BytesPipe().transform(this.user.videoQuota, 0).toString() 36 this.userVideoQuota = new BytesPipe().transform(this.user.videoQuota, 0).toString()
35 } else { 37 } else {
36 this.userVideoQuota = 'Unlimited' 38 this.userVideoQuota = this.i18n('Unlimited')
37 } 39 }
38 } 40 }
39 ) 41 )
@@ -51,12 +53,12 @@ export class MyAccountSettingsComponent implements OnInit {
51 this.userService.changeAvatar(formData) 53 this.userService.changeAvatar(formData)
52 .subscribe( 54 .subscribe(
53 data => { 55 data => {
54 this.notificationsService.success('Success', 'Avatar changed.') 56 this.notificationsService.success(this.i18n('Success'), this.i18n('Avatar changed.'))
55 57
56 this.user.account.avatar = data.avatar 58 this.user.account.avatar = data.avatar
57 }, 59 },
58 60
59 err => this.notificationsService.error('Error', err.message) 61 err => this.notificationsService.error(this.i18n('Error'), err.message)
60 ) 62 )
61 } 63 }
62 64
diff --git a/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.html b/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.html
index 0e8598e9e..98587eb18 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.html
+++ b/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.html
@@ -1,13 +1,16 @@
1<form role="form" (ngSubmit)="updateDetails()" [formGroup]="form"> 1<form role="form" (ngSubmit)="updateDetails()" [formGroup]="form">
2 <div class="form-group"> 2 <div class="form-group">
3 <label for="nsfwPolicy">Default policy on videos containing sensitive content</label> 3 <label i18n for="nsfwPolicy">Default policy on videos containing sensitive content</label>
4 <my-help helpType="custom" customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."></my-help> 4 <my-help
5 helpType="custom" i18n-customHtml
6 customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."
7 ></my-help>
5 8
6 <div class="peertube-select-container"> 9 <div class="peertube-select-container">
7 <select id="nsfwPolicy" formControlName="nsfwPolicy"> 10 <select id="nsfwPolicy" formControlName="nsfwPolicy">
8 <option value="do_not_list">Do not list</option> 11 <option i18n value="do_not_list">Do not list</option>
9 <option value="blur">Blur thumbnails</option> 12 <option i18n value="blur">Blur thumbnails</option>
10 <option value="display">Display</option> 13 <option i18n value="display">Display</option>
11 </select> 14 </select>
12 </div> 15 </div>
13 </div> 16 </div>
@@ -18,8 +21,8 @@
18 formControlName="autoPlayVideo" 21 formControlName="autoPlayVideo"
19 > 22 >
20 <label for="autoPlayVideo"></label> 23 <label for="autoPlayVideo"></label>
21 <label for="autoPlayVideo">Automatically plays video</label> 24 <label i18n for="autoPlayVideo">Automatically plays video</label>
22 </div> 25 </div>
23 26
24 <input type="submit" value="Save" [disabled]="!form.valid"> 27 <input type="submit" i18n-value value="Save" [disabled]="!form.valid">
25</form> 28</form>
diff --git a/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.ts b/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.ts
index acc70c14d..4588f73db 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.ts
@@ -4,6 +4,7 @@ import { NotificationsService } from 'angular2-notifications'
4import { UserUpdateMe } from '../../../../../../shared' 4import { UserUpdateMe } from '../../../../../../shared'
5import { AuthService } from '../../../core' 5import { AuthService } from '../../../core'
6import { FormReactive, User, UserService } from '../../../shared' 6import { FormReactive, User, UserService } from '../../../shared'
7import { I18n } from '@ngx-translate/i18n-polyfill'
7 8
8@Component({ 9@Component({
9 selector: 'my-account-video-settings', 10 selector: 'my-account-video-settings',
@@ -21,7 +22,8 @@ export class MyAccountVideoSettingsComponent extends FormReactive implements OnI
21 private authService: AuthService, 22 private authService: AuthService,
22 private formBuilder: FormBuilder, 23 private formBuilder: FormBuilder,
23 private notificationsService: NotificationsService, 24 private notificationsService: NotificationsService,
24 private userService: UserService 25 private userService: UserService,
26 private i18n: I18n
25 ) { 27 ) {
26 super() 28 super()
27 } 29 }
@@ -49,12 +51,12 @@ export class MyAccountVideoSettingsComponent extends FormReactive implements OnI
49 51
50 this.userService.updateMyProfile(details).subscribe( 52 this.userService.updateMyProfile(details).subscribe(
51 () => { 53 () => {
52 this.notificationsService.success('Success', 'Information updated.') 54 this.notificationsService.success(this.i18n('Success'), this.i18n('Information updated.'))
53 55
54 this.authService.refreshUserInformation() 56 this.authService.refreshUserInformation()
55 }, 57 },
56 58
57 err => this.notificationsService.error('Error', err.message) 59 err => this.notificationsService.error(this.i18n('Error'), err.message)
58 ) 60 )
59 } 61 }
60} 62}
diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-create.component.ts b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-create.component.ts
index c82bb39c8..9b5a12d18 100644
--- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-create.component.ts
+++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-create.component.ts
@@ -11,6 +11,7 @@ import {
11} from '@app/shared/forms/form-validators/video-channel' 11} from '@app/shared/forms/form-validators/video-channel'
12import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' 12import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
13import { AuthService } from '@app/core' 13import { AuthService } from '@app/core'
14import { I18n } from '@ngx-translate/i18n-polyfill'
14 15
15@Component({ 16@Component({
16 selector: 'my-account-video-channel-create', 17 selector: 'my-account-video-channel-create',
@@ -37,7 +38,8 @@ export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelE
37 private notificationsService: NotificationsService, 38 private notificationsService: NotificationsService,
38 private router: Router, 39 private router: Router,
39 private formBuilder: FormBuilder, 40 private formBuilder: FormBuilder,
40 private videoChannelService: VideoChannelService 41 private videoChannelService: VideoChannelService,
42 private i18n: I18n
41 ) { 43 ) {
42 super() 44 super()
43 } 45 }
@@ -69,7 +71,10 @@ export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelE
69 this.videoChannelService.createVideoChannel(videoChannelCreate).subscribe( 71 this.videoChannelService.createVideoChannel(videoChannelCreate).subscribe(
70 () => { 72 () => {
71 this.authService.refreshUserInformation() 73 this.authService.refreshUserInformation()
72 this.notificationsService.success('Success', `Video channel ${videoChannelCreate.displayName} created.`) 74 this.notificationsService.success(
75 this.i18n('Success'),
76 this.i18n('Video channel {{ videoChannelName }} created.', { videoChannelName: videoChannelCreate.displayName })
77 )
73 this.router.navigate([ '/my-account', 'video-channels' ]) 78 this.router.navigate([ '/my-account', 'video-channels' ])
74 }, 79 },
75 80
@@ -82,6 +87,6 @@ export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelE
82 } 87 }
83 88
84 getFormButtonTitle () { 89 getFormButtonTitle () {
85 return 'Create' 90 return this.i18n('Create')
86 } 91 }
87} 92}
diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html
index 10d408d55..1c08cfdca 100644
--- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html
+++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html
@@ -1,11 +1,11 @@
1<div class="form-sub-title" *ngIf="isCreation() === true">Create a video channel</div> 1<div i18n class="form-sub-title" *ngIf="isCreation() === true">Create a video channel</div>
2<div class="form-sub-title" *ngIf="isCreation() === false">Update {{ videoChannel?.displayName }}</div> 2<div i18n class="form-sub-title" *ngIf="isCreation() === false">Update {{ videoChannel?.displayName }}</div>
3 3
4<div *ngIf="error" class="alert alert-danger">{{ error }}</div> 4<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
5 5
6<form role="form" (ngSubmit)="formValidated()" [formGroup]="form"> 6<form role="form" (ngSubmit)="formValidated()" [formGroup]="form">
7 <div class="form-group"> 7 <div class="form-group">
8 <label for="display-name">Display name</label> 8 <label i18n for="display-name">Display name</label>
9 <input 9 <input
10 type="text" id="display-name" 10 type="text" id="display-name"
11 formControlName="display-name" [ngClass]="{ 'input-error': formErrors['display-name'] }" 11 formControlName="display-name" [ngClass]="{ 'input-error': formErrors['display-name'] }"
@@ -16,7 +16,7 @@
16 </div> 16 </div>
17 17
18 <div class="form-group"> 18 <div class="form-group">
19 <label for="description">Description</label> 19 <label i18n for="description">Description</label>
20 <textarea 20 <textarea
21 id="description" formControlName="description" 21 id="description" formControlName="description"
22 [ngClass]="{ 'input-error': formErrors['description'] }" 22 [ngClass]="{ 'input-error': formErrors['description'] }"
@@ -29,7 +29,7 @@
29 <div class="form-group"> 29 <div class="form-group">
30 <label for="support">Support</label> 30 <label for="support">Support</label>
31 <my-help 31 <my-help
32 helpType="markdownEnhanced" preHtml="Short text to tell people how they can support your channel (membership platform...).<br /><br /> 32 helpType="markdownEnhanced" i18n-preHtml preHtml="Short text to tell people how they can support your channel (membership platform...).<br /><br />
33When you will upload a video in this channel, the video support field will be automatically filled by this text." 33When you will upload a video in this channel, the video support field will be automatically filled by this text."
34 ></my-help> 34 ></my-help>
35 <my-markdown-textarea 35 <my-markdown-textarea
diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-update.component.ts b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-update.component.ts
index b4c8df3cd..78c578764 100644
--- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-update.component.ts
+++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-update.component.ts
@@ -13,6 +13,7 @@ import { VideoChannelService } from '@app/shared/video-channel/video-channel.ser
13import { Subscription } from 'rxjs' 13import { Subscription } from 'rxjs'
14import { VideoChannel } from '@app/shared/video-channel/video-channel.model' 14import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
15import { AuthService } from '@app/core' 15import { AuthService } from '@app/core'
16import { I18n } from '@ngx-translate/i18n-polyfill'
16 17
17@Component({ 18@Component({
18 selector: 'my-account-video-channel-update', 19 selector: 'my-account-video-channel-update',
@@ -43,7 +44,8 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE
43 private router: Router, 44 private router: Router,
44 private route: ActivatedRoute, 45 private route: ActivatedRoute,
45 private formBuilder: FormBuilder, 46 private formBuilder: FormBuilder,
46 private videoChannelService: VideoChannelService 47 private videoChannelService: VideoChannelService,
48 private i18n: I18n
47 ) { 49 ) {
48 super() 50 super()
49 } 51 }
@@ -97,7 +99,10 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE
97 this.videoChannelService.updateVideoChannel(this.videoChannelToUpdate.uuid, videoChannelUpdate).subscribe( 99 this.videoChannelService.updateVideoChannel(this.videoChannelToUpdate.uuid, videoChannelUpdate).subscribe(
98 () => { 100 () => {
99 this.authService.refreshUserInformation() 101 this.authService.refreshUserInformation()
100 this.notificationsService.success('Success', `Video channel ${videoChannelUpdate.displayName} updated.`) 102 this.notificationsService.success(
103 this.i18n('Success'),
104 this.i18n('Video channel {{ videoChannelName }} updated.', { videoChannelName: videoChannelUpdate.displayName })
105 )
101 this.router.navigate([ '/my-account', 'video-channels' ]) 106 this.router.navigate([ '/my-account', 'video-channels' ])
102 }, 107 },
103 108
@@ -110,6 +115,6 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE
110 } 115 }
111 116
112 getFormButtonTitle () { 117 getFormButtonTitle () {
113 return 'Update' 118 return this.i18n('Update')
114 } 119 }
115} 120}
diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html b/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html
index 90c401bc5..d27c3b4ec 100644
--- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html
+++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html
@@ -1,7 +1,7 @@
1<div class="video-channels-header"> 1<div class="video-channels-header">
2 <a class="create-button" routerLink="create"> 2 <a class="create-button" routerLink="create">
3 <span class="icon icon-add"></span> 3 <span class="icon icon-add"></span>
4 Create another video channel 4 <ng-container i18n>Create another video channel</ng-container>
5 </a> 5 </a>
6</div> 6</div>
7 7
@@ -12,13 +12,13 @@
12 </a> 12 </a>
13 13
14 <div class="video-channel-info"> 14 <div class="video-channel-info">
15 <a [routerLink]="[ '/video-channels', videoChannel.uuid ]" class="video-channel-names" title="Go to the channel"> 15 <a [routerLink]="[ '/video-channels', videoChannel.uuid ]" class="video-channel-names" i18n-title title="Go to the channel">
16 <div class="video-channel-display-name">{{ videoChannel.displayName }}</div> 16 <div class="video-channel-display-name">{{ videoChannel.displayName }}</div>
17 <!-- Hide the name for now, because it's an UUID not very friendly --> 17 <!-- Hide the name for now, because it's an UUID not very friendly -->
18 <!--<div class="video-channel-name">{{ videoChannel.name }}</div>--> 18 <!--<div class="video-channel-name">{{ videoChannel.name }}</div>-->
19 </a> 19 </a>
20 20
21 <div class="video-channel-followers">{{ videoChannel.followersCount }} subscribers</div> 21 <div i18n class="video-channel-followers">{{ videoChannel.followersCount }} subscribers</div>
22 </div> 22 </div>
23 23
24 <div class="video-channel-buttons"> 24 <div class="video-channel-buttons">
diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.ts b/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.ts
index 20c8798d1..cff1041f6 100644
--- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.ts
+++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.ts
@@ -6,6 +6,7 @@ import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
6import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' 6import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
7import { User } from '@app/shared' 7import { User } from '@app/shared'
8import { flatMap } from 'rxjs/operators' 8import { flatMap } from 'rxjs/operators'
9import { I18n } from '@ngx-translate/i18n-polyfill'
9 10
10@Component({ 11@Component({
11 selector: 'my-account-video-channels', 12 selector: 'my-account-video-channels',
@@ -21,7 +22,8 @@ export class MyAccountVideoChannelsComponent implements OnInit {
21 private authService: AuthService, 22 private authService: AuthService,
22 private notificationsService: NotificationsService, 23 private notificationsService: NotificationsService,
23 private confirmService: ConfirmService, 24 private confirmService: ConfirmService,
24 private videoChannelService: VideoChannelService 25 private videoChannelService: VideoChannelService,
26 private i18n: I18n
25 ) {} 27 ) {}
26 28
27 ngOnInit () { 29 ngOnInit () {
@@ -32,10 +34,13 @@ export class MyAccountVideoChannelsComponent implements OnInit {
32 34
33 async deleteVideoChannel (videoChannel: VideoChannel) { 35 async deleteVideoChannel (videoChannel: VideoChannel) {
34 const res = await this.confirmService.confirmWithInput( 36 const res = await this.confirmService.confirmWithInput(
35 `Do you really want to delete ${videoChannel.displayName}? It will delete all videos uploaded in this channel too.`, 37 this.i18n(
36 'Please type the name of the video channel to confirm', 38 'Do you really want to delete {{ videoChannelName }}? It will delete all videos uploaded in this channel too.',
39 { videoChannelName: videoChannel.displayName }
40 ),
41 this.i18n('Please type the name of the video channel to confirm'),
37 videoChannel.displayName, 42 videoChannel.displayName,
38 'Delete' 43 this.i18n('Delete')
39 ) 44 )
40 if (res === false) return 45 if (res === false) return
41 46
@@ -43,10 +48,13 @@ export class MyAccountVideoChannelsComponent implements OnInit {
43 .subscribe( 48 .subscribe(
44 status => { 49 status => {
45 this.loadVideoChannels() 50 this.loadVideoChannels()
46 this.notificationsService.success('Success', `Video channel ${videoChannel.name} deleted.`) 51 this.notificationsService.success(
52 this.i18n('Success'),
53 this.i18n('Video channel {{ videoChannelName } deleted.', { videoChannelName: videoChannel.displayName })
54 )
47 }, 55 },
48 56
49 error => this.notificationsService.error('Error', error.message) 57 error => this.notificationsService.error(this.i18n('Error'), error.message)
50 ) 58 )
51 } 59 }
52 60
diff --git a/client/src/app/+my-account/my-account-videos/my-account-videos.component.html b/client/src/app/+my-account/my-account-videos/my-account-videos.component.html
index a31cb0c3d..35a99d0b3 100644
--- a/client/src/app/+my-account/my-account-videos/my-account-videos.component.html
+++ b/client/src/app/+my-account/my-account-videos/my-account-videos.component.html
@@ -1,4 +1,4 @@
1<div *ngIf="pagination.totalItems === 0">No results.</div> 1<div i18n *ngIf="pagination.totalItems === 0">No results.</div>
2 2
3<div 3<div
4 myInfiniteScroller 4 myInfiniteScroller
@@ -17,18 +17,18 @@
17 17
18 <div class="video-info"> 18 <div class="video-info">
19 <a class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a> 19 <a class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a>
20 <span class="video-info-date-views">{{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span> 20 <span i18n class="video-info-date-views">{{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span>
21 <div class="video-info-private">{{ video.privacy.label }}</div> 21 <div class="video-info-private">{{ video.privacy.label }}</div>
22 </div> 22 </div>
23 23
24 <!-- Display only once --> 24 <!-- Display only once -->
25 <div class="action-selection-mode" *ngIf="isInSelectionMode() === true && i === 0 && j === 0"> 25 <div class="action-selection-mode" *ngIf="isInSelectionMode() === true && i === 0 && j === 0">
26 <div class="action-selection-mode-child"> 26 <div class="action-selection-mode-child">
27 <span class="action-button action-button-cancel-selection" (click)="abortSelectionMode()"> 27 <span i18n class="action-button action-button-cancel-selection" (click)="abortSelectionMode()">
28 Cancel 28 Cancel
29 </span> 29 </span>
30 30
31 <span class="action-button action-button-delete-selection" (click)="deleteSelectedVideos()"> 31 <span i18n class="action-button action-button-delete-selection" (click)="deleteSelectedVideos()">
32 <span class="icon icon-delete-white"></span> 32 <span class="icon icon-delete-white"></span>
33 Delete 33 Delete
34 </span> 34 </span>
diff --git a/client/src/app/+my-account/my-account-videos/my-account-videos.component.ts b/client/src/app/+my-account/my-account-videos/my-account-videos.component.ts
index 6ab6c2aa5..eed4be01f 100644
--- a/client/src/app/+my-account/my-account-videos/my-account-videos.component.ts
+++ b/client/src/app/+my-account/my-account-videos/my-account-videos.component.ts
@@ -11,6 +11,7 @@ import { ConfirmService } from '../../core/confirm'
11import { AbstractVideoList } from '../../shared/video/abstract-video-list' 11import { AbstractVideoList } from '../../shared/video/abstract-video-list'
12import { Video } from '../../shared/video/video.model' 12import { Video } from '../../shared/video/video.model'
13import { VideoService } from '../../shared/video/video.service' 13import { VideoService } from '../../shared/video/video.service'
14import { I18n } from '@ngx-translate/i18n-polyfill'
14 15
15@Component({ 16@Component({
16 selector: 'my-account-videos', 17 selector: 'my-account-videos',
@@ -18,7 +19,7 @@ import { VideoService } from '../../shared/video/video.service'
18 styleUrls: [ './my-account-videos.component.scss' ] 19 styleUrls: [ './my-account-videos.component.scss' ]
19}) 20})
20export class MyAccountVideosComponent extends AbstractVideoList implements OnInit, OnDestroy { 21export class MyAccountVideosComponent extends AbstractVideoList implements OnInit, OnDestroy {
21 titlePage = 'My videos' 22 titlePage: string
22 currentRoute = '/my-account/videos' 23 currentRoute = '/my-account/videos'
23 checkedVideos: { [ id: number ]: boolean } = {} 24 checkedVideos: { [ id: number ]: boolean } = {}
24 pagination: ComponentPagination = { 25 pagination: ComponentPagination = {
@@ -30,14 +31,19 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni
30 protected baseVideoWidth = -1 31 protected baseVideoWidth = -1
31 protected baseVideoHeight = 155 32 protected baseVideoHeight = 155
32 33
33 constructor (protected router: Router, 34 constructor (
34 protected route: ActivatedRoute, 35 protected router: Router,
35 protected authService: AuthService, 36 protected route: ActivatedRoute,
36 protected notificationsService: NotificationsService, 37 protected authService: AuthService,
37 protected confirmService: ConfirmService, 38 protected notificationsService: NotificationsService,
38 protected location: Location, 39 protected confirmService: ConfirmService,
39 private videoService: VideoService) { 40 protected location: Location,
41 protected i18n: I18n,
42 private videoService: VideoService
43 ) {
40 super() 44 super()
45
46 this.titlePage = this.i18n('My videos')
41 } 47 }
42 48
43 ngOnInit () { 49 ngOnInit () {
diff --git a/client/src/app/+my-account/my-account.component.html b/client/src/app/+my-account/my-account.component.html
index 591d58cf9..48db55ad3 100644
--- a/client/src/app/+my-account/my-account.component.html
+++ b/client/src/app/+my-account/my-account.component.html
@@ -1,10 +1,10 @@
1<div class="row"> 1<div class="row">
2 <div class="sub-menu"> 2 <div class="sub-menu">
3 <a routerLink="/my-account/settings" routerLinkActive="active" class="title-page">My settings</a> 3 <a i18n routerLink="/my-account/settings" routerLinkActive="active" class="title-page">My settings</a>
4 4
5 <a routerLink="/my-account/video-channels" routerLinkActive="active" class="title-page">My video channels</a> 5 <a i18n routerLink="/my-account/video-channels" routerLinkActive="active" class="title-page">My video channels</a>
6 6
7 <a routerLink="/my-account/videos" routerLinkActive="active" class="title-page">My videos</a> 7 <a i18n routerLink="/my-account/videos" routerLinkActive="active" class="title-page">My videos</a>
8 </div> 8 </div>
9 9
10 <div class="margin-content"> 10 <div class="margin-content">
diff --git a/client/src/app/+page-not-found/page-not-found.component.html b/client/src/app/+page-not-found/page-not-found.component.html
index 66aa2aec7..2934003ab 100644
--- a/client/src/app/+page-not-found/page-not-found.component.html
+++ b/client/src/app/+page-not-found/page-not-found.component.html
@@ -1,3 +1,3 @@
1<div> 1<div i18n>
2 Sorry, but we couldn't find the page you were looking for. 2 Sorry, but we couldn't find the page you were looking for.
3</div> \ No newline at end of file 3</div> \ No newline at end of file
diff --git a/client/src/app/+video-channels/video-channel-about/video-channel-about.component.html b/client/src/app/+video-channels/video-channel-about/video-channel-about.component.html
index a4338121f..b7125ff71 100644
--- a/client/src/app/+video-channels/video-channel-about/video-channel-about.component.html
+++ b/client/src/app/+video-channels/video-channel-about/video-channel-about.component.html
@@ -1,20 +1,20 @@
1<div *ngIf="videoChannel" class="row"> 1<div *ngIf="videoChannel" class="row">
2 <div class="description col-md-6 col-sm-12"> 2 <div class="description col-md-6 col-sm-12">
3 <div class="block"> 3 <div class="block">
4 <div class="small-title">Description</div> 4 <div i18n class="small-title">Description</div>
5 <div class="content">{{ getVideoChannelDescription() }}</div> 5 <div class="content">{{ getVideoChannelDescription() }}</div>
6 </div> 6 </div>
7 7
8 <div class="block" *ngIf="videoChannel.support"> 8 <div class="block" *ngIf="videoChannel.support">
9 <div class="small-title">Support this channel</div> 9 <div i18n class="small-title">Support this channel</div>
10 <div class="content">{{ videoChannel.support }}</div> 10 <div class="content">{{ videoChannel.support }}</div>
11 </div> 11 </div>
12 </div> 12 </div>
13 13
14 <div class="stats col-md-6 col-sm-12"> 14 <div class="stats col-md-6 col-sm-12">
15 <div class="block"> 15 <div class="block">
16 <div class="small-title">Stats</div> 16 <div i18n class="small-title">Stats</div>
17 <div class="content">Created {{ videoChannel.createdAt | date }}</div> 17 <div i18n class="content">Created {{ videoChannel.createdAt | date }}</div>
18 </div> 18 </div>
19 </div> 19 </div>
20</div> \ No newline at end of file 20</div> \ No newline at end of file
diff --git a/client/src/app/+video-channels/video-channel-about/video-channel-about.component.ts b/client/src/app/+video-channels/video-channel-about/video-channel-about.component.ts
index 6f862718f..c5fd442c6 100644
--- a/client/src/app/+video-channels/video-channel-about/video-channel-about.component.ts
+++ b/client/src/app/+video-channels/video-channel-about/video-channel-about.component.ts
@@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'
2import { ActivatedRoute } from '@angular/router' 2import { ActivatedRoute } from '@angular/router'
3import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' 3import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
4import { VideoChannel } from '@app/shared/video-channel/video-channel.model' 4import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
5import { I18n } from '@ngx-translate/i18n-polyfill'
5 6
6@Component({ 7@Component({
7 selector: 'my-video-channel-about', 8 selector: 'my-video-channel-about',
@@ -12,7 +13,8 @@ export class VideoChannelAboutComponent implements OnInit {
12 videoChannel: VideoChannel 13 videoChannel: VideoChannel
13 14
14 constructor ( 15 constructor (
15 protected route: ActivatedRoute, 16 private route: ActivatedRoute,
17 private i18n: I18n,
16 private videoChannelService: VideoChannelService 18 private videoChannelService: VideoChannelService
17 ) { } 19 ) { }
18 20
@@ -25,6 +27,6 @@ export class VideoChannelAboutComponent implements OnInit {
25 getVideoChannelDescription () { 27 getVideoChannelDescription () {
26 if (this.videoChannel.description) return this.videoChannel.description 28 if (this.videoChannel.description) return this.videoChannel.description
27 29
28 return 'No description' 30 return this.i18n('No description')
29 } 31 }
30} 32}
diff --git a/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts b/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts
index c9e72e512..22239d75b 100644
--- a/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts
+++ b/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts
@@ -10,6 +10,7 @@ import { VideoService } from '../../shared/video/video.service'
10import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' 10import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
11import { VideoChannel } from '@app/shared/video-channel/video-channel.model' 11import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
12import { tap } from 'rxjs/operators' 12import { tap } from 'rxjs/operators'
13import { I18n } from '@ngx-translate/i18n-polyfill'
13 14
14@Component({ 15@Component({
15 selector: 'my-video-channel-videos', 16 selector: 'my-video-channel-videos',
@@ -20,7 +21,7 @@ import { tap } from 'rxjs/operators'
20 ] 21 ]
21}) 22})
22export class VideoChannelVideosComponent extends AbstractVideoList implements OnInit, OnDestroy { 23export class VideoChannelVideosComponent extends AbstractVideoList implements OnInit, OnDestroy {
23 titlePage = 'Published videos' 24 titlePage: string
24 marginContent = false // Disable margin 25 marginContent = false // Disable margin
25 currentRoute = '/video-channel/videos' 26 currentRoute = '/video-channel/videos'
26 loadOnInit = false 27 loadOnInit = false
@@ -34,10 +35,13 @@ export class VideoChannelVideosComponent extends AbstractVideoList implements On
34 protected notificationsService: NotificationsService, 35 protected notificationsService: NotificationsService,
35 protected confirmService: ConfirmService, 36 protected confirmService: ConfirmService,
36 protected location: Location, 37 protected location: Location,
38 protected i18n: I18n,
37 private videoChannelService: VideoChannelService, 39 private videoChannelService: VideoChannelService,
38 private videoService: VideoService 40 private videoService: VideoService
39 ) { 41 ) {
40 super() 42 super()
43
44 this.titlePage = this.i18n('Published videos')
41 } 45 }
42 46
43 ngOnInit () { 47 ngOnInit () {
@@ -63,7 +67,11 @@ export class VideoChannelVideosComponent extends AbstractVideoList implements On
63 67
64 return this.videoService 68 return this.videoService
65 .getVideoChannelVideos(this.videoChannel, newPagination, this.sort) 69 .getVideoChannelVideos(this.videoChannel, newPagination, this.sort)
66 .pipe(tap(({ totalVideos }) => this.titlePage = `Published ${totalVideos} videos`)) 70 .pipe(
71 tap(({ totalVideos }) => {
72 this.titlePage = this.i18n('Published {{ totalVideos }} videos', { totalVideos })
73 })
74 )
67 } 75 }
68 76
69 generateSyndicationList () { 77 generateSyndicationList () {
diff --git a/client/src/app/+video-channels/video-channels.component.html b/client/src/app/+video-channels/video-channels.component.html
index 6b25d16ab..a52894cac 100644
--- a/client/src/app/+video-channels/video-channels.component.html
+++ b/client/src/app/+video-channels/video-channels.component.html
@@ -8,19 +8,19 @@
8 <div class="actor-names"> 8 <div class="actor-names">
9 <div class="actor-display-name">{{ videoChannel.displayName }}</div> 9 <div class="actor-display-name">{{ videoChannel.displayName }}</div>
10 </div> 10 </div>
11 <div class="actor-followers">{{ videoChannel.followersCount }} subscribers</div> 11 <div i18n class="actor-followers">{{ videoChannel.followersCount }} subscribers</div>
12 12
13 <a [routerLink]="[ '/accounts', videoChannel.ownerBy ]" title="Go the owner account page" class="actor-owner"> 13 <a [routerLink]="[ '/accounts', videoChannel.ownerBy ]" i18n-title title="Go the owner account page" class="actor-owner">
14 <span>Created by {{ videoChannel.ownerBy }}</span> 14 <span i18n>Created by {{ videoChannel.ownerBy }}</span>
15 <img [src]="videoChannel.ownerAvatarUrl" alt="Owner account avatar" /> 15 <img [src]="videoChannel.ownerAvatarUrl" alt="Owner account avatar" />
16 </a> 16 </a>
17 </div> 17 </div>
18 </div> 18 </div>
19 19
20 <div class="links"> 20 <div class="links">
21 <a routerLink="videos" routerLinkActive="active" class="title-page">Videos</a> 21 <a i18n routerLink="videos" routerLinkActive="active" class="title-page">Videos</a>
22 22
23 <a routerLink="about" routerLinkActive="active" class="title-page">About</a> 23 <a i18n routerLink="about" routerLinkActive="active" class="title-page">About</a>
24 </div> 24 </div>
25 </div> 25 </div>
26 26
diff --git a/client/src/app/about/about.component.html b/client/src/app/about/about.component.html
index 6fa856742..22d3c9a36 100644
--- a/client/src/app/about/about.component.html
+++ b/client/src/app/about/about.component.html
@@ -1,5 +1,5 @@
1<div class="margin-content"> 1<div class="margin-content">
2 <div class="title-page title-page-single"> 2 <div i18n class="title-page title-page-single">
3 Welcome to the {{ instanceName }} instance 3 Welcome to the {{ instanceName }} instance
4 </div> 4 </div>
5 5
@@ -8,41 +8,41 @@
8 </div> 8 </div>
9 9
10 <div class="description"> 10 <div class="description">
11 <div class="section-title">Description</div> 11 <div i18n class="section-title">Description</div>
12 12
13 <div [innerHTML]="descriptionHTML"></div> 13 <div [innerHTML]="descriptionHTML"></div>
14 </div> 14 </div>
15 15
16 <div class="terms"> 16 <div class="terms">
17 <div class="section-title">Terms</div> 17 <div i18n class="section-title">Terms</div>
18 18
19 <div [innerHTML]="termsHTML"></div> 19 <div [innerHTML]="termsHTML"></div>
20 </div> 20 </div>
21 21
22 <div class="signup"> 22 <div class="signup">
23 <div class="section-title">Signup</div> 23 <div i18n class="section-title">Signup</div>
24 24
25 <div *ngIf="isSignupAllowed"> 25 <div *ngIf="isSignupAllowed">
26 User registration is allowed and 26 <ng-container i18n>User registration is allowed and</ng-container>
27 27
28 <ng-template [ngIf]="userVideoQuota !== -1"> 28 <ng-container i18n *ngIf="userVideoQuota !== -1">
29 this instance provides a baseline quota of {{ userVideoQuota | bytes: 0 }} space for the videos of its users. 29 this instance provides a baseline quota of {{ userVideoQuota | bytes: 0 }} space for the videos of its users.
30 </ng-template> 30 </ng-container>
31 31
32 <ng-template [ngIf]="userVideoQuota === -1"> 32 <ng-container i18n *ngIf="userVideoQuota === -1">
33 this instance provides unlimited space for the videos of its users. 33 this instance provides unlimited space for the videos of its users.
34 </ng-template> 34 </ng-container>
35 </div> 35 </div>
36 36
37 <div *ngIf="isSignupAllowed === false"> 37 <div i18n *ngIf="isSignupAllowed === false">
38 User registration is currently not allowed. 38 User registration is currently not allowed.
39 </div> 39 </div>
40 </div> 40 </div>
41 41
42 <div id="p2p-privacy"> 42 <div id="p2p-privacy">
43 <div class="section-title">P2P & Privacy</div> 43 <div i18n class="section-title">P2P & Privacy</div>
44 44
45 <p> 45 <p i18n>
46 PeerTube uses the BitTorrent protocol to share bandwidth between users. It implies that your public IP address is stored in the public BitTorrent tracker of the video PeerTube instance as long as you're watching the video. 46 PeerTube uses the BitTorrent protocol to share bandwidth between users. It implies that your public IP address is stored in the public BitTorrent tracker of the video PeerTube instance as long as you're watching the video.
47 If you want to keep your public IP address private, please use a VPN or Tor. 47 If you want to keep your public IP address private, please use a VPN or Tor.
48 </p> 48 </p>
diff --git a/client/src/app/about/about.component.ts b/client/src/app/about/about.component.ts
index 3948d5c46..c37b9318b 100644
--- a/client/src/app/about/about.component.ts
+++ b/client/src/app/about/about.component.ts
@@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'
2import { ServerService } from '@app/core' 2import { ServerService } from '@app/core'
3import { MarkdownService } from '@app/videos/shared' 3import { MarkdownService } from '@app/videos/shared'
4import { NotificationsService } from 'angular2-notifications' 4import { NotificationsService } from 'angular2-notifications'
5import { I18n } from '@ngx-translate/i18n-polyfill'
5 6
6@Component({ 7@Component({
7 selector: 'my-about', 8 selector: 'my-about',
@@ -17,7 +18,8 @@ export class AboutComponent implements OnInit {
17 constructor ( 18 constructor (
18 private notificationsService: NotificationsService, 19 private notificationsService: NotificationsService,
19 private serverService: ServerService, 20 private serverService: ServerService,
20 private markdownService: MarkdownService 21 private markdownService: MarkdownService,
22 private i18n: I18n
21 ) {} 23 ) {}
22 24
23 get instanceName () { 25 get instanceName () {
@@ -41,7 +43,7 @@ export class AboutComponent implements OnInit {
41 this.termsHTML = this.markdownService.textMarkdownToHTML(res.instance.terms) 43 this.termsHTML = this.markdownService.textMarkdownToHTML(res.instance.terms)
42 }, 44 },
43 45
44 err => this.notificationsService.error('Error getting about from server', err) 46 err => this.notificationsService.error(this.i18n('Error getting about from server'), err)
45 ) 47 )
46 } 48 }
47 49
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts
index 614d38d08..363f58155 100644
--- a/client/src/app/core/auth/auth.service.ts
+++ b/client/src/app/core/auth/auth.service.ts
@@ -1,5 +1,5 @@
1import { Observable, ReplaySubject, Subject, throwError as observableThrowError } from 'rxjs' 1import { Observable, ReplaySubject, Subject, throwError as observableThrowError } from 'rxjs'
2import { catchError, map, mergeMap, tap, share } from 'rxjs/operators' 2import { catchError, map, mergeMap, share, tap } from 'rxjs/operators'
3import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http' 3import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'
4import { Injectable } from '@angular/core' 4import { Injectable } from '@angular/core'
5import { Router } from '@angular/router' 5import { Router } from '@angular/router'
@@ -13,6 +13,7 @@ import { AuthStatus } from './auth-status.model'
13import { AuthUser } from './auth-user.model' 13import { AuthUser } from './auth-user.model'
14import { objectToUrlEncoded } from '@app/shared/misc/utils' 14import { objectToUrlEncoded } from '@app/shared/misc/utils'
15import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage' 15import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage'
16import { I18n } from '@ngx-translate/i18n-polyfill'
16 17
17interface UserLoginWithUsername extends UserLogin { 18interface UserLoginWithUsername extends UserLogin {
18 access_token: string 19 access_token: string
@@ -46,7 +47,8 @@ export class AuthService {
46 private http: HttpClient, 47 private http: HttpClient,
47 private notificationsService: NotificationsService, 48 private notificationsService: NotificationsService,
48 private restExtractor: RestExtractor, 49 private restExtractor: RestExtractor,
49 private router: Router 50 private router: Router,
51 private i18n: I18n
50 ) { 52 ) {
51 this.loginChanged = new Subject<AuthStatus>() 53 this.loginChanged = new Subject<AuthStatus>()
52 this.loginChangedSource = this.loginChanged.asObservable() 54 this.loginChangedSource = this.loginChanged.asObservable()
@@ -74,14 +76,15 @@ export class AuthService {
74 let errorMessage = error.message 76 let errorMessage = error.message
75 77
76 if (error.status === 403) { 78 if (error.status === 403) {
77 errorMessage = `Cannot retrieve OAuth Client credentials: ${error.text}. \n` 79 errorMessage = this.i18n('Cannot retrieve OAuth Client credentials: {{ errorText }}.\n', { errorText: error.text })
78 errorMessage += 'Ensure you have correctly configured PeerTube (config/ directory), ' + 80 errorMessage += this.i18n(
79 'in particular the "webserver" section.' 81 'Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.'
82 )
80 } 83 }
81 84
82 // We put a bigger timeout 85 // We put a bigger timeout
83 // This is an important message 86 // This is an important message
84 this.notificationsService.error('Error', errorMessage, { timeOut: 7000 }) 87 this.notificationsService.error(this.i18n('Error'), errorMessage, { timeOut: 7000 })
85 } 88 }
86 ) 89 )
87 } 90 }
@@ -180,7 +183,7 @@ export class AuthService {
180 this.router.navigate([ '/login' ]) 183 this.router.navigate([ '/login' ])
181 184
182 return observableThrowError({ 185 return observableThrowError({
183 error: 'You need to reconnect.' 186 error: this.i18n('You need to reconnect.')
184 }) 187 })
185 }), 188 }),
186 share() 189 share()
diff --git a/client/src/app/core/confirm/confirm.component.html b/client/src/app/core/confirm/confirm.component.html
index 01a4e0ac4..3253de4a0 100644
--- a/client/src/app/core/confirm/confirm.component.html
+++ b/client/src/app/core/confirm/confirm.component.html
@@ -16,7 +16,7 @@
16 </div> 16 </div>
17 17
18 <div class="form-group inputs"> 18 <div class="form-group inputs">
19 <span class="action-button action-button-cancel" (click)="cancel()"> 19 <span i18n class="action-button action-button-cancel" (click)="cancel()">
20 Cancel 20 Cancel
21 </span> 21 </span>
22 22
diff --git a/client/src/app/core/confirm/confirm.component.ts b/client/src/app/core/confirm/confirm.component.ts
index 147bc9d65..02b38489a 100644
--- a/client/src/app/core/confirm/confirm.component.ts
+++ b/client/src/app/core/confirm/confirm.component.ts
@@ -3,6 +3,7 @@ import { Component, HostListener, OnInit, ViewChild } from '@angular/core'
3import { ModalDirective } from 'ngx-bootstrap/modal' 3import { ModalDirective } from 'ngx-bootstrap/modal'
4 4
5import { ConfirmService } from './confirm.service' 5import { ConfirmService } from './confirm.service'
6import { I18n } from '@ngx-translate/i18n-polyfill'
6 7
7@Component({ 8@Component({
8 selector: 'my-confirm', 9 selector: 'my-confirm',
@@ -20,7 +21,10 @@ export class ConfirmComponent implements OnInit {
20 inputValue = '' 21 inputValue = ''
21 confirmButtonText = '' 22 confirmButtonText = ''
22 23
23 constructor (private confirmService: ConfirmService) { 24 constructor (
25 private confirmService: ConfirmService,
26 private i18n: I18n
27 ) {
24 // Empty 28 // Empty
25 } 29 }
26 30
@@ -38,7 +42,7 @@ export class ConfirmComponent implements OnInit {
38 this.inputLabel = inputLabel 42 this.inputLabel = inputLabel
39 this.expectedInputValue = expectedInputValue 43 this.expectedInputValue = expectedInputValue
40 44
41 this.confirmButtonText = confirmButtonText || 'Confirm' 45 this.confirmButtonText = confirmButtonText || this.i18n('Confirm')
42 46
43 this.showModal() 47 this.showModal()
44 } 48 }
diff --git a/client/src/app/header/header.component.html b/client/src/app/header/header.component.html
index c853d2b1b..a04354db5 100644
--- a/client/src/app/header/header.component.html
+++ b/client/src/app/header/header.component.html
@@ -1,10 +1,10 @@
1<input 1<input
2 type="text" id="search-video" name="search-video" placeholder="Search..." 2 type="text" id="search-video" name="search-video" i18n-placeholder placeholder="Search..."
3 [(ngModel)]="searchValue" (keyup.enter)="doSearch()" 3 [(ngModel)]="searchValue" (keyup.enter)="doSearch()"
4> 4>
5<span (click)="doSearch()" class="icon icon-search"></span> 5<span (click)="doSearch()" class="icon icon-search"></span>
6 6
7<a class="upload-button" routerLink="/videos/upload"> 7<a class="upload-button" routerLink="/videos/upload">
8 <span class="icon icon-upload"></span> 8 <span class="icon icon-upload"></span>
9 <span class="upload-button-label">Upload</span> 9 <span i18n class="upload-button-label">Upload</span>
10</a> 10</a>
diff --git a/client/src/app/login/login.component.html b/client/src/app/login/login.component.html
index b7fd25d27..1f6cfdb90 100644
--- a/client/src/app/login/login.component.html
+++ b/client/src/app/login/login.component.html
@@ -1,5 +1,5 @@
1<div class="margin-content"> 1<div class="margin-content">
2 <div class="title-page title-page-single"> 2 <div i18n class="title-page title-page-single">
3 Login 3 Login
4 </div> 4 </div>
5 5
@@ -8,21 +8,21 @@
8 <form role="form" (ngSubmit)="login()" [formGroup]="form"> 8 <form role="form" (ngSubmit)="login()" [formGroup]="form">
9 <div class="form-group"> 9 <div class="form-group">
10 <div> 10 <div>
11 <label for="username">User</label> 11 <label i18n for="username">User</label>
12 <input 12 <input
13 type="text" id="username" placeholder="Username or email address" required tabindex="1" 13 type="text" id="username" i18n-placeholder placeholder="Username or email address" required tabindex="1"
14 formControlName="username" [ngClass]="{ 'input-error': formErrors['username'] }" 14 formControlName="username" [ngClass]="{ 'input-error': formErrors['username'] }"
15 > 15 >
16 <a *ngIf="signupAllowed === true" routerLink="/signup" class="create-an-account"> 16 <a i18n *ngIf="signupAllowed === true" routerLink="/signup" class="create-an-account">
17 or create an account 17 or create an account
18 </a> 18 </a>
19 19
20 <a *ngIf="signupAllowed === false" href="https://joinpeertube.org/en/#getting-started" target="_blank" class="create-an-account"> 20 <a i18n *ngIf="signupAllowed === false" href="https://joinpeertube.org/en/#getting-started" target="_blank" class="create-an-account">
21 or create an account on another instance 21 or create an account on another instance
22 </a> 22 </a>
23 23
24 <my-help 24 <my-help
25 *ngIf="signupAllowed === false" helpType="custom" 25 *ngIf="signupAllowed === false" helpType="custom" i18n-customHtml
26 customHtml="User registration is not allowed on this instance, but you can register on many others!" 26 customHtml="User registration is not allowed on this instance, but you can register on many others!"
27 ></my-help> 27 ></my-help>
28 </div> 28 </div>
@@ -33,13 +33,13 @@
33 </div> 33 </div>
34 34
35 <div class="form-group"> 35 <div class="form-group">
36 <label for="password">Password</label> 36 <label i18n for="password">Password</label>
37 <div> 37 <div>
38 <input 38 <input
39 type="password" name="password" id="password" placeholder="Password" required tabindex="2" 39 type="password" name="password" id="password" i18n-placeholder placeholder="Password" required tabindex="2"
40 formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }" 40 formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }"
41 > 41 >
42 <div class="forgot-password-button" (click)="openForgotPasswordModal()">I forgot my password</div> 42 <div i18n class="forgot-password-button" (click)="openForgotPasswordModal()">I forgot my password</div>
43 </div> 43 </div>
44 <div *ngIf="formErrors.password" class="form-error"> 44 <div *ngIf="formErrors.password" class="form-error">
45 {{ formErrors.password }} 45 {{ formErrors.password }}
@@ -56,25 +56,25 @@
56 56
57 <div class="modal-header"> 57 <div class="modal-header">
58 <span class="close" aria-hidden="true" (click)="hideForgotPasswordModal()"></span> 58 <span class="close" aria-hidden="true" (click)="hideForgotPasswordModal()"></span>
59 <h4 class="modal-title">Forgot your password</h4> 59 <h4 i18n class="modal-title">Forgot your password</h4>
60 </div> 60 </div>
61 61
62 <div class="modal-body"> 62 <div class="modal-body">
63 <div class="form-group"> 63 <div class="form-group">
64 <label for="forgot-password-email">Email</label> 64 <label i18n for="forgot-password-email">Email</label>
65 <input 65 <input
66 type="email" id="forgot-password-email" placeholder="Email address" required 66 type="email" id="forgot-password-email" i18n-placeholder placeholder="Email address" required
67 [(ngModel)]="forgotPasswordEmail" #forgotPasswordEmailInput 67 [(ngModel)]="forgotPasswordEmail" #forgotPasswordEmailInput
68 > 68 >
69 </div> 69 </div>
70 70
71 <div class="form-group inputs"> 71 <div class="form-group inputs">
72 <span class="action-button action-button-cancel" (click)="hideForgotPasswordModal()"> 72 <span i18n class="action-button action-button-cancel" (click)="hideForgotPasswordModal()">
73 Cancel 73 Cancel
74 </span> 74 </span>
75 75
76 <input 76 <input
77 type="submit" value="Send me an email to reset my password" class="action-button-submit" 77 type="submit" i18n-value value="Send me an email to reset my password" class="action-button-submit"
78 (click)="askResetPassword()" [disabled]="!forgotPasswordEmailInput.validity.valid" 78 (click)="askResetPassword()" [disabled]="!forgotPasswordEmailInput.validity.valid"
79 > 79 >
80 </div> 80 </div>
diff --git a/client/src/app/login/login.component.ts b/client/src/app/login/login.component.ts
index 2514faf94..a14cb2eb7 100644
--- a/client/src/app/login/login.component.ts
+++ b/client/src/app/login/login.component.ts
@@ -7,6 +7,7 @@ import { NotificationsService } from 'angular2-notifications'
7import { ModalDirective } from 'ngx-bootstrap/modal' 7import { ModalDirective } from 'ngx-bootstrap/modal'
8import { AuthService } from '../core' 8import { AuthService } from '../core'
9import { FormReactive } from '../shared' 9import { FormReactive } from '../shared'
10import { I18n } from '@ngx-translate/i18n-polyfill'
10 11
11@Component({ 12@Component({
12 selector: 'my-login', 13 selector: 'my-login',
@@ -35,12 +36,15 @@ export class LoginComponent extends FormReactive implements OnInit {
35 } 36 }
36 forgotPasswordEmail = '' 37 forgotPasswordEmail = ''
37 38
38 constructor (private authService: AuthService, 39 constructor (
39 private userService: UserService, 40 private authService: AuthService,
40 private serverService: ServerService, 41 private userService: UserService,
41 private redirectService: RedirectService, 42 private serverService: ServerService,
42 private notificationsService: NotificationsService, 43 private redirectService: RedirectService,
43 private formBuilder: FormBuilder) { 44 private notificationsService: NotificationsService,
45 private formBuilder: FormBuilder,
46 private i18n: I18n
47 ) {
44 super() 48 super()
45 } 49 }
46 50
@@ -78,12 +82,15 @@ export class LoginComponent extends FormReactive implements OnInit {
78 this.userService.askResetPassword(this.forgotPasswordEmail) 82 this.userService.askResetPassword(this.forgotPasswordEmail)
79 .subscribe( 83 .subscribe(
80 res => { 84 res => {
81 const message = `An email with the reset password instructions will be sent to ${this.forgotPasswordEmail}.` 85 const message = this.i18n(
82 this.notificationsService.success('Success', message) 86 'An email with the reset password instructions will be sent to {{ email }}.',
87 { email: this.forgotPasswordEmail }
88 )
89 this.notificationsService.success(this.i18n('Success'), message)
83 this.hideForgotPasswordModal() 90 this.hideForgotPasswordModal()
84 }, 91 },
85 92
86 err => this.notificationsService.error('Error', err.message) 93 err => this.notificationsService.error(this.i18n('Error'), err.message)
87 ) 94 )
88 } 95 }
89 96
diff --git a/client/src/app/menu/menu.component.html b/client/src/app/menu/menu.component.html
index 1a95477b7..167729405 100644
--- a/client/src/app/menu/menu.component.html
+++ b/client/src/app/menu/menu.component.html
@@ -18,11 +18,11 @@
18 My public profile 18 My public profile
19 </a> 19 </a>
20 20
21 <a routerLink="/my-account/settings" class="dropdown-item" title="My settings"> 21 <a i18n routerLink="/my-account/settings" class="dropdown-item" title="My settings">
22 My settings 22 My settings
23 </a> 23 </a>
24 24
25 <a (click)="logout($event)" class="dropdown-item" title="Log out" href="#"> 25 <a i18n (click)="logout($event)" class="dropdown-item" title="Log out" href="#">
26 Log out 26 Log out
27 </a> 27 </a>
28 </li> 28 </li>
@@ -31,26 +31,26 @@
31 </div> 31 </div>
32 32
33 <div *ngIf="!isLoggedIn" class="button-block"> 33 <div *ngIf="!isLoggedIn" class="button-block">
34 <a routerLink="/login" class="login-button">Login</a> 34 <a i18n routerLink="/login" class="login-button">Login</a>
35 <a *ngIf="isRegistrationAllowed()" routerLink="/signup" class="create-account-button">Create an account</a> 35 <a i18n *ngIf="isRegistrationAllowed()" routerLink="/signup" class="create-account-button">Create an account</a>
36 </div> 36 </div>
37 37
38 <div class="panel-block"> 38 <div class="panel-block">
39 <div class="block-title">Videos</div> 39 <div i18n class="block-title">Videos</div>
40 40
41 <a routerLink="/videos/trending" routerLinkActive="active"> 41 <a routerLink="/videos/trending" routerLinkActive="active">
42 <span class="icon icon-videos-trending"></span> 42 <span class="icon icon-videos-trending"></span>
43 Trending 43 <ng-container i18n>Trending</ng-container>
44 </a> 44 </a>
45 45
46 <a routerLink="/videos/recently-added" routerLinkActive="active"> 46 <a routerLink="/videos/recently-added" routerLinkActive="active">
47 <span class="icon icon-videos-recently-added"></span> 47 <span class="icon icon-videos-recently-added"></span>
48 Recently added 48 <ng-container i18n>Recently added</ng-container>
49 </a> 49 </a>
50 50
51 <a routerLink="/videos/local" routerLinkActive="active"> 51 <a routerLink="/videos/local" routerLinkActive="active">
52 <span class="icon icon-videos-local"></span> 52 <span class="icon icon-videos-local"></span>
53 Local 53 <ng-container i18n>Local</ng-container>
54 </a> 54 </a>
55 </div> 55 </div>
56 56
@@ -59,12 +59,12 @@
59 59
60 <a *ngIf="userHasAdminAccess" [routerLink]="getFirstAdminRouteAvailable()" routerLinkActive="active"> 60 <a *ngIf="userHasAdminAccess" [routerLink]="getFirstAdminRouteAvailable()" routerLinkActive="active">
61 <span class="icon icon-administration"></span> 61 <span class="icon icon-administration"></span>
62 Administration 62 <ng-container i18n>Administration</ng-container>
63 </a> 63 </a>
64 64
65 <a routerLink="/about" routerLinkActive="active"> 65 <a routerLink="/about" routerLinkActive="active">
66 <span class="icon icon-about"></span> 66 <span class="icon icon-about"></span>
67 About 67 <ng-container i18n>About</ng-container>
68 </a> 68 </a>
69 </div> 69 </div>
70</menu> 70</menu>
diff --git a/client/src/app/menu/menu.component.ts b/client/src/app/menu/menu.component.ts
index 69216e215..c0aea89b3 100644
--- a/client/src/app/menu/menu.component.ts
+++ b/client/src/app/menu/menu.component.ts
@@ -1,7 +1,7 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { Router } from '@angular/router' 2import { Router } from '@angular/router'
3import { UserRight } from '../../../../shared/models/users/user-right.enum' 3import { UserRight } from '../../../../shared/models/users/user-right.enum'
4import { AuthService, AuthStatus, ServerService } from '../core' 4import { AuthService, AuthStatus, RedirectService, ServerService } from '../core'
5import { User } from '../shared/users/user.model' 5import { User } from '../shared/users/user.model'
6 6
7@Component({ 7@Component({
@@ -24,7 +24,7 @@ export class MenuComponent implements OnInit {
24 constructor ( 24 constructor (
25 private authService: AuthService, 25 private authService: AuthService,
26 private serverService: ServerService, 26 private serverService: ServerService,
27 private router: Router 27 private redirectService: RedirectService
28 ) {} 28 ) {}
29 29
30 ngOnInit () { 30 ngOnInit () {
@@ -87,7 +87,7 @@ export class MenuComponent implements OnInit {
87 87
88 this.authService.logout() 88 this.authService.logout()
89 // Redirect to home page 89 // Redirect to home page
90 this.router.navigate(['/videos/list']) 90 this.redirectService.redirectToHomepage()
91 } 91 }
92 92
93 private computeIsUserHasAdminAccess () { 93 private computeIsUserHasAdminAccess () {
diff --git a/client/src/app/reset-password/reset-password.component.html b/client/src/app/reset-password/reset-password.component.html
index 9cf623c64..20e62d0b7 100644
--- a/client/src/app/reset-password/reset-password.component.html
+++ b/client/src/app/reset-password/reset-password.component.html
@@ -1,13 +1,13 @@
1<div class="margin-content"> 1<div class="margin-content">
2 <div class="title-page title-page-single"> 2 <div i18n class="title-page title-page-single">
3 Reset my password 3 Reset my password
4 </div> 4 </div>
5 5
6 <form role="form" (ngSubmit)="resetPassword()" [formGroup]="form"> 6 <form role="form" (ngSubmit)="resetPassword()" [formGroup]="form">
7 <div class="form-group"> 7 <div class="form-group">
8 <label for="password">Password</label> 8 <label i18n for="password">Password</label>
9 <input 9 <input
10 type="password" name="password" id="password" placeholder="Password" required 10 type="password" name="password" id="password" i18n-placeholder placeholder="Password" required
11 formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }" 11 formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }"
12 > 12 >
13 <div *ngIf="formErrors.password" class="form-error"> 13 <div *ngIf="formErrors.password" class="form-error">
@@ -16,9 +16,9 @@
16 </div> 16 </div>
17 17
18 <div class="form-group"> 18 <div class="form-group">
19 <label for="password-confirm">Confirm password</label> 19 <label i18n for="password-confirm">Confirm password</label>
20 <input 20 <input
21 type="password" name="password-confirm" id="password-confirm" placeholder="Confirmed password" required 21 type="password" name="password-confirm" id="password-confirm" i18n-placeholder placeholder="Confirmed password" required
22 formControlName="password-confirm" [ngClass]="{ 'input-error': formErrors['password-confirm'] }" 22 formControlName="password-confirm" [ngClass]="{ 'input-error': formErrors['password-confirm'] }"
23 > 23 >
24 <div *ngIf="formErrors['password-confirm']" class="form-error"> 24 <div *ngIf="formErrors['password-confirm']" class="form-error">
@@ -26,6 +26,6 @@
26 </div> 26 </div>
27 </div> 27 </div>
28 28
29 <input type="submit" value="Reset my password" [disabled]="!form.valid && isConfirmedPasswordValid()"> 29 <input type="submit" i18n-value value="Reset my password" [disabled]="!form.valid && isConfirmedPasswordValid()">
30 </form> 30 </form>
31</div> 31</div>
diff --git a/client/src/app/reset-password/reset-password.component.ts b/client/src/app/reset-password/reset-password.component.ts
index 408374779..c8bd368c1 100644
--- a/client/src/app/reset-password/reset-password.component.ts
+++ b/client/src/app/reset-password/reset-password.component.ts
@@ -5,6 +5,7 @@ import { USER_PASSWORD, UserService } from '@app/shared'
5import { NotificationsService } from 'angular2-notifications' 5import { NotificationsService } from 'angular2-notifications'
6import { AuthService } from '../core' 6import { AuthService } from '../core'
7import { FormReactive } from '../shared' 7import { FormReactive } from '../shared'
8import { I18n } from '@ngx-translate/i18n-polyfill'
8 9
9@Component({ 10@Component({
10 selector: 'my-login', 11 selector: 'my-login',
@@ -34,7 +35,8 @@ export class ResetPasswordComponent extends FormReactive implements OnInit {
34 private notificationsService: NotificationsService, 35 private notificationsService: NotificationsService,
35 private formBuilder: FormBuilder, 36 private formBuilder: FormBuilder,
36 private router: Router, 37 private router: Router,
37 private route: ActivatedRoute 38 private route: ActivatedRoute,
39 private i18n: I18n
38 ) { 40 ) {
39 super() 41 super()
40 } 42 }
@@ -55,7 +57,7 @@ export class ResetPasswordComponent extends FormReactive implements OnInit {
55 this.verificationString = this.route.snapshot.queryParams['verificationString'] 57 this.verificationString = this.route.snapshot.queryParams['verificationString']
56 58
57 if (!this.userId || !this.verificationString) { 59 if (!this.userId || !this.verificationString) {
58 this.notificationsService.error('Error', 'Unable to find user id or verification string.') 60 this.notificationsService.error(this.i18n('Error'), this.i18n('Unable to find user id or verification string.'))
59 this.router.navigate([ '/' ]) 61 this.router.navigate([ '/' ])
60 } 62 }
61 } 63 }
@@ -64,7 +66,7 @@ export class ResetPasswordComponent extends FormReactive implements OnInit {
64 this.userService.resetPassword(this.userId, this.verificationString, this.form.value.password) 66 this.userService.resetPassword(this.userId, this.verificationString, this.form.value.password)
65 .subscribe( 67 .subscribe(
66 () => { 68 () => {
67 this.notificationsService.success('Success', 'Your password has been successfully reset!') 69 this.notificationsService.success(this.i18n('Success'), this.i18n('Your password has been successfully reset!'))
68 this.router.navigate([ '/login' ]) 70 this.router.navigate([ '/login' ])
69 }, 71 },
70 72
diff --git a/client/src/app/shared/forms/markdown-textarea.component.html b/client/src/app/shared/forms/markdown-textarea.component.html
index 46a97b163..6effda5b8 100644
--- a/client/src/app/shared/forms/markdown-textarea.component.html
+++ b/client/src/app/shared/forms/markdown-textarea.component.html
@@ -6,7 +6,7 @@
6 </textarea> 6 </textarea>
7 7
8 <tabset *ngIf="arePreviewsDisplayed()" class="previews"> 8 <tabset *ngIf="arePreviewsDisplayed()" class="previews">
9 <tab *ngIf="truncate !== undefined" heading="Truncated preview" [innerHTML]="truncatedPreviewHTML"></tab> 9 <tab *ngIf="truncate !== undefined" i18n-heading heading="Truncated preview" [innerHTML]="truncatedPreviewHTML"></tab>
10 <tab heading="Complete preview" [innerHTML]="previewHTML"></tab> 10 <tab i18n-heading heading="Complete preview" [innerHTML]="previewHTML"></tab>
11 </tabset> 11 </tabset>
12</div> 12</div>
diff --git a/client/src/app/shared/forms/markdown-textarea.component.ts b/client/src/app/shared/forms/markdown-textarea.component.ts
index dcc85f3cd..8b932cd15 100644
--- a/client/src/app/shared/forms/markdown-textarea.component.ts
+++ b/client/src/app/shared/forms/markdown-textarea.component.ts
@@ -3,7 +3,7 @@ import { Component, forwardRef, Input, OnInit } from '@angular/core'
3import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' 3import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
4import { isInSmallView } from '@app/shared/misc/utils' 4import { isInSmallView } from '@app/shared/misc/utils'
5import { MarkdownService } from '@app/videos/shared' 5import { MarkdownService } from '@app/videos/shared'
6import { Subject } from 'rxjs' 6import { Subject } from 'rxjs/Subject'
7import truncate from 'lodash-es/truncate' 7import truncate from 'lodash-es/truncate'
8 8
9@Component({ 9@Component({
diff --git a/client/src/app/shared/guards/can-deactivate-guard.service.ts b/client/src/app/shared/guards/can-deactivate-guard.service.ts
index 550dd656e..c038a5c0e 100644
--- a/client/src/app/shared/guards/can-deactivate-guard.service.ts
+++ b/client/src/app/shared/guards/can-deactivate-guard.service.ts
@@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'
2import { ActivatedRouteSnapshot, CanDeactivate, RouterStateSnapshot } from '@angular/router' 2import { ActivatedRouteSnapshot, CanDeactivate, RouterStateSnapshot } from '@angular/router'
3import { Observable } from 'rxjs' 3import { Observable } from 'rxjs'
4import { ConfirmService } from '../../core/index' 4import { ConfirmService } from '../../core/index'
5import { I18n } from '@ngx-translate/i18n-polyfill'
5 6
6export interface CanComponentDeactivate { 7export interface CanComponentDeactivate {
7 canDeactivate: () => { text?: string, canDeactivate: Observable<boolean> | boolean } 8 canDeactivate: () => { text?: string, canDeactivate: Observable<boolean> | boolean }
@@ -9,7 +10,10 @@ export interface CanComponentDeactivate {
9 10
10@Injectable() 11@Injectable()
11export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> { 12export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
12 constructor (private confirmService: ConfirmService) { } 13 constructor (
14 private confirmService: ConfirmService,
15 private i18n: I18n
16 ) { }
13 17
14 canDeactivate (component: CanComponentDeactivate, 18 canDeactivate (component: CanComponentDeactivate,
15 currentRoute: ActivatedRouteSnapshot, 19 currentRoute: ActivatedRouteSnapshot,
@@ -17,11 +21,11 @@ export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate>
17 nextState: RouterStateSnapshot 21 nextState: RouterStateSnapshot
18 ) { 22 ) {
19 const result = component.canDeactivate() 23 const result = component.canDeactivate()
20 const text = result.text || 'All unsaved data will be lost, are you sure you want to leave this page?' 24 const text = result.text || this.i18n('All unsaved data will be lost, are you sure you want to leave this page?')
21 25
22 return result.canDeactivate || this.confirmService.confirm( 26 return result.canDeactivate || this.confirmService.confirm(
23 text, 27 text,
24 'Warning' 28 this.i18n('Warning')
25 ) 29 )
26 } 30 }
27 31
diff --git a/client/src/app/shared/misc/edit-button.component.html b/client/src/app/shared/misc/edit-button.component.html
index 632d6bba2..78fbc326e 100644
--- a/client/src/app/shared/misc/edit-button.component.html
+++ b/client/src/app/shared/misc/edit-button.component.html
@@ -1,4 +1,4 @@
1<a class="action-button action-button-edit" [routerLink]="routerLink" title="Edit"> 1<a class="action-button action-button-edit" [routerLink]="routerLink" title="Edit">
2 <span class="icon icon-edit"></span> 2 <span class="icon icon-edit"></span>
3 <span class="button-label">Edit</span> 3 <span i18n class="button-label">Edit</span>
4</a> 4</a>
diff --git a/client/src/app/shared/misc/from-now.pipe.ts b/client/src/app/shared/misc/from-now.pipe.ts
index fac02af0b..3a64a4077 100644
--- a/client/src/app/shared/misc/from-now.pipe.ts
+++ b/client/src/app/shared/misc/from-now.pipe.ts
@@ -1,36 +1,39 @@
1import { Pipe, PipeTransform } from '@angular/core' 1import { Pipe, PipeTransform } from '@angular/core'
2import { I18n } from '@ngx-translate/i18n-polyfill'
2 3
3// Thanks: https://stackoverflow.com/questions/3177836/how-to-format-time-since-xxx-e-g-4-minutes-ago-similar-to-stack-exchange-site 4// Thanks: https://stackoverflow.com/questions/3177836/how-to-format-time-since-xxx-e-g-4-minutes-ago-similar-to-stack-exchange-site
4@Pipe({ name: 'myFromNow' }) 5@Pipe({ name: 'myFromNow' })
5export class FromNowPipe implements PipeTransform { 6export class FromNowPipe implements PipeTransform {
6 7
8 constructor (private i18n: I18n) { }
9
7 transform (value: number) { 10 transform (value: number) {
8 const seconds = Math.floor((Date.now() - value) / 1000) 11 const seconds = Math.floor((Date.now() - value) / 1000)
9 12
10 let interval = Math.floor(seconds / 31536000) 13 let interval = Math.floor(seconds / 31536000)
11 if (interval > 1) { 14 if (interval > 1) {
12 return interval + ' years ago' 15 return this.i18n('{{ interval }} years ago', { interval })
13 } 16 }
14 17
15 interval = Math.floor(seconds / 2592000) 18 interval = Math.floor(seconds / 2592000)
16 if (interval > 1) return interval + ' months ago' 19 if (interval > 1) return this.i18n('{{ interval }} months ago', { interval })
17 if (interval === 1) return interval + ' month ago' 20 if (interval === 1) return this.i18n('{{ interval }} month ago', { interval })
18 21
19 interval = Math.floor(seconds / 604800) 22 interval = Math.floor(seconds / 604800)
20 if (interval > 1) return interval + ' weeks ago' 23 if (interval > 1) return this.i18n('{{ interval }} weeks ago', { interval })
21 if (interval === 1) return interval + ' week ago' 24 if (interval === 1) return this.i18n('{{ interval }} week ago', { interval })
22 25
23 interval = Math.floor(seconds / 86400) 26 interval = Math.floor(seconds / 86400)
24 if (interval > 1) return interval + ' days ago' 27 if (interval > 1) return this.i18n('{{ interval }} days ago', { interval })
25 if (interval === 1) return interval + ' day ago' 28 if (interval === 1) return this.i18n('{{ interval }} day ago', { interval })
26 29
27 interval = Math.floor(seconds / 3600) 30 interval = Math.floor(seconds / 3600)
28 if (interval > 1) return interval + ' hours ago' 31 if (interval > 1) return this.i18n('{{ interval }} hours ago', { interval })
29 if (interval === 1) return interval + ' hour ago' 32 if (interval === 1) return this.i18n('{{ interval }} hour ago', { interval })
30 33
31 interval = Math.floor(seconds / 60) 34 interval = Math.floor(seconds / 60)
32 if (interval >= 1) return interval + ' min ago' 35 if (interval >= 1) return this.i18n('{{ interval }} min ago', { interval })
33 36
34 return Math.floor(seconds) + ' sec ago' 37 return this.i18n('{{ interval }} sec ago', { interval: Math.floor(seconds) })
35 } 38 }
36} 39}
diff --git a/client/src/app/shared/misc/help.component.html b/client/src/app/shared/misc/help.component.html
index 3da5701a0..f2b6eca33 100644
--- a/client/src/app/shared/misc/help.component.html
+++ b/client/src/app/shared/misc/help.component.html
@@ -15,6 +15,7 @@
15<span 15<span
16 class="help-tooltip-button" 16 class="help-tooltip-button"
17 title="Get help" 17 title="Get help"
18 i18n-title
18 [popover]="tooltipTemplate" 19 [popover]="tooltipTemplate"
19 [placement]="tooltipPlacement" 20 [placement]="tooltipPlacement"
20 [outsideClick]="true" 21 [outsideClick]="true"
diff --git a/client/src/app/shared/rest/rest-extractor.service.ts b/client/src/app/shared/rest/rest-extractor.service.ts
index 39e601e20..468ac3e32 100644
--- a/client/src/app/shared/rest/rest-extractor.service.ts
+++ b/client/src/app/shared/rest/rest-extractor.service.ts
@@ -3,11 +3,15 @@ import { Injectable } from '@angular/core'
3import { dateToHuman } from '@app/shared/misc/utils' 3import { dateToHuman } from '@app/shared/misc/utils'
4import { ResultList } from '../../../../../shared' 4import { ResultList } from '../../../../../shared'
5import { Router } from '@angular/router' 5import { Router } from '@angular/router'
6import { I18n } from '@ngx-translate/i18n-polyfill'
6 7
7@Injectable() 8@Injectable()
8export class RestExtractor { 9export class RestExtractor {
9 10
10 constructor (private router: Router) { 11 constructor (
12 private router: Router,
13 private i18n: I18n
14 ) {
11 // empty 15 // empty
12 } 16 }
13 17
@@ -60,17 +64,19 @@ export class RestExtractor {
60 } else if (err.error && err.error.error) { 64 } else if (err.error && err.error.error) {
61 errorMessage = err.error.error 65 errorMessage = err.error.error
62 } else if (err.status === 413) { 66 } else if (err.status === 413) {
63 errorMessage = 'Request is too large for the server. Please contact you administrator if you want to increase the limit size.' 67 errorMessage = this.i18n(
68 'Request is too large for the server. Please contact you administrator if you want to increase the limit size.'
69 )
64 } else if (err.status === 429) { 70 } else if (err.status === 429) {
65 const secondsLeft = err.headers.get('retry-after') 71 const secondsLeft = err.headers.get('retry-after')
66 if (secondsLeft) { 72 if (secondsLeft) {
67 const minutesLeft = Math.floor(parseInt(secondsLeft, 10) / 60) 73 const minutesLeft = Math.floor(parseInt(secondsLeft, 10) / 60)
68 errorMessage = 'Too many attempts, please try again after ' + minutesLeft + ' minutes.' 74 errorMessage = this.i18n('Too many attempts, please try again after {{ minutesLeft }} minutes.', { minutesLeft })
69 } else { 75 } else {
70 errorMessage = 'Too many attempts, please try again later.' 76 errorMessage = this.i18n('Too many attempts, please try again later.')
71 } 77 }
72 } else if (err.status === 500) { 78 } else if (err.status === 500) {
73 errorMessage = 'Server error. Please retry later.' 79 errorMessage = this.i18n('Server error. Please retry later.')
74 } 80 }
75 81
76 errorMessage = errorMessage ? errorMessage : 'Unknown error.' 82 errorMessage = errorMessage ? errorMessage : 'Unknown error.'
diff --git a/client/src/app/shared/video/abstract-video-list.html b/client/src/app/shared/video/abstract-video-list.html
index 690529dcf..e8ded6ab8 100644
--- a/client/src/app/shared/video/abstract-video-list.html
+++ b/client/src/app/shared/video/abstract-video-list.html
@@ -4,7 +4,7 @@
4 </div> 4 </div>
5 <my-video-feed [syndicationItems]="syndicationItems"></my-video-feed> 5 <my-video-feed [syndicationItems]="syndicationItems"></my-video-feed>
6 6
7 <div *ngIf="pagination.totalItems === 0">No results.</div> 7 <div i18n *ngIf="pagination.totalItems === 0">No results.</div>
8 <div 8 <div
9 myInfiniteScroller 9 myInfiniteScroller
10 [pageHeight]="pageHeight" 10 [pageHeight]="pageHeight"
diff --git a/client/src/app/shared/video/abstract-video-list.ts b/client/src/app/shared/video/abstract-video-list.ts
index 100cbff8d..1c84573da 100644
--- a/client/src/app/shared/video/abstract-video-list.ts
+++ b/client/src/app/shared/video/abstract-video-list.ts
@@ -10,6 +10,7 @@ import { AuthService } from '../../core/auth'
10import { ComponentPagination } from '../rest/component-pagination.model' 10import { ComponentPagination } from '../rest/component-pagination.model'
11import { VideoSortField } from './sort-field.type' 11import { VideoSortField } from './sort-field.type'
12import { Video } from './video.model' 12import { Video } from './video.model'
13import { I18n } from '@ngx-translate/i18n-polyfill'
13 14
14export abstract class AbstractVideoList implements OnInit, OnDestroy { 15export abstract class AbstractVideoList implements OnInit, OnDestroy {
15 private static LINES_PER_PAGE = 4 16 private static LINES_PER_PAGE = 4
@@ -40,6 +41,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
40 protected abstract authService: AuthService 41 protected abstract authService: AuthService
41 protected abstract router: Router 42 protected abstract router: Router
42 protected abstract route: ActivatedRoute 43 protected abstract route: ActivatedRoute
44 protected abstract i18n: I18n
43 protected abstract location: Location 45 protected abstract location: Location
44 protected abstract currentRoute: string 46 protected abstract currentRoute: string
45 abstract titlePage: string 47 abstract titlePage: string
@@ -124,7 +126,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
124 }, 126 },
125 error => { 127 error => {
126 this.loadingPage[page] = false 128 this.loadingPage[page] = false
127 this.notificationsService.error('Error', error.message) 129 this.notificationsService.error(this.i18n('Error'), error.message)
128 } 130 }
129 ) 131 )
130 } 132 }
diff --git a/client/src/app/shared/video/video-miniature.component.html b/client/src/app/shared/video/video-miniature.component.html
index 09ce0ef7f..3010e5ccc 100644
--- a/client/src/app/shared/video/video-miniature.component.html
+++ b/client/src/app/shared/video/video-miniature.component.html
@@ -9,7 +9,7 @@
9 {{ video.name }} 9 {{ video.name }}
10 </a> 10 </a>
11 11
12 <span class="video-miniature-created-at-views">{{ video.publishedAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span> 12 <span i18n class="video-miniature-created-at-views">{{ video.publishedAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span>
13 <a class="video-miniature-account" [routerLink]="[ '/accounts', video.by ]">{{ video.by }}</a> 13 <a class="video-miniature-account" [routerLink]="[ '/accounts', video.by ]">{{ video.by }}</a>
14 </div> 14 </div>
15</div> 15</div>
diff --git a/client/src/app/shared/video/video-thumbnail.component.html b/client/src/app/shared/video/video-thumbnail.component.html
index 4604d10e2..971f352ba 100644
--- a/client/src/app/shared/video/video-thumbnail.component.html
+++ b/client/src/app/shared/video/video-thumbnail.component.html
@@ -1,6 +1,6 @@
1<a 1<a
2 [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name" 2 [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name"
3class="video-thumbnail" 3 class="video-thumbnail"
4> 4>
5<img [attr.src]="getImageUrl()" [ngClass]="{ 'blur-filter': nsfw }" /> 5<img [attr.src]="getImageUrl()" [ngClass]="{ 'blur-filter': nsfw }" />
6 6
diff --git a/client/src/app/signup/signup.component.html b/client/src/app/signup/signup.component.html
index 5f48786e5..2fe9a4281 100644
--- a/client/src/app/signup/signup.component.html
+++ b/client/src/app/signup/signup.component.html
@@ -1,11 +1,11 @@
1<div class="margin-content"> 1<div class="margin-content">
2 2
3 <div class="title-page title-page-single"> 3 <div i18n class="title-page title-page-single">
4 Create an account 4 Create an account
5 </div> 5 </div>
6 6
7 <div class="initial-user-quota"> 7 <div class="initial-user-quota">
8 <span class="initial-user-quota-label">Initial video quota:</span> 8 <span i18n class="initial-user-quota-label">Initial video quota:</span>
9 9
10 <span *ngIf="initialUserVideoQuota !== -1"> 10 <span *ngIf="initialUserVideoQuota !== -1">
11 {{ initialUserVideoQuota | bytes: 0 }} 11 {{ initialUserVideoQuota | bytes: 0 }}
@@ -13,18 +13,18 @@
13 <my-help helpType="custom" [customHtml]="quotaHelpIndication"></my-help> 13 <my-help helpType="custom" [customHtml]="quotaHelpIndication"></my-help>
14 </span> 14 </span>
15 15
16 <ng-template [ngIf]="initialUserVideoQuota === -1"> 16 <ng-container i18n *ngIf="initialUserVideoQuota === -1">
17 Unlimited 17 Unlimited
18 </ng-template> 18 </ng-container>
19 </div> 19 </div>
20 20
21 <div *ngIf="error" class="alert alert-danger">{{ error }}</div> 21 <div *ngIf="error" class="alert alert-danger">{{ error }}</div>
22 22
23 <form role="form" (ngSubmit)="signup()" [formGroup]="form"> 23 <form role="form" (ngSubmit)="signup()" [formGroup]="form">
24 <div class="form-group"> 24 <div class="form-group">
25 <label for="username">Username</label> 25 <label for="username" i18n>Username</label>
26 <input 26 <input
27 type="text" id="username" placeholder="Username" 27 type="text" id="username" i18n-placeholder placeholder="Username"
28 formControlName="username" [ngClass]="{ 'input-error': formErrors['username'] }" 28 formControlName="username" [ngClass]="{ 'input-error': formErrors['username'] }"
29 > 29 >
30 <div *ngIf="formErrors.username" class="form-error"> 30 <div *ngIf="formErrors.username" class="form-error">
@@ -33,9 +33,9 @@
33 </div> 33 </div>
34 34
35 <div class="form-group"> 35 <div class="form-group">
36 <label for="email">Email</label> 36 <label for="email" i18n>Email</label>
37 <input 37 <input
38 type="text" id="email" placeholder="Email" 38 type="text" id="email" i18n-placeholder placeholder="Email"
39 formControlName="email" [ngClass]="{ 'input-error': formErrors['email'] }" 39 formControlName="email" [ngClass]="{ 'input-error': formErrors['email'] }"
40 > 40 >
41 <div *ngIf="formErrors.email" class="form-error"> 41 <div *ngIf="formErrors.email" class="form-error">
@@ -44,9 +44,9 @@
44 </div> 44 </div>
45 45
46 <div class="form-group"> 46 <div class="form-group">
47 <label for="password">Password</label> 47 <label for="password" i18n>Password</label>
48 <input 48 <input
49 type="password" id="password" placeholder="Password" 49 type="password" id="password" i18n-placeholder placeholder="Password"
50 formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }" 50 formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }"
51 > 51 >
52 <div *ngIf="formErrors.password" class="form-error"> 52 <div *ngIf="formErrors.password" class="form-error">
@@ -54,7 +54,7 @@
54 </div> 54 </div>
55 </div> 55 </div>
56 56
57 <input type="submit" value="Signup" [disabled]="!form.valid"> 57 <input type="submit" i18n-value value="Signup" [disabled]="!form.valid">
58 </form> 58 </form>
59 59
60</div> 60</div>
diff --git a/client/src/app/signup/signup.component.ts b/client/src/app/signup/signup.component.ts
index 1f3e2e146..4a49ead50 100644
--- a/client/src/app/signup/signup.component.ts
+++ b/client/src/app/signup/signup.component.ts
@@ -6,6 +6,8 @@ import { ServerService } from '@app/core/server'
6import { NotificationsService } from 'angular2-notifications' 6import { NotificationsService } from 'angular2-notifications'
7import { UserCreate } from '../../../../shared' 7import { UserCreate } from '../../../../shared'
8import { FormReactive, USER_EMAIL, USER_PASSWORD, USER_USERNAME, UserService } from '../shared' 8import { FormReactive, USER_EMAIL, USER_PASSWORD, USER_USERNAME, UserService } from '../shared'
9import { RedirectService } from '@app/core'
10import { I18n } from '@ngx-translate/i18n-polyfill'
9 11
10@Component({ 12@Component({
11 selector: 'my-signup', 13 selector: 'my-signup',
@@ -45,7 +47,9 @@ export class SignupComponent extends FormReactive implements OnInit {
45 private router: Router, 47 private router: Router,
46 private notificationsService: NotificationsService, 48 private notificationsService: NotificationsService,
47 private userService: UserService, 49 private userService: UserService,
48 private serverService: ServerService 50 private redirectService: RedirectService,
51 private serverService: ServerService,
52 private i18n: I18n
49 ) { 53 ) {
50 super() 54 super()
51 } 55 }
@@ -78,8 +82,11 @@ export class SignupComponent extends FormReactive implements OnInit {
78 82
79 this.userService.signup(userCreate).subscribe( 83 this.userService.signup(userCreate).subscribe(
80 () => { 84 () => {
81 this.notificationsService.success('Success', `Registration for ${userCreate.username} complete.`) 85 this.notificationsService.success(
82 this.router.navigate([ '/videos/list' ]) 86 this.i18n('Success'),
87 this.i18n('Registration for {{ username }} complete.', { username: userCreate.username})
88 )
89 this.redirectService.redirectToHomepage()
83 }, 90 },
84 91
85 err => this.error = err.message 92 err => this.error = err.message
@@ -99,9 +106,9 @@ export class SignupComponent extends FormReactive implements OnInit {
99 const normalSeconds = initialUserVideoQuotaBit / (1.5 * 1000 * 1000) 106 const normalSeconds = initialUserVideoQuotaBit / (1.5 * 1000 * 1000)
100 107
101 const lines = [ 108 const lines = [
102 SignupComponent.getApproximateTime(fullHdSeconds) + ' of full HD videos', 109 this.i18n('{{ seconds }} of full HD videos', { seconds: SignupComponent.getApproximateTime(fullHdSeconds) }),
103 SignupComponent.getApproximateTime(hdSeconds) + ' of HD videos', 110 this.i18n('{{ seconds }} of HD videos', { seconds: SignupComponent.getApproximateTime(hdSeconds) }),
104 SignupComponent.getApproximateTime(normalSeconds) + ' of average quality videos' 111 this.i18n('{{ seconds }} of average quality videos', { seconds: SignupComponent.getApproximateTime(normalSeconds) })
105 ] 112 ]
106 113
107 this.quotaHelpIndication = lines.join('<br />') 114 this.quotaHelpIndication = lines.join('<br />')
diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.html b/client/src/app/videos/+video-edit/shared/video-edit.component.html
index 7e1be4467..bd9f69362 100644
--- a/client/src/app/videos/+video-edit/shared/video-edit.component.html
+++ b/client/src/app/videos/+video-edit/shared/video-edit.component.html
@@ -4,7 +4,7 @@
4 <tab heading="Basic info"> 4 <tab heading="Basic info">
5 <div class="col-md-8"> 5 <div class="col-md-8">
6 <div class="form-group"> 6 <div class="form-group">
7 <label for="name">Title</label> 7 <label i18n for="name">Title</label>
8 <input type="text" id="name" formControlName="name" /> 8 <input type="text" id="name" formControlName="name" />
9 <div *ngIf="formErrors.name" class="form-error"> 9 <div *ngIf="formErrors.name" class="form-error">
10 {{ formErrors.name }} 10 {{ formErrors.name }}
@@ -12,7 +12,7 @@
12 </div> 12 </div>
13 13
14 <div class="form-group"> 14 <div class="form-group">
15 <label class="label-tags">Tags</label> <span>(press Enter to add)</span> 15 <label i18n class="label-tags">Tags</label> <span i18n>(press Enter to add)</span>
16 <tag-input 16 <tag-input
17 [validators]="tagValidators" [errorMessages]="tagValidatorsMessages" 17 [validators]="tagValidators" [errorMessages]="tagValidatorsMessages"
18 formControlName="tags" maxItems="5" modelAsStrings="true" 18 formControlName="tags" maxItems="5" modelAsStrings="true"
@@ -20,8 +20,8 @@
20 </div> 20 </div>
21 21
22 <div class="form-group"> 22 <div class="form-group">
23 <label for="description">Description</label> 23 <label i18n for="description">Description</label>
24 <my-help helpType="markdownText" preHtml="Video descriptions are truncated by default and require manual action to expand them."></my-help> 24 <my-help helpType="markdownText" i18n-preHtml preHtml="Video descriptions are truncated by default and require manual action to expand them."></my-help>
25 <my-markdown-textarea truncate="250" formControlName="description"></my-markdown-textarea> 25 <my-markdown-textarea truncate="250" formControlName="description"></my-markdown-textarea>
26 26
27 <div *ngIf="formErrors.description" class="form-error"> 27 <div *ngIf="formErrors.description" class="form-error">
@@ -32,7 +32,7 @@
32 32
33 <div class="col-md-4"> 33 <div class="col-md-4">
34 <div class="form-group"> 34 <div class="form-group">
35 <label>Channel</label> 35 <label i18n>Channel</label>
36 <div class="peertube-select-container"> 36 <div class="peertube-select-container">
37 <select formControlName="channelId"> 37 <select formControlName="channelId">
38 <option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option> 38 <option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option>
@@ -41,7 +41,7 @@
41 </div> 41 </div>
42 42
43 <div class="form-group"> 43 <div class="form-group">
44 <label for="category">Category</label> 44 <label i18n for="category">Category</label>
45 <div class="peertube-select-container"> 45 <div class="peertube-select-container">
46 <select id="category" formControlName="category"> 46 <select id="category" formControlName="category">
47 <option></option> 47 <option></option>
@@ -55,7 +55,7 @@
55 </div> 55 </div>
56 56
57 <div class="form-group"> 57 <div class="form-group">
58 <label for="licence">Licence</label> 58 <label i18n for="licence">Licence</label>
59 <div class="peertube-select-container"> 59 <div class="peertube-select-container">
60 <select id="licence" formControlName="licence"> 60 <select id="licence" formControlName="licence">
61 <option></option> 61 <option></option>
@@ -69,7 +69,7 @@
69 </div> 69 </div>
70 70
71 <div class="form-group"> 71 <div class="form-group">
72 <label for="language">Language</label> 72 <label i18n for="language">Language</label>
73 <div class="peertube-select-container"> 73 <div class="peertube-select-container">
74 <select id="language" formControlName="language"> 74 <select id="language" formControlName="language">
75 <option></option> 75 <option></option>
@@ -83,7 +83,7 @@
83 </div> 83 </div>
84 84
85 <div class="form-group"> 85 <div class="form-group">
86 <label for="privacy">Privacy</label> 86 <label i18n for="privacy">Privacy</label>
87 <div class="peertube-select-container"> 87 <div class="peertube-select-container">
88 <select id="privacy" formControlName="privacy"> 88 <select id="privacy" formControlName="privacy">
89 <option></option> 89 <option></option>
@@ -99,14 +99,14 @@
99 <div class="form-group form-group-checkbox"> 99 <div class="form-group form-group-checkbox">
100 <input type="checkbox" id="nsfw" formControlName="nsfw" /> 100 <input type="checkbox" id="nsfw" formControlName="nsfw" />
101 <label for="nsfw"></label> 101 <label for="nsfw"></label>
102 <label for="nsfw">This video contains mature or explicit content</label> 102 <label i18n for="nsfw">This video contains mature or explicit content</label>
103 <my-help tooltipPlacement="top" helpType="custom" customHtml="Some instances do not list NSFW videos by default."></my-help> 103 <my-help tooltipPlacement="top" helpType="custom" i18n-customHtml customHtml="Some instances do not list NSFW videos by default."></my-help>
104 </div> 104 </div>
105 105
106 <div class="form-group form-group-checkbox"> 106 <div class="form-group form-group-checkbox">
107 <input type="checkbox" id="commentsEnabled" formControlName="commentsEnabled" /> 107 <input type="checkbox" id="commentsEnabled" formControlName="commentsEnabled" />
108 <label for="commentsEnabled"></label> 108 <label for="commentsEnabled"></label>
109 <label for="commentsEnabled">Enable video comments</label> 109 <label i18n for="commentsEnabled">Enable video comments</label>
110 </div> 110 </div>
111 111
112 </div> 112 </div>
@@ -116,24 +116,24 @@
116 <div class="col-md-12 advanced-settings"> 116 <div class="col-md-12 advanced-settings">
117 <div class="form-group"> 117 <div class="form-group">
118 <my-video-image 118 <my-video-image
119 inputLabel="Upload thumbnail" inputName="thumbnailfile" formControlName="thumbnailfile" 119 i18n-inputLabel inputLabel="Upload thumbnail" inputName="thumbnailfile" formControlName="thumbnailfile"
120 previewWidth="200px" previewHeight="110px" 120 previewWidth="200px" previewHeight="110px"
121 ></my-video-image> 121 ></my-video-image>
122 </div> 122 </div>
123 123
124 <div class="form-group"> 124 <div class="form-group">
125 <my-video-image 125 <my-video-image
126 inputLabel="Upload preview" inputName="previewfile" formControlName="previewfile" 126 i18n-inputLabel inputLabel="Upload preview" inputName="previewfile" formControlName="previewfile"
127 previewWidth="360px" previewHeight="200px" 127 previewWidth="360px" previewHeight="200px"
128 ></my-video-image> 128 ></my-video-image>
129 </div> 129 </div>
130 130
131 <div class="form-group"> 131 <div class="form-group">
132 <label for="support">Support</label> 132 <label i18n for="support">Support</label>
133 <my-help helpType="markdownEnhanced" preHtml="Short text to tell people how they can support you (membership platform...)."></my-help> 133 <my-help helpType="markdownEnhanced" i18n-preHtml preHtml="Short text to tell people how they can support you (membership platform...)."></my-help>
134 <my-markdown-textarea 134 <my-markdown-textarea
135 id="support" formControlName="support" textareaWidth="500px" [previewColumn]="true" markdownType="enhanced" 135 id="support" formControlName="support" textareaWidth="500px" [previewColumn]="true" markdownType="enhanced"
136 [classes]="{ 'input-error': formErrors['support'] }" 136 [classes]="{ 'input-error': formErrors['support'] }"
137 ></my-markdown-textarea> 137 ></my-markdown-textarea>
138 <div *ngIf="formErrors.support" class="form-error"> 138 <div *ngIf="formErrors.support" class="form-error">
139 {{ formErrors.support }} 139 {{ formErrors.support }}
diff --git a/client/src/app/videos/+video-edit/shared/video-image.component.html b/client/src/app/videos/+video-edit/shared/video-image.component.html
index 5d0624f8c..e319d7ee7 100644
--- a/client/src/app/videos/+video-edit/shared/video-image.component.html
+++ b/client/src/app/videos/+video-edit/shared/video-image.component.html
@@ -8,7 +8,7 @@
8 (change)="fileChange($event)" 8 (change)="fileChange($event)"
9 /> 9 />
10 </div> 10 </div>
11 <div class="image-constraints">(extensions: {{ videoImageExtensions }}, max size: {{ maxVideoImageSize | bytes }})</div> 11 <div i18n class="image-constraints">(extensions: {{ videoImageExtensions }}, max size: {{ maxVideoImageSize | bytes }})</div>
12 </div> 12 </div>
13 13
14 <img *ngIf="imageSrc" [ngStyle]="{ width: previewWidth, height: previewHeight }" [src]="imageSrc" class="preview" /> 14 <img *ngIf="imageSrc" [ngStyle]="{ width: previewWidth, height: previewHeight }" [src]="imageSrc" class="preview" />
diff --git a/client/src/app/videos/+video-edit/video-add.component.html b/client/src/app/videos/+video-edit/video-add.component.html
index 440556562..f00cfe016 100644
--- a/client/src/app/videos/+video-edit/video-add.component.html
+++ b/client/src/app/videos/+video-edit/video-add.component.html
@@ -1,7 +1,7 @@
1<div class="margin-content"> 1<div class="margin-content">
2 <div class="title-page title-page-single"> 2 <div class="title-page title-page-single">
3 <ng-template [ngIf]="!videoFileName">Upload your video</ng-template> 3 <ng-container *ngIf="!videoFileName" i18n>Upload your video</ng-container>
4 <ng-template [ngIf]="videoFileName">Upload {{ videoFileName }}</ng-template> 4 <ng-container *ngIf="videoFileName" i18n>Upload {{ videoFileName }}</ng-container>
5 </div> 5 </div>
6 6
7 <div *ngIf="!isUploadingVideo" class="upload-video-container"> 7 <div *ngIf="!isUploadingVideo" class="upload-video-container">
@@ -9,12 +9,12 @@
9 <div class="icon icon-upload"></div> 9 <div class="icon icon-upload"></div>
10 10
11 <div class="button-file"> 11 <div class="button-file">
12 <span>Select the file to upload</span> 12 <span i18n>Select the file to upload</span>
13 <input #videofileInput type="file" name="videofile" id="videofile" [accept]="videoExtensions" (change)="fileChange()" /> 13 <input #videofileInput type="file" name="videofile" id="videofile" [accept]="videoExtensions" (change)="fileChange()" />
14 </div> 14 </div>
15 15
16 <div class="form-group form-group-channel"> 16 <div class="form-group form-group-channel">
17 <label for="first-step-channel">Channel</label> 17 <label i18n for="first-step-channel">Channel</label>
18 <div class="peertube-select-container"> 18 <div class="peertube-select-container">
19 <select id="first-step-channel" [(ngModel)]="firstStepChannelId"> 19 <select id="first-step-channel" [(ngModel)]="firstStepChannelId">
20 <option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option> 20 <option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option>
@@ -23,7 +23,7 @@
23 </div> 23 </div>
24 24
25 <div class="form-group"> 25 <div class="form-group">
26 <label for="first-step-privacy">Privacy</label> 26 <label i18n for="first-step-privacy">Privacy</label>
27 <div class="peertube-select-container"> 27 <div class="peertube-select-container">
28 <select id="first-step-privacy" [(ngModel)]="firstStepPrivacyId"> 28 <select id="first-step-privacy" [(ngModel)]="firstStepPrivacyId">
29 <option *ngFor="let privacy of videoPrivacies" [value]="privacy.id">{{ privacy.label }}</option> 29 <option *ngFor="let privacy of videoPrivacies" [value]="privacy.id">{{ privacy.label }}</option>
@@ -49,14 +49,14 @@
49 ></my-video-edit> 49 ></my-video-edit>
50 50
51 <div class="submit-container"> 51 <div class="submit-container">
52 <div *ngIf="videoUploaded === false" class="message-submit">Publish will be available when upload is finished</div> 52 <div i18n *ngIf="videoUploaded === false" class="message-submit">Publish will be available when upload is finished</div>
53 53
54 <div class="submit-button" 54 <div class="submit-button"
55 (click)="updateSecondStep()" 55 (click)="updateSecondStep()"
56 [ngClass]="{ disabled: !form.valid || isUpdatingVideo === true || videoUploaded !== true }" 56 [ngClass]="{ disabled: !form.valid || isUpdatingVideo === true || videoUploaded !== true }"
57 > 57 >
58 <span class="icon icon-validate"></span> 58 <span class="icon icon-validate"></span>
59 <input type="button" value="Publish" /> 59 <input type="button" i18n-value value="Publish" />
60 </div> 60 </div>
61 </div> 61 </div>
62 </form> 62 </form>
diff --git a/client/src/app/videos/+video-edit/video-add.component.ts b/client/src/app/videos/+video-edit/video-add.component.ts
index 997f033b7..a615fd92c 100644
--- a/client/src/app/videos/+video-edit/video-add.component.ts
+++ b/client/src/app/videos/+video-edit/video-add.component.ts
@@ -15,6 +15,7 @@ import { ValidatorMessage } from '../../shared/forms/form-validators/validator-m
15import { populateAsyncUserVideoChannels } from '../../shared/misc/utils' 15import { populateAsyncUserVideoChannels } from '../../shared/misc/utils'
16import { VideoEdit } from '../../shared/video/video-edit.model' 16import { VideoEdit } from '../../shared/video/video-edit.model'
17import { VideoService } from '../../shared/video/video.service' 17import { VideoService } from '../../shared/video/video.service'
18import { I18n } from '@ngx-translate/i18n-polyfill'
18 19
19@Component({ 20@Component({
20 selector: 'my-videos-add', 21 selector: 'my-videos-add',
@@ -56,7 +57,8 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
56 private userService: UserService, 57 private userService: UserService,
57 private serverService: ServerService, 58 private serverService: ServerService,
58 private videoService: VideoService, 59 private videoService: VideoService,
59 private loadingBar: LoadingBarService 60 private loadingBar: LoadingBarService,
61 private i18n: I18n
60 ) { 62 ) {
61 super() 63 super()
62 } 64 }
@@ -99,10 +101,11 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
99 let text = '' 101 let text = ''
100 102
101 if (this.videoUploaded === true) { 103 if (this.videoUploaded === true) {
102 text = 'Your video was uploaded in your account and is private.' + 104 // FIXME: cannot concatenate strings inside i18n service :/
103 ' But associated data (tags, description...) will be lost, are you sure you want to leave this page?' 105 text = this.i18n('Your video was uploaded in your account and is private.') +
106 this.i18n('But associated data (tags, description...) will be lost, are you sure you want to leave this page?')
104 } else { 107 } else {
105 text = 'Your video is not uploaded yet, are you sure you want to leave this page?' 108 text = this.i18n('Your video is not uploaded yet, are you sure you want to leave this page?')
106 } 109 }
107 110
108 return { 111 return {
@@ -127,7 +130,7 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
127 this.isUploadingVideo = false 130 this.isUploadingVideo = false
128 this.videoUploadPercents = 0 131 this.videoUploadPercents = 0
129 this.videoUploadObservable = null 132 this.videoUploadObservable = null
130 this.notificationsService.info('Info', 'Upload cancelled') 133 this.notificationsService.info(this.i18n('Info'), this.i18n('Upload cancelled'))
131 } 134 }
132 } 135 }
133 136
@@ -137,7 +140,7 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
137 140
138 // Cannot upload videos > 4GB for now 141 // Cannot upload videos > 4GB for now
139 if (videofile.size > 4 * 1024 * 1024 * 1024) { 142 if (videofile.size > 4 * 1024 * 1024 * 1024) {
140 this.notificationsService.error('Error', 'We are sorry but PeerTube cannot handle videos > 4GB') 143 this.notificationsService.error(this.i18n('Error'), this.i18n('We are sorry but PeerTube cannot handle videos > 4GB'))
141 return 144 return
142 } 145 }
143 146
@@ -145,11 +148,15 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
145 if (videoQuota !== -1 && (this.userVideoQuotaUsed + videofile.size) > videoQuota) { 148 if (videoQuota !== -1 && (this.userVideoQuotaUsed + videofile.size) > videoQuota) {
146 const bytePipes = new BytesPipe() 149 const bytePipes = new BytesPipe()
147 150
148 const msg = 'Your video quota is exceeded with this video ' + 151 const msg = this.i18n(
149 `(video size: ${bytePipes.transform(videofile.size, 0)}, ` + 152 'Your video quota is exceeded with this video (video size: {{ videoSize }}, used: {{ videoQuotaUsed }}, quota: {{ videoQuota }})',
150 `used: ${bytePipes.transform(this.userVideoQuotaUsed, 0)}, ` + 153 {
151 `quota: ${bytePipes.transform(videoQuota, 0)})` 154 videoSize: bytePipes.transform(videofile.size, 0),
152 this.notificationsService.error('Error', msg) 155 videoQuotaUsed: bytePipes.transform(this.userVideoQuotaUsed, 0),
156 videoQuota: bytePipes.transform(videoQuota, 0)
157 }
158 )
159 this.notificationsService.error(this.i18n('Error'), msg)
153 return 160 return
154 } 161 }
155 162
@@ -192,8 +199,6 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
192 if (event.type === HttpEventType.UploadProgress) { 199 if (event.type === HttpEventType.UploadProgress) {
193 this.videoUploadPercents = Math.round(100 * event.loaded / event.total) 200 this.videoUploadPercents = Math.round(100 * event.loaded / event.total)
194 } else if (event instanceof HttpResponse) { 201 } else if (event instanceof HttpResponse) {
195 console.log('Video uploaded.')
196
197 this.videoUploaded = true 202 this.videoUploaded = true
198 203
199 this.videoUploadedIds = event.body.video 204 this.videoUploadedIds = event.body.video
@@ -207,7 +212,7 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
207 this.isUploadingVideo = false 212 this.isUploadingVideo = false
208 this.videoUploadPercents = 0 213 this.videoUploadPercents = 0
209 this.videoUploadObservable = null 214 this.videoUploadObservable = null
210 this.notificationsService.error('Error', err.message) 215 this.notificationsService.error(this.i18n('Error'), err.message)
211 } 216 }
212 ) 217 )
213 } 218 }
@@ -231,13 +236,13 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
231 this.isUploadingVideo = false 236 this.isUploadingVideo = false
232 this.loadingBar.complete() 237 this.loadingBar.complete()
233 238
234 this.notificationsService.success('Success', 'Video published.') 239 this.notificationsService.success(this.i18n('Success'), this.i18n('Video published.'))
235 this.router.navigate([ '/videos/watch', video.uuid ]) 240 this.router.navigate([ '/videos/watch', video.uuid ])
236 }, 241 },
237 242
238 err => { 243 err => {
239 this.isUpdatingVideo = false 244 this.isUpdatingVideo = false
240 this.notificationsService.error('Error', err.message) 245 this.notificationsService.error(this.i18n('Error'), err.message)
241 console.error(err) 246 console.error(err)
242 } 247 }
243 ) 248 )
diff --git a/client/src/app/videos/+video-edit/video-update.component.html b/client/src/app/videos/+video-edit/video-update.component.html
index a1b4707be..73b2bc08f 100644
--- a/client/src/app/videos/+video-edit/video-update.component.html
+++ b/client/src/app/videos/+video-edit/video-update.component.html
@@ -1,5 +1,5 @@
1<div class="margin-content"> 1<div class="margin-content">
2 <div class="title-page title-page-single"> 2 <div i18n class="title-page title-page-single">
3 Update {{ video?.name }} 3 Update {{ video?.name }}
4 </div> 4 </div>
5 5
@@ -13,7 +13,7 @@
13 <div class="submit-container"> 13 <div class="submit-container">
14 <div class="submit-button" (click)="update()" [ngClass]="{ disabled: !form.valid || isUpdatingVideo === true }"> 14 <div class="submit-button" (click)="update()" [ngClass]="{ disabled: !form.valid || isUpdatingVideo === true }">
15 <span class="icon icon-validate"></span> 15 <span class="icon icon-validate"></span>
16 <input type="button" value="Update" /> 16 <input type="button" i18n-value value="Update" />
17 </div> 17 </div>
18 </div> 18 </div>
19 </form> 19 </form>
diff --git a/client/src/app/videos/+video-edit/video-update.component.ts b/client/src/app/videos/+video-edit/video-update.component.ts
index 310285f92..e37dd526f 100644
--- a/client/src/app/videos/+video-edit/video-update.component.ts
+++ b/client/src/app/videos/+video-edit/video-update.component.ts
@@ -12,6 +12,7 @@ import { ValidatorMessage } from '../../shared/forms/form-validators/validator-m
12import { VideoEdit } from '../../shared/video/video-edit.model' 12import { VideoEdit } from '../../shared/video/video-edit.model'
13import { VideoService } from '../../shared/video/video.service' 13import { VideoService } from '../../shared/video/video.service'
14import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' 14import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
15import { I18n } from '@ngx-translate/i18n-polyfill'
15 16
16@Component({ 17@Component({
17 selector: 'my-videos-update', 18 selector: 'my-videos-update',
@@ -37,7 +38,8 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
37 private videoService: VideoService, 38 private videoService: VideoService,
38 private authService: AuthService, 39 private authService: AuthService,
39 private loadingBar: LoadingBarService, 40 private loadingBar: LoadingBarService,
40 private videoChannelService: VideoChannelService 41 private videoChannelService: VideoChannelService,
42 private i18n: I18n
41 ) { 43 ) {
42 super() 44 super()
43 } 45 }
@@ -91,7 +93,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
91 93
92 err => { 94 err => {
93 console.error(err) 95 console.error(err)
94 this.notificationsService.error('Error', err.message) 96 this.notificationsService.error(this.i18n('Error'), err.message)
95 } 97 }
96 ) 98 )
97 } 99 }
@@ -116,13 +118,13 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
116 () => { 118 () => {
117 this.isUpdatingVideo = false 119 this.isUpdatingVideo = false
118 this.loadingBar.complete() 120 this.loadingBar.complete()
119 this.notificationsService.success('Success', 'Video updated.') 121 this.notificationsService.success(this.i18n('Success'), this.i18n('Video updated.'))
120 this.router.navigate([ '/videos/watch', this.video.uuid ]) 122 this.router.navigate([ '/videos/watch', this.video.uuid ])
121 }, 123 },
122 124
123 err => { 125 err => {
124 this.isUpdatingVideo = false 126 this.isUpdatingVideo = false
125 this.notificationsService.error('Error', err.message) 127 this.notificationsService.error(this.i18n('Error'), err.message)
126 console.error(err) 128 console.error(err)
127 } 129 }
128 ) 130 )
diff --git a/client/src/app/videos/+video-watch/comment/video-comment-add.component.html b/client/src/app/videos/+video-watch/comment/video-comment-add.component.html
index 54d7286db..eaf068cfa 100644
--- a/client/src/app/videos/+video-watch/comment/video-comment-add.component.html
+++ b/client/src/app/videos/+video-watch/comment/video-comment-add.component.html
@@ -3,7 +3,7 @@
3 <img [src]="user.accountAvatarUrl" alt="Avatar" /> 3 <img [src]="user.accountAvatarUrl" alt="Avatar" />
4 4
5 <div class="form-group"> 5 <div class="form-group">
6 <textarea placeholder="Add comment..." formControlName="text" [ngClass]="{ 'input-error': formErrors['text'] }" 6 <textarea i18n-placeholder placeholder="Add comment..." formControlName="text" [ngClass]="{ 'input-error': formErrors['text'] }"
7 (keyup.control.enter)="onValidKey()" (keyup.meta.enter)="onValidKey()" #textarea> 7 (keyup.control.enter)="onValidKey()" (keyup.meta.enter)="onValidKey()" #textarea>
8 8
9 </textarea> 9 </textarea>
@@ -14,7 +14,7 @@
14 </div> 14 </div>
15 15
16 <div class="submit-comment"> 16 <div class="submit-comment">
17 <button *ngIf="isAddButtonDisplayed()" [ngClass]="{ disabled: !form.valid }"> 17 <button *ngIf="isAddButtonDisplayed()" [ngClass]="{ disabled: !form.valid }" i18n>
18 Post comment 18 Post comment
19 </button> 19 </button>
20 </div> 20 </div>
diff --git a/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts b/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts
index b1f446475..2ee5f5ef1 100644
--- a/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts
+++ b/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts
@@ -9,6 +9,7 @@ import { User } from '../../../shared/users'
9import { Video } from '../../../shared/video/video.model' 9import { Video } from '../../../shared/video/video.model'
10import { VideoComment } from './video-comment.model' 10import { VideoComment } from './video-comment.model'
11import { VideoCommentService } from './video-comment.service' 11import { VideoCommentService } from './video-comment.service'
12import { I18n } from '@ngx-translate/i18n-polyfill'
12 13
13@Component({ 14@Component({
14 selector: 'my-video-comment-add', 15 selector: 'my-video-comment-add',
@@ -37,7 +38,8 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
37 constructor ( 38 constructor (
38 private formBuilder: FormBuilder, 39 private formBuilder: FormBuilder,
39 private notificationsService: NotificationsService, 40 private notificationsService: NotificationsService,
40 private videoCommentService: VideoCommentService 41 private videoCommentService: VideoCommentService,
42 private i18n: I18n
41 ) { 43 ) {
42 super() 44 super()
43 } 45 }
@@ -92,7 +94,7 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
92 this.form.reset() 94 this.form.reset()
93 }, 95 },
94 96
95 err => this.notificationsService.error('Error', err.text) 97 err => this.notificationsService.error(this.i18n('Error'), err.text)
96 ) 98 )
97 } 99 }
98 100
diff --git a/client/src/app/videos/+video-watch/comment/video-comment.component.html b/client/src/app/videos/+video-watch/comment/video-comment.component.html
index 06808ef80..60b803206 100644
--- a/client/src/app/videos/+video-watch/comment/video-comment.component.html
+++ b/client/src/app/videos/+video-watch/comment/video-comment.component.html
@@ -2,7 +2,7 @@
2 <img [src]="comment.accountAvatarUrl" alt="Avatar" /> 2 <img [src]="comment.accountAvatarUrl" alt="Avatar" />
3 3
4 <div class="comment"> 4 <div class="comment">
5 <div *ngIf="highlightedComment === true" class="highlighted-comment">Highlighted comment</div> 5 <div *ngIf="highlightedComment === true" class="highlighted-comment" i18n>Highlighted comment</div>
6 6
7 <div class="comment-account-date"> 7 <div class="comment-account-date">
8 <a [href]="comment.account.url" target="_blank" rel="noopener noreferrer" class="comment-account">{{ comment.by }}</a> 8 <a [href]="comment.account.url" target="_blank" rel="noopener noreferrer" class="comment-account">{{ comment.by }}</a>
@@ -11,8 +11,8 @@
11 <div class="comment-html" [innerHTML]="sanitizedCommentHTML"></div> 11 <div class="comment-html" [innerHTML]="sanitizedCommentHTML"></div>
12 12
13 <div class="comment-actions"> 13 <div class="comment-actions">
14 <div *ngIf="isUserLoggedIn()" (click)="onWantToReply()" class="comment-action-reply">Reply</div> 14 <div *ngIf="isUserLoggedIn()" (click)="onWantToReply()" class="comment-action-reply" i18n>Reply</div>
15 <div *ngIf="isRemovableByUser()" (click)="onWantToDelete()" class="comment-action-delete">Delete</div> 15 <div *ngIf="isRemovableByUser()" (click)="onWantToDelete()" class="comment-action-delete" i18n>Delete</div>
16 </div> 16 </div>
17 17
18 <my-video-comment-add 18 <my-video-comment-add
diff --git a/client/src/app/videos/+video-watch/comment/video-comments.component.html b/client/src/app/videos/+video-watch/comment/video-comments.component.html
index 114a56dc7..ac7c03648 100644
--- a/client/src/app/videos/+video-watch/comment/video-comments.component.html
+++ b/client/src/app/videos/+video-watch/comment/video-comments.component.html
@@ -3,7 +3,10 @@
3 <div class="title-page title-page-single"> 3 <div class="title-page title-page-single">
4 Comments 4 Comments
5 </div> 5 </div>
6 <my-help *ngIf="video.commentsEnabled === true" helpType="custom" customHtml="You can either comment on the page of your instance where this video is federated with your PeerTube account, or 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 find back the video. Direct commenting capabilities are being worked on in <a href='https://github.com/Chocobozzz/PeerTube/issues/224'>#224</a>."></my-help> 6 <my-help
7 *ngIf="video.commentsEnabled === true" helpType="custom" i18n-customHtml
8 customHtml="You can either comment on the page of your instance where this video is federated with your PeerTube account, or 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 find back the video. Direct commenting capabilities are being worked on in <a href='https://github.com/Chocobozzz/PeerTube/issues/224'>#224</a>."
9 ></my-help>
7 </div> 10 </div>
8 11
9 <ng-template [ngIf]="video.commentsEnabled === true"> 12 <ng-template [ngIf]="video.commentsEnabled === true">
@@ -14,7 +17,7 @@
14 (commentCreated)="onCommentThreadCreated($event)" 17 (commentCreated)="onCommentThreadCreated($event)"
15 ></my-video-comment-add> 18 ></my-video-comment-add>
16 19
17 <div *ngIf="componentPagination.totalItems === 0 && comments.length === 0">No comments.</div> 20 <div *ngIf="componentPagination.totalItems === 0 && comments.length === 0" i18n>No comments.</div>
18 21
19 <div 22 <div
20 class="comment-threads" 23 class="comment-threads"
@@ -24,15 +27,15 @@
24 > 27 >
25 <div *ngIf="highlightedThread" id="highlighted-comment"> 28 <div *ngIf="highlightedThread" id="highlighted-comment">
26 <my-video-comment 29 <my-video-comment
27 [comment]="highlightedThread" 30 [comment]="highlightedThread"
28 [video]="video" 31 [video]="video"
29 [inReplyToCommentId]="inReplyToCommentId" 32 [inReplyToCommentId]="inReplyToCommentId"
30 [commentTree]="threadComments[highlightedThread.id]" 33 [commentTree]="threadComments[highlightedThread.id]"
31 [highlightedComment]="true" 34 [highlightedComment]="true"
32 (wantedToReply)="onWantedToReply($event)" 35 (wantedToReply)="onWantedToReply($event)"
33 (wantedToDelete)="onWantedToDelete($event)" 36 (wantedToDelete)="onWantedToDelete($event)"
34 (threadCreated)="onThreadCreated($event)" 37 (threadCreated)="onThreadCreated($event)"
35 (resetReply)="onResetReply()" 38 (resetReply)="onResetReply()"
36 ></my-video-comment> 39 ></my-video-comment>
37 </div> 40 </div>
38 41
@@ -50,7 +53,7 @@
50 ></my-video-comment> 53 ></my-video-comment>
51 54
52 <div *ngIf="comment.totalReplies !== 0 && !threadComments[comment.id]" (click)="viewReplies(comment.id)" class="view-replies"> 55 <div *ngIf="comment.totalReplies !== 0 && !threadComments[comment.id]" (click)="viewReplies(comment.id)" class="view-replies">
53 View all {{ comment.totalReplies }} replies 56 <ng-container i18n>View all {{ comment.totalReplies }} replies</ng-container>
54 57
55 <span *ngIf="!threadLoading[comment.id]" class="glyphicon glyphicon-menu-down"></span> 58 <span *ngIf="!threadLoading[comment.id]" class="glyphicon glyphicon-menu-down"></span>
56 <my-loader class="comment-thread-loading" [loading]="threadLoading[comment.id]"></my-loader> 59 <my-loader class="comment-thread-loading" [loading]="threadLoading[comment.id]"></my-loader>
@@ -59,7 +62,7 @@
59 </div> 62 </div>
60 </ng-template> 63 </ng-template>
61 64
62 <div *ngIf="video.commentsEnabled === false"> 65 <div *ngIf="video.commentsEnabled === false" i18n>
63 Comments are disabled. 66 Comments are disabled.
64 </div> 67 </div>
65</div> 68</div>
diff --git a/client/src/app/videos/+video-watch/comment/video-comments.component.ts b/client/src/app/videos/+video-watch/comment/video-comments.component.ts
index 34f4a0701..8c6ddb89e 100644
--- a/client/src/app/videos/+video-watch/comment/video-comments.component.ts
+++ b/client/src/app/videos/+video-watch/comment/video-comments.component.ts
@@ -11,6 +11,7 @@ import { VideoSortField } from '../../../shared/video/sort-field.type'
11import { VideoDetails } from '../../../shared/video/video-details.model' 11import { VideoDetails } from '../../../shared/video/video-details.model'
12import { VideoComment } from './video-comment.model' 12import { VideoComment } from './video-comment.model'
13import { VideoCommentService } from './video-comment.service' 13import { VideoCommentService } from './video-comment.service'
14import { I18n } from '@ngx-translate/i18n-polyfill'
14 15
15@Component({ 16@Component({
16 selector: 'my-video-comments', 17 selector: 'my-video-comments',
@@ -40,7 +41,8 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
40 private notificationsService: NotificationsService, 41 private notificationsService: NotificationsService,
41 private confirmService: ConfirmService, 42 private confirmService: ConfirmService,
42 private videoCommentService: VideoCommentService, 43 private videoCommentService: VideoCommentService,
43 private activatedRoute: ActivatedRoute 44 private activatedRoute: ActivatedRoute,
45 private i18n: I18n
44 ) {} 46 ) {}
45 47
46 ngOnInit () { 48 ngOnInit () {
@@ -77,7 +79,7 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
77 if (highlightThread) this.highlightedThread = new VideoComment(res.comment) 79 if (highlightThread) this.highlightedThread = new VideoComment(res.comment)
78 }, 80 },
79 81
80 err => this.notificationsService.error('Error', err.message) 82 err => this.notificationsService.error(this.i18n('Error'), err.message)
81 ) 83 )
82 } 84 }
83 85
@@ -89,7 +91,7 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
89 this.componentPagination.totalItems = res.totalComments 91 this.componentPagination.totalItems = res.totalComments
90 }, 92 },
91 93
92 err => this.notificationsService.error('Error', err.message) 94 err => this.notificationsService.error(this.i18n('Error'), err.message)
93 ) 95 )
94 } 96 }
95 97
@@ -111,9 +113,11 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
111 113
112 async onWantedToDelete (commentToDelete: VideoComment) { 114 async onWantedToDelete (commentToDelete: VideoComment) {
113 let message = 'Do you really want to delete this comment?' 115 let message = 'Do you really want to delete this comment?'
114 if (commentToDelete.totalReplies !== 0) message += `${commentToDelete.totalReplies} would be deleted too.` 116 if (commentToDelete.totalReplies !== 0) {
117 message += this.i18n(' {{ totalReplies }} replies will be deleted too.', { totalReplies: commentToDelete.totalReplies })
118 }
115 119
116 const res = await this.confirmService.confirm(message, 'Delete') 120 const res = await this.confirmService.confirm(message, this.i18n('Delete'))
117 if (res === false) return 121 if (res === false) return
118 122
119 this.videoCommentService.deleteVideoComment(commentToDelete.videoId, commentToDelete.id) 123 this.videoCommentService.deleteVideoComment(commentToDelete.videoId, commentToDelete.id)
@@ -136,7 +140,7 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
136 this.componentPagination.totalItems-- 140 this.componentPagination.totalItems--
137 }, 141 },
138 142
139 err => this.notificationsService.error('Error', err.message) 143 err => this.notificationsService.error(this.i18n('Error'), err.message)
140 ) 144 )
141 } 145 }
142 146
diff --git a/client/src/app/videos/+video-watch/modal/video-download.component.html b/client/src/app/videos/+video-watch/modal/video-download.component.html
index 617892b11..d30a49345 100644
--- a/client/src/app/videos/+video-watch/modal/video-download.component.html
+++ b/client/src/app/videos/+video-watch/modal/video-download.component.html
@@ -4,7 +4,7 @@
4 4
5 <div class="modal-header"> 5 <div class="modal-header">
6 <span class="close" aria-hidden="true" (click)="hide()"></span> 6 <span class="close" aria-hidden="true" (click)="hide()"></span>
7 <h4 class="modal-title">Download video</h4> 7 <h4 i18n class="modal-title">Download video</h4>
8 </div> 8 </div>
9 9
10 <div class="modal-body"> 10 <div class="modal-body">
@@ -17,22 +17,22 @@
17 <div class="download-type"> 17 <div class="download-type">
18 <div class="peertube-radio-container"> 18 <div class="peertube-radio-container">
19 <input type="radio" name="download" id="download-torrent" [(ngModel)]="downloadType" value="torrent"> 19 <input type="radio" name="download" id="download-torrent" [(ngModel)]="downloadType" value="torrent">
20 <label for="download-torrent">Torrent</label> 20 <label i18n for="download-torrent">Torrent</label>
21 </div> 21 </div>
22 22
23 <div class="peertube-radio-container"> 23 <div class="peertube-radio-container">
24 <input type="radio" name="download" id="download-direct" [(ngModel)]="downloadType" value="direct"> 24 <input type="radio" name="download" id="download-direct" [(ngModel)]="downloadType" value="direct">
25 <label for="download-direct">Direct download</label> 25 <label i18n for="download-direct">Direct download</label>
26 </div> 26 </div>
27 </div> 27 </div>
28 28
29 <div class="form-group inputs"> 29 <div class="form-group inputs">
30 <span class="action-button action-button-cancel" (click)="hide()"> 30 <span i18n class="action-button action-button-cancel" (click)="hide()">
31 Cancel 31 Cancel
32 </span> 32 </span>
33 33
34 <input 34 <input
35 type="submit" value="Download" class="action-button-submit" 35 type="submit" i18n-value value="Download" class="action-button-submit"
36 (click)="download()" 36 (click)="download()"
37 > 37 >
38 </div> 38 </div>
diff --git a/client/src/app/videos/+video-watch/modal/video-report.component.html b/client/src/app/videos/+video-watch/modal/video-report.component.html
index a9a7beb48..4ee3721fb 100644
--- a/client/src/app/videos/+video-watch/modal/video-report.component.html
+++ b/client/src/app/videos/+video-watch/modal/video-report.component.html
@@ -4,14 +4,14 @@
4 4
5 <div class="modal-header"> 5 <div class="modal-header">
6 <span class="close" aria-hidden="true" (click)="hide()"></span> 6 <span class="close" aria-hidden="true" (click)="hide()"></span>
7 <h4 class="modal-title">Report video</h4> 7 <h4 i18n class="modal-title">Report video</h4>
8 </div> 8 </div>
9 9
10 <div class="modal-body"> 10 <div class="modal-body">
11 11
12 <form novalidate [formGroup]="form" (ngSubmit)="report()"> 12 <form novalidate [formGroup]="form" (ngSubmit)="report()">
13 <div class="form-group"> 13 <div class="form-group">
14 <textarea placeholder="Reason..." formControlName="reason" [ngClass]="{ 'input-error': formErrors['reason'] }"> 14 <textarea i18n-placeholder placeholder="Reason..." formControlName="reason" [ngClass]="{ 'input-error': formErrors['reason'] }">
15 </textarea> 15 </textarea>
16 <div *ngIf="formErrors.reason" class="form-error"> 16 <div *ngIf="formErrors.reason" class="form-error">
17 {{ formErrors.reason }} 17 {{ formErrors.reason }}
@@ -19,12 +19,12 @@
19 </div> 19 </div>
20 20
21 <div class="form-group inputs"> 21 <div class="form-group inputs">
22 <span class="action-button action-button-cancel" (click)="hide()"> 22 <span i18n class="action-button action-button-cancel" (click)="hide()">
23 Cancel 23 Cancel
24 </span> 24 </span>
25 25
26 <input 26 <input
27 type="submit" value="Submit" class="action-button-submit" 27 type="submit" i18n-value value="Submit" class="action-button-submit"
28 [disabled]="!form.valid" 28 [disabled]="!form.valid"
29 > 29 >
30 </div> 30 </div>
diff --git a/client/src/app/videos/+video-watch/modal/video-report.component.ts b/client/src/app/videos/+video-watch/modal/video-report.component.ts
index 050e827e7..1bbd30226 100644
--- a/client/src/app/videos/+video-watch/modal/video-report.component.ts
+++ b/client/src/app/videos/+video-watch/modal/video-report.component.ts
@@ -4,6 +4,7 @@ import { NotificationsService } from 'angular2-notifications'
4import { ModalDirective } from 'ngx-bootstrap/modal' 4import { ModalDirective } from 'ngx-bootstrap/modal'
5import { FormReactive, VIDEO_ABUSE_REASON, VideoAbuseService } from '../../../shared/index' 5import { FormReactive, VIDEO_ABUSE_REASON, VideoAbuseService } from '../../../shared/index'
6import { VideoDetails } from '../../../shared/video/video-details.model' 6import { VideoDetails } from '../../../shared/video/video-details.model'
7import { I18n } from '@ngx-translate/i18n-polyfill'
7 8
8@Component({ 9@Component({
9 selector: 'my-video-report', 10 selector: 'my-video-report',
@@ -27,8 +28,9 @@ export class VideoReportComponent extends FormReactive implements OnInit {
27 constructor ( 28 constructor (
28 private formBuilder: FormBuilder, 29 private formBuilder: FormBuilder,
29 private videoAbuseService: VideoAbuseService, 30 private videoAbuseService: VideoAbuseService,
30 private notificationsService: NotificationsService 31 private notificationsService: NotificationsService,
31 ) { 32 private i18n: I18n
33 ) {
32 super() 34 super()
33 } 35 }
34 36
@@ -58,11 +60,11 @@ export class VideoReportComponent extends FormReactive implements OnInit {
58 this.videoAbuseService.reportVideo(this.video.id, reason) 60 this.videoAbuseService.reportVideo(this.video.id, reason)
59 .subscribe( 61 .subscribe(
60 () => { 62 () => {
61 this.notificationsService.success('Success', 'Video reported.') 63 this.notificationsService.success(this.i18n('Success'), this.i18n('Video reported.'))
62 this.hide() 64 this.hide()
63 }, 65 },
64 66
65 err => this.notificationsService.error('Error', err.message) 67 err => this.notificationsService.error(this.i18n('Error'), err.message)
66 ) 68 )
67 } 69 }
68} 70}
diff --git a/client/src/app/videos/+video-watch/modal/video-share.component.html b/client/src/app/videos/+video-watch/modal/video-share.component.html
index 85cf10a6c..26ab5144a 100644
--- a/client/src/app/videos/+video-watch/modal/video-share.component.html
+++ b/client/src/app/videos/+video-watch/modal/video-share.component.html
@@ -4,12 +4,12 @@
4 4
5 <div class="modal-header"> 5 <div class="modal-header">
6 <span class="close" aria-hidden="true" (click)="hide()"></span> 6 <span class="close" aria-hidden="true" (click)="hide()"></span>
7 <h4 class="modal-title">Share</h4> 7 <h4 i18n class="modal-title">Share</h4>
8 </div> 8 </div>
9 9
10 <div class="modal-body"> 10 <div class="modal-body">
11 <div class="form-group"> 11 <div class="form-group">
12 <label>URL</label> 12 <label i18n>URL</label>
13 <div class="input-group input-group-sm"> 13 <div class="input-group input-group-sm">
14 <input #urlInput (click)="urlInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="getVideoUrl()" /> 14 <input #urlInput (click)="urlInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="getVideoUrl()" />
15 <div class="input-group-btn" placement="bottom right"> 15 <div class="input-group-btn" placement="bottom right">
@@ -21,7 +21,7 @@
21 </div> 21 </div>
22 22
23 <div class="form-group"> 23 <div class="form-group">
24 <label>Embed</label> 24 <label i18n>Embed</label>
25 <div class="input-group input-group-sm"> 25 <div class="input-group input-group-sm">
26 <input #shareInput (click)="shareInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="getVideoIframeCode()" /> 26 <input #shareInput (click)="shareInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="getVideoIframeCode()" />
27 <div class="input-group-btn" placement="bottom right"> 27 <div class="input-group-btn" placement="bottom right">
@@ -32,12 +32,12 @@
32 </div> 32 </div>
33 </div> 33 </div>
34 34
35 <div *ngIf="notSecure()" class="alert alert-warning"> 35 <div i18n *ngIf="notSecure()" class="alert alert-warning">
36 The url is not secured (no HTTPS), so the embed video won't work on HTTPS websites (web browsers block non secured HTTP requests on HTTPS websites). 36 The url is not secured (no HTTPS), so the embed video won't work on HTTPS websites (web browsers block non secured HTTP requests on HTTPS websites).
37 </div> 37 </div>
38 38
39 <div class="form-group inputs"> 39 <div class="form-group inputs">
40 <span class="action-button action-button-cancel" (click)="hide()"> 40 <span i18n class="action-button action-button-cancel" (click)="hide()">
41 Cancel 41 Cancel
42 </span> 42 </span>
43 </div> 43 </div>
diff --git a/client/src/app/videos/+video-watch/modal/video-share.component.ts b/client/src/app/videos/+video-watch/modal/video-share.component.ts
index 33998c5d8..5c988a43b 100644
--- a/client/src/app/videos/+video-watch/modal/video-share.component.ts
+++ b/client/src/app/videos/+video-watch/modal/video-share.component.ts
@@ -5,6 +5,7 @@ import { NotificationsService } from 'angular2-notifications'
5import { ModalDirective } from 'ngx-bootstrap/modal' 5import { ModalDirective } from 'ngx-bootstrap/modal'
6import { VideoDetails } from '../../../shared/video/video-details.model' 6import { VideoDetails } from '../../../shared/video/video-details.model'
7import { buildVideoEmbed } from '../../../../assets/player/utils' 7import { buildVideoEmbed } from '../../../../assets/player/utils'
8import { I18n } from '@ngx-translate/i18n-polyfill'
8 9
9@Component({ 10@Component({
10 selector: 'my-video-share', 11 selector: 'my-video-share',
@@ -16,7 +17,10 @@ export class VideoShareComponent {
16 17
17 @ViewChild('modal') modal: ModalDirective 18 @ViewChild('modal') modal: ModalDirective
18 19
19 constructor (private notificationsService: NotificationsService) { 20 constructor (
21 private notificationsService: NotificationsService,
22 private i18n: I18n
23 ) {
20 // empty 24 // empty
21 } 25 }
22 26
@@ -41,6 +45,6 @@ export class VideoShareComponent {
41 } 45 }
42 46
43 activateCopiedMessage () { 47 activateCopiedMessage () {
44 this.notificationsService.success('Success', 'Copied') 48 this.notificationsService.success(this.i18n('Success'), this.i18n('Copied'))
45 } 49 }
46} 50}
diff --git a/client/src/app/videos/+video-watch/modal/video-support.component.html b/client/src/app/videos/+video-watch/modal/video-support.component.html
index 16ad9502a..9bcfcea47 100644
--- a/client/src/app/videos/+video-watch/modal/video-support.component.html
+++ b/client/src/app/videos/+video-watch/modal/video-support.component.html
@@ -4,7 +4,7 @@
4 4
5 <div class="modal-header"> 5 <div class="modal-header">
6 <span class="close" aria-hidden="true" (click)="hide()"></span> 6 <span class="close" aria-hidden="true" (click)="hide()"></span>
7 <h4 class="modal-title">Support</h4> 7 <h4 i18n class="modal-title">Support</h4>
8 </div> 8 </div>
9 9
10 <div class="modal-body"> 10 <div class="modal-body">
@@ -12,7 +12,7 @@
12 <div [innerHTML]="videoHTMLSupport"></div> 12 <div [innerHTML]="videoHTMLSupport"></div>
13 13
14 <div class="form-group inputs"> 14 <div class="form-group inputs">
15 <span class="action-button action-button-cancel" (click)="hide()"> 15 <span i18n class="action-button action-button-cancel" (click)="hide()">
16 Cancel 16 Cancel
17 </span> 17 </span>
18 </div> 18 </div>
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 03568b618..2fd82a940 100644
--- a/client/src/app/videos/video-list/video-local.component.ts
+++ b/client/src/app/videos/video-list/video-local.component.ts
@@ -27,8 +27,8 @@ export class VideoLocalComponent extends AbstractVideoList implements OnInit, On
27 protected notificationsService: NotificationsService, 27 protected notificationsService: NotificationsService,
28 protected authService: AuthService, 28 protected authService: AuthService,
29 protected location: Location, 29 protected location: Location,
30 private videoService: VideoService, 30 protected i18n: I18n,
31 private i18n: I18n 31 private videoService: VideoService
32 ) { 32 ) {
33 super() 33 super()
34 34
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 5768d9fe0..8183357f8 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
@@ -25,8 +25,8 @@ export class VideoRecentlyAddedComponent extends AbstractVideoList implements On
25 protected location: Location, 25 protected location: Location,
26 protected notificationsService: NotificationsService, 26 protected notificationsService: NotificationsService,
27 protected authService: AuthService, 27 protected authService: AuthService,
28 private videoService: VideoService, 28 protected i18n: I18n,
29 private i18n: I18n 29 private videoService: VideoService
30 ) { 30 ) {
31 super() 31 super()
32 32
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 35566a7bd..b6434f347 100644
--- a/client/src/app/videos/video-list/video-search.component.ts
+++ b/client/src/app/videos/video-list/video-search.component.ts
@@ -31,9 +31,9 @@ export class VideoSearchComponent extends AbstractVideoList implements OnInit, O
31 protected notificationsService: NotificationsService, 31 protected notificationsService: NotificationsService,
32 protected authService: AuthService, 32 protected authService: AuthService,
33 protected location: Location, 33 protected location: Location,
34 protected i18n: I18n,
34 private videoService: VideoService, 35 private videoService: VideoService,
35 private redirectService: RedirectService, 36 private redirectService: RedirectService
36 private i18n: I18n
37 ) { 37 ) {
38 super() 38 super()
39 39
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 760470e8c..e56b749d1 100644
--- a/client/src/app/videos/video-list/video-trending.component.ts
+++ b/client/src/app/videos/video-list/video-trending.component.ts
@@ -25,8 +25,8 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
25 protected notificationsService: NotificationsService, 25 protected notificationsService: NotificationsService,
26 protected authService: AuthService, 26 protected authService: AuthService,
27 protected location: Location, 27 protected location: Location,
28 private videoService: VideoService, 28 protected i18n: I18n,
29 private i18n: I18n 29 private videoService: VideoService
30 ) { 30 ) {
31 super() 31 super()
32 32