NestboltNestbolt

@nestbolt/translatable

Configuration

Configure @nestbolt/translatable with forRoot and forRootAsync -- default locale, fallback chains, and fallback-any behavior.

The TranslatableModule supports both static (forRoot) and async (forRootAsync) configuration. The module is registered as global, meaning you only need to import it once in your root AppModule.

Static Configuration (forRoot)

Use forRoot when your configuration values are known at compile time:

import { Module } from "@nestjs/common";
import { TranslatableModule } from "@nestbolt/translatable";

@Module({
  imports: [
    TranslatableModule.forRoot({
      defaultLocale: "en",
      fallbackLocales: ["en", "fr", "ar"],
      fallbackAny: false,
    }),
  ],
})
export class AppModule {}

Async Configuration (forRootAsync)

Use forRootAsync when your configuration depends on other providers, such as ConfigService or environment variables:

import { Module } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { TranslatableModule } from "@nestbolt/translatable";

@Module({
  imports: [
    ConfigModule.forRoot(),
    TranslatableModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: (config: ConfigService) => ({
        defaultLocale: config.get<string>("DEFAULT_LOCALE", "en"),
        fallbackLocales: config.get<string>("FALLBACK_LOCALES", "en,fr").split(","),
        fallbackAny: config.get<boolean>("FALLBACK_ANY", false),
      }),
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}

The useFactory function can return either a TranslatableModuleOptions object or a Promise<TranslatableModuleOptions> for async initialization:

TranslatableModule.forRootAsync({
  imports: [ConfigModule],
  useFactory: async (config: ConfigService) => {
    // Async operations are supported
    const localeConfig = await fetchLocaleConfigFromRemote();
    return {
      defaultLocale: localeConfig.default,
      fallbackLocales: localeConfig.fallbacks,
    };
  },
  inject: [ConfigService],
});

Configuration Options

OptionTypeDefaultDescription
defaultLocalestring"en"The default locale used when no locale is explicitly set via middleware or runWithLocale.
fallbackLocalesstring[][defaultLocale]An ordered list of fallback locales. When a translation is missing in the requested locale, the system tries each locale in this list in order.
fallbackLocalestring--Deprecated. Shorthand for a single fallback locale. Sets fallbackLocales: [value]. Use fallbackLocales instead.
fallbackAnybooleanfalseWhen true, if no translation is found in the requested locale or any fallback locale, the system returns the first available translation in any locale.

Options in Detail

defaultLocale

The defaultLocale is the locale used when no Accept-Language header is present and no locale has been set via TranslatableService.runWithLocale(). It is also the implicit locale for methods like getTranslation and hasTranslation when no locale argument is provided.

TranslatableModule.forRoot({
  defaultLocale: "ar",
})

With this configuration, calling entity.getTranslation("name") without a locale argument will return the Arabic translation.

fallbackLocales

The fallbackLocales option defines the order in which the system searches for a translation when the requested locale is not available. The system walks the chain from left to right and returns the first match.

TranslatableModule.forRoot({
  defaultLocale: "en",
  fallbackLocales: ["en", "fr", "ar"],
})

With this configuration, if a user requests German (de) and the entity has:

{ fr: "Bonjour", ar: "مرحبا" }

The resolution proceeds:

  1. Check de -- not found
  2. Check en (first fallback) -- not found
  3. Check fr (second fallback) -- found, return "Bonjour"

If fallbackLocales is not provided, it defaults to [defaultLocale].

fallbackAny

When fallbackAny is set to true, the system will return the first available translation in any locale if both the requested locale and all fallback locales are missing. This is a last-resort behavior.

TranslatableModule.forRoot({
  defaultLocale: "en",
  fallbackLocales: ["en"],
  fallbackAny: true,
})

With this configuration, if a user requests German (de) and the entity has only a Japanese translation:

{ ja: "ラップトップ" }

The resolution proceeds:

  1. Check de -- not found
  2. Check en (fallback) -- not found
  3. fallbackAny is true -- return first available: "ラップトップ"

Without fallbackAny: true, the result would be null.

Full Locale Resolution Order

When resolving a translation, the system follows this order:

  1. Requested locale -- The locale from the Accept-Language header, or the locale passed to getTranslation(key, locale).
  2. Fallback chain -- Each locale in fallbackLocales, tried in order.
  3. Any available locale -- If fallbackAny: true, the first available translation in any locale.
  4. null -- If no translation is found anywhere.

Module Registration

The module registers the following providers globally:

ProviderDescription
TranslatableServiceCentral service for locale management, fallback resolution, and event emission.
TranslatableSubscriberTypeORM subscriber that ensures translation maps are properly serialized/deserialized.
TranslatableInterceptorGlobal interceptor that resolves translatable fields in API responses.
IsTranslationsConstraintValidation constraint for the @IsTranslations() decorator.

Because the module is global, all of these providers are available for injection in any module without additional imports.