How to use results from i18nextBrowserLanguageDetector
I'm trying to use i18nextBrowserLanguageDetector to detect the user's browser language settings and redirect them to the translated page if we support their preferred language. The first part of the code below seems to be working fine, and will cache the user's language preference in a cookie correctly.
import i18next from 'i18next';
import LngDetector from 'i18next-browser-languagedetector';
i18next
.use(LngDetector)
.init({
detection: {
// order and from where user language should be detected
order: ['cookie', 'navigator', 'htmlTag', 'path'],
// keys or params to lookup language from
lookupCookie: LANG_COOKIE,
lookupFromPathIndex: 0,
// cache user language on
caches: ['cookie']
}
});
However, the problem I have is that I want to do something based on the user's language setting but it seems like i18next runs their detection code asynchronously but doesn't provide any callback that I can see. Part of the problem is that their docs are really slim. What I've done is to add some code on document ready like so:
document.addEventListener('DOMContentLoaded', () => {
const langPref = Cookies(LANG_COOKIE);
if (!!langPref && SUPPORTED_LANGS.includes(langPref) && langPref !== currentLocale())
window.location.href = `${location.origin}/${langPref}${location.pathname}`;
});
But the language cookie is never set by the time the page completes loading. I could add a setTimeout/Interval but that's kind of hacky and inefficient.
1 answer
-
answered 2018-10-11 20:21
jamuhl
what about using:
i18next.on('languageChanged', function(lng) { window.location.href = `${location.origin}/${lng}${location.pathname}`; });)
also use the whitelist option so lng is always what you support ;)
See also questions close to this topic
-
How to decode url from ajax response?
inside
token.php
i have this code:$url = file_get_contents($urlPost); preg_match('/\"amplem\":"(.*)\",/', $url, $matches); echo $matches[1];
This
echo $matches[1]
will print out a url like this one:https://example.com/?id\u003dg596c2f91a67e518\u0026itag\u003d18\u0026source\u003dtest
.on
index.php
i do a ajax call:$.ajax({ type:'post', url:siteURL+'/token.php', data:{token:token}, success:function(data){ var i = decodeURIComponent(data); console.log(i); } })
Current result of
console.log(i)
:https://example.com/?id\u003dg596c2f91a67e518\u0026itag\u003d18\u0026source\u003dtest
.Desired result of
console.log(i)
:https://example.com/?id=g596c2f91a67e518&itag=18&source=test
. -
here map api js https
I am using here map api with javascript.
- http:// no problem
- https:// markers are displayed but no map at all !
i have this kind of message in Chrome
Mixed Content: The page at '<URL>' was loaded over HTTPS, but requested an insecure script '<URL>)'. This request has been blocked; the content must be served over HTTPS. VM24:10 Mixed Content: The page at 'https://toto.com/toto.html' was loaded over HTTPS, but requested an insecure script 'http://1.base.maps.api.here.com/maptile/2.1/info?xnlp=CL_JSMv3.0.17.0&app_i…RNQDIayXUw8AOaQ&output=json&callback_func=H.service.jsonp.handleResponse(0)'. This request has been blocked; the content must be served over HTTPS. VM24:10 Mixed Content: The page at 'https://toto.com/toto.html' was loaded over HTTPS, but requested an insecure script 'http://1.aerial.maps.api.here.com/maptile/2.1/info?xnlp=CL_JSMv3.0.17.0&app…RNQDIayXUw8AOaQ&output=json&callback_func=H.service.jsonp.handleResponse(1)'. This request has been blocked; the content must be served over HTTPS. VM24:10 Mixed Content: The page at 'https://toto.com/toto.html' was loaded over HTTPS, but requested an insecure script 'http://1.traffic.maps.api.here.com/maptile/2.1/info?xnlp=CL_JSMv3.0.17.0&ap…RNQDIayXUw8AOaQ&output=json&callback_func=H.service.jsonp.handleResponse(2)'. This request has been blocked; the content must be served over HTTPS. VM24:10 Mixed Content: The page at 'https://toto.com/toto.html' was loaded over HTTPS, but requested an insecure script 'http://1.pano.maps.api.here.com/maptile/2.1/info?xnlp=CL_JSMv3.0.17.0&app_i…RNQDIayXUw8AOaQ&output=json&callback_func=H.service.jsonp.handleResponse(3)'. This request has been blocked; the content must be served over HTTPS. VM24:10 Mixed Content: The page at 'https://toto.com/toto.html' was loaded over HTTPS, but requested an insecure script 'http://1.base.maps.api.here.com/maptile/2.1/info?xnlp=CL_JSMv3.0.17.0&app_i…RNQDIayXUw8AOaQ&output=json&callback_func=H.service.jsonp.handleResponse(4)'. This request has been blocked; the content must be served over HTTPS.
i must be missing something, i want to render maps on both (http:// & https://) with the same code.
Thank you for your help
-
Javascript If Else Statement with mathFoor and mathRandom not working
Hello i am having some troubles with my code i am fairly new to this if anyone could assist me Thanks.
var a = 5; var b = 6; var c = 7; document.write(Math.floor((Math.random() * 3) + 5)); if (5) { alert("mission failed we will get them next time"); } else { alert("sorry"); }
-
Using react-i18next within a class component
I am trying to translate my application using react-i18next. I know how to use it with simple const components, but not within a class.
I'm working with the I18nextProvider. This is my App.js file.
import React, { Component } from 'react'; import { Provider } from 'react-redux'; import { BrowserRouter } from 'react-router-dom'; import { I18nextProvider } from 'react-i18next'; import i18next from 'i18next'; // eslint-disable-next-line import/no-extraneous-dependencies import { hot } from 'react-hot-loader'; import 'bootstrap/dist/css/bootstrap.css'; import '../../scss/app.scss'; import Router from './Router'; import store from './store'; import ScrollToTop from './ScrollToTop'; import { config as i18nextConfig } from '../../translations'; i18next.init(i18nextConfig); class App extends Component { constructor() { super(); this.state = { loading: true, loaded: false, }; } componentDidMount() { window.addEventListener('load', () => { this.setState({ loading: false }); setTimeout(() => this.setState({ loaded: true }), 500); }); } render() { const { loaded, loading } = this.state; return ( <Provider store={store}> <BrowserRouter basename="/easydev"> <I18nextProvider i18n={i18next}> <ScrollToTop> {!loaded && <div className={`load${loading ? '' : ' loaded'}`}> <div className="load__icon-wrap"> <svg className="load__icon"> <path fill="#4ce1b6" d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z" /> </svg> </div> </div> } <div> <Router /> </div> </ScrollToTop> </I18nextProvider> </BrowserRouter> </Provider> ); } } export default hot(module)(App);
Now using it within a const based component is quite easy. Here is an example:
import React from 'react'; import { Card, CardBody, Col } from 'reactstrap'; import HeartOutlineIcon from 'mdi-react/HeartOutlineIcon'; import { translate } from 'react-i18next'; import PropTypes from 'prop-types'; const InfoCard = ({ t }) => ( <Col md={12} xl={3} lg={6} sm={12} xs={12}> <Card> <CardBody className="dashboard__health-chart-card"> <div className="card__title"> <h5 className="bold-text">{t('dashboard_fitness.heartrate')}</h5> </div> <div className="dashboard__health-chart"> <div className="dashboard__health-chart-info"> <HeartOutlineIcon style={{ fill: '#ff4861' }} /> <p className="dashboard__health-chart-number">96</p> <p className="dashboard__health-chart-units">b/min</p> </div> </div> </CardBody> </Card> </Col> ); InfoCard.propTypes = { t: PropTypes.func.isRequired, }; export default translate('common')(InfoCard);
As you can see I just import the translate from the react-i18next and I'm almost ready to go with the t function.
How can I achieve the same within a class component? I want to implement it in this class:
/* eslint-disable react/no-typos */ import React, { PureComponent } from 'react'; import { DropdownItem, DropdownMenu, DropdownToggle, UncontrolledDropdown } from 'reactstrap'; import { translate } from 'react-i18next'; import MenuDownIcon from 'mdi-react/ChevronDownIcon'; import MagnifyIcon from 'mdi-react/MagnifyIcon'; class TradesTableControls extends PureComponent { constructor() { super(); this.state = { rows: 10, }; } changeRowAmount = (rows) => { switch (rows) { case 10: this.setState({ rows: 10 }); break; case 25: this.setState({ rows: 25 }); break; case 50: this.setState({ rows: 50 }); break; case 100: this.setState({ rows: 100 }); break; default: this.setState({ rows: 10 }); break; } }; render() { return ( <div className="trades-table__controls-wrap"> <div className="trades-table__controls"> <UncontrolledDropdown> <DropdownToggle className="icon icon--right" outline size="sm"> <p> {t('history.controls.show')} {this.state.rows} {t('history.controls.results')} <MenuDownIcon /> </p> </DropdownToggle> <DropdownMenu className="dropdown__menu"> <DropdownItem onClick={() => this.changeRowAmount(10)}>10</DropdownItem> <DropdownItem onClick={() => this.changeRowAmount(25)}>25</DropdownItem> <DropdownItem onClick={() => this.changeRowAmount(50)}>50</DropdownItem> <DropdownItem onClick={() => this.changeRowAmount(100)}>100</DropdownItem> </DropdownMenu> </UncontrolledDropdown> </div> <div className="trades-table__controls-right"> <div className="trades-table__control-search"> <input placeholder="Search" /> <div className="trades-table__control-search-icon"><MagnifyIcon /></div> </div> </div> </div> ); } } export default translate('common')(TradesTableControls);
I'm quite new to React and ES6, but I was unable to find the solution online. Would really appreciate any help!
Thanks!
-
"i18next: missingKey" whilst using 'react-i18next' and 'react-router-dom'
I'm working on a React-app in which I'd like to use i18next. To do so, I've added
i18next
andreact-i18next
to my app and everything seems to work like a charm.My app uses the
BrowserRouter
from react-router to switch between various views. When I load the root URL at http://localhost:3000, everything runs like a charm. I can switch pages, which are then translated without any problem. TheBrowserRouter
pushes URL changes to the browser history so users can use the Back- and Forward-buttons in their browser to navigate. This means that once a user clicks one of the menu items in the app, the URL changes, i.e. from http://localhost:3000 to http://localhost:3000/suppliers.If I hit refresh after such a change though, a problem occurs: the console shows
i18next::translator: missingKey undefined translation
and the view is not translated. The same behavior is shown when a redirect from the root URL to a default page is added. The strange thing is that the entire app's root UI (headerbar, sidebar) is translated, it's only the part of the page that is managed by react-router (every component nested within the relevant<Route>
) that shows untranslated. This is also only the fact on browser load, so after a full page refresh or after an initial navigation to the page. If I navigate in my app after loading, even navigating to the same view, the translated view is shown instead.I wrapped everything within my
<App>
-component in a<Suspense>
-block, including the<BrowserRouter>
, but the problem still occurs. My JSX-structure looks somewhat like this:<App> <Suspense fallback={<Loader /}> <BrowserRouter> <SideMenu> {/* Translated objects are shown here */} </SideMenu> <HeaderBar> {/* Translated objects are shown here as well */} </HeaderBar> <Views> <Route path="/sample" component={SampleComponent} /> {/* SampleComponent will not be translated after changing language or navigating to `/sample` immediately/by refreshing` */} </Views> </BrowserRouter> </Suspense> </App>
I'm using a very simple configuration:
import i18n from 'i18next'; import Backend from 'i18next-xhr-backend'; import { initReactI18next } from 'react-i18next'; i18n .use(Backend) .use(initReactI18next) .init({ fallbackLng: 'nl', debug: true, interpolation: { escapeValue: false } }); export default i18n;
I found an older issue on GitHub (https://github.com/i18next/react-i18next/issues/322) without a clear solution and for an older version of react-i18next. I'm not entirely sure how to integrate react-router with react-i18next to prevent this behavior.
Edit another strange thing is the fact that a call to
i18n.changeLanguage
is propagated to every view except the components nested within the<Route>
... I think it might be related but I've still been unable to find a solution... -
How to mock i18n.addResourceBundle?
I'm having some issues with testing i18next, we are using jest + enzyme in a react project + redux.
We implemented
react-i18next
like so in alib/i18n
file:import i18n from 'i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; import { reactI18nextModule } from 'react-i18next'; import moment from 'moment'; import 'moment/locale/nl'; i18n .on('languageChanged', (lng) => { moment.locale(lng); }) .use(LanguageDetector) .use(reactI18nextModule) .init({ fallbackLng: 'en', debug: true, react: { wait: true, }, }); export default i18n;
we created a
__mocks__
folder and used the provided example in (https://github.com/i18next/react-i18next)__mocks__/react-18next.js
we are seeing that once we importi18n
and try to manipulate its properties from a non-jsx file this mock is not called. Our use case is the following:We have an action that requests all the translations and uses the
i18n.addResourceBundle
in the response to set all the translations.in this action, we import the
../lib/i18n.js
file created for the effect, but once we start testing our action we keep on getting the following errorsTypeError: _i18next.default.on is not a function 6 | 7 | i18n > 8 | .on('languageChanged', (lng) => { | ^ 9 | moment.locale(lng); 10 | }) 11 | .use(LanguageDetector) at Object.on (src/lib/i18n.js:8:4) at Object.<anonymous> (src/actions/translation.js:2:1) at Object.<anonymous> (src/actions/translation.test.js:2:1)
we detected that the
__mocks__/react-i18next.js
does not get called, but if we create a__mocks__/i18next.js
file it will mock the importedi18n
instance what makes sense since within the configuration filelib/i18n.js
we are importing itimport i18n from 'i18next';
but once again the provided mock file within your project does not seem to work.We are considering the following scenarios and would like to ask for an opinion: - migrate our fetch action to i18next-xhr-backend and we would like to ask you if this would fix the issue? since we no longer are going to import the config file and therefore no need to mock it, but we would lose our redux action logging functionality since we are using redux store to keep track of the user actions. - mock the file correctly in order to maintain the current functionality and for this, we would like to ask if anyone already experienced the same behavior.
Occurs in react-i18next version
"react-i18next": "^9.0.2",