Localization, Markets and Pricing
The OpenTiendas API supports localized content (i18n) and market-specific pricing (l10n) for resources such as brands, categories, products and more.
Localization in OpenTiendas covers two related areas:
- Content localization (i18n): localized text, HTML, SEO metadata, etc.
- Pricing localization (l10n): prices and discounts for the corresponding commercial market.
OpenTiendas uses locales to request localized content. Each locale deterministically resolves to a single market for pricing.
Selecting locales
To request localized data, use the locales query parameter.
The parameter supports multiple values, separated by commas.
GET /api/v1/brands?locales=fr-FR,es-ES
Behavior:
- If
localesis not provided, the API defaults to the store’s primary locale (e.g.es-ES). - If
localesis provided, the API returns exactly the requested locales. - If
locales=ALL, the API returns all locales enabled for the store.
If any requested locale is not supported by the store, the API returns 400 Bad Request with a descriptive error message.
Only locales configured in the store can be requested.
locales=ALL is intended for export/synchronization use cases.
Avoid it in high-frequency polling, large list endpoints or latency-sensitive integrations.
Locales vs Markets
OpenTiendas uses separate concepts for content localization, pricing and the customer's destination country:
- A locale selects the content language and format (and localized URLs where applicable).
- A market selects the pricing context used for product prices and pricing rules.
- A country represents the destination country selected for the customer's storefront context. It is used to determine country-specific behavior such as the displayed currency and the default shipping zone.
In OpenTiendas, each supported locale resolves to exactly one pricing market. Multiple locales may resolve to the same market.
On the storefront, multiple countries may share the same locale and market. Changing the selected country may change the displayed currency and shipping conditions without changing the locale or pricing market.
Locale → Market mapping
The following table shows how supported locales are mapped to pricing markets and the countries currently covered by each market:
| Locale(s) | Resolved market key (used in prices) | Countries |
|---|---|---|
es-ES, ca-ES | market_es | Spain |
fr-FR | market_fr | France, Belgium |
de-DE | market_de | Germany, Austria |
it-IT | market_it | Italy |
en-GB, nl-NL, sv-SE, da-DK, pl-PL, el-GR, hr-HR | market_en | United Kingdom, Czechia, Denmark, Netherlands, Norway, Poland, Sweden, Finland, Greece, Ireland, Slovakia, Israel, Croatia |
pt-PT | market_pt | Portugal |
ru-RU | market_ru | Russia |
Market identifiers are platform-defined (for example, market_fr) and do not need to match locale or country codes.
Always read market keys from the API response and do not infer them from locale or country codes.
Translatable fields and translations
All translatable fields appear only inside translations.
Translatable fields are never returned at the root level.
translations is a dictionary:
- Keys are locale codes (e.g.
es-ES) - Values are objects containing the localized fields for that locale
When you request a translatable field by name (e.g. name, meta_title), it will appear under translations.{locale} for each requested locale.
Example: Brand with localized fields
GET /api/v1/brands/2?locales=es-ES,fr-FR&fields=internal_id,name,description,meta_title,meta_description,slug
{
"id": 2,
"internal_id": "BRAND-001",
"translations": {
"es-ES": {
"name": "Marca de ejemplo",
"description": "Descripción en español",
"meta_title": "Meta title ES",
"meta_description": "Meta description ES",
"slug": "example-brand"
},
"fr-FR": {
"name": "Marque d'exemple",
"description": "Description en français",
"meta_title": "Meta title FR",
"meta_description": "Meta description FR",
"slug": "example-brand"
}
}
}
In this example:
idis a fixed field (always returned).- The other fields are included because they were explicitly requested via
fields.
If you request a single locale (for example, locales=fr-FR), translations will contain a single key.
The keys present in translations correspond exactly to the locales requested via the locales parameter.
For a given locale, fields may be present with empty values or null, depending on their applicability and state.
Translatable fields are controlled using the same fields mechanism described above;
their values are always returned under translations.{locale}.
Pricing (prices)
Pricing is not part of translations.
For resources where pricing applies (typically variants), pricing is returned under a dedicated prices object.
prices is keyed by market identifiers (for example, market_es, market_fr).
In OpenTiendas, pricing resolution is deterministic:
- each requested locale resolves to exactly one market
- pricing is returned for the markets derived from the requested locales
If multiple requested locales resolve to the same market, the response will include a single prices.{market_*} entry.
As a result, you may receive fewer market entries than locales requested.
Prices are always returned in the market’s canonical currency.
If the platform displays prices in other currencies (for example, based on user preference), those values are derived from the canonical market price using currency conversion.
Currency conversion does not modify the underlying market price.
Example: Variant prices by market
{
"id": 2001,
"sku": "CAM-BLANCA-M",
"prices": {
"market_es": {
"selling_price": { "amount": "18.00", "currency": "EUR" }
},
"market_fr": {
"selling_price": { "amount": "19.00", "currency": "EUR" }
}
}
}
Do not assume that prices uses the same keys as translations.
translations is keyed by locale, while prices is keyed by market.
Updating translations
To create or update localized fields, send them inside the translations object in your POST or PATCH payload.
Each key in translations represents a locale, and its value contains the localized fields to set for that locale.
Only provided fields are updated; others remain unchanged.
{
"translations": {
"en-GB": {
"description": "New English description"
},
"es-ES": {
"meta_description": "Nueva meta descripción"
}
}
}
Supported locales
Locale codes follow the format language-COUNTRY (for example, es-ES, fr-FR).
Supported locales are configured per store.
If you request a locale that is not enabled for the store, the API returns 400 Bad Request.
es-ES— Spanish (Spain)ca-ES— Catalan (Spain)en-GB— English (United Kingdom)fr-FR— French (France)it-IT— Italian (Italy)pt-PT— Portuguese (Portugal)de-DE— German (Germany)ru-RU— Russian (Russia)nl-NL— Dutch (Netherlands)sv-SE— Swedish (Sweden)da-DK— Danish (Denmark)pl-PL— Polish (Poland)el-GR— Greek (Greece)hr_HR— Croatian (Croatia)
Locales outside this list (for example, es-AR, en-AU) are not currently supported and will result in a 400 Bad Request.
Common translatable fields
Common translatable fields (depending on the resource) include:
namedescriptionbody_htmllong_namemeta_titlemeta_descriptionslugslug_baseurl
For the full list of translatable fields per resource, refer to the API Reference.