Closed codehan-de closed 4 years ago
What you want to do is to add new translation sources during runtime, right?
This is not really supported by translate loader / multitranslate loader.
But maybe you can try this way:
Do not create new languages for each of your business cases like "de_riego", "de_ceres". This should not be needed.
Instead when changing a business case use the API to change translation source configuration:
function businessCaseChanged(newBC) {
this.translate.currentLoader.resources = [
{prefix: './assets/i18n/default/', suffix: '.json'},
{prefix: './assets/i18n/bc/' + newBC, suffix: '.json'},
];
// This will reset currently loaded translations. Maybe you also need to trigger reload of new translations.
this.translate.resetLang('de');
this.translate.resetLang('en');
}
resources
is a private property of the MultiTranslateLoader so this is a bit hacky but it should work.
See MultitranslateLoader source here.
You will probably now reload not only new bc translations but also the default translation files, so this is not optimal.
Hi.
First of all thank you for your quick reply.
Well, with my app, a business case is already given directly after the login. If the user uses the navbar to get into specific areas, this business case may change as well.
If I shouldn't create a separate file for each business case, I do not understand how a distinction should be made here.
For example, in assets /i18n/default I have a de.json that contains, for example, the following contents:
{
"Hello": "Hello",
"World": "World"
}
For example, in assets /i18n/bc, there is a de_riego.json with the following content:
{
"World": "Erde"
}
From this the following should be taken for the translation:
{
"Hello": "Hello",
"World": "Erde"
}
In my current version, however, only one file (de_riego.json) is taken, so the other translations are missing and the browser uses the default language.
In addition, I have the mentioned error messages in the browser, because the loader also looks under assets /i18n/bc for the default files (de.json, en.json, ..) and of course doesn't find them. Do the files have to have the same name to be merged correctly?
I will try your approach tomorrow. But doesn't the function have to be an exported function? And why exactly should I use this.translate.resetLang('de') to reset the currently loaded translations?
I have put my suggestion into the stackblitz here: https://stackblitz.com/edit/ngx-translate-multi-http-loader-sample-yss2uc
Does this work for you?
It is not optimal because translation files are reloaded. If you want a better version you will need to write your custom TranslateLoader.
I've got it, but errors are still being thrown in the console because it still unnecessarily searches for the specific files in the default folder.
My version worked by simply deleting the translate.setDefaultLang(config.defaultLanguage) line and swapping the remaining lines from the constructor to the ngOnInit lifecyle method.
Is there perhaps a simple way to correct the error messages or just ignore them?
I don't think that it is possible to correct the messages because your case is not really supported by this plugin. I would suggest you go with the solution in the stackblitz I posted. This won't lead to error messages.
I have already tried, unfortunately, your version does not work for me. The business cases are placed in many places of the application, for this reason probably. Currently everything works, but of course the error messages are annoying.
This is my current AppComponent (it's working like that, but only with error messages):
import { Component, HostListener, OnDestroy, OnInit } from '@angular/core';
import { UserAuthService } from './auth/user-auth.service';
import { SettingsService } from './auth/settings.service';
import { TranslateService, LangChangeEvent } from '@ngx-translate/core';
import { ConfigurationService } from './config/configuration.service';
import * as moment from 'moment';
import { Title } from '@angular/platform-browser';
import { environment } from '../environments/environment';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { MultiTranslateHttpLoader } from 'ngx-translate-multi-http-loader';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnDestroy, OnInit {
// Needed for unsubscribing
private destroy$ = new Subject<void>();
constructor(private translate: TranslateService,
private config: ConfigurationService,
private settings: SettingsService,
private authService: UserAuthService,
private titleService: Title) {
}
@HostListener('window:focus')
onFocus() {
this.authService.checkAutoLogout();
}
ngOnInit() {
this.translate.addLangs(Object.keys(this.config.languages));
this.initLanguageBC();
this.initTabName();
}
// Subscribe to the observable getAutoConfiguredBCLanguage$ to retrieve the selected language
initLanguageBC() {
const userDefinedLanguage = this.settings.language;
if (userDefinedLanguage) {
// Use the selected language by the user
this.translate.use(userDefinedLanguage);
} else {
// Use configured language
this.config.getAutoConfiguredBCLanguage$()
.pipe(takeUntil(this.destroy$))
.subscribe(lang => {
this.translate.use(lang);
});
}
// Listen to translation change events
this.translate.onLangChange.subscribe((langEvent: LangChangeEvent) => {
console.log('set global language: ' + langEvent.lang);
// Set locale and get specific locale settings
moment.locale(langEvent.lang, this.getCustomMomentLocaleSettings(langEvent.lang));
});
}
getCustomMomentLocaleSettings(lang) {
if (lang === 'en') {
return {
relativeTime: {
future: 'in %s',
past: '%s ago',
s: '1 second',
ss: '%d seconds',
m: '1 minute',
mm: '%d minutes',
h: '1 hour',
hh: '%d hours',
d: '1 day',
dd: '%d days',
M: '1 month',
MM: '%d months',
y: '1 year',
yy: '%d years'
}
};
}
if (lang === 'de') {
return {
relativeTime: {
future: 'in %s',
past: 'vor %s',
s: '1 Sekunde',
ss: '%d Sekunden',
m: '1 Minute',
mm: '%d Minuten',
h: '1 Stunde',
hh: '%d Stunden',
d: '1 Tag',
dd: '%d Tage',
M: '1 Monat',
MM: '%d Monate',
y: '1 Jahr',
yy: '%d Jahre'
}
};
}
return undefined;
}
initTabName() {
if (environment.default_business_case === 'DEMO') {
this.titleService.setTitle('SmAg');
} else {
this.titleService.setTitle(environment.default_business_case);
}
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
export function multiTranslateHttpLoaderFactory(http: HttpClient, authService: UserAuthService) {
return new MultiTranslateHttpLoader(http, [
{prefix: './assets/i18n/default/', suffix: '.json'},
{prefix: './assets/i18n/bc/', suffix: '.json'},
]);
}
And this is the current configurations.service:
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import 'moment/locale/de';
import { UserAuthService } from 'app/auth/user-auth.service';
import { Observable } from 'rxjs';
import { map, distinctUntilChanged } from 'rxjs/operators';
@Injectable()
export class ConfigurationService {
defaultLanguage = 'en';
languages = {
en: 'English',
de: 'German',
es: 'Spanish',
ja: 'Japanese',
pt: 'Portuguese',
};
// Business cases with available translation files
businessCases = {
riego: '_riego',
ceres: '_ceres',
milk: '_milk',
asparagus: '_asparagus',
traci: '_traci',
smag: '_smag',
demo: '_demo'
};
constructor(
private translate: TranslateService,
private authService: UserAuthService
) {}
// Will emit a new language every time the activeBusinessCase changes
getAutoConfiguredBCLanguage$(): Observable<string> {
return this.authService.activeBusinessCase$.pipe(
// using 'distinctUntilChanged' to only emit values that are different from the previous
distinctUntilChanged(),
map(bc => this.getAutoConfiguredBCLanguage(bc))
);
}
// Gets the currently activeBusinessCase as an input parameter
getAutoConfiguredBCLanguage(activeBusinessCase?: string) {
const browserLang = this.translate.getBrowserLang();
console.log('BROWSER LANGUAGE: ', browserLang);
console.log('ACTIVE BUSINESS CASE: ', activeBusinessCase);
if (this.languages.hasOwnProperty(browserLang)) {
switch (activeBusinessCase) {
case 'RIEGO':
return browserLang.concat(this.businessCases.riego);
case 'CERES':
return browserLang.concat(this.businessCases.ceres);
case 'MILK':
return browserLang.concat(this.businessCases.milk);
case 'ASPARAGUS':
return browserLang.concat(this.businessCases.asparagus);
case 'TRACI':
return browserLang.concat(this.businessCases.traci);
case 'SMAG':
return browserLang.concat(this.businessCases.smag);
case 'DEMO':
return browserLang.concat(this.businessCases.demo);
default:
return browserLang;
}
} else {
return this.defaultLanguage;
}
}
}
And these are the relevant parts of my authService:
@Injectable()
export class UserAuthService {
public activeBusinessCase$ = new BehaviorSubject<string>(null);
constructor(private router: Router,
private httpClient: HttpClient,
private pls: PathLocationStrategy,
private titleService: Title) {
}
login(name: string, password: string, imTid: string): Observable<UiInfo> {
return this.loginWithBackend(name, password, imTid).pipe(
tap(() => {
this.user.user_name = translate('default-user');
// TODO: Check if loggedoff is obsolete?
if (this.loggedOff) {
this.pls.back();
} else if (this.redirectUrl) {
this.router.navigate([this.redirectUrl]);
this.redirectUrl = null;
console.log(Constants.texts.loginSuccessRedirect);
} else {
const bc = this.activeBusinessCase$.getValue();
if (bc) {
this.router.navigate([Constants.routing.explorer + bc.toLowerCase()]);
} else {
const err = new LoginError('Business case is missing');
throw err;
}
}
this.loggedOff = false;
}));
}
setDefaultBusinessCaseIfNotExisting() {
if (this.businessCases.length === 0) {
this.businessCases.push(environment.default_business_case);
}
this.setActiveBusinessCase(this.businessCases[0]);
}
setActiveBusinessCase(bcase: string) {
this.activeBusinessCase$.next(bcase); // Emit the value
this.setTitle(bcase);
}
I use this extension to create business case-specific translations based on the current logged-in business case.
But I currently have the problem that the extension tries to load unnecessary files and throws a corresponding error message in the console.
Here is an example of my implementation:
My factory
The logic inside my AppComponent:
The configuration of the language based on the currently logged in business case in my configuration service:
The translations are working correctly, but the console sais:
or
And this happens, because the loader is searching for the specific files in the default folder, where only the default json files are placed (de.json, en.js, etc.).
The specific files are located in the bc folder (de_riego.json, de_ceres.json etc.)
Conversely, the loader also searches for the default files (de.json, en.json etc.) in the bc folder, although only the specific JSONs are stored here. This causes these error messages:
and
I also noticed that the files are not correctly merged. It is always a default file to be taken (eg de.json) and an additional specific file (eg de_riego.json). Currently, however, only the de_riego.json is taken and the contents of de.json are getting ignored, so that the browser automatically selects english.
What am I doing wrong here? Hope someone can help me..