How to work with translations in Formly when using ngx-translate

In this guide your will learn how to to make @ngx-formly validation messages and labels translatable when using @ngx-translate translation service.

What we want to achieve

  1. Create a namespace/group in our i18n translations file containing the error messages for formly validation.
  2. Inject TranslateService to FORMLY_CONFIG provider.
  3. Use the TranslateService.stream() method to provide observable translation keys for our messages.
  4. Extend formly to use a registerTranslateExtension factory for our i18n extension.

Set up Formly module

In this example, I have created a standalone module called ClientFormlyModule just for formly. This way, we can import this module inheriting the settings we set up only once. This generally a good practice and decouples your modules for when you have multiple angular apps in your project.

First let's take a look at ClientFormlyModule.

...
import { FormlyModule, FORMLY_CONFIG } from '@ngx-formly/core';

@NgModule({
  declarations: [],
  imports: [
    CommonModule,
    ReactiveFormsModule,
    FormlyModule.forRoot(),
    FormlyBootstrapModule, // Optional
    TranslateModule.forChild(),
  ],
  providers: [
    // Provide `FORMLY_CONFIG` with our settings
    {
      provide: FORMLY_CONFIG,
      multi: true,
      useFactory: registerTranslateExtension,
      // Inject the `TranslateService` into the factory
      deps: [TranslateService],
    },
  ],
  exports: [FormlyModule],
})
export class ClientFormlyModule {}

Notice that in the providers we inject a factory formlyConfig which I will explain next. Notice also that we need the TranslateService as a dependency as well. This makes the instance of TranslateService alliable to our injected method formlyConfig.

The translations file

Depending on how you decided to setup @ngx-translate you might have something similar to this.

export const en_US = {
  FORM: {
    VALIDATION: {
      REQUIRED: "{{fieldName}} cannot be empty",
      EMAIL: "Please enter a valid email address",
    },
  },
};

The idea is to have a namespace/group here to organize the translations for your validation messages.

Create the formlyConfig factory function

When we set up the ClientFormlyModule we specifically told formly to use a factory for it's FORMLY_CONFIG and inject the TranslateService as a dependency.

Therefore we now need to create a method that will take TranslateService as an argument and return ConfigOption interface (used for formly config)

import { ConfigOption, FormlyFieldConfig } from "@ngx-formly/core";
import { TranslateService } from "@ngx-translate/core";

export function formlyConfig(t: TranslateService): ConfigOption {
  return {
    validationMessages: [
      {
        name: "required",
        message(err: Error, field: FormlyFieldConfig) {
          return t.stream("FORM.VALIDATION.REQUIRED", {
            fieldName: field?.templateOptions?.label,
          });
        },
      },

      {
        name: "email",
        message() {
          return t.stream("FORM.VALIDATION.EMAIL");
        },
      },
    ],
    validators: [],
  };
}

That's it! Now formly is aware of our TranslateService and will generate the correct i18n messages for your validation messages.

References

More posts in JavaScript