Skip to content

Commit 2e213ec

Browse files
author
wg102
committed
feat: Add Official Languages with ngx-translate
1 parent 49dea69 commit 2e213ec

29 files changed

+543
-158
lines changed

frontend/package-lock.json

+22-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@
2626
"@fortawesome/fontawesome-svg-core": "^1.2.30",
2727
"@fortawesome/free-brands-svg-icons": "^5.14.0",
2828
"@fortawesome/free-solid-svg-icons": "^5.14.0",
29+
"@ngx-translate/core": "^13.0.0",
30+
"@ngx-translate/http-loader": "^6.0.0",
2931
"core-js": "^3.6.5",
3032
"fontawesome": "^5.6.3",
3133
"hammerjs": "^2.0.8",
3234
"lodash": "^4.17.19",
3335
"material-icons": "^0.3.1",
3436
"node-sass": "^4.14.1",
35-
"rxjs": "^6.6.0",
37+
"rxjs": "^6.6.3",
3638
"tslib": "^2.0.0",
3739
"zone.js": "~0.10.3"
3840
},

frontend/src/app/app.component.ts

+6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
import { Component } from "@angular/core";
2+
import {TranslateService} from "@ngx-translate/core";
23

34
@Component({
45
selector: "app-root",
56
templateUrl: "./app.component.html",
67
styleUrls: ["./app.component.scss"]
78
})
89
export class AppComponent {
10+
constructor(private translate: TranslateService) {
11+
const currentLanguage = this.translate.getBrowserLang();
12+
const lang = currentLanguage.match(/en|fr/) ? currentLanguage : "en";
13+
translate.setDefaultLang(lang);
14+
}
915
title = "Volumes";
1016
}

frontend/src/app/app.module.ts

+22-2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ import { FormConfigurationsComponent } from "./resource-form/form-configurations
5151
import { FormGpusComponent } from "./resource-form/form-gpus/form-gpus.component";
5252
import { VolumeTableComponent } from "./main-table/volumes-table/volume-table.component";
5353
import { KubecostService } from './services/kubecost.service';
54+
import {TranslateLoader, TranslateModule} from "@ngx-translate/core";
55+
import {TranslateHttpLoader} from "@ngx-translate/http-loader";
56+
import {HttpClient} from "@angular/common/http";
5457

5558
@NgModule({
5659
declarations: [
@@ -90,9 +93,21 @@ import { KubecostService } from './services/kubecost.service';
9093
FontAwesomeModule,
9194
BrowserAnimationsModule,
9295
FormsModule,
93-
ReactiveFormsModule
96+
ReactiveFormsModule,
97+
TranslateModule.forRoot({
98+
loader: {
99+
provide: TranslateLoader,
100+
useFactory: httpTranslateLoader,
101+
deps: [HttpClient]
102+
}
103+
})
104+
],
105+
providers: [
106+
NamespaceService,
107+
KubecostService,
108+
KubernetesService,
109+
SnackBarService
94110
],
95-
providers: [NamespaceService, KubecostService, KubernetesService, SnackBarService],
96111
bootstrap: [AppComponent],
97112
entryComponents: [SnackBarComponent, ConfirmDialogComponent]
98113
})
@@ -110,3 +125,8 @@ export class AppModule {
110125
);
111126
}
112127
}
128+
129+
// AOT compilation support
130+
export function httpTranslateLoader(http: HttpClient) {
131+
return new TranslateHttpLoader(http, "../jupyter/assets/i18n/", ".json");
132+
}
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,30 @@
11
<div class="card mat-elevation-z2 mat-typography">
22
<div class="header">
33
<mat-icon>attach_money</mat-icon>
4-
<p>Cost</p>
4+
<p>{{ "costTable.title" | translate }}</p>
55
</div>
66

77
<p>
8-
Estimated cost per day of notebook server resources – summed values may not
9-
match total due to rounding
8+
{{ "costTable.txtCost" | translate }}
109
</p>
1110

1211
<mat-divider></mat-divider>
1312
<table style="width: auto">
14-
<tr class="mat-header-row" style="text-align: left;">
15-
<th class="mat-header-cell">Compute</th>
16-
<th class="mat-header-cell">GPUs</th>
17-
<th class="mat-header-cell">Storage</th>
18-
<th class="mat-header-cell" width="99%">Total</th>
13+
<tr class="mat-header-row" style="text-align: left">
14+
<th class="mat-header-cell">{{ "costTable.thCompute" | translate }}</th>
15+
<th class="mat-header-cell">{{ "costTable.thGpus" | translate }}</th>
16+
<th class="mat-header-cell">{{ "costTable.thStorage" | translate }}</th>
17+
<th class="mat-header-cell" width="99%">
18+
{{ "costTable.thTotal" | translate }}
19+
</th>
1920
</tr>
2021
<tr *ngIf="aggregatedCost?.data[currNamespace]; let cost" class="mat-row" style="text-align: left;">
2122
<td class="mat-cell">{{ formatCost(cost.cpuCost + cost.ramCost) }}</td>
2223
<td class="mat-cell">{{ formatCost(cost.gpuCost) }}</td>
2324
<td class="mat-cell">{{ formatCost(cost.pvCost) }}</td>
24-
<td class="mat-cell" style="font-weight: 500;">{{ formatCost(cost.totalCost) }}</td>
25+
<td class="mat-cell" style="font-weight: 500;">
26+
{{ formatCost(cost.totalCost) }}
27+
</td>
2528
</tr>
2629
</table>
27-
</div>
30+
</div>

frontend/src/app/main-table/namespace-select/namespace-select.component.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<!-- Namespaces Selector -->
22
<mat-form-field>
3-
<mat-label>Select Namespace</mat-label>
3+
<mat-label>{{ "namespaceSelect.lblSelectNamespace" | translate }}</mat-label>
44
<input
55
matInput
66
[(ngModel)]="currNamespace"
@@ -14,5 +14,5 @@
1414
class="margin"
1515
(click)="namespaceChanged(currNamespace)"
1616
>
17-
Update
17+
{{ "namespaceSelect.btnUpdate" | translate }}
1818
</button>

frontend/src/app/main-table/resource-table/resource-table.component.html

+28-14
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
<div class="card mat-elevation-z2">
22
<div class="header">
33
<mat-icon>computer</mat-icon>
4-
<p>Notebook Servers</p>
4+
<p>{{ "resourceTable.title" | translate }}</p>
55

66
<div class="spacer"></div>
77

88
<a routerLink="/new">
99
<button mat-button id="add-nb" color="accent">
10-
<mat-icon>add</mat-icon>NEW SERVER
10+
<mat-icon>add</mat-icon>{{ "resourceTable.btnNewServer" | translate }}
1111
</button>
1212
</a>
1313
</div>
@@ -17,36 +17,38 @@
1717
<table mat-table [dataSource]="notebooks" matSort>
1818
<!-- Status Column -->
1919
<ng-container matColumnDef="status">
20-
<th mat-header-cell *matHeaderCellDef>Status</th>
20+
<th mat-header-cell *matHeaderCellDef>
21+
{{ "resourceTable.thStatus" | translate }}
22+
</th>
2123
<td mat-cell *matCellDef="let elem">
2224
<!-- Running -->
2325
<mat-icon
2426
*ngIf="elem.status === 'running'"
2527
[ngClass]="['running', 'status']"
26-
[matTooltip]="elem.reason"
28+
matTooltip="{{ 'resourceTable.tooltipRunning' | translate }}"
2729
>check_circle
2830
</mat-icon>
2931

3032
<!-- Warning -->
3133
<mat-icon
3234
*ngIf="elem.status === 'warning'"
3335
[ngClass]="['warning', 'status']"
34-
[matTooltip]="elem.reason"
36+
matTooltip="{{ 'resourceTable.tooltipWarning' | translate }}"
3537
>warning
3638
</mat-icon>
3739

3840
<!-- Error -->
3941
<mat-icon
4042
*ngIf="elem.status === 'error'"
4143
[ngClass]="['error', 'status']"
42-
[matTooltip]="elem.reason"
44+
matTooltip="{{ 'resourceTable.tooltipError' | translate }}"
4345
>clear
4446
</mat-icon>
4547

4648
<!-- Waiting -->
4749
<mat-spinner
4850
*ngIf="elem.status === 'waiting'"
49-
[matTooltip]="elem.reason"
51+
matTooltip="{{ 'resourceTable.tooltipWaiting' | translate }}"
5052
diameter="24"
5153
class="inline"
5254
>
@@ -56,39 +58,51 @@
5658

5759
<!-- Name Column -->
5860
<ng-container matColumnDef="name">
59-
<th mat-header-cell *matHeaderCellDef>Name</th>
61+
<th mat-header-cell *matHeaderCellDef>
62+
{{ "resourceTable.thName" | translate }}
63+
</th>
6064
<td mat-cell *matCellDef="let elem">{{ elem.name }}</td>
6165
</ng-container>
6266

6367
<!-- Age Column -->
6468
<ng-container matColumnDef="age">
65-
<th mat-header-cell *matHeaderCellDef>Age</th>
69+
<th mat-header-cell *matHeaderCellDef>
70+
{{ "resourceTable.thAge" | translate }}
71+
</th>
6672
<td mat-cell *matCellDef="let elem">{{ elem.age }}</td>
6773
</ng-container>
6874

6975
<!-- Image Column -->
7076
<ng-container matColumnDef="image">
71-
<th mat-header-cell *matHeaderCellDef>Image</th>
77+
<th mat-header-cell *matHeaderCellDef>
78+
{{ "resourceTable.thImage" | translate }}
79+
</th>
7280
<td mat-cell *matCellDef="let elem">
7381
<span [matTooltip]="elem.image">{{ elem.shortImage }}</span>
7482
</td>
7583
</ng-container>
7684

7785
<!-- CPU Column -->
7886
<ng-container matColumnDef="cpu">
79-
<th mat-header-cell *matHeaderCellDef>CPU</th>
87+
<th mat-header-cell *matHeaderCellDef>
88+
{{ "resourceTable.thCpu" | translate }}
89+
</th>
8090
<td mat-cell *matCellDef="let elem">{{ elem.cpu }}</td>
8191
</ng-container>
8292

8393
<!-- Memory Column -->
8494
<ng-container matColumnDef="memory">
85-
<th mat-header-cell *matHeaderCellDef>Memory</th>
95+
<th mat-header-cell *matHeaderCellDef>
96+
{{ "resourceTable.thMemory" | translate }}
97+
</th>
8698
<td mat-cell *matCellDef="let elem">{{ elem.memory }}</td>
8799
</ng-container>
88100

89101
<!-- Volumes Column -->
90102
<ng-container matColumnDef="volumes">
91-
<th mat-header-cell *matHeaderCellDef>Volumes</th>
103+
<th mat-header-cell *matHeaderCellDef>
104+
{{ "resourceTable.thVolumes" | translate }}
105+
</th>
92106
<td mat-cell *matCellDef="let elem">
93107
<button mat-icon-button [matMenuTriggerFor]="volsMenu">
94108
<mat-icon>more_vert</mat-icon>
@@ -113,7 +127,7 @@
113127
color="accent"
114128
[disabled]="elem.status !== 'running'"
115129
>
116-
CONNECT
130+
{{ "resourceTable.btnConnect" | translate }}
117131
</button>
118132

119133
<button

frontend/src/app/main-table/resource-table/resource-table.component.ts

+11-9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Resource } from "src/app/utils/types";
44
import {MatDialog} from "@angular/material/dialog";
55
import {ConfirmDialogComponent} from "../confirm-dialog/confirm-dialog.component";
66
import {first} from "rxjs/operators";
7+
import {TranslateService} from "@ngx-translate/core";
78

89
@Component({
910
selector: "app-resource-table",
@@ -27,31 +28,32 @@ export class ResourceTableComponent{
2728
dataSource = new MatTableDataSource();
2829

2930
constructor(
30-
private dialog: MatDialog
31-
) {}
31+
private dialog: MatDialog,
32+
private translate: TranslateService
33+
) { }
3234

3335
// Resource (Notebook) Actions
3436
connectResource(rsrc: Resource): void {
3537
window.open(`/notebook/${rsrc.namespace}/${rsrc.name}/`);
3638
}
37-
39+
3840
deleteResource(rsrc: Resource): void {
41+
const yesAnswer = this.translate.instant("resourceTable.deleteDialogYes");
3942
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
4043
width: "fit-content",
4144
data: {
42-
title: "You are about to delete Notebook Server: " + rsrc.name,
45+
title: this.translate.instant("resourceTable.deleteDialogTitle") + rsrc.name,
4346
message:
44-
"Are you sure you want to delete this Notebook Server? " +
45-
"Your data might be lost if the Server is not backed by persistent storage.",
46-
yes: "delete",
47-
no: "cancel"
47+
this.translate.instant("resourceTable.deleteDialogMessage"),
48+
yes: yesAnswer,
49+
no: this.translate.instant("resourceTable.deleteDialogNo")
4850
}
4951
});
5052
dialogRef
5153
.afterClosed()
5254
.pipe(first())
5355
.subscribe(result => {
54-
if (result !== "delete") {
56+
if (result !== yesAnswer) {
5557
return;
5658
}
5759
this.deleteNotebookEvent.emit(rsrc);

0 commit comments

Comments
 (0)