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

Emit an "added-all" store event on finishing loading all the dictionaries queued to be load #1993

Closed
vituchon opened this issue Jul 6, 2023 · 7 comments

Comments

@vituchon
Copy link

vituchon commented Jul 6, 2023

Hi Guys, How you doing?

Don't wanna be too annoying, but I reach a case where I do need an store event that is triggred after all the dictionaries are loaded.

Looking at the sources, I reached this line and I see that the code is intentionally emiting an "added" store event for each dictionary beign loaded.

And I need a handler invoked when all the dictionaries to be load are done loading.. so, my rought guess is, that I need something like this in the source code:

/i18next.js#L1650

toLoad.toLoad.forEach(function (name) {
  _this4.loadOne(name)
});
this.emit('added-all', languages, namespaces) ; // what about something like this?

I know is not that easy because loadOne eventually invoke an async function, and you mainly work with callbacks on your code... so probably there is some tedious work to do for it.

My motivation behind this request is explained with full detail here... you may read all in order to understand what is behind this corner case.

Thanks

Greetings

Víctor

@adrai
Copy link
Member

adrai commented Jul 6, 2023

Having something like an added-all event will be confusing, because what is "all"? The BackendConnector does not know what all means? In theory only on user land you know what all means. In your case es + es-AR...
What if the load trigger comes in multiple calls... for example: first for unspecific languages and then for specific languages... that added-all event would not work in your case.

So I would suggest you make use of hasResourceBundle (and/or hasLoadedNamespace) like react-i18next is using it: https://github.com/i18next/react-i18next/blob/e6cb106a134ddc1e21c5cb69fedbab466f4b9223/src/utils.js#L110

@jamuhl
Copy link
Member

jamuhl commented Jul 7, 2023

you can also call: https://www.i18next.com/overview/api#loadnamespaces if you need a callback -> it does not trigger a loading again if already in queue for loading but still properly calls the callback when all is loaded

@vituchon
Copy link
Author

vituchon commented Jul 7, 2023

Hi Guys, How you doing? You may greet me as well (it won't bother me at all, instead would be nice!)

First of all, thanks for you replies!

I guess I stumble into this problem because the way I program things: Listeting to "add" store event is not enought and I do need to make something else before resolving translations. You both give me good advises that I will have in mind for polishing my workaround into a final stable solution using the i18next proper instrumentation.

TD;DR;

I spend some hours trying to describe better the situation and trying to answer to you both properly, quoting texts if necessary. Yoy may skip this because I do know that I'm kind of -verbose it guy,

So... @adrai

Having something like an added-all event will be confusing, because what is "all"?

By "all", I refer to all the dictionaries queued to load => allow me to show it in the code.
So when you switch the current language and you are using a dialect (like es-AR) then a dictionary for that dialect is loaded and then, in another step, the dictionary for the "base language" (es) too, right? By "All" i mean loading that "family of dictionaries", I know that is not an oficial term, allow me to use it a little bit.

What if the load trigger comes in multiple calls... for example: first for unspecific languages and then for specific languages... that added-all event would not work in your case.

I'm sorry, I'm not getting this as I do not get the concept of "unspecific language"; all the languages we have are in our app are well know.

So I would suggest you make use of hasResourceBundle (and/or hasLoadedNamespace) like react-i18next is using it: https://github.com/i18next/react-i18next/blob/e6cb106a134ddc1e21c5cb69fedbab466f4b9223/src/utils.js#L110

OKEY, ADVICE taken! Thanks! I will code my solution using this guideline!.

And I guess this kind of advice is what I need. Off course we can continue talking about the above threads, if you have time. But I understand that my proposal is not a trivial change and can take long time before, eventually (if approved), made it into the master branch.

And @jamuhl....

you can also call: https://www.i18next.com/overview/api#loadnamespaces if you need a callback -> it does not trigger a loading again if already in queue for loading but still properly calls the callback when all is loaded

Well I'm using the function loadnamespaces... allow me to explain a little depper: We use angularjs with ng-route for fine tuning the navigation. I configure it to load required namespaces prior to landing on a screen.

Speaking in code, it is something like this:
image

and buildI18NextResolver definition is:
image

And you know, that the highlighted line actually gets invoked after "all" dictionaries were loaded (so the then's promise handler). So I could tackle this affair there enforcing some synchronization in the callback or then's promise handler... but the thing is not quite there...

I know that i arrived to this corner case because how I program the events handlers: I'm listening to store event "add" like this, in another file, that is not related to an screen, and the registration of the event is performed when the browser evaluates the fetched js file (after the i18next.init function gets executed OK!).
image.

And eventually inside resolveTranslation I perform some calls to i18next.t (actually we are using ng-i18next, so we call ng-i18next that, in turn, call 18next.t)

And in the exact moment when the "add" store event gets executed (because a dictionary is loaded), the flow goes here (as expected):
image

And I trace it into the i18n code and I put a breakpoint in the line that actually performs the resolution:
image

I open the network panel and I do see that a dialect dictionary is loaded but not the base language dictionary.

The dialect
image

The base language
image

So, the base language is still not fetched, and that causes the resolution to be undefined, because we expected the fallback mechanism to work properly... as long both dictionaries are effectively loaded.

Hope to clarify at least something.

Again thanks for reading me and for the patiente 🙏🏿

Keep up the good work 💪🏿

P.S: Of course, If anyone reply I will gladly read it.

@jamuhl
Copy link
Member

jamuhl commented Jul 7, 2023

Hi vituchon...hope you're well and getting closer to solve the issue.

I think you should get away from using that added event from the store...it's just not what you're looking for.

The next best idea that came to my mind was using a custom event.

Where you're loading the namespaces (buildI18NextResolver):

return window.i18next.loadNamespaces(namespaces).then(() => {
  window.i18next.emit('custom-loadedAll');
}).catch( ....;

in the other file you used the window.i18next.store.on('added' ... use window.i18next.on('custom-loadedAll, handler)`

Hope this could be a suitable solution?

@vituchon
Copy link
Author

vituchon commented Jul 7, 2023

Well jamuhl, my day is getting better with answers like this!

You provide me an awesome advice there! I will try that....

I guess I bothered enough for these days! So BIG thanks and until next time!

@vituchon
Copy link
Author

vituchon commented Jul 11, 2023

@adrai @jamuhl how you doing? hope you both to be fine! Allow me to share a good 🧉 with 🥐!

I try your idea, and it works passing an extra parameter to the emit function.

BTW I observe that the code actually work with aditional arguments (as it is intentionally programmed to do so) but the type definition doesn't fit as it prevents from using an extra parameter...

I have to..

1️⃣ : manually disable type safety using this:

(<any>window).i18next.emit("added-all", namespaces);

OR

2️⃣ : modify a little bit the definition file (that would be my recommend approach)

emit(eventName: string, ...args: any[]): void;

Either way works like a charm!

Greetings

Víctor.

@adrai
Copy link
Member

adrai commented Jul 12, 2023

v23.2.10 should address this

@adrai adrai closed this as completed Jul 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants