aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/shared
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/shared')
-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
54 files changed, 430 insertions, 574 deletions
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 () {