diff --git a/package-lock.json b/package-lock.json index 9b7fde0996c432ef5ba23f4c21ada440658c7778..b55cac4b4c2598321099732493386d243facd4f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,8 @@ "@angular/platform-server": "^17.3.0", "@angular/router": "^17.3.0", "@angular/ssr": "^17.3.3", + "@ngx-translate/core": "^15.0.0", + "@ngx-translate/http-loader": "^8.0.0", "base64-js": "^1.5.1", "chart.js": "^4.4.4", "express": "^4.18.2", @@ -4051,6 +4053,33 @@ "webpack": "^5.54.0" } }, + "node_modules/@ngx-translate/core": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-15.0.0.tgz", + "integrity": "sha512-Am5uiuR0bOOxyoercDnAA3rJVizo4RRqJHo8N3RqJ+XfzVP/I845yEnMADykOHvM6HkVm4SZSnJBOiz0Anx5BA==", + "engines": { + "node": "^16.13.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/common": ">=16.0.0", + "@angular/core": ">=16.0.0", + "rxjs": "^6.5.5 || ^7.4.0" + } + }, + "node_modules/@ngx-translate/http-loader": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-8.0.0.tgz", + "integrity": "sha512-SFMsdUcmHF5OdZkL1CHEoSAwbP5EbAOPTLLboOCRRoOg21P4GJx+51jxGdJeGve6LSKLf4Pay7BkTwmE6vxYlg==", + "engines": { + "node": "^16.13.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/common": ">=16.0.0", + "@angular/core": ">=16.0.0", + "@ngx-translate/core": ">=15.0.0", + "rxjs": "^6.5.5 || ^7.4.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", diff --git a/package.json b/package.json index 6779e75c2e3be028b2af1dcbe2e253d78aec89c2..72b7e5454101b8c8060a52fd15cf949724418f3e 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,8 @@ "@angular/platform-server": "^17.3.0", "@angular/router": "^17.3.0", "@angular/ssr": "^17.3.3", + "@ngx-translate/core": "^15.0.0", + "@ngx-translate/http-loader": "^8.0.0", "base64-js": "^1.5.1", "chart.js": "^4.4.4", "express": "^4.18.2", diff --git a/src/app/app.component.html b/src/app/app.component.html index e14291917d8852ca95d254be01d14968b774d818..b063db6192deb4dd07b6a22f9d7938053ba293e0 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,13 +1,13 @@ <mat-toolbar> - <button mat-icon-button class="app-sidenav-toggle-button" (click)="sidenav.toggle()" matTooltip="Menu"> + <button mat-icon-button class="app-sidenav-toggle-button" (click)="sidenav.toggle()" matTooltip="{{ 'TOOLBAR.MENU' | translate }}"> <mat-icon>menu</mat-icon> </button> - <span>Moulinet</span> + <span>{{ 'TOOLBAR.TITLE' | translate }}</span> <span class="toolbar-item-spacer"></span> - <button mat-icon-button matTooltip="Search"> + <button mat-icon-button matTooltip="{{ 'TOOLBAR.SEARCH' | translate }}"> <mat-icon>search</mat-icon> </button> - <button mat-icon-button matTooltip="Help"> + <button mat-icon-button matTooltip="{{ 'TOOLBAR.HELP' | translate }}"> <mat-icon>help</mat-icon> </button> </mat-toolbar> @@ -16,37 +16,46 @@ <mat-sidenav #sidenav mode="over" [class.mat-elevation-z4]="true" class="app-sidenav"> <mat-nav-list> <mat-list-item routerLink="equipment" (click)="sidenav.close()"> - <mat-icon class="icon-list-item">construction</mat-icon><span>Matériel</span> + <mat-icon class="icon-list-item">construction</mat-icon> + <span>{{ 'SIDENAV.MATERIAL' | translate }}</span> </mat-list-item> <mat-list-item routerLink="list" (click)="sidenav.close()"> - <mat-icon class="icon-list-item">waves icon</mat-icon><span>Liste des jaugeages</span> + <mat-icon class="icon-list-item">waves</mat-icon> + <span>{{ 'SIDENAV.GAUGING_LIST' | translate }}</span> </mat-list-item> <mat-list-item (click)="sidenav.close()"> - <button (click)="createNewGauging()"> <mat-icon - class="icon-list-item">waves icon</mat-icon><span>Nouveau jaugeage</span> + <button (click)="createNewGauging()"> + <mat-icon class="icon-list-item">waves</mat-icon> + <span>{{ 'SIDENAV.NEW_GAUGING' | translate }}</span> </button> </mat-list-item> <mat-list-item (click)="sidenav.close()"> - <button [disabled]="(materialList$ | async)?.length! === 0" (click)="newSession()"> <mat-icon - class="icon-list-item">insert_drive_file</mat-icon><span>Réinitialiser la session</span></button> + <button [disabled]="(materialList$ | async)?.length! === 0 && (gaugingList$ | async)?.length! === 0" (click)="newSession()"> + <mat-icon class="icon-list-item">insert_drive_file</mat-icon> + <span>{{ 'SIDENAV.RESET_SESSION' | translate }}</span> + </button> </mat-list-item> <mat-list-item (click)="loadSession(); sidenav.close()"> - <mat-icon class="icon-list-item">folder_open</mat-icon><span>Charger une session</span> + <mat-icon class="icon-list-item">folder_open</mat-icon> + <span>{{ 'SIDENAV.LOAD_SESSION' | translate }}</span> </mat-list-item> <mat-list-item (click)="sidenav.close()"> - <button [disabled]="(materialList$ | async)?.length! === 0 && (gaugingList$ | async)?.length! === 0" (click)="saveSession()"> <mat-icon - class="icon-list-item">file_download</mat-icon><span>Enregistrer la session</span></button> + <button [disabled]="(materialList$ | async)?.length! === 0 && (gaugingList$ | async)?.length! === 0" (click)="saveSession()"> + <mat-icon class="icon-list-item">file_download</mat-icon> + <span>{{ 'SIDENAV.SAVE_SESSION' | translate }}</span> + </button> </mat-list-item> - <mat-list-item (click)="sidenav.close()"> - <mat-icon class="icon-list-item">settings</mat-icon><span>Configuration</span> + <mat-list-item routerLink="settings" (click)="sidenav.close()"> + <mat-icon class="icon-list-item">settings</mat-icon> + <span>{{ 'SIDENAV.CONFIGURATION' | translate }}</span> </mat-list-item> <mat-list-item (click)="sidenav.close()"> - <mat-icon class="icon-list-item">help</mat-icon><span>Documentation</span> + <mat-icon class="icon-list-item">help</mat-icon> + <span>{{ 'SIDENAV.DOCUMENTATION' | translate }}</span> </mat-list-item> </mat-nav-list> </mat-sidenav> <mat-sidenav-content> - <router-outlet> - </router-outlet> + <router-outlet></router-outlet> </mat-sidenav-content> -</mat-sidenav-container> \ No newline at end of file +</mat-sidenav-container> diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 5afb4af9ebad6b98abdd1419b8b344dba0f9ba60..6f77559d52348f98468c6d77c63d0c57ddc0f4e2 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -22,11 +22,15 @@ import { GaugingService } from './services/gauging.service'; import { Gauging } from './models/gauging'; import { DialogConfirmNewGaugingComponent } from './components/dialog/dialog-confirm-new-gauging/dialog-confirm-new-gauging.component'; import { GaugingFormService } from './services/forms/gauging-form.service'; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { LanguageService } from './services/language/language.service'; + @Component({ selector: 'app-root', standalone: true, - imports: [AsyncPipe, RouterOutlet, RouterModule, RouterLink, MatButtonModule, MatToolbarModule, MatSidenavModule, MatListModule, MatExpansionModule, MatIconModule, MatMenuModule], + imports: [MatTooltipModule,AsyncPipe, RouterOutlet, RouterModule, RouterLink, MatButtonModule, MatToolbarModule, MatSidenavModule, MatListModule, MatExpansionModule, MatIconModule, MatMenuModule, TranslateModule], templateUrl: './app.component.html', styleUrl: './app.component.scss' }) @@ -38,15 +42,17 @@ export class AppComponent { gaugingList$: Observable<Gauging[]>; materialList: Material[] = []; - constructor(private dialog: MatDialog, private equipmentService: EquipmentService, private gaugingService: GaugingService, private gaugingFormService: GaugingFormService, private router: Router) { + constructor(private languageService: LanguageService, private dialog: MatDialog, private equipmentService: EquipmentService, private gaugingService: GaugingService, private gaugingFormService: GaugingFormService, private router: Router) { this.materialList$ = this.equipmentService.materialList$; this.gaugingList$ = this.gaugingService.gaugingList$; } ngOnInit(): void { // Charger les données de session depuis le localStorage + // Détecter et appliquer la langue par défaut via le LanguageService + const browserLang = this.languageService.detectBrowserLanguage(); + this.languageService.setDefaultLanguage(browserLang); this.session.loadFromLocalStorage(); - // localStorage.removeItem("currentSessionMoulinet") if (typeof window !== 'undefined' && typeof sessionStorage !== 'undefined') { const wasPageRefreshed = sessionStorage.getItem('pageRefreshed'); if (wasPageRefreshed) { diff --git a/src/app/app.config.ts b/src/app/app.config.ts index cfa828c37815e0008a63025780c2357dfa35a17d..2115cd0e33f8794d39496f5c7cc88195149970eb 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -1,11 +1,26 @@ -import { ApplicationConfig } from '@angular/core'; +import { ApplicationConfig, importProvidersFrom, provideZoneChangeDetection } from '@angular/core'; import { provideRouter, PreloadAllModules, withPreloading } from '@angular/router'; import { routes } from './app.routes'; import { provideClientHydration } from '@angular/platform-browser'; import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; import { MAT_DATE_LOCALE } from '@angular/material/core'; +import { HttpClient, provideHttpClient } from '@angular/common/http'; +import { TranslateHttpLoader } from '@ngx-translate/http-loader'; +import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; + +const httpLoaderFactory: (http: HttpClient) => TranslateHttpLoader = (http: HttpClient) => + new TranslateHttpLoader(http, './assets/i18n/', '.json'); export const appConfig: ApplicationConfig = { providers: [ - provideRouter(routes, withPreloading(PreloadAllModules)), provideClientHydration(), provideAnimationsAsync(), {provide: MAT_DATE_LOCALE, useValue: 'en-GB'}] + provideRouter(routes, withPreloading(PreloadAllModules)), provideClientHydration(), provideAnimationsAsync(), {provide: MAT_DATE_LOCALE, useValue: 'en-GB'}, + provideHttpClient(), + importProvidersFrom([TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useFactory: httpLoaderFactory, + deps: [HttpClient], + }, + })]) + ] }; diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 7b886d510429d77f9f1fa5593b23e0bad9bee508..6362f7ea9270eed3550f41aaf745dc5e24318d2f 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -6,4 +6,5 @@ export const routes: Routes = [ { path: 'list', loadComponent: () => import('../app/views/gauging-list/gauging-list.component').then(m => m.GaugingListComponent)}, { path: 'gauging/:id', loadComponent: () => import('../app/views/gauging/gauging.component').then(m => m.GaugingComponent)}, { path: 'gauging/edit/:id', loadComponent: () => import('../app/views/gauging/gauging.component').then(m => m.GaugingComponent)}, + { path: 'settings', loadComponent: () => import('../app/views/settings/settings.component').then(m => m.SettingsComponent) }, ]; diff --git a/src/app/components/dialog/dialog-confirm-delete-equipment/dialog-confirm-delete-equipment.component.html b/src/app/components/dialog/dialog-confirm-delete-equipment/dialog-confirm-delete-equipment.component.html index f8724050dd4ced75d1920d8cba6ec30452833dfb..5c29c4f8a4680c5de81fa3e11bc70d3264c68af7 100644 --- a/src/app/components/dialog/dialog-confirm-delete-equipment/dialog-confirm-delete-equipment.component.html +++ b/src/app/components/dialog/dialog-confirm-delete-equipment/dialog-confirm-delete-equipment.component.html @@ -1,10 +1,10 @@ -<h2 mat-dialog-title><mat-icon color="warn">warning</mat-icon> Suppression du matériel</h2> +<h2 mat-dialog-title><mat-icon color="warn">warning</mat-icon> {{ 'DIALOG.DELETE_MATERIAL_TITLE' | translate }}</h2> <mat-dialog-content> - Attention! Le <strong>{{ data.materialType }} {{ data.materialName }}</strong> sera supprimé de la liste. + <span [innerHTML]="'DIALOG.WARNING_DELETE_MATERIAL_MESSAGE' | translate: { materialType: translate.instant(data.materialType), materialName: data.materialName }"></span> </mat-dialog-content> <mat-dialog-actions> - <button mat-raised-button mat-dialog-close color="primary">Annuler</button> - <button mat-raised-button [mat-dialog-close]="'confirm'" color="warn" cdkFocusInitial>Supprimer</button> + <button mat-raised-button mat-dialog-close color="primary">{{ 'DIALOG.CANCEL_BUTTON' | translate }}</button> + <button mat-raised-button [mat-dialog-close]="'confirm'" color="warn" cdkFocusInitial>{{ 'DIALOG.CONFIRM_BUTTON' | translate }}</button> </mat-dialog-actions> diff --git a/src/app/components/dialog/dialog-confirm-delete-equipment/dialog-confirm-delete-equipment.component.ts b/src/app/components/dialog/dialog-confirm-delete-equipment/dialog-confirm-delete-equipment.component.ts index 01c4c0b1c71a68663c814a1f6620de7bb995038f..0a450eb3335aaa3532a2c7312985d62fa4320455 100644 --- a/src/app/components/dialog/dialog-confirm-delete-equipment/dialog-confirm-delete-equipment.component.ts +++ b/src/app/components/dialog/dialog-confirm-delete-equipment/dialog-confirm-delete-equipment.component.ts @@ -8,16 +8,18 @@ import { import { MatIconModule } from '@angular/material/icon'; import {MatButtonModule} from '@angular/material/button' import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; @Component({ selector: 'app-dialog-confirm-delete-equipment', standalone: true, - imports: [MatButtonModule, MatDialogActions, MatDialogClose, MatDialogTitle, MatDialogContent, MatIconModule], + imports: [MatButtonModule, MatDialogActions, MatDialogClose, MatDialogTitle, MatDialogContent, MatIconModule, TranslateModule], templateUrl: './dialog-confirm-delete-equipment.component.html', styleUrl: './dialog-confirm-delete-equipment.component.scss' }) export class DialogConfirmDeleteEquipmentComponent { - constructor(@Inject(MAT_DIALOG_DATA) public data: any) {} - + constructor(@Inject(MAT_DIALOG_DATA) public data: any, protected translate: TranslateService) { + } + } diff --git a/src/app/components/dialog/dialog-confirm-delete-gauging/dialog-confirm-delete-gauging.component.html b/src/app/components/dialog/dialog-confirm-delete-gauging/dialog-confirm-delete-gauging.component.html index 36ff4515e603de68cbbb1c6ba7597516633048c8..c2eb0501d8ffbbf4f5f5c4d7f28bbcac3bd240b7 100644 --- a/src/app/components/dialog/dialog-confirm-delete-gauging/dialog-confirm-delete-gauging.component.html +++ b/src/app/components/dialog/dialog-confirm-delete-gauging/dialog-confirm-delete-gauging.component.html @@ -1,11 +1,8 @@ -<h2 mat-dialog-title><mat-icon color="warn">warning</mat-icon> Suppression du matériel</h2> - <mat-dialog-content> - Attention! Le jaugeage réalisé sur le <strong>{{ data.watercourse }}</strong> à la date du <strong>{{ data.date | date: 'dd/MM/yyyy' }}</strong> sera supprimé de la liste. - </mat-dialog-content> - <mat-dialog-actions> - <button mat-raised-button mat-dialog-close color="primary">Annuler</button> - <button mat-raised-button [mat-dialog-close]="'confirm'" color="warn" cdkFocusInitial>Supprimer</button> - </mat-dialog-actions> - - - +<h2 mat-dialog-title><mat-icon color="warn">warning</mat-icon> {{ 'DIALOG.DELETE_GAUGING_TITLE' | translate }}</h2> +<mat-dialog-content> + <span [innerHTML]="'DIALOG.DELETE_GAUGING_MESSAGE' | translate: { watercourse: data.watercourse, date: (data.date | date: 'dd/MM/yyyy') }"></span> +</mat-dialog-content> +<mat-dialog-actions> + <button mat-raised-button mat-dialog-close color="primary">{{ 'DIALOG.CANCEL_BUTTON' | translate }}</button> + <button mat-raised-button [mat-dialog-close]="'confirm'" color="warn" cdkFocusInitial>{{ 'DIALOG.CONFIRM_BUTTON' | translate }}</button> +</mat-dialog-actions> diff --git a/src/app/components/dialog/dialog-confirm-delete-gauging/dialog-confirm-delete-gauging.component.scss b/src/app/components/dialog/dialog-confirm-delete-gauging/dialog-confirm-delete-gauging.component.scss index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..72fee1cf023b14e4193f0ed5bbbba0189c36be8b 100644 --- a/src/app/components/dialog/dialog-confirm-delete-gauging/dialog-confirm-delete-gauging.component.scss +++ b/src/app/components/dialog/dialog-confirm-delete-gauging/dialog-confirm-delete-gauging.component.scss @@ -0,0 +1,5 @@ +mat-icon { + margin-right: 1%; + font-size: 25px; + vertical-align: middle; +} \ No newline at end of file diff --git a/src/app/components/dialog/dialog-confirm-delete-gauging/dialog-confirm-delete-gauging.component.ts b/src/app/components/dialog/dialog-confirm-delete-gauging/dialog-confirm-delete-gauging.component.ts index 8ecb6415e0f0429ab2f9266f0ff8dfb1386b05fc..7c38ccb0200674dc5a2197ce0f1a927c9ecbf77e 100644 --- a/src/app/components/dialog/dialog-confirm-delete-gauging/dialog-confirm-delete-gauging.component.ts +++ b/src/app/components/dialog/dialog-confirm-delete-gauging/dialog-confirm-delete-gauging.component.ts @@ -9,11 +9,12 @@ import { MatIconModule } from '@angular/material/icon'; import {MatButtonModule} from '@angular/material/button' import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { DatePipe } from '@angular/common'; +import { TranslateModule } from '@ngx-translate/core'; @Component({ selector: 'app-dialog-confirm-delete-gauging', standalone: true, - imports: [DatePipe,MatButtonModule, MatDialogActions, MatDialogClose, MatDialogTitle, MatDialogContent, MatIconModule], + imports: [DatePipe,MatButtonModule, MatDialogActions, MatDialogClose, MatDialogTitle, MatDialogContent, MatIconModule, TranslateModule], templateUrl: './dialog-confirm-delete-gauging.component.html', styleUrl: './dialog-confirm-delete-gauging.component.scss', }) diff --git a/src/app/components/dialog/dialog-confirm-delete-vertical/dialog-confirm-delete-vertical.component.html b/src/app/components/dialog/dialog-confirm-delete-vertical/dialog-confirm-delete-vertical.component.html index c6b072cc9ebb95ca57c292532879844dbb3ef370..c1b8a6a51433abf48e12634f3c9749232d9a3a81 100644 --- a/src/app/components/dialog/dialog-confirm-delete-vertical/dialog-confirm-delete-vertical.component.html +++ b/src/app/components/dialog/dialog-confirm-delete-vertical/dialog-confirm-delete-vertical.component.html @@ -1,11 +1,8 @@ -<h2 mat-dialog-title><mat-icon color="warn">warning</mat-icon> Suppression du matériel</h2> - <mat-dialog-content> - Attention! La verticale <strong> n°{{ data.selectedVerticalIndex}}</strong> sera supprimée de la liste. - </mat-dialog-content> - <mat-dialog-actions> - <button mat-raised-button mat-dialog-close color="primary">Annuler</button> - <button mat-raised-button [mat-dialog-close]="'confirm'" color="warn" cdkFocusInitial>Supprimer</button> - </mat-dialog-actions> - - - +<h2 mat-dialog-title><mat-icon color="warn">warning</mat-icon> {{ 'DIALOG.DELETE_VERTICAL_TITLE' | translate }}</h2> +<mat-dialog-content> + <span [innerHTML]="'DIALOG.DELETE_VERTICAL_MESSAGE' | translate: { verticalIndex: data.selectedVerticalIndex }"></span> +</mat-dialog-content> +<mat-dialog-actions> + <button mat-raised-button mat-dialog-close color="primary">{{ 'DIALOG.CANCEL_BUTTON' | translate }}</button> + <button mat-raised-button [mat-dialog-close]="'confirm'" color="warn" cdkFocusInitial>{{ 'DIALOG.CONFIRM_BUTTON' | translate }}</button> +</mat-dialog-actions> diff --git a/src/app/components/dialog/dialog-confirm-delete-vertical/dialog-confirm-delete-vertical.component.ts b/src/app/components/dialog/dialog-confirm-delete-vertical/dialog-confirm-delete-vertical.component.ts index 884280db09c653b1e1ebe1d7a1930fceeab1b844..bb3ea094c8a0c8a7f183834385d1c809713dbe59 100644 --- a/src/app/components/dialog/dialog-confirm-delete-vertical/dialog-confirm-delete-vertical.component.ts +++ b/src/app/components/dialog/dialog-confirm-delete-vertical/dialog-confirm-delete-vertical.component.ts @@ -8,11 +8,12 @@ import { import { MatIconModule } from '@angular/material/icon'; import {MatButtonModule} from '@angular/material/button' import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { TranslateModule } from '@ngx-translate/core'; @Component({ selector: 'app-dialog-confirm-delete-vertical', standalone: true, - imports: [MatButtonModule, MatDialogActions, MatDialogClose, MatDialogTitle, MatDialogContent, MatIconModule], + imports: [MatButtonModule, MatDialogActions, MatDialogClose, MatDialogTitle, MatDialogContent, MatIconModule, TranslateModule], templateUrl: './dialog-confirm-delete-vertical.component.html', styleUrl: './dialog-confirm-delete-vertical.component.scss' }) diff --git a/src/app/components/dialog/dialog-load-session/dialog-load-session/dialog-load-session.component.html b/src/app/components/dialog/dialog-load-session/dialog-load-session/dialog-load-session.component.html index 6c3ff1ff1f0139e8d032f9b39758bac0fd45bfdd..c18017660189564baca38e566cceece6594fd3a7 100644 --- a/src/app/components/dialog/dialog-load-session/dialog-load-session/dialog-load-session.component.html +++ b/src/app/components/dialog/dialog-load-session/dialog-load-session/dialog-load-session.component.html @@ -1,7 +1,7 @@ -<h2 mat-dialog-title class="dialog-title">Charger une session</h2> +<h2 mat-dialog-title class="dialog-title">{{ 'DIALOG.LOAD_SESSION_TITLE' | translate }}</h2> <mat-dialog-content> <mat-form-field class="file-input-field" [formGroup]="loadingForm" appearance="outline"> - <mat-label>Choisir un fichier</mat-label> + <mat-label>{{ 'DIALOG.CHOOSE_FILE_LABEL' | translate }}</mat-label> <ngx-mat-file-input id="session-file-input" formControlName="file" (change)="onFileSelected($event)" #sessionFile> </ngx-mat-file-input> @if(!sessionFile.empty) { @@ -12,31 +12,31 @@ </mat-form-field> @if(!sessionFile.empty) { @if(materialList.length > 0 && !notJSONfile && !invalidJSONFile) { - <app-mat-expansion-panel [panelTitle]="panelEquipmentTitle" [panelItems]="materialList" [panelItems]="materialList"></app-mat-expansion-panel> + <app-mat-expansion-panel [panelTitle]="'DIALOG.PANEL_EQUIPMENT_TITLE' | translate" [panelItems]="materialList" [panelItems]="materialList"></app-mat-expansion-panel> } @if(gaugingsList.length > 0 && !notJSONfile && !invalidJSONFile) { - <app-mat-expansion-panel [panelTitle]="panelGaugingTitle" [panelItems]="gaugingsList"></app-mat-expansion-panel> + <app-mat-expansion-panel [panelTitle]="'DIALOG.PANEL_GAUGING_TITLE' | translate" [panelItems]="gaugingsList"></app-mat-expansion-panel> } @if(notJSONfile) { - <p><mat-icon color="warn">error_outline</mat-icon>Le fichier téléchargé est invalide</p> + <p><mat-icon color="warn">error_outline</mat-icon>{{ 'DIALOG.INVALID_FILE' | translate }}</p> } @if(invalidJSONFile) { - <p><mat-icon color="warn">error_outline</mat-icon>Le fichier JSON contient des données incorrectes</p> + <p><mat-icon color="warn">error_outline</mat-icon>{{ 'DIALOG.INVALID_JSON_FILE' | translate }}</p> } <!-- @if(duplicatedEquipment) { <p><mat-icon color="warn">error_outline</mat-icon>Impossible d'ajouter un équipement déjà existant</p> } --> } @else { - <p style="color: red">Veuillez télécharger un fichier</p> + <p style="color: red">{{ 'DIALOG.UPLOAD_PROMPT' | translate }}</p> } @if(equipmentService.getAllEquipment().length || gaugingService.getAllGauging().length) { <mat-checkbox color="primary" [(ngModel)]="clearCurrentSession" (ngModelChange)="onClearCurrentSessionChange($event)"> - <span>Vider la session en cours</span> + <span>{{ 'DIALOG.CLEAR_SESSION_CHECKBOX' | translate }}</span> </mat-checkbox> } <mat-dialog-actions> <button mat-raised-button mat-dialog-close color="primary" (click)="loadSession()" - [disabled]="sessionFile.empty||notJSONfile || invalidJSONFile" [mat-dialog-close]="'confirm'">Confirmer</button> - <button mat-raised-button color="warn" cdkFocusInitial mat-dialog-close>Annuler</button> + [disabled]="sessionFile.empty||notJSONfile || invalidJSONFile" [mat-dialog-close]="'confirm'">{{ 'DIALOG.CONFIRM_BUTTON' | translate }}</button> + <button mat-raised-button color="warn" cdkFocusInitial mat-dialog-close>{{ 'DIALOG.CANCEL_BUTTON' | translate }}</button> </mat-dialog-actions> \ No newline at end of file diff --git a/src/app/components/dialog/dialog-load-session/dialog-load-session/dialog-load-session.component.ts b/src/app/components/dialog/dialog-load-session/dialog-load-session/dialog-load-session.component.ts index 57cb7d0122db74afe8a119dc7b5a753d67c07f58..e6827feacac9af2d19f44ba8c093f8cc7d52d20b 100644 --- a/src/app/components/dialog/dialog-load-session/dialog-load-session/dialog-load-session.component.ts +++ b/src/app/components/dialog/dialog-load-session/dialog-load-session/dialog-load-session.component.ts @@ -18,12 +18,13 @@ import { Session } from '../../../../models/session'; import { Gauging } from '../../../../models/gauging'; import { GaugingService } from '../../../../services/gauging.service'; import { MaterialPropeller } from '../../../../models/material_propeller'; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; @Component({ selector: 'app-dialog-load-session', standalone: true, templateUrl: './dialog-load-session.component.html', styleUrl: './dialog-load-session.component.scss', - imports: [MatExpansionPanelComponent, MatListModule, MatCheckboxModule, MatExpansionModule, MaterialFileInputModule, MatDialogClose, MatDialogActions, MatDialogContent, ReactiveFormsModule, FormsModule, MatFormFieldModule, MatIconModule, MatButtonModule, ErrorMessageComponent] + imports: [MatExpansionPanelComponent, MatListModule, MatCheckboxModule, MatExpansionModule, MaterialFileInputModule, MatDialogClose, MatDialogActions, MatDialogContent, ReactiveFormsModule, FormsModule, MatFormFieldModule, MatIconModule, MatButtonModule, ErrorMessageComponent, TranslateModule] }) export class DialogLoadSessionComponent { private fileReader = new FileReader(); diff --git a/src/app/components/dialog/dialog-new-session/dialog-new-session/dialog-new-session.component.html b/src/app/components/dialog/dialog-new-session/dialog-new-session/dialog-new-session.component.html index b2fdb6237d55a1d891ce31fceab981edc06c28c2..5b6aa9192a8340b0df240de823ffc37382a55f0d 100644 --- a/src/app/components/dialog/dialog-new-session/dialog-new-session/dialog-new-session.component.html +++ b/src/app/components/dialog/dialog-new-session/dialog-new-session/dialog-new-session.component.html @@ -1,11 +1,14 @@ -<h2 mat-dialog-title class="dialog-title"><mat-icon color="warn">warning</mat-icon> Réinitialiser la session</h2> - <mat-dialog-content> - Attention! La session actuelle sera perdue. - <mat-dialog-actions> - <button mat-raised-button [mat-dialog-close]="'confirm'" color="primary" cdkFocusInitial>Confirmer</button> - <button mat-raised-button mat-dialog-close color="warn">Annuler</button> - </mat-dialog-actions> - - - - +<h2 mat-dialog-title class="dialog-title"> + <mat-icon color="warn">warning</mat-icon> {{ 'DIALOG.RESET_SESSION_TITLE' | translate }} +</h2> +<mat-dialog-content> + {{ 'DIALOG.RESET_SESSION_WARNING' | translate }} +</mat-dialog-content> +<mat-dialog-actions> + <button mat-raised-button [mat-dialog-close]="'confirm'" color="primary" cdkFocusInitial> + {{ 'DIALOG.CONFIRM_BUTTON' | translate }} + </button> + <button mat-raised-button mat-dialog-close color="warn"> + {{ 'DIALOG.CANCEL_BUTTON' | translate }} + </button> +</mat-dialog-actions> diff --git a/src/app/components/dialog/dialog-new-session/dialog-new-session/dialog-new-session.component.spec.ts b/src/app/components/dialog/dialog-new-session/dialog-new-session/dialog-new-session.component.spec.ts deleted file mode 100644 index 0dc2581cb9492adade9dee1db5bbcfb4b5d794cc..0000000000000000000000000000000000000000 --- a/src/app/components/dialog/dialog-new-session/dialog-new-session/dialog-new-session.component.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { DialogNewSessionComponent } from './dialog-new-session.component'; - -describe('DialogNewSessionComponent', () => { - let component: DialogNewSessionComponent; - let fixture: ComponentFixture<DialogNewSessionComponent>; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [DialogNewSessionComponent] - }) - .compileComponents(); - - fixture = TestBed.createComponent(DialogNewSessionComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/components/dialog/dialog-new-session/dialog-new-session/dialog-new-session.component.ts b/src/app/components/dialog/dialog-new-session/dialog-new-session/dialog-new-session.component.ts index dd42cd68cdf70d82bc632b904838b6eeb3574020..c09f03b79ce88d06e9bb8229dcd242ba820a468d 100644 --- a/src/app/components/dialog/dialog-new-session/dialog-new-session/dialog-new-session.component.ts +++ b/src/app/components/dialog/dialog-new-session/dialog-new-session/dialog-new-session.component.ts @@ -2,11 +2,12 @@ import { Component } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatDialogActions, MatDialogClose, MatDialogContent } from '@angular/material/dialog'; import { MatIcon } from '@angular/material/icon'; +import { TranslateModule } from '@ngx-translate/core'; @Component({ selector: 'app-dialog-new-session', standalone: true, - imports: [MatIcon, MatDialogActions, MatDialogClose, MatDialogContent, MatButtonModule], + imports: [MatIcon, MatDialogActions, MatDialogClose, MatDialogContent, MatButtonModule, TranslateModule], templateUrl: './dialog-new-session.component.html', styleUrl: './dialog-new-session.component.scss' }) diff --git a/src/app/components/dialog/dialog-save-session/dialog-save-session/dialog-save-session.component.html b/src/app/components/dialog/dialog-save-session/dialog-save-session/dialog-save-session.component.html index e4569cb1ebe8d8ad81974a2c9fd3d28fd81c8508..e3c24fd422c090e79748c7d3240d3ffa5a65a154 100644 --- a/src/app/components/dialog/dialog-save-session/dialog-save-session/dialog-save-session.component.html +++ b/src/app/components/dialog/dialog-save-session/dialog-save-session/dialog-save-session.component.html @@ -1,13 +1,13 @@ -<h2 mat-dialog-title class="dialog-title">Sauvegarder la session</h2> +<h2 mat-dialog-title class="dialog-title">{{ 'DIALOG.SAVE_TITLE' | translate }}</h2> <mat-dialog-content> @if(equipment.length > 0){ - <app-mat-expansion-panel [panelTitle]="panelEquipmentTitle" [panelItems]="equipment"></app-mat-expansion-panel> + <app-mat-expansion-panel [panelTitle]="'DIALOG.PANEL_EQUIPMENT_TITLE' | translate" [panelItems]="equipment"></app-mat-expansion-panel> } @if(gaugings.length > 0 ){ - <app-mat-expansion-panel [panelTitle]="panelGaugingTitle" [panelItems]="gaugings"></app-mat-expansion-panel> + <app-mat-expansion-panel [panelTitle]="'DIALOG.PANEL_GAUGING_TITLE' | translate" [panelItems]="gaugings"></app-mat-expansion-panel> } <mat-form-field class="file-input-field" appearance="outline"> - <mat-label>Nom du fichier</mat-label> + <mat-label>{{ 'DIALOG.FILE_NAME_LABEL' | translate }}</mat-label> <input matInput [(ngModel)]="fileName" #sessionFile> @if(fileName !== "") { <button mat-icon-button matSuffix (click)="fileName = ''" class="clear-button"> @@ -16,10 +16,10 @@ } </mat-form-field> @if(fileName === "") { - <p style="color: red">Veuillez nommer le fichier</p> + <p style="color: red">{{ 'DIALOG.NO_FILE_NAME_ERROR' | translate }}</p> } <mat-dialog-actions> - <button mat-raised-button mat-dialog-close color="primary" [disabled]="fileName === ''" (click)="saveSession()">Confirmer</button> - <button mat-raised-button color="warn" cdkFocusInitial [mat-dialog-close]="'confirm'">Annuler</button> + <button mat-raised-button mat-dialog-close color="primary" [disabled]="fileName === ''" (click)="saveSession()">{{ 'DIALOG.CONFIRM_BUTTON' | translate }}</button> + <button mat-raised-button color="warn" cdkFocusInitial [mat-dialog-close]="'confirm'">{{ 'DIALOG.CANCEL_BUTTON' | translate }}</button> </mat-dialog-actions> diff --git a/src/app/components/dialog/dialog-save-session/dialog-save-session/dialog-save-session.component.ts b/src/app/components/dialog/dialog-save-session/dialog-save-session/dialog-save-session.component.ts index 8cb7f61e21dbd47805f381513c7e72f5adf1dc5a..16b5363932ed1bbcc97a2ca3eda42d1c0f1753e8 100644 --- a/src/app/components/dialog/dialog-save-session/dialog-save-session/dialog-save-session.component.ts +++ b/src/app/components/dialog/dialog-save-session/dialog-save-session/dialog-save-session.component.ts @@ -14,6 +14,7 @@ import { Material } from '../../../../models/material'; import { GaugingService } from '../../../../services/gauging.service'; import { Gauging } from '../../../../models/gauging'; import { MaterialType } from '../../../../models/equipment.model'; +import { TranslateModule } from '@ngx-translate/core'; interface SelectableMaterial extends Material { selected?: boolean; @@ -26,7 +27,7 @@ interface SelectableGauging extends Gauging { @Component({ selector: 'app-dialog-save-session', standalone: true, - imports: [MatExpansionPanelComponent, MatIconModule, MatCheckboxModule, MatExpansionModule, MatFormField, MatInputModule, MatDialogClose, FormsModule, MatButtonModule, MatDialogContent, MatDialogActions], + imports: [MatExpansionPanelComponent, MatIconModule, MatCheckboxModule, MatExpansionModule, MatFormField, MatInputModule, MatDialogClose, FormsModule, MatButtonModule, MatDialogContent, MatDialogActions, TranslateModule], templateUrl: './dialog-save-session.component.html', styleUrl: './dialog-save-session.component.scss' }) diff --git a/src/app/components/dialog/dialog-warning-material-associated-to-gauging/dialog-warning-material-associated-to-gauging.component.html b/src/app/components/dialog/dialog-warning-material-associated-to-gauging/dialog-warning-material-associated-to-gauging.component.html index a9487a977c6d89854db434f685a44a61bfa2ab81..f894a63818c9cb1f228fc03128a01632e78bfcf5 100644 --- a/src/app/components/dialog/dialog-warning-material-associated-to-gauging/dialog-warning-material-associated-to-gauging.component.html +++ b/src/app/components/dialog/dialog-warning-material-associated-to-gauging/dialog-warning-material-associated-to-gauging.component.html @@ -1,12 +1,9 @@ -<h2 mat-dialog-title><mat-icon color="warn">warning</mat-icon> Suppression du matériel impossible</h2> - <mat-dialog-content> - Attention! Le <strong>{{ data.materialType }} {{ data.materialName }}</strong> est associé à un ou plusieurs jaugeages existants.<br> - Supprimer d'abord le ou les jaugeages associés pour supprimer ce matériel. - </mat-dialog-content> - <mat-dialog-actions> - <button mat-raised-button [mat-dialog-close]="'confirm'" color="primary" cdkFocusInitial>Liste des jaugeages </button> - <button mat-raised-button mat-dialog-close color="warn">Annuler</button> - </mat-dialog-actions> - - - +<h2 mat-dialog-title><mat-icon color="warn">warning</mat-icon> {{ 'DIALOG.DELETE_IMPOSSIBLE_TITLE' | translate }}</h2> +<mat-dialog-content> + <span [innerHTML]="'DIALOG.DELETE_IMPOSSIBLE_MESSAGE' | translate: { materialType: translate.instant(data.materialType), materialName: data.materialName }"></span><br> + {{ 'DIALOG.DELETE_IMPOSSIBLE_INSTRUCTION' | translate }} +</mat-dialog-content> +<mat-dialog-actions> + <button mat-raised-button [mat-dialog-close]="'confirm'" color="primary" cdkFocusInitial>{{ 'DIALOG.GAUGING_LIST_BUTTON' | translate }}</button> + <button mat-raised-button mat-dialog-close color="warn">{{ 'DIALOG.CANCEL_BUTTON' | translate }}</button> +</mat-dialog-actions> diff --git a/src/app/components/dialog/dialog-warning-material-associated-to-gauging/dialog-warning-material-associated-to-gauging.component.ts b/src/app/components/dialog/dialog-warning-material-associated-to-gauging/dialog-warning-material-associated-to-gauging.component.ts index 9ec31f921cf7d96b53115e583c22d2910f492480..f77c33b3284492da48059fe274e9b408574d3181 100644 --- a/src/app/components/dialog/dialog-warning-material-associated-to-gauging/dialog-warning-material-associated-to-gauging.component.ts +++ b/src/app/components/dialog/dialog-warning-material-associated-to-gauging/dialog-warning-material-associated-to-gauging.component.ts @@ -2,16 +2,17 @@ import { Component, Inject } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatDialogActions, MatDialogClose, MatDialogTitle, MatDialogContent, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MatIconModule } from '@angular/material/icon'; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; @Component({ selector: 'app-dialog-warning-material-associated-to-gauging', standalone: true, - imports: [MatButtonModule, MatDialogActions, MatDialogClose, MatDialogTitle, MatDialogContent, MatIconModule], + imports: [MatButtonModule, MatDialogActions, MatDialogClose, MatDialogTitle, MatDialogContent, MatIconModule, TranslateModule], templateUrl: './dialog-warning-material-associated-to-gauging.component.html', styleUrl: './dialog-warning-material-associated-to-gauging.component.scss' }) export class DialogWarningMaterialAssociatedToGaugingComponent { - constructor(@Inject(MAT_DIALOG_DATA) public data: any) { + constructor(@Inject(MAT_DIALOG_DATA) public data: any, protected translate: TranslateService) { } } diff --git a/src/app/components/mat-expansion-panel/mat-expansion-panel.component.ts b/src/app/components/mat-expansion-panel/mat-expansion-panel.component.ts index ce9cac39da057d548804238da66109990417bf99..18f75dad58b7c061e6c6fd0c88d8a95bea41b976 100644 --- a/src/app/components/mat-expansion-panel/mat-expansion-panel.component.ts +++ b/src/app/components/mat-expansion-panel/mat-expansion-panel.component.ts @@ -4,18 +4,22 @@ import { MatButtonModule } from '@angular/material/button'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatExpansionModule } from '@angular/material/expansion'; import { MatDividerModule } from '@angular/material/divider'; +import { TranslateModule } from '@ngx-translate/core'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'app-mat-expansion-panel', standalone: true, - imports: [MatCheckboxModule, MatExpansionModule, FormsModule, MatButtonModule, MatDividerModule], + imports: [MatCheckboxModule, MatExpansionModule, FormsModule, MatButtonModule, MatDividerModule, TranslateModule,], templateUrl: './mat-expansion-panel.component.html', styleUrl: './mat-expansion-panel.component.scss' }) export class MatExpansionPanelComponent { @Input() panelTitle: string = ""; @Input() panelItems: any[] = []; - selectButtonTitle: string = 'Aucun'; + selectButtonTitle: string = this.translate.instant('PANEL.SELECT_NONE'); + + constructor(private translate: TranslateService) { } ngOnInit(): void { this.selectAll(); @@ -45,9 +49,9 @@ export class MatExpansionPanelComponent { } selectNoneOrAll() { - this.selectButtonTitle = (this.selectButtonTitle === 'Aucun') ? 'Tous' : 'Aucun'; + this.selectButtonTitle = (this.selectButtonTitle === this.translate.instant('PANEL.SELECT_NONE')) ? this.translate.instant('PANEL.SELECT_ALL') : this.translate.instant('PANEL.SELECT_NONE'); - if (this.selectButtonTitle === 'Aucun') { + if (this.selectButtonTitle === this.translate.instant('PANEL.SELECT_NONE')) { this.panelItems.forEach(item => item.selected = true); } else { this.panelItems.forEach(item => item.selected = false); diff --git a/src/app/models/equipment.model.ts b/src/app/models/equipment.model.ts index 26d5b1651dac00f6b1048f221578cb947708f19f..f7770d680d0eb59dce15c9b08d78cbc9ad9bd31a 100644 --- a/src/app/models/equipment.model.ts +++ b/src/app/models/equipment.model.ts @@ -1,5 +1,6 @@ export enum MaterialType { - ElectromagneticFlowMeter = 'courantomètre électromagnétique', - PropellerFlowMeter = 'moulinet', - VelocityHeadRod = 'perche à charge dynamique transparente' + ElectromagneticFlowMeter = 'EQUIPMENT.MATERIAL_TYPES.ELECTROMAGNETIC_FLOW_METER', + PropellerFlowMeter = 'EQUIPMENT.MATERIAL_TYPES.PROPELLER_FLOW_METER', + VelocityHeadRod = 'EQUIPMENT.MATERIAL_TYPES.VELOCITY_HEAD_ROAD' } + diff --git a/src/app/models/grain_size.ts b/src/app/models/grain_size.ts index 0038b8b9264020a1e342c7900e69167a712bfe33..7952a9093818cec55a74619b0767d7c108527711 100644 --- a/src/app/models/grain_size.ts +++ b/src/app/models/grain_size.ts @@ -1,14 +1,14 @@ export enum GrainSize { - CONCRETE = 'Béton', - ROCKS = 'Rochers', - BLOCSS = 'Blocs', - LARGE_STONES = 'Pierres grossières', - LARGE_PEBBLES = 'Cailloux grossiers', - FINE_PEBBLES = 'Cailloux fins', - LARGE_GRAVELS= 'Graviers grossiers', - FINE_GRAVELS = 'Graviers fins', - LARGE_SAND = 'Sable grossier', - FINE_SAND = 'Sable fin', - SILT = 'Limon', - CLAY = 'Argile' -} \ No newline at end of file + CONCRETE = 'GAUGING.GRAIN_SIZE.CONCRETE', + ROCKS = 'GAUGING.GRAIN_SIZE.ROCKS', + BLOCSS = 'GAUGING.GRAIN_SIZE.BLOCSS', + LARGE_STONES = 'GAUGING.GRAIN_SIZE.LARGE_STONES', + LARGE_PEBBLES = 'GAUGING.GRAIN_SIZE.LARGE_PEBBLES', + FINE_PEBBLES = 'GAUGING.GRAIN_SIZE.FINE_PEBBLES', + LARGE_GRAVELS = 'GAUGING.GRAIN_SIZE.LARGE_GRAVELS', + FINE_GRAVELS = 'GAUGING.GRAIN_SIZE.FINE_GRAVELS', + LARGE_SAND = 'GAUGING.GRAIN_SIZE.LARGE_SAND', + FINE_SAND = 'GAUGING.GRAIN_SIZE.FINE_SAND', + SILT = 'GAUGING.GRAIN_SIZE.SILT', + CLAY = 'GAUGING.GRAIN_SIZE.CLAY' +} diff --git a/src/app/models/origin_measures.ts b/src/app/models/origin_measures.ts index d75899eff40bee4fd3fc2547de10069e2b6cee5a..5d51bafb1492af28e6e4095dd74002aab665421f 100644 --- a/src/app/models/origin_measures.ts +++ b/src/app/models/origin_measures.ts @@ -1,7 +1,8 @@ export enum OriginMeasures { - BANK_LEFT = 'Rive gauche', - BANK_RIGHT = 'Rive droite' - } + BANK_LEFT = 'GAUGING.BANK_LEFT', + BANK_RIGHT = 'GAUGING.BANK_RIGHT' +} + // BOTTOM = 'Fond', // SURFACE = 'Surface', \ No newline at end of file diff --git a/src/app/services/equipment.service.ts b/src/app/services/equipment.service.ts index 77843944486b51f538af4ece4a5c87f13caad8d1..f28e9f37a69bba58b66e6a6e7489d8a064e799ba 100644 --- a/src/app/services/equipment.service.ts +++ b/src/app/services/equipment.service.ts @@ -6,6 +6,7 @@ import { MaterialPropeller } from '../models/material_propeller'; import { MaterialHeadRod } from '../models/material_headRod'; import { Gauging } from '../models/gauging'; import { GaugingService } from './gauging.service'; +import { MaterialType } from '../models/equipment.model'; @Injectable({ providedIn: 'root' @@ -66,18 +67,18 @@ export class EquipmentService { public createInstanceofMaterial(materialType: string, ...args: any[]): any { const MaterialTypeDictionary: { [key: string]: any } = { - 'courantomètre électromagnétique': MaterialCouranto, - 'moulinet': MaterialPropeller, - 'perche à charge dynamique transparente': MaterialHeadRod + [MaterialType.ElectromagneticFlowMeter]: MaterialCouranto, + [MaterialType.PropellerFlowMeter]: MaterialPropeller, + [MaterialType.VelocityHeadRod]: MaterialHeadRod }; - const SelectedMaterialTypeClass = MaterialTypeDictionary[materialType]; - if (SelectedMaterialTypeClass) { - return new SelectedMaterialTypeClass(...args); - } else { - throw new Error(`La classe ${materialType} n'existe pas.`); + const MaterialClass = MaterialTypeDictionary[materialType]; + if (MaterialClass) { + return new MaterialClass(...args); } - } + throw new Error(`Unknown material type: ${materialType}`); +} + // Nouvelle méthode pour vérifier si un matériel est associé à un gauging isMaterialAssociatedWithGauging(materialId: string): boolean { diff --git a/src/app/services/language/language.service.ts b/src/app/services/language/language.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..a1802bb22b5e37e2a43dcaa4ee1f71cee800d4c0 --- /dev/null +++ b/src/app/services/language/language.service.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; + +@Injectable({ + providedIn: 'root' +}) +export class LanguageService { + supportedLanguages = ['fr', 'en']; + constructor(private translate: TranslateService) {} + + // Détecter la langue du navigateur et retourner l'anglais par défaut si elle n'est pas supportée + detectBrowserLanguage(): string { + const browserLang = navigator.language.split('-')[0]; + // Si la langue du navigateur est supportée, on l'utilise, sinon on met 'en' (anglais) par défaut + return this.supportedLanguages.includes(browserLang) ? browserLang : 'en'; + } + + // Changer la langue + changeLanguage(lang: string) { + this.translate.use(lang); + } + + // Définir la langue par défaut + setDefaultLanguage(lang: string) { + this.translate.setDefaultLang(lang); + this.translate.use(lang); + } +} diff --git a/src/app/views/equipment/equipment.component.html b/src/app/views/equipment/equipment.component.html index 80366f67ece7c1df6793ba97161330969e2cd7cc..355efbec7d29b1368b7c9956475f1e466c8f2e5b 100644 --- a/src/app/views/equipment/equipment.component.html +++ b/src/app/views/equipment/equipment.component.html @@ -4,17 +4,19 @@ <div class="equipment-list"> @if((materialList$ | async)?.length! > 0){ <div class="equipment-list-title"> - <h1>Liste du Matériel</h1> + <h1>{{ 'EQUIPMENT.MATERIAL_LIST_TITLE' | translate }}</h1> <button mat-raised-button color="primary" [disabled]="showEditMaterialForm" class="equipment-list-title-add" - [disabled]="needMaterial" (click)="addMaterial()"><span>Ajouter</span><mat-icon - class="icon-list-item">add</mat-icon></button> + [disabled]="needMaterial" (click)="addMaterial()"> + <span>{{ 'EQUIPMENT.ADD_MATERIAL_BUTTON' | translate }}</span> + <mat-icon class="icon-list-item">add</mat-icon> + </button> </div> <table mat-table [dataSource]="(materialList$ | async)!" cdkDropList [cdkDropListDisabled]="!enableDragFunctionality" (cdkDropListDropped)="dropTable($event)" [cdkDropListLockAxis]="'y'"> <!-- Reorder column --> <ng-container matColumnDef="reorder"> - <th mat-header-cell *matHeaderCellDef style="width:5%"></th>> <th> + <th mat-header-cell *matHeaderCellDef style="width:5%"></th> <td mat-cell *matCellDef="let material" style="text-overflow: unset !important;"> <!-- draggable button --> <mat-icon (click)="toggleDragFunctionality()" class="drag-icon">drag_indicator</mat-icon> @@ -23,7 +25,7 @@ <!-- Name Column --> <ng-container matColumnDef="name"> - <th mat-header-cell *matHeaderCellDef>Nom</th> + <th mat-header-cell *matHeaderCellDef>{{ 'EQUIPMENT.NAME' | translate }}</th> <td mat-cell *matCellDef="let materiel, let i=index" (click)="editMaterial(materiel, i)" id="name-row">{{ materiel.name }} </td> @@ -31,27 +33,27 @@ <!-- Type Column --> <ng-container matColumnDef="type"> - <th mat-header-cell *matHeaderCellDef>Type</th> - <td mat-cell *matCellDef="let materiel, let i=index" (click)="editMaterial(materiel, i)">{{ materiel.type }} + <th mat-header-cell *matHeaderCellDef>{{ 'EQUIPMENT.TYPE' | translate }}</th> + <td mat-cell *matCellDef="let materiel, let i=index" (click)="editMaterial(materiel, i)">{{ materiel.type | translate}} </td> </ng-container> <!-- Serial Number Column --> <ng-container matColumnDef="serialNumber"> - <th mat-header-cell *matHeaderCellDef>Numéro de série</th> + <th mat-header-cell *matHeaderCellDef>{{ 'EQUIPMENT.SERIALNUMBER' | translate }}</th> <td mat-cell *matCellDef="let materiel, let i=index" (click)="editMaterial(materiel, i)">{{ materiel.serialNumber || '-' }}</td> </ng-container> <!-- Description Column --> <ng-container matColumnDef="description"> - <th class="equipment-list-description" mat-header-cell *matHeaderCellDef>Description</th> + <th class="equipment-list-description" mat-header-cell *matHeaderCellDef>{{ 'EQUIPMENT.DESCRIPTION' | translate }}</th> <td mat-cell *matCellDef="let materiel, let i=index" (click)="editMaterial(materiel, i)">{{ materiel.description || '-' }}</td> </ng-container> <!-- Edit/Remove Column --> <ng-container matColumnDef="edit"> - <th class="equipment-list-action" mat-header-cell *matHeaderCellDef style=" text-align: center">Actions</th> + <th class="equipment-list-action" mat-header-cell *matHeaderCellDef style=" text-align: center">{{ 'EQUIPMENT.ACTIONS' | translate }}</th> <td mat-cell *matCellDef="let materiel, let i =index" style="text-overflow: unset; text-align: center;"> <button mat-icon-button color="primary" (click)="editMaterial(materiel, i)" [disabled]="needMaterial || showEditMaterialForm" id="edit"> @@ -75,10 +77,10 @@ @else{ <!-- if no created material --> <div class="equipment-form-title"> - <h1>Liste du Matériel</h1> - <p>Aucun matériel disponible.</p> + <h1>{{ 'EQUIPMENT.MATERIAL_LIST_TITLE' | translate }}</h1> + <p>{{ 'EQUIPMENT.NO_MATERIAL_AVAILABLE' | translate }}</p> @if(!needMaterial) { - <button mat-raised-button color="primary" (click)="addMaterial()" class="add-button">Ajouter</button> + <button mat-raised-button color="primary" (click)="addMaterial()" class="add-button">{{ 'EQUIPMENT.ADD_MATERIAL_BUTTON' | translate }}</button> } </div> } @@ -86,112 +88,108 @@ </div> <!-- Creation/Edition form --> @if(needMaterial || showEditMaterialForm ){ - <div class="equipment-form-container"> - <form class="equipment-form" [formGroup]="form" (submit)="onSubmit()"> - <h1 class="equipment-form-title">{{needMaterial ? 'Nouveau matériel' : 'Modifier le matériel'}}</h1> - <!-- Material Type Section --> - <section class="form-section"> - <h2>Type de matériel</h2> - <mat-form-field appearance="outline"> - <mat-label>Type de matériel</mat-label> - <mat-select formControlName="type" placeholder="Type" (selectionChange)="onMaterialTypeChange()"> - @for (type of materielTypes; track type) { - <mat-option [value]="type"> - {{type}} - </mat-option> - } - </mat-select> - </mat-form-field> - </section> - - <!-- Model Section --> - <section class="form-section"> - <h2>Modèle</h2> - <mat-form-field appearance="outline"> - <mat-label>Nom</mat-label> - <input matInput placeholder="Nom" formControlName="name" autocomplete="name"> - </mat-form-field> - @if(hasError('name', 'required')){ - <app-error-message [displayed]="hasError('name', 'required')">Le nom est - requis</app-error-message> - } - <mat-form-field appearance="outline"> - <mat-label>Numéro de série - </mat-label> - <input matInput placeholder="Numéro de série" formControlName="serialNumber" autocomplete="off"> - </mat-form-field> - @if(isPropeller) { - <!-- Numéro de l'hélice --> - <mat-form-field appearance="outline"> - <mat-label>Numéro de l'hélice</mat-label> - <input matInput placeholder="Numéro de l'hélice" formControlName="propellerNumber" autocomplete="off"> - </mat-form-field> - - <!-- Numéro de série de l'hélice --> - <mat-form-field appearance="outline"> - <mat-label>Numéro de série de l'hélice</mat-label> - <input matInput placeholder="Numéro de série de l'hélice" formControlName="propellerSerialNumber" - autocomplete="off"> - </mat-form-field> - - <!-- Nombre de formules de vitesse --> - <mat-form-field appearance="outline"> - <mat-label>Nombre de formules de vitesse</mat-label> - <mat-select formControlName="velocityFormulaCount" placeholder="Nombre de formules de vitesse" - (selectionChange)="onVelocityFormulaCountChange($event.value)"> - <mat-option *ngFor="let count of [1, 2, 3, 4, 5]" [value]="count"> - {{count}} - </mat-option> - </mat-select> - </mat-form-field> - <div formArrayName="velocityFormulas" [ngClass]="{'formulas-section': velocityFormulas.controls.length > 0}"> - @for(formula of velocityFormulas.controls; track formula; let i = $index ) { - <div [formGroupName]="i"> - <h3>Formule de vitesse n°{{ i + 1 }}</h3> - <div class="form-field-row"> - <mat-form-field appearance="outline" class="form-field-inline"> - <mat-label>Borne inférieure</mat-label> - <input matInput placeholder="n >" formControlName="lowerBound"> - </mat-form-field> - <!-- Message d'erreur --> - <mat-form-field appearance="outline" class="form-field-inline"> - <mat-label>Borne supérieure</mat-label> - <input matInput placeholder="n <" formControlName="upperBound"> - </mat-form-field> - </div> - @if(formula.errors?.invalidBounds) { - <mat-error>Les bornes spécifiées ne sont pas correctes. La borne inférieure doit être strictement inférieure - à la borne supérieure.</mat-error> - } - - <!-- Deuxième ligne : Coef a et Coef b --> - <div class="form-field-row"> - <mat-form-field appearance="outline" class="form-field-inline"> - <mat-label>Coefficient A</mat-label> - <input matInput placeholder="Coef a" formControlName="coefA"> - </mat-form-field> - <mat-form-field appearance="outline" class="form-field-inline"> - <mat-label>Constante B</mat-label> - <input matInput placeholder="Constante b" formControlName="constantB"> - </mat-form-field> + <div class="equipment-form-container"> + <form class="equipment-form" [formGroup]="form" (submit)="onSubmit()"> + <h1 class="equipment-form-title">{{needMaterial ? ('EQUIPMENT.NEW_MATERIAL_TITLE' | translate) : ('EQUIPMENT.EDIT_MATERIAL_TITLE' | translate)}}</h1> + + <!-- Material Type Section --> + <section class="form-section"> + <h2>{{ 'EQUIPMENT.MATERIAL_TYPE_LABEL' | translate }}</h2> + <mat-form-field appearance="outline"> + <mat-label>{{ 'EQUIPMENT.MATERIAL_TYPE_LABEL' | translate }}</mat-label> + <mat-select formControlName="type" placeholder="{{ 'EQUIPMENT.MATERIAL_TYPE_LABEL' | translate }}" (selectionChange)="onMaterialTypeChange()"> + @for (type of materielTypes; track type) { + <mat-option [value]="type"> + {{ type | translate }} + </mat-option> + } + </mat-select> + </mat-form-field> + </section> + + <!-- Model Section --> + <section class="form-section"> + <h2>{{ 'EQUIPMENT.MODEL_SECTION_TITLE' | translate }}</h2> + <mat-form-field appearance="outline"> + <mat-label>{{ 'EQUIPMENT.NAME_LABEL' | translate }}</mat-label> + <input matInput placeholder="{{ 'EQUIPMENT.NAME_LABEL' | translate }}" formControlName="name" autocomplete="name"> + </mat-form-field> + @if(hasError('name', 'required')){ + <app-error-message [displayed]="hasError('name', 'required')">{{ 'EQUIPMENT.REQUIRED_NAME_ERROR' | translate }}</app-error-message> + } + <mat-form-field appearance="outline"> + <mat-label>{{ 'EQUIPMENT.SERIALNUMBER_LABEL' | translate }}</mat-label> + <input matInput placeholder="{{ 'EQUIPMENT.SERIALNUMBER_LABEL' | translate }}" formControlName="serialNumber" autocomplete="off"> + </mat-form-field> + @if(isPropeller) { + <!-- Propeller Number --> + <mat-form-field appearance="outline"> + <mat-label>{{ 'EQUIPMENT.PROPELLERNUMBER_LABEL' | translate }}</mat-label> + <input matInput placeholder="{{ 'EQUIPMENT.PROPELLERNUMBER_LABEL' | translate }}" formControlName="propellerNumber" autocomplete="off"> + </mat-form-field> + + <!-- Propeller Serial Number --> + <mat-form-field appearance="outline"> + <mat-label>{{ 'EQUIPMENT.PROPELLERSERIALNUMBER_LABEL' | translate }}</mat-label> + <input matInput placeholder="{{ 'EQUIPMENT.PROPELLERSERIALNUMBER_LABEL' | translate }}" formControlName="propellerSerialNumber" autocomplete="off"> + </mat-form-field> + + <!-- Number of Velocity Formulas --> + <mat-form-field appearance="outline"> + <mat-label>{{ 'EQUIPMENT.VELOCITY_FORMULA_COUNT_LABEL' | translate }}</mat-label> + <mat-select formControlName="velocityFormulaCount" placeholder="{{ 'EQUIPMENT.VELOCITY_FORMULA_COUNT_LABEL' | translate }}" (selectionChange)="onVelocityFormulaCountChange($event.value)"> + <mat-option *ngFor="let count of [1, 2, 3, 4, 5]" [value]="count"> + {{count}} + </mat-option> + </mat-select> + </mat-form-field> + <div formArrayName="velocityFormulas" [ngClass]="{'formulas-section': velocityFormulas.controls.length > 0}"> + @for(formula of velocityFormulas.controls; track formula; let i = $index ) { + <div [formGroupName]="i"> + <h3>{{ 'EQUIPMENT.VELOCITY_FORMULA_TITLE_LABEL' | translate }} {{ i + 1 }}</h3> + <div class="form-field-row"> + <mat-form-field appearance="outline" class="form-field-inline"> + <mat-label>{{ 'EQUIPMENT.LOWER_BOUND_LABEL' | translate }}</mat-label> + <input matInput placeholder="{{ 'EQUIPMENT.LOWER_BOUND_LABEL' | translate }}" formControlName="lowerBound"> + </mat-form-field> + <!-- Error Message --> + <mat-form-field appearance="outline" class="form-field-inline"> + <mat-label>{{ 'EQUIPMENT.UPPER_BOUND_LABEL' | translate }}</mat-label> + <input matInput placeholder="{{ 'EQUIPMENT.UPPER_BOUND_LABEL' | translate }}" formControlName="upperBound"> + </mat-form-field> + </div> + @if(formula.errors?.invalidBounds) { + <mat-error>{{ 'EQUIPMENT.INVALID_BOUNDS_ERROR' | translate }}</mat-error> + } + + <!-- Second row: Coefficient A and B --> + <div class="form-field-row"> + <mat-form-field appearance="outline" class="form-field-inline"> + <mat-label>{{ 'EQUIPMENT.COEF_A_LABEL' | translate }}</mat-label> + <input matInput placeholder="{{ 'EQUIPMENT.COEF_A_LABEL' | translate }}" formControlName="coefA"> + </mat-form-field> + <mat-form-field appearance="outline" class="form-field-inline"> + <mat-label>{{ 'EQUIPMENT.CONSTANT_B_LABEL' | translate }}</mat-label> + <input matInput placeholder="{{ 'EQUIPMENT.CONSTANT_B_LABEL' | translate }}" formControlName="constantB"> + </mat-form-field> + </div> + </div> + } </div> - </div> } - </div> - } - </section> - <section class="form-section"> - <h2>Infomations complémentaires</h2> - <mat-form-field class="equipment-description" appearance="outline"> - <mat-label>Description</mat-label> - <textarea matInput placeholder="Description" formControlName="description" rows="4" - autocomplete="off"></textarea> - </mat-form-field> - </section> - - <button style="margin-right: 4%;" mat-flat-button color="primary" class="submit-button" - [disabled]="!form.valid">Valider</button> - <button mat-flat-button color="warn" (click)="closeMaterialForm()" class="submit-button">Annuler</button> - </form> - </div> - } \ No newline at end of file + </section> + + <section class="form-section"> + <h2>{{ 'EQUIPMENT.ADDITIONAL_INFO_TITLE' | translate }}</h2> + <mat-form-field class="equipment-description" appearance="outline"> + <mat-label>{{ 'EQUIPMENT.DESCRIPTION_LABEL' | translate }}</mat-label> + <textarea matInput placeholder="{{ 'EQUIPMENT.DESCRIPTION_LABEL' | translate }}" formControlName="description" rows="4" autocomplete="off"></textarea> + </mat-form-field> + </section> + + <button style="margin-right: 4%;" mat-flat-button color="primary" class="submit-button" [disabled]="!form.valid">{{ 'EQUIPMENT.SUBMIT_BUTTON' | translate }}</button> + <button mat-flat-button color="warn" (click)="closeMaterialForm()" class="submit-button">{{ 'EQUIPMENT.CANCEL_BUTTON' | translate }}</button> + </form> + </div> + } + \ No newline at end of file diff --git a/src/app/views/equipment/equipment.component.ts b/src/app/views/equipment/equipment.component.ts index c944ab1c2c9ea57eb3848cbda1fb78a235a89002..ee043ab636d2dcfafd163477c13118cbedc4e5f5 100644 --- a/src/app/views/equipment/equipment.component.ts +++ b/src/app/views/equipment/equipment.component.ts @@ -25,13 +25,14 @@ import { boundsValidator } from '../../tools/Vallidators/propellerFormulaBoundsV import { ActivatedRoute, Router } from '@angular/router'; import { Session } from '../../models/session'; import { DialogWarningMaterialAssociatedToGaugingComponent } from '../../components/dialog/dialog-warning-material-associated-to-gauging/dialog-warning-material-associated-to-gauging.component'; +import { TranslateModule } from '@ngx-translate/core'; @Component({ selector: 'app-equipment', standalone: true, templateUrl: './equipment.component.html', styleUrl: './equipment.component.scss', - imports: [ReactiveFormsModule, FormsModule, CommonModule, MatFormFieldModule, MatSelectModule, MatRadioModule, MatInputModule, MatButtonModule, MatTableModule, DragDropModule, MatIconModule, ErrorMessageComponent] + imports: [TranslateModule, ReactiveFormsModule, FormsModule, CommonModule, MatFormFieldModule, MatSelectModule, MatRadioModule, MatInputModule, MatButtonModule, MatTableModule, DragDropModule, MatIconModule, ErrorMessageComponent] }) export class EquipmentComponent extends AbstractFormComponent { diff --git a/src/app/views/gauging-list/gauging-list.component.html b/src/app/views/gauging-list/gauging-list.component.html index baa0574091c6c8885bb4890f48bf99029df60ad2..1b55a0bbba7ca0c8c100a0da55afeb3ea0b42724 100644 --- a/src/app/views/gauging-list/gauging-list.component.html +++ b/src/app/views/gauging-list/gauging-list.component.html @@ -1,53 +1,54 @@ -<!-- template.html --> <div class="container"> <div class="gauging-list-container"> <div class="gauging-list"> @if((gaugingsList$ | async)?.length! > 0){ <div class="gauging-list-title"> - <h1>Liste des Jaugeages</h1> - <button mat-raised-button color="primary" (click)="addGauging()" class="gauging-list-title-add">Ajouter un jaugeage</button> + <h1>{{ 'GAUGING.LIST_TITLE' | translate }}</h1> + <button mat-raised-button color="primary" (click)="addGauging()" class="gauging-list-title-add"> + {{ 'GAUGING.ADD_GAUGING' | translate }} + </button> </div> <table mat-table [dataSource]="(gaugingsList$ | async)!" cdkDropList> <!-- Watercourse Column --> <ng-container matColumnDef="watercourse"> - <th mat-header-cell *matHeaderCellDef> Cours d'eau </th> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.WATERCOURSE' | translate }} </th> <td mat-cell *matCellDef="let gauging" (click)="editGauging(gauging)" > {{ gauging.watercourse || '-' }} </td> </ng-container> <!-- Department Column --> <ng-container matColumnDef="department"> - <th mat-header-cell *matHeaderCellDef> Département </th> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.DEPARTMENT' | translate }} </th> <td mat-cell *matCellDef="let gauging"(click)="editGauging(gauging)" > {{ gauging.department || '-' }} </td> </ng-container> <!-- Municipality Column --> <ng-container matColumnDef="municipality"> - <th mat-header-cell *matHeaderCellDef> Commune </th> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.MUNICIPALITY' | translate }} </th> <td mat-cell *matCellDef="let gauging" (click)="editGauging(gauging)"> {{ gauging.municipality || '-' }} </td> </ng-container> <!-- Locality Column --> <ng-container matColumnDef="locality"> - <th mat-header-cell *matHeaderCellDef> Lieu-dit </th> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.LOCALITY' | translate }} </th> <td mat-cell *matCellDef="let gauging" (click)="editGauging(gauging)"> {{ gauging.locality || '-' }} </td> </ng-container> <!-- Date Column --> <ng-container matColumnDef="date"> - <th mat-header-cell *matHeaderCellDef> Date </th> - <td mat-cell *matCellDef="let gauging" (click)="editGauging(gauging)"> {{ gauging.date | date: 'dd/MM/yyyy' }}</td> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.DATE' | translate }} </th> + <td mat-cell *matCellDef="let gauging" (click)="editGauging(gauging)"> {{ gauging.date | date: 'dd/MM/yyyy' }}</td> </ng-container> <!-- State Column --> <ng-container matColumnDef="state"> - <th mat-header-cell *matHeaderCellDef> Etat </th> - <td mat-cell *matCellDef="let gauging" (click)="editGauging(gauging)"> Validé </td> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.STATE' | translate }} </th> + <td mat-cell *matCellDef="let gauging" (click)="editGauging(gauging)"> {{ 'GAUGING.VALIDATED' | translate }} </td> </ng-container> <!-- Actions Column --> <ng-container matColumnDef="actions"> - <th mat-header-cell *matHeaderCellDef> Actions </th> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.ACTIONS' | translate }} </th> <td mat-cell *matCellDef="let gauging, , let i =index"> <button mat-icon-button color="primary" (click)="editGauging(gauging)"> <mat-icon class="edit-button">edit</mat-icon> @@ -67,12 +68,13 @@ @else{ <!-- if no created gauging --> <div class="no-gauging-title"> - <h1>Liste des jaugeages</h1> - <p>Aucun jaugeage réalisé ou en cours de réalisation.</p> - <button mat-raised-button color="primary" (click)="addGauging()" class="add-button">Ajouter</button> + <h1>{{ 'GAUGING.LIST_TITLE' | translate }}</h1> + <p>{{ 'GAUGING.NO_GAUGING' | translate }}</p> + <button mat-raised-button color="primary" (click)="addGauging()" class="add-button"> + {{ 'GAUGING.ADD_GAUGING' | translate }} + </button> </div> } </div> </div> </div> - \ No newline at end of file diff --git a/src/app/views/gauging-list/gauging-list.component.ts b/src/app/views/gauging-list/gauging-list.component.ts index 33056f1596ab4adc037dfe82b5788ff2f36fb4b0..1a9f62218e2b769f5f1b2a7744c2d2bea802f42f 100644 --- a/src/app/views/gauging-list/gauging-list.component.ts +++ b/src/app/views/gauging-list/gauging-list.component.ts @@ -13,11 +13,12 @@ import { Router} from '@angular/router'; import { subscribeOnce } from '../../tools/ObservableHelper'; import { DialogConfirmDeleteGaugingComponent } from '../../components/dialog/dialog-confirm-delete-gauging/dialog-confirm-delete-gauging.component'; import { MatDialog } from '@angular/material/dialog'; +import { TranslateModule } from '@ngx-translate/core'; @Component({ selector: 'app-gauging-list', standalone: true, - imports: [ReactiveFormsModule, FormsModule, CommonModule, MatButtonModule, MatTableModule, DragDropModule, MatIconModule, ErrorMessageComponent], + imports: [TranslateModule, ReactiveFormsModule, FormsModule, CommonModule, MatButtonModule, MatTableModule, DragDropModule, MatIconModule, ErrorMessageComponent], templateUrl: './gauging-list.component.html', styleUrl: './gauging-list.component.scss', providers: [DatePipe] diff --git a/src/app/views/gauging/gauging.component.html b/src/app/views/gauging/gauging.component.html index 5d00a065b48e5f417864fcc2b74fe7b19f59e28e..c91018d60bc7ecdeba574ef4f83705e9fa61cfb1 100644 --- a/src/app/views/gauging/gauging.component.html +++ b/src/app/views/gauging/gauging.component.html @@ -1,31 +1,31 @@ <mat-tab-group #tabGroup animationDuration="0ms"> <!-- Tab1: Création/édition d'un jaugeage --> - <mat-tab label="Description du jaugeage"> + <mat-tab label="{{ 'GAUGING.DESCRIPTION' | translate }}"> <div class="tab-content"> - <h2>Description du jaugeage</h2> + <h2><mat-label>{{ 'GAUGING.DESCRIPTION' | translate }}</mat-label></h2> <form [formGroup]="descriptionForm"> <!-- Localisation Section --> <mat-card class="form-section"> - <mat-card-title class="card-title"><mat-icon>location_on</mat-icon>Localisation</mat-card-title> + <mat-card-title class="card-title"><mat-icon>location_on</mat-icon>{{ 'GAUGING.LOCATION' | translate }}</mat-card-title> <mat-card-content> <div class="form-row"> <mat-form-field appearance="outline"> - <mat-label>Cours d'eau</mat-label> + <mat-label>{{ 'GAUGING.WATERCOURSE' | translate }}</mat-label> <input matInput formControlName="watercourse"> </mat-form-field> <mat-form-field appearance="outline"> - <mat-label>Lieu-dit</mat-label> + <mat-label>{{ 'GAUGING.LOCALITY' | translate }}</mat-label> <input matInput formControlName="locality"> </mat-form-field> </div> <div class="form-row"> <mat-form-field appearance="outline"> - <mat-label>Commune</mat-label> + <mat-label>{{ 'GAUGING.MUNICIPALITY' | translate }}</mat-label> <input matInput formControlName="municipality"> </mat-form-field> <mat-form-field appearance="outline"> - <mat-label>Département</mat-label> + <mat-label>{{ 'GAUGING.DEPARTMENT' | translate }}</mat-label> <input matInput formControlName="department"> </mat-form-field> </div> @@ -34,7 +34,7 @@ <!-- Géolocalisation Section --> <mat-card class="form-section"> - <mat-card-title class="card-title"><mat-icon>my_location</mat-icon>Géolocalisation</mat-card-title> + <mat-card-title class="card-title"><mat-icon>my_location</mat-icon>{{ 'GAUGING.GEOLOCATION' | translate }}</mat-card-title> <mat-card-content> <div class="form-row"> <mat-form-field appearance="outline"> @@ -47,11 +47,11 @@ </mat-select> </mat-form-field> <mat-form-field appearance="outline"> - <mat-label>Point X</mat-label> + <mat-label>{{ 'GAUGING.X_LOCATION' | translate }}</mat-label> <input matInput formControlName="locationX" type="number" step="0.01"> </mat-form-field> <mat-form-field appearance="outline"> - <mat-label>Point Y</mat-label> + <mat-label>{{ 'GAUGING.Y_LOCATION' | translate }}</mat-label> <input matInput formControlName="locationY" type="number" step="0.01"> </mat-form-field> </div> @@ -61,24 +61,24 @@ <!-- Date/time section --> <mat-card class="form-section"> <mat-card-title class="card-title"> - <mat-icon>access_time</mat-icon> Date/Heure + <mat-icon>access_time</mat-icon> {{ 'GAUGING.DATE_TIME' | translate }} </mat-card-title> <mat-card-content> <div class="form-row"> <mat-form-field appearance="outline"> - <mat-label>Date</mat-label> + <mat-label>{{ 'GAUGING.DATE' | translate }}</mat-label> <input matInput [matDatepicker]="picker" formControlName="date"> <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle> <mat-datepicker #picker></mat-datepicker> </mat-form-field> <mat-form-field appearance="outline"> - <mat-label>Heure de début</mat-label> + <mat-label>{{ 'GAUGING.START_TIME' | translate }}</mat-label> <input matInput formControlName="timeStart" [format]="24" [max]="getMaxTimeStart()" [ngxTimepicker]="startTimePicker"> <ngx-material-timepicker #startTimePicker></ngx-material-timepicker> </mat-form-field> <mat-form-field appearance="outline"> - <mat-label>Heure de fin</mat-label> + <mat-label>{{ 'GAUGING.END_TIME' | translate }}</mat-label> <input matInput formControlName="timeEnd" [format]="24" [min]="descriptionForm.get('timeStart')?.value" [ngxTimepicker]="endTimePicker"> <ngx-material-timepicker #endTimePicker></ngx-material-timepicker> @@ -91,7 +91,7 @@ <mat-card class="form-section"> <mat-card-title class="card-title"> <div class="card-title-with-button"> - <mat-icon>person</mat-icon> Opérateurs + <mat-icon>person</mat-icon> {{ 'GAUGING.OPERATORS' | translate }} <button mat-mini-fab color="primary" (click)="addOperatorField()" class="add-button"><mat-icon>add</mat-icon></button> @if(operatorNames.length > 2) { @@ -106,7 +106,7 @@ @for (operator of operatorNames.controls; track operator; let i = $index) { <div [formGroupName]="i" class="operator-form-field"> <mat-form-field appearance="outline"> - <mat-label>Opérateur {{i + 1}}</mat-label> + <mat-label>{{ 'GAUGING.OPERATOR' | translate }} {{i + 1}}</mat-label> <input matInput formControlName="name"> </mat-form-field> </div> @@ -118,12 +118,12 @@ <!-- Observations section --> <mat-card class="form-section"> <mat-card-title class="card-title"> - <mat-icon>book</mat-icon> Observations + <mat-icon>book</mat-icon> {{ 'GAUGING.OBSERVATIONS' | translate }} </mat-card-title> <mat-card-content> <div class="form-row"> <mat-form-field appearance="outline" class="full-width"> - <mat-label>Observation</mat-label> + <mat-label>{{ 'GAUGING.OBSERVATION' | translate }}</mat-label> <textarea matInput formControlName="observations" rows="4"></textarea> </mat-form-field> </div> @@ -134,7 +134,7 @@ <mat-card class="form-section"> <mat-card-title class="card-title"> <div class="card-title-with-button"> - <mat-icon>brightness_1</mat-icon> Granulométrie Représentative + <mat-icon>brightness_1</mat-icon> {{ 'GAUGING.REPRESENTATIVE_GRAIN_SIZE' | translate }} <button mat-mini-fab color="primary" (click)="addGrainSizeField()" class="add-button"><mat-icon>add</mat-icon> </button> @@ -149,12 +149,12 @@ <div class="grainSize-container"> @for (grainsize of grainSizesArray.controls; track grainsize; let i = $index) { <div [formGroupName]="i" class="grainSize-form-field"> - <mat-form-field appearance="outline" class="full-width" placeholder="Granulométrie"> - <mat-label>{{ i === 0 ? 'Dominante' : 'Accessoire ' + i }}</mat-label> + <mat-form-field appearance="outline" class="full-width"> + <mat-label>{{ i === 0 ? ('GAUGING.DOMINANT_GRAIN_SIZE' | translate) : ('GAUGING.ACCESSORY_GRAIN_SIZE' | translate) + ' ' + i }}</mat-label> <mat-select formControlName="size" disableRipple> @for (size of grainSizes; track size) { <mat-option [value]="size"> - {{size}} + {{size | translate}} </mat-option> } </mat-select> @@ -167,25 +167,24 @@ <!-- Boutons Précédent et Suivant --> <div class="form-buttons"> - <button mat-raised-button color="primary" type="button" (click)="selectNextTab()">Suivant</button> + <button mat-raised-button color="primary" type="button" (click)="selectNextTab()">{{ 'NEXT' | translate }}</button> </div> </form> </div> </mat-tab> <!-- Paramètres --> - <mat-tab label="Paramètres du jaugeage"> + <mat-tab label="{{ 'GAUGING.PARAMETERS' | translate }}"> <div class="tab-content"> - <h2>Paramètres du jaugeage</h2> + <h2>{{ 'GAUGING.PARAMETERS' | translate }}</h2> <form [formGroup]="parametersForm"> <!-- Largeur de la section --> <mat-card class="form-section"> - <mat-card-title class="card-title"><mat-icon>aspect_ratio</mat-icon>Largeur de la - section</mat-card-title> + <mat-card-title class="card-title"><mat-icon>aspect_ratio</mat-icon>{{ 'GAUGING.SECTION_WIDTH' | translate }}</mat-card-title> <mat-card-content class="form-row"> <mat-form-field appearance="outline"> - <mat-label>Largeur (m)</mat-label> + <mat-label>{{ 'GAUGING.WIDTH' | translate }}(m)</mat-label> <input matInput formControlName="sectionWidth" type="number" step="0.01"> </mat-form-field> </mat-card-content> @@ -193,14 +192,14 @@ <!-- Hauteur d'eau en rive --> <mat-card class="form-section"> - <mat-card-title class="card-title"><mat-icon>waves</mat-icon>Hauteur d'eau en rive</mat-card-title> + <mat-card-title class="card-title"><mat-icon>waves</mat-icon>{{ 'GAUGING.HEIGHT_BANK_WATER' | translate }}</mat-card-title> <mat-card-content class="form-row"> <mat-form-field appearance="outline"> - <mat-label>Hauteur d'eau rive gauche (m)</mat-label> + <mat-label>{{ 'GAUGING.HEIGHT_BANK_LEFT' | translate }} (m)</mat-label> <input matInput formControlName="heightBankLeft" type="number" step="0.01"> </mat-form-field> <mat-form-field appearance="outline"> - <mat-label>Hauteur d'eau rive droite (m)</mat-label> + <mat-label>{{ 'GAUGING.HEIGHT_BANK_RIGHT' | translate }} (m)</mat-label> <input matInput formControlName="heightBankRight" type="number" step="0.01"> </mat-form-field> </mat-card-content> @@ -208,14 +207,13 @@ <!-- Origine des mesures --> <mat-card class="form-section"> - <mat-card-title class="card-title"><mat-icon>gps_fixed</mat-icon>Origine des - mesures</mat-card-title> + <mat-card-title class="card-title"><mat-icon>gps_fixed</mat-icon>{{ 'GAUGING.ORIGIN_MEASURES' | translate }}</mat-card-title> <mat-card-content class="form-row"> <div> <mat-radio-group formControlName="originMeasures"> @for (origin of originMeasures; track origin) { <mat-radio-button color="primary" [value]="origin"> - {{origin}} + {{origin | translate}} </mat-radio-button> } </mat-radio-group> @@ -226,19 +224,19 @@ <!-- Type de matériel --> <mat-card class="form-section"> <mat-card-title class="card-title"><mat-icon>construction</mat-icon> - Type de matériel</mat-card-title> + {{ 'GAUGING.ORIGIN_MEASURES' | translate }}</mat-card-title> <mat-card-content class="form-row"> @if((materialList$ | async)?.length! > 0){ <mat-radio-group formControlName="materialType" (change)="onChangeMaterialType()" style="width: 50%;"> @for (type of materielTypes; track type) { <mat-radio-button [value]="type" color="primary"> - {{type}} + {{type | translate}} </mat-radio-button> } </mat-radio-group> @if(filteredMaterials?.length) { <mat-form-field appearance="outline" class="full-width"> - <mat-label>Choix du matériel</mat-label> + <mat-label>{{ 'GAUGING.MATERIAL_CHOICE' | translate }}</mat-label> <mat-select formControlName="materialId" (selectionChange)="onChangeMaterial()"> @for (material of filteredMaterials ; track material.id) { <mat-option [value]="material.id">{{material.name}} ( n°{{material.serialNumber}} )</mat-option> @@ -249,14 +247,14 @@ } @else { <div class="no-material-message" (click)="openMaterialForm()"> - <p>Aucun matériel de ce type</p> + <p>{{ 'NO_AVALAIBLE_MATERIAL' | translate }}</p> <button mat-mini-fab color="primary"><mat-icon>add</mat-icon></button> </div> } } @else { <div class="no-material-message" (click)="openMaterialForm()"> - <p>Veuillez ajouter d'abord le matériel</p> + <p>{{ 'NEED_MATERIAL' | translate }}</p> <button mat-mini-fab color="primary"><mat-icon>add</mat-icon></button> </div> } @@ -266,23 +264,23 @@ <div class="form-buttons"> - <button mat-raised-button color="primary" type="button" (click)="selectPreviousTab()">Précédent</button> - <button mat-raised-button color="primary" type="button" (click)="selectNextTab()">Suivant</button> + <button mat-raised-button color="primary" type="button" (click)="selectPreviousTab()">{{ 'PREVIOUS' | translate }}</button> + <button mat-raised-button color="primary" type="button" (click)="selectNextTab()">{{ 'NEXT' | translate }}</button> </div> </form> </div> </mat-tab> <!-- Saisies des mesures --> - <mat-tab label="Saisie des mesures" ngSkipHydration [disabled]="!this.selectedMaterialId"> + <mat-tab label="{{ 'GAUGING.TAB_MEASUREMENTS' | translate }}" ngSkipHydration [disabled]="!this.selectedMaterialId"> <div class="tab-content"> - <h2>Saisie des mesures</h2> + <h2>{{ 'GAUGING.TAB_MEASUREMENTS' | translate }}</h2> <div class="vertical-section-container"> <!-- Verticales --> <mat-card class="form-section"> <mat-card-title class="vertical-card-title"> <mat-icon> vertical_align_bottom</mat-icon> - Verticales + {{ 'GAUGING.VERTICALS' | translate }} <button mat-mini-fab color="primary" (click)="addVertical()" class="add-button"> <mat-icon>add</mat-icon> </button> @@ -291,7 +289,8 @@ <div formArrayName="verticals"> <table mat-table [dataSource]="verticals.controls" #verticalsTable class="mat-elevation-z2"> <ng-container matColumnDef="verticalNumber"> - <th mat-header-cell *matHeaderCellDef class="vertical-number"> Verticales n° </th> + <th mat-header-cell *matHeaderCellDef class="vertical-number"> {{ 'GAUGING.VERTICAL_NUMBER' | translate }} + </th> <td mat-cell *matCellDef="let vertical; let i = index" (click)="onVerticalUpdate(i)" class="vertical-number" [ngClass]="{ 'selected-vertical': i === selectedVerticalIndex && i !== deletingRowIndex, @@ -303,17 +302,17 @@ <!-- Repeat for other columns --> <ng-container matColumnDef="distanceFromBank"> - <th mat-header-cell *matHeaderCellDef> Distance à la rive (m) </th> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.DISTANCE_FROM_BANK' | translate }} </th> <td mat-cell *matCellDef="let vertical; let i = index" (click)="onVerticalUpdate(i)" [ngClass]="{ 'selected-vertical': i === selectedVerticalIndex && i !== deletingRowIndex, 'deleting': i === deletingRowIndex }" (focusin)="onRowFocusIn(i)" (focusout)="onRowFocusOut(i)"> <div [formGroupName]="i"> <mat-form-field appearance="outline" class="vertical-table-field"> - <input matInput formControlName="distanceFromBank" placeholder="Distance" type="number" + <input matInput formControlName="distanceFromBank" placeholder="{{ 'GAUGING.DISTANCE' | translate }}" type="number" min="0"> @if(vertical.get('distanceFromBank')?.hasError('invalidDistanceFromBank')) { - <mat-error>Distance invalide</mat-error> + <mat-error>{{ 'GAUGING.INVALID_DISTANCE' | translate }}</mat-error> } </mat-form-field> @@ -323,14 +322,14 @@ <!-- Repeat for other columns --> <ng-container matColumnDef="depth"> - <th mat-header-cell *matHeaderCellDef> Profondeur (m) </th> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.DEPTH' | translate }}</th> <td mat-cell *matCellDef="let vertical; let i = index" (click)="onVerticalUpdate(i)" [ngClass]="{ 'selected-vertical': i === selectedVerticalIndex && i !== deletingRowIndex, 'deleting': i === deletingRowIndex }" (focusin)="onRowFocusIn(i)" (focusout)="onRowFocusOut(i)"> <div [formGroupName]="i"> <mat-form-field appearance="outline" class="vertical-table-field"> - <input matInput formControlName="depth" placeholder="Profondeur" type="number" step="1" min="0"> + <input matInput formControlName="depth" placeholder="{{ 'GAUGING.DEPTH' | translate }}" type="number" step="1" min="0"> </mat-form-field> </div> </td> @@ -338,14 +337,14 @@ <!-- Repeat for other columns --> <ng-container matColumnDef="duration"> - <th mat-header-cell *matHeaderCellDef> Durée(s) </th> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.DURATION' | translate }} </th> <td mat-cell *matCellDef="let vertical; let i = index" (click)="onVerticalUpdate(i)" [ngClass]="{ 'selected-vertical': i === selectedVerticalIndex && i !== deletingRowIndex, 'deleting': i === deletingRowIndex }" (focusin)="onRowFocusIn(i)" (focusout)="onRowFocusOut(i)"> <div [formGroupName]="i"> <mat-form-field appearance="outline" class="vertical-table-field"> - <input matInput formControlName="duration" placeholder="Durée" type="number" step="1" min="0"> + <input matInput formControlName="duration" placeholder="{{ 'GAUGING.DURATION' | translate }}" type="number" step="1" min="0"> </mat-form-field> </div> </td> @@ -353,7 +352,7 @@ <!-- Repeat for other columns --> <ng-container matColumnDef="numberOfPoint"> - <th mat-header-cell *matHeaderCellDef> Nombre de points </th> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.NUMBER_OF_POINTS' | translate }} </th> <td mat-cell *matCellDef="let vertical; let i = index" (click)="onVerticalUpdate(i)" [ngClass]="{ 'selected-vertical': i === selectedVerticalIndex && i !== deletingRowIndex, 'deleting': i === deletingRowIndex @@ -368,7 +367,7 @@ </ng-container> <ng-container matColumnDef="delete"> - <th mat-header-cell *matHeaderCellDef> Actions </th> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.ACTIONS' | translate }} </th> <td mat-cell *matCellDef="let vertical; let i = index" [ngClass]="{ 'selected-vertical': i === selectedVerticalIndex && i !== deletingRowIndex, 'deleting': i === deletingRowIndex @@ -397,7 +396,7 @@ <mat-card class="form-section"> <mat-card-title class="vertical-card-title"> <mat-icon>gps_fixed</mat-icon> - Mesures sur la verticale n°{{ selectedVerticalIndex + 1 }} + {{ 'GAUGING.MEASUREMENTS_ON_VERTICAL' | translate }} {{ selectedVerticalIndex + 1 }} </mat-card-title> <mat-card-content [formGroup]="verticalsForm"> <div formArrayName="verticals"> @@ -407,12 +406,12 @@ class="mat-elevation-z2" #verticalMeasurementsTable> <!-- Distance From Bottom Column --> <ng-container matColumnDef="distanceFromBottom"> - <th mat-header-cell *matHeaderCellDef> Distance du fond (m) </th> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.DISTANCE_FROM_BOTTOM' | translate }} </th> <td mat-cell *matCellDef="let measurement; let i = index" (click)="onVerticalMeasurementUpdate(i)"> <div [formGroupName]="i"> <mat-form-field appearance="outline" class="vertical-table-field"> - <input matInput formControlName="distanceFromBottom" placeholder="Distance" min="0" + <input matInput formControlName="distanceFromBottom" placeholder="{{ 'GAUGING.DISTANCE' | translate }}" min="0" readonly="readonly"> </mat-form-field> </div> @@ -421,12 +420,12 @@ <!-- Speed Column --> <ng-container matColumnDef="speed"> - <th mat-header-cell *matHeaderCellDef> Vitesse (m/s) </th> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.SPEED' | translate }} </th> <td mat-cell *matCellDef="let measurement; let i = index" (click)="onVerticalMeasurementUpdate(i)"> <div [formGroupName]="i"> <mat-form-field appearance="outline" class="vertical-table-field"> - <input matInput formControlName="speed" placeholder="Vitesse" type="number" step="0.01" + <input matInput formControlName="speed" placeholder="{{ 'GAUGING.SPEED' | translate }}" type="number" step="0.01" min="0"> </mat-form-field> </div> @@ -435,7 +434,7 @@ <!-- Rotation Column (Conditionnelle) --> <ng-container matColumnDef="rotations"> - <th mat-header-cell *matHeaderCellDef> Rotations </th> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.ROTATIONS' | translate }} </th> <td mat-cell *matCellDef="let measurement; let i = index" (click)="onVerticalMeasurementUpdate(i)"> <div [formGroupName]="i"> @@ -463,18 +462,18 @@ <!-- Résultats --> - <mat-tab label="Résultats" ngSkipHydration [disabled]="!this.selectedMaterialId"> + <mat-tab label="{{ 'GAUGING.TAB_RESULTS' | translate }}" ngSkipHydration [disabled]="!this.selectedMaterialId"> <div class="tab-content"> - <h2>Résultats</h2> + <h2>{{ 'GAUGING.TAB_RESULTS' | translate }}</h2> <!-- Résultats par verticale --> <mat-card class="form-section"> <mat-card-title class="vertical-card-title"> - <mat-icon>equalizer</mat-icon> Résultats par verticale + <mat-icon>equalizer</mat-icon> {{ 'GAUGING.RESULTS_PER_VERTICAL' | translate }} </mat-card-title> <mat-card-content> <table mat-table [dataSource]="verticalResults" class="mat-elevation-z2"> <ng-container matColumnDef="verticalNumber"> - <th mat-header-cell *matHeaderCellDef class="vertical-number"> Verticales n° </th> + <th mat-header-cell *matHeaderCellDef class="vertical-number"> {{ 'GAUGING.VERTICAL_NUMBER' | translate }} </th> <td mat-cell *matCellDef="let vertical; let i = index" class="vertical-number"> {{i+1}} </td> @@ -482,33 +481,33 @@ <!-- Distance from Bank Column --> <ng-container matColumnDef="distanceFromBank"> - <th mat-header-cell *matHeaderCellDef> Distance à la rive (m) </th> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.DISTANCE_FROM_BANK' | translate }} </th> <td mat-cell *matCellDef="let vertical"> {{vertical.distanceFromBank}} </td> </ng-container> <!-- Depth Column --> <ng-container matColumnDef="depth"> - <th mat-header-cell *matHeaderCellDef> Profondeur (m) </th> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.DEPTH' | translate }} </th> <td mat-cell *matCellDef="let vertical"> {{vertical.depth}} </td> </ng-container> <!-- Speed Column --> <ng-container matColumnDef="speed"> - <th mat-header-cell *matHeaderCellDef> Vitesse moyenne (m/s) </th> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.AVERAGE_SPEED' | translate }} </th> <td mat-cell *matCellDef="let vertical" style="font-weight: bold; color:#735fff "> {{vertical.averageSpeed.toFixed(3)}} </td> </ng-container> <!-- Surface Column --> <ng-container matColumnDef="surface"> - <th mat-header-cell *matHeaderCellDef> Surface (m²) </th> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.SURFACE' | translate }} </th> <td mat-cell *matCellDef="let vertical; let i = index" class="surface-cell" style="font-weight: bold; color:#00a3a6;"> {{ vertical.surface.toFixed(3) }}</td> </ng-container> <!-- Flow Column --> <ng-container matColumnDef="flow"> - <th mat-header-cell *matHeaderCellDef> Débit (m³/s) </th> + <th mat-header-cell *matHeaderCellDef> {{ 'GAUGING.FLOW' | translate }} </th> <td mat-cell *matCellDef="let vertical; let i = index" style="font-weight: bold; color:#ff6347;"> {{vertical.flow.toFixed(3)}}</td> </ng-container> @@ -525,23 +524,23 @@ <!-- Résultats globaux --> <mat-card class="form-section"> <mat-card-title class="vertical-card-title"> - <mat-icon>assessment</mat-icon> Résultats globaux du jaugeage + <mat-icon>assessment</mat-icon>{{ 'GAUGING.OVERALL_GAUGING_RESULTS' | translate }} </mat-card-title> <mat-card-content> <div class="global-results-grid"> <div class="global-result-card"> <mat-icon class="result-icon">crop_square</mat-icon> - <h3>Surface totale</h3> + <h3>{{ 'GAUGING.TOTAL_SURFACE' | translate }}</h3> <div class="result-value">{{ calculateTotalSurface(verticalResults).toFixed(3) }} m²</div> </div> <div class="global-result-card"> <mat-icon class="result-icon">speed</mat-icon> - <h3>Vitesse moyenne</h3> + <h3>{{ 'GAUGING.AVERAGE_SPEED' | translate }}</h3> <div class="result-value">{{calculateAverageSpeedSection().toFixed(3)}} m/s</div> </div> <div class="global-result-card"> <mat-icon class="result-icon">waves</mat-icon> - <h3>Débit total</h3> + <h3>{{ 'GAUGING.TOTAL_FLOW' | translate }}</h3> <div class="result-value">{{ calculateTotalFlow(verticalResults).toFixed(3) }} m³/s</div> </div> </div> @@ -551,7 +550,7 @@ <!-- Représentation graphique des champs de vitesse --> <mat-card class="form-section"> <mat-card-title class="vertical-card-title"> - <mat-icon>show_chart</mat-icon> Représentation des champs de vitesse + <mat-icon>show_chart</mat-icon> {{ 'GAUGING.GRAPHICAL_REPRESENTATION' | translate }} </mat-card-title> <mat-card-content> <!-- Placeholder for the graph --> diff --git a/src/app/views/gauging/gauging.component.ts b/src/app/views/gauging/gauging.component.ts index 9ef2c4731453219ad915367394bc6db35c932050..27b952e5f00e0d62307730628cfe039b1b040c4e 100644 --- a/src/app/views/gauging/gauging.component.ts +++ b/src/app/views/gauging/gauging.component.ts @@ -34,12 +34,13 @@ import { Vertical } from '../../models/vertical'; import { MaterialPropeller } from '../../models/material_propeller'; import { rowsAnimation } from '../../tools/Animations/table-row-animation'; import { animate, state, style, transition, trigger } from '@angular/animations'; +import { TranslateModule } from '@ngx-translate/core'; Chart.register(...registerables) @Component({ selector: 'app-gauging', standalone: true, - imports: [MatNativeDateModule, MatDatepickerModule, NgxMaterialTimepickerModule, MatTabsModule, ReactiveFormsModule, FormsModule, CommonModule, MatCardModule, MatFormFieldModule, MatSelectModule, MatRadioModule, MatInputModule, MatButtonModule, MatTableModule, MatIconModule, ErrorMessageComponent, BaseChartDirective,], + imports: [TranslateModule, MatNativeDateModule, MatDatepickerModule, NgxMaterialTimepickerModule, MatTabsModule, ReactiveFormsModule, FormsModule, CommonModule, MatCardModule, MatFormFieldModule, MatSelectModule, MatRadioModule, MatInputModule, MatButtonModule, MatTableModule, MatIconModule, ErrorMessageComponent, BaseChartDirective,], templateUrl: './gauging.component.html', styleUrls: ['./gauging.component.scss'], providers: [DatePipe], diff --git a/src/app/views/settings/settings.component.html b/src/app/views/settings/settings.component.html new file mode 100644 index 0000000000000000000000000000000000000000..7ecbe977632230f90a0f484c241890a4a9208550 --- /dev/null +++ b/src/app/views/settings/settings.component.html @@ -0,0 +1,23 @@ +<div class="container"> + <h1> + <mat-label> + <mat-icon class="icon-list-item">settings</mat-icon> Paramètres + </mat-label> + </h1> + <mat-card class="form-section"> + <mat-card-header> + <mat-card-title> + <mat-icon>public</mat-icon> Langue + </mat-card-title> + </mat-card-header> + <mat-card-content> + <mat-form-field appearance="fill"> + <mat-label>Langue</mat-label> + <mat-select [(value)]="selectedLanguage" (selectionChange)="changeLanguage($event.value)"> + <mat-option value="fr">Français</mat-option> + <mat-option value="en">English</mat-option> + </mat-select> + </mat-form-field> + </mat-card-content> + </mat-card> +</div> diff --git a/src/app/views/settings/settings.component.scss b/src/app/views/settings/settings.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..05f5eadee6687a1dd2580ca893597e1759d6b3df --- /dev/null +++ b/src/app/views/settings/settings.component.scss @@ -0,0 +1,88 @@ +$primary: #00a3a6; +$light-primary: #b3e3e4; +$warn: #ff6347; +$light-warn: #FBE9E7; +$accent: #735fff; +$light-accent: #c6c1dc; +$sub-container-background: #f5f5f5; +$table-header-background: #797870; +$border-color: #ccc; +$odd-row-color: rgba(0, 0, 0, 0.028); +$pair-row-color: #ffffff; +$card-shadow-color: rgba(0, 0, 0, 0.1); + +.container { + display: flex; + flex-direction: column; + height: 95%; + margin: 20px; + border: 1px solid $border-color; + border-radius: 12px; + box-shadow: 0 4px 8px $card-shadow-color; + background-color: $sub-container-background; + padding: 20px; +} + +.settings-container { + flex: 1; + margin: 10px; + padding: 15px; + border: 1px solid $border-color; + border-radius: 10px; + background-color: #ffffff; + box-shadow: 0 2px 5px $card-shadow-color; +} + +h1 { + font-size: 24px; + text-align: center; + margin-bottom: 20px; +} + +mat-icon { + vertical-align: middle; +} + +.form-section { + margin-bottom: 30px; + padding: 25px; + border: 1px solid $border-color; + border-radius: 10px; + background-color: #ffffff; + box-shadow: 0 2px 4px $card-shadow-color; + transition: box-shadow 0.3s ease; + + &:hover { + box-shadow: 0 4px 10px $card-shadow-color; + } +} + +mat-card-header { + border-radius: 8px 8px 0 0; +} + +mat-card-title { + font-size: 20px; + color: $primary; + font-weight: 500; +} + +mat-form-field { + width: 100%; + margin-top: 15px; +} + +mat-select { + border-radius: 5px; +} + +mat-option { + &:hover { + background-color: $light-accent; + } +} + +mat-card-content { + padding: 20px; + font-size: 16px; +} diff --git a/src/app/views/settings/settings.component.ts b/src/app/views/settings/settings.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..19c6e8f4a8fb340f6a068b3e22ed27cd17cbeb28 --- /dev/null +++ b/src/app/views/settings/settings.component.ts @@ -0,0 +1,29 @@ +import { Component, OnInit } from '@angular/core'; +import { MatCardModule } from '@angular/material/card'; +import { MatSelectModule } from '@angular/material/select'; +import { MatIconModule } from '@angular/material/icon'; +import { LanguageService } from '../../services/language/language.service'; + +@Component({ + selector: 'app-settings', + standalone: true, + imports: [MatSelectModule, MatCardModule, MatIconModule], + templateUrl: './settings.component.html', + styleUrls: ['./settings.component.scss'] +}) +export class SettingsComponent implements OnInit { + selectedLanguage: string | undefined; + + constructor(private languageService: LanguageService) {} + + ngOnInit(): void { + // Utiliser le service pour détecter la langue du navigateur et définir la langue par défaut (anglais si aucune n'est supportée) + this.selectedLanguage = this.languageService.detectBrowserLanguage(); + this.languageService.setDefaultLanguage(this.selectedLanguage); + } + + changeLanguage(lang: string) { + this.languageService.changeLanguage(lang); + this.selectedLanguage = lang; + } +} diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json new file mode 100644 index 0000000000000000000000000000000000000000..ae2ddd6f95b9843308c8be384d438f93a0f20efb --- /dev/null +++ b/src/assets/i18n/en.json @@ -0,0 +1,172 @@ +{ + "TOOLBAR": { + "MENU": "Menu", + "TITLE": "Moulinet", + "SEARCH": "Search", + "HELP": "Help" + }, + "SIDENAV": { + "MATERIAL": "Equipment", + "GAUGING_LIST": "List of Gaugings", + "NEW_GAUGING": "New Gauging", + "RESET_SESSION": "Reset Session", + "LOAD_SESSION": "Load Session", + "SAVE_SESSION": "Save Session", + "CONFIGURATION": "Configuration", + "DOCUMENTATION": "Documentation" + }, + "BUTTONS": { + "CREATE_NEW_GAUGING": "Create New Gauging", + "RESET_SESSION": "Reset Session", + "LOAD_SESSION": "Load Session", + "SAVE_SESSION": "Save Session" + }, + "DIALOG": { + "RESET_SESSION_TITLE": "Reset Session", + "RESET_SESSION_WARNING": "Warning! The current session will be lost.", + "CONFIRM_BUTTON": "Confirm", + "CANCEL_BUTTON": "Cancel", + "LOAD_SESSION_TITLE": "Load a session", + "CHOOSE_FILE_LABEL": "Choose a file", + "INVALID_FILE": "The uploaded file is invalid", + "INVALID_JSON_FILE": "The JSON file contains incorrect data", + "UPLOAD_PROMPT": "Please upload a file", + "CLEAR_SESSION_CHECKBOX": "Clear current session", + "SAVE_TITLE": "Save session", + "FILE_NAME_LABEL": "File name", + "NO_FILE_NAME_ERROR": "Please name the file", + "PANEL_GAUGING_TITLE": "Gaugings", + "PANEL_EQUIPMENT_TITLE": "Equipment", + "DELETE_MATERIAL_TITLE": "Delete material", + "WARNING_DELETE_MATERIAL_MESSAGE": "Warning! The <strong>{{ materialType }} {{ materialName }}</strong> will be removed from the list.", + "DELETE_IMPOSSIBLE_TITLE": "Unable to delete material", + "DELETE_IMPOSSIBLE_MESSAGE": "Warning! The <strong>{{ materialType }} {{ materialName }}</strong> is associated with one or more existing gaugings.", + "DELETE_IMPOSSIBLE_INSTRUCTION": "Delete the associated gauging(s) first to remove this material.", + "GAUGING_LIST_BUTTON": "Gaugings list", + "DELETE_GAUGING_TITLE": "Delete Gauging", + "DELETE_GAUGING_MESSAGE": "Warning! The gauging performed on the <strong>{{ watercourse }}</strong> on <strong>{{ date }}</strong> will be removed from the list.", + "DELETE_VERTICAL_TITLE": "Delete Vertical", + "DELETE_VERTICAL_MESSAGE": "Warning! The vertical <strong>number {{ verticalIndex }}</strong> will be removed from the list." + }, + "PANEL": { + "SELECT_ALL": "All", + "SELECT_NONE": "None" + }, + "GAUGING": { + "LIST_TITLE": "Gauging List", + "ADD_GAUGING": "Add Gauging", + "WATERCOURSE": "Watercourse", + "DEPARTMENT": "Department", + "MUNICIPALITY": "Municipality", + "LOCALITY": "Locality", + "DATE": "Date", + "STATE": "State", + "ACTIONS": "Actions", + "VALIDATED": "Validated", + "NO_GAUGING": "No gauging has been carried out or is in progress.", + "DESCRIPTION": "Gauging description", + "LOCATION": "Location", + "GEOLOCATION": "Geolocation", + "X_LOCATION": "Point X", + "Y_LOCATION": "Point Y", + "DATE_TIME": "Date/Time", + "START_TIME": "Start time", + "END_TIME": "End time", + "OPERATORS": "Operators", + "OPERATOR": "Operator", + "OBSERVATIONS": "Observations", + "OBSERVATION": "Observation", + "REPRESENTATIVE_GRAIN_SIZE": "Representative grain size", + "GRAIN_SIZE": { + "CONCRETE": "Concrete", + "ROCKS": "Rocks", + "BLOCSS": "Blocks", + "LARGE_STONES": "Large stones", + "LARGE_PEBBLES": "Large pebbles", + "FINE_PEBBLES": "Fine pebbles", + "LARGE_GRAVELS": "Large gravels", + "FINE_GRAVELS": "Fine gravels", + "LARGE_SAND": "Coarse sand", + "FINE_SAND": "Fine sand", + "SILT": "Silt", + "CLAY": "Clay" + }, + "DOMINANT_GRAIN_SIZE": "Dominant", + "ACCESSORY_GRAIN_SIZE": "Accessory", + "PARAMETERS": "Gauging parameters", + "SECTION_WIDTH": "Section width", + "WIDTH": "Width", + "HEIGHT_BANK_WATER": "Height of water on bank", + "HEIGHT_BANK_LEFT": "Height bank left", + "HEIGHT_BANK_RIGHT": "Height bank right", + "ORIGIN_MEASURES": "Origin of measures", + "BANK_LEFT": "Left bank", + "BANK_RIGHT": "Right bank", + "MATERIAL_CHOICE": "Material choice", + "TAB_MEASUREMENTS": "Measurement entries", + "VERTICALS": "Verticals", + "VERTICAL_NUMBER": "Vertical n°", + "DISTANCE_FROM_BANK": "Distance from bank (m)", + "DEPTH": "Depth (m)", + "DURATION": "Duration (s)", + "NUMBER_OF_POINTS": "Number of points", + "INVALID_DISTANCE": "Invalid distance", + "MEASUREMENTS_ON_VERTICAL": "Measurements on vertical n°", + "DISTANCE_FROM_BOTTOM": "Distance from bottom (m)", + "SPEED": "Speed (m/s)", + "ROTATIONS": "Number of rotations", + "ROTATION_NUMBER": "Rotations", + "DISTANCE": "Distance", + "TAB_RESULTS": "Results", + "RESULTS_PER_VERTICAL": "Results by Vertical", + "AVERAGE_SPEED": "Average Speed (m/s)", + "SURFACE": "Surface Area (m²)", + "FLOW": "Flow Rate (m³/s)", + "OVERALL_GAUGING_RESULTS": "Overall gauging results", + "TOTAL_SURFACE": "Total Surface (m²)", + "AVERAGE_SPEED_GLOBAL": "Average Speed", + "TOTAL_FLOW": "Total Flow Rate (m³/s)", + "GRAPHICAL_REPRESENTATION": "Graphical representation" +}, +"EQUIPMENT": { + "MATERIAL_LIST_TITLE": "Material List", + "ADD_MATERIAL_BUTTON": "Add", + "NO_MATERIAL_AVAILABLE": "No material available.", + "ACTIONS": "Actions", + "NAME": "Name", + "TYPE": "Type", + "SERIALNUMBER": "Serial Number", + "DESCRIPTION": "Description", + "EDIT_BUTTON": "Edit", + "DELETE_BUTTON": "Delete", + "NEW_MATERIAL_TITLE": "New Material", + "EDIT_MATERIAL_TITLE": "Edit Material", + "MATERIAL_TYPE_LABEL": "Material Type", + "MATERIAL_TYPES": { + "ELECTROMAGNETIC_FLOW_METER": "Electromagnetic flow meter", + "PROPELLER_FLOW_METER": "Propeller flow meter", + "VELOCITY_HEAD_ROAD": "Velocity head road" + }, + "MODEL_SECTION_TITLE": "Model", + "NAME_LABEL": "Name", + "SERIALNUMBER_LABEL": "Serial Number", + "PROPELLERNUMBER_LABEL": "Propeller Number", + "PROPELLERSERIALNUMBER_LABEL": "Propeller Serial Number", + "VELOCITY_FORMULA_COUNT_LABEL": "Number of Velocity Formulas", + "VELOCITY_FORMULA_TITLE_LABEL": "Velocity Formula n°", + "LOWER_BOUND_LABEL": "Lower Bound", + "UPPER_BOUND_LABEL": "Upper Bound", + "COEF_A_LABEL": "Coefficient A", + "CONSTANT_B_LABEL": "Constant B", + "INVALID_BOUNDS_ERROR": "The specified bounds are incorrect. The lower bound must be strictly less than the upper bound.", + "REQUIRED_NAME_ERROR": "The name is required.", + "ADDITIONAL_INFO_TITLE": "Additional information", + "DESCRIPTION_LABEL": "Description", + "SUBMIT_BUTTON": "Submit", + "CANCEL_BUTTON": "Cancel" +}, +"NEXT": "Next", +"PREVIOUS": "Previous", +"NO_AVALAIBLE_MATERIAL": "No material of this type", +"NEED_MATERIAL": "Please add the equipment first" +} \ No newline at end of file diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json new file mode 100644 index 0000000000000000000000000000000000000000..467cbb47eccff6a29ee3c9b04aff600bf00b3cb7 --- /dev/null +++ b/src/assets/i18n/fr.json @@ -0,0 +1,172 @@ +{ + "TOOLBAR": { + "MENU": "Menu", + "TITLE": "Moulinet", + "SEARCH": "Rechercher", + "HELP": "Aide" + }, + "SIDENAV": { + "MATERIAL": "Matériel", + "GAUGING_LIST": "Liste des jaugeages", + "NEW_GAUGING": "Nouveau jaugeage", + "RESET_SESSION": "Réinitialiser la session", + "LOAD_SESSION": "Charger une session", + "SAVE_SESSION": "Enregistrer la session", + "CONFIGURATION": "Configuration", + "DOCUMENTATION": "Documentation" + }, + "BUTTONS": { + "CREATE_NEW_GAUGING": "Créer un nouveau jaugeage", + "RESET_SESSION": "Réinitialiser la session", + "LOAD_SESSION": "Charger une session", + "SAVE_SESSION": "Enregistrer la session" + }, + "DIALOG": { + "RESET_SESSION_TITLE": "Réinitialiser la session", + "RESET_SESSION_WARNING": "Attention! La session actuelle sera perdue.", + "CONFIRM_BUTTON": "Confirmer", + "CANCEL_BUTTON": "Annuler", + "LOAD_SESSION_TITLE": "Charger une session", + "CHOOSE_FILE_LABEL": "Choisir un fichier", + "INVALID_FILE": "Le fichier téléchargé est invalide", + "INVALID_JSON_FILE": "Le fichier JSON contient des données incorrectes", + "UPLOAD_PROMPT": "Veuillez télécharger un fichier", + "CLEAR_SESSION_CHECKBOX": "Vider la session en cours", + "SAVE_TITLE": "Sauvegarder la session", + "FILE_NAME_LABEL": "Nom du fichier", + "NO_FILE_NAME_ERROR": "Veuillez nommer le fichier", + "PANEL_GAUGING_TITLE": "Jaugeages", + "PANEL_EQUIPMENT_TITLE": "Matériel", + "DELETE_MATERIAL_TITLE": "Suppression du matériel", + "WARNING_DELETE_MATERIAL_MESSAGE": "Attention! Le <strong>{{ materialType }} {{ materialName }}</strong> sera supprimé de la liste.", + "DELETE_IMPOSSIBLE_TITLE": "Suppression du matériel impossible", + "DELETE_IMPOSSIBLE_MESSAGE": "Attention! Le <strong>{{ materialType }} {{ materialName }}</strong> est associé à un ou plusieurs jaugeages existants.", + "DELETE_IMPOSSIBLE_INSTRUCTION": "Supprimez d'abord le ou les jaugeages associés pour supprimer ce matériel.", + "GAUGING_LIST_BUTTON": "Liste des jaugeages", + "DELETE_GAUGING_TITLE": "Suppression d'un jaugeage", + "DELETE__GAUGING_MESSAGE": "Attention! Le jaugeage réalisé sur le <strong>{{ watercourse }}</strong> à la date du <strong>{{ date }}</strong> sera supprimé de la liste.", + "DELETE_VERTICAL_TITLE": "Suppression de la verticale", + "DELETE_VERTICAL_MESSAGE": "Attention! La verticale <strong>n°{{ verticalIndex }}</strong> sera supprimée de la liste." + }, + "PANEL": { + "SELECT_ALL": "Tous", + "SELECT_NONE": "Aucun" + }, + "GAUGING": { + "LIST_TITLE": "Liste des Jaugeages", + "ADD_GAUGING": "Ajouter un jaugeage", + "WATERCOURSE": "Cours d'eau", + "DEPARTMENT": "Département", + "MUNICIPALITY": "Commune", + "LOCALITY": "Lieu-dit", + "DATE": "Date", + "STATE": "État", + "ACTIONS": "Actions", + "VALIDATED": "Validé", + "NO_GAUGING": "Aucun jaugeage réalisé ou en cours de réalisation.", + "DESCRIPTION": "Description du jaugeage", + "LOCATION": "Localisation", + "GEOLOCATION": "Géolcalisation", + "X_LOCATION": "Point X", + "Y_LOCATION": "Point Y", + "DATE_TIME": "Date/Heure", + "START_TIME": "Heure de début", + "END_TIME": "End time", + "OPERATORS": "Operators", + "OPERATOR": "Operator", + "OBSERVATIONS": "Observations", + "OBSERVATION": "Observation", + "REPRESENTATIVE_GRAIN_SIZE": "Garunulométrie représentative", + "GRAIN_SIZE": { + "CONCRETE": "Béton", + "ROCKS": "Rochers", + "BLOCSS": "Blocs", + "LARGE_STONES": "Pierres grossières", + "LARGE_PEBBLES": "Cailloux grossiers", + "FINE_PEBBLES": "Cailloux fins", + "LARGE_GRAVELS": "Graviers grossiers", + "FINE_GRAVELS": "Graviers fins", + "LARGE_SAND": "Sable grossier", + "FINE_SAND": "Sable fin", + "SILT": "Limon", + "CLAY": "Argile" + }, + "DOMINANT_GRAIN_SIZE": "Dominante", + "ACCESSORY_GRAIN_SIZE": "Accessoire", + "PARAMETERS": "Paramètres du jaugeage", + "SECTION_WIDTH": "Largeur de la section", + "WIDTH": "Largeur", + "HEIGHT_BANK_WATER": "Hauteur d'eau en rive", + "HEIGHT_BANK_LEFT": "Hauteur rive gauche", + "HEIGHT_BANK_RIGHT": "Hauteur rive droite", + "ORIGIN_MEASURES": "Origine des mesures", + "BANK_LEFT": "Rive gauche", + "BANK_RIGHT": "Rive droite", + "MATERIAL_TYPE": "Type de matériel", + "MATERIAL_CHOICE": "Choix du matériel", + "TAB_MEASUREMENTS": "Saisies des mesures", + "VERTICALS": "Verticales", + "VERTICAL_NUMBER": "Verticale n°", + "DISTANCE_FROM_BANK": "Distance à la rive (m)", + "DEPTH": "Profondeur (m)", + "DURATION": "Durée (s)", + "NUMBER_OF_POINTS": "Nombre de points", + "INVALID_DISTANCE": "Distance invalide", + "MEASUREMENTS_ON_VERTICAL": "Mesures sur la verticale n°", + "DISTANCE_FROM_BOTTOM": "Distance au fond (m)", + "SPEED": "Vitesse (m/s)", + "ROTATIONS": "Rotations", + "ROTATION_NUMBER": "Tours", + "DISTANCE": "Distance", + "TAB_RESULTS": "Résultats", + "RESULTS_PER_VERTICAL": "Résultats par verticale", + "AVERAGE_SPEED": "Vitesse moyenne (m/s)", + "SURFACE": "Surface (m²)", + "FLOW": "Débit (m³/s)", + "OVERALL_GAUGING_RESULTS": "Résultats globaux du jaugeage", + "TOTAL_SURFACE": "Surface totale (m²)", + "TOTAL_FLOW": "Débit total (m³/s)", + "GRAPHICAL_REPRESENTATION": "Représentation graphique" + }, + "EQUIPMENT": { + "MATERIAL_LIST_TITLE": "Liste de Matériel", + "ADD_MATERIAL_BUTTON": "Ajouter", + "NO_MATERIAL_AVAILABLE": "Aucun matériel disponible.", + "ACTIONS": "Actions", + "NAME": "Nom", + "TYPE": "Type", + "SERIALNUMBER": "Numéro de Série", + "DESCRIPTION": "Description", + "EDIT_BUTTON": "Modifier", + "DELETE_BUTTON": "Supprimer", + "NEW_MATERIAL_TITLE": "Nouveau Matériel", + "EDIT_MATERIAL_TITLE": "Modifier Matériel", + "MATERIAL_TYPE_LABEL": "Type de Matériel", + "MATERIAL_TYPES": { + "ELECTROMAGNETIC_FLOW_METER": "Courantomètre Électromagnétique", + "PROPELLER_FLOW_METER": "Moulinet", + "VELOCITY_HEAD_ROAD": "Perche à Charge Dynamique Transparente" + }, + "MODEL_SECTION_TITLE": "Modèle", + "NAME_LABEL": "Nom", + "SERIALNUMBER_LABEL": "Numéro de Série", + "PROPELLERNUMBER_LABEL": "Numéro de Moulinet", + "PROPELLERSERIALNUMBER_LABEL": "Numéro de Série du Moulinet", + "VELOCITY_FORMULA_COUNT_LABEL": "Nombre de Formules de Vitesse", + "VELOCITY_FORMULA_TITLE_LABEL": "Formule de Vitesse n°", + "LOWER_BOUND_LABEL": "Limite Inférieure", + "UPPER_BOUND_LABEL": "Limite Supérieure", + "COEF_A_LABEL": "Coefficient A", + "CONSTANT_B_LABEL": "Constante B", + "INVALID_BOUNDS_ERROR": "Les limites spécifiées sont incorrectes. La limite inférieure doit être strictement inférieure à la limite supérieure.", + "REQUIRED_NAME_ERROR": "Le nom est requis.", + "ADDITIONAL_INFO_TITLE": "Informations complémentaires", + "DESCRIPTION_LABEL": "Description", + "SUBMIT_BUTTON": "Valider", + "CANCEL_BUTTON": "Annuler" + }, + "NEXT": "Suivant", + "PREVIOUS": "Précédent", + "NO_AVALAIBLE_MATERIAL": "Aucun matériel de ce type", + "NEED_MATERIAL": "Veuillez ajouter d'abord le matériel" +} \ No newline at end of file