The following items in Vendure can be translated:
Translatable interface.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:
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 field here does not in fact exist in the database in the table. Instead, it belongs to the table, which brings us to the entity (again simplified for clarity):
namefacetfacet_translationsFacetTranslationThus 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.
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:
See the Adding Admin UI Translations guide.
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 the error, errorResult, message and configurableOperation resource keys you are free to use your own.
This example shows how to create a custom translatable error
To receive an error in a specific language you need to use the languageCode query parameter
query(QUERY_WITH_ERROR_RESULT, { variables }, { languageCode: LanguageCode.de });
The description of a ConfigurableOperationDef — a promotion condition, shipping calculator, collection filter and so on — as well as the label and description of each of its args, can be defined inline as an array of localized strings:
The same strings can also come from the message catalogs, under the configurableOperation namespace. This keeps the operation definition readable when you support many languages, and it lets you translate an operation defined by someone else without forking it.
The keys are derived from the operation, so there is nothing to register:
<type> is the registry the operation belongs to, such as ShippingCalculator or PromotionCondition. It forms part of the key because a code is only unique within its own registry — core itself uses buy_x_get_y_free for both a promotion condition and a promotion action.
So the German strings above could instead be registered like this:
These strings describe the operation itself, so they should appear in whatever language the administrator reads. That is a separate setting from the one which selects the translation of your product and Collection data.
The two come from different parts of the request. The languageCode query parameter selects which translation of your data to return, such as the product name or the Collection description. The Accept-Language header tells the server which language to write its own text in, and operation descriptions and argument labels count as its own text. So an administrator reading the Admin UI in Japanese while editing the German translation of a product sends ?languageCode=de together with Accept-Language: ja, and gets German product data described by Japanese labels.
A client which sends no Accept-Language header falls back to languageCode.
Vendure tries each language in turn: first the languages from Accept-Language in the order the header gives them, then the request's languageCode, then the Channel's default language, then English. For each one it looks in the catalogs first and in the inline array second. A catalog entry therefore wins over an inline string for the same language, which is what allows a third-party operation's wording to be overridden. An operation which ships only inline strings still resolves to its own language rather than to an English catalog entry defined elsewhere.
Select option labels behave slightly differently. The description and the argument labels arrive at the client already resolved to a single string, but option labels are sent as the full array with the catalog translation merged into it. The merged entry carries the language code the client asked for. Clients look up their own display language and fall back to the first entry in the array when they find no exact match, so a translation filed under any other code would be passed over.
Browsers send an Accept-Language header of their own, derived from the operating system locale, on every request. A browser-based Admin API client which relies on languageCode alone for these strings will therefore resolve them against that OS locale.
If you maintain such a client, set the header explicitly from whatever language your interface is displayed in. The Dashboard and the Admin UI both do this. Server-to-server clients are unaffected, since they only send the header if they choose to.
A key which matches no operation is never read, so a mistyped key produces no error — the inline string is used instead and the mistake is silent. Call getTranslationKeys() on the operation, which returns the exact key paths it expects.
Several of core's descriptions are templates rather than sentences, for example Discount order by { discount }%. The admin UI substitutes the live argument values into these placeholders, so a translated string must reproduce them exactly, spaces inside the braces included.
Vendure uses the internationalization-framework i18next.
Therefore you are free to use the i18next translate function to access keys
i18next.t('error.any-message');
Architecture reviews, custom plugin work, migrations, ongoing support. Get a hand from the team that builds Vendure.
Talk to the team