Skip to content

Reconsider FluentBundle#formatToParts() #383

Open
@eemeli

Description

@eemeli

With the bundle & message API getting reconsidered (most recently in #380), and being redirected to being more explicitly a lowest-level API for messages, I would like for the earlier decision to remove formatToParts() to be reconsidered, possibly to the extent of only providing a formatToParts method on the bundle, rather than format or formatPattern. My main argument here is that it should be an implementation-specific decision for how to stringify or represent each non-string part.

Having looked through the history, the only arguments for this that I've been able to find were from @stasm:

  1. Its only use case in fluent-react was replaced by overlays (Remove MessageFormat.formatToParts #104)
  2. Its implementation was "buggy and under-spec'ed" (Remove MessageFormat.formatToParts #104)
  3. Passing "React elements as props to to interpolate them into the translation [...] is a bad localization practice because it results in the translation being split into multiple strings and then interpolated." (DOM fragments localization for fluent-react #103)

Addressing each of the above concerns in turn:

  1. At least vue-i18n supports what it calls component interpolation, which effectively allows for components (i.e. objects) to be passed through the localization without stringification. For messageformat I've just filed a PR (Add option for array output messageformat/messageformat#242) enabling this by making the output type configurable, such that its compiled formatter functions may return an array of parts instead of a single string. Similar use cases are likely to be found outside of the core Fluent libraries, should a formatToParts method be available.

    For another use case, consider translations that would be used in more than one output format. One that I've encountered personally is using the same translations both in React/HTML as well as plain-text emails. In HTML, it's useful to be able to express emphasis for the same strings as <i>foo</i>, but then use markdown-ish _foo_ in plain-text contexts.

  2. As is, the implementation of the formatting functions is getting refactored, and from that premise it'd be rather easy to define method's behaviour and output.

  3. I don't agree with this assertion. If it were true, why would terms be included in the Fluent spec? Of course it's possible to construct over-complex localizations with interpolated parts, but exactly that is already enabled by terms. Without the variable pass-through that formatToParts would enable, the only currently available solution is to use overlays, and to re-parse the output string as XML before being able to construct the actual output. And that seems rather clumsy.

Implementation-wise, the change here would be minimal. Based on stasm:formatPattern:

  • Return result rather than result.join("") from Pattern() in resolver.js
  • For string patterns, wrap the return from formatPattern() in bundle.js in an array, or allow the function to return either a string or an array.

With those changes, here's an example of what would be possible:

accept-terms = I accept the {$tosLink}.
  .tos = Terms of service
import React from { react } // just for the example

function AcceptTermsLabel({ bundle, href, ...props }) {
  const msg = bundle.getMessage('accept-terms')
  const tos = bundle.formatToParts(msg.attributes.tos)
  const tosLink = <a href={href} key="tos">{tos}</a>
  return (
    <label {...props}>
      {bundle.formatToParts(msg.value, { tosLink })}
    </label>
  )
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions