diff --git a/package-lock.json b/package-lock.json index 02dd16f83f1f746eef2f30535e54278dfc5cfd65..7e83adef2515d9cbd440251709a1661be0f183dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -249,6 +249,187 @@ "tslib": "1.9.0" } }, + "@biesbjerg/ngx-translate-extract": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@biesbjerg/ngx-translate-extract/-/ngx-translate-extract-2.3.4.tgz", + "integrity": "sha512-FzOdm5Jr2TMgdzTW+c6CGIgMQMCAXCyN6JYzz+hfnYjcvPrYbyR05AhM08W70nXD3a2RnbqjImNjEEcXY9pZ/g==", + "dev": true, + "requires": { + "chalk": "2.0.1", + "cheerio": "1.0.0-rc.2", + "flat": "2.0.1", + "fs": "0.0.1-security", + "gettext-parser": "1.2.2", + "glob": "7.1.2", + "mkdirp": "0.5.1", + "path": "0.12.7", + "typescript": "2.4.1", + "yargs": "8.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "chalk": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.0.1.tgz", + "integrity": "sha512-Mp+FXEI+FrwY/XYV45b2YD3E8i3HwnEAoFcM0qlZzq/RZ9RwWitt2Y/c7cqRAz70U7hfekqx6qNYthuKFO6K0g==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "strip-bom": "3.0.0" + } + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" + } + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "2.3.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "2.0.0", + "normalize-package-data": "2.4.0", + "path-type": "2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "2.1.0", + "read-pkg": "2.0.0" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "typescript": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.4.1.tgz", + "integrity": "sha1-w8yxbdqgsjFN4DHn5v7onlujRrw=", + "dev": true + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yargs": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", + "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", + "dev": true, + "requires": { + "camelcase": "4.1.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "read-pkg-up": "2.0.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "7.0.0" + } + }, + "yargs-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", + "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + "dev": true, + "requires": { + "camelcase": "4.1.0" + } + } + } + }, "@ngtools/json-schema": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ngtools/json-schema/-/json-schema-1.2.0.tgz", @@ -271,6 +452,16 @@ "webpack-sources": "1.1.0" } }, + "@ngx-translate/core": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-9.1.1.tgz", + "integrity": "sha1-rhA5KINrip4Gn9Li52+iGYzH5ig=" + }, + "@ngx-translate/http-loader": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-2.0.1.tgz", + "integrity": "sha1-qmd4jmS/qGUmkad7Ais7QDEgkRM=" + }, "@schematics/angular": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.3.2.tgz", @@ -1655,6 +1846,45 @@ "supports-color": "4.5.0" } }, + "cheerio": { + "version": "1.0.0-rc.2", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.2.tgz", + "integrity": "sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs=", + "dev": true, + "requires": { + "css-select": "1.2.0", + "dom-serializer": "0.1.0", + "entities": "1.1.1", + "htmlparser2": "3.9.2", + "lodash": "4.17.5", + "parse5": "3.0.3" + }, + "dependencies": { + "domhandler": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.1.tgz", + "integrity": "sha1-iS5HAAqZvlW783dP/qBWHYh5wlk=", + "dev": true, + "requires": { + "domelementtype": "1.3.0" + } + }, + "htmlparser2": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", + "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", + "dev": true, + "requires": { + "domelementtype": "1.3.0", + "domhandler": "2.4.1", + "domutils": "1.5.1", + "entities": "1.1.1", + "inherits": "2.0.3", + "readable-stream": "2.3.5" + } + } + } + }, "chokidar": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", @@ -2648,6 +2878,27 @@ "buffer-indexof": "1.1.1" } }, + "doctrine": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-0.7.2.tgz", + "integrity": "sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=", + "requires": { + "esutils": "1.1.6", + "isarray": "0.0.1" + }, + "dependencies": { + "esutils": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz", + "integrity": "sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U=" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + } + } + }, "dom-converter": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz", @@ -2815,6 +3066,15 @@ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", "dev": true }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "dev": true, + "requires": { + "iconv-lite": "0.4.19" + } + }, "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", @@ -3497,6 +3757,15 @@ "locate-path": "2.0.0" } }, + "flat": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat/-/flat-2.0.1.tgz", + "integrity": "sha1-cOKRiKdL4MPIlAnu0fqVd5B64y8=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + }, "flush-write-stream": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.2.tgz", @@ -3586,6 +3855,12 @@ "readable-stream": "2.3.5" } }, + "fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=", + "dev": true + }, "fs-access": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", @@ -4685,6 +4960,15 @@ } } }, + "gettext-parser": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/gettext-parser/-/gettext-parser-1.2.2.tgz", + "integrity": "sha1-HvDadcHnWa4wicc++k0Z5AKYdI4=", + "dev": true, + "requires": { + "encoding": "0.1.12" + } + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -8026,6 +8310,15 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, + "parse5": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", + "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", + "dev": true, + "requires": { + "@types/node": "6.0.101" + } + }, "parseqs": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", @@ -8056,6 +8349,16 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, + "path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", + "dev": true, + "requires": { + "process": "0.11.10", + "util": "0.10.3" + } + }, "path-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", @@ -10674,6 +10977,26 @@ } } }, + "tslint-eslint-rules": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/tslint-eslint-rules/-/tslint-eslint-rules-5.1.0.tgz", + "integrity": "sha512-lNIaLDymwts58SmocvVxzY9DSDONwZhHMt8T0J4uFMQV4jgYMMAFa89oBhNl87WIoXO+h+H6uU8f41mM0wyIqw==", + "requires": { + "doctrine": "0.7.2", + "tslib": "1.9.0", + "tsutils": "2.8.0" + }, + "dependencies": { + "tsutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.8.0.tgz", + "integrity": "sha1-AWAXNymzvxOGKN0UoVN+AIUdgUo=", + "requires": { + "tslib": "1.9.0" + } + } + } + }, "tsscmp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.5.tgz", diff --git a/package.json b/package.json index 8f70d9d4d76372ef5573a4c0b28b3bb02a2ad05f..46228712adea25752eb2d05a77288a4ba0f898af 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "build": "ng build --prod", "test": "ng test", "lint": "ng lint", - "e2e": "ng e2e" + "e2e": "ng e2e", + "extract": "ngx-translate-extract -m _ --input ./src --output ./src/assets/i18n/de.json ./src/assets/i18n/en.json --clean --sort --format namespaced-json" }, "private": true, "dependencies": { @@ -24,6 +25,8 @@ "@angular/platform-browser": "^5.2.0", "@angular/platform-browser-dynamic": "^5.2.0", "@angular/router": "^5.2.0", + "@ngx-translate/core": "^9.1.1", + "@ngx-translate/http-loader": "^2.0.1", "core-js": "^2.4.1", "hammerjs": "^2.0.8", "rxjs": "^5.5.6", @@ -34,6 +37,7 @@ "@angular/cli": "~1.7.2", "@angular/compiler-cli": "^5.2.0", "@angular/language-service": "^5.2.0", + "@biesbjerg/ngx-translate-extract": "^2.3.4", "@types/jasmine": "~2.8.3", "@types/jasminewd2": "~2.0.2", "@types/node": "~6.0.60", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index c84ba49899bb31af08b6665b9f738298bebffcc3..f66bf62b4d2c632eb8fe2df414c507e0ad55c94c 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -47,7 +47,7 @@ import { MatToolbarModule, MatTooltipModule } from '@angular/material'; -import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; +import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from '@angular/common/http'; import { RoomPageComponent } from './components/pages/room-page/room-page.component'; import { RoomCreateComponent } from './components/dialogs/room-create/room-create.component'; import { LoginComponentPageComponent } from './components/pages/login-page/login-page.component'; @@ -85,6 +85,8 @@ import { ContentYesNoCreatorComponent } from './components/fragments/content-yes import { AnswerEditComponent } from './components/dialogs/answer-edit/answer-edit.component'; import { ContentDeleteComponent } from './components/dialogs/content-delete/content-delete.component'; import { FeedbackBarometerPageComponent } from './components/pages/feedback-barometer-page/feedback-barometer-page.component'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { TranslateHttpLoader } from '@ngx-translate/http-loader'; @NgModule({ declarations: [ @@ -143,6 +145,7 @@ import { FeedbackBarometerPageComponent } from './components/pages/feedback-baro BrowserAnimationsModule, FlexLayoutModule, FormsModule, + HttpClientModule, MatAutocompleteModule, MatButtonModule, MatButtonToggleModule, @@ -175,7 +178,13 @@ import { FeedbackBarometerPageComponent } from './components/pages/feedback-baro MatToolbarModule, MatTooltipModule, ReactiveFormsModule, - HttpClientModule + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useFactory: HttpLoaderFactory, + deps: [HttpClient] + } + }) ], providers: [ { @@ -207,3 +216,7 @@ import { FeedbackBarometerPageComponent } from './components/pages/feedback-baro }) export class AppModule { } + +export function HttpLoaderFactory(http: HttpClient) { + return new TranslateHttpLoader(http); +} diff --git a/src/app/components/dialogs/password-reset/password-reset.component.html b/src/app/components/dialogs/password-reset/password-reset.component.html index 601982d7fbf08d24e794d79e209b8ec70693a188..3f6f8c1216da114190472cb06307f75347fbf867 100644 --- a/src/app/components/dialogs/password-reset/password-reset.component.html +++ b/src/app/components/dialogs/password-reset/password-reset.component.html @@ -1,11 +1,17 @@ <form (ngSubmit)="resetPassword(email.value)" fxLayout="column" fxLayoutAlign="space-around" fxLayoutGap="10px"> <mat-form-field class="input-block"> - <input matInput #email placeholder="E-Mail" [formControl]="usernameFormControl" [errorStateMatcher]="matcher" - name="email"/> - <mat-error *ngIf="usernameFormControl.hasError('required')">Email address is <strong>required</strong>.</mat-error> - <mat-error *ngIf="usernameFormControl.hasError('email') && !usernameFormControl.hasError('required')">Email is not <strong>valid</strong>.</mat-error> + <input matInput #email placeholder="{{ 'password-reset.email' | translate }}" [formControl]="usernameFormControl" + [errorStateMatcher]="matcher" name="email"/> + <mat-error *ngIf="usernameFormControl.hasError('required')"> + {{ 'password-reset.email-required' | translate }} + </mat-error> + <mat-error *ngIf="usernameFormControl.hasError('email') && !usernameFormControl.hasError('required')"> + {{ 'password-reset.email-invalid' | translate }} + </mat-error> </mat-form-field> - <button mat-raised-button color="primary" type="submit">Reset password</button> + <button mat-raised-button color="primary" type="submit"> + {{ 'password-reset.reset-password' | translate }} + </button> </form> diff --git a/src/app/components/dialogs/password-reset/password-reset.component.ts b/src/app/components/dialogs/password-reset/password-reset.component.ts index af3a40155cebc3cd58eeeb4d866acc05fd0681c8..9057d61477585fd84ef5c52fc8ba2eb7ee5eb362 100644 --- a/src/app/components/dialogs/password-reset/password-reset.component.ts +++ b/src/app/components/dialogs/password-reset/password-reset.component.ts @@ -4,6 +4,7 @@ import { ErrorStateMatcher, MAT_DIALOG_DATA, MatDialogRef } from '@angular/mater import { RegisterComponent } from '../register/register.component'; import { AuthenticationService } from '../../../services/http/authentication.service'; import { NotificationService } from '../../../services/util/notification.service'; +import { TranslateService } from '@ngx-translate/core'; export class PasswordResetErrorStateMatcher implements ErrorStateMatcher { isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { @@ -24,7 +25,8 @@ export class PasswordResetComponent implements OnInit { matcher = new PasswordResetErrorStateMatcher(); - constructor(public authenticationService: AuthenticationService, + constructor(private translationService: TranslateService, + public authenticationService: AuthenticationService, public notificationService: NotificationService, public dialogRef: MatDialogRef<RegisterComponent>, @Inject(MAT_DIALOG_DATA) public data: any) { @@ -38,11 +40,15 @@ export class PasswordResetComponent implements OnInit { if (!this.usernameFormControl.hasError('required') && !this.usernameFormControl.hasError('email')) { this.authenticationService.resetPassword(username).subscribe(() => { - this.notificationService.show('Password was reset. Please check your mail!'); + this.translationService.get('password-reset.reset-successful').subscribe(message => { + this.notificationService.show(message); + }); this.dialogRef.close(); }); } else { - this.notificationService.show('Please fit the requirements shown above.'); + this.translationService.get('password-reset.input-incorrect').subscribe(message => { + this.notificationService.show(message); + }); } } diff --git a/src/app/components/dialogs/register/register.component.html b/src/app/components/dialogs/register/register.component.html index 7dff487294e1fdf3fbcf8fd70c005520ab92032b..0b5f0f64d0de9082a02440d07a5b90bf5cde2368 100644 --- a/src/app/components/dialogs/register/register.component.html +++ b/src/app/components/dialogs/register/register.component.html @@ -1,28 +1,35 @@ <form (ngSubmit)="register(userName.value, userPassword1.value)" fxLayout="column" fxLayoutAlign="space-around" fxLayoutGap="10px"> <mat-form-field class="input-block"> - <input matInput #userName placeholder="E-mail" [formControl]="usernameFormControl" [errorStateMatcher]="matcher"> - <mat-error *ngIf="usernameFormControl.hasError('email') && !usernameFormControl.hasError('required')">Please enter a - <strong>valid</strong> email address. + <input matInput #userName placeholder="{{ 'register.email' | translate }}" [formControl]="usernameFormControl" + [errorStateMatcher]="matcher"> + <mat-error *ngIf="usernameFormControl.hasError('email') && !usernameFormControl.hasError('required')"> + {{ 'register.email-invalid' | translate }} + </mat-error> + <mat-error *ngIf="usernameFormControl.hasError('required')"> + {{ 'register.email-required' |translate }} </mat-error> - <mat-error *ngIf="usernameFormControl.hasError('required')">Email address is <strong>required</strong>.</mat-error> </mat-form-field> <mat-form-field class="input-block"> - <input matInput type="password" #userPassword1 placeholder="Password" [formControl]="password1FormControl" - [errorStateMatcher]="matcher"> - <mat-error *ngIf="password1FormControl.hasError('required')">Password is <strong>required</strong>.</mat-error> + <input matInput type="password" #userPassword1 placeholder="{{ 'register.password' | translate }}" + [formControl]="password1FormControl" [errorStateMatcher]="matcher"> + <mat-error *ngIf="password1FormControl.hasError('required')"> + {{ 'register.password-required' | translate }} + </mat-error> </mat-form-field> <mat-form-field class="input-block"> - <input matInput type="password" #userPassword2 placeholder="Verify password" [formControl]="password2FormControl" - [errorStateMatcher]="matcher"> - <mat-error *ngIf="password2FormControl.hasError('required')">Password is <strong>required</strong>.</mat-error> + <input matInput type="password" #userPassword2 placeholder="{{ 'register.password-verify' | translate}}" + [formControl]="password2FormControl" [errorStateMatcher]="matcher"> + <mat-error *ngIf="password2FormControl.hasError('required')"> + {{ 'register.password-required' | translate }} + </mat-error> <mat-error *ngIf="password2FormControl.hasError('passwordIsEqual') && !password2FormControl.hasError('required')"> - Passwords do <strong>not</strong> match. + {{ 'register.password-unmatch' | translate }} </mat-error> </mat-form-field> - <button mat-raised-button color="primary" type="submit">Register</button> + <button mat-raised-button color="primary" type="submit">{{ 'register.register' | translate }}</button> </form> diff --git a/src/app/components/dialogs/register/register.component.ts b/src/app/components/dialogs/register/register.component.ts index 7a7674706dc938c12e5d7a1e7ffe7abdb8e46e0b..db7cdd2a57729948093f163870ed6a056859671e 100644 --- a/src/app/components/dialogs/register/register.component.ts +++ b/src/app/components/dialogs/register/register.component.ts @@ -3,6 +3,7 @@ import { ErrorStateMatcher, MAT_DIALOG_DATA, MatDialogRef } from '@angular/mater import { FormControl, FormGroupDirective, NgForm, Validators } from '@angular/forms'; import { AuthenticationService } from '../../../services/http/authentication.service'; import { NotificationService } from '../../../services/util/notification.service'; +import { TranslateService } from '@ngx-translate/core'; export class RegisterErrorStateMatcher implements ErrorStateMatcher { isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { @@ -41,7 +42,8 @@ export class RegisterComponent implements OnInit { matcher = new RegisterErrorStateMatcher(); - constructor(public authenticationService: AuthenticationService, + constructor(private translationService: TranslateService, + public authenticationService: AuthenticationService, public notificationService: NotificationService, public dialogRef: MatDialogRef<RegisterComponent>, @Inject(MAT_DIALOG_DATA) public data: any) { @@ -59,11 +61,15 @@ export class RegisterComponent implements OnInit { !this.password1FormControl.hasError('required') && !this.password2FormControl.hasError('required') && !this.password2FormControl.hasError('passwordIsEqual')) { this.authenticationService.register(username, password1).subscribe(() => { - this.notificationService.show('Successfully registered. Please check your mail!'); + this.translationService.get('register.register-successful').subscribe(message => { + this.notificationService.show(message); + }); this.dialogRef.close(); }); } else { - this.notificationService.show('Please fit the requirements shown above.'); + this.translationService.get('register.register-unsuccessful').subscribe(message => { + this.notificationService.show(message); + }); } } } diff --git a/src/app/components/fragments/header/header.component.html b/src/app/components/fragments/header/header.component.html index f7d6889ff5de5063f1e25e40f5978e231a729e7e..dfe19ec75792be1cc56cd2546a138b9c8a994348 100644 --- a/src/app/components/fragments/header/header.component.html +++ b/src/app/components/fragments/header/header.component.html @@ -5,10 +5,20 @@ </button> <span class="app-title" (click)="goToHomepage()">ARSnova</span> <span class="fill-remaining-space"></span> + + <mat-menu #langMenu="matMenu" [overlapTrigger]="false"> + <button mat-menu-item (click)="useLanguage('de')">{{ 'header.german' | translate }}</button> + <button mat-menu-item (click)="useLanguage('en')">{{ 'header.english' | translate }}</button> + </mat-menu> + + <button mat-icon-button [matMenuTriggerFor]="langMenu"> + <mat-icon>language</mat-icon> + </button> + <mat-menu #appMenu="matMenu" [overlapTrigger]="false"> <button mat-menu-item (click)="logout()"> <mat-icon>exit_to_app</mat-icon> - <span>Logout</span> + <span>{{ 'header.logout' | translate }}</span> </button> </mat-menu> <button *ngIf="user" mat-button [matMenuTriggerFor]="appMenu"> diff --git a/src/app/components/fragments/header/header.component.ts b/src/app/components/fragments/header/header.component.ts index 222744b119fc8911eb8c7cf77a96195b03603114..21c751daf90560618ecdbbd72167b2d3810b9cab 100644 --- a/src/app/components/fragments/header/header.component.ts +++ b/src/app/components/fragments/header/header.component.ts @@ -5,6 +5,7 @@ import { Router } from '@angular/router'; import { User } from '../../../models/user'; import { UserRole } from '../../../models/user-roles.enum'; import { Location } from '@angular/common'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'app-header', @@ -17,7 +18,10 @@ export class HeaderComponent implements OnInit { constructor(public location: Location, private authenticationService: AuthenticationService, private notification: NotificationService, - public router: Router) { + public router: Router, + private translationService: TranslateService) { + + translationService.setDefaultLang('en'); } ngOnInit() { @@ -51,4 +55,8 @@ export class HeaderComponent implements OnInit { } this.router.navigate([route]); } + + useLanguage(language: string) { + this.translationService.use(language); + } } diff --git a/src/app/components/fragments/login/login.component.html b/src/app/components/fragments/login/login.component.html index 0005f0e6e3e7f175608430a7b6e585a0f5055f16..4feb68c06d11e85a337f982a6c09d0034e292dd4 100644 --- a/src/app/components/fragments/login/login.component.html +++ b/src/app/components/fragments/login/login.component.html @@ -1,18 +1,22 @@ <form (ngSubmit)="login(userEmail.value, userPassword.value)" fxLayout="column" fxLayoutAlign="space-around" fxLayoutGap="10px"> <mat-form-field class="input-block"> - <input matInput #userEmail placeholder="E-mail" [formControl]="usernameFormControl" [errorStateMatcher]="matcher" - name="username"/> - <mat-error *ngIf="usernameFormControl.hasError('email') && !usernameFormControl.hasError('required')">Please enter a - <strong>valid</strong> e-mail address. + <input matInput #userEmail placeholder="{{ 'login.email' | translate }}" [formControl]="usernameFormControl" + [errorStateMatcher]="matcher" name="username"/> + <mat-error *ngIf="usernameFormControl.hasError('email') && !usernameFormControl.hasError('required')"> + {{ 'login.email-invalid' | translate }} + </mat-error> + <mat-error *ngIf="usernameFormControl.hasError('required')"> + {{ 'login.email-required' | translate }} </mat-error> - <mat-error *ngIf="usernameFormControl.hasError('required')">E-mail is <strong>required</strong>.</mat-error> </mat-form-field> <mat-form-field class="input-block"> - <input matInput #userPassword type="password" placeholder="Password" [formControl]="passwordFormControl" - [errorStateMatcher]="matcher" name="password"/> - <mat-error *ngIf="passwordFormControl.hasError('required')">Password is <strong>required</strong>.</mat-error> + <input matInput #userPassword type="password" placeholder="{{ 'login.password' | translate }}" + [formControl]="passwordFormControl" [errorStateMatcher]="matcher" name="password"/> + <mat-error *ngIf="passwordFormControl.hasError('required')">{{ 'login.password-required' | translate }}</mat-error> </mat-form-field> - <button mat-raised-button color="primary" type="submit">Login</button> - <button mat-raised-button *ngIf="role === UserRole.PARTICIPANT" (click)="guestLogin()" type="button">Login as guest</button> + <button mat-raised-button color="primary" type="submit">{{ 'login.login' | translate }}</button> + <button mat-raised-button *ngIf="role === UserRole.PARTICIPANT" (click)="guestLogin()" type="button"> + {{ 'login.guest-login' | translate }} + </button> </form> diff --git a/src/app/components/fragments/login/login.component.ts b/src/app/components/fragments/login/login.component.ts index da7494c3fed92a0058620efd56eb908a7649d9c1..768796b6f09fde01ffd30adef8ea1a30b0922c74 100644 --- a/src/app/components/fragments/login/login.component.ts +++ b/src/app/components/fragments/login/login.component.ts @@ -5,6 +5,7 @@ import { NotificationService } from '../../../services/util/notification.service import { ErrorStateMatcher } from '@angular/material'; import { FormControl, FormGroupDirective, NgForm, Validators } from '@angular/forms'; import { UserRole } from '../../../models/user-roles.enum'; +import { TranslateService } from '@ngx-translate/core'; export class LoginErrorStateMatcher implements ErrorStateMatcher { isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { @@ -30,6 +31,7 @@ export class LoginComponent implements OnInit { constructor(public authenticationService: AuthenticationService, public router: Router, + private translationService: TranslateService, public notificationService: NotificationService) { } @@ -44,7 +46,9 @@ export class LoginComponent implements OnInit { !this.passwordFormControl.hasError('required')) { this.authenticationService.login(username, password, this.role).subscribe(loginSuccessful => this.checkLogin(loginSuccessful)); } else { - this.notificationService.show('Please fit the requirements shown above.'); + this.translationService.get('login.input-incorrect').subscribe(message => { + this.notificationService.show(message); + }); } } @@ -61,7 +65,9 @@ export class LoginComponent implements OnInit { this.router.navigate(['participant']); } } else { - this.notificationService.show('Username or password incorrect.'); + this.translationService.get('login.login-data-incorrect').subscribe(message => { + this.notificationService.show(message); + }); } } } diff --git a/src/app/components/pages/login-page/login-page.component.html b/src/app/components/pages/login-page/login-page.component.html index c6c493e03b9580f62d7b5fcb9f6800a68b46f142..42fb20f73351057e91935f40bf191f03280d4ac5 100644 --- a/src/app/components/pages/login-page/login-page.component.html +++ b/src/app/components/pages/login-page/login-page.component.html @@ -1,25 +1,33 @@ <div fxLayout="column" fxLayoutAlign="center" fxLayoutGap="20px" fxFill> <div fxLayout="row" fxLayoutAlign="center"> <mat-tab-group class="login-tab-group"> - <mat-tab label="Creator"> + <mat-tab label="{{ 'login-page.creator' | translate }}"> <div fxLayout="column" fxLayoutAlign="start" fxLayoutGap="20px"> <div fxLayout="row" fxLayoutAlign="center" fxLayoutGap="5px"> <app-login fxFill [role]="UserRole.CREATOR"></app-login> </div> <div fxLayout="row" fxLayoutAlign="center" fxLayoutGap="5px"> - <button mat-button color="primary" (click)="openRegisterDialog()">Register</button> - <button mat-button color="primary" (click)="openPasswordDialog()">Password reset</button> + <button mat-button color="primary" (click)="openRegisterDialog()"> + {{ 'login-page.register' | translate }} + </button> + <button mat-button color="primary" (click)="openPasswordDialog()"> + {{ 'login-page.password-reset' | translate }} + </button> </div> </div> </mat-tab> - <mat-tab label="Participant"> + <mat-tab label="{{ 'login-page.participant' | translate }}"> <div fxLayout="column" fxLayoutAlign="start" fxLayoutGap="20px"> <div fxLayout="row" fxLayoutAlign="center" fxLayoutGap="5px"> <app-login fxFill [role]="UserRole.PARTICIPANT"></app-login> </div> <div fxLayout="row" fxLayoutAlign="center" fxLayoutGap="5px"> - <button mat-button color="primary" (click)="openRegisterDialog()">Register</button> - <button mat-button color="primary" (click)="openPasswordDialog()">Password reset</button> + <button mat-button color="primary" (click)="openRegisterDialog()"> + {{ 'login-page.register' | translate }} + </button> + <button mat-button color="primary" (click)="openPasswordDialog()"> + {{ 'login-page.password-reset' | translate }} + </button> </div> </div> </mat-tab> diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json new file mode 100644 index 0000000000000000000000000000000000000000..9a29da1d22b9880540320e56190cce9772dc39a9 --- /dev/null +++ b/src/assets/i18n/de.json @@ -0,0 +1,44 @@ +{ + "header": { + "english": "English", + "german": "Deutsch", + "logout": "Abmelden" + }, + "login-page": { + "creator": "Autor", + "participant": "Teilnehmer", + "password-reset": "Passwort zurücksetzen", + "register": "Registrieren" + }, + "login": { + "email": "E-Mail", + "email-invalid": "E-Mail Adresse ist ungültig", + "email-required": "E-Mail Adresse ist erforderlich", + "guest-login": "Anmelden als Gast", + "input-incorrect": "Bitte prüfen Sie Ihre Eingaben.", + "login": "Anmelden", + "login-data-incorrect": "Benutzername oder Passwort nicht korrekt.", + "password": "Passwort", + "password-required": "Passwort ist erforderlich" + }, + "password-reset": { + "email": "E-Mail", + "email-invalid": "E-Mail Adresse ist nicht gültig.", + "email-required": "E-Mail Adresse ist erforderlich.", + "input-incorrect": "Bitte prüfen Sie Ihre Eingabe.", + "reset-password": "Passwort zurücksetzen", + "reset-successful": "Passwort wurde zurückgesetzt. Bitte prüfen Sie Ihre E-Mails" + }, + "register": { + "email": "E-Mail", + "email-invalid": "E-Mail Adresse ist nicht gültig", + "email-required": "E-Mail Adresse ist erforderlich", + "password": "Passwort", + "password-required": "Passwort ist erforderlich", + "password-unmatch": "Passwörter stimmen nicht überein", + "password-verify": "Passwort bestätigen", + "register": "Registrieren", + "register-successful": "Erfolgreich registriert. Bitte prüfen Sie Ihre E-Mails", + "register-unsuccessful": "Bitte prüfen Sie Ihre Eingaben." + } +} diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json new file mode 100644 index 0000000000000000000000000000000000000000..745c2e809b9bb35720b46ac6171a1ae8d8f5df2e --- /dev/null +++ b/src/assets/i18n/en.json @@ -0,0 +1,44 @@ +{ + "header": { + "english": "English", + "german": "Deutsch", + "logout": "Logout" + }, + "login-page": { + "creator": "Creator", + "participant": "Participant", + "password-reset": "Reset password", + "register": "Register" + }, + "login": { + "email": "E-Mail", + "email-invalid": "E-Mail is invalid", + "email-required": "E-Mail is required", + "guest-login": "Guest login", + "input-incorrect": "Please check your data.", + "login": "Login", + "login-data-incorrect": "Username or password incorrect.", + "password": "Password", + "password-required": "Password is required" + }, + "password-reset": { + "email": "E-Mail", + "email-invalid": "E-Mail is not valid", + "email-required": "E-Mail is required", + "input-incorrect": "Please check your data.", + "reset-password": "Reset password", + "reset-successful": "Password was reset. Please check your mail." + }, + "register": { + "email": "E-Mail", + "email-invalid": "E-Mail is not valid", + "email-required": "E-Mail is required", + "password": "Password", + "password-required": "Password is required", + "password-unmatch": "Passwords do not match", + "password-verify": "Verify password", + "register": "Register", + "register-successful": "Successfully registered. Please check your mail.", + "register-unsuccessful": "Please check your data." + } +}