aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/angular.json81
-rw-r--r--client/package.json23
-rw-r--r--client/src/app/+about/about-instance/contact-admin-modal.component.ts8
-rw-r--r--client/src/app/+accounts/account-about/account-about.component.ts4
-rw-r--r--client/src/app/+accounts/account-videos/account-videos.component.ts4
-rw-r--r--client/src/app/+accounts/accounts.component.ts24
-rw-r--r--client/src/app/+admin/admin.component.ts32
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts24
-rw-r--r--client/src/app/+admin/config/shared/config.service.ts38
-rw-r--r--client/src/app/+admin/follows/followers-list/followers-list.component.ts16
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.ts14
-rw-r--r--client/src/app/+admin/follows/shared/redundancy-checkbox.component.ts10
-rw-r--r--client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts14
-rw-r--r--client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts42
-rw-r--r--client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts16
-rw-r--r--client/src/app/+admin/plugins/plugin-search/plugin-search.component.ts13
-rw-r--r--client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts4
-rw-r--r--client/src/app/+admin/plugins/shared/plugin-api.service.ts10
-rw-r--r--client/src/app/+admin/system/jobs/jobs.component.ts6
-rw-r--r--client/src/app/+admin/system/logs/logs.component.ts24
-rw-r--r--client/src/app/+admin/users/user-edit/user-create.component.ts10
-rw-r--r--client/src/app/+admin/users/user-edit/user-password.component.ts12
-rw-r--r--client/src/app/+admin/users/user-edit/user-update.component.ts14
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.ts40
-rw-r--r--client/src/app/+login/login.component.ts17
-rw-r--r--client/src/app/+my-account/+my-account-video-channels/my-account-video-channel-create.component.ts14
-rw-r--r--client/src/app/+my-account/+my-account-video-channels/my-account-video-channel-update.component.ts10
-rw-r--r--client/src/app/+my-account/+my-account-video-channels/my-account-video-channels.component.ts48
-rw-r--r--client/src/app/+my-account/my-account-history/my-account-history.component.ts14
-rw-r--r--client/src/app/+my-account/my-account-ownership/my-account-accept-ownership/my-account-accept-ownership.component.ts8
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts12
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts10
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts16
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts32
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts8
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-settings.component.ts8
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component.ts14
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.ts18
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component.ts9
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.ts17
-rw-r--r--client/src/app/+my-account/my-account-videos/my-account-videos.component.ts17
-rw-r--r--client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.ts8
-rw-r--r--client/src/app/+my-account/my-account.component.ts34
-rw-r--r--client/src/app/+reset-password/reset-password.component.ts10
-rw-r--r--client/src/app/+search/search-filters.component.ts28
-rw-r--r--client/src/app/+search/search.component.ts8
-rw-r--r--client/src/app/+signup/+register/register.component.ts10
-rw-r--r--client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts12
-rw-r--r--client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts8
-rw-r--r--client/src/app/+video-channels/video-channel-about/video-channel-about.component.ts4
-rw-r--r--client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts8
-rw-r--r--client/src/app/+video-channels/video-channels.component.ts12
-rw-r--r--client/src/app/+videos/+video-edit/shared/i18n-primeng-calendar.service.ts102
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.ts10
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts10
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts10
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts48
-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.component.ts4
-rw-r--r--client/src/app/+videos/+video-watch/comment/video-comments.component.ts8
-rw-r--r--client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.ts6
-rw-r--r--client/src/app/+videos/+video-watch/video-duration-formatter.pipe.ts11
-rw-r--r--client/src/app/+videos/+video-watch/video-watch-playlist.component.ts12
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.ts55
-rw-r--r--client/src/app/+videos/video-list/video-local.component.ts4
-rw-r--r--client/src/app/+videos/video-list/video-most-liked.component.ts6
-rw-r--r--client/src/app/+videos/video-list/video-recently-added.component.ts4
-rw-r--r--client/src/app/+videos/video-list/video-trending.component.ts13
-rw-r--r--client/src/app/+videos/video-list/video-user-subscriptions.component.ts8
-rw-r--r--client/src/app/app.component.ts18
-rw-r--r--client/src/app/app.module.ts16
-rw-r--r--client/src/app/core/auth/auth.service.ts24
-rw-r--r--client/src/app/core/hotkeys/hotkeys.component.ts12
-rw-r--r--client/src/app/core/notification/notifier.service.ts14
-rw-r--r--client/src/app/core/rest/rest-extractor.service.ts17
-rw-r--r--client/src/app/core/routing/can-deactivate-guard.service.ts11
-rw-r--r--client/src/app/core/users/user.service.ts12
-rw-r--r--client/src/app/menu/menu.component.ts16
-rw-r--r--client/src/app/modal/confirm.component.ts6
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-details.component.ts21
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts77
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-message-modal.component.ts10
-rw-r--r--client/src/app/shared/shared-abuse-list/moderation-comment-modal.component.ts8
-rw-r--r--client/src/app/shared/shared-forms/form-validators/abuse-validators.service.ts23
-rw-r--r--client/src/app/shared/shared-forms/form-validators/batch-domains-validators.service.ts11
-rw-r--r--client/src/app/shared/shared-forms/form-validators/custom-config-validators.service.ts41
-rw-r--r--client/src/app/shared/shared-forms/form-validators/instance-validators.service.ts27
-rw-r--r--client/src/app/shared/shared-forms/form-validators/login-validators.service.ts9
-rw-r--r--client/src/app/shared/shared-forms/form-validators/reset-password-validators.service.ts7
-rw-r--r--client/src/app/shared/shared-forms/form-validators/user-validators.service.ts63
-rw-r--r--client/src/app/shared/shared-forms/form-validators/video-accept-ownership-validators.service.ts7
-rw-r--r--client/src/app/shared/shared-forms/form-validators/video-block-validators.service.ts9
-rw-r--r--client/src/app/shared/shared-forms/form-validators/video-captions-validators.service.ts9
-rw-r--r--client/src/app/shared/shared-forms/form-validators/video-change-ownership-validators.service.ts9
-rw-r--r--client/src/app/shared/shared-forms/form-validators/video-channel-validators.service.ts27
-rw-r--r--client/src/app/shared/shared-forms/form-validators/video-comment-validators.service.ts11
-rw-r--r--client/src/app/shared/shared-forms/form-validators/video-playlist-validators.service.ts21
-rw-r--r--client/src/app/shared/shared-forms/form-validators/video-validators.service.ts33
-rw-r--r--client/src/app/shared/shared-forms/input-readonly-copy.component.ts8
-rw-r--r--client/src/app/shared/shared-forms/preview-upload.component.ts6
-rw-r--r--client/src/app/shared/shared-forms/reactive-file.component.ts13
-rw-r--r--client/src/app/shared/shared-forms/select/select-checkbox.component.ts11
-rw-r--r--client/src/app/shared/shared-instance/instance-features-table.component.ts23
-rw-r--r--client/src/app/shared/shared-main/account/actor-avatar-info.component.ts6
-rw-r--r--client/src/app/shared/shared-main/account/avatar.component.ts15
-rw-r--r--client/src/app/shared/shared-main/angular/from-now.pipe.ts27
-rw-r--r--client/src/app/shared/shared-main/buttons/delete-button.component.ts7
-rw-r--r--client/src/app/shared/shared-main/buttons/edit-button.component.ts7
-rw-r--r--client/src/app/shared/shared-main/misc/help.component.ts15
-rw-r--r--client/src/app/shared/shared-main/shared-main.module.ts3
-rw-r--r--client/src/app/shared/shared-main/users/user-quota.component.ts10
-rw-r--r--client/src/app/shared/shared-main/video/video.service.ts16
-rw-r--r--client/src/app/shared/shared-moderation/abuse.service.ts34
-rw-r--r--client/src/app/shared/shared-moderation/account-blocklist.component.ts10
-rw-r--r--client/src/app/shared/shared-moderation/batch-domains-modal.component.ts6
-rw-r--r--client/src/app/shared/shared-moderation/report-modals/account-report.component.ts8
-rw-r--r--client/src/app/shared/shared-moderation/report-modals/comment-report.component.ts8
-rw-r--r--client/src/app/shared/shared-moderation/report-modals/video-report.component.ts8
-rw-r--r--client/src/app/shared/shared-moderation/server-blocklist.component.ts12
-rw-r--r--client/src/app/shared/shared-moderation/user-ban-modal.component.ts8
-rw-r--r--client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts103
-rw-r--r--client/src/app/shared/shared-moderation/video-block.component.ts6
-rw-r--r--client/src/app/shared/shared-thumbnail/video-thumbnail.component.ts10
-rw-r--r--client/src/app/shared/shared-user-settings/user-interface-settings.component.ts8
-rw-r--r--client/src/app/shared/shared-user-settings/user-video-settings.component.ts24
-rw-r--r--client/src/app/shared/shared-user-subscription/subscribe-button.component.ts26
-rw-r--r--client/src/app/shared/shared-video-miniature/abstract-video-list.ts14
-rw-r--r--client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts53
-rw-r--r--client/src/app/shared/shared-video-miniature/video-download.component.ts36
-rw-r--r--client/src/app/shared/shared-video-miniature/video-miniature.component.ts17
-rw-r--r--client/src/app/shared/shared-video-miniature/videos-selection.component.ts2
-rw-r--r--client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts8
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts13
-rw-r--r--client/yarn.lock124
-rwxr-xr-xscripts/build/client.sh189
-rwxr-xr-xscripts/i18n/generate.sh8
136 files changed, 1061 insertions, 1628 deletions
diff --git a/client/angular.json b/client/angular.json
index 06223b837..c893cc1e9 100644
--- a/client/angular.json
+++ b/client/angular.json
@@ -124,6 +124,7 @@
124 "builder": "@angular-devkit/build-angular:browser", 124 "builder": "@angular-devkit/build-angular:browser",
125 "options": { 125 "options": {
126 "aot": true, 126 "aot": true,
127 "localize": true,
127 "outputPath": "dist", 128 "outputPath": "dist",
128 "index": "src/index.html", 129 "index": "src/index.html",
129 "main": "src/main.ts", 130 "main": "src/main.ts",
@@ -214,6 +215,7 @@
214 "budgets": [] 215 "budgets": []
215 }, 216 },
216 "hmr": { 217 "hmr": {
218 "localize": false,
217 "budgets": [ 219 "budgets": [
218 { 220 {
219 "type": "anyComponentStyle", 221 "type": "anyComponentStyle",
@@ -228,6 +230,7 @@
228 ] 230 ]
229 }, 231 },
230 "e2e": { 232 "e2e": {
233 "localize": false,
231 "budgets": [ 234 "budgets": [
232 { 235 {
233 "type": "anyComponentStyle", 236 "type": "anyComponentStyle",
@@ -240,84 +243,6 @@
240 "with": "src/environments/environment.e2e.ts" 243 "with": "src/environments/environment.e2e.ts"
241 } 244 }
242 ] 245 ]
243 },
244 "ar": {
245 "localize": [ "ar" ]
246 },
247 "hu-HU": {
248 "localize": [ "hu" ]
249 },
250 "th-TH": {
251 "localize": [ "th" ]
252 },
253 "fi-FI": {
254 "localize": [ "fi" ]
255 },
256 "nl-NL": {
257 "localize": [ "nl" ]
258 },
259 "gd": {
260 "localize": [ "gd" ]
261 },
262 "el-GR": {
263 "localize": [ "el" ]
264 },
265 "es-ES": {
266 "localize": [ "es" ]
267 },
268 "oc": {
269 "localize": [ "oc" ]
270 },
271 "pt-BR": {
272 "localize": [ "pt" ]
273 },
274 "pt-PT": {
275 "localize": [ "pt-PT" ]
276 },
277 "sv-SE": {
278 "localize": [ "sv" ]
279 },
280 "pl-PL": {
281 "localize": [ "pl" ]
282 },
283 "ru-RU": {
284 "localize": [ "ru" ]
285 },
286 "zh-Hans-CN": {
287 "localize": [ "zh-Hans" ]
288 },
289 "zh-Hant-TW": {
290 "localize": [ "zh-Hant" ]
291 },
292 "fr-FR": {
293 "localize": [ "fr" ]
294 },
295 "ja-JP": {
296 "localize": [ "ja" ]
297 },
298 "eu-ES": {
299 "localize": [ "eu" ]
300 },
301 "ca-ES": {
302 "localize": [ "ca" ]
303 },
304 "cs-CZ": {
305 "localize": [ "cs" ]
306 },
307 "eo": {
308 "localize": [ "eo" ]
309 },
310 "de-DE": {
311 "localize": [ "de" ]
312 },
313 "it-IT": {
314 "localize": [ "it" ]
315 },
316 "vi-VN": {
317 "localize": [ "vi" ]
318 },
319 "kab": {
320 "localize": [ "kab" ]
321 } 246 }
322 } 247 }
323 }, 248 },
diff --git a/client/package.json b/client/package.json
index 3c4b9ead4..9769cc813 100644
--- a/client/package.json
+++ b/client/package.json
@@ -28,19 +28,19 @@
28 "typings": "*.d.ts", 28 "typings": "*.d.ts",
29 "devDependencies": { 29 "devDependencies": {
30 "@angular-devkit/build-angular": "^0.1001.0-next.4", 30 "@angular-devkit/build-angular": "^0.1001.0-next.4",
31 "@angular/animations": "^10.1.0-next.4", 31 "@angular/animations": "^10.1.0-next.5",
32 "@angular/cdk": "^10.0.0", 32 "@angular/cdk": "^10.0.0",
33 "@angular/cli": "^10.1.0-next.4", 33 "@angular/cli": "^10.1.0-next.4",
34 "@angular/common": "^10.1.0-next.4", 34 "@angular/common": "^10.1.0-next.5",
35 "@angular/compiler": "^10.1.0-next.4", 35 "@angular/compiler": "^10.1.0-next.5",
36 "@angular/compiler-cli": "^10.1.0-next.4", 36 "@angular/compiler-cli": "^10.1.0-next.5",
37 "@angular/core": "^10.1.0-next.4", 37 "@angular/core": "^10.1.0-next.5",
38 "@angular/forms": "^10.1.0-next.4", 38 "@angular/forms": "^10.1.0-next.5",
39 "@angular/localize": "^10.1.0-next.4", 39 "@angular/localize": "^10.1.0-next.5",
40 "@angular/platform-browser": "^10.1.0-next.4", 40 "@angular/platform-browser": "^10.1.0-next.5",
41 "@angular/platform-browser-dynamic": "^10.1.0-next.4", 41 "@angular/platform-browser-dynamic": "^10.1.0-next.5",
42 "@angular/router": "^10.1.0-next.4", 42 "@angular/router": "^10.1.0-next.5",
43 "@angular/service-worker": "^10.1.0-next.4", 43 "@angular/service-worker": "^10.1.0-next.5",
44 "@angularclass/hmr": "^2.1.3", 44 "@angularclass/hmr": "^2.1.3",
45 "@neos21/bootstrap3-glyphicons": "^1.0.1", 45 "@neos21/bootstrap3-glyphicons": "^1.0.1",
46 "@ng-bootstrap/ng-bootstrap": "^7.0.0", 46 "@ng-bootstrap/ng-bootstrap": "^7.0.0",
@@ -51,7 +51,6 @@
51 "@ngx-loading-bar/http-client": "^5.0.0", 51 "@ngx-loading-bar/http-client": "^5.0.0",
52 "@ngx-loading-bar/router": "^5.0.0", 52 "@ngx-loading-bar/router": "^5.0.0",
53 "@ngx-meta/core": "^9.0.0", 53 "@ngx-meta/core": "^9.0.0",
54 "@ngx-translate/i18n-polyfill": "^1.0.0",
55 "@types/chart.js": "^2.9.16", 54 "@types/chart.js": "^2.9.16",
56 "@types/core-js": "^2.5.2", 55 "@types/core-js": "^2.5.2",
57 "@types/debug": "^4.1.5", 56 "@types/debug": "^4.1.5",
diff --git a/client/src/app/+about/about-instance/contact-admin-modal.component.ts b/client/src/app/+about/about-instance/contact-admin-modal.component.ts
index 5199402e6..215e281bb 100644
--- a/client/src/app/+about/about-instance/contact-admin-modal.component.ts
+++ b/client/src/app/+about/about-instance/contact-admin-modal.component.ts
@@ -4,7 +4,6 @@ import { FormReactive, FormValidatorService, InstanceValidatorsService } from '@
4import { InstanceService } from '@app/shared/shared-instance' 4import { InstanceService } from '@app/shared/shared-instance'
5import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 5import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
6import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 6import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { ServerConfig } from '@shared/models' 7import { ServerConfig } from '@shared/models'
9 8
10@Component({ 9@Component({
@@ -26,8 +25,7 @@ export class ContactAdminModalComponent extends FormReactive implements OnInit {
26 private instanceValidatorsService: InstanceValidatorsService, 25 private instanceValidatorsService: InstanceValidatorsService,
27 private instanceService: InstanceService, 26 private instanceService: InstanceService,
28 private serverService: ServerService, 27 private serverService: ServerService,
29 private notifier: Notifier, 28 private notifier: Notifier
30 private i18n: I18n
31 ) { 29 ) {
32 super() 30 super()
33 } 31 }
@@ -70,13 +68,13 @@ export class ContactAdminModalComponent extends FormReactive implements OnInit {
70 this.instanceService.contactAdministrator(fromEmail, fromName, subject, body) 68 this.instanceService.contactAdministrator(fromEmail, fromName, subject, body)
71 .subscribe( 69 .subscribe(
72 () => { 70 () => {
73 this.notifier.success(this.i18n('Your message has been sent.')) 71 this.notifier.success($localize`Your message has been sent.`)
74 this.hide() 72 this.hide()
75 }, 73 },
76 74
77 err => { 75 err => {
78 this.error = err.status === 403 76 this.error = err.status === 403
79 ? this.i18n('You already sent this form recently') 77 ? $localize`You already sent this form recently`
80 : err.message 78 : err.message
81 } 79 }
82 ) 80 )
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 8c01e4007..6cf846d72 100644
--- a/client/src/app/+accounts/account-about/account-about.component.ts
+++ b/client/src/app/+accounts/account-about/account-about.component.ts
@@ -2,7 +2,6 @@ import { Subscription } from 'rxjs'
2import { Component, OnDestroy, OnInit } from '@angular/core' 2import { Component, OnDestroy, OnInit } from '@angular/core'
3import { MarkdownService } from '@app/core' 3import { MarkdownService } from '@app/core'
4import { Account, AccountService } from '@app/shared/shared-main' 4import { Account, AccountService } from '@app/shared/shared-main'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6 5
7@Component({ 6@Component({
8 selector: 'my-account-about', 7 selector: 'my-account-about',
@@ -16,7 +15,6 @@ export class AccountAboutComponent implements OnInit, OnDestroy {
16 private accountSub: Subscription 15 private accountSub: Subscription
17 16
18 constructor ( 17 constructor (
19 private i18n: I18n,
20 private accountService: AccountService, 18 private accountService: AccountService,
21 private markdownService: MarkdownService 19 private markdownService: MarkdownService
22 ) { } 20 ) { }
@@ -37,6 +35,6 @@ export class AccountAboutComponent implements OnInit, OnDestroy {
37 getAccountDescription () { 35 getAccountDescription () {
38 if (this.descriptionHTML) return this.descriptionHTML 36 if (this.descriptionHTML) return this.descriptionHTML
39 37
40 return this.i18n('No description') 38 return $localize`No description`
41 } 39 }
42} 40}
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 5a9241f8e..3134a8ee2 100644
--- a/client/src/app/+accounts/account-videos/account-videos.component.ts
+++ b/client/src/app/+accounts/account-videos/account-videos.component.ts
@@ -6,7 +6,6 @@ import { AuthService, ConfirmService, LocalStorageService, Notifier, ScreenServi
6import { immutableAssign } from '@app/helpers' 6import { immutableAssign } from '@app/helpers'
7import { Account, AccountService, VideoService } from '@app/shared/shared-main' 7import { Account, AccountService, VideoService } from '@app/shared/shared-main'
8import { AbstractVideoList } from '@app/shared/shared-video-miniature' 8import { AbstractVideoList } from '@app/shared/shared-video-miniature'
9import { I18n } from '@ngx-translate/i18n-polyfill'
10 9
11@Component({ 10@Component({
12 selector: 'my-account-videos', 11 selector: 'my-account-videos',
@@ -23,7 +22,6 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit,
23 private accountSub: Subscription 22 private accountSub: Subscription
24 23
25 constructor ( 24 constructor (
26 protected i18n: I18n,
27 protected router: Router, 25 protected router: Router,
28 protected serverService: ServerService, 26 protected serverService: ServerService,
29 protected route: ActivatedRoute, 27 protected route: ActivatedRoute,
@@ -66,7 +64,7 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit,
66 .getAccountVideos(this.account, newPagination, this.sort) 64 .getAccountVideos(this.account, newPagination, this.sort)
67 .pipe( 65 .pipe(
68 tap(({ total }) => { 66 tap(({ total }) => {
69 this.titlePage = this.i18n('Published {{total}} videos', { total }) 67 this.titlePage = $localize`Published ${total} videos`
70 }) 68 })
71 ) 69 )
72 } 70 }
diff --git a/client/src/app/+accounts/accounts.component.ts b/client/src/app/+accounts/accounts.component.ts
index 9288fcb42..bbce62c35 100644
--- a/client/src/app/+accounts/accounts.component.ts
+++ b/client/src/app/+accounts/accounts.component.ts
@@ -5,7 +5,6 @@ import { ActivatedRoute } from '@angular/router'
5import { AuthService, Notifier, RedirectService, RestExtractor, ScreenService, UserService } from '@app/core' 5import { AuthService, Notifier, RedirectService, RestExtractor, ScreenService, UserService } from '@app/core'
6import { Account, AccountService, DropdownAction, ListOverflowItem, VideoChannel, VideoChannelService } from '@app/shared/shared-main' 6import { Account, AccountService, DropdownAction, ListOverflowItem, VideoChannel, VideoChannelService } from '@app/shared/shared-main'
7import { AccountReportComponent } from '@app/shared/shared-moderation' 7import { AccountReportComponent } from '@app/shared/shared-moderation'
8import { I18n } from '@ngx-translate/i18n-polyfill'
9import { User, UserRight } from '@shared/models' 8import { User, UserRight } from '@shared/models'
10 9
11@Component({ 10@Component({
@@ -36,8 +35,7 @@ export class AccountsComponent implements OnInit, OnDestroy {
36 private restExtractor: RestExtractor, 35 private restExtractor: RestExtractor,
37 private redirectService: RedirectService, 36 private redirectService: RedirectService,
38 private authService: AuthService, 37 private authService: AuthService,
39 private screenService: ScreenService, 38 private screenService: ScreenService
40 private i18n: I18n
41 ) { 39 ) {
42 } 40 }
43 41
@@ -58,9 +56,9 @@ export class AccountsComponent implements OnInit, OnDestroy {
58 ) 56 )
59 57
60 this.links = [ 58 this.links = [
61 { label: this.i18n('VIDEO CHANNELS'), routerLink: 'video-channels' }, 59 { label: $localize`VIDEO CHANNELS`, routerLink: 'video-channels' },
62 { label: this.i18n('VIDEOS'), routerLink: 'videos' }, 60 { label: $localize`VIDEOS`, routerLink: 'videos' },
63 { label: this.i18n('ABOUT'), routerLink: 'about' } 61 { label: $localize`ABOUT`, routerLink: 'about' }
64 ] 62 ]
65 } 63 }
66 64
@@ -88,11 +86,13 @@ export class AccountsComponent implements OnInit, OnDestroy {
88 } 86 }
89 87
90 activateCopiedMessage () { 88 activateCopiedMessage () {
91 this.notifier.success(this.i18n('Username copied')) 89 this.notifier.success($localize`Username copied`)
92 } 90 }
93 91
94 subscribersDisplayFor (count: number) { 92 subscribersDisplayFor (count: number) {
95 return this.i18n('{count, plural, =1 {1 subscriber} other {{{count}} subscribers}}', { count }) 93 if (count === 1) return $localize`1 subscriber`
94
95 return $localize`${count} subscribers`
96 } 96 }
97 97
98 private onAccount (account: Account) { 98 private onAccount (account: Account) {
@@ -105,16 +105,14 @@ export class AccountsComponent implements OnInit, OnDestroy {
105 () => { 105 () => {
106 this.isAccountManageable = this.account.userId && this.account.userId === this.authService.getUser().id 106 this.isAccountManageable = this.account.userId && this.account.userId === this.authService.getUser().id
107 107
108 this.accountFollowerTitle = this.i18n( 108 const followers = this.subscribersDisplayFor(account.followersCount)
109 '{{followers}} direct account followers', 109 this.accountFollowerTitle = $localize`${followers} direct account followers`
110 { followers: this.subscribersDisplayFor(account.followersCount) }
111 )
112 110
113 // It's not our account, we can report it 111 // It's not our account, we can report it
114 if (!this.isAccountManageable) { 112 if (!this.isAccountManageable) {
115 this.prependModerationActions = [ 113 this.prependModerationActions = [
116 { 114 {
117 label: this.i18n('Report account'), 115 label: $localize`Report account`,
118 handler: () => this.showReportModal() 116 handler: () => this.showReportModal()
119 } 117 }
120 ] 118 ]
diff --git a/client/src/app/+admin/admin.component.ts b/client/src/app/+admin/admin.component.ts
index 66e068c4c..b661a5517 100644
--- a/client/src/app/+admin/admin.component.ts
+++ b/client/src/app/+admin/admin.component.ts
@@ -1,9 +1,8 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { AuthService, ScreenService } from '@app/core' 2import { AuthService, ScreenService } from '@app/core'
3import { ListOverflowItem } from '@app/shared/shared-main' 3import { ListOverflowItem } from '@app/shared/shared-main'
4import { I18n } from '@ngx-translate/i18n-polyfill'
5import { UserRight } from '@shared/models'
6import { TopMenuDropdownParam } from '@app/shared/shared-main/misc/top-menu-dropdown.component' 4import { TopMenuDropdownParam } from '@app/shared/shared-main/misc/top-menu-dropdown.component'
5import { UserRight } from '@shared/models'
7 6
8@Component({ 7@Component({
9 templateUrl: './admin.component.html', 8 templateUrl: './admin.component.html',
@@ -15,8 +14,7 @@ export class AdminComponent implements OnInit {
15 14
16 constructor ( 15 constructor (
17 private auth: AuthService, 16 private auth: AuthService,
18 private screen: ScreenService, 17 private screen: ScreenService
19 private i18n: I18n
20 ) { } 18 ) { }
21 19
22 get isBroadcastMessageDisplayed () { 20 get isBroadcastMessageDisplayed () {
@@ -25,20 +23,20 @@ export class AdminComponent implements OnInit {
25 23
26 ngOnInit () { 24 ngOnInit () {
27 const federationItems: TopMenuDropdownParam = { 25 const federationItems: TopMenuDropdownParam = {
28 label: this.i18n('Federation'), 26 label: $localize`Federation`,
29 children: [ 27 children: [
30 { 28 {
31 label: this.i18n('Instances you follow'), 29 label: $localize`Instances you follow`,
32 routerLink: '/admin/follows/following-list', 30 routerLink: '/admin/follows/following-list',
33 iconName: 'following' 31 iconName: 'following'
34 }, 32 },
35 { 33 {
36 label: this.i18n('Instances following you'), 34 label: $localize`Instances following you`,
37 routerLink: '/admin/follows/followers-list', 35 routerLink: '/admin/follows/followers-list',
38 iconName: 'follower' 36 iconName: 'follower'
39 }, 37 },
40 { 38 {
41 label: this.i18n('Video redundancies'), 39 label: $localize`Video redundancies`,
42 routerLink: '/admin/follows/video-redundancies-list', 40 routerLink: '/admin/follows/video-redundancies-list',
43 iconName: 'videos' 41 iconName: 'videos'
44 } 42 }
@@ -46,56 +44,56 @@ export class AdminComponent implements OnInit {
46 } 44 }
47 45
48 const moderationItems: TopMenuDropdownParam = { 46 const moderationItems: TopMenuDropdownParam = {
49 label: this.i18n('Moderation'), 47 label: $localize`Moderation`,
50 children: [] 48 children: []
51 } 49 }
52 50
53 if (this.hasAbusesRight()) { 51 if (this.hasAbusesRight()) {
54 moderationItems.children.push({ 52 moderationItems.children.push({
55 label: this.i18n('Reports'), 53 label: $localize`Reports`,
56 routerLink: '/admin/moderation/abuses/list', 54 routerLink: '/admin/moderation/abuses/list',
57 iconName: 'flag' 55 iconName: 'flag'
58 }) 56 })
59 } 57 }
60 if (this.hasVideoBlocklistRight()) { 58 if (this.hasVideoBlocklistRight()) {
61 moderationItems.children.push({ 59 moderationItems.children.push({
62 label: this.i18n('Video blocks'), 60 label: $localize`Video blocks`,
63 routerLink: '/admin/moderation/video-blocks/list', 61 routerLink: '/admin/moderation/video-blocks/list',
64 iconName: 'cross' 62 iconName: 'cross'
65 }) 63 })
66 } 64 }
67 if (this.hasAccountsBlocklistRight()) { 65 if (this.hasAccountsBlocklistRight()) {
68 moderationItems.children.push({ 66 moderationItems.children.push({
69 label: this.i18n('Muted accounts'), 67 label: $localize`Muted accounts`,
70 routerLink: '/admin/moderation/blocklist/accounts', 68 routerLink: '/admin/moderation/blocklist/accounts',
71 iconName: 'user-x' 69 iconName: 'user-x'
72 }) 70 })
73 } 71 }
74 if (this.hasServersBlocklistRight()) { 72 if (this.hasServersBlocklistRight()) {
75 moderationItems.children.push({ 73 moderationItems.children.push({
76 label: this.i18n('Muted servers'), 74 label: $localize`Muted servers`,
77 routerLink: '/admin/moderation/blocklist/servers', 75 routerLink: '/admin/moderation/blocklist/servers',
78 iconName: 'peertube-x' 76 iconName: 'peertube-x'
79 }) 77 })
80 } 78 }
81 79
82 if (this.hasUsersRight()) { 80 if (this.hasUsersRight()) {
83 this.menuEntries.push({ label: this.i18n('Users'), routerLink: '/admin/users' }) 81 this.menuEntries.push({ label: $localize`Users`, routerLink: '/admin/users' })
84 } 82 }
85 83
86 if (this.hasServerFollowRight()) this.menuEntries.push(federationItems) 84 if (this.hasServerFollowRight()) this.menuEntries.push(federationItems)
87 if (this.hasAbusesRight() || this.hasVideoBlocklistRight()) this.menuEntries.push(moderationItems) 85 if (this.hasAbusesRight() || this.hasVideoBlocklistRight()) this.menuEntries.push(moderationItems)
88 86
89 if (this.hasConfigRight()) { 87 if (this.hasConfigRight()) {
90 this.menuEntries.push({ label: this.i18n('Configuration'), routerLink: '/admin/config' }) 88 this.menuEntries.push({ label: $localize`Configuration`, routerLink: '/admin/config' })
91 } 89 }
92 90
93 if (this.hasPluginsRight()) { 91 if (this.hasPluginsRight()) {
94 this.menuEntries.push({ label: this.i18n('Plugins/Themes'), routerLink: '/admin/plugins' }) 92 this.menuEntries.push({ label: $localize`Plugins/Themes`, routerLink: '/admin/plugins' })
95 } 93 }
96 94
97 if (this.hasJobsRight() || this.hasLogsRight() || this.hasDebugRight()) { 95 if (this.hasJobsRight() || this.hasLogsRight() || this.hasDebugRight()) {
98 this.menuEntries.push({ label: this.i18n('System'), routerLink: '/admin/system' }) 96 this.menuEntries.push({ label: $localize`System`, routerLink: '/admin/system' })
99 } 97 }
100 } 98 }
101 99
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 00a0bfad2..3a60b144f 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
@@ -12,7 +12,6 @@ import {
12 UserValidatorsService 12 UserValidatorsService
13} from '@app/shared/shared-forms' 13} from '@app/shared/shared-forms'
14import { NgbNav } from '@ng-bootstrap/ng-bootstrap' 14import { NgbNav } from '@ng-bootstrap/ng-bootstrap'
15import { I18n } from '@ngx-translate/i18n-polyfill'
16import { CustomConfig, ServerConfig } from '@shared/models' 15import { CustomConfig, ServerConfig } from '@shared/models'
17 16
18@Component({ 17@Component({
@@ -42,45 +41,44 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit, A
42 private userValidatorsService: UserValidatorsService, 41 private userValidatorsService: UserValidatorsService,
43 private notifier: Notifier, 42 private notifier: Notifier,
44 private configService: ConfigService, 43 private configService: ConfigService,
45 private serverService: ServerService, 44 private serverService: ServerService
46 private i18n: I18n
47 ) { 45 ) {
48 super() 46 super()
49 47
50 this.resolutions = [ 48 this.resolutions = [
51 { 49 {
52 id: '0p', 50 id: '0p',
53 label: this.i18n('Audio-only'), 51 label: $localize`Audio-only`,
54 description: this.i18n('A <code>.mp4</code> that keeps the original audio track, with no video') 52 description: $localize`A <code>.mp4</code> that keeps the original audio track, with no video`
55 }, 53 },
56 { 54 {
57 id: '240p', 55 id: '240p',
58 label: this.i18n('240p') 56 label: $localize`240p`
59 }, 57 },
60 { 58 {
61 id: '360p', 59 id: '360p',
62 label: this.i18n('360p') 60 label: $localize`360p`
63 }, 61 },
64 { 62 {
65 id: '480p', 63 id: '480p',
66 label: this.i18n('480p') 64 label: $localize`480p`
67 }, 65 },
68 { 66 {
69 id: '720p', 67 id: '720p',
70 label: this.i18n('720p') 68 label: $localize`720p`
71 }, 69 },
72 { 70 {
73 id: '1080p', 71 id: '1080p',
74 label: this.i18n('1080p') 72 label: $localize`1080p`
75 }, 73 },
76 { 74 {
77 id: '2160p', 75 id: '2160p',
78 label: this.i18n('2160p') 76 label: $localize`2160p`
79 } 77 }
80 ] 78 ]
81 79
82 this.transcodingThreadOptions = [ 80 this.transcodingThreadOptions = [
83 { value: 0, label: this.i18n('Auto (via ffmpeg)') }, 81 { value: 0, label: $localize`Auto (via ffmpeg)` },
84 { value: 1, label: '1' }, 82 { value: 1, label: '1' },
85 { value: 2, label: '2' }, 83 { value: 2, label: '2' },
86 { value: 4, label: '4' }, 84 { value: 4, label: '4' },
@@ -288,7 +286,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit, A
288 286
289 this.updateForm() 287 this.updateForm()
290 288
291 this.notifier.success(this.i18n('Configuration updated.')) 289 this.notifier.success($localize`Configuration updated.`)
292 }, 290 },
293 291
294 err => this.notifier.error(err.message) 292 err => this.notifier.error(err.message)
diff --git a/client/src/app/+admin/config/shared/config.service.ts b/client/src/app/+admin/config/shared/config.service.ts
index f182946b8..5f98aa545 100644
--- a/client/src/app/+admin/config/shared/config.service.ts
+++ b/client/src/app/+admin/config/shared/config.service.ts
@@ -2,7 +2,6 @@ import { catchError } from 'rxjs/operators'
2import { HttpClient } from '@angular/common/http' 2import { HttpClient } from '@angular/common/http'
3import { Injectable } from '@angular/core' 3import { Injectable } from '@angular/core'
4import { RestExtractor } from '@app/core' 4import { RestExtractor } from '@app/core'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { CustomConfig } from '@shared/models' 5import { CustomConfig } from '@shared/models'
7import { environment } from '../../../../environments/environment' 6import { environment } from '../../../../environments/environment'
8 7
@@ -15,33 +14,32 @@ export class ConfigService {
15 14
16 constructor ( 15 constructor (
17 private authHttp: HttpClient, 16 private authHttp: HttpClient,
18 private restExtractor: RestExtractor, 17 private restExtractor: RestExtractor
19 private i18n: I18n 18 ) {
20 ) {
21 this.videoQuotaOptions = [ 19 this.videoQuotaOptions = [
22 { value: undefined, label: 'Default quota', disabled: true }, 20 { value: undefined, label: 'Default quota', disabled: true },
23 { value: -1, label: this.i18n('Unlimited') }, 21 { value: -1, label: $localize`Unlimited` },
24 { value: undefined, label: '─────', disabled: true }, 22 { value: undefined, label: '─────', disabled: true },
25 { value: 0, label: this.i18n('None - no upload possible') }, 23 { value: 0, label: $localize`None - no upload possible` },
26 { value: 100 * 1024 * 1024, label: this.i18n('100MB') }, 24 { value: 100 * 1024 * 1024, label: $localize`100MB` },
27 { value: 500 * 1024 * 1024, label: this.i18n('500MB') }, 25 { value: 500 * 1024 * 1024, label: $localize`500MB` },
28 { value: 1024 * 1024 * 1024, label: this.i18n('1GB') }, 26 { value: 1024 * 1024 * 1024, label: $localize`1GB` },
29 { value: 5 * 1024 * 1024 * 1024, label: this.i18n('5GB') }, 27 { value: 5 * 1024 * 1024 * 1024, label: $localize`5GB` },
30 { value: 20 * 1024 * 1024 * 1024, label: this.i18n('20GB') }, 28 { value: 20 * 1024 * 1024 * 1024, label: $localize`20GB` },
31 { value: 50 * 1024 * 1024 * 1024, label: this.i18n('50GB') } 29 { value: 50 * 1024 * 1024 * 1024, label: $localize`50GB` }
32 ] 30 ]
33 31
34 this.videoQuotaDailyOptions = [ 32 this.videoQuotaDailyOptions = [
35 { value: undefined, label: 'Default daily upload limit', disabled: true }, 33 { value: undefined, label: 'Default daily upload limit', disabled: true },
36 { value: -1, label: this.i18n('Unlimited') }, 34 { value: -1, label: $localize`Unlimited` },
37 { value: undefined, label: '─────', disabled: true }, 35 { value: undefined, label: '─────', disabled: true },
38 { value: 0, label: this.i18n('None - no upload possible') }, 36 { value: 0, label: $localize`None - no upload possible` },
39 { value: 10 * 1024 * 1024, label: this.i18n('10MB') }, 37 { value: 10 * 1024 * 1024, label: $localize`10MB` },
40 { value: 50 * 1024 * 1024, label: this.i18n('50MB') }, 38 { value: 50 * 1024 * 1024, label: $localize`50MB` },
41 { value: 100 * 1024 * 1024, label: this.i18n('100MB') }, 39 { value: 100 * 1024 * 1024, label: $localize`100MB` },
42 { value: 500 * 1024 * 1024, label: this.i18n('500MB') }, 40 { value: 500 * 1024 * 1024, label: $localize`500MB` },
43 { value: 2 * 1024 * 1024 * 1024, label: this.i18n('2GB') }, 41 { value: 2 * 1024 * 1024 * 1024, label: $localize`2GB` },
44 { value: 5 * 1024 * 1024 * 1024, label: this.i18n('5GB') } 42 { value: 5 * 1024 * 1024 * 1024, label: $localize`5GB` }
45 ] 43 ]
46 } 44 }
47 45
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 63135f898..904e3c338 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
@@ -2,7 +2,6 @@ import { SortMeta } from 'primeng/api'
2import { Component, OnInit } from '@angular/core' 2import { Component, OnInit } from '@angular/core'
3import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core' 3import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
4import { InstanceFollowService } from '@app/shared/shared-instance' 4import { InstanceFollowService } from '@app/shared/shared-instance'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { ActorFollow } from '@shared/models' 5import { ActorFollow } from '@shared/models'
7 6
8@Component({ 7@Component({
@@ -19,7 +18,6 @@ export class FollowersListComponent extends RestTable implements OnInit {
19 constructor ( 18 constructor (
20 private confirmService: ConfirmService, 19 private confirmService: ConfirmService,
21 private notifier: Notifier, 20 private notifier: Notifier,
22 private i18n: I18n,
23 private followService: InstanceFollowService 21 private followService: InstanceFollowService
24 ) { 22 ) {
25 super() 23 super()
@@ -40,7 +38,7 @@ export class FollowersListComponent extends RestTable implements OnInit {
40 .subscribe( 38 .subscribe(
41 () => { 39 () => {
42 const handle = follow.follower.name + '@' + follow.follower.host 40 const handle = follow.follower.name + '@' + follow.follower.host
43 this.notifier.success(this.i18n('{{handle}} accepted in instance followers', { handle })) 41 this.notifier.success($localize`${handle} accepted in instance followers`)
44 }, 42 },
45 43
46 err => { 44 err => {
@@ -51,15 +49,15 @@ export class FollowersListComponent extends RestTable implements OnInit {
51 } 49 }
52 50
53 async rejectFollower (follow: ActorFollow) { 51 async rejectFollower (follow: ActorFollow) {
54 const message = this.i18n('Do you really want to reject this follower?') 52 const message = $localize`Do you really want to reject this follower?`
55 const res = await this.confirmService.confirm(message, this.i18n('Reject')) 53 const res = await this.confirmService.confirm(message, $localize`Reject`)
56 if (res === false) return 54 if (res === false) return
57 55
58 this.followService.rejectFollower(follow) 56 this.followService.rejectFollower(follow)
59 .subscribe( 57 .subscribe(
60 () => { 58 () => {
61 const handle = follow.follower.name + '@' + follow.follower.host 59 const handle = follow.follower.name + '@' + follow.follower.host
62 this.notifier.success(this.i18n('{{handle}} rejected from instance followers', { handle })) 60 this.notifier.success($localize`${handle} rejected from instance followers`)
63 61
64 this.loadData() 62 this.loadData()
65 }, 63 },
@@ -72,15 +70,15 @@ export class FollowersListComponent extends RestTable implements OnInit {
72 } 70 }
73 71
74 async deleteFollower (follow: ActorFollow) { 72 async deleteFollower (follow: ActorFollow) {
75 const message = this.i18n('Do you really want to delete this follower?') 73 const message = $localize`Do you really want to delete this follower?`
76 const res = await this.confirmService.confirm(message, this.i18n('Delete')) 74 const res = await this.confirmService.confirm(message, $localize`Delete`)
77 if (res === false) return 75 if (res === false) return
78 76
79 this.followService.removeFollower(follow) 77 this.followService.removeFollower(follow)
80 .subscribe( 78 .subscribe(
81 () => { 79 () => {
82 const handle = follow.follower.name + '@' + follow.follower.host 80 const handle = follow.follower.name + '@' + follow.follower.host
83 this.notifier.success(this.i18n('{{handle}} removed from instance followers', { handle })) 81 this.notifier.success($localize`${handle} removed from instance followers`)
84 82
85 this.loadData() 83 this.loadData()
86 }, 84 },
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 dae8923b5..5f71f1238 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
@@ -3,7 +3,6 @@ import { Component, OnInit, ViewChild } from '@angular/core'
3import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core' 3import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
4import { InstanceFollowService } from '@app/shared/shared-instance' 4import { InstanceFollowService } from '@app/shared/shared-instance'
5import { BatchDomainsModalComponent } from '@app/shared/shared-moderation' 5import { BatchDomainsModalComponent } from '@app/shared/shared-moderation'
6import { I18n } from '@ngx-translate/i18n-polyfill'
7import { ActorFollow } from '@shared/models' 6import { ActorFollow } from '@shared/models'
8 7
9@Component({ 8@Component({
@@ -22,9 +21,8 @@ export class FollowingListComponent extends RestTable implements OnInit {
22 constructor ( 21 constructor (
23 private notifier: Notifier, 22 private notifier: Notifier,
24 private confirmService: ConfirmService, 23 private confirmService: ConfirmService,
25 private followService: InstanceFollowService, 24 private followService: InstanceFollowService
26 private i18n: I18n 25 ) {
27 ) {
28 super() 26 super()
29 } 27 }
30 28
@@ -47,7 +45,7 @@ export class FollowingListComponent extends RestTable implements OnInit {
47 async addFollowing (hosts: string[]) { 45 async addFollowing (hosts: string[]) {
48 this.followService.follow(hosts).subscribe( 46 this.followService.follow(hosts).subscribe(
49 () => { 47 () => {
50 this.notifier.success(this.i18n('Follow request(s) sent!')) 48 this.notifier.success($localize`Follow request(s) sent!`)
51 this.loadData() 49 this.loadData()
52 }, 50 },
53 51
@@ -57,14 +55,14 @@ export class FollowingListComponent extends RestTable implements OnInit {
57 55
58 async removeFollowing (follow: ActorFollow) { 56 async removeFollowing (follow: ActorFollow) {
59 const res = await this.confirmService.confirm( 57 const res = await this.confirmService.confirm(
60 this.i18n('Do you really want to unfollow {{host}}?', { host: follow.following.host }), 58 $localize`Do you really want to unfollow ${follow.following.host}?`,
61 this.i18n('Unfollow') 59 $localize`Unfollow`
62 ) 60 )
63 if (res === false) return 61 if (res === false) return
64 62
65 this.followService.unfollow(follow).subscribe( 63 this.followService.unfollow(follow).subscribe(
66 () => { 64 () => {
67 this.notifier.success(this.i18n('You are not following {{host}} anymore.', { host: follow.following.host })) 65 this.notifier.success($localize`You are not following ${follow.following.host} anymore.`)
68 this.loadData() 66 this.loadData()
69 }, 67 },
70 68
diff --git a/client/src/app/+admin/follows/shared/redundancy-checkbox.component.ts b/client/src/app/+admin/follows/shared/redundancy-checkbox.component.ts
index 662143abc..729b7f599 100644
--- a/client/src/app/+admin/follows/shared/redundancy-checkbox.component.ts
+++ b/client/src/app/+admin/follows/shared/redundancy-checkbox.component.ts
@@ -1,7 +1,6 @@
1import { Component, Input } from '@angular/core' 1import { Component, Input } from '@angular/core'
2import { Notifier } from '@app/core' 2import { Notifier } from '@app/core'
3import { RedundancyService } from '@app/shared/shared-main' 3import { RedundancyService } from '@app/shared/shared-main'
4import { I18n } from '@ngx-translate/i18n-polyfill'
5 4
6@Component({ 5@Component({
7 selector: 'my-redundancy-checkbox', 6 selector: 'my-redundancy-checkbox',
@@ -14,17 +13,16 @@ export class RedundancyCheckboxComponent {
14 13
15 constructor ( 14 constructor (
16 private notifier: Notifier, 15 private notifier: Notifier,
17 private redundancyService: RedundancyService, 16 private redundancyService: RedundancyService
18 private i18n: I18n 17 ) { }
19 ) { }
20 18
21 updateRedundancyState () { 19 updateRedundancyState () {
22 this.redundancyService.updateRedundancy(this.host, this.redundancyAllowed) 20 this.redundancyService.updateRedundancy(this.host, this.redundancyAllowed)
23 .subscribe( 21 .subscribe(
24 () => { 22 () => {
25 const stateLabel = this.redundancyAllowed ? this.i18n('enabled') : this.i18n('disabled') 23 const stateLabel = this.redundancyAllowed ? $localize`enabled` : $localize`disabled`
26 24
27 this.notifier.success(this.i18n('Redundancy for {{host}} is {{stateLabel}}', { host: this.host, stateLabel })) 25 this.notifier.success($localize`Redundancy for ${this.host} is ${stateLabel}`)
28 }, 26 },
29 27
30 err => this.notifier.error(err.message) 28 err => this.notifier.error(err.message)
diff --git a/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts b/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts
index 07e2e0ff3..d6fd1a1ab 100644
--- a/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts
+++ b/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts
@@ -2,7 +2,6 @@ import { SortMeta } from 'primeng/api'
2import { Component, OnInit } from '@angular/core' 2import { Component, OnInit } from '@angular/core'
3import { ConfirmService, Notifier, RestPagination, RestTable, ServerService } from '@app/core' 3import { ConfirmService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
4import { BytesPipe, RedundancyService } from '@app/shared/shared-main' 4import { BytesPipe, RedundancyService } from '@app/shared/shared-main'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' 5import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
7import { VideoRedundanciesTarget, VideoRedundancy } from '@shared/models' 6import { VideoRedundanciesTarget, VideoRedundancy } from '@shared/models'
8import { VideosRedundancyStats } from '@shared/models/server' 7import { VideosRedundancyStats } from '@shared/models/server'
@@ -32,9 +31,8 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
32 private notifier: Notifier, 31 private notifier: Notifier,
33 private confirmService: ConfirmService, 32 private confirmService: ConfirmService,
34 private redundancyService: RedundancyService, 33 private redundancyService: RedundancyService,
35 private serverService: ServerService, 34 private serverService: ServerService
36 private i18n: I18n 35 ) {
37 ) {
38 super() 36 super()
39 37
40 this.bytesPipe = new BytesPipe() 38 this.bytesPipe = new BytesPipe()
@@ -100,7 +98,7 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
100 this.redundanciesGraphsData.push({ 98 this.redundanciesGraphsData.push({
101 stats, 99 stats,
102 graphData: { 100 graphData: {
103 labels: [ this.i18n('Used'), this.i18n('Available') ], 101 labels: [ $localize`Used`, $localize`Available` ],
104 datasets: [ 102 datasets: [
105 { 103 {
106 data: [ stats.totalUsed, totalSize ], 104 data: [ stats.totalUsed, totalSize ],
@@ -139,14 +137,14 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
139 } 137 }
140 138
141 async removeRedundancy (redundancy: VideoRedundancy) { 139 async removeRedundancy (redundancy: VideoRedundancy) {
142 const message = this.i18n('Do you really want to remove this video redundancy?') 140 const message = $localize`Do you really want to remove this video redundancy?`
143 const res = await this.confirmService.confirm(message, this.i18n('Remove redundancy')) 141 const res = await this.confirmService.confirm(message, $localize`Remove redundancy`)
144 if (res === false) return 142 if (res === false) return
145 143
146 this.redundancyService.removeVideoRedundancies(redundancy) 144 this.redundancyService.removeVideoRedundancies(redundancy)
147 .subscribe( 145 .subscribe(
148 () => { 146 () => {
149 this.notifier.success(this.i18n('Video redundancies removed!')) 147 this.notifier.success($localize`Video redundancies removed!`)
150 this.loadData() 148 this.loadData()
151 }, 149 },
152 150
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
index 422d873c0..2b1ef663c 100644
--- a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
+++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
@@ -1,15 +1,14 @@
1import { SortMeta } from 'primeng/api' 1import { SortMeta } from 'primeng/api'
2import { filter, switchMap } from 'rxjs/operators' 2import { filter, switchMap } from 'rxjs/operators'
3import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
4import { environment } from 'src/environments/environment'
3import { AfterViewInit, Component, OnInit } from '@angular/core' 5import { AfterViewInit, Component, OnInit } from '@angular/core'
6import { DomSanitizer } from '@angular/platform-browser'
4import { ActivatedRoute, Params, Router } from '@angular/router' 7import { ActivatedRoute, Params, Router } from '@angular/router'
5import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable, ServerService } from '@app/core' 8import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
6import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' 9import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
7import { VideoBlockService } from '@app/shared/shared-moderation' 10import { VideoBlockService } from '@app/shared/shared-moderation'
8import { I18n } from '@ngx-translate/i18n-polyfill'
9import { VideoBlacklist, VideoBlacklistType } from '@shared/models' 11import { VideoBlacklist, VideoBlacklistType } from '@shared/models'
10import { buildVideoOrPlaylistEmbed, buildVideoLink } from 'src/assets/player/utils'
11import { environment } from 'src/environments/environment'
12import { DomSanitizer } from '@angular/platform-browser'
13 12
14@Component({ 13@Component({
15 selector: 'my-video-block-list', 14 selector: 'my-video-block-list',
@@ -34,26 +33,25 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV
34 private sanitizer: DomSanitizer, 33 private sanitizer: DomSanitizer,
35 private videoService: VideoService, 34 private videoService: VideoService,
36 private route: ActivatedRoute, 35 private route: ActivatedRoute,
37 private router: Router, 36 private router: Router
38 private i18n: I18n 37 ) {
39 ) {
40 super() 38 super()
41 39
42 this.videoBlocklistActions = [ 40 this.videoBlocklistActions = [
43 [ 41 [
44 { 42 {
45 label: this.i18n('Internal actions'), 43 label: $localize`Internal actions`,
46 isHeader: true, 44 isHeader: true,
47 isDisplayed: videoBlock => videoBlock.type === VideoBlacklistType.AUTO_BEFORE_PUBLISHED 45 isDisplayed: videoBlock => videoBlock.type === VideoBlacklistType.AUTO_BEFORE_PUBLISHED
48 }, 46 },
49 { 47 {
50 label: this.i18n('Switch video block to manual'), 48 label: $localize`Switch video block to manual`,
51 handler: videoBlock => { 49 handler: videoBlock => {
52 this.videoBlocklistService.unblockVideo(videoBlock.video.id).pipe( 50 this.videoBlocklistService.unblockVideo(videoBlock.video.id).pipe(
53 switchMap(_ => this.videoBlocklistService.blockVideo(videoBlock.video.id, undefined, true)) 51 switchMap(_ => this.videoBlocklistService.blockVideo(videoBlock.video.id, undefined, true))
54 ).subscribe( 52 ).subscribe(
55 () => { 53 () => {
56 this.notifier.success(this.i18n('Video {{name}} switched to manual block.', { name: videoBlock.video.name })) 54 this.notifier.success($localize`Video ${videoBlock.video.name} switched to manual block.`)
57 this.loadData() 55 this.loadData()
58 }, 56 },
59 57
@@ -65,27 +63,27 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV
65 ], 63 ],
66 [ 64 [
67 { 65 {
68 label: this.i18n('Actions for the video'), 66 label: $localize`Actions for the video`,
69 isHeader: true 67 isHeader: true
70 }, 68 },
71 { 69 {
72 label: this.i18n('Unblock'), 70 label: $localize`Unblock`,
73 handler: videoBlock => this.unblockVideo(videoBlock) 71 handler: videoBlock => this.unblockVideo(videoBlock)
74 }, 72 },
75 73
76 { 74 {
77 label: this.i18n('Delete'), 75 label: $localize`Delete`,
78 handler: async videoBlock => { 76 handler: async videoBlock => {
79 const res = await this.confirmService.confirm( 77 const res = await this.confirmService.confirm(
80 this.i18n('Do you really want to delete this video?'), 78 $localize`Do you really want to delete this video?`,
81 this.i18n('Delete') 79 $localize`Delete`
82 ) 80 )
83 if (res === false) return 81 if (res === false) return
84 82
85 this.videoService.removeVideo(videoBlock.video.id) 83 this.videoService.removeVideo(videoBlock.video.id)
86 .subscribe( 84 .subscribe(
87 () => { 85 () => {
88 this.notifier.success(this.i18n('Video deleted.')) 86 this.notifier.success($localize`Video deleted.`)
89 }, 87 },
90 88
91 err => this.notifier.error(err.message) 89 err => this.notifier.error(err.message)
@@ -148,9 +146,9 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV
148 } 146 }
149 147
150 booleanToText (value: boolean) { 148 booleanToText (value: boolean) {
151 if (value === true) return this.i18n('yes') 149 if (value === true) return $localize`yes`
152 150
153 return this.i18n('no') 151 return $localize`no`
154 } 152 }
155 153
156 toHtml (text: string) { 154 toHtml (text: string) {
@@ -158,16 +156,14 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV
158 } 156 }
159 157
160 async unblockVideo (entry: VideoBlacklist) { 158 async unblockVideo (entry: VideoBlacklist) {
161 const confirmMessage = this.i18n( 159 const confirmMessage = $localize`Do you really want to unblock this video? It will be available again in the videos list.`
162 'Do you really want to unblock this video? It will be available again in the videos list.'
163 )
164 160
165 const res = await this.confirmService.confirm(confirmMessage, this.i18n('Unblock')) 161 const res = await this.confirmService.confirm(confirmMessage, $localize`Unblock`)
166 if (res === false) return 162 if (res === false) return
167 163
168 this.videoBlocklistService.unblockVideo(entry.video.id).subscribe( 164 this.videoBlocklistService.unblockVideo(entry.video.id).subscribe(
169 () => { 165 () => {
170 this.notifier.success(this.i18n('Video {{name}} unblocked.', { name: entry.video.name })) 166 this.notifier.success($localize`Video ${entry.video.name} unblocked.`)
171 this.loadData() 167 this.loadData()
172 }, 168 },
173 169
diff --git a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts
index af31f1144..1ffd001c6 100644
--- a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts
+++ b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts
@@ -4,7 +4,6 @@ import { ActivatedRoute, Router } from '@angular/router'
4import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service' 4import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service'
5import { ComponentPagination, ConfirmService, hasMoreItems, Notifier } from '@app/core' 5import { ComponentPagination, ConfirmService, hasMoreItems, Notifier } from '@app/core'
6import { PluginService } from '@app/core/plugins/plugin.service' 6import { PluginService } from '@app/core/plugins/plugin.service'
7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { compareSemVer } from '@shared/core-utils/miscs/miscs' 7import { compareSemVer } from '@shared/core-utils/miscs/miscs'
9import { PeerTubePlugin } from '@shared/models/plugins/peertube-plugin.model' 8import { PeerTubePlugin } from '@shared/models/plugins/peertube-plugin.model'
10import { PluginType } from '@shared/models/plugins/plugin.type' 9import { PluginType } from '@shared/models/plugins/plugin.type'
@@ -37,7 +36,6 @@ export class PluginListInstalledComponent implements OnInit {
37 onDataSubject = new Subject<any[]>() 36 onDataSubject = new Subject<any[]>()
38 37
39 constructor ( 38 constructor (
40 private i18n: I18n,
41 private pluginService: PluginService, 39 private pluginService: PluginService,
42 private pluginApiService: PluginApiService, 40 private pluginApiService: PluginApiService,
43 private notifier: Notifier, 41 private notifier: Notifier,
@@ -88,10 +86,10 @@ export class PluginListInstalledComponent implements OnInit {
88 86
89 getNoResultMessage () { 87 getNoResultMessage () {
90 if (this.pluginType === PluginType.PLUGIN) { 88 if (this.pluginType === PluginType.PLUGIN) {
91 return this.i18n("You don't have plugins installed yet.") 89 return $localize`You don't have plugins installed yet.`
92 } 90 }
93 91
94 return this.i18n("You don't have themes installed yet.") 92 return $localize`You don't have themes installed yet.`
95 } 93 }
96 94
97 isUpdateAvailable (plugin: PeerTubePlugin) { 95 isUpdateAvailable (plugin: PeerTubePlugin) {
@@ -99,7 +97,7 @@ export class PluginListInstalledComponent implements OnInit {
99 } 97 }
100 98
101 getUpdateLabel (plugin: PeerTubePlugin) { 99 getUpdateLabel (plugin: PeerTubePlugin) {
102 return this.i18n('Update to {{version}}', { version: plugin.latestVersion }) 100 return $localize`Update to ${plugin.latestVersion}`
103 } 101 }
104 102
105 isUpdating (plugin: PeerTubePlugin) { 103 isUpdating (plugin: PeerTubePlugin) {
@@ -108,15 +106,15 @@ export class PluginListInstalledComponent implements OnInit {
108 106
109 async uninstall (plugin: PeerTubePlugin) { 107 async uninstall (plugin: PeerTubePlugin) {
110 const res = await this.confirmService.confirm( 108 const res = await this.confirmService.confirm(
111 this.i18n('Do you really want to uninstall {{pluginName}}?', { pluginName: plugin.name }), 109 $localize`Do you really want to uninstall ${plugin.name}?`,
112 this.i18n('Uninstall') 110 $localize`Uninstall`
113 ) 111 )
114 if (res === false) return 112 if (res === false) return
115 113
116 this.pluginApiService.uninstall(plugin.name, plugin.type) 114 this.pluginApiService.uninstall(plugin.name, plugin.type)
117 .subscribe( 115 .subscribe(
118 () => { 116 () => {
119 this.notifier.success(this.i18n('{{pluginName}} uninstalled.', { pluginName: plugin.name })) 117 this.notifier.success($localize`${plugin.name} uninstalled.`)
120 118
121 this.plugins = this.plugins.filter(p => p.name !== plugin.name) 119 this.plugins = this.plugins.filter(p => p.name !== plugin.name)
122 this.pagination.totalItems-- 120 this.pagination.totalItems--
@@ -138,7 +136,7 @@ export class PluginListInstalledComponent implements OnInit {
138 res => { 136 res => {
139 this.updating[updatingKey] = false 137 this.updating[updatingKey] = false
140 138
141 this.notifier.success(this.i18n('{{pluginName}} updated.', { pluginName: plugin.name })) 139 this.notifier.success($localize`${plugin.name} updated.`)
142 140
143 Object.assign(plugin, res) 141 Object.assign(plugin, res)
144 }, 142 },
diff --git a/client/src/app/+admin/plugins/plugin-search/plugin-search.component.ts b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.ts
index ccf9f1ed5..1a6b4eba3 100644
--- a/client/src/app/+admin/plugins/plugin-search/plugin-search.component.ts
+++ b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.ts
@@ -3,8 +3,7 @@ import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
3import { Component, OnInit } from '@angular/core' 3import { Component, OnInit } from '@angular/core'
4import { ActivatedRoute, Router } from '@angular/router' 4import { ActivatedRoute, Router } from '@angular/router'
5import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service' 5import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service'
6import { ComponentPagination, ConfirmService, hasMoreItems, Notifier, ServerService } from '@app/core' 6import { ComponentPagination, ConfirmService, hasMoreItems, Notifier } from '@app/core'
7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { PeerTubePluginIndex } from '@shared/models/plugins/peertube-plugin-index.model' 7import { PeerTubePluginIndex } from '@shared/models/plugins/peertube-plugin-index.model'
9import { PluginType } from '@shared/models/plugins/plugin.type' 8import { PluginType } from '@shared/models/plugins/plugin.type'
10 9
@@ -40,8 +39,6 @@ export class PluginSearchComponent implements OnInit {
40 private searchSubject = new Subject<string>() 39 private searchSubject = new Subject<string>()
41 40
42 constructor ( 41 constructor (
43 private server: ServerService,
44 private i18n: I18n,
45 private pluginService: PluginApiService, 42 private pluginService: PluginApiService,
46 private notifier: Notifier, 43 private notifier: Notifier,
47 private confirmService: ConfirmService, 44 private confirmService: ConfirmService,
@@ -100,7 +97,7 @@ export class PluginSearchComponent implements OnInit {
100 err => { 97 err => {
101 console.error(err) 98 console.error(err)
102 99
103 const message = this.i18n('The plugin index is not available. Please retry later.') 100 const message = $localize`The plugin index is not available. Please retry later.`
104 this.notifier.error(message) 101 this.notifier.error(message)
105 } 102 }
106 ) 103 )
@@ -122,8 +119,8 @@ export class PluginSearchComponent implements OnInit {
122 if (this.installing[plugin.npmName]) return 119 if (this.installing[plugin.npmName]) return
123 120
124 const res = await this.confirmService.confirm( 121 const res = await this.confirmService.confirm(
125 this.i18n('Please only install plugins or themes you trust, since they can execute any code on your instance.'), 122 $localize`Please only install plugins or themes you trust, since they can execute any code on your instance.`,
126 this.i18n('Install {{pluginName}}?', { pluginName: plugin.name }) 123 $localize`Install ${plugin.name}?`
127 ) 124 )
128 if (res === false) return 125 if (res === false) return
129 126
@@ -135,7 +132,7 @@ export class PluginSearchComponent implements OnInit {
135 this.installing[plugin.npmName] = false 132 this.installing[plugin.npmName] = false
136 this.pluginInstalled = true 133 this.pluginInstalled = true
137 134
138 this.notifier.success(this.i18n('{{pluginName}} installed.', { pluginName: plugin.name })) 135 this.notifier.success($localize`${plugin.name} installed.`)
139 136
140 plugin.installed = true 137 plugin.installed = true
141 }, 138 },
diff --git a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts
index dde03f1da..a33f01691 100644
--- a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts
+++ b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts
@@ -4,7 +4,6 @@ import { Component, OnDestroy, OnInit } from '@angular/core'
4import { ActivatedRoute } from '@angular/router' 4import { ActivatedRoute } from '@angular/router'
5import { Notifier } from '@app/core' 5import { Notifier } from '@app/core'
6import { BuildFormArgument, FormReactive, FormValidatorService } from '@app/shared/shared-forms' 6import { BuildFormArgument, FormReactive, FormValidatorService } from '@app/shared/shared-forms'
7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { PeerTubePlugin, RegisterServerSettingOptions } from '@shared/models' 7import { PeerTubePlugin, RegisterServerSettingOptions } from '@shared/models'
9import { PluginApiService } from '../shared/plugin-api.service' 8import { PluginApiService } from '../shared/plugin-api.service'
10 9
@@ -22,7 +21,6 @@ export class PluginShowInstalledComponent extends FormReactive implements OnInit
22 21
23 constructor ( 22 constructor (
24 protected formValidatorService: FormValidatorService, 23 protected formValidatorService: FormValidatorService,
25 private i18n: I18n,
26 private pluginService: PluginApiService, 24 private pluginService: PluginApiService,
27 private notifier: Notifier, 25 private notifier: Notifier,
28 private route: ActivatedRoute 26 private route: ActivatedRoute
@@ -50,7 +48,7 @@ export class PluginShowInstalledComponent extends FormReactive implements OnInit
50 this.pluginService.updatePluginSettings(this.plugin.name, this.plugin.type, settings) 48 this.pluginService.updatePluginSettings(this.plugin.name, this.plugin.type, settings)
51 .subscribe( 49 .subscribe(
52 () => { 50 () => {
53 this.notifier.success(this.i18n('Settings updated.')) 51 this.notifier.success($localize`Settings updated.`)
54 }, 52 },
55 53
56 err => this.notifier.error(err.message) 54 err => this.notifier.error(err.message)
diff --git a/client/src/app/+admin/plugins/shared/plugin-api.service.ts b/client/src/app/+admin/plugins/shared/plugin-api.service.ts
index 1fb827832..b28d46df4 100644
--- a/client/src/app/+admin/plugins/shared/plugin-api.service.ts
+++ b/client/src/app/+admin/plugins/shared/plugin-api.service.ts
@@ -4,7 +4,6 @@ import { HttpClient, HttpParams } from '@angular/common/http'
4import { Injectable } from '@angular/core' 4import { Injectable } from '@angular/core'
5import { ComponentPagination, RestExtractor, RestService } from '@app/core' 5import { ComponentPagination, RestExtractor, RestService } from '@app/core'
6import { PluginService } from '@app/core/plugins/plugin.service' 6import { PluginService } from '@app/core/plugins/plugin.service'
7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { peertubeTranslate } from '@shared/core-utils/i18n' 7import { peertubeTranslate } from '@shared/core-utils/i18n'
9import { 8import {
10 InstallOrUpdatePlugin, 9 InstallOrUpdatePlugin,
@@ -25,18 +24,17 @@ export class PluginApiService {
25 private authHttp: HttpClient, 24 private authHttp: HttpClient,
26 private restExtractor: RestExtractor, 25 private restExtractor: RestExtractor,
27 private restService: RestService, 26 private restService: RestService,
28 private i18n: I18n,
29 private pluginService: PluginService 27 private pluginService: PluginService
30 ) { } 28 ) { }
31 29
32 getPluginTypeOptions () { 30 getPluginTypeOptions () {
33 return [ 31 return [
34 { 32 {
35 label: this.i18n('Plugins'), 33 label: $localize`Plugins`,
36 value: PluginType.PLUGIN 34 value: PluginType.PLUGIN
37 }, 35 },
38 { 36 {
39 label: this.i18n('Themes'), 37 label: $localize`Themes`,
40 value: PluginType.THEME 38 value: PluginType.THEME
41 } 39 }
42 ] 40 ]
@@ -44,10 +42,10 @@ export class PluginApiService {
44 42
45 getPluginTypeLabel (type: PluginType) { 43 getPluginTypeLabel (type: PluginType) {
46 if (type === PluginType.PLUGIN) { 44 if (type === PluginType.PLUGIN) {
47 return this.i18n('plugin') 45 return $localize`plugin`
48 } 46 }
49 47
50 return this.i18n('theme') 48 return $localize`theme`
51 } 49 }
52 50
53 getPlugins ( 51 getPlugins (
diff --git a/client/src/app/+admin/system/jobs/jobs.component.ts b/client/src/app/+admin/system/jobs/jobs.component.ts
index ceb848976..96e0f25b0 100644
--- a/client/src/app/+admin/system/jobs/jobs.component.ts
+++ b/client/src/app/+admin/system/jobs/jobs.component.ts
@@ -2,7 +2,6 @@ import { SortMeta } from 'primeng/api'
2import { Component, OnInit } from '@angular/core' 2import { Component, OnInit } from '@angular/core'
3import { Notifier, RestPagination, RestTable } from '@app/core' 3import { Notifier, RestPagination, RestTable } from '@app/core'
4import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' 4import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { Job, JobState, JobType } from '@shared/models' 5import { Job, JobState, JobType } from '@shared/models'
7import { JobStateClient } from '../../../../types/job-state-client.type' 6import { JobStateClient } from '../../../../types/job-state-client.type'
8import { JobTypeClient } from '../../../../types/job-type-client.type' 7import { JobTypeClient } from '../../../../types/job-type-client.type'
@@ -43,9 +42,8 @@ export class JobsComponent extends RestTable implements OnInit {
43 42
44 constructor ( 43 constructor (
45 private notifier: Notifier, 44 private notifier: Notifier,
46 private jobsService: JobService, 45 private jobsService: JobService
47 private i18n: I18n 46 ) {
48 ) {
49 super() 47 super()
50 } 48 }
51 49
diff --git a/client/src/app/+admin/system/logs/logs.component.ts b/client/src/app/+admin/system/logs/logs.component.ts
index 51f047188..c9c9dc3d1 100644
--- a/client/src/app/+admin/system/logs/logs.component.ts
+++ b/client/src/app/+admin/system/logs/logs.component.ts
@@ -1,6 +1,5 @@
1import { Component, ElementRef, OnInit, ViewChild } from '@angular/core' 1import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'
2import { Notifier } from '@app/core' 2import { Notifier } from '@app/core'
3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { LogLevel } from '@shared/models' 3import { LogLevel } from '@shared/models'
5import { LogRow } from './log-row.model' 4import { LogRow } from './log-row.model'
6import { LogsService } from './logs.service' 5import { LogsService } from './logs.service'
@@ -25,9 +24,8 @@ export class LogsComponent implements OnInit {
25 24
26 constructor ( 25 constructor (
27 private logsService: LogsService, 26 private logsService: LogsService,
28 private notifier: Notifier, 27 private notifier: Notifier
29 private i18n: I18n 28 ) { }
30 ) { }
31 29
32 ngOnInit (): void { 30 ngOnInit (): void {
33 this.buildTimeChoices() 31 this.buildTimeChoices()
@@ -78,15 +76,15 @@ export class LogsComponent implements OnInit {
78 this.timeChoices = [ 76 this.timeChoices = [
79 { 77 {
80 id: lastWeek.toISOString(), 78 id: lastWeek.toISOString(),
81 label: this.i18n('Last week') 79 label: $localize`Last week`
82 }, 80 },
83 { 81 {
84 id: lastDay.toISOString(), 82 id: lastDay.toISOString(),
85 label: this.i18n('Last day') 83 label: $localize`Last day`
86 }, 84 },
87 { 85 {
88 id: lastHour.toISOString(), 86 id: lastHour.toISOString(),
89 label: this.i18n('Last hour') 87 label: $localize`Last hour`
90 } 88 }
91 ] 89 ]
92 90
@@ -97,19 +95,19 @@ export class LogsComponent implements OnInit {
97 this.levelChoices = [ 95 this.levelChoices = [
98 { 96 {
99 id: 'debug', 97 id: 'debug',
100 label: this.i18n('Debug') 98 label: $localize`Debug`
101 }, 99 },
102 { 100 {
103 id: 'info', 101 id: 'info',
104 label: this.i18n('Info') 102 label: $localize`Info`
105 }, 103 },
106 { 104 {
107 id: 'warn', 105 id: 'warn',
108 label: this.i18n('Warning') 106 label: $localize`Warning`
109 }, 107 },
110 { 108 {
111 id: 'error', 109 id: 'error',
112 label: this.i18n('Error') 110 label: $localize`Error`
113 } 111 }
114 ] 112 ]
115 113
@@ -120,11 +118,11 @@ export class LogsComponent implements OnInit {
120 this.logTypeChoices = [ 118 this.logTypeChoices = [
121 { 119 {
122 id: 'standard', 120 id: 'standard',
123 label: this.i18n('Standard logs') 121 label: $localize`Standard logs`
124 }, 122 },
125 { 123 {
126 id: 'audit', 124 id: 'audit',
127 label: this.i18n('Audit logs') 125 label: $localize`Audit logs`
128 } 126 }
129 ] 127 ]
130 128
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 3fddb9c09..36d71a927 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
@@ -3,7 +3,6 @@ import { ActivatedRoute, Router } from '@angular/router'
3import { ConfigService } from '@app/+admin/config/shared/config.service' 3import { ConfigService } from '@app/+admin/config/shared/config.service'
4import { AuthService, Notifier, ScreenService, ServerService, UserService } from '@app/core' 4import { AuthService, Notifier, ScreenService, ServerService, UserService } from '@app/core'
5import { FormValidatorService, UserValidatorsService } from '@app/shared/shared-forms' 5import { FormValidatorService, UserValidatorsService } from '@app/shared/shared-forms'
6import { I18n } from '@ngx-translate/i18n-polyfill'
7import { UserCreate, UserRole } from '@shared/models' 6import { UserCreate, UserRole } from '@shared/models'
8import { UserEdit } from './user-edit' 7import { UserEdit } from './user-edit'
9 8
@@ -25,9 +24,8 @@ export class UserCreateComponent extends UserEdit implements OnInit {
25 private route: ActivatedRoute, 24 private route: ActivatedRoute,
26 private router: Router, 25 private router: Router,
27 private notifier: Notifier, 26 private notifier: Notifier,
28 private userService: UserService, 27 private userService: UserService
29 private i18n: I18n 28 ) {
30 ) {
31 super() 29 super()
32 30
33 this.buildQuotaOptions() 31 this.buildQuotaOptions()
@@ -67,7 +65,7 @@ export class UserCreateComponent extends UserEdit implements OnInit {
67 65
68 this.userService.addUser(userCreate).subscribe( 66 this.userService.addUser(userCreate).subscribe(
69 () => { 67 () => {
70 this.notifier.success(this.i18n('User {{username}} created.', { username: userCreate.username })) 68 this.notifier.success($localize`User ${userCreate.username} created.`)
71 this.router.navigate([ '/admin/users/list' ]) 69 this.router.navigate([ '/admin/users/list' ])
72 }, 70 },
73 71
@@ -85,6 +83,6 @@ export class UserCreateComponent extends UserEdit implements OnInit {
85 } 83 }
86 84
87 getFormButtonTitle () { 85 getFormButtonTitle () {
88 return this.i18n('Create user') 86 return $localize`Create user`
89 } 87 }
90} 88}
diff --git a/client/src/app/+admin/users/user-edit/user-password.component.ts b/client/src/app/+admin/users/user-edit/user-password.component.ts
index 33c7de31f..25f13495a 100644
--- a/client/src/app/+admin/users/user-edit/user-password.component.ts
+++ b/client/src/app/+admin/users/user-edit/user-password.component.ts
@@ -1,7 +1,6 @@
1import { Component, Input, OnInit } from '@angular/core' 1import { Component, Input, OnInit } from '@angular/core'
2import { Notifier, UserService } from '@app/core' 2import { Notifier, UserService } from '@app/core'
3import { FormReactive, FormValidatorService, UserValidatorsService } from '@app/shared/shared-forms' 3import { FormReactive, FormValidatorService, UserValidatorsService } from '@app/shared/shared-forms'
4import { I18n } from '@ngx-translate/i18n-polyfill'
5import { UserUpdate } from '@shared/models' 4import { UserUpdate } from '@shared/models'
6 5
7@Component({ 6@Component({
@@ -20,9 +19,8 @@ export class UserPasswordComponent extends FormReactive implements OnInit {
20 protected formValidatorService: FormValidatorService, 19 protected formValidatorService: FormValidatorService,
21 private userValidatorsService: UserValidatorsService, 20 private userValidatorsService: UserValidatorsService,
22 private notifier: Notifier, 21 private notifier: Notifier,
23 private userService: UserService, 22 private userService: UserService
24 private i18n: I18n 23 ) {
25 ) {
26 super() 24 super()
27 } 25 }
28 26
@@ -39,9 +37,7 @@ export class UserPasswordComponent extends FormReactive implements OnInit {
39 37
40 this.userService.updateUser(this.userId, userUpdate).subscribe( 38 this.userService.updateUser(this.userId, userUpdate).subscribe(
41 () => { 39 () => {
42 this.notifier.success( 40 this.notifier.success($localize`Password changed for user ${this.username}.`)
43 this.i18n('Password changed for user {{username}}.', { username: this.username })
44 )
45 }, 41 },
46 42
47 err => this.error = err.message 43 err => this.error = err.message
@@ -53,6 +49,6 @@ export class UserPasswordComponent extends FormReactive implements OnInit {
53 } 49 }
54 50
55 getFormButtonTitle () { 51 getFormButtonTitle () {
56 return this.i18n('Update user password') 52 return $localize`Update user password`
57 } 53 }
58} 54}
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 870880fee..55bc7290e 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
@@ -4,7 +4,6 @@ import { ActivatedRoute, Router } from '@angular/router'
4import { ConfigService } from '@app/+admin/config/shared/config.service' 4import { ConfigService } from '@app/+admin/config/shared/config.service'
5import { AuthService, Notifier, ScreenService, ServerService, User, UserService } from '@app/core' 5import { AuthService, Notifier, ScreenService, ServerService, User, UserService } from '@app/core'
6import { FormValidatorService, UserValidatorsService } from '@app/shared/shared-forms' 6import { FormValidatorService, UserValidatorsService } from '@app/shared/shared-forms'
7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { User as UserType, UserAdminFlag, UserRole, UserUpdate } from '@shared/models' 7import { User as UserType, UserAdminFlag, UserRole, UserUpdate } from '@shared/models'
9import { UserEdit } from './user-edit' 8import { UserEdit } from './user-edit'
10 9
@@ -28,9 +27,8 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
28 private route: ActivatedRoute, 27 private route: ActivatedRoute,
29 private router: Router, 28 private router: Router,
30 private notifier: Notifier, 29 private notifier: Notifier,
31 private userService: UserService, 30 private userService: UserService
32 private i18n: I18n 31 ) {
33 ) {
34 super() 32 super()
35 33
36 this.buildQuotaOptions() 34 this.buildQuotaOptions()
@@ -79,7 +77,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
79 77
80 this.userService.updateUser(this.user.id, userUpdate).subscribe( 78 this.userService.updateUser(this.user.id, userUpdate).subscribe(
81 () => { 79 () => {
82 this.notifier.success(this.i18n('User {{username}} updated.', { username: this.user.username })) 80 this.notifier.success($localize`User ${this.user.username} updated.`)
83 this.router.navigate([ '/admin/users/list' ]) 81 this.router.navigate([ '/admin/users/list' ])
84 }, 82 },
85 83
@@ -96,15 +94,13 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
96 } 94 }
97 95
98 getFormButtonTitle () { 96 getFormButtonTitle () {
99 return this.i18n('Update user') 97 return $localize`Update user`
100 } 98 }
101 99
102 resetPassword () { 100 resetPassword () {
103 this.userService.askResetPassword(this.user.email).subscribe( 101 this.userService.askResetPassword(this.user.email).subscribe(
104 () => { 102 () => {
105 this.notifier.success( 103 this.notifier.success($localize`An email asking for password reset has been sent to ${this.user.username}.`)
106 this.i18n('An email asking for password reset has been sent to {{username}}.', { username: this.user.username })
107 )
108 }, 104 },
109 105
110 err => this.error = err.message 106 err => this.error = err.message
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 69d4e917d..86812f73d 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 { SortMeta } from 'primeng/api' 1import { SortMeta } from 'primeng/api'
2import { Component, OnInit, ViewChild } from '@angular/core' 2import { Component, OnInit, ViewChild } from '@angular/core'
3import { ActivatedRoute, Params, Router } from '@angular/router'
3import { AuthService, ConfirmService, Notifier, RestPagination, RestTable, ServerService, UserService } from '@app/core' 4import { AuthService, ConfirmService, Notifier, RestPagination, RestTable, ServerService, UserService } from '@app/core'
4import { Actor, DropdownAction } from '@app/shared/shared-main' 5import { Actor, DropdownAction } from '@app/shared/shared-main'
5import { UserBanModalComponent } from '@app/shared/shared-moderation' 6import { UserBanModalComponent } from '@app/shared/shared-moderation'
6import { I18n } from '@ngx-translate/i18n-polyfill'
7import { ServerConfig, User, UserRole } from '@shared/models' 7import { ServerConfig, User, UserRole } from '@shared/models'
8import { Params, Router, ActivatedRoute } from '@angular/router'
9 8
10type UserForList = User & { 9type UserForList = User & {
11 rawVideoQuota: number 10 rawVideoQuota: number
@@ -42,9 +41,8 @@ export class UserListComponent extends RestTable implements OnInit {
42 private userService: UserService, 41 private userService: UserService,
43 private auth: AuthService, 42 private auth: AuthService,
44 private route: ActivatedRoute, 43 private route: ActivatedRoute,
45 private router: Router, 44 private router: Router
46 private i18n: I18n 45 ) {
47 ) {
48 super() 46 super()
49 } 47 }
50 48
@@ -82,26 +80,26 @@ export class UserListComponent extends RestTable implements OnInit {
82 this.bulkUserActions = [ 80 this.bulkUserActions = [
83 [ 81 [
84 { 82 {
85 label: this.i18n('Delete'), 83 label: $localize`Delete`,
86 description: this.i18n('Videos will be deleted, comments will be tombstoned.'), 84 description: $localize`Videos will be deleted, comments will be tombstoned.`,
87 handler: users => this.removeUsers(users), 85 handler: users => this.removeUsers(users),
88 isDisplayed: users => users.every(u => this.authUser.canManage(u)) 86 isDisplayed: users => users.every(u => this.authUser.canManage(u))
89 }, 87 },
90 { 88 {
91 label: this.i18n('Ban'), 89 label: $localize`Ban`,
92 description: this.i18n('User won\'t be able to login anymore, but videos and comments will be kept as is.'), 90 description: $localize`User won't be able to login anymore, but videos and comments will be kept as is.`,
93 handler: users => this.openBanUserModal(users), 91 handler: users => this.openBanUserModal(users),
94 isDisplayed: users => users.every(u => this.authUser.canManage(u) && u.blocked === false) 92 isDisplayed: users => users.every(u => this.authUser.canManage(u) && u.blocked === false)
95 }, 93 },
96 { 94 {
97 label: this.i18n('Unban'), 95 label: $localize`Unban`,
98 handler: users => this.unbanUsers(users), 96 handler: users => this.unbanUsers(users),
99 isDisplayed: users => users.every(u => this.authUser.canManage(u) && u.blocked === true) 97 isDisplayed: users => users.every(u => this.authUser.canManage(u) && u.blocked === true)
100 } 98 }
101 ], 99 ],
102 [ 100 [
103 { 101 {
104 label: this.i18n('Set Email as Verified'), 102 label: $localize`Set Email as Verified`,
105 handler: users => this.setEmailsAsVerified(users), 103 handler: users => this.setEmailsAsVerified(users),
106 isDisplayed: users => { 104 isDisplayed: users => {
107 return this.requiresEmailVerification && 105 return this.requiresEmailVerification &&
@@ -160,7 +158,7 @@ export class UserListComponent extends RestTable implements OnInit {
160 openBanUserModal (users: User[]) { 158 openBanUserModal (users: User[]) {
161 for (const user of users) { 159 for (const user of users) {
162 if (user.username === 'root') { 160 if (user.username === 'root') {
163 this.notifier.error(this.i18n('You cannot ban root.')) 161 this.notifier.error($localize`You cannot ban root.`)
164 return 162 return
165 } 163 }
166 } 164 }
@@ -197,17 +195,13 @@ export class UserListComponent extends RestTable implements OnInit {
197 } 195 }
198 196
199 async unbanUsers (users: User[]) { 197 async unbanUsers (users: User[]) {
200 const message = this.i18n('Do you really want to unban {{num}} users?', { num: users.length }) 198 const res = await this.confirmService.confirm($localize`Do you really want to unban ${users.length} users?`, $localize`Unban`)
201
202 const res = await this.confirmService.confirm(message, this.i18n('Unban'))
203 if (res === false) return 199 if (res === false) return
204 200
205 this.userService.unbanUsers(users) 201 this.userService.unbanUsers(users)
206 .subscribe( 202 .subscribe(
207 () => { 203 () => {
208 const message = this.i18n('{{num}} users unbanned.', { num: users.length }) 204 this.notifier.success($localize`${users.length} users unbanned.`)
209
210 this.notifier.success(message)
211 this.loadData() 205 this.loadData()
212 }, 206 },
213 207
@@ -218,18 +212,18 @@ export class UserListComponent extends RestTable implements OnInit {
218 async removeUsers (users: User[]) { 212 async removeUsers (users: User[]) {
219 for (const user of users) { 213 for (const user of users) {
220 if (user.username === 'root') { 214 if (user.username === 'root') {
221 this.notifier.error(this.i18n('You cannot delete root.')) 215 this.notifier.error($localize`You cannot delete root.`)
222 return 216 return
223 } 217 }
224 } 218 }
225 219
226 const message = this.i18n('If you remove these users, you will not be able to create others with the same username!') 220 const message = $localize`If you remove these users, you will not be able to create others with the same username!`
227 const res = await this.confirmService.confirm(message, this.i18n('Delete')) 221 const res = await this.confirmService.confirm(message, $localize`Delete`)
228 if (res === false) return 222 if (res === false) return
229 223
230 this.userService.removeUser(users).subscribe( 224 this.userService.removeUser(users).subscribe(
231 () => { 225 () => {
232 this.notifier.success(this.i18n('{{num}} users deleted.', { num: users.length })) 226 this.notifier.success($localize`${users.length} users deleted.`)
233 this.loadData() 227 this.loadData()
234 }, 228 },
235 229
@@ -240,7 +234,7 @@ export class UserListComponent extends RestTable implements OnInit {
240 async setEmailsAsVerified (users: User[]) { 234 async setEmailsAsVerified (users: User[]) {
241 this.userService.updateUsers(users, { emailVerified: true }).subscribe( 235 this.userService.updateUsers(users, { emailVerified: true }).subscribe(
242 () => { 236 () => {
243 this.notifier.success(this.i18n('{{num}} users email set as verified.', { num: users.length })) 237 this.notifier.success($localize`${users.length} users email set as verified.`)
244 this.loadData() 238 this.loadData()
245 }, 239 },
246 240
diff --git a/client/src/app/+login/login.component.ts b/client/src/app/+login/login.component.ts
index e5a382218..e9336172e 100644
--- a/client/src/app/+login/login.component.ts
+++ b/client/src/app/+login/login.component.ts
@@ -5,7 +5,6 @@ import { AuthService, Notifier, RedirectService, UserService } from '@app/core'
5import { HooksService } from '@app/core/plugins/hooks.service' 5import { HooksService } from '@app/core/plugins/hooks.service'
6import { FormReactive, FormValidatorService, LoginValidatorsService } from '@app/shared/shared-forms' 6import { FormReactive, FormValidatorService, LoginValidatorsService } from '@app/shared/shared-forms'
7import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' 7import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
8import { I18n } from '@ngx-translate/i18n-polyfill'
9import { RegisteredExternalAuthConfig, ServerConfig } from '@shared/models' 8import { RegisteredExternalAuthConfig, ServerConfig } from '@shared/models'
10 9
11@Component({ 10@Component({
@@ -37,9 +36,8 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
37 private userService: UserService, 36 private userService: UserService,
38 private redirectService: RedirectService, 37 private redirectService: RedirectService,
39 private notifier: Notifier, 38 private notifier: Notifier,
40 private hooks: HooksService, 39 private hooks: HooksService
41 private i18n: I18n 40 ) {
42 ) {
43 super() 41 super()
44 } 42 }
45 43
@@ -105,10 +103,9 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
105 this.userService.askResetPassword(this.forgotPasswordEmail) 103 this.userService.askResetPassword(this.forgotPasswordEmail)
106 .subscribe( 104 .subscribe(
107 () => { 105 () => {
108 const message = this.i18n( 106 const message = $localize`An email with the reset password instructions will be sent to ${this.forgotPasswordEmail}.
109 'An email with the reset password instructions will be sent to {{email}}. The link will expire within 1 hour.', 107The link will expire within 1 hour.`
110 { email: this.forgotPasswordEmail } 108
111 )
112 this.notifier.success(message) 109 this.notifier.success(message)
113 this.hideForgotPasswordModal() 110 this.hideForgotPasswordModal()
114 }, 111 },
@@ -140,8 +137,8 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
140 } 137 }
141 138
142 private handleError (err: any) { 139 private handleError (err: any) {
143 if (err.message.indexOf('credentials are invalid') !== -1) this.error = this.i18n('Incorrect username or password.') 140 if (err.message.indexOf('credentials are invalid') !== -1) this.error = $localize`Incorrect username or password.`
144 else if (err.message.indexOf('blocked') !== -1) this.error = this.i18n('Your account is blocked.') 141 else if (err.message.indexOf('blocked') !== -1) this.error = $localize`Your account is blocked.`
145 else this.error = err.message 142 else this.error = err.message
146 } 143 }
147} 144}
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 039c389e4..5c438c3bf 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
@@ -3,7 +3,6 @@ import { Router } from '@angular/router'
3import { AuthService, Notifier } from '@app/core' 3import { AuthService, Notifier } from '@app/core'
4import { FormValidatorService, VideoChannelValidatorsService } from '@app/shared/shared-forms' 4import { FormValidatorService, VideoChannelValidatorsService } from '@app/shared/shared-forms'
5import { VideoChannelService } from '@app/shared/shared-main' 5import { VideoChannelService } from '@app/shared/shared-main'
6import { I18n } from '@ngx-translate/i18n-polyfill'
7import { VideoChannelCreate } from '@shared/models' 6import { VideoChannelCreate } from '@shared/models'
8import { MyAccountVideoChannelEdit } from './my-account-video-channel-edit' 7import { MyAccountVideoChannelEdit } from './my-account-video-channel-edit'
9 8
@@ -21,9 +20,8 @@ export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelE
21 private videoChannelValidatorsService: VideoChannelValidatorsService, 20 private videoChannelValidatorsService: VideoChannelValidatorsService,
22 private notifier: Notifier, 21 private notifier: Notifier,
23 private router: Router, 22 private router: Router,
24 private videoChannelService: VideoChannelService, 23 private videoChannelService: VideoChannelService
25 private i18n: I18n 24 ) {
26 ) {
27 super() 25 super()
28 } 26 }
29 27
@@ -55,15 +53,13 @@ export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelE
55 () => { 53 () => {
56 this.authService.refreshUserInformation() 54 this.authService.refreshUserInformation()
57 55
58 this.notifier.success( 56 this.notifier.success($localize`Video channel ${videoChannelCreate.displayName} created.`)
59 this.i18n('Video channel {{videoChannelName}} created.', { videoChannelName: videoChannelCreate.displayName })
60 )
61 this.router.navigate([ '/my-account', 'video-channels' ]) 57 this.router.navigate([ '/my-account', 'video-channels' ])
62 }, 58 },
63 59
64 err => { 60 err => {
65 if (err.status === 409) { 61 if (err.status === 409) {
66 this.error = this.i18n('This name already exists on this instance.') 62 this.error = $localize`This name already exists on this instance.`
67 return 63 return
68 } 64 }
69 65
@@ -77,6 +73,6 @@ export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelE
77 } 73 }
78 74
79 getFormButtonTitle () { 75 getFormButtonTitle () {
80 return this.i18n('Create') 76 return $localize`Create`
81 } 77 }
82} 78}
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 489c437ea..485521dcc 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
@@ -4,7 +4,6 @@ import { ActivatedRoute, Router } from '@angular/router'
4import { AuthService, Notifier, ServerService } from '@app/core' 4import { AuthService, Notifier, ServerService } from '@app/core'
5import { FormValidatorService, VideoChannelValidatorsService } from '@app/shared/shared-forms' 5import { FormValidatorService, VideoChannelValidatorsService } from '@app/shared/shared-forms'
6import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' 6import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { ServerConfig, VideoChannelUpdate } from '@shared/models' 7import { ServerConfig, VideoChannelUpdate } from '@shared/models'
9import { MyAccountVideoChannelEdit } from './my-account-video-channel-edit' 8import { MyAccountVideoChannelEdit } from './my-account-video-channel-edit'
10 9
@@ -29,7 +28,6 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE
29 private router: Router, 28 private router: Router,
30 private route: ActivatedRoute, 29 private route: ActivatedRoute,
31 private videoChannelService: VideoChannelService, 30 private videoChannelService: VideoChannelService,
32 private i18n: I18n,
33 private serverService: ServerService 31 private serverService: ServerService
34 ) { 32 ) {
35 super() 33 super()
@@ -87,9 +85,7 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE
87 () => { 85 () => {
88 this.authService.refreshUserInformation() 86 this.authService.refreshUserInformation()
89 87
90 this.notifier.success( 88 this.notifier.success($localize`Video channel ${videoChannelUpdate.displayName} updated.`)
91 this.i18n('Video channel {{videoChannelName}} updated.', { videoChannelName: videoChannelUpdate.displayName })
92 )
93 89
94 this.router.navigate([ '/my-account', 'video-channels' ]) 90 this.router.navigate([ '/my-account', 'video-channels' ])
95 }, 91 },
@@ -102,7 +98,7 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE
102 this.videoChannelService.changeVideoChannelAvatar(this.videoChannelToUpdate.name, formData) 98 this.videoChannelService.changeVideoChannelAvatar(this.videoChannelToUpdate.name, formData)
103 .subscribe( 99 .subscribe(
104 data => { 100 data => {
105 this.notifier.success(this.i18n('Avatar changed.')) 101 this.notifier.success($localize`Avatar changed.`)
106 102
107 this.videoChannelToUpdate.updateAvatar(data.avatar) 103 this.videoChannelToUpdate.updateAvatar(data.avatar)
108 }, 104 },
@@ -124,7 +120,7 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE
124 } 120 }
125 121
126 getFormButtonTitle () { 122 getFormButtonTitle () {
127 return this.i18n('Update') 123 return $localize`Update`
128 } 124 }
129 125
130 isBulkUpdateVideosDisplayed () { 126 isBulkUpdateVideosDisplayed () {
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 ad9368794..2aff09cd9 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
@@ -1,11 +1,10 @@
1import { ChartData } from 'chart.js' 1import { ChartData } from 'chart.js'
2import { max, maxBy, min, minBy } from 'lodash-es' 2import { max, maxBy, min, minBy } from 'lodash-es'
3import { flatMap, debounceTime } from 'rxjs/operators' 3import { Subject } from 'rxjs'
4import { debounceTime, mergeMap } from 'rxjs/operators'
4import { Component, OnInit } from '@angular/core' 5import { Component, OnInit } from '@angular/core'
5import { AuthService, ConfirmService, Notifier, ScreenService, User } from '@app/core' 6import { AuthService, ConfirmService, Notifier, ScreenService, User } from '@app/core'
6import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' 7import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { Subject } from 'rxjs'
9 8
10@Component({ 9@Component({
11 selector: 'my-account-video-channels', 10 selector: 'my-account-video-channels',
@@ -30,9 +29,8 @@ export class MyAccountVideoChannelsComponent implements OnInit {
30 private notifier: Notifier, 29 private notifier: Notifier,
31 private confirmService: ConfirmService, 30 private confirmService: ConfirmService,
32 private videoChannelService: VideoChannelService, 31 private videoChannelService: VideoChannelService,
33 private screenService: ScreenService, 32 private screenService: ScreenService
34 private i18n: I18n 33 ) {}
35 ) {}
36 34
37 ngOnInit () { 35 ngOnInit () {
38 this.user = this.authService.getUser() 36 this.user = this.authService.getUser()
@@ -110,17 +108,13 @@ export class MyAccountVideoChannelsComponent implements OnInit {
110 108
111 async deleteVideoChannel (videoChannel: VideoChannel) { 109 async deleteVideoChannel (videoChannel: VideoChannel) {
112 const res = await this.confirmService.confirmWithInput( 110 const res = await this.confirmService.confirmWithInput(
113 this.i18n( 111 $localize`Do you really want to delete ${videoChannel.displayName}?
114 // tslint:disable 112It will delete ${videoChannel.videosCount} videos uploaded in this channel, and you will not be able to create another
115 'Do you really want to delete {{channelDisplayName}}? It will delete {{videosCount}} videos uploaded in this channel, and you will not be able to create another channel with the same name ({{channelName}})!', 113channel with the same name (${videoChannel.name})!`,
116 { channelDisplayName: videoChannel.displayName, videosCount: videoChannel.videosCount, channelName: videoChannel.name } 114
117 ), 115 $localize`Please type the display name of the video channel (${videoChannel.displayName}) to confirm`,
118 this.i18n( 116
119 'Please type the display name of the video channel ({{displayName}}) to confirm', 117 $localize`Delete`
120 { displayName: videoChannel.displayName }
121 ),
122 videoChannel.displayName,
123 this.i18n('Delete')
124 ) 118 )
125 if (res === false) return 119 if (res === false) return
126 120
@@ -128,9 +122,7 @@ export class MyAccountVideoChannelsComponent implements OnInit {
128 .subscribe( 122 .subscribe(
129 () => { 123 () => {
130 this.loadVideoChannels() 124 this.loadVideoChannels()
131 this.notifier.success( 125 this.notifier.success($localize`Video channel ${videoChannel.displayName} deleted.`)
132 this.i18n('Video channel {{videoChannelName}} deleted.', { videoChannelName: videoChannel.displayName })
133 )
134 }, 126 },
135 127
136 error => this.notifier.error(error.message) 128 error => this.notifier.error(error.message)
@@ -139,7 +131,7 @@ export class MyAccountVideoChannelsComponent implements OnInit {
139 131
140 private loadVideoChannels () { 132 private loadVideoChannels () {
141 this.authService.userInformationLoaded 133 this.authService.userInformationLoaded
142 .pipe(flatMap(() => this.videoChannelService.listAccountVideoChannels(this.user.account, null, true, this.channelsSearch))) 134 .pipe(mergeMap(() => this.videoChannelService.listAccountVideoChannels(this.user.account, null, true, this.channelsSearch)))
143 .subscribe(res => { 135 .subscribe(res => {
144 this.videoChannels = res.data 136 this.videoChannels = res.data
145 this.totalItems = res.total 137 this.totalItems = res.total
@@ -149,10 +141,10 @@ export class MyAccountVideoChannelsComponent implements OnInit {
149 labels: v.viewsPerDay.map(day => day.date.toLocaleDateString()), 141 labels: v.viewsPerDay.map(day => day.date.toLocaleDateString()),
150 datasets: [ 142 datasets: [
151 { 143 {
152 label: this.i18n('Views for the day'), 144 label: $localize`Views for the day`,
153 data: v.viewsPerDay.map(day => day.views), 145 data: v.viewsPerDay.map(day => day.views),
154 fill: false, 146 fill: false,
155 borderColor: "#c6c6c6" 147 borderColor: '#c6c6c6'
156 } 148 }
157 ] 149 ]
158 } as ChartData)) 150 } as ChartData))
@@ -160,13 +152,15 @@ export class MyAccountVideoChannelsComponent implements OnInit {
160 // chart options that depend on chart data: 152 // chart options that depend on chart data:
161 // we don't want to skew values and have min at 0, so we define what the floor/ceiling is here 153 // we don't want to skew values and have min at 0, so we define what the floor/ceiling is here
162 this.videoChannelsMinimumDailyViews = min( 154 this.videoChannelsMinimumDailyViews = min(
163 this.videoChannels.map(v => minBy( // compute local minimum daily views for each channel, by their "views" attribute 155 // compute local minimum daily views for each channel, by their "views" attribute
156 this.videoChannels.map(v => minBy(
164 v.viewsPerDay, 157 v.viewsPerDay,
165 day => day.views 158 day => day.views
166 ).views) // the object returned is a ViewPerDate, so we still need to get the views attribute 159 ).views) // the object returned is a ViewPerDate, so we still need to get the views attribute
167 ) 160 )
168 this.videoChannelsMaximumDailyViews = max( 161 this.videoChannelsMaximumDailyViews = max(
169 this.videoChannels.map(v => maxBy( // compute local maximum daily views for each channel, by their "views" attribute 162 // compute local maximum daily views for each channel, by their "views" attribute
163 this.videoChannels.map(v => maxBy(
170 v.viewsPerDay, 164 v.viewsPerDay,
171 day => day.views 165 day => day.views
172 ).views) // the object returned is a ViewPerDate, so we still need to get the views attribute 166 ).views) // the object returned is a ViewPerDate, so we still need to get the views attribute
diff --git a/client/src/app/+my-account/my-account-history/my-account-history.component.ts b/client/src/app/+my-account/my-account-history/my-account-history.component.ts
index dc78b3d6e..3298c56c7 100644
--- a/client/src/app/+my-account/my-account-history/my-account-history.component.ts
+++ b/client/src/app/+my-account/my-account-history/my-account-history.component.ts
@@ -13,7 +13,6 @@ import {
13import { immutableAssign } from '@app/helpers' 13import { immutableAssign } from '@app/helpers'
14import { UserHistoryService } from '@app/shared/shared-main' 14import { UserHistoryService } from '@app/shared/shared-main'
15import { AbstractVideoList } from '@app/shared/shared-video-miniature' 15import { AbstractVideoList } from '@app/shared/shared-video-miniature'
16import { I18n } from '@ngx-translate/i18n-polyfill'
17 16
18@Component({ 17@Component({
19 selector: 'my-account-history', 18 selector: 'my-account-history',
@@ -30,7 +29,6 @@ export class MyAccountHistoryComponent extends AbstractVideoList implements OnIn
30 videosHistoryEnabled: boolean 29 videosHistoryEnabled: boolean
31 30
32 constructor ( 31 constructor (
33 protected i18n: I18n,
34 protected router: Router, 32 protected router: Router,
35 protected serverService: ServerService, 33 protected serverService: ServerService,
36 protected route: ActivatedRoute, 34 protected route: ActivatedRoute,
@@ -44,7 +42,7 @@ export class MyAccountHistoryComponent extends AbstractVideoList implements OnIn
44 ) { 42 ) {
45 super() 43 super()
46 44
47 this.titlePage = this.i18n('My videos history') 45 this.titlePage = $localize`My videos history`
48 } 46 }
49 47
50 ngOnInit () { 48 ngOnInit () {
@@ -72,8 +70,8 @@ export class MyAccountHistoryComponent extends AbstractVideoList implements OnIn
72 .subscribe( 70 .subscribe(
73 () => { 71 () => {
74 const message = this.videosHistoryEnabled === true ? 72 const message = this.videosHistoryEnabled === true ?
75 this.i18n('Videos history is enabled') : 73 $localize`Videos history is enabled` :
76 this.i18n('Videos history is disabled') 74 $localize`Videos history is disabled`
77 75
78 this.notifier.success(message) 76 this.notifier.success(message)
79 77
@@ -85,8 +83,8 @@ export class MyAccountHistoryComponent extends AbstractVideoList implements OnIn
85 } 83 }
86 84
87 async deleteHistory () { 85 async deleteHistory () {
88 const title = this.i18n('Delete videos history') 86 const title = $localize`Delete videos history`
89 const message = this.i18n('Are you sure you want to delete all your videos history?') 87 const message = $localize`Are you sure you want to delete all your videos history?`
90 88
91 const res = await this.confirmService.confirm(message, title) 89 const res = await this.confirmService.confirm(message, title)
92 if (res !== true) return 90 if (res !== true) return
@@ -94,7 +92,7 @@ export class MyAccountHistoryComponent extends AbstractVideoList implements OnIn
94 this.userHistoryService.deleteUserVideosHistory() 92 this.userHistoryService.deleteUserVideosHistory()
95 .subscribe( 93 .subscribe(
96 () => { 94 () => {
97 this.notifier.success(this.i18n('Videos history deleted')) 95 this.notifier.success($localize`Videos history deleted`)
98 96
99 this.reloadVideos() 97 this.reloadVideos()
100 }, 98 },
diff --git a/client/src/app/+my-account/my-account-ownership/my-account-accept-ownership/my-account-accept-ownership.component.ts b/client/src/app/+my-account/my-account-ownership/my-account-accept-ownership/my-account-accept-ownership.component.ts
index 0e62b5ca5..3bfffe2da 100644
--- a/client/src/app/+my-account/my-account-ownership/my-account-accept-ownership/my-account-accept-ownership.component.ts
+++ b/client/src/app/+my-account/my-account-ownership/my-account-accept-ownership/my-account-accept-ownership.component.ts
@@ -3,7 +3,6 @@ import { AuthService, Notifier } from '@app/core'
3import { FormReactive, FormValidatorService, VideoAcceptOwnershipValidatorsService } from '@app/shared/shared-forms' 3import { FormReactive, FormValidatorService, VideoAcceptOwnershipValidatorsService } from '@app/shared/shared-forms'
4import { VideoChannelService, VideoOwnershipService } from '@app/shared/shared-main' 4import { VideoChannelService, VideoOwnershipService } from '@app/shared/shared-main'
5import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 5import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
6import { I18n } from '@ngx-translate/i18n-polyfill'
7import { VideoChangeOwnership, VideoChannel } from '@shared/models' 6import { VideoChangeOwnership, VideoChannel } from '@shared/models'
8 7
9@Component({ 8@Component({
@@ -29,9 +28,8 @@ export class MyAccountAcceptOwnershipComponent extends FormReactive implements O
29 private notifier: Notifier, 28 private notifier: Notifier,
30 private authService: AuthService, 29 private authService: AuthService,
31 private videoChannelService: VideoChannelService, 30 private videoChannelService: VideoChannelService,
32 private modalService: NgbModal, 31 private modalService: NgbModal
33 private i18n: I18n 32 ) {
34 ) {
35 super() 33 super()
36 } 34 }
37 35
@@ -63,7 +61,7 @@ export class MyAccountAcceptOwnershipComponent extends FormReactive implements O
63 .acceptOwnership(videoChangeOwnership.id, { channelId: channel }) 61 .acceptOwnership(videoChangeOwnership.id, { channelId: channel })
64 .subscribe( 62 .subscribe(
65 () => { 63 () => {
66 this.notifier.success(this.i18n('Ownership accepted')) 64 this.notifier.success($localize`Ownership accepted`)
67 if (this.accepted) this.accepted.emit() 65 if (this.accepted) this.accepted.emit()
68 this.videoChangeOwnership = undefined 66 this.videoChangeOwnership = undefined
69 }, 67 },
diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts b/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts
index 5444b97ae..396936ef3 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts
@@ -3,7 +3,6 @@ import { tap } from 'rxjs/operators'
3import { Component, OnInit } from '@angular/core' 3import { Component, OnInit } from '@angular/core'
4import { AuthService, ServerService, UserService } from '@app/core' 4import { AuthService, ServerService, UserService } from '@app/core'
5import { FormReactive, FormValidatorService, UserValidatorsService } from '@app/shared/shared-forms' 5import { FormReactive, FormValidatorService, UserValidatorsService } from '@app/shared/shared-forms'
6import { I18n } from '@ngx-translate/i18n-polyfill'
7import { User } from '@shared/models' 6import { User } from '@shared/models'
8 7
9@Component({ 8@Component({
@@ -21,9 +20,8 @@ export class MyAccountChangeEmailComponent extends FormReactive implements OnIni
21 private userValidatorsService: UserValidatorsService, 20 private userValidatorsService: UserValidatorsService,
22 private authService: AuthService, 21 private authService: AuthService,
23 private userService: UserService, 22 private userService: UserService,
24 private serverService: ServerService, 23 private serverService: ServerService
25 private i18n: I18n 24 ) {
26 ) {
27 super() 25 super()
28 } 26 }
29 27
@@ -52,15 +50,15 @@ export class MyAccountChangeEmailComponent extends FormReactive implements OnIni
52 this.form.reset() 50 this.form.reset()
53 51
54 if (config.signup.requiresEmailVerification) { 52 if (config.signup.requiresEmailVerification) {
55 this.success = this.i18n('Please check your emails to verify your new email.') 53 this.success = $localize`Please check your emails to verify your new email.`
56 } else { 54 } else {
57 this.success = this.i18n('Email updated.') 55 this.success = $localize`Email updated.`
58 } 56 }
59 }, 57 },
60 58
61 err => { 59 err => {
62 if (err.status === 401) { 60 if (err.status === 401) {
63 this.error = this.i18n('You current password is invalid.') 61 this.error = $localize`You current password is invalid.`
64 return 62 return
65 } 63 }
66 64
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 6a16f8a2c..91fe4ec72 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,7 +2,6 @@ import { filter } from 'rxjs/operators'
2import { Component, OnInit } from '@angular/core' 2import { Component, OnInit } from '@angular/core'
3import { AuthService, Notifier, UserService } from '@app/core' 3import { AuthService, Notifier, UserService } from '@app/core'
4import { FormReactive, FormValidatorService, UserValidatorsService } from '@app/shared/shared-forms' 4import { FormReactive, FormValidatorService, UserValidatorsService } from '@app/shared/shared-forms'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { User } from '@shared/models' 5import { User } from '@shared/models'
7 6
8@Component({ 7@Component({
@@ -19,9 +18,8 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On
19 private userValidatorsService: UserValidatorsService, 18 private userValidatorsService: UserValidatorsService,
20 private notifier: Notifier, 19 private notifier: Notifier,
21 private authService: AuthService, 20 private authService: AuthService,
22 private userService: UserService, 21 private userService: UserService
23 private i18n: I18n 22 ) {
24 ) {
25 super() 23 super()
26 } 24 }
27 25
@@ -47,7 +45,7 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On
47 45
48 this.userService.changePassword(currentPassword, newPassword).subscribe( 46 this.userService.changePassword(currentPassword, newPassword).subscribe(
49 () => { 47 () => {
50 this.notifier.success(this.i18n('Password updated.')) 48 this.notifier.success($localize`Password updated.`)
51 49
52 this.form.reset() 50 this.form.reset()
53 this.error = null 51 this.error = null
@@ -55,7 +53,7 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On
55 53
56 err => { 54 err => {
57 if (err.status === 401) { 55 if (err.status === 401) {
58 this.error = this.i18n('You current password is invalid.') 56 this.error = $localize`You current password is invalid.`
59 return 57 return
60 } 58 }
61 59
diff --git a/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts b/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts
index ae6ac5387..387e9e7cd 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts
@@ -1,6 +1,5 @@
1import { Component, Input } from '@angular/core' 1import { Component, Input } from '@angular/core'
2import { AuthService, ConfirmService, Notifier, RedirectService, User, UserService } from '@app/core' 2import { AuthService, ConfirmService, Notifier, RedirectService, User, UserService } from '@app/core'
3import { I18n } from '@ngx-translate/i18n-polyfill'
4 3
5@Component({ 4@Component({
6 selector: 'my-account-danger-zone', 5 selector: 'my-account-danger-zone',
@@ -15,23 +14,22 @@ export class MyAccountDangerZoneComponent {
15 private notifier: Notifier, 14 private notifier: Notifier,
16 private userService: UserService, 15 private userService: UserService,
17 private confirmService: ConfirmService, 16 private confirmService: ConfirmService,
18 private redirectService: RedirectService, 17 private redirectService: RedirectService
19 private i18n: I18n 18 ) { }
20 ) { }
21 19
22 async deleteMe () { 20 async deleteMe () {
23 const res = await this.confirmService.confirmWithInput( 21 const res = await this.confirmService.confirmWithInput(
24 this.i18n('Are you sure you want to delete your account? This will delete all your data, including channels, videos and comments. Content cached by other servers and other third-parties might make longer to be deleted.'), 22 $localize`Are you sure you want to delete your account? This will delete all your data, including channels, videos and comments. Content cached by other servers and other third-parties might make longer to be deleted.`,
25 this.i18n('Type your username to confirm'), 23 $localize`Type your username to confirm`,
26 this.user.username, 24 this.user.username,
27 this.i18n('Delete your account'), 25 $localize`Delete your account`,
28 this.i18n('Delete my account') 26 $localize`Delete my account`
29 ) 27 )
30 if (res === false) return 28 if (res === false) return
31 29
32 this.userService.deleteMe().subscribe( 30 this.userService.deleteMe().subscribe(
33 () => { 31 () => {
34 this.notifier.success(this.i18n('Your account is deleted.')) 32 this.notifier.success($localize`Your account is deleted.`)
35 33
36 this.authService.logout() 34 this.authService.logout()
37 this.redirectService.redirectToHomepage() 35 this.redirectService.redirectToHomepage()
diff --git a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts
index b892ab479..bcbea7fad 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts
@@ -3,7 +3,6 @@ import { Subject } from 'rxjs'
3import { Component, Input, OnInit } from '@angular/core' 3import { Component, Input, OnInit } from '@angular/core'
4import { Notifier, ServerService, User } from '@app/core' 4import { Notifier, ServerService, User } from '@app/core'
5import { UserNotificationService } from '@app/shared/shared-main' 5import { UserNotificationService } from '@app/shared/shared-main'
6import { I18n } from '@ngx-translate/i18n-polyfill'
7import { UserNotificationSetting, UserNotificationSettingValue, UserRight } from '@shared/models' 6import { UserNotificationSetting, UserNotificationSettingValue, UserRight } from '@shared/models'
8 7
9@Component({ 8@Component({
@@ -25,26 +24,25 @@ export class MyAccountNotificationPreferencesComponent implements OnInit {
25 private savePreferences = debounce(this.savePreferencesImpl.bind(this), 500) 24 private savePreferences = debounce(this.savePreferencesImpl.bind(this), 500)
26 25
27 constructor ( 26 constructor (
28 private i18n: I18n,
29 private userNotificationService: UserNotificationService, 27 private userNotificationService: UserNotificationService,
30 private serverService: ServerService, 28 private serverService: ServerService,
31 private notifier: Notifier 29 private notifier: Notifier
32 ) { 30 ) {
33 this.labelNotifications = { 31 this.labelNotifications = {
34 newVideoFromSubscription: this.i18n('New video from your subscriptions'), 32 newVideoFromSubscription: $localize`New video from your subscriptions`,
35 newCommentOnMyVideo: this.i18n('New comment on your video'), 33 newCommentOnMyVideo: $localize`New comment on your video`,
36 abuseAsModerator: this.i18n('New abuse'), 34 abuseAsModerator: $localize`New abuse`,
37 videoAutoBlacklistAsModerator: this.i18n('Video blocked automatically waiting review'), 35 videoAutoBlacklistAsModerator: $localize`Video blocked automatically waiting review`,
38 blacklistOnMyVideo: this.i18n('One of your video is blocked/unblocked'), 36 blacklistOnMyVideo: $localize`One of your video is blocked/unblocked`,
39 myVideoPublished: this.i18n('Video published (after transcoding/scheduled update)'), 37 myVideoPublished: $localize`Video published (after transcoding/scheduled update)`,
40 myVideoImportFinished: this.i18n('Video import finished'), 38 myVideoImportFinished: $localize`Video import finished`,
41 newUserRegistration: this.i18n('A new user registered on your instance'), 39 newUserRegistration: $localize`A new user registered on your instance`,
42 newFollow: this.i18n('You or your channel(s) has a new follower'), 40 newFollow: $localize`You or your channel(s) has a new follower`,
43 commentMention: this.i18n('Someone mentioned you in video comments'), 41 commentMention: $localize`Someone mentioned you in video comments`,
44 newInstanceFollower: this.i18n('Your instance has a new follower'), 42 newInstanceFollower: $localize`Your instance has a new follower`,
45 autoInstanceFollowing: this.i18n('Your instance automatically followed another instance'), 43 autoInstanceFollowing: $localize`Your instance automatically followed another instance`,
46 abuseNewMessage: this.i18n('An abuse report received a new message'), 44 abuseNewMessage: $localize`An abuse report received a new message`,
47 abuseStateChange: this.i18n('One of your abuse reports has been accepted or rejected by moderators') 45 abuseStateChange: $localize`One of your abuse reports has been accepted or rejected by moderators`
48 } 46 }
49 this.notificationSettingKeys = Object.keys(this.labelNotifications) as (keyof UserNotificationSetting)[] 47 this.notificationSettingKeys = Object.keys(this.labelNotifications) as (keyof UserNotificationSetting)[]
50 48
@@ -91,7 +89,7 @@ export class MyAccountNotificationPreferencesComponent implements OnInit {
91 this.userNotificationService.updateNotificationSettings(this.user, this.user.notificationSettings) 89 this.userNotificationService.updateNotificationSettings(this.user, this.user.notificationSettings)
92 .subscribe( 90 .subscribe(
93 () => { 91 () => {
94 this.notifier.success(this.i18n('Preferences saved'), undefined, 2000) 92 this.notifier.success($localize`Preferences saved`, undefined, 2000)
95 }, 93 },
96 94
97 err => this.notifier.error(err.message) 95 err => this.notifier.error(err.message)
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 b0d8494e7..ed0984bf7 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
@@ -2,7 +2,6 @@ import { Subject } from 'rxjs'
2import { Component, Input, OnInit } from '@angular/core' 2import { Component, Input, OnInit } from '@angular/core'
3import { Notifier, User, UserService } from '@app/core' 3import { Notifier, User, UserService } from '@app/core'
4import { FormReactive, FormValidatorService, UserValidatorsService } from '@app/shared/shared-forms' 4import { FormReactive, FormValidatorService, UserValidatorsService } from '@app/shared/shared-forms'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6 5
7@Component({ 6@Component({
8 selector: 'my-account-profile', 7 selector: 'my-account-profile',
@@ -19,9 +18,8 @@ export class MyAccountProfileComponent extends FormReactive implements OnInit {
19 protected formValidatorService: FormValidatorService, 18 protected formValidatorService: FormValidatorService,
20 private userValidatorsService: UserValidatorsService, 19 private userValidatorsService: UserValidatorsService,
21 private notifier: Notifier, 20 private notifier: Notifier,
22 private userService: UserService, 21 private userService: UserService
23 private i18n: I18n 22 ) {
24 ) {
25 super() 23 super()
26 } 24 }
27 25
@@ -50,7 +48,7 @@ export class MyAccountProfileComponent extends FormReactive implements OnInit {
50 this.user.account.displayName = displayName 48 this.user.account.displayName = displayName
51 this.user.account.description = description 49 this.user.account.description = description
52 50
53 this.notifier.success(this.i18n('Profile updated.')) 51 this.notifier.success($localize`Profile updated.`)
54 }, 52 },
55 53
56 err => this.error = err.message 54 err => this.error = err.message
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 a3a8ff1f1..7ea4610d4 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
@@ -1,7 +1,6 @@
1import { ViewportScroller } from '@angular/common' 1import { ViewportScroller } from '@angular/common'
2import { AfterViewChecked, Component, OnInit } from '@angular/core' 2import { AfterViewChecked, Component, OnInit } from '@angular/core'
3import { AuthService, Notifier, User, UserService } from '@app/core' 3import { AuthService, Notifier, User, UserService } from '@app/core'
4import { I18n } from '@ngx-translate/i18n-polyfill'
5 4
6@Component({ 5@Component({
7 selector: 'my-account-settings', 6 selector: 'my-account-settings',
@@ -17,9 +16,8 @@ export class MyAccountSettingsComponent implements OnInit, AfterViewChecked {
17 private viewportScroller: ViewportScroller, 16 private viewportScroller: ViewportScroller,
18 private userService: UserService, 17 private userService: UserService,
19 private authService: AuthService, 18 private authService: AuthService,
20 private notifier: Notifier, 19 private notifier: Notifier
21 private i18n: I18n 20 ) {}
22 ) {}
23 21
24 get userInformationLoaded () { 22 get userInformationLoaded () {
25 return this.authService.userInformationLoaded 23 return this.authService.userInformationLoaded
@@ -41,7 +39,7 @@ export class MyAccountSettingsComponent implements OnInit, AfterViewChecked {
41 this.userService.changeAvatar(formData) 39 this.userService.changeAvatar(formData)
42 .subscribe( 40 .subscribe(
43 data => { 41 data => {
44 this.notifier.success(this.i18n('Avatar changed.')) 42 this.notifier.success($localize`Avatar changed.`)
45 43
46 this.user.updateAccountAvatar(data.avatar) 44 this.user.updateAccountAvatar(data.avatar)
47 }, 45 },
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component.ts b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component.ts
index e72ae2366..5427dc3a0 100644
--- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component.ts
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component.ts
@@ -1,13 +1,12 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { Router } from '@angular/router' 2import { Router } from '@angular/router'
3import { AuthService, Notifier, ServerService } from '@app/core' 3import { AuthService, Notifier, ServerService } from '@app/core'
4import { populateAsyncUserVideoChannels } from '@app/helpers'
4import { FormValidatorService, VideoPlaylistValidatorsService } from '@app/shared/shared-forms' 5import { FormValidatorService, VideoPlaylistValidatorsService } from '@app/shared/shared-forms'
5import { VideoPlaylistService } from '@app/shared/shared-video-playlist' 6import { VideoPlaylistService } from '@app/shared/shared-video-playlist'
6import { I18n } from '@ngx-translate/i18n-polyfill'
7import { VideoPlaylistCreate } from '@shared/models/videos/playlist/video-playlist-create.model' 7import { VideoPlaylistCreate } from '@shared/models/videos/playlist/video-playlist-create.model'
8import { VideoPlaylistPrivacy } from '@shared/models/videos/playlist/video-playlist-privacy.model' 8import { VideoPlaylistPrivacy } from '@shared/models/videos/playlist/video-playlist-privacy.model'
9import { MyAccountVideoPlaylistEdit } from './my-account-video-playlist-edit' 9import { MyAccountVideoPlaylistEdit } from './my-account-video-playlist-edit'
10import { populateAsyncUserVideoChannels } from '@app/helpers'
11 10
12@Component({ 11@Component({
13 selector: 'my-account-video-playlist-create', 12 selector: 'my-account-video-playlist-create',
@@ -24,9 +23,8 @@ export class MyAccountVideoPlaylistCreateComponent extends MyAccountVideoPlaylis
24 private notifier: Notifier, 23 private notifier: Notifier,
25 private router: Router, 24 private router: Router,
26 private videoPlaylistService: VideoPlaylistService, 25 private videoPlaylistService: VideoPlaylistService,
27 private serverService: ServerService, 26 private serverService: ServerService
28 private i18n: I18n 27 ) {
29 ) {
30 super() 28 super()
31 } 29 }
32 30
@@ -70,9 +68,7 @@ export class MyAccountVideoPlaylistCreateComponent extends MyAccountVideoPlaylis
70 68
71 this.videoPlaylistService.createVideoPlaylist(videoPlaylistCreate).subscribe( 69 this.videoPlaylistService.createVideoPlaylist(videoPlaylistCreate).subscribe(
72 () => { 70 () => {
73 this.notifier.success( 71 this.notifier.success($localize`Playlist ${videoPlaylistCreate.displayName} created.`)
74 this.i18n('Playlist {{playlistName}} created.', { playlistName: videoPlaylistCreate.displayName })
75 )
76 this.router.navigate([ '/my-account', 'video-playlists' ]) 72 this.router.navigate([ '/my-account', 'video-playlists' ])
77 }, 73 },
78 74
@@ -85,6 +81,6 @@ export class MyAccountVideoPlaylistCreateComponent extends MyAccountVideoPlaylis
85 } 81 }
86 82
87 getFormButtonTitle () { 83 getFormButtonTitle () {
88 return this.i18n('Create') 84 return $localize`Create`
89 } 85 }
90} 86}
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.ts b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.ts
index e278d9ed2..f6cdf1067 100644
--- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.ts
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.ts
@@ -6,7 +6,6 @@ import { ComponentPagination, ConfirmService, Notifier, ScreenService } from '@a
6import { DropdownAction } from '@app/shared/shared-main' 6import { DropdownAction } from '@app/shared/shared-main'
7import { VideoShareComponent } from '@app/shared/shared-share-modal' 7import { VideoShareComponent } from '@app/shared/shared-share-modal'
8import { VideoPlaylist, VideoPlaylistElement, VideoPlaylistService } from '@app/shared/shared-video-playlist' 8import { VideoPlaylist, VideoPlaylistElement, VideoPlaylistService } from '@app/shared/shared-video-playlist'
9import { I18n } from '@ngx-translate/i18n-polyfill'
10import { VideoPlaylistType } from '@shared/models' 9import { VideoPlaylistType } from '@shared/models'
11 10
12@Component({ 11@Component({
@@ -35,7 +34,6 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro
35 34
36 constructor ( 35 constructor (
37 private notifier: Notifier, 36 private notifier: Notifier,
38 private i18n: I18n,
39 private router: Router, 37 private router: Router,
40 private confirmService: ConfirmService, 38 private confirmService: ConfirmService,
41 private route: ActivatedRoute, 39 private route: ActivatedRoute,
@@ -47,12 +45,12 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro
47 this.playlistActions = [ 45 this.playlistActions = [
48 [ 46 [
49 { 47 {
50 label: this.i18n('Update playlist'), 48 label: $localize`Update playlist`,
51 iconName: 'edit', 49 iconName: 'edit',
52 linkBuilder: playlist => [ '/my-account', 'video-playlists', 'update', playlist.uuid ] 50 linkBuilder: playlist => [ '/my-account', 'video-playlists', 'update', playlist.uuid ]
53 }, 51 },
54 { 52 {
55 label: this.i18n('Delete playlist'), 53 label: $localize`Delete playlist`,
56 iconName: 'delete', 54 iconName: 'delete',
57 handler: playlist => this.deleteVideoPlaylist(playlist) 55 handler: playlist => this.deleteVideoPlaylist(playlist)
58 } 56 }
@@ -126,11 +124,8 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro
126 124
127 async deleteVideoPlaylist (videoPlaylist: VideoPlaylist) { 125 async deleteVideoPlaylist (videoPlaylist: VideoPlaylist) {
128 const res = await this.confirmService.confirm( 126 const res = await this.confirmService.confirm(
129 this.i18n( 127 $localize`Do you really want to delete ${videoPlaylist.displayName}?`,
130 'Do you really want to delete {{playlistDisplayName}}?', 128 $localize`Delete`
131 { playlistDisplayName: videoPlaylist.displayName }
132 ),
133 this.i18n('Delete')
134 ) 129 )
135 if (res === false) return 130 if (res === false) return
136 131
@@ -138,10 +133,7 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro
138 .subscribe( 133 .subscribe(
139 () => { 134 () => {
140 this.router.navigate([ '/my-account', 'video-playlists' ]) 135 this.router.navigate([ '/my-account', 'video-playlists' ])
141 136 this.notifier.success($localize`Playlist ${videoPlaylist.displayName} deleted.`)
142 this.notifier.success(
143 this.i18n('Playlist {{playlistDisplayName}} deleted.', { playlistDisplayName: videoPlaylist.displayName })
144 )
145 }, 137 },
146 138
147 error => this.notifier.error(error.message) 139 error => this.notifier.error(error.message)
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component.ts b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component.ts
index 6787fb757..149d0d94f 100644
--- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component.ts
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component.ts
@@ -6,7 +6,6 @@ import { AuthService, Notifier, ServerService } from '@app/core'
6import { populateAsyncUserVideoChannels } from '@app/helpers' 6import { populateAsyncUserVideoChannels } from '@app/helpers'
7import { FormValidatorService, VideoPlaylistValidatorsService } from '@app/shared/shared-forms' 7import { FormValidatorService, VideoPlaylistValidatorsService } from '@app/shared/shared-forms'
8import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' 8import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
9import { I18n } from '@ngx-translate/i18n-polyfill'
10import { VideoPlaylistUpdate } from '@shared/models' 9import { VideoPlaylistUpdate } from '@shared/models'
11import { MyAccountVideoPlaylistEdit } from './my-account-video-playlist-edit' 10import { MyAccountVideoPlaylistEdit } from './my-account-video-playlist-edit'
12 11
@@ -29,7 +28,6 @@ export class MyAccountVideoPlaylistUpdateComponent extends MyAccountVideoPlaylis
29 private router: Router, 28 private router: Router,
30 private route: ActivatedRoute, 29 private route: ActivatedRoute,
31 private videoPlaylistService: VideoPlaylistService, 30 private videoPlaylistService: VideoPlaylistService,
32 private i18n: I18n,
33 private serverService: ServerService 31 private serverService: ServerService
34 ) { 32 ) {
35 super() 33 super()
@@ -91,10 +89,7 @@ export class MyAccountVideoPlaylistUpdateComponent extends MyAccountVideoPlaylis
91 89
92 this.videoPlaylistService.updateVideoPlaylist(this.videoPlaylistToUpdate, videoPlaylistUpdate).subscribe( 90 this.videoPlaylistService.updateVideoPlaylist(this.videoPlaylistToUpdate, videoPlaylistUpdate).subscribe(
93 () => { 91 () => {
94 this.notifier.success( 92 this.notifier.success($localize`Playlist ${videoPlaylistUpdate.displayName} updated.`)
95 this.i18n('Playlist {{videoPlaylistName}} updated.', { videoPlaylistName: videoPlaylistUpdate.displayName })
96 )
97
98 this.router.navigate([ '/my-account', 'video-playlists' ]) 93 this.router.navigate([ '/my-account', 'video-playlists' ])
99 }, 94 },
100 95
@@ -107,7 +102,7 @@ export class MyAccountVideoPlaylistUpdateComponent extends MyAccountVideoPlaylis
107 } 102 }
108 103
109 getFormButtonTitle () { 104 getFormButtonTitle () {
110 return this.i18n('Update') 105 return $localize`Update`
111 } 106 }
112 107
113 private hydrateFormFromPlaylist () { 108 private hydrateFormFromPlaylist () {
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.ts b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.ts
index ba95b6c8b..1e569c0b6 100644
--- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.ts
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.ts
@@ -3,7 +3,6 @@ import { debounceTime, mergeMap } from 'rxjs/operators'
3import { Component, OnInit } from '@angular/core' 3import { Component, OnInit } from '@angular/core'
4import { AuthService, ComponentPagination, ConfirmService, Notifier, User } from '@app/core' 4import { AuthService, ComponentPagination, ConfirmService, Notifier, User } from '@app/core'
5import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' 5import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
6import { I18n } from '@ngx-translate/i18n-polyfill'
7import { VideoPlaylistType } from '@shared/models' 6import { VideoPlaylistType } from '@shared/models'
8 7
9@Component({ 8@Component({
@@ -30,9 +29,8 @@ export class MyAccountVideoPlaylistsComponent implements OnInit {
30 private authService: AuthService, 29 private authService: AuthService,
31 private notifier: Notifier, 30 private notifier: Notifier,
32 private confirmService: ConfirmService, 31 private confirmService: ConfirmService,
33 private videoPlaylistService: VideoPlaylistService, 32 private videoPlaylistService: VideoPlaylistService
34 private i18n: I18n 33 ) {}
35 ) {}
36 34
37 ngOnInit () { 35 ngOnInit () {
38 this.user = this.authService.getUser() 36 this.user = this.authService.getUser()
@@ -49,11 +47,8 @@ export class MyAccountVideoPlaylistsComponent implements OnInit {
49 47
50 async deleteVideoPlaylist (videoPlaylist: VideoPlaylist) { 48 async deleteVideoPlaylist (videoPlaylist: VideoPlaylist) {
51 const res = await this.confirmService.confirm( 49 const res = await this.confirmService.confirm(
52 this.i18n( 50 $localize`Do you really want to delete ${videoPlaylist.displayName}?`,
53 'Do you really want to delete {{playlistDisplayName}}?', 51 $localize`Delete`
54 { playlistDisplayName: videoPlaylist.displayName }
55 ),
56 this.i18n('Delete')
57 ) 52 )
58 if (res === false) return 53 if (res === false) return
59 54
@@ -63,9 +58,7 @@ export class MyAccountVideoPlaylistsComponent implements OnInit {
63 this.videoPlaylists = this.videoPlaylists 58 this.videoPlaylists = this.videoPlaylists
64 .filter(p => p.id !== videoPlaylist.id) 59 .filter(p => p.id !== videoPlaylist.id)
65 60
66 this.notifier.success( 61 this.notifier.success($localize`Playlist ${videoPlaylist.displayName}} deleted.`)
67 this.i18n('Playlist {{playlistDisplayName}} deleted.', { playlistDisplayName: videoPlaylist.displayName })
68 )
69 }, 62 },
70 63
71 error => this.notifier.error(error.message) 64 error => this.notifier.error(error.message)
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 2274c6a7b..46a02a41a 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
@@ -7,7 +7,6 @@ import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook'
7import { immutableAssign } from '@app/helpers' 7import { immutableAssign } from '@app/helpers'
8import { Video, VideoService } from '@app/shared/shared-main' 8import { Video, VideoService } from '@app/shared/shared-main'
9import { MiniatureDisplayOptions, OwnerDisplayType, SelectionType, VideosSelectionComponent } from '@app/shared/shared-video-miniature' 9import { MiniatureDisplayOptions, OwnerDisplayType, SelectionType, VideosSelectionComponent } from '@app/shared/shared-video-miniature'
10import { I18n } from '@ngx-translate/i18n-polyfill'
11import { VideoSortField } from '@shared/models' 10import { VideoSortField } from '@shared/models'
12import { VideoChangeOwnershipComponent } from './video-change-ownership/video-change-ownership.component' 11import { VideoChangeOwnershipComponent } from './video-change-ownership/video-change-ownership.component'
13 12
@@ -50,11 +49,10 @@ export class MyAccountVideosComponent implements OnInit, DisableForReuseHook {
50 protected authService: AuthService, 49 protected authService: AuthService,
51 protected notifier: Notifier, 50 protected notifier: Notifier,
52 protected screenService: ScreenService, 51 protected screenService: ScreenService,
53 private i18n: I18n,
54 private confirmService: ConfirmService, 52 private confirmService: ConfirmService,
55 private videoService: VideoService 53 private videoService: VideoService
56 ) { 54 ) {
57 this.titlePage = this.i18n('My videos') 55 this.titlePage = $localize`My videos`
58 } 56 }
59 57
60 ngOnInit () { 58 ngOnInit () {
@@ -97,8 +95,8 @@ export class MyAccountVideosComponent implements OnInit, DisableForReuseHook {
97 .map(k => parseInt(k, 10)) 95 .map(k => parseInt(k, 10))
98 96
99 const res = await this.confirmService.confirm( 97 const res = await this.confirmService.confirm(
100 this.i18n('Do you really want to delete {{deleteLength}} videos?', { deleteLength: toDeleteVideosIds.length }), 98 $localize`Do you really want to delete ${toDeleteVideosIds.length} videos?`,
101 this.i18n('Delete') 99 $localize`Delete`
102 ) 100 )
103 if (res === false) return 101 if (res === false) return
104 102
@@ -114,8 +112,7 @@ export class MyAccountVideosComponent implements OnInit, DisableForReuseHook {
114 .pipe(toArray()) 112 .pipe(toArray())
115 .subscribe( 113 .subscribe(
116 () => { 114 () => {
117 this.notifier.success(this.i18n('{{deleteLength}} videos deleted.', { deleteLength: toDeleteVideosIds.length })) 115 this.notifier.success($localize`${toDeleteVideosIds.length} videos deleted.`)
118
119 this.selection = {} 116 this.selection = {}
120 }, 117 },
121 118
@@ -125,15 +122,15 @@ export class MyAccountVideosComponent implements OnInit, DisableForReuseHook {
125 122
126 async deleteVideo (video: Video) { 123 async deleteVideo (video: Video) {
127 const res = await this.confirmService.confirm( 124 const res = await this.confirmService.confirm(
128 this.i18n('Do you really want to delete {{videoName}}?', { videoName: video.name }), 125 $localize`Do you really want to delete ${video.name}?`,
129 this.i18n('Delete') 126 $localize`Delete`
130 ) 127 )
131 if (res === false) return 128 if (res === false) return
132 129
133 this.videoService.removeVideo(video.id) 130 this.videoService.removeVideo(video.id)
134 .subscribe( 131 .subscribe(
135 () => { 132 () => {
136 this.notifier.success(this.i18n('Video {{videoName}} deleted.', { videoName: video.name })) 133 this.notifier.success($localize`Video ${video.name} deleted.`)
137 this.removeVideoFromArray(video.id) 134 this.removeVideoFromArray(video.id)
138 }, 135 },
139 136
diff --git a/client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.ts b/client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.ts
index 18e716a09..edd691694 100644
--- a/client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.ts
+++ b/client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.ts
@@ -3,7 +3,6 @@ import { Notifier, UserService } from '@app/core'
3import { FormReactive, FormValidatorService, VideoChangeOwnershipValidatorsService } from '@app/shared/shared-forms' 3import { FormReactive, FormValidatorService, VideoChangeOwnershipValidatorsService } from '@app/shared/shared-forms'
4import { Video, VideoOwnershipService } from '@app/shared/shared-main' 4import { Video, VideoOwnershipService } from '@app/shared/shared-main'
5import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 5import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
6import { I18n } from '@ngx-translate/i18n-polyfill'
7 6
8@Component({ 7@Component({
9 selector: 'my-video-change-ownership', 8 selector: 'my-video-change-ownership',
@@ -25,9 +24,8 @@ export class VideoChangeOwnershipComponent extends FormReactive implements OnIni
25 private videoOwnershipService: VideoOwnershipService, 24 private videoOwnershipService: VideoOwnershipService,
26 private notifier: Notifier, 25 private notifier: Notifier,
27 private userService: UserService, 26 private userService: UserService,
28 private modalService: NgbModal, 27 private modalService: NgbModal
29 private i18n: I18n 28 ) {
30 ) {
31 super() 29 super()
32 } 30 }
33 31
@@ -63,7 +61,7 @@ export class VideoChangeOwnershipComponent extends FormReactive implements OnIni
63 this.videoOwnershipService 61 this.videoOwnershipService
64 .changeOwnership(this.video.id, username) 62 .changeOwnership(this.video.id, username)
65 .subscribe( 63 .subscribe(
66 () => this.notifier.success(this.i18n('Ownership change request sent.')), 64 () => this.notifier.success($localize`Ownership change request sent.`),
67 65
68 err => this.notifier.error(err.message) 66 err => this.notifier.error(err.message)
69 ) 67 )
diff --git a/client/src/app/+my-account/my-account.component.ts b/client/src/app/+my-account/my-account.component.ts
index 56373e5f1..d3bf8d143 100644
--- a/client/src/app/+my-account/my-account.component.ts
+++ b/client/src/app/+my-account/my-account.component.ts
@@ -1,6 +1,5 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { AuthService, ScreenService, ServerService, AuthUser } from '@app/core' 2import { AuthService, AuthUser, ScreenService, ServerService } from '@app/core'
3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { ServerConfig } from '@shared/models' 3import { ServerConfig } from '@shared/models'
5import { TopMenuDropdownParam } from '../shared/shared-main/misc/top-menu-dropdown.component' 4import { TopMenuDropdownParam } from '../shared/shared-main/misc/top-menu-dropdown.component'
6 5
@@ -18,9 +17,8 @@ export class MyAccountComponent implements OnInit {
18 constructor ( 17 constructor (
19 private serverService: ServerService, 18 private serverService: ServerService,
20 private authService: AuthService, 19 private authService: AuthService,
21 private screenService: ScreenService, 20 private screenService: ScreenService
22 private i18n: I18n 21 ) { }
23 ) { }
24 22
25 get isBroadcastMessageDisplayed () { 23 get isBroadcastMessageDisplayed () {
26 return this.screenService.isBroadcastMessageDisplayed 24 return this.screenService.isBroadcastMessageDisplayed
@@ -46,31 +44,31 @@ export class MyAccountComponent implements OnInit {
46 44
47 private buildMenu () { 45 private buildMenu () {
48 const libraryEntries: TopMenuDropdownParam = { 46 const libraryEntries: TopMenuDropdownParam = {
49 label: this.i18n('My library'), 47 label: $localize`My library`,
50 children: [ 48 children: [
51 { 49 {
52 label: this.i18n('My channels'), 50 label: $localize`My channels`,
53 routerLink: '/my-account/video-channels', 51 routerLink: '/my-account/video-channels',
54 iconName: 'channel' 52 iconName: 'channel'
55 }, 53 },
56 { 54 {
57 label: this.i18n('My videos'), 55 label: $localize`My videos`,
58 routerLink: '/my-account/videos', 56 routerLink: '/my-account/videos',
59 iconName: 'videos', 57 iconName: 'videos',
60 isDisplayed: () => this.user.canSeeVideosLink 58 isDisplayed: () => this.user.canSeeVideosLink
61 }, 59 },
62 { 60 {
63 label: this.i18n('My playlists'), 61 label: $localize`My playlists`,
64 routerLink: '/my-account/video-playlists', 62 routerLink: '/my-account/video-playlists',
65 iconName: 'playlists' 63 iconName: 'playlists'
66 }, 64 },
67 { 65 {
68 label: this.i18n('My subscriptions'), 66 label: $localize`My subscriptions`,
69 routerLink: '/my-account/subscriptions', 67 routerLink: '/my-account/subscriptions',
70 iconName: 'subscriptions' 68 iconName: 'subscriptions'
71 }, 69 },
72 { 70 {
73 label: this.i18n('My history'), 71 label: $localize`My history`,
74 routerLink: '/my-account/history/videos', 72 routerLink: '/my-account/history/videos',
75 iconName: 'history' 73 iconName: 'history'
76 } 74 }
@@ -87,25 +85,25 @@ export class MyAccountComponent implements OnInit {
87 } 85 }
88 86
89 const miscEntries: TopMenuDropdownParam = { 87 const miscEntries: TopMenuDropdownParam = {
90 label: this.i18n('Misc'), 88 label: $localize`Misc`,
91 children: [ 89 children: [
92 { 90 {
93 label: this.i18n('Muted accounts'), 91 label: $localize`Muted accounts`,
94 routerLink: '/my-account/blocklist/accounts', 92 routerLink: '/my-account/blocklist/accounts',
95 iconName: 'user-x' 93 iconName: 'user-x'
96 }, 94 },
97 { 95 {
98 label: this.i18n('Muted servers'), 96 label: $localize`Muted servers`,
99 routerLink: '/my-account/blocklist/servers', 97 routerLink: '/my-account/blocklist/servers',
100 iconName: 'peertube-x' 98 iconName: 'peertube-x'
101 }, 99 },
102 { 100 {
103 label: this.i18n('My abuse reports'), 101 label: $localize`My abuse reports`,
104 routerLink: '/my-account/abuses', 102 routerLink: '/my-account/abuses',
105 iconName: 'flag' 103 iconName: 'flag'
106 }, 104 },
107 { 105 {
108 label: this.i18n('Ownership changes'), 106 label: $localize`Ownership changes`,
109 routerLink: '/my-account/ownership', 107 routerLink: '/my-account/ownership',
110 iconName: 'download' 108 iconName: 'download'
111 } 109 }
@@ -114,11 +112,11 @@ export class MyAccountComponent implements OnInit {
114 112
115 this.menuEntries = [ 113 this.menuEntries = [
116 { 114 {
117 label: this.i18n('My settings'), 115 label: $localize`My settings`,
118 routerLink: '/my-account/settings' 116 routerLink: '/my-account/settings'
119 }, 117 },
120 { 118 {
121 label: this.i18n('My notifications'), 119 label: $localize`My notifications`,
122 routerLink: '/my-account/notifications' 120 routerLink: '/my-account/notifications'
123 }, 121 },
124 libraryEntries, 122 libraryEntries,
diff --git a/client/src/app/+reset-password/reset-password.component.ts b/client/src/app/+reset-password/reset-password.component.ts
index 8d50e9839..16e4f4090 100644
--- a/client/src/app/+reset-password/reset-password.component.ts
+++ b/client/src/app/+reset-password/reset-password.component.ts
@@ -2,7 +2,6 @@ import { Component, OnInit } from '@angular/core'
2import { ActivatedRoute, Router } from '@angular/router' 2import { ActivatedRoute, Router } from '@angular/router'
3import { Notifier, UserService } from '@app/core' 3import { Notifier, UserService } from '@app/core'
4import { FormReactive, FormValidatorService, ResetPasswordValidatorsService, UserValidatorsService } from '@app/shared/shared-forms' 4import { FormReactive, FormValidatorService, ResetPasswordValidatorsService, UserValidatorsService } from '@app/shared/shared-forms'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6 5
7@Component({ 6@Component({
8 selector: 'my-login', 7 selector: 'my-login',
@@ -21,9 +20,8 @@ export class ResetPasswordComponent extends FormReactive implements OnInit {
21 private userService: UserService, 20 private userService: UserService,
22 private notifier: Notifier, 21 private notifier: Notifier,
23 private router: Router, 22 private router: Router,
24 private route: ActivatedRoute, 23 private route: ActivatedRoute
25 private i18n: I18n 24 ) {
26 ) {
27 super() 25 super()
28 } 26 }
29 27
@@ -37,7 +35,7 @@ export class ResetPasswordComponent extends FormReactive implements OnInit {
37 this.verificationString = this.route.snapshot.queryParams['verificationString'] 35 this.verificationString = this.route.snapshot.queryParams['verificationString']
38 36
39 if (!this.userId || !this.verificationString) { 37 if (!this.userId || !this.verificationString) {
40 this.notifier.error(this.i18n('Unable to find user id or verification string.')) 38 this.notifier.error($localize`Unable to find user id or verification string.`)
41 this.router.navigate([ '/' ]) 39 this.router.navigate([ '/' ])
42 } 40 }
43 } 41 }
@@ -46,7 +44,7 @@ export class ResetPasswordComponent extends FormReactive implements OnInit {
46 this.userService.resetPassword(this.userId, this.verificationString, this.form.value.password) 44 this.userService.resetPassword(this.userId, this.verificationString, this.form.value.password)
47 .subscribe( 45 .subscribe(
48 () => { 46 () => {
49 this.notifier.success(this.i18n('Your password has been successfully reset!')) 47 this.notifier.success($localize`Your password has been successfully reset!`)
50 this.router.navigate([ '/login' ]) 48 this.router.navigate([ '/login' ])
51 }, 49 },
52 50
diff --git a/client/src/app/+search/search-filters.component.ts b/client/src/app/+search/search-filters.component.ts
index 13ad61647..a2af9a942 100644
--- a/client/src/app/+search/search-filters.component.ts
+++ b/client/src/app/+search/search-filters.component.ts
@@ -1,8 +1,6 @@
1import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' 1import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
2import { ServerService } from '@app/core' 2import { ServerService } from '@app/core'
3import { VideoValidatorsService } from '@app/shared/shared-forms'
4import { AdvancedSearch } from '@app/shared/shared-search' 3import { AdvancedSearch } from '@app/shared/shared-search'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { ServerConfig, VideoConstant } from '@shared/models' 4import { ServerConfig, VideoConstant } from '@shared/models'
7 5
8@Component({ 6@Component({
@@ -32,64 +30,62 @@ export class SearchFiltersComponent implements OnInit {
32 private serverConfig: ServerConfig 30 private serverConfig: ServerConfig
33 31
34 constructor ( 32 constructor (
35 private i18n: I18n,
36 private videoValidatorsService: VideoValidatorsService,
37 private serverService: ServerService 33 private serverService: ServerService
38 ) { 34 ) {
39 this.publishedDateRanges = [ 35 this.publishedDateRanges = [
40 { 36 {
41 id: 'any_published_date', 37 id: 'any_published_date',
42 label: this.i18n('Any') 38 label: $localize`Any`
43 }, 39 },
44 { 40 {
45 id: 'today', 41 id: 'today',
46 label: this.i18n('Today') 42 label: $localize`Today`
47 }, 43 },
48 { 44 {
49 id: 'last_7days', 45 id: 'last_7days',
50 label: this.i18n('Last 7 days') 46 label: $localize`Last 7 days`
51 }, 47 },
52 { 48 {
53 id: 'last_30days', 49 id: 'last_30days',
54 label: this.i18n('Last 30 days') 50 label: $localize`Last 30 days`
55 }, 51 },
56 { 52 {
57 id: 'last_365days', 53 id: 'last_365days',
58 label: this.i18n('Last 365 days') 54 label: $localize`Last 365 days`
59 } 55 }
60 ] 56 ]
61 57
62 this.durationRanges = [ 58 this.durationRanges = [
63 { 59 {
64 id: 'any_duration', 60 id: 'any_duration',
65 label: this.i18n('Any') 61 label: $localize`Any`
66 }, 62 },
67 { 63 {
68 id: 'short', 64 id: 'short',
69 label: this.i18n('Short (< 4 min)') 65 label: $localize`Short (< 4 min)`
70 }, 66 },
71 { 67 {
72 id: 'medium', 68 id: 'medium',
73 label: this.i18n('Medium (4-10 min)') 69 label: $localize`Medium (4-10 min)`
74 }, 70 },
75 { 71 {
76 id: 'long', 72 id: 'long',
77 label: this.i18n('Long (> 10 min)') 73 label: $localize`Long (> 10 min)`
78 } 74 }
79 ] 75 ]
80 76
81 this.sorts = [ 77 this.sorts = [
82 { 78 {
83 id: '-match', 79 id: '-match',
84 label: this.i18n('Relevance') 80 label: $localize`Relevance`
85 }, 81 },
86 { 82 {
87 id: '-publishedAt', 83 id: '-publishedAt',
88 label: this.i18n('Publish date') 84 label: $localize`Publish date`
89 }, 85 },
90 { 86 {
91 id: '-views', 87 id: '-views',
92 label: this.i18n('Views') 88 label: $localize`Views`
93 } 89 }
94 ] 90 ]
95 } 91 }
diff --git a/client/src/app/+search/search.component.ts b/client/src/app/+search/search.component.ts
index 70116fab3..2be952e16 100644
--- a/client/src/app/+search/search.component.ts
+++ b/client/src/app/+search/search.component.ts
@@ -7,7 +7,6 @@ import { Video, VideoChannel } from '@app/shared/shared-main'
7import { AdvancedSearch, SearchService } from '@app/shared/shared-search' 7import { AdvancedSearch, SearchService } from '@app/shared/shared-search'
8import { MiniatureDisplayOptions, VideoLinkType } from '@app/shared/shared-video-miniature' 8import { MiniatureDisplayOptions, VideoLinkType } from '@app/shared/shared-video-miniature'
9import { MetaService } from '@ngx-meta/core' 9import { MetaService } from '@ngx-meta/core'
10import { I18n } from '@ngx-translate/i18n-polyfill'
11import { SearchTargetType, ServerConfig } from '@shared/models' 10import { SearchTargetType, ServerConfig } from '@shared/models'
12 11
13@Component({ 12@Component({
@@ -52,7 +51,6 @@ export class SearchComponent implements OnInit, OnDestroy {
52 private lastSearchTarget: SearchTargetType 51 private lastSearchTarget: SearchTargetType
53 52
54 constructor ( 53 constructor (
55 private i18n: I18n,
56 private route: ActivatedRoute, 54 private route: ActivatedRoute,
57 private router: Router, 55 private router: Router,
58 private metaService: MetaService, 56 private metaService: MetaService,
@@ -170,8 +168,8 @@ export class SearchComponent implements OnInit, OnDestroy {
170 } 168 }
171 169
172 this.notifier.error( 170 this.notifier.error(
173 this.i18n('Search index is unavailable. Retrying with instance results instead.'), 171 $localize`Search index is unavailable. Retrying with instance results instead.`,
174 this.i18n('Search error') 172 $localize`Search error`
175 ) 173 )
176 this.advancedSearch.searchTarget = 'local' 174 this.advancedSearch.searchTarget = 'local'
177 this.search() 175 this.search()
@@ -229,7 +227,7 @@ export class SearchComponent implements OnInit, OnDestroy {
229 227
230 private updateTitle () { 228 private updateTitle () {
231 const suffix = this.currentSearch ? ' ' + this.currentSearch : '' 229 const suffix = this.currentSearch ? ' ' + this.currentSearch : ''
232 this.metaService.setTitle(this.i18n('Search') + suffix) 230 this.metaService.setTitle($localize`Search` + suffix)
233 } 231 }
234 232
235 private updateUrlFromAdvancedSearch () { 233 private updateUrlFromAdvancedSearch () {
diff --git a/client/src/app/+signup/+register/register.component.ts b/client/src/app/+signup/+register/register.component.ts
index 3e8171b27..153a6d2b5 100644
--- a/client/src/app/+signup/+register/register.component.ts
+++ b/client/src/app/+signup/+register/register.component.ts
@@ -5,7 +5,6 @@ import { AuthService, Notifier, UserService } from '@app/core'
5import { HooksService } from '@app/core/plugins/hooks.service' 5import { HooksService } from '@app/core/plugins/hooks.service'
6import { InstanceService } from '@app/shared/shared-instance' 6import { InstanceService } from '@app/shared/shared-instance'
7import { NgbAccordion } from '@ng-bootstrap/ng-bootstrap' 7import { NgbAccordion } from '@ng-bootstrap/ng-bootstrap'
8import { I18n } from '@ngx-translate/i18n-polyfill'
9import { UserRegister } from '@shared/models' 8import { UserRegister } from '@shared/models'
10import { About, ServerConfig } from '@shared/models/server' 9import { About, ServerConfig } from '@shared/models/server'
11 10
@@ -42,9 +41,8 @@ export class RegisterComponent implements OnInit {
42 private notifier: Notifier, 41 private notifier: Notifier,
43 private userService: UserService, 42 private userService: UserService,
44 private instanceService: InstanceService, 43 private instanceService: InstanceService,
45 private hooks: HooksService, 44 private hooks: HooksService
46 private i18n: I18n 45 ) {
47 ) {
48 } 46 }
49 47
50 get requiresEmailVerification () { 48 get requiresEmailVerification () {
@@ -114,7 +112,7 @@ export class RegisterComponent implements OnInit {
114 this.signupDone = true 112 this.signupDone = true
115 113
116 if (this.requiresEmailVerification) { 114 if (this.requiresEmailVerification) {
117 this.info = this.i18n('Now please check your emails to verify your account and complete signup.') 115 this.info = $localize`Now please check your emails to verify your account and complete signup.`
118 return 116 return
119 } 117 }
120 118
@@ -122,7 +120,7 @@ export class RegisterComponent implements OnInit {
122 this.authService.login(body.username, body.password) 120 this.authService.login(body.username, body.password)
123 .subscribe( 121 .subscribe(
124 () => { 122 () => {
125 this.success = this.i18n('You are now logged in as {{username}}!', { username: body.username }) 123 this.success = $localize`You are now logged in as ${body.username}!`
126 }, 124 },
127 125
128 err => this.error = err.message 126 err => this.error = err.message
diff --git a/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts b/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts
index 51910471b..b26581d2b 100644
--- a/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts
+++ b/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts
@@ -1,7 +1,6 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { Notifier, RedirectService, ServerService, UserService } from '@app/core' 2import { Notifier, RedirectService, ServerService, UserService } from '@app/core'
3import { FormReactive, FormValidatorService, UserValidatorsService } from '@app/shared/shared-forms' 3import { FormReactive, FormValidatorService, UserValidatorsService } from '@app/shared/shared-forms'
4import { I18n } from '@ngx-translate/i18n-polyfill'
5import { ServerConfig } from '@shared/models' 4import { ServerConfig } from '@shared/models'
6 5
7@Component({ 6@Component({
@@ -19,9 +18,8 @@ export class VerifyAccountAskSendEmailComponent extends FormReactive implements
19 private userService: UserService, 18 private userService: UserService,
20 private serverService: ServerService, 19 private serverService: ServerService,
21 private notifier: Notifier, 20 private notifier: Notifier,
22 private redirectService: RedirectService, 21 private redirectService: RedirectService
23 private i18n: I18n 22 ) {
24 ) {
25 super() 23 super()
26 } 24 }
27 25
@@ -44,11 +42,7 @@ export class VerifyAccountAskSendEmailComponent extends FormReactive implements
44 this.userService.askSendVerifyEmail(email) 42 this.userService.askSendVerifyEmail(email)
45 .subscribe( 43 .subscribe(
46 () => { 44 () => {
47 const message = this.i18n( 45 this.notifier.success($localize`An email with verification link will be sent to ${email}.`)
48 'An email with verification link will be sent to {{email}}.',
49 { email }
50 )
51 this.notifier.success(message)
52 this.redirectService.redirectToHomepage() 46 this.redirectService.redirectToHomepage()
53 }, 47 },
54 48
diff --git a/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts b/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts
index 586f4e231..acc688ab3 100644
--- a/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts
+++ b/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts
@@ -1,7 +1,6 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { ActivatedRoute } from '@angular/router' 2import { ActivatedRoute } from '@angular/router'
3import { AuthService, Notifier, UserService } from '@app/core' 3import { AuthService, Notifier, UserService } from '@app/core'
4import { I18n } from '@ngx-translate/i18n-polyfill'
5 4
6@Component({ 5@Component({
7 selector: 'my-verify-account-email', 6 selector: 'my-verify-account-email',
@@ -20,9 +19,8 @@ export class VerifyAccountEmailComponent implements OnInit {
20 private userService: UserService, 19 private userService: UserService,
21 private authService: AuthService, 20 private authService: AuthService,
22 private notifier: Notifier, 21 private notifier: Notifier,
23 private route: ActivatedRoute, 22 private route: ActivatedRoute
24 private i18n: I18n 23 ) {
25 ) {
26 } 24 }
27 25
28 ngOnInit () { 26 ngOnInit () {
@@ -32,7 +30,7 @@ export class VerifyAccountEmailComponent implements OnInit {
32 this.isPendingEmail = queryParams['isPendingEmail'] === 'true' 30 this.isPendingEmail = queryParams['isPendingEmail'] === 'true'
33 31
34 if (!this.userId || !this.verificationString) { 32 if (!this.userId || !this.verificationString) {
35 this.notifier.error(this.i18n('Unable to find user id or verification string.')) 33 this.notifier.error($localize`Unable to find user id or verification string.`)
36 } else { 34 } else {
37 this.verifyEmail() 35 this.verifyEmail()
38 } 36 }
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 19e4bc1f4..537c7d08e 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,7 +2,6 @@ import { Subscription } from 'rxjs'
2import { Component, OnDestroy, OnInit } from '@angular/core' 2import { Component, OnDestroy, OnInit } from '@angular/core'
3import { MarkdownService } from '@app/core' 3import { MarkdownService } from '@app/core'
4import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' 4import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6 5
7@Component({ 6@Component({
8 selector: 'my-video-channel-about', 7 selector: 'my-video-channel-about',
@@ -17,7 +16,6 @@ export class VideoChannelAboutComponent implements OnInit, OnDestroy {
17 private videoChannelSub: Subscription 16 private videoChannelSub: Subscription
18 17
19 constructor ( 18 constructor (
20 private i18n: I18n,
21 private videoChannelService: VideoChannelService, 19 private videoChannelService: VideoChannelService,
22 private markdownService: MarkdownService 20 private markdownService: MarkdownService
23 ) { } 21 ) { }
@@ -40,6 +38,6 @@ export class VideoChannelAboutComponent implements OnInit, OnDestroy {
40 getVideoChannelDescription () { 38 getVideoChannelDescription () {
41 if (this.descriptionHTML) return this.descriptionHTML 39 if (this.descriptionHTML) return this.descriptionHTML
42 40
43 return this.i18n('No description') 41 return $localize`No description`
44 } 42 }
45} 43}
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 599d38da9..e1ec6bbcb 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
@@ -6,7 +6,6 @@ import { AuthService, ConfirmService, LocalStorageService, Notifier, ScreenServi
6import { immutableAssign } from '@app/helpers' 6import { immutableAssign } from '@app/helpers'
7import { VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main' 7import { VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main'
8import { AbstractVideoList } from '@app/shared/shared-video-miniature' 8import { AbstractVideoList } from '@app/shared/shared-video-miniature'
9import { I18n } from '@ngx-translate/i18n-polyfill'
10 9
11@Component({ 10@Component({
12 selector: 'my-video-channel-videos', 11 selector: 'my-video-channel-videos',
@@ -23,7 +22,6 @@ export class VideoChannelVideosComponent extends AbstractVideoList implements On
23 private videoChannelSub: Subscription 22 private videoChannelSub: Subscription
24 23
25 constructor ( 24 constructor (
26 protected i18n: I18n,
27 protected router: Router, 25 protected router: Router,
28 protected serverService: ServerService, 26 protected serverService: ServerService,
29 protected route: ActivatedRoute, 27 protected route: ActivatedRoute,
@@ -38,7 +36,7 @@ export class VideoChannelVideosComponent extends AbstractVideoList implements On
38 ) { 36 ) {
39 super() 37 super()
40 38
41 this.titlePage = this.i18n('Published videos') 39 this.titlePage = $localize`Published videos`
42 this.displayOptions = { 40 this.displayOptions = {
43 ...this.displayOptions, 41 ...this.displayOptions,
44 avatar: false 42 avatar: false
@@ -72,7 +70,9 @@ export class VideoChannelVideosComponent extends AbstractVideoList implements On
72 .getVideoChannelVideos(this.videoChannel, newPagination, this.sort, this.nsfwPolicy) 70 .getVideoChannelVideos(this.videoChannel, newPagination, this.sort, this.nsfwPolicy)
73 .pipe( 71 .pipe(
74 tap(({ total }) => { 72 tap(({ total }) => {
75 this.titlePage = this.i18n(`{total, plural, =1 {Published 1 video} other {Published {{total}} videos}}`, { total }) 73 this.titlePage = total === 1
74 ? $localize`Published 1 video`
75 : $localize`Published ${total} videos`
76 }) 76 })
77 ) 77 )
78 } 78 }
diff --git a/client/src/app/+video-channels/video-channels.component.ts b/client/src/app/+video-channels/video-channels.component.ts
index cae442ee7..ea8bda1cf 100644
--- a/client/src/app/+video-channels/video-channels.component.ts
+++ b/client/src/app/+video-channels/video-channels.component.ts
@@ -6,7 +6,6 @@ import { ActivatedRoute } from '@angular/router'
6import { AuthService, Notifier, RestExtractor, ScreenService } from '@app/core' 6import { AuthService, Notifier, RestExtractor, ScreenService } from '@app/core'
7import { ListOverflowItem, VideoChannel, VideoChannelService } from '@app/shared/shared-main' 7import { ListOverflowItem, VideoChannel, VideoChannelService } from '@app/shared/shared-main'
8import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' 8import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
9import { I18n } from '@ngx-translate/i18n-polyfill'
10 9
11@Component({ 10@Component({
12 templateUrl: './video-channels.component.html', 11 templateUrl: './video-channels.component.html',
@@ -23,7 +22,6 @@ export class VideoChannelsComponent implements OnInit, OnDestroy {
23 private routeSub: Subscription 22 private routeSub: Subscription
24 23
25 constructor ( 24 constructor (
26 private i18n: I18n,
27 private route: ActivatedRoute, 25 private route: ActivatedRoute,
28 private notifier: Notifier, 26 private notifier: Notifier,
29 private authService: AuthService, 27 private authService: AuthService,
@@ -59,14 +57,14 @@ export class VideoChannelsComponent implements OnInit, OnDestroy {
59 this.subscribeButton.unsubscribe() : 57 this.subscribeButton.unsubscribe() :
60 this.subscribeButton.subscribe() 58 this.subscribeButton.subscribe()
61 return false 59 return false
62 }, undefined, this.i18n('Subscribe to the account')) 60 }, undefined, $localize`Subscribe to the account`)
63 ] 61 ]
64 if (this.isUserLoggedIn()) this.hotkeysService.add(this.hotkeys) 62 if (this.isUserLoggedIn()) this.hotkeysService.add(this.hotkeys)
65 63
66 this.links = [ 64 this.links = [
67 { label: this.i18n('VIDEOS'), routerLink: 'videos' }, 65 { label: $localize`VIDEOS`, routerLink: 'videos' },
68 { label: this.i18n('VIDEO PLAYLISTS'), routerLink: 'video-playlists' }, 66 { label: $localize`VIDEO PLAYLISTS`, routerLink: 'video-playlists' },
69 { label: this.i18n('ABOUT'), routerLink: 'about' } 67 { label: $localize`ABOUT`, routerLink: 'about' }
70 ] 68 ]
71 } 69 }
72 70
@@ -91,6 +89,6 @@ export class VideoChannelsComponent implements OnInit, OnDestroy {
91 } 89 }
92 90
93 activateCopiedMessage () { 91 activateCopiedMessage () {
94 this.notifier.success(this.i18n('Username copied')) 92 this.notifier.success($localize`Username copied`)
95 } 93 }
96} 94}
diff --git a/client/src/app/+videos/+video-edit/shared/i18n-primeng-calendar.service.ts b/client/src/app/+videos/+video-edit/shared/i18n-primeng-calendar.service.ts
index b05852ff8..2acbace48 100644
--- a/client/src/app/+videos/+video-edit/shared/i18n-primeng-calendar.service.ts
+++ b/client/src/app/+videos/+video-edit/shared/i18n-primeng-calendar.service.ts
@@ -1,76 +1,75 @@
1import { I18n } from '@ngx-translate/i18n-polyfill'
2import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
3 2
4@Injectable() 3@Injectable()
5export class I18nPrimengCalendarService { 4export class I18nPrimengCalendarService {
6 private readonly calendarLocale: any = {} 5 private readonly calendarLocale: any = {}
7 6
8 constructor (private i18n: I18n) { 7 constructor () {
9 this.calendarLocale = { 8 this.calendarLocale = {
10 firstDayOfWeek: 0, 9 firstDayOfWeek: 0,
11 dayNames: [ 10 dayNames: [
12 this.i18n('Sunday'), 11 $localize`Sunday`,
13 this.i18n('Monday'), 12 $localize`Monday`,
14 this.i18n('Tuesday'), 13 $localize`Tuesday`,
15 this.i18n('Wednesday'), 14 $localize`Wednesday`,
16 this.i18n('Thursday'), 15 $localize`Thursday`,
17 this.i18n('Friday'), 16 $localize`Friday`,
18 this.i18n('Saturday') 17 $localize`Saturday`
19 ], 18 ],
20 19
21 dayNamesShort: [ 20 dayNamesShort: [
22 this.i18n({ value: 'Sun', description: 'Day name short' }), 21 $localize`:Day name short:Sun`,
23 this.i18n({ value: 'Mon', description: 'Day name short' }), 22 $localize`:Day name short:Mon`,
24 this.i18n({ value: 'Tue', description: 'Day name short' }), 23 $localize`:Day name short:Tue`,
25 this.i18n({ value: 'Wed', description: 'Day name short' }), 24 $localize`:Day name short:Wed`,
26 this.i18n({ value: 'Thu', description: 'Day name short' }), 25 $localize`:Day name short:Thu`,
27 this.i18n({ value: 'Fri', description: 'Day name short' }), 26 $localize`:Day name short:Fri`,
28 this.i18n({ value: 'Sat', description: 'Day name short' }) 27 $localize`:Day name short:Sat`
29 ], 28 ],
30 29
31 dayNamesMin: [ 30 dayNamesMin: [
32 this.i18n({ value: 'Su', description: 'Day name min' }), 31 $localize`:Day name min:Su`,
33 this.i18n({ value: 'Mo', description: 'Day name min' }), 32 $localize`:Day name min:Mo`,
34 this.i18n({ value: 'Tu', description: 'Day name min' }), 33 $localize`:Day name min:Tu`,
35 this.i18n({ value: 'We', description: 'Day name min' }), 34 $localize`:Day name min:We`,
36 this.i18n({ value: 'Th', description: 'Day name min' }), 35 $localize`:Day name min:Th`,
37 this.i18n({ value: 'Fr', description: 'Day name min' }), 36 $localize`:Day name min:Fr`,
38 this.i18n({ value: 'Sa', description: 'Day name min' }) 37 $localize`:Day name min:Sa`
39 ], 38 ],
40 39
41 monthNames: [ 40 monthNames: [
42 this.i18n('January'), 41 $localize`January`,
43 this.i18n('February'), 42 $localize`February`,
44 this.i18n('March'), 43 $localize`March`,
45 this.i18n('April'), 44 $localize`April`,
46 this.i18n('May'), 45 $localize`May`,
47 this.i18n('June'), 46 $localize`June`,
48 this.i18n('July'), 47 $localize`July`,
49 this.i18n('August'), 48 $localize`August`,
50 this.i18n('September'), 49 $localize`September`,
51 this.i18n('October'), 50 $localize`October`,
52 this.i18n('November'), 51 $localize`November`,
53 this.i18n('December') 52 $localize`December`
54 ], 53 ],
55 54
56 monthNamesShort: [ 55 monthNamesShort: [
57 this.i18n({ value: 'Jan', description: 'Month name short' }), 56 $localize`:Month name short:Jan`,
58 this.i18n({ value: 'Feb', description: 'Month name short' }), 57 $localize`:Month name short:Feb`,
59 this.i18n({ value: 'Mar', description: 'Month name short' }), 58 $localize`:Month name short:Mar`,
60 this.i18n({ value: 'Apr', description: 'Month name short' }), 59 $localize`:Month name short:Apr`,
61 this.i18n({ value: 'May', description: 'Month name short' }), 60 $localize`:Month name short:May`,
62 this.i18n({ value: 'Jun', description: 'Month name short' }), 61 $localize`:Month name short:Jun`,
63 this.i18n({ value: 'Jul', description: 'Month name short' }), 62 $localize`:Month name short:Jul`,
64 this.i18n({ value: 'Aug', description: 'Month name short' }), 63 $localize`:Month name short:Aug`,
65 this.i18n({ value: 'Sep', description: 'Month name short' }), 64 $localize`:Month name short:Sep`,
66 this.i18n({ value: 'Oct', description: 'Month name short' }), 65 $localize`:Month name short:Oct`,
67 this.i18n({ value: 'Nov', description: 'Month name short' }), 66 $localize`:Month name short:Nov`,
68 this.i18n({ value: 'Dec', description: 'Month name short' }) 67 $localize`:Month name short:Dec`
69 ], 68 ],
70 69
71 today: this.i18n('Today'), 70 today: $localize`Today`,
72 71
73 clear: this.i18n('Clear') 72 clear: $localize`Clear`
74 } 73 }
75 } 74 }
76 75
@@ -86,9 +85,6 @@ export class I18nPrimengCalendarService {
86 } 85 }
87 86
88 getDateFormat () { 87 getDateFormat () {
89 return this.i18n({ 88 return $localize`:Date format in this locale.:yy-mm-dd`
90 value: 'yy-mm-dd ',
91 description: 'Date format in this locale.'
92 })
93 } 89 }
94} 90}
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts
index ba3b7c96a..050b6d931 100644
--- a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts
+++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts
@@ -7,7 +7,6 @@ import { removeElementFromArray } from '@app/helpers'
7import { FormReactiveValidationMessages, FormValidatorService, SelectChannelItem, VideoValidatorsService } from '@app/shared/shared-forms' 7import { FormReactiveValidationMessages, FormValidatorService, SelectChannelItem, VideoValidatorsService } from '@app/shared/shared-forms'
8import { InstanceService } from '@app/shared/shared-instance' 8import { InstanceService } from '@app/shared/shared-instance'
9import { VideoCaptionEdit, VideoEdit, VideoService } from '@app/shared/shared-main' 9import { VideoCaptionEdit, VideoEdit, VideoService } from '@app/shared/shared-main'
10import { I18n } from '@ngx-translate/i18n-polyfill'
11import { ServerConfig, VideoConstant, VideoPrivacy } from '@shared/models' 10import { ServerConfig, VideoConstant, VideoPrivacy } from '@shared/models'
12import { I18nPrimengCalendarService } from './i18n-primeng-calendar.service' 11import { I18nPrimengCalendarService } from './i18n-primeng-calendar.service'
13import { VideoCaptionAddModalComponent } from './video-caption-add-modal.component' 12import { VideoCaptionAddModalComponent } from './video-caption-add-modal.component'
@@ -63,7 +62,6 @@ export class VideoEditComponent implements OnInit, OnDestroy {
63 private serverService: ServerService, 62 private serverService: ServerService,
64 private instanceService: InstanceService, 63 private instanceService: InstanceService,
65 private i18nPrimengCalendarService: I18nPrimengCalendarService, 64 private i18nPrimengCalendarService: I18nPrimengCalendarService,
66 private i18n: I18n,
67 private ngZone: NgZone 65 private ngZone: NgZone
68 ) { 66 ) {
69 this.calendarLocale = this.i18nPrimengCalendarService.getCalendarLocale() 67 this.calendarLocale = this.i18nPrimengCalendarService.getCalendarLocale()
@@ -137,8 +135,8 @@ export class VideoEditComponent implements OnInit, OnDestroy {
137 .subscribe(res => { 135 .subscribe(res => {
138 this.videoLanguages = res.languages 136 this.videoLanguages = res.languages
139 .map(l => res.about.instance.languages.includes(l.id) 137 .map(l => res.about.instance.languages.includes(l.id)
140 ? { ...l, group: this.i18n('Instance languages'), groupOrder: 0 } 138 ? { ...l, group: $localize`Instance languages`, groupOrder: 0 }
141 : { ...l, group: this.i18n('All languages'), groupOrder: 1 }) 139 : { ...l, group: $localize`All languages`, groupOrder: 1 })
142 .sort((a, b) => a.groupOrder - b.groupOrder) 140 .sort((a, b) => a.groupOrder - b.groupOrder)
143 }) 141 })
144 142
@@ -148,8 +146,8 @@ export class VideoEditComponent implements OnInit, OnDestroy {
148 if (this.schedulePublicationPossible) { 146 if (this.schedulePublicationPossible) {
149 this.videoPrivacies.push({ 147 this.videoPrivacies.push({
150 id: this.SPECIAL_SCHEDULED_PRIVACY, 148 id: this.SPECIAL_SCHEDULED_PRIVACY,
151 label: this.i18n('Scheduled'), 149 label: $localize`Scheduled`,
152 description: this.i18n('Hide the video until a specific date') 150 description: $localize`Hide the video until a specific date`
153 }) 151 })
154 } 152 }
155 }) 153 })
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts
index 3a8e6eecc..e9ad8af7a 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts
@@ -4,10 +4,9 @@ import { AuthService, CanComponentDeactivate, Notifier, ServerService } from '@a
4import { scrollToTop } from '@app/helpers' 4import { scrollToTop } from '@app/helpers'
5import { FormValidatorService } from '@app/shared/shared-forms' 5import { FormValidatorService } from '@app/shared/shared-forms'
6import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main' 6import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main'
7import { VideoSend } from './video-send'
8import { LoadingBarService } from '@ngx-loading-bar/core' 7import { LoadingBarService } from '@ngx-loading-bar/core'
9import { I18n } from '@ngx-translate/i18n-polyfill'
10import { VideoPrivacy, VideoUpdate } from '@shared/models' 8import { VideoPrivacy, VideoUpdate } from '@shared/models'
9import { VideoSend } from './video-send'
11 10
12@Component({ 11@Component({
13 selector: 'my-video-import-torrent', 12 selector: 'my-video-import-torrent',
@@ -43,9 +42,8 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Ca
43 protected videoService: VideoService, 42 protected videoService: VideoService,
44 protected videoCaptionService: VideoCaptionService, 43 protected videoCaptionService: VideoCaptionService,
45 private router: Router, 44 private router: Router,
46 private videoImportService: VideoImportService, 45 private videoImportService: VideoImportService
47 private i18n: I18n 46 ) {
48 ) {
49 super() 47 super()
50 } 48 }
51 49
@@ -127,7 +125,7 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Ca
127 .subscribe( 125 .subscribe(
128 () => { 126 () => {
129 this.isUpdatingVideo = false 127 this.isUpdatingVideo = false
130 this.notifier.success(this.i18n('Video to import updated.')) 128 this.notifier.success($localize`Video to import updated.`)
131 129
132 this.router.navigate([ '/my-account', 'video-imports' ]) 130 this.router.navigate([ '/my-account', 'video-imports' ])
133 }, 131 },
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts
index da25663d7..8bad81097 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts
@@ -5,10 +5,9 @@ import { AuthService, CanComponentDeactivate, Notifier, ServerService } from '@a
5import { getAbsoluteAPIUrl, scrollToTop } from '@app/helpers' 5import { getAbsoluteAPIUrl, scrollToTop } from '@app/helpers'
6import { FormValidatorService } from '@app/shared/shared-forms' 6import { FormValidatorService } from '@app/shared/shared-forms'
7import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main' 7import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main'
8import { VideoSend } from './video-send'
9import { LoadingBarService } from '@ngx-loading-bar/core' 8import { LoadingBarService } from '@ngx-loading-bar/core'
10import { I18n } from '@ngx-translate/i18n-polyfill'
11import { VideoPrivacy, VideoUpdate } from '@shared/models' 9import { VideoPrivacy, VideoUpdate } from '@shared/models'
10import { VideoSend } from './video-send'
12 11
13@Component({ 12@Component({
14 selector: 'my-video-import-url', 13 selector: 'my-video-import-url',
@@ -42,9 +41,8 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, CanCom
42 protected videoService: VideoService, 41 protected videoService: VideoService,
43 protected videoCaptionService: VideoCaptionService, 42 protected videoCaptionService: VideoCaptionService,
44 private router: Router, 43 private router: Router,
45 private videoImportService: VideoImportService, 44 private videoImportService: VideoImportService
46 private i18n: I18n 45 ) {
47 ) {
48 super() 46 super()
49 } 47 }
50 48
@@ -137,7 +135,7 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, CanCom
137 .subscribe( 135 .subscribe(
138 () => { 136 () => {
139 this.isUpdatingVideo = false 137 this.isUpdatingVideo = false
140 this.notifier.success(this.i18n('Video to import updated.')) 138 this.notifier.success($localize`Video to import updated.`)
141 139
142 this.router.navigate([ '/my-account', 'video-imports' ]) 140 this.router.navigate([ '/my-account', 'video-imports' ])
143 }, 141 },
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
index e18e3c9a7..416b655a4 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
@@ -7,7 +7,6 @@ import { scrollToTop } from '@app/helpers'
7import { FormValidatorService } from '@app/shared/shared-forms' 7import { FormValidatorService } from '@app/shared/shared-forms'
8import { BytesPipe, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' 8import { BytesPipe, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main'
9import { LoadingBarService } from '@ngx-loading-bar/core' 9import { LoadingBarService } from '@ngx-loading-bar/core'
10import { I18n } from '@ngx-translate/i18n-polyfill'
11import { VideoPrivacy } from '@shared/models' 10import { VideoPrivacy } from '@shared/models'
12import { VideoSend } from './video-send' 11import { VideoSend } from './video-send'
13 12
@@ -59,9 +58,8 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
59 protected videoService: VideoService, 58 protected videoService: VideoService,
60 protected videoCaptionService: VideoCaptionService, 59 protected videoCaptionService: VideoCaptionService,
61 private userService: UserService, 60 private userService: UserService,
62 private router: Router, 61 private router: Router
63 private i18n: I18n 62 ) {
64 ) {
65 super() 63 super()
66 } 64 }
67 65
@@ -88,10 +86,10 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
88 86
89 if (this.videoUploaded === true) { 87 if (this.videoUploaded === true) {
90 // FIXME: cannot concatenate strings inside i18n service :/ 88 // FIXME: cannot concatenate strings inside i18n service :/
91 text = this.i18n('Your video was uploaded to your account and is private.') + ' ' + 89 text = $localize`Your video was uploaded to your account and is private.` + ' ' +
92 this.i18n('But associated data (tags, description...) will be lost, are you sure you want to leave this page?') 90 $localize`But associated data (tags, description...) will be lost, are you sure you want to leave this page?`
93 } else { 91 } else {
94 text = this.i18n('Your video is not uploaded yet, are you sure you want to leave this page?') 92 text = $localize`Your video is not uploaded yet, are you sure you want to leave this page?`
95 } 93 }
96 94
97 return { 95 return {
@@ -111,9 +109,9 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
111 109
112 getAudioUploadLabel () { 110 getAudioUploadLabel () {
113 const videofile = this.getVideoFile() 111 const videofile = this.getVideoFile()
114 if (!videofile) return this.i18n('Upload') 112 if (!videofile) return $localize`Upload`
115 113
116 return this.i18n('Upload {{videofileName}}', { videofileName: videofile.name }) 114 return $localize`Upload ${videofile.name}`
117 } 115 }
118 116
119 fileChange () { 117 fileChange () {
@@ -130,7 +128,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
130 128
131 this.firstStepError.emit() 129 this.firstStepError.emit()
132 130
133 this.notifier.info(this.i18n('Upload cancelled')) 131 this.notifier.info($localize`Upload cancelled`)
134 } 132 }
135 } 133 }
136 134
@@ -242,7 +240,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
242 this.isUpdatingVideo = false 240 this.isUpdatingVideo = false
243 this.isUploadingVideo = false 241 this.isUploadingVideo = false
244 242
245 this.notifier.success(this.i18n('Video published.')) 243 this.notifier.success($localize`Video published.`)
246 this.router.navigate([ '/videos/watch', video.uuid ]) 244 this.router.navigate([ '/videos/watch', video.uuid ])
247 }, 245 },
248 246
@@ -260,14 +258,12 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
260 // Check global user quota 258 // Check global user quota
261 const videoQuota = this.authService.getUser().videoQuota 259 const videoQuota = this.authService.getUser().videoQuota
262 if (videoQuota !== -1 && (this.userVideoQuotaUsed + videofile.size) > videoQuota) { 260 if (videoQuota !== -1 && (this.userVideoQuotaUsed + videofile.size) > videoQuota) {
263 const msg = this.i18n( 261 const videoSizeBytes = bytePipes.transform(videofile.size, 0)
264 'Your video quota is exceeded with this video (video size: {{videoSize}}, used: {{videoQuotaUsed}}, quota: {{videoQuota}})', 262 const videoQuotaUsedBytes = bytePipes.transform(this.userVideoQuotaUsed, 0)
265 { 263 const videoQuotaBytes = bytePipes.transform(videoQuota, 0)
266 videoSize: bytePipes.transform(videofile.size, 0), 264
267 videoQuotaUsed: bytePipes.transform(this.userVideoQuotaUsed, 0), 265 const msg = $localize`Your video quota is exceeded with this video (
268 videoQuota: bytePipes.transform(videoQuota, 0) 266video size: ${videoSizeBytes}, used: ${videoQuotaUsedBytes}, quota: ${videoQuotaBytes})`
269 }
270 )
271 this.notifier.error(msg) 267 this.notifier.error(msg)
272 268
273 return false 269 return false
@@ -282,14 +278,12 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
282 // Check daily user quota 278 // Check daily user quota
283 const videoQuotaDaily = this.authService.getUser().videoQuotaDaily 279 const videoQuotaDaily = this.authService.getUser().videoQuotaDaily
284 if (videoQuotaDaily !== -1 && (this.userVideoQuotaUsedDaily + videofile.size) > videoQuotaDaily) { 280 if (videoQuotaDaily !== -1 && (this.userVideoQuotaUsedDaily + videofile.size) > videoQuotaDaily) {
285 const msg = this.i18n( 281 const videoSizeBytes = bytePipes.transform(videofile.size, 0)
286 'Your daily video quota is exceeded with this video (video size: {{videoSize}}, used: {{quotaUsedDaily}}, quota: {{quotaDaily}})', 282 const quotaUsedDailyBytes = bytePipes.transform(this.userVideoQuotaUsedDaily, 0)
287 { 283 const quotaDailyBytes = bytePipes.transform(videoQuotaDaily, 0)
288 videoSize: bytePipes.transform(videofile.size, 0), 284
289 quotaUsedDaily: bytePipes.transform(this.userVideoQuotaUsedDaily, 0), 285 const msg = $localize`Your daily video quota is exceeded with this video (
290 quotaDaily: bytePipes.transform(videoQuotaDaily, 0) 286video size: ${videoSizeBytes}, used: ${quotaUsedDailyBytes}, quota: ${quotaDailyBytes})`
291 }
292 )
293 this.notifier.error(msg) 287 this.notifier.error(msg)
294 288
295 return false 289 return false
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 abd08f05c..2e1d0f89d 100644
--- a/client/src/app/+videos/+video-edit/video-update.component.ts
+++ b/client/src/app/+videos/+video-edit/video-update.component.ts
@@ -5,7 +5,6 @@ import { Notifier } from '@app/core'
5import { FormReactive, FormValidatorService, SelectChannelItem } from '@app/shared/shared-forms' 5import { FormReactive, FormValidatorService, SelectChannelItem } from '@app/shared/shared-forms'
6import { VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main' 6import { VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main'
7import { LoadingBarService } from '@ngx-loading-bar/core' 7import { LoadingBarService } from '@ngx-loading-bar/core'
8import { I18n } from '@ngx-translate/i18n-polyfill'
9import { VideoPrivacy } from '@shared/models' 8import { VideoPrivacy } from '@shared/models'
10 9
11@Component({ 10@Component({
@@ -31,9 +30,8 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
31 private notifier: Notifier, 30 private notifier: Notifier,
32 private videoService: VideoService, 31 private videoService: VideoService,
33 private loadingBar: LoadingBarService, 32 private loadingBar: LoadingBarService,
34 private videoCaptionService: VideoCaptionService, 33 private videoCaptionService: VideoCaptionService
35 private i18n: I18n 34 ) {
36 ) {
37 super() 35 super()
38 } 36 }
39 37
@@ -78,7 +76,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
78 canDeactivate (): { canDeactivate: boolean, text?: string } { 76 canDeactivate (): { canDeactivate: boolean, text?: string } {
79 if (this.updateDone === true) return { canDeactivate: true } 77 if (this.updateDone === true) return { canDeactivate: true }
80 78
81 const text = this.i18n('You have unsaved changes! If you leave, your changes will be lost.') 79 const text = $localize`You have unsaved changes! If you leave, your changes will be lost.`
82 80
83 for (const caption of this.videoCaptions) { 81 for (const caption of this.videoCaptions) {
84 if (caption.action) return { canDeactivate: false, text } 82 if (caption.action) return { canDeactivate: false, text }
@@ -115,7 +113,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
115 this.updateDone = true 113 this.updateDone = true
116 this.isUpdatingVideo = false 114 this.isUpdatingVideo = false
117 this.loadingBar.useRef().complete() 115 this.loadingBar.useRef().complete()
118 this.notifier.success(this.i18n('Video updated.')) 116 this.notifier.success($localize`Video updated.`)
119 this.router.navigate([ '/videos/watch', this.video.uuid ]) 117 this.router.navigate([ '/videos/watch', this.video.uuid ])
120 }, 118 },
121 119
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment.component.ts b/client/src/app/+videos/+video-watch/comment/video-comment.component.ts
index 36ec6e9f9..a84e91fd3 100644
--- a/client/src/app/+videos/+video-watch/comment/video-comment.component.ts
+++ b/client/src/app/+videos/+video-watch/comment/video-comment.component.ts
@@ -5,7 +5,6 @@ import { AuthService } from '@app/core/auth'
5import { Account, Actor, DropdownAction, Video } from '@app/shared/shared-main' 5import { Account, Actor, DropdownAction, Video } from '@app/shared/shared-main'
6import { CommentReportComponent } from '@app/shared/shared-moderation/report-modals/comment-report.component' 6import { CommentReportComponent } from '@app/shared/shared-moderation/report-modals/comment-report.component'
7import { VideoComment, VideoCommentThreadTree } from '@app/shared/shared-video-comment' 7import { VideoComment, VideoCommentThreadTree } from '@app/shared/shared-video-comment'
8import { I18n } from '@ngx-translate/i18n-polyfill'
9import { User, UserRight } from '@shared/models' 8import { User, UserRight } from '@shared/models'
10 9
11@Component({ 10@Component({
@@ -39,7 +38,6 @@ export class VideoCommentComponent implements OnInit, OnChanges {
39 commentUser: User 38 commentUser: User
40 39
41 constructor ( 40 constructor (
42 private i18n: I18n,
43 private markdownService: MarkdownService, 41 private markdownService: MarkdownService,
44 private authService: AuthService, 42 private authService: AuthService,
45 private userService: UserService, 43 private userService: UserService,
@@ -138,7 +136,7 @@ export class VideoCommentComponent implements OnInit, OnChanges {
138 if (this.isUserLoggedIn() && this.comment.isDeleted === false && this.authService.getUser().account.id !== this.comment.account.id) { 136 if (this.isUserLoggedIn() && this.comment.isDeleted === false && this.authService.getUser().account.id !== this.comment.account.id) {
139 this.prependModerationActions = [ 137 this.prependModerationActions = [
140 { 138 {
141 label: this.i18n('Report comment'), 139 label: $localize`Report comment`,
142 handler: () => this.showReportModal() 140 handler: () => this.showReportModal()
143 } 141 }
144 ] 142 ]
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 66494a20a..517844ab2 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
@@ -5,7 +5,6 @@ import { AuthService, ComponentPagination, ConfirmService, hasMoreItems, Notifie
5import { HooksService } from '@app/core/plugins/hooks.service' 5import { HooksService } from '@app/core/plugins/hooks.service'
6import { Syndication, VideoDetails } from '@app/shared/shared-main' 6import { Syndication, VideoDetails } from '@app/shared/shared-main'
7import { VideoComment, VideoCommentService, VideoCommentThreadTree } from '@app/shared/shared-video-comment' 7import { VideoComment, VideoCommentService, VideoCommentThreadTree } from '@app/shared/shared-video-comment'
8import { I18n } from '@ngx-translate/i18n-polyfill'
9 8
10@Component({ 9@Component({
11 selector: 'my-video-comments', 10 selector: 'my-video-comments',
@@ -43,7 +42,6 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
43 private confirmService: ConfirmService, 42 private confirmService: ConfirmService,
44 private videoCommentService: VideoCommentService, 43 private videoCommentService: VideoCommentService,
45 private activatedRoute: ActivatedRoute, 44 private activatedRoute: ActivatedRoute,
46 private i18n: I18n,
47 private hooks: HooksService 45 private hooks: HooksService
48 ) {} 46 ) {}
49 47
@@ -162,12 +160,12 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
162 let message = 'Do you really want to delete this comment?' 160 let message = 'Do you really want to delete this comment?'
163 161
164 if (commentToDelete.isLocal || this.video.isLocal) { 162 if (commentToDelete.isLocal || this.video.isLocal) {
165 message += this.i18n(' The deletion will be sent to remote instances so they can reflect the change.') 163 message += $localize` The deletion will be sent to remote instances so they can reflect the change.`
166 } else { 164 } else {
167 message += this.i18n(' It is a remote comment, so the deletion will only be effective on your instance.') 165 message += $localize` It is a remote comment, so the deletion will only be effective on your instance.`
168 } 166 }
169 167
170 const res = await this.confirmService.confirm(message, this.i18n('Delete')) 168 const res = await this.confirmService.confirm(message, $localize`Delete`)
171 if (res === false) return 169 if (res === false) return
172 170
173 this.videoCommentService.deleteVideoComment(commentToDelete.videoId, commentToDelete.id) 171 this.videoCommentService.deleteVideoComment(commentToDelete.videoId, commentToDelete.id)
diff --git a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.ts b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.ts
index d2372023f..a1c8e0661 100644
--- a/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.ts
+++ b/client/src/app/+videos/+video-watch/recommendations/recommended-videos.component.ts
@@ -4,10 +4,9 @@ import { AuthService, Notifier, SessionStorageService, User, UserService } from
4import { Video } from '@app/shared/shared-main' 4import { Video } from '@app/shared/shared-main'
5import { MiniatureDisplayOptions } from '@app/shared/shared-video-miniature' 5import { MiniatureDisplayOptions } from '@app/shared/shared-video-miniature'
6import { VideoPlaylist } from '@app/shared/shared-video-playlist' 6import { VideoPlaylist } from '@app/shared/shared-video-playlist'
7import { I18n } from '@ngx-translate/i18n-polyfill' 7import { UserLocalStorageKeys } from '@root-helpers/users'
8import { RecommendationInfo } from './recommendation-info.model' 8import { RecommendationInfo } from './recommendation-info.model'
9import { RecommendedVideosStore } from './recommended-videos.store' 9import { RecommendedVideosStore } from './recommended-videos.store'
10import { UserLocalStorageKeys } from '@root-helpers/users'
11 10
12@Component({ 11@Component({
13 selector: 'my-recommended-videos', 12 selector: 'my-recommended-videos',
@@ -38,7 +37,6 @@ export class RecommendedVideosComponent implements OnInit, OnChanges {
38 private userService: UserService, 37 private userService: UserService,
39 private authService: AuthService, 38 private authService: AuthService,
40 private notifier: Notifier, 39 private notifier: Notifier,
41 private i18n: I18n,
42 private store: RecommendedVideosStore, 40 private store: RecommendedVideosStore,
43 private sessionStorageService: SessionStorageService 41 private sessionStorageService: SessionStorageService
44 ) { 42 ) {
@@ -58,7 +56,7 @@ export class RecommendedVideosComponent implements OnInit, OnChanges {
58 ) 56 )
59 } 57 }
60 58
61 this.autoPlayNextVideoTooltip = this.i18n('When active, the next video is automatically played after the current one.') 59 this.autoPlayNextVideoTooltip = $localize`When active, the next video is automatically played after the current one.`
62 } 60 }
63 61
64 ngOnInit () { 62 ngOnInit () {
diff --git a/client/src/app/+videos/+video-watch/video-duration-formatter.pipe.ts b/client/src/app/+videos/+video-watch/video-duration-formatter.pipe.ts
index 4b6767415..19b34f984 100644
--- a/client/src/app/+videos/+video-watch/video-duration-formatter.pipe.ts
+++ b/client/src/app/+videos/+video-watch/video-duration-formatter.pipe.ts
@@ -1,28 +1,23 @@
1import { Pipe, PipeTransform } from '@angular/core' 1import { Pipe, PipeTransform } from '@angular/core'
2import { I18n } from '@ngx-translate/i18n-polyfill'
3 2
4@Pipe({ 3@Pipe({
5 name: 'myVideoDurationFormatter' 4 name: 'myVideoDurationFormatter'
6}) 5})
7export class VideoDurationPipe implements PipeTransform { 6export class VideoDurationPipe implements PipeTransform {
8 7
9 constructor (private i18n: I18n) {
10
11 }
12
13 transform (value: number): string { 8 transform (value: number): string {
14 const hours = Math.floor(value / 3600) 9 const hours = Math.floor(value / 3600)
15 const minutes = Math.floor((value % 3600) / 60) 10 const minutes = Math.floor((value % 3600) / 60)
16 const seconds = value % 60 11 const seconds = value % 60
17 12
18 if (hours > 0) { 13 if (hours > 0) {
19 return this.i18n('{{hours}} h {{minutes}} min {{seconds}} sec', { hours, minutes, seconds }) 14 return $localize`${hours} h ${minutes} min ${seconds} sec`
20 } 15 }
21 16
22 if (minutes > 0) { 17 if (minutes > 0) {
23 return this.i18n('{{minutes}} min {{seconds}} sec', { minutes, seconds }) 18 return $localize`${minutes} min ${seconds} sec`
24 } 19 }
25 20
26 return this.i18n('{{seconds}} sec', { seconds }) 21 return $localize`${seconds} sec`
27 } 22 }
28} 23}
diff --git a/client/src/app/+videos/+video-watch/video-watch-playlist.component.ts b/client/src/app/+videos/+video-watch/video-watch-playlist.component.ts
index 519ce2974..c60ca4671 100644
--- a/client/src/app/+videos/+video-watch/video-watch-playlist.component.ts
+++ b/client/src/app/+videos/+video-watch/video-watch-playlist.component.ts
@@ -1,9 +1,8 @@
1import { Component, Input } from '@angular/core' 1import { Component, Input } from '@angular/core'
2import { Router } from '@angular/router' 2import { Router } from '@angular/router'
3import { AuthService, ComponentPagination, LocalStorageService, Notifier, SessionStorageService, UserService } from '@app/core' 3import { AuthService, ComponentPagination, LocalStorageService, Notifier, SessionStorageService, UserService } from '@app/core'
4import { peertubeLocalStorage, peertubeSessionStorage } from '@root-helpers/peertube-web-storage'
5import { VideoPlaylist, VideoPlaylistElement, VideoPlaylistService } from '@app/shared/shared-video-playlist' 4import { VideoPlaylist, VideoPlaylistElement, VideoPlaylistService } from '@app/shared/shared-video-playlist'
6import { I18n } from '@ngx-translate/i18n-polyfill' 5import { peertubeLocalStorage, peertubeSessionStorage } from '@root-helpers/peertube-web-storage'
7import { VideoDetails, VideoPlaylistPrivacy } from '@shared/models' 6import { VideoDetails, VideoPlaylistPrivacy } from '@shared/models'
8 7
9@Component({ 8@Component({
@@ -36,7 +35,6 @@ export class VideoWatchPlaylistComponent {
36 private userService: UserService, 35 private userService: UserService,
37 private auth: AuthService, 36 private auth: AuthService,
38 private notifier: Notifier, 37 private notifier: Notifier,
39 private i18n: I18n,
40 private videoPlaylist: VideoPlaylistService, 38 private videoPlaylist: VideoPlaylistService,
41 private localStorageService: LocalStorageService, 39 private localStorageService: LocalStorageService,
42 private sessionStorageService: SessionStorageService, 40 private sessionStorageService: SessionStorageService,
@@ -189,13 +187,13 @@ export class VideoWatchPlaylistComponent {
189 187
190 private setAutoPlayNextVideoPlaylistSwitchText () { 188 private setAutoPlayNextVideoPlaylistSwitchText () {
191 this.autoPlayNextVideoPlaylistSwitchText = this.autoPlayNextVideoPlaylist 189 this.autoPlayNextVideoPlaylistSwitchText = this.autoPlayNextVideoPlaylist
192 ? this.i18n('Stop autoplaying next video') 190 ? $localize`Stop autoplaying next video`
193 : this.i18n('Autoplay next video') 191 : $localize`Autoplay next video`
194 } 192 }
195 193
196 private setLoopPlaylistSwitchText () { 194 private setLoopPlaylistSwitchText () {
197 this.loopPlaylistSwitchText = this.loopPlaylist 195 this.loopPlaylistSwitchText = this.loopPlaylist
198 ? this.i18n('Stop looping playlist videos') 196 ? $localize`Stop looping playlist videos`
199 : this.i18n('Loop playlist videos') 197 : $localize`Loop playlist videos`
200 } 198 }
201} 199}
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.ts b/client/src/app/+videos/+video-watch/video-watch.component.ts
index a53af210a..fb89bf6cd 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/+videos/+video-watch/video-watch.component.ts
@@ -14,7 +14,6 @@ import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
14import { VideoDownloadComponent } from '@app/shared/shared-video-miniature' 14import { VideoDownloadComponent } from '@app/shared/shared-video-miniature'
15import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' 15import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
16import { MetaService } from '@ngx-meta/core' 16import { MetaService } from '@ngx-meta/core'
17import { I18n } from '@ngx-translate/i18n-polyfill'
18import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' 17import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
19import { ServerConfig, UserVideoRateType, VideoCaption, VideoPrivacy, VideoState } from '@shared/models' 18import { ServerConfig, UserVideoRateType, VideoCaption, VideoPrivacy, VideoState } from '@shared/models'
20import { getStoredP2PEnabled, getStoredTheater } from '../../../assets/player/peertube-player-local-storage' 19import { getStoredP2PEnabled, getStoredTheater } from '../../../assets/player/peertube-player-local-storage'
@@ -97,16 +96,15 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
97 private zone: NgZone, 96 private zone: NgZone,
98 private redirectService: RedirectService, 97 private redirectService: RedirectService,
99 private videoCaptionService: VideoCaptionService, 98 private videoCaptionService: VideoCaptionService,
100 private i18n: I18n,
101 private hotkeysService: HotkeysService, 99 private hotkeysService: HotkeysService,
102 private hooks: HooksService, 100 private hooks: HooksService,
103 private location: PlatformLocation, 101 private location: PlatformLocation,
104 @Inject(LOCALE_ID) private localeId: string 102 @Inject(LOCALE_ID) private localeId: string
105 ) { 103 ) {
106 this.tooltipLike = this.i18n('Like this video') 104 this.tooltipLike = $localize`Like this video`
107 this.tooltipDislike = this.i18n('Dislike this video') 105 this.tooltipDislike = $localize`Dislike this video`
108 this.tooltipSupport = this.i18n('Support options for this video') 106 this.tooltipSupport = $localize`Support options for this video`
109 this.tooltipSaveToPlaylist = this.i18n('Save to playlist') 107 this.tooltipSaveToPlaylist = $localize`Save to playlist`
110 } 108 }
111 109
112 get user () { 110 get user () {
@@ -188,7 +186,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
188 getRatePopoverText () { 186 getRatePopoverText () {
189 if (this.isUserLoggedIn()) return undefined 187 if (this.isUserLoggedIn()) return undefined
190 188
191 return this.i18n('You need to be connected to rate this content.') 189 return $localize`You need to be connected to rate this content.`
192 } 190 }
193 191
194 showMoreDescription () { 192 showMoreDescription () {
@@ -409,10 +407,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
409 } 407 }
410 408
411 private setVideoLikesBarTooltipText () { 409 private setVideoLikesBarTooltipText () {
412 this.likesBarTooltipText = this.i18n('{{likesNumber}} likes / {{dislikesNumber}} dislikes', { 410 this.likesBarTooltipText = `${this.video.likes} likes / ${this.video.dislikes} dislikes`
413 likesNumber: this.video.likes,
414 dislikesNumber: this.video.dislikes
415 })
416 } 411 }
417 412
418 private handleError (err: any) { 413 private handleError (err: any) {
@@ -465,8 +460,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
465 460
466 if (this.isVideoBlur(this.video)) { 461 if (this.isVideoBlur(this.video)) {
467 const res = await this.confirmService.confirm( 462 const res = await this.confirmService.confirm(
468 this.i18n('This video contains mature or explicit content. Are you sure you want to watch it?'), 463 $localize`This video contains mature or explicit content. Are you sure you want to watch it?`,
469 this.i18n('Mature or explicit content') 464 $localize`Mature or explicit content`
470 ) 465 )
471 if (res === false) return this.location.back() 466 if (res === false) return this.location.back()
472 } 467 }
@@ -515,9 +510,9 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
515 */ 510 */
516 this.player.upnext({ 511 this.player.upnext({
517 timeout: 10000, // 10s 512 timeout: 10000, // 10s
518 headText: this.i18n('Up Next'), 513 headText: $localize`Up Next`,
519 cancelText: this.i18n('Cancel'), 514 cancelText: $localize`Cancel`,
520 suspendedText: this.i18n('Autoplay is suspended'), 515 suspendedText: $localize`Autoplay is suspended`,
521 getTitle: () => this.nextVideoTitle, 516 getTitle: () => this.nextVideoTitle,
522 next: () => this.zone.run(() => this.autoplayNext()), 517 next: () => this.zone.run(() => this.autoplayNext()),
523 condition: () => { 518 condition: () => {
@@ -781,22 +776,22 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
781 private initHotkeys () { 776 private initHotkeys () {
782 this.hotkeys = [ 777 this.hotkeys = [
783 // These hotkeys are managed by the player 778 // These hotkeys are managed by the player
784 new Hotkey('f', e => e, undefined, this.i18n('Enter/exit fullscreen (requires player focus)')), 779 new Hotkey('f', e => e, undefined, $localize`Enter/exit fullscreen (requires player focus)`),
785 new Hotkey('space', e => e, undefined, this.i18n('Play/Pause the video (requires player focus)')), 780 new Hotkey('space', e => e, undefined, $localize`Play/Pause the video (requires player focus)`),
786 new Hotkey('m', e => e, undefined, this.i18n('Mute/unmute the video (requires player focus)')), 781 new Hotkey('m', e => e, undefined, $localize`Mute/unmute the video (requires player focus)`),
787 782
788 new Hotkey('0-9', e => e, undefined, this.i18n('Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)')), 783 new Hotkey('0-9', e => e, undefined, $localize`Skip to a percentage of the video: 0 is 0% and 9 is 90% (requires player focus)`),
789 784
790 new Hotkey('up', e => e, undefined, this.i18n('Increase the volume (requires player focus)')), 785 new Hotkey('up', e => e, undefined, $localize`Increase the volume (requires player focus)`),
791 new Hotkey('down', e => e, undefined, this.i18n('Decrease the volume (requires player focus)')), 786 new Hotkey('down', e => e, undefined, $localize`Decrease the volume (requires player focus)`),
792 787
793 new Hotkey('right', e => e, undefined, this.i18n('Seek the video forward (requires player focus)')), 788 new Hotkey('right', e => e, undefined, $localize`Seek the video forward (requires player focus)`),
794 new Hotkey('left', e => e, undefined, this.i18n('Seek the video backward (requires player focus)')), 789 new Hotkey('left', e => e, undefined, $localize`Seek the video backward (requires player focus)`),
795 790
796 new Hotkey('>', e => e, undefined, this.i18n('Increase playback rate (requires player focus)')), 791 new Hotkey('>', e => e, undefined, $localize`Increase playback rate (requires player focus)`),
797 new Hotkey('<', e => e, undefined, this.i18n('Decrease playback rate (requires player focus)')), 792 new Hotkey('<', e => e, undefined, $localize`Decrease playback rate (requires player focus)`),
798 793
799 new Hotkey('.', e => e, undefined, this.i18n('Navigate in the video frame by frame (requires player focus)')) 794 new Hotkey('.', e => e, undefined, $localize`Navigate in the video frame by frame (requires player focus)`)
800 ] 795 ]
801 796
802 if (this.isUserLoggedIn()) { 797 if (this.isUserLoggedIn()) {
@@ -804,17 +799,17 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
804 new Hotkey('shift+l', () => { 799 new Hotkey('shift+l', () => {
805 this.setLike() 800 this.setLike()
806 return false 801 return false
807 }, undefined, this.i18n('Like the video')), 802 }, undefined, $localize`Like the video`),
808 803
809 new Hotkey('shift+d', () => { 804 new Hotkey('shift+d', () => {
810 this.setDislike() 805 this.setDislike()
811 return false 806 return false
812 }, undefined, this.i18n('Dislike the video')), 807 }, undefined, $localize`Dislike the video`),
813 808
814 new Hotkey('shift+s', () => { 809 new Hotkey('shift+s', () => {
815 this.subscribeButton.subscribed ? this.subscribeButton.unsubscribe() : this.subscribeButton.subscribe() 810 this.subscribeButton.subscribed ? this.subscribeButton.unsubscribe() : this.subscribeButton.subscribe()
816 return false 811 return false
817 }, undefined, this.i18n('Subscribe to the account')) 812 }, undefined, $localize`Subscribe to the account`)
818 ]) 813 ])
819 } 814 }
820 815
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 b4c71ac49..07063d4d4 100644
--- a/client/src/app/+videos/video-list/video-local.component.ts
+++ b/client/src/app/+videos/video-list/video-local.component.ts
@@ -5,7 +5,6 @@ import { HooksService } from '@app/core/plugins/hooks.service'
5import { immutableAssign } from '@app/helpers' 5import { immutableAssign } from '@app/helpers'
6import { VideoService } from '@app/shared/shared-main' 6import { VideoService } from '@app/shared/shared-main'
7import { AbstractVideoList } from '@app/shared/shared-video-miniature' 7import { AbstractVideoList } from '@app/shared/shared-video-miniature'
8import { I18n } from '@ngx-translate/i18n-polyfill'
9import { UserRight, VideoFilter, VideoSortField } from '@shared/models' 8import { UserRight, VideoFilter, VideoSortField } from '@shared/models'
10 9
11@Component({ 10@Component({
@@ -21,7 +20,6 @@ export class VideoLocalComponent extends AbstractVideoList implements OnInit, On
21 useUserVideoPreferences = true 20 useUserVideoPreferences = true
22 21
23 constructor ( 22 constructor (
24 protected i18n: I18n,
25 protected router: Router, 23 protected router: Router,
26 protected serverService: ServerService, 24 protected serverService: ServerService,
27 protected route: ActivatedRoute, 25 protected route: ActivatedRoute,
@@ -35,7 +33,7 @@ export class VideoLocalComponent extends AbstractVideoList implements OnInit, On
35 ) { 33 ) {
36 super() 34 super()
37 35
38 this.titlePage = i18n('Local videos') 36 this.titlePage = $localize`Local videos`
39 } 37 }
40 38
41 ngOnInit () { 39 ngOnInit () {
diff --git a/client/src/app/+videos/video-list/video-most-liked.component.ts b/client/src/app/+videos/video-list/video-most-liked.component.ts
index ca14851bb..e5f7bd152 100644
--- a/client/src/app/+videos/video-list/video-most-liked.component.ts
+++ b/client/src/app/+videos/video-list/video-most-liked.component.ts
@@ -5,7 +5,6 @@ import { HooksService } from '@app/core/plugins/hooks.service'
5import { immutableAssign } from '@app/helpers' 5import { immutableAssign } from '@app/helpers'
6import { VideoService } from '@app/shared/shared-main' 6import { VideoService } from '@app/shared/shared-main'
7import { AbstractVideoList } from '@app/shared/shared-video-miniature' 7import { AbstractVideoList } from '@app/shared/shared-video-miniature'
8import { I18n } from '@ngx-translate/i18n-polyfill'
9import { VideoSortField } from '@shared/models' 8import { VideoSortField } from '@shared/models'
10 9
11@Component({ 10@Component({
@@ -20,7 +19,6 @@ export class VideoMostLikedComponent extends AbstractVideoList implements OnInit
20 useUserVideoPreferences = true 19 useUserVideoPreferences = true
21 20
22 constructor ( 21 constructor (
23 protected i18n: I18n,
24 protected router: Router, 22 protected router: Router,
25 protected serverService: ServerService, 23 protected serverService: ServerService,
26 protected route: ActivatedRoute, 24 protected route: ActivatedRoute,
@@ -40,8 +38,8 @@ export class VideoMostLikedComponent extends AbstractVideoList implements OnInit
40 38
41 this.generateSyndicationList() 39 this.generateSyndicationList()
42 40
43 this.titlePage = this.i18n('Most liked videos') 41 this.titlePage = $localize`Most liked videos`
44 this.titleTooltip = this.i18n('Videos that have the higher number of likes.') 42 this.titleTooltip = $localize`Videos that have the higher number of likes.`
45 } 43 }
46 44
47 getVideosObservable (page: number) { 45 getVideosObservable (page: number) {
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 c9395133f..34db6aabd 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
@@ -5,7 +5,6 @@ import { HooksService } from '@app/core/plugins/hooks.service'
5import { immutableAssign } from '@app/helpers' 5import { immutableAssign } from '@app/helpers'
6import { VideoService } from '@app/shared/shared-main' 6import { VideoService } from '@app/shared/shared-main'
7import { AbstractVideoList } from '@app/shared/shared-video-miniature' 7import { AbstractVideoList } from '@app/shared/shared-video-miniature'
8import { I18n } from '@ngx-translate/i18n-polyfill'
9import { VideoSortField } from '@shared/models' 8import { VideoSortField } from '@shared/models'
10 9
11@Component({ 10@Component({
@@ -21,7 +20,6 @@ export class VideoRecentlyAddedComponent extends AbstractVideoList implements On
21 useUserVideoPreferences = true 20 useUserVideoPreferences = true
22 21
23 constructor ( 22 constructor (
24 protected i18n: I18n,
25 protected route: ActivatedRoute, 23 protected route: ActivatedRoute,
26 protected serverService: ServerService, 24 protected serverService: ServerService,
27 protected router: Router, 25 protected router: Router,
@@ -35,7 +33,7 @@ export class VideoRecentlyAddedComponent extends AbstractVideoList implements On
35 ) { 33 ) {
36 super() 34 super()
37 35
38 this.titlePage = i18n('Recently added') 36 this.titlePage = $localize`Recently added`
39 } 37 }
40 38
41 ngOnInit () { 39 ngOnInit () {
diff --git a/client/src/app/+videos/video-list/video-trending.component.ts b/client/src/app/+videos/video-list/video-trending.component.ts
index 10eab18de..babcb9067 100644
--- a/client/src/app/+videos/video-list/video-trending.component.ts
+++ b/client/src/app/+videos/video-list/video-trending.component.ts
@@ -5,7 +5,6 @@ import { HooksService } from '@app/core/plugins/hooks.service'
5import { immutableAssign } from '@app/helpers' 5import { immutableAssign } from '@app/helpers'
6import { VideoService } from '@app/shared/shared-main' 6import { VideoService } from '@app/shared/shared-main'
7import { AbstractVideoList } from '@app/shared/shared-video-miniature' 7import { AbstractVideoList } from '@app/shared/shared-video-miniature'
8import { I18n } from '@ngx-translate/i18n-polyfill'
9import { VideoSortField } from '@shared/models' 8import { VideoSortField } from '@shared/models'
10 9
11@Component({ 10@Component({
@@ -20,7 +19,6 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
20 useUserVideoPreferences = true 19 useUserVideoPreferences = true
21 20
22 constructor ( 21 constructor (
23 protected i18n: I18n,
24 protected router: Router, 22 protected router: Router,
25 protected serverService: ServerService, 23 protected serverService: ServerService,
26 protected route: ActivatedRoute, 24 protected route: ActivatedRoute,
@@ -45,14 +43,11 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
45 const trendingDays = config.trending.videos.intervalDays 43 const trendingDays = config.trending.videos.intervalDays
46 44
47 if (trendingDays === 1) { 45 if (trendingDays === 1) {
48 this.titlePage = this.i18n('Trending for the last 24 hours') 46 this.titlePage = $localize`Trending for the last 24 hours`
49 this.titleTooltip = this.i18n('Trending videos are those totalizing the greatest number of views during the last 24 hours') 47 this.titleTooltip = $localize`Trending videos are those totalizing the greatest number of views during the last 24 hours`
50 } else { 48 } else {
51 this.titlePage = this.i18n('Trending for the last {{days}} days', { days: trendingDays }) 49 this.titlePage = `Trending for the last ${trendingDays} days`
52 this.titleTooltip = this.i18n( 50 this.titleTooltip = `Trending videos are those totalizing the greatest number of views during the last ${trendingDays} days`
53 'Trending videos are those totalizing the greatest number of views during the last {{days}} days',
54 { days: trendingDays }
55 )
56 } 51 }
57 }) 52 })
58 } 53 }
diff --git a/client/src/app/+videos/video-list/video-user-subscriptions.component.ts b/client/src/app/+videos/video-list/video-user-subscriptions.component.ts
index 41ad9b277..b02988169 100644
--- a/client/src/app/+videos/video-list/video-user-subscriptions.component.ts
+++ b/client/src/app/+videos/video-list/video-user-subscriptions.component.ts
@@ -3,10 +3,8 @@ import { ActivatedRoute, Router } from '@angular/router'
3import { AuthService, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core' 3import { AuthService, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core'
4import { HooksService } from '@app/core/plugins/hooks.service' 4import { HooksService } from '@app/core/plugins/hooks.service'
5import { immutableAssign } from '@app/helpers' 5import { immutableAssign } from '@app/helpers'
6import { VideoService } from '@app/shared/shared-main'
7import { UserSubscriptionService } from '@app/shared/shared-user-subscription' 6import { UserSubscriptionService } from '@app/shared/shared-user-subscription'
8import { AbstractVideoList, OwnerDisplayType } from '@app/shared/shared-video-miniature' 7import { AbstractVideoList, OwnerDisplayType } from '@app/shared/shared-video-miniature'
9import { I18n } from '@ngx-translate/i18n-polyfill'
10import { VideoSortField } from '@shared/models' 8import { VideoSortField } from '@shared/models'
11 9
12@Component({ 10@Component({
@@ -21,7 +19,6 @@ export class VideoUserSubscriptionsComponent extends AbstractVideoList implement
21 groupByDate = true 19 groupByDate = true
22 20
23 constructor ( 21 constructor (
24 protected i18n: I18n,
25 protected router: Router, 22 protected router: Router,
26 protected serverService: ServerService, 23 protected serverService: ServerService,
27 protected route: ActivatedRoute, 24 protected route: ActivatedRoute,
@@ -31,15 +28,14 @@ export class VideoUserSubscriptionsComponent extends AbstractVideoList implement
31 protected screenService: ScreenService, 28 protected screenService: ScreenService,
32 protected storageService: LocalStorageService, 29 protected storageService: LocalStorageService,
33 private userSubscription: UserSubscriptionService, 30 private userSubscription: UserSubscriptionService,
34 private videoService: VideoService,
35 private hooks: HooksService 31 private hooks: HooksService
36 ) { 32 ) {
37 super() 33 super()
38 34
39 this.titlePage = i18n('Videos from your subscriptions') 35 this.titlePage = $localize`Videos from your subscriptions`
40 this.actions.push({ 36 this.actions.push({
41 routerLink: '/my-account/subscriptions', 37 routerLink: '/my-account/subscriptions',
42 label: i18n('Subscriptions'), 38 label: $localize`Subscriptions`,
43 iconName: 'cog' 39 iconName: 'cog'
44 }) 40 })
45 } 41 }
diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts
index ee95d2cbb..ef0b1ae56 100644
--- a/client/src/app/app.component.ts
+++ b/client/src/app/app.component.ts
@@ -12,12 +12,11 @@ import { CustomModalComponent } from '@app/modal/custom-modal.component'
12import { InstanceConfigWarningModalComponent } from '@app/modal/instance-config-warning-modal.component' 12import { InstanceConfigWarningModalComponent } from '@app/modal/instance-config-warning-modal.component'
13import { WelcomeModalComponent } from '@app/modal/welcome-modal.component' 13import { WelcomeModalComponent } from '@app/modal/welcome-modal.component'
14import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 14import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
15import { I18n } from '@ngx-translate/i18n-polyfill' 15import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
16import { getShortLocale, is18nPath } from '@shared/core-utils/i18n' 16import { getShortLocale, is18nPath } from '@shared/core-utils/i18n'
17import { BroadcastMessageLevel, ServerConfig, UserRole } from '@shared/models' 17import { BroadcastMessageLevel, ServerConfig, UserRole } from '@shared/models'
18import { MenuService } from './core/menu/menu.service' 18import { MenuService } from './core/menu/menu.service'
19import { POP_STATE_MODAL_DISMISS } from './helpers' 19import { POP_STATE_MODAL_DISMISS } from './helpers'
20import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
21import { InstanceService } from './shared/shared-instance' 20import { InstanceService } from './shared/shared-instance'
22 21
23@Component({ 22@Component({
@@ -40,7 +39,6 @@ export class AppComponent implements OnInit, AfterViewInit {
40 constructor ( 39 constructor (
41 @Inject(DOCUMENT) private document: Document, 40 @Inject(DOCUMENT) private document: Document,
42 @Inject(LOCALE_ID) private localeId: string, 41 @Inject(LOCALE_ID) private localeId: string,
43 private i18n: I18n,
44 private viewportScroller: ViewportScroller, 42 private viewportScroller: ViewportScroller,
45 private router: Router, 43 private router: Router,
46 private authService: AuthService, 44 private authService: AuthService,
@@ -288,37 +286,37 @@ export class AppComponent implements OnInit, AfterViewInit {
288 new Hotkey(['/', 's'], (event: KeyboardEvent): boolean => { 286 new Hotkey(['/', 's'], (event: KeyboardEvent): boolean => {
289 document.getElementById('search-video').focus() 287 document.getElementById('search-video').focus()
290 return false 288 return false
291 }, undefined, this.i18n('Focus the search bar')), 289 }, undefined, $localize`Focus the search bar`),
292 290
293 new Hotkey('b', (event: KeyboardEvent): boolean => { 291 new Hotkey('b', (event: KeyboardEvent): boolean => {
294 this.menu.toggleMenu() 292 this.menu.toggleMenu()
295 return false 293 return false
296 }, undefined, this.i18n('Toggle the left menu')), 294 }, undefined, $localize`Toggle the left menu`),
297 295
298 new Hotkey('g o', (event: KeyboardEvent): boolean => { 296 new Hotkey('g o', (event: KeyboardEvent): boolean => {
299 this.router.navigate([ '/videos/overview' ]) 297 this.router.navigate([ '/videos/overview' ])
300 return false 298 return false
301 }, undefined, this.i18n('Go to the discover videos page')), 299 }, undefined, $localize`Go to the discover videos page`),
302 300
303 new Hotkey('g t', (event: KeyboardEvent): boolean => { 301 new Hotkey('g t', (event: KeyboardEvent): boolean => {
304 this.router.navigate([ '/videos/trending' ]) 302 this.router.navigate([ '/videos/trending' ])
305 return false 303 return false
306 }, undefined, this.i18n('Go to the trending videos page')), 304 }, undefined, $localize`Go to the trending videos page`),
307 305
308 new Hotkey('g r', (event: KeyboardEvent): boolean => { 306 new Hotkey('g r', (event: KeyboardEvent): boolean => {
309 this.router.navigate([ '/videos/recently-added' ]) 307 this.router.navigate([ '/videos/recently-added' ])
310 return false 308 return false
311 }, undefined, this.i18n('Go to the recently added videos page')), 309 }, undefined, $localize`Go to the recently added videos page`),
312 310
313 new Hotkey('g l', (event: KeyboardEvent): boolean => { 311 new Hotkey('g l', (event: KeyboardEvent): boolean => {
314 this.router.navigate([ '/videos/local' ]) 312 this.router.navigate([ '/videos/local' ])
315 return false 313 return false
316 }, undefined, this.i18n('Go to the local videos page')), 314 }, undefined, $localize`Go to the local videos page`),
317 315
318 new Hotkey('g u', (event: KeyboardEvent): boolean => { 316 new Hotkey('g u', (event: KeyboardEvent): boolean => {
319 this.router.navigate([ '/videos/upload' ]) 317 this.router.navigate([ '/videos/upload' ])
320 return false 318 return false
321 }, undefined, this.i18n('Go to the videos upload page')) 319 }, undefined, $localize`Go to the videos upload page`)
322 ]) 320 ])
323 } 321 }
324} 322}
diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts
index b35cc1716..5c4616153 100644
--- a/client/src/app/app.module.ts
+++ b/client/src/app/app.module.ts
@@ -81,21 +81,7 @@ registerLocaleData(localeOc, 'oc')
81 { 81 {
82 provide: APP_BASE_HREF, 82 provide: APP_BASE_HREF,
83 useValue: '/' 83 useValue: '/'
84 }, 84 }
85
86 {
87 provide: TRANSLATIONS,
88 useFactory: (locale: string) => {
89 // Default locale, nothing to translate
90 const completeLocale = getCompleteLocale(locale)
91 if (isDefaultLocale(completeLocale)) return ''
92
93 const fileLocale = buildFileLocale(locale)
94 return require(`raw-loader!../locale/angular.${fileLocale}.xlf`).default
95 },
96 deps: [ LOCALE_ID ]
97 },
98 { provide: TRANSLATIONS_FORMAT, useValue: 'xlf' }
99 ] 85 ]
100}) 86})
101export class AppModule {} 87export class AppModule {}
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts
index d3dc48267..10e2c2c58 100644
--- a/client/src/app/core/auth/auth.service.ts
+++ b/client/src/app/core/auth/auth.service.ts
@@ -6,7 +6,6 @@ import { Injectable } from '@angular/core'
6import { Router } from '@angular/router' 6import { Router } from '@angular/router'
7import { Notifier } from '@app/core/notification/notifier.service' 7import { Notifier } from '@app/core/notification/notifier.service'
8import { objectToUrlEncoded, peertubeLocalStorage } from '@root-helpers/index' 8import { objectToUrlEncoded, peertubeLocalStorage } from '@root-helpers/index'
9import { I18n } from '@ngx-translate/i18n-polyfill'
10import { MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models' 9import { MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models'
11import { environment } from '../../../environments/environment' 10import { environment } from '../../../environments/environment'
12import { RestExtractor } from '../rest/rest-extractor.service' 11import { RestExtractor } from '../rest/rest-extractor.service'
@@ -48,9 +47,8 @@ export class AuthService {
48 private notifier: Notifier, 47 private notifier: Notifier,
49 private hotkeysService: HotkeysService, 48 private hotkeysService: HotkeysService,
50 private restExtractor: RestExtractor, 49 private restExtractor: RestExtractor,
51 private router: Router, 50 private router: Router
52 private i18n: I18n 51 ) {
53 ) {
54 this.loginChanged = new Subject<AuthStatus>() 52 this.loginChanged = new Subject<AuthStatus>()
55 this.loginChangedSource = this.loginChanged.asObservable() 53 this.loginChangedSource = this.loginChanged.asObservable()
56 54
@@ -62,19 +60,19 @@ export class AuthService {
62 new Hotkey('m s', (event: KeyboardEvent): boolean => { 60 new Hotkey('m s', (event: KeyboardEvent): boolean => {
63 this.router.navigate([ '/videos/subscriptions' ]) 61 this.router.navigate([ '/videos/subscriptions' ])
64 return false 62 return false
65 }, undefined, this.i18n('Go to my subscriptions')), 63 }, undefined, $localize`Go to my subscriptions`),
66 new Hotkey('m v', (event: KeyboardEvent): boolean => { 64 new Hotkey('m v', (event: KeyboardEvent): boolean => {
67 this.router.navigate([ '/my-account/videos' ]) 65 this.router.navigate([ '/my-account/videos' ])
68 return false 66 return false
69 }, undefined, this.i18n('Go to my videos')), 67 }, undefined, $localize`Go to my videos`),
70 new Hotkey('m i', (event: KeyboardEvent): boolean => { 68 new Hotkey('m i', (event: KeyboardEvent): boolean => {
71 this.router.navigate([ '/my-account/video-imports' ]) 69 this.router.navigate([ '/my-account/video-imports' ])
72 return false 70 return false
73 }, undefined, this.i18n('Go to my imports')), 71 }, undefined, $localize`Go to my imports`),
74 new Hotkey('m c', (event: KeyboardEvent): boolean => { 72 new Hotkey('m c', (event: KeyboardEvent): boolean => {
75 this.router.navigate([ '/my-account/video-channels' ]) 73 this.router.navigate([ '/my-account/video-channels' ])
76 return false 74 return false
77 }, undefined, this.i18n('Go to my channels')) 75 }, undefined, $localize`Go to my channels`)
78 ] 76 ]
79 } 77 }
80 78
@@ -97,14 +95,12 @@ export class AuthService {
97 let errorMessage = error.message 95 let errorMessage = error.message
98 96
99 if (error.status === 403) { 97 if (error.status === 403) {
100 errorMessage = this.i18n('Cannot retrieve OAuth Client credentials: {{errorText}}.\n', { errorText: error.text }) 98 errorMessage = $localize`Cannot retrieve OAuth Client credentials: ${error.text}.
101 errorMessage += this.i18n( 99Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.`
102 'Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.'
103 )
104 } 100 }
105 101
106 // We put a bigger timeout: this is an important message 102 // We put a bigger timeout: this is an important message
107 this.notifier.error(errorMessage, this.i18n('Error'), 7000) 103 this.notifier.error(errorMessage, $localize`Error`, 7000)
108 } 104 }
109 ) 105 )
110 } 106 }
@@ -216,7 +212,7 @@ export class AuthService {
216 this.router.navigate([ '/login' ]) 212 this.router.navigate([ '/login' ])
217 213
218 return observableThrowError({ 214 return observableThrowError({
219 error: this.i18n('You need to reconnect.') 215 error: $localize`You need to reconnect.`
220 }) 216 })
221 }), 217 }),
222 share() 218 share()
diff --git a/client/src/app/core/hotkeys/hotkeys.component.ts b/client/src/app/core/hotkeys/hotkeys.component.ts
index 512a2399a..315e1a25e 100644
--- a/client/src/app/core/hotkeys/hotkeys.component.ts
+++ b/client/src/app/core/hotkeys/hotkeys.component.ts
@@ -1,7 +1,6 @@
1import { Component, OnInit, OnDestroy, Input } from '@angular/core' 1import { Hotkey, HotkeysService } from 'angular2-hotkeys'
2import { Subscription } from 'rxjs' 2import { Subscription } from 'rxjs'
3import { I18n } from '@ngx-translate/i18n-polyfill' 3import { Component, Input, OnDestroy, OnInit } from '@angular/core'
4import { HotkeysService, Hotkey } from 'angular2-hotkeys'
5 4
6@Component({ 5@Component({
7 selector : 'my-hotkeys-cheatsheet', 6 selector : 'my-hotkeys-cheatsheet',
@@ -9,16 +8,15 @@ import { HotkeysService, Hotkey } from 'angular2-hotkeys'
9 styleUrls: [ './hotkeys.component.scss' ] 8 styleUrls: [ './hotkeys.component.scss' ]
10}) 9})
11export class CheatSheetComponent implements OnInit, OnDestroy { 10export class CheatSheetComponent implements OnInit, OnDestroy {
12 @Input() title = this.i18n('Keyboard Shortcuts:') 11 @Input() title = $localize`Keyboard Shortcuts:`
13 helpVisible = false 12 helpVisible = false
14 subscription: Subscription 13 subscription: Subscription
15 14
16 hotkeys: Hotkey[] 15 hotkeys: Hotkey[]
17 16
18 constructor ( 17 constructor (
19 private hotkeysService: HotkeysService, 18 private hotkeysService: HotkeysService
20 private i18n: I18n 19 ) {}
21 ) {}
22 20
23 public ngOnInit (): void { 21 public ngOnInit (): void {
24 this.subscription = this.hotkeysService.cheatSheetToggle.subscribe((isOpen) => { 22 this.subscription = this.hotkeysService.cheatSheetToggle.subscribe((isOpen) => {
diff --git a/client/src/app/core/notification/notifier.service.ts b/client/src/app/core/notification/notifier.service.ts
index 9833c65a0..f736672bb 100644
--- a/client/src/app/core/notification/notifier.service.ts
+++ b/client/src/app/core/notification/notifier.service.ts
@@ -1,30 +1,26 @@
1import { Injectable } from '@angular/core'
2import { MessageService } from 'primeng/api' 1import { MessageService } from 'primeng/api'
3import { I18n } from '@ngx-translate/i18n-polyfill' 2import { Injectable } from '@angular/core'
4 3
5@Injectable() 4@Injectable()
6export class Notifier { 5export class Notifier {
7 readonly TIMEOUT = 5000 6 readonly TIMEOUT = 5000
8 7
9 constructor ( 8 constructor (private messageService: MessageService) { }
10 private i18n: I18n,
11 private messageService: MessageService) {
12 }
13 9
14 info (text: string, title?: string, timeout?: number) { 10 info (text: string, title?: string, timeout?: number) {
15 if (!title) title = this.i18n('Info') 11 if (!title) title = $localize`Info`
16 12
17 return this.notify('info', text, title, timeout) 13 return this.notify('info', text, title, timeout)
18 } 14 }
19 15
20 error (text: string, title?: string, timeout?: number) { 16 error (text: string, title?: string, timeout?: number) {
21 if (!title) title = this.i18n('Error') 17 if (!title) title = $localize`Error`
22 18
23 return this.notify('error', text, title, timeout) 19 return this.notify('error', text, title, timeout)
24 } 20 }
25 21
26 success (text: string, title?: string, timeout?: number) { 22 success (text: string, title?: string, timeout?: number) {
27 if (!title) title = this.i18n('Success') 23 if (!title) title = $localize`Success`
28 24
29 return this.notify('success', text, title, timeout) 25 return this.notify('success', text, title, timeout)
30 } 26 }
diff --git a/client/src/app/core/rest/rest-extractor.service.ts b/client/src/app/core/rest/rest-extractor.service.ts
index 9de964f79..36702b371 100644
--- a/client/src/app/core/rest/rest-extractor.service.ts
+++ b/client/src/app/core/rest/rest-extractor.service.ts
@@ -2,16 +2,12 @@ import { throwError as observableThrowError } from 'rxjs'
2import { Injectable } from '@angular/core' 2import { Injectable } from '@angular/core'
3import { Router } from '@angular/router' 3import { Router } from '@angular/router'
4import { dateToHuman } from '@app/helpers' 4import { dateToHuman } from '@app/helpers'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { ResultList } from '@shared/models' 5import { ResultList } from '@shared/models'
7 6
8@Injectable() 7@Injectable()
9export class RestExtractor { 8export class RestExtractor {
10 9
11 constructor ( 10 constructor (private router: Router) { }
12 private router: Router,
13 private i18n: I18n
14 ) { }
15 11
16 extractDataBool () { 12 extractDataBool () {
17 return true 13 return true
@@ -62,19 +58,18 @@ export class RestExtractor {
62 } else if (err.error && err.error.error) { 58 } else if (err.error && err.error.error) {
63 errorMessage = err.error.error 59 errorMessage = err.error.error
64 } else if (err.status === 413) { 60 } else if (err.status === 413) {
65 errorMessage = this.i18n( 61 errorMessage = $localize`Request is too large for the server.
66 'Request is too large for the server. Please contact you administrator if you want to increase the limit size.' 62 Please contact you administrator if you want to increase the limit size.`
67 )
68 } else if (err.status === 429) { 63 } else if (err.status === 429) {
69 const secondsLeft = err.headers.get('retry-after') 64 const secondsLeft = err.headers.get('retry-after')
70 if (secondsLeft) { 65 if (secondsLeft) {
71 const minutesLeft = Math.floor(parseInt(secondsLeft, 10) / 60) 66 const minutesLeft = Math.floor(parseInt(secondsLeft, 10) / 60)
72 errorMessage = this.i18n('Too many attempts, please try again after {{minutesLeft}} minutes.', { minutesLeft }) 67 errorMessage = $localize`Too many attempts, please try again after ${minutesLeft} minutes.`
73 } else { 68 } else {
74 errorMessage = this.i18n('Too many attempts, please try again later.') 69 errorMessage = $localize`Too many attempts, please try again later.`
75 } 70 }
76 } else if (err.status === 500) { 71 } else if (err.status === 500) {
77 errorMessage = this.i18n('Server error. Please retry later.') 72 errorMessage = $localize`Server error. Please retry later.`
78 } 73 }
79 74
80 errorMessage = errorMessage ? errorMessage : 'Unknown error.' 75 errorMessage = errorMessage ? errorMessage : 'Unknown error.'
diff --git a/client/src/app/core/routing/can-deactivate-guard.service.ts b/client/src/app/core/routing/can-deactivate-guard.service.ts
index e0405293a..8fce495e9 100644
--- a/client/src/app/core/routing/can-deactivate-guard.service.ts
+++ b/client/src/app/core/routing/can-deactivate-guard.service.ts
@@ -2,7 +2,6 @@ import { Observable } from 'rxjs'
2import { Injectable } from '@angular/core' 2import { Injectable } from '@angular/core'
3import { CanDeactivate } from '@angular/router' 3import { CanDeactivate } from '@angular/router'
4import { ConfirmService } from '@app/core/confirm' 4import { ConfirmService } from '@app/core/confirm'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6 5
7export type CanComponentDeactivateResult = { text?: string, canDeactivate: Observable<boolean> | boolean } 6export type CanComponentDeactivateResult = { text?: string, canDeactivate: Observable<boolean> | boolean }
8 7
@@ -12,18 +11,16 @@ export interface CanComponentDeactivate {
12 11
13@Injectable() 12@Injectable()
14export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> { 13export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
15 constructor ( 14
16 private confirmService: ConfirmService, 15 constructor (private confirmService: ConfirmService) { }
17 private i18n: I18n
18 ) { }
19 16
20 canDeactivate (component: CanComponentDeactivate) { 17 canDeactivate (component: CanComponentDeactivate) {
21 const result = component.canDeactivate() 18 const result = component.canDeactivate()
22 const text = result.text || this.i18n('All unsaved data will be lost, are you sure you want to leave this page?') 19 const text = result.text || $localize`All unsaved data will be lost, are you sure you want to leave this page?`
23 20
24 return result.canDeactivate || this.confirmService.confirm( 21 return result.canDeactivate || this.confirmService.confirm(
25 text, 22 text,
26 this.i18n('Warning') 23 $localize`Warning`
27 ) 24 )
28 } 25 }
29 26
diff --git a/client/src/app/core/users/user.service.ts b/client/src/app/core/users/user.service.ts
index 6e7a48519..01c9be5bf 100644
--- a/client/src/app/core/users/user.service.ts
+++ b/client/src/app/core/users/user.service.ts
@@ -5,7 +5,6 @@ import { HttpClient, HttpParams } from '@angular/common/http'
5import { Injectable } from '@angular/core' 5import { Injectable } from '@angular/core'
6import { AuthService } from '@app/core/auth' 6import { AuthService } from '@app/core/auth'
7import { BytesPipe } from '@app/shared/shared-main' 7import { BytesPipe } from '@app/shared/shared-main'
8import { I18n } from '@ngx-translate/i18n-polyfill'
9import { UserLocalStorageKeys } from '@root-helpers/users' 8import { UserLocalStorageKeys } from '@root-helpers/users'
10import { 9import {
11 Avatar, 10 Avatar,
@@ -38,9 +37,8 @@ export class UserService {
38 private restExtractor: RestExtractor, 37 private restExtractor: RestExtractor,
39 private restService: RestService, 38 private restService: RestService,
40 private localStorageService: LocalStorageService, 39 private localStorageService: LocalStorageService,
41 private sessionStorageService: SessionStorageService, 40 private sessionStorageService: SessionStorageService
42 private i18n: I18n 41 ) { }
43 ) { }
44 42
45 changePassword (currentPassword: string, newPassword: string) { 43 changePassword (currentPassword: string, newPassword: string) {
46 const url = UserService.BASE_USERS_URL + 'me' 44 const url = UserService.BASE_USERS_URL + 'me'
@@ -383,9 +381,9 @@ export class UserService {
383 } 381 }
384 382
385 const roleLabels: { [ id in UserRole ]: string } = { 383 const roleLabels: { [ id in UserRole ]: string } = {
386 [UserRole.USER]: this.i18n('User'), 384 [UserRole.USER]: $localize`User`,
387 [UserRole.ADMINISTRATOR]: this.i18n('Administrator'), 385 [UserRole.ADMINISTRATOR]: $localize`Administrator`,
388 [UserRole.MODERATOR]: this.i18n('Moderator') 386 [UserRole.MODERATOR]: $localize`Moderator`
389 } 387 }
390 388
391 return Object.assign(user, { 389 return Object.assign(user, {
diff --git a/client/src/app/menu/menu.component.ts b/client/src/app/menu/menu.component.ts
index 2c55b9a84..3d6cbda24 100644
--- a/client/src/app/menu/menu.component.ts
+++ b/client/src/app/menu/menu.component.ts
@@ -5,7 +5,6 @@ import { Component, OnInit, ViewChild } from '@angular/core'
5import { AuthService, AuthStatus, AuthUser, RedirectService, ScreenService, ServerService, UserService } from '@app/core' 5import { AuthService, AuthStatus, AuthUser, RedirectService, ScreenService, ServerService, UserService } from '@app/core'
6import { LanguageChooserComponent } from '@app/menu/language-chooser.component' 6import { LanguageChooserComponent } from '@app/menu/language-chooser.component'
7import { QuickSettingsModalComponent } from '@app/modal/quick-settings-modal.component' 7import { QuickSettingsModalComponent } from '@app/modal/quick-settings-modal.component'
8import { I18n } from '@ngx-translate/i18n-polyfill'
9import { ServerConfig, UserRight, VideoConstant } from '@shared/models' 8import { ServerConfig, UserRight, VideoConstant } from '@shared/models'
10 9
11const logger = debug('peertube:menu:MenuComponent') 10const logger = debug('peertube:menu:MenuComponent')
@@ -44,9 +43,8 @@ export class MenuComponent implements OnInit {
44 private serverService: ServerService, 43 private serverService: ServerService,
45 private redirectService: RedirectService, 44 private redirectService: RedirectService,
46 private hotkeysService: HotkeysService, 45 private hotkeysService: HotkeysService,
47 private screenService: ScreenService, 46 private screenService: ScreenService
48 private i18n: I18n 47 ) { }
49 ) { }
50 48
51 get isInMobileView () { 49 get isInMobileView () {
52 return this.screenService.isInMobileView() 50 return this.screenService.isInMobileView()
@@ -117,13 +115,13 @@ export class MenuComponent implements OnInit {
117 115
118 switch (this.user.nsfwPolicy) { 116 switch (this.user.nsfwPolicy) {
119 case 'do_not_list': 117 case 'do_not_list':
120 return this.i18n('hide') 118 return $localize`hide`
121 119
122 case 'blur': 120 case 'blur':
123 return this.i18n('blur') 121 return $localize`blur`
124 122
125 case 'display': 123 case 'display':
126 return this.i18n('display') 124 return $localize`display`
127 } 125 }
128 } 126 }
129 127
@@ -189,7 +187,7 @@ export class MenuComponent implements OnInit {
189 } 187 }
190 188
191 langForLocale (localeId: string) { 189 langForLocale (localeId: string) {
192 if (localeId === '_unknown') return this.i18n('Unknown') 190 if (localeId === '_unknown') return $localize`Unknown`
193 191
194 return this.languages.find(lang => lang.id === localeId).label 192 return this.languages.find(lang => lang.id === localeId).label
195 } 193 }
@@ -201,7 +199,7 @@ export class MenuComponent implements OnInit {
201 } 199 }
202 200
203 if (!this.user.videoLanguages) { 201 if (!this.user.videoLanguages) {
204 this.videoLanguages = [this.i18n('any language')] 202 this.videoLanguages = [$localize`any language`]
205 return 203 return
206 } 204 }
207 205
diff --git a/client/src/app/modal/confirm.component.ts b/client/src/app/modal/confirm.component.ts
index 2c7ef46c4..457dd1f3f 100644
--- a/client/src/app/modal/confirm.component.ts
+++ b/client/src/app/modal/confirm.component.ts
@@ -3,7 +3,6 @@ import { ConfirmService } from '@app/core/confirm/confirm.service'
3import { POP_STATE_MODAL_DISMISS } from '@app/helpers' 3import { POP_STATE_MODAL_DISMISS } from '@app/helpers'
4import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 4import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
5import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 5import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
6import { I18n } from '@ngx-translate/i18n-polyfill'
7 6
8@Component({ 7@Component({
9 selector: 'my-confirm', 8 selector: 'my-confirm',
@@ -25,8 +24,7 @@ export class ConfirmComponent implements OnInit {
25 24
26 constructor ( 25 constructor (
27 private modalService: NgbModal, 26 private modalService: NgbModal,
28 private confirmService: ConfirmService, 27 private confirmService: ConfirmService
29 private i18n: I18n
30 ) { } 28 ) { }
31 29
32 ngOnInit () { 30 ngOnInit () {
@@ -38,7 +36,7 @@ export class ConfirmComponent implements OnInit {
38 this.inputLabel = inputLabel 36 this.inputLabel = inputLabel
39 this.expectedInputValue = expectedInputValue 37 this.expectedInputValue = expectedInputValue
40 38
41 this.confirmButtonText = confirmButtonText || this.i18n('Confirm') 39 this.confirmButtonText = confirmButtonText || $localize`Confirm`
42 40
43 this.showModal() 41 this.showModal()
44 } 42 }
diff --git a/client/src/app/shared/shared-abuse-list/abuse-details.component.ts b/client/src/app/shared/shared-abuse-list/abuse-details.component.ts
index 0e872079a..282a6fe19 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-details.component.ts
+++ b/client/src/app/shared/shared-abuse-list/abuse-details.component.ts
@@ -1,7 +1,6 @@
1import { Component, Input } from '@angular/core' 1import { Component, Input } from '@angular/core'
2import { durationToString } from '@app/helpers' 2import { durationToString } from '@app/helpers'
3import { Actor } from '@app/shared/shared-main' 3import { Actor } from '@app/shared/shared-main'
4import { I18n } from '@ngx-translate/i18n-polyfill'
5import { AbusePredefinedReasonsString } from '@shared/models' 4import { AbusePredefinedReasonsString } from '@shared/models'
6import { ProcessedAbuse } from './processed-abuse.model' 5import { ProcessedAbuse } from './processed-abuse.model'
7 6
@@ -17,18 +16,16 @@ export class AbuseDetailsComponent {
17 16
18 private predefinedReasonsTranslations: { [key in AbusePredefinedReasonsString]: string } 17 private predefinedReasonsTranslations: { [key in AbusePredefinedReasonsString]: string }
19 18
20 constructor ( 19 constructor () {
21 private i18n: I18n
22 ) {
23 this.predefinedReasonsTranslations = { 20 this.predefinedReasonsTranslations = {
24 violentOrRepulsive: this.i18n('Violent or Repulsive'), 21 violentOrRepulsive: $localize`Violent or Repulsive`,
25 hatefulOrAbusive: this.i18n('Hateful or Abusive'), 22 hatefulOrAbusive: $localize`Hateful or Abusive`,
26 spamOrMisleading: this.i18n('Spam or Misleading'), 23 spamOrMisleading: $localize`Spam or Misleading`,
27 privacy: this.i18n('Privacy'), 24 privacy: $localize`Privacy`,
28 rights: this.i18n('Copyright'), 25 rights: $localize`Copyright`,
29 serverRules: this.i18n('Server rules'), 26 serverRules: $localize`Server rules`,
30 thumbnails: this.i18n('Thumbnails'), 27 thumbnails: $localize`Thumbnails`,
31 captions: this.i18n('Captions') 28 captions: $localize`Captions`
32 } 29 }
33 } 30 }
34 31
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
index c7dc5f4d2..cc933db0d 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
+++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
@@ -1,16 +1,15 @@
1import * as debug from 'debug' 1import * as debug from 'debug'
2import truncate from 'lodash-es/truncate' 2import truncate from 'lodash-es/truncate'
3import { SortMeta } from 'primeng/api' 3import { SortMeta } from 'primeng/api'
4import { buildVideoOrPlaylistEmbed, buildVideoLink } from 'src/assets/player/utils' 4import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
5import { environment } from 'src/environments/environment' 5import { environment } from 'src/environments/environment'
6import { AfterViewInit, Component, OnInit, ViewChild, Input } from '@angular/core' 6import { AfterViewInit, Component, Input, OnInit, ViewChild } from '@angular/core'
7import { DomSanitizer } from '@angular/platform-browser' 7import { DomSanitizer } from '@angular/platform-browser'
8import { ActivatedRoute, Params, Router } from '@angular/router' 8import { ActivatedRoute, Params, Router } from '@angular/router'
9import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core' 9import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core'
10import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main' 10import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main'
11import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation' 11import { AbuseService, BlocklistService, VideoBlockService } from '@app/shared/shared-moderation'
12import { VideoCommentService } from '@app/shared/shared-video-comment' 12import { VideoCommentService } from '@app/shared/shared-video-comment'
13import { I18n } from '@ngx-translate/i18n-polyfill'
14import { AbuseState, AdminAbuse } from '@shared/models' 13import { AbuseState, AdminAbuse } from '@shared/models'
15import { AbuseMessageModalComponent } from './abuse-message-modal.component' 14import { AbuseMessageModalComponent } from './abuse-message-modal.component'
16import { ModerationCommentModalComponent } from './moderation-comment-modal.component' 15import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
@@ -45,7 +44,6 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
45 private videoService: VideoService, 44 private videoService: VideoService,
46 private videoBlocklistService: VideoBlockService, 45 private videoBlocklistService: VideoBlockService,
47 private confirmService: ConfirmService, 46 private confirmService: ConfirmService,
48 private i18n: I18n,
49 private markdownRenderer: MarkdownService, 47 private markdownRenderer: MarkdownService,
50 private sanitizer: DomSanitizer, 48 private sanitizer: DomSanitizer,
51 private route: ActivatedRoute, 49 private route: ActivatedRoute,
@@ -157,12 +155,12 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
157 } 155 }
158 156
159 async removeAbuse (abuse: AdminAbuse) { 157 async removeAbuse (abuse: AdminAbuse) {
160 const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this abuse report?'), this.i18n('Delete')) 158 const res = await this.confirmService.confirm($localize`Do you really want to delete this abuse report?`, $localize`Delete`)
161 if (res === false) return 159 if (res === false) return
162 160
163 this.abuseService.removeAbuse(abuse).subscribe( 161 this.abuseService.removeAbuse(abuse).subscribe(
164 () => { 162 () => {
165 this.notifier.success(this.i18n('Abuse deleted.')) 163 this.notifier.success($localize`Abuse deleted.`)
166 this.loadData() 164 this.loadData()
167 }, 165 },
168 166
@@ -238,7 +236,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
238 236
239 if (abuse.comment) { 237 if (abuse.comment) {
240 if (abuse.comment.deleted) { 238 if (abuse.comment.deleted) {
241 abuse.truncatedCommentHtml = abuse.commentHtml = this.i18n('Deleted comment') 239 abuse.truncatedCommentHtml = abuse.commentHtml = $localize`Deleted comment`
242 } else { 240 } else {
243 const truncated = truncate(abuse.comment.text, { length: 100 }) 241 const truncated = truncate(abuse.comment.text, { length: 100 })
244 abuse.truncatedCommentHtml = await this.markdownRenderer.textMarkdownToHTML(truncated, true) 242 abuse.truncatedCommentHtml = await this.markdownRenderer.textMarkdownToHTML(truncated, true)
@@ -267,38 +265,38 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
267 private buildInternalActions (): DropdownAction<ProcessedAbuse>[] { 265 private buildInternalActions (): DropdownAction<ProcessedAbuse>[] {
268 return [ 266 return [
269 { 267 {
270 label: this.i18n('Internal actions'), 268 label: $localize`Internal actions`,
271 isHeader: true 269 isHeader: true
272 }, 270 },
273 { 271 {
274 label: this.isAdminView() 272 label: this.isAdminView()
275 ? this.i18n('Messages with reporter') 273 ? $localize`Messages with reporter`
276 : this.i18n('Messages with moderators'), 274 : $localize`Messages with moderators`,
277 handler: abuse => this.openAbuseMessagesModal(abuse), 275 handler: abuse => this.openAbuseMessagesModal(abuse),
278 isDisplayed: abuse => this.isLocalAbuse(abuse) 276 isDisplayed: abuse => this.isLocalAbuse(abuse)
279 }, 277 },
280 { 278 {
281 label: this.i18n('Update internal note'), 279 label: $localize`Update internal note`,
282 handler: abuse => this.openModerationCommentModal(abuse), 280 handler: abuse => this.openModerationCommentModal(abuse),
283 isDisplayed: abuse => this.isAdminView() && !!abuse.moderationComment 281 isDisplayed: abuse => this.isAdminView() && !!abuse.moderationComment
284 }, 282 },
285 { 283 {
286 label: this.i18n('Mark as accepted'), 284 label: $localize`Mark as accepted`,
287 handler: abuse => this.updateAbuseState(abuse, AbuseState.ACCEPTED), 285 handler: abuse => this.updateAbuseState(abuse, AbuseState.ACCEPTED),
288 isDisplayed: abuse => this.isAdminView() && !this.isAbuseAccepted(abuse) 286 isDisplayed: abuse => this.isAdminView() && !this.isAbuseAccepted(abuse)
289 }, 287 },
290 { 288 {
291 label: this.i18n('Mark as rejected'), 289 label: $localize`Mark as rejected`,
292 handler: abuse => this.updateAbuseState(abuse, AbuseState.REJECTED), 290 handler: abuse => this.updateAbuseState(abuse, AbuseState.REJECTED),
293 isDisplayed: abuse => this.isAdminView() && !this.isAbuseRejected(abuse) 291 isDisplayed: abuse => this.isAdminView() && !this.isAbuseRejected(abuse)
294 }, 292 },
295 { 293 {
296 label: this.i18n('Add internal note'), 294 label: $localize`Add internal note`,
297 handler: abuse => this.openModerationCommentModal(abuse), 295 handler: abuse => this.openModerationCommentModal(abuse),
298 isDisplayed: abuse => this.isAdminView() && !abuse.moderationComment 296 isDisplayed: abuse => this.isAdminView() && !abuse.moderationComment
299 }, 297 },
300 { 298 {
301 label: this.i18n('Delete report'), 299 label: $localize`Delete report`,
302 handler: abuse => this.isAdminView() && this.removeAbuse(abuse) 300 handler: abuse => this.isAdminView() && this.removeAbuse(abuse)
303 } 301 }
304 ] 302 ]
@@ -309,19 +307,19 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
309 307
310 return [ 308 return [
311 { 309 {
312 label: this.i18n('Actions for the flagged account'), 310 label: $localize`Actions for the flagged account`,
313 isHeader: true, 311 isHeader: true,
314 isDisplayed: abuse => abuse.flaggedAccount && !abuse.comment && !abuse.video 312 isDisplayed: abuse => abuse.flaggedAccount && !abuse.comment && !abuse.video
315 }, 313 },
316 314
317 { 315 {
318 label: this.i18n('Mute account'), 316 label: $localize`Mute account`,
319 isDisplayed: abuse => abuse.flaggedAccount && !abuse.comment && !abuse.video, 317 isDisplayed: abuse => abuse.flaggedAccount && !abuse.comment && !abuse.video,
320 handler: abuse => this.muteAccountHelper(abuse.flaggedAccount) 318 handler: abuse => this.muteAccountHelper(abuse.flaggedAccount)
321 }, 319 },
322 320
323 { 321 {
324 label: this.i18n('Mute server account'), 322 label: $localize`Mute server account`,
325 isDisplayed: abuse => abuse.flaggedAccount && !abuse.comment && !abuse.video, 323 isDisplayed: abuse => abuse.flaggedAccount && !abuse.comment && !abuse.video,
326 handler: abuse => this.muteServerHelper(abuse.flaggedAccount.host) 324 handler: abuse => this.muteServerHelper(abuse.flaggedAccount.host)
327 } 325 }
@@ -333,19 +331,19 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
333 331
334 return [ 332 return [
335 { 333 {
336 label: this.i18n('Actions for the reporter'), 334 label: $localize`Actions for the reporter`,
337 isHeader: true, 335 isHeader: true,
338 isDisplayed: abuse => !!abuse.reporterAccount 336 isDisplayed: abuse => !!abuse.reporterAccount
339 }, 337 },
340 338
341 { 339 {
342 label: this.i18n('Mute reporter'), 340 label: $localize`Mute reporter`,
343 isDisplayed: abuse => !!abuse.reporterAccount, 341 isDisplayed: abuse => !!abuse.reporterAccount,
344 handler: abuse => this.muteAccountHelper(abuse.reporterAccount) 342 handler: abuse => this.muteAccountHelper(abuse.reporterAccount)
345 }, 343 },
346 344
347 { 345 {
348 label: this.i18n('Mute server'), 346 label: $localize`Mute server`,
349 isDisplayed: abuse => abuse.reporterAccount && !abuse.reporterAccount.userId, 347 isDisplayed: abuse => abuse.reporterAccount && !abuse.reporterAccount.userId,
350 handler: abuse => this.muteServerHelper(abuse.reporterAccount.host) 348 handler: abuse => this.muteServerHelper(abuse.reporterAccount.host)
351 } 349 }
@@ -357,18 +355,18 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
357 355
358 return [ 356 return [
359 { 357 {
360 label: this.i18n('Actions for the video'), 358 label: $localize`Actions for the video`,
361 isHeader: true, 359 isHeader: true,
362 isDisplayed: abuse => abuse.video && !abuse.video.deleted 360 isDisplayed: abuse => abuse.video && !abuse.video.deleted
363 }, 361 },
364 { 362 {
365 label: this.i18n('Block video'), 363 label: $localize`Block video`,
366 isDisplayed: abuse => abuse.video && !abuse.video.deleted && !abuse.video.blacklisted, 364 isDisplayed: abuse => abuse.video && !abuse.video.deleted && !abuse.video.blacklisted,
367 handler: abuse => { 365 handler: abuse => {
368 this.videoBlocklistService.blockVideo(abuse.video.id, undefined, true) 366 this.videoBlocklistService.blockVideo(abuse.video.id, undefined, true)
369 .subscribe( 367 .subscribe(
370 () => { 368 () => {
371 this.notifier.success(this.i18n('Video blocked.')) 369 this.notifier.success($localize`Video blocked.`)
372 370
373 this.updateAbuseState(abuse, AbuseState.ACCEPTED) 371 this.updateAbuseState(abuse, AbuseState.ACCEPTED)
374 }, 372 },
@@ -378,13 +376,13 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
378 } 376 }
379 }, 377 },
380 { 378 {
381 label: this.i18n('Unblock video'), 379 label: $localize`Unblock video`,
382 isDisplayed: abuse => abuse.video && !abuse.video.deleted && abuse.video.blacklisted, 380 isDisplayed: abuse => abuse.video && !abuse.video.deleted && abuse.video.blacklisted,
383 handler: abuse => { 381 handler: abuse => {
384 this.videoBlocklistService.unblockVideo(abuse.video.id) 382 this.videoBlocklistService.unblockVideo(abuse.video.id)
385 .subscribe( 383 .subscribe(
386 () => { 384 () => {
387 this.notifier.success(this.i18n('Video unblocked.')) 385 this.notifier.success($localize`Video unblocked.`)
388 386
389 this.updateAbuseState(abuse, AbuseState.ACCEPTED) 387 this.updateAbuseState(abuse, AbuseState.ACCEPTED)
390 }, 388 },
@@ -394,19 +392,19 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
394 } 392 }
395 }, 393 },
396 { 394 {
397 label: this.i18n('Delete video'), 395 label: $localize`Delete video`,
398 isDisplayed: abuse => abuse.video && !abuse.video.deleted, 396 isDisplayed: abuse => abuse.video && !abuse.video.deleted,
399 handler: async abuse => { 397 handler: async abuse => {
400 const res = await this.confirmService.confirm( 398 const res = await this.confirmService.confirm(
401 this.i18n('Do you really want to delete this video?'), 399 $localize`Do you really want to delete this video?`,
402 this.i18n('Delete') 400 $localize`Delete`
403 ) 401 )
404 if (res === false) return 402 if (res === false) return
405 403
406 this.videoService.removeVideo(abuse.video.id) 404 this.videoService.removeVideo(abuse.video.id)
407 .subscribe( 405 .subscribe(
408 () => { 406 () => {
409 this.notifier.success(this.i18n('Video deleted.')) 407 this.notifier.success($localize`Video deleted.`)
410 408
411 this.updateAbuseState(abuse, AbuseState.ACCEPTED) 409 this.updateAbuseState(abuse, AbuseState.ACCEPTED)
412 }, 410 },
@@ -423,25 +421,25 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
423 421
424 return [ 422 return [
425 { 423 {
426 label: this.i18n('Actions for the comment'), 424 label: $localize`Actions for the comment`,
427 isHeader: true, 425 isHeader: true,
428 isDisplayed: abuse => abuse.comment && !abuse.comment.deleted 426 isDisplayed: abuse => abuse.comment && !abuse.comment.deleted
429 }, 427 },
430 428
431 { 429 {
432 label: this.i18n('Delete comment'), 430 label: $localize`Delete comment`,
433 isDisplayed: abuse => abuse.comment && !abuse.comment.deleted, 431 isDisplayed: abuse => abuse.comment && !abuse.comment.deleted,
434 handler: async abuse => { 432 handler: async abuse => {
435 const res = await this.confirmService.confirm( 433 const res = await this.confirmService.confirm(
436 this.i18n('Do you really want to delete this comment?'), 434 $localize`Do you really want to delete this comment?`,
437 this.i18n('Delete') 435 $localize`Delete`
438 ) 436 )
439 if (res === false) return 437 if (res === false) return
440 438
441 this.commentService.deleteVideoComment(abuse.comment.video.id, abuse.comment.id) 439 this.commentService.deleteVideoComment(abuse.comment.video.id, abuse.comment.id)
442 .subscribe( 440 .subscribe(
443 () => { 441 () => {
444 this.notifier.success(this.i18n('Comment deleted.')) 442 this.notifier.success($localize`Comment deleted.`)
445 443
446 this.updateAbuseState(abuse, AbuseState.ACCEPTED) 444 this.updateAbuseState(abuse, AbuseState.ACCEPTED)
447 }, 445 },
@@ -457,10 +455,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
457 this.blocklistService.blockAccountByInstance(account) 455 this.blocklistService.blockAccountByInstance(account)
458 .subscribe( 456 .subscribe(
459 () => { 457 () => {
460 this.notifier.success( 458 this.notifier.success($localize`Account ${account.nameWithHost} muted by the instance.`)
461 this.i18n('Account {{nameWithHost}} muted by the instance.', { nameWithHost: account.nameWithHost })
462 )
463
464 account.mutedByInstance = true 459 account.mutedByInstance = true
465 }, 460 },
466 461
@@ -472,9 +467,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV
472 this.blocklistService.blockServerByInstance(host) 467 this.blocklistService.blockServerByInstance(host)
473 .subscribe( 468 .subscribe(
474 () => { 469 () => {
475 this.notifier.success( 470 this.notifier.success($localize`Server ${host} muted by the instance.`)
476 this.i18n('Server {{host}} muted by the instance.', { host: host })
477 )
478 }, 471 },
479 472
480 err => this.notifier.error(err.message) 473 err => this.notifier.error(err.message)
diff --git a/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.ts b/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.ts
index 6686d91f4..0c3c8ff48 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.ts
+++ b/client/src/app/shared/shared-abuse-list/abuse-message-modal.component.ts
@@ -1,9 +1,8 @@
1import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' 1import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'
2import { AuthService, Notifier, HtmlRendererService } from '@app/core' 2import { AuthService, HtmlRendererService, Notifier } from '@app/core'
3import { AbuseValidatorsService, FormReactive, FormValidatorService } from '@app/shared/shared-forms' 3import { AbuseValidatorsService, FormReactive, FormValidatorService } from '@app/shared/shared-forms'
4import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 4import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
5import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 5import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
6import { I18n } from '@ngx-translate/i18n-polyfill'
7import { AbuseMessage, UserAbuse } from '@shared/models' 6import { AbuseMessage, UserAbuse } from '@shared/models'
8import { AbuseService } from '../shared-moderation' 7import { AbuseService } from '../shared-moderation'
9 8
@@ -31,7 +30,6 @@ export class AbuseMessageModalComponent extends FormReactive implements OnInit {
31 protected formValidatorService: FormValidatorService, 30 protected formValidatorService: FormValidatorService,
32 private abuseValidatorsService: AbuseValidatorsService, 31 private abuseValidatorsService: AbuseValidatorsService,
33 private modalService: NgbModal, 32 private modalService: NgbModal,
34 private i18n: I18n,
35 private htmlRenderer: HtmlRendererService, 33 private htmlRenderer: HtmlRendererService,
36 private auth: AuthService, 34 private auth: AuthService,
37 private notifier: Notifier, 35 private notifier: Notifier,
@@ -99,10 +97,10 @@ export class AbuseMessageModalComponent extends FormReactive implements OnInit {
99 97
100 getPlaceholderMessage () { 98 getPlaceholderMessage () {
101 if (this.isAdminView) { 99 if (this.isAdminView) {
102 return this.i18n('Add a message to communicate with the reporter') 100 return $localize`Add a message to communicate with the reporter`
103 } 101 }
104 102
105 return this.i18n('Add a message to communicate with the moderation team') 103 return $localize`Add a message to communicate with the moderation team`
106 } 104 }
107 105
108 private loadMessages () { 106 private loadMessages () {
diff --git a/client/src/app/shared/shared-abuse-list/moderation-comment-modal.component.ts b/client/src/app/shared/shared-abuse-list/moderation-comment-modal.component.ts
index ecb7966bf..fad7f888d 100644
--- a/client/src/app/shared/shared-abuse-list/moderation-comment-modal.component.ts
+++ b/client/src/app/shared/shared-abuse-list/moderation-comment-modal.component.ts
@@ -1,10 +1,9 @@
1import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core' 1import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'
2import { Notifier } from '@app/core' 2import { Notifier } from '@app/core'
3import { FormReactive, FormValidatorService, AbuseValidatorsService } from '@app/shared/shared-forms' 3import { AbuseValidatorsService, FormReactive, FormValidatorService } from '@app/shared/shared-forms'
4import { AbuseService } from '@app/shared/shared-moderation' 4import { AbuseService } from '@app/shared/shared-moderation'
5import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 5import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
6import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 6import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { AdminAbuse } from '@shared/models' 7import { AdminAbuse } from '@shared/models'
9 8
10@Component({ 9@Component({
@@ -24,8 +23,7 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI
24 private modalService: NgbModal, 23 private modalService: NgbModal,
25 private notifier: Notifier, 24 private notifier: Notifier,
26 private abuseService: AbuseService, 25 private abuseService: AbuseService,
27 private abuseValidatorsService: AbuseValidatorsService, 26 private abuseValidatorsService: AbuseValidatorsService
28 private i18n: I18n
29 ) { 27 ) {
30 super() 28 super()
31 } 29 }
@@ -57,7 +55,7 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI
57 this.abuseService.updateAbuse(this.abuseToComment, { moderationComment }) 55 this.abuseService.updateAbuse(this.abuseToComment, { moderationComment })
58 .subscribe( 56 .subscribe(
59 () => { 57 () => {
60 this.notifier.success(this.i18n('Comment updated.')) 58 this.notifier.success($localize`Comment updated.`)
61 59
62 this.commentUpdated.emit(moderationComment) 60 this.commentUpdated.emit(moderationComment)
63 this.hide() 61 this.hide()
diff --git a/client/src/app/shared/shared-forms/form-validators/abuse-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/abuse-validators.service.ts
index 5f15963f3..56d30d6f9 100644
--- a/client/src/app/shared/shared-forms/form-validators/abuse-validators.service.ts
+++ b/client/src/app/shared/shared-forms/form-validators/abuse-validators.service.ts
@@ -1,6 +1,5 @@
1import { I18n } from '@ngx-translate/i18n-polyfill'
2import { Validators } from '@angular/forms'
3import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { Validators } from '@angular/forms'
4import { BuildFormValidator } from './form-validator.service' 3import { BuildFormValidator } from './form-validator.service'
5 4
6@Injectable() 5@Injectable()
@@ -9,31 +8,31 @@ export class AbuseValidatorsService {
9 readonly ABUSE_MODERATION_COMMENT: BuildFormValidator 8 readonly ABUSE_MODERATION_COMMENT: BuildFormValidator
10 readonly ABUSE_MESSAGE: BuildFormValidator 9 readonly ABUSE_MESSAGE: BuildFormValidator
11 10
12 constructor (private i18n: I18n) { 11 constructor () {
13 this.ABUSE_REASON = { 12 this.ABUSE_REASON = {
14 VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ], 13 VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ],
15 MESSAGES: { 14 MESSAGES: {
16 'required': this.i18n('Report reason is required.'), 15 'required': $localize`Report reason is required.`,
17 'minlength': this.i18n('Report reason must be at least 2 characters long.'), 16 'minlength': $localize`Report reason must be at least 2 characters long.`,
18 'maxlength': this.i18n('Report reason cannot be more than 3000 characters long.') 17 'maxlength': $localize`Report reason cannot be more than 3000 characters long.`
19 } 18 }
20 } 19 }
21 20
22 this.ABUSE_MODERATION_COMMENT = { 21 this.ABUSE_MODERATION_COMMENT = {
23 VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ], 22 VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ],
24 MESSAGES: { 23 MESSAGES: {
25 'required': this.i18n('Moderation comment is required.'), 24 'required': $localize`Moderation comment is required.`,
26 'minlength': this.i18n('Moderation comment must be at least 2 characters long.'), 25 'minlength': $localize`Moderation comment must be at least 2 characters long.`,
27 'maxlength': this.i18n('Moderation comment cannot be more than 3000 characters long.') 26 'maxlength': $localize`Moderation comment cannot be more than 3000 characters long.`
28 } 27 }
29 } 28 }
30 29
31 this.ABUSE_MESSAGE = { 30 this.ABUSE_MESSAGE = {
32 VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ], 31 VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ],
33 MESSAGES: { 32 MESSAGES: {
34 'required': this.i18n('Abuse message is required.'), 33 'required': $localize`Abuse message is required.`,
35 'minlength': this.i18n('Abuse message must be at least 2 characters long.'), 34 'minlength': $localize`Abuse message must be at least 2 characters long.`,
36 'maxlength': this.i18n('Abuse message cannot be more than 3000 characters long.') 35 'maxlength': $localize`Abuse message cannot be more than 3000 characters long.`
37 } 36 }
38 } 37 }
39 } 38 }
diff --git a/client/src/app/shared/shared-forms/form-validators/batch-domains-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/batch-domains-validators.service.ts
index f270b602b..6c7da833f 100644
--- a/client/src/app/shared/shared-forms/form-validators/batch-domains-validators.service.ts
+++ b/client/src/app/shared/shared-forms/form-validators/batch-domains-validators.service.ts
@@ -1,6 +1,5 @@
1import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { ValidatorFn, Validators } from '@angular/forms' 2import { ValidatorFn, Validators } from '@angular/forms'
3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { BuildFormValidator } from './form-validator.service' 3import { BuildFormValidator } from './form-validator.service'
5import { validateHost } from './host' 4import { validateHost } from './host'
6 5
@@ -8,13 +7,13 @@ import { validateHost } from './host'
8export class BatchDomainsValidatorsService { 7export class BatchDomainsValidatorsService {
9 readonly DOMAINS: BuildFormValidator 8 readonly DOMAINS: BuildFormValidator
10 9
11 constructor (private i18n: I18n) { 10 constructor () {
12 this.DOMAINS = { 11 this.DOMAINS = {
13 VALIDATORS: [ Validators.required, this.validDomains, this.isHostsUnique ], 12 VALIDATORS: [ Validators.required, this.validDomains, this.isHostsUnique ],
14 MESSAGES: { 13 MESSAGES: {
15 'required': this.i18n('Domain is required.'), 14 'required': $localize`Domain is required.`,
16 'validDomains': this.i18n('Domains entered are invalid.'), 15 'validDomains': $localize`Domains entered are invalid.`,
17 'uniqueDomains': this.i18n('Domains entered contain duplicates.') 16 'uniqueDomains': $localize`Domains entered contain duplicates.`
18 } 17 }
19 } 18 }
20 } 19 }
@@ -33,7 +32,7 @@ export class BatchDomainsValidatorsService {
33 32
34 for (const host of hosts) { 33 for (const host of hosts) {
35 if (validateHost(host) === false) { 34 if (validateHost(host) === false) {
36 newHostsErrors.push(this.i18n('{{host}} is not valid', { host })) 35 newHostsErrors.push($localize`${host} is not valid`)
37 } 36 }
38 } 37 }
39 38
diff --git a/client/src/app/shared/shared-forms/form-validators/custom-config-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/custom-config-validators.service.ts
index c77aba6a1..862ff5470 100644
--- a/client/src/app/shared/shared-forms/form-validators/custom-config-validators.service.ts
+++ b/client/src/app/shared/shared-forms/form-validators/custom-config-validators.service.ts
@@ -1,7 +1,6 @@
1import { Injectable } from '@angular/core'
1import { Validators } from '@angular/forms' 2import { Validators } from '@angular/forms'
2import { I18n } from '@ngx-translate/i18n-polyfill'
3import { BuildFormValidator } from './form-validator.service' 3import { BuildFormValidator } from './form-validator.service'
4import { Injectable } from '@angular/core'
5 4
6@Injectable() 5@Injectable()
7export class CustomConfigValidatorsService { 6export class CustomConfigValidatorsService {
@@ -16,82 +15,82 @@ export class CustomConfigValidatorsService {
16 readonly INDEX_URL: BuildFormValidator 15 readonly INDEX_URL: BuildFormValidator
17 readonly SEARCH_INDEX_URL: BuildFormValidator 16 readonly SEARCH_INDEX_URL: BuildFormValidator
18 17
19 constructor (private i18n: I18n) { 18 constructor () {
20 this.INSTANCE_NAME = { 19 this.INSTANCE_NAME = {
21 VALIDATORS: [ Validators.required ], 20 VALIDATORS: [ Validators.required ],
22 MESSAGES: { 21 MESSAGES: {
23 'required': this.i18n('Instance name is required.') 22 'required': $localize`Instance name is required.`
24 } 23 }
25 } 24 }
26 25
27 this.INSTANCE_SHORT_DESCRIPTION = { 26 this.INSTANCE_SHORT_DESCRIPTION = {
28 VALIDATORS: [ Validators.max(250) ], 27 VALIDATORS: [ Validators.max(250) ],
29 MESSAGES: { 28 MESSAGES: {
30 'max': this.i18n('Short description should not be longer than 250 characters.') 29 'max': $localize`Short description should not be longer than 250 characters.`
31 } 30 }
32 } 31 }
33 32
34 this.SERVICES_TWITTER_USERNAME = { 33 this.SERVICES_TWITTER_USERNAME = {
35 VALIDATORS: [ Validators.required ], 34 VALIDATORS: [ Validators.required ],
36 MESSAGES: { 35 MESSAGES: {
37 'required': this.i18n('Twitter username is required.') 36 'required': $localize`Twitter username is required.`
38 } 37 }
39 } 38 }
40 39
41 this.CACHE_PREVIEWS_SIZE = { 40 this.CACHE_PREVIEWS_SIZE = {
42 VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ], 41 VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ],
43 MESSAGES: { 42 MESSAGES: {
44 'required': this.i18n('Previews cache size is required.'), 43 'required': $localize`Previews cache size is required.`,
45 'min': this.i18n('Previews cache size must be greater than 1.'), 44 'min': $localize`Previews cache size must be greater than 1.`,
46 'pattern': this.i18n('Previews cache size must be a number.') 45 'pattern': $localize`Previews cache size must be a number.`
47 } 46 }
48 } 47 }
49 48
50 this.CACHE_CAPTIONS_SIZE = { 49 this.CACHE_CAPTIONS_SIZE = {
51 VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ], 50 VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ],
52 MESSAGES: { 51 MESSAGES: {
53 'required': this.i18n('Captions cache size is required.'), 52 'required': $localize`Captions cache size is required.`,
54 'min': this.i18n('Captions cache size must be greater than 1.'), 53 'min': $localize`Captions cache size must be greater than 1.`,
55 'pattern': this.i18n('Captions cache size must be a number.') 54 'pattern': $localize`Captions cache size must be a number.`
56 } 55 }
57 } 56 }
58 57
59 this.SIGNUP_LIMIT = { 58 this.SIGNUP_LIMIT = {
60 VALIDATORS: [ Validators.required, Validators.min(-1), Validators.pattern('-?[0-9]+') ], 59 VALIDATORS: [ Validators.required, Validators.min(-1), Validators.pattern('-?[0-9]+') ],
61 MESSAGES: { 60 MESSAGES: {
62 'required': this.i18n('Signup limit is required.'), 61 'required': $localize`Signup limit is required.`,
63 'min': this.i18n('Signup limit must be greater than 1.'), 62 'min': $localize`Signup limit must be greater than 1.`,
64 'pattern': this.i18n('Signup limit must be a number.') 63 'pattern': $localize`Signup limit must be a number.`
65 } 64 }
66 } 65 }
67 66
68 this.ADMIN_EMAIL = { 67 this.ADMIN_EMAIL = {
69 VALIDATORS: [ Validators.required, Validators.email ], 68 VALIDATORS: [ Validators.required, Validators.email ],
70 MESSAGES: { 69 MESSAGES: {
71 'required': this.i18n('Admin email is required.'), 70 'required': $localize`Admin email is required.`,
72 'email': this.i18n('Admin email must be valid.') 71 'email': $localize`Admin email must be valid.`
73 } 72 }
74 } 73 }
75 74
76 this.TRANSCODING_THREADS = { 75 this.TRANSCODING_THREADS = {
77 VALIDATORS: [ Validators.required, Validators.min(0) ], 76 VALIDATORS: [ Validators.required, Validators.min(0) ],
78 MESSAGES: { 77 MESSAGES: {
79 'required': this.i18n('Transcoding threads is required.'), 78 'required': $localize`Transcoding threads is required.`,
80 'min': this.i18n('Transcoding threads must be greater or equal to 0.') 79 'min': $localize`Transcoding threads must be greater or equal to 0.`
81 } 80 }
82 } 81 }
83 82
84 this.INDEX_URL = { 83 this.INDEX_URL = {
85 VALIDATORS: [ Validators.pattern(/^https:\/\//) ], 84 VALIDATORS: [ Validators.pattern(/^https:\/\//) ],
86 MESSAGES: { 85 MESSAGES: {
87 'pattern': this.i18n('Index URL should be a URL') 86 'pattern': $localize`Index URL should be a URL`
88 } 87 }
89 } 88 }
90 89
91 this.SEARCH_INDEX_URL = { 90 this.SEARCH_INDEX_URL = {
92 VALIDATORS: [ Validators.pattern(/^https?:\/\//) ], 91 VALIDATORS: [ Validators.pattern(/^https?:\/\//) ],
93 MESSAGES: { 92 MESSAGES: {
94 'pattern': this.i18n('Search index URL should be a URL') 93 'pattern': $localize`Search index URL should be a URL`
95 } 94 }
96 } 95 }
97 } 96 }
diff --git a/client/src/app/shared/shared-forms/form-validators/instance-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/instance-validators.service.ts
index 96a35a48f..3628f0b60 100644
--- a/client/src/app/shared/shared-forms/form-validators/instance-validators.service.ts
+++ b/client/src/app/shared/shared-forms/form-validators/instance-validators.service.ts
@@ -1,7 +1,6 @@
1import { I18n } from '@ngx-translate/i18n-polyfill' 1import { Injectable } from '@angular/core'
2import { Validators } from '@angular/forms' 2import { Validators } from '@angular/forms'
3import { BuildFormValidator } from './form-validator.service' 3import { BuildFormValidator } from './form-validator.service'
4import { Injectable } from '@angular/core'
5 4
6@Injectable() 5@Injectable()
7export class InstanceValidatorsService { 6export class InstanceValidatorsService {
@@ -10,13 +9,13 @@ export class InstanceValidatorsService {
10 readonly SUBJECT: BuildFormValidator 9 readonly SUBJECT: BuildFormValidator
11 readonly BODY: BuildFormValidator 10 readonly BODY: BuildFormValidator
12 11
13 constructor (private i18n: I18n) { 12 constructor () {
14 13
15 this.FROM_EMAIL = { 14 this.FROM_EMAIL = {
16 VALIDATORS: [ Validators.required, Validators.email ], 15 VALIDATORS: [ Validators.required, Validators.email ],
17 MESSAGES: { 16 MESSAGES: {
18 'required': this.i18n('Email is required.'), 17 'required': $localize`Email is required.`,
19 'email': this.i18n('Email must be valid.') 18 'email': $localize`Email must be valid.`
20 } 19 }
21 } 20 }
22 21
@@ -27,9 +26,9 @@ export class InstanceValidatorsService {
27 Validators.maxLength(120) 26 Validators.maxLength(120)
28 ], 27 ],
29 MESSAGES: { 28 MESSAGES: {
30 'required': this.i18n('Your name is required.'), 29 'required': $localize`Your name is required.`,
31 'minlength': this.i18n('Your name must be at least 1 character long.'), 30 'minlength': $localize`Your name must be at least 1 character long.`,
32 'maxlength': this.i18n('Your name cannot be more than 120 characters long.') 31 'maxlength': $localize`Your name cannot be more than 120 characters long.`
33 } 32 }
34 } 33 }
35 34
@@ -40,9 +39,9 @@ export class InstanceValidatorsService {
40 Validators.maxLength(120) 39 Validators.maxLength(120)
41 ], 40 ],
42 MESSAGES: { 41 MESSAGES: {
43 'required': this.i18n('A subject is required.'), 42 'required': $localize`A subject is required.`,
44 'minlength': this.i18n('The subject must be at least 1 character long.'), 43 'minlength': $localize`The subject must be at least 1 character long.`,
45 'maxlength': this.i18n('The subject cannot be more than 120 characters long.') 44 'maxlength': $localize`The subject cannot be more than 120 characters long.`
46 } 45 }
47 } 46 }
48 47
@@ -53,9 +52,9 @@ export class InstanceValidatorsService {
53 Validators.maxLength(5000) 52 Validators.maxLength(5000)
54 ], 53 ],
55 MESSAGES: { 54 MESSAGES: {
56 'required': this.i18n('A message is required.'), 55 'required': $localize`A message is required.`,
57 'minlength': this.i18n('The message must be at least 3 characters long.'), 56 'minlength': $localize`The message must be at least 3 characters long.`,
58 'maxlength': this.i18n('The message cannot be more than 5000 characters long.') 57 'maxlength': $localize`The message cannot be more than 5000 characters long.`
59 } 58 }
60 } 59 }
61 } 60 }
diff --git a/client/src/app/shared/shared-forms/form-validators/login-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/login-validators.service.ts
index a5837357e..67ea11f20 100644
--- a/client/src/app/shared/shared-forms/form-validators/login-validators.service.ts
+++ b/client/src/app/shared/shared-forms/form-validators/login-validators.service.ts
@@ -1,6 +1,5 @@
1import { I18n } from '@ngx-translate/i18n-polyfill'
2import { Validators } from '@angular/forms'
3import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { Validators } from '@angular/forms'
4import { BuildFormValidator } from './form-validator.service' 3import { BuildFormValidator } from './form-validator.service'
5 4
6@Injectable() 5@Injectable()
@@ -8,13 +7,13 @@ export class LoginValidatorsService {
8 readonly LOGIN_USERNAME: BuildFormValidator 7 readonly LOGIN_USERNAME: BuildFormValidator
9 readonly LOGIN_PASSWORD: BuildFormValidator 8 readonly LOGIN_PASSWORD: BuildFormValidator
10 9
11 constructor (private i18n: I18n) { 10 constructor () {
12 this.LOGIN_USERNAME = { 11 this.LOGIN_USERNAME = {
13 VALIDATORS: [ 12 VALIDATORS: [
14 Validators.required 13 Validators.required
15 ], 14 ],
16 MESSAGES: { 15 MESSAGES: {
17 'required': this.i18n('Username is required.') 16 'required': $localize`Username is required.`
18 } 17 }
19 } 18 }
20 19
@@ -23,7 +22,7 @@ export class LoginValidatorsService {
23 Validators.required 22 Validators.required
24 ], 23 ],
25 MESSAGES: { 24 MESSAGES: {
26 'required': this.i18n('Password is required.') 25 'required': $localize`Password is required.`
27 } 26 }
28 } 27 }
29 } 28 }
diff --git a/client/src/app/shared/shared-forms/form-validators/reset-password-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/reset-password-validators.service.ts
index d2085a309..3d0b4dd64 100644
--- a/client/src/app/shared/shared-forms/form-validators/reset-password-validators.service.ts
+++ b/client/src/app/shared/shared-forms/form-validators/reset-password-validators.service.ts
@@ -1,19 +1,18 @@
1import { I18n } from '@ngx-translate/i18n-polyfill'
2import { Validators } from '@angular/forms'
3import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { Validators } from '@angular/forms'
4import { BuildFormValidator } from './form-validator.service' 3import { BuildFormValidator } from './form-validator.service'
5 4
6@Injectable() 5@Injectable()
7export class ResetPasswordValidatorsService { 6export class ResetPasswordValidatorsService {
8 readonly RESET_PASSWORD_CONFIRM: BuildFormValidator 7 readonly RESET_PASSWORD_CONFIRM: BuildFormValidator
9 8
10 constructor (private i18n: I18n) { 9 constructor () {
11 this.RESET_PASSWORD_CONFIRM = { 10 this.RESET_PASSWORD_CONFIRM = {
12 VALIDATORS: [ 11 VALIDATORS: [
13 Validators.required 12 Validators.required
14 ], 13 ],
15 MESSAGES: { 14 MESSAGES: {
16 'required': this.i18n('Confirmation of the password is required.') 15 'required': $localize`Confirmation of the password is required.`
17 } 16 }
18 } 17 }
19 } 18 }
diff --git a/client/src/app/shared/shared-forms/form-validators/user-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/user-validators.service.ts
index 61486bbab..312fc9b1e 100644
--- a/client/src/app/shared/shared-forms/form-validators/user-validators.service.ts
+++ b/client/src/app/shared/shared-forms/form-validators/user-validators.service.ts
@@ -1,7 +1,6 @@
1import { I18n } from '@ngx-translate/i18n-polyfill' 1import { Injectable } from '@angular/core'
2import { Validators } from '@angular/forms' 2import { Validators } from '@angular/forms'
3import { BuildFormValidator } from './form-validator.service' 3import { BuildFormValidator } from './form-validator.service'
4import { Injectable } from '@angular/core'
5 4
6@Injectable() 5@Injectable()
7export class UserValidatorsService { 6export class UserValidatorsService {
@@ -20,7 +19,7 @@ export class UserValidatorsService {
20 19
21 readonly USER_BAN_REASON: BuildFormValidator 20 readonly USER_BAN_REASON: BuildFormValidator
22 21
23 constructor (private i18n: I18n) { 22 constructor () {
24 23
25 this.USER_USERNAME = { 24 this.USER_USERNAME = {
26 VALIDATORS: [ 25 VALIDATORS: [
@@ -30,10 +29,10 @@ export class UserValidatorsService {
30 Validators.pattern(/^[a-z0-9][a-z0-9._]*$/) 29 Validators.pattern(/^[a-z0-9][a-z0-9._]*$/)
31 ], 30 ],
32 MESSAGES: { 31 MESSAGES: {
33 'required': this.i18n('Username is required.'), 32 'required': $localize`Username is required.`,
34 'minlength': this.i18n('Username must be at least 1 character long.'), 33 'minlength': $localize`Username must be at least 1 character long.`,
35 'maxlength': this.i18n('Username cannot be more than 50 characters long.'), 34 'maxlength': $localize`Username cannot be more than 50 characters long.`,
36 'pattern': this.i18n('Username should be lowercase alphanumeric; dots and underscores are allowed.') 35 'pattern': $localize`Username should be lowercase alphanumeric; dots and underscores are allowed.`
37 } 36 }
38 } 37 }
39 38
@@ -45,18 +44,18 @@ export class UserValidatorsService {
45 Validators.pattern(/^[a-z0-9][a-z0-9._]*$/) 44 Validators.pattern(/^[a-z0-9][a-z0-9._]*$/)
46 ], 45 ],
47 MESSAGES: { 46 MESSAGES: {
48 'required': this.i18n('Channel name is required.'), 47 'required': $localize`Channel name is required.`,
49 'minlength': this.i18n('Channel name must be at least 1 character long.'), 48 'minlength': $localize`Channel name must be at least 1 character long.`,
50 'maxlength': this.i18n('Channel name cannot be more than 50 characters long.'), 49 'maxlength': $localize`Channel name cannot be more than 50 characters long.`,
51 'pattern': this.i18n('Channel name should be lowercase alphanumeric; dots and underscores are allowed.') 50 'pattern': $localize`Channel name should be lowercase alphanumeric; dots and underscores are allowed.`
52 } 51 }
53 } 52 }
54 53
55 this.USER_EMAIL = { 54 this.USER_EMAIL = {
56 VALIDATORS: [ Validators.required, Validators.email ], 55 VALIDATORS: [ Validators.required, Validators.email ],
57 MESSAGES: { 56 MESSAGES: {
58 'required': this.i18n('Email is required.'), 57 'required': $localize`Email is required.`,
59 'email': this.i18n('Email must be valid.') 58 'email': $localize`Email must be valid.`
60 } 59 }
61 } 60 }
62 61
@@ -67,9 +66,9 @@ export class UserValidatorsService {
67 Validators.maxLength(255) 66 Validators.maxLength(255)
68 ], 67 ],
69 MESSAGES: { 68 MESSAGES: {
70 'required': this.i18n('Password is required.'), 69 'required': $localize`Password is required.`,
71 'minlength': this.i18n('Password must be at least 6 characters long.'), 70 'minlength': $localize`Password must be at least 6 characters long.`,
72 'maxlength': this.i18n('Password cannot be more than 255 characters long.') 71 'maxlength': $localize`Password cannot be more than 255 characters long.`
73 } 72 }
74 } 73 }
75 74
@@ -79,37 +78,37 @@ export class UserValidatorsService {
79 Validators.maxLength(255) 78 Validators.maxLength(255)
80 ], 79 ],
81 MESSAGES: { 80 MESSAGES: {
82 'minlength': this.i18n('Password must be at least 6 characters long.'), 81 'minlength': $localize`Password must be at least 6 characters long.`,
83 'maxlength': this.i18n('Password cannot be more than 255 characters long.') 82 'maxlength': $localize`Password cannot be more than 255 characters long.`
84 } 83 }
85 } 84 }
86 85
87 this.USER_CONFIRM_PASSWORD = { 86 this.USER_CONFIRM_PASSWORD = {
88 VALIDATORS: [], 87 VALIDATORS: [],
89 MESSAGES: { 88 MESSAGES: {
90 'matchPassword': this.i18n('The new password and the confirmed password do not correspond.') 89 'matchPassword': $localize`The new password and the confirmed password do not correspond.`
91 } 90 }
92 } 91 }
93 92
94 this.USER_VIDEO_QUOTA = { 93 this.USER_VIDEO_QUOTA = {
95 VALIDATORS: [ Validators.required, Validators.min(-1) ], 94 VALIDATORS: [ Validators.required, Validators.min(-1) ],
96 MESSAGES: { 95 MESSAGES: {
97 'required': this.i18n('Video quota is required.'), 96 'required': $localize`Video quota is required.`,
98 'min': this.i18n('Quota must be greater than -1.') 97 'min': $localize`Quota must be greater than -1.`
99 } 98 }
100 } 99 }
101 this.USER_VIDEO_QUOTA_DAILY = { 100 this.USER_VIDEO_QUOTA_DAILY = {
102 VALIDATORS: [ Validators.required, Validators.min(-1) ], 101 VALIDATORS: [ Validators.required, Validators.min(-1) ],
103 MESSAGES: { 102 MESSAGES: {
104 'required': this.i18n('Daily upload limit is required.'), 103 'required': $localize`Daily upload limit is required.`,
105 'min': this.i18n('Daily upload limit must be greater than -1.') 104 'min': $localize`Daily upload limit must be greater than -1.`
106 } 105 }
107 } 106 }
108 107
109 this.USER_ROLE = { 108 this.USER_ROLE = {
110 VALIDATORS: [ Validators.required ], 109 VALIDATORS: [ Validators.required ],
111 MESSAGES: { 110 MESSAGES: {
112 'required': this.i18n('User role is required.') 111 'required': $localize`User role is required.`
113 } 112 }
114 } 113 }
115 114
@@ -121,8 +120,8 @@ export class UserValidatorsService {
121 Validators.maxLength(1000) 120 Validators.maxLength(1000)
122 ], 121 ],
123 MESSAGES: { 122 MESSAGES: {
124 'minlength': this.i18n('Description must be at least 3 characters long.'), 123 'minlength': $localize`Description must be at least 3 characters long.`,
125 'maxlength': this.i18n('Description cannot be more than 1000 characters long.') 124 'maxlength': $localize`Description cannot be more than 1000 characters long.`
126 } 125 }
127 } 126 }
128 127
@@ -131,7 +130,7 @@ export class UserValidatorsService {
131 Validators.requiredTrue 130 Validators.requiredTrue
132 ], 131 ],
133 MESSAGES: { 132 MESSAGES: {
134 'required': this.i18n('You must agree with the instance terms in order to register on it.') 133 'required': $localize`You must agree with the instance terms in order to register on it.`
135 } 134 }
136 } 135 }
137 136
@@ -141,8 +140,8 @@ export class UserValidatorsService {
141 Validators.maxLength(250) 140 Validators.maxLength(250)
142 ], 141 ],
143 MESSAGES: { 142 MESSAGES: {
144 'minlength': this.i18n('Ban reason must be at least 3 characters long.'), 143 'minlength': $localize`Ban reason must be at least 3 characters long.`,
145 'maxlength': this.i18n('Ban reason cannot be more than 250 characters long.') 144 'maxlength': $localize`Ban reason cannot be more than 250 characters long.`
146 } 145 }
147 } 146 }
148 } 147 }
@@ -154,9 +153,9 @@ export class UserValidatorsService {
154 Validators.maxLength(120) 153 Validators.maxLength(120)
155 ], 154 ],
156 MESSAGES: { 155 MESSAGES: {
157 'required': this.i18n('Display name is required.'), 156 'required': $localize`Display name is required.`,
158 'minlength': this.i18n('Display name must be at least 1 character long.'), 157 'minlength': $localize`Display name must be at least 1 character long.`,
159 'maxlength': this.i18n('Display name cannot be more than 50 characters long.') 158 'maxlength': $localize`Display name cannot be more than 50 characters long.`
160 } 159 }
161 } 160 }
162 161
diff --git a/client/src/app/shared/shared-forms/form-validators/video-accept-ownership-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/video-accept-ownership-validators.service.ts
index 998d616ec..aed9e9cdd 100644
--- a/client/src/app/shared/shared-forms/form-validators/video-accept-ownership-validators.service.ts
+++ b/client/src/app/shared/shared-forms/form-validators/video-accept-ownership-validators.service.ts
@@ -1,17 +1,16 @@
1import { I18n } from '@ngx-translate/i18n-polyfill'
2import { Validators } from '@angular/forms'
3import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { Validators } from '@angular/forms'
4import { BuildFormValidator } from './form-validator.service' 3import { BuildFormValidator } from './form-validator.service'
5 4
6@Injectable() 5@Injectable()
7export class VideoAcceptOwnershipValidatorsService { 6export class VideoAcceptOwnershipValidatorsService {
8 readonly CHANNEL: BuildFormValidator 7 readonly CHANNEL: BuildFormValidator
9 8
10 constructor (private i18n: I18n) { 9 constructor () {
11 this.CHANNEL = { 10 this.CHANNEL = {
12 VALIDATORS: [ Validators.required ], 11 VALIDATORS: [ Validators.required ],
13 MESSAGES: { 12 MESSAGES: {
14 'required': this.i18n('The channel is required.') 13 'required': $localize`The channel is required.`
15 } 14 }
16 } 15 }
17 } 16 }
diff --git a/client/src/app/shared/shared-forms/form-validators/video-block-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/video-block-validators.service.ts
index ddf0ab5eb..bce1880dc 100644
--- a/client/src/app/shared/shared-forms/form-validators/video-block-validators.service.ts
+++ b/client/src/app/shared/shared-forms/form-validators/video-block-validators.service.ts
@@ -1,18 +1,17 @@
1import { I18n } from '@ngx-translate/i18n-polyfill'
2import { Validators } from '@angular/forms'
3import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { Validators } from '@angular/forms'
4import { BuildFormValidator } from './form-validator.service' 3import { BuildFormValidator } from './form-validator.service'
5 4
6@Injectable() 5@Injectable()
7export class VideoBlockValidatorsService { 6export class VideoBlockValidatorsService {
8 readonly VIDEO_BLOCK_REASON: BuildFormValidator 7 readonly VIDEO_BLOCK_REASON: BuildFormValidator
9 8
10 constructor (private i18n: I18n) { 9 constructor () {
11 this.VIDEO_BLOCK_REASON = { 10 this.VIDEO_BLOCK_REASON = {
12 VALIDATORS: [ Validators.minLength(2), Validators.maxLength(300) ], 11 VALIDATORS: [ Validators.minLength(2), Validators.maxLength(300) ],
13 MESSAGES: { 12 MESSAGES: {
14 'minlength': this.i18n('Block reason must be at least 2 characters long.'), 13 'minlength': $localize`Block reason must be at least 2 characters long.`,
15 'maxlength': this.i18n('Block reason cannot be more than 300 characters long.') 14 'maxlength': $localize`Block reason cannot be more than 300 characters long.`
16 } 15 }
17 } 16 }
18 } 17 }
diff --git a/client/src/app/shared/shared-forms/form-validators/video-captions-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/video-captions-validators.service.ts
index 280d28414..7e90264e5 100644
--- a/client/src/app/shared/shared-forms/form-validators/video-captions-validators.service.ts
+++ b/client/src/app/shared/shared-forms/form-validators/video-captions-validators.service.ts
@@ -1,6 +1,5 @@
1import { I18n } from '@ngx-translate/i18n-polyfill'
2import { Validators } from '@angular/forms'
3import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { Validators } from '@angular/forms'
4import { BuildFormValidator } from './form-validator.service' 3import { BuildFormValidator } from './form-validator.service'
5 4
6@Injectable() 5@Injectable()
@@ -8,19 +7,19 @@ export class VideoCaptionsValidatorsService {
8 readonly VIDEO_CAPTION_LANGUAGE: BuildFormValidator 7 readonly VIDEO_CAPTION_LANGUAGE: BuildFormValidator
9 readonly VIDEO_CAPTION_FILE: BuildFormValidator 8 readonly VIDEO_CAPTION_FILE: BuildFormValidator
10 9
11 constructor (private i18n: I18n) { 10 constructor () {
12 11
13 this.VIDEO_CAPTION_LANGUAGE = { 12 this.VIDEO_CAPTION_LANGUAGE = {
14 VALIDATORS: [ Validators.required ], 13 VALIDATORS: [ Validators.required ],
15 MESSAGES: { 14 MESSAGES: {
16 'required': this.i18n('Video caption language is required.') 15 'required': $localize`Video caption language is required.`
17 } 16 }
18 } 17 }
19 18
20 this.VIDEO_CAPTION_FILE = { 19 this.VIDEO_CAPTION_FILE = {
21 VALIDATORS: [ Validators.required ], 20 VALIDATORS: [ Validators.required ],
22 MESSAGES: { 21 MESSAGES: {
23 'required': this.i18n('Video caption file is required.') 22 'required': $localize`Video caption file is required.`
24 } 23 }
25 } 24 }
26 } 25 }
diff --git a/client/src/app/shared/shared-forms/form-validators/video-change-ownership-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/video-change-ownership-validators.service.ts
index 59659defd..8c809a0d5 100644
--- a/client/src/app/shared/shared-forms/form-validators/video-change-ownership-validators.service.ts
+++ b/client/src/app/shared/shared-forms/form-validators/video-change-ownership-validators.service.ts
@@ -1,18 +1,17 @@
1import { I18n } from '@ngx-translate/i18n-polyfill'
2import { AbstractControl, ValidationErrors, Validators } from '@angular/forms'
3import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { AbstractControl, ValidationErrors, Validators } from '@angular/forms'
4import { BuildFormValidator } from './form-validator.service' 3import { BuildFormValidator } from './form-validator.service'
5 4
6@Injectable() 5@Injectable()
7export class VideoChangeOwnershipValidatorsService { 6export class VideoChangeOwnershipValidatorsService {
8 readonly USERNAME: BuildFormValidator 7 readonly USERNAME: BuildFormValidator
9 8
10 constructor (private i18n: I18n) { 9 constructor () {
11 this.USERNAME = { 10 this.USERNAME = {
12 VALIDATORS: [ Validators.required, this.localAccountValidator ], 11 VALIDATORS: [ Validators.required, this.localAccountValidator ],
13 MESSAGES: { 12 MESSAGES: {
14 'required': this.i18n('The username is required.'), 13 'required': $localize`The username is required.`,
15 'localAccountOnly': this.i18n('You can only transfer ownership to a local account') 14 'localAccountOnly': $localize`You can only transfer ownership to a local account`
16 } 15 }
17 } 16 }
18 } 17 }
diff --git a/client/src/app/shared/shared-forms/form-validators/video-channel-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/video-channel-validators.service.ts
index bb650b149..3e7444196 100644
--- a/client/src/app/shared/shared-forms/form-validators/video-channel-validators.service.ts
+++ b/client/src/app/shared/shared-forms/form-validators/video-channel-validators.service.ts
@@ -1,6 +1,5 @@
1import { I18n } from '@ngx-translate/i18n-polyfill'
2import { Validators } from '@angular/forms'
3import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { Validators } from '@angular/forms'
4import { BuildFormValidator } from './form-validator.service' 3import { BuildFormValidator } from './form-validator.service'
5 4
6@Injectable() 5@Injectable()
@@ -10,7 +9,7 @@ export class VideoChannelValidatorsService {
10 readonly VIDEO_CHANNEL_DESCRIPTION: BuildFormValidator 9 readonly VIDEO_CHANNEL_DESCRIPTION: BuildFormValidator
11 readonly VIDEO_CHANNEL_SUPPORT: BuildFormValidator 10 readonly VIDEO_CHANNEL_SUPPORT: BuildFormValidator
12 11
13 constructor (private i18n: I18n) { 12 constructor () {
14 this.VIDEO_CHANNEL_NAME = { 13 this.VIDEO_CHANNEL_NAME = {
15 VALIDATORS: [ 14 VALIDATORS: [
16 Validators.required, 15 Validators.required,
@@ -19,10 +18,10 @@ export class VideoChannelValidatorsService {
19 Validators.pattern(/^[a-z0-9][a-z0-9._]*$/) 18 Validators.pattern(/^[a-z0-9][a-z0-9._]*$/)
20 ], 19 ],
21 MESSAGES: { 20 MESSAGES: {
22 'required': this.i18n('Name is required.'), 21 'required': $localize`Name is required.`,
23 'minlength': this.i18n('Name must be at least 1 character long.'), 22 'minlength': $localize`Name must be at least 1 character long.`,
24 'maxlength': this.i18n('Name cannot be more than 50 characters long.'), 23 'maxlength': $localize`Name cannot be more than 50 characters long.`,
25 'pattern': this.i18n('Name should be lowercase alphanumeric; dots and underscores are allowed.') 24 'pattern': $localize`Name should be lowercase alphanumeric; dots and underscores are allowed.`
26 } 25 }
27 } 26 }
28 27
@@ -33,9 +32,9 @@ export class VideoChannelValidatorsService {
33 Validators.maxLength(50) 32 Validators.maxLength(50)
34 ], 33 ],
35 MESSAGES: { 34 MESSAGES: {
36 'required': i18n('Display name is required.'), 35 'required': $localize`Display name is required.`,
37 'minlength': i18n('Display name must be at least 1 character long.'), 36 'minlength': $localize`Display name must be at least 1 character long.`,
38 'maxlength': i18n('Display name cannot be more than 50 characters long.') 37 'maxlength': $localize`Display name cannot be more than 50 characters long.`
39 } 38 }
40 } 39 }
41 40
@@ -45,8 +44,8 @@ export class VideoChannelValidatorsService {
45 Validators.maxLength(1000) 44 Validators.maxLength(1000)
46 ], 45 ],
47 MESSAGES: { 46 MESSAGES: {
48 'minlength': i18n('Description must be at least 3 characters long.'), 47 'minlength': $localize`Description must be at least 3 characters long.`,
49 'maxlength': i18n('Description cannot be more than 1000 characters long.') 48 'maxlength': $localize`Description cannot be more than 1000 characters long.`
50 } 49 }
51 } 50 }
52 51
@@ -56,8 +55,8 @@ export class VideoChannelValidatorsService {
56 Validators.maxLength(1000) 55 Validators.maxLength(1000)
57 ], 56 ],
58 MESSAGES: { 57 MESSAGES: {
59 'minlength': i18n('Support text must be at least 3 characters long.'), 58 'minlength': $localize`Support text must be at least 3 characters long.`,
60 'maxlength': i18n('Support text cannot be more than 1000 characters long.') 59 'maxlength': $localize`Support text cannot be more than 1000 characters long`
61 } 60 }
62 } 61 }
63 } 62 }
diff --git a/client/src/app/shared/shared-forms/form-validators/video-comment-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/video-comment-validators.service.ts
index 97c8e967e..18e7ae264 100644
--- a/client/src/app/shared/shared-forms/form-validators/video-comment-validators.service.ts
+++ b/client/src/app/shared/shared-forms/form-validators/video-comment-validators.service.ts
@@ -1,19 +1,18 @@
1import { I18n } from '@ngx-translate/i18n-polyfill'
2import { Validators } from '@angular/forms'
3import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { Validators } from '@angular/forms'
4import { BuildFormValidator } from './form-validator.service' 3import { BuildFormValidator } from './form-validator.service'
5 4
6@Injectable() 5@Injectable()
7export class VideoCommentValidatorsService { 6export class VideoCommentValidatorsService {
8 readonly VIDEO_COMMENT_TEXT: BuildFormValidator 7 readonly VIDEO_COMMENT_TEXT: BuildFormValidator
9 8
10 constructor (private i18n: I18n) { 9 constructor () {
11 this.VIDEO_COMMENT_TEXT = { 10 this.VIDEO_COMMENT_TEXT = {
12 VALIDATORS: [ Validators.required, Validators.minLength(1), Validators.maxLength(3000) ], 11 VALIDATORS: [ Validators.required, Validators.minLength(1), Validators.maxLength(3000) ],
13 MESSAGES: { 12 MESSAGES: {
14 'required': this.i18n('Comment is required.'), 13 'required': $localize`Comment is required.`,
15 'minlength': this.i18n('Comment must be at least 2 characters long.'), 14 'minlength': $localize`Comment must be at least 2 characters long.`,
16 'maxlength': this.i18n('Comment cannot be more than 3000 characters long.') 15 'maxlength': $localize`Comment cannot be more than 3000 characters long.`
17 } 16 }
18 } 17 }
19 } 18 }
diff --git a/client/src/app/shared/shared-forms/form-validators/video-playlist-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/video-playlist-validators.service.ts
index ab9c43625..3b45a40fd 100644
--- a/client/src/app/shared/shared-forms/form-validators/video-playlist-validators.service.ts
+++ b/client/src/app/shared/shared-forms/form-validators/video-playlist-validators.service.ts
@@ -1,8 +1,7 @@
1import { I18n } from '@ngx-translate/i18n-polyfill'
2import { AbstractControl, FormControl, Validators } from '@angular/forms'
3import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
4import { BuildFormValidator } from './form-validator.service' 2import { AbstractControl, Validators } from '@angular/forms'
5import { VideoPlaylistPrivacy } from '@shared/models' 3import { VideoPlaylistPrivacy } from '@shared/models'
4import { BuildFormValidator } from './form-validator.service'
6 5
7@Injectable() 6@Injectable()
8export class VideoPlaylistValidatorsService { 7export class VideoPlaylistValidatorsService {
@@ -11,7 +10,7 @@ export class VideoPlaylistValidatorsService {
11 readonly VIDEO_PLAYLIST_DESCRIPTION: BuildFormValidator 10 readonly VIDEO_PLAYLIST_DESCRIPTION: BuildFormValidator
12 readonly VIDEO_PLAYLIST_CHANNEL_ID: BuildFormValidator 11 readonly VIDEO_PLAYLIST_CHANNEL_ID: BuildFormValidator
13 12
14 constructor (private i18n: I18n) { 13 constructor () {
15 this.VIDEO_PLAYLIST_DISPLAY_NAME = { 14 this.VIDEO_PLAYLIST_DISPLAY_NAME = {
16 VALIDATORS: [ 15 VALIDATORS: [
17 Validators.required, 16 Validators.required,
@@ -19,9 +18,9 @@ export class VideoPlaylistValidatorsService {
19 Validators.maxLength(120) 18 Validators.maxLength(120)
20 ], 19 ],
21 MESSAGES: { 20 MESSAGES: {
22 'required': this.i18n('Display name is required.'), 21 'required': $localize`Display name is required.`,
23 'minlength': this.i18n('Display name must be at least 1 character long.'), 22 'minlength': $localize`Display name must be at least 1 character long.`,
24 'maxlength': this.i18n('Display name cannot be more than 120 characters long.') 23 'maxlength': $localize`Display name cannot be more than 120 characters long.`
25 } 24 }
26 } 25 }
27 26
@@ -30,7 +29,7 @@ export class VideoPlaylistValidatorsService {
30 Validators.required 29 Validators.required
31 ], 30 ],
32 MESSAGES: { 31 MESSAGES: {
33 'required': this.i18n('Privacy is required.') 32 'required': $localize`Privacy is required.`
34 } 33 }
35 } 34 }
36 35
@@ -40,15 +39,15 @@ export class VideoPlaylistValidatorsService {
40 Validators.maxLength(1000) 39 Validators.maxLength(1000)
41 ], 40 ],
42 MESSAGES: { 41 MESSAGES: {
43 'minlength': i18n('Description must be at least 3 characters long.'), 42 'minlength': $localize`Description must be at least 3 characters long.`,
44 'maxlength': i18n('Description cannot be more than 1000 characters long.') 43 'maxlength': $localize`Description cannot be more than 1000 characters long.`
45 } 44 }
46 } 45 }
47 46
48 this.VIDEO_PLAYLIST_CHANNEL_ID = { 47 this.VIDEO_PLAYLIST_CHANNEL_ID = {
49 VALIDATORS: [ ], 48 VALIDATORS: [ ],
50 MESSAGES: { 49 MESSAGES: {
51 'required': this.i18n('The channel is required when the playlist is public.') 50 'required': $localize`The channel is required when the playlist is public.`
52 } 51 }
53 } 52 }
54 } 53 }
diff --git a/client/src/app/shared/shared-forms/form-validators/video-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/video-validators.service.ts
index c96e4ef66..8119c1ae7 100644
--- a/client/src/app/shared/shared-forms/form-validators/video-validators.service.ts
+++ b/client/src/app/shared/shared-forms/form-validators/video-validators.service.ts
@@ -1,6 +1,5 @@
1import { I18n } from '@ngx-translate/i18n-polyfill'
2import { Validators, ValidatorFn, ValidationErrors, AbstractControl } from '@angular/forms'
3import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { AbstractControl, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'
4import { BuildFormValidator } from './form-validator.service' 3import { BuildFormValidator } from './form-validator.service'
5 4
6@Injectable() 5@Injectable()
@@ -19,21 +18,21 @@ export class VideoValidatorsService {
19 readonly VIDEO_SCHEDULE_PUBLICATION_AT: BuildFormValidator 18 readonly VIDEO_SCHEDULE_PUBLICATION_AT: BuildFormValidator
20 readonly VIDEO_ORIGINALLY_PUBLISHED_AT: BuildFormValidator 19 readonly VIDEO_ORIGINALLY_PUBLISHED_AT: BuildFormValidator
21 20
22 constructor (private i18n: I18n) { 21 constructor () {
23 22
24 this.VIDEO_NAME = { 23 this.VIDEO_NAME = {
25 VALIDATORS: [ Validators.required, Validators.minLength(3), Validators.maxLength(120) ], 24 VALIDATORS: [ Validators.required, Validators.minLength(3), Validators.maxLength(120) ],
26 MESSAGES: { 25 MESSAGES: {
27 'required': this.i18n('Video name is required.'), 26 'required': $localize`Video name is required.`,
28 'minlength': this.i18n('Video name must be at least 3 characters long.'), 27 'minlength': $localize`Video name must be at least 3 characters long.`,
29 'maxlength': this.i18n('Video name cannot be more than 120 characters long.') 28 'maxlength': $localize`Video name cannot be more than 120 characters long.`
30 } 29 }
31 } 30 }
32 31
33 this.VIDEO_PRIVACY = { 32 this.VIDEO_PRIVACY = {
34 VALIDATORS: [ Validators.required ], 33 VALIDATORS: [ Validators.required ],
35 MESSAGES: { 34 MESSAGES: {
36 'required': this.i18n('Video privacy is required.') 35 'required': $localize`Video privacy is required.`
37 } 36 }
38 } 37 }
39 38
@@ -60,46 +59,46 @@ export class VideoValidatorsService {
60 this.VIDEO_CHANNEL = { 59 this.VIDEO_CHANNEL = {
61 VALIDATORS: [ Validators.required ], 60 VALIDATORS: [ Validators.required ],
62 MESSAGES: { 61 MESSAGES: {
63 'required': this.i18n('Video channel is required.') 62 'required': $localize`Video channel is required.`
64 } 63 }
65 } 64 }
66 65
67 this.VIDEO_DESCRIPTION = { 66 this.VIDEO_DESCRIPTION = {
68 VALIDATORS: [ Validators.minLength(3), Validators.maxLength(10000) ], 67 VALIDATORS: [ Validators.minLength(3), Validators.maxLength(10000) ],
69 MESSAGES: { 68 MESSAGES: {
70 'minlength': this.i18n('Video description must be at least 3 characters long.'), 69 'minlength': $localize`Video description must be at least 3 characters long.`,
71 'maxlength': this.i18n('Video description cannot be more than 10000 characters long.') 70 'maxlength': $localize`Video description cannot be more than 10000 characters long.`
72 } 71 }
73 } 72 }
74 73
75 this.VIDEO_TAG = { 74 this.VIDEO_TAG = {
76 VALIDATORS: [ Validators.minLength(2), Validators.maxLength(30) ], 75 VALIDATORS: [ Validators.minLength(2), Validators.maxLength(30) ],
77 MESSAGES: { 76 MESSAGES: {
78 'minlength': this.i18n('A tag should be more than 2 characters long.'), 77 'minlength': $localize`A tag should be more than 2 characters long.`,
79 'maxlength': this.i18n('A tag should be less than 30 characters long.') 78 'maxlength': $localize`A tag should be less than 30 characters long.`
80 } 79 }
81 } 80 }
82 81
83 this.VIDEO_TAGS_ARRAY = { 82 this.VIDEO_TAGS_ARRAY = {
84 VALIDATORS: [ Validators.maxLength(5), this.arrayTagLengthValidator() ], 83 VALIDATORS: [ Validators.maxLength(5), this.arrayTagLengthValidator() ],
85 MESSAGES: { 84 MESSAGES: {
86 'maxlength': this.i18n('A maximum of 5 tags can be used on a video.'), 85 'maxlength': $localize`A maximum of 5 tags can be used on a video.`,
87 'arrayTagLength': this.i18n('A tag should be more than 2, and less than 30 characters long.') 86 'arrayTagLength': $localize`A tag should be more than 2, and less than 30 characters long.`
88 } 87 }
89 } 88 }
90 89
91 this.VIDEO_SUPPORT = { 90 this.VIDEO_SUPPORT = {
92 VALIDATORS: [ Validators.minLength(3), Validators.maxLength(1000) ], 91 VALIDATORS: [ Validators.minLength(3), Validators.maxLength(1000) ],
93 MESSAGES: { 92 MESSAGES: {
94 'minlength': this.i18n('Video support must be at least 3 characters long.'), 93 'minlength': $localize`Video support must be at least 3 characters long.`,
95 'maxlength': this.i18n('Video support cannot be more than 1000 characters long.') 94 'maxlength': $localize`Video support cannot be more than 1000 characters long.`
96 } 95 }
97 } 96 }
98 97
99 this.VIDEO_SCHEDULE_PUBLICATION_AT = { 98 this.VIDEO_SCHEDULE_PUBLICATION_AT = {
100 VALIDATORS: [ ], 99 VALIDATORS: [ ],
101 MESSAGES: { 100 MESSAGES: {
102 'required': this.i18n('A date is required to schedule video update.') 101 'required': $localize`A date is required to schedule video update.`
103 } 102 }
104 } 103 }
105 104
diff --git a/client/src/app/shared/shared-forms/input-readonly-copy.component.ts b/client/src/app/shared/shared-forms/input-readonly-copy.component.ts
index 7528fb7a1..a67b0c691 100644
--- a/client/src/app/shared/shared-forms/input-readonly-copy.component.ts
+++ b/client/src/app/shared/shared-forms/input-readonly-copy.component.ts
@@ -1,6 +1,5 @@
1import { Component, Input } from '@angular/core' 1import { Component, Input } from '@angular/core'
2import { Notifier } from '@app/core' 2import { Notifier } from '@app/core'
3import { I18n } from '@ngx-translate/i18n-polyfill'
4 3
5@Component({ 4@Component({
6 selector: 'my-input-readonly-copy', 5 selector: 'my-input-readonly-copy',
@@ -10,12 +9,9 @@ import { I18n } from '@ngx-translate/i18n-polyfill'
10export class InputReadonlyCopyComponent { 9export class InputReadonlyCopyComponent {
11 @Input() value = '' 10 @Input() value = ''
12 11
13 constructor ( 12 constructor (private notifier: Notifier) { }
14 private notifier: Notifier,
15 private i18n: I18n
16 ) { }
17 13
18 activateCopiedMessage () { 14 activateCopiedMessage () {
19 this.notifier.success(this.i18n('Copied')) 15 this.notifier.success($localize`Copied`)
20 } 16 }
21} 17}
diff --git a/client/src/app/shared/shared-forms/preview-upload.component.ts b/client/src/app/shared/shared-forms/preview-upload.component.ts
index 7afff0b31..a55dcdd9a 100644
--- a/client/src/app/shared/shared-forms/preview-upload.component.ts
+++ b/client/src/app/shared/shared-forms/preview-upload.component.ts
@@ -2,7 +2,6 @@ import { Component, forwardRef, Input, OnInit } from '@angular/core'
2import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' 2import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
3import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser' 3import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'
4import { ServerService } from '@app/core' 4import { ServerService } from '@app/core'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { ServerConfig } from '@shared/models' 5import { ServerConfig } from '@shared/models'
7import { BytesPipe } from '../shared-main' 6import { BytesPipe } from '../shared-main'
8 7
@@ -34,11 +33,10 @@ export class PreviewUploadComponent implements OnInit, ControlValueAccessor {
34 33
35 constructor ( 34 constructor (
36 private sanitizer: DomSanitizer, 35 private sanitizer: DomSanitizer,
37 private serverService: ServerService, 36 private serverService: ServerService
38 private i18n: I18n
39 ) { 37 ) {
40 this.bytesPipe = new BytesPipe() 38 this.bytesPipe = new BytesPipe()
41 this.maxSizeText = this.i18n('max size') 39 this.maxSizeText = $localize`max size`
42 } 40 }
43 41
44 get videoImageExtensions () { 42 get videoImageExtensions () {
diff --git a/client/src/app/shared/shared-forms/reactive-file.component.ts b/client/src/app/shared/shared-forms/reactive-file.component.ts
index 9ebf487ce..eeb2a3fd8 100644
--- a/client/src/app/shared/shared-forms/reactive-file.component.ts
+++ b/client/src/app/shared/shared-forms/reactive-file.component.ts
@@ -2,7 +2,6 @@ import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@ang
2import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' 2import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
3import { Notifier } from '@app/core' 3import { Notifier } from '@app/core'
4import { GlobalIconName } from '@app/shared/shared-icons' 4import { GlobalIconName } from '@app/shared/shared-icons'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6 5
7@Component({ 6@Component({
8 selector: 'my-reactive-file', 7 selector: 'my-reactive-file',
@@ -31,10 +30,7 @@ export class ReactiveFileComponent implements OnInit, ControlValueAccessor {
31 30
32 private file: File 31 private file: File
33 32
34 constructor ( 33 constructor (private notifier: Notifier) { }
35 private notifier: Notifier,
36 private i18n: I18n
37 ) {}
38 34
39 get filename () { 35 get filename () {
40 if (!this.file) return '' 36 if (!this.file) return ''
@@ -51,16 +47,13 @@ export class ReactiveFileComponent implements OnInit, ControlValueAccessor {
51 const [ file ] = event.target.files 47 const [ file ] = event.target.files
52 48
53 if (file.size > this.maxFileSize) { 49 if (file.size > this.maxFileSize) {
54 this.notifier.error(this.i18n('This file is too large.')) 50 this.notifier.error($localize`This file is too large.`)
55 return 51 return
56 } 52 }
57 53
58 const extension = '.' + file.name.split('.').pop() 54 const extension = '.' + file.name.split('.').pop()
59 if (this.extensions.includes(extension) === false) { 55 if (this.extensions.includes(extension) === false) {
60 const message = this.i18n( 56 const message = $localize`PeerTube cannot handle this kind of file. Accepted extensions are ${this.allowedExtensionsMessage}}.`
61 'PeerTube cannot handle this kind of file. Accepted extensions are {{extensions}}.',
62 { extensions: this.allowedExtensionsMessage }
63 )
64 this.notifier.error(message) 57 this.notifier.error(message)
65 58
66 return 59 return
diff --git a/client/src/app/shared/shared-forms/select/select-checkbox.component.ts b/client/src/app/shared/shared-forms/select/select-checkbox.component.ts
index fd683ae5d..eb0c49034 100644
--- a/client/src/app/shared/shared-forms/select/select-checkbox.component.ts
+++ b/client/src/app/shared/shared-forms/select/select-checkbox.component.ts
@@ -1,7 +1,6 @@
1import { Component, Input, forwardRef, OnInit } from '@angular/core' 1import { Component, forwardRef, Input, OnInit } from '@angular/core'
2import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms' 2import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
3import { SelectOptionsItem } from './select-options.component' 3import { SelectOptionsItem } from './select-options.component'
4import { I18n } from '@ngx-translate/i18n-polyfill'
5 4
6export type ItemSelectCheckboxValue = { id?: string | number, group?: string } | string 5export type ItemSelectCheckboxValue = { id?: string | number, group?: string } | string
7 6
@@ -25,12 +24,8 @@ export class SelectCheckboxComponent implements OnInit, ControlValueAccessor {
25 @Input() maxSelectedItems: number 24 @Input() maxSelectedItems: number
26 @Input() placeholder: string 25 @Input() placeholder: string
27 26
28 constructor (
29 private i18n: I18n
30 ) {}
31
32 ngOnInit () { 27 ngOnInit () {
33 if (!this.placeholder) this.placeholder = this.i18n('Add a new option') 28 if (!this.placeholder) this.placeholder = $localize`Add a new option`
34 } 29 }
35 30
36 propagateChange = (_: any) => { /* empty */ } 31 propagateChange = (_: any) => { /* empty */ }
diff --git a/client/src/app/shared/shared-instance/instance-features-table.component.ts b/client/src/app/shared/shared-instance/instance-features-table.component.ts
index 8fd15ebad..76b595c20 100644
--- a/client/src/app/shared/shared-instance/instance-features-table.component.ts
+++ b/client/src/app/shared/shared-instance/instance-features-table.component.ts
@@ -1,6 +1,5 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { ServerService } from '@app/core' 2import { ServerService } from '@app/core'
3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { ServerConfig } from '@shared/models' 3import { ServerConfig } from '@shared/models'
5 4
6@Component({ 5@Component({
@@ -12,11 +11,7 @@ export class InstanceFeaturesTableComponent implements OnInit {
12 quotaHelpIndication = '' 11 quotaHelpIndication = ''
13 serverConfig: ServerConfig 12 serverConfig: ServerConfig
14 13
15 constructor ( 14 constructor (private serverService: ServerService) { }
16 private i18n: I18n,
17 private serverService: ServerService
18 ) {
19 }
20 15
21 get initialUserVideoQuota () { 16 get initialUserVideoQuota () {
22 return this.serverConfig.user.videoQuota 17 return this.serverConfig.user.videoQuota
@@ -38,9 +33,9 @@ export class InstanceFeaturesTableComponent implements OnInit {
38 buildNSFWLabel () { 33 buildNSFWLabel () {
39 const policy = this.serverConfig.instance.defaultNSFWPolicy 34 const policy = this.serverConfig.instance.defaultNSFWPolicy
40 35
41 if (policy === 'do_not_list') return this.i18n('Hidden') 36 if (policy === 'do_not_list') return $localize`Hidden`
42 if (policy === 'blur') return this.i18n('Blurred with confirmation request') 37 if (policy === 'blur') return $localize`Blurred with confirmation request`
43 if (policy === 'display') return this.i18n('Displayed') 38 if (policy === 'display') return $localize`Displayed`
44 } 39 }
45 40
46 getServerVersionAndCommit () { 41 getServerVersionAndCommit () {
@@ -55,7 +50,9 @@ export class InstanceFeaturesTableComponent implements OnInit {
55 50
56 const minutes = Math.floor(seconds % 3600 / 60) 51 const minutes = Math.floor(seconds % 3600 / 60)
57 52
58 return this.i18n('~ {{minutes}} {minutes, plural, =1 {minute} other {minutes}}', { minutes }) 53 if (minutes === 1) return $localize`~ 1 minute`
54
55 return $localize`~ ${minutes} minutes`
59 } 56 }
60 57
61 private buildQuotaHelpIndication () { 58 private buildQuotaHelpIndication () {
@@ -71,9 +68,9 @@ export class InstanceFeaturesTableComponent implements OnInit {
71 const normalSeconds = initialUserVideoQuotaBit / (1.5 * 1000 * 1000) 68 const normalSeconds = initialUserVideoQuotaBit / (1.5 * 1000 * 1000)
72 69
73 const lines = [ 70 const lines = [
74 this.i18n('{{seconds}} of full HD videos', { seconds: this.getApproximateTime(fullHdSeconds) }), 71 $localize`${this.getApproximateTime(fullHdSeconds)} of full HD videos`,
75 this.i18n('{{seconds}} of HD videos', { seconds: this.getApproximateTime(hdSeconds) }), 72 $localize`${this.getApproximateTime(hdSeconds)} of HD videos`,
76 this.i18n('{{seconds}} of average quality videos', { seconds: this.getApproximateTime(normalSeconds) }) 73 $localize`${this.getApproximateTime(normalSeconds)} of average quality videos`
77 ] 74 ]
78 75
79 this.quotaHelpIndication = lines.join('<br />') 76 this.quotaHelpIndication = lines.join('<br />')
diff --git a/client/src/app/shared/shared-main/account/actor-avatar-info.component.ts b/client/src/app/shared/shared-main/account/actor-avatar-info.component.ts
index 1389218cc..3a86e5b21 100644
--- a/client/src/app/shared/shared-main/account/actor-avatar-info.component.ts
+++ b/client/src/app/shared/shared-main/account/actor-avatar-info.component.ts
@@ -1,7 +1,6 @@
1import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core' 1import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'
2import { Notifier, ServerService } from '@app/core' 2import { Notifier, ServerService } from '@app/core'
3import { Account, BytesPipe, VideoChannel } from '@app/shared/shared-main' 3import { Account, BytesPipe, VideoChannel } from '@app/shared/shared-main'
4import { I18n } from '@ngx-translate/i18n-polyfill'
5import { ServerConfig } from '@shared/models' 4import { ServerConfig } from '@shared/models'
6 5
7@Component({ 6@Component({
@@ -23,11 +22,10 @@ export class ActorAvatarInfoComponent implements OnInit {
23 22
24 constructor ( 23 constructor (
25 private serverService: ServerService, 24 private serverService: ServerService,
26 private notifier: Notifier, 25 private notifier: Notifier
27 private i18n: I18n
28 ) { 26 ) {
29 this.bytesPipe = new BytesPipe() 27 this.bytesPipe = new BytesPipe()
30 this.maxSizeText = this.i18n('max size') 28 this.maxSizeText = $localize`max size`
31 } 29 }
32 30
33 ngOnInit (): void { 31 ngOnInit (): void {
diff --git a/client/src/app/shared/shared-main/account/avatar.component.ts b/client/src/app/shared/shared-main/account/avatar.component.ts
index 73c145ef9..2967828a0 100644
--- a/client/src/app/shared/shared-main/account/avatar.component.ts
+++ b/client/src/app/shared/shared-main/account/avatar.component.ts
@@ -1,6 +1,5 @@
1import { Component, Input, OnInit } from '@angular/core' 1import { Component, Input, OnInit } from '@angular/core'
2import { Video } from '../video/video.model' 2import { Video } from '../video/video.model'
3import { I18n } from '@ngx-translate/i18n-polyfill'
4 3
5@Component({ 4@Component({
6 selector: 'avatar-channel', 5 selector: 'avatar-channel',
@@ -15,19 +14,9 @@ export class AvatarComponent implements OnInit {
15 channelLinkTitle = '' 14 channelLinkTitle = ''
16 accountLinkTitle = '' 15 accountLinkTitle = ''
17 16
18 constructor (
19 private i18n: I18n
20 ) {}
21
22 ngOnInit () { 17 ngOnInit () {
23 this.channelLinkTitle = this.i18n( 18 this.channelLinkTitle = $localize`${this.video.account.name} (channel page)`
24 '{{name}} (channel page)', 19 this.accountLinkTitle = $localize`${this.video.byAccount} (account page)`
25 { name: this.video.channel.name, handle: this.video.byVideoChannel }
26 )
27 this.accountLinkTitle = this.i18n(
28 '{{name}} (account page)',
29 { name: this.video.account.name, handle: this.video.byAccount }
30 )
31 } 20 }
32 21
33 isChannelAvatarNull () { 22 isChannelAvatarNull () {
diff --git a/client/src/app/shared/shared-main/angular/from-now.pipe.ts b/client/src/app/shared/shared-main/angular/from-now.pipe.ts
index 9851468ee..5d85590bb 100644
--- a/client/src/app/shared/shared-main/angular/from-now.pipe.ts
+++ b/client/src/app/shared/shared-main/angular/from-now.pipe.ts
@@ -1,39 +1,36 @@
1import { Pipe, PipeTransform } from '@angular/core' 1import { Pipe, PipeTransform } from '@angular/core'
2import { I18n } from '@ngx-translate/i18n-polyfill'
3 2
4// Thanks: https://stackoverflow.com/questions/3177836/how-to-format-time-since-xxx-e-g-4-minutes-ago-similar-to-stack-exchange-site 3// Thanks: https://stackoverflow.com/questions/3177836/how-to-format-time-since-xxx-e-g-4-minutes-ago-similar-to-stack-exchange-site
5@Pipe({ name: 'myFromNow' }) 4@Pipe({ name: 'myFromNow' })
6export class FromNowPipe implements PipeTransform { 5export class FromNowPipe implements PipeTransform {
7 6
8 constructor (private i18n: I18n) { }
9
10 transform (arg: number | Date | string) { 7 transform (arg: number | Date | string) {
11 const argDate = new Date(arg) 8 const argDate = new Date(arg)
12 const seconds = Math.floor((Date.now() - argDate.getTime()) / 1000) 9 const seconds = Math.floor((Date.now() - argDate.getTime()) / 1000)
13 10
14 let interval = Math.floor(seconds / 31536000) 11 let interval = Math.floor(seconds / 31536000)
15 if (interval > 1) return this.i18n('{{interval}} years ago', { interval }) 12 if (interval > 1) return $localize`${interval} years ago`
16 if (interval === 1) return this.i18n('{{interval}} year ago', { interval }) 13 if (interval === 1) return $localize`${interval} year ago`
17 14
18 interval = Math.floor(seconds / 2592000) 15 interval = Math.floor(seconds / 2592000)
19 if (interval > 1) return this.i18n('{{interval}} months ago', { interval }) 16 if (interval > 1) return $localize`${interval} months ago`
20 if (interval === 1) return this.i18n('{{interval}} month ago', { interval }) 17 if (interval === 1) return $localize`${interval} month ago`
21 18
22 interval = Math.floor(seconds / 604800) 19 interval = Math.floor(seconds / 604800)
23 if (interval > 1) return this.i18n('{{interval}} weeks ago', { interval }) 20 if (interval > 1) return $localize`${interval} weeks ago`
24 if (interval === 1) return this.i18n('{{interval}} week ago', { interval }) 21 if (interval === 1) return $localize`${interval} week ago`
25 22
26 interval = Math.floor(seconds / 86400) 23 interval = Math.floor(seconds / 86400)
27 if (interval > 1) return this.i18n('{{interval}} days ago', { interval }) 24 if (interval > 1) return $localize`${interval} days ago`
28 if (interval === 1) return this.i18n('{{interval}} day ago', { interval }) 25 if (interval === 1) return $localize`${interval} day ago`
29 26
30 interval = Math.floor(seconds / 3600) 27 interval = Math.floor(seconds / 3600)
31 if (interval > 1) return this.i18n('{{interval}} hours ago', { interval }) 28 if (interval > 1) return $localize`${interval} hours ago`
32 if (interval === 1) return this.i18n('{{interval}} hour ago', { interval }) 29 if (interval === 1) return $localize`${interval} hour ago`
33 30
34 interval = Math.floor(seconds / 60) 31 interval = Math.floor(seconds / 60)
35 if (interval >= 1) return this.i18n('{{interval}} min ago', { interval }) 32 if (interval >= 1) return $localize`${interval} min ago`
36 33
37 return this.i18n('just now') 34 return $localize`just now`
38 } 35 }
39} 36}
diff --git a/client/src/app/shared/shared-main/buttons/delete-button.component.ts b/client/src/app/shared/shared-main/buttons/delete-button.component.ts
index aced0f881..18995422a 100644
--- a/client/src/app/shared/shared-main/buttons/delete-button.component.ts
+++ b/client/src/app/shared/shared-main/buttons/delete-button.component.ts
@@ -1,5 +1,4 @@
1import { Component, Input, OnInit } from '@angular/core' 1import { Component, Input, OnInit } from '@angular/core'
2import { I18n } from '@ngx-translate/i18n-polyfill'
3 2
4@Component({ 3@Component({
5 selector: 'my-delete-button', 4 selector: 'my-delete-button',
@@ -11,17 +10,15 @@ export class DeleteButtonComponent implements OnInit {
11 @Input() label: string 10 @Input() label: string
12 @Input() title: string 11 @Input() title: string
13 12
14 constructor (private i18n: I18n) { }
15
16 ngOnInit () { 13 ngOnInit () {
17 // <my-delete-button /> No label 14 // <my-delete-button /> No label
18 if (this.label === undefined && !this.title) { 15 if (this.label === undefined && !this.title) {
19 this.title = this.i18n('Delete') 16 this.title = $localize`Delete`
20 } 17 }
21 18
22 // <my-delete-button label /> Use default label 19 // <my-delete-button label /> Use default label
23 if (this.label === '') { 20 if (this.label === '') {
24 this.label = this.i18n('Delete') 21 this.label = $localize`Delete`
25 22
26 if (!this.title) { 23 if (!this.title) {
27 this.title = this.label 24 this.title = this.label
diff --git a/client/src/app/shared/shared-main/buttons/edit-button.component.ts b/client/src/app/shared/shared-main/buttons/edit-button.component.ts
index d8ae39b84..4b76551ca 100644
--- a/client/src/app/shared/shared-main/buttons/edit-button.component.ts
+++ b/client/src/app/shared/shared-main/buttons/edit-button.component.ts
@@ -1,5 +1,4 @@
1import { Component, Input, OnInit } from '@angular/core' 1import { Component, Input, OnInit } from '@angular/core'
2import { I18n } from '@ngx-translate/i18n-polyfill'
3 2
4@Component({ 3@Component({
5 selector: 'my-edit-button', 4 selector: 'my-edit-button',
@@ -12,17 +11,15 @@ export class EditButtonComponent implements OnInit {
12 @Input() title: string 11 @Input() title: string
13 @Input() routerLink: string[] | string = [] 12 @Input() routerLink: string[] | string = []
14 13
15 constructor (private i18n: I18n) { }
16
17 ngOnInit () { 14 ngOnInit () {
18 // <my-edit-button /> No label 15 // <my-edit-button /> No label
19 if (this.label === undefined && !this.title) { 16 if (this.label === undefined && !this.title) {
20 this.title = this.i18n('Update') 17 this.title = $localize`Update`
21 } 18 }
22 19
23 // <my-edit-button label /> Use default label 20 // <my-edit-button label /> Use default label
24 if (this.label === '') { 21 if (this.label === '') {
25 this.label = this.i18n('Update') 22 this.label = $localize`Update`
26 23
27 if (!this.title) { 24 if (!this.title) {
28 this.title = this.label 25 this.title = this.label
diff --git a/client/src/app/shared/shared-main/misc/help.component.ts b/client/src/app/shared/shared-main/misc/help.component.ts
index 0825b96de..ebc965a88 100644
--- a/client/src/app/shared/shared-main/misc/help.component.ts
+++ b/client/src/app/shared/shared-main/misc/help.component.ts
@@ -1,6 +1,5 @@
1import { AfterContentInit, Component, ContentChildren, Input, OnChanges, OnInit, QueryList, TemplateRef } from '@angular/core' 1import { AfterContentInit, Component, ContentChildren, Input, OnChanges, OnInit, QueryList, TemplateRef } from '@angular/core'
2import { MarkdownService } from '@app/core' 2import { MarkdownService } from '@app/core'
3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { PeerTubeTemplateDirective } from '../angular' 3import { PeerTubeTemplateDirective } from '../angular'
5 4
6@Component({ 5@Component({
@@ -22,8 +21,6 @@ export class HelpComponent implements OnInit, OnChanges, AfterContentInit {
22 customHtmlTemplate: TemplateRef<any> 21 customHtmlTemplate: TemplateRef<any>
23 postHtmlTemplate: TemplateRef<any> 22 postHtmlTemplate: TemplateRef<any>
24 23
25 constructor (private i18n: I18n) { }
26
27 ngOnInit () { 24 ngOnInit () {
28 this.init() 25 this.init()
29 } 26 }
@@ -71,17 +68,17 @@ export class HelpComponent implements OnInit, OnChanges, AfterContentInit {
71 68
72 private formatMarkdownSupport (rules: string[]) { 69 private formatMarkdownSupport (rules: string[]) {
73 // tslint:disable:max-line-length 70 // tslint:disable:max-line-length
74 return this.i18n('<a href="https://en.wikipedia.org/wiki/Markdown#Example" target="_blank" rel="noopener noreferrer">Markdown</a> compatible that supports:') + 71 return $localize`<a href="https://en.wikipedia.org/wiki/Markdown#Example" target="_blank" rel="noopener noreferrer">Markdown</a> compatible that supports:` +
75 this.createMarkdownList(rules) 72 this.createMarkdownList(rules)
76 } 73 }
77 74
78 private createMarkdownList (rules: string[]) { 75 private createMarkdownList (rules: string[]) {
79 const rulesToText = { 76 const rulesToText = {
80 'emphasis': this.i18n('Emphasis'), 77 'emphasis': $localize`Emphasis`,
81 'link': this.i18n('Links'), 78 'link': $localize`Links`,
82 'newline': this.i18n('New lines'), 79 'newline': $localize`New lines`,
83 'list': this.i18n('Lists'), 80 'list': $localize`Lists`,
84 'image': this.i18n('Images') 81 'image': $localize`Images`
85 } 82 }
86 83
87 const bullets = rules.map(r => rulesToText[r]) 84 const bullets = rules.map(r => rulesToText[r])
diff --git a/client/src/app/shared/shared-main/shared-main.module.ts b/client/src/app/shared/shared-main/shared-main.module.ts
index 6186db4d6..7f4676dd1 100644
--- a/client/src/app/shared/shared-main/shared-main.module.ts
+++ b/client/src/app/shared/shared-main/shared-main.module.ts
@@ -13,7 +13,6 @@ import {
13 NgbPopoverModule, 13 NgbPopoverModule,
14 NgbTooltipModule 14 NgbTooltipModule
15} from '@ng-bootstrap/ng-bootstrap' 15} from '@ng-bootstrap/ng-bootstrap'
16import { I18n } from '@ngx-translate/i18n-polyfill'
17import { SharedGlobalIconModule } from '../shared-icons' 16import { SharedGlobalIconModule } from '../shared-icons'
18import { AccountService, ActorAvatarInfoComponent, AvatarComponent } from './account' 17import { AccountService, ActorAvatarInfoComponent, AvatarComponent } from './account'
19import { FromNowPipe, InfiniteScrollerDirective, NumberFormatterPipe, PeerTubeTemplateDirective, BytesPipe } from './angular' 18import { FromNowPipe, InfiniteScrollerDirective, NumberFormatterPipe, PeerTubeTemplateDirective, BytesPipe } from './angular'
@@ -129,8 +128,6 @@ import { VideoChannelService } from './video-channel'
129 ], 128 ],
130 129
131 providers: [ 130 providers: [
132 I18n,
133
134 DatePipe, 131 DatePipe,
135 132
136 FromNowPipe, 133 FromNowPipe,
diff --git a/client/src/app/shared/shared-main/users/user-quota.component.ts b/client/src/app/shared/shared-main/users/user-quota.component.ts
index 6830ad8fe..b38619186 100644
--- a/client/src/app/shared/shared-main/users/user-quota.component.ts
+++ b/client/src/app/shared/shared-main/users/user-quota.component.ts
@@ -1,7 +1,6 @@
1import { Subject } from 'rxjs' 1import { Subject } from 'rxjs'
2import { Component, Input, OnInit } from '@angular/core' 2import { Component, Input, OnInit } from '@angular/core'
3import { User, UserService } from '@app/core' 3import { User, UserService } from '@app/core'
4import { I18n } from '@ngx-translate/i18n-polyfill'
5import { BytesPipe } from '../angular' 4import { BytesPipe } from '../angular'
6 5
7@Component({ 6@Component({
@@ -22,10 +21,7 @@ export class UserQuotaComponent implements OnInit {
22 userVideoQuotaUsedDaily = 0 21 userVideoQuotaUsedDaily = 0
23 userVideoQuotaDailyPercentage = 15 22 userVideoQuotaDailyPercentage = 15
24 23
25 constructor ( 24 constructor (private userService: UserService) { }
26 private userService: UserService,
27 private i18n: I18n
28 ) { }
29 25
30 ngOnInit () { 26 ngOnInit () {
31 this.userInformationLoaded.subscribe( 27 this.userInformationLoaded.subscribe(
@@ -33,13 +29,13 @@ export class UserQuotaComponent implements OnInit {
33 if (this.user.videoQuota !== -1) { 29 if (this.user.videoQuota !== -1) {
34 this.userVideoQuota = new BytesPipe().transform(this.user.videoQuota, 0).toString() 30 this.userVideoQuota = new BytesPipe().transform(this.user.videoQuota, 0).toString()
35 } else { 31 } else {
36 this.userVideoQuota = this.i18n('Unlimited') 32 this.userVideoQuota = $localize`Unlimited`
37 } 33 }
38 34
39 if (this.user.videoQuotaDaily !== -1) { 35 if (this.user.videoQuotaDaily !== -1) {
40 this.userVideoQuotaDaily = new BytesPipe().transform(this.user.videoQuotaDaily, 0).toString() 36 this.userVideoQuotaDaily = new BytesPipe().transform(this.user.videoQuotaDaily, 0).toString()
41 } else { 37 } else {
42 this.userVideoQuotaDaily = this.i18n('Unlimited') 38 this.userVideoQuotaDaily = $localize`Unlimited`
43 } 39 }
44 } 40 }
45 ) 41 )
diff --git a/client/src/app/shared/shared-main/video/video.service.ts b/client/src/app/shared/shared-main/video/video.service.ts
index 978f775bf..b01e919aa 100644
--- a/client/src/app/shared/shared-main/video/video.service.ts
+++ b/client/src/app/shared/shared-main/video/video.service.ts
@@ -4,7 +4,6 @@ import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http'
4import { Injectable } from '@angular/core' 4import { Injectable } from '@angular/core'
5import { ComponentPaginationLight, RestExtractor, RestService, ServerService, UserService } from '@app/core' 5import { ComponentPaginationLight, RestExtractor, RestService, ServerService, UserService } from '@app/core'
6import { objectToFormData } from '@app/helpers' 6import { objectToFormData } from '@app/helpers'
7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { 7import {
9 FeedFormat, 8 FeedFormat,
10 NSFWPolicyType, 9 NSFWPolicyType,
@@ -15,11 +14,11 @@ import {
15 Video as VideoServerModel, 14 Video as VideoServerModel,
16 VideoConstant, 15 VideoConstant,
17 VideoDetails as VideoDetailsServerModel, 16 VideoDetails as VideoDetailsServerModel,
17 VideoFileMetadata,
18 VideoFilter, 18 VideoFilter,
19 VideoPrivacy, 19 VideoPrivacy,
20 VideoSortField, 20 VideoSortField,
21 VideoUpdate, 21 VideoUpdate
22 VideoFileMetadata
23} from '@shared/models' 22} from '@shared/models'
24import { environment } from '../../../../environments/environment' 23import { environment } from '../../../../environments/environment'
25import { Account, AccountService } from '../account' 24import { Account, AccountService } from '../account'
@@ -48,8 +47,7 @@ export class VideoService implements VideosProvider {
48 private authHttp: HttpClient, 47 private authHttp: HttpClient,
49 private restExtractor: RestExtractor, 48 private restExtractor: RestExtractor,
50 private restService: RestService, 49 private restService: RestService,
51 private serverService: ServerService, 50 private serverService: ServerService
52 private i18n: I18n
53 ) {} 51 ) {}
54 52
55 getVideoViewUrl (uuid: string) { 53 getVideoViewUrl (uuid: string) {
@@ -339,19 +337,19 @@ export class VideoService implements VideosProvider {
339 const base = [ 337 const base = [
340 { 338 {
341 id: VideoPrivacy.PRIVATE, 339 id: VideoPrivacy.PRIVATE,
342 description: this.i18n('Only I can see this video') 340 description: $localize`Only I can see this video`
343 }, 341 },
344 { 342 {
345 id: VideoPrivacy.UNLISTED, 343 id: VideoPrivacy.UNLISTED,
346 description: this.i18n('Only shareable via a private link') 344 description: $localize`Only shareable via a private link`
347 }, 345 },
348 { 346 {
349 id: VideoPrivacy.PUBLIC, 347 id: VideoPrivacy.PUBLIC,
350 description: this.i18n('Anyone can see this video') 348 description: $localize`Anyone can see this video`
351 }, 349 },
352 { 350 {
353 id: VideoPrivacy.INTERNAL, 351 id: VideoPrivacy.INTERNAL,
354 description: this.i18n('Only users of this instance can see this video') 352 description: $localize`Only users of this instance can see this video`
355 } 353 }
356 ] 354 ]
357 355
diff --git a/client/src/app/shared/shared-moderation/abuse.service.ts b/client/src/app/shared/shared-moderation/abuse.service.ts
index 06b236d1e..bf98d4b36 100644
--- a/client/src/app/shared/shared-moderation/abuse.service.ts
+++ b/client/src/app/shared/shared-moderation/abuse.service.ts
@@ -5,7 +5,6 @@ import { catchError, map } from 'rxjs/operators'
5import { HttpClient, HttpParams } from '@angular/common/http' 5import { HttpClient, HttpParams } from '@angular/common/http'
6import { Injectable } from '@angular/core' 6import { Injectable } from '@angular/core'
7import { RestExtractor, RestPagination, RestService } from '@app/core' 7import { RestExtractor, RestPagination, RestService } from '@app/core'
8import { I18n } from '@ngx-translate/i18n-polyfill'
9import { 8import {
10 AbuseCreate, 9 AbuseCreate,
11 AbuseFilter, 10 AbuseFilter,
@@ -25,7 +24,6 @@ export class AbuseService {
25 private static BASE_MY_ABUSE_URL = environment.apiUrl + '/api/v1/users/me/abuses' 24 private static BASE_MY_ABUSE_URL = environment.apiUrl + '/api/v1/users/me/abuses'
26 25
27 constructor ( 26 constructor (
28 private i18n: I18n,
29 private authHttp: HttpClient, 27 private authHttp: HttpClient,
30 private restService: RestService, 28 private restService: RestService,
31 private restExtractor: RestExtractor 29 private restExtractor: RestExtractor
@@ -138,33 +136,33 @@ export class AbuseService {
138 let reasons: { id: AbusePredefinedReasonsString, label: string, description?: string, help?: string }[] = [ 136 let reasons: { id: AbusePredefinedReasonsString, label: string, description?: string, help?: string }[] = [
139 { 137 {
140 id: 'violentOrRepulsive', 138 id: 'violentOrRepulsive',
141 label: this.i18n('Violent or repulsive'), 139 label: $localize`Violent or repulsive`,
142 help: this.i18n('Contains offensive, violent, or coarse language or iconography.') 140 help: $localize`Contains offensive, violent, or coarse language or iconography.`
143 }, 141 },
144 { 142 {
145 id: 'hatefulOrAbusive', 143 id: 'hatefulOrAbusive',
146 label: this.i18n('Hateful or abusive'), 144 label: $localize`Hateful or abusive`,
147 help: this.i18n('Contains abusive, racist or sexist language or iconography.') 145 help: $localize`Contains abusive, racist or sexist language or iconography.`
148 }, 146 },
149 { 147 {
150 id: 'spamOrMisleading', 148 id: 'spamOrMisleading',
151 label: this.i18n('Spam, ad or false news'), 149 label: $localize`Spam, ad or false news`,
152 help: this.i18n('Contains marketing, spam, purposefully deceitful news, or otherwise misleading thumbnail/text/tags. Please provide reputable sources to report hoaxes.') 150 help: $localize`Contains marketing, spam, purposefully deceitful news, or otherwise misleading thumbnail/text/tags. Please provide reputable sources to report hoaxes.`
153 }, 151 },
154 { 152 {
155 id: 'privacy', 153 id: 'privacy',
156 label: this.i18n('Privacy breach or doxxing'), 154 label: $localize`Privacy breach or doxxing`,
157 help: this.i18n('Contains personal information that could be used to track, identify, contact or impersonate someone (e.g. name, address, phone number, email, or credit card details).') 155 help: $localize`Contains personal information that could be used to track, identify, contact or impersonate someone (e.g. name, address, phone number, email, or credit card details).`
158 }, 156 },
159 { 157 {
160 id: 'rights', 158 id: 'rights',
161 label: this.i18n('Copyright'), 159 label: $localize`Copyright`,
162 help: this.i18n('Infringes your copyright wrt. the regional laws with which the server must comply.') 160 help: $localize`Infringes your copyright wrt. the regional laws with which the server must comply.`
163 }, 161 },
164 { 162 {
165 id: 'serverRules', 163 id: 'serverRules',
166 label: this.i18n('Breaks server rules'), 164 label: $localize`Breaks server rules`,
167 description: this.i18n('Anything not included in the above that breaks the terms of service, code of conduct, or general rules in place on the server.') 165 description: $localize`Anything not included in the above that breaks the terms of service, code of conduct, or general rules in place on the server.`
168 } 166 }
169 ] 167 ]
170 168
@@ -172,13 +170,13 @@ export class AbuseService {
172 reasons = reasons.concat([ 170 reasons = reasons.concat([
173 { 171 {
174 id: 'thumbnails', 172 id: 'thumbnails',
175 label: this.i18n('Thumbnails'), 173 label: $localize`Thumbnails`,
176 help: this.i18n('The above can only be seen in thumbnails.') 174 help: $localize`The above can only be seen in thumbnails.`
177 }, 175 },
178 { 176 {
179 id: 'captions', 177 id: 'captions',
180 label: this.i18n('Captions'), 178 label: $localize`Captions`,
181 help: this.i18n('The above can only be seen in captions (please describe which).') 179 help: $localize`The above can only be seen in captions (please describe which).`
182 } 180 }
183 ]) 181 ])
184 } 182 }
diff --git a/client/src/app/shared/shared-moderation/account-blocklist.component.ts b/client/src/app/shared/shared-moderation/account-blocklist.component.ts
index a5a3c27cd..68928a2c2 100644
--- a/client/src/app/shared/shared-moderation/account-blocklist.component.ts
+++ b/client/src/app/shared/shared-moderation/account-blocklist.component.ts
@@ -1,8 +1,7 @@
1import { SortMeta } from 'primeng/api' 1import { SortMeta } from 'primeng/api'
2import { OnInit, Directive } from '@angular/core' 2import { Directive, OnInit } from '@angular/core'
3import { Notifier, RestPagination, RestTable } from '@app/core' 3import { Notifier, RestPagination, RestTable } from '@app/core'
4import { Actor } from '@app/shared/shared-main' 4import { Actor } from '@app/shared/shared-main'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { AccountBlock } from './account-block.model' 5import { AccountBlock } from './account-block.model'
7import { BlocklistComponentType, BlocklistService } from './blocklist.service' 6import { BlocklistComponentType, BlocklistService } from './blocklist.service'
8 7
@@ -19,8 +18,7 @@ export class GenericAccountBlocklistComponent extends RestTable implements OnIni
19 18
20 constructor ( 19 constructor (
21 private notifier: Notifier, 20 private notifier: Notifier,
22 private blocklistService: BlocklistService, 21 private blocklistService: BlocklistService
23 private i18n: I18n
24 ) { 22 ) {
25 super() 23 super()
26 } 24 }
@@ -46,8 +44,8 @@ export class GenericAccountBlocklistComponent extends RestTable implements OnIni
46 () => { 44 () => {
47 this.notifier.success( 45 this.notifier.success(
48 this.mode === BlocklistComponentType.Account 46 this.mode === BlocklistComponentType.Account
49 ? this.i18n('Account {{nameWithHost}} unmuted.', { nameWithHost: blockedAccount.nameWithHost }) 47 ? $localize`Account ${blockedAccount.nameWithHost} unmuted.`
50 : this.i18n('Account {{nameWithHost}} unmuted by your instance.', { nameWithHost: blockedAccount.nameWithHost }) 48 : $localize`Account ${blockedAccount.nameWithHost} unmuted by your instance.`
51 ) 49 )
52 50
53 this.loadData() 51 this.loadData()
diff --git a/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts b/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts
index fdd4a79a9..7193ccb1b 100644
--- a/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts
+++ b/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts
@@ -2,7 +2,6 @@ import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angu
2import { BatchDomainsValidatorsService, FormReactive, FormValidatorService } from '@app/shared/shared-forms' 2import { BatchDomainsValidatorsService, FormReactive, FormValidatorService } from '@app/shared/shared-forms'
3import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 3import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
4import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 4import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6 5
7@Component({ 6@Component({
8 selector: 'my-batch-domains-modal', 7 selector: 'my-batch-domains-modal',
@@ -20,14 +19,13 @@ export class BatchDomainsModalComponent extends FormReactive implements OnInit {
20 constructor ( 19 constructor (
21 protected formValidatorService: FormValidatorService, 20 protected formValidatorService: FormValidatorService,
22 private modalService: NgbModal, 21 private modalService: NgbModal,
23 private batchDomainsValidatorsService: BatchDomainsValidatorsService, 22 private batchDomainsValidatorsService: BatchDomainsValidatorsService
24 private i18n: I18n
25 ) { 23 ) {
26 super() 24 super()
27 } 25 }
28 26
29 ngOnInit () { 27 ngOnInit () {
30 if (!this.action) this.action = this.i18n('Process domains') 28 if (!this.action) this.action = $localize`Process domains`
31 29
32 this.buildForm({ 30 this.buildForm({
33 domains: this.batchDomainsValidatorsService.DOMAINS 31 domains: this.batchDomainsValidatorsService.DOMAINS
diff --git a/client/src/app/shared/shared-moderation/report-modals/account-report.component.ts b/client/src/app/shared/shared-moderation/report-modals/account-report.component.ts
index 08dbe9538..8ab2fe940 100644
--- a/client/src/app/shared/shared-moderation/report-modals/account-report.component.ts
+++ b/client/src/app/shared/shared-moderation/report-modals/account-report.component.ts
@@ -5,7 +5,6 @@ import { AbuseValidatorsService, FormReactive, FormValidatorService } from '@app
5import { Account } from '@app/shared/shared-main' 5import { Account } from '@app/shared/shared-main'
6import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 6import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
7import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 7import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
8import { I18n } from '@ngx-translate/i18n-polyfill'
9import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse' 8import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse'
10import { AbusePredefinedReasonsString } from '@shared/models' 9import { AbusePredefinedReasonsString } from '@shared/models'
11import { AbuseService } from '../abuse.service' 10import { AbuseService } from '../abuse.service'
@@ -31,8 +30,7 @@ export class AccountReportComponent extends FormReactive implements OnInit {
31 private modalService: NgbModal, 30 private modalService: NgbModal,
32 private abuseValidatorsService: AbuseValidatorsService, 31 private abuseValidatorsService: AbuseValidatorsService,
33 private abuseService: AbuseService, 32 private abuseService: AbuseService,
34 private notifier: Notifier, 33 private notifier: Notifier
35 private i18n: I18n
36 ) { 34 ) {
37 super() 35 super()
38 } 36 }
@@ -50,7 +48,7 @@ export class AccountReportComponent extends FormReactive implements OnInit {
50 } 48 }
51 49
52 ngOnInit () { 50 ngOnInit () {
53 this.modalTitle = this.i18n('Report {{displayName}}', { displayName: this.account.displayName }) 51 this.modalTitle = $localize`Report ${this.account.displayName}`
54 52
55 this.buildForm({ 53 this.buildForm({
56 reason: this.abuseValidatorsService.ABUSE_REASON, 54 reason: this.abuseValidatorsService.ABUSE_REASON,
@@ -81,7 +79,7 @@ export class AccountReportComponent extends FormReactive implements OnInit {
81 } 79 }
82 }).subscribe( 80 }).subscribe(
83 () => { 81 () => {
84 this.notifier.success(this.i18n('Account reported.')) 82 this.notifier.success($localize`Account reported.`)
85 this.hide() 83 this.hide()
86 }, 84 },
87 85
diff --git a/client/src/app/shared/shared-moderation/report-modals/comment-report.component.ts b/client/src/app/shared/shared-moderation/report-modals/comment-report.component.ts
index 2769874d9..d75f4d717 100644
--- a/client/src/app/shared/shared-moderation/report-modals/comment-report.component.ts
+++ b/client/src/app/shared/shared-moderation/report-modals/comment-report.component.ts
@@ -5,7 +5,6 @@ import { AbuseValidatorsService, FormReactive, FormValidatorService } from '@app
5import { VideoComment } from '@app/shared/shared-video-comment' 5import { VideoComment } from '@app/shared/shared-video-comment'
6import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 6import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
7import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 7import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
8import { I18n } from '@ngx-translate/i18n-polyfill'
9import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse' 8import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse'
10import { AbusePredefinedReasonsString } from '@shared/models' 9import { AbusePredefinedReasonsString } from '@shared/models'
11import { AbuseService } from '../abuse.service' 10import { AbuseService } from '../abuse.service'
@@ -31,8 +30,7 @@ export class CommentReportComponent extends FormReactive implements OnInit {
31 private modalService: NgbModal, 30 private modalService: NgbModal,
32 private abuseValidatorsService: AbuseValidatorsService, 31 private abuseValidatorsService: AbuseValidatorsService,
33 private abuseService: AbuseService, 32 private abuseService: AbuseService,
34 private notifier: Notifier, 33 private notifier: Notifier
35 private i18n: I18n
36 ) { 34 ) {
37 super() 35 super()
38 } 36 }
@@ -50,7 +48,7 @@ export class CommentReportComponent extends FormReactive implements OnInit {
50 } 48 }
51 49
52 ngOnInit () { 50 ngOnInit () {
53 this.modalTitle = this.i18n('Report comment') 51 this.modalTitle = $localize`Report comment`
54 52
55 this.buildForm({ 53 this.buildForm({
56 reason: this.abuseValidatorsService.ABUSE_REASON, 54 reason: this.abuseValidatorsService.ABUSE_REASON,
@@ -81,7 +79,7 @@ export class CommentReportComponent extends FormReactive implements OnInit {
81 } 79 }
82 }).subscribe( 80 }).subscribe(
83 () => { 81 () => {
84 this.notifier.success(this.i18n('Comment reported.')) 82 this.notifier.success($localize`Comment reported.`)
85 this.hide() 83 this.hide()
86 }, 84 },
87 85
diff --git a/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts b/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts
index 794dd54bb..edff6d325 100644
--- a/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts
+++ b/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts
@@ -1,12 +1,11 @@
1import { mapValues, pickBy } from 'lodash-es' 1import { mapValues, pickBy } from 'lodash-es'
2import { buildVideoOrPlaylistEmbed, buildVideoLink } from 'src/assets/player/utils' 2import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
3import { Component, Input, OnInit, ViewChild } from '@angular/core' 3import { Component, Input, OnInit, ViewChild } from '@angular/core'
4import { DomSanitizer, SafeHtml } from '@angular/platform-browser' 4import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
5import { Notifier } from '@app/core' 5import { Notifier } from '@app/core'
6import { AbuseValidatorsService, FormReactive, FormValidatorService } from '@app/shared/shared-forms' 6import { AbuseValidatorsService, FormReactive, FormValidatorService } from '@app/shared/shared-forms'
7import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 7import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
8import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 8import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
9import { I18n } from '@ngx-translate/i18n-polyfill'
10import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse' 9import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse'
11import { AbusePredefinedReasonsString } from '@shared/models' 10import { AbusePredefinedReasonsString } from '@shared/models'
12import { Video } from '../../shared-main' 11import { Video } from '../../shared-main'
@@ -34,8 +33,7 @@ export class VideoReportComponent extends FormReactive implements OnInit {
34 private abuseValidatorsService: AbuseValidatorsService, 33 private abuseValidatorsService: AbuseValidatorsService,
35 private abuseService: AbuseService, 34 private abuseService: AbuseService,
36 private notifier: Notifier, 35 private notifier: Notifier,
37 private sanitizer: DomSanitizer, 36 private sanitizer: DomSanitizer
38 private i18n: I18n
39 ) { 37 ) {
40 super() 38 super()
41 } 39 }
@@ -109,7 +107,7 @@ export class VideoReportComponent extends FormReactive implements OnInit {
109 } 107 }
110 }).subscribe( 108 }).subscribe(
111 () => { 109 () => {
112 this.notifier.success(this.i18n('Video reported.')) 110 this.notifier.success($localize`Video reported.`)
113 this.hide() 111 this.hide()
114 }, 112 },
115 113
diff --git a/client/src/app/shared/shared-moderation/server-blocklist.component.ts b/client/src/app/shared/shared-moderation/server-blocklist.component.ts
index 8f65cdb71..546fd53c3 100644
--- a/client/src/app/shared/shared-moderation/server-blocklist.component.ts
+++ b/client/src/app/shared/shared-moderation/server-blocklist.component.ts
@@ -2,7 +2,6 @@ import { SortMeta } from 'primeng/api'
2import { Directive, OnInit, ViewChild } from '@angular/core' 2import { Directive, OnInit, ViewChild } from '@angular/core'
3import { Notifier, RestPagination, RestTable } from '@app/core' 3import { Notifier, RestPagination, RestTable } from '@app/core'
4import { BatchDomainsModalComponent } from '@app/shared/shared-moderation/batch-domains-modal.component' 4import { BatchDomainsModalComponent } from '@app/shared/shared-moderation/batch-domains-modal.component'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { ServerBlock } from '@shared/models' 5import { ServerBlock } from '@shared/models'
7import { BlocklistComponentType, BlocklistService } from './blocklist.service' 6import { BlocklistComponentType, BlocklistService } from './blocklist.service'
8 7
@@ -21,8 +20,7 @@ export class GenericServerBlocklistComponent extends RestTable implements OnInit
21 20
22 constructor ( 21 constructor (
23 protected notifier: Notifier, 22 protected notifier: Notifier,
24 protected blocklistService: BlocklistService, 23 protected blocklistService: BlocklistService
25 protected i18n: I18n
26 ) { 24 ) {
27 super() 25 super()
28 } 26 }
@@ -44,8 +42,8 @@ export class GenericServerBlocklistComponent extends RestTable implements OnInit
44 () => { 42 () => {
45 this.notifier.success( 43 this.notifier.success(
46 this.mode === BlocklistComponentType.Account 44 this.mode === BlocklistComponentType.Account
47 ? this.i18n('Instance {{host}} unmuted.', { host }) 45 ? $localize`Instance ${host} unmuted.`
48 : this.i18n('Instance {{host}} unmuted by your instance.', { host }) 46 : $localize`Instance ${host} unmuted by your instance.`
49 ) 47 )
50 48
51 this.loadData() 49 this.loadData()
@@ -67,8 +65,8 @@ export class GenericServerBlocklistComponent extends RestTable implements OnInit
67 () => { 65 () => {
68 this.notifier.success( 66 this.notifier.success(
69 this.mode === BlocklistComponentType.Account 67 this.mode === BlocklistComponentType.Account
70 ? this.i18n('Instance {{domain}} muted.', { domain }) 68 ? $localize`Instance ${domain} muted.`
71 : this.i18n('Instance {{domain}} muted by your instance.', { domain }) 69 : $localize`Instance ${domain} muted by your instance.`
72 ) 70 )
73 71
74 this.loadData() 72 this.loadData()
diff --git a/client/src/app/shared/shared-moderation/user-ban-modal.component.ts b/client/src/app/shared/shared-moderation/user-ban-modal.component.ts
index 124e58669..f9a0381c5 100644
--- a/client/src/app/shared/shared-moderation/user-ban-modal.component.ts
+++ b/client/src/app/shared/shared-moderation/user-ban-modal.component.ts
@@ -3,7 +3,6 @@ import { Notifier, UserService } from '@app/core'
3import { FormReactive, FormValidatorService, UserValidatorsService } from '@app/shared/shared-forms' 3import { FormReactive, FormValidatorService, UserValidatorsService } from '@app/shared/shared-forms'
4import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 4import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
5import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 5import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
6import { I18n } from '@ngx-translate/i18n-polyfill'
7import { User } from '@shared/models' 6import { User } from '@shared/models'
8 7
9@Component({ 8@Component({
@@ -23,8 +22,7 @@ export class UserBanModalComponent extends FormReactive implements OnInit {
23 private modalService: NgbModal, 22 private modalService: NgbModal,
24 private notifier: Notifier, 23 private notifier: Notifier,
25 private userService: UserService, 24 private userService: UserService,
26 private userValidatorsService: UserValidatorsService, 25 private userValidatorsService: UserValidatorsService
27 private i18n: I18n
28 ) { 26 ) {
29 super() 27 super()
30 } 28 }
@@ -52,8 +50,8 @@ export class UserBanModalComponent extends FormReactive implements OnInit {
52 .subscribe( 50 .subscribe(
53 () => { 51 () => {
54 const message = Array.isArray(this.usersToBan) 52 const message = Array.isArray(this.usersToBan)
55 ? this.i18n('{{num}} users banned.', { num: this.usersToBan.length }) 53 ? $localize`${this.usersToBan.length} users banned.`
56 : this.i18n('User {{username}} banned.', { username: this.usersToBan.username }) 54 : $localize`User ${this.usersToBan.username} banned.`
57 55
58 this.notifier.success(message) 56 this.notifier.success(message)
59 57
diff --git a/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts b/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts
index 34fa7366c..44aefa853 100644
--- a/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts
+++ b/client/src/app/shared/shared-moderation/user-moderation-dropdown.component.ts
@@ -1,7 +1,6 @@
1import { Component, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild } from '@angular/core' 1import { Component, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild } from '@angular/core'
2import { AuthService, ConfirmService, Notifier, ServerService, UserService } from '@app/core' 2import { AuthService, ConfirmService, Notifier, ServerService, UserService } from '@app/core'
3import { Account, DropdownAction } from '@app/shared/shared-main' 3import { Account, DropdownAction } from '@app/shared/shared-main'
4import { I18n } from '@ngx-translate/i18n-polyfill'
5import { BulkRemoveCommentsOfBody, ServerConfig, User, UserRight } from '@shared/models' 4import { BulkRemoveCommentsOfBody, ServerConfig, User, UserRight } from '@shared/models'
6import { BlocklistService } from './blocklist.service' 5import { BlocklistService } from './blocklist.service'
7import { BulkService } from './bulk.service' 6import { BulkService } from './bulk.service'
@@ -37,8 +36,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
37 private serverService: ServerService, 36 private serverService: ServerService,
38 private userService: UserService, 37 private userService: UserService,
39 private blocklistService: BlocklistService, 38 private blocklistService: BlocklistService,
40 private bulkService: BulkService, 39 private bulkService: BulkService
41 private i18n: I18n
42 ) { } 40 ) { }
43 41
44 get requiresEmailVerification () { 42 get requiresEmailVerification () {
@@ -57,7 +55,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
57 55
58 openBanUserModal (user: User) { 56 openBanUserModal (user: User) {
59 if (user.username === 'root') { 57 if (user.username === 'root') {
60 this.notifier.error(this.i18n('You cannot ban root.')) 58 this.notifier.error($localize`You cannot ban root.`)
61 return 59 return
62 } 60 }
63 61
@@ -69,15 +67,13 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
69 } 67 }
70 68
71 async unbanUser (user: User) { 69 async unbanUser (user: User) {
72 const message = this.i18n('Do you really want to unban {{username}}?', { username: user.username }) 70 const res = await this.confirmService.confirm($localize`Do you really want to unban ${user.username}?`, $localize`Unban`)
73 const res = await this.confirmService.confirm(message, this.i18n('Unban'))
74 if (res === false) return 71 if (res === false) return
75 72
76 this.userService.unbanUsers(user) 73 this.userService.unbanUsers(user)
77 .subscribe( 74 .subscribe(
78 () => { 75 () => {
79 this.notifier.success(this.i18n('User {{username}} unbanned.', { username: user.username })) 76 this.notifier.success($localize`User ${user.username} unbanned.`)
80
81 this.userChanged.emit() 77 this.userChanged.emit()
82 }, 78 },
83 79
@@ -87,17 +83,17 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
87 83
88 async removeUser (user: User) { 84 async removeUser (user: User) {
89 if (user.username === 'root') { 85 if (user.username === 'root') {
90 this.notifier.error(this.i18n('You cannot delete root.')) 86 this.notifier.error($localize`You cannot delete root.`)
91 return 87 return
92 } 88 }
93 89
94 const message = this.i18n('If you remove this user, you will not be able to create another with the same username!') 90 const message = $localize`If you remove this user, you will not be able to create another with the same username!`
95 const res = await this.confirmService.confirm(message, this.i18n('Delete')) 91 const res = await this.confirmService.confirm(message, $localize`Delete`)
96 if (res === false) return 92 if (res === false) return
97 93
98 this.userService.removeUser(user).subscribe( 94 this.userService.removeUser(user).subscribe(
99 () => { 95 () => {
100 this.notifier.success(this.i18n('User {{username}} deleted.', { username: user.username })) 96 this.notifier.success($localize`User ${user.username} deleted.`)
101 this.userDeleted.emit() 97 this.userDeleted.emit()
102 }, 98 },
103 99
@@ -108,8 +104,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
108 setEmailAsVerified (user: User) { 104 setEmailAsVerified (user: User) {
109 this.userService.updateUser(user.id, { emailVerified: true }).subscribe( 105 this.userService.updateUser(user.id, { emailVerified: true }).subscribe(
110 () => { 106 () => {
111 this.notifier.success(this.i18n('User {{username}} email set as verified', { username: user.username })) 107 this.notifier.success($localize`User ${user.username} email set as verified`)
112
113 this.userChanged.emit() 108 this.userChanged.emit()
114 }, 109 },
115 110
@@ -121,7 +116,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
121 this.blocklistService.blockAccountByUser(account) 116 this.blocklistService.blockAccountByUser(account)
122 .subscribe( 117 .subscribe(
123 () => { 118 () => {
124 this.notifier.success(this.i18n('Account {{nameWithHost}} muted.', { nameWithHost: account.nameWithHost })) 119 this.notifier.success($localize`Account ${account.nameWithHost} muted.`)
125 120
126 this.account.mutedByUser = true 121 this.account.mutedByUser = true
127 this.userChanged.emit() 122 this.userChanged.emit()
@@ -135,7 +130,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
135 this.blocklistService.unblockAccountByUser(account) 130 this.blocklistService.unblockAccountByUser(account)
136 .subscribe( 131 .subscribe(
137 () => { 132 () => {
138 this.notifier.success(this.i18n('Account {{nameWithHost}} unmuted.', { nameWithHost: account.nameWithHost })) 133 this.notifier.success($localize`Account ${account.nameWithHost} unmuted.`)
139 134
140 this.account.mutedByUser = false 135 this.account.mutedByUser = false
141 this.userChanged.emit() 136 this.userChanged.emit()
@@ -149,7 +144,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
149 this.blocklistService.blockServerByUser(host) 144 this.blocklistService.blockServerByUser(host)
150 .subscribe( 145 .subscribe(
151 () => { 146 () => {
152 this.notifier.success(this.i18n('Instance {{host}} muted.', { host })) 147 this.notifier.success($localize`Instance ${host} muted.`)
153 148
154 this.account.mutedServerByUser = true 149 this.account.mutedServerByUser = true
155 this.userChanged.emit() 150 this.userChanged.emit()
@@ -163,7 +158,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
163 this.blocklistService.unblockServerByUser(host) 158 this.blocklistService.unblockServerByUser(host)
164 .subscribe( 159 .subscribe(
165 () => { 160 () => {
166 this.notifier.success(this.i18n('Instance {{host}} unmuted.', { host })) 161 this.notifier.success($localize`Instance ${host} unmuted.`)
167 162
168 this.account.mutedServerByUser = false 163 this.account.mutedServerByUser = false
169 this.userChanged.emit() 164 this.userChanged.emit()
@@ -177,7 +172,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
177 this.blocklistService.blockAccountByInstance(account) 172 this.blocklistService.blockAccountByInstance(account)
178 .subscribe( 173 .subscribe(
179 () => { 174 () => {
180 this.notifier.success(this.i18n('Account {{nameWithHost}} muted by the instance.', { nameWithHost: account.nameWithHost })) 175 this.notifier.success($localize`Account ${account.nameWithHost} muted by the instance.`)
181 176
182 this.account.mutedByInstance = true 177 this.account.mutedByInstance = true
183 this.userChanged.emit() 178 this.userChanged.emit()
@@ -191,7 +186,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
191 this.blocklistService.unblockAccountByInstance(account) 186 this.blocklistService.unblockAccountByInstance(account)
192 .subscribe( 187 .subscribe(
193 () => { 188 () => {
194 this.notifier.success(this.i18n('Account {{nameWithHost}} unmuted by the instance.', { nameWithHost: account.nameWithHost })) 189 this.notifier.success($localize`Account ${account.nameWithHost} unmuted by the instance.`)
195 190
196 this.account.mutedByInstance = false 191 this.account.mutedByInstance = false
197 this.userChanged.emit() 192 this.userChanged.emit()
@@ -205,7 +200,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
205 this.blocklistService.blockServerByInstance(host) 200 this.blocklistService.blockServerByInstance(host)
206 .subscribe( 201 .subscribe(
207 () => { 202 () => {
208 this.notifier.success(this.i18n('Instance {{host}} muted by the instance.', { host })) 203 this.notifier.success($localize`Instance ${host} muted by the instance.`)
209 204
210 this.account.mutedServerByInstance = true 205 this.account.mutedServerByInstance = true
211 this.userChanged.emit() 206 this.userChanged.emit()
@@ -219,7 +214,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
219 this.blocklistService.unblockServerByInstance(host) 214 this.blocklistService.unblockServerByInstance(host)
220 .subscribe( 215 .subscribe(
221 () => { 216 () => {
222 this.notifier.success(this.i18n('Instance {{host}} unmuted by the instance.', { host })) 217 this.notifier.success($localize`Instance ${host} unmuted by the instance.`)
223 218
224 this.account.mutedServerByInstance = false 219 this.account.mutedServerByInstance = false
225 this.userChanged.emit() 220 this.userChanged.emit()
@@ -230,14 +225,14 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
230 } 225 }
231 226
232 async bulkRemoveCommentsOf (body: BulkRemoveCommentsOfBody) { 227 async bulkRemoveCommentsOf (body: BulkRemoveCommentsOfBody) {
233 const message = this.i18n('Are you sure you want to remove all the comments of this account?') 228 const message = $localize`Are you sure you want to remove all the comments of this account?`
234 const res = await this.confirmService.confirm(message, this.i18n('Delete account comments')) 229 const res = await this.confirmService.confirm(message, $localize`Delete account comments`)
235 if (res === false) return 230 if (res === false) return
236 231
237 this.bulkService.removeCommentsOf(body) 232 this.bulkService.removeCommentsOf(body)
238 .subscribe( 233 .subscribe(
239 () => { 234 () => {
240 this.notifier.success(this.i18n('Will remove comments of this account (may take several minutes).')) 235 this.notifier.success($localize`Will remove comments of this account (may take several minutes).`)
241 }, 236 },
242 237
243 err => this.notifier.error(err.message) 238 err => this.notifier.error(err.message)
@@ -265,29 +260,29 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
265 if (this.user && authUser.hasRight(UserRight.MANAGE_USERS) && authUser.canManage(this.user)) { 260 if (this.user && authUser.hasRight(UserRight.MANAGE_USERS) && authUser.canManage(this.user)) {
266 this.userActions.push([ 261 this.userActions.push([
267 { 262 {
268 label: this.i18n('Edit user'), 263 label: $localize`Edit user`,
269 description: this.i18n('Change quota, role, and more.'), 264 description: $localize`Change quota, role, and more.`,
270 linkBuilder: ({ user }) => this.getRouterUserEditLink(user) 265 linkBuilder: ({ user }) => this.getRouterUserEditLink(user)
271 }, 266 },
272 { 267 {
273 label: this.i18n('Delete user'), 268 label: $localize`Delete user`,
274 description: this.i18n('Videos will be deleted, comments will be tombstoned.'), 269 description: $localize`Videos will be deleted, comments will be tombstoned.`,
275 handler: ({ user }) => this.removeUser(user) 270 handler: ({ user }) => this.removeUser(user)
276 }, 271 },
277 { 272 {
278 label: this.i18n('Ban'), 273 label: $localize`Ban`,
279 description: this.i18n('User won\'t be able to login anymore, but videos and comments will be kept as is.'), 274 description: $localize`User won't be able to login anymore, but videos and comments will be kept as is.`,
280 handler: ({ user }) => this.openBanUserModal(user), 275 handler: ({ user }) => this.openBanUserModal(user),
281 isDisplayed: ({ user }) => !user.blocked 276 isDisplayed: ({ user }) => !user.blocked
282 }, 277 },
283 { 278 {
284 label: this.i18n('Unban user'), 279 label: $localize`Unban user`,
285 description: this.i18n('Allow the user to login and create videos/comments again'), 280 description: $localize`Allow the user to login and create videos/comments again`,
286 handler: ({ user }) => this.unbanUser(user), 281 handler: ({ user }) => this.unbanUser(user),
287 isDisplayed: ({ user }) => user.blocked 282 isDisplayed: ({ user }) => user.blocked
288 }, 283 },
289 { 284 {
290 label: this.i18n('Set Email as Verified'), 285 label: $localize`Set Email as Verified`,
291 handler: ({ user }) => this.setEmailAsVerified(user), 286 handler: ({ user }) => this.setEmailAsVerified(user),
292 isDisplayed: ({ user }) => this.requiresEmailVerification && !user.blocked && user.emailVerified === false 287 isDisplayed: ({ user }) => this.requiresEmailVerification && !user.blocked && user.emailVerified === false
293 } 288 }
@@ -299,32 +294,32 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
299 // User actions 294 // User actions
300 this.userActions.push([ 295 this.userActions.push([
301 { 296 {
302 label: this.i18n('Mute this account'), 297 label: $localize`Mute this account`,
303 description: this.i18n('Hide any content from that user for you.'), 298 description: $localize`Hide any content from that user for you.`,
304 isDisplayed: ({ account }) => account.mutedByUser === false, 299 isDisplayed: ({ account }) => account.mutedByUser === false,
305 handler: ({ account }) => this.blockAccountByUser(account) 300 handler: ({ account }) => this.blockAccountByUser(account)
306 }, 301 },
307 { 302 {
308 label: this.i18n('Unmute this account'), 303 label: $localize`Unmute this account`,
309 description: this.i18n('Show back content from that user for you.'), 304 description: $localize`Show back content from that user for you.`,
310 isDisplayed: ({ account }) => account.mutedByUser === true, 305 isDisplayed: ({ account }) => account.mutedByUser === true,
311 handler: ({ account }) => this.unblockAccountByUser(account) 306 handler: ({ account }) => this.unblockAccountByUser(account)
312 }, 307 },
313 { 308 {
314 label: this.i18n('Mute the instance'), 309 label: $localize`Mute the instance`,
315 description: this.i18n('Hide any content from that instance for you.'), 310 description: $localize`Hide any content from that instance for you.`,
316 isDisplayed: ({ account }) => !account.userId && account.mutedServerByInstance === false, 311 isDisplayed: ({ account }) => !account.userId && account.mutedServerByInstance === false,
317 handler: ({ account }) => this.blockServerByUser(account.host) 312 handler: ({ account }) => this.blockServerByUser(account.host)
318 }, 313 },
319 { 314 {
320 label: this.i18n('Unmute the instance'), 315 label: $localize`Unmute the instance`,
321 description: this.i18n('Show back content from that instance for you.'), 316 description: $localize`Show back content from that instance for you.`,
322 isDisplayed: ({ account }) => !account.userId && account.mutedServerByInstance === true, 317 isDisplayed: ({ account }) => !account.userId && account.mutedServerByInstance === true,
323 handler: ({ account }) => this.unblockServerByUser(account.host) 318 handler: ({ account }) => this.unblockServerByUser(account.host)
324 }, 319 },
325 { 320 {
326 label: this.i18n('Remove comments from your videos'), 321 label: $localize`Remove comments from your videos`,
327 description: this.i18n('Remove comments of this account from your videos.'), 322 description: $localize`Remove comments of this account from your videos.`,
328 handler: ({ account }) => this.bulkRemoveCommentsOf({ accountName: account.nameWithHost, scope: 'my-videos' }) 323 handler: ({ account }) => this.bulkRemoveCommentsOf({ accountName: account.nameWithHost, scope: 'my-videos' })
329 } 324 }
330 ]) 325 ])
@@ -335,14 +330,14 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
335 if (authUser.hasRight(UserRight.MANAGE_ACCOUNTS_BLOCKLIST)) { 330 if (authUser.hasRight(UserRight.MANAGE_ACCOUNTS_BLOCKLIST)) {
336 instanceActions = instanceActions.concat([ 331 instanceActions = instanceActions.concat([
337 { 332 {
338 label: this.i18n('Mute this account by your instance'), 333 label: $localize`Mute this account by your instance`,
339 description: this.i18n('Hide any content from that user for you, your instance and its users.'), 334 description: $localize`Hide any content from that user for you, your instance and its users.`,
340 isDisplayed: ({ account }) => account.mutedByInstance === false, 335 isDisplayed: ({ account }) => account.mutedByInstance === false,
341 handler: ({ account }) => this.blockAccountByInstance(account) 336 handler: ({ account }) => this.blockAccountByInstance(account)
342 }, 337 },
343 { 338 {
344 label: this.i18n('Unmute this account by your instance'), 339 label: $localize`Unmute this account by your instance`,
345 description: this.i18n('Show back content from that user for you, your instance and its users.'), 340 description: $localize`Show back content from that user for you, your instance and its users.`,
346 isDisplayed: ({ account }) => account.mutedByInstance === true, 341 isDisplayed: ({ account }) => account.mutedByInstance === true,
347 handler: ({ account }) => this.unblockAccountByInstance(account) 342 handler: ({ account }) => this.unblockAccountByInstance(account)
348 } 343 }
@@ -353,14 +348,14 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
353 if (authUser.hasRight(UserRight.MANAGE_SERVERS_BLOCKLIST)) { 348 if (authUser.hasRight(UserRight.MANAGE_SERVERS_BLOCKLIST)) {
354 instanceActions = instanceActions.concat([ 349 instanceActions = instanceActions.concat([
355 { 350 {
356 label: this.i18n('Mute the instance by your instance'), 351 label: $localize`Mute the instance by your instance`,
357 description: this.i18n('Hide any content from that instance for you, your instance and its users.'), 352 description: $localize`Hide any content from that instance for you, your instance and its users.`,
358 isDisplayed: ({ account }) => !account.userId && account.mutedServerByInstance === false, 353 isDisplayed: ({ account }) => !account.userId && account.mutedServerByInstance === false,
359 handler: ({ account }) => this.blockServerByInstance(account.host) 354 handler: ({ account }) => this.blockServerByInstance(account.host)
360 }, 355 },
361 { 356 {
362 label: this.i18n('Unmute the instance by your instance'), 357 label: $localize`Unmute the instance by your instance`,
363 description: this.i18n('Show back content from that instance for you, your instance and its users.'), 358 description: $localize`Show back content from that instance for you, your instance and its users.`,
364 isDisplayed: ({ account }) => !account.userId && account.mutedServerByInstance === true, 359 isDisplayed: ({ account }) => !account.userId && account.mutedServerByInstance === true,
365 handler: ({ account }) => this.unblockServerByInstance(account.host) 360 handler: ({ account }) => this.unblockServerByInstance(account.host)
366 } 361 }
@@ -370,8 +365,8 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
370 if (authUser.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT)) { 365 if (authUser.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT)) {
371 instanceActions = instanceActions.concat([ 366 instanceActions = instanceActions.concat([
372 { 367 {
373 label: this.i18n('Remove comments from your instance'), 368 label: $localize`Remove comments from your instance`,
374 description: this.i18n('Remove comments of this account from your instance.'), 369 description: $localize`Remove comments of this account from your instance.`,
375 handler: ({ account }) => this.bulkRemoveCommentsOf({ accountName: account.nameWithHost, scope: 'instance' }) 370 handler: ({ account }) => this.bulkRemoveCommentsOf({ accountName: account.nameWithHost, scope: 'instance' })
376 } 371 }
377 ]) 372 ])
diff --git a/client/src/app/shared/shared-moderation/video-block.component.ts b/client/src/app/shared/shared-moderation/video-block.component.ts
index 054651e71..2bef9efdd 100644
--- a/client/src/app/shared/shared-moderation/video-block.component.ts
+++ b/client/src/app/shared/shared-moderation/video-block.component.ts
@@ -4,7 +4,6 @@ import { FormReactive, FormValidatorService, VideoBlockValidatorsService } from
4import { Video } from '@app/shared/shared-main' 4import { Video } from '@app/shared/shared-main'
5import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 5import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
6import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 6import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { VideoBlockService } from './video-block.service' 7import { VideoBlockService } from './video-block.service'
9 8
10@Component({ 9@Component({
@@ -28,8 +27,7 @@ export class VideoBlockComponent extends FormReactive implements OnInit {
28 private modalService: NgbModal, 27 private modalService: NgbModal,
29 private videoBlockValidatorsService: VideoBlockValidatorsService, 28 private videoBlockValidatorsService: VideoBlockValidatorsService,
30 private videoBlocklistService: VideoBlockService, 29 private videoBlocklistService: VideoBlockService,
31 private notifier: Notifier, 30 private notifier: Notifier
32 private i18n: I18n
33 ) { 31 ) {
34 super() 32 super()
35 } 33 }
@@ -59,7 +57,7 @@ export class VideoBlockComponent extends FormReactive implements OnInit {
59 this.videoBlocklistService.blockVideo(this.video.id, reason, unfederate) 57 this.videoBlocklistService.blockVideo(this.video.id, reason, unfederate)
60 .subscribe( 58 .subscribe(
61 () => { 59 () => {
62 this.notifier.success(this.i18n('Video blocked.')) 60 this.notifier.success($localize`Video blocked.`)
63 this.hide() 61 this.hide()
64 62
65 this.video.blacklisted = true 63 this.video.blacklisted = true
diff --git a/client/src/app/shared/shared-thumbnail/video-thumbnail.component.ts b/client/src/app/shared/shared-thumbnail/video-thumbnail.component.ts
index 812c7a508..b2a2cf240 100644
--- a/client/src/app/shared/shared-thumbnail/video-thumbnail.component.ts
+++ b/client/src/app/shared/shared-thumbnail/video-thumbnail.component.ts
@@ -1,6 +1,5 @@
1import { Component, EventEmitter, Input, Output } from '@angular/core' 1import { Component, EventEmitter, Input, Output } from '@angular/core'
2import { ScreenService } from '@app/core' 2import { ScreenService } from '@app/core'
3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { Video } from '../shared-main' 3import { Video } from '../shared-main'
5 4
6@Component({ 5@Component({
@@ -25,12 +24,9 @@ export class VideoThumbnailComponent {
25 addToWatchLaterText: string 24 addToWatchLaterText: string
26 addedToWatchLaterText: string 25 addedToWatchLaterText: string
27 26
28 constructor ( 27 constructor (private screenService: ScreenService) {
29 private screenService: ScreenService, 28 this.addToWatchLaterText = $localize`Add to watch later`
30 private i18n: I18n 29 this.addedToWatchLaterText = $localize`Remove from watch later`
31 ) {
32 this.addToWatchLaterText = this.i18n('Add to watch later')
33 this.addedToWatchLaterText = this.i18n('Remove from watch later')
34 } 30 }
35 31
36 getImageUrl () { 32 getImageUrl () {
diff --git a/client/src/app/shared/shared-user-settings/user-interface-settings.component.ts b/client/src/app/shared/shared-user-settings/user-interface-settings.component.ts
index 875ffa3f1..80b88c129 100644
--- a/client/src/app/shared/shared-user-settings/user-interface-settings.component.ts
+++ b/client/src/app/shared/shared-user-settings/user-interface-settings.component.ts
@@ -2,7 +2,6 @@ import { Subject, Subscription } from 'rxjs'
2import { Component, Input, OnDestroy, OnInit } from '@angular/core' 2import { Component, Input, OnDestroy, OnInit } from '@angular/core'
3import { AuthService, Notifier, ServerService, UserService } from '@app/core' 3import { AuthService, Notifier, ServerService, UserService } from '@app/core'
4import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 4import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { ServerConfig, User, UserUpdateMe } from '@shared/models' 5import { ServerConfig, User, UserUpdateMe } from '@shared/models'
7 6
8@Component({ 7@Component({
@@ -25,8 +24,7 @@ export class UserInterfaceSettingsComponent extends FormReactive implements OnIn
25 private authService: AuthService, 24 private authService: AuthService,
26 private notifier: Notifier, 25 private notifier: Notifier,
27 private userService: UserService, 26 private userService: UserService,
28 private serverService: ServerService, 27 private serverService: ServerService
29 private i18n: I18n
30 ) { 28 ) {
31 super() 29 super()
32 } 30 }
@@ -73,14 +71,14 @@ export class UserInterfaceSettingsComponent extends FormReactive implements OnIn
73 () => { 71 () => {
74 this.authService.refreshUserInformation() 72 this.authService.refreshUserInformation()
75 73
76 if (this.notifyOnUpdate) this.notifier.success(this.i18n('Interface settings updated.')) 74 if (this.notifyOnUpdate) this.notifier.success($localize`Interface settings updated.`)
77 }, 75 },
78 76
79 err => this.notifier.error(err.message) 77 err => this.notifier.error(err.message)
80 ) 78 )
81 } else { 79 } else {
82 this.userService.updateMyAnonymousProfile(details) 80 this.userService.updateMyAnonymousProfile(details)
83 if (this.notifyOnUpdate) this.notifier.success(this.i18n('Interface settings updated.')) 81 if (this.notifyOnUpdate) this.notifier.success($localize`Interface settings updated.`)
84 } 82 }
85 } 83 }
86} 84}
diff --git a/client/src/app/shared/shared-user-settings/user-video-settings.component.ts b/client/src/app/shared/shared-user-settings/user-video-settings.component.ts
index eb340e24d..ab77f6f9c 100644
--- a/client/src/app/shared/shared-user-settings/user-video-settings.component.ts
+++ b/client/src/app/shared/shared-user-settings/user-video-settings.component.ts
@@ -4,7 +4,6 @@ import { first } from 'rxjs/operators'
4import { Component, Input, OnDestroy, OnInit } from '@angular/core' 4import { Component, Input, OnDestroy, OnInit } from '@angular/core'
5import { AuthService, Notifier, ServerService, User, UserService } from '@app/core' 5import { AuthService, Notifier, ServerService, User, UserService } from '@app/core'
6import { FormReactive, FormValidatorService, ItemSelectCheckboxValue, SelectOptionsItem } from '@app/shared/shared-forms' 6import { FormReactive, FormValidatorService, ItemSelectCheckboxValue, SelectOptionsItem } from '@app/shared/shared-forms'
7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { UserUpdateMe } from '@shared/models' 7import { UserUpdateMe } from '@shared/models'
9import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type' 8import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type'
10 9
@@ -30,14 +29,13 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit,
30 private authService: AuthService, 29 private authService: AuthService,
31 private notifier: Notifier, 30 private notifier: Notifier,
32 private userService: UserService, 31 private userService: UserService,
33 private serverService: ServerService, 32 private serverService: ServerService
34 private i18n: I18n
35 ) { 33 ) {
36 super() 34 super()
37 } 35 }
38 36
39 ngOnInit () { 37 ngOnInit () {
40 this.allLanguagesGroup = this.i18n('All languages') 38 this.allLanguagesGroup = $localize`All languages`
41 39
42 let oldForm: any 40 let oldForm: any
43 41
@@ -56,7 +54,7 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit,
56 ]).subscribe(([ languages, config ]) => { 54 ]).subscribe(([ languages, config ]) => {
57 const group = this.allLanguagesGroup 55 const group = this.allLanguagesGroup
58 56
59 this.languageItems = [ { label: this.i18n('Unknown language'), id: '_unknown', group } ] 57 this.languageItems = [ { label: $localize`Unknown language`, id: '_unknown', group } ]
60 this.languageItems = this.languageItems 58 this.languageItems = this.languageItems
61 .concat(languages.map(l => ({ label: l.label, id: l.id, group }))) 59 .concat(languages.map(l => ({ label: l.label, id: l.id, group })))
62 60
@@ -101,12 +99,12 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit,
101 99
102 if (Array.isArray(videoLanguages)) { 100 if (Array.isArray(videoLanguages)) {
103 if (videoLanguages.length > 20) { 101 if (videoLanguages.length > 20) {
104 this.notifier.error(this.i18n('Too many languages are enabled. Please enable them all or stay below 20 enabled languages.')) 102 this.notifier.error($localize`Too many languages are enabled. Please enable them all or stay below 20 enabled languages.`)
105 return 103 return
106 } 104 }
107 105
108 if (videoLanguages.length === 0) { 106 if (videoLanguages.length === 0) {
109 this.notifier.error(this.i18n('You need to enable at least 1 video language.')) 107 this.notifier.error($localize`You need to enable at least 1 video language.`)
110 return 108 return
111 } 109 }
112 110
@@ -133,22 +131,14 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit,
133 () => { 131 () => {
134 this.authService.refreshUserInformation() 132 this.authService.refreshUserInformation()
135 133
136 if (this.notifyOnUpdate) this.notifier.success(this.i18n('Video settings updated.')) 134 if (this.notifyOnUpdate) this.notifier.success($localize`Video settings updated.`)
137 }, 135 },
138 136
139 err => this.notifier.error(err.message) 137 err => this.notifier.error(err.message)
140 ) 138 )
141 } else { 139 } else {
142 this.userService.updateMyAnonymousProfile(details) 140 this.userService.updateMyAnonymousProfile(details)
143 if (this.notifyOnUpdate) this.notifier.success(this.i18n('Display/Video settings updated.')) 141 if (this.notifyOnUpdate) this.notifier.success($localize`Display/Video settings updated.`)
144 } 142 }
145 } 143 }
146
147 getDefaultVideoLanguageLabel () {
148 return this.i18n('No language')
149 }
150
151 getSelectedVideoLanguageLabel () {
152 return this.i18n('{{\'{0} languages selected')
153 }
154} 144}
diff --git a/client/src/app/shared/shared-user-subscription/subscribe-button.component.ts b/client/src/app/shared/shared-user-subscription/subscribe-button.component.ts
index 72fa3f4fd..b918fda06 100644
--- a/client/src/app/shared/shared-user-subscription/subscribe-button.component.ts
+++ b/client/src/app/shared/shared-user-subscription/subscribe-button.component.ts
@@ -3,7 +3,6 @@ import { Component, Input, OnChanges, OnInit } from '@angular/core'
3import { Router } from '@angular/router' 3import { Router } from '@angular/router'
4import { AuthService, Notifier } from '@app/core' 4import { AuthService, Notifier } from '@app/core'
5import { Account, VideoChannel, VideoService } from '@app/shared/shared-main' 5import { Account, VideoChannel, VideoService } from '@app/shared/shared-main'
6import { I18n } from '@ngx-translate/i18n-polyfill'
7import { FeedFormat } from '@shared/models' 6import { FeedFormat } from '@shared/models'
8import { UserSubscriptionService } from './user-subscription.service' 7import { UserSubscriptionService } from './user-subscription.service'
9 8
@@ -31,7 +30,6 @@ export class SubscribeButtonComponent implements OnInit, OnChanges {
31 private router: Router, 30 private router: Router,
32 private notifier: Notifier, 31 private notifier: Notifier,
33 private userSubscriptionService: UserSubscriptionService, 32 private userSubscriptionService: UserSubscriptionService,
34 private i18n: I18n,
35 private videoService: VideoService 33 private videoService: VideoService
36 ) { } 34 ) { }
37 35
@@ -108,20 +106,14 @@ export class SubscribeButtonComponent implements OnInit, OnChanges {
108 () => { 106 () => {
109 this.notifier.success( 107 this.notifier.success(
110 this.account 108 this.account
111 ? this.i18n( 109 ? $localize`Subscribed to all current channels of ${this.account.displayName}. You will be notified of all their new videos.`
112 'Subscribed to all current channels of {{nameWithHost}}. You will be notified of all their new videos.', 110 : $localize`Subscribed to ${this.videoChannels[0].displayName}. You will be notified of all their new videos.`,
113 { nameWithHost: this.account.displayName } 111
114 ) 112 $localize`Subscribed`
115 : this.i18n(
116 'Subscribed to {{nameWithHost}}. You will be notified of all their new videos.',
117 { nameWithHost: this.videoChannels[0].displayName }
118 )
119 ,
120 this.i18n('Subscribed')
121 ) 113 )
122 }, 114 },
123 115
124 err => this.notifier.error(err.message) 116 err => this.notifier.error(err.message)
125 ) 117 )
126 } 118 }
127 119
@@ -144,10 +136,10 @@ export class SubscribeButtonComponent implements OnInit, OnChanges {
144 complete: () => { 136 complete: () => {
145 this.notifier.success( 137 this.notifier.success(
146 this.account 138 this.account
147 ? this.i18n('Unsubscribed from all channels of {{nameWithHost}}', { nameWithHost: this.account.nameWithHost }) 139 ? $localize`Unsubscribed from all channels of ${this.account.nameWithHost}`
148 : this.i18n('Unsubscribed from {{nameWithHost}}', { nameWithHost: this.videoChannels[ 0 ].nameWithHost }) 140 : $localize`Unsubscribed from ${this.videoChannels[ 0 ].nameWithHost}`,
149 , 141
150 this.i18n('Unsubscribed') 142 $localize`Unsubscribed`
151 ) 143 )
152 }, 144 },
153 145
diff --git a/client/src/app/shared/shared-video-miniature/abstract-video-list.ts b/client/src/app/shared/shared-video-miniature/abstract-video-list.ts
index e18002b74..1b5b8a64b 100644
--- a/client/src/app/shared/shared-video-miniature/abstract-video-list.ts
+++ b/client/src/app/shared/shared-video-miniature/abstract-video-list.ts
@@ -14,7 +14,6 @@ import {
14} from '@app/core' 14} from '@app/core'
15import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook' 15import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook'
16import { GlobalIconName } from '@app/shared/shared-icons' 16import { GlobalIconName } from '@app/shared/shared-icons'
17import { I18n } from '@ngx-translate/i18n-polyfill'
18import { isLastMonth, isLastWeek, isToday, isYesterday } from '@shared/core-utils/miscs/date' 17import { isLastMonth, isLastWeek, isToday, isYesterday } from '@shared/core-utils/miscs/date'
19import { ServerConfig, VideoSortField } from '@shared/models' 18import { ServerConfig, VideoSortField } from '@shared/models'
20import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type' 19import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type'
@@ -89,7 +88,6 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
89 protected abstract screenService: ScreenService 88 protected abstract screenService: ScreenService
90 protected abstract storageService: LocalStorageService 89 protected abstract storageService: LocalStorageService
91 protected abstract router: Router 90 protected abstract router: Router
92 protected abstract i18n: I18n
93 abstract titlePage: string 91 abstract titlePage: string
94 92
95 private resizeSubscription: Subscription 93 private resizeSubscription: Subscription
@@ -111,11 +109,11 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
111 109
112 this.groupedDateLabels = { 110 this.groupedDateLabels = {
113 [GroupDate.UNKNOWN]: null, 111 [GroupDate.UNKNOWN]: null,
114 [GroupDate.TODAY]: this.i18n('Today'), 112 [GroupDate.TODAY]: $localize`Today`,
115 [GroupDate.YESTERDAY]: this.i18n('Yesterday'), 113 [GroupDate.YESTERDAY]: $localize`Yesterday`,
116 [GroupDate.LAST_WEEK]: this.i18n('Last week'), 114 [GroupDate.LAST_WEEK]: $localize`Last week`,
117 [GroupDate.LAST_MONTH]: this.i18n('Last month'), 115 [GroupDate.LAST_MONTH]: $localize`Last month`,
118 [GroupDate.OLDER]: this.i18n('Older') 116 [GroupDate.OLDER]: $localize`Older`
119 } 117 }
120 118
121 // Subscribe to route changes 119 // Subscribe to route changes
@@ -192,7 +190,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
192 }, 190 },
193 191
194 error => { 192 error => {
195 const message = this.i18n('Cannot load more videos. Try again later.') 193 const message = $localize`Cannot load more videos. Try again later.`
196 194
197 console.error(message, { error }) 195 console.error(message, { error })
198 this.notifier.error(message) 196 this.notifier.error(message)
diff --git a/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts b/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts
index 3d1fc8690..39358e08b 100644
--- a/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts
+++ b/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts
@@ -1,10 +1,18 @@
1import { Component, EventEmitter, Input, OnChanges, Output, ViewChild } from '@angular/core' 1import { Component, EventEmitter, Input, OnChanges, Output, ViewChild } from '@angular/core'
2import { AuthService, ConfirmService, Notifier, ScreenService } from '@app/core' 2import { AuthService, ConfirmService, Notifier, ScreenService } from '@app/core'
3import { VideoBlockComponent, VideoBlockService, VideoReportComponent, BlocklistService } from '@app/shared/shared-moderation' 3import { BlocklistService, VideoBlockComponent, VideoBlockService, VideoReportComponent } from '@app/shared/shared-moderation'
4import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap' 4import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { VideoCaption } from '@shared/models' 5import { VideoCaption } from '@shared/models'
7import { DropdownAction, DropdownButtonSize, DropdownDirection, RedundancyService, Video, VideoDetails, VideoService, Actor } from '../shared-main' 6import {
7 Actor,
8 DropdownAction,
9 DropdownButtonSize,
10 DropdownDirection,
11 RedundancyService,
12 Video,
13 VideoDetails,
14 VideoService
15} from '../shared-main'
8import { VideoAddToPlaylistComponent } from '../shared-video-playlist' 16import { VideoAddToPlaylistComponent } from '../shared-video-playlist'
9import { VideoDownloadComponent } from './video-download.component' 17import { VideoDownloadComponent } from './video-download.component'
10 18
@@ -71,8 +79,7 @@ export class VideoActionsDropdownComponent implements OnChanges {
71 private videoBlocklistService: VideoBlockService, 79 private videoBlocklistService: VideoBlockService,
72 private screenService: ScreenService, 80 private screenService: ScreenService,
73 private videoService: VideoService, 81 private videoService: VideoService,
74 private redundancyService: RedundancyService, 82 private redundancyService: RedundancyService
75 private i18n: I18n
76 ) { } 83 ) { }
77 84
78 get user () { 85 get user () {
@@ -153,17 +160,15 @@ export class VideoActionsDropdownComponent implements OnChanges {
153 /* Action handlers */ 160 /* Action handlers */
154 161
155 async unblockVideo () { 162 async unblockVideo () {
156 const confirmMessage = this.i18n( 163 const confirmMessage = $localize`Do you really want to unblock this video? It will be available again in the videos list.`
157 'Do you really want to unblock this video? It will be available again in the videos list.'
158 )
159 164
160 const res = await this.confirmService.confirm(confirmMessage, this.i18n('Unblock')) 165 const res = await this.confirmService.confirm(confirmMessage, $localize`Unblock`)
161 if (res === false) return 166 if (res === false) return
162 167
163 this.videoBlocklistService.unblockVideo(this.video.id) 168 this.videoBlocklistService.unblockVideo(this.video.id)
164 .subscribe( 169 .subscribe(
165 () => { 170 () => {
166 this.notifier.success(this.i18n('Video {{name}} unblocked.', { name: this.video.name })) 171 this.notifier.success($localize`Video ${this.video.name} unblocked.`)
167 172
168 this.video.blacklisted = false 173 this.video.blacklisted = false
169 this.video.blockedReason = null 174 this.video.blockedReason = null
@@ -178,14 +183,13 @@ export class VideoActionsDropdownComponent implements OnChanges {
178 async removeVideo () { 183 async removeVideo () {
179 this.modalOpened.emit() 184 this.modalOpened.emit()
180 185
181 const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this video?'), this.i18n('Delete')) 186 const res = await this.confirmService.confirm($localize`Do you really want to delete this video?`, $localize`Delete`)
182 if (res === false) return 187 if (res === false) return
183 188
184 this.videoService.removeVideo(this.video.id) 189 this.videoService.removeVideo(this.video.id)
185 .subscribe( 190 .subscribe(
186 () => { 191 () => {
187 this.notifier.success(this.i18n('Video {{videoName}} deleted.', { videoName: this.video.name })) 192 this.notifier.success($localize`Video ${this.video.name} deleted.`)
188
189 this.videoRemoved.emit() 193 this.videoRemoved.emit()
190 }, 194 },
191 195
@@ -197,7 +201,7 @@ export class VideoActionsDropdownComponent implements OnChanges {
197 this.redundancyService.addVideoRedundancy(this.video) 201 this.redundancyService.addVideoRedundancy(this.video)
198 .subscribe( 202 .subscribe(
199 () => { 203 () => {
200 const message = this.i18n('This video will be duplicated by your instance.') 204 const message = $localize`This video will be duplicated by your instance.`
201 this.notifier.success(message) 205 this.notifier.success(message)
202 }, 206 },
203 207
@@ -211,8 +215,7 @@ export class VideoActionsDropdownComponent implements OnChanges {
211 this.blocklistService.blockAccountByUser(params) 215 this.blocklistService.blockAccountByUser(params)
212 .subscribe( 216 .subscribe(
213 () => { 217 () => {
214 this.notifier.success(this.i18n('Account {{nameWithHost}} muted.', params)) 218 this.notifier.success($localize`Account ${params.nameWithHost} muted.`)
215
216 this.videoAccountMuted.emit() 219 this.videoAccountMuted.emit()
217 }, 220 },
218 221
@@ -236,7 +239,7 @@ export class VideoActionsDropdownComponent implements OnChanges {
236 this.videoActions = [ 239 this.videoActions = [
237 [ 240 [
238 { 241 {
239 label: this.i18n('Save to playlist'), 242 label: $localize`Save to playlist`,
240 handler: () => this.playlistDropdown.toggle(), 243 handler: () => this.playlistDropdown.toggle(),
241 isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.playlist, 244 isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.playlist,
242 iconName: 'playlist-add' 245 iconName: 'playlist-add'
@@ -244,43 +247,43 @@ export class VideoActionsDropdownComponent implements OnChanges {
244 ], 247 ],
245 [ // actions regarding the video 248 [ // actions regarding the video
246 { 249 {
247 label: this.i18n('Download'), 250 label: $localize`Download`,
248 handler: () => this.showDownloadModal(), 251 handler: () => this.showDownloadModal(),
249 isDisplayed: () => this.displayOptions.download && this.isVideoDownloadable(), 252 isDisplayed: () => this.displayOptions.download && this.isVideoDownloadable(),
250 iconName: 'download' 253 iconName: 'download'
251 }, 254 },
252 { 255 {
253 label: this.i18n('Update'), 256 label: $localize`Update`,
254 linkBuilder: ({ video }) => [ '/videos/update', video.uuid ], 257 linkBuilder: ({ video }) => [ '/videos/update', video.uuid ],
255 iconName: 'edit', 258 iconName: 'edit',
256 isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.update && this.isVideoUpdatable() 259 isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.update && this.isVideoUpdatable()
257 }, 260 },
258 { 261 {
259 label: this.i18n('Block'), 262 label: $localize`Block`,
260 handler: () => this.showBlockModal(), 263 handler: () => this.showBlockModal(),
261 iconName: 'no', 264 iconName: 'no',
262 isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.blacklist && this.isVideoBlockable() 265 isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.blacklist && this.isVideoBlockable()
263 }, 266 },
264 { 267 {
265 label: this.i18n('Unblock'), 268 label: $localize`Unblock`,
266 handler: () => this.unblockVideo(), 269 handler: () => this.unblockVideo(),
267 iconName: 'undo', 270 iconName: 'undo',
268 isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.blacklist && this.isVideoUnblockable() 271 isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.blacklist && this.isVideoUnblockable()
269 }, 272 },
270 { 273 {
271 label: this.i18n('Mirror'), 274 label: $localize`Mirror`,
272 handler: () => this.duplicateVideo(), 275 handler: () => this.duplicateVideo(),
273 isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.duplicate && this.canVideoBeDuplicated(), 276 isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.duplicate && this.canVideoBeDuplicated(),
274 iconName: 'cloud-download' 277 iconName: 'cloud-download'
275 }, 278 },
276 { 279 {
277 label: this.i18n('Delete'), 280 label: $localize`Delete`,
278 handler: () => this.removeVideo(), 281 handler: () => this.removeVideo(),
279 isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.delete && this.isVideoRemovable(), 282 isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.delete && this.isVideoRemovable(),
280 iconName: 'delete' 283 iconName: 'delete'
281 }, 284 },
282 { 285 {
283 label: this.i18n('Report'), 286 label: $localize`Report`,
284 handler: () => this.showReportModal(), 287 handler: () => this.showReportModal(),
285 isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.report, 288 isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.report,
286 iconName: 'flag' 289 iconName: 'flag'
@@ -288,7 +291,7 @@ export class VideoActionsDropdownComponent implements OnChanges {
288 ], 291 ],
289 [ // actions regarding the account/its server 292 [ // actions regarding the account/its server
290 { 293 {
291 label: this.i18n('Mute account'), 294 label: $localize`Mute account`,
292 handler: () => this.muteVideoAccount(), 295 handler: () => this.muteVideoAccount(),
293 isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.mute && this.isVideoAccountMutable(), 296 isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.mute && this.isVideoAccountMutable(),
294 iconName: 'no' 297 iconName: 'no'
diff --git a/client/src/app/shared/shared-video-miniature/video-download.component.ts b/client/src/app/shared/shared-video-miniature/video-download.component.ts
index e3d6a1969..99838f712 100644
--- a/client/src/app/shared/shared-video-miniature/video-download.component.ts
+++ b/client/src/app/shared/shared-video-miniature/video-download.component.ts
@@ -2,7 +2,6 @@ import { mapValues, pick } from 'lodash-es'
2import { Component, ElementRef, ViewChild } from '@angular/core' 2import { Component, ElementRef, ViewChild } from '@angular/core'
3import { AuthService, Notifier } from '@app/core' 3import { AuthService, Notifier } from '@app/core'
4import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap' 4import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models' 5import { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models'
7import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoService } from '../shared-main' 6import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoService } from '../shared-main'
8 7
@@ -38,8 +37,7 @@ export class VideoDownloadComponent {
38 private notifier: Notifier, 37 private notifier: Notifier,
39 private modalService: NgbModal, 38 private modalService: NgbModal,
40 private videoService: VideoService, 39 private videoService: VideoService,
41 private auth: AuthService, 40 private auth: AuthService
42 private i18n: I18n
43 ) { 41 ) {
44 this.bytesPipe = new BytesPipe() 42 this.bytesPipe = new BytesPipe()
45 this.numbersPipe = new NumberFormatterPipe() 43 this.numbersPipe = new NumberFormatterPipe()
@@ -47,8 +45,8 @@ export class VideoDownloadComponent {
47 45
48 get typeText () { 46 get typeText () {
49 return this.type === 'video' 47 return this.type === 'video'
50 ? this.i18n('video') 48 ? $localize`video`
51 : this.i18n('subtitles') 49 : $localize`subtitles`
52 } 50 }
53 51
54 getVideoFiles () { 52 getVideoFiles () {
@@ -135,7 +133,7 @@ export class VideoDownloadComponent {
135 } 133 }
136 134
137 activateCopiedMessage () { 135 activateCopiedMessage () {
138 this.notifier.success(this.i18n('Copied')) 136 this.notifier.success($localize`Copied`)
139 } 137 }
140 138
141 switchToType (type: DownloadType) { 139 switchToType (type: DownloadType) {
@@ -144,11 +142,11 @@ export class VideoDownloadComponent {
144 142
145 getMetadataFormat (format: any) { 143 getMetadataFormat (format: any) {
146 const keyToTranslateFunction = { 144 const keyToTranslateFunction = {
147 'encoder': (value: string) => ({ label: this.i18n('Encoder'), value }), 145 'encoder': (value: string) => ({ label: $localize`Encoder`, value }),
148 'format_long_name': (value: string) => ({ label: this.i18n('Format name'), value }), 146 'format_long_name': (value: string) => ({ label: $localize`Format name`, value }),
149 'size': (value: number) => ({ label: this.i18n('Size'), value: this.bytesPipe.transform(value, 2) }), 147 'size': (value: number) => ({ label: $localize`Size`, value: this.bytesPipe.transform(value, 2) }),
150 'bit_rate': (value: number) => ({ 148 'bit_rate': (value: number) => ({
151 label: this.i18n('Bitrate'), 149 label: $localize`Bitrate`,
152 value: `${this.numbersPipe.transform(value)}bps` 150 value: `${this.numbersPipe.transform(value)}bps`
153 }) 151 })
154 } 152 }
@@ -168,25 +166,25 @@ export class VideoDownloadComponent {
168 if (!stream) return undefined 166 if (!stream) return undefined
169 167
170 let keyToTranslateFunction = { 168 let keyToTranslateFunction = {
171 'codec_long_name': (value: string) => ({ label: this.i18n('Codec'), value }), 169 'codec_long_name': (value: string) => ({ label: $localize`Codec`, value }),
172 'profile': (value: string) => ({ label: this.i18n('Profile'), value }), 170 'profile': (value: string) => ({ label: $localize`Profile`, value }),
173 'bit_rate': (value: number) => ({ 171 'bit_rate': (value: number) => ({
174 label: this.i18n('Bitrate'), 172 label: $localize`Bitrate`,
175 value: `${this.numbersPipe.transform(value)}bps` 173 value: `${this.numbersPipe.transform(value)}bps`
176 }) 174 })
177 } 175 }
178 176
179 if (type === 'video') { 177 if (type === 'video') {
180 keyToTranslateFunction = Object.assign(keyToTranslateFunction, { 178 keyToTranslateFunction = Object.assign(keyToTranslateFunction, {
181 'width': (value: number) => ({ label: this.i18n('Resolution'), value: `${value}x${stream.height}` }), 179 'width': (value: number) => ({ label: $localize`Resolution`, value: `${value}x${stream.height}` }),
182 'display_aspect_ratio': (value: string) => ({ label: this.i18n('Aspect ratio'), value }), 180 'display_aspect_ratio': (value: string) => ({ label: $localize`Aspect ratio`, value }),
183 'avg_frame_rate': (value: string) => ({ label: this.i18n('Average frame rate'), value }), 181 'avg_frame_rate': (value: string) => ({ label: $localize`Average frame rate`, value }),
184 'pix_fmt': (value: string) => ({ label: this.i18n('Pixel format'), value }) 182 'pix_fmt': (value: string) => ({ label: $localize`Pixel format`, value })
185 }) 183 })
186 } else { 184 } else {
187 keyToTranslateFunction = Object.assign(keyToTranslateFunction, { 185 keyToTranslateFunction = Object.assign(keyToTranslateFunction, {
188 'sample_rate': (value: number) => ({ label: this.i18n('Sample rate'), value }), 186 'sample_rate': (value: number) => ({ label: $localize`Sample rate`, value }),
189 'channel_layout': (value: number) => ({ label: this.i18n('Channel Layout'), value }) 187 'channel_layout': (value: number) => ({ label: $localize`Channel Layout`, value })
190 }) 188 })
191 } 189 }
192 190
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.ts b/client/src/app/shared/shared-video-miniature/video-miniature.component.ts
index 3c7046de5..cc5665ab1 100644
--- a/client/src/app/shared/shared-video-miniature/video-miniature.component.ts
+++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.ts
@@ -11,7 +11,6 @@ import {
11 Output 11 Output
12} from '@angular/core' 12} from '@angular/core'
13import { AuthService, ScreenService, ServerService, User } from '@app/core' 13import { AuthService, ScreenService, ServerService, User } from '@app/core'
14import { I18n } from '@ngx-translate/i18n-polyfill'
15import { ServerConfig, VideoPlaylistType, VideoPrivacy, VideoState } from '@shared/models' 14import { ServerConfig, VideoPlaylistType, VideoPrivacy, VideoState } from '@shared/models'
16import { Video } from '../shared-main' 15import { Video } from '../shared-main'
17import { VideoPlaylistService } from '../shared-video-playlist' 16import { VideoPlaylistService } from '../shared-video-playlist'
@@ -95,7 +94,6 @@ export class VideoMiniatureComponent implements OnInit {
95 constructor ( 94 constructor (
96 private screenService: ScreenService, 95 private screenService: ScreenService,
97 private serverService: ServerService, 96 private serverService: ServerService,
98 private i18n: I18n,
99 private authService: AuthService, 97 private authService: AuthService,
100 private videoPlaylistService: VideoPlaylistService, 98 private videoPlaylistService: VideoPlaylistService,
101 private cd: ChangeDetectorRef, 99 private cd: ChangeDetectorRef,
@@ -116,10 +114,7 @@ export class VideoMiniatureComponent implements OnInit {
116 114
117 this.setUpBy() 115 this.setUpBy()
118 116
119 this.channelLinkTitle = this.i18n( 117 this.channelLinkTitle = $localize`${this.video.channel.name} (channel page)`
120 '{{name}} (channel page)',
121 { name: this.video.channel.name, handle: this.video.byVideoChannel }
122 )
123 118
124 // We rely on mouseenter to lazy load actions 119 // We rely on mouseenter to lazy load actions
125 if (this.screenService.isInTouchScreen()) { 120 if (this.screenService.isInTouchScreen()) {
@@ -164,24 +159,24 @@ export class VideoMiniatureComponent implements OnInit {
164 if (!video.state) return '' 159 if (!video.state) return ''
165 160
166 if (video.privacy.id !== VideoPrivacy.PRIVATE && video.state.id === VideoState.PUBLISHED) { 161 if (video.privacy.id !== VideoPrivacy.PRIVATE && video.state.id === VideoState.PUBLISHED) {
167 return this.i18n('Published') 162 return $localize`Published`
168 } 163 }
169 164
170 if (video.scheduledUpdate) { 165 if (video.scheduledUpdate) {
171 const updateAt = new Date(video.scheduledUpdate.updateAt.toString()).toLocaleString(this.localeId) 166 const updateAt = new Date(video.scheduledUpdate.updateAt.toString()).toLocaleString(this.localeId)
172 return this.i18n('Publication scheduled on ') + updateAt 167 return $localize`Publication scheduled on ` + updateAt
173 } 168 }
174 169
175 if (video.state.id === VideoState.TO_TRANSCODE && video.waitTranscoding === true) { 170 if (video.state.id === VideoState.TO_TRANSCODE && video.waitTranscoding === true) {
176 return this.i18n('Waiting transcoding') 171 return $localize`Waiting transcoding`
177 } 172 }
178 173
179 if (video.state.id === VideoState.TO_TRANSCODE) { 174 if (video.state.id === VideoState.TO_TRANSCODE) {
180 return this.i18n('To transcode') 175 return $localize`To transcode`
181 } 176 }
182 177
183 if (video.state.id === VideoState.TO_IMPORT) { 178 if (video.state.id === VideoState.TO_IMPORT) {
184 return this.i18n('To import') 179 return $localize`To import`
185 } 180 }
186 181
187 return '' 182 return ''
diff --git a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts
index 3e0e3b983..2b060b130 100644
--- a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts
+++ b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts
@@ -13,7 +13,6 @@ import {
13} from '@angular/core' 13} from '@angular/core'
14import { ActivatedRoute, Router } from '@angular/router' 14import { ActivatedRoute, Router } from '@angular/router'
15import { AuthService, ComponentPagination, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core' 15import { AuthService, ComponentPagination, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core'
16import { I18n } from '@ngx-translate/i18n-polyfill'
17import { ResultList, VideoSortField } from '@shared/models' 16import { ResultList, VideoSortField } from '@shared/models'
18import { PeerTubeTemplateDirective, Video } from '../shared-main' 17import { PeerTubeTemplateDirective, Video } from '../shared-main'
19import { AbstractVideoList } from './abstract-video-list' 18import { AbstractVideoList } from './abstract-video-list'
@@ -45,7 +44,6 @@ export class VideosSelectionComponent extends AbstractVideoList implements OnIni
45 globalButtonsTemplate: TemplateRef<any> 44 globalButtonsTemplate: TemplateRef<any>
46 45
47 constructor ( 46 constructor (
48 protected i18n: I18n,
49 protected router: Router, 47 protected router: Router,
50 protected route: ActivatedRoute, 48 protected route: ActivatedRoute,
51 protected notifier: Notifier, 49 protected notifier: Notifier,
diff --git a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts
index f611fc46b..757ffa099 100644
--- a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts
+++ b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts
@@ -4,7 +4,6 @@ import { debounceTime, filter } from 'rxjs/operators'
4import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core' 4import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'
5import { AuthService, DisableForReuseHook, Notifier } from '@app/core' 5import { AuthService, DisableForReuseHook, Notifier } from '@app/core'
6import { FormReactive, FormValidatorService, VideoPlaylistValidatorsService } from '@app/shared/shared-forms' 6import { FormReactive, FormValidatorService, VideoPlaylistValidatorsService } from '@app/shared/shared-forms'
7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { Video, VideoExistInPlaylist, VideoPlaylistCreate, VideoPlaylistElementCreate, VideoPlaylistPrivacy } from '@shared/models' 7import { Video, VideoExistInPlaylist, VideoPlaylistCreate, VideoPlaylistElementCreate, VideoPlaylistPrivacy } from '@shared/models'
9import { secondsToTime } from '../../../assets/player/utils' 8import { secondsToTime } from '../../../assets/player/utils'
10import { CachedPlaylist, VideoPlaylistService } from './video-playlist.service' 9import { CachedPlaylist, VideoPlaylistService } from './video-playlist.service'
@@ -53,7 +52,6 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
53 protected formValidatorService: FormValidatorService, 52 protected formValidatorService: FormValidatorService,
54 private authService: AuthService, 53 private authService: AuthService,
55 private notifier: Notifier, 54 private notifier: Notifier,
56 private i18n: I18n,
57 private videoPlaylistService: VideoPlaylistService, 55 private videoPlaylistService: VideoPlaylistService,
58 private videoPlaylistValidatorsService: VideoPlaylistValidatorsService, 56 private videoPlaylistValidatorsService: VideoPlaylistValidatorsService,
59 private cd: ChangeDetectorRef 57 private cd: ChangeDetectorRef
@@ -204,7 +202,7 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
204 this.videoPlaylistService.removeVideoFromPlaylist(playlist.id, playlist.playlistElementId, this.video.id) 202 this.videoPlaylistService.removeVideoFromPlaylist(playlist.id, playlist.playlistElementId, this.video.id)
205 .subscribe( 203 .subscribe(
206 () => { 204 () => {
207 this.notifier.success(this.i18n('Video removed from {{name}}', { name: playlist.displayName })) 205 this.notifier.success($localize`Video removed from ${playlist.displayName}`)
208 }, 206 },
209 207
210 err => { 208 err => {
@@ -262,8 +260,8 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
262 .subscribe( 260 .subscribe(
263 () => { 261 () => {
264 const message = body.startTimestamp || body.stopTimestamp 262 const message = body.startTimestamp || body.stopTimestamp
265 ? this.i18n('Video added in {{n}} at timestamps {{t}}', { n: playlist.displayName, t: this.formatTimestamp(playlist) }) 263 ? $localize`Video added in ${playlist.displayName} at timestamps ${this.formatTimestamp(playlist)}`
266 : this.i18n('Video added in {{n}}', { n: playlist.displayName }) 264 : $localize`Video added in ${playlist.displayName}`
267 265
268 this.notifier.success(message) 266 this.notifier.success(message)
269 }, 267 },
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts b/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts
index 57a5fbe61..5879c4978 100644
--- a/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts
+++ b/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts
@@ -2,7 +2,6 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, In
2import { AuthService, Notifier, ServerService } from '@app/core' 2import { AuthService, Notifier, ServerService } from '@app/core'
3import { Video } from '@app/shared/shared-main' 3import { Video } from '@app/shared/shared-main'
4import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap' 4import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { ServerConfig, VideoPlaylistElementType, VideoPlaylistElementUpdate } from '@shared/models' 5import { ServerConfig, VideoPlaylistElementType, VideoPlaylistElementUpdate } from '@shared/models'
7import { secondsToTime } from '../../../assets/player/utils' 6import { secondsToTime } from '../../../assets/player/utils'
8import { VideoPlaylistElement } from './video-playlist-element.model' 7import { VideoPlaylistElement } from './video-playlist-element.model'
@@ -44,7 +43,6 @@ export class VideoPlaylistElementMiniatureComponent implements OnInit {
44 private authService: AuthService, 43 private authService: AuthService,
45 private serverService: ServerService, 44 private serverService: ServerService,
46 private notifier: Notifier, 45 private notifier: Notifier,
47 private i18n: I18n,
48 private videoPlaylistService: VideoPlaylistService, 46 private videoPlaylistService: VideoPlaylistService,
49 private cdr: ChangeDetectorRef 47 private cdr: ChangeDetectorRef
50 ) {} 48 ) {}
@@ -97,8 +95,7 @@ export class VideoPlaylistElementMiniatureComponent implements OnInit {
97 this.videoPlaylistService.removeVideoFromPlaylist(this.playlist.id, playlistElement.id, videoId) 95 this.videoPlaylistService.removeVideoFromPlaylist(this.playlist.id, playlistElement.id, videoId)
98 .subscribe( 96 .subscribe(
99 () => { 97 () => {
100 this.notifier.success(this.i18n('Video removed from {{name}}', { name: this.playlist.displayName })) 98 this.notifier.success($localize`Video removed from ${this.playlist.displayName}`)
101
102 this.elementRemoved.emit(playlistElement) 99 this.elementRemoved.emit(playlistElement)
103 }, 100 },
104 101
@@ -117,7 +114,7 @@ export class VideoPlaylistElementMiniatureComponent implements OnInit {
117 this.videoPlaylistService.updateVideoOfPlaylist(this.playlist.id, playlistElement.id, body, this.playlistElement.video.id) 114 this.videoPlaylistService.updateVideoOfPlaylist(this.playlist.id, playlistElement.id, body, this.playlistElement.video.id)
118 .subscribe( 115 .subscribe(
119 () => { 116 () => {
120 this.notifier.success(this.i18n('Timestamps updated')) 117 this.notifier.success($localize`Timestamps updated`)
121 118
122 playlistElement.startTimestamp = body.startTimestamp 119 playlistElement.startTimestamp = body.startTimestamp
123 playlistElement.stopTimestamp = body.stopTimestamp 120 playlistElement.stopTimestamp = body.stopTimestamp
@@ -140,10 +137,10 @@ export class VideoPlaylistElementMiniatureComponent implements OnInit {
140 137
141 if (start === null && stop === null) return '' 138 if (start === null && stop === null) return ''
142 139
143 if (start !== null && stop === null) return this.i18n('Starts at ') + startFormatted 140 if (start !== null && stop === null) return $localize`Starts at ` + startFormatted
144 if (start === null && stop !== null) return this.i18n('Stops at ') + stopFormatted 141 if (start === null && stop !== null) return $localize`Stops at ` + stopFormatted
145 142
146 return this.i18n('Starts at ') + startFormatted + this.i18n(' and stops at ') + stopFormatted 143 return $localize`Starts at ` + startFormatted + $localize` and stops at ` + stopFormatted
147 } 144 }
148 145
149 onDropdownOpenChange () { 146 onDropdownOpenChange () {
diff --git a/client/yarn.lock b/client/yarn.lock
index 4fe21c1a5..db52a4a54 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -120,10 +120,10 @@
120 ora "4.0.5" 120 ora "4.0.5"
121 rxjs "6.6.2" 121 rxjs "6.6.2"
122 122
123"@angular/animations@^10.1.0-next.4": 123"@angular/animations@^10.1.0-next.5":
124 version "10.1.0-next.4" 124 version "10.1.0-next.5"
125 resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-10.1.0-next.4.tgz#e2e46e23f4aa02d6b04b1db795f85d51e2fcc62b" 125 resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-10.1.0-next.5.tgz#af5c9365ef5473d430797eb1b2f08a8ed4e75b96"
126 integrity sha512-3PWWtORaHjspKFbff+NdB2/DdgsNMNDz02/oCylK5+UAAtRzci885EAuk0oC5wn6pkzU4WrBbUYkVWikbQEy9Q== 126 integrity sha512-1ssVuf4in1nLqiC7ZOXQyjbk4KMj9g4jTB5LkhPgXmkMr5cdHckIgeSSk0sF0I7F1+Xo0P0Dz4CL7AZY/IlxQw==
127 dependencies: 127 dependencies:
128 tslib "^2.0.0" 128 tslib "^2.0.0"
129 129
@@ -162,17 +162,17 @@
162 universal-analytics "0.4.23" 162 universal-analytics "0.4.23"
163 uuid "8.3.0" 163 uuid "8.3.0"
164 164
165"@angular/common@^10.1.0-next.4": 165"@angular/common@^10.1.0-next.5":
166 version "10.1.0-next.4" 166 version "10.1.0-next.5"
167 resolved "https://registry.yarnpkg.com/@angular/common/-/common-10.1.0-next.4.tgz#712850da191bcd41e9d8fe0da9484d30b3b8e248" 167 resolved "https://registry.yarnpkg.com/@angular/common/-/common-10.1.0-next.5.tgz#9c8e43e6a319e9d65e4d6c8e000ee1ca9775eaf8"
168 integrity sha512-iiVUqc+ManJHak3HPhZ9ajp5f3IZmm8f9ev8Bu8bVRDjk/76hSeCRln6fykbZPwpbAOlVgVWjjTf2bgf6QMpQQ== 168 integrity sha512-vK7btoMtC3hE6BfNY3QLlDipW9sd8CGdOfHyl8smNtRyw7qAMSxgvIkQSaclN4K7h3uD7QMppqPAjZMgeUxfzw==
169 dependencies: 169 dependencies:
170 tslib "^2.0.0" 170 tslib "^2.0.0"
171 171
172"@angular/compiler-cli@^10.1.0-next.4": 172"@angular/compiler-cli@^10.1.0-next.5":
173 version "10.1.0-next.4" 173 version "10.1.0-next.5"
174 resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-10.1.0-next.4.tgz#b9edfe7c04ef5faeaecdab095e58137fdbe14f3d" 174 resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-10.1.0-next.5.tgz#4ef0ae92ecee8b1de003a82180c5813f2c507cdb"
175 integrity sha512-yKv0bCr+vdb34uBiv18iHC+V83gWQt4AuEvUF/WYVqnmE7gKa1PO0LjMyWB8lHrur/3k5uBug1NAqOqAQGcyTw== 175 integrity sha512-+MI4enl5N0xyjzvL9vfqVaDqDm6RkuX/jydvaFr8yTESBRXCu8g4UvTcxnOwLcQehl7ZD45WmOeOnRXdwcI3kA==
176 dependencies: 176 dependencies:
177 canonical-path "1.0.0" 177 canonical-path "1.0.0"
178 chokidar "^3.0.0" 178 chokidar "^3.0.0"
@@ -188,61 +188,61 @@
188 tslib "^2.0.0" 188 tslib "^2.0.0"
189 yargs "15.3.0" 189 yargs "15.3.0"
190 190
191"@angular/compiler@^10.1.0-next.4": 191"@angular/compiler@^10.1.0-next.5":
192 version "10.1.0-next.4" 192 version "10.1.0-next.5"
193 resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-10.1.0-next.4.tgz#b86b51492e59edcc507d13ef1842bae9b36ae855" 193 resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-10.1.0-next.5.tgz#3b638e8708d24310b81003271bee5006ea9c7d9e"
194 integrity sha512-TT4kh41AkU4oatHnKwfQ5A10RPg+A7xSBPxSsqKBC8bphel0HL4Lf4hn5shIK4G8aZaieBfntkC9Nf7WoWgqaQ== 194 integrity sha512-pDUTIerTs6A9yDBGeKiSFzu0CsG3l0ct4UkIWE1PsW9ui63ar+UytGpiLBBJ8pXfktMOZle8NVmlKAzhXG2g/g==
195 dependencies: 195 dependencies:
196 tslib "^2.0.0" 196 tslib "^2.0.0"
197 197
198"@angular/core@^10.1.0-next.4": 198"@angular/core@^10.1.0-next.5":
199 version "10.1.0-next.4" 199 version "10.1.0-next.5"
200 resolved "https://registry.yarnpkg.com/@angular/core/-/core-10.1.0-next.4.tgz#622cd9245c178422b896149e9d9aa387109142b3" 200 resolved "https://registry.yarnpkg.com/@angular/core/-/core-10.1.0-next.5.tgz#70269efc5ede9553bac231f4d61e2a99bb5b69bb"
201 integrity sha512-buAMY+gTeX6jXfXSxnLnwLHeJnQ7obLr27JZo8FNt3JTI6nYAdLEf2Jw2jsqwJ8H6fSNjOHmm+tLRqSVHSyeRg== 201 integrity sha512-bLV1sSaEZqda+cmYwfYKvy4KX0xtoAoLwtNQi6/TGVBj4c+6NsUp5oN2SwSEQKTo7vi0qaP1/Q/XUGSX4ZYHQw==
202 dependencies: 202 dependencies:
203 tslib "^2.0.0" 203 tslib "^2.0.0"
204 204
205"@angular/forms@^10.1.0-next.4": 205"@angular/forms@^10.1.0-next.5":
206 version "10.1.0-next.4" 206 version "10.1.0-next.5"
207 resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-10.1.0-next.4.tgz#15798595ffb50ad924f4be89d0de38a76a367a6f" 207 resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-10.1.0-next.5.tgz#02b0a361cda71fbd6ed7c45e707db7ab98abd950"
208 integrity sha512-6fINEZoxj4vRmgfDUG7ICt381hCizMp6wzYPUd55+fQjNGRy/BwkbmzZI0tf3QoiRjqiTZIq/L+sQxQMfKFZhg== 208 integrity sha512-aE/Q0VT4k1AtUzC/2YL7//mXBZQDENwtFUVnMjv8Krbvlka9EylZHN0MdFNhzW2zuNmELnv2Eud4O+vEF9vJ6Q==
209 dependencies: 209 dependencies:
210 tslib "^2.0.0" 210 tslib "^2.0.0"
211 211
212"@angular/localize@^10.1.0-next.4": 212"@angular/localize@^10.1.0-next.5":
213 version "10.1.0-next.4" 213 version "10.1.0-next.5"
214 resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-10.1.0-next.4.tgz#a2dbe3c6c4d72421baca87ec1e59f6a4dbc1d547" 214 resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-10.1.0-next.5.tgz#f2722ac407d34ac7f0c6b748f58b8966c08f797b"
215 integrity sha512-q/RiyZvMzhXabXVwYqjljS30mKsUL06CZAsrS1EvXC+dcA2qTpVoHUFLr5DHiqcNlxENXtJ/+56O7N+V85nH1w== 215 integrity sha512-EBTsMw66snjNOl1FZJB/bhPJC19E5K5TImS4+Pf1lEbHBLuvfEwZLQnrFoyPWwU98ozFCxYtNZvNJUOHFpM5hQ==
216 dependencies: 216 dependencies:
217 "@babel/core" "7.8.3" 217 "@babel/core" "7.8.3"
218 glob "7.1.2" 218 glob "7.1.2"
219 yargs "15.3.0" 219 yargs "15.3.0"
220 220
221"@angular/platform-browser-dynamic@^10.1.0-next.4": 221"@angular/platform-browser-dynamic@^10.1.0-next.5":
222 version "10.1.0-next.4" 222 version "10.1.0-next.5"
223 resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-10.1.0-next.4.tgz#664f48844152785b1af79a906766661bfbd8e030" 223 resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-10.1.0-next.5.tgz#bc348ed36dc4f97cc90bb6817191e15d3f1b6f23"
224 integrity sha512-amy8aLpE2Hjq5D4cwY8oDPFW963jXJPaMuXWwQsJUqKgmYwUOS4PU86x1BceTb6wOl4Gf4+sJREx3dMjh1AaAQ== 224 integrity sha512-dgxGMyz5wwVegFEaE3RS4XSurlrqK1RMvFKpIpb/EbJVP7ZBYMhtX6Wqw0p81MYo0GIssaSaKJd+j4rjjxWR2g==
225 dependencies: 225 dependencies:
226 tslib "^2.0.0" 226 tslib "^2.0.0"
227 227
228"@angular/platform-browser@^10.1.0-next.4": 228"@angular/platform-browser@^10.1.0-next.5":
229 version "10.1.0-next.4" 229 version "10.1.0-next.5"
230 resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-10.1.0-next.4.tgz#533411a5a2c2fd5ec43198297a032a67c324e59c" 230 resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-10.1.0-next.5.tgz#205c99c3edb11b7a83bfa2f967d1e4617a4671ee"
231 integrity sha512-DOEL1RDCEd/xttZNyf3VlWC639/3MSlYsDKpdn9FzgLNnu/CXerilSGQQlzaR0b3V8f64csUtuejH9YB/jp0HQ== 231 integrity sha512-RDgLlDYqWYDdNEPYHJIoG5JsO6m81xxfJNsDVI4DE9z6iCqUxnWappRoDNs/yXFXbdF8p+WbjtL2pru50TWFEA==
232 dependencies: 232 dependencies:
233 tslib "^2.0.0" 233 tslib "^2.0.0"
234 234
235"@angular/router@^10.1.0-next.4": 235"@angular/router@^10.1.0-next.5":
236 version "10.1.0-next.4" 236 version "10.1.0-next.5"
237 resolved "https://registry.yarnpkg.com/@angular/router/-/router-10.1.0-next.4.tgz#341951c3db2c206bf85bedfa496c5d75e41ac46c" 237 resolved "https://registry.yarnpkg.com/@angular/router/-/router-10.1.0-next.5.tgz#3882a2ed1ea5b7f2efca67af4fa3fd28b9e25762"
238 integrity sha512-KjvLIMZcxrDtXVYsWpizq91p50C8JQ4i7wsgxD9O7r0bvMJPn/LngUpaNz+9zKmk4UUwpxZ65sd8sfw+mOR26g== 238 integrity sha512-Jyh62a0zP1gX/R3hV5ILnd/t3AwBLTvRetXfDuOB5c5vImjgwpl84gX3+aJsSvc1cZ0Rs31zqV8JR9M9yEAAbQ==
239 dependencies: 239 dependencies:
240 tslib "^2.0.0" 240 tslib "^2.0.0"
241 241
242"@angular/service-worker@^10.1.0-next.4": 242"@angular/service-worker@^10.1.0-next.5":
243 version "10.1.0-next.4" 243 version "10.1.0-next.5"
244 resolved "https://registry.yarnpkg.com/@angular/service-worker/-/service-worker-10.1.0-next.4.tgz#ae240fdc2957b27e71a738fe8a01d31dfbae2ee6" 244 resolved "https://registry.yarnpkg.com/@angular/service-worker/-/service-worker-10.1.0-next.5.tgz#699bfd88e7b02c9e30fa9103c1fd508b86725e60"
245 integrity sha512-xF9J+bPGRPAxeC07cOKHoXxJ32skNPDj9bEpymrfJaRxzmQ41gN4+IlwmiFJjoHUwYVX3x4OZyb18HTJPhWqXw== 245 integrity sha512-lXne4/88dre8NXY/jHEm+bONdG4EswzBGln2PnaHGZ1i3ALATcSwzrCEAqh29eeP9/OGXRMc7iBdAj2N24WxUw==
246 dependencies: 246 dependencies:
247 tslib "^2.0.0" 247 tslib "^2.0.0"
248 248
@@ -1412,15 +1412,6 @@
1412 resolved "https://registry.yarnpkg.com/@ngx-meta/core/-/core-9.0.0.tgz#2c3b27bd32d41f7aaf0712d4007e0afed8e5dd3f" 1412 resolved "https://registry.yarnpkg.com/@ngx-meta/core/-/core-9.0.0.tgz#2c3b27bd32d41f7aaf0712d4007e0afed8e5dd3f"
1413 integrity sha512-WL/HyKM+4gJPS3TNTO0M+46LlIvGTSyofL//8hxoozAyRPsiMyR0xrwUvPBJkVqFyD6j0iI5FaNcQ9d0JCVJhg== 1413 integrity sha512-WL/HyKM+4gJPS3TNTO0M+46LlIvGTSyofL//8hxoozAyRPsiMyR0xrwUvPBJkVqFyD6j0iI5FaNcQ9d0JCVJhg==
1414 1414
1415"@ngx-translate/i18n-polyfill@^1.0.0":
1416 version "1.0.0"
1417 resolved "https://registry.yarnpkg.com/@ngx-translate/i18n-polyfill/-/i18n-polyfill-1.0.0.tgz#145edb28bcfc1332e1bc25279eadf9d4ed0a20f8"
1418 integrity sha512-+UKmSr6cWBJiMDex6w2FwVjEeVnlEsINDGYvTgRaFRI3/IKZrsSVcfISDcBX2wWr6m4jumfOyCcimIl2TxcaoA==
1419 dependencies:
1420 glob "7.1.2"
1421 tslib "^1.9.0"
1422 yargs "10.0.3"
1423
1424"@nodelib/fs.scandir@2.1.3": 1415"@nodelib/fs.scandir@2.1.3":
1425 version "2.1.3" 1416 version "2.1.3"
1426 resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" 1417 resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b"
@@ -5149,7 +5140,7 @@ find-up@^1.0.0:
5149 path-exists "^2.0.0" 5140 path-exists "^2.0.0"
5150 pinkie-promise "^2.0.0" 5141 pinkie-promise "^2.0.0"
5151 5142
5152find-up@^2.0.0, find-up@^2.1.0: 5143find-up@^2.0.0:
5153 version "2.1.0" 5144 version "2.1.0"
5154 resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" 5145 resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
5155 integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= 5146 integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c=
@@ -12355,31 +12346,6 @@ yargs-parser@^7.0.0:
12355 dependencies: 12346 dependencies:
12356 camelcase "^4.1.0" 12347 camelcase "^4.1.0"
12357 12348
12358yargs-parser@^8.0.0:
12359 version "8.1.0"
12360 resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950"
12361 integrity sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==
12362 dependencies:
12363 camelcase "^4.1.0"
12364
12365yargs@10.0.3:
12366 version "10.0.3"
12367 resolved "https://registry.yarnpkg.com/yargs/-/yargs-10.0.3.tgz#6542debd9080ad517ec5048fb454efe9e4d4aaae"
12368 integrity sha512-DqBpQ8NAUX4GyPP/ijDGHsJya4tYqLQrjPr95HNsr1YwL3+daCfvBwg7+gIC6IdJhR2kATh3hb61vjzMWEtjdw==
12369 dependencies:
12370 cliui "^3.2.0"
12371 decamelize "^1.1.1"
12372 find-up "^2.1.0"
12373 get-caller-file "^1.0.1"
12374 os-locale "^2.0.0"
12375 require-directory "^2.1.1"
12376 require-main-filename "^1.0.1"
12377 set-blocking "^2.0.0"
12378 string-width "^2.0.0"
12379 which-module "^2.0.0"
12380 y18n "^3.2.1"
12381 yargs-parser "^8.0.0"
12382
12383yargs@15.3.0: 12349yargs@15.3.0:
12384 version "15.3.0" 12350 version "15.3.0"
12385 resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.0.tgz#403af6edc75b3ae04bf66c94202228ba119f0976" 12351 resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.0.tgz#403af6edc75b3ae04bf66c94202228ba119f0976"
diff --git a/scripts/build/client.sh b/scripts/build/client.sh
index 644a0807f..9cd8d8cce 100755
--- a/scripts/build/client.sh
+++ b/scripts/build/client.sh
@@ -3,160 +3,75 @@
3set -eu 3set -eu
4 4
5declare -A languages 5declare -A languages
6defaultLanguage="en-US"
6 7
7pre_build_hook () { 8# Supported languages
8 mkdir "./src/pending_locale" > /dev/null || true 9languages=(
9 mv ./src/locale/angular.*.xlf "./src/pending_locale" 10 ["ar"]="ar"
10 11 ["en"]="en-US"
11 if [ ! -z ${1+x} ]; then 12 ["vi"]="vi-VN"
12 mv "./src/pending_locale/angular.$1.xlf" "./src/locale" 13 ["hu"]="hu-HU"
13 fi 14 ["th"]="th-TH"
14} 15 ["fi"]="fi-FI"
15 16 ["nl"]="nl-NL"
16post_build_hook () { 17 ["gd"]="gd"
17 mv ./src/pending_locale/* "./src/locale" 18 ["el"]="el-GR"
18 rmdir "./src/pending_locale/" 19 ["es"]="es-ES"
19} 20 ["oc"]="oc"
20 21 ["pt"]="pt-BR"
21# Previous build failed 22 ["pt-PT"]="pt-PT"
22if [ ! -f "client/src/locale/angular.fr-FR.xlf" ]; then 23 ["sv"]="sv-SE"
23 git checkout -- client/src/locale/ 24 ["pl"]="pl-PL"
24 rm -r client/src/pending_locale 25 ["ru"]="ru-RU"
25fi 26 ["zh-Hans"]="zh-Hans-CN"
27 ["zh-Hant"]="zh-Hant-TW"
28 ["fr"]="fr-FR"
29 ["ja"]="ja-JP"
30 ["eu"]="eu-ES"
31 ["ca"]="ca-ES"
32 ["cs"]="cs-CZ"
33 ["eo"]="eo"
34 ["de"]="de-DE"
35 ["it"]="it-IT"
36 ["kab"]="kab"
37)
26 38
27cd client 39cd client
28 40
29rm -rf ./dist ./compiled 41rm -rf ./dist ./compiled
30 42
31pre_build_hook
32
33additionalParams=""
34if [ ! -z ${1+x} ] && [ "$1" == "--analyze-bundle" ]; then
35 additionalParams="--namedChunks=true --outputHashing=none"
36 export ANALYZE_BUNDLE=true
37fi
38
39if [ ! -z ${1+x} ] && [ "$1" == "--i18n" ]; then
40 additionalParams="--configuration=i18n"
41 export ANALYZE_BUNDLE=true
42fi
43
44defaultLanguage="en-US"
45npm run ng build -- --output-path "dist/$defaultLanguage/" --deploy-url "/client/$defaultLanguage/" --prod --stats-json $additionalParams
46mv "./dist/$defaultLanguage/assets" "./dist"
47mv "./dist/$defaultLanguage/manifest.webmanifest" "./dist/manifest.webmanifest"
48
49post_build_hook
50
51# Don't build other languages if --light arg is provided 43# Don't build other languages if --light arg is provided
52if [ -z ${1+x} ] || ([ "$1" != "--light" ] && [ "$1" != "--analyze-bundle" ] && [ "$1" != "--i18n" ]); then 44if [ -z ${1+x} ] || ([ "$1" != "--light" ] && [ "$1" != "--analyze-bundle" ] && [ "$1" != "--i18n" ]); then
53 if [ ! -z ${1+x} ] && [ "$1" == "--light-hu" ]; then 45 npm run ng build -- --prod --output-path "dist/build"
54 languages=(["hu"]="hu-HU")
55 elif [ ! -z ${1+x} ] && [ "$1" == "--light-ar" ]; then
56 languages=(["ar"]="ar")
57 elif [ ! -z ${1+x} ] && [ "$1" == "--light-vi" ]; then
58 languages=(["vi"]="vi-VN")
59 elif [ ! -z ${1+x} ] && [ "$1" == "--light-kab" ]; then
60 languages=(["kab"]="kab")
61 elif [ ! -z ${1+x} ] && [ "$1" == "--light-th" ]; then
62 languages=(["th"]="th-TH")
63 elif [ ! -z ${1+x} ] && [ "$1" == "--light-fi" ]; then
64 languages=(["fi"]="fi-FI")
65 elif [ ! -z ${1+x} ] && [ "$1" == "--light-nl" ]; then
66 languages=(["nl"]="nl-NL")
67 elif [ ! -z ${1+x} ] && [ "$1" == "--light-gd" ]; then
68 languages=(["gd"]="gd")
69 elif [ ! -z ${1+x} ] && [ "$1" == "--light-el" ]; then
70 languages=(["el"]="el-GR")
71 elif [ ! -z ${1+x} ] && [ "$1" == "--light-es" ]; then
72 languages=(["es"]="es-ES")
73 elif [ ! -z ${1+x} ] && [ "$1" == "--light-oc" ]; then
74 languages=(["oc"]="oc")
75 elif [ ! -z ${1+x} ] && [ "$1" == "--light-pt" ]; then
76 languages=(["pt"]="pt-BR")
77 elif [ ! -z ${1+x} ] && [ "$1" == "--light-pt-PT" ]; then
78 languages=(["pt-PT"]="pt-PT")
79 elif [ ! -z ${1+x} ] && [ "$1" == "--light-sv" ]; then
80 languages=(["sv"]="sv-SE")
81 elif [ ! -z ${1+x} ] && [ "$1" == "--light-pl" ]; then
82 languages=(["pl"]="pl-PL")
83 elif [ ! -z ${1+x} ] && [ "$1" == "--light-ru" ]; then
84 languages=(["ru"]="ru-RU")
85 elif [ ! -z ${1+x} ] && [ "$1" == "--light-zh-Hans" ]; then
86 languages=(["zh-Hans"]="zh-Hans-CN")
87 elif [ ! -z ${1+x} ] && [ "$1" == "--light-zh-Hant" ]; then
88 languages=(["zh-Hant"]="zh-Hant-TW")
89 elif [ ! -z ${1+x} ] && [ "$1" == "--light-fr" ]; then
90 languages=(["fr"]="fr-FR")
91 elif [ ! -z ${1+x} ] && [ "$1" == "--light-ja" ]; then
92 languages=(["ja"]="ja-JP")
93 elif [ ! -z ${1+x} ] && [ "$1" == "--light-eu" ]; then
94 languages=(["eu"]="eu-ES")
95 elif [ ! -z ${1+x} ] && [ "$1" == "--light-ca" ]; then
96 languages=(["ca"]="ca-ES")
97 elif [ ! -z ${1+x} ] && [ "$1" == "--light-cs" ]; then
98 languages=(["cs"]="cs-CZ")
99 elif [ ! -z ${1+x} ] && [ "$1" == "--light-eo" ]; then
100 languages=(["eo"]="eo")
101 elif [ ! -z ${1+x} ] && [ "$1" == "--light-de" ]; then
102 languages=(["de"]="de-DE")
103 elif [ ! -z ${1+x} ] && [ "$1" == "--light-it" ]; then
104 languages=(["it"]="it-IT")
105 else
106 # Supported languages
107 languages=(
108 ["ar"]="ar"
109 ["vi"]="vi-VN"
110 ["hu"]="hu-HU"
111 ["th"]="th-TH"
112 ["fi"]="fi-FI"
113 ["nl"]="nl-NL"
114 ["gd"]="gd"
115 ["el"]="el-GR"
116 ["es"]="es-ES"
117 ["oc"]="oc"
118 ["pt"]="pt-BR"
119 ["pt-PT"]="pt-PT"
120 ["sv"]="sv-SE"
121 ["pl"]="pl-PL"
122 ["ru"]="ru-RU"
123 ["zh-Hans"]="zh-Hans-CN"
124 ["zh-Hant"]="zh-Hant-TW"
125 ["fr"]="fr-FR"
126 ["ja"]="ja-JP"
127 ["eu"]="eu-ES"
128 ["ca"]="ca-ES"
129 ["cs"]="cs-CZ"
130 ["eo"]="eo"
131 ["de"]="de-DE"
132 ["it"]="it-IT"
133 ["kab"]="kab"
134 )
135 fi
136 46
137 for key in "${!languages[@]}"; do 47 for key in "${!languages[@]}"; do
138 lang=${languages[$key]} 48 lang=${languages[$key]}
139 49
140 # TODO: remove when the project will use runtime translations 50 mv "dist/build/$key" "dist/$lang"
141 pre_build_hook "$lang"
142 51
143 npm run ng build -- --prod --configuration="$lang" --output-path "dist/build" 52 if [ "$lang" != "en-US" ]; then
53 # Do not duplicate assets
54 rm -r "./dist/$lang/assets"
55 fi
56 done
144 57
145 # If --localize is not used 58 mv "./dist/$defaultLanguage/assets" "./dist"
146 mv "dist/build/$key" "dist/$lang" 59 mv "./dist/$defaultLanguage/manifest.webmanifest" "./dist/manifest.webmanifest"
147 rmdir "dist/build"
148 60
149 # If --localize is used 61 rmdir "dist/build"
150 # if [ ! "$lang" = "$key" ]; then 62else
151 # mv "dist/$key" "dist/$lang" 63 additionalParams=""
152 # fi 64 if [ ! -z ${1+x} ] && [ "$1" == "--analyze-bundle" ]; then
65 additionalParams="--namedChunks=true --outputHashing=none"
66 export ANALYZE_BUNDLE=true
67 fi
153 68
154 # Do not duplicate assets 69 if [ ! -z ${1+x} ] && [ "$1" == "--i18n" ]; then
155 rm -r "./dist/$lang/assets" 70 additionalParams="--configuration=i18n"
71 export ANALYZE_BUNDLE=true
72 fi
156 73
157 # TODO: remove when the project will use runtime translations 74 npm run ng build -- --localize=false --output-path "dist/$defaultLanguage/" --deploy-url "/client/$defaultLanguage/" --prod --stats-json $additionalParams
158 post_build_hook
159 done
160fi 75fi
161 76
162cd ../ && npm run build:embed && cd client/ 77cd ../ && npm run build:embed && cd client/
diff --git a/scripts/i18n/generate.sh b/scripts/i18n/generate.sh
index f9c2f0613..0d1cd35c0 100755
--- a/scripts/i18n/generate.sh
+++ b/scripts/i18n/generate.sh
@@ -2,9 +2,13 @@
2 2
3set -eu 3set -eu
4 4
5npm run build -- --i18n
6
5cd client 7cd client
6npm run ng -- xi18n --i18n-locale "en-US" --output-path src/locale --out-file angular.xlf 8./node_modules/.bin/localize-extract -r . -f xliff --locale "en-US" -s 'dist/en-US/*.js' -o src/locale/angular.xlf
7npm run ngx-extractor -- --locale "en-US" -i 'src/**/*.ts' -f xlf -o src/locale/angular.xlf 9
10# Workaround of https://github.com/angular/angular/issues/38437
11sed -i 's/other {{INTERPOLATION}/other {<x id="INTERPOLATION"\/>/g' src/locale/angular.xlf
8 12
9# Merge new translations in other language files 13# Merge new translations in other language files
10npm run ng run -- PeerTube:xliffmerge 14npm run ng run -- PeerTube:xliffmerge