Skip to content

Commit

Permalink
feat: Add Official Languages with ngx-translate
Browse files Browse the repository at this point in the history
  • Loading branch information
wg102 committed Nov 4, 2020
1 parent 49dea69 commit 2e213ec
Show file tree
Hide file tree
Showing 29 changed files with 543 additions and 158 deletions.
28 changes: 22 additions & 6 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@
"@fortawesome/fontawesome-svg-core": "^1.2.30",
"@fortawesome/free-brands-svg-icons": "^5.14.0",
"@fortawesome/free-solid-svg-icons": "^5.14.0",
"@ngx-translate/core": "^13.0.0",
"@ngx-translate/http-loader": "^6.0.0",
"core-js": "^3.6.5",
"fontawesome": "^5.6.3",
"hammerjs": "^2.0.8",
"lodash": "^4.17.19",
"material-icons": "^0.3.1",
"node-sass": "^4.14.1",
"rxjs": "^6.6.0",
"rxjs": "^6.6.3",
"tslib": "^2.0.0",
"zone.js": "~0.10.3"
},
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { Component } from "@angular/core";
import {TranslateService} from "@ngx-translate/core";

@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.scss"]
})
export class AppComponent {
constructor(private translate: TranslateService) {
const currentLanguage = this.translate.getBrowserLang();
const lang = currentLanguage.match(/en|fr/) ? currentLanguage : "en";
translate.setDefaultLang(lang);
}
title = "Volumes";
}
24 changes: 22 additions & 2 deletions frontend/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ import { FormConfigurationsComponent } from "./resource-form/form-configurations
import { FormGpusComponent } from "./resource-form/form-gpus/form-gpus.component";
import { VolumeTableComponent } from "./main-table/volumes-table/volume-table.component";
import { KubecostService } from './services/kubecost.service';
import {TranslateLoader, TranslateModule} from "@ngx-translate/core";
import {TranslateHttpLoader} from "@ngx-translate/http-loader";
import {HttpClient} from "@angular/common/http";

@NgModule({
declarations: [
Expand Down Expand Up @@ -90,9 +93,21 @@ import { KubecostService } from './services/kubecost.service';
FontAwesomeModule,
BrowserAnimationsModule,
FormsModule,
ReactiveFormsModule
ReactiveFormsModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: httpTranslateLoader,
deps: [HttpClient]
}
})
],
providers: [
NamespaceService,
KubecostService,
KubernetesService,
SnackBarService
],
providers: [NamespaceService, KubecostService, KubernetesService, SnackBarService],
bootstrap: [AppComponent],
entryComponents: [SnackBarComponent, ConfirmDialogComponent]
})
Expand All @@ -110,3 +125,8 @@ export class AppModule {
);
}
}

// AOT compilation support
export function httpTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, "../jupyter/assets/i18n/", ".json");
}
23 changes: 13 additions & 10 deletions frontend/src/app/main-table/cost-table/cost-table.component.html
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
<div class="card mat-elevation-z2 mat-typography">
<div class="header">
<mat-icon>attach_money</mat-icon>
<p>Cost</p>
<p>{{ "costTable.title" | translate }}</p>
</div>

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

<mat-divider></mat-divider>
<table style="width: auto">
<tr class="mat-header-row" style="text-align: left;">
<th class="mat-header-cell">Compute</th>
<th class="mat-header-cell">GPUs</th>
<th class="mat-header-cell">Storage</th>
<th class="mat-header-cell" width="99%">Total</th>
<tr class="mat-header-row" style="text-align: left">
<th class="mat-header-cell">{{ "costTable.thCompute" | translate }}</th>
<th class="mat-header-cell">{{ "costTable.thGpus" | translate }}</th>
<th class="mat-header-cell">{{ "costTable.thStorage" | translate }}</th>
<th class="mat-header-cell" width="99%">
{{ "costTable.thTotal" | translate }}
</th>
</tr>
<tr *ngIf="aggregatedCost?.data[currNamespace]; let cost" class="mat-row" style="text-align: left;">
<td class="mat-cell">{{ formatCost(cost.cpuCost + cost.ramCost) }}</td>
<td class="mat-cell">{{ formatCost(cost.gpuCost) }}</td>
<td class="mat-cell">{{ formatCost(cost.pvCost) }}</td>
<td class="mat-cell" style="font-weight: 500;">{{ formatCost(cost.totalCost) }}</td>
<td class="mat-cell" style="font-weight: 500;">
{{ formatCost(cost.totalCost) }}
</td>
</tr>
</table>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!-- Namespaces Selector -->
<mat-form-field>
<mat-label>Select Namespace</mat-label>
<mat-label>{{ "namespaceSelect.lblSelectNamespace" | translate }}</mat-label>
<input
matInput
[(ngModel)]="currNamespace"
Expand All @@ -14,5 +14,5 @@
class="margin"
(click)="namespaceChanged(currNamespace)"
>
Update
{{ "namespaceSelect.btnUpdate" | translate }}
</button>
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<div class="card mat-elevation-z2">
<div class="header">
<mat-icon>computer</mat-icon>
<p>Notebook Servers</p>
<p>{{ "resourceTable.title" | translate }}</p>

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

<a routerLink="/new">
<button mat-button id="add-nb" color="accent">
<mat-icon>add</mat-icon>NEW SERVER
<mat-icon>add</mat-icon>{{ "resourceTable.btnNewServer" | translate }}
</button>
</a>
</div>
Expand All @@ -17,36 +17,38 @@
<table mat-table [dataSource]="notebooks" matSort>
<!-- Status Column -->
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef>Status</th>
<th mat-header-cell *matHeaderCellDef>
{{ "resourceTable.thStatus" | translate }}
</th>
<td mat-cell *matCellDef="let elem">
<!-- Running -->
<mat-icon
*ngIf="elem.status === 'running'"
[ngClass]="['running', 'status']"
[matTooltip]="elem.reason"
matTooltip="{{ 'resourceTable.tooltipRunning' | translate }}"
>check_circle
</mat-icon>

<!-- Warning -->
<mat-icon
*ngIf="elem.status === 'warning'"
[ngClass]="['warning', 'status']"
[matTooltip]="elem.reason"
matTooltip="{{ 'resourceTable.tooltipWarning' | translate }}"
>warning
</mat-icon>

<!-- Error -->
<mat-icon
*ngIf="elem.status === 'error'"
[ngClass]="['error', 'status']"
[matTooltip]="elem.reason"
matTooltip="{{ 'resourceTable.tooltipError' | translate }}"
>clear
</mat-icon>

<!-- Waiting -->
<mat-spinner
*ngIf="elem.status === 'waiting'"
[matTooltip]="elem.reason"
matTooltip="{{ 'resourceTable.tooltipWaiting' | translate }}"
diameter="24"
class="inline"
>
Expand All @@ -56,39 +58,51 @@

<!-- Name Column -->
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>Name</th>
<th mat-header-cell *matHeaderCellDef>
{{ "resourceTable.thName" | translate }}
</th>
<td mat-cell *matCellDef="let elem">{{ elem.name }}</td>
</ng-container>

<!-- Age Column -->
<ng-container matColumnDef="age">
<th mat-header-cell *matHeaderCellDef>Age</th>
<th mat-header-cell *matHeaderCellDef>
{{ "resourceTable.thAge" | translate }}
</th>
<td mat-cell *matCellDef="let elem">{{ elem.age }}</td>
</ng-container>

<!-- Image Column -->
<ng-container matColumnDef="image">
<th mat-header-cell *matHeaderCellDef>Image</th>
<th mat-header-cell *matHeaderCellDef>
{{ "resourceTable.thImage" | translate }}
</th>
<td mat-cell *matCellDef="let elem">
<span [matTooltip]="elem.image">{{ elem.shortImage }}</span>
</td>
</ng-container>

<!-- CPU Column -->
<ng-container matColumnDef="cpu">
<th mat-header-cell *matHeaderCellDef>CPU</th>
<th mat-header-cell *matHeaderCellDef>
{{ "resourceTable.thCpu" | translate }}
</th>
<td mat-cell *matCellDef="let elem">{{ elem.cpu }}</td>
</ng-container>

<!-- Memory Column -->
<ng-container matColumnDef="memory">
<th mat-header-cell *matHeaderCellDef>Memory</th>
<th mat-header-cell *matHeaderCellDef>
{{ "resourceTable.thMemory" | translate }}
</th>
<td mat-cell *matCellDef="let elem">{{ elem.memory }}</td>
</ng-container>

<!-- Volumes Column -->
<ng-container matColumnDef="volumes">
<th mat-header-cell *matHeaderCellDef>Volumes</th>
<th mat-header-cell *matHeaderCellDef>
{{ "resourceTable.thVolumes" | translate }}
</th>
<td mat-cell *matCellDef="let elem">
<button mat-icon-button [matMenuTriggerFor]="volsMenu">
<mat-icon>more_vert</mat-icon>
Expand All @@ -113,7 +127,7 @@
color="accent"
[disabled]="elem.status !== 'running'"
>
CONNECT
{{ "resourceTable.btnConnect" | translate }}
</button>

<button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Resource } from "src/app/utils/types";
import {MatDialog} from "@angular/material/dialog";
import {ConfirmDialogComponent} from "../confirm-dialog/confirm-dialog.component";
import {first} from "rxjs/operators";
import {TranslateService} from "@ngx-translate/core";

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

constructor(
private dialog: MatDialog
) {}
private dialog: MatDialog,
private translate: TranslateService
) { }

// Resource (Notebook) Actions
connectResource(rsrc: Resource): void {
window.open(`/notebook/${rsrc.namespace}/${rsrc.name}/`);
}

deleteResource(rsrc: Resource): void {
const yesAnswer = this.translate.instant("resourceTable.deleteDialogYes");
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
width: "fit-content",
data: {
title: "You are about to delete Notebook Server: " + rsrc.name,
title: this.translate.instant("resourceTable.deleteDialogTitle") + rsrc.name,
message:
"Are you sure you want to delete this Notebook Server? " +
"Your data might be lost if the Server is not backed by persistent storage.",
yes: "delete",
no: "cancel"
this.translate.instant("resourceTable.deleteDialogMessage"),
yes: yesAnswer,
no: this.translate.instant("resourceTable.deleteDialogNo")
}
});
dialogRef
.afterClosed()
.pipe(first())
.subscribe(result => {
if (result !== "delete") {
if (result !== yesAnswer) {
return;
}
this.deleteNotebookEvent.emit(rsrc);
Expand Down
Loading

0 comments on commit 2e213ec

Please sign in to comment.