Localize custom Freedom UI component implemented using remote module
Localize custom Freedom UI components during development to save time spent on translating the ready-to-use custom Freedom UI component. Implementing localization during development ensures that all user-facing labels, captions, and messages are available in the target language from the first release. This applies to custom Freedom UI components as well as related artifacts such as validators, converters, and request handlers implemented using remote module.
Localize custom validators, converters and request handlers
Localize custom validators, converters and request handlers using Angular DI (dependency injection). To do this:
-
Import the
SysValuesServicefunctionality from the@creatio-devkit/commonlibrary into the component to retrieve the Creatio UI language of the current user from theuserCulturesystem variable. We recommend using a dedicated function for this purpose. TheSysValuesServiceservice is used to localize system variables."some-component.component.ts" file/* Import the required functionality from the libraries. */import { SysValuesService } from '@creatio-devkit/common';constructor() {}public async getUserCulture(): Promise<string> {/* Create an instance of the "SysValuesService" service from "@creatio-devkit/common." */const sysValuesService = new SysValuesService();/* Retrieve the system variable values. */const sysValues = await sysValuesService.loadSysValues();return sysValues.userCulture.displayValue;} -
Use an external library or implement the custom service to localize custom Freedom UI component. We recommend using the
@ngx-translateexternal library for this purpose. Learn more: official vendor documentation (GitHub).For example,
SomeTranslationServiceis an external library or custom service to localize custom Freedom UI components. -
If needed, enable support for emitting type metadata for decorators that works with the
reflect-metadatamodule.- Run the
npm i reflect-metadatacommand in the Visual Studio Code terminal to install thereflect-metadatanpm package if needed. - Open the "tsconfig.json" file of the Angular project and make sure the
experimentalDecoratorsandemitDecoratorMetadataproperties are set totrue.
"tsconfig.json" file{"compilerOptions": {"experimentalDecorators": true,"emitDecoratorMetadata": true,},} - Run the
-
Receive the dependencies of the validator, converter, or request handler.
- Open the "runtime.feature-activation.ts" file.
- Ensure that the existing
bootstrapCrtModule()call contains theresolveDependency()method. ThebootstrapCrtModule()call registers the custom validator, converter, or request handler flagged using the@CrtModuledecorator. - Import the required functionality from the libraries into the file.
- Save the file.
"runtime.feature-activation.ts" file/* Import the required functionality from the libraries. */import { ProviderToken } from '@angular/core';import { bootstrapCrtModule } from '@creatio-devkit/common';import { ensureFeatureModuleRef } from '../../remote-app-context';import { RuntimeFeatureModule } from './runtime-feature.module';export async function activateRuntimeFeature(): Promise<string> {const moduleRef = await ensureFeatureModuleRef(RuntimeFeatureModule);const injector = moduleRef.injector;/* Bootstrap CrtModule definitions. */bootstrapCrtModule('some_package', RuntimeFeatureModule, {/* Receive the dependencies. */resolveDependency: (token) =>injector.get(token as ProviderToken<unknown>),});} -
Retrieve the instance of the translation service.
- Open the file that implements a custom validator, converter or request handler. For example, open the "some-handler.handler.ts" file that implements a custom request handler.
- Receive the instant translated value using the
instant()method. - Import the required functionality from the libraries into the file.
- Save the file.
"some-handler.handler.ts" file/* Import the required functionality from the libraries. */import {BaseRequestHandler,CrtRequestHandler} from "@creatio-devkit/common";import { SomeRequest } from "../requests/some-request.request";import {SomeTranslationService} from "../services/some-translation-service.service";/* Register the SomeHandler as a Freedom UI request handler. */@CrtRequestHandler({type: 'usr.SomeHandler',requestType: 'usr.SomeRequest',})export class SomeHandler extends BaseRequestHandler {constructor(private _someTranslationService: SomeTranslationService) {super();}public async handle(request: SomeRequest): Promise<string> {/* Receive the instant translated value. */const localizedMessage = this._someTranslationService.instant('SomeRequest.Message');alert(localizedMessage);}}
As a result, the custom validator, converter, or request handler will display localized values based on the current Creatio UI language after you add the corresponding artifact to the source code of the Freedom UI page schema.
Localize properties of custom Freedom UI component
-
Use an external library or implement the custom service to localize custom Freedom UI component. We recommend using the
@ngx-translateexternal library for this purpose. Learn more: official vendor documentation (GitHub).For example,
SomeTranslationServiceis an external library or custom service to localize custom Freedom UI components. -
Return translated definitions of Freedom UI Designer.
- Open the "runtime.designer-definitions.ts" file.
- Add the Freedom UI Designer properties of the custom Freedom UI component to the
viewElementscollection returned by theloadRuntimeDesignerDefinitions()function. - Use
context.cultureNameto request the required culture from the shared remote app context. - Return the already translated values of the designer definitions, for example,
captionanddefaultPropertyValues. - Save the file.
The deferred remote entry API does not localize designer definitions automatically. The remote module returns the translated interface designer definitions immediately.
"runtime.designer-definitions.ts" file/* Import the required functionality from the libraries. */import type {RemoteDesignerDefinitionsLoadContext,RemoteFeatureDesignerDefinitions,} from '@creatio-devkit/common';import { ensureRemoteAppContext } from '../../remote-app-context';export async function loadRuntimeDesignerDefinitions(context: RemoteDesignerDefinitionsLoadContext): Promise<RemoteFeatureDesignerDefinitions> {/* The ensureRemoteAppContext() function resolves the shared remoteapplication context, including translateService, using the"remote-app-context.ts" file. */const { translateService } = await ensureRemoteAppContext(context.cultureName);return {viewElements: [{type: 'usr.SomeComponent',toolbarConfig: {/* The localizable name of the component. */caption: translateService.instant('SomeComponent.Caption'),/* The icon of the component displayed in the Freedom UI Designerelement library. Required property. */icon: SOME_COMPONENT_ICON,},defaultPropertyValues: {label: translateService.instant('SomeComponent.DefaultLabel'),},}],};}
As a result, the Freedom UI Designer will display the localized properties of the custom Freedom UI component, such as captions and default values.
Upload the translations of custom Freedom UI component from static content
-
Find the URL to upload the translations. To do this, use the
__webpack_public_path__global variable. Learn more: Public Path (official vendor documentation). -
Create a dedicated localization module. For example, create the "remote-localization.module.ts" file.
-
Add an app initializer that retrieves the Creatio UI language of the current user from the
userCulturesystem variable and passes the value to the translation service."remote-localization.module.ts" file/* Import the required functionality from the libraries. */import { HttpClient, HttpClientModule } from '@angular/common/http';import { APP_INITIALIZER, NgModule } from '@angular/core';import { SysValuesService } from '@creatio-devkit/common';import {TranslateLoader,TranslateModule,TranslateService} from '@ngx-translate/core';import { TranslateHttpLoader } from '@ngx-translate/http-loader';import { firstValueFrom } from 'rxjs';declare const __webpack_public_path__: string;const TRANSLATIONS_PATH = 'assets/i18n/';const DEFAULT_CULTURE_NAME = 'en-US';async function getUserCultureNameFromSysValues(): Promise<string> {try {const sysValues = await new SysValuesService().loadSysValues();return sysValues?.userCulture?.displayValue || DEFAULT_CULTURE_NAME;} catch {return DEFAULT_CULTURE_NAME;}}@NgModule({imports: [HttpClientModule,TranslateModule.forRoot({defaultLanguage: DEFAULT_CULTURE_NAME,loader: {provide: TranslateLoader,useFactory: (httpClient: HttpClient) => new TranslateHttpLoader(httpClient,`${__webpack_public_path__}${TRANSLATIONS_PATH}`,'.json'),deps: [HttpClient],},}),],exports: [TranslateModule],providers: [{provide: APP_INITIALIZER,useFactory: (translateService: TranslateService) => async () => {const cultureName = await getUserCultureNameFromSysValues();await firstValueFrom(translateService.use(cultureName));},multi: true,deps: [TranslateService],}],})export class RemoteLocalizationModule {} -
Import the localization module into the "remote-app.module.ts" file.
-
If needed, import the translation module into the runtime and design feature modules.
"remote-app.module.ts" file/* Import the required functionality from the libraries. */import { BrowserModule } from '@angular/platform-browser';import { DoBootstrap, NgModule } from '@angular/core';import {RemoteLocalizationModule} from './localization/remote-localization.module';@NgModule({imports: [BrowserModule, RemoteLocalizationModule],})export class RemoteAppModule implements DoBootstrap {public ngDoBootstrap(): void {}} -
Extend the shared remote app context. Update
remote-app-context.tsto expose theTranslateServiceand switch the active culture whencultureNameis provided."remote-app-context.ts" file/* Import the required functionality from the libraries. */import { Injector, NgModuleRef, Type, createNgModule } from '@angular/core';import { platformBrowser } from '@angular/platform-browser';import { TranslateService } from '@ngx-translate/core';import { firstValueFrom } from 'rxjs';import { RemoteAppModule } from './remote-app.module';export interface RemoteAppContext {injector: Injector;moduleRef: NgModuleRef<RemoteAppModule>;translateService: TranslateService;}let remoteAppContextPromise: Promise<RemoteAppContext> | undefined;const featureModuleRefs = new Map<Type<unknown>, NgModuleRef<unknown>>();async function bootstrapRemoteApp(): Promise<RemoteAppContext> {const moduleRef = await platformBrowser().bootstrapModule(RemoteAppModule);const injector = moduleRef.injector;return {injector,moduleRef,translateService: injector.get(TranslateService),};}export async function ensureRemoteAppContext(cultureName?: string): Promise<RemoteAppContext> {if (!remoteAppContextPromise) {remoteAppContextPromise = bootstrapRemoteApp().catch((error: unknown) => {remoteAppContextPromise = undefined;throw error;});}const context = await remoteAppContextPromise;if (cultureName && context.translateService.currentLang !== cultureName) {await firstValueFrom(context.translateService.use(cultureName));}return context;} -
Save the files.
As a result, the custom Freedom UI component will load translations from static content and display localized values based on the current Creatio UI language.
See also
Custom Freedom UI component implemented using remote module
Custom validator implemented using remote module
Custom converter implemented using remote module
Custom request handler implemented using remote module
Resources
Public Path (official vendor documentation)