Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

i18next pages flash when loaded #697

Closed
rga777 opened this issue Jan 25, 2019 · 5 comments
Closed

i18next pages flash when loaded #697

rga777 opened this issue Jan 25, 2019 · 5 comments
Labels

Comments

@rga777
Copy link

rga777 commented Jan 25, 2019

I’m using typescript, SSR and react-18next. When I load or refresh a page, you can see the DOM tree flash on every reload. There is also a warning on the browser console: “Warning: Did not expect server HTML to contain a <div> in <div>.”

If I replace react: {wait: true} with initImmediate: false, the DOM does no longer flash and the browser warning disappears “Warning: Did not expect server HTML to contain a <div> in <div>.” However I get a set of “i18next::translator: missingKey” and some warnings that “Warning: Text content did not match. Server:…”

Not sure what can be done to fix this.

Versions -

"react-i18next": "^9.0.8",
"i18next": "^14.0.1",
"i18next-browser-languagedetector": "^2.2.4",
"i18next-express-middleware": "^1.7.1",
"i18next-localstorage-cache": "^1.1.1",
"i18next-node-fs-backend": "^2.1.1",
"i18next-xhr-backend": "^1.5.1",

i18n.ts

const i18next = require('i18next'); // issues with typings on current i18next version hence cannot use import
import * as XHR from 'i18next-xhr-backend';
import * as LanguageDetector from 'i18next-browser-languagedetector';
import * as Cache from 'i18next-localstorage-cache'

const detectorOptions = {
    order: [ 'path', 'navigator', 'localStorage', 'header', 'htmlTag'],
    lookupFromPathIndex: 0,
    // cache user language on
    caches: ['localStorage'],
}

const backendBrowserOpts = {
  loadPath: '/locales/{{lng}}/{{ns}}.json',
  addPath:  '/translations/{{lng}}/{{ns}}',
}

export const i18nextOptions = {
  debug: process.env.NODE_ENV !== 'production',
  fallbackLng: ["en", "fr"],
  load: 'languageOnly', 
  allLanguages: ["en", "fr"],
 
  ns: ['common', 'layout', 'user'],
  defaultNS: 'common',
  whitelist: ['en', 'fr'],
  nonExplicitWhitelist: true,
 
  backend: backendBrowserOpts,
  detection: detectorOptions,
  keySeparator: false,
  nsSeparator: false,

  saveMissing: process.env.NODE_ENV !== 'production',
  saveMissingTo: 'all',

  interpolation: {
    escapeValue: false,
    formatSeparator: ',',
    format: (value: string, format: string, _lng: string) => {
      if (format === 'uppercase') return value.toUpperCase();
      return value;
    },
  },

  react: {
    // **** BROWSER WARNING *******
    // the wait option throws browser warning 
    // -> "warning: Did not expect server HTML to contain a <div> in <div>."
    // it causes the screen to flash when the page is refreshed / loading
    wait: process && !process.release,  
    bindI18n: false,
    bindStore: false,
    nsMode: 'default'
  },
  // if react { } is commented out and replaced by initImmediate, 
  // the browser warning disappears. However, the browser 
  // shows translation missing before it initialises
  // initImmediate: !(process && !process.release),
  
  cache: {
     enabled: true,
     expirationTime: 24 * 60 * 60 * 1000
     },

};
// Need to create an instance for typescript otherwise getting undefined errors in browser
const i18n = i18next.createInstance(); 

if (process && !process.release) {
  i18n
    .use(XHR)
    .use(Cache)
    .use(LanguageDetector);
}

// initialize if not already initialized
if (!i18n.isInitialized) i18n.init(i18nextOptions);

export default i18n;

client.ts

import { I18nextProvider } from 'react-i18next';
import i18n from 'src/config/i18n';

hydrate(
  <I18nextProvider
    i18n={i18n}
    initialI18nStore={window.initialI18nStore}
    initialLanguage={window.initialLanguage}
  >
    <ApolloProvider client={apolloClient}>
      <Router>
        <ScrollToTop>
            {renderRoutes(Routes)}
        </ScrollToTop>
      </Router>
    </ApolloProvider>
  </I18nextProvider>,
  document.getElementById('app'),
);

server.ts

import { I18nextProvider } from 'react-i18next'; 
import * as Backend from 'i18next-node-fs-backend';
import * as i18nextMiddleware from 'i18next-express-middleware';
import i18n from 'src/config/i18n';

const app = express();
i18n
  .use(Backend)
  .use(i18nextMiddleware.LanguageDetector)
  .init(
    {
      preload: ['en', 'fr'],
      backend: {
        loadPath: `public/locales/{{lng}}/{{ns}}.json`,
        addPath: `translations/{{lng}}/{{ns}}.missing.json`,
      },
    },
    () => {
      ...
      app.get('/*', serverRender);
      ...
    }
  )


function serverRender(req: any, res: any) {

  const location = req.url;
  const context: any = {};

  const InitialView = (
    <I18nextProvider i18n={req.i18n}>
      <ApolloProvider client={apolloClient}>
        <StaticRouter location={location} context={context}>
          <ScrollToTop>
            {renderRoutes(Routes)}
          </ScrollToTop>
        </StaticRouter>
      </ApolloProvider>
    </I18nextProvider>
  );

  const html = renderToString(InitialView);
  const { url } = context;
  if (url) {
    res.redirect(url);
  } else {
    const initialI18nStore: any = {};
    req.i18n.languages.forEach((l: any) => {
      initialI18nStore[l] = req.i18n.services.resourceStore.data[l];
    });
    const initialLanguage = req.i18n.language;

    return res
      .status(200)
      .set('content-type', 'text/html')
      .send(template(head, html, initialI18nStore, initialLanguage));
  }
}
@rga777 rga777 changed the title i18Next pages flash when loaded i18next pages flash when loaded Jan 25, 2019
@jamuhl
Copy link
Member

jamuhl commented Jan 26, 2019

This is not an issue - there somewhere is a mistake in your code -> not passing proper initial data down to client from the server.

Rolling this proper needs a deep understanding of both next.js and i18next -> my tip head over to https://github.com/isaachinman/next-i18next and adapt the solution there - will save you a lot of pain.

On the way there do not forget to star this repo 🙏

@rga777
Copy link
Author

rga777 commented Jan 26, 2019

Thanks for the quick response. I'm not using next.js, it's my own standard SSR implementation in typescript. I followed the react-i18next razzle SSR example. Any idea what could be wrong in my config? Already liked the repo by the way.

@jamuhl
Copy link
Member

jamuhl commented Jan 26, 2019

🤷‍♂️ hard to tell - from the config i do not see a problem on the first glance - you will need to debug through this. Or simplify it by not using XHR stuff and hard code the translations on init options.resources (so no async loading happens)

@jamuhl
Copy link
Member

jamuhl commented Jan 30, 2019

please start adding changes related to the v9 version to the v9.x.x branch (master will be now for the upcoming v10 hooks)

--> https://github.com/i18next/react-i18next/tree/v9.x.x

@jamuhl
Copy link
Member

jamuhl commented Feb 7, 2019

closing this for now - can't really help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants