Multi-language
One feed can carry every language version of your content. You do not need a separate feed, source, or search instance per language — the widget picks the language at query time.
Each record appears once per language, marked with a language attribute on
the record element:
| Feed type | Record element | Attribute |
|---|---|---|
| Products | <item> | language="cs" |
| Articles | <article> | language="cs" |
| Categories | <category> | language="cs" |
| Brands | <brand> | language="cs" |
The attribute is optional. A record without it is treated as being in the search instance's default content language, so an existing single-language feed keeps working unchanged — you can adopt multi-language content incrementally.
<?xml version="1.0" encoding="UTF-8"?><items><item language="cs"><title>Ibalgin 400 mg, 24 tablet</title><shop_item_id>SKU-4711</shop_item_id><url>https://shop.cz/ibalgin-400</url><availability>1</availability><price>89 Kč</price><category primary="true">Bolest a teplota | Volne prodejne leky</category><brand>Sanofi</brand><product_code>IBA-400-24</product_code></item><item language="de"><title>Ibalgin 400 mg, 24 Tabletten</title><shop_item_id>SKU-4711</shop_item_id><url>https://shop.de/ibalgin-400</url><availability>0</availability><price>3,49 €</price><availability_rank_text>Nicht lieferbar</availability_rank_text><category primary="true">Schmerz und Fieber | OTC-Arzneimittel</category><brand>Sanofi</brand><product_code>IBA-400-24</product_code></item></items>
Because the whole record repeats, every field may differ between languages —
not just text. That includes price, price_old, availability,
availability_rank_text, url, labels, and the image links.
This is deliberate: a language version is usually also a market, with its own
currency, stock, and URL space. Keep shop_item_id (or identify, or
hierarchy) the same across languages so the same product is recognisable across
markets.
Each search instance declares which content languages it serves and which one is the default. The declared list is authoritative:
Both appear on the import job in the admin application, so a half-translated feed is visible rather than silent.
The widget's lang option selects the content language:
<script>SearchWidget.init({instanceId: 'YOUR_INSTANCE_ID',lang: 'de',});</script>
lang also selects the widget's own interface strings. The two are independent:
if the interface is not translated into your content language it falls back to
English, while results still come back in the language you asked for.
An unknown or undeclared lang falls back to the instance's default content
language. It never returns an error, so a typo in an embed snippet degrades
gracefully instead of breaking search on your site.
If your language switcher reloads the page, you are done — each load passes the
right lang to init and there is nothing else to do.
If it swaps the language in place, tell the widget:
// Your language switcher, wherever it lives:SearchWidget.setLanguage('de');// On a page with more than one widget, pass the target you mounted into:SearchWidget.setLanguage('de', '#search-box');
The widget re-runs the current query against the new market and clears any
active category filters. Call it after widget.js has loaded — the Google Tag
Manager snippet queues init calls, not setLanguage.
There is no equivalent for init: calling it twice on the same element is
ignored, so that a Tag Manager tag re-firing on a single-page-app route change
cannot mount a second widget on top of the first.
If you call the search API yourself instead
of using the widget, pass the same lang as a query parameter. The fallback
rules are identical, and it is your code's job to drop active category filters
when the language changes.
Some search providers require a separate feed and a separate index per language version. If that is how your feeds are set up today, you do not have to restructure them — this format accepts them as they are, so combining them is mechanical:
language="xx" to each record, matching the feed it came from.Everything else about the feed stays as it is. If you would rather keep separate feeds per language for now, that also works — register each as its own source with the matching default language.
Categories are referenced by their hierarchy path and brands by name, exactly as in a single-language feed. References resolve within one language, so a Czech product's category path is matched against your Czech category feed and a German product's against your German one. Emit the paths in the same language as the record that references them.
Because category identifiers are per language, a visitor switching language
starts with filters cleared rather than carrying an untranslatable filter across.
The widget does this for you on setLanguage; if you build your own filtering
UI on top of the API, drop the active categories filter yourself when lang
changes, or you will filter German results by a Czech category path and get
nothing back.