ngstack / translate Goto Github PK
View Code? Open in Web Editor NEWTranslation library for Angular and Ionic applications.
License: MIT License
Translation library for Angular and Ionic applications.
License: MIT License
I've decided to drop my @ngx-translate
implementation and turn it into @ngstack/translate
implementation. Along the way, I've hit some obstacles, with @ngstack/translate
missing some basic features @ngx-translate
has. So, I suggest the following improvements:
export interface TranslateParams {
[key: string]: string;
}
should be transformed into:
export interface TranslateParams {
[key: string]: string | number;
}
get
method should translate array of strings, along with string:get(key: string, params?: TranslateParams, lang?: string): string;
should be transformed into:
get(key: string | string[], params?: TranslateParams, lang?: string): string | string[];
Keep up the good work! :D
//app.module.ts
import { TranslateModule, TranslateService } from '@ngstack/translate';
export function setupTranslateFactory(service: TranslateService): Function {
return () => service.load();
}
imports: [
TranslateModule.forRoot({
debugMode: true,
activeLang: 'en'
})
]
providers: [
{ provide: APP_INITIALIZER, useFactory: setupTranslateFactory, deps: [ TranslateService ], multi: true }
]
// app.component.ts
import { TitleService } from '@ngstack/translate';
export class AppComponent implements OnInit {
constructor(private titleService: TitleService) {}
ngOnInit() {
this.titleService.setTitle('TITLE');
}
}
//##Document Title is working fine but not in templae LOGIN
{{ 'LOGIN' | translate }}
// package.json
"@angular/core": "^8.2.1"
"@ngstack/translate": "^2.1.0"
TitleService is working, but pipe transforming is not. Just wondering am I missing anything.
Use element value as a translation key when using attribute directive:
<element translate>KEY</element>
Hi,
the version 1.2.2 is not backwards-compatible anymore due to the upgrade to Angular 8.
Provide better fallback for browser cultures. For example, when browser is set to zh-CN
and translation file is missing, the probing flow should be:
zh-CN.json
zh.json
en.json
(or fallback language of choice)Consider support for explicit support matrix in configuration/code to avoid useless HTTP calls for non-supported cultures.
Provide support for ICU MessageFormat (https://github.com/messageformat/messageformat)
Currently the fallback language is hard coded to "en"
. In my application I use locales instead of language abbreviation (ex: "en-US").
I'd like to be able to change the fallback language to "en-US" to fallback to when there isn't a match.
Maybe a new config property that can be passed to forRoot()
Something like:
TranslateModule.forRoot({
activeLang: 'en-US',
fallbackLang: 'en-US',
supportedLangs: [
// ...
]
}),
Provide the attribute directive support similar to the following:
<div [translate]="key"></div>
Should provide translation into the host element content.
Is it possible to add languages and translations after initialisation? Parts of our app are highly configurable and only accessible after a login. Therefore we need a way to download additional translations after the app has started.
To add support for storing/restoring a preferred language into/from local storage, I extended the TranslateService
like this:
import { HttpClient } from '@angular/common/http';
import { EventEmitter, Inject, Injectable } from '@angular/core';
import { Settings } from '@app/app.settings';
import { Languages } from '@app/shared/models/language.model';
import { TranslateService, TranslateSettings, TRANSLATE_SETTINGS } from '@ngstack/translate';
import { LocalStorageUtil } from '@shared/helpers/local.storage.util';
@Injectable({
providedIn: 'root'
})
export class LanguageService extends TranslateService {
static readonly Language = Settings.Prefix + 'language';
constructor(http: HttpClient, @Inject(TRANSLATE_SETTINGS) settings: TranslateSettings, private ls: LocalStorageUtil) {
super(http, settings);
}
/**
* The prefered language to use for the translations.
*/
get preferedLanguage(): string {
const defaultLanguage = Languages.find(l => l.default).i18n;
const language = this.ls.readDecompressed(LanguageService.Language);
return language || defaultLanguage;
}
/**
* The language to use for the translations.
*/
get language(): string {
return this.activeLang;
}
/**
* The language to use for the translations.
* @param language Language to use for translation
*/
set language(language: string) {
this.activeLang = language;
this.ls.saveCompressed(LanguageService.Language, language);
}
/**
* Raised each time active language gets changed.
*/
get language$(): EventEmitter<{
previousValue: string;
currentValue: string;
}> {
return this.activeLangChanged;
}
}
The core module settings are the following:
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { MatPaginatorIntl, MatSidenavModule } from '@angular/material';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterModule } from '@angular/router';
import { AppRoutingModule } from '@app/app-routing.module';
import { AvatarModule } from '@app/layout/avatar/avatar.module';
import { MainSidebarContentModule } from '@app/layout/main-sidebar-content/main-sidebar-content.module';
import { TopbarModule } from '@app/layout/topbar/topbar.module';
import { CodemirrorModule } from '@app/shared/components/codemirror/codemirror.module';
import { DataTablePaginatorIntl } from '@app/shared/components/data-table/data-table.paginator';
import { LoaderModule } from '@app/shared/components/loader/loader.module';
import { Languages } from '@app/shared/models/language.model';
import { WildcardRoutingModule } from '@app/wildcard-routing.module';
import { TranslateModule, TranslateService } from '@ngstack/translate';
import 'hammerjs';
import { LoaderInterceptor } from './interceptors/loader.interceptor';
import { TokenInterceptor } from './interceptors/token.interceptor';
// AoT requires an exported function for factories
export function setupTranslateService(service: TranslateService) {
return () => service.load();
}
@NgModule({
imports: [
// Angular modules
BrowserModule,
BrowserAnimationsModule,
RouterModule,
AppRoutingModule,
HttpClientModule,
// Material modules
MatSidenavModule,
// Translate module
TranslateModule.forRoot({
translationRoot: 'assets/i18n',
supportedLangs: Languages.map(language => language.i18n),
activeLang: Languages.find(language => language.default).i18n
}),
// Loader module
LoaderModule,
// Layout modules
TopbarModule,
MainSidebarContentModule,
AvatarModule,
// CodeMirror
CodemirrorModule,
// WILDCARD ROUTER MUST BE AT LAST POSITION
WildcardRoutingModule
],
declarations: [],
entryComponents: [],
exports: [
RouterModule,
HttpClientModule,
MatSidenavModule,
LoaderModule,
TopbarModule,
MainSidebarContentModule,
AvatarModule
],
providers: [
{
provide: APP_INITIALIZER,
useFactory: setupTranslateService,
deps: [TranslateService],
multi: true
},
{
provide: HTTP_INTERCEPTORS,
useClass: TokenInterceptor,
multi: true
},
{
provide: HTTP_INTERCEPTORS,
useClass: LoaderInterceptor,
multi: true
},
{ provide: MatPaginatorIntl, useClass: DataTablePaginatorIntl }
]
})
export class CoreModule {}
The activeLangChanged
event emitter fires on a button click, with activeLang
change, but the page doesn't get translated into selected language. Turning on the debugMode
, I can see that the active language stays default. Tracking down the network activities, I can see that language files get loaded, but the page remains translated into default language.
Am I missing something here?
Thank you very much for this project, it is very good.
I have a question, how could I use in @ngstack/translate a library, and use that library in an application that also uses @ngstack/translate?
This library can be lazy loaded or it can be loaded normally. I wish it could work for both cases.
Of course, this use case is a common when you use Monorepo in your company.
I also have the need for the library to be publishable, so it should have its own translation
Angular 8 is out there. Is Angular 8 support planned any time soon, or should I create a fork?
It would be nice to have Angular 9 support:
warning " > @ngstack/[email protected]" has incorrect peer dependency "@angular/common@^8.0.1".
warning " > @ngstack/[email protected]" has incorrect peer dependency "@angular/core@^8.0.1".
Hello,
Do you have any plans on supporting routes? I'm looking for an alternative for localize-router since it seems abandoned and has a bunch of bugs. Just wondering if I should keep an eye open for changes to this project.
Custom HttpInterceptor
not triggering with lazy loaded modules, when using TranslateModule
as stated in docs.
It's a complex project, so I cannot show the example code.
The problem with lazy loaded modules and HttpInterceptor
is that HttpClientModule
can only be imported once in the project inside app.module.ts
. Looking into this library's code, I found that HttpClientModule
is imported internally, which, I presume, could cause this problem.
The files to look for these imports are:
projects/translate/src/lib/translate.module.ts
projects/translate/src/lib/translate.pipe.spec.ts
projects/translate/src/lib/title.service.spec.ts
projects/translate/src/lib/translate.service.spec.ts
I'm using ngstack/translate in an Angular/Ionic project. Although the application works, I do get errors in the IDE (IntelliJ), telling me that the pipe "translate" in unknown.
Now I tried the new Ivy renderer and do get the same error messages:
ERROR in : Template parse errors: The pipe 'translate' could not be found ("<ion-header> <ion-toolbar> <ion-title>{{[ERROR ->]'Edit' | translate}}</ion-title> </ion-toolbar> </ion-header>
I can ignore the IDE warnings, although they are annoying. But it would be very nice to be able to use the upcoming Angular compiler.
Thanks in advance, Heiner
.forChild() expected 0 arguments.
Anyway to load another json file in lazy load module?
When I set active language:
translateService.activeLang = 'en';
How do I make translateService.activeLangChanged
trigger, whenever the active language is set, no matter if it stayed the same?
When an unsupported active lang is defined the fallback resources are not loaded and application shows resource keys instead of translation.
This is my translation file's content:
"AccessDenied": {
"Title": "Access Denied",
"Message": "Access is denied to user {name}!"
}
This works:
<div class="page">
<span class="logo"></span>
<h1>{{'AccessDenied.Title' | translate}}</h1>
<p>{{'AccessDenied.Message' | translate:{ 'name': account.name } }}</p>
</div>
This doesn't (no space after param closing curly bracket):
<div class="page">
<span class="logo"></span>
<h1>{{'AccessDenied.Title' | translate}}</h1>
<p>{{'AccessDenied.Message' | translate:{ 'name': account.name }}}</p>
</div>
Error thrown by Angular compiler:
Parser Error: Missing expected } at the end of the expression [{{'AccessDenied.Message' | translate:{ 'name': account.name }}}]
This looks like more like an Angular limitation or a bug.
Also, this should be working to, but currently, it's not (added spaces around param for readability):
"AccessDenied": {
"Title": "Access Denied",
"Message": "Access is denied to user { name }!"
}
Result:
Param name is not translated.
I presume that some trimming should be executed when parsing params.
Also, when passing param object, single quoting the param name is not necessary, as we are passing an object.
So, this:
<p>{{'AccessDenied.Message' | translate:{ 'name': account.name } }}</p>
can be written like this (no quotes):
<p>{{'AccessDenied.Message' | translate:{ name: account.name } }}</p>
When build an app for production with ng build --prod
, the following error is thrown:
ERROR in Error during template compile of 'AppModule' Function calls are not supported in decorators but 'TranslateModule' was called.
Reason is the forRoot()
call in the declaration of the main module.
Will this package continue to be updated?
I use three languages: en
, sr-Latn
and hr
, with en
being the default one.
When I switch to any language other than the default one, the translations are loaded nicely. But, then, when I switch to the third, non-default language, the translations show the default language translations briefly, until the new language file is loaded.
So en
=> sr-Latn
=> OK
So en
=> hr
=> OK
So en
=> sr-Latn
=> hr
=> Glitch
So en
=> hr
=> sr-Latn
=> Glitch
Use current browser language at runtime
Is it possible to split a single language translation file into multiple .json files (per module or component)?
Single language file can get really huge for big apps and difficult to maintain.
Setting .json file to be loaded dynamically in routes would be an extra feature.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.