@sinequa/atomic

Localization

Telling the server which language to answer in, and decoding the multilingual labels it returns.

Two halves, and they are independent. One key tells the server what language to work in; one function decodes the labels it sends back.

Telling the server

uiLanguage is sent as ui-language in the body of every request that has one, and it also becomes the request locale:

one key, two fields
client.configure({ uiLanguage: "fr" });

The resolution order for locale, most specific first:

query.locale uiLanguage navigator.language

So a per-request locale overrides the configuration, and with neither set the browser's language is the fallback. Omitted entirely when nothing is configured, which leaves the server on its own default.

The browser used to have the last word

locale was resolved as body.locale || navigator.language, so an interface switched to English still asked the server in fr-FR — and the server went on localising accordingly. Setting uiLanguage now moves both fields together.

Decoding what comes back

Labels in a Sinequa configuration are multilingual strings: one value carrying every translation.

what a raw label looks like
all[fr]tous[de]alle

Rendered as-is, the user sees every language at once. sysLang(label, locale) returns the right one, and passes a plain string through unchanged — so it is safe to apply to anything.

One language configured, labels decoded
src/localized.ts
import { createAtomicClient, sysLang } from "@sinequa/atomic";

const language = navigator.language;

const client = createAtomicClient({
  app: "<your-app>",
  backendUrl: "https://<your-sinequa-server>",
  // Sent as `ui-language`, and used as the request `locale`.
  uiLanguage: language,
});

await client.initializeConfig();
await client.auth.login();

const result = await client.api.query.search({
  name: "<your-query>",
  text: "report",
  pageSize: 10,
});

// Every label that comes from the configuration goes through sysLang.
for (const aggregation of result.aggregations) {
  console.log(sysLang(aggregation.name, language));

  for (const item of aggregation.items ?? []) {
    // `display` is the label; `value` is what a filter uses. Only the label is
    // multilingual — never decode a value.
    console.log(" ", sysLang(String(item.display ?? item.value), language), item.count);
  }
}

for (const tab of result.tabs ?? []) {
  console.log("tab:", sysLang(tab.display, language));
}

Where multilingual labels appear

Anywhere a label comes from the server's configuration:

Aggregation names, and item displayFacet headings and values
Tab.displayTab labels
CCSortingChoice.displaySort options
CCColumnInfo.label, labelPluralColumn headings
CCScope.displayScope names
Suggestion categoryAutocomplete group headings

Document titles and metadata values are not multilingual — they come from the indexed content, not from the configuration. Passing them through sysLang is harmless but pointless.

Search language versus interface language

Two different things, and the query keeps them apart:

  • uiLanguage — what language the server answers in: labels, messages.
  • questionLanguage on a query — what language the search terms are interpreted in, overriding automatic detection.

A French interface searching English documents sets uiLanguage: "fr" and leaves questionLanguage to detection, or pins it to "en". See Query anatomy.

Pitfalls

What's next

On this page