i18next-scanner icon indicating copy to clipboard operation
i18next-scanner copied to clipboard

<Trans> component in Typescript files is no longer parsed

Open renchap opened this issue 5 years ago • 37 comments

This is most probably caused by dfd5db12262ac404ba4cb6414c6e7f6312de14bb (2.6.2)

Version

  • i18next: 11.3.6
  • i18next-scanner: 2.6.3

Configuration

{
  options: {
    debug: "true",
    lngs: ["en"],
    attr: false,
    func: {
      extensions: [".ts", ".tsx"],
      list: ["t", "props.t", "i18n.t"],
    },
    trans: {
      extensions: [".ts", ".tsx"],
      fallbackKey: true,
    },
    keySeparator: false,
    nsSeparator: false,
  },
}

Sample file

// test.tsx

import * as React from "react"
import { Trans } from "react-i18next"

const TestComp: React.SFC = () => (
  <div className="test-comp">
    <h3>
      <Trans>This is a test</Trans>
    </h3>
  </div>
)

Result

"This is a test" is not present in the output. The strings using the t() helper function are still correctly extracted.

renchap avatar Jul 17 '18 10:07 renchap

Yeah, we ran into the same issue for out project. The problem is that the scanner now uses a full-blown ES6 parser instead of the primitive RegExp. The parser breaks if it encounters any Typescript syntax that isn't valid ES6 (like type annotations).

Fortunatenly, the solution is simple: just transpile your code to Javascript using tsc and run the scanner over that.

beheh avatar Jul 17 '18 13:07 beheh

For me it fails on arrow functions as well. As for Typescript, I tried to add a @babel/plugin-transform-typescript plugin to remove Typescript but it was not removed.

Reverting back to 2.6.1 for now...

Tsury avatar Jul 17 '18 21:07 Tsury

I think this makes the extraction fail for any non-ES5 input, eg if you are using Typescript, or some recent non-ES5 features.

One solution could be to have some hooks so you can pre-process the files and run the parser on the ES-5 output:

  • one hook called before each file, so you can run Babel on the file and feed the output to the scanner
  • one hook called before any file is processed, so you can invoke tsc, and have the scanner process the output directory

Another solution (probably easier) would be to change the README and tell people to change their workflow to build their application using whatever build tool they are using, and run the scanner over the result. However, I am not sure if this will work correctly if the output is minified/optimised, so most probably not using a production build for extracting I18n data.

renchap avatar Jul 18 '18 08:07 renchap

I think especially if you use Typescript, the solution is pretty simple (you don't need a babel transform): Just run Typescript itself over your codebase and let it deal with all the modern non-ES5 features by specifying ES5 as the target. We use this pattern in our codebase, and we extensively use arrow functions, and very recent features such as rest spread parameters. Typescript can handle and transpile these (as it includes ES5-valid transforms out of the box).

A suitable command line might be tsc --outDir build/locale-extract/ --target ES5 --jsx preserve (note that you want to preserve JSX so that i18next-scanner picks it up).

beheh avatar Jul 18 '18 09:07 beheh

Yes this is not complex, but it needs to be documented in the README imo. Also this change has been introduced in a minor version, and it broke things for many people.

renchap avatar Jul 18 '18 09:07 renchap

@beheh I think it might be useful if we can provide built-in support for transpiling TypeScript to ES5 if the file extension is .ts or .tsx.

cheton avatar Jul 23 '18 15:07 cheton

It does not work properly for me, even after doing everything mentioned here. Seems like it misses complex uses. All of my tooltip texts are <Trans> components which are supplied to the tooltip component's content attribute, and those are missing from the target files.

Example:

<Tooltip content={<TooltipTextContainer><Trans i18nKey="settings:behavior.exampleTooltip" parent={'span'} i18n={i18n} t={t}>Some example text.</Trans></TooltipTextContainer>}>
  <InfoIconStyled />
</Tooltip>

Tsury avatar Jul 24 '18 17:07 Tsury

@Tsury Are you on v2.6.4? It was released yesterday and fixes components inside JSX props.

beheh avatar Jul 24 '18 17:07 beheh

@beheh positive. I used ncu to update just before I tried to fix it and it showed v2.6.4.

Also checked my stash, yeah it's v2.6.4 100%.

Tsury avatar Jul 24 '18 17:07 Tsury

@Tsury @beheh I can confirm it is a missing test in v2.6.4, I will re-open https://github.com/i18next/i18next-scanner/issues/90. You can update your examples there to help increase the coverage. Thank you.

cheton avatar Jul 25 '18 01:07 cheton

@Tsury You can try v2.6.5 to check if it works for you.

cheton avatar Jul 25 '18 11:07 cheton

@cheton just checked, still no go...

I'll show more of the code:

  <Container welcome={welcome}>
        {this.renderLogo()}
        {this.renderPrevMatchButton()}
        <ButtonsContainer>
          {this.renderErrorSymbol()}
          {this.renderUpdateAvailable()}
          <OpacitySliderStyled white={welcome}/>
          <Tooltip
            open={unreadUpdate}
            content={unreadUpdate ? <Trans i18nKey="menubar:tooltips.update" parent={'span'} t={t} i18n={i18n}>App has been updated! Click here to find out what&#39;s new.</Trans> : <Trans i18nKey="menubar:tooltips.changelog" parent={'span'} t={t} i18n={i18n}>Changelog</Trans>}>
            <SvgButtonStyled
              onClick={onChangelogClicked}
              white={welcome}
              flash={unreadUpdate}>
              <MdCardGiftcard size={16}/>
            </SvgButtonStyled>
          </Tooltip>
        </ButtonsContainer>
      </Container>

Tsury avatar Aug 04 '18 10:08 Tsury

I'm having the same problem with v2.6.5, I had to reverse to v2.6.1 because acorn-jsx-walk is not working for Trans components. I don't use typescrypt, I'm using ES6 for my code so that's the "problem". Here's my example on a .jsx file:

render() {
  return (
    <Dialog onClose={this.closeDialog} className="upgrade-dialog">
      <h3>{t('wantMoreSkips')}</h3>
      <p>
        <Trans i18nKey="getUnlimitedSkips">
          Get unlimited skips with <span className="pro-badge">Pro</span>!
        </Trans>
      </p>
      <Button className="button" onClick={this.closeDialog} text={t('gotIt')} />
    </Dialog>
  );
}

On version <=2.6.1 that would show up on my language files, and now it's not. I don't think this issue should be marked as an enhacement, this is a bug that broke things for many people.

fcastilloec avatar Sep 16 '18 17:09 fcastilloec

Any news regarding this issue? Still happening in 2.6.6.

Tsury avatar Oct 09 '18 12:10 Tsury

Same problem, not limited to TS as mentioned by @fcastilloec

Coriou avatar Oct 19 '18 15:10 Coriou

Here is workaround for this problem in case you are using some new JS stuff (e.g. arrow functions). Sorry TS guys :-)

Add following to your i18next-scanner.config.js:

const fs = require('fs');

module.exports = {
    options: {
        ...
        // this code is to disable default attempt to parse Trans
        trans: {
            component: 'Trans',
            extensions: [],
        },
        ...
    },
    transform: function customTransform(file, enc, done) {
        const parser = this.parser;
        const content = fs.readFileSync(file.path, enc);

        const options = {
            presets: ['babel-preset-stage-2'],
            plugins: ['babel-plugin-syntax-jsx'],
        };

        let code = require('babel-core').transform(content, options).code;

        parser.parseTransFromString(code);

        done();
    },
};

You need to install some packages:

npm i -D babel-preset-stage-2
npm i -D babel-plugin-syntax-jsx

I think this somehow could be integrated into i18next-scanner itself.

This has revealed number of other problems with scanner:

  • If Trans content looks like this "Test {{ formatCount }} Test Z" scanner generates string: "Test <1><0>{{formatCount}}</0></1> Test Z". What <0> points at? It looks unnecessary if we follow documentation (https://react.i18next.com/components/trans-component) and removing <0> does not harm.

  • If Trans is missing i18nKey then scanner does not extract anything. i18nKey is optional and scanner should handle this case as well.

daliusd avatar Nov 13 '18 15:11 daliusd

If you are using newer babel (e.g. newer React) then you might want to use babel 7 style plugins, e.g.:

const options = { plugins: ["@babel/plugin-syntax-jsx", "@babel/plugin-proposal-class-properties"] };

It might be not enough and you will need to follow suggestions from error messages.

daliusd avatar Nov 16 '18 10:11 daliusd

This (almost) works with newer babel: plugins: [ '@babel/plugin-syntax-jsx', '@babel/plugin-proposal-class-properties', '@babel/plugin-proposal-object-rest-spread', '@babel/plugin-transform-spread', '@babel/plugin-transform-arrow-functions', ],

Remaining problem is that acorn-jsx stops on JSX comments, e.g. {/* comment *.}. Maybe newest acorn version would solve the problem.

daliusd avatar Nov 22 '18 12:11 daliusd

I have the same problems described above for pure JavaScript code where we use object decomposition {...ohterObject}. I was checking the code and found that acorn-jsx-walk is 3 years old and still uses on [email protected]. I wonder whether it would be worth to switch a more recent walker which works with the latest version of [email protected]?

Edit: I see this is what @daliusd basically proposed in his latest post :).

messerm avatar Nov 27 '18 08:11 messerm

@messerm @daliusd and for those interested I've proposed a patch for the latest acorn-jsx in #112

If you can't wait, try npm install @contentpass/i18next-scanner.

Would be curious to hear if this solves your issues as well.

ctavan avatar Nov 28 '18 20:11 ctavan

@ctavan I can confirm that your version solved the issue for me. Thanks!

fcastilloec avatar Nov 28 '18 21:11 fcastilloec

Since my patch has been released, can you check again with [email protected]?

Should solve most issues with modern JavaScript. Typescript will likely require some more love.

ctavan avatar Nov 29 '18 07:11 ctavan

@ctavan your fix solves all my problems with modern js.

daliusd avatar Nov 29 '18 07:11 daliusd

For anyone looking for hints towards a typescript solution, it appears possible using the typescript compiler api: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API

An untested example of a custom transform function might be something like:

function transform(file, enc, done) {
  const extension = path.extname(file.path)
  const parser = this.parser;
  let content = fs.readFileSync(file.path, enc);


  if (extension == '.ts') {
    content = typescript.transpileModule(content, {
      compilerOptions: tsCompilerOptions,
      fileName: path.basename(file.path)
    }).outputText;
  }

  if (options.attr.extensions.indexOf(extension) !== -1)
    parser.parseAttrFromString(content);

  if (options.func.extensions.indexOf(extension) !== -1) {
    parser.parseFuncFromString(content);
  }

  done();
}

peitschie avatar Jan 08 '19 04:01 peitschie

I made package from @peitschie comment. Feel free to PR. https://github.com/nucleartux/i18next-scanner-typescript

nucleartux avatar Feb 18 '19 20:02 nucleartux

This is still an issue, Any plan to make it happen? :)

I have also experienced problems when using decorators.

koalalorenzo avatar Mar 13 '19 08:03 koalalorenzo

@koalalorenzo Have you tried using @peitschie or @nucleartux solutions? Both are working. Honestly, I don't see any reason why general purpose tool should handle Typescript specifically (even if it is becoming de facto language chooise for many JS projects).

daliusd avatar Mar 13 '19 08:03 daliusd

@daliusd yes, I had to manually modify it. It would be nice to have it in place. I guess we all are a little opinionated

koalalorenzo avatar Mar 13 '19 08:03 koalalorenzo

@daliusd ,I tried @nucleartux solution, but it cannot works.

moweiwei avatar Oct 23 '19 09:10 moweiwei

@daliusd ,I tried @nucleartux solution, but it cannot works.

I am using that solution at least in two projects and it is working fine. You should at least show your config and errors you are getting. You must have configuration error somewhere.

daliusd avatar Oct 23 '19 09:10 daliusd