Skip to content

Help wanted Using react-intl #1227

Open

Description

@folivi

Note from Maintainers

Please try the solution in #1227 (comment) and let us know if it works well for you.


Hi all,

I'm following this tutorial https://medium.freecodecamp.com/internationalization-in-react-7264738274a0#.pgfd03878 to use internationalization in my app.
Apparently the integreation is done by modifying the .babelrc file but to do so I have to eject, and I prefer not to.
Is there any way to use React-Intl without ejecting create-react-app?

Activity

  1. robcaldecott commented on Dec 10, 2016

    @robcaldecott

    You only need the Babel plugin to generate the translate files which you can do as part of your production build without ejecting although it's a little messy. I'll post how later.

  2. EnoahNetzach commented on Dec 10, 2016

    @EnoahNetzach
    Contributor

    You can just install the required dependencies, add your own .babelrc, and add your "script": "translation" to your package.json.

    E.g.:

    npm install --save react-intl
    
    npm install --save-dev babel-cli babel-preset-react-app babel-plugin-react-intl
    

    add to the root folder:

    {
      "presets": ["react-app"],
      "plugins": [
        [
          "react-intl", {
            "messagesDir": "translations/messages",
            "enforceDescriptions": false
          }
        ]
      ]
    }

    then add to your package.json's "scripts" this line:

    "translate": "NODE_ENV=production babel ./src >/dev/null"

    You should be able to run both npm start/npm run build for running/building the project, and npm run translate for generating the translations.

  3. firaskrichi commented on Jan 20, 2017

    @firaskrichi

    @EnoahNetzach thanks for the answer!

    How does : "NODE_ENV=production babel ./src >/dev/null" generate the translation? How does it work exactly?

    Thank you 🙏

  4. arshmakker commented on Jan 20, 2017

    @arshmakker

    hi,

    Internationlization is a pretty important feature for mobile apps these days. Could we have react-intl inbuilt in the create-react flow?

    Regards
    Arsh

  5. EnoahNetzach commented on Jan 20, 2017

    @EnoahNetzach
    Contributor

    @firaskrichi it works as intended by react-intl, by using babel's AST to catch translations and replace them (I suggest you to look it up, it's really interesting, if you like to know how exactly it - and in general babel plugins - works!).
    The > /dev/null part is there simply to discharge the babel parsed output.

    Moreover what I suggested is simply what the official doc tells to do ;)

    @arshmakker being that it's easily pluggable, not everybody wants to use i18n, and the specific tool (format.js) is not that reliable if you want to do substantial i18n (@see), I'm not sure at all it would be possible.

  6. draperd commented on Feb 10, 2017

    @draperd

    This is a really useful discussion and the information provided by @EnoahNetzach has really helped get react-intl into my create-react-app application. I think it would be exceptionally valuable to have i18n working out-of-the-box with create-react-app (or at least some good documentation explaining how to use it).

    The main thing that I don't quite understand is how this works with a production build. I've followed the various scripts to combine individual component message files into a single JSON file for the default locale, I've then added a different locale (with translated messages) and updated the IntlProvider locale to be the new locale.

    When you use npm run build the new locale translations are lost (probably because the translate scripts provided by the blogs combine the component messages into locale files in the build directory).

    If anyone can shed any light on the correct approach to combining the information provided so far in a production story I'd be very grateful.

  7. EnoahNetzach commented on Feb 10, 2017

    @EnoahNetzach
    Contributor

    @draperd, as a non English native speaker, I find that unfortunately no i18n instrument in js is a "catch all" solution.
    I've used a couple and always ended up tweaking them to my needs (e.g. subject/direct object gender and numerosity, probably @gaearon could say something about Russian desinences, I don't know if there is someone from Iceland here, and so on and so forth..).
    Whichever translation package we could add, it's going to solve only a limited set of cases and needs, so I'm against adding them in altogether, specially when the integration is not "that" painful.

  8. draperd commented on Feb 10, 2017

    @draperd

    @EnoahNetzach Thanks for your response... I wasn't suggesting that there is a one-size fits all solution, but it would seem that React-Intl is a pretty good fit. I can understand the rationale of not wanting to tie "create-react-app" to a single solution, however a solution of some kind is almost going to be required. Any serious application is going to want to support i18n so I'm simply suggesting that either something is provided by default, or that documentation is provided to assist people in achieving a solution.

    The information you have previously provided has been invaluable but only as far as the development experience goes. My point is that when I'm ready to ship my finished application I need translation to work in production for multiple locales and I've not been able to find that information anywhere so far.

    I'll continue to experiment and hopefully find a working solution which I'll then write up, I was just hoping that someone may have already encountered and solved this problem using react-intl.

  9. draperd commented on Feb 13, 2017

    @draperd

    Just to follow up my previous comment... what I actually need is a per component solution which I've now managed to achieve using the higher order component pattern. I will write this solution up as soon as I can for anyone that comes across this thread as it might be useful.

  10. EnoahNetzach commented on Feb 13, 2017

    @EnoahNetzach
    Contributor

    @draperd good!

  11. draperd commented on Feb 13, 2017

    @draperd
  12. sthuber90 commented on Mar 10, 2017

    @sthuber90

    For everyone that the suggestion from @EnoahNetzach is not working out of the box I had to put following in my package.json, probably as I'm working on windows

    "translate": "cross-env NODE_ENV=production ./node_modules/.bin/babel ./src > NUL",

  13. EnoahNetzach commented on Mar 10, 2017

    @EnoahNetzach
    Contributor

    Yep, env var definition and the output indirection to /dev/null work only in Linux/Mac.

    cross-env solves the first part, for the second sincerely I have no idea how to accomplish that on Windows..

  14. cwalv commented on Mar 18, 2017

    @cwalv

    If you don't want to have to npm install --save-dev babel-cli or create the babel.rc file, or worry about issues on Windows, you can use the babel API:

    // scripts/translate.js
    
    process.env.NODE_ENV = 'development';
    
    const fs = require('fs');
    const path = require('path');
    const globSync = require('glob').sync;
    const mkdirpSync = require('mkdirp').sync;
    const transformFileSync = require('babel-core').transformFileSync;
    
    const paths = require('../config/paths');
    
    //////////////////////////////
    // Add to `/config/paths.js`:
    //  appBuildMessages: .. where to output the messages
    //////////////////////////////
    
    globSync(path.join(paths.appSrc, '/**/*.js'))
        .map((filename) => {
            const result = transformFileSync(filename, {
                plugins: ['react-intl']
            });
            const messages = result.metadata["react-intl"]["messages"];
            if (messages.length > 0) {
                const relFn = path.relative(paths.appSrc, filename);
                const outFilename = path.join(
                    paths.appBuildMessages,
                    path.dirname(relFn), `${path.basename(relFn, ".js")}.json`);
                mkdirpSync(path.dirname(outFilename));
                const outFd = fs.openSync(outFilename, 'w');
                fs.write(outFd, JSON.stringify(messages));
            }
        });
    ...
      "scripts": {
        ...,
        "translate": "node scripts/translate.js",
       }
  15. 14 remaining items

  16. gaearon commented on Jan 10, 2018

    @gaearon
    Contributor

    Nice, thanks.

  17. arshmakker commented on Jan 11, 2018

    @arshmakker

    Currently I am using the react-intl-cra in production at our end and have not face any issues so far.

  18. evenchange4 commented on Jan 25, 2018

    @evenchange4
    Contributor

    Here is the macro solution POC:

    Your code

    // Component.js
    -import {defineMessages} from 'react-Intl';
    +import { defineMessages } from 'react-intl.macro';
    
    const messages = defineMessages({
      'Component.greet': {
        id: 'Component.greet',
        defaultMessage: 'Hello, {name}!',
        description: 'Greeting to welcome the user to the app',
      },
    });

    Extract messages

    // package.json
    "scripts": {
      "start": "react-scripts start",
      "build": "react-scripts build",
    + "extract": "MESSAGE_DIR='./.messages' react-scripts build"
    },

    Pros and Cons

    The react-intl-cra CLI is pretty easy to use, but it only works with CRA project.
    The react-intl.macro is more flexible even working with others (e.g.,Next.js).

    References

    GitHub: https://github.com/evenchange4/react-intl.macro
    Example: https://github.com/evenchange4/react-intl.macro-example
    Demo: https://react-intlmacro.netlify.com

  19. dachinat commented on Mar 31, 2018

    @dachinat

    May I know why does react-intl-cra and other examples here generate in

    [
      {
        "id": "...",
        "defaultMessage": "..."
      }
    ]
    

    format?

    If you do import translations from './i18n/messages.json'; and <IntlProvider locale="en" messages={translations}> it won't work. It accepts key value paris like

    {
      "...": "..."
    }
    

    Thanks.

  20. kopax commented on Apr 9, 2018

    @kopax

    @EnoahNetzach I have tried with your solution and

    Error: Couldn't find preset "react-app" relative to directory 
    "/home/dka/workspace/gitlab.example.com/dev-tools/test"
    

    Should I install anything else in my project ? What plugin can I reuse and where are they stored ?

    Did anybody made a workaround ? I dont' get why react-intl-cra './src/**/*.js' -o messages.json generated me this file :

    [
      {
        "id": "test.action.resetViews",
        "defaultMessage": "Reset views",
        "filepath": "./src/messages.js"
      }
    ]

    While I expect

    {
      "test.action.resetViews": "Reset views"
    }

    I want to use react-intl. As @dachinat pointed oput, we need key:value pair.

    How can I do without ejecting?

  21. noliiva commented on May 7, 2018

    @noliiva

    Personnaly, I use the internal script extract-intl from react-boilerplate.
    No need of babel-plugin-react-intl or react-intl-cra, just some adaptations according the project, like changing paths and install needed modules (mainly babel-cli) and voila. :D

    "extract-intl": "babel-node --presets env,stage-0 -- ./internals/scripts/extract-intl.js"
  22. kopax commented on May 7, 2018

    @kopax

    @noliiva I have placed this react-boilerplate script in a program that you can run through npx and it work without any dependency :

    You must have in your package.json

    +  "declinationId": "intl"

    Then run

    $ npx rollup-umd-scripts install fr en de ch vi --default=en

    This will create in your package.json

    +  "translation": {
    +    "locale": "en",
    +    "locales": [
    +      "en",
    +      "fr",
    +      "de",
    +      "ch",
    +      "vi"
    +    ]
      },

    You can now extract what's in src directory with :

    $ NODE_ENV=production npx rollup-umd-scripts intl extract

    You can add the following scripts to your package.json to keep the snippet for any project:

    +  "extract-intl": "NODE_ENV=production npx rollup-umd-scripts intl extract --target=src/i18n/translation",

    --target is for changing the destination path for the generated json files.

    It's basically the same as evenchange4 except with the standard format for json and without any install.

  23. arnaudriegert commented on Aug 22, 2018

    @arnaudriegert

    I'm following up on the questions asked by @dachinat and @kopax

    From what I understand, there are two steps:

    1. Extract all the strings that need to be translated
    2. Generate a file that will store the translations for each language

    In the gettext universe, the first step is the equivalent of generating the (single) .pot file while the second step is the generation of the (multiple) .po files.

    The "messageId": "value" syntax would apply to the result of the second step, while babel-plugin-react-intl only does the first step.

    I didn't think the second step would be so complicated to implement.

    So far all I've found (outside of this discussion) is this software that provides a GUI to do it. I don't know what it is based on.
    I was expecting the guys behind react-intl would provide a command such as translations-update -i build/**/*.json -o src/messages/fr.json.

    Did I miss something here or is it really that complicated to have a translations file for each language?

  24. sunknudsen commented on Feb 16, 2019

    @sunknudsen

    Looking for a way to extract messages from components localized using react-intl and manage translations effortlessly?

    The following worked for me: formatjs/babel-plugin-react-intl#108 (comment)

  25. jamieallen59 commented on Mar 18, 2019

    @jamieallen59

    I was using the same tutorial and couldn't find a tutorial from start => finish without ejecting so wrote one here: https://medium.com/@jamieallen59/using-react-intl-with-create-react-app-6667ee3e19f3. Hope it helps.

  26. shsunmoonlee commented on Apr 26, 2019

    @shsunmoonlee

    @noliiva I have placed this react-boilerplate script in a program that you can run through npx and it work without any dependency :

    $ npx rollup-umd-scripts install fr en de ch vi --default=en

    This will create in your package.json

    +  "translation": {
    +    "locale": "en",
    +    "locales": [
    +      "en",
    +      "fr",
    +      "de",
    +      "ch",
    +      "vi"
    +    ]
      },

    You can now extract what's in src directory with :

    $ NODE_ENV=production npx rollup-umd-scripts intl extract

    You can add the following scripts to your package.json to keep the snippet for any project:

    +  "extract-intl": "NODE_ENV=production npx rollup-umd-scripts intl extract --target=src/i18n/translation",

    --target is for changing the destination path for the generated json files.

    It's basically the same as evenchange4 except with the standard format for json and without any install.

    Hey I get error saying ' You must use a intl declination to use this command!'

  27. kopax commented on Apr 26, 2019

    @kopax

    @SeunghunSunmoonLee sorry I did a change in that script, you must have in your package.json

    +  "declinationId": "intl"

    It wont break anymore, otherwise you can fix to an older version.

  28. erikhansen commented on Jun 25, 2019

    @erikhansen

    @jamieallen59 Your tutorial worked great for me. Thanks.

  29. mihanizm56 commented on Aug 11, 2019

    @mihanizm56

    @jamieallen59
    You use react-intl v2 version in your guide. Nowadays in v3 there are a lot of APIs have breaking changes

  30. lucasbasquerotto commented on Sep 28, 2020

    @lucasbasquerotto

    In my case I'm able to use react-intl with create-react-app by just including 2 simple scripts to extract and compile the translation files:

    scripts/i18n-extract.sh:

    #!/bin/bash
    lang='en'
    
    npm run i18n:formatjs:extract \
    	-- 'src/**/*.ts*' --ignore 'src/**/*.d.ts*' \
    	--out-file src/src-lang/"$lang".json \
    	--id-interpolation-pattern '[sha512:contenthash:base64:6]'

    * I define the extension as ts* because I'm using typescript, if you are using javascript just change it to js*

    scripts/i18n-compile.sh:

    #!/bin/bash
    langs=( 'en' 'fr' 'ar' )
    
    if [ "${1:-}" = 'default' ]; then
        langs=( 'en' )
    fi
    
    for lang in "${langs[@]}"; do
        src="src/src-lang/$lang.json"
        dest="src/lang/$lang.json"
    
        if [ ! -f "$src" ]; then
            echo "[warn] src file not found ($src)"
        else
    	    npm run i18n:formatjs:compile -- "$src" --ast --out-file "$dest"
        fi
    done

    * In this case I'm considering the languages en, fr and ar (en being the default), you can change it to your use case.

    And in package.json:

    "scripts": {
        "start": "HTTPS=true react-scripts start",
        "build": "react-scripts build",
        "test": "react-scripts test",
        "eject": "react-scripts eject",
        "i18n:dev": "echo '{}' > src/lang/en.json",
        "i18n:extract": "bash ./scripts/i18n-extract.sh",
        "i18n:compile": "bash ./scripts/i18n-compile.sh",
        "i18n:formatjs:extract": "formatjs extract",
        "i18n:formatjs:compile": "formatjs compile"
    }

    I only need bash to be installed and everything works fine so far. I can run npm run i18n:extract in a CI environment to generate the file with the translations in the default language, and in another CI process, when deploying to staging/production I can retrieve the translation files (from wherever they are), put them at src/src-lang/ and call npm run i18n:compile to generate the final (compiled) translation files at src/lang/ (the files in these folders aren't versioned).

    In smaller projects you can run npm run i18n:extract locally, put the generated file (default language) in a versioned folder and apply the translation on the other files in the folder based on the changes in the default language. Then when deploying in a CI environment you can just move the files from the versioned folder to src/src-lang/ and run npm run i18n:compile to generate the compiled translation files at src/lang/.

    In development I just run npm run i18n:dev to create an empty json file ({}) so as to not receive errors when trying to import the file (the default language messages are already in the code, so the file doesn't need to have translations in the default language).

    The only thing I haven't achieved was to be able to generate ids (https://formatjs.io/docs/guides/bundler-plugins), and use the generated ids in development (I don't know if this is possible without ejecting and stop using create-react-app, because it would need to transpile the source files to generate the ids). So, for now, I'm using static ids (if there is the same id for different messages I receive an error, so it's ok).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions