[ACS-5629] enhanced way of providing translations (#8763)

* enhanced way providing translations

* update documentation

* update documentation

* test fixes

* try add missing module import

* inject i18n to core module to cause the setup
This commit is contained in:
Denys Vuika 2023-07-18 20:06:09 +01:00 committed by GitHub
parent d70f689e06
commit 1a4d7ba008
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 99 additions and 183 deletions

View File

@ -24,11 +24,11 @@ import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { import {
AppConfigService, AppConfigService,
TRANSLATION_PROVIDER,
DebugAppConfigService, DebugAppConfigService,
CoreModule, CoreModule,
CoreAutomationService, CoreAutomationService,
AuthModule AuthModule,
provideTranslations
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { ExtensionsModule } from '@alfresco/adf-extensions'; import { ExtensionsModule } from '@alfresco/adf-extensions';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
@ -211,22 +211,8 @@ registerLocaleData(localeSv);
], ],
providers: [ providers: [
{ provide: AppConfigService, useClass: DebugAppConfigService }, // not use this service in production { provide: AppConfigService, useClass: DebugAppConfigService }, // not use this service in production
{ provideTranslations('app', 'resources'),
provide: TRANSLATION_PROVIDER, provideTranslations('lazy-loading', 'resources/lazy-loading'),
multi: true,
useValue: {
name: 'app',
source: 'resources'
}
},
{
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: 'lazy-loading',
source: 'resources/lazy-loading'
}
},
AppNotificationsService, AppNotificationsService,
{ {
provide: APP_INITIALIZER, provide: APP_INITIALIZER,

View File

@ -79,16 +79,21 @@ general format of the path to this folder will be:
If you wanted English and French translations then you would add If you wanted English and French translations then you would add
`en.json` and `fr.json` files into the `i18n` folder and add your new keys: `en.json` and `fr.json` files into the `i18n` folder and add your new keys:
// en.json **en.json**
... ```json
{
"WELCOME_MESSAGE": "Welcome!" "WELCOME_MESSAGE": "Welcome!"
... }
```
// fr.json **fr.json**
...
```json
{
"WELCOME_MESSAGE": "Bienvenue !" "WELCOME_MESSAGE": "Bienvenue !"
... }
```
The files follow the same hierarchical key:value JSON format as the built-in translations. The files follow the same hierarchical key:value JSON format as the built-in translations.
You can add new keys to your local files or redefine existing keys but the built-in definitions You can add new keys to your local files or redefine existing keys but the built-in definitions
@ -106,57 +111,44 @@ look like the following:
} }
``` ```
To enable the new translations in your app, you also need to register them in your To enable the new translations in your app, you also need to register them in your `app.module.ts` file using `provideTranslations` api:
`app.module.ts` file. Import `TRANSLATION_PROVIDER` and add the path of your
translations folder to the `providers`:
```ts ```ts
// Other imports... import { provideTranslations } from "@alfresco/adf-core";
import { TRANSLATION_PROVIDER } from "@alfresco/adf-core";
...
@NgModule({ @NgModule({
imports: [
...
],
declarations: [
...
],
providers: [ providers: [
{ provideTranslations('my-translations', 'assets/my-translations')
provide: TRANSLATION_PROVIDER, ]
multi: true, })
useValue: { export class MyModule {}
name: 'my-translations',
source: 'assets/my-translations'
}
}
...
``` ```
You can now use your new keys in your component: You can now use your new keys in your component:
```ts ```ts
... export class MyComponent implements OnInit {
ngOnInit() { translateService = inject(TranslationService);
this.trans.use("fr"); translatedText = '';
this.trans.get("WELCOME_MESSAGE").subscribe(translation => { ngOnInit() {
this.translatedText = translation; this.translateService.use("fr");
}); this.translatedText = this.translateService.instant('WELCOME_MESSAGE');
} }
... }
``` ```
Note: the `source` property points to the web application root. Ensure you have Note: the `source` property points to the web application root.
webpack correctly set up to copy all the i18n files at compile time. Do not forget to configure your Angular application to copy the newly created files to the build output, for example:
```text **angular.json**
index.html
assets/ng2-alfresco-core/i18n/en.json ```json
... {
"glob": "**/*",
"input": "lib/core/src/lib/i18n",
"output": "/assets/adf-core/i18n"
}
``` ```
You can register as many entries as you like. You can register as many entries as you like.

View File

@ -18,7 +18,7 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { NgModule, ModuleWithProviders, APP_INITIALIZER } from '@angular/core'; import { NgModule, ModuleWithProviders, APP_INITIALIZER } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { CoreModule, TRANSLATION_PROVIDER, SearchTextModule } from '@alfresco/adf-core'; import { CoreModule, SearchTextModule, provideTranslations } from '@alfresco/adf-core';
import { MaterialModule } from './material.module'; import { MaterialModule } from './material.module';
@ -89,14 +89,7 @@ import { CategoriesModule } from './category/category.module';
CategoriesModule CategoriesModule
], ],
providers: [ providers: [
{ provideTranslations('adf-content-services', 'assets/adf-content-services')
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: 'adf-content-services',
source: 'assets/adf-content-services'
}
}
], ],
exports: [ exports: [
ContentPipeModule, ContentPipeModule,
@ -134,14 +127,7 @@ export class ContentModule {
return { return {
ngModule: ContentModule, ngModule: ContentModule,
providers: [ providers: [
{ provideTranslations('adf-content-services', 'assets/adf-content-services'),
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: 'adf-content-services',
source: 'assets/adf-content-services'
}
},
{ {
provide: APP_INITIALIZER, provide: APP_INITIALIZER,
useFactory: versionCompatibilityFactory, useFactory: versionCompatibilityFactory,

View File

@ -19,7 +19,7 @@ import { NgModule } from '@angular/core';
import { CoreModule } from '../core.module'; import { CoreModule } from '../core.module';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { TRANSLATION_PROVIDER } from '../translation/translation.service'; import { provideTranslations } from '../translation/translation.service';
@NgModule({ @NgModule({
imports: [ imports: [
@ -28,14 +28,7 @@ import { TRANSLATION_PROVIDER } from '../translation/translation.service';
BrowserAnimationsModule BrowserAnimationsModule
], ],
providers: [ providers: [
{ provideTranslations('adf-core', 'assets/adf-core')
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: 'adf-core',
source: 'assets/adf-core'
}
}
] ]
}) })
export class CoreStoryModule { } export class CoreStoryModule { }

View File

@ -21,7 +21,7 @@ import { getTestBed, TestBed } from '@angular/core/testing';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateLoaderService } from './translate-loader.service'; import { TranslateLoaderService } from './translate-loader.service';
import { TRANSLATION_PROVIDER, TranslationService } from './translation.service'; import { provideTranslations, TranslationService } from './translation.service';
import { AppConfigService } from '../app-config/app-config.service'; import { AppConfigService } from '../app-config/app-config.service';
import { AppConfigServiceMock } from '../common/mock/app-config.service.mock'; import { AppConfigServiceMock } from '../common/mock/app-config.service.mock';
import { AlfrescoApiService } from '../services/alfresco-api.service'; import { AlfrescoApiService } from '../services/alfresco-api.service';
@ -47,14 +47,7 @@ describe('TranslationService', () => {
providers: [ providers: [
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock }, { provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock },
{ provide: AppConfigService, useClass: AppConfigServiceMock }, { provide: AppConfigService, useClass: AppConfigServiceMock },
{ provideTranslations('@alfresco/adf-core', 'assets/ng2-alfresco-core')
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: '@alfresco/adf-core',
source: 'assets/ng2-alfresco-core'
}
}
] ]
}); });

View File

@ -28,6 +28,24 @@ export interface TranslationProvider {
source: string; source: string;
} }
/**
* Generate translation provider
*
* @param id Unique identifier
* @param path Path to translation files
* @returns Provider
*/
export function provideTranslations(id: string, path: string) {
return {
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: id,
source: path
}
};
}
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })

View File

@ -18,7 +18,7 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { NgModule, ModuleWithProviders } from '@angular/core'; import { NgModule, ModuleWithProviders } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { CoreModule, TRANSLATION_PROVIDER } from '@alfresco/adf-core'; import { CoreModule, provideTranslations } from '@alfresco/adf-core';
import { DiagramsModule } from './diagram/diagram.module'; import { DiagramsModule } from './diagram/diagram.module';
import { AnalyticsProcessModule } from './analytics-process/analytics-process.module'; import { AnalyticsProcessModule } from './analytics-process/analytics-process.module';
@ -45,14 +45,7 @@ export class InsightsModule {
return { return {
ngModule: InsightsModule, ngModule: InsightsModule,
providers: [ providers: [
{ provideTranslations('adf-insights', 'assets/adf-insights')
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: 'adf-insights',
source: 'assets/adf-insights'
}
}
] ]
}; };
} }

View File

@ -25,7 +25,7 @@ import {
FormModel, FormModel,
FormOutcomeEvent, FormOutcomeEvent,
FormOutcomeModel, FormRenderingService, FormService, FormOutcomeModel, FormRenderingService, FormService,
TRANSLATION_PROVIDER, UploadWidgetContentLinkModel, WidgetVisibilityService UploadWidgetContentLinkModel, WidgetVisibilityService, provideTranslations
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { Node } from '@alfresco/js-api'; import { Node } from '@alfresco/js-api';
import { ESCAPE } from '@angular/cdk/keycodes'; import { ESCAPE } from '@angular/cdk/keycodes';
@ -54,6 +54,7 @@ import { FormCloudRepresentation } from '../models/form-cloud-representation.mod
import { FormCloudService } from '../services/form-cloud.service'; import { FormCloudService } from '../services/form-cloud.service';
import { CloudFormRenderingService } from './cloud-form-rendering.service'; import { CloudFormRenderingService } from './cloud-form-rendering.service';
import { FormCloudComponent } from './form-cloud.component'; import { FormCloudComponent } from './form-cloud.component';
import { ProcessServicesCloudModule } from '../../process-services-cloud.module';
const mockOauth2Auth: any = { const mockOauth2Auth: any = {
oauth2Auth: { oauth2Auth: {
@ -1152,17 +1153,11 @@ describe('Multilingual Form', () => {
imports: [ imports: [
NoopAnimationsModule, NoopAnimationsModule,
TranslateModule.forRoot(), TranslateModule.forRoot(),
CoreModule.forRoot() CoreModule.forRoot(),
ProcessServicesCloudModule.forRoot()
], ],
providers: [ providers: [
{ provideTranslations('app', 'resources')
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: 'app',
source: 'resources'
}
}
] ]
}); });
translateService = TestBed.inject(TranslateService); translateService = TestBed.inject(TranslateService);
@ -1184,20 +1179,20 @@ describe('Multilingual Form', () => {
formComponent.ngOnChanges({ appName: new SimpleChange(null, appName, true) }); formComponent.ngOnChanges({ appName: new SimpleChange(null, appName, true) });
expect(formCloudService.getForm).toHaveBeenCalledWith(appName, formId, 1); expect(formCloudService.getForm).toHaveBeenCalledWith(appName, formId, 1);
fixture.ngZone.run(() => translateService.use('fr')); await translateService.use('fr').toPromise();
await fixture.whenStable();
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable();
expect(getLabelValue('textField')).toEqual('Champ de texte'); expect(getLabelValue('textField')).toEqual('Champ de texte');
expect(getLabelValue('fildUploadField')).toEqual('Téléchargement de fichiers'); expect(getLabelValue('fildUploadField')).toEqual('Téléchargement de fichiers');
expect(getLabelValue('dateField')).toEqual('Champ de date (D-M-YYYY)'); expect(getLabelValue('dateField')).toEqual('Champ de date (D-M-YYYY)');
expect(getLabelValue('amountField')).toEqual('Champ Montant'); expect(getLabelValue('amountField')).toEqual('Champ Montant');
fixture.ngZone.run(() => translateService.use('en')); await translateService.use('en').toPromise();
await fixture.whenStable();
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable();
expect(getLabelValue('textField')).toEqual('Text field'); expect(getLabelValue('textField')).toEqual('Text field');
expect(getLabelValue('fildUploadField')).toEqual('File Upload'); expect(getLabelValue('fildUploadField')).toEqual('File Upload');
@ -1237,14 +1232,7 @@ describe('retrieve metadata on submit', () => {
FormCloudModule FormCloudModule
], ],
providers: [ providers: [
{ provideTranslations('app', 'resources'),
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: 'app',
source: 'resources'
}
},
{ {
provide: VersionCompatibilityService, provide: VersionCompatibilityService,
useValue: {} useValue: {}

View File

@ -16,7 +16,7 @@
*/ */
import { NgModule, ModuleWithProviders } from '@angular/core'; import { NgModule, ModuleWithProviders } from '@angular/core';
import { TRANSLATION_PROVIDER, CoreModule, FormRenderingService } from '@alfresco/adf-core'; import { CoreModule, FormRenderingService, provideTranslations } from '@alfresco/adf-core';
import { AppListCloudModule } from './app/app-list-cloud.module'; import { AppListCloudModule } from './app/app-list-cloud.module';
import { TaskCloudModule } from './task/task-cloud.module'; import { TaskCloudModule } from './task/task-cloud.module';
import { ProcessCloudModule } from './process/process-cloud.module'; import { ProcessCloudModule } from './process/process-cloud.module';
@ -52,14 +52,7 @@ import { RichTextEditorModule } from './rich-text-editor/rich-text-editor.module
RichTextEditorModule RichTextEditorModule
], ],
providers: [ providers: [
{ provideTranslations('adf-process-services-cloud', 'assets/adf-process-services-cloud')
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: 'adf-process-services-cloud',
source: 'assets/adf-process-services-cloud'
}
}
], ],
exports: [ exports: [
AppListCloudModule, AppListCloudModule,
@ -81,14 +74,7 @@ export class ProcessServicesCloudModule {
return { return {
ngModule: ProcessServicesCloudModule, ngModule: ProcessServicesCloudModule,
providers: [ providers: [
{ provideTranslations('adf-process-services-cloud', 'assets/adf-process-services-cloud'),
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: 'adf-process-services-cloud',
source: 'assets/adf-process-services-cloud'
}
},
{ provide: PROCESS_FILTERS_SERVICE_TOKEN, useExisting: filterPreferenceServiceInstance ?? LocalPreferenceCloudService }, { provide: PROCESS_FILTERS_SERVICE_TOKEN, useExisting: filterPreferenceServiceInstance ?? LocalPreferenceCloudService },
{ provide: TASK_FILTERS_SERVICE_TOKEN, useExisting: filterPreferenceServiceInstance ?? LocalPreferenceCloudService }, { provide: TASK_FILTERS_SERVICE_TOKEN, useExisting: filterPreferenceServiceInstance ?? LocalPreferenceCloudService },
{ provide: PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN, useExisting: listPreferenceServiceInstance ?? LocalPreferenceCloudService }, { provide: PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN, useExisting: listPreferenceServiceInstance ?? LocalPreferenceCloudService },

View File

@ -16,25 +16,20 @@
*/ */
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { CoreModule, TRANSLATION_PROVIDER } from '@alfresco/adf-core'; import { CoreModule, provideTranslations } from '@alfresco/adf-core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { ProcessServicesCloudModule } from '../process-services-cloud.module';
@NgModule({ @NgModule({
imports: [ imports: [
BrowserAnimationsModule,
TranslateModule.forRoot(), TranslateModule.forRoot(),
CoreModule.forRoot(), CoreModule.forRoot(),
BrowserAnimationsModule ProcessServicesCloudModule.forRoot()
], ],
providers: [ providers: [
{ provideTranslations('adf-process-services-cloud', 'assets/adf-process-services-cloud')
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: 'adf-process-services-cloud',
source: 'assets/adf-process-services-cloud'
}
}
] ]
}) })
export class ProcessServicesCloudStoryModule { } export class ProcessServicesCloudStoryModule { }

View File

@ -18,7 +18,7 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { NgModule, ModuleWithProviders } from '@angular/core'; import { NgModule, ModuleWithProviders } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { CoreModule, TRANSLATION_PROVIDER, FormRenderingService } from '@alfresco/adf-core'; import { CoreModule, FormRenderingService, provideTranslations } from '@alfresco/adf-core';
import { MaterialModule } from './material.module'; import { MaterialModule } from './material.module';
@ -53,14 +53,7 @@ import { ProcessUserInfoModule } from './process-user-info/process-user-info.mod
ProcessServicesPipeModule ProcessServicesPipeModule
], ],
providers: [ providers: [
{ provideTranslations('adf-process-services', 'assets/adf-process-services')
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: 'adf-process-services',
source: 'assets/adf-process-services'
}
}
], ],
exports: [ exports: [
CommonModule, CommonModule,
@ -83,14 +76,7 @@ export class ProcessModule {
return { return {
ngModule: ProcessModule, ngModule: ProcessModule,
providers: [ providers: [
{ provideTranslations('adf-process-services', 'assets/adf-process-services'),
provide: TRANSLATION_PROVIDER,
multi: true,
useValue: {
name: 'adf-process-services',
source: 'assets/adf-process-services'
}
},
FormRenderingService, FormRenderingService,
{ provide: FormRenderingService, useClass: ProcessFormRenderingService } { provide: FormRenderingService, useClass: ProcessFormRenderingService }
] ]