Vastly improved Internationalization support (ref #228)

This commit is contained in:
George Kalampokis 2020-01-23 18:35:11 +02:00
parent 88f0d80b1e
commit 63ac6df2ab
15 changed files with 1377 additions and 16 deletions

View File

@ -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);
}
}

View File

@ -67,3 +67,5 @@ zenodo.access_token=
#############CONTACT EMAIL CONFIGURATIONS#########
contact_email.mail=
language.path=/tempLang/i18n/

View File

@ -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');
}
}

View File

@ -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 = {

View File

@ -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
],
};
}

View File

@ -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;
}
}

View File

@ -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<any> {
return this.http.get(`${this.languageUrl}/${lang}`);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -22,6 +22,31 @@
</div>
</form> -->
<!-- Language
<div class="col-auto">
<button mat-icon-button [matMenuTriggerFor]="languageMenu">
<mat-icon>language</mat-icon>
</button>
<mat-menu #languageMenu="matMenu">
<button mat-menu-item
(click)="onLanguageSelected(languageTypes.English)">{{'NAVIGATION.ENGLISH-LANGUAGE' | translate}}</button>
<button mat-menu-item
(click)="onLanguageSelected(languageTypes.Greek)">{{'NAVIGATION.GREEK-LANGUAGE' | translate}}</button>
</mat-menu>
</div> -->
<div class="col-md-auto" *ngIf="!isAuthenticated()">
<button mat-icon-button [matMenuTriggerFor]="languageMenu">
<mat-icon>language</mat-icon>
</button>
<mat-menu #languageMenu="matMenu">
<button mat-menu-item *ngFor="let lang of languages" (click)="onLanguageSelected(lang)">
{{ lang.label }}
</button>
</mat-menu>
</div>
<app-search></app-search>
<ul class="navbar-nav">

View File

@ -9,6 +9,11 @@ import { BaseComponent } from '@common/base/base.component';
import { takeUntil } from 'rxjs/operators';
import { UserDialogComponent } from '../misc/navigation/user-dialog/user-dialog.component';
import { DATASETS_ROUTES, DMP_ROUTES, GENERAL_ROUTES } from '../sidebar/sidebar.component';
import { TranslateService } from '@ngx-translate/core';
import { Language } from '@app/models/language/Language';
import { LanguageService } from '@app/core/services/language/language.service';
const availableLanguages: any[] = require('../../../assets/resources/language.json');
@Component({
selector: 'app-navbar',
@ -22,13 +27,17 @@ export class NavbarComponent extends BaseComponent implements OnInit {
mobile_menu_visible: any = 0;
private toggleButton: any;
private sidebarVisible: boolean;
languages = availableLanguages;
language = this.languages[0];
constructor(location: Location,
private element: ElementRef,
private router: Router,
private authentication: AuthService,
private dialog: MatDialog,
private progressIndicationService: ProgressIndicationService
private progressIndicationService: ProgressIndicationService,
private languageService: LanguageService
) {
super();
this.location = location;
@ -182,4 +191,9 @@ export class NavbarComponent extends BaseComponent implements OnInit {
position: { top: '64px', right: '1em' }
});
}
onLanguageSelected(selectedLanguage: any) {
this.languageService.changeLanguage(selectedLanguage.value);
this.router.navigate([this.router.url]);
}
}

View File

@ -12,6 +12,7 @@ import { TranslateService } from '@ngx-translate/core';
import * as moment from 'moment-timezone';
import { Observable, of } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import { LanguageService } from '@app/core/services/language/language.service';
const availableLanguages: any[] = require('../../../assets/resources/language.json');
@ -37,8 +38,8 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes
private authService: AuthService,
private language: TranslateService,
private cultureService: CultureService,
private translate: TranslateService,
private authentication: AuthService
private authentication: AuthService,
private languageService: LanguageService
) { super(); }
ngOnInit() {
@ -126,7 +127,7 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes
.subscribe(
x => {
this.editMode = false;
this.translate.use(this.formGroup.value.language);
this.languageService.changeLanguage(this.formGroup.value.language.value);
this.authService.current().culture = this.formGroup.value.culture.name;
this.formGroup.disable();
this.authService.me()

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,10 @@
"GENERAL": {
"VALIDATION": {
"REQUIRED": "Required"
}
},
"ACTIONS": {
"LOG-IN": "Είσοδος"
}
},
"USER-PROFILE": {
"SETTINGS": {

View File

@ -6,5 +6,9 @@
{
"label": "Greek",
"value": "gr"
},
{
"label": "Spanish",
"value": "es"
}
]