Js-lingui: No messages extracted outside of react

Created on 12 Dec 2018  Β·  47Comments  Β·  Source: lingui/js-lingui

No messages get extracted when using:

import i18n from './utils/i18n'
import {t} from '@lingui/macro';

i18n._(t`hello`)

They get extracted when using it like:

i18n._("hello")

They also get extracted as expected when using a react component:

import {Trans} from '@lingui/react';

<Trans>Hello</Trans>

any ideas?

πŸ“– documentation

Most helpful comment

That's great. How does it work with shared components libraries that contain catalogs?

In our case we have

  • ui-1 - catalog-ui-1
  • ui-2 - catalog-ui-2
  • shared-components - catalog-shared-components

There are going to be different instances / catalogs. How do these get reconciled?

They get merged by locale internally, you have more information in lingui website on configuration section.

Tomorrow I'll expand this a lit

All 47 comments

Hey @diegolacarta,
what version of Lingui do you have?

"@lingui/cli": "2.7.2",
"@lingui/loader": "2.7.2",
"@lingui/macro": "2.7.2",
"@lingui/react": "2.7.2",

Could you please also post your Babel config? Do you have babel-plugin-macros enabled?

{
  "env": {
    "local": {
      "presets": ["@babel/preset-react", "@lingui/babel-preset-react"],
      "plugins": [
        ["@babel/plugin-syntax-dynamic-import"],
        ["macros"],
        [
          "emotion",
          {
            "sourceMap": true,
            "autoLabel": true
          }
        ],
        [
          "react-intl",
          {
            "messagesDir": "./i18n/messages/",
            "extractSourceLocation": true
          }
        ]
      ]
    }
  }
}

react-intl in there just while transitioning away from it

That looks good. Could you also please show how babel transforms the file with i18n? babel src/file.with.i18n.js

Finally, does extract work for Trans imported from @lingui/macro package?

There's also an example repo with js-macros (no React): https://github.com/lingui/js-lingui/tree/master/examples/vanilla-js Maybe it would help you find the root cause.

That looks good. Could you also please show how babel transforms the file with i18n? babel src/file.with.i18n.js

Finally, does extract work for Trans imported from @lingui/macro package?

I'm using typescript, following the setup from https://github.com/lingui/js-lingui/blob/master/docs/guides/typescript.rst

I could debug at which point babel plugin is not doing what it should, or if you could point me into where to look that'd help

Trans doesn't work when imported from @lingui/macro:
<Trans>random text</Trans>
...

> Missing message ID, skipping.
> <Trans>random text</Trans>

Right, so now we now the macros doesn't work.

It would be great to see how transpiled source code looks like. Unfortunately I don't use Typescript, but maybe you run TS before Babel?

Also not even this works:

import { setupI18n } from '@lingui/core';
import { t } from '@lingui/macro';
const i18n = setupI18n()
i18n._(t`Hello`)

This is the final code after building with webpack:

Original:

import {setupI18n} from '@lingui/core'
import {t} from '@lingui/macro'

const i18n = setupI18n()
i18n._(t`Hello`)

Transpiled:

! function(e) {
    function r(r) {
        for (var n, i, l = r[0], f = r[1], c = r[2], a = 0, s = []; a < l.length; a++) i = l[a], o[i] && s.push(o[i][0]), o[i] = 0;
        for (n in f) Object.prototype.hasOwnProperty.call(f, n) && (e[n] = f[n]);
        for (p && p(r); s.length;) s.shift()();
        return u.push.apply(u, c || []), t()
    }

    function t() {
        for (var e, r = 0; r < u.length; r++) {
            for (var t = u[r], n = !0, l = 1; l < t.length; l++) {
                var f = t[l];
                0 !== o[f] && (n = !1)
            }
            n && (u.splice(r--, 1), e = i(i.s = t[0]))
        }
        return e
    }
    var n = {},
        o = {
            0: 0
        },
        u = [];

    function i(r) {
        if (n[r]) return n[r].exports;
        var t = n[r] = {
            i: r,
            l: !1,
            exports: {}
        };
        return e[r].call(t.exports, t, t.exports, i), t.l = !0, t.exports
    }
    i.m = e, i.c = n, i.d = function(e, r, t) {
        i.o(e, r) || Object.defineProperty(e, r, {
            enumerable: !0,
            get: t
        })
    }, i.r = function(e) {
        "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, {
            value: "Module"
        }), Object.defineProperty(e, "__esModule", {
            value: !0
        })
    }, i.t = function(e, r) {
        if (1 & r && (e = i(e)), 8 & r) return e;
        if (4 & r && "object" == typeof e && e && e.__esModule) return e;
        var t = Object.create(null);
        if (i.r(t), Object.defineProperty(t, "default", {
                enumerable: !0,
                value: e
            }), 2 & r && "string" != typeof e)
            for (var n in e) i.d(t, n, function(r) {
                return e[r]
            }.bind(null, n));
        return t
    }, i.n = function(e) {
        var r = e && e.__esModule ? function() {
            return e.default
        } : function() {
            return e
        };
        return i.d(r, "a", r), r
    }, i.o = function(e, r) {
        return Object.prototype.hasOwnProperty.call(e, r)
    }, i.p = "\x3c!-- server-replace:PUBLIC_PATH --\x3e/";
    var l = window.webpackJsonp = window.webpackJsonp || [],
        f = l.push.bind(l);
    l.push = r, l = l.slice();
    for (var c = 0; c < l.length; c++) r(l[c]);
    var p = f;
    u.push([39, 1]), t()
}({
    39: function(e, r, t) {
        e.exports = t(40)
    },
    40: function(e, r, t) {
        "use strict";
        t.r(r);
        var n = t(38);
        Object(n.setupI18n)()._({
            id: "Hello"
        })
    }
});

I just tried changing my .linguirc to:

{
   "localeDir": "src/locales/",
   "srcPathDirs": ["src/"],
   "format": "po",
   "extractBabelOptions": {
      "plugins": ["@babel/plugin-syntax-dynamic-import", "macros"]
   }
}

Adding "macros" it now extracts the message with both \

and then finally adding:
"presets": ["@babel/preset-react", "@lingui/babel-preset-react"],

to .linguirc seems to make everything work as expected.

I thought it would pick up my .babelrc from the project

Interesting. What's your project structure? Do you have .babelrc right next to the package.json? And how do you run the lingui extract command?

yes, .babelrc is at same level as package.json, and I run lingui extract via npm script: npm run extract-locales

Could you please share minimal example so I can take a look? It should work as you described, pick the config from .babelrc

will try to reproduce when I find some time

I'm having the same issue with lingui extract not picking up strings from i18n._(t`...`). I'm importing t from @lingui/react, since my project is on React Native and macros don't work (babel-plugin-macros tries to import Node standard modules path, os and fs when I import Trans from @lingui/macros, should it actually work?). The docs don't mention anything about using lingui without macros though, is it possible?

Using Trans from @lingui/react works just fine though. Strings get extracted, catalogs are compiled and no errors are thrown during runtime.

Any help is much appreciated. πŸ™

EDIT: Actually, t is not part of @lingui/react is it? πŸ˜† It seems the whole problem is simply that macros aren't working, so of course I can't use t, and strings using it aren't being extracted.

I'm facing similar issue. The whole extraction behaves very weird/doesn't work.

Versions:

$ yarn list --depth 0 --pattern @lingui
yarn list v1.13.0
β”œβ”€ @lingui/[email protected]
β”œβ”€ @lingui/[email protected]
β”œβ”€ @lingui/[email protected]
β”œβ”€ @lingui/[email protected]
β”œβ”€ @lingui/[email protected]
β”œβ”€ @lingui/[email protected]
β”œβ”€ @lingui/[email protected]
└─ @lingui/[email protected]
  • We're using typescript with babel7 , so types are just stripped away, that definitely shouldn't be the cause of this issue
  • it's a monorepo project

config:

const config = {
  compileNamespace: 'es',
  format: 'minimal',
  localeDir: 'public/locale',
  srcPathDirs: ['src'],
};

module.exports = config;
import React from 'react'
import {Trans} from '@lingui/macro'

// Missing message ID, skipping. <Trans>Continue</Trans>
const App = () => <div><h1>hello</h2><button><Trans>Continue</Trans></button></div>

// Works -> {
  "continue": ""
}
const App = () => <div><h1>hello</h2><button><Trans id="continue">Continue</Trans></button></div>

Additional bugs:

  • Plural doesn't work with id
import React from 'react';
import { Plural } from '@lingui/macro';

const App = () => {
  return (
    <div>
      /* WORKS ->  
        "{count, plural, one {At least # lower-case letter} other {At least # lower-case letters}}": "{count, plural, one {At least # lower-case letter} other {At least # lower-case letters}}"
      */
      <Plural
        one="At least # lower-case letter"
        other="At least # lower-case letters"
        value={count}
      />
      /* doesn't! 
        it even removes valid translation when run with --clean flag
      */
      <Plural
        id="validator.lowerCase"
        one="At least # lower-case letter"
        other="At least # lower-case letters"
        value={count}
      />
    </div>
  );
};

What's super weird is that once I add id to Plural, then I run lignui extract --clean,

->

{
-{count, plural, one {At least # lower-case letter} other {At least # lower-case letters}}": "{count, plural, one {At least # lower-case letter} other {At least # lower-case letters}}
+
}

after that, once I remove id from plural and run lignui extract again, it won't work anymore and generates nothing within messages.json.

Hey @Hotell, what's your babel config? It seems you're mixing macros with plugins. You should use either one and not mix them together.

Ideally please create a repository with minimal example so we can debug the issue quickly.

what's your babel config?

 const presets = [
    [
      '@babel/preset-env',
      {
        useBuiltIns: 'entry',
        corejs: 2,
        // Do not transform modules to CJS
        modules: IS_TEST ? 'commonjs' : false,
        loose: true,
      },
    ],
    '@babel/preset-react',
    '@babel/preset-typescript'
  ]

  /** @type {import('babel').BabelConfigOptions['plugins']} */
  const plugins = [
    '@babel/plugin-syntax-dynamic-import',
    ['@babel/plugin-proposal-class-properties', { loose: true }],
    ['@babel/plugin-proposal-object-rest-spread', { loose: true }],
    'macros',
  ]

It seems you're mixing macros with plugins. You should use either one and not mix them together.

I'm not sure I follow

Also

  • Plural gets caught when used from @lingui/react
  • if Trans from @lingui/macro is used for plural it is caught within extraction as well <Trans id={ '{count, plural, one {At least # lower-case letter} other {At least # lower-case letters}}' } />

Ideally please create a repository with minimal example so we can debug the issue quickly.

I wished I'd have time for that hah. That's a non trivial request when taking into consideration the size of the project...

Also Plural gets caught when used from @lingui/react

If you use macros, definitely don't import Plural from @lingui/react.

In LinguiJS 2.x (current version), there're two ways how to transform components into ICU MessageFormat - plugins and macros:

Plugins:

  • add @lingui/babel-plugin-transform-{js,react} to .babelrc
  • import Trans, Plural and other components from @lingui/react
  • this approach is deprecated and removed in LinguiJS 3.x

Macros:

  • add macros to .babelrc
  • import Trans and other components from @lingui/macro
  • preferred and the only supported since LinguiJS 3.x

It seems you're using the second option, macros. I just misinterpreted the dependencies.

I wished I'd have time for that hah. That's a non trivial request when taking into consideration the size of the project...

Well, the problem is somewhere in your configuration. If you look into examples, you'll find working projects. The only way to figure out the problem is create a repository with one file, your babel config and install LinguiJS dependencies. If you don't have time, then I can't help you, sorry.

I'm using @lingui/macro with v2 solely. It doesn't work with Plural -> strangely enough id does when Trans "low level" ICU is used directly <Trans id={ '{count, plural, one {At least # lower-case letter} other {At least # lower-case letters}}' } />

I'm gonna try to use v3 and get back here with results ( if there will be any )

In the meantime I appreciate your time and help ✌️.

Cheers

Unfortunately, the v3 isn't production ready yet :/ See #334

I'll try to setup the repo this evening. I'm suspicious that it's either Typescript related (LinguiJS uses TypeScript 2.x, not Babel preset) or related to some babel plugins.

Unfortunately, the v3 isn't production ready yet :/ See #334

I'll try to setup the repo this evening. I'm suspicious that it's either Typescript related (LinguiJS uses TypeScript 2.x, not Babel preset) or related to some babel plugins.

I appreciate your efforts Tomas, but it's open source... In my experience it should be consumer responsibility to provide demo which simulates described "bug", not the authors. Our time as maintainers is very precious. So please don't waste your time on creating repro. I'll do it, when I find some time today...

cheers πŸ‘

I know, you’re right. I’m just open-source junkie - when I see a problem I can’t resist solving it. If I weren’t super busy at work right now, I would already start working on it ;)

Let me know if I can further help or clarify something.

Cheers
On 26 Jun 2019, 16:01 +0200, Martin Hochel notifications@github.com, wrote:

Unfortunately, the v3 isn't production ready yet :/ See #334
I'll try to setup the repo this evening. I'm suspicious that it's either Typescript related (LinguiJS uses TypeScript 2.x, not Babel preset) or related to some babel plugins.
I appreciate your efforts Tomas, but it's open source... In my experience it should be consumer responsibility to provide demo which simulates described "bug", not the authors. Our time as maintainers is very precious. So please don't waste your time on creating repro. I'll do it, when I find some time today...
cheers πŸ‘
β€”
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.

Hmm so, I've converted weback js/flow/react example to typescript with latest versions of deps. Looks like works there (attached) Check Form within Children.tsx.

  • There are some type errors because 3rd party definitions are not correct...
  • I didn't tried tests ( not important at the moment )
  • I might submit PR with this example, WDYT ?

Everything works, hmm. Looks like I don't have @lingui/babel-plugin-transform-{js,react} within dependencies at work (as it's not properly stated in the docs ?)

webpack-react-typescript.tar.gz

I’m just open-source junkie - when I see a problem I can’t resist solving it
I know, been there, don't that. don't do that :D

so after adding "@lingui/babel-preset-react": "2.8.3", Plural macro works.

  • strange thing is that CLI won't crash when that plugin is missing πŸ‘€

Ups so I found the real culprit.
lingui CLI doesn't work within monorepo...

It won't find root babel.config.js. That's why it was behaving so strange all along...

No idea how to solve this situation...

What lingui cli needs to support is:

babelOptions: {
    rootMode: 'upward',
  },

HA! so actually that's supported already :D πŸ‘

const config = {
extractBabelOptions: {
    rootMode: 'upward',
}
}

Great, I'm glad you've figured it out.

So what's the summary? You have a monorepo, each package has it's own .babelrc or they all use the root babel.config.js? I'm just asking so I can include it in docs.

Cheers πŸ‘

Yes, one babel.config.js within root.

-> lingui.config.js within root
-> lingui.config.js per package(app) which extends/overrides from root

✌️

image

image

Hey @Hotell,

Just to confirm, did you make this work in a monorepo, in which Components are being translated?

I'd love to replicate what you did and when I get it working, I'll help @tricoder42 with the documentation.

I'm currently facing a similar problem with monorepos, I can get everything to work except Lingui, and it's crucial to the developer's experience.

Hey @Hotell,

Just to confirm, did you make this work in a monorepo, in which Components are being translated?

I'd love to replicate what you did and when I get it working, I'll help @tricoder42 with the documentation.

I'm currently facing a similar problem with monorepos, I can get everything to work except Lingui, and it's crucial to the developer's experience.

I have the same problem, any solution for monorepos?

I was able to get the extraction working in a monorepo setup.
We have multiple applications and shared common-ui component library built with rollup.js.
In our case the problem was that we were configuring babel plugins directly through rollup rather than .babelrc

I was puzzled about how lignui actually works and then realized it uses .babelrc so once I had that (using macros plugin) the extraction worked.

Now there's a bigger / more interesting problem remaining.
The component library will have its own catalog and it's unclear what's the right way to get that loaded.

Each app has a global provider:

```typescript tsx
language={i18nStore.language}
catalogs={i18nStore.catalogs}>

But then somewhere deeper in `MainContainer` we use a component that comes with its own catalog.

This adapte picks up the language provided by the upper provider and replaces the catalog with the internal one. Perhaps merging the catalog (or a way to provide fallback catalogs) would be better, just in case the shared components are wrapping components from outside (which get their catalog overriden) 

```typescript tsx
export const withI18nAdapter = <P extends any>(
  Component: React.ComponentType<P>
) =>
  withI18n()((_props: P & withI18nProps) => {
    const language = _props.i18n?.language ?? 'en';
    return (
      <I18nProvider language={language} catalogs={catalogs}>
        <Component {...(_props as P)} />
      </I18nProvider>
    );
  })

Then, when exporting the components

const ComponentI18n = withI18nAdapter(Component);
export { ComponentI18n as Component };

Hi Cosmin @clehene ,

did you get this to work somehow? i have the same situation. component library in monorepo. not sure how to get it to work.
thanks,
Cezar
ps: @tricoder42 any input on this?

Hey @cezarneaga. Yes we built an adapter. Likely suboptimal, but it works.
We can contribute it as a helper to lingui-js. Meanwhile I'm posting the code here

import { I18nProvider, withI18n, withI18nProps } from '@lingui/react';
import * as React from 'react';

import catalogRo from '../locales/ro/messages.js';
import catalogEn from '../locales/en/messages.js';

/**
 *
 * Similar to Lingui JS `withI18n` but wraps component with an i18n adapter
 * that:
 *  * picks the `language` parameter from the provided `i18n` instance
 *  * changes the i18n catalog to the wrapped component's (internal) one
 *
 * embed-ui        provider(lang1, catalog1) ->
 *  adapter => new provider(lang1, catalog2)
 *
 * TODO use defaults if instance not provided (we'll fail with undefined otherwise
 * TODO load catalog dynamically
 * TODO merge catalog message instead of override?
 *
 * @param Component to wrap
 */

export const withI18nAdapter: <P extends object>(
  Component: React.ComponentType<P>
) => React.ComponentClass<
  Pick<P & withI18nProps, Exclude<keyof P, keyof withI18nProps>>
> = <P extends object>(Component: React.ComponentType<P>) =>
  withI18n()((props: P & withI18nProps) => {
    const language = props.i18n?.language ?? 'en';
    return (
      <I18nProvider language={language} catalogs={catalogs}>
        <Component {...(props as P)} />
      </I18nProvider>
    );
  });
const catalogs = { ro: catalogRo, en: catalogEn };

And then in index.tsx

import { ActionSet } from './components/ActionSet';
const ActionSetI18n = withI18nAdapter(ActionSet);
export { ActionSetI18n as ActionSet };

New version has been released. Please check if the problem persist in the new version.

Hey @cezarneaga. Yes we built an adapter. Likely suboptimal, but it works.
We can contribute it as a helper to lingui-js. Meanwhile I'm posting the code here

Thanks! would like to try the v3 and see if this is still needed.

Is the new behavior documented somewhere? I'd like to track that and change our code if that works.

@cezarneaga does 3 work properly? Can we remove the adapter?

@cezarneaga does 3 work properly? Can we remove the adapter?

On v3.x, practically you don't need withI18n for anything, macros work out of the box for example:

t`some translate`

and if you need i18n instance, you can directly import it from core

import { i18n } from "@lingui/core"

I'll try to find some time tomorrow to build a sample mono-repo with lingui 3 and multiple catalogs πŸ‘πŸ»

That's great. How does it work with shared components libraries that contain catalogs?

In our case we have

  • ui-1 - catalog-ui-1
  • ui-2 - catalog-ui-2
  • shared-components - catalog-shared-components

There are going to be different instances / catalogs. How do these get reconciled?

That's great. How does it work with shared components libraries that contain catalogs?

In our case we have

  • ui-1 - catalog-ui-1
  • ui-2 - catalog-ui-2
  • shared-components - catalog-shared-components

There are going to be different instances / catalogs. How do these get reconciled?

They get merged by locale internally, you have more information in lingui website on configuration section.

Tomorrow I'll expand this a lit

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vitexikora picture vitexikora  Β·  6Comments

ludwigbacklund picture ludwigbacklund  Β·  6Comments

felipeko picture felipeko  Β·  6Comments

LFDMR picture LFDMR  Β·  4Comments

tkvw picture tkvw  Β·  6Comments