Skip to content
This repository was archived by the owner on Jun 1, 2025. It is now read-only.

Commit 0a6f2e5

Browse files
Ghislain BeaulacGhislain Beaulac
authored andcommitted
Support localization with ngx-translate
1 parent 5c27298 commit 0a6f2e5

File tree

15 files changed

+358
-46
lines changed

15 files changed

+358
-46
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "angular-slickgrid",
3-
"version": "0.6.7",
3+
"version": "0.7.0",
44
"description": "Slickgrid components made available in Angular",
55
"keywords": [
66
"angular",
@@ -35,6 +35,8 @@
3535
},
3636
"private": false,
3737
"dependencies": {
38+
"@ngx-translate/core": "^9.0.1",
39+
"@ngx-translate/http-loader": "^2.0.0",
3840
"@types/flatpickr": "^3.1.2",
3941
"@types/moment": "^2.13.0",
4042
"bootstrap": "^3.3.7",

src/app/app-routing.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { GridEditorComponent } from './examples/grid-editor.component';
66
import { GridFormatterComponent } from './examples/grid-formatter.component';
77
import { GridHeaderButtonComponent } from './examples/grid-headerbutton.component';
88
import { GridHeaderMenuComponent } from './examples/grid-headermenu.component';
9+
import { GridLocalizationComponent } from './examples/grid-localization.component';
910
import { GridOdataComponent } from './examples/grid-odata.component';
1011
import { GridGraphqlComponent } from './examples/grid-graphql.component';
1112
import { GridRowSelectionComponent } from './examples/grid-rowselection.component';
@@ -23,6 +24,7 @@ const routes: Routes = [
2324
{ path: 'headermenu', component: GridHeaderMenuComponent },
2425
{ path: 'gridgraphql', component: GridGraphqlComponent },
2526
{ path: 'gridmenu', component: GridMenuComponent },
27+
{ path: 'localization', component: GridLocalizationComponent },
2628
{ path: 'clientside', component: GridClientSideComponent },
2729
{ path: 'odata', component: GridOdataComponent },
2830
{ path: 'selection', component: GridRowSelectionComponent },

src/app/app.component.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@
5757
<li routerLinkActive="active">
5858
<a [routerLink]="['/additem']">11- Add/Update Grid Item</a>
5959
</li>
60+
<li routerLinkActive="active">
61+
<a [routerLink]="['/localization']">12- Localization (i18n)</a>
62+
</li>
6063
</ul>
6164
</div>
6265
</section>

src/app/app.module.ts

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { GridSingleSelectionComponent } from './examples/grid-single-selection.component';
2-
import { GridOdataService } from './modules/angular-slickgrid/services/grid-odata.service';
3-
import { ResizerService } from './modules/angular-slickgrid/services/resizer.service';
41
import { AppRoutingRoutingModule } from './app-routing.module';
52
import { BrowserModule } from '@angular/platform-browser';
6-
import { HttpClientModule } from '@angular/common/http';
7-
import { NgModule } from '@angular/core';
3+
import { HttpClient, HttpClientModule } from '@angular/common/http';
4+
import { Injector, APP_INITIALIZER, NgModule } from '@angular/core';
5+
import { LOCATION_INITIALIZED } from '@angular/common';
6+
import { TranslateModule, TranslateLoader, TranslateService } from '@ngx-translate/core';
7+
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
8+
import { GridOdataService } from './modules/angular-slickgrid/services/grid-odata.service';
9+
import { ResizerService } from './modules/angular-slickgrid/services/resizer.service';
810

911
import { AppComponent } from './app.component';
1012
import { GridAddItemComponent } from './examples/grid-additem.component';
@@ -15,8 +17,10 @@ import { GridFormatterComponent } from './examples/grid-formatter.component';
1517
import { GridGraphqlComponent } from './examples/grid-graphql.component';
1618
import { GridHeaderButtonComponent } from './examples/grid-headerbutton.component';
1719
import { GridHeaderMenuComponent } from './examples/grid-headermenu.component';
20+
import { GridLocalizationComponent } from './examples/grid-localization.component';
1821
import { GridMenuComponent } from './examples/grid-menu.component';
1922
import { GridOdataComponent } from './examples/grid-odata.component';
23+
import { GridSingleSelectionComponent } from './examples/grid-single-selection.component';
2024
import { GridRowSelectionComponent } from './examples/grid-rowselection.component';
2125
import { HomeComponent } from './examples/home.component';
2226

@@ -25,6 +29,29 @@ import { HomeComponent } from './examples/home.component';
2529
import { AngularSlickgridModule } from './modules/angular-slickgrid/modules/angular-slickgrid.module';
2630
// import { SlickgridModule } from 'angular-slickgrid';
2731

32+
// AoT requires an exported function for factories
33+
export function createTranslateLoader(http: HttpClient) {
34+
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
35+
}
36+
37+
// use an Initializer Factory as describe here: https://github.com/ngx-translate/core/issues/517#issuecomment-299637956
38+
export function appInitializerFactory(translate: TranslateService, injector: Injector) {
39+
return () => new Promise<any>((resolve: any) => {
40+
const locationInitialized = injector.get(LOCATION_INITIALIZED, Promise.resolve(null));
41+
locationInitialized.then(() => {
42+
const langToSet = 'en';
43+
translate.setDefaultLang('en');
44+
translate.use(langToSet).subscribe(() => {
45+
// console.info(`Successfully initialized '${langToSet}' language.'`);
46+
}, err => {
47+
console.error(`Problem with '${langToSet}' language initialization.'`);
48+
}, () => {
49+
resolve(null);
50+
});
51+
});
52+
});
53+
}
54+
2855
@NgModule({
2956
declarations: [
3057
AppComponent,
@@ -36,6 +63,7 @@ import { AngularSlickgridModule } from './modules/angular-slickgrid/modules/angu
3663
GridGraphqlComponent,
3764
GridHeaderButtonComponent,
3865
GridHeaderMenuComponent,
66+
GridLocalizationComponent,
3967
GridMenuComponent,
4068
GridOdataComponent,
4169
GridSingleSelectionComponent,
@@ -46,9 +74,25 @@ import { AngularSlickgridModule } from './modules/angular-slickgrid/modules/angu
4674
AppRoutingRoutingModule,
4775
BrowserModule,
4876
HttpClientModule,
77+
TranslateModule.forRoot({
78+
loader: {
79+
provide: TranslateLoader,
80+
useFactory: (createTranslateLoader),
81+
deps: [HttpClient]
82+
}
83+
}),
4984
AngularSlickgridModule
5085
],
51-
providers: [GridOdataService, ResizerService],
86+
providers: [
87+
{
88+
provide: APP_INITIALIZER,
89+
useFactory: appInitializerFactory,
90+
deps: [TranslateService, Injector],
91+
multi: true
92+
},
93+
GridOdataService,
94+
ResizerService
95+
],
5296
bootstrap: [AppComponent]
5397
})
5498
export class AppModule { }
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<!--The content below is only a placeholder and can be replaced.-->
2+
<div class="container">
3+
<h2>{{title}}</h2>
4+
<div class="subtitle" [innerHTML]="subTitle"></div>
5+
6+
<hr/>
7+
8+
<div class="row col-sm-12">
9+
<button class="btn btn-default btn-sm" (click)="switchLanguage()">Switch Language</button>
10+
<b>Locale:</b> <span style="font-style: italic">{{selectedLanguage + '.json'}}</span>
11+
</div>
12+
13+
<div class="col-sm-12">
14+
<angular-slickgrid gridId="grid12" [columnDefinitions]="columnDefinitions"
15+
[gridOptions]="gridOptions" [dataset]="dataset">
16+
</angular-slickgrid>
17+
</div>
18+
</div>
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { Component, OnInit, Injectable } from '@angular/core';
2+
import { Column, Editors, FieldType, Formatter, Formatters, GridExtraService, GridExtraUtils, GridOption, OnEventArgs, ResizerService } from './../modules/angular-slickgrid';
3+
import { TranslateService } from '@ngx-translate/core';
4+
5+
@Component({
6+
templateUrl: './grid-localization.component.html'
7+
})
8+
@Injectable()
9+
export class GridLocalizationComponent implements OnInit {
10+
title = 'Example 12: Localization (i18n)';
11+
subTitle = `Support multiple locales with the i18next plugin, following these steps
12+
<ol class="small">
13+
<li>You first need to "enableTranslate" in the Grid Options</li>
14+
<li>In the Column Definitions, you have following options</li>
15+
<ul>
16+
<li>To translate a header title, use "headerKey" with a translate key (headerKey: 'TITLE')</li>
17+
<li>For the cell values, you need to use a Formatter, there's 2 ways of doing it</li>
18+
<ul>
19+
<li>formatter: myCustomTranslateFormatter <b>&lt;= "Title" column uses it</b></li>
20+
<li>formatter: Formatters.translate, params: { i18n: this.translateService } <b>&lt;= "Completed" column uses it</b></li>
21+
</ul>
22+
</ul>
23+
<li>For date localization, you need to create your own custom formatter. </li>
24+
<ul>
25+
<li>You can easily implement logic to switch between Formatters "dateIso" or "dateUs", depending on current locale.</li>
26+
</ul>
27+
</ol>
28+
`;
29+
30+
columnDefinitions: Column[];
31+
gridOptions: GridOption;
32+
dataset: any[];
33+
selectedLanguage: string;
34+
35+
constructor(private gridExtraService: GridExtraService, private translate: TranslateService) {
36+
this.selectedLanguage = this.translate.getDefaultLang();
37+
}
38+
39+
ngOnInit(): void {
40+
this.columnDefinitions = [
41+
{ id: 'title', name: 'Title', field: 'title', headerKey: 'TITLE', formatter: this.taskTranslateFormatter, sortable: true, minWidth: 100 },
42+
{ id: 'duration', name: 'Duration (days)', field: 'duration', headerKey: 'DURATION', sortable: true, minWidth: 100 },
43+
{ id: 'start', name: 'Start', field: 'start', headerKey: 'START', formatter: Formatters.dateIso, minWidth: 100 },
44+
{ id: 'finish', name: 'Finish', field: 'finish', headerKey: 'FINISH', formatter: Formatters.dateIso, minWidth: 100 },
45+
{ id: 'completed', name: 'Completed', field: 'completed', headerKey: 'COMPLETED', formatter: Formatters.translate, params: { i18n: this.translate }, sortable: true, minWidth: 100 }
46+
// OR via your own custom translate formatter
47+
// { id: 'completed', name: 'Completed', field: 'completed', headerKey: 'COMPLETED', formatter: translateFormatter, sortable: true, minWidth: 100 }
48+
];
49+
this.gridOptions = {
50+
autoResize: {
51+
containerId: 'demo-container',
52+
sidePadding: 15
53+
},
54+
enableAutoResize: true,
55+
enableTranslate: true
56+
};
57+
58+
this.loadData();
59+
}
60+
61+
// mock a dataset
62+
loadData() {
63+
this.dataset = [];
64+
for (let i = 0; i < 1000; i++) {
65+
const randomYear = 2000 + Math.floor(Math.random() * 10);
66+
const randomMonth = Math.floor(Math.random() * 11);
67+
const randomDay = Math.floor((Math.random() * 29));
68+
const randomPercent = Math.round(Math.random() * 100);
69+
70+
this.dataset[i] = {
71+
id: i,
72+
title: i,
73+
duration: Math.round(Math.random() * 100) + '',
74+
start: new Date(randomYear, randomMonth, randomDay),
75+
finish: new Date(randomYear, (randomMonth + 1), randomDay),
76+
completed: (i % 5 === 0) ? 'TRUE' : 'FALSE'
77+
};
78+
}
79+
}
80+
81+
switchLanguage() {
82+
this.selectedLanguage = (this.selectedLanguage === 'en') ? 'fr' : 'en';
83+
this.translate.use(this.selectedLanguage);
84+
}
85+
86+
// create a custom translate Formatter
87+
taskTranslateFormatter: Formatter = (row, cell, value, columnDef, dataContext) => {
88+
return this.translate.instant('TASK_X', { x: value });
89+
}
90+
}

src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { GridEventService } from './../services/gridEvent.service';
2929
import { GridExtraService } from './../services/gridExtra.service';
3030
import { ResizerService } from './../services/resizer.service';
3131
import { SortService } from './../services/sort.service';
32+
import { TranslateService } from '@ngx-translate/core';
3233

3334
import $ from 'jquery';
3435

@@ -82,8 +83,9 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn
8283
private gridExtraService: GridExtraService,
8384
private gridEventService: GridEventService,
8485
private resizer: ResizerService,
85-
private controlAndPluginService: ControlAndPluginService
86-
) {}
86+
private controlAndPluginService: ControlAndPluginService,
87+
private translate: TranslateService
88+
) { }
8789

8890
ngOnInit(): void {
8991
this.gridHeightString = `${this.gridHeight}px`;
@@ -124,9 +126,22 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn
124126

125127
// attach grid extra service
126128
const gridExtraService = this.gridExtraService.init(this.grid, this.columnDefinitions, this._gridOptions, this._dataView);
129+
130+
// when user enables translation, we need to translate Headers on first pass & subsequently in the attachDifferentHooks
131+
if (this._gridOptions.enableTranslate) {
132+
this.controlAndPluginService.translateHeaders();
133+
}
127134
}
128135

129136
attachDifferentHooks(grid: any, options: GridOption, dataView: any) {
137+
// on locale change, we have to manually translate the Headers, GridMenu
138+
this.translate.onLangChange.subscribe((event) => {
139+
if (options.enableTranslate) {
140+
this.controlAndPluginService.translateHeaders();
141+
this.controlAndPluginService.translateGridMenu();
142+
}
143+
});
144+
130145
// attach external sorting (backend) when available or default onSort (dataView)
131146
if (options.enableSorting) {
132147
(options.onBackendEventApi) ? this.sortService.attachBackendOnSort(grid, options) : this.sortService.attachLocalOnSort(grid, options, this._dataView);
@@ -217,7 +232,7 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn
217232
if (this._gridOptions.enableAutoResize) {
218233
// resize the grid inside a slight timeout, in case other DOM element changed prior to the resize (like a filter/pagination changed)
219234
this.resizer.resizeGrid(10);
220-
// this.grid.autosizeColumns();
235+
// this.grid.autosizeColumns();
221236
}
222237
}
223238
}

src/app/modules/angular-slickgrid/formatters/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { editIconFormatter } from './editIconFormatter';
1616
import { percentCompleteFormatter } from './percentCompleteFormatter';
1717
import { percentCompleteBarFormatter } from './percentCompleteBarFormatter';
1818
import { progressBarFormatter } from './progressBarFormatter';
19+
import { translateFormatter } from './translateFormatter';
1920
import { yesNoFormatter } from './yesNoFormatter';
2021

2122
/*
@@ -43,5 +44,6 @@ export const Formatters = {
4344
percentComplete: percentCompleteFormatter,
4445
percentCompleteBar: percentCompleteBarFormatter,
4546
progressBar: progressBarFormatter,
47+
translate: translateFormatter,
4648
yesNo: yesNoFormatter
4749
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Column, Formatter } from './../models/index';
2+
import { TranslateService } from '@ngx-translate/core';
3+
4+
export const translateFormatter: Formatter = (row: number, cell: number, value: any, columnDef: Column, dataContext: any) => {
5+
const params = columnDef.params || {};
6+
if (!params.i18n || !(params.i18n instanceof TranslateService)) {
7+
throw new Error(`The translate formatter requires the ngx-translate "TranslateService" to be provided as a column params.
8+
For example: this.columnDefinitions = [{ id: title, field: title, formatter: Formatters.translate, params: { i18n: this.translateService }`);
9+
}
10+
11+
return params.i18n.instant(value);
12+
};

src/app/modules/angular-slickgrid/models/column.interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export interface Column {
2727
};
2828
};
2929
headerCssClass?: string;
30+
headerKey?: string;
3031
id: number | string;
3132
isEditable?: boolean;
3233
isHidden?: boolean;

0 commit comments

Comments
 (0)