Skip to content

Commit

Permalink
docs: add missing Localization docs, closes #1595 (#1597)
Browse files Browse the repository at this point in the history
- add docs for using translations in 3 different ways:
1. single Locale (other than English)
2. with I18Next
3. with custom Translate Service
  • Loading branch information
ghiscoding committed Jul 9, 2024
1 parent fb3d8b7 commit 49523d1
Show file tree
Hide file tree
Showing 4 changed files with 299 additions and 5 deletions.
16 changes: 11 additions & 5 deletions docs/TOC.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
* [Formatters](column-functionalities/Formatters.md)
* [Sorting](column-functionalities/Sorting.md)

## Column Functionalities
## Events

* [Available events](events/Available-Events.md)
* [On Events](events/Grid-&-DataView-Events.md)
Expand All @@ -58,6 +58,16 @@
* [Tree Data Grid](grid-functionalities/Tree-Data-Grid.md)
* [Row Based Editing Plugin](grid-functionalities/Row-based-edit.md)

## Developer Guides

* [CSP Compliance](developer-guides/csp-compliance.md)

## Localization

* [with I18N](localization/localization-i18n.md)
* [with Custom Translate Service](localization/localization-translate-service.md)
* [with Single Locale](localization/localization-single-locale.md)

## Backend Services

* [Custom Backend Service](backend-services/Custom-Backend-Service.md)
Expand All @@ -68,10 +78,6 @@
* [Pagination Schema](backend-services/graphql/GraphQL-Pagination.md)
* [Sorting Schema](backend-services/graphql/GraphQL-Sorting.md)

## Developer Guides

* [CSP Compliance](developer-guides/csp-compliance.md)

## Migrations

* [Migration Guide to 1.x](migrations/migration-to-1.x.md)
Expand Down
122 changes: 122 additions & 0 deletions docs/localization/localization-i18n.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
### Demo
[Demo Page](https://ghiscoding.github.io/slickgrid-universal/#/example07) / [Demo Component](https://github.com/ghiscoding/slickgrid-universal/blob/master/examples/vite-demo-vanilla-bundle/src/examples/example07.ts)

### Installation
Install the `i18next` library with a backend loader, typically `i18next-xhr-backend`

##### Install NPM package
```ts
npm install i18next i18next-xhr-backend
```

##### Main.ts
###### configure i18n loader with assets folder
```ts
import i18n from 'i18next';
import Backend from 'i18next-http-backend';

i18n
.use(Backend)
.init({
// the translations (tip move them in a JSON file and import them or even better, manage them via a UI
// backend: {
// loadPath: 'assets/locales/{{lng}}/{{ns}}.json',
// },
resources: {
en: { translation: localeEn },
fr: { translation: localeFr },
},
lng: 'en',
fallbackLng: 'en',
debug: false,
interpolation: {
escapeValue: false
}
});
```

#### Class sample
You need to add a translation key via the property `headerKey` to each column definition, for example: `headerKey: 'TITLE'`

##### Note
For the `Select` Filter, you will use `labelKey` instead of `label`. Anytime a translation key will come in play, we will add the word `key` to the end (hence `headerKey`, `labelKey`, more to come...)

```ts
import { Formatters } from '@slickgrid-universal/common';
import i18next, { TFunction } from 'i18next';

// create a custom translate Formatter
function taskTranslateFormatter: Formatter = (row, cell, value, columnDef, dataContext, grid) => {
const gridOptions: GridOption = (grid && typeof grid.getOptions === 'function') ? grid.getOptions() : {};
return gridOptions.i18n?.t('TASK_X', { x: value }) ?? '';
}

export class Example {
constructor() {
// define the grid options & columns and then create the grid itself
this.defineGrid();
}

// Define grid Options and Columns
// provide a headerKey for each column and enableTranslate to True in GridOption
defineGrid() {
const columnDefinitions = [
{ id: 'title', name: 'Title', field: 'title', headerKey: 'TITLE', formatter: this.taskTranslateFormatter, sortable: true, minWidth: 100 },
{ id: 'duration', name: 'Duration (days)', field: 'duration', headerKey: 'DURATION', sortable: true, minWidth: 100 },
{ id: 'start', name: 'Start', field: 'start', headerKey: 'START', formatter: Formatters.dateIso, minWidth: 100 },
{ id: 'finish', name: 'Finish', field: 'finish', headerKey: 'FINISH', formatter: Formatters.dateIso, minWidth: 100 },
{ id: 'completed', name: 'Completed', field: 'completed', headerKey: 'COMPLETED', formatter: Formatters.translate, params: { i18n: i18next }, sortable: true, minWidth: 100 }
// OR via your own custom translate formatter
// { id: 'completed', name: 'Completed', field: 'completed', headerKey: 'COMPLETED', formatter: translateFormatter, sortable: true, minWidth: 100 }
];
const gridOptions = {
enableTranslate: true,
i18n: i18next,
};
}
}
```

#### Custom Formatter (cell values)
You can define your own custom Formatter by providing the `i18n` Service into the Formatter and using the `.tr()` function to translate the cell value.
```ts
const taskTranslateFormatter: Formatter = (row, cell, value, columnDef, dataContext, grid) => {
const gridOptions: GridOption = (grid && typeof grid.getOptions === 'function') ? grid.getOptions() : {};
return gridOptions.i18n?.t('TASK_X', { x: value }) ?? '';
}
```

#### Using Formatters.Translate
Instead of defining a custom formatter over and over, you could also use the built-in `Formatters.translate`. However for the formatter to work, you need to provide the `i18n` Service instance, you can do so using the `params` properties which is made to pass any type of data, however you need to pass it with this structure: `params: { i18n: i18next } `.
```ts
const columnDefinitions = [
{
id: 'title',
name: 'Title',
field: 'title',
headerKey: 'TITLE',
formatter: Formatters.translate,
params: { i18n: i18next } // provide the `i18n instance through the params.i18n property
}
];
```

#### Passing `i18n` in the Grid Options for Formatter
The best and quick way to pass the `i18n` service is to pass it through the generic `params` grid option. However make sure that you use the following structure: `params: { i18n: i18next } `.
```ts
const gridOptions = {
enableTranslate: true,
params: { i18n: i18next } // provide the `i18n instance through the params.i18n property
};
```

#### Locales
The final step is of course the actual translations. There's multiple ways to copy them to your `assets` folder. See below for a few ways:
1. Manually copy the translation keys/values
2. Manually copy the JSON files to your `assets` folder
3. Or modify your `package.json` and add a script to copy the JSON files to your `assets` folder
- install NPM packages `cross-env` and `copyfiles` (`npm install copy-files cross-env`)
- add a new script in your `package.json`
- run the below script **once** with `npm run copy:i18n` and you should now have the JSON files in your `src/assets` folder

If you want to manually re-create the translation in your own files, the list of translations that you will need are displayed in the asset i18n translation folder (from that file, you need all translations shown before the 'BILLING', the next few ones are for the demo page only).
47 changes: 47 additions & 0 deletions docs/localization/localization-single-locale.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
## Description
Most of example that you will find across this library were made with a Translation Service or `I18N` (dynamic translation) support. However a few users of the lib only use 1 single locale (English or any other locale). Since not all users requires multiple translations, it is now possible to use the project without any Translation Service at all. What is the difference with/without Translation Service or `I18N`? Not much, the only difference is that `I18N` is an optional dependency, it will simply try to use Custom Locales, you can provide your own locales (see instruction below), or if none are provided it will use the English locales by default.

## Installation
There are 2 ways of using and defining Custom Locales, see below on how to achieve that.

### 1. Define your Custom Locales
#### English Locale
English is the default, if that is the locale you want to use, then there's nothing to do, move along...

#### Any other Locales (not English)
To use any other Locales, you will need to create a TypeScript (or JavaScript) of all the Locale Texts required for the library to work properly (if you forget to define a locale text, it will simply show up in English). For example, if we define a French Locale, it would look like this (for the complete list of required field take a look at the default [English Locale](https://github.com/ghiscoding/angular-slickgrid-demos/blob/master/bootstrap5-demo-with-locales/src/app/locales/en.ts) of Angular-Slickgrid)

```ts
// localeFrench.ts or fr.ts
export const localeFrench = {
// texte requis
TEXT_ALL_SELECTED: 'Tout sélectionnés',
TEXT_CANCEL: 'Annuler',
TEXT_CLEAR_ALL_FILTERS: 'Supprimer tous les filtres',
TEXT_CLEAR_ALL_SORTING: 'Supprimer tous les tris',
// ... the rest of the text
```
#### 2. Use the Custom Locales
##### Through the Grid Option of any grid
You can alternatively provide Custom Locales through any grid declaration through the `locales` Grid Options (it's the same as the global one, except that it's per grid)
```ts
import { localeFrench } from 'locales/fr';

export class MyGrid {
defineGrid() {
const columnDefinitions = [ /* ... */ ];

const gridOptions = {
enableAutoResize: true,

// provide Custom Locale to this grid only
locales: localeFrench
};
}
}
```

#### 3. Use the lib (without I18N)
There's nothing else to do, just use the library without defining or providing I18N and you're good to go.
119 changes: 119 additions & 0 deletions docs/localization/localization-translate-service.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
### Demo
[Demo Page](https://ghiscoding.github.io/slickgrid-universal/#/example07) / [Demo Component](https://github.com/ghiscoding/slickgrid-universal/blob/master/examples/vite-demo-vanilla-bundle/src/examples/example07.ts)

### Installation

You can create your own Translate Service, for example installing the `whatwg-fetch` library to load JSON files for translations.

##### Install NPM package

```ts
npm install whatwg-fetch
```

##### Main.ts

You must make sure to implement all functions of the `TranslaterService` interface.

```ts
export class TranslateService implements TranslaterService {
/** Method to return the current language used by the App */
getCurrentLanguage(): string {}

/** Method which receives a translation key and returns the translated value from that key */
translate(translationKey: string, params?: any): string {}

/** Method to set the language to use in the App and Translate Service */
use(language: string): Promise<any> | any {}
}
```

#### Class sample
You need to add a translation key via the property `headerKey` to each column definition, for example: `headerKey: 'TITLE'`

##### Note
For the `Select` Filter, you will use `labelKey` instead of `label`. Anytime a translation key will come in play, we will add the word `key` to the end (hence `headerKey`, `labelKey`, more to come...)

##### Load App

Load the App (i.e.: `main.ts`) and instantiate the `TranslateService`

```ts
class Main {
constructor(private renderer: Renderer) { }

async loadApp() {
const translate = new TranslateService();
translate.setup({
loadPath: 'i18n/{{lang}}.json',
lang: 'en'
});
await translate.use('en');

// it might be better to use proper Dependency Injection
// but for now let's use the window object to save keep a reference to our instantiated service
(<any>window).TranslateService = translate;

// ... do other things
}
}
```

##### Use it

```ts
import { Formatters } from '@slickgrid-universal/common';

// create a custom translate Formatter
function taskTranslateFormatter: Formatter = (row, cell, value, columnDef, dataContext) => {
return this.translateService.translate('TASK_X', { x: value }) ?? '';
}

export class Example {
constructor() {
// define the grid options & columns and then create the grid itself
this.defineGrid();
}

// Define grid Options and Columns
// provide a headerKey for each column and enableTranslate to True in GridOption
defineGrid() {
const columnDefinitions = [
{ id: 'title', name: 'Title', field: 'title', headerKey: 'TITLE', formatter: this.taskTranslateFormatter, sortable: true, minWidth: 100 },
{ id: 'duration', name: 'Duration (days)', field: 'duration', headerKey: 'DURATION', sortable: true, minWidth: 100 },
{ id: 'start', name: 'Start', field: 'start', headerKey: 'START', formatter: Formatters.dateIso, minWidth: 100 },
{ id: 'finish', name: 'Finish', field: 'finish', headerKey: 'FINISH', formatter: Formatters.dateIso, minWidth: 100 },
{ id: 'completed', name: 'Completed', field: 'completed', headerKey: 'COMPLETED', formatter: Formatters.translate, params: { i18n: this.translateService }, sortable: true, minWidth: 100 }
// OR via your own custom translate formatter
// { id: 'completed', name: 'Completed', field: 'completed', headerKey: 'COMPLETED', formatter: translateFormatter, sortable: true, minWidth: 100 }
];

const gridOptions = {
enableTranslate: true,
translater: this.translateService,
};
}
}
```

#### Custom Formatter (cell values)

You can define your own custom Formatter by providing the `TranslateService` Service into the Formatter and using the `.translate(key)` function to translate the cell value.

```ts
const taskTranslateFormatter: Formatter = (row, cell, value, columnDef, dataContext, grid) => {
return this.translateService.translate('TASK_X', { x: value }) ?? '';
}
```

#### Using Formatters.Translate
Instead of defining a custom formatter over and over, you could also use the built-in `Formatters.translate`. However for the formatter to work, you need to provide the `TranslateService` instance, you can do so using the `params` properties which is made to pass any type of data.

#### Passing `translater` in the Grid Options for the Translate Service

```ts
const gridOptions = {
enableTranslate: true,
translater: this.translateService, // pass the TranslateService instance to the grid
};
```

0 comments on commit 49523d1

Please sign in to comment.