Js-lingui: How to localize text unknown at compile time?

Created on 27 Aug 2018  路  12Comments  路  Source: lingui/js-lingui

Problem: A lot of our application text (or keys, that's irrelevant in this case) comes from a backend. For example, enum types. We'd like to localize those texts too, and we can't use i18nMark because, as discussed at #177, this content is unknown at compile time.

Question: Is there a way to solve this problem using linguijs? Or plans to support this in the future?

Feature request:
Given a fully automated solution seems unfeasible (beyond what was commented at https://github.com/lingui/js-lingui/issues/177#issuecomment-373749758), our current approach is to manually add entries (without an origin) to the catalogs. However, in this case lingui extract marks these entries as obsolete, so we risk deleting them whenever anyone runs lingui extract --clean. Is it even acceptable to manually modify the catalog? Would it be possible to support adding manual entries to the catalog?

wontfix 馃挕new feature 馃摝 cli

Most helpful comment

I'm trying to say that my original proposal isn't about loading locales, but only message definitions. No matter if messages are extracted from source files or remote API, first you always need just message definitions and then you create locale files by merging with existing catalogs. Yes, we could also add loading translations from API, but that's another step.

In your case you would load message IDs and default values:

export default async function loadEnumsFromGraphQL() {
    await response
    return [
        {
            id: 'Episode.NEWHOPE',
            defaults: 'New Hope'
        }, {
            id: 'Episode.EMPIRE',,
            defaults: 'Empire Strikes Back'
        }, {
            id: 'Episode.JEDI',,
            defaults: 'Return of the Jedi'
        }, 
    ]
}

In gettext, the workflow was usually extract file with message definitions messages.pot (mind the pot extension) from source files and then generate locale files en.po, cs.po, etc.

My idea here is similar: query API for message definitions only, because then we can guarantee that each locale files have the same messages (and warn user about missing translations, etc).

Also if export of that file is supposed to be a catalog, how would you tackle asynchronous nature of it? It has to be able to return Promise and wait for it.

Reading from FS is synchronous at the moment, but we could change it to be async and then the messages from all sources would be loaded in parallel.

All 12 comments

No, it's not possible at the moment besides the workaround described at https://github.com/lingui/js-lingui/issues/177#issuecomment-373749758.

If you want to create those files manually, why can't you use i18nMark? Or even better, query the server, get the enums and create a file with i18nMark calls.

Yeah, that's what I meant, one can use i18nMark but only for literal strings, as has been said before. However, it feels kind of messy to have to maintain these auxiliary files just to use that helper. Hence my question: lacking a more automated solution, are there any plans to support manual additions to the catalog? I'm not sure if adding entries to that json is considered acceptable usage or may break things.

At the moment I'm implementing this RFC which improves localeDir config. I guess we could add support for a) static catalogs and b) catalogs loaded from scripts (e.g. query graphql backend)

Something like:

{
   "lingui": {
      "localeDir": {
         // messages.json extracted from source code
         "./locale/{locale}/messages": "./src/",

         // graphql.json loaded from script
         "./locale/{locale}/graphql": "!./scripts/loadGraphQLEnums.js",

         // static.json edited manually
         "./locale/{locale}/static": true,
      }
   }
}

Loading from script

Load message descriptors from local FS or remote API and export them. lingui extract will collect them and create a catalog for them, something like this:

const catalog = loadEnumsFromGraphQL()
// catalog = [
  {
    id: 'Episode.NEWHOPE',
  }, {
    id: 'Episode.EMPIRE',
  }, {
    id: 'Episode.JEDI',
  }, 
]

export default catalog

Generated graphql.json will look like this (depending on format, this is minimal json, where values are translations):

{
    "Episode.NEWHOPE": "",
    "Episode.EMPIRE": "",
    "Episode.JEDI": "",
}

Static files

These files are basically ignored in lingui extract, but messages from them are used in lingui compile.


What do you think?

I must say that I am going to solve something similar very soon. We were thinking something along the lines that BE would be sending just keys with optional variables and all translation happens on FE. That would super easy as it would be just about using i18n._ directly without any extraction needed. The only issue seemed where to get default translation for this keys. The proposition above could help solve that for sure.

One small tweak thou, pass the locale to the exported function so it's possible to ask remote server for correct locale or even ignore it for some locales.

@FredyC Something like this?

"./locale/{locale}/graphql": "!./scripts/loadGraphQLEnums.js {locale}"

@FredyC Actually, the script is called only once to get all available messages. This is the same as extracting messages from source code. Once we have message descriptors, they're merged with any existing translations for each locale.

That's why the script returns a list of messages, rather than a object with translations.

Well, my concern is basically that I can ask server only for English locale because it won't have anything else. For other locales I would return empty catalog so it can be translated later. I am thinking something along these lines.

export default async function loadEnumsFromGraphQL(locale) {
    await response
    return [
        {
            id: 'Episode.NEWHOPE',
        }, {
            id: 'Episode.EMPIRE',
        }, {
            id: 'Episode.JEDI',
        }, 
    ]
}

Also if export of that file is supposed to be a catalog, how would you tackle asynchronous nature of it? It has to be able to return Promise and wait for it.

I'm trying to say that my original proposal isn't about loading locales, but only message definitions. No matter if messages are extracted from source files or remote API, first you always need just message definitions and then you create locale files by merging with existing catalogs. Yes, we could also add loading translations from API, but that's another step.

In your case you would load message IDs and default values:

export default async function loadEnumsFromGraphQL() {
    await response
    return [
        {
            id: 'Episode.NEWHOPE',
            defaults: 'New Hope'
        }, {
            id: 'Episode.EMPIRE',,
            defaults: 'Empire Strikes Back'
        }, {
            id: 'Episode.JEDI',,
            defaults: 'Return of the Jedi'
        }, 
    ]
}

In gettext, the workflow was usually extract file with message definitions messages.pot (mind the pot extension) from source files and then generate locale files en.po, cs.po, etc.

My idea here is similar: query API for message definitions only, because then we can guarantee that each locale files have the same messages (and warn user about missing translations, etc).

Also if export of that file is supposed to be a catalog, how would you tackle asynchronous nature of it? It has to be able to return Promise and wait for it.

Reading from FS is synchronous at the moment, but we could change it to be async and then the messages from all sources would be loaded in parallel.

Alright, I've updated RFC: Loading messages from scripts.

Hi the UI of the app I am working on is mostly server driven and this RFC is exactly we're looking for. Has there been any progress on this ?

@jatinsandilya Not likely since there is still a heavy workload to get 3.0 out. I guess you have to be patient or try to help out with 3.0 as much as you can, there are tasks in #334.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

LFDMR picture LFDMR  路  4Comments

ludwigbacklund picture ludwigbacklund  路  6Comments

landsman picture landsman  路  4Comments

tkvw picture tkvw  路  6Comments

landsman picture landsman  路  5Comments