@ngx-i18n-router/core
v5.0.0
Published
Route internationalization utility for Angular
Downloads
29
Maintainers
Readme
@ngx-i18n-router/core
Route internationalization utility for Angular
Please support this project by simply putting a Github star. Share this library with friends on Twitter and everywhere else you can.
@ngx-i18n-router/core
translates each path
and redirectTo
property of routes, during Angular app initialization
and also during runtime - when the working language gets changed.
NOTICE
This 5.x.x branch is intented to work with
@angular v5.x.x
. If you're developing on a later release of Angular thanv5.x.x
, then you should probably choose the appropriate version of this library by visiting the master branch.
Also, please check the Workaround for '@ngtools/webpack' section if your app depends on @angular/cli or
@ngtools/webpack
for AoT compilation.
Table of contents:
- Prerequisites
- Getting started
- Settings
- Change language (runtime)
- Pipe
- Workaround for '@ngtools/webpack'
- Credits
- License
Prerequisites
This library depends on Angular v4.0.0
. Older versions contain outdated dependencies, might produce errors.
Also, please ensure that you are using Typescript v2.5.3
or higher.
Getting started
Installation
You can install @ngx-i18n-router/core
using npm
npm install @ngx-i18n-router/core --save
Examples
- ng-seed/universal and fulls1z3/example-app are officially maintained projects, showcasing common patterns and best
practices for
@ngx-i18n-router/core
.
Related packages
The following packages may be used in conjunction with @ngx-i18n-router/core
- [@ngx-i18n-router/http-loader]
- @ngx-i18n-router/config-loader
Recommended packages
The following package(s) have no dependency for @ngx-i18n-router/core
, however may provide supplementary/shorthand
functionality:
- @ngx-config/core: provides route translations from the application settings loaded during application initialization
- @ngx-cache/core: provides caching features to retrieve the route translations using
non-static loaders
(http
,fs
, etc.)
Adding @ngx-i18n-router/core
to your project (SystemJS)
Add map
for @ngx-i18n-router/core
in your systemjs.config
'@ngx-i18n-router/core': 'node_modules/@ngx-i18n-router/core/bundles/core.umd.min.js'
### Route configuration
In order to use @ngx-i18n-router/core
properly, you should have more or less a similar route structure as follows:
app.routes.ts
export const routes: Routes = [
{
path: '',
children: [
{
path: '',
loadChildren: './+home/home.module#HomeModule'
},
{
path: 'about',
loadChildren: './+about/about.module#AboutModule'
},
...
],
data: {
i18n: {
isRoot: true
}
}
},
{
path: 'change-language/:languageCode',
component: ChangeLanguageComponent
},
...
{
path: '**',
redirectTo: '',
pathMatch: 'full'
}
];
I18N-ROOT
The route configuration above shows that, one of the routes contains i18n
property inside the data
property.
When its value is set to true
, @ngx-i18n-router/core
will prepend descendants of this route with the 2-letter
language code (ex: en
/fr
/de
/nl
/tr
).
We call this route, with
data
property containingi18n\isRoot
set totrue
,I18N-ROOT
of Angular application.
(English)
http://mysite.com/about -> http://mysite.com/en/about
Note: There must be a maximum of one i18n\isRoot
in the route configuration, and (if exists) this must be placed
inside the data
property of any of the first-level routes.
Note: It is always a good practice to have exactly one route (and component) in the Home
feature module, with
a path set to ''
(see home.routes.ts
in this readme).
Non-prefixed routes
Routes outside I18N-ROOT
scope will NOT have this 2-letter language code prefix. It allows the Angular
application to support both prefixed
and non-prefixed
routes.
(English)
http://mysite.com/about -> http://mysite.com/en/about
http://mysite.com/sitemap
http://mysite.com/admin
Catchall route
There must be a catchall route in the route configuration, redirecting to the I18N-ROOT
of the app (here, redirects
to ''
). The redirectTo
property will be reset to the 2-letter language code by @ngx-i18n-router/core
.
app.module configuration
Import I18NRouterModule
using the mapping '@ngx-i18n-router/core'
and append I18NRouterModule.forRoot(routes, {...})
within the imports property of app.module (considering the app.module is the core module in Angular application).
Also, don't forget to provide I18N_ROUTER_PROVIDERS
within the providers property of app.module.
Note: I18N_ROUTER_PROVIDERS
must be imported in the app.module (instead of in I18NRouterModule
), to resolve
the I18NRouterService
dependency at the uppermost level (otherwise child modules will have different instances of
I18NRouterService
).
app.module.ts
...
import { I18NRouterModule, I18N_ROUTER_PROVIDERS } from '@ngx-i18n-router/core';
...
@NgModule({
declarations: [
AppComponent,
...
],
...
imports: [
RouterModule.forRoot(routes),
I18NRouterModule.forRoot(routes),
...
],
...
providers: [
I18N_ROUTER_PROVIDERS,
...
],
...
bootstrap: [AppComponent]
})
Feature modules configuration
Import I18NRouterModule
using the mapping '@ngx-i18n-router/core'
and append I18NRouterModule.forChild(routes, moduleKey)
within the imports property of the feature module. The moduleKey
parameter for the forChild
method obviously refers
to the module's root path (in kebab-case).
home.routes.ts
export const routes: Routes = [
{
path: '',
component: HomeComponent
}
];
home.module.ts
...
import { I18NRouterModule } from '@ngx-i18n-router/core';
...
@NgModule({
...
imports: [
//RouterModule.forChild(routes),
I18NRouterModule.forChild(routes, 'home'),
...
],
...
})
about.routes.ts
export const routes: Routes = [
{
path: '',
component: AboutComponent
},
{
path: 'us/:topicId',
component: AboutUsComponent
},
{
path: 'banana',
component: AboutBananaComponent
},
{
path: 'apple/:fruitId/pear',
component: AboutApplePearComponent
}
];
about.module.ts
...
import { I18NRouterModule } from '@ngx-i18n-router/core';
...
@NgModule({
...
imports: [
//RouterModule.forChild(routes),
I18NRouterModule.forChild(routes, 'about'),
...
],
...
})
Note: You must comment (or better delete) the line with RouterModule.forChild(routes)
, in order to get @ngx-i18n-router/core
working. forChild
method of I18NRouterModule
provides routes for feature modules itself (if imports both RouterModule
and I18NRouterModule
, it will cause @ngx-i18n-router/core
to malfunction, even crash).
app.component configuration
Import I18NRouterService
using the mapping '@ngx-i18n-router/core'
and inject it in the constructor of app.component
(considering the app.component is the bootstrap component in Angular application).
Then, invoke the init
method to fetch route translations loaded during application initialization and allow the use
of @ngx-i18n-router/core
by the Angular app.
Lastly, you need to invoke the changeLanguage
method by supplying the 2-letter language code, which translates routes
to the specified language.
app.component.ts
...
import { I18NRouterService } from '@ngx-i18n-router/core';
...
@Component({
...
})
export class AppComponent implements OnInit {
...
constructor(private readonly i18nRouter: I18NRouterService) {
// invoking the `init` method with false won't allow the use of i18n-router,
// would be handy in the case you need to use i18n-router programmatically
i18nRouter.init();
}
...
ngOnInit(): void {
this.i18nRouter.changeLanguage('en');
}
...
}
Settings
You can call the forRoot static method using I18NRouterStaticLoader
. By default, it is configured to pass the routes
to @ngx-i18n-router/core
and have no translations.
You can customize this behavior (and ofc other settings) by supplying route translations to
I18NRouterStaticLoader
.
If you provide route translations using a JSON
file or an API
, you can call the forRoot static method using the I18NRouterHttpLoader
.
By default, it is configured to retrieve route translations from the path /routes.json
(if not specified).
You can customize this behavior (and ofc other settings) by supplying a file path/api endpoint to I18NRouterHttpLoader.
You can also use the @ngx-i18n-router/config-loader, to reduce the amount of HTTP
requests during application
initialization, by including route translations within the application settings - if @ngx-config/core is already
used to retrieve settings by the Angular app.
The following examples show the use of an exported function (instead of an inline function) for AoT compilation.
Setting up I18NRouterModule
to use I18NRouterStaticLoader
app.module.ts
...
import { I18NRouterModule, I18NRouterLoader, I18NRouterStaticLoader, I18N_ROUTER_PROVIDERS, RAW_ROUTES } from '@ngx-i18n-router/core';
...
export function i18nRouterFactory(rawRoutes: Routes): I18NRouterLoader {
return new I18NRouterStaticLoader({
routes: rawRoutes,
translations: {
"en": {
"ROOT.ABOUT": "about",
"ROOT.ABOUT.US": "us",
"ROOT.ABOUT.BANANA": "banana",
"ROOT.ABOUT.APPLE": "apple",
"ROOT.ABOUT.APPLE.PEAR": "pear",
"CHANGE_LANGUAGE": "change-language"
},
"tr": {
"ROOT.ABOUT": "hakkinda",
"ROOT.ABOUT.US": "biz",
"ROOT.ABOUT.BANANA": "muz",
"ROOT.ABOUT.APPLE": "elma",
"ROOT.ABOUT.APPLE.PEAR": "armut",
"CHANGE_LANGUAGE": "dil-secimi"
}
}
});
}
...
@NgModule({
declarations: [
AppComponent,
...
],
...
imports: [
RouterModule.forRoot(routes),
I18NRouterModule.forRoot(routes, [
{
provide: I18NRouterLoader,
useFactory: (i18nRouterFactory),
deps: [RAW_ROUTES]
}
]),
...
],
...
providers: [
I18N_ROUTER_PROVIDERS,
...
],
...
bootstrap: [AppComponent]
})
I18NRouterStaticLoader
has one parameter:
- providedSettings:
I18NRouterSettings
: i18n-router settings- routes:
Routes
: raw routes - translations:
any
: route translations
- routes:
Setting up I18NRouterModule
to use I18NRouterHttpLoader
If you provide route translations using a JSON
file or an API
, you can call the forRoot static method using the I18NRouterHttpLoader
.
By default, it is configured to retrieve route translations from the endpoint /routes.json
(if not specified).
You can customize this behavior (and ofc other settings) by supplying a api endpoint to
I18NRouterHttpLoader
.
You can find detailed information about the usage guidelines for the ConfigHttpLoader
here.
Setting up I18NRouterModule
to use I18NRouterConfigLoader
I18NRouterConfigLoader
provides route translations to @ngx-i18n-router/core
using @ngx-config/core
.
You can find detailed information about the usage guidelines for the I18NRouterConfigLoader
here.
Translations object
The translations object is designed to contain route translations in every language supported by Angular application, in JSON format.
When the changeLanguage
method of @ngx-i18n-router/core
is invoked, route configuration is reset based on
the supplied translations.
You should use the following data structure while working with the translation object:
- Assume that there're a number of {N} supported languages. The object contains {N} times first-level children, keyed with its 2-letter language code and valued by translations specific to that language.
- Language keys are in lowercase.
ex: Angular app supports en, fr and tr
{
"en": { ... },
"fr": { ... },
"tr": { ... }
}
- Routes within the
I18N-ROOT
scope must be keyed with theROOT
prefix, followed by a dot char.
, and then the moduleKey (module's root path in kebab-case). - Route keys are followed (if any, after prefixes) by the
path
attribute of routes. - Route keys are in UPPERCASE.
- Dot char
.
is used as a separator between prefixes/parts (ex: ROOT.ABOUT).
ex: Angular app supports en and tr
modules:
AboutModule (path: 'about')
components:
AboutComponent (path: '')
routes:
http://mysite.com/about -> http://mysite.com/en/about, http://mysite.com/tr/hakkinda
{
"en": {
"ROOT.ABOUT": "about"
},
"tr": {
"ROOT.ABOUT": "iletisim"
}
}
- Routes outside the
I18N-ROOT
scope must be keyed with the moduleKey (module's root path in kebab-case).
ex: Angular app supports en and tr
modules:
AboutModule (path: 'about')
SitemapModule (path: 'sitemap')
components:
AboutComponent (path: '')
SitemapComponent (path: '')
routes:
http://mysite.com/about -> http://mysite.com/en/about, http://mysite.com/tr/hakkinda
http://mysite.com/sitemap -> http://mysite.com/sitemap
{
"en": {
"ROOT.ABOUT": "about"
},
"tr": {
"ROOT.ABOUT": "iletisim"
}
}
- Underscores
_
must be used instead of dashes-
in the route keys (using dashes in keys causes errors). They're automatically replaced with dashes-
whileI18nRouterService
is translating the routes.
ex: Angular app supports en and tr
modules:
AboutModule (path: 'about')
SitemapModule (path: 'site-map')
components:
AboutComponent (path: '')
SitemapComponent (path: '')
routes:
http://mysite.com/about -> http://mysite.com/en/about, http://mysite.com/tr/hakkinda
http://mysite.com/site-map -> http://mysite.com/site-map, http://mysite.com/site-haritasi
{
"en": {
"ROOT.ABOUT": "about",
"SITE_MAP": "site-map"
},
"tr": {
"ROOT.ABOUT": "iletisim",
"SITE_MAP": "site-haritasi"
}
}
- Route paths are split by slashes
/
into string parts. When keying and they're separated by dots.
(ex: path: 'goes/to/your-page' -> 'GOES.TO.YOUR_PAGE'). - Route params (followed by colon) are not needed to be included in the translations object (ex: path: goes/to/:id/page -> 'GOES.TO.PAGE')
ex: Angular app supports en and tr
modules:
AboutModule (path: 'about'), ContactModule (path: 'contact'), ProfileComponent (path: 'profile')
SitemapModule (path: 'site-map')
components:
AboutComponent (path: ''), ContactComponent (path: ''), ContactMapComponent (path: 'map'), ProfileComponent (path: 'profile'), ProfileEditComponent (path: 'profile/:id/edit')
SitemapComponent (path: '')
routes:
http://mysite.com/about -> http://mysite.com/en/about, http://mysite.com/tr/hakkinda
http://mysite.com/contact -> http://mysite.com/en/contact, http://mysite.com/tr/iletisim
http://mysite.com/contact/map -> http://mysite.com/en/contact/map, http://mysite.com/tr/iletisim/harita
http://mysite.com/profile -> http://mysite.com/en/profile, http://mysite.com/tr/profil
http://mysite.com/profile/:id/edit -> http://mysite.com/en/profile/:id/edit, http://mysite.com/tr/profil/:id/duzenle
{
"en": {
"ROOT.ABOUT": "about",
"ROOT.CONTACT": "contact",
"ROOT.PROFILE": "profile",
"ROOT.PROFILE.EDIT": "edit",
"SITE_MAP": "site-map"
},
"tr": {
"ROOT.ABOUT": "iletisim",
"ROOT.CONTACT": "hakkimizda",
"ROOT.PROFILE": "profil",
"ROOT.PROFILE.EDIT": "duzenle",
"SITE_MAP": "site-haritasi"
}
}
:+1: Hooyah! It was quite a long story, but
@ngx-i18n-router/core
will now translate eachpath
andredirectTo
property of routes.
Change language (runtime)
Import I18NRouterService
using the mapping '@ngx-i18n-router/core'
and inject it in the constructor of change-language.component
(considering the change-language.component changes the language in Angular application).
Then, invoke the changeLanguage
method by supplying the 2-letter language code, which translates routes to the destination
language.
...
import { I18NRouterService } from '@ngx-i18n-router/core';
...
@Component({
...
})
export class ChangeLanguageComponent implements OnInit {
...
constructor(private readonly route: ActivatedRoute,
private readonly i18nRouter: I18NRouterService,
private readonly router: Router) { }
...
ngOnInit(): void {
this.route.params.subscribe(params => {
var languageCode = params['languageCode'];
if (languageCode)
// change language
this.i18nRouter.changeLanguage(languageCode);
this.router.navigate(['/']);
});
}
...
}
Pipe
I18nRouterPipe
is used to prefix and translate routerLink
directive's content. Pipe can be appended ONLY
to a single empty string in the routerLink
's definition or to an entire array element:
<a [routerLink]="['' | i18nRouter]">Home</a>
<a [routerLink]="['about'] | i18nRouter">About</a>
<a [routerLink]="['about', 'us', 22] | i18nRouter">About us</a>
<a [routerLink]="['about', 'banana'] | i18nRouter">Banana Republic</a>
<a [routerLink]="['about', 'apple', 6, 'pear'] | i18nRouter">(apple || pear)</a>
Example for Turkish language and link to 'about':
['about'] | i18nRouter -> '/tr/hakkinda'
Workaround for '@ngtools/webpack'
@ngx-i18n-router/core
does not work with angular-cli (yet), and giving the following error during AoT compilation:
ERROR in Cannot read property 'loadChildren' of undefined
@ngx-i18n-router/core
injects routes with the ROUTES
DI token using the useFactory
property. However @ngtools/webpack
forces routes to be static, and prevents code splitting (for lazy-loaded modules) by third parties.
This issue is caused by the ngtools_impl
located in the package @angular/compiler-cli
.
You can track the actual status of this issue at the following URLs:
- https://github.com/fulls1z3/ngx-i18n-router/issues/2
- https://github.com/angular/angular/issues/15305
On the other hand, the ng-router-loader (together with awesome-typescipt-loader) is safe to go with - it compiles without a problem. There's an overhead: you need to manually configure build tools (dev/prod sever, task runners, webpack, etc).
If you really need to stick to angular-cli, you can use the following workaround, by changing the contents of /node_modules/@angular/compiler-cli/src/ngtools_impl.js
as described below:
- Method name:
_collectRoutes
- Line number: 139
- Replacement: comment the line containing
return routeList.concat(p.useValue);
, and replace with:
if (p.useFactory != null) {
return routeList.concat(p.useFactory);
} else {
return routeList.concat(p.useValue);
}
ngtools_impl.js
function _collectRoutes(providers, reflector, ROUTES) {
return providers.reduce(function (routeList, p) {
if (p.provide === ROUTES) {
// return routeList.concat(p.useValue);
if (p.useFactory != null) {
return routeList.concat(p.useFactory);
} else {
return routeList.concat(p.useValue);
}
}
else if (Array.isArray(p)) {
return routeList.concat(_collectRoutes(p, reflector, ROUTES));
}
else {
return routeList;
}
}, []);
}
Credits
- localize-router: An implementation of routes localization for Angular 2
License
The MIT License (MIT)
Copyright (c) 2018 Burak Tasci