Skip to content

Internationalisation

Announcements default to English. Every announcement is a function, not a template string, so it can interpolate the position, pluralise properly, and phrase RTL text the way that language actually works.

Other languages are opt-in files under src/locales/. Loading one — after the library itself — registers it on Sorta11y.locales under its language code, and from then on the name is all you need:

<script src="sorta11y.js"></script>
<script src="sorta11y/locales/de.js"></script>
<script>
Sorta11y.setDefaultLabels("de"); // make German the default…
const list = Sorta11y.create(el, { locale: "de" }); // …or pick per instance
</script>

Importing the file registers it the same way — with a bundler, in Node, as CommonJS or as ESM:

import Sorta11y from "sorta11y";
import "sorta11y/locales/de"; // registers Sorta11y.locales.de
const list = Sorta11y.create(el, { locale: "de" });

Each locale file also exports its labels object, so you can pass that directly instead of a name:

import de from "sorta11y/locales/de";
Sorta11y.setDefaultLabels(de);

A fully custom labels object always wins. This is the integration path for apps that already ship their own translation files — you do not have to adopt a second translation system for one widget:

Sorta11y.create(el, {
labels: {
grabbed: (c) =>
`Aufgenommen: ${c.itemLabel}. Position ${c.position} von ${c.total}.`,
dropped: (c) => `Abgelegt an Position ${c.position} von ${c.total}.`,
moved: (c) => `Position ${c.position} von ${c.total}.`,
cancelled: (c) => `Abgebrochen. Zurück auf Position ${c.position}.`,
instructions:
"Sortierbar. Leertaste zum Aufnehmen, Pfeiltasten zum Bewegen, " +
"Leertaste zum Ablegen, Escape zum Abbrechen.",
applicationLabel: "Sortierbare Liste",
},
});

Partial objects are fine — anything you leave out falls back to the locale or the built-in English.

KeyTypeUsed for
instructionsstringThe hidden aria-describedby text on each item
applicationLabelstringAccessible name for the role="application" wrapper, when the list has no aria-label/aria-labelledby to mirror
grabbedfunctionAnnounced on pickup
movedfunctionAnnounced after each move while held
droppedfunctionAnnounced on drop
cancelledfunctionAnnounced on Esc or an aborted grab or drag

Each key also takes the other type: a string for an announcement is spoken verbatim, and a function for instructions or applicationLabel is called with an empty context.

Each label function receives one argument:

const context = {
itemLabel, // string — the item's aria-label, data-label, or text content
position, // number — 1-indexed
total, // number — item count
announceTotal, // boolean — the announceTotal option, for "of Y" phrasing
order, // (string | null)[] — the current order of data-ids (null: no id)
};

itemLabel can be an empty string when an item has no accessible name, so guard against it the way the built-in labels do:

const name = (c) => (c.itemLabel ? `${c.itemLabel}, ` : "");

From lowest to highest — the later one always wins:

  1. Built-in English
  2. Sorta11y.setDefaultLabels(labelsOrLocale) — a global default
  3. { locale: "de" } — per instance
  4. { labels: {…} } — per instance, explicit

setDefaultLabels() applies to lists created after the call, so call it before create() or autoInit(). An unknown locale name falls back to the built-in English.

Both labels and locale can be swapped at runtime; the hidden instructions element is re-rendered so it follows the switch:

list.option("locale", "de"); // the locale file must already be loaded

The rtl option (and data-rtl with autoInit()) is reserved and currently has no effect. The list only moves up and down, so there is no axis to mirror; right-to-left languages are handled by the label functions, which can phrase each announcement however the language needs.

Locale files are small and self-registering — copy src/locales/en.js, translate the functions, and open a PR. Announcement wording is a genuinely hard thing to get right in a second language, so translations from native speakers who actually use a screen reader are especially welcome.