[ADF-2228] Added i18n guide (#2903)

This commit is contained in:
Andy Stark
2018-02-02 10:27:08 +00:00
committed by Eugenio Romano
parent e5afe96a52
commit e5c025aa35
6 changed files with 326 additions and 18 deletions

View File

@@ -22,6 +22,7 @@ may be listed here before their documentation is available.
- [Angular Material Design](angular-material-design.md)
- [Theming](theming.md)
- [Typography](typography.md)
- [Internationalization](internationalization.md)
- [Walkthrough - adding indicators to highlight information about a node](metadata-indicators.md)
<!--guide end-->

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,217 @@
# Internationalization in ADF
Internationalization (abbreviated to i18n) is the process of providing UI messages
and captions in different human languages to make them easier for readers of those
languages to understand. ADF provides full support for i18n in apps. The process does
require some extra effort in planning and designing the UI but once implemented, it is
fairly straightforward to maintain.
## Contents
- [I18n concepts](#i18n-concepts)
- [ADF support for i18n](#adf-support-for-i18n)
- [Using the translate pipe](#using-the-translate-pipe)
- [Adding your own messages](#adding-your-own-messages)
- [Interpolations](#interpolations)
- [Selecting the display language](#selecting-the-display-language)
- [Support for i18n within ADF components](#support-for-i18n-within-adf-components)
- [See also](#see-also)
## I18n concepts
The main idea behind i18n is to avoid adding natural language text directly into the
HTML. Instead, UI messages are represented by short strings known as
**keys**. Keys are not displayed directly; they are used to look up the actual text
in a list of predefined messages. A typical key/message pair might look like the
following:
"CS_URL_ERROR": "Content Services address doesn't match the URL format"
Separate lists are kept for each language supported by the app, so for German, the
same message would be defined as:
"CS_URL_ERROR": "Content Services-Adresse nicht im richtigen URL-Format"
Note that the key is the same in both cases. As long as the UI only ever refers to
the keys then changing languages is a simple matter of changing the look-up list.
## ADF support for i18n
ADF implements i18n for more than ten languages internally in the display text for
components, so you can try out some simple messages without any configuration. The
keys are defined in a set of files in the `lib/core/i18n` folder in the ADF sources.
The files are named according to standard
[two-letter language codes](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes),
so `en.json` is the look-up list for English, etc. An excerpt from `en.json` is shown
below:
```json
{
"FORM": {
"START_FORM": {
"TITLE": "Start Form"
},
"PREVIEW": {
"IMAGE_NOT_AVAILABLE": "Preview not available"
},
"FIELD": {
"LOCALSTORAGE" : "Local storage",
"SOURCE": "Select source from ",
"UPLOAD": "UPLOAD",
"REQUIRED": "*Required",
...
```
The hierarchical structure is referred to in the UI using the familiar "dot"
notation (so `FORM.START_FORM.TITLE` would be the key for the "Start Form"
string here). This is useful for grouping related messages and providing
singular and plural versions, among other things.
The [Translation service](translation.service.md) defines the `get` method to
get the translation of a key in the current language. A simple component might
contain code like this:
```ts
import { Component, OnInit } from '@angular/core';
import { TranslationService } from "@alfresco/adf-core";
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
constructor(private trans: TranslationService) { }
translatedText: string = "";
ngOnInit() {
this.trans.get("FORM.START_FORM.TITLE").subscribe(translation => {
this.translatedText = translation;
});
}
}
```
...with very simple corresponding HTML:
```html
{{translatedText}}
```
In the browser, this is displayed as:
![English translation text](docassets/images/TransExEn.png)
English is used by default but you can easily change the language with the
`use` method:
```ts
ngOnInit() {
this.trans.use("de");
this.trans.get("FORM.START_FORM.TITLE").subscribe(translation => {
this.translatedText = translation;
});
}
```
The user will now see:
![German translation text](docassets/images/TransExDe.png)
Note that an unrecognized key will be returned unchanged as the "translation".
If you see strings like "FORM.START_FORM.TITLE" displayed in your app then you
should check you are using the key correctly.
## Using the translate pipe
Using `TranslationService.get` is straightforward but it is often more
convenient to add translation keys directly into your page's HTML.
Use the `translate` pipe to convert a key in the page directly to the
corresponding text. For example, the following will display the
"Start Form" text as above but without any code or variables in the
component's `.ts` file:
{{ "FORM.START_FORM.TITLE" | translate }}
## Adding your own messages
The built-in translations certainly won't cover everything you will need for
your app but you can easily replace them with your own lists. This involves
making copies of the existing lists in your app's folder and adding your
own keys. See the [Translation service](translation.service.md) page for
full details and examples.
## Interpolations
Translation messages have support for _interpolation_ (ie, including another
string at a specified position within a message). This is very useful for
messages whose content can change at runtime. For example, in the built-in
`en.json` there is the `CORE.PAGINATION.ITEMS_RANGE` key:
```json
...
"CORE": {
...
"PAGINATION": {
"ITEMS_RANGE": "Showing {{ range }} of {{ total }}",
"ITEMS_PER_PAGE": "Items per page",
...
},
...
```
The sections in curly braces are _interpolation variables_ that you supply
at runtime. You can specify them by passing an extra parameter to
`TranslationService.get`; this is an object whose properties have the same
names as the interpolation variables in the string:
```ts
this.trans.get(
"CORE.PAGINATION.ITEMS_RANGE",
{
range: "1..10",
total: "122"
}
).subscribe(translation => {
this.translatedText = translation;
});
```
You can use interpolations with the `translate` pipe in a similar way:
{{ "CORE.PAGINATION.ITEMS_RANGE" | translate: { range: "1..10", total: "122"} }}
## Selecting the display language
ADF provides a [Language Menu component](language-menu.component.md) that
you can add to a page to let the user choose their preferred language. The
available languages are defined in the `app.config.json` file for the app.
Note that when the user selects an item from the menu, it simply changes the "locale"
preference (which you can get via the [User Preferences service](user-preferences.service.md)).
The `translate` pipe reacts automatically to this and changes the page text
immediately to the new language. However, text added via a variable set using
`TranslationService.get`, as in the example above, will not be updated like this;
you will need to get a new translation and set the variable's value again explicitly
from the code.
See the [Language Menu component](language-menu.component.md) page for further
details and usage examples.
## Support for i18n within ADF components
Some components allow you to use translation keys in places where you would normally
supply your own messages directly. For example, the
[Data Column component](data-column.component.md) can accept a key instead of
normal text to specify the column title. Consult the documentation for a
component to see if it has built-in support for i18n.
## See also
- [Translation service](translation.service.md)
- [Language Menu component](language-menu.component.md)

View File

@@ -65,3 +65,7 @@ How to attach an ADF Language Menu as nested menu
### Nested menu details
In the previous example we are using the ADF Language Menu as nested menu.
## See Also
- [Internationalization](internationalization.md)

View File

@@ -2,35 +2,117 @@
Supports localisation.
## Methods
`addTranslationFolder(name: string = '', path: string = '')`<br/>
Adds a new folder of translation source files.
`use(lang: string): Observable<any>`<br/>
Sets the target language for translations.
`get(key: string|Array<string>, interpolateParams?: Object): Observable<string|any>`<br/>
Gets the translation for the supplied key.
`instant(key: string | Array<string>, interpolateParams?: Object): string | any`<br/>
Directly returns the translation for the supplied key.
## Details
In the `get` and `instant` methods, the `interpolateParams` parameter supplies
interpolation strings for keys that include them. For example, in the standard
`en.json`, the `CORE.PAGINATION.ITEMS_RANGE` key is defined as:
"Showing {{ range }} of {{ total }}"
The `range` and `total` interpolations are supplied to the `get` method using
an object with fields of the same name:
```ts
this.trans.get(
"CORE.PAGINATION.ITEMS_RANGE",
{
range: "1..10",
total: "122"
}
).subscribe(translation => {
this.translatedText = translation;
});
```
### Registering translation sources
In order to enable localisation support you will need to create a `/resources/i18n/en.json` file
and register its parent `i18n` folder with your component or application module.
To supply your own set of translation source files, you
first need to create a subfolder for them within your application's
`assets` folder. The folder can have any name you like but it must also have
a sub-folder called `i18n` where the translation lists will be stored. So, the
general format of the path to this folder will be:
For example:
`<app>/src/assets/my-translations/i18n`
If you wanted English and French translations then you would copy the built-in
`en.json` and `fr.json` files into the `i18n` folder and add your new keys:
// en.json
...
"WELCOME_MESSAGE": "Welcome!"
...
// fr.json
...
"WELCOME_MESSAGE": "Bienvenue!"
...
To enable the new translations in your app, you also need to register them in your
`app.module.ts` file. Import `TRANSLATION_PROVIDER` and add the path of your
translations folder to the `providers`:
```ts
import { TRANSLATION_PROVIDER } from '@alfresco/adf-core';
// Other imports...
import { TRANSLATION_PROVIDER } from "@alfresco/adf-core";
...
@NgModule({
imports: [
...
providers: [
...
{
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: 'ng2-alfresco-core',
source: 'assets/ng2-alfresco-core'
}
}
]
})
],
declarations: [
...
],
providers: [
{
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: 'my-translations',
source: 'assets/my-translations'
}
}
...
```
Note: the `source` property points to the web application root, please ensure you have webpack settings to copy all the i18n files at compile time.
You can now use your new keys in your component:
```ts
...
ngOnInit() {
this.trans.use("fr");
this.trans.get("WELCOME_MESSAGE").subscribe(translation => {
this.translatedText = translation;
});
}
...
```
The new translation files completely replace the built-in ones.
If you want to continue using the built-in keys then you must add your new
keys to copies of the existing files.
Note: the `source` property points to the web application root. Ensure you have
webpack correctly set up to copy all the i18n files at compile time.
```text
index.html
@@ -38,7 +120,7 @@ assets/ng2-alfresco-core/i18n/en.json
...
```
You can register as many entries as you would like.
You can register as many entries as you like.
### Switching languages
@@ -56,3 +138,7 @@ class MyComponent {
}
}
```
## See Also
- [Internationalization](internationalization.md)