Search code examples
jsonreactjsreact-intl

How to use code splitting on a ressouce (.json, .csv, etc.) with React


Issue:

I am using react-intl and I would like to load the language-related JSON file only when it is needed, in brief to lazy load it.

Documentation :

https://reactjs.org/docs/code-splitting.html the problem here is I wish to lazy load a JSON file not a component, so I am a bit lost about how to use it without JSX.

Actual code :

import React from 'react';
import {IntlProvider} from 'react-intl';
import English from "../i18n/en.json";
import Polish from "../i18n/pl.json";

const local = navigator.language;
let lang;
switch (local){
    case "pl":
    case "pl-pl":
        lang = Polish;
        break;
    default:
        lang = English;
}

function Wrapper () {
[...]
}

export default Wrapper

What I try which did not works :

test 1:

import React from 'react';
import {IntlProvider} from 'react-intl';
const English = React.lazy(() => import('../i18n/en.json'));
const Polish = React.lazy(() => import('../i18n/pl.json'));

const local = navigator.language;
let lang;
switch (local){
    case "pl":
    case "pl-pl":
        lang = Polish;
        break;
    default:
        lang = English;
}

function Wrapper () {
[...]
}

export default Wrapper

test 2:

import React from 'react';
import {IntlProvider} from 'react-intl';
const English = React.lazy(() => import('../i18n/en.json'));
const Polish = React.lazy(() => import('../i18n/pl.json'));

const local = navigator.language;
let lang;
switch (local){
    case "pl":
    case "pl-pl":
        Polish.then(polish => {lang = polish});
        break;
    default:
        English.then(english=> {lang = english});
}

function Wrapper () {
[...]
}

export default Wrapper

test 3 (inspired from How to import Json file with React lazy loading?) :

import React from 'react';
import {IntlProvider} from 'react-intl';

const local = navigator.language;
let lang;
switch (local){
    case "pl":
    case "pl-pl":
        import("../i18n/pl.json").then(Polish=> {
            lang = Polish);
        });
        break;
    default:
        import("../i18n/en.json").then(English=> {
            lang = English);
        });
}

function Wrapper () {
[...]
}

export default Wrapper

In case more code is needed (the function Wrapper for example), please let me know in a comment :)


Solution

  • I also had the same issue where I wanted to code split my JSON in React. I found a work around that uses the dynamic import function.

    I wasn't using Intl so I can't confirm this works for your needs but this is what worked for my needs (and I think it should work for you)

    I created a function to return a promise with my data that I needed to code split. I then called this in my Component in an Async func with an await on the data.

    My use case was to fetch data from an API, if that was done, load a cached JSON of the data.

    const loadCached = (key) => {
      return new Promise((res, rej) => {
        import(`../datasets/cached/${key}.json`).then((data) => {
          res(data?.default);
        });
      });
    };
    

    I then called it from my async catch

    const cachedData = await loadCached(key);
    

    So for you use I would keep my loadCached function (maybe rename it to like loadLang)

    then wrap it in a useEffect to fire on load to change the language and gather the JSON.

    Here is how I would approach your issue (untested code)

    const local = navigator.language; // get language
    const [lang, setLang] = useState("English"); // for a default of english
    
    // fire when we get local from DOM
    useEffect(() => {
      async function getLangData() {
        const response = await loadLang(local);
        setLang(reponse);
      }
    getLangData();
    }, [local])
    
    
    const setLang = (lang) => {
      return new Promise((res, rej) => {
        import(`../i18n/${lang}.json`).then((data) => {
          res(data?.default);
        });
      });
    };