RFC: I18n Routing #17078
Replies: 62 comments 168 replies
-
Thanks for the great work on this RFC, it's really great to see better i18n support in Next.js itself! I just have one question:
Does this means that automatic language detection only runs on the index page (ie: |
Beta Was this translation helpful? Give feedback.
-
Looks great! I find it unfortunate that translating non dynamic route segments is not part of this RFC. It is a hugely important feature (for SEO as well). At least consider providing an example manually implementing it with rewrites (that's what we're currently doing ourselves in production), but having it be an official feature would be beneficial for everyone. |
Beta Was this translation helpful? Give feedback.
-
This RFC strongly looks like our own implementation at Next Right Now. (We only implemented "Subpath holding the locale" option) SSG locale subpath handling: Réusable usage within SSG page: There are a few differences, though:
Overall, this RFC looks like it does everything NRN do, and even more in a near future. I'd say it looks really great and I (personally) love it that you intend to implement it so similarly compared to NRN. Kinda means we were on the right track of things 😄 |
Beta Was this translation helpful? Give feedback.
-
Some use cases we had at my last job, not particularly unique but worth highlighting:
These use cases (along with the translated static segments mentioned in the original post) were the primary motivators for me creating our own route config library (https://github.com/Zegocover/next-route-resolver). I can't see me moving away from such a library entirely (because I much prefer a config file to the pages directory for managing routes), but it'll be exciting to see more of its use cases support natively. |
Beta Was this translation helpful? Give feedback.
-
Finally, been looking forward to the last one! "Setting lang to the html tag" |
Beta Was this translation helpful? Give feedback.
-
Will this RFC eventually cover optimization of fetching translations? I'm asking because optimization is strongly related to the way the data is used, and is very different whether handled server or client side. For instance, in Next Right Now, we implemented two presets/examples using GraphCMS (Headless CMS) and Airtable. Eventually, we had to build a built-in caching mechanism to make sure not to overfetch during initial build (SSG) or on the server side (SSR) to avoid hitting the 3rd party API too often (Locize), which would incur much higher costs. It wasn't a simple task to make sure all the proper caching mechanisms were in place. Implementation (with LOTS of documentation): Docs extract: /**
* Fetch translations from Locize API (both base translations + translations that are specific to the customer)
*
* We use a "common" namespace that contains all our translations
* It's easier to manage, as we don't need to split translations under multiple files (we don't have so many translations)
* (that may be a bit more optimized to split them under different namespaces, but that feels like anticipated optimization that isn't needed atm)
*
* We don't use the Locize backend for fetching data, because it doesn't play well with Next framework (no serverless support)
* @see https://github.com/isaachinman/next-i18next/issues/274
*
* Instead, we manually fetch (pre-fetch) the translations ourselves from getServerSideProps/getStaticProps (or getInitialProps)
* @see https://github.com/i18next/i18next/issues/1373
*
* XXX Make sure you don't use "auto" publish format, but either "nested" or "flat", to avoid the format to dynamically change (We use "nested")
* @see https://faq.locize.com/#/general-questions/why-is-my-namespace-suddenly-a-flat-json
*
* XXX Caching is quite complicated, because the caching strategy depends on the runtime engine (browser vs server), the stage (development, staging, production) and the rendering mode (SSG, SSR)
* For instance, we don't want to cache in development/staging, but we do for production in order to improve performances and decrease network usage (when using SSR)
* When using SSG, caching doesn't matter because the pre-fetch is performed during the initial build (build time) and never at run time, so we don't need a caching mechanism
*
* Also, we want to invalidate the cache as soon as a new version is deployed, so that the end-users get the latest version immediately
* (and avoid missing translations because we'd be using a cached version)
* @see https://github.com/i18next/i18next/issues/1373
*
* Explanation about our cache implementation:
* - Browser:
* * In-memory: Once the translations are fetched from Locize, they're memoized
* - Development/staging stages:
* - It's very useful in development/staging, where we don't have any browser caching enabled, because it'll reduce the API calls
* With this in-memory cache, the Locize API will be hit only once by the browser, and then the in-memory cache will take over when performing client-side navigation
* - The in-memory cache will be invalidated as soon as the page is refreshed (F5, cmd+r, etc.) and a new API call will be made
* - XXX Without in-memory cache, a new API call would be sent for each page transition, which is slower, costly and not necessary
* * Browser's local cache:
* - Development/staging stages:
* - The browser's local cache is completely disabled in development/staging, so that we may work/test with the latest translation
* See the official recommendations https://docs.locize.com/more/caching
* - Production stage:
* - When in production, the browser will also cache for 1h, because we configured a "Cache-control: Max-Age" at https://www.locize.app/p/w7jrmdie/settings
* We followed the official recommendations https://docs.locize.com/more/caching
* 1h seems to be a good compromise between over-fetching (we don't expect our users to use the app for more than 1h straight)
* and applying translation "hot-fix" (worst case: "hot-fix"" be applied 1h later, which is acceptable)
* XXX SSG pages won't be affected by changes made in Locize once the app is built. A rebuild will be necessary to update the translations
* - The browser's local cache will be invalidated as soon as a new release of the app is deployed,
* because the value of "build" in the "loadPath" url will change and thus the browser will not use its cached version anymore
* - Server (XXX this affects SSR pages in particular, as mentioned above):
* * In-memory: Once the translations are fetched from Locize, they're memoized
* - Development:
* - Once the translations are memoized, they'll be kept in-memory until the server restarts
* Note that full page reload may clear the cache if max-age is reached
* - Staging/production:
* - Once the translations are memoized, they'll be kept in-memory until... God knows what.
* - It's very hard to know how/when the memoized cache will be invalidated, as it's very much related to AWS Lambda lifecycle, which can be quite surprising
* - It will likely be invalidated within 5-15mn if there aren't any request performed against the Lambda (when nobody is using the app)
* - But if there is a steady usage of the app then it could be memoized for hours/days
* - If there is a burst usage, then new lambdas will be triggered, with their own version of the memoized cache
* (thus, depending on which Lambda serves the requests, the result could be different if they don't use the same memoized cache)
* This could be the most surprising, as you don't know which Lambda will serve you, so you may see a different content by refreshing the page
* (especially when browser cache is disabled, like when using the browser in Anonymous mode)
* - To sum up: The memoized cache will be invalidated when the server restarts
* - When a new release is deployed (this ensures that a new deployment invalidates the cache for all running Lambda instances at once)
* - When a Lambda that serves the app is started
* - The in-memory cache is automatically invalidated after 1h (see memoizedCacheMaxAge). Thus ensuring that all Lambdas will be in-sync every hour or so,
* even when hot-fixes are deployed on Locize
* * Server's local cache:
* - Development:
* - There is no known server-side caching abilities while in development, and we already rely on our in-memory cache while in development
* - Staging/production:
* - We could use on-disk caching mechanism during build, but it's not necessary because our in-memory cache is less complicated and doesn't require a writable file system
*
* @param {string} lang
* @return {Promise<string>}
*/ As you can see, it got complicated real fast. Between client/server and development/staging/production, the rules for caching are quite different in order to provide the best DX and UX. Is that something you're thinking of helping about at some point in the future? I'm conscious it's out of the scope of the first version of this RFC, but might it be built-in within Next.js, eventually? |
Beta Was this translation helpful? Give feedback.
-
Awesome! Glad to finally see Next.js starting to add i18n features. I have a couple of questions:
|
Beta Was this translation helpful? Give feedback.
-
This support will be really amazing, today in the current solution that I'm working on we use exportPathMap to have i18n support by subfolder. |
Beta Was this translation helpful? Give feedback.
-
Thanks, been waiting for this feature for a long time 🙌🏾 Question: will it let us do client-side navigation when changing the locale, or full page reload is still needed? |
Beta Was this translation helpful? Give feedback.
-
I'm curious how locale detection would be solved in the underlying serverless environment, I'd expect the function for that to have some startup time, resulting in longer response times than serving the static content from a CDN. Wonder if there is any more sophisticated approach to this than Lambda@Edge. |
Beta Was this translation helpful? Give feedback.
-
I am very happy to see this RFC. Something like this is needed. 😊
With these strategies in place, how will they be managed at the navigation level? For example, the first one, how would it be like from next/link or next/router to navigate? If we enter <Link locale="es" href="/contact" /> Would this also fit into this proposal? Or how will this be handled? |
Beta Was this translation helpful? Give feedback.
-
We see SSG and SSR support, but this RFC would also work for static websites ( I think it's great that the language is not inside pages like |
Beta Was this translation helpful? Give feedback.
-
this will support for custom server for express/hapi? |
Beta Was this translation helpful? Give feedback.
-
Are there any API route implications for this? I imagine it would be assumed that the developer can handle "localized" api requests by accessing headers from the request directly in the API route. I can't think of any particular use case for it, but it's fun to ask ¯_(ツ)_/¯ |
Beta Was this translation helpful? Give feedback.
-
Great seeing this addressed ❤️
oww - would really love to see that happen as part of this. It's one of the things we have extremely hacky workarounds in place for and I'm excited that you're even considering it. Hoping it doesn't fall through the cracks somewhere 🤞 😅 |
Beta Was this translation helpful? Give feedback.
-
Hey. Is it possible to somehow force pass locale to next? The RFC describes a mechanism for storing locale in cookies, but so far, as I understand it, it is not implemented. How else can you do this? And it's also very annoying when, for example, from France, but you want to use the English version of the site, but you cannot do this, because when you go to / you are redirected to the French version |
Beta Was this translation helpful? Give feedback.
-
Hi there, I just upgraded to Next 10 and modifying our code to suit the changes introduced in the new version. The following localeDetection rule doesn't seem to be working with the Next 10 release candidate. Also is there a documentation on what would be the effect of turning off locale detection ?
I want to retain the locale in a cookie or local storage so that we could remember it when the user comes back to the site again.
I want to modify this getInitialLocale to use cloudflare headers. In the current state it goes into an infinite loaded state when use a |
Beta Was this translation helpful? Give feedback.
-
Is it possible to get some page using lang-locale like I don't want to build n * locales pages for pages like |
Beta Was this translation helpful? Give feedback.
This comment was marked as spam.
This comment was marked as spam.
-
Hi everyone, I've just published next-intl 🌐, a minimal, but complete solution for managing internationalization in Next.js apps. It complements the internationalized routing capabilities of Next.js by managing translations and providing them to components. Features:
Usage: function LatestFollower({user}) {
const t = useTranslations('LatestFollower');
return (
<>
<Text>{t('latestFollower', {username: user.name})}</Text>
<IconButton aria-label={t('followBack')} icon={<FollowIcon />} />
</>
);
} // en.json
{
"LatestFollower": {
"latestFollower": "{username} started following you",
"followBack": "Follow back"
}
} I'm currently using this approach in two production apps and I'm quite happy with it. Have a look and let me know what you think! 🙂 |
Beta Was this translation helpful? Give feedback.
-
I did a short demo video about next-translate:
I hope you find this useful 😊 (sorry my English) |
Beta Was this translation helpful? Give feedback.
-
Hi guys. Is there any recommended approach to localize the URL ?
It is clearly the same page just translated (both URL and text), and indeed you can not mix language and address:
For sure I can redirect from one to the other and use a component to share the implementation, or consider all the routes dynamic, but just guessing if there is a more elegant way to do it (please notice it does scales with the number of languages of the site so you end up with 50 pages that are really 1 just translated). |
Beta Was this translation helpful? Give feedback.
-
Just wanna share my two starters for static pages (SSG |
Beta Was this translation helpful? Give feedback.
-
For those interested in consume translations in Next 10 with:
In version 1.0 of Next-translate we have made a webpack loader that automatically loads the translations without having to worry about typing in each page getStaticProps, getServerSideProps etc. The loader does it for you. And all the routing part is using the i18n routing of Next.js. Next-translate 1.0 is just released today: |
Beta Was this translation helpful? Give feedback.
-
Where can I see current goals for next realeses of this feature? Is there something like a todo list? |
Beta Was this translation helpful? Give feedback.
-
Perhaps I'm missing something, my default language is set to When a user with language set to However, when the same user accesses one of the pages directly, manually typing the url to a page the For example, accessing: Is it possible to rewrite the url on all pages, urls, so users accessing a page directly via the url receive the correct translations? |
Beta Was this translation helpful? Give feedback.
-
Is there an ETA for localizing URLs? It's crucial for SEO. For example, an |
Beta Was this translation helpful? Give feedback.
-
I'm going to repost a question asked a few times above, as we use a similar url pattern and not being able to customize the locale format is preventing us from adopting the new i18n features:
Thanks for all the great new features in Next v10 by the way 🍻 |
Beta Was this translation helpful? Give feedback.
-
For those who are looking for an internationalized dynamic routing example (localized dynamic page URLs), have a look at this PR that I have created to add to Next.js examples. |
Beta Was this translation helpful? Give feedback.
-
Is there a way to override locale returned from Let me elaborate: Let's say we already have a preferred locale from a user in a DB. We fetch that data and want to override/overwrite the returned locale from Any clues? |
Beta Was this translation helpful? Give feedback.
-
Edit: This RFC has shipped! Learn more: https://nextjs.org/blog/next-10#internationalized-routing
Goals
Provide i18n libraries with the supported locales / the default locale / the current locale
Provide different strategies as every application has different requirements
/[locale]/blog/hello-world
example.com
,example.nl
(different TLD)Provide "language affinity", ensuring a certain user is redirected or shown their selected language once they access it for the first time
/
route which then redirects to the previously selected languageProvide language detection
Accept-Language
header/
routeSupport the hybrid server-side rendering and static generation use case
Non-goals
[locale].example.com
— Likely to be added later if there's interestBackground
Over the past few months we've talked with a lot of enterprises and mid-sized/small business about their usage / plans to adopt Next.js. A recurring theme especially with companies planning to adopt Next.js is their requirement for internationalizing and localizing their Next.js project.
Generally, this requirement is mostly related to routing. There's 3 different strategies that came up the most which are outlined in the goals section of this document.
This RFC focuses only on the routing goals of internationalization and localization, this would allow i18n libraries like react-intl, react-i18next, lingui, rosetta, and other to be used.
Once this RFC has landed we can evaluate implementing the other part of internationalization, which is translation and extraction of translation strings. However, this is handled by the currently used libraries quite well (see previous paragraph for options) – just requires some extra setup.
Proposal
Providing supported locales
Next.js has to be made aware of the concept of locales, this can be provided through
next.config.js
:Setting a
defaultLocale
is required in every i18n library so it'd be useful for Next.js to provide it to the application.locales
is an array of UTS Locale Identifiers.A UTS Locale Identifier tag generally looks like
language-region-script
(there's more parameters, they're omitted to keep the explanation short). Theregion
andscript
are optional. An example:en-US
(English as spoken in the United States)nl-NL
(Dutch as spoken in the Netherlands) andnl
(Dutch, no specific region).Language Strategy
The main goal of this RFC is to solve the 3 strategies outlined:
Subpath holding the locale: e.g.
/[locale]/blog/hello-world
pages
directory. The locale will be handled outside of thepages
directory structure. Nopages/[locale]
directory will be required./blog/hello-world
would show the default locale.Different domains holding the locale: e.g.
example.com
,example.nl
(different TLD)next.config.js
Locale Detection
Locale detection will be built-in based on the
Accept-Language
header. We'll find the closest matching locale for the supported locales that the browser is providing.It will work on the
/
route which will do a307
(temporary redirect) to the matched locale.For example if
Accept-Language: nl-NL,nl;q=0.9,en-US;q=0.8,en;q=0.7
(taken this from my own Browser which has 2 languages set up) we'd try to findnl-NL
ornl
first before checking foren-Us
and thenen
. If the application supports none of these locales we'll redirect to thedefaultLocale
.A Locale can be forced by setting the
NEXT_LOCALE
cookie to a locale that is supported by the application. E.g.NEXT_LOCALE=nl-NL
It can be disabled if needed by setting the
localeDetection
option innext.config.js
tofalse
:apple.com has a client-side detection which then shows a message asking if you want to change to a different locale. If there's enough interest in this type of solution we can provide tools for this approach as well.
Data Fetching
Static Generation:
Server-Side Rendering:
Search Engines / Crawlers
Initially Next.js won't automatically add in
<link rel="alternate"
with the differenthreflang
per locale given that the alternate links are highly dependent on the individual application. E.g. maybe you only have blogposts in a single language instead of multiple languages. With SSG it could potentially be known but with incremental static generation and SSR this becomes significantly complicated to provide automatically.You can learn more about
hreflang
here: https://support.google.com/webmasters/answer/189077?hl=enAs said earlier in this document the automatic language detection will only run on the
/
route and automatically redirect to the correct locale. The reason behind this is to avoid cloaking urls / breaking crawling of the different languages. For example what if a crawler did not setAccept-Language
and goes to/nl/blog/hello-world
, we'd want it to be indexed and not redirected to/en/blog/hello-world
(ifen
is thedefaultLanguage
).Next.js will automatically set the
lang
attribute on the<html>
tag.Beta Was this translation helpful? Give feedback.
All reactions