Route internationalization utility for Angular
Adding
Add Setting up
Setting up
If you provide route translations using a Setting up
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
than v5.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 dependson @angular/cli or
@ngtools/webpack
for AoT compilation.
Table of contents:
- Installation- [Examples](#examples)
- [Related packages](#related-packages)
- [Recommended packages](#recommended-packages)
- [Adding `@ngx-i18n-router/core` to your project (SystemJS)](#adding-systemjs)
- [Route configuration](#route-config)
- app.module configuration
- [Feature modules configuration](#feature-modules-config)
- [app.component configuration](#appcomponent-config)
- [Setting up `I18NRouterModule` to use `I18NRouterStaticLoader`](#setting-up-staticloader)
- [Setting up `I18NRouterModule` to use `I18NRouterHttpLoader`](#setting-up-httploader)
- [Setting up `I18NRouterModule` to use `I18NRouterConfigLoader`](#setting-up-configloader)
- [Translations object](#translations-object)
Prerequisites
This library depends onAngular 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
@ngx-i18n-router/core
.
Related packages
The following packages may be used in conjunction with@ngx-i18n-router/core
Recommended packages
The following package(s) have no dependency for@ngx-i18n-router/core
, however may provide supplementary/shorthand
functionality:
Adding @ngx-i18n-router/core
to your project (SystemJS)
Add map
for @ngx-i18n-router/core
in your systemjs.config
```javascript
'@ngx-i18n-router/core': 'nodemodules/@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
```TypeScript 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 containsi18n
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``` (English) http://mysite.com/about -> http://mysite.com/en/about ``` Note: There must be a maximum of onedata
property containingi18n\isRoot
set totrue
,I18N-ROOT
of Angular application.
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 outsideI18N-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 theI18N-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
ImportI18NRouterModule
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
```TypeScript ... import { I18NRouterModule, I18NROUTERPROVIDERS } from '@ngx-i18n-router/core'; ... @NgModule({ declarations:AppComponent,
...
,
...
imports:
RouterModule.forRoot(routes),
I18NRouterModule.forRoot(routes),
...
,
...
providers:
I18N_ROUTER_PROVIDERS,
...
,
...
bootstrap: AppComponent
})
```
Feature modules configuration
ImportI18NRouterModule
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
```TypeScript export const routes: Routes = {path: '',
component: HomeComponent
}
;
```
home.module.ts
```TypeScript ... import { I18NRouterModule } from '@ngx-i18n-router/core'; ... @NgModule({ ... imports://RouterModule.forChild(routes),
I18NRouterModule.forChild(routes, 'home'),
...
,
...
})
```
about.routes.ts
```TypeScript 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
```TypeScript ... 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
ImportI18NRouterService
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
```TypeScript ... 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 usingI18NRouterStaticLoader
. 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
```TypeScript ... import { I18NRouterModule, I18NRouterLoader, I18NRouterStaticLoader, I18NROUTERPROVIDERS, RAWROUTES } 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
: raw routes
- translations: any
: route translations
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 thechangeLanguage
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,
- Language keys are in lowercase.
- Routes within the
I18N-ROOT
scope must be keyed with theROOT
prefix, followed by a dot char.
, and
- 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).
"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).
"ROOT.ABOUT": "about"
},
"tr": {
"ROOT.ABOUT": "iletisim"
}
}
```
- Underscores
_
must be used instead of dashes-
in the route keys (using dashes in keys causes errors).
-
while I18nRouterService
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.YOURPAGE').
- Route params (followed by colon) are not needed to be included in the translations object (ex: path: goes/to/:id/page
"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, butproperty of routes.@ngx-i18n-router/core
will now translate eachpath
andredirectTo
Change language (runtime)
ImportI18NRouterService
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.
```TypeScript
...
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:
```Html
routerLink="'' | i18nRouter">Home
routerLink="'about' | i18nRouter">About
routerLink="'about', 'us', 22 | i18nRouter">About us
routerLink="'about', 'banana' | i18nRouter">Banana Republic
routerLink="'about', 'apple', 6, 'pear' | i18nRouter">(apple || pear)
```
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
/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:
ngtoolsimpl.js
```JavaScript 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