Cognito Search
Go to App

Multi-language

Multi-language feeds

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.

How it works

Each record appears once per language, marked with a language attribute on the record element:

Feed typeRecord elementAttribute
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>

Every field can differ per market

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.

Configuring languages

Each search instance declares which content languages it serves and which one is the default. The declared list is authoritative:

  • A record in a language the instance does not declare is skipped, and the import job reports how many records were dropped.
  • A declared language with no records in the feed produces no index, and the job reports it as missing.

Both appear on the import job in the admin application, so a half-translated feed is visible rather than silent.

Searching in a language

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.

Switching language without a page reload

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.

Querying the API directly

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.

Already generating one feed per language?

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:

  1. Concatenate your existing per-language feeds into one document.
  2. Add language="xx" to each record, matching the feed it came from.
  3. Register a single source and declare those languages on the instance.

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.

Category and brand references

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.