NestboltNestbolt

@nestbolt/translatable

Reading Translations

Read translations from entities -- get by locale with fallback, list all translations, check existence, and track translation completeness.

This page covers all the methods available for reading translations from your entities. These methods are provided by the TranslatableMixin and are available on any entity that extends it.

getTranslation

Get the translation value for a specific field and locale. Returns string | null.

const product = new Product();
product.setTranslations("name", {
  en: "Laptop",
  ar: "حاسوب محمول",
  fr: "Ordinateur portable",
});

product.getTranslation("name", "en");
// → "Laptop"

product.getTranslation("name", "ar");
// → "حاسوب محمول"

Signature:

getTranslation(key: string, locale?: string, useFallback?: boolean): string | null
ParameterTypeDefaultDescription
keystring--The translatable field name.
localestringCurrent localeThe locale to retrieve. If omitted, uses the locale from AsyncLocalStorage (set by the middleware) or the configured defaultLocale.
useFallbackbooleantrueWhether to use the fallback chain when the requested locale is not available.

Implicit Locale Resolution

When you omit the locale parameter, the method uses the current request locale (set by TranslatableMiddleware) or the configured defaultLocale:

// Inside a request handler where Accept-Language: ar was sent
product.getTranslation("name");
// → "حاسوب محمول" (uses the request's locale)

// Outside a request context (e.g., a cron job)
product.getTranslation("name");
// → "Laptop" (uses defaultLocale, typically "en")

Fallback Behavior

When useFallback is true (the default), the method searches for a translation in this order:

  1. The requested locale
  2. Each locale in fallbackLocales in order
  3. Any available locale (if fallbackAny: true is configured)
  4. Returns null if no translation is found
// Configuration: fallbackLocales: ["en", "fr"]

const product = new Product();
product.setTranslations("name", {
  fr: "Ordinateur portable",
  ar: "حاسوب محمول",
});

// Request German -- not available, falls back through chain
product.getTranslation("name", "de");
// → "Ordinateur portable" (skipped "en", found "fr")

// Disable fallback
product.getTranslation("name", "de", false);
// → null (strict -- only returns "de" or nothing)

Standalone Usage

Without TranslatableModule registered (for example, in unit tests or scripts), locale resolution defaults to "en" and the fallback chain is not available. Direct locale arguments still work:

const entity = new Product();
entity.setTranslation("name", "en", "Laptop");

entity.getTranslation("name", "en");
// → "Laptop"

getTranslations

Get all translations for a field, or all translations for all translatable fields. Returns a TranslationMap (when a key is provided) or a Record<string, TranslationMap> (when no key is provided).

For a Single Field

product.getTranslations("name");
// → { en: "Laptop", ar: "حاسوب محمول", fr: "Ordinateur portable" }

For All Fields

product.getTranslations();
// → {
//   name: { en: "Laptop", ar: "حاسوب محمول", fr: "Ordinateur portable" },
//   description: { en: "A powerful laptop", ar: "حاسوب محمول قوي" }
// }

Filtering by Allowed Locales

You can pass an array of allowed locales to filter the returned translations:

product.getTranslations("name", ["en", "fr"]);
// → { en: "Laptop", fr: "Ordinateur portable" }
// The "ar" translation is excluded

Signature:

getTranslations(key?: string, allowedLocales?: string[]): TranslationMap | Record<string, TranslationMap>
ParameterTypeDescription
keystring (optional)The translatable field name. If omitted, returns translations for all fields.
allowedLocalesstring[] (optional)Only include translations for these locales.

Empty or null values are excluded from the result.

hasTranslation

Check if a translation exists for a field in a specific locale. Returns boolean.

product.hasTranslation("name", "en");
// → true

product.hasTranslation("name", "de");
// → false

When the locale parameter is omitted, it uses the current request locale or the default locale:

// Inside a request where Accept-Language: ar was sent
product.hasTranslation("name");
// → true (checks "ar")

Signature:

hasTranslation(key: string, locale?: string): boolean
ParameterTypeDefaultDescription
keystring--The translatable field name.
localestringCurrent localeThe locale to check.

A translation is considered to "exist" if its value is not null and not an empty string.

getTranslatedLocales

Get an array of all locale codes that have a translation for a specific field.

product.setTranslations("name", {
  en: "Laptop",
  ar: "حاسوب محمول",
  fr: "Ordinateur portable",
});

product.getTranslatedLocales("name");
// → ["en", "ar", "fr"]

Signature:

getTranslatedLocales(key: string): string[]

Empty and null values are excluded. Only locales with actual non-empty translations are returned.

locales

Get all unique locale codes that have translations across all translatable fields on the entity.

product.setTranslations("name", { en: "Laptop", ar: "حاسوب محمول" });
product.setTranslations("description", { en: "A laptop", fr: "Un ordinateur" });

product.locales();
// → ["en", "ar", "fr"]

Signature:

locales(): string[]

This aggregates locales from all translatable fields and returns a deduplicated array.

getTranslatableAttributes

Get the names of all fields marked with @Translatable() on the entity.

product.getTranslatableAttributes();
// → ["name", "description"]

Signature:

getTranslatableAttributes(): string[]

isTranslatableAttribute

Check whether a specific field is marked with @Translatable().

product.isTranslatableAttribute("name");
// → true

product.isTranslatableAttribute("slug");
// → false

Signature:

isTranslatableAttribute(key: string): boolean

Translation Completeness

These methods help you track which locales are missing translations. They are particularly useful for admin dashboards, CI checks, or content management workflows.

getMissingLocales

Get the locales that are missing a translation for a specific field, given a set of expected locales.

product.setTranslations("name", { en: "Laptop", ar: "حاسوب محمول" });

product.getMissingLocales("name", ["en", "ar", "fr", "de"]);
// → ["fr", "de"]

Signature:

getMissingLocales(key: string, locales: string[]): string[]
ParameterTypeDescription
keystringThe translatable field name.
localesstring[]The set of expected locales.

isFullyTranslated

Check whether all translatable fields have translations for every locale in the provided list.

product.setTranslations("name", { en: "Laptop", ar: "حاسوب محمول" });
product.setTranslations("description", { en: "A laptop" });

product.isFullyTranslated(["en"]);
// → true (both fields have "en")

product.isFullyTranslated(["en", "ar"]);
// → false (description is missing "ar")

Signature:

isFullyTranslated(locales: string[]): boolean

getTranslationCompleteness

Get a detailed completeness report for all translatable fields across a set of locales. Returns a nested object mapping each field to each locale with a boolean indicating presence.

product.setTranslations("name", { en: "Laptop", ar: "حاسوب محمول" });
product.setTranslations("description", { en: "A laptop" });

product.getTranslationCompleteness(["en", "ar", "fr"]);
// → {
//   name: {
//     en: true,
//     ar: true,
//     fr: false
//   },
//   description: {
//     en: true,
//     ar: false,
//     fr: false
//   }
// }

Signature:

getTranslationCompleteness(locales: string[]): Record<string, Record<string, boolean>>

This is useful for rendering translation completeness indicators in admin interfaces or for generating reports on content translation status across your application.

Example: Admin Dashboard Completeness Check

@Injectable()
export class TranslationReportService {
  constructor(
    @InjectRepository(Product)
    private readonly repo: Repository<Product>,
  ) {}

  async getIncompleteProducts(requiredLocales: string[]) {
    const products = await this.repo.find();

    return products
      .filter((p) => !p.isFullyTranslated(requiredLocales))
      .map((p) => ({
        id: p.id,
        slug: p.slug,
        completeness: p.getTranslationCompleteness(requiredLocales),
        missingName: p.getMissingLocales("name", requiredLocales),
        missingDescription: p.getMissingLocales("description", requiredLocales),
      }));
  }
}