Right now, we don't hard-code any English strings in Codex, instead we make our users pass in strings through required props, e.g. the `closeButtonLabel` prop in the Dialog component. This keeps Codex internationalizable, but it puts the burden of internationalization on the user.## Problem statement
We should consider instead `inject()`ing these strings into the components that need them. Users would be expected to `provide()` internationalized strings in the appropriate language, and if they don't we'd fall back on hard-coded English strings in Codex. We could take inspiration from [[https://vuetifyjs.com/en/features/internationalization/#vue-i18n|how Vuetify integrates with vue-i18n]]Many components contain text, and have the user provide an i18n function (that takes a key + params and returns an i18n-ed string) instead of an object/map;either visible or for assistive technology, that way we'd provide a lot of flexsupports UX and accessibility to plug in any i18n system (vue-i18n,and is generally the same across features. MW i18n and a simple map of strings are all easy to integrate this way).Some examples:
This change would make using Codex in MediaWiki easier and more consistent, because we wouldn't have to rely on e.g. every use of Dialog using the same i18n message for its `closeButtonLabel`.- The visually-hidden "Close" label on the Dialog's icon-only close button
- The visually-hidden "Select row" and "Select all rows" checkbox labels in the Table component
- The visually-hidden aria-description on each InputChip, e.g. "Press Enter to edit or Delete to remove", in the ChipInput
Since there is no i18n system in Codex, we require that these strings be passed in as props. In MediaWiki, users can pass in a `mw.msg()` call to pass in a translated message. Outside of MediaWiki, users must either provide a translatable string somehow, or pass in a hard-coded value.
This is problematic for a few reasons:
- The dev user must provide these props every time, when the messages are usually the same across features
- Extra props are confusing and noisy
- It puts the burden of meeting accessibility criteria on the dev user, when Codex should be providing this by default
We have also used these string props to enable certain features. For example, to enable the Message component's dismiss button, you would think you'd use a boolean prop like `useDismissButton`. Instead, if you provide the string prop `dismissButtonlabel`, the dismiss button is enabled. This is counterintuitive.
---
## Implementation
### Ideal future state
A better system would include:
- Translated messages for the default values of these common strings
- A way to override these strings if needed (more on this below)
- A way to plug an existing i18n system, such as MediaWiki's, into Codex
- A basic i18n system provided by default in Codex
#### MVP state
For the most basic solution to this problem, we could rely on MediaWiki's i18n system, and only provide translated strings inside MediaWiki. We would:
- Create a directory of messages for Codex in MediaWiki core (or in Codex if we're able to hook that up to translatewiki easily. If not, we can create the messages in core for now, and move them to Codex later)
- Build a plugin in Codex that injects a provided i18n function* (see [[ https://gerrit.wikimedia.org/r/c/design/codex/+/958566 | proof-of-concept ]]). In core, provide `mw.msg()`.
- Update components with affected props in 1 of 2 ways:
- For props that should never be customized (like the InputChip's `aria-description`), remove the existing prop and use the composable to seek a provided message. If there isn't one, fall back to a hard-coded English string.
- For props that should be customizable, update the prop to be optional, and add a boolean prop to enable the feature if needed. Then decide which message to show, depending on whether one is provided, whether the prop is provided, or whether the default English string should be used.
*Further implementation notes: We could take inspiration from [[https://vuetifyjs.com/en/features/internationalization/#vue-i18n|how Vuetify integrates with vue-i18n]], and have the user provide an i18n function (that takes a key + params and returns an i18n-ed string) instead of an object/map; that way we'd provide a lot of flexibility to plug in any i18n system (vue-i18n, MW i18n and a simple map of strings are all easy to integrate this way).
### Inventory of affected string props
**Note that this needs to be updated, especially with further Table props**
This change would allow us to replace the following props with i18n messages:
- ChipInput: `removeButtonLabel`
- ChipInput: (potentially) a screen reader description explaining how to edit/delete a chip using the keyboard, if we end up adding one as part of T344849
- Dialog: `closeButtonLabel` (Note: we might need a new prop to allow the close button to be shown/hidden)
- Field/Label: `optionalFlag` (maybe? we may want to keep allowing arbitrary text here)
- Message: `dismissButtonLabel` (Note: we would need a new prop to allow the dismiss button to be shown/hidden)
- SearchInput: `buttonLabel` (maybe?)
- Table: `selectAllLabel`
- Table: `selectRowLabel`
- Table: A whole bunch of strings for pagination UI parts
- TypeaheadSearch: `searchResultsLabel`
- TypeaheadSearch: `buttonLabel` (maybe?)
---
## Open questions
- How can we build this iteratively, with an initial MVP and then further development?
- How should we prioritize the MVP and the further development?
- What exactly are our end goals for the full solution?
- How will we handle the fact that these are breaking changes?