Skip to main content


The following items in Vendure can be translated:

  • Entities which implement the Translatable interface.
  • Admin UI text labels and messages
  • Server error message

Translatable entities

The following entities implement the Translatable interface:

To understand how translatable entities are implemented, let's take a look at a simplified version of the Facet entity:

export class Facet extends VendureEntity implements Translatable {

name: LocaleString;

@Column({ unique: true })
code: string;

@OneToMany(type => FacetTranslation, translation => translation.base, { eager: true })
translations: Array<Translation<Facet>>;

All translatable entities have a translations field which is a relation to the translations. Any fields that are to be translated are of type LocaleString, and do note have a @Column() decorator. This is because the name field here does not in fact exist in the database in the facet table. Instead, it belongs to the facet_translations table, which brings us to the FacetTranslation entity (again simplified for clarity):

export class FacetTranslation extends VendureEntity implements Translation<Facet> {

@Column('varchar') languageCode: LanguageCode;

@Column() name: string;

@ManyToOne(type => Facet, base => base.translations, { onDelete: 'CASCADE' })
base: Facet;

Thus there is a one-to-many relation between Facet and FacetTranslation, which allows Vendure to handle multiple translations of the same entity. The FacetTranslation entity also implements the Translation interface, which requires the languageCode field and a reference to the base entity.

Loading translatable entities

If your plugin needs to load a translatable entity, you will need to use the TranslatorService to hydrate all the LocaleString fields will the actual translated values from the correct translation.

For example, if you are loading a Facet entity, you would do the following:

import { Facet } from '@vendure/core';
import { LanguageCode, RequestContext, TranslatorService, TransactionalConnection } from '@vendure/core';

export class MyService {

constructor(private connection: TransactionalConnection, private translator: TranslatorService) {}

async getFacet(ctx: RequestContext, id: ID): Promise<Facet | undefined> {
const facet = await this.connection.getRepository(ctx, Facet).findOne(id);
if (facet) {
return this.translatorService.translate(facet, ctx);

async getFacets(ctx: RequestContext): Promise<Facet[]> {
const facets = await this.connection.getRepository(ctx, Facet).find();
return Promise.all( => this.translatorService.translate(facet, ctx)));

Admin UI translations

See the Adding Admin UI Translations guide.

Server message translations

Let's say you've implemented some custom server-side functionality as part of a plugin. You may be returning custom errors or other messages. Here's how you can provide these messages in multiple languages.

Using addTranslation inside the onApplicationBootstrap (Nestjs lifecycle hooks) of a Plugin is the easiest way to add new translations. While Vendure is only using error, errorResult and message resource keys you are free to use your own.

Translatable Error

This example shows how to create a custom translatable error

* Custom error class
class CustomError extends ErrorResult {
readonly __typename = 'CustomError';
readonly errorCode = 'CUSTOM_ERROR';
readonly message = 'CUSTOM_ERROR'; //< looks up errorResult.CUSTOM_ERROR

imports: [PluginCommonModule],
providers: [I18nService],
// ...
export class TranslationTestPlugin implements OnApplicationBootstrap {

constructor(private i18nService: I18nService) {


onApplicationBootstrap(): any {
this.i18nService.addTranslation('en', {
errorResult: {
CUSTOM_ERROR: 'A custom error message',
anything: {
foo: 'bar'

this.i18nService.addTranslation('de', {
errorResult: {
CUSTOM_ERROR: 'Eine eigene Fehlermeldung',
anything: {
foo: 'bar'


To receive an error in a specific language you need to use the languageCode query parameter query(QUERY_WITH_ERROR_RESULT, { variables }, { languageCode: });

Use translations

Vendure uses the internationalization-framework i18next.

Therefore you are free to use the i18next translate function to access keys