@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
3 |
+
"version": 1,
|
4 |
+
"newProjectRoot": "projects",
|
5 |
+
"projects": {
|
6 |
+
"book-monkey": {
|
7 |
+
"projectType": "application",
|
8 |
+
"schematics": {},
|
9 |
+
"root": "",
|
10 |
+
"sourceRoot": "src",
|
11 |
+
"prefix": "bm",
|
12 |
+
"architect": {
|
13 |
+
"build": {
|
14 |
+
"builder": "@angular-devkit/build-angular:browser",
|
15 |
+
"options": {
|
16 |
+
"outputPath": "dist/book-monkey",
|
17 |
+
"index": "src/index.html",
|
18 |
+
"main": "src/main.ts",
|
19 |
+
"polyfills": [
|
20 |
+
"zone.js"
|
21 |
+
],
|
22 |
+
"tsConfig": "tsconfig.app.json",
|
23 |
+
"assets": [
|
24 |
+
{
|
25 |
+
"glob": "**/*",
|
26 |
+
"input": "public"
|
27 |
+
}
|
28 |
+
],
|
29 |
+
"styles": [
|
30 |
+
"src/styles.css"
|
31 |
+
],
|
32 |
+
"scripts": [],
|
33 |
+
"localize": true
|
34 |
+
},
|
35 |
+
"configurations": {
|
36 |
+
"production": {
|
37 |
+
"budgets": [
|
38 |
+
{
|
39 |
+
"type": "initial",
|
40 |
+
"maximumWarning": "500kB",
|
41 |
+
"maximumError": "1MB"
|
42 |
+
},
|
43 |
+
{
|
44 |
+
"type": "anyComponentStyle",
|
45 |
+
"maximumWarning": "4kB",
|
46 |
+
"maximumError": "8kB"
|
47 |
+
}
|
48 |
+
],
|
49 |
+
"outputHashing": "all"
|
50 |
+
},
|
51 |
+
"development": {
|
52 |
+
"buildOptimizer": false,
|
53 |
+
"optimization": false,
|
54 |
+
"vendorChunk": true,
|
55 |
+
"extractLicenses": false,
|
56 |
+
"sourceMap": true,
|
57 |
+
"namedChunks": true,
|
58 |
+
"localize": ["en-US"]
|
59 |
+
},
|
60 |
+
"locale-de": {
|
61 |
+
"localize": ["de"]
|
62 |
+
}
|
63 |
+
},
|
64 |
+
"defaultConfiguration": "production"
|
65 |
+
},
|
66 |
+
"serve": {
|
67 |
+
"builder": "@angular-devkit/build-angular:dev-server",
|
68 |
+
"configurations": {
|
69 |
+
"production": {
|
70 |
+
"buildTarget": "book-monkey:build:production"
|
71 |
+
},
|
72 |
+
"development": {
|
73 |
+
"buildTarget": "book-monkey:build:development"
|
74 |
+
},
|
75 |
+
"development-de": {
|
76 |
+
"buildTarget": "book-monkey:build:development,locale-de"
|
77 |
+
}
|
78 |
+
},
|
79 |
+
"defaultConfiguration": "development"
|
80 |
+
},
|
81 |
+
"extract-i18n": {
|
82 |
+
"builder": "@angular-devkit/build-angular:extract-i18n"
|
83 |
+
},
|
84 |
+
"test": {
|
85 |
+
"builder": "@angular-devkit/build-angular:karma",
|
86 |
+
"options": {
|
87 |
+
"polyfills": [
|
88 |
+
"zone.js",
|
89 |
+
"zone.js/testing"
|
90 |
+
],
|
91 |
+
"tsConfig": "tsconfig.spec.json",
|
92 |
+
"assets": [
|
93 |
+
{
|
94 |
+
"glob": "**/*",
|
95 |
+
"input": "public"
|
96 |
+
}
|
97 |
+
],
|
98 |
+
"styles": [
|
99 |
+
"src/styles.css"
|
100 |
+
],
|
101 |
+
"scripts": []
|
102 |
+
}
|
103 |
+
},
|
104 |
+
"lint": {
|
105 |
+
"builder": "@angular-eslint/builder:lint",
|
106 |
+
"options": {
|
107 |
+
"lintFilePatterns": [
|
108 |
+
"src/**/*.ts",
|
109 |
+
"src/**/*.html"
|
110 |
+
]
|
111 |
+
}
|
112 |
+
}
|
113 |
+
},
|
114 |
+
"i18n": {
|
115 |
+
"sourceLocale": "en-US",
|
116 |
+
"locales": {
|
117 |
+
"de": {
|
118 |
+
"translation": "messages.de.xlf"
|
119 |
+
}
|
120 |
+
}
|
121 |
+
}
|
122 |
+
}
|
123 |
+
},
|
124 |
+
"cli": {
|
125 |
+
"schematicCollections": [
|
126 |
+
"angular-eslint"
|
127 |
+
]
|
128 |
+
}
|
129 |
+
}
|
@@ -27,7 +27,8 @@
|
|
27 |
"index": "src/index.html",
|
28 |
"browser": "src/main.ts",
|
29 |
"polyfills": [
|
30 |
-
"zone.js"
|
|
|
31 |
],
|
32 |
"tsConfig": "tsconfig.app.json",
|
33 |
"assets": [
|
@@ -85,7 +86,8 @@
|
|
85 |
"options": {
|
86 |
"polyfills": [
|
87 |
"zone.js",
|
88 |
-
"zone.js/testing"
|
|
|
89 |
],
|
90 |
"tsConfig": "tsconfig.spec.json",
|
91 |
"assets": [
|
27 |
"index": "src/index.html",
|
28 |
"browser": "src/main.ts",
|
29 |
"polyfills": [
|
30 |
+
"zone.js",
|
31 |
+
"@angular/localize/init"
|
32 |
],
|
33 |
"tsConfig": "tsconfig.app.json",
|
34 |
"assets": [
|
86 |
"options": {
|
87 |
"polyfills": [
|
88 |
"zone.js",
|
89 |
+
"zone.js/testing",
|
90 |
+
"@angular/localize/init"
|
91 |
],
|
92 |
"tsConfig": "tsconfig.spec.json",
|
93 |
"assets": [
|
@@ -30,6 +30,7 @@
|
|
30 |
"@angular-devkit/build-angular": "^19.1.1",
|
31 |
"@angular/cli": "^19.1.1",
|
32 |
"@angular/compiler-cli": "^19.1.0",
|
|
|
33 |
"@types/jasmine": "~5.1.0",
|
34 |
"angular-eslint": "19.0.2",
|
35 |
"eslint": "^9.16.0",
|
30 |
"@angular-devkit/build-angular": "^19.1.1",
|
31 |
"@angular/cli": "^19.1.1",
|
32 |
"@angular/compiler-cli": "^19.1.0",
|
33 |
+
"@angular/localize": "^19.1.1",
|
34 |
"@types/jasmine": "~5.1.0",
|
35 |
"angular-eslint": "19.0.2",
|
36 |
"eslint": "^9.16.0",
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"locale": "de",
|
3 |
+
"translations": {
|
4 |
+
"BookCreateComponentTitle": "Buch anlegen",
|
5 |
+
"BookEditComponentTitle": "Buch bearbeiten",
|
6 |
+
"BookFormComponentLabelTitle": "Titel",
|
7 |
+
"BookFormComponentLabelSubtitle": "Untertitel",
|
8 |
+
"BookFormComponentLabelIsbn": "ISBN",
|
9 |
+
"BookFormComponentLabelAuthors": "Autorinnen/Autoren",
|
10 |
+
"BookFormComponentAddAuthor": " + Autorin/Autor ",
|
11 |
+
"BookFormComponentLabelDescription": "Beschreibung",
|
12 |
+
"BookFormComponentLabelPublished": "Veröffentlicht",
|
13 |
+
"BookFormComponentLabelThumbnail": "Vorschaubild URL",
|
14 |
+
"BookFormComponentSave": " Speichern ",
|
15 |
+
"BookFormComponentErrorTitleRequired": "Ein Titel muss angegeben werden.",
|
16 |
+
"BookFormComponentErrorIsbnRequired": "Eine ISBN muss angegeben werden.",
|
17 |
+
"BookFormComponentErrorIsbnFormat": "Die ISBN muss aus 10 oder 13 Zahlen bestehen.",
|
18 |
+
"BookFormComponentErrorIsbnExists": "DIe ISBN existiert bereits.",
|
19 |
+
"BookFormComponentErrorAtLeastOneAuthor": "Es muss mindestens ein Name angegeben werden.",
|
20 |
+
"AppComponentHome": "Startseite",
|
21 |
+
"AppComponentBooks": "Bücher",
|
22 |
+
"AppComponentAdmin": "Administration",
|
23 |
+
"AppComponentLogin": "Anmelden",
|
24 |
+
"AppComponentLogout": "Abmelden",
|
25 |
+
"BookDetailsComponentAuthors": "Autorinnen/Autoren",
|
26 |
+
"BookDetailsComponentIsbn": "ISBN",
|
27 |
+
"BookDetailsComponentPublished": "Veröffentlicht",
|
28 |
+
"BookDetailsComponentDescription": "Beschreibung",
|
29 |
+
"BookDetailsComponentAltCover": "Buchcover",
|
30 |
+
"BookDetailsComponentBackToList": "Zurück zur Liste",
|
31 |
+
"BookDetailsComponentRemove": " Buch entfernen ",
|
32 |
+
"BookDetailsComponentEdit": " Buch bearbeiten ",
|
33 |
+
"BookListItemComponentAltCover": "Buchcover",
|
34 |
+
"BookListItemComponentIsbn": "ISBN",
|
35 |
+
"BookListComponentTitle": "Bücher",
|
36 |
+
"BookListComponentNoBooks": " Keine Bücher verfügbar. ",
|
37 |
+
"HomeComponentTitle": "Startseite",
|
38 |
+
"HomeComponentBooks": " Zur Buchliste\n",
|
39 |
+
"HomeComponentSearchHeadline": "Suche",
|
40 |
+
"SearchComponentInput": "Suche",
|
41 |
+
"AuthGuardAlert": "Nicht angemeldet!"
|
42 |
+
}
|
43 |
+
}
|
@@ -1,3 +1,3 @@
|
|
1 |
-
<h1>Create Book</h1>
|
2 |
|
3 |
<bm-book-form (submitBook)="create($event)"></bm-book-form>
|
1 |
+
<h1 i18n="title create book|Title for the create book page@@BookCreateComponentTitle">Create Book</h1>
|
2 |
|
3 |
<bm-book-form (submitBook)="create($event)"></bm-book-form>
|
@@ -1,4 +1,4 @@
|
|
1 |
-
<h1>Edit Book</h1>
|
2 |
|
3 |
<bm-book-form
|
4 |
*ngIf="book$ | async as book"
|
1 |
+
<h1 i18n="title edit book|Title for the edit book page@@BookEditComponentTitle">Edit Book</h1>
|
2 |
|
3 |
<bm-book-form
|
4 |
*ngIf="book$ | async as book"
|
@@ -1,28 +1,25 @@
|
|
1 |
<form [formGroup]="form" (ngSubmit)="submitForm()">
|
2 |
-
<label for="title">Title</label>
|
3 |
<input id="title" formControlName="title">
|
4 |
<bm-form-errors
|
5 |
controlName="title"
|
6 |
-
[messages]="
|
7 |
</bm-form-errors>
|
8 |
|
9 |
-
<label for="subtitle">Subtitle</label>
|
10 |
<input id="subtitle" formControlName="subtitle">
|
11 |
|
12 |
-
<label for="isbn">ISBN</label>
|
13 |
<input id="isbn" formControlName="isbn">
|
14 |
<bm-form-errors
|
15 |
controlName="isbn"
|
16 |
-
[messages]="
|
17 |
-
required: 'ISBN is required',
|
18 |
-
isbnformat: 'ISBN must have 10 or 13 chars',
|
19 |
-
isbnexists: 'ISBN already exists'
|
20 |
-
}">
|
21 |
</bm-form-errors>
|
22 |
|
23 |
-
<label>Authors</label>
|
24 |
<button type="button" class="add"
|
25 |
-
(click)="addAuthorControl()"
|
|
|
26 |
+ Author
|
27 |
</button>
|
28 |
<fieldset formArrayName="authors">
|
@@ -33,19 +30,19 @@
|
|
33 |
</fieldset>
|
34 |
<bm-form-errors
|
35 |
controlName="authors"
|
36 |
-
[messages]="
|
37 |
</bm-form-errors>
|
38 |
|
39 |
-
<label for="description">Description</label>
|
40 |
<textarea id="description" formControlName="description"></textarea>
|
41 |
|
42 |
-
<label for="published">Published</label>
|
43 |
<input type="date" useValueAsLocalIso id="published" formControlName="published">
|
44 |
|
45 |
-
<label for="thumbnailUrl">Thumbnail URL</label>
|
46 |
<input type="url" id="thumbnailUrl" formControlName="thumbnailUrl">
|
47 |
|
48 |
-
<button type="submit" [disabled]="form.invalid">
|
49 |
Save
|
50 |
</button>
|
51 |
</form>
|
1 |
<form [formGroup]="form" (ngSubmit)="submitForm()">
|
2 |
+
<label for="title" i18n="label title|Label for the title input@@BookFormComponentLabelTitle">Title</label>
|
3 |
<input id="title" formControlName="title">
|
4 |
<bm-form-errors
|
5 |
controlName="title"
|
6 |
+
[messages]="errorsTranslated('title')">
|
7 |
</bm-form-errors>
|
8 |
|
9 |
+
<label for="subtitle" i18n="label subtitle|Label for the subtitle input@@BookFormComponentLabelSubtitle">Subtitle</label>
|
10 |
<input id="subtitle" formControlName="subtitle">
|
11 |
|
12 |
+
<label for="isbn" i18n="label isbn|Label for the ISBN input@@BookFormComponentLabelIsbn">ISBN</label>
|
13 |
<input id="isbn" formControlName="isbn">
|
14 |
<bm-form-errors
|
15 |
controlName="isbn"
|
16 |
+
[messages]="errorsTranslated('isbn')">
|
|
|
|
|
|
|
|
|
17 |
</bm-form-errors>
|
18 |
|
19 |
+
<label i18n="label authors|Label for the authors inputs@@BookFormComponentLabelAuthors">Authors</label>
|
20 |
<button type="button" class="add"
|
21 |
+
(click)="addAuthorControl()"
|
22 |
+
i18n="button add author|Text for the button to add an author input@@BookFormComponentAddAuthor">
|
23 |
+ Author
|
24 |
</button>
|
25 |
<fieldset formArrayName="authors">
|
30 |
</fieldset>
|
31 |
<bm-form-errors
|
32 |
controlName="authors"
|
33 |
+
[messages]="errorsTranslated('authors')">
|
34 |
</bm-form-errors>
|
35 |
|
36 |
+
<label for="description" i18n="label description|Label for the description text@@BookFormComponentLabelDescription">Description</label>
|
37 |
<textarea id="description" formControlName="description"></textarea>
|
38 |
|
39 |
+
<label for="published" i18n="label published|Label for the published input@@BookFormComponentLabelPublished">Published</label>
|
40 |
<input type="date" useValueAsLocalIso id="published" formControlName="published">
|
41 |
|
42 |
+
<label for="thumbnailUrl" i18n="label thumbnail|Label for the thumbnail input@@BookFormComponentLabelThumbnail">Thumbnail URL</label>
|
43 |
<input type="url" id="thumbnailUrl" formControlName="thumbnailUrl">
|
44 |
|
45 |
+
<button type="submit" [disabled]="form.invalid" i18n="button save|Text for save button@@BookFormComponentSave">
|
46 |
Save
|
47 |
</button>
|
48 |
</form>
|
@@ -89,4 +89,21 @@ export class BookFormComponent implements OnChanges {
|
|
89 |
|
90 |
this.submitBook.emit(newBook);
|
91 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
}
|
89 |
|
90 |
this.submitBook.emit(newBook);
|
91 |
}
|
92 |
+
|
93 |
+
errorsTranslated(controlName: string) {
|
94 |
+
const errorTexts: { [controlName: string]: { [errorCode: string]: string }} = {
|
95 |
+
title: {
|
96 |
+
required: $localize`:title required error@@BookFormComponentErrorTitleRequired:Title is required`
|
97 |
+
},
|
98 |
+
isbn: {
|
99 |
+
required: $localize`:isbn required error@@BookFormComponentErrorIsbnRequired:ISBN is required`,
|
100 |
+
isbnformat: $localize`:isbn format error@@BookFormComponentErrorIsbnFormat:ISBN must have 10 or 13 characters`,
|
101 |
+
isbnexists: $localize`:isbn exists error@@BookFormComponentErrorIsbnExists:ISBN already exists`,
|
102 |
+
},
|
103 |
+
authors: {
|
104 |
+
atleastonevalue: $localize`:at least one author error@@BookFormComponentErrorAtLeastOneAuthor:At least one author required`
|
105 |
+
}
|
106 |
+
}
|
107 |
+
return errorTexts[controlName] || {};
|
108 |
+
}
|
109 |
}
|
@@ -1,14 +1,18 @@
|
|
1 |
<nav>
|
2 |
-
<a routerLink="/home" routerLinkActive="active" ariaCurrentWhenActive="page">Home</a>
|
3 |
-
<a routerLink="/books" routerLinkActive="active" ariaCurrentWhenActive="page">Books</a>
|
4 |
-
<a routerLink="/admin" routerLinkActive="active" ariaCurrentWhenActive="page">Administration</a>
|
5 |
<div class="actions">
|
6 |
<button class="green"
|
7 |
(click)="auth.login()"
|
8 |
-
*ngIf="!auth.isAuthenticated"
|
|
|
9 |
<button class="red"
|
10 |
(click)="auth.logout()"
|
11 |
-
*ngIf="auth.isAuthenticated"
|
|
|
|
|
|
|
12 |
</div>
|
13 |
</nav>
|
14 |
|
1 |
<nav>
|
2 |
+
<a routerLink="/home" routerLinkActive="active" ariaCurrentWhenActive="page" i18n="nav home@@AppComponentHome">Home</a>
|
3 |
+
<a routerLink="/books" routerLinkActive="active" ariaCurrentWhenActive="page" i18n="nav books@@AppComponentBooks">Books</a>
|
4 |
+
<a routerLink="/admin" routerLinkActive="active" ariaCurrentWhenActive="page" i18n="nav admin@@AppComponentAdmin">Administration</a>
|
5 |
<div class="actions">
|
6 |
<button class="green"
|
7 |
(click)="auth.login()"
|
8 |
+
*ngIf="!auth.isAuthenticated"
|
9 |
+
i18n="login@@AppComponentLogin">Login</button>
|
10 |
<button class="red"
|
11 |
(click)="auth.logout()"
|
12 |
+
*ngIf="auth.isAuthenticated"
|
13 |
+
i18n="logout@@AppComponentLogout">Logout</button>
|
14 |
+
<button (click)="changeLocale('de')">DE</button>
|
15 |
+
<button (click)="changeLocale('en')">EN</button>
|
16 |
</div>
|
17 |
</nav>
|
18 |
|
@@ -10,4 +10,9 @@ import { AuthService } from './shared/auth.service';
|
|
10 |
})
|
11 |
export class AppComponent {
|
12 |
constructor(public auth: AuthService) {}
|
|
|
|
|
|
|
|
|
|
|
13 |
}
|
10 |
})
|
11 |
export class AppComponent {
|
12 |
constructor(public auth: AuthService) {}
|
13 |
+
|
14 |
+
changeLocale(targetLang: string) {
|
15 |
+
localStorage.setItem('locale', targetLang);
|
16 |
+
location.reload();
|
17 |
+
}
|
18 |
}
|
@@ -3,32 +3,34 @@
|
|
3 |
<p role="doc-subtitle" *ngIf="book.subtitle">{{ book.subtitle }}</p>
|
4 |
<div class="header">
|
5 |
<div>
|
6 |
-
<h2>Authors</h2>
|
7 |
<ul>
|
8 |
<li *ngFor="let author of book.authors">{{ author }}</li>
|
9 |
</ul>
|
10 |
</div>
|
11 |
<div>
|
12 |
-
<h2>ISBN</h2>
|
13 |
{{ book.isbn | isbn }}
|
14 |
</div>
|
15 |
<div *ngIf="book.published">
|
16 |
-
<h2>Published</h2>
|
17 |
{{ book.published | date:'longDate' }}
|
18 |
</div>
|
19 |
</div>
|
20 |
-
<h2>Description</h2>
|
21 |
<p>{{ book.description }}</p>
|
22 |
<img *ngIf="book.thumbnailUrl"
|
23 |
[src]="book.thumbnailUrl"
|
24 |
-
alt="Cover"
|
25 |
-
|
|
|
26 |
<ng-container *bmLoggedinOnly>
|
27 |
-
<button class="red" bmConfirm="Remove book?" (confirm)="removeBook(book.isbn)">
|
28 |
Remove book
|
29 |
</button>
|
30 |
<a class="button"
|
31 |
-
[routerLink]="['/admin/edit', book.isbn]"
|
|
|
32 |
Edit book
|
33 |
</a>
|
34 |
</ng-container>
|
3 |
<p role="doc-subtitle" *ngIf="book.subtitle">{{ book.subtitle }}</p>
|
4 |
<div class="header">
|
5 |
<div>
|
6 |
+
<h2 i18n="headline authors|Headline for the authors@@BookDetailsComponentAuthors">Authors</h2>
|
7 |
<ul>
|
8 |
<li *ngFor="let author of book.authors">{{ author }}</li>
|
9 |
</ul>
|
10 |
</div>
|
11 |
<div>
|
12 |
+
<h2 i18n="headline ISBN|Headline for the ISBN@@BookDetailsComponentIsbn">ISBN</h2>
|
13 |
{{ book.isbn | isbn }}
|
14 |
</div>
|
15 |
<div *ngIf="book.published">
|
16 |
+
<h2 i18n="headline published|Headline for the published date@@BookDetailsComponentPublished">Published</h2>
|
17 |
{{ book.published | date:'longDate' }}
|
18 |
</div>
|
19 |
</div>
|
20 |
+
<h2 i18n="headline description|Headline for the description@@BookDetailsComponentDescription">Description</h2>
|
21 |
<p>{{ book.description }}</p>
|
22 |
<img *ngIf="book.thumbnailUrl"
|
23 |
[src]="book.thumbnailUrl"
|
24 |
+
alt="Cover"
|
25 |
+
i18n-alt="cover alt text|Alternative text when no cover is defined@@BookDetailsComponentAltCover">
|
26 |
+
<a class="button arrow-left" routerLink=".." i18n="link back|Link for navigating back to the books list@@BookDetailsComponentBackToList">Back to list</a>
|
27 |
<ng-container *bmLoggedinOnly>
|
28 |
+
<button class="red" bmConfirm="Remove book?" (confirm)="removeBook(book.isbn)" i18n="button remove|Button text to remove a book@@BookDetailsComponentRemove">
|
29 |
Remove book
|
30 |
</button>
|
31 |
<a class="button"
|
32 |
+
[routerLink]="['/admin/edit', book.isbn]"
|
33 |
+
i18n="button edit|Button text to edit a book@@BookDetailsComponentEdit">
|
34 |
Edit book
|
35 |
</a>
|
36 |
</ng-container>
|
@@ -1,9 +1,9 @@
|
|
1 |
-
<h1>Books</h1>
|
2 |
<ul class="book-list" *ngIf="books$ | async as books">
|
3 |
<li *ngFor="let book of books">
|
4 |
<bm-book-list-item [book]="book"></bm-book-list-item>
|
5 |
</li>
|
6 |
-
<li *ngIf="!books.length">
|
7 |
No books available.
|
8 |
</li>
|
9 |
</ul>
|
1 |
+
<h1 i18n="title books|Title for the books page@@BookListComponentTitle">Books</h1>
|
2 |
<ul class="book-list" *ngIf="books$ | async as books">
|
3 |
<li *ngFor="let book of books">
|
4 |
<bm-book-list-item [book]="book"></bm-book-list-item>
|
5 |
</li>
|
6 |
+
<li *ngIf="!books.length" i18n="text no books|Text displayed when no books are available@@BookListComponentNoBooks">
|
7 |
No books available.
|
8 |
</li>
|
9 |
</ul>
|
@@ -1,5 +1,5 @@
|
|
1 |
<a *ngIf="book" [routerLink]="book.isbn" class="list-item">
|
2 |
-
<img *ngIf="book.thumbnailUrl" [src]="book.thumbnailUrl" alt="Cover">
|
3 |
<h2>{{ book.title }}</h2>
|
4 |
<p role="doc-subtitle" *ngIf="book.subtitle">
|
5 |
{{ book.subtitle }}
|
@@ -9,5 +9,5 @@
|
|
9 |
{{ author }}
|
10 |
</li>
|
11 |
</ul>
|
12 |
-
<div>ISBN {{ book.isbn | isbn }}</div>
|
13 |
</a>
|
1 |
<a *ngIf="book" [routerLink]="book.isbn" class="list-item">
|
2 |
+
<img *ngIf="book.thumbnailUrl" [src]="book.thumbnailUrl" alt="Cover" i18n-alt="cover alt text|Alternative text when no cover is defined@@BookListItemComponentAltCover">
|
3 |
<h2>{{ book.title }}</h2>
|
4 |
<p role="doc-subtitle" *ngIf="book.subtitle">
|
5 |
{{ book.subtitle }}
|
9 |
{{ author }}
|
10 |
</li>
|
11 |
</ul>
|
12 |
+
<div><ng-container i18n="book text before ISBN|Text for ISBN right before the number@@BookListItemComponentIsbn">ISBN</ng-container> {{ book.isbn | isbn }}</div>
|
13 |
</a>
|
@@ -1,8 +1,8 @@
|
|
1 |
-
<h1>Home</h1>
|
2 |
|
3 |
-
<a routerLink="/books" class="button red">
|
4 |
Show book list
|
5 |
</a>
|
6 |
|
7 |
-
<h2>Search</h2>
|
8 |
<bm-search></bm-search>
|
1 |
+
<h1 i18n="title home|Title for the home page@@HomeComponentTitle">Home</h1>
|
2 |
|
3 |
+
<a routerLink="/books" class="button red" i18n="link books list|Text of the link to the books screen@@HomeComponentBooks">
|
4 |
Show book list
|
5 |
</a>
|
6 |
|
7 |
+
<h2 i18n="headline search|Headline text above the search input@@HomeComponentSearchHeadline">Search</h2>
|
8 |
<bm-search></bm-search>
|
@@ -1,6 +1,7 @@
|
|
1 |
<input type="search"
|
2 |
autocomplete="off"
|
3 |
aria-label="Search"
|
|
|
4 |
[class.loading]="isLoading"
|
5 |
#searchInput
|
6 |
(input)="input$.next(searchInput.value)">
|
1 |
<input type="search"
|
2 |
autocomplete="off"
|
3 |
aria-label="Search"
|
4 |
+
i18n-aria-label="search input ARIA label|input search@@SearchComponentInput"
|
5 |
[class.loading]="isLoading"
|
6 |
#searchInput
|
7 |
(input)="input$.next(searchInput.value)">
|
@@ -1,7 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
|
|
|
|
2 |
import { AppModule } from './app/app.module';
|
3 |
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/// <reference types="@angular/localize" />
|
2 |
+
|
3 |
+
import { registerLocaleData } from '@angular/common';
|
4 |
+
import { LOCALE_ID } from '@angular/core';
|
5 |
+
import { loadTranslations } from '@angular/localize';
|
6 |
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
7 |
+
import localeDe from '@angular/common/locales/de';
|
8 |
+
|
9 |
import { AppModule } from './app/app.module';
|
10 |
|
11 |
+
async function setupLocale() {
|
12 |
+
if (localStorage.getItem('locale') !== 'de') {
|
13 |
+
return 'en-US';
|
14 |
+
}
|
15 |
+
const response = await fetch('messages.de.json');
|
16 |
+
const result = await response.json();
|
17 |
+
loadTranslations(result.translations);
|
18 |
+
registerLocaleData(localeDe);
|
19 |
+
return 'de';
|
20 |
+
}
|
21 |
+
|
22 |
+
setupLocale().then(localeValue => {
|
23 |
+
platformBrowserDynamic([
|
24 |
+
{ provide: LOCALE_ID, useValue: localeValue }
|
25 |
+
])
|
26 |
+
.bootstrapModule(AppModule, {
|
27 |
+
ngZoneEventCoalescing: true
|
28 |
+
})
|
29 |
+
.catch(err => console.error(err));
|
30 |
+
});
|
@@ -4,7 +4,9 @@
|
|
4 |
"extends": "./tsconfig.json",
|
5 |
"compilerOptions": {
|
6 |
"outDir": "./out-tsc/app",
|
7 |
-
"types": [
|
|
|
|
|
8 |
},
|
9 |
"files": [
|
10 |
"src/main.ts"
|
4 |
"extends": "./tsconfig.json",
|
5 |
"compilerOptions": {
|
6 |
"outDir": "./out-tsc/app",
|
7 |
+
"types": [
|
8 |
+
"@angular/localize"
|
9 |
+
]
|
10 |
},
|
11 |
"files": [
|
12 |
"src/main.ts"
|
@@ -5,7 +5,8 @@
|
|
5 |
"compilerOptions": {
|
6 |
"outDir": "./out-tsc/spec",
|
7 |
"types": [
|
8 |
-
"jasmine"
|
|
|
9 |
]
|
10 |
},
|
11 |
"include": [
|
5 |
"compilerOptions": {
|
6 |
"outDir": "./out-tsc/spec",
|
7 |
"types": [
|
8 |
+
"jasmine",
|
9 |
+
"@angular/localize"
|
10 |
]
|
11 |
},
|
12 |
"include": [
|