Page MenuHomePhabricator

i18n: Build a system to support common translated strings in components
Open, MediumPublic

Description

Problem statement

Many components contain text, either visible or for assistive technology, that supports UX and accessibility and is generally the same across features. Some examples:

  • 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 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 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?

Event Timeline

Restricted Application added a subscriber: Aklapper. · View Herald Transcript

This is a great idea! Even if we enable use of provide/inject, I think it would make sense to keep the props but just make them optional with the default being the English fallback we choose. If someone is using Codex outside of MediaWiki in a single-language context, they can just use the prop, and we can still use the fallback if nothing is provided.

We may also want to consider providing default messages for these things in MediaWiki if possible. If i remember correctly, this isn't usually recommended, and instead we're supposed to create a new message for every context (even if it means there are a bunch of messages out there that just say "Close"). However, in lieu of the required prop, having a translated fallback would be preferable over defaulting to the hardcoded English fallback.

I agree, I think it makes sense to have a single "Close" message. For the ones I marked with "maybe?", I could see an argument that they could be bespoke for each use case, but I think the others are slam dunk cases where they should always be the same.

This would represent a breaking change for some components. For example, if we change the Message component's dismissButtonLabel prop from required to having a default value of 'Dismiss', that prop can no longer be relied upon to enable the dismiss button. We would need to add a new boolean prop, useDismissButton, to do this. We probably should have done this from the beginning, but it seemed redundant at the time. IMO, this seems like a good case for Codex 2.0.0.

For new props like this, I have suggested in this patch that we go ahead and add the boolean prop, plus string props with default English values. Happy to hear feedback on this plan!

AnneT renamed this task from Set internationalized messages using provide/inject instead of required props to i18n: Build a system to support common translated strings in components.Thu, Apr 25, 8:34 PM
AnneT added a project: Epic.
AnneT updated the task description. (Show Details)

@CCiufo-WMF The engineers discussed this issue this week as it is becoming more prominent with the addition of the Table component, which has many accessibility-related visually-hidden strings that currently must be passed in as props. We see the MVP as critical, potentially even a soft-blocker for Table (we'd at least need to warn our known users about these props), and also something that is fairly achievable in the short-term since it relies on the i18n system already built into core. That said, it would only work in MediaWiki, and given our commitment to MediaWiki-agnosticism and supporting internationalization in a robust way, we would like to see a full solution implemented in the future. I've updated this task to reflect our recent discussion, and we'd like to hear your thoughts on how this might fit in with our other priorities. Let me know if you have any questions or if you'd like to schedule a follow-up conversation!

Let's discuss further at the next sprint planning

CCiufo-WMF triaged this task as Medium priority.Wed, May 1, 2:06 PM

Let's treat this as a follow up to T303320. We should start with a spike to answer the open questions.