Add i18n attributes

This commit is contained in:
Chocobozzz 2018-06-04 16:21:17 +02:00
parent 989e526abf
commit b1d40cff89
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
96 changed files with 3107 additions and 586 deletions

View File

@ -1,12 +1,12 @@
<div *ngIf="account" class="row"> <div *ngIf="account" class="row">
<div class="block col-md-6 col-sm-12"> <div class="block col-md-6 col-sm-12">
<div class="small-title">Description</div> <div i18n class="small-title">Description</div>
<div class="content">{{ getAccountDescription() }}</div> <div class="content">{{ getAccountDescription() }}</div>
</div> </div>
<div class="block col-md-6 col-sm-12"> <div class="block col-md-6 col-sm-12">
<div class="small-title">Stats</div> <div i18n class="small-title">Stats</div>
<div class="content">Joined {{ account.createdAt | date }}</div> <div i18n class="content">Joined {{ account.createdAt | date }}</div>
</div> </div>
</div> </div>

View File

@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
import { Account } from '@app/shared/account/account.model' import { Account } from '@app/shared/account/account.model'
import { AccountService } from '@app/shared/account/account.service' import { AccountService } from '@app/shared/account/account.service'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-account-about', selector: 'my-account-about',
@ -12,7 +13,8 @@ export class AccountAboutComponent implements OnInit {
account: Account account: Account
constructor ( constructor (
protected route: ActivatedRoute, private route: ActivatedRoute,
private i18n: I18n,
private accountService: AccountService private accountService: AccountService
) { } ) { }
@ -25,6 +27,6 @@ export class AccountAboutComponent implements OnInit {
getAccountDescription () { getAccountDescription () {
if (this.account.description) return this.account.description if (this.account.description) return this.account.description
return 'No description' return this.i18n('No description')
} }
} }

View File

@ -1,11 +1,11 @@
<div *ngIf="account" class="row"> <div *ngIf="account" class="row">
<a <a
*ngFor="let videoChannel of videoChannels" [routerLink]="[ '/video-channels', videoChannel.uuid ]" *ngFor="let videoChannel of videoChannels" [routerLink]="[ '/video-channels', videoChannel.uuid ]"
class="video-channel" title="See this video channel" class="video-channel" i18n-title title="See this video channel"
> >
<img [src]="videoChannel.avatarUrl" alt="Avatar" /> <img [src]="videoChannel.avatarUrl" alt="Avatar" />
<div class="video-channel-display-name">{{ videoChannel.displayName }}</div> <div class="video-channel-display-name">{{ videoChannel.displayName }}</div>
<div class="video-channel-followers">{{ videoChannel.followersCount }} subscribers</div> <div i18n class="video-channel-followers">{{ videoChannel.followersCount }} subscribers</div>
</a> </a>
</div> </div>

View File

@ -10,6 +10,7 @@ import { VideoService } from '../../shared/video/video.service'
import { Account } from '@app/shared/account/account.model' import { Account } from '@app/shared/account/account.model'
import { AccountService } from '@app/shared/account/account.service' import { AccountService } from '@app/shared/account/account.service'
import { tap } from 'rxjs/operators' import { tap } from 'rxjs/operators'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-account-videos', selector: 'my-account-videos',
@ -20,7 +21,7 @@ import { tap } from 'rxjs/operators'
] ]
}) })
export class AccountVideosComponent extends AbstractVideoList implements OnInit, OnDestroy { export class AccountVideosComponent extends AbstractVideoList implements OnInit, OnDestroy {
titlePage = 'Published videos' titlePage: string
marginContent = false // Disable margin marginContent = false // Disable margin
currentRoute = '/account/videos' currentRoute = '/account/videos'
loadOnInit = false loadOnInit = false
@ -34,10 +35,13 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit,
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected confirmService: ConfirmService, protected confirmService: ConfirmService,
protected location: Location, protected location: Location,
protected i18n: I18n,
private accountService: AccountService, private accountService: AccountService,
private videoService: VideoService private videoService: VideoService
) { ) {
super() super()
this.titlePage = this.i18n('Published videos')
} }
ngOnInit () { ngOnInit () {
@ -63,7 +67,11 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit,
return this.videoService return this.videoService
.getAccountVideos(this.account, newPagination, this.sort) .getAccountVideos(this.account, newPagination, this.sort)
.pipe(tap(({ totalVideos }) => this.titlePage = `Published ${totalVideos} videos`)) .pipe(
tap(({ totalVideos }) => {
this.titlePage = this.i18n('Published {{ totalVideos }} videos', { totalVideos })
})
)
} }
generateSyndicationList () { generateSyndicationList () {

View File

@ -9,16 +9,16 @@
<div class="actor-display-name">{{ account.displayName }}</div> <div class="actor-display-name">{{ account.displayName }}</div>
<div class="actor-name">{{ account.nameWithHost }}</div> <div class="actor-name">{{ account.nameWithHost }}</div>
</div> </div>
<div class="actor-followers">{{ account.followersCount }} subscribers</div> <div i18n class="actor-followers">{{ account.followersCount }} subscribers</div>
</div> </div>
</div> </div>
<div class="links"> <div class="links">
<a routerLink="videos" routerLinkActive="active" class="title-page">Videos</a> <a i18n routerLink="videos" routerLinkActive="active" class="title-page">Videos</a>
<a routerLink="video-channels" routerLinkActive="active" class="title-page">Video channels</a> <a i18n routerLink="video-channels" routerLinkActive="active" class="title-page">Video channels</a>
<a routerLink="about" routerLinkActive="active" class="title-page">About</a> <a i18n routerLink="about" routerLinkActive="active" class="title-page">About</a>
</div> </div>
</div> </div>

View File

@ -1,26 +1,26 @@
<div class="row"> <div class="row">
<div class="sub-menu"> <div class="sub-menu">
<a *ngIf="hasUsersRight()" routerLink="/admin/users" routerLinkActive="active" class="title-page"> <a i18n *ngIf="hasUsersRight()" routerLink="/admin/users" routerLinkActive="active" class="title-page">
Users Users
</a> </a>
<a *ngIf="hasServerFollowRight()" routerLink="/admin/follows" routerLinkActive="active" class="title-page"> <a i18n *ngIf="hasServerFollowRight()" routerLink="/admin/follows" routerLinkActive="active" class="title-page">
Manage follows Manage follows
</a> </a>
<a *ngIf="hasVideoAbusesRight()" routerLink="/admin/video-abuses" routerLinkActive="active" class="title-page"> <a i18n *ngIf="hasVideoAbusesRight()" routerLink="/admin/video-abuses" routerLinkActive="active" class="title-page">
Video abuses Video abuses
</a> </a>
<a *ngIf="hasVideoBlacklistRight()" routerLink="/admin/video-blacklist" routerLinkActive="active" class="title-page"> <a i18n *ngIf="hasVideoBlacklistRight()" routerLink="/admin/video-blacklist" routerLinkActive="active" class="title-page">
Video blacklist Video blacklist
</a> </a>
<a *ngIf="hasJobsRight()" routerLink="/admin/jobs" routerLinkActive="active" class="title-page"> <a i18n *ngIf="hasJobsRight()" routerLink="/admin/jobs" routerLinkActive="active" class="title-page">
Jobs Jobs
</a> </a>
<a *ngIf="hasConfigRight()" routerLink="/admin/config" routerLinkActive="active" class="title-page"> <a i18n *ngIf="hasConfigRight()" routerLink="/admin/config" routerLinkActive="active" class="title-page">
Configuration Configuration
</a> </a>
</div> </div>

View File

@ -4,13 +4,13 @@
<tab heading="Basic configuration"> <tab heading="Basic configuration">
<div class="inner-form-title">Instance</div> <div i18n class="inner-form-title">Instance</div>
<div class="form-group"> <div class="form-group">
<label for="instanceName">Name</label> <label i18n for="instanceName">Name</label>
<input <input
type="text" id="instanceName" type="text" id="instanceName"
formControlName="instanceName" [ngClass]="{ 'input-error': formErrors['instanceName'] }" formControlName="instanceName" [ngClass]="{ 'input-error': formErrors['instanceName'] }"
> >
<div *ngIf="formErrors.instanceName" class="form-error"> <div *ngIf="formErrors.instanceName" class="form-error">
{{ formErrors.instanceName }} {{ formErrors.instanceName }}
@ -18,10 +18,10 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="instanceShortDescription">Short description</label> <label i18n for="instanceShortDescription">Short description</label>
<textarea <textarea
id="instanceShortDescription" formControlName="instanceShortDescription" id="instanceShortDescription" formControlName="instanceShortDescription"
[ngClass]="{ 'input-error': formErrors['instanceShortDescription'] }" [ngClass]="{ 'input-error': formErrors['instanceShortDescription'] }"
></textarea> ></textarea>
<div *ngIf="formErrors.instanceShortDescription" class="form-error"> <div *ngIf="formErrors.instanceShortDescription" class="form-error">
{{ formErrors.instanceShortDescription }} {{ formErrors.instanceShortDescription }}
@ -29,10 +29,10 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help> <label i18n for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help>
<my-markdown-textarea <my-markdown-textarea
id="instanceDescription" formControlName="instanceDescription" textareaWidth="500px" [previewColumn]="true" id="instanceDescription" formControlName="instanceDescription" textareaWidth="500px" [previewColumn]="true"
[classes]="{ 'input-error': formErrors['instanceDescription'] }" [classes]="{ 'input-error': formErrors['instanceDescription'] }"
></my-markdown-textarea> ></my-markdown-textarea>
<div *ngIf="formErrors.instanceDescription" class="form-error"> <div *ngIf="formErrors.instanceDescription" class="form-error">
{{ formErrors.instanceDescription }} {{ formErrors.instanceDescription }}
@ -40,10 +40,10 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help> <label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help>
<my-markdown-textarea <my-markdown-textarea
id="instanceTerms" formControlName="instanceTerms" textareaWidth="500px" [previewColumn]="true" id="instanceTerms" formControlName="instanceTerms" textareaWidth="500px" [previewColumn]="true"
[ngClass]="{ 'input-error': formErrors['instanceTerms'] }" [ngClass]="{ 'input-error': formErrors['instanceTerms'] }"
></my-markdown-textarea> ></my-markdown-textarea>
<div *ngIf="formErrors.instanceTerms" class="form-error"> <div *ngIf="formErrors.instanceTerms" class="form-error">
{{ formErrors.instanceTerms }} {{ formErrors.instanceTerms }}
@ -51,12 +51,12 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="instanceDefaultClientRoute">Default client route</label> <label i18n for="instanceDefaultClientRoute">Default client route</label>
<div class="peertube-select-container"> <div class="peertube-select-container">
<select id="instanceDefaultClientRoute" formControlName="instanceDefaultClientRoute"> <select id="instanceDefaultClientRoute" formControlName="instanceDefaultClientRoute">
<option value="/videos/trending">Videos Trending</option> <option i18n value="/videos/trending">Videos Trending</option>
<option value="/videos/recently-added">Videos Recently Added</option> <option i18n value="/videos/recently-added">Videos Recently Added</option>
<option value="/videos/local">Local videos</option> <option i18n value="/videos/local">Local videos</option>
</select> </select>
</div> </div>
<div *ngIf="formErrors.instanceDefaultClientRoute" class="form-error"> <div *ngIf="formErrors.instanceDefaultClientRoute" class="form-error">
@ -65,14 +65,17 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label> <label i18n for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label>
<my-help helpType="custom" customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."></my-help> <my-help
helpType="custom" i18n-customHtml
customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."
></my-help>
<div class="peertube-select-container"> <div class="peertube-select-container">
<select id="instanceDefaultNSFWPolicy" formControlName="instanceDefaultNSFWPolicy"> <select id="instanceDefaultNSFWPolicy" formControlName="instanceDefaultNSFWPolicy">
<option value="do_not_list">Do not list</option> <option i18n value="do_not_list">Do not list</option>
<option value="blur">Blur thumbnails</option> <option i18n value="blur">Blur thumbnails</option>
<option value="display">Display</option> <option i18n value="display">Display</option>
</select> </select>
</div> </div>
<div *ngIf="formErrors.instanceDefaultNSFWPolicy" class="form-error"> <div *ngIf="formErrors.instanceDefaultNSFWPolicy" class="form-error">
@ -80,43 +83,43 @@
</div> </div>
</div> </div>
<div class="inner-form-title">Signup</div> <div i18n class="inner-form-title">Signup</div>
<div class="form-group"> <div class="form-group">
<input type="checkbox" id="signupEnabled" formControlName="signupEnabled"> <input type="checkbox" id="signupEnabled" formControlName="signupEnabled">
<label for="signupEnabled"></label> <label for="signupEnabled"></label>
<label for="signupEnabled">Signup enabled</label> <label i18n for="signupEnabled">Signup enabled</label>
</div> </div>
<div *ngIf="isSignupEnabled()" class="form-group"> <div *ngIf="isSignupEnabled()" class="form-group">
<label for="signupLimit">Signup limit</label> <label i18n for="signupLimit">Signup limit</label>
<input <input
type="text" id="signupLimit" type="text" id="signupLimit"
formControlName="signupLimit" [ngClass]="{ 'input-error': formErrors['signupLimit'] }" formControlName="signupLimit" [ngClass]="{ 'input-error': formErrors['signupLimit'] }"
> >
<div *ngIf="formErrors.signupLimit" class="form-error"> <div *ngIf="formErrors.signupLimit" class="form-error">
{{ formErrors.signupLimit }} {{ formErrors.signupLimit }}
</div> </div>
</div> </div>
<div class="inner-form-title">Administrator</div> <div i18n class="inner-form-title">Administrator</div>
<div class="form-group"> <div class="form-group">
<label for="adminEmail">Admin email</label> <label i18n for="adminEmail">Admin email</label>
<input <input
type="text" id="adminEmail" type="text" id="adminEmail"
formControlName="adminEmail" [ngClass]="{ 'input-error': formErrors['adminEmail'] }" formControlName="adminEmail" [ngClass]="{ 'input-error': formErrors['adminEmail'] }"
> >
<div *ngIf="formErrors.adminEmail" class="form-error"> <div *ngIf="formErrors.adminEmail" class="form-error">
{{ formErrors.adminEmail }} {{ formErrors.adminEmail }}
</div> </div>
</div> </div>
<div class="inner-form-title">Users</div> <div i18n class="inner-form-title">Users</div>
<div class="form-group"> <div class="form-group">
<label for="userVideoQuota">User default video quota</label> <label i18n for="userVideoQuota">User default video quota</label>
<div class="peertube-select-container"> <div class="peertube-select-container">
<select id="userVideoQuota" formControlName="userVideoQuota"> <select id="userVideoQuota" formControlName="userVideoQuota">
<option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value"> <option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value">
@ -132,14 +135,17 @@
<tab heading="Services"> <tab heading="Services">
<div class="inner-form-title">Twitter</div> <div i18n class="inner-form-title">Twitter</div>
<div class="form-group"> <div class="form-group">
<label for="signupLimit">Your Twitter username</label> <label i18n for="signupLimit">Your Twitter username</label>
<my-help helpType="custom" customHtml="Indicates the Twitter account for the website or platform on which the content was published."></my-help> <my-help
helpType="custom" i18n-customHtml
customHtml="Indicates the Twitter account for the website or platform on which the content was published."
></my-help>
<input <input
type="text" id="servicesTwitterUsername" type="text" id="servicesTwitterUsername"
formControlName="servicesTwitterUsername" [ngClass]="{ 'input-error': formErrors['servicesTwitterUsername'] }" formControlName="servicesTwitterUsername" [ngClass]="{ 'input-error': formErrors['servicesTwitterUsername'] }"
> >
<div *ngIf="formErrors.servicesTwitterUsername" class="form-error"> <div *ngIf="formErrors.servicesTwitterUsername" class="form-error">
{{ formErrors.servicesTwitterUsername }} {{ formErrors.servicesTwitterUsername }}
@ -150,29 +156,32 @@
<input type="checkbox" id="servicesTwitterWhitelisted" formControlName="servicesTwitterWhitelisted"> <input type="checkbox" id="servicesTwitterWhitelisted" formControlName="servicesTwitterWhitelisted">
<label for="servicesTwitterWhitelisted"></label> <label for="servicesTwitterWhitelisted"></label>
<label for="servicesTwitterWhitelisted">Instance whitelisted by Twitter</label> <label i18n for="servicesTwitterWhitelisted">Instance whitelisted by Twitter</label>
<my-help helpType="custom" customHtml="If your instance is whitelisted by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br /> <my-help
helpType="custom" i18n-customHtml
customHtml="If your instance is whitelisted by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br />
If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br /> If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br />
Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on <a target='_blank' rel='noopener noreferrer' href='https://cards-dev.twitter.com/validator'>https://cards-dev.twitter.com/validator</a> to see if you instance is whitelisted."></my-help> Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on <a target='_blank' rel='noopener noreferrer' href='https://cards-dev.twitter.com/validator'>https://cards-dev.twitter.com/validator</a> to see if you instance is whitelisted."
></my-help>
</div> </div>
</tab> </tab>
<tab heading="Advanced configuration"> <tab heading="Advanced configuration">
<div class="inner-form-title">Transcoding</div> <div i18n class="inner-form-title">Transcoding</div>
<div class="form-group"> <div class="form-group">
<input type="checkbox" id="transcodingEnabled" formControlName="transcodingEnabled"> <input type="checkbox" id="transcodingEnabled" formControlName="transcodingEnabled">
<label for="transcodingEnabled"></label> <label for="transcodingEnabled"></label>
<label for="transcodingEnabled">Transcoding enabled</label> <label i18n for="transcodingEnabled">Transcoding enabled</label>
</div> </div>
<ng-template [ngIf]="isTranscodingEnabled()"> <ng-template [ngIf]="isTranscodingEnabled()">
<div class="form-group"> <div class="form-group">
<label for="transcodingThreads">Transcoding threads</label> <label i18n for="transcodingThreads">Transcoding threads</label>
<div class="peertube-select-container"> <div class="peertube-select-container">
<select id="transcodingThreads" formControlName="transcodingThreads"> <select id="transcodingThreads" formControlName="transcodingThreads">
<option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value"> <option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value">
@ -191,33 +200,39 @@ Check this checkbox, save the configuration and test with a video URL of your in
[formControlName]="getResolutionKey(resolution)" [formControlName]="getResolutionKey(resolution)"
> >
<label [for]="getResolutionKey(resolution)"></label> <label [for]="getResolutionKey(resolution)"></label>
<label [for]="getResolutionKey(resolution)">Resolution {{ resolution }} enabled</label> <label i18n [for]="getResolutionKey(resolution)">Resolution {{ resolution }} enabled</label>
</div> </div>
</ng-template> </ng-template>
<div class="inner-form-title">Cache</div> <div i18n class="inner-form-title">Cache</div>
<div class="form-group"> <div class="form-group">
<label for="cachePreviewsSize">Preview cache size</label> <label i18n for="cachePreviewsSize">Preview cache size</label>
<my-help helpType="custom" customHtml="Previews are not federated. We fetch them directly from the origin instance and cache them."></my-help> <my-help
helpType="custom" i18n-customHtml
customHtml="Previews are not federated. We fetch them directly from the origin instance and cache them."
></my-help>
<input <input
type="text" id="cachePreviewsSize" type="text" id="cachePreviewsSize"
formControlName="cachePreviewsSize" [ngClass]="{ 'input-error': formErrors['cachePreviewsSize'] }" formControlName="cachePreviewsSize" [ngClass]="{ 'input-error': formErrors['cachePreviewsSize'] }"
> >
<div *ngIf="formErrors.cachePreviewsSize" class="form-error"> <div *ngIf="formErrors.cachePreviewsSize" class="form-error">
{{ formErrors.cachePreviewsSize }} {{ formErrors.cachePreviewsSize }}
</div> </div>
</div> </div>
<div class="inner-form-title">Customizations</div> <div i18n class="inner-form-title">Customizations</div>
<div class="form-group"> <div class="form-group">
<label for="customizationJavascript">JavaScript</label> <label i18n for="customizationJavascript">JavaScript</label>
<my-help helpType="custom" customHtml="Write directly JavaScript code.<br />Example: <pre>console.log('my instance is amazing');</pre>"></my-help> <my-help
helpType="custom" i18n-customHtml
customHtml="Write directly JavaScript code.<br />Example: <pre>console.log('my instance is amazing');</pre>"
></my-help>
<textarea <textarea
id="customizationJavascript" formControlName="customizationJavascript" id="customizationJavascript" formControlName="customizationJavascript"
[ngClass]="{ 'input-error': formErrors['customizationJavascript'] }" [ngClass]="{ 'input-error': formErrors['customizationJavascript'] }"
></textarea> ></textarea>
<div *ngIf="formErrors.customizationJavascript" class="form-error"> <div *ngIf="formErrors.customizationJavascript" class="form-error">
{{ formErrors.customizationJavascript }} {{ formErrors.customizationJavascript }}
@ -228,25 +243,26 @@ Check this checkbox, save the configuration and test with a video URL of your in
<label for="customizationCSS">CSS</label> <label for="customizationCSS">CSS</label>
<my-help <my-help
helpType="custom" helpType="custom"
i18n-customHtml
customHtml=" customHtml="
Write directly CSS code. Example:<br /> Write directly CSS code. Example:<br />
<pre> <pre>
body { body {{ '{' }}
background-color: red; background-color: red;
} {{ '}' }}
</pre> </pre>
Prepend with <em>#custom-css</em> to override styles. Example: Prepend with <em>#custom-css</em> to override styles. Example:
<pre> <pre>
#custom-css .logged-in-email { #custom-css .logged-in-email {{ '{' }}
color: red; color: red;
} {{ '}' }}
</pre> </pre>
" "
></my-help> ></my-help>
<textarea <textarea
id="customizationCSS" formControlName="customizationCSS" id="customizationCSS" formControlName="customizationCSS"
[ngClass]="{ 'input-error': formErrors['customizationCSS'] }" [ngClass]="{ 'input-error': formErrors['customizationCSS'] }"
></textarea> ></textarea>
<div *ngIf="formErrors.customizationCSS" class="form-error"> <div *ngIf="formErrors.customizationCSS" class="form-error">
{{ formErrors.customizationCSS }} {{ formErrors.customizationCSS }}
@ -255,5 +271,5 @@ Check this checkbox, save the configuration and test with a video URL of your in
</tab> </tab>
</tabset> </tabset>
<input (click)="formValidated()" type="submit" value="Update configuration" [disabled]="!form.valid"> <input (click)="formValidated()" type="submit" i18n-value value="Update configuration" [disabled]="!form.valid">
</form> </form>

View File

@ -8,12 +8,15 @@ import { FormReactive, USER_VIDEO_QUOTA } from '@app/shared'
import { import {
ADMIN_EMAIL, ADMIN_EMAIL,
CACHE_PREVIEWS_SIZE, CACHE_PREVIEWS_SIZE,
INSTANCE_NAME, INSTANCE_SHORT_DESCRIPTION, SERVICES_TWITTER_USERNAME, INSTANCE_NAME,
INSTANCE_SHORT_DESCRIPTION,
SERVICES_TWITTER_USERNAME,
SIGNUP_LIMIT, SIGNUP_LIMIT,
TRANSCODING_THREADS TRANSCODING_THREADS
} from '@app/shared/forms/form-validators/custom-config' } from '@app/shared/forms/form-validators/custom-config'
import { NotificationsService } from 'angular2-notifications' import { NotificationsService } from 'angular2-notifications'
import { CustomConfig } from '../../../../../../shared/models/server/custom-config.model' import { CustomConfig } from '../../../../../../shared/models/server/custom-config.model'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-edit-custom-config', selector: 'my-edit-custom-config',
@ -77,7 +80,8 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private configService: ConfigService, private configService: ConfigService,
private serverService: ServerService, private serverService: ServerService,
private confirmService: ConfirmService private confirmService: ConfirmService,
private i18n: I18n
) { ) {
super() super()
} }
@ -133,7 +137,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
this.forceCheck() this.forceCheck()
}, },
err => this.notificationsService.error('Error', err.message) err => this.notificationsService.error(this.i18n('Error'), err.message)
) )
} }
@ -156,11 +160,15 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
if (customizations.length !== 0) { if (customizations.length !== 0) {
const customizationsText = customizations.join('/') const customizationsText = customizations.join('/')
const message = `You set custom ${customizationsText}. ` + // FIXME: i18n service does not support string concatenation
'This could lead to security issues or bugs if you do not understand it. ' + const message = this.i18n('You set custom {{ customizationsText }}. ', { customizationsText }) +
'Are you sure you want to update the configuration?' this.i18n('This could lead to security issues or bugs if you do not understand it. ') +
const label = `Please type "I understand the ${customizationsText} I set" to confirm.` this.i18n('Are you sure you want to update the configuration?')
const expectedInputValue = `I understand the ${customizationsText} I set` const label = this.i18n(
'Please type "I understand the {{ customizationsText }} I set" to confirm.',
{ customizationsText }
)
const expectedInputValue = this.i18n('I understand the {{ customizationsText }} I set', { customizationsText})
const confirmRes = await this.confirmService.confirmWithInput(message, label, expectedInputValue) const confirmRes = await this.confirmService.confirmWithInput(message, label, expectedInputValue)
if (confirmRes === false) return if (confirmRes === false) return
@ -223,10 +231,10 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
this.updateForm() this.updateForm()
this.notificationsService.success('Success', 'Configuration updated.') this.notificationsService.success(this.i18n('Success'), this.i18n('Configuration updated.'))
}, },
err => this.notificationsService.error('Error', err.message) err => this.notificationsService.error(this.i18n('Error'), err.message)
) )
} }

View File

@ -4,12 +4,12 @@
> >
<ng-template pTemplate="header"> <ng-template pTemplate="header">
<tr> <tr>
<th style="width: 60px">ID</th> <th i18n style="width: 60px">ID</th>
<th>Score</th> <th i18n>Score</th>
<th>Name</th> <th i18n>Name</th>
<th>Host</th> <th i18n>Host</th>
<th>State</th> <th i18n>State</th>
<th pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> <th i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
</tr> </tr>
</ng-template> </ng-template>

View File

@ -5,6 +5,7 @@ import { SortMeta } from 'primeng/primeng'
import { AccountFollow } from '../../../../../../shared/models/actors/follow.model' import { AccountFollow } from '../../../../../../shared/models/actors/follow.model'
import { RestPagination, RestTable } from '../../../shared' import { RestPagination, RestTable } from '../../../shared'
import { FollowService } from '../shared' import { FollowService } from '../shared'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-followers-list', selector: 'my-followers-list',
@ -20,7 +21,8 @@ export class FollowersListComponent extends RestTable implements OnInit {
constructor ( constructor (
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private followService: FollowService private followService: FollowService,
private i18n: I18n
) { ) {
super() super()
} }
@ -37,7 +39,7 @@ export class FollowersListComponent extends RestTable implements OnInit {
this.totalRecords = resultList.total this.totalRecords = resultList.total
}, },
err => this.notificationsService.error('Error', err.message) err => this.notificationsService.error(this.i18n('Error'), err.message)
) )
} }
} }

View File

@ -2,7 +2,7 @@
<form (ngSubmit)="addFollowing()"> <form (ngSubmit)="addFollowing()">
<div class="form-group"> <div class="form-group">
<label for="hosts">1 host (without "http://") per line</label> <label i18n for="hosts">1 host (without "http://") per line</label>
<textarea <textarea
type="text" class="form-control" placeholder="example.com" id="hosts" name="hosts" type="text" class="form-control" placeholder="example.com" id="hosts" name="hosts"
@ -14,9 +14,9 @@
</div> </div>
</div> </div>
<div *ngIf="httpEnabled() === false" class="alert alert-warning"> <div i18n *ngIf="httpEnabled() === false" class="alert alert-warning">
It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers. It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
</div> </div>
<input type="submit" value="Add following" [disabled]="hostsError || !hostsString" class="btn btn-default"> <input type="submit" i18n-value value="Add following" [disabled]="hostsError || !hostsString" class="btn btn-default">
</form> </form>

View File

@ -4,6 +4,7 @@ import { NotificationsService } from 'angular2-notifications'
import { ConfirmService } from '../../../core' import { ConfirmService } from '../../../core'
import { validateHost } from '../../../shared' import { validateHost } from '../../../shared'
import { FollowService } from '../shared' import { FollowService } from '../shared'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-following-add', selector: 'my-following-add',
@ -19,7 +20,8 @@ export class FollowingAddComponent {
private router: Router, private router: Router,
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private confirmService: ConfirmService, private confirmService: ConfirmService,
private followService: FollowService private followService: FollowService,
private i18n: I18n
) {} ) {}
httpEnabled () { httpEnabled () {
@ -34,7 +36,7 @@ export class FollowingAddComponent {
for (const host of hosts) { for (const host of hosts) {
if (validateHost(host) === false) { if (validateHost(host) === false) {
newHostsErrors.push(`${host} is not valid`) newHostsErrors.push(this.i18n('{{ host }} is not valid', { host }))
} }
} }
@ -48,26 +50,26 @@ export class FollowingAddComponent {
const hosts = this.getNotEmptyHosts() const hosts = this.getNotEmptyHosts()
if (hosts.length === 0) { if (hosts.length === 0) {
this.error = 'You need to specify hosts to follow.' this.error = this.i18n('You need to specify hosts to follow.')
} }
if (!this.isHostsUnique(hosts)) { if (!this.isHostsUnique(hosts)) {
this.error = 'Hosts need to be unique.' this.error = this.i18n('Hosts need to be unique.')
return return
} }
const confirmMessage = 'If you confirm, you will send a follow request to:<br /> - ' + hosts.join('<br /> - ') const confirmMessage = this.i18n('If you confirm, you will send a follow request to:<br /> - ') + hosts.join('<br /> - ')
const res = await this.confirmService.confirm(confirmMessage, 'Follow new server(s)') const res = await this.confirmService.confirm(confirmMessage, this.i18n('Follow new server(s)'))
if (res === false) return if (res === false) return
this.followService.follow(hosts).subscribe( this.followService.follow(hosts).subscribe(
() => { () => {
this.notificationsService.success('Success', 'Follow request(s) sent!') this.notificationsService.success(this.i18n('Success'), this.i18n('Follow request(s) sent!'))
setTimeout(() => this.router.navigate([ '/admin/follows/following-list' ]), 500) setTimeout(() => this.router.navigate([ '/admin/follows/following-list' ]), 500)
}, },
err => this.notificationsService.error('Error', err.message) err => this.notificationsService.error(this.i18n('Error'), err.message)
) )
} }
@ -76,10 +78,8 @@ export class FollowingAddComponent {
} }
private getNotEmptyHosts () { private getNotEmptyHosts () {
const hosts = this.hostsString return this.hostsString
.split('\n') .split('\n')
.filter(host => host && host.length !== 0) // Eject empty hosts .filter(host => host && host.length !== 0) // Eject empty hosts
return hosts
} }
} }

View File

@ -4,10 +4,10 @@
> >
<ng-template pTemplate="header"> <ng-template pTemplate="header">
<tr> <tr>
<th style="width: 60px">ID</th> <th i18n style="width: 60px">ID</th>
<th>Host</th> <th i18n>Host</th>
<th>State</th> <th i18n>State</th>
<th pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> <th i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
<th></th> <th></th>
</tr> </tr>
</ng-template> </ng-template>

View File

@ -5,6 +5,7 @@ import { AccountFollow } from '../../../../../../shared/models/actors/follow.mod
import { ConfirmService } from '../../../core/confirm/confirm.service' import { ConfirmService } from '../../../core/confirm/confirm.service'
import { RestPagination, RestTable } from '../../../shared' import { RestPagination, RestTable } from '../../../shared'
import { FollowService } from '../shared' import { FollowService } from '../shared'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-followers-list', selector: 'my-followers-list',
@ -20,7 +21,8 @@ export class FollowingListComponent extends RestTable implements OnInit {
constructor ( constructor (
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private confirmService: ConfirmService, private confirmService: ConfirmService,
private followService: FollowService private followService: FollowService,
private i18n: I18n
) { ) {
super() super()
} }
@ -30,16 +32,22 @@ export class FollowingListComponent extends RestTable implements OnInit {
} }
async removeFollowing (follow: AccountFollow) { async removeFollowing (follow: AccountFollow) {
const res = await this.confirmService.confirm(`Do you really want to unfollow ${follow.following.host}?`, 'Unfollow') const res = await this.confirmService.confirm(
this.i18n('Do you really want to unfollow {{ host }}?', { host: follow.following.host }),
this.i18n('Unfollow')
)
if (res === false) return if (res === false) return
this.followService.unfollow(follow).subscribe( this.followService.unfollow(follow).subscribe(
() => { () => {
this.notificationsService.success('Success', `You are not following ${follow.following.host} anymore.`) this.notificationsService.success(
this.i18n('Success'),
this.i18n('You are not following {{ host }} anymore.', { host: follow.following.host })
)
this.loadData() this.loadData()
}, },
err => this.notificationsService.error('Error', err.message) err => this.notificationsService.error(this.i18n('Error'), err.message)
) )
} }
@ -51,7 +59,7 @@ export class FollowingListComponent extends RestTable implements OnInit {
this.totalRecords = resultList.total this.totalRecords = resultList.total
}, },
err => this.notificationsService.error('Error', err.message) err => this.notificationsService.error(this.i18n('Error'), err.message)
) )
} }
} }

View File

@ -1,5 +1,5 @@
<div class="admin-sub-header"> <div class="admin-sub-header">
<div class="form-sub-title">Manage follows</div> <div i18n class="form-sub-title">Manage follows</div>
<tabset #followsMenuTabs> <tabset #followsMenuTabs>
<tab *ngFor="let link of links"> <tab *ngFor="let link of links">

View File

@ -1 +1,4 @@
export * from './' export * from './shared'
export * from './jobs-list'
export * from './job.routes'
export * from './job.component'

View File

@ -1,5 +1,5 @@
<div class="admin-sub-header"> <div class="admin-sub-header">
<div class="form-sub-title">Jobs list</div> <div i18n class="form-sub-title">Jobs list</div>
<div class="peertube-select-container"> <div class="peertube-select-container">
<select [(ngModel)]="jobState" (ngModelChange)="onJobStateChanged()"> <select [(ngModel)]="jobState" (ngModelChange)="onJobStateChanged()">
@ -15,11 +15,11 @@
<ng-template pTemplate="header"> <ng-template pTemplate="header">
<tr> <tr>
<th style="width: 27px"></th> <th style="width: 27px"></th>
<th style="width: 60px">ID</th> <th i18n style="width: 60px">ID</th>
<th style="width: 210px">Type</th> <th i18n style="width: 210px">Type</th>
<th style="width: 130px">State</th> <th i18n style="width: 130px">State</th>
<th style="width: 250px" pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> <th i18n style="width: 250px" pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
<th style="width: 250px">Updated</th> <th i18n style="width: 250px">Updated</th>
</tr> </tr>
</ng-template> </ng-template>

View File

@ -7,6 +7,7 @@ import { JobState } from '../../../../../../shared/models'
import { RestPagination, RestTable } from '../../../shared' import { RestPagination, RestTable } from '../../../shared'
import { RestExtractor } from '../../../shared/rest/rest-extractor.service' import { RestExtractor } from '../../../shared/rest/rest-extractor.service'
import { JobService } from '../shared' import { JobService } from '../shared'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-jobs-list', selector: 'my-jobs-list',
@ -27,7 +28,8 @@ export class JobsListComponent extends RestTable implements OnInit {
constructor ( constructor (
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private restExtractor: RestExtractor, private restExtractor: RestExtractor,
private jobsService: JobService private jobsService: JobService,
private i18n: I18n
) { ) {
super() super()
} }
@ -51,7 +53,7 @@ export class JobsListComponent extends RestTable implements OnInit {
this.totalRecords = resultList.total this.totalRecords = resultList.total
}, },
err => this.notificationsService.error('Error', err.message) err => this.notificationsService.error(this.i18n('Error'), err.message)
) )
} }

View File

@ -7,6 +7,7 @@ import { Observable } from 'rxjs'
import { ResultList, UserCreate, UserUpdate } from '../../../../../../shared' import { ResultList, UserCreate, UserUpdate } from '../../../../../../shared'
import { environment } from '../../../../environments/environment' import { environment } from '../../../../environments/environment'
import { RestExtractor, RestPagination, RestService, User } from '../../../shared' import { RestExtractor, RestPagination, RestService, User } from '../../../shared'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Injectable() @Injectable()
export class UserService { export class UserService {
@ -16,9 +17,9 @@ export class UserService {
constructor ( constructor (
private authHttp: HttpClient, private authHttp: HttpClient,
private restService: RestService, private restService: RestService,
private restExtractor: RestExtractor private restExtractor: RestExtractor,
) { private i18n: I18n
} ) { }
addUser (userCreate: UserCreate) { addUser (userCreate: UserCreate) {
return this.authHttp.post(UserService.BASE_USERS_URL, userCreate) return this.authHttp.post(UserService.BASE_USERS_URL, userCreate)

View File

@ -1,20 +1,13 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit } from '@angular/core'
import { FormBuilder, FormGroup } from '@angular/forms' import { FormBuilder, FormGroup } from '@angular/forms'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { NotificationsService } from 'angular2-notifications' import { NotificationsService } from 'angular2-notifications'
import { UserService } from '../shared' import { UserService } from '../shared'
import { import { USER_EMAIL, USER_PASSWORD, USER_ROLE, USER_USERNAME, USER_VIDEO_QUOTA } from '../../../shared'
USER_USERNAME,
USER_EMAIL,
USER_PASSWORD,
USER_VIDEO_QUOTA,
USER_ROLE
} from '../../../shared'
import { ServerService } from '../../../core' import { ServerService } from '../../../core'
import { UserCreate, UserRole } from '../../../../../../shared' import { UserCreate, UserRole } from '../../../../../../shared'
import { UserEdit } from './user-edit' import { UserEdit } from './user-edit'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-user-create', selector: 'my-user-create',
@ -45,7 +38,8 @@ export class UserCreateComponent extends UserEdit implements OnInit {
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private router: Router, private router: Router,
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private userService: UserService private userService: UserService,
private i18n: I18n
) { ) {
super() super()
} }
@ -76,7 +70,10 @@ export class UserCreateComponent extends UserEdit implements OnInit {
this.userService.addUser(userCreate).subscribe( this.userService.addUser(userCreate).subscribe(
() => { () => {
this.notificationsService.success('Success', `User ${userCreate.username} created.`) this.notificationsService.success(
this.i18n('Success'),
this.i18n('User {{ username }} created.', { username: userCreate.username })
)
this.router.navigate([ '/admin/users/list' ]) this.router.navigate([ '/admin/users/list' ])
}, },
@ -89,6 +86,6 @@ export class UserCreateComponent extends UserEdit implements OnInit {
} }
getFormButtonTitle () { getFormButtonTitle () {
return 'Create user' return this.i18n('Create user')
} }
} }

View File

@ -1,13 +1,13 @@
<div class="form-sub-title" *ngIf="isCreation() === true">Create user</div> <div i18n class="form-sub-title" *ngIf="isCreation() === true">Create user</div>
<div class="form-sub-title" *ngIf="isCreation() === false">Edit user {{ username }}</div> <div i18n class="form-sub-title" *ngIf="isCreation() === false">Edit user {{ username }}</div>
<div *ngIf="error" class="alert alert-danger">{{ error }}</div> <div *ngIf="error" class="alert alert-danger">{{ error }}</div>
<form role="form" (ngSubmit)="formValidated()" [formGroup]="form"> <form role="form" (ngSubmit)="formValidated()" [formGroup]="form">
<div class="form-group" *ngIf="isCreation()"> <div class="form-group" *ngIf="isCreation()">
<label for="username">Username</label> <label i18n for="username">Username</label>
<input <input
type="text" id="username" placeholder="john" type="text" id="username" i18n-placeholder placeholder="john"
formControlName="username" [ngClass]="{ 'input-error': formErrors['username'] }" formControlName="username" [ngClass]="{ 'input-error': formErrors['username'] }"
> >
<div *ngIf="formErrors.username" class="form-error"> <div *ngIf="formErrors.username" class="form-error">
@ -16,9 +16,9 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="email">Email</label> <label i18n for="email">Email</label>
<input <input
type="text" id="email" placeholder="mail@example.com" type="text" id="email" i18n-placeholder placeholder="mail@example.com"
formControlName="email" [ngClass]="{ 'input-error': formErrors['email'] }" formControlName="email" [ngClass]="{ 'input-error': formErrors['email'] }"
> >
<div *ngIf="formErrors.email" class="form-error"> <div *ngIf="formErrors.email" class="form-error">
@ -27,7 +27,7 @@
</div> </div>
<div class="form-group" *ngIf="isCreation()"> <div class="form-group" *ngIf="isCreation()">
<label for="password">Password</label> <label i18n for="password">Password</label>
<input <input
type="password" id="password" type="password" id="password"
formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }" formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }"
@ -38,7 +38,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="role">Role</label> <label i18n for="role">Role</label>
<div class="peertube-select-container"> <div class="peertube-select-container">
<select id="role" formControlName="role"> <select id="role" formControlName="role">
<option *ngFor="let role of roles" [value]="role.value"> <option *ngFor="let role of roles" [value]="role.value">
@ -53,7 +53,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="videoQuota">Video quota</label> <label i18n for="videoQuota">Video quota</label>
<div class="peertube-select-container"> <div class="peertube-select-container">
<select id="videoQuota" formControlName="videoQuota"> <select id="videoQuota" formControlName="videoQuota">
<option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value"> <option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value">
@ -62,7 +62,7 @@
</select> </select>
</div> </div>
<div class="transcoding-information" *ngIf="isTranscodingInformationDisplayed()"> <div i18n class="transcoding-information" *ngIf="isTranscodingInformationDisplayed()">
Transcoding is enabled on server. The video quota only take in account <strong>original</strong> video. <br /> Transcoding is enabled on server. The video quota only take in account <strong>original</strong> video. <br />
At most, this user could use ~ {{ computeQuotaWithTranscoding() | bytes: 0 }}. At most, this user could use ~ {{ computeQuotaWithTranscoding() | bytes: 0 }}.
</div> </div>

View File

@ -8,6 +8,7 @@ import { User, USER_EMAIL, USER_ROLE, USER_VIDEO_QUOTA } from '../../../shared'
import { ServerService } from '../../../core' import { ServerService } from '../../../core'
import { UserEdit } from './user-edit' import { UserEdit } from './user-edit'
import { UserUpdate } from '../../../../../../shared' import { UserUpdate } from '../../../../../../shared'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-user-update', selector: 'my-user-update',
@ -39,7 +40,8 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
private router: Router, private router: Router,
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private userService: UserService private userService: UserService,
private i18n: I18n
) { ) {
super() super()
} }
@ -81,7 +83,10 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
this.userService.updateUser(this.userId, userUpdate).subscribe( this.userService.updateUser(this.userId, userUpdate).subscribe(
() => { () => {
this.notificationsService.success('Success', `User ${this.username} updated.`) this.notificationsService.success(
this.i18n('Success'),
this.i18n('User {{ username }} updated.', { username: this.username })
)
this.router.navigate([ '/admin/users/list' ]) this.router.navigate([ '/admin/users/list' ])
}, },
@ -94,7 +99,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
} }
getFormButtonTitle () { getFormButtonTitle () {
return 'Update user' return this.i18n('Update user')
} }
private onUserFetched (userJson: User) { private onUserFetched (userJson: User) {

View File

@ -1,9 +1,9 @@
<div class="admin-sub-header"> <div class="admin-sub-header">
<div class="form-sub-title">Users list</div> <div i18n class="form-sub-title">Users list</div>
<a class="add-button" routerLink="/admin/users/create"> <a class="add-button" routerLink="/admin/users/create">
<span class="icon icon-add"></span> <span class="icon icon-add"></span>
Create user <ng-container i18n>Create user</ng-container>
</a> </a>
</div> </div>
@ -13,11 +13,11 @@
> >
<ng-template pTemplate="header"> <ng-template pTemplate="header">
<tr> <tr>
<th pSortableColumn="username">Username <p-sortIcon field="username"></p-sortIcon></th> <th i18n pSortableColumn="username">Username <p-sortIcon field="username"></p-sortIcon></th>
<th>Email</th> <th i18n>Email</th>
<th>Video quota</th> <th i18n>Video quota</th>
<th>Role</th> <th i18n>Role</th>
<th pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> <th i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
<th></th> <th></th>
</tr> </tr>
</ng-template> </ng-template>

View File

@ -1,11 +1,10 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit } from '@angular/core'
import { NotificationsService } from 'angular2-notifications' import { NotificationsService } from 'angular2-notifications'
import { SortMeta } from 'primeng/components/common/sortmeta' import { SortMeta } from 'primeng/components/common/sortmeta'
import { ConfirmService } from '../../../core' import { ConfirmService } from '../../../core'
import { RestPagination, RestTable, User } from '../../../shared' import { RestPagination, RestTable, User } from '../../../shared'
import { UserService } from '../shared' import { UserService } from '../shared'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-user-list', selector: 'my-user-list',
@ -22,7 +21,8 @@ export class UserListComponent extends RestTable implements OnInit {
constructor ( constructor (
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private confirmService: ConfirmService, private confirmService: ConfirmService,
private userService: UserService private userService: UserService,
private i18n: I18n
) { ) {
super() super()
} }
@ -33,20 +33,23 @@ export class UserListComponent extends RestTable implements OnInit {
async removeUser (user: User) { async removeUser (user: User) {
if (user.username === 'root') { if (user.username === 'root') {
this.notificationsService.error('Error', 'You cannot delete root.') this.notificationsService.error(this.i18n('Error'), this.i18n('You cannot delete root.'))
return return
} }
const res = await this.confirmService.confirm('Do you really want to delete this user?', 'Delete') const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this user?'), this.i18n('Delete'))
if (res === false) return if (res === false) return
this.userService.removeUser(user).subscribe( this.userService.removeUser(user).subscribe(
() => { () => {
this.notificationsService.success('Success', `User ${user.username} deleted.`) this.notificationsService.success(
this.i18n('Success'),
this.i18n('User {{ username }} deleted.', { username: user.username })
)
this.loadData() this.loadData()
}, },
err => this.notificationsService.error('Error', err.message) err => this.notificationsService.error(this.i18n('Error'), err.message)
) )
} }
@ -62,7 +65,7 @@ export class UserListComponent extends RestTable implements OnInit {
this.totalRecords = resultList.total this.totalRecords = resultList.total
}, },
err => this.notificationsService.error('Error', err.message) err => this.notificationsService.error(this.i18n('Error'), err.message)
) )
} }
} }

View File

@ -1,5 +1,5 @@
<div class="admin-sub-header"> <div class="admin-sub-header">
<div class="form-sub-title">Video abuses list</div> <div i18n class="form-sub-title">Video abuses list</div>
</div> </div>
<p-table <p-table
@ -8,10 +8,10 @@
> >
<ng-template pTemplate="header"> <ng-template pTemplate="header">
<tr> <tr>
<th>Reason</th> <th i18n>Reason</th>
<th>Reporter</th> <th i18n>Reporter</th>
<th pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> <th i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
<th>Video</th> <th i18n>Video</th>
</tr> </tr>
</ng-template> </ng-template>
@ -19,13 +19,13 @@
<tr> <tr>
<td>{{ videoAbuse.reason }}</td> <td>{{ videoAbuse.reason }}</td>
<td> <td>
<a [href]="videoAbuse.reporterAccount.url" title="Go to the account" target="_blank" rel="noopener noreferrer"> <a [href]="videoAbuse.reporterAccount.url" i18n-title title="Go to the account" target="_blank" rel="noopener noreferrer">
{{ createByString(videoAbuse.reporterAccount) }} {{ createByString(videoAbuse.reporterAccount) }}
</a> </a>
</td> </td>
<td>{{ videoAbuse.createdAt }}</td> <td>{{ videoAbuse.createdAt }}</td>
<td> <td>
<a [href]="videoAbuse.video.url" title="Go to the video" target="_blank" rel="noopener noreferrer"> <a [href]="videoAbuse.video.url" i18n-title title="Go to the video" target="_blank" rel="noopener noreferrer">
{{ videoAbuse.video.name }} {{ videoAbuse.video.name }}
</a> </a>
</td> </td>

View File

@ -5,6 +5,7 @@ import { SortMeta } from 'primeng/components/common/sortmeta'
import { VideoAbuse } from '../../../../../../shared' import { VideoAbuse } from '../../../../../../shared'
import { RestPagination, RestTable, VideoAbuseService } from '../../../shared' import { RestPagination, RestTable, VideoAbuseService } from '../../../shared'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-video-abuse-list', selector: 'my-video-abuse-list',
@ -20,7 +21,8 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
constructor ( constructor (
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private videoAbuseService: VideoAbuseService private videoAbuseService: VideoAbuseService,
private i18n: I18n
) { ) {
super() super()
} }
@ -41,7 +43,7 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
this.totalRecords = resultList.total this.totalRecords = resultList.total
}, },
err => this.notificationsService.error('Error', err.message) err => this.notificationsService.error(this.i18n('Error'), err.message)
) )
} }
} }

View File

@ -1,5 +1,5 @@
<div class="admin-sub-header"> <div class="admin-sub-header">
<div class="form-sub-title">Blacklisted videos</div> <div i18n class="form-sub-title">Blacklisted videos</div>
</div> </div>
<p-table <p-table
@ -8,12 +8,12 @@
> >
<ng-template pTemplate="header"> <ng-template pTemplate="header">
<tr> <tr>
<th pSortableColumn="name">Name <p-sortIcon field="name"></p-sortIcon></th> <th i18n pSortableColumn="name">Name <p-sortIcon field="name"></p-sortIcon></th>
<th>Description</th> <th i18n>Description</th>
<th pSortableColumn="views">Views <p-sortIcon field="views"></p-sortIcon></th> <th i18n pSortableColumn="views">Views <p-sortIcon field="views"></p-sortIcon></th>
<th>NSFW</th> <th i18n>NSFW</th>
<th>UUID</th> <th i18n>UUID</th>
<th pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> <th i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
<th></th> <th></th>
</tr> </tr>
</ng-template> </ng-template>
@ -27,7 +27,7 @@
<td>{{ videoBlacklist.uuid }}</td> <td>{{ videoBlacklist.uuid }}</td>
<td>{{ videoBlacklist.createdAt }}</td> <td>{{ videoBlacklist.createdAt }}</td>
<td class="action-cell"> <td class="action-cell">
<my-delete-button label="Unblacklist" (click)="removeVideoFromBlacklist(videoBlacklist)"></my-delete-button> <my-delete-button i18n-label label="Unblacklist" (click)="removeVideoFromBlacklist(videoBlacklist)"></my-delete-button>
</td> </td>
</tr> </tr>
</ng-template> </ng-template>

View File

@ -1,11 +1,10 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit } from '@angular/core'
import { SortMeta } from 'primeng/components/common/sortmeta' import { SortMeta } from 'primeng/components/common/sortmeta'
import { NotificationsService } from 'angular2-notifications' import { NotificationsService } from 'angular2-notifications'
import { ConfirmService } from '../../../core' import { ConfirmService } from '../../../core'
import { VideoBlacklistService, RestTable, RestPagination } from '../../../shared' import { RestPagination, RestTable, VideoBlacklistService } from '../../../shared'
import { BlacklistedVideo } from '../../../../../../shared' import { BlacklistedVideo } from '../../../../../../shared'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-video-blacklist-list', selector: 'my-video-blacklist-list',
@ -22,7 +21,8 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
constructor ( constructor (
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private confirmService: ConfirmService, private confirmService: ConfirmService,
private videoBlacklistService: VideoBlacklistService private videoBlacklistService: VideoBlacklistService,
private i18n: I18n
) { ) {
super() super()
} }
@ -32,18 +32,23 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
} }
async removeVideoFromBlacklist (entry: BlacklistedVideo) { async removeVideoFromBlacklist (entry: BlacklistedVideo) {
const confirmMessage = 'Do you really want to remove this video from the blacklist ? It will be available again in the videos list.' const confirmMessage = this.i18n(
'Do you really want to remove this video from the blacklist ? It will be available again in the videos list.'
)
const res = await this.confirmService.confirm(confirmMessage, 'Unblacklist') const res = await this.confirmService.confirm(confirmMessage, this.i18n('Unblacklist'))
if (res === false) return if (res === false) return
this.videoBlacklistService.removeVideoFromBlacklist(entry.videoId).subscribe( this.videoBlacklistService.removeVideoFromBlacklist(entry.videoId).subscribe(
() => { () => {
this.notificationsService.success('Success', `Video ${entry.name} removed from the blacklist.`) this.notificationsService.success(
this.i18n('Success'),
this.i18n('Video {{ name }} removed from the blacklist.', { name: entry.name })
)
this.loadData() this.loadData()
}, },
err => this.notificationsService.error('Error', err.message) err => this.notificationsService.error(this.i18n('Error'), err.message)
) )
} }
@ -55,7 +60,7 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
this.totalRecords = resultList.total this.totalRecords = resultList.total
}, },
err => this.notificationsService.error('Error', err.message) err => this.notificationsService.error(this.i18n('Error'), err.message)
) )
} }
} }

View File

@ -2,9 +2,9 @@
<form role="form" (ngSubmit)="changePassword()" [formGroup]="form"> <form role="form" (ngSubmit)="changePassword()" [formGroup]="form">
<label for="new-password">Change password</label> <label i18n for="new-password">Change password</label>
<input <input
type="password" id="new-password" placeholder="New password" type="password" id="new-password" i18n-placeholder placeholder="New password"
formControlName="new-password" [ngClass]="{ 'input-error': formErrors['new-password'] }" formControlName="new-password" [ngClass]="{ 'input-error': formErrors['new-password'] }"
> >
<div *ngIf="formErrors['new-password']" class="form-error"> <div *ngIf="formErrors['new-password']" class="form-error">
@ -12,9 +12,9 @@
</div> </div>
<input <input
type="password" id="new-confirmed-password" placeholder="Confirm new password" type="password" id="new-confirmed-password" i18n-placeholder placeholder="Confirm new password"
formControlName="new-confirmed-password" formControlName="new-confirmed-password"
> >
<input type="submit" value="Change password" [disabled]="!form.valid"> <input type="submit" i18n-value value="Change password" [disabled]="!form.valid">
</form> </form>

View File

@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'
import { FormBuilder, FormGroup } from '@angular/forms' import { FormBuilder, FormGroup } from '@angular/forms'
import { NotificationsService } from 'angular2-notifications' import { NotificationsService } from 'angular2-notifications'
import { FormReactive, USER_PASSWORD, UserService } from '../../../shared' import { FormReactive, USER_PASSWORD, UserService } from '../../../shared'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-account-change-password', selector: 'my-account-change-password',
@ -24,7 +25,8 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On
constructor ( constructor (
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private userService: UserService private userService: UserService,
private i18n: I18n
) { ) {
super() super()
} }
@ -49,12 +51,12 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On
this.error = null this.error = null
if (newPassword !== newConfirmedPassword) { if (newPassword !== newConfirmedPassword) {
this.error = 'The new password and the confirmed password do not correspond.' this.error = this.i18n('The new password and the confirmed password do not correspond.')
return return
} }
this.userService.changePassword(newPassword).subscribe( this.userService.changePassword(newPassword).subscribe(
() => this.notificationsService.success('Success', 'Password updated.'), () => this.notificationsService.success(this.i18n('Success'), this.i18n('Password updated.')),
err => this.error = err.message err => this.error = err.message
) )

View File

@ -2,7 +2,7 @@
<form role="form" (ngSubmit)="updateMyProfile()" [formGroup]="form"> <form role="form" (ngSubmit)="updateMyProfile()" [formGroup]="form">
<label for="display-name">Display name</label> <label i18n for="display-name">Display name</label>
<input <input
type="text" id="display-name" type="text" id="display-name"
formControlName="display-name" [ngClass]="{ 'input-error': formErrors['display-name'] }" formControlName="display-name" [ngClass]="{ 'input-error': formErrors['display-name'] }"
@ -11,7 +11,7 @@
{{ formErrors['display-name'] }} {{ formErrors['display-name'] }}
</div> </div>
<label for="description">Description</label> <label i18n for="description">Description</label>
<textarea <textarea
id="description" formControlName="description" id="description" formControlName="description"
[ngClass]="{ 'input-error': formErrors['description'] }" [ngClass]="{ 'input-error': formErrors['description'] }"
@ -20,5 +20,5 @@
{{ formErrors.description }} {{ formErrors.description }}
</div> </div>
<input type="submit" value="Update my profile" [disabled]="!form.valid"> <input type="submit" i18n-value value="Update my profile" [disabled]="!form.valid">
</form> </form>

View File

@ -3,6 +3,7 @@ import { FormBuilder, FormGroup } from '@angular/forms'
import { NotificationsService } from 'angular2-notifications' import { NotificationsService } from 'angular2-notifications'
import { FormReactive, USER_DESCRIPTION, USER_DISPLAY_NAME, UserService } from '../../../shared' import { FormReactive, USER_DESCRIPTION, USER_DISPLAY_NAME, UserService } from '../../../shared'
import { User } from '@app/shared' import { User } from '@app/shared'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-account-profile', selector: 'my-account-profile',
@ -27,7 +28,8 @@ export class MyAccountProfileComponent extends FormReactive implements OnInit {
constructor ( constructor (
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private userService: UserService private userService: UserService,
private i18n: I18n
) { ) {
super() super()
} }
@ -56,7 +58,7 @@ export class MyAccountProfileComponent extends FormReactive implements OnInit {
this.user.account.displayName = displayName this.user.account.displayName = displayName
this.user.account.description = description this.user.account.description = description
this.notificationsService.success('Success', 'Profile updated.') this.notificationsService.success(this.i18n('Success'), this.i18n('Profile updated.'))
}, },
err => this.error = err.message err => this.error = err.message

View File

@ -6,27 +6,27 @@
<div class="user-info-display-name">{{ user.account.displayName }}</div> <div class="user-info-display-name">{{ user.account.displayName }}</div>
<div class="user-info-username">{{ user.username }}</div> <div class="user-info-username">{{ user.username }}</div>
</div> </div>
<div class="user-info-followers">{{ user.account?.followersCount }} subscribers</div> <div i18n class="user-info-followers">{{ user.account?.followersCount }} subscribers</div>
</div> </div>
</div> </div>
<div class="button-file"> <div class="button-file">
<span>Change your avatar</span> <span i18n>Change your avatar</span>
<input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="changeAvatar()" /> <input #avatarfileInput type="file" name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="changeAvatar()" />
</div> </div>
<div class="file-max-size">(extensions: {{ avatarExtensions }}, max size: {{ maxAvatarSize | bytes }})</div> <div i18n class="file-max-size">(extensions: {{ avatarExtensions }}, max size: {{ maxAvatarSize | bytes }})</div>
<div class="user-quota"> <div class="user-quota">
<span class="user-quota-label">Video quota:</span> {{ userVideoQuotaUsed | bytes: 0 }} / {{ userVideoQuota }} <span i18n class="user-quota-label">Video quota:</span> {{ userVideoQuotaUsed | bytes: 0 }} / {{ userVideoQuota }}
</div> </div>
<ng-template [ngIf]="user && user.account"> <ng-template [ngIf]="user && user.account">
<div class="account-title">Profile</div> <div i18n class="account-title">Profile</div>
<my-account-profile [user]="user"></my-account-profile> <my-account-profile [user]="user"></my-account-profile>
</ng-template> </ng-template>
<div class="account-title">Password</div> <div i18n class="account-title">Password</div>
<my-account-change-password></my-account-change-password> <my-account-change-password></my-account-change-password>
<div class="account-title">Video settings</div> <div i18n class="account-title">Video settings</div>
<my-account-video-settings [user]="user"></my-account-video-settings> <my-account-video-settings [user]="user"></my-account-video-settings>

View File

@ -5,6 +5,7 @@ import { AuthService } from '../../core'
import { ServerService } from '../../core/server' import { ServerService } from '../../core/server'
import { User } from '../../shared' import { User } from '../../shared'
import { UserService } from '../../shared/users' import { UserService } from '../../shared/users'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-account-settings', selector: 'my-account-settings',
@ -22,7 +23,8 @@ export class MyAccountSettingsComponent implements OnInit {
private userService: UserService, private userService: UserService,
private authService: AuthService, private authService: AuthService,
private serverService: ServerService, private serverService: ServerService,
private notificationsService: NotificationsService private notificationsService: NotificationsService,
private i18n: I18n
) {} ) {}
ngOnInit () { ngOnInit () {
@ -33,7 +35,7 @@ export class MyAccountSettingsComponent implements OnInit {
if (this.user.videoQuota !== -1) { if (this.user.videoQuota !== -1) {
this.userVideoQuota = new BytesPipe().transform(this.user.videoQuota, 0).toString() this.userVideoQuota = new BytesPipe().transform(this.user.videoQuota, 0).toString()
} else { } else {
this.userVideoQuota = 'Unlimited' this.userVideoQuota = this.i18n('Unlimited')
} }
} }
) )
@ -51,12 +53,12 @@ export class MyAccountSettingsComponent implements OnInit {
this.userService.changeAvatar(formData) this.userService.changeAvatar(formData)
.subscribe( .subscribe(
data => { data => {
this.notificationsService.success('Success', 'Avatar changed.') this.notificationsService.success(this.i18n('Success'), this.i18n('Avatar changed.'))
this.user.account.avatar = data.avatar this.user.account.avatar = data.avatar
}, },
err => this.notificationsService.error('Error', err.message) err => this.notificationsService.error(this.i18n('Error'), err.message)
) )
} }

View File

@ -1,13 +1,16 @@
<form role="form" (ngSubmit)="updateDetails()" [formGroup]="form"> <form role="form" (ngSubmit)="updateDetails()" [formGroup]="form">
<div class="form-group"> <div class="form-group">
<label for="nsfwPolicy">Default policy on videos containing sensitive content</label> <label i18n for="nsfwPolicy">Default policy on videos containing sensitive content</label>
<my-help helpType="custom" customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."></my-help> <my-help
helpType="custom" i18n-customHtml
customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."
></my-help>
<div class="peertube-select-container"> <div class="peertube-select-container">
<select id="nsfwPolicy" formControlName="nsfwPolicy"> <select id="nsfwPolicy" formControlName="nsfwPolicy">
<option value="do_not_list">Do not list</option> <option i18n value="do_not_list">Do not list</option>
<option value="blur">Blur thumbnails</option> <option i18n value="blur">Blur thumbnails</option>
<option value="display">Display</option> <option i18n value="display">Display</option>
</select> </select>
</div> </div>
</div> </div>
@ -18,8 +21,8 @@
formControlName="autoPlayVideo" formControlName="autoPlayVideo"
> >
<label for="autoPlayVideo"></label> <label for="autoPlayVideo"></label>
<label for="autoPlayVideo">Automatically plays video</label> <label i18n for="autoPlayVideo">Automatically plays video</label>
</div> </div>
<input type="submit" value="Save" [disabled]="!form.valid"> <input type="submit" i18n-value value="Save" [disabled]="!form.valid">
</form> </form>

View File

@ -4,6 +4,7 @@ import { NotificationsService } from 'angular2-notifications'
import { UserUpdateMe } from '../../../../../../shared' import { UserUpdateMe } from '../../../../../../shared'
import { AuthService } from '../../../core' import { AuthService } from '../../../core'
import { FormReactive, User, UserService } from '../../../shared' import { FormReactive, User, UserService } from '../../../shared'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-account-video-settings', selector: 'my-account-video-settings',
@ -21,7 +22,8 @@ export class MyAccountVideoSettingsComponent extends FormReactive implements OnI
private authService: AuthService, private authService: AuthService,
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private userService: UserService private userService: UserService,
private i18n: I18n
) { ) {
super() super()
} }
@ -49,12 +51,12 @@ export class MyAccountVideoSettingsComponent extends FormReactive implements OnI
this.userService.updateMyProfile(details).subscribe( this.userService.updateMyProfile(details).subscribe(
() => { () => {
this.notificationsService.success('Success', 'Information updated.') this.notificationsService.success(this.i18n('Success'), this.i18n('Information updated.'))
this.authService.refreshUserInformation() this.authService.refreshUserInformation()
}, },
err => this.notificationsService.error('Error', err.message) err => this.notificationsService.error(this.i18n('Error'), err.message)
) )
} }
} }

View File

@ -11,6 +11,7 @@ import {
} from '@app/shared/forms/form-validators/video-channel' } from '@app/shared/forms/form-validators/video-channel'
import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
import { AuthService } from '@app/core' import { AuthService } from '@app/core'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-account-video-channel-create', selector: 'my-account-video-channel-create',
@ -37,7 +38,8 @@ export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelE
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private router: Router, private router: Router,
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private videoChannelService: VideoChannelService private videoChannelService: VideoChannelService,
private i18n: I18n
) { ) {
super() super()
} }
@ -69,7 +71,10 @@ export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelE
this.videoChannelService.createVideoChannel(videoChannelCreate).subscribe( this.videoChannelService.createVideoChannel(videoChannelCreate).subscribe(
() => { () => {
this.authService.refreshUserInformation() this.authService.refreshUserInformation()
this.notificationsService.success('Success', `Video channel ${videoChannelCreate.displayName} created.`) this.notificationsService.success(
this.i18n('Success'),
this.i18n('Video channel {{ videoChannelName }} created.', { videoChannelName: videoChannelCreate.displayName })
)
this.router.navigate([ '/my-account', 'video-channels' ]) this.router.navigate([ '/my-account', 'video-channels' ])
}, },
@ -82,6 +87,6 @@ export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelE
} }
getFormButtonTitle () { getFormButtonTitle () {
return 'Create' return this.i18n('Create')
} }
} }

View File

@ -1,11 +1,11 @@
<div class="form-sub-title" *ngIf="isCreation() === true">Create a video channel</div> <div i18n class="form-sub-title" *ngIf="isCreation() === true">Create a video channel</div>
<div class="form-sub-title" *ngIf="isCreation() === false">Update {{ videoChannel?.displayName }}</div> <div i18n class="form-sub-title" *ngIf="isCreation() === false">Update {{ videoChannel?.displayName }}</div>
<div *ngIf="error" class="alert alert-danger">{{ error }}</div> <div *ngIf="error" class="alert alert-danger">{{ error }}</div>
<form role="form" (ngSubmit)="formValidated()" [formGroup]="form"> <form role="form" (ngSubmit)="formValidated()" [formGroup]="form">
<div class="form-group"> <div class="form-group">
<label for="display-name">Display name</label> <label i18n for="display-name">Display name</label>
<input <input
type="text" id="display-name" type="text" id="display-name"
formControlName="display-name" [ngClass]="{ 'input-error': formErrors['display-name'] }" formControlName="display-name" [ngClass]="{ 'input-error': formErrors['display-name'] }"
@ -16,7 +16,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="description">Description</label> <label i18n for="description">Description</label>
<textarea <textarea
id="description" formControlName="description" id="description" formControlName="description"
[ngClass]="{ 'input-error': formErrors['description'] }" [ngClass]="{ 'input-error': formErrors['description'] }"
@ -29,7 +29,7 @@
<div class="form-group"> <div class="form-group">
<label for="support">Support</label> <label for="support">Support</label>
<my-help <my-help
helpType="markdownEnhanced" preHtml="Short text to tell people how they can support your channel (membership platform...).<br /><br /> helpType="markdownEnhanced" i18n-preHtml preHtml="Short text to tell people how they can support your channel (membership platform...).<br /><br />
When you will upload a video in this channel, the video support field will be automatically filled by this text." When you will upload a video in this channel, the video support field will be automatically filled by this text."
></my-help> ></my-help>
<my-markdown-textarea <my-markdown-textarea

View File

@ -13,6 +13,7 @@ import { VideoChannelService } from '@app/shared/video-channel/video-channel.ser
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
import { VideoChannel } from '@app/shared/video-channel/video-channel.model' import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
import { AuthService } from '@app/core' import { AuthService } from '@app/core'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-account-video-channel-update', selector: 'my-account-video-channel-update',
@ -43,7 +44,8 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE
private router: Router, private router: Router,
private route: ActivatedRoute, private route: ActivatedRoute,
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private videoChannelService: VideoChannelService private videoChannelService: VideoChannelService,
private i18n: I18n
) { ) {
super() super()
} }
@ -97,7 +99,10 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE
this.videoChannelService.updateVideoChannel(this.videoChannelToUpdate.uuid, videoChannelUpdate).subscribe( this.videoChannelService.updateVideoChannel(this.videoChannelToUpdate.uuid, videoChannelUpdate).subscribe(
() => { () => {
this.authService.refreshUserInformation() this.authService.refreshUserInformation()
this.notificationsService.success('Success', `Video channel ${videoChannelUpdate.displayName} updated.`) this.notificationsService.success(
this.i18n('Success'),
this.i18n('Video channel {{ videoChannelName }} updated.', { videoChannelName: videoChannelUpdate.displayName })
)
this.router.navigate([ '/my-account', 'video-channels' ]) this.router.navigate([ '/my-account', 'video-channels' ])
}, },
@ -110,6 +115,6 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE
} }
getFormButtonTitle () { getFormButtonTitle () {
return 'Update' return this.i18n('Update')
} }
} }

View File

@ -1,7 +1,7 @@
<div class="video-channels-header"> <div class="video-channels-header">
<a class="create-button" routerLink="create"> <a class="create-button" routerLink="create">
<span class="icon icon-add"></span> <span class="icon icon-add"></span>
Create another video channel <ng-container i18n>Create another video channel</ng-container>
</a> </a>
</div> </div>
@ -12,13 +12,13 @@
</a> </a>
<div class="video-channel-info"> <div class="video-channel-info">
<a [routerLink]="[ '/video-channels', videoChannel.uuid ]" class="video-channel-names" title="Go to the channel"> <a [routerLink]="[ '/video-channels', videoChannel.uuid ]" class="video-channel-names" i18n-title title="Go to the channel">
<div class="video-channel-display-name">{{ videoChannel.displayName }}</div> <div class="video-channel-display-name">{{ videoChannel.displayName }}</div>
<!-- Hide the name for now, because it's an UUID not very friendly --> <!-- Hide the name for now, because it's an UUID not very friendly -->
<!--<div class="video-channel-name">{{ videoChannel.name }}</div>--> <!--<div class="video-channel-name">{{ videoChannel.name }}</div>-->
</a> </a>
<div class="video-channel-followers">{{ videoChannel.followersCount }} subscribers</div> <div i18n class="video-channel-followers">{{ videoChannel.followersCount }} subscribers</div>
</div> </div>
<div class="video-channel-buttons"> <div class="video-channel-buttons">

View File

@ -6,6 +6,7 @@ import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
import { User } from '@app/shared' import { User } from '@app/shared'
import { flatMap } from 'rxjs/operators' import { flatMap } from 'rxjs/operators'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-account-video-channels', selector: 'my-account-video-channels',
@ -21,7 +22,8 @@ export class MyAccountVideoChannelsComponent implements OnInit {
private authService: AuthService, private authService: AuthService,
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private confirmService: ConfirmService, private confirmService: ConfirmService,
private videoChannelService: VideoChannelService private videoChannelService: VideoChannelService,
private i18n: I18n
) {} ) {}
ngOnInit () { ngOnInit () {
@ -32,10 +34,13 @@ export class MyAccountVideoChannelsComponent implements OnInit {
async deleteVideoChannel (videoChannel: VideoChannel) { async deleteVideoChannel (videoChannel: VideoChannel) {
const res = await this.confirmService.confirmWithInput( const res = await this.confirmService.confirmWithInput(
`Do you really want to delete ${videoChannel.displayName}? It will delete all videos uploaded in this channel too.`, this.i18n(
'Please type the name of the video channel to confirm', 'Do you really want to delete {{ videoChannelName }}? It will delete all videos uploaded in this channel too.',
{ videoChannelName: videoChannel.displayName }
),
this.i18n('Please type the name of the video channel to confirm'),
videoChannel.displayName, videoChannel.displayName,
'Delete' this.i18n('Delete')
) )
if (res === false) return if (res === false) return
@ -43,10 +48,13 @@ export class MyAccountVideoChannelsComponent implements OnInit {
.subscribe( .subscribe(
status => { status => {
this.loadVideoChannels() this.loadVideoChannels()
this.notificationsService.success('Success', `Video channel ${videoChannel.name} deleted.`) this.notificationsService.success(
this.i18n('Success'),
this.i18n('Video channel {{ videoChannelName } deleted.', { videoChannelName: videoChannel.displayName })
)
}, },
error => this.notificationsService.error('Error', error.message) error => this.notificationsService.error(this.i18n('Error'), error.message)
) )
} }

View File

@ -1,4 +1,4 @@
<div *ngIf="pagination.totalItems === 0">No results.</div> <div i18n *ngIf="pagination.totalItems === 0">No results.</div>
<div <div
myInfiniteScroller myInfiniteScroller
@ -17,18 +17,18 @@
<div class="video-info"> <div class="video-info">
<a class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a> <a class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a>
<span class="video-info-date-views">{{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span> <span i18n class="video-info-date-views">{{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span>
<div class="video-info-private">{{ video.privacy.label }}</div> <div class="video-info-private">{{ video.privacy.label }}</div>
</div> </div>
<!-- Display only once --> <!-- Display only once -->
<div class="action-selection-mode" *ngIf="isInSelectionMode() === true && i === 0 && j === 0"> <div class="action-selection-mode" *ngIf="isInSelectionMode() === true && i === 0 && j === 0">
<div class="action-selection-mode-child"> <div class="action-selection-mode-child">
<span class="action-button action-button-cancel-selection" (click)="abortSelectionMode()"> <span i18n class="action-button action-button-cancel-selection" (click)="abortSelectionMode()">
Cancel Cancel
</span> </span>
<span class="action-button action-button-delete-selection" (click)="deleteSelectedVideos()"> <span i18n class="action-button action-button-delete-selection" (click)="deleteSelectedVideos()">
<span class="icon icon-delete-white"></span> <span class="icon icon-delete-white"></span>
Delete Delete
</span> </span>

View File

@ -11,6 +11,7 @@ import { ConfirmService } from '../../core/confirm'
import { AbstractVideoList } from '../../shared/video/abstract-video-list' import { AbstractVideoList } from '../../shared/video/abstract-video-list'
import { Video } from '../../shared/video/video.model' import { Video } from '../../shared/video/video.model'
import { VideoService } from '../../shared/video/video.service' import { VideoService } from '../../shared/video/video.service'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-account-videos', selector: 'my-account-videos',
@ -18,7 +19,7 @@ import { VideoService } from '../../shared/video/video.service'
styleUrls: [ './my-account-videos.component.scss' ] styleUrls: [ './my-account-videos.component.scss' ]
}) })
export class MyAccountVideosComponent extends AbstractVideoList implements OnInit, OnDestroy { export class MyAccountVideosComponent extends AbstractVideoList implements OnInit, OnDestroy {
titlePage = 'My videos' titlePage: string
currentRoute = '/my-account/videos' currentRoute = '/my-account/videos'
checkedVideos: { [ id: number ]: boolean } = {} checkedVideos: { [ id: number ]: boolean } = {}
pagination: ComponentPagination = { pagination: ComponentPagination = {
@ -30,14 +31,19 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni
protected baseVideoWidth = -1 protected baseVideoWidth = -1
protected baseVideoHeight = 155 protected baseVideoHeight = 155
constructor (protected router: Router, constructor (
protected route: ActivatedRoute, protected router: Router,
protected authService: AuthService, protected route: ActivatedRoute,
protected notificationsService: NotificationsService, protected authService: AuthService,
protected confirmService: ConfirmService, protected notificationsService: NotificationsService,
protected location: Location, protected confirmService: ConfirmService,
private videoService: VideoService) { protected location: Location,
protected i18n: I18n,
private videoService: VideoService
) {
super() super()
this.titlePage = this.i18n('My videos')
} }
ngOnInit () { ngOnInit () {

View File

@ -1,10 +1,10 @@
<div class="row"> <div class="row">
<div class="sub-menu"> <div class="sub-menu">
<a routerLink="/my-account/settings" routerLinkActive="active" class="title-page">My settings</a> <a i18n routerLink="/my-account/settings" routerLinkActive="active" class="title-page">My settings</a>
<a routerLink="/my-account/video-channels" routerLinkActive="active" class="title-page">My video channels</a> <a i18n routerLink="/my-account/video-channels" routerLinkActive="active" class="title-page">My video channels</a>
<a routerLink="/my-account/videos" routerLinkActive="active" class="title-page">My videos</a> <a i18n routerLink="/my-account/videos" routerLinkActive="active" class="title-page">My videos</a>
</div> </div>
<div class="margin-content"> <div class="margin-content">

View File

@ -1,3 +1,3 @@
<div> <div i18n>
Sorry, but we couldn't find the page you were looking for. Sorry, but we couldn't find the page you were looking for.
</div> </div>

View File

@ -1,20 +1,20 @@
<div *ngIf="videoChannel" class="row"> <div *ngIf="videoChannel" class="row">
<div class="description col-md-6 col-sm-12"> <div class="description col-md-6 col-sm-12">
<div class="block"> <div class="block">
<div class="small-title">Description</div> <div i18n class="small-title">Description</div>
<div class="content">{{ getVideoChannelDescription() }}</div> <div class="content">{{ getVideoChannelDescription() }}</div>
</div> </div>
<div class="block" *ngIf="videoChannel.support"> <div class="block" *ngIf="videoChannel.support">
<div class="small-title">Support this channel</div> <div i18n class="small-title">Support this channel</div>
<div class="content">{{ videoChannel.support }}</div> <div class="content">{{ videoChannel.support }}</div>
</div> </div>
</div> </div>
<div class="stats col-md-6 col-sm-12"> <div class="stats col-md-6 col-sm-12">
<div class="block"> <div class="block">
<div class="small-title">Stats</div> <div i18n class="small-title">Stats</div>
<div class="content">Created {{ videoChannel.createdAt | date }}</div> <div i18n class="content">Created {{ videoChannel.createdAt | date }}</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
import { VideoChannel } from '@app/shared/video-channel/video-channel.model' import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-video-channel-about', selector: 'my-video-channel-about',
@ -12,7 +13,8 @@ export class VideoChannelAboutComponent implements OnInit {
videoChannel: VideoChannel videoChannel: VideoChannel
constructor ( constructor (
protected route: ActivatedRoute, private route: ActivatedRoute,
private i18n: I18n,
private videoChannelService: VideoChannelService private videoChannelService: VideoChannelService
) { } ) { }
@ -25,6 +27,6 @@ export class VideoChannelAboutComponent implements OnInit {
getVideoChannelDescription () { getVideoChannelDescription () {
if (this.videoChannel.description) return this.videoChannel.description if (this.videoChannel.description) return this.videoChannel.description
return 'No description' return this.i18n('No description')
} }
} }

View File

@ -10,6 +10,7 @@ import { VideoService } from '../../shared/video/video.service'
import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
import { VideoChannel } from '@app/shared/video-channel/video-channel.model' import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
import { tap } from 'rxjs/operators' import { tap } from 'rxjs/operators'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-video-channel-videos', selector: 'my-video-channel-videos',
@ -20,7 +21,7 @@ import { tap } from 'rxjs/operators'
] ]
}) })
export class VideoChannelVideosComponent extends AbstractVideoList implements OnInit, OnDestroy { export class VideoChannelVideosComponent extends AbstractVideoList implements OnInit, OnDestroy {
titlePage = 'Published videos' titlePage: string
marginContent = false // Disable margin marginContent = false // Disable margin
currentRoute = '/video-channel/videos' currentRoute = '/video-channel/videos'
loadOnInit = false loadOnInit = false
@ -34,10 +35,13 @@ export class VideoChannelVideosComponent extends AbstractVideoList implements On
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected confirmService: ConfirmService, protected confirmService: ConfirmService,
protected location: Location, protected location: Location,
protected i18n: I18n,
private videoChannelService: VideoChannelService, private videoChannelService: VideoChannelService,
private videoService: VideoService private videoService: VideoService
) { ) {
super() super()
this.titlePage = this.i18n('Published videos')
} }
ngOnInit () { ngOnInit () {
@ -63,7 +67,11 @@ export class VideoChannelVideosComponent extends AbstractVideoList implements On
return this.videoService return this.videoService
.getVideoChannelVideos(this.videoChannel, newPagination, this.sort) .getVideoChannelVideos(this.videoChannel, newPagination, this.sort)
.pipe(tap(({ totalVideos }) => this.titlePage = `Published ${totalVideos} videos`)) .pipe(
tap(({ totalVideos }) => {
this.titlePage = this.i18n('Published {{ totalVideos }} videos', { totalVideos })
})
)
} }
generateSyndicationList () { generateSyndicationList () {

View File

@ -8,19 +8,19 @@
<div class="actor-names"> <div class="actor-names">
<div class="actor-display-name">{{ videoChannel.displayName }}</div> <div class="actor-display-name">{{ videoChannel.displayName }}</div>
</div> </div>
<div class="actor-followers">{{ videoChannel.followersCount }} subscribers</div> <div i18n class="actor-followers">{{ videoChannel.followersCount }} subscribers</div>
<a [routerLink]="[ '/accounts', videoChannel.ownerBy ]" title="Go the owner account page" class="actor-owner"> <a [routerLink]="[ '/accounts', videoChannel.ownerBy ]" i18n-title title="Go the owner account page" class="actor-owner">
<span>Created by {{ videoChannel.ownerBy }}</span> <span i18n>Created by {{ videoChannel.ownerBy }}</span>
<img [src]="videoChannel.ownerAvatarUrl" alt="Owner account avatar" /> <img [src]="videoChannel.ownerAvatarUrl" alt="Owner account avatar" />
</a> </a>
</div> </div>
</div> </div>
<div class="links"> <div class="links">
<a routerLink="videos" routerLinkActive="active" class="title-page">Videos</a> <a i18n routerLink="videos" routerLinkActive="active" class="title-page">Videos</a>
<a routerLink="about" routerLinkActive="active" class="title-page">About</a> <a i18n routerLink="about" routerLinkActive="active" class="title-page">About</a>
</div> </div>
</div> </div>

View File

@ -1,5 +1,5 @@
<div class="margin-content"> <div class="margin-content">
<div class="title-page title-page-single"> <div i18n class="title-page title-page-single">
Welcome to the {{ instanceName }} instance Welcome to the {{ instanceName }} instance
</div> </div>
@ -8,41 +8,41 @@
</div> </div>
<div class="description"> <div class="description">
<div class="section-title">Description</div> <div i18n class="section-title">Description</div>
<div [innerHTML]="descriptionHTML"></div> <div [innerHTML]="descriptionHTML"></div>
</div> </div>
<div class="terms"> <div class="terms">
<div class="section-title">Terms</div> <div i18n class="section-title">Terms</div>
<div [innerHTML]="termsHTML"></div> <div [innerHTML]="termsHTML"></div>
</div> </div>
<div class="signup"> <div class="signup">
<div class="section-title">Signup</div> <div i18n class="section-title">Signup</div>
<div *ngIf="isSignupAllowed"> <div *ngIf="isSignupAllowed">
User registration is allowed and <ng-container i18n>User registration is allowed and</ng-container>
<ng-template [ngIf]="userVideoQuota !== -1"> <ng-container i18n *ngIf="userVideoQuota !== -1">
this instance provides a baseline quota of {{ userVideoQuota | bytes: 0 }} space for the videos of its users. this instance provides a baseline quota of {{ userVideoQuota | bytes: 0 }} space for the videos of its users.
</ng-template> </ng-container>
<ng-template [ngIf]="userVideoQuota === -1"> <ng-container i18n *ngIf="userVideoQuota === -1">
this instance provides unlimited space for the videos of its users. this instance provides unlimited space for the videos of its users.
</ng-template> </ng-container>
</div> </div>
<div *ngIf="isSignupAllowed === false"> <div i18n *ngIf="isSignupAllowed === false">
User registration is currently not allowed. User registration is currently not allowed.
</div> </div>
</div> </div>
<div id="p2p-privacy"> <div id="p2p-privacy">
<div class="section-title">P2P & Privacy</div> <div i18n class="section-title">P2P & Privacy</div>
<p> <p i18n>
PeerTube uses the BitTorrent protocol to share bandwidth between users. It implies that your public IP address is stored in the public BitTorrent tracker of the video PeerTube instance as long as you're watching the video. PeerTube uses the BitTorrent protocol to share bandwidth between users. It implies that your public IP address is stored in the public BitTorrent tracker of the video PeerTube instance as long as you're watching the video.
If you want to keep your public IP address private, please use a VPN or Tor. If you want to keep your public IP address private, please use a VPN or Tor.
</p> </p>

View File

@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'
import { ServerService } from '@app/core' import { ServerService } from '@app/core'
import { MarkdownService } from '@app/videos/shared' import { MarkdownService } from '@app/videos/shared'
import { NotificationsService } from 'angular2-notifications' import { NotificationsService } from 'angular2-notifications'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-about', selector: 'my-about',
@ -17,7 +18,8 @@ export class AboutComponent implements OnInit {
constructor ( constructor (
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private serverService: ServerService, private serverService: ServerService,
private markdownService: MarkdownService private markdownService: MarkdownService,
private i18n: I18n
) {} ) {}
get instanceName () { get instanceName () {
@ -41,7 +43,7 @@ export class AboutComponent implements OnInit {
this.termsHTML = this.markdownService.textMarkdownToHTML(res.instance.terms) this.termsHTML = this.markdownService.textMarkdownToHTML(res.instance.terms)
}, },
err => this.notificationsService.error('Error getting about from server', err) err => this.notificationsService.error(this.i18n('Error getting about from server'), err)
) )
} }

View File

@ -1,5 +1,5 @@
import { Observable, ReplaySubject, Subject, throwError as observableThrowError } from 'rxjs' import { Observable, ReplaySubject, Subject, throwError as observableThrowError } from 'rxjs'
import { catchError, map, mergeMap, tap, share } from 'rxjs/operators' import { catchError, map, mergeMap, share, tap } from 'rxjs/operators'
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http' import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { Router } from '@angular/router' import { Router } from '@angular/router'
@ -13,6 +13,7 @@ import { AuthStatus } from './auth-status.model'
import { AuthUser } from './auth-user.model' import { AuthUser } from './auth-user.model'
import { objectToUrlEncoded } from '@app/shared/misc/utils' import { objectToUrlEncoded } from '@app/shared/misc/utils'
import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage' import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage'
import { I18n } from '@ngx-translate/i18n-polyfill'
interface UserLoginWithUsername extends UserLogin { interface UserLoginWithUsername extends UserLogin {
access_token: string access_token: string
@ -46,7 +47,8 @@ export class AuthService {
private http: HttpClient, private http: HttpClient,
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private restExtractor: RestExtractor, private restExtractor: RestExtractor,
private router: Router private router: Router,
private i18n: I18n
) { ) {
this.loginChanged = new Subject<AuthStatus>() this.loginChanged = new Subject<AuthStatus>()
this.loginChangedSource = this.loginChanged.asObservable() this.loginChangedSource = this.loginChanged.asObservable()
@ -74,14 +76,15 @@ export class AuthService {
let errorMessage = error.message let errorMessage = error.message
if (error.status === 403) { if (error.status === 403) {
errorMessage = `Cannot retrieve OAuth Client credentials: ${error.text}. \n` errorMessage = this.i18n('Cannot retrieve OAuth Client credentials: {{ errorText }}.\n', { errorText: error.text })
errorMessage += 'Ensure you have correctly configured PeerTube (config/ directory), ' + errorMessage += this.i18n(
'in particular the "webserver" section.' 'Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.'
)
} }
// We put a bigger timeout // We put a bigger timeout
// This is an important message // This is an important message
this.notificationsService.error('Error', errorMessage, { timeOut: 7000 }) this.notificationsService.error(this.i18n('Error'), errorMessage, { timeOut: 7000 })
} }
) )
} }
@ -180,7 +183,7 @@ export class AuthService {
this.router.navigate([ '/login' ]) this.router.navigate([ '/login' ])
return observableThrowError({ return observableThrowError({
error: 'You need to reconnect.' error: this.i18n('You need to reconnect.')
}) })
}), }),
share() share()

View File

@ -16,7 +16,7 @@
</div> </div>
<div class="form-group inputs"> <div class="form-group inputs">
<span class="action-button action-button-cancel" (click)="cancel()"> <span i18n class="action-button action-button-cancel" (click)="cancel()">
Cancel Cancel
</span> </span>

View File

@ -3,6 +3,7 @@ import { Component, HostListener, OnInit, ViewChild } from '@angular/core'
import { ModalDirective } from 'ngx-bootstrap/modal' import { ModalDirective } from 'ngx-bootstrap/modal'
import { ConfirmService } from './confirm.service' import { ConfirmService } from './confirm.service'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-confirm', selector: 'my-confirm',
@ -20,7 +21,10 @@ export class ConfirmComponent implements OnInit {
inputValue = '' inputValue = ''
confirmButtonText = '' confirmButtonText = ''
constructor (private confirmService: ConfirmService) { constructor (
private confirmService: ConfirmService,
private i18n: I18n
) {
// Empty // Empty
} }
@ -38,7 +42,7 @@ export class ConfirmComponent implements OnInit {
this.inputLabel = inputLabel this.inputLabel = inputLabel
this.expectedInputValue = expectedInputValue this.expectedInputValue = expectedInputValue
this.confirmButtonText = confirmButtonText || 'Confirm' this.confirmButtonText = confirmButtonText || this.i18n('Confirm')
this.showModal() this.showModal()
} }

View File

@ -1,10 +1,10 @@
<input <input
type="text" id="search-video" name="search-video" placeholder="Search..." type="text" id="search-video" name="search-video" i18n-placeholder placeholder="Search..."
[(ngModel)]="searchValue" (keyup.enter)="doSearch()" [(ngModel)]="searchValue" (keyup.enter)="doSearch()"
> >
<span (click)="doSearch()" class="icon icon-search"></span> <span (click)="doSearch()" class="icon icon-search"></span>
<a class="upload-button" routerLink="/videos/upload"> <a class="upload-button" routerLink="/videos/upload">
<span class="icon icon-upload"></span> <span class="icon icon-upload"></span>
<span class="upload-button-label">Upload</span> <span i18n class="upload-button-label">Upload</span>
</a> </a>

View File

@ -1,5 +1,5 @@
<div class="margin-content"> <div class="margin-content">
<div class="title-page title-page-single"> <div i18n class="title-page title-page-single">
Login Login
</div> </div>
@ -8,21 +8,21 @@
<form role="form" (ngSubmit)="login()" [formGroup]="form"> <form role="form" (ngSubmit)="login()" [formGroup]="form">
<div class="form-group"> <div class="form-group">
<div> <div>
<label for="username">User</label> <label i18n for="username">User</label>
<input <input
type="text" id="username" placeholder="Username or email address" required tabindex="1" type="text" id="username" i18n-placeholder placeholder="Username or email address" required tabindex="1"
formControlName="username" [ngClass]="{ 'input-error': formErrors['username'] }" formControlName="username" [ngClass]="{ 'input-error': formErrors['username'] }"
> >
<a *ngIf="signupAllowed === true" routerLink="/signup" class="create-an-account"> <a i18n *ngIf="signupAllowed === true" routerLink="/signup" class="create-an-account">
or create an account or create an account
</a> </a>
<a *ngIf="signupAllowed === false" href="https://joinpeertube.org/en/#getting-started" target="_blank" class="create-an-account"> <a i18n *ngIf="signupAllowed === false" href="https://joinpeertube.org/en/#getting-started" target="_blank" class="create-an-account">
or create an account on another instance or create an account on another instance
</a> </a>
<my-help <my-help
*ngIf="signupAllowed === false" helpType="custom" *ngIf="signupAllowed === false" helpType="custom" i18n-customHtml
customHtml="User registration is not allowed on this instance, but you can register on many others!" customHtml="User registration is not allowed on this instance, but you can register on many others!"
></my-help> ></my-help>
</div> </div>
@ -33,13 +33,13 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="password">Password</label> <label i18n for="password">Password</label>
<div> <div>
<input <input
type="password" name="password" id="password" placeholder="Password" required tabindex="2" type="password" name="password" id="password" i18n-placeholder placeholder="Password" required tabindex="2"
formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }" formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }"
> >
<div class="forgot-password-button" (click)="openForgotPasswordModal()">I forgot my password</div> <div i18n class="forgot-password-button" (click)="openForgotPasswordModal()">I forgot my password</div>
</div> </div>
<div *ngIf="formErrors.password" class="form-error"> <div *ngIf="formErrors.password" class="form-error">
{{ formErrors.password }} {{ formErrors.password }}
@ -56,25 +56,25 @@
<div class="modal-header"> <div class="modal-header">
<span class="close" aria-hidden="true" (click)="hideForgotPasswordModal()"></span> <span class="close" aria-hidden="true" (click)="hideForgotPasswordModal()"></span>
<h4 class="modal-title">Forgot your password</h4> <h4 i18n class="modal-title">Forgot your password</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="form-group"> <div class="form-group">
<label for="forgot-password-email">Email</label> <label i18n for="forgot-password-email">Email</label>
<input <input
type="email" id="forgot-password-email" placeholder="Email address" required type="email" id="forgot-password-email" i18n-placeholder placeholder="Email address" required
[(ngModel)]="forgotPasswordEmail" #forgotPasswordEmailInput [(ngModel)]="forgotPasswordEmail" #forgotPasswordEmailInput
> >
</div> </div>
<div class="form-group inputs"> <div class="form-group inputs">
<span class="action-button action-button-cancel" (click)="hideForgotPasswordModal()"> <span i18n class="action-button action-button-cancel" (click)="hideForgotPasswordModal()">
Cancel Cancel
</span> </span>
<input <input
type="submit" value="Send me an email to reset my password" class="action-button-submit" type="submit" i18n-value value="Send me an email to reset my password" class="action-button-submit"
(click)="askResetPassword()" [disabled]="!forgotPasswordEmailInput.validity.valid" (click)="askResetPassword()" [disabled]="!forgotPasswordEmailInput.validity.valid"
> >
</div> </div>

View File

@ -7,6 +7,7 @@ import { NotificationsService } from 'angular2-notifications'
import { ModalDirective } from 'ngx-bootstrap/modal' import { ModalDirective } from 'ngx-bootstrap/modal'
import { AuthService } from '../core' import { AuthService } from '../core'
import { FormReactive } from '../shared' import { FormReactive } from '../shared'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-login', selector: 'my-login',
@ -35,12 +36,15 @@ export class LoginComponent extends FormReactive implements OnInit {
} }
forgotPasswordEmail = '' forgotPasswordEmail = ''
constructor (private authService: AuthService, constructor (
private userService: UserService, private authService: AuthService,
private serverService: ServerService, private userService: UserService,
private redirectService: RedirectService, private serverService: ServerService,
private notificationsService: NotificationsService, private redirectService: RedirectService,
private formBuilder: FormBuilder) { private notificationsService: NotificationsService,
private formBuilder: FormBuilder,
private i18n: I18n
) {
super() super()
} }
@ -78,12 +82,15 @@ export class LoginComponent extends FormReactive implements OnInit {
this.userService.askResetPassword(this.forgotPasswordEmail) this.userService.askResetPassword(this.forgotPasswordEmail)
.subscribe( .subscribe(
res => { res => {
const message = `An email with the reset password instructions will be sent to ${this.forgotPasswordEmail}.` const message = this.i18n(
this.notificationsService.success('Success', message) 'An email with the reset password instructions will be sent to {{ email }}.',
{ email: this.forgotPasswordEmail }
)
this.notificationsService.success(this.i18n('Success'), message)
this.hideForgotPasswordModal() this.hideForgotPasswordModal()
}, },
err => this.notificationsService.error('Error', err.message) err => this.notificationsService.error(this.i18n('Error'), err.message)
) )
} }

View File

@ -18,11 +18,11 @@
My public profile My public profile
</a> </a>
<a routerLink="/my-account/settings" class="dropdown-item" title="My settings"> <a i18n routerLink="/my-account/settings" class="dropdown-item" title="My settings">
My settings My settings
</a> </a>
<a (click)="logout($event)" class="dropdown-item" title="Log out" href="#"> <a i18n (click)="logout($event)" class="dropdown-item" title="Log out" href="#">
Log out Log out
</a> </a>
</li> </li>
@ -31,26 +31,26 @@
</div> </div>
<div *ngIf="!isLoggedIn" class="button-block"> <div *ngIf="!isLoggedIn" class="button-block">
<a routerLink="/login" class="login-button">Login</a> <a i18n routerLink="/login" class="login-button">Login</a>
<a *ngIf="isRegistrationAllowed()" routerLink="/signup" class="create-account-button">Create an account</a> <a i18n *ngIf="isRegistrationAllowed()" routerLink="/signup" class="create-account-button">Create an account</a>
</div> </div>
<div class="panel-block"> <div class="panel-block">
<div class="block-title">Videos</div> <div i18n class="block-title">Videos</div>
<a routerLink="/videos/trending" routerLinkActive="active"> <a routerLink="/videos/trending" routerLinkActive="active">
<span class="icon icon-videos-trending"></span> <span class="icon icon-videos-trending"></span>
Trending <ng-container i18n>Trending</ng-container>
</a> </a>
<a routerLink="/videos/recently-added" routerLinkActive="active"> <a routerLink="/videos/recently-added" routerLinkActive="active">
<span class="icon icon-videos-recently-added"></span> <span class="icon icon-videos-recently-added"></span>
Recently added <ng-container i18n>Recently added</ng-container>
</a> </a>
<a routerLink="/videos/local" routerLinkActive="active"> <a routerLink="/videos/local" routerLinkActive="active">
<span class="icon icon-videos-local"></span> <span class="icon icon-videos-local"></span>
Local <ng-container i18n>Local</ng-container>
</a> </a>
</div> </div>
@ -59,12 +59,12 @@
<a *ngIf="userHasAdminAccess" [routerLink]="getFirstAdminRouteAvailable()" routerLinkActive="active"> <a *ngIf="userHasAdminAccess" [routerLink]="getFirstAdminRouteAvailable()" routerLinkActive="active">
<span class="icon icon-administration"></span> <span class="icon icon-administration"></span>
Administration <ng-container i18n>Administration</ng-container>
</a> </a>
<a routerLink="/about" routerLinkActive="active"> <a routerLink="/about" routerLinkActive="active">
<span class="icon icon-about"></span> <span class="icon icon-about"></span>
About <ng-container i18n>About</ng-container>
</a> </a>
</div> </div>
</menu> </menu>

View File

@ -1,7 +1,7 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit } from '@angular/core'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { UserRight } from '../../../../shared/models/users/user-right.enum' import { UserRight } from '../../../../shared/models/users/user-right.enum'
import { AuthService, AuthStatus, ServerService } from '../core' import { AuthService, AuthStatus, RedirectService, ServerService } from '../core'
import { User } from '../shared/users/user.model' import { User } from '../shared/users/user.model'
@Component({ @Component({
@ -24,7 +24,7 @@ export class MenuComponent implements OnInit {
constructor ( constructor (
private authService: AuthService, private authService: AuthService,
private serverService: ServerService, private serverService: ServerService,
private router: Router private redirectService: RedirectService
) {} ) {}
ngOnInit () { ngOnInit () {
@ -87,7 +87,7 @@ export class MenuComponent implements OnInit {
this.authService.logout() this.authService.logout()
// Redirect to home page // Redirect to home page
this.router.navigate(['/videos/list']) this.redirectService.redirectToHomepage()
} }
private computeIsUserHasAdminAccess () { private computeIsUserHasAdminAccess () {

View File

@ -1,13 +1,13 @@
<div class="margin-content"> <div class="margin-content">
<div class="title-page title-page-single"> <div i18n class="title-page title-page-single">
Reset my password Reset my password
</div> </div>
<form role="form" (ngSubmit)="resetPassword()" [formGroup]="form"> <form role="form" (ngSubmit)="resetPassword()" [formGroup]="form">
<div class="form-group"> <div class="form-group">
<label for="password">Password</label> <label i18n for="password">Password</label>
<input <input
type="password" name="password" id="password" placeholder="Password" required type="password" name="password" id="password" i18n-placeholder placeholder="Password" required
formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }" formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }"
> >
<div *ngIf="formErrors.password" class="form-error"> <div *ngIf="formErrors.password" class="form-error">
@ -16,9 +16,9 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="password-confirm">Confirm password</label> <label i18n for="password-confirm">Confirm password</label>
<input <input
type="password" name="password-confirm" id="password-confirm" placeholder="Confirmed password" required type="password" name="password-confirm" id="password-confirm" i18n-placeholder placeholder="Confirmed password" required
formControlName="password-confirm" [ngClass]="{ 'input-error': formErrors['password-confirm'] }" formControlName="password-confirm" [ngClass]="{ 'input-error': formErrors['password-confirm'] }"
> >
<div *ngIf="formErrors['password-confirm']" class="form-error"> <div *ngIf="formErrors['password-confirm']" class="form-error">
@ -26,6 +26,6 @@
</div> </div>
</div> </div>
<input type="submit" value="Reset my password" [disabled]="!form.valid && isConfirmedPasswordValid()"> <input type="submit" i18n-value value="Reset my password" [disabled]="!form.valid && isConfirmedPasswordValid()">
</form> </form>
</div> </div>

View File

@ -5,6 +5,7 @@ import { USER_PASSWORD, UserService } from '@app/shared'
import { NotificationsService } from 'angular2-notifications' import { NotificationsService } from 'angular2-notifications'
import { AuthService } from '../core' import { AuthService } from '../core'
import { FormReactive } from '../shared' import { FormReactive } from '../shared'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-login', selector: 'my-login',
@ -34,7 +35,8 @@ export class ResetPasswordComponent extends FormReactive implements OnInit {
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private router: Router, private router: Router,
private route: ActivatedRoute private route: ActivatedRoute,
private i18n: I18n
) { ) {
super() super()
} }
@ -55,7 +57,7 @@ export class ResetPasswordComponent extends FormReactive implements OnInit {
this.verificationString = this.route.snapshot.queryParams['verificationString'] this.verificationString = this.route.snapshot.queryParams['verificationString']
if (!this.userId || !this.verificationString) { if (!this.userId || !this.verificationString) {
this.notificationsService.error('Error', 'Unable to find user id or verification string.') this.notificationsService.error(this.i18n('Error'), this.i18n('Unable to find user id or verification string.'))
this.router.navigate([ '/' ]) this.router.navigate([ '/' ])
} }
} }
@ -64,7 +66,7 @@ export class ResetPasswordComponent extends FormReactive implements OnInit {
this.userService.resetPassword(this.userId, this.verificationString, this.form.value.password) this.userService.resetPassword(this.userId, this.verificationString, this.form.value.password)
.subscribe( .subscribe(
() => { () => {
this.notificationsService.success('Success', 'Your password has been successfully reset!') this.notificationsService.success(this.i18n('Success'), this.i18n('Your password has been successfully reset!'))
this.router.navigate([ '/login' ]) this.router.navigate([ '/login' ])
}, },

View File

@ -6,7 +6,7 @@
</textarea> </textarea>
<tabset *ngIf="arePreviewsDisplayed()" class="previews"> <tabset *ngIf="arePreviewsDisplayed()" class="previews">
<tab *ngIf="truncate !== undefined" heading="Truncated preview" [innerHTML]="truncatedPreviewHTML"></tab> <tab *ngIf="truncate !== undefined" i18n-heading heading="Truncated preview" [innerHTML]="truncatedPreviewHTML"></tab>
<tab heading="Complete preview" [innerHTML]="previewHTML"></tab> <tab i18n-heading heading="Complete preview" [innerHTML]="previewHTML"></tab>
</tabset> </tabset>
</div> </div>

View File

@ -3,7 +3,7 @@ import { Component, forwardRef, Input, OnInit } from '@angular/core'
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
import { isInSmallView } from '@app/shared/misc/utils' import { isInSmallView } from '@app/shared/misc/utils'
import { MarkdownService } from '@app/videos/shared' import { MarkdownService } from '@app/videos/shared'
import { Subject } from 'rxjs' import { Subject } from 'rxjs/Subject'
import truncate from 'lodash-es/truncate' import truncate from 'lodash-es/truncate'
@Component({ @Component({

View File

@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'
import { ActivatedRouteSnapshot, CanDeactivate, RouterStateSnapshot } from '@angular/router' import { ActivatedRouteSnapshot, CanDeactivate, RouterStateSnapshot } from '@angular/router'
import { Observable } from 'rxjs' import { Observable } from 'rxjs'
import { ConfirmService } from '../../core/index' import { ConfirmService } from '../../core/index'
import { I18n } from '@ngx-translate/i18n-polyfill'
export interface CanComponentDeactivate { export interface CanComponentDeactivate {
canDeactivate: () => { text?: string, canDeactivate: Observable<boolean> | boolean } canDeactivate: () => { text?: string, canDeactivate: Observable<boolean> | boolean }
@ -9,7 +10,10 @@ export interface CanComponentDeactivate {
@Injectable() @Injectable()
export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> { export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
constructor (private confirmService: ConfirmService) { } constructor (
private confirmService: ConfirmService,
private i18n: I18n
) { }
canDeactivate (component: CanComponentDeactivate, canDeactivate (component: CanComponentDeactivate,
currentRoute: ActivatedRouteSnapshot, currentRoute: ActivatedRouteSnapshot,
@ -17,11 +21,11 @@ export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate>
nextState: RouterStateSnapshot nextState: RouterStateSnapshot
) { ) {
const result = component.canDeactivate() const result = component.canDeactivate()
const text = result.text || 'All unsaved data will be lost, are you sure you want to leave this page?' const text = result.text || this.i18n('All unsaved data will be lost, are you sure you want to leave this page?')
return result.canDeactivate || this.confirmService.confirm( return result.canDeactivate || this.confirmService.confirm(
text, text,
'Warning' this.i18n('Warning')
) )
} }

View File

@ -1,4 +1,4 @@
<a class="action-button action-button-edit" [routerLink]="routerLink" title="Edit"> <a class="action-button action-button-edit" [routerLink]="routerLink" title="Edit">
<span class="icon icon-edit"></span> <span class="icon icon-edit"></span>
<span class="button-label">Edit</span> <span i18n class="button-label">Edit</span>
</a> </a>

View File

@ -1,36 +1,39 @@
import { Pipe, PipeTransform } from '@angular/core' import { Pipe, PipeTransform } from '@angular/core'
import { I18n } from '@ngx-translate/i18n-polyfill'
// Thanks: https://stackoverflow.com/questions/3177836/how-to-format-time-since-xxx-e-g-4-minutes-ago-similar-to-stack-exchange-site // Thanks: https://stackoverflow.com/questions/3177836/how-to-format-time-since-xxx-e-g-4-minutes-ago-similar-to-stack-exchange-site
@Pipe({ name: 'myFromNow' }) @Pipe({ name: 'myFromNow' })
export class FromNowPipe implements PipeTransform { export class FromNowPipe implements PipeTransform {
constructor (private i18n: I18n) { }
transform (value: number) { transform (value: number) {
const seconds = Math.floor((Date.now() - value) / 1000) const seconds = Math.floor((Date.now() - value) / 1000)
let interval = Math.floor(seconds / 31536000) let interval = Math.floor(seconds / 31536000)
if (interval > 1) { if (interval > 1) {
return interval + ' years ago' return this.i18n('{{ interval }} years ago', { interval })
} }
interval = Math.floor(seconds / 2592000) interval = Math.floor(seconds / 2592000)
if (interval > 1) return interval + ' months ago' if (interval > 1) return this.i18n('{{ interval }} months ago', { interval })
if (interval === 1) return interval + ' month ago' if (interval === 1) return this.i18n('{{ interval }} month ago', { interval })
interval = Math.floor(seconds / 604800) interval = Math.floor(seconds / 604800)
if (interval > 1) return interval + ' weeks ago' if (interval > 1) return this.i18n('{{ interval }} weeks ago', { interval })
if (interval === 1) return interval + ' week ago' if (interval === 1) return this.i18n('{{ interval }} week ago', { interval })
interval = Math.floor(seconds / 86400) interval = Math.floor(seconds / 86400)
if (interval > 1) return interval + ' days ago' if (interval > 1) return this.i18n('{{ interval }} days ago', { interval })
if (interval === 1) return interval + ' day ago' if (interval === 1) return this.i18n('{{ interval }} day ago', { interval })
interval = Math.floor(seconds / 3600) interval = Math.floor(seconds / 3600)
if (interval > 1) return interval + ' hours ago' if (interval > 1) return this.i18n('{{ interval }} hours ago', { interval })
if (interval === 1) return interval + ' hour ago' if (interval === 1) return this.i18n('{{ interval }} hour ago', { interval })
interval = Math.floor(seconds / 60) interval = Math.floor(seconds / 60)
if (interval >= 1) return interval + ' min ago' if (interval >= 1) return this.i18n('{{ interval }} min ago', { interval })
return Math.floor(seconds) + ' sec ago' return this.i18n('{{ interval }} sec ago', { interval: Math.floor(seconds) })
} }
} }

View File

@ -15,6 +15,7 @@
<span <span
class="help-tooltip-button" class="help-tooltip-button"
title="Get help" title="Get help"
i18n-title
[popover]="tooltipTemplate" [popover]="tooltipTemplate"
[placement]="tooltipPlacement" [placement]="tooltipPlacement"
[outsideClick]="true" [outsideClick]="true"

View File

@ -3,11 +3,15 @@ import { Injectable } from '@angular/core'
import { dateToHuman } from '@app/shared/misc/utils' import { dateToHuman } from '@app/shared/misc/utils'
import { ResultList } from '../../../../../shared' import { ResultList } from '../../../../../shared'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Injectable() @Injectable()
export class RestExtractor { export class RestExtractor {
constructor (private router: Router) { constructor (
private router: Router,
private i18n: I18n
) {
// empty // empty
} }
@ -60,17 +64,19 @@ export class RestExtractor {
} else if (err.error && err.error.error) { } else if (err.error && err.error.error) {
errorMessage = err.error.error errorMessage = err.error.error
} else if (err.status === 413) { } else if (err.status === 413) {
errorMessage = 'Request is too large for the server. Please contact you administrator if you want to increase the limit size.' errorMessage = this.i18n(
'Request is too large for the server. Please contact you administrator if you want to increase the limit size.'
)
} else if (err.status === 429) { } else if (err.status === 429) {
const secondsLeft = err.headers.get('retry-after') const secondsLeft = err.headers.get('retry-after')
if (secondsLeft) { if (secondsLeft) {
const minutesLeft = Math.floor(parseInt(secondsLeft, 10) / 60) const minutesLeft = Math.floor(parseInt(secondsLeft, 10) / 60)
errorMessage = 'Too many attempts, please try again after ' + minutesLeft + ' minutes.' errorMessage = this.i18n('Too many attempts, please try again after {{ minutesLeft }} minutes.', { minutesLeft })
} else { } else {
errorMessage = 'Too many attempts, please try again later.' errorMessage = this.i18n('Too many attempts, please try again later.')
} }
} else if (err.status === 500) { } else if (err.status === 500) {
errorMessage = 'Server error. Please retry later.' errorMessage = this.i18n('Server error. Please retry later.')
} }
errorMessage = errorMessage ? errorMessage : 'Unknown error.' errorMessage = errorMessage ? errorMessage : 'Unknown error.'

View File

@ -4,7 +4,7 @@
</div> </div>
<my-video-feed [syndicationItems]="syndicationItems"></my-video-feed> <my-video-feed [syndicationItems]="syndicationItems"></my-video-feed>
<div *ngIf="pagination.totalItems === 0">No results.</div> <div i18n *ngIf="pagination.totalItems === 0">No results.</div>
<div <div
myInfiniteScroller myInfiniteScroller
[pageHeight]="pageHeight" [pageHeight]="pageHeight"

View File

@ -10,6 +10,7 @@ import { AuthService } from '../../core/auth'
import { ComponentPagination } from '../rest/component-pagination.model' import { ComponentPagination } from '../rest/component-pagination.model'
import { VideoSortField } from './sort-field.type' import { VideoSortField } from './sort-field.type'
import { Video } from './video.model' import { Video } from './video.model'
import { I18n } from '@ngx-translate/i18n-polyfill'
export abstract class AbstractVideoList implements OnInit, OnDestroy { export abstract class AbstractVideoList implements OnInit, OnDestroy {
private static LINES_PER_PAGE = 4 private static LINES_PER_PAGE = 4
@ -40,6 +41,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
protected abstract authService: AuthService protected abstract authService: AuthService
protected abstract router: Router protected abstract router: Router
protected abstract route: ActivatedRoute protected abstract route: ActivatedRoute
protected abstract i18n: I18n
protected abstract location: Location protected abstract location: Location
protected abstract currentRoute: string protected abstract currentRoute: string
abstract titlePage: string abstract titlePage: string
@ -124,7 +126,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
}, },
error => { error => {
this.loadingPage[page] = false this.loadingPage[page] = false
this.notificationsService.error('Error', error.message) this.notificationsService.error(this.i18n('Error'), error.message)
} }
) )
} }

View File

@ -9,7 +9,7 @@
{{ video.name }} {{ video.name }}
</a> </a>
<span class="video-miniature-created-at-views">{{ video.publishedAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span> <span i18n class="video-miniature-created-at-views">{{ video.publishedAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span>
<a class="video-miniature-account" [routerLink]="[ '/accounts', video.by ]">{{ video.by }}</a> <a class="video-miniature-account" [routerLink]="[ '/accounts', video.by ]">{{ video.by }}</a>
</div> </div>
</div> </div>

View File

@ -1,6 +1,6 @@
<a <a
[routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name"
class="video-thumbnail" class="video-thumbnail"
> >
<img [attr.src]="getImageUrl()" [ngClass]="{ 'blur-filter': nsfw }" /> <img [attr.src]="getImageUrl()" [ngClass]="{ 'blur-filter': nsfw }" />

View File

@ -1,11 +1,11 @@
<div class="margin-content"> <div class="margin-content">
<div class="title-page title-page-single"> <div i18n class="title-page title-page-single">
Create an account Create an account
</div> </div>
<div class="initial-user-quota"> <div class="initial-user-quota">
<span class="initial-user-quota-label">Initial video quota:</span> <span i18n class="initial-user-quota-label">Initial video quota:</span>
<span *ngIf="initialUserVideoQuota !== -1"> <span *ngIf="initialUserVideoQuota !== -1">
{{ initialUserVideoQuota | bytes: 0 }} {{ initialUserVideoQuota | bytes: 0 }}
@ -13,18 +13,18 @@
<my-help helpType="custom" [customHtml]="quotaHelpIndication"></my-help> <my-help helpType="custom" [customHtml]="quotaHelpIndication"></my-help>
</span> </span>
<ng-template [ngIf]="initialUserVideoQuota === -1"> <ng-container i18n *ngIf="initialUserVideoQuota === -1">
Unlimited Unlimited
</ng-template> </ng-container>
</div> </div>
<div *ngIf="error" class="alert alert-danger">{{ error }}</div> <div *ngIf="error" class="alert alert-danger">{{ error }}</div>
<form role="form" (ngSubmit)="signup()" [formGroup]="form"> <form role="form" (ngSubmit)="signup()" [formGroup]="form">
<div class="form-group"> <div class="form-group">
<label for="username">Username</label> <label for="username" i18n>Username</label>
<input <input
type="text" id="username" placeholder="Username" type="text" id="username" i18n-placeholder placeholder="Username"
formControlName="username" [ngClass]="{ 'input-error': formErrors['username'] }" formControlName="username" [ngClass]="{ 'input-error': formErrors['username'] }"
> >
<div *ngIf="formErrors.username" class="form-error"> <div *ngIf="formErrors.username" class="form-error">
@ -33,9 +33,9 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="email">Email</label> <label for="email" i18n>Email</label>
<input <input
type="text" id="email" placeholder="Email" type="text" id="email" i18n-placeholder placeholder="Email"
formControlName="email" [ngClass]="{ 'input-error': formErrors['email'] }" formControlName="email" [ngClass]="{ 'input-error': formErrors['email'] }"
> >
<div *ngIf="formErrors.email" class="form-error"> <div *ngIf="formErrors.email" class="form-error">
@ -44,9 +44,9 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="password">Password</label> <label for="password" i18n>Password</label>
<input <input
type="password" id="password" placeholder="Password" type="password" id="password" i18n-placeholder placeholder="Password"
formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }" formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }"
> >
<div *ngIf="formErrors.password" class="form-error"> <div *ngIf="formErrors.password" class="form-error">
@ -54,7 +54,7 @@
</div> </div>
</div> </div>
<input type="submit" value="Signup" [disabled]="!form.valid"> <input type="submit" i18n-value value="Signup" [disabled]="!form.valid">
</form> </form>
</div> </div>

View File

@ -6,6 +6,8 @@ import { ServerService } from '@app/core/server'
import { NotificationsService } from 'angular2-notifications' import { NotificationsService } from 'angular2-notifications'
import { UserCreate } from '../../../../shared' import { UserCreate } from '../../../../shared'
import { FormReactive, USER_EMAIL, USER_PASSWORD, USER_USERNAME, UserService } from '../shared' import { FormReactive, USER_EMAIL, USER_PASSWORD, USER_USERNAME, UserService } from '../shared'
import { RedirectService } from '@app/core'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-signup', selector: 'my-signup',
@ -45,7 +47,9 @@ export class SignupComponent extends FormReactive implements OnInit {
private router: Router, private router: Router,
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private userService: UserService, private userService: UserService,
private serverService: ServerService private redirectService: RedirectService,
private serverService: ServerService,
private i18n: I18n
) { ) {
super() super()
} }
@ -78,8 +82,11 @@ export class SignupComponent extends FormReactive implements OnInit {
this.userService.signup(userCreate).subscribe( this.userService.signup(userCreate).subscribe(
() => { () => {
this.notificationsService.success('Success', `Registration for ${userCreate.username} complete.`) this.notificationsService.success(
this.router.navigate([ '/videos/list' ]) this.i18n('Success'),
this.i18n('Registration for {{ username }} complete.', { username: userCreate.username})
)
this.redirectService.redirectToHomepage()
}, },
err => this.error = err.message err => this.error = err.message
@ -99,9 +106,9 @@ export class SignupComponent extends FormReactive implements OnInit {
const normalSeconds = initialUserVideoQuotaBit / (1.5 * 1000 * 1000) const normalSeconds = initialUserVideoQuotaBit / (1.5 * 1000 * 1000)
const lines = [ const lines = [
SignupComponent.getApproximateTime(fullHdSeconds) + ' of full HD videos', this.i18n('{{ seconds }} of full HD videos', { seconds: SignupComponent.getApproximateTime(fullHdSeconds) }),
SignupComponent.getApproximateTime(hdSeconds) + ' of HD videos', this.i18n('{{ seconds }} of HD videos', { seconds: SignupComponent.getApproximateTime(hdSeconds) }),
SignupComponent.getApproximateTime(normalSeconds) + ' of average quality videos' this.i18n('{{ seconds }} of average quality videos', { seconds: SignupComponent.getApproximateTime(normalSeconds) })
] ]
this.quotaHelpIndication = lines.join('<br />') this.quotaHelpIndication = lines.join('<br />')

View File

@ -4,7 +4,7 @@
<tab heading="Basic info"> <tab heading="Basic info">
<div class="col-md-8"> <div class="col-md-8">
<div class="form-group"> <div class="form-group">
<label for="name">Title</label> <label i18n for="name">Title</label>
<input type="text" id="name" formControlName="name" /> <input type="text" id="name" formControlName="name" />
<div *ngIf="formErrors.name" class="form-error"> <div *ngIf="formErrors.name" class="form-error">
{{ formErrors.name }} {{ formErrors.name }}
@ -12,7 +12,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="label-tags">Tags</label> <span>(press Enter to add)</span> <label i18n class="label-tags">Tags</label> <span i18n>(press Enter to add)</span>
<tag-input <tag-input
[validators]="tagValidators" [errorMessages]="tagValidatorsMessages" [validators]="tagValidators" [errorMessages]="tagValidatorsMessages"
formControlName="tags" maxItems="5" modelAsStrings="true" formControlName="tags" maxItems="5" modelAsStrings="true"
@ -20,8 +20,8 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="description">Description</label> <label i18n for="description">Description</label>
<my-help helpType="markdownText" preHtml="Video descriptions are truncated by default and require manual action to expand them."></my-help> <my-help helpType="markdownText" i18n-preHtml preHtml="Video descriptions are truncated by default and require manual action to expand them."></my-help>
<my-markdown-textarea truncate="250" formControlName="description"></my-markdown-textarea> <my-markdown-textarea truncate="250" formControlName="description"></my-markdown-textarea>
<div *ngIf="formErrors.description" class="form-error"> <div *ngIf="formErrors.description" class="form-error">
@ -32,7 +32,7 @@
<div class="col-md-4"> <div class="col-md-4">
<div class="form-group"> <div class="form-group">
<label>Channel</label> <label i18n>Channel</label>
<div class="peertube-select-container"> <div class="peertube-select-container">
<select formControlName="channelId"> <select formControlName="channelId">
<option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option> <option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option>
@ -41,7 +41,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="category">Category</label> <label i18n for="category">Category</label>
<div class="peertube-select-container"> <div class="peertube-select-container">
<select id="category" formControlName="category"> <select id="category" formControlName="category">
<option></option> <option></option>
@ -55,7 +55,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="licence">Licence</label> <label i18n for="licence">Licence</label>
<div class="peertube-select-container"> <div class="peertube-select-container">
<select id="licence" formControlName="licence"> <select id="licence" formControlName="licence">
<option></option> <option></option>
@ -69,7 +69,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="language">Language</label> <label i18n for="language">Language</label>
<div class="peertube-select-container"> <div class="peertube-select-container">
<select id="language" formControlName="language"> <select id="language" formControlName="language">
<option></option> <option></option>
@ -83,7 +83,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="privacy">Privacy</label> <label i18n for="privacy">Privacy</label>
<div class="peertube-select-container"> <div class="peertube-select-container">
<select id="privacy" formControlName="privacy"> <select id="privacy" formControlName="privacy">
<option></option> <option></option>
@ -99,14 +99,14 @@
<div class="form-group form-group-checkbox"> <div class="form-group form-group-checkbox">
<input type="checkbox" id="nsfw" formControlName="nsfw" /> <input type="checkbox" id="nsfw" formControlName="nsfw" />
<label for="nsfw"></label> <label for="nsfw"></label>
<label for="nsfw">This video contains mature or explicit content</label> <label i18n for="nsfw">This video contains mature or explicit content</label>
<my-help tooltipPlacement="top" helpType="custom" customHtml="Some instances do not list NSFW videos by default."></my-help> <my-help tooltipPlacement="top" helpType="custom" i18n-customHtml customHtml="Some instances do not list NSFW videos by default."></my-help>
</div> </div>
<div class="form-group form-group-checkbox"> <div class="form-group form-group-checkbox">
<input type="checkbox" id="commentsEnabled" formControlName="commentsEnabled" /> <input type="checkbox" id="commentsEnabled" formControlName="commentsEnabled" />
<label for="commentsEnabled"></label> <label for="commentsEnabled"></label>
<label for="commentsEnabled">Enable video comments</label> <label i18n for="commentsEnabled">Enable video comments</label>
</div> </div>
</div> </div>
@ -116,24 +116,24 @@
<div class="col-md-12 advanced-settings"> <div class="col-md-12 advanced-settings">
<div class="form-group"> <div class="form-group">
<my-video-image <my-video-image
inputLabel="Upload thumbnail" inputName="thumbnailfile" formControlName="thumbnailfile" i18n-inputLabel inputLabel="Upload thumbnail" inputName="thumbnailfile" formControlName="thumbnailfile"
previewWidth="200px" previewHeight="110px" previewWidth="200px" previewHeight="110px"
></my-video-image> ></my-video-image>
</div> </div>
<div class="form-group"> <div class="form-group">
<my-video-image <my-video-image
inputLabel="Upload preview" inputName="previewfile" formControlName="previewfile" i18n-inputLabel inputLabel="Upload preview" inputName="previewfile" formControlName="previewfile"
previewWidth="360px" previewHeight="200px" previewWidth="360px" previewHeight="200px"
></my-video-image> ></my-video-image>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="support">Support</label> <label i18n for="support">Support</label>
<my-help helpType="markdownEnhanced" preHtml="Short text to tell people how they can support you (membership platform...)."></my-help> <my-help helpType="markdownEnhanced" i18n-preHtml preHtml="Short text to tell people how they can support you (membership platform...)."></my-help>
<my-markdown-textarea <my-markdown-textarea
id="support" formControlName="support" textareaWidth="500px" [previewColumn]="true" markdownType="enhanced" id="support" formControlName="support" textareaWidth="500px" [previewColumn]="true" markdownType="enhanced"
[classes]="{ 'input-error': formErrors['support'] }" [classes]="{ 'input-error': formErrors['support'] }"
></my-markdown-textarea> ></my-markdown-textarea>
<div *ngIf="formErrors.support" class="form-error"> <div *ngIf="formErrors.support" class="form-error">
{{ formErrors.support }} {{ formErrors.support }}

View File

@ -8,7 +8,7 @@
(change)="fileChange($event)" (change)="fileChange($event)"
/> />
</div> </div>
<div class="image-constraints">(extensions: {{ videoImageExtensions }}, max size: {{ maxVideoImageSize | bytes }})</div> <div i18n class="image-constraints">(extensions: {{ videoImageExtensions }}, max size: {{ maxVideoImageSize | bytes }})</div>
</div> </div>
<img *ngIf="imageSrc" [ngStyle]="{ width: previewWidth, height: previewHeight }" [src]="imageSrc" class="preview" /> <img *ngIf="imageSrc" [ngStyle]="{ width: previewWidth, height: previewHeight }" [src]="imageSrc" class="preview" />

View File

@ -1,7 +1,7 @@
<div class="margin-content"> <div class="margin-content">
<div class="title-page title-page-single"> <div class="title-page title-page-single">
<ng-template [ngIf]="!videoFileName">Upload your video</ng-template> <ng-container *ngIf="!videoFileName" i18n>Upload your video</ng-container>
<ng-template [ngIf]="videoFileName">Upload {{ videoFileName }}</ng-template> <ng-container *ngIf="videoFileName" i18n>Upload {{ videoFileName }}</ng-container>
</div> </div>
<div *ngIf="!isUploadingVideo" class="upload-video-container"> <div *ngIf="!isUploadingVideo" class="upload-video-container">
@ -9,12 +9,12 @@
<div class="icon icon-upload"></div> <div class="icon icon-upload"></div>
<div class="button-file"> <div class="button-file">
<span>Select the file to upload</span> <span i18n>Select the file to upload</span>
<input #videofileInput type="file" name="videofile" id="videofile" [accept]="videoExtensions" (change)="fileChange()" /> <input #videofileInput type="file" name="videofile" id="videofile" [accept]="videoExtensions" (change)="fileChange()" />
</div> </div>
<div class="form-group form-group-channel"> <div class="form-group form-group-channel">
<label for="first-step-channel">Channel</label> <label i18n for="first-step-channel">Channel</label>
<div class="peertube-select-container"> <div class="peertube-select-container">
<select id="first-step-channel" [(ngModel)]="firstStepChannelId"> <select id="first-step-channel" [(ngModel)]="firstStepChannelId">
<option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option> <option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option>
@ -23,7 +23,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="first-step-privacy">Privacy</label> <label i18n for="first-step-privacy">Privacy</label>
<div class="peertube-select-container"> <div class="peertube-select-container">
<select id="first-step-privacy" [(ngModel)]="firstStepPrivacyId"> <select id="first-step-privacy" [(ngModel)]="firstStepPrivacyId">
<option *ngFor="let privacy of videoPrivacies" [value]="privacy.id">{{ privacy.label }}</option> <option *ngFor="let privacy of videoPrivacies" [value]="privacy.id">{{ privacy.label }}</option>
@ -49,14 +49,14 @@
></my-video-edit> ></my-video-edit>
<div class="submit-container"> <div class="submit-container">
<div *ngIf="videoUploaded === false" class="message-submit">Publish will be available when upload is finished</div> <div i18n *ngIf="videoUploaded === false" class="message-submit">Publish will be available when upload is finished</div>
<div class="submit-button" <div class="submit-button"
(click)="updateSecondStep()" (click)="updateSecondStep()"
[ngClass]="{ disabled: !form.valid || isUpdatingVideo === true || videoUploaded !== true }" [ngClass]="{ disabled: !form.valid || isUpdatingVideo === true || videoUploaded !== true }"
> >
<span class="icon icon-validate"></span> <span class="icon icon-validate"></span>
<input type="button" value="Publish" /> <input type="button" i18n-value value="Publish" />
</div> </div>
</div> </div>
</form> </form>

View File

@ -15,6 +15,7 @@ import { ValidatorMessage } from '../../shared/forms/form-validators/validator-m
import { populateAsyncUserVideoChannels } from '../../shared/misc/utils' import { populateAsyncUserVideoChannels } from '../../shared/misc/utils'
import { VideoEdit } from '../../shared/video/video-edit.model' import { VideoEdit } from '../../shared/video/video-edit.model'
import { VideoService } from '../../shared/video/video.service' import { VideoService } from '../../shared/video/video.service'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-videos-add', selector: 'my-videos-add',
@ -56,7 +57,8 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
private userService: UserService, private userService: UserService,
private serverService: ServerService, private serverService: ServerService,
private videoService: VideoService, private videoService: VideoService,
private loadingBar: LoadingBarService private loadingBar: LoadingBarService,
private i18n: I18n
) { ) {
super() super()
} }
@ -99,10 +101,11 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
let text = '' let text = ''
if (this.videoUploaded === true) { if (this.videoUploaded === true) {
text = 'Your video was uploaded in your account and is private.' + // FIXME: cannot concatenate strings inside i18n service :/
' But associated data (tags, description...) will be lost, are you sure you want to leave this page?' text = this.i18n('Your video was uploaded in your account and is private.') +
this.i18n('But associated data (tags, description...) will be lost, are you sure you want to leave this page?')
} else { } else {
text = 'Your video is not uploaded yet, are you sure you want to leave this page?' text = this.i18n('Your video is not uploaded yet, are you sure you want to leave this page?')
} }
return { return {
@ -127,7 +130,7 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
this.isUploadingVideo = false this.isUploadingVideo = false
this.videoUploadPercents = 0 this.videoUploadPercents = 0
this.videoUploadObservable = null this.videoUploadObservable = null
this.notificationsService.info('Info', 'Upload cancelled') this.notificationsService.info(this.i18n('Info'), this.i18n('Upload cancelled'))
} }
} }
@ -137,7 +140,7 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
// Cannot upload videos > 4GB for now // Cannot upload videos > 4GB for now
if (videofile.size > 4 * 1024 * 1024 * 1024) { if (videofile.size > 4 * 1024 * 1024 * 1024) {
this.notificationsService.error('Error', 'We are sorry but PeerTube cannot handle videos > 4GB') this.notificationsService.error(this.i18n('Error'), this.i18n('We are sorry but PeerTube cannot handle videos > 4GB'))
return return
} }
@ -145,11 +148,15 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
if (videoQuota !== -1 && (this.userVideoQuotaUsed + videofile.size) > videoQuota) { if (videoQuota !== -1 && (this.userVideoQuotaUsed + videofile.size) > videoQuota) {
const bytePipes = new BytesPipe() const bytePipes = new BytesPipe()
const msg = 'Your video quota is exceeded with this video ' + const msg = this.i18n(
`(video size: ${bytePipes.transform(videofile.size, 0)}, ` + 'Your video quota is exceeded with this video (video size: {{ videoSize }}, used: {{ videoQuotaUsed }}, quota: {{ videoQuota }})',
`used: ${bytePipes.transform(this.userVideoQuotaUsed, 0)}, ` + {
`quota: ${bytePipes.transform(videoQuota, 0)})` videoSize: bytePipes.transform(videofile.size, 0),
this.notificationsService.error('Error', msg) videoQuotaUsed: bytePipes.transform(this.userVideoQuotaUsed, 0),
videoQuota: bytePipes.transform(videoQuota, 0)
}
)
this.notificationsService.error(this.i18n('Error'), msg)
return return
} }
@ -192,8 +199,6 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
if (event.type === HttpEventType.UploadProgress) { if (event.type === HttpEventType.UploadProgress) {
this.videoUploadPercents = Math.round(100 * event.loaded / event.total) this.videoUploadPercents = Math.round(100 * event.loaded / event.total)
} else if (event instanceof HttpResponse) { } else if (event instanceof HttpResponse) {
console.log('Video uploaded.')
this.videoUploaded = true this.videoUploaded = true
this.videoUploadedIds = event.body.video this.videoUploadedIds = event.body.video
@ -207,7 +212,7 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
this.isUploadingVideo = false this.isUploadingVideo = false
this.videoUploadPercents = 0 this.videoUploadPercents = 0
this.videoUploadObservable = null this.videoUploadObservable = null
this.notificationsService.error('Error', err.message) this.notificationsService.error(this.i18n('Error'), err.message)
} }
) )
} }
@ -231,13 +236,13 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
this.isUploadingVideo = false this.isUploadingVideo = false
this.loadingBar.complete() this.loadingBar.complete()
this.notificationsService.success('Success', 'Video published.') this.notificationsService.success(this.i18n('Success'), this.i18n('Video published.'))
this.router.navigate([ '/videos/watch', video.uuid ]) this.router.navigate([ '/videos/watch', video.uuid ])
}, },
err => { err => {
this.isUpdatingVideo = false this.isUpdatingVideo = false
this.notificationsService.error('Error', err.message) this.notificationsService.error(this.i18n('Error'), err.message)
console.error(err) console.error(err)
} }
) )

View File

@ -1,5 +1,5 @@
<div class="margin-content"> <div class="margin-content">
<div class="title-page title-page-single"> <div i18n class="title-page title-page-single">
Update {{ video?.name }} Update {{ video?.name }}
</div> </div>
@ -13,7 +13,7 @@
<div class="submit-container"> <div class="submit-container">
<div class="submit-button" (click)="update()" [ngClass]="{ disabled: !form.valid || isUpdatingVideo === true }"> <div class="submit-button" (click)="update()" [ngClass]="{ disabled: !form.valid || isUpdatingVideo === true }">
<span class="icon icon-validate"></span> <span class="icon icon-validate"></span>
<input type="button" value="Update" /> <input type="button" i18n-value value="Update" />
</div> </div>
</div> </div>
</form> </form>

View File

@ -12,6 +12,7 @@ import { ValidatorMessage } from '../../shared/forms/form-validators/validator-m
import { VideoEdit } from '../../shared/video/video-edit.model' import { VideoEdit } from '../../shared/video/video-edit.model'
import { VideoService } from '../../shared/video/video.service' import { VideoService } from '../../shared/video/video.service'
import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-videos-update', selector: 'my-videos-update',
@ -37,7 +38,8 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
private videoService: VideoService, private videoService: VideoService,
private authService: AuthService, private authService: AuthService,
private loadingBar: LoadingBarService, private loadingBar: LoadingBarService,
private videoChannelService: VideoChannelService private videoChannelService: VideoChannelService,
private i18n: I18n
) { ) {
super() super()
} }
@ -91,7 +93,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
err => { err => {
console.error(err) console.error(err)
this.notificationsService.error('Error', err.message) this.notificationsService.error(this.i18n('Error'), err.message)
} }
) )
} }
@ -116,13 +118,13 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
() => { () => {
this.isUpdatingVideo = false this.isUpdatingVideo = false
this.loadingBar.complete() this.loadingBar.complete()
this.notificationsService.success('Success', 'Video updated.') this.notificationsService.success(this.i18n('Success'), this.i18n('Video updated.'))
this.router.navigate([ '/videos/watch', this.video.uuid ]) this.router.navigate([ '/videos/watch', this.video.uuid ])
}, },
err => { err => {
this.isUpdatingVideo = false this.isUpdatingVideo = false
this.notificationsService.error('Error', err.message) this.notificationsService.error(this.i18n('Error'), err.message)
console.error(err) console.error(err)
} }
) )

View File

@ -3,7 +3,7 @@
<img [src]="user.accountAvatarUrl" alt="Avatar" /> <img [src]="user.accountAvatarUrl" alt="Avatar" />
<div class="form-group"> <div class="form-group">
<textarea placeholder="Add comment..." formControlName="text" [ngClass]="{ 'input-error': formErrors['text'] }" <textarea i18n-placeholder placeholder="Add comment..." formControlName="text" [ngClass]="{ 'input-error': formErrors['text'] }"
(keyup.control.enter)="onValidKey()" (keyup.meta.enter)="onValidKey()" #textarea> (keyup.control.enter)="onValidKey()" (keyup.meta.enter)="onValidKey()" #textarea>
</textarea> </textarea>
@ -14,7 +14,7 @@
</div> </div>
<div class="submit-comment"> <div class="submit-comment">
<button *ngIf="isAddButtonDisplayed()" [ngClass]="{ disabled: !form.valid }"> <button *ngIf="isAddButtonDisplayed()" [ngClass]="{ disabled: !form.valid }" i18n>
Post comment Post comment
</button> </button>
</div> </div>

View File

@ -9,6 +9,7 @@ import { User } from '../../../shared/users'
import { Video } from '../../../shared/video/video.model' import { Video } from '../../../shared/video/video.model'
import { VideoComment } from './video-comment.model' import { VideoComment } from './video-comment.model'
import { VideoCommentService } from './video-comment.service' import { VideoCommentService } from './video-comment.service'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-video-comment-add', selector: 'my-video-comment-add',
@ -37,7 +38,8 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
constructor ( constructor (
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private videoCommentService: VideoCommentService private videoCommentService: VideoCommentService,
private i18n: I18n
) { ) {
super() super()
} }
@ -92,7 +94,7 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
this.form.reset() this.form.reset()
}, },
err => this.notificationsService.error('Error', err.text) err => this.notificationsService.error(this.i18n('Error'), err.text)
) )
} }

View File

@ -2,7 +2,7 @@
<img [src]="comment.accountAvatarUrl" alt="Avatar" /> <img [src]="comment.accountAvatarUrl" alt="Avatar" />
<div class="comment"> <div class="comment">
<div *ngIf="highlightedComment === true" class="highlighted-comment">Highlighted comment</div> <div *ngIf="highlightedComment === true" class="highlighted-comment" i18n>Highlighted comment</div>
<div class="comment-account-date"> <div class="comment-account-date">
<a [href]="comment.account.url" target="_blank" rel="noopener noreferrer" class="comment-account">{{ comment.by }}</a> <a [href]="comment.account.url" target="_blank" rel="noopener noreferrer" class="comment-account">{{ comment.by }}</a>
@ -11,8 +11,8 @@
<div class="comment-html" [innerHTML]="sanitizedCommentHTML"></div> <div class="comment-html" [innerHTML]="sanitizedCommentHTML"></div>
<div class="comment-actions"> <div class="comment-actions">
<div *ngIf="isUserLoggedIn()" (click)="onWantToReply()" class="comment-action-reply">Reply</div> <div *ngIf="isUserLoggedIn()" (click)="onWantToReply()" class="comment-action-reply" i18n>Reply</div>
<div *ngIf="isRemovableByUser()" (click)="onWantToDelete()" class="comment-action-delete">Delete</div> <div *ngIf="isRemovableByUser()" (click)="onWantToDelete()" class="comment-action-delete" i18n>Delete</div>
</div> </div>
<my-video-comment-add <my-video-comment-add

View File

@ -3,7 +3,10 @@
<div class="title-page title-page-single"> <div class="title-page title-page-single">
Comments Comments
</div> </div>
<my-help *ngIf="video.commentsEnabled === true" helpType="custom" customHtml="You can either comment on the page of your instance where this video is federated with your PeerTube account, or via any ActivityPub-capable fediverse instance. For instance with Mastodon or Pleroma you can type in the search box <strong>@{{video.account.displayName}}@{{video.account.host}}</strong> and find back the video. Direct commenting capabilities are being worked on in <a href='https://github.com/Chocobozzz/PeerTube/issues/224'>#224</a>."></my-help> <my-help
*ngIf="video.commentsEnabled === true" helpType="custom" i18n-customHtml
customHtml="You can either comment on the page of your instance where this video is federated with your PeerTube account, or via any ActivityPub-capable fediverse instance. For instance with Mastodon or Pleroma you can type in the search box <strong>@{{video.account.displayName}}@{{video.account.host}}</strong> and find back the video. Direct commenting capabilities are being worked on in <a href='https://github.com/Chocobozzz/PeerTube/issues/224'>#224</a>."
></my-help>
</div> </div>
<ng-template [ngIf]="video.commentsEnabled === true"> <ng-template [ngIf]="video.commentsEnabled === true">
@ -14,7 +17,7 @@
(commentCreated)="onCommentThreadCreated($event)" (commentCreated)="onCommentThreadCreated($event)"
></my-video-comment-add> ></my-video-comment-add>
<div *ngIf="componentPagination.totalItems === 0 && comments.length === 0">No comments.</div> <div *ngIf="componentPagination.totalItems === 0 && comments.length === 0" i18n>No comments.</div>
<div <div
class="comment-threads" class="comment-threads"
@ -24,15 +27,15 @@
> >
<div *ngIf="highlightedThread" id="highlighted-comment"> <div *ngIf="highlightedThread" id="highlighted-comment">
<my-video-comment <my-video-comment
[comment]="highlightedThread" [comment]="highlightedThread"
[video]="video" [video]="video"
[inReplyToCommentId]="inReplyToCommentId" [inReplyToCommentId]="inReplyToCommentId"
[commentTree]="threadComments[highlightedThread.id]" [commentTree]="threadComments[highlightedThread.id]"
[highlightedComment]="true" [highlightedComment]="true"
(wantedToReply)="onWantedToReply($event)" (wantedToReply)="onWantedToReply($event)"
(wantedToDelete)="onWantedToDelete($event)" (wantedToDelete)="onWantedToDelete($event)"
(threadCreated)="onThreadCreated($event)" (threadCreated)="onThreadCreated($event)"
(resetReply)="onResetReply()" (resetReply)="onResetReply()"
></my-video-comment> ></my-video-comment>
</div> </div>
@ -50,7 +53,7 @@
></my-video-comment> ></my-video-comment>
<div *ngIf="comment.totalReplies !== 0 && !threadComments[comment.id]" (click)="viewReplies(comment.id)" class="view-replies"> <div *ngIf="comment.totalReplies !== 0 && !threadComments[comment.id]" (click)="viewReplies(comment.id)" class="view-replies">
View all {{ comment.totalReplies }} replies <ng-container i18n>View all {{ comment.totalReplies }} replies</ng-container>
<span *ngIf="!threadLoading[comment.id]" class="glyphicon glyphicon-menu-down"></span> <span *ngIf="!threadLoading[comment.id]" class="glyphicon glyphicon-menu-down"></span>
<my-loader class="comment-thread-loading" [loading]="threadLoading[comment.id]"></my-loader> <my-loader class="comment-thread-loading" [loading]="threadLoading[comment.id]"></my-loader>
@ -59,7 +62,7 @@
</div> </div>
</ng-template> </ng-template>
<div *ngIf="video.commentsEnabled === false"> <div *ngIf="video.commentsEnabled === false" i18n>
Comments are disabled. Comments are disabled.
</div> </div>
</div> </div>

View File

@ -11,6 +11,7 @@ import { VideoSortField } from '../../../shared/video/sort-field.type'
import { VideoDetails } from '../../../shared/video/video-details.model' import { VideoDetails } from '../../../shared/video/video-details.model'
import { VideoComment } from './video-comment.model' import { VideoComment } from './video-comment.model'
import { VideoCommentService } from './video-comment.service' import { VideoCommentService } from './video-comment.service'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-video-comments', selector: 'my-video-comments',
@ -40,7 +41,8 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private confirmService: ConfirmService, private confirmService: ConfirmService,
private videoCommentService: VideoCommentService, private videoCommentService: VideoCommentService,
private activatedRoute: ActivatedRoute private activatedRoute: ActivatedRoute,
private i18n: I18n
) {} ) {}
ngOnInit () { ngOnInit () {
@ -77,7 +79,7 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
if (highlightThread) this.highlightedThread = new VideoComment(res.comment) if (highlightThread) this.highlightedThread = new VideoComment(res.comment)
}, },
err => this.notificationsService.error('Error', err.message) err => this.notificationsService.error(this.i18n('Error'), err.message)
) )
} }
@ -89,7 +91,7 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
this.componentPagination.totalItems = res.totalComments this.componentPagination.totalItems = res.totalComments
}, },
err => this.notificationsService.error('Error', err.message) err => this.notificationsService.error(this.i18n('Error'), err.message)
) )
} }
@ -111,9 +113,11 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
async onWantedToDelete (commentToDelete: VideoComment) { async onWantedToDelete (commentToDelete: VideoComment) {
let message = 'Do you really want to delete this comment?' let message = 'Do you really want to delete this comment?'
if (commentToDelete.totalReplies !== 0) message += `${commentToDelete.totalReplies} would be deleted too.` if (commentToDelete.totalReplies !== 0) {
message += this.i18n(' {{ totalReplies }} replies will be deleted too.', { totalReplies: commentToDelete.totalReplies })
}
const res = await this.confirmService.confirm(message, 'Delete') const res = await this.confirmService.confirm(message, this.i18n('Delete'))
if (res === false) return if (res === false) return
this.videoCommentService.deleteVideoComment(commentToDelete.videoId, commentToDelete.id) this.videoCommentService.deleteVideoComment(commentToDelete.videoId, commentToDelete.id)
@ -136,7 +140,7 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
this.componentPagination.totalItems-- this.componentPagination.totalItems--
}, },
err => this.notificationsService.error('Error', err.message) err => this.notificationsService.error(this.i18n('Error'), err.message)
) )
} }

View File

@ -4,7 +4,7 @@
<div class="modal-header"> <div class="modal-header">
<span class="close" aria-hidden="true" (click)="hide()"></span> <span class="close" aria-hidden="true" (click)="hide()"></span>
<h4 class="modal-title">Download video</h4> <h4 i18n class="modal-title">Download video</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@ -17,22 +17,22 @@
<div class="download-type"> <div class="download-type">
<div class="peertube-radio-container"> <div class="peertube-radio-container">
<input type="radio" name="download" id="download-torrent" [(ngModel)]="downloadType" value="torrent"> <input type="radio" name="download" id="download-torrent" [(ngModel)]="downloadType" value="torrent">
<label for="download-torrent">Torrent</label> <label i18n for="download-torrent">Torrent</label>
</div> </div>
<div class="peertube-radio-container"> <div class="peertube-radio-container">
<input type="radio" name="download" id="download-direct" [(ngModel)]="downloadType" value="direct"> <input type="radio" name="download" id="download-direct" [(ngModel)]="downloadType" value="direct">
<label for="download-direct">Direct download</label> <label i18n for="download-direct">Direct download</label>
</div> </div>
</div> </div>
<div class="form-group inputs"> <div class="form-group inputs">
<span class="action-button action-button-cancel" (click)="hide()"> <span i18n class="action-button action-button-cancel" (click)="hide()">
Cancel Cancel
</span> </span>
<input <input
type="submit" value="Download" class="action-button-submit" type="submit" i18n-value value="Download" class="action-button-submit"
(click)="download()" (click)="download()"
> >
</div> </div>

View File

@ -4,14 +4,14 @@
<div class="modal-header"> <div class="modal-header">
<span class="close" aria-hidden="true" (click)="hide()"></span> <span class="close" aria-hidden="true" (click)="hide()"></span>
<h4 class="modal-title">Report video</h4> <h4 i18n class="modal-title">Report video</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form novalidate [formGroup]="form" (ngSubmit)="report()"> <form novalidate [formGroup]="form" (ngSubmit)="report()">
<div class="form-group"> <div class="form-group">
<textarea placeholder="Reason..." formControlName="reason" [ngClass]="{ 'input-error': formErrors['reason'] }"> <textarea i18n-placeholder placeholder="Reason..." formControlName="reason" [ngClass]="{ 'input-error': formErrors['reason'] }">
</textarea> </textarea>
<div *ngIf="formErrors.reason" class="form-error"> <div *ngIf="formErrors.reason" class="form-error">
{{ formErrors.reason }} {{ formErrors.reason }}
@ -19,12 +19,12 @@
</div> </div>
<div class="form-group inputs"> <div class="form-group inputs">
<span class="action-button action-button-cancel" (click)="hide()"> <span i18n class="action-button action-button-cancel" (click)="hide()">
Cancel Cancel
</span> </span>
<input <input
type="submit" value="Submit" class="action-button-submit" type="submit" i18n-value value="Submit" class="action-button-submit"
[disabled]="!form.valid" [disabled]="!form.valid"
> >
</div> </div>

View File

@ -4,6 +4,7 @@ import { NotificationsService } from 'angular2-notifications'
import { ModalDirective } from 'ngx-bootstrap/modal' import { ModalDirective } from 'ngx-bootstrap/modal'
import { FormReactive, VIDEO_ABUSE_REASON, VideoAbuseService } from '../../../shared/index' import { FormReactive, VIDEO_ABUSE_REASON, VideoAbuseService } from '../../../shared/index'
import { VideoDetails } from '../../../shared/video/video-details.model' import { VideoDetails } from '../../../shared/video/video-details.model'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-video-report', selector: 'my-video-report',
@ -27,8 +28,9 @@ export class VideoReportComponent extends FormReactive implements OnInit {
constructor ( constructor (
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private videoAbuseService: VideoAbuseService, private videoAbuseService: VideoAbuseService,
private notificationsService: NotificationsService private notificationsService: NotificationsService,
) { private i18n: I18n
) {
super() super()
} }
@ -58,11 +60,11 @@ export class VideoReportComponent extends FormReactive implements OnInit {
this.videoAbuseService.reportVideo(this.video.id, reason) this.videoAbuseService.reportVideo(this.video.id, reason)
.subscribe( .subscribe(
() => { () => {
this.notificationsService.success('Success', 'Video reported.') this.notificationsService.success(this.i18n('Success'), this.i18n('Video reported.'))
this.hide() this.hide()
}, },
err => this.notificationsService.error('Error', err.message) err => this.notificationsService.error(this.i18n('Error'), err.message)
) )
} }
} }

View File

@ -4,12 +4,12 @@
<div class="modal-header"> <div class="modal-header">
<span class="close" aria-hidden="true" (click)="hide()"></span> <span class="close" aria-hidden="true" (click)="hide()"></span>
<h4 class="modal-title">Share</h4> <h4 i18n class="modal-title">Share</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="form-group"> <div class="form-group">
<label>URL</label> <label i18n>URL</label>
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<input #urlInput (click)="urlInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="getVideoUrl()" /> <input #urlInput (click)="urlInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="getVideoUrl()" />
<div class="input-group-btn" placement="bottom right"> <div class="input-group-btn" placement="bottom right">
@ -21,7 +21,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Embed</label> <label i18n>Embed</label>
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<input #shareInput (click)="shareInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="getVideoIframeCode()" /> <input #shareInput (click)="shareInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="getVideoIframeCode()" />
<div class="input-group-btn" placement="bottom right"> <div class="input-group-btn" placement="bottom right">
@ -32,12 +32,12 @@
</div> </div>
</div> </div>
<div *ngIf="notSecure()" class="alert alert-warning"> <div i18n *ngIf="notSecure()" class="alert alert-warning">
The url is not secured (no HTTPS), so the embed video won't work on HTTPS websites (web browsers block non secured HTTP requests on HTTPS websites). The url is not secured (no HTTPS), so the embed video won't work on HTTPS websites (web browsers block non secured HTTP requests on HTTPS websites).
</div> </div>
<div class="form-group inputs"> <div class="form-group inputs">
<span class="action-button action-button-cancel" (click)="hide()"> <span i18n class="action-button action-button-cancel" (click)="hide()">
Cancel Cancel
</span> </span>
</div> </div>

View File

@ -5,6 +5,7 @@ import { NotificationsService } from 'angular2-notifications'
import { ModalDirective } from 'ngx-bootstrap/modal' import { ModalDirective } from 'ngx-bootstrap/modal'
import { VideoDetails } from '../../../shared/video/video-details.model' import { VideoDetails } from '../../../shared/video/video-details.model'
import { buildVideoEmbed } from '../../../../assets/player/utils' import { buildVideoEmbed } from '../../../../assets/player/utils'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-video-share', selector: 'my-video-share',
@ -16,7 +17,10 @@ export class VideoShareComponent {
@ViewChild('modal') modal: ModalDirective @ViewChild('modal') modal: ModalDirective
constructor (private notificationsService: NotificationsService) { constructor (
private notificationsService: NotificationsService,
private i18n: I18n
) {
// empty // empty
} }
@ -41,6 +45,6 @@ export class VideoShareComponent {
} }
activateCopiedMessage () { activateCopiedMessage () {
this.notificationsService.success('Success', 'Copied') this.notificationsService.success(this.i18n('Success'), this.i18n('Copied'))
} }
} }

View File

@ -4,7 +4,7 @@
<div class="modal-header"> <div class="modal-header">
<span class="close" aria-hidden="true" (click)="hide()"></span> <span class="close" aria-hidden="true" (click)="hide()"></span>
<h4 class="modal-title">Support</h4> <h4 i18n class="modal-title">Support</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@ -12,7 +12,7 @@
<div [innerHTML]="videoHTMLSupport"></div> <div [innerHTML]="videoHTMLSupport"></div>
<div class="form-group inputs"> <div class="form-group inputs">
<span class="action-button action-button-cancel" (click)="hide()"> <span i18n class="action-button action-button-cancel" (click)="hide()">
Cancel Cancel
</span> </span>
</div> </div>

View File

@ -27,8 +27,8 @@ export class VideoLocalComponent extends AbstractVideoList implements OnInit, On
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected authService: AuthService, protected authService: AuthService,
protected location: Location, protected location: Location,
private videoService: VideoService, protected i18n: I18n,
private i18n: I18n private videoService: VideoService
) { ) {
super() super()

View File

@ -25,8 +25,8 @@ export class VideoRecentlyAddedComponent extends AbstractVideoList implements On
protected location: Location, protected location: Location,
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected authService: AuthService, protected authService: AuthService,
private videoService: VideoService, protected i18n: I18n,
private i18n: I18n private videoService: VideoService
) { ) {
super() super()

View File

@ -31,9 +31,9 @@ export class VideoSearchComponent extends AbstractVideoList implements OnInit, O
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected authService: AuthService, protected authService: AuthService,
protected location: Location, protected location: Location,
protected i18n: I18n,
private videoService: VideoService, private videoService: VideoService,
private redirectService: RedirectService, private redirectService: RedirectService
private i18n: I18n
) { ) {
super() super()

View File

@ -25,8 +25,8 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected authService: AuthService, protected authService: AuthService,
protected location: Location, protected location: Location,
private videoService: VideoService, protected i18n: I18n,
private i18n: I18n private videoService: VideoService
) { ) {
super() super()

File diff suppressed because it is too large Load Diff