Js-lingui: Feature: add metadata to translations

Created on 31 Mar 2018  路  15Comments  路  Source: lingui/js-lingui

I want to add some metadata to translations, which would also be extracted during lingui extract process.

Use case: I want to specify the type of the translation, and later, in the CMS, present the user with a different editor based on the translation type.

<h1 className="title">
    <Trans id="home.title" type="text">Page title</Trans>
</h1>
<p className="lead">
    <Trans id="home.lead"" type="textarea">Some long text, bla bla. Users will expect textarea or maybe even rich text editor</Trans>
</p>

I can write a custom extractor to do this, but I was thinking that maybe we can add support for this directly to the Lingui library. What do you think? Does it make sense to put metadata on translations?

To be honest, I haven't really thought things through :) I mean, I could easily write my message ids like ${ns}.${key}.${type} :)

<Trans id="home.title.text" />
or
<Trans id="home.lead.textarea" />
馃挕new feature 馃摝 plugin-extract

Most helpful comment

Yeah, it should be possible to add metadata when using lingui format.

One metadata I have thought about was help text for translators:

{/* i18n: Month */}
<Trans>May</Trans>

This would be extracted as:

{
  "May": {
    "translation": "",
    "help": "Month",
    "origin": [...]
  }
}

We might use similar syntax to add other meta tags depending on usecase.

I'm not 100% convinced about type as it could be guessed from content (e.g: type = text, markdown, icu).

All 15 comments

Yeah, it should be possible to add metadata when using lingui format.

One metadata I have thought about was help text for translators:

{/* i18n: Month */}
<Trans>May</Trans>

This would be extracted as:

{
  "May": {
    "translation": "",
    "help": "Month",
    "origin": [...]
  }
}

We might use similar syntax to add other meta tags depending on usecase.

I'm not 100% convinced about type as it could be guessed from content (e.g: type = text, markdown, icu).

I'm not sure if I understand how you can guess the type of the content if you only have message id.

I would prefer to use props, but I can work around it by using comments and parsing content type from help string.

I can maybe give a PR a try. Just to see how it goes.

How about instructions or hint? Wordpress (which I know almost nothing about) uses a combo of structured type and instructions.

screen shot 2018-04-03 at 16 57 10

I think it would make much more sense to allow keys to be dynamic. If we add i18n or instructions or hint, people will use that, but sooner or later someone will need something else, for whatever reason.

But if we allow dynamic keys, people can do whatever they want (even misuse them).

<Trans id={'message.id'} meta={{ type: 'editor', help: 'Instructions bla bla' }}>Default translation</Trans>

Parser could remove meta prop from the production code, or we simply ignore it.

Not sure how it would go with comments

{/* i18n type: 'editor', help: 'Instructions bla bla' */}
<Trans id={'message.id'}>Default translation</Trans>

In any case, I can parse the i18n string with something like parse-key-value

{/* i18n: key1=value1;"key 2"="value 2" */}
<Trans id={'message.id'}>Default translation</Trans>

It's a bit ugly, but I can live with that.

Ohhh Wordpress ... please just don't :P

react-intl have a description prop which can be an object.

@langpavel Indeed, that would be helpful! I'm planning to add support for description as string, but having an object would solve this problem as well. Is it documented somewhere? I tried to google it briefly right now and I've found just examples with description as strings...

For anyone interested working on this:

  • [ ] use next branch
  • [ ] first part is tricky - figure out the best API for js.macro (react.macro is easy, because it's just another attribute on component)
// When using keys for IDs, it's easy - just another argument
t("message.id", "description")`Default message`

// How about when using messages as IDs?
t(null, "description")`Default message`

// In the end we want to get this
i18n._("message.id", values, { default: "Default message", description })

Ideas welcome! 馃挕

  • [ ] update extract plugin to collect description. Only static values are allowed, not variables.
  • [ ] update cli to collect description metadata
  • [ ] update po file format to include these metadata in comments. lingui format handles it out-of-the-box and minimal format ignores them.

@tricoder42 description in react-intl doc: https://github.com/yahoo/react-intl/wiki/Components#message-descriptor:

type MessageDescriptor = {
    id: string,
    defaultMessage?: string,
    description?: string | object,
};

I've found this, but it doesn't say what's the shape of description if it's an object. Probably something completely arbitrary?

It's up to developer, shape of description may vary

RFC - support metadata in js.macro

t macro is the same as i18n.t what we have now. If we want to add support for metadata, we need to figure out all possible variation, how the macro is used.

Description is type Description = string | Object, where object might contain arbitrary data. Using string is a shortcut for { description: string } object.

The problem is, that using Description as a string clashes with message ID, which is also a string. In simple usecase t("hello")'Message' it's impossible to determine whether hello is ID or description.

Please vote with reaction or suggest different API.

Variation 馃憤

t template tag is used for all messages. Description must be always second argument.

// Message used as an ID, without description
t`Default message`

// Message with custom ID, without description
t("message.id")`Default message`

// Message with custom ID and description
t("message.id", "description")`Default message`

// Message used as an ID, with description
t(null, "description")`Default message`

Variation 鉂わ笍

t template tag is used only for messages used as IDs, t.id template tag is used for messages with custom IDs

``js // Message used as an ID, without description tDefault message`

// Message used as an ID, with description
t("description")Default message

// Message with custom ID, without description
t.id("message.id")Default message

// Message with custom ID and description
t.id("message.id", "description")Default message
``

Actually it seems to be more complex. I wrote down RFC and I would appreciate your comments.

I tried to imagine full developer workflow for both cases: messages used as IDs and messages with custom IDs. Also, there are related usecases (see #258) when developers define common catalogs (similar to defineMessages in react-intl).

Please let me know what you think!

Alright, tracking PR is #274, let's move the discussion there. t.id seems to be a good choice. In addition there're will be also .lazy variants, t.lazy and t.id.lazy (I'm not 100% sure about the second one). Comments welcome!

Right now only message description supported which was released in v2.7.0.

Was this page helpful?
0 / 5 - 0 ratings