Scope: Let the user choose which language(s) the Caption column edits, by adding additional Caption columns each bound to a different language; never two columns for the same language; choices persist across reloads.
What's wanted
- The Caption column is currently single-language (English, hard-coded). The header reads "Caption EN" via a placeholder DEFAULT_DESCRIPTION_LANG = "EN" constant in src/table.jsx:1266 whose own comment flags this as the missing piece.
- Users should be able to (a) change the active Caption column's language, or (b) add another Caption column for a different language. Two visible Caption columns for the same language must be impossible.
- The set of languages the user has enabled (and the column count) is part of user preferences, persisted across reloads and across devices via the existing Preferences.json user-store page (no new persistence schema required — see Persistence below).
Where in the code
- src/table.jsx
- TABLE_COLUMNS (~line 70-90): the single description column descriptor with its caption-specific header info popover. Per-language variants need column descriptors that share the same look but each carry a lang field.
- Cell() case "description": (~line 1841) and CellEditor case "description": (~line 2198) read/write item.description. New per-lang cells need to read/write item.descriptions[lang].
- HeaderCell (~line 1268) currently appends DEFAULT_DESCRIPTION_LANG to the label. Replace with the column's lang (uppercased, e.g. "EN", "NL").
- EDITABLE_KEYS (~line 141): the editable check is currently keyed on a static Set<string>. Per-lang columns will need a per-key suffix (description:nl) or a more general predicate that recognises caption keys.
- getAllColumns(customProps) (~line 203): today returns [...TABLE_COLUMNS, ...customProps]. It should also expand the canonical description template into one descriptor per active caption language, so visibility/order/widths flow through without special-casing.
- src/api/normalize.js
- normalizeStashFile (~line 213): descriptions: { en: '', nl: '' } is the existing partial shape. Initialise descriptions as {} and populate from SDC labels in normalizePublishedFile (~line 489) for every language present, not just en.
- src/wikitext-templates.js formatDescription (~line 161): currently emits {{en|...}} and {{nl|...}} only. Make it emit one block per language present in item.descriptions. Keep the legacy fallback for item.description (treat as en).
- src/api/user-store.js DRAFT_FIELDS (~line 395): add descriptions so per-language drafts persist alongside the legacy description string.
- src/columns-modal.jsx: add a way to pick which language to show when adding a Caption column (so two columns can't share a language).
Persistence model
Per the maintainer's "Don't persist derived data" rule, only the user's choices belong in Preferences.json:
- The list of caption-column descriptors (which languages, which order, which widths) is already stored as part of columnState.visible / columnState.order / columnState.widths. No new pref shape needed — the column key is the language identifier (e.g. description:nl).
- The legacy "description" key (no suffix) continues to mean "English caption" for backward compat with stored prefs / drafts.
- Per-language draft text (descriptions[lang]) goes through setDraft like any other field, into Metadata.json (user-authored content).
Default language
- The base column key description keeps meaning English (zero migration cost, matches the existing data flow).
- Adding a new caption column defaults to the user's browser locale (navigator.language) two-letter prefix, falling back to en. The duplicate-language guard ensures the picker shows only languages not already on screen.
- The set of offered languages comes from a small static list (the most common Commons languages: en, nl, de, fr, es, it, pt, pl, ru, ja, zh, ar). The list is curated, not exhaustive — this matches the column-defaults approach for licences (a curated catalog).
Acceptance criteria
- User can change the language of the existing Caption column from a header control or the columns modal.
- User can add a second (third, …) Caption column with a different language.
- The duplicate-language guard prevents two visible Caption columns from sharing a language (the picker hides already-used languages).
- Edits to one language do not affect the other.
- The choice (language set, order, count) persists across page reloads in the same browser.
- The choice persists across browsers/devices for the same user (via Preferences.json round-trip).
- The published wikitext emits one {{<lang>|1=...}} block per non-empty caption language, in the order shown in the table.
- Existing rows with the legacy description: "..." field still render under the English column (no data loss).
- npm run build (which runs the undefined-identifier scanner) passes.
Out of scope
- Pushing captions as SDC labels via wbeditentity.labels (currently only claims are pushed via addStructuredData). The on-wiki rendering still relies on the wikitext {{lang|1=...}} blocks. SDC label publish is a separate task.
- Per-row "this row uses these languages" overrides — every visible column applies to every row.
- Unbounded language picker (the curated list is the v1; an autocomplete over MediaWiki's site list can come later if requested).