The plan is to provide both versions in the future:
We for sure will have to maintain v9 for a while.
TODO:
useT -> useTranslation, withT -> withTranslationsRelease winter 2018/19: https://reactjs.org/blog/2018/11/27/react-16-roadmap.html (won't be 16.7!!! https://reactjs.org/blog/2018/12/19/react-v-16-7.html)
Release seems to be 4th of February: https://github.com/facebook/react/pull/14692
Is it going to be a full (or close to it) rewrite? Wanna give typescript a try 馃殌 ? Either way, you can count on me to accomplish that!
@schettino will sleep over it...
@isaachinman made a suggestion for next.js / SSR in general:
all useTranslation report used namespaces: https://github.com/i18next/react-i18next/blob/master/src/hooks/useTranslation.js#L38
there is a new hook useSSR which enables passing initialI18nStore, initialLanguage which is used in the withSSR HOC which does two things:
1) use that useSSR hook to add those initial values on client side: https://github.com/i18next/react-i18next/blob/master/src/hooks/withSSR.js#L8 (does nothing than add store and lng to i18next like before - and asserts this gets done only once)
2) sets getInitialProps function on the Wrapped component: https://github.com/i18next/react-i18next/blob/master/src/hooks/withSSR.js#L15 -> that function can also be used for people prefer only using _app.js i guess: https://github.com/i18next/react-i18next/blob/master/src/hooks/context.js#L44
Not yet had time to make the exampe to validate the implementation - but would be sufficient to get your first opinion and change suggestions.
I like the context code, and especially like addUsedNamespaces in useTranslation. This is a great, simple solution.
As far as useSSR, the implementation looks reasonable, but I wonder why we need to have something called useSSR in the first place? It seems it's just to catch the initialLanguage, right?
yes useSSR is more or less a setInitialStore and setInitialLanguage (by calling i18n.changeLanguage) - i'm neither happy with the naming nor to much of exposing it...but using it in the HOC seems to have it seems reasonable -> but yes...naming it as a hook seems a little off - as it is just a function not a real hook -> so i'm open for suggestions.
Yes that makes sense. I don't think I made my other point as clear as it could be: what would prevent us from doing this useSSR step by default, so that it's one less thing users have to worry about? I am a big fan of SSR-by-default behaviour.
We could either pass default args into withSSR, or do some fallback/bail out inside the function itself, where we simply return the wrapped component if our initial setting needs aren't met.
Or maybe I am misunderstanding, and the withSSR HOC is only for internal use after all?
@isaachinman the useSSR is default in withSSR -> so withSSR is what using withI18next is today (with the only difference not giving you the t function).
the useSSR could be moved just into withSSR (i just thought having that for non next.js samples would / might make sense)
So, how is it going?
@zaguiini how is what going?
The typings for Typescript, mainly. I want to remove all those @ts-ignores from my code :D If I can help in any way...
ah...we not yet started with those honestly...there is as far as i know no eta for react 16.7 yet - so not the highest prio yet. If you like you might provide a PR so those typings will be ready when we can release v10.
@schettino not sure if you would prefer adding those or your ok with reviewing a PR from @zaguiini ?!
@zaguiini I just added a file types/react-i18next/hooks.d.ts to my own project containing the following lines in order to use useTranslation:
import { TranslationFunction, i18n } from "i18next"
import { Namespace } from "react-i18next"
export function useTranslation(ns?: Namespace): [TranslationFunction, i18n]
I've never written types separate from the source before but I guess the index.d.ts from react-i18next needs the following code:
import { TranslationFunction, i18n } from "i18next";
import { Namespace } from "react-i18next";
declare module 'hooks' {
export function useTranslation(ns?: Namespace): [TranslationFunction, i18n];
}
That file needs a lot more function definitions as react-i18next/hooks exports a lot more. As I'm currently only in need of useTranslation I just added that one to mine and move forward with my project.
Amazing, @bikeshedder. We could maybe use those definitions in order to submit the PR. Would you mind opening it with those changes?
You're augmenting a top level "hooks" module. Make sure you include the
full name of the module you are declaring, and to declare a new module you
cannot have any top level imports or exports.
Is it intentional that the hooks version only supports a single global i18next instance?
Despite having a file named context it doesn't actually do anything contextual, it's just a bunch of module-scoped globals (aka singletons).
Also, one thing I noticed in the old I18nProvider is, the reason why _only_ NamespacesConsumer or withNamespaces could react to language changes is that the I18nProvider itself doesn't ever update the lng key in the context unless it is re-mounted or receives a new instance of i18next. More of the listening/updating work needs to be put in the provider, and that is something that can be fixed in the hooks API too (once this global stuff is fixed).
Yes, that context is more an internal context not related to the context of react (renaming that file to i18n-scope or whatever might be reasonable).
NamespaceConsumer is part of withNamespaces and listens to the languageChanged event - so every component using those will get updated. The Provider only passes down the i18n instance and is obsolete in cases not needing multiple i18n instances in one app.
Yes, currently having more than one i18next instance in one app is not supported in the hooks version. Not sure if we should support that - as it does not really offer much value - edge case.
Still my plan is to work on an option to eventual support the react context api from within the hook. Not yet sure.
PR'd some typings in #650.
As a library author I'd value a react context api support in the hooks variant.
My case is to leverage multi i18next instance to provide default instance so Apps using my library don't need to provide one in case they don't need to support i18n in their Apps.
My library will use i18next.createInstance to create default instance, and provide it to I18nextProvider wrapped in other context provider. User can override this using i18next default instance or other.
Or is this case supported already in current hooks version? Maybe I'm missing something, but I thought I18nextProvider is the way to solve my case.
@panjiesw we might incorporate the useContext hook inside our useTranslation hook - but currently i'm not sure if we should support multiple i18next instances -> it just adds more complexity for being used seldom
@panjiesw just thought a little more about the context provider stuff - while i don't like to add it to the core you should easily be able now to run your own as both Trans and useTranslation allow to pass in your own i18n instance:
// Trans Component
<Trans i18n={i18n}>...</Trans> // -> create a Custom component wrapped in your ContextConsumer
// useTranslation
const myComponent = () => {
const [t] = useTranslation('ns1', { i18n });
return <div>{t('myKey')}</div>
}
// -> create a own hook combining useContext and useTranslation
Should be rather straight forward. But might be i do not yet fully understand your usecase for needing multiple instances.
Thanks @jamuhl, that's a great addition! It fits my use case above
EDIT:
Looks like the typescript definitions for useTranslation and Trans component also need to be updated to allow above changes
Hi @jamuhl I do have a similar use case with SSR and the latest version.
When my server starts, it creates one instance of i18n per available language. When a request is received, the correct language is determined and the correct instance of i18n is given to the server rendering process through the i18nextProvider, so I don't have to create a new i18n instance for each request (language cannot be changed server side so the language of the i18n instances never change). I updated to the v10 and it doesn't seem to work anymore as the i18NextProvider doesn't seem to exist anymore. Is there something planed in the upcoming version to handle this use case ?
Thanks!
@Vincz i would prefer avoiding it as it adds unneeded complexity for the regular use case...but we might add back a react context provider and components extending the useTranslation and Trans consuming i18n instance from context -> eg. useTranslationWithContext and TransWithContext. On the other hand we might just leave that to userland or next-i18next
If I understand correctly, currently there is no way to specify which i18n instance should be use in the components tree ? The used i18n instance will always be the one specified in let i18nInstance;
of the context.js, right?
The idea wouldn't be to add new hoc like useTranslationWithContext, but to use the existing methods / functions / hooks with a different i18n instance.
My use case seems pretty common to me and more optimized than creating a new instance of i18n for each request.
Why not just keep the i18nextProvider and add a check in the hooks and hoc to see if there is a i18n instance in the context, if so, use it, otherwise, use the "global" one ?
because i don't like the context luring around if not needed.
All the components allow to pass a i18n to them:
// Trans Component
<Trans i18n={i18n}>...</Trans> // -> create a Custom component wrapped in your ContextConsumer
// useTranslation
const myComponent = () => {
const [t] = useTranslation('ns1', { i18n });
return <div>{t('myKey')}</div>
}
// -> create a own hook combining useContext and useTranslation
So it's easy to control which i18n instance is used (just not built in)
Why not just keep the i18nextProvider and add a check in the hooks and hoc to see if there is a i18n instance in the context, if so, use it, otherwise, use the "global" one ?
Easy for the hook - not so easy for the Trans component which would need to be wrapped in Context consumer
Ok, I understand.
I gave a shot at what you suggest (create a custom hook using a i18next context).
But it seems there is an error in the commit of this morning. I get the following error coming from the withTranslation hoc.
Invariant Violation: Hooks can only be called inside the body of a function component.
@Vincz give me a few minutes...just working on bringing it back - would be awesome if you got time later to test
@jamuhl Yep! No problem. I'll check and let you know :)
@Vincz @panjiesw [email protected] brings back I18nextProvider
import { I18nextProvider } from 'react-i18next';
<I18nextProvider i18n={myI18nInstance}>{children}</I18nextProvider>
Using it should pass i18n instance down via React context all Trans, useTranslation, useSSR should pick that instance via useContext provided by react (withTranslation, withSSR use the hooks above so it should be picked everywhere).
Ok, I tried and I still get the same issue Invariant Violation: Hooks can only be called inside the body of a function component. when using:
`
export default withTranslation()(App);
https://github.com/i18next/react-i18next/blob/master/src/withTranslation.js#L6 hm...wondering it's a function and i do not get that warning in https://github.com/i18next/react-i18next/blob/master/example/react/src/App.js#L13
I'm so sorry. The issue was that I was using react 16.8.0-alpha.1 with ... react-dom 16.7
Seems to work fine now. I'll keep testing and let you know if I find something.
@Vincz good catch -> will need to check this against latest alpha...
@jamuhl by looking at /src/useTranslation.js#L33, you're conditionally calling useContext hooks, as far as read the Hooks Docs, it's required to Only Call Hooks at the Top Level, which means Don鈥檛 call Hooks inside loops, conditions, or nested functions.
Just pointing out that this has a possibility to bite in the future
@Vincz hm...still can't reproduce in the example also when using 16.8.0-alpha.1 - would be great if you can dig a little deeper - don't like to run into a surprise when this lands finally
@panjiesw guess this is ok for now (if it works) in future if need to remove the condition we can do that.
@jamuhl after further code reading, yeah I also think it'd be fine for now.
Also what @Vincz experienced is because his react-dom dependency version is different than his react dependency version. This breaks the internal Hooks flow.
I have a question, I know hooks is coming out in 16.8.. but Suspense has 2 releases, one thats already live and another that won't be released till mid 2019.. is the suspense being used here going to work with 16.8 (if its just hooks, no data fetching suspense)? We're trying to build our app using hooks, but it will be launching before the Suspense API is released.
Already working suspense (not the "loading API") -> see the sample in https://github.com/i18next/react-i18next/tree/master/example/react
react v16.8.0 is published ---> [email protected] is on npm ready for production
Looked to avoid getting issues posted here - open a new issue if running into problems.
Thanks again to all of you participating into getting to the final state of v10 馃憦
Most helpful comment
Already working suspense (not the "loading API") -> see the sample in https://github.com/i18next/react-i18next/tree/master/example/react