fix(examples): devinxi Tanstack Start example#2267
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
size-limit report 📦
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main-renamed #2267 +/- ##
===============================================
Coverage ? 76.27%
===============================================
Files ? 99
Lines ? 2651
Branches ? 692
===============================================
Hits ? 2022
Misses ? 505
Partials ? 124 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
I briefly checked the integration, and came across that global i18n instance used in some places eq The global i18n should be never ever used in server side rendered applications because all requests to the server would share the same context and using one global instance will cause a mess |
|
@timofei-iatsenko thanks for taking a look. What would you use instead of the global i18n instance? I'm not in production yet, but have not seen any particular issue so far in local env. |
|
@depsimon you will not see it in local development because you don't have a concurent access to your server-rendered app. You need to have a users which will access app at the same time and then you will get issues. The basic rule - you should create one i18n instance per request and share this instance everywhere. I'm not particulare familiar with tanstack start, so i could not help you with an example. |
|
I'm briefly checking the docs for tanstack start + tanstack router and probably Router Context + You might want to create i18n instance in before load hook and then pass it down using a RouterContext. |
|
Thanks for the pointers, I'll try to take a look at this before merging. It should be trivial to simply add the i18n instance to the router context in the It might be a bit trickier to pass it around in server routes (APIs). |
|
I pushed a commit which is getting rid of all global i18n instances usages. I think that the integration could be improved even more, instead of loading a catalogs using asynchronous import ( This will ensure that the loading of the dynamic catalog would follow the same invalidation / awaiting rules as other async recourses and you will not need handle loading state for catalogs separately. I think that the starting point might be Please check the code i pushed, because i didn't test all possible cases. |
Thanks @timofei-iatsenko I definitely missed the I updated the PR to remove unused imports & add a couple missing translations. The middleware is great, I used it in a project, the only downside is that this code is run twice for each API request that use the middleware: const locale = getLocaleFromRequest()
const i18n = setupI18n({})
await dynamicActivate(i18n, locale)You can attest this by adding a log in the
Interesting, we might achieve this in the |
that's fine, once es module loaded next time it will be taken from cache. it will not affect performance.
Yes i also was curios what is the recomended way to pass a hydration state from server to client. And was a bit disapointed when discovered that they are not showing how to do that and hide all logic in the |
|
@timofei-iatsenko I managed to make a simple router wrapper that handle the catalog hydration. I'm not sure how I did as I never played with that, but it seems to work. We could also delete the export function createRouter({ i18n: serverI18n }: { i18n?: I18n } = {}) {
const i18n = serverI18n ?? setupI18n({})
const router = routerWithLingui(createTanStackRouter({
routeTree,
context: {
i18n,
},
defaultErrorComponent: DefaultCatchBoundary,
defaultNotFoundComponent: () => <NotFound />,
scrollRestoration: true
}), i18n)
return router
}This way, only the server sets the locale & loads the initial catalog. Then it's loaded in the client. WDYT? |
So hydrate/dehydrate is the magic behind the I also don't like the fact that when you switch the language the catalog will be loaded in a different way. So imagine the situation:
I'm actually a big fun of the "stateless" as much as possible approach for the clientside applications. That means when you switch the langauge, i would assume that the whole page will reload from scratch. Because approach with switching language "on the fly" is requires many assumptions/testing and generally effort which is not really worth it. You need not only switch the translation of messages which is in the codebase, but also keep track that all resources will be reload on the correct language, if user in the middle of filling the form, all fields will be populated for correct language and so on. |
Yes, here's the I'd definitely need to be battle tested. It looked okay with all the examples in the example, but that's far from covering everything.
I think I agree with all of this. In this case I chose to loadAndActive + router.invalidate when switching the locale. To be honest that's what I usually do as it makes everything simpler at the cost of a reload, usually users don't expect to keep the current page as is when changing the locale, they just expect to be redirected to the same page in the correct locale. Though the form scenario you are talking about is already covered with the current state as long as the form values are identical (like the value of an option in a select). I'm not sure how we could handle loading the catalog in the server only as the SSR only happens once per session. We could probably return the locale + catalog from the |
|
I believe that localized version of the page should live in in its own path, according to http rules one URI should point to one resource, if you have FR and EN version on the same url ( It could be by domain Basically both versions are the same and difference would be only in middleware / reverse-proxy configuration magic for domain approach. With all above mentioned the app should have a "splat" route segment for a locale, and for this segment a usual "loader" for catalog could be implemented. So switching the language would be as easy as "<Link to="." params={{ locale: 'fr' }}>FR" Storing saved locale to the cookie could be kept, because this will improve user experience on subsequent visits if the language autodetect didn't work properly. |
|
The loader will run in the client when you click the Perhaps the URL-based locale detection can be left to the user's attention. I don't personally use that and there are probably many ways to approach that. In the end it's just yet another way to define the locale. |
Not really, it will always be loaded from the server on the navigation, there will be no call to any lingui methods other then navigating with a router. Anyway, what i'm trying to bring here, due to the fact that this example in the official repo, people will treat it as "official" way of integration. Sometimes just copying without wondering. So i believe it should showcase best practicies, not the dirty hacks. As i said the best practice to support SEO and caching and static site generation would be to have a separate path for each locale |
|
I've added an example page that uses a
Are you sure? In TSS, the loader is called client-side except for the first (SSR) request. Only the server functions are called in the server, but that wouldn't let us manipulate the client's i18n instance.
Do you think the In such a case, the |
|
Hi @depsimon, following up on this one. TanStack Start has gained a lot of momentum, so an up-to-date Lingui example would be really valuable. Is there anything still blocking it? Would be great to have this example refreshed. Thanks for the work here! |
22576ad to
f9f6acf
Compare
f9f6acf to
9fe4b30
Compare
|
@andrii-bodnar I just updated all dependencies to run on the latest tanstack & lingui versions. Everything seems to still work fine. I think it's ready to be tested again & perhaps merged. I haven't updated to lingui 6 on my project, but this is how I use it currently. |
Description
This PR updates the tanstack-start example to use the latest version which relies on vite instead of vinxi. It also updates to tailwind 4
Types of changes
Could be fixing TanStack/router#4279 & TanStack/router#4409
Checklist