From 63ac6df2ab8ff60973fdc2f5d0cb35bb24326ee1 Mon Sep 17 00:00:00 2001 From: George Kalampokis Date: Thu, 23 Jan 2020 18:35:11 +0200 Subject: [PATCH] Vastly improved Internationalization support (ref #228) --- .../eudat/controllers/LanguageController.java | 45 + .../resources/application-devel.properties | 2 + dmp-frontend/src/app/app.component.ts | 5 +- dmp-frontend/src/app/app.module.ts | 5 +- .../src/app/core/core-service.module.ts | 4 +- .../services/language/language.service.ts | 22 + .../core/services/language/server.loader.ts | 15 + .../auth/admin-login/admin-login.component.ts | 12 +- .../ui/auth/login/utilities/login.service.ts | 12 +- .../src/app/ui/navbar/navbar.component.html | 25 + .../src/app/ui/navbar/navbar.component.ts | 16 +- .../ui/user-profile/user-profile.component.ts | 7 +- dmp-frontend/src/assets/i18n/es.json | 1214 +++++++++++++++++ dmp-frontend/src/assets/i18n/gr.json | 5 +- .../src/assets/resources/language.json | 4 + 15 files changed, 1377 insertions(+), 16 deletions(-) create mode 100644 dmp-backend/web/src/main/java/eu/eudat/controllers/LanguageController.java create mode 100644 dmp-frontend/src/app/core/services/language/language.service.ts create mode 100644 dmp-frontend/src/app/core/services/language/server.loader.ts create mode 100644 dmp-frontend/src/assets/i18n/es.json diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/LanguageController.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/LanguageController.java new file mode 100644 index 000000000..0abc43925 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/LanguageController.java @@ -0,0 +1,45 @@ +package eu.eudat.controllers; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.io.InputStream; + +@RestController +@CrossOrigin +@RequestMapping(value = {"/api/language/"}) +public class LanguageController { + + Environment environment; + + @Autowired + public LanguageController(Environment environment) { + this.environment = environment; + } + + @RequestMapping(value = "{lang}", method = RequestMethod.GET) + public ResponseEntity getLanguage(@PathVariable String lang) throws IOException { + + String fileName = this.environment.getProperty("language.path") + lang + ".json"; + InputStream is = getClass().getClassLoader().getResource(fileName).openStream(); + + HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.setContentLength(is.available()); + responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM); + responseHeaders.set("Content-Disposition", "attachment;filename=" + fileName); + responseHeaders.set("Access-Control-Expose-Headers", "Content-Disposition"); + responseHeaders.get("Access-Control-Expose-Headers").add("Content-Type"); + + byte[] content = new byte[is.available()]; + is.read(content); + is.close(); + + return new ResponseEntity<>(content, responseHeaders, HttpStatus.OK); + } +} diff --git a/dmp-backend/web/src/main/resources/application-devel.properties b/dmp-backend/web/src/main/resources/application-devel.properties index e1c0f7883..581a79c7e 100644 --- a/dmp-backend/web/src/main/resources/application-devel.properties +++ b/dmp-backend/web/src/main/resources/application-devel.properties @@ -67,3 +67,5 @@ zenodo.access_token= #############CONTACT EMAIL CONFIGURATIONS######### contact_email.mail= + +language.path=/tempLang/i18n/ \ No newline at end of file diff --git a/dmp-frontend/src/app/app.component.ts b/dmp-frontend/src/app/app.component.ts index 013a2c0f5..3dceca965 100644 --- a/dmp-frontend/src/app/app.component.ts +++ b/dmp-frontend/src/app/app.component.ts @@ -12,6 +12,7 @@ import { BreadCrumbResolverService } from './ui/misc/breadcrumb/service/breadcru import { Title } from '@angular/platform-browser'; import { NgcCookieConsentService, NgcStatusChangeEvent } from "ngx-cookieconsent"; import { CookieService } from "ngx-cookie-service"; +import { LanguageService } from './core/services/language/language.service'; declare const gapi: any; @@ -38,7 +39,8 @@ export class AppComponent implements OnInit { private titleService: Title, private cultureService: CultureService, private cookieService: CookieService, - private ccService: NgcCookieConsentService + private ccService: NgcCookieConsentService, + private language: LanguageService ) { this.initializeServices(); } @@ -145,6 +147,7 @@ export class AppComponent implements OnInit { initializeServices() { this.translate.setDefaultLang('en'); this.authentication.current() && this.authentication.current().culture ? this.cultureService.cultureSelected(this.authentication.current().culture) : this.cultureService.cultureSelected(environment.defaultCulture); + this.authentication.current() && this.authentication.current().language ? this.language.changeLanguage(this.authentication.current().language) : this.language.changeLanguage('en'); } } diff --git a/dmp-frontend/src/app/app.module.ts b/dmp-frontend/src/app/app.module.ts index 5f0014ec1..a480eeb31 100644 --- a/dmp-frontend/src/app/app.module.ts +++ b/dmp-frontend/src/app/app.module.ts @@ -27,10 +27,13 @@ import { TranslateHttpLoader } from '@ngx-translate/http-loader'; import { environment } from 'environments/environment'; import { CookieService } from 'ngx-cookie-service'; import { NgcCookieConsentConfig, NgcCookieConsentModule } from 'ngx-cookieconsent'; +import { TranslateServerLoader } from './core/services/language/server.loader'; +import { BaseHttpService } from './core/services/http/base-http.service'; // AoT requires an exported function for factories export function HttpLoaderFactory(http: HttpClient) { - return new TranslateHttpLoader(http, 'assets/i18n/', '.json'); + return new TranslateServerLoader(http); + //return new TranslateHttpLoader(http, 'assets/i18n/', '.json'); } const cookieConfig: NgcCookieConsentConfig = { diff --git a/dmp-frontend/src/app/core/core-service.module.ts b/dmp-frontend/src/app/core/core-service.module.ts index 285f3c347..2c397567f 100644 --- a/dmp-frontend/src/app/core/core-service.module.ts +++ b/dmp-frontend/src/app/core/core-service.module.ts @@ -35,6 +35,7 @@ import { OrganisationService } from './services/organisation/organisation.servic import { EmailConfirmationService } from './services/email-confirmation/email-confirmation.service'; import { FunderService } from './services/funder/funder.service'; import { ContactSupportService } from './services/contact-support/contact-support.service'; +import { LanguageService } from './services/language/language.service'; // // // This is shared module that provides all the services. Its imported only once on the AppModule. @@ -89,7 +90,8 @@ export class CoreServiceModule { QuickWizardService, OrganisationService, EmailConfirmationService, - ContactSupportService + ContactSupportService, + LanguageService ], }; } diff --git a/dmp-frontend/src/app/core/services/language/language.service.ts b/dmp-frontend/src/app/core/services/language/language.service.ts new file mode 100644 index 000000000..3b1054f4d --- /dev/null +++ b/dmp-frontend/src/app/core/services/language/language.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { isNullOrUndefined } from 'util'; + +@Injectable() +export class LanguageService { + private currentLanguage: string = 'en'; + + constructor( + private translate: TranslateService + ) {} + + public changeLanguage(lang: string) { + this.currentLanguage = lang; + this.translate.use(lang); + } + + public getCurrentLanguage() { + return this.currentLanguage; + } + +} diff --git a/dmp-frontend/src/app/core/services/language/server.loader.ts b/dmp-frontend/src/app/core/services/language/server.loader.ts new file mode 100644 index 000000000..a9030e633 --- /dev/null +++ b/dmp-frontend/src/app/core/services/language/server.loader.ts @@ -0,0 +1,15 @@ +import { TranslateLoader } from '@ngx-translate/core'; +import { Observable } from 'rxjs'; +import { environment } from 'environments/environment'; +import { HttpClient } from '@angular/common/http'; + +export class TranslateServerLoader implements TranslateLoader{ + private languageUrl = `${environment.Server}language`; + + constructor( + private http: HttpClient + ) {} + getTranslation(lang: string): Observable { + return this.http.get(`${this.languageUrl}/${lang}`); + } +} diff --git a/dmp-frontend/src/app/ui/auth/admin-login/admin-login.component.ts b/dmp-frontend/src/app/ui/auth/admin-login/admin-login.component.ts index 589b8a41c..3619d9897 100644 --- a/dmp-frontend/src/app/ui/auth/admin-login/admin-login.component.ts +++ b/dmp-frontend/src/app/ui/auth/admin-login/admin-login.component.ts @@ -7,6 +7,8 @@ import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/serv import { BaseComponent } from '@common/base/base.component'; import { TranslateService } from '@ngx-translate/core'; import { takeUntil } from 'rxjs/operators'; +import { TranslateServerLoader } from '@app/core/services/language/server.loader'; +import { LanguageService } from '@app/core/services/language/language.service'; @Component({ selector: 'app-admin-login', templateUrl: './admin-login.component.html', @@ -24,9 +26,10 @@ export class AdminLoginComponent extends BaseComponent implements OnInit { constructor( private authService: AuthService, private uiNotificationService: UiNotificationService, - private language: TranslateService, + private translate: TranslateService, private cultureService: CultureService, - private router: Router + private router: Router, + private language: LanguageService ) { super(); } @@ -48,12 +51,13 @@ export class AdminLoginComponent extends BaseComponent implements OnInit { } public onLogInSuccess(loginResponse: any) { - this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-LOGIN'), SnackBarNotificationLevel.Success); + this.uiNotificationService.snackBarNotification(this.translate.instant('GENERAL.SNACK-BAR.SUCCESSFUL-LOGIN'), SnackBarNotificationLevel.Success); if (this.authService.current().culture) { this.cultureService.cultureSelected(this.authService.current().culture); } + if (this.authService.current().language) { this.language.changeLanguage(this.authService.current().language); } this.router.navigate(['/']); } public onLogInError(errorMessage: string) { - this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-LOGIN'), SnackBarNotificationLevel.Error); + this.uiNotificationService.snackBarNotification(this.translate.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-LOGIN'), SnackBarNotificationLevel.Error); } } diff --git a/dmp-frontend/src/app/ui/auth/login/utilities/login.service.ts b/dmp-frontend/src/app/ui/auth/login/utilities/login.service.ts index 121f54bb1..9e66b278e 100644 --- a/dmp-frontend/src/app/ui/auth/login/utilities/login.service.ts +++ b/dmp-frontend/src/app/ui/auth/login/utilities/login.service.ts @@ -5,6 +5,8 @@ import { CultureService } from '@app/core/services/culture/culture-service'; import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; import { BaseService } from '@common/base/base.service'; import { TranslateService } from '@ngx-translate/core'; +import { TranslateServerLoader } from '@app/core/services/language/server.loader'; +import { LanguageService } from '@app/core/services/language/language.service'; @Injectable() export class LoginService extends BaseService { @@ -12,10 +14,11 @@ export class LoginService extends BaseService { constructor( private router: Router, private authService: AuthService, - private language: TranslateService, + private translate: TranslateService, private zone: NgZone, private cultureService: CultureService, - private uiNotificationService: UiNotificationService + private uiNotificationService: UiNotificationService, + private language: LanguageService ) { super(); } @@ -44,14 +47,15 @@ export class LoginService extends BaseService { public onLogInSuccess(loginResponse: any, returnUrl: string) { this.zone.run(() => { - this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-LOGIN'), SnackBarNotificationLevel.Success); + this.uiNotificationService.snackBarNotification(this.translate.instant('GENERAL.SNACK-BAR.SUCCESSFUL-LOGIN'), SnackBarNotificationLevel.Success); if (this.authService.current().culture) { this.cultureService.cultureSelected(this.authService.current().culture); } + if (this.authService.current().language) { this.language.changeLanguage(this.authService.current().language); } const redirectUrl = returnUrl || '/'; this.router.navigate([redirectUrl]); }); } public onLogInError(errorMessage: string) { - this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-LOGIN'), SnackBarNotificationLevel.Error); + this.uiNotificationService.snackBarNotification(this.translate.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-LOGIN'), SnackBarNotificationLevel.Error); } } diff --git a/dmp-frontend/src/app/ui/navbar/navbar.component.html b/dmp-frontend/src/app/ui/navbar/navbar.component.html index 023476217..e4c998706 100644 --- a/dmp-frontend/src/app/ui/navbar/navbar.component.html +++ b/dmp-frontend/src/app/ui/navbar/navbar.component.html @@ -22,6 +22,31 @@ --> + + +
+ + + + +
+