Skip to content

ocaml ppx to reduce boilerplate from react-intl messages

License

Notifications You must be signed in to change notification settings

ahrefs/melange-react-intl

Repository files navigation

melange-react-intl

The react-intl bindings for Melange and a preprocessor that eases the creation of translated messages.

Installation

Install the opam package manager, and then run:

opam pin add melange-react-intl.dev git+https://github.com/ahrefs/melange-react-intl.git#master

Overview

The package consists of two parts:

  • The bindings to the react-intl JavaScript library.
  • The PPX, which allows you to create ReactIntl.messages records without specifying id, significantly reducing the amount of boilerplate code and simplifying the translation process.

Examples

This ReasonML code:

let message: ReactIntl.message = [%intl "I am a message"];

will be compiled to JavaScript as:

var message = {
  id: "168c9a2987fad481c5882847ac102aaf",
  defaultMessage: "I am a message"
};

The PPX also supports messages with a description:

let withDescription: ReactIntl.message = [%intl {msg: "blabla", desc: "I am a description"}];

Usage with the bindings

[@react.component]
let make = () => {
  let intl = ReactIntl.useIntl();
  // Helper functions
  let l = message => intl->ReactIntl.Intl.formatMessage(message);

  <>
    <h1>[%intl "Some header"]->l->React.string</h1>
    <p>[%intl "Some body"]->l->React.string</p>
  </>
}

You can define your own FormattedMessage component, which accepts a ReactIntl.message instead of id and defaultMessage:

/// FormattedMessage.re
[@react.component]
let make = (~item: ReactIntl.message, ~values: Js.t({..})) =>
  <ReactIntl.FormattedMessage id={item.id} defaultMessage={item.defaultMessage} values />;

Draft phrases

If you want to make the extractor ignore some phrases, you can use intl_draft, intl_draft.s, or intl_draft.el extensions. This is helpful if you don't want some draft phrases (likely to change soon) to be sent to translators.

Advanced usage

There is an option to generate a localized string straight from the PPX (without explicit helper functions usage). This option could be helpful if you don't need to change the page language without page reloading.

  • Support for the intl.s annotation:
let message: string = [%intl.s "I am a message"]; // type string

Converts to ⬇️

let message: string = {id: "168c9a2987fad481c5882847ac102aaf", defaultMessage: "I am a message"}->ReactIntPpxAdaptor.Message.to_s;
  • Support for the intl.el annotation:
let element: React.element = [%intl.el "I am a message"];

Converts to ⬇️

let element: React.element = {id: "168c9a2987fad481c5882847ac102aaf", defaultMessage: "I am a message"}->ReactIntPpxAdaptor.Message.to_s->React.string;
  • Support for variables, plural forms, and rich text formatting in payload. In this case, the PPX will return a function instead of ReactIntl.message:
let element: React.element = [%intl.el "I am a message with {variable}"];

Converts to ⬇️

let element: React.element =
  (values: {. "variable": React.element}) => {id: "168c9a2987fad481c5kgmcntg5k3dsd5", defaultMessage: "I am a message with {variable}"}->ReactIntPpxAdaptor.Message.format_to_s(_, values)->React.string;

To use these features, you have to define ReactIntlPpxAdaptor in your app (see the test folder for details).