I have an Angular2 application and it need to support multiple languages. Some of these languages may be RTL (i.e Persian) and some will be LTR (i.e. English). Apart from lo
based on @marbug 's answer I would try to use a service and dynamically load and unload your "language" components.
that will work as follows
import { Injectable, ViewChild, ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
import { LanguageAComponent } from "./language-a.component";
import { LanguageBComponent } from "./language-b.component";
import { Router } from '@angular/router';
@Injectable()
export class LanguageComponentService {
componentRef: any;
constructor(private componentFactoryResolver: ComponentFactoryResolver, private router: Router
) { }
loadLanguageA(viewContainerRef: ViewContainerRef) {
let componentFactory = this.componentFactoryResolver.resolveComponentFactory(LanguageAComponent);
this.componentRef = viewContainerRef.createComponent(componentFactory);
}
loadLanguageB(viewContainerRef: ViewContainerRef) {
let componentFactory = this.componentFactoryResolver.resolveComponentFactory(LanguageBComponent);
this.componentRef = viewContainerRef.createComponent(componentFactory);
}
unloadComponent() {
this.componentRef.destroy();
}
}
Maybe you could conditionally add a class to <body>
based on the direction of the user's language? For example, <body ng-class="{'dir-ltr': ltr, 'dir-rtl': !ltr}">
. Then of course you would have to wrap all your sass styles in .dir-ltr
and .dir-rtl
, respectively, so that the rest of the DOM would use the correct set of styles. This assumes that the language direction is resolved when the state loads, and also that <body>
is within the scope of your angular app.
You'd need to conditionally add or remove the style from the browser:
to add
loadIfNot(url,cssId){
if (!document.getElementById(cssId)){
var head = document.getElementsByTagName('head')[0];
var link = document.createElement('link');
link.id = cssId;
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = url;
link.media = 'all';
head.appendChild(link);
}else{
document.getElementById(cssId).disabled = false;///i fit's already there, enable it
}
}
and for remove
disable(cssId){
document.getElementById(cssId).disabled = true
}
Disable
and Enable
is because browser tends to cache your styles, and in order to make them enable or disable, you'd change that attribute
1) I would suggest you to use lazy loaded modules instead of dynamic css files (please take a look at official Angular docs for more info)
2) I would also suggest to put current language to some service, inject service into the appropriate component and to use needed css class. Advantages:
2.1) component style does not depend on body element - just on value in service - this is more good/correct for unit testing
2.2) there could be usecases when it's not enough to use LTR vs RTL classes, but to use some additional classes for particular languages (for instance, table columns could have different width for English, German, etc languages)
3) loading css dynamically could cause UI issues: initial styles are applied first and then they 'jump' to "other" styles (when the appropriate css file is loaded). So 1) & 2) are much better (IMHO indeed)
UPDATE 2017-06-07 (just for being more clear)
app-routing.module could be like:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
export const routes: Routes = [
{ path: '', redirectTo: '/home', pathMatch: 'full'},
{
path: 'home',
loadChildren: 'app/my-home-page/my-home-page.module#MyHomePageModule'
},
...
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
my-global.service could be like:
@Injectable()
export class MyGlobalService {
currentLanguageDirection: string; // i.e. 'ltr' or 'rtl'
currentLanguage: string; // i.e. 'en', 'de', 'ru', etc
}
my-home-page.component.ts could be:
export class MyHomePageComponent {
...
constructor(
private myGlobalService: MyGlobalService, // inject global service
...
) {
}
...
}
my-home-page.component.html could be:
<div [ngSwitch]="myGlobalService.currentLanguageDirection">
<my-home-page-rtl *ngSwitchCase="'rtl'"></my-home-page-rtl>
<my-home-page-ltr *ngSwitchDefault></my-home-page-ltr>
</div>
I.e. two additional components could be used: my-home-page-ltr and my-home-page-rtl. They will have separate scss|css files.
If you need unique implementation for German then my-home-page-ltr.component.html could be:
<div [ngSwitch]="myGlobalService.currentLanguage">
<my-home-page-ltr-de *ngSwitchCase="'de'"></my-home-page-ltr-de>
<div *ngSwitchDefault>
... (default html code)
</div>
</div>
I.e. a separate my-home-page-ltr-de component will have separate scss|css for German.
P.S. Please note that you could reuse html files by setting them in @Component:
@Component({
selector: 'my-home-page...',
templateUrl: './my-home-page....html', // <-------------- reuse here
styleUrls: ['./my-home-page....scss']
})
P.P.S. I would recommend not to use manually creation/deletion of components. Usage of ngSwitch or ngIf is a more correct way (IMHO)
P.P.P.S. Advantages are the following. Component and its children should be loaded/released by angular automatically. Language switching will be performed very fast because current component is loaded with its children - you do not need to load styles of all components during language switching.