diff --git a/docs/README.md b/docs/README.md
index 0180a5a869..49074c3054 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -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)
diff --git a/docs/docassets/images/TransExDe.png b/docs/docassets/images/TransExDe.png
new file mode 100644
index 0000000000..19b51f7426
Binary files /dev/null and b/docs/docassets/images/TransExDe.png differ
diff --git a/docs/docassets/images/TransExEn.png b/docs/docassets/images/TransExEn.png
new file mode 100644
index 0000000000..ce1baec2dc
Binary files /dev/null and b/docs/docassets/images/TransExEn.png differ
diff --git a/docs/internationalization.md b/docs/internationalization.md
new file mode 100644
index 0000000000..cacf1d9b7d
--- /dev/null
+++ b/docs/internationalization.md
@@ -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 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:
+
+
+
+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)
diff --git a/docs/language-menu.component.md b/docs/language-menu.component.md
index 0692d71fec..252184e003 100644
--- a/docs/language-menu.component.md
+++ b/docs/language-menu.component.md
@@ -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)
diff --git a/docs/translation.service.md b/docs/translation.service.md
index b6ad36ba3a..56675f5a66 100644
--- a/docs/translation.service.md
+++ b/docs/translation.service.md
@@ -2,35 +2,117 @@
Supports localisation.
+## Methods
+
+`addTranslationFolder(name: string = '', path: string = '')`
+Adds a new folder of translation source files.
+
+`use(lang: string): Observable`
+Sets the target language for translations.
+
+`get(key: string|Array, interpolateParams?: Object): Observable`
+Gets the translation for the supplied key.
+
+`instant(key: string | Array, interpolateParams?: Object): string | any`
+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:
+`/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)