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

feat: Support providing a locale in i18n.ts instead of reading it from the pathname #1017

Merged
merged 25 commits into from
May 15, 2024

Conversation

amannn
Copy link
Owner

@amannn amannn commented Apr 24, 2024

Resolves #960

Use cases

  1. Apps where the locale should be read from user settings instead of the [locale] segment
  2. Apps where only a single locale is supported

Changes

The gist is instead of reading the locale from the argument that's passed to getRequestConfig in i18n.ts you can now return a locale from the function:

// i18n.ts
import {getRequestConfig} from 'next-intl/server';

export default getRequestConfig(async () => {
  // This can either be defined statically if only a single locale
  // is supported, or alternatively read from user settings
  const locale = 'en';

  return {
    locale,
    messages: (await import(`../messages/${locale}.json`)).default
  };
});

This simplified setup comes with some benefits:

  1. No [locale] segment needs to be added
  2. No routing APIs need to be used (middleware, navigation APIs)
  3. Static rendering works without tradeoffs if you only support a single language

Example implementation

Docs changes

  • Provide a separate getting started guide for usage without i18n routing
  • Remove overview/index pages from table of contents (expandable areas now have a main click area and the expand icon button)
  • Adapt docs where necessary to cater to both use cases (with/without i18n routing)
  • Mark external links as such

Proposed docs

Copy link

vercel bot commented Apr 24, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
next-intl-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 15, 2024 9:58am
next-intl-example-app-router ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 15, 2024 9:58am

@belgattitude
Copy link

Would be awesome ! Thx for your work

@AhmedBaset
Copy link
Contributor

  1. Static rendering works without tradeoffs

How is Static rendering supposed to work for more than one locale (most cases)?

If the value is derived from a runtime variable, such as userSession, then Dynamic Rendering will be opted for. If the value comes from predefined routes, such as ["en", "de"], then only one locale will be generated, which contradicts the concept of internationalization.

@amannn
Copy link
Owner Author

amannn commented May 1, 2024

@AhmedBaset Yep, that's of course correct—thanks for the feedback! The statement only applies to using a single language, I've changed it accordingly above.

@amannn
Copy link
Owner Author

amannn commented May 7, 2024

My main concern with this currently is that the locale is returned asynchronously, in case routing APIs are used in combination with this feature.

Looking through all the options we support in i18n.ts, I think they can be grouped like this:

  1. Definitely not async: formats, defaultTranslationValues, onError
  2. Likely not async: now
  3. Might be async: locale (if read from user profile), timeZone
  4. Likely async: messages

So generally, it seems reasonable that we support an async config object.

However, this currently has the implication that the locale can't be read synchronously during an RSC render. This is mostly not an issue, but the navigation APIs are affected by this. For Link it's no problem since we can make the component async, but for redirect this makes a difference.

I need to investigate if there's a way where we can call redirect before we know which locale we're redirecting to …

Note that as long as we advertise this feature as something to be used without routing APIs from next-intl, this would be fine. I do however have the hope that this could pave the path to a very customizable solution to #653, in case the user could read parts of the pathname deeply in an RSC render (see vercel/next.js#58862). Let's see.

EDIT: Declaring this a non-issue for now as routing APIs don't need to be used when you're not using i18n routing. Note to self though, in case we move to a place in the future where the locale is generally returned from i18n (i.e. Next.js allows to read params deeply), we could make the locale a mandatory argument for redirect. Not super elegant, but given all other aspects that this enables (e.g. market prefixes), possibly something worth considering.

# Conflicts:
#	packages/next-intl/package.json
#	pnpm-lock.yaml
# Conflicts:
#	docs/pages/docs/getting-started/app-router.mdx
# Conflicts:
#	docs/pages/docs/environments.mdx
{children && <div className="mt-3">{children}</div>}
</NextLink>
);
}
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copied and forked from Nextra for customization (being able to add a description)

example](https://next-intl-example-app-router.vercel.app) to explore a working
app with error handling.
</Callout>
**Tip:** You can have a look at [the App Router example](https://next-intl-example-app-router.vercel.app) to explore a working app with error handling.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Downgraded" this to a tip to not have too many callouts in this area.

@@ -163,9 +164,9 @@ export default function Error({error, reset}) {
}
```

Note that `error.tsx` is loaded right after your app has initialized. If your app is performance-senstive and you want to avoid loading translation functionality from `next-intl` as part of the initial bundle, you can export a lazy reference from your `error` file:
Note that `error.tsx` is loaded right after your app has initialized. If your app is performance-senstive and you want to avoid loading translation functionality from `next-intl` as part of this bundle, you can export a lazy reference from your `error` file:
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's actually not the "initial bundle", but a lazy loaded one after init.

Useful if you'd like to provide a locale to `next-intl`, e.g. based on user
settings, or if your app only supports a single language
</Card>
</Cards>
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another level of navigation until the user reaches the getting started guide, but I believe if developers have a use case that qualifies for "without i18n routing", it's a good idea to get them into the right "lane" quickly.

This provides an easier getting started experience (no middleware) and also easier usage down the road (no need for navigation APIs, static/dynamic rendering is decided by user).

In order to use unique pathnames for every language that your app supports, `next-intl` can be used to handle the following routing setups:

1. Prefix-based routing (e.g. `/en/about`)
2. Domain-based routing (e.g. `en.example.com/about`)
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first draft used full sentences to describe these two cases, but I later converted them to bullets.

Bullets seem much more readable to me in comparison to full sentences for cases like this. From my experience developers scan docs much more than they read them and want to progress quickly. At least that's what I'm doing 😄

export default function Layout({children, params: {locale}}) {
const direction = useTextDirection(locale);
export default async function RootLayout(/* ... */) {
const locale = await getLocale();
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've adapted this example slightly to work with an async root layout. Users can still create a useTextDirection hook if desired, we just show the basic setup here.

@amannn amannn merged commit 5c968b2 into main May 15, 2024
5 checks passed
@amannn amannn deleted the feat/960-provide-locale branch May 15, 2024 11:35
thedannywahl added a commit to thedannywahl/inst-app-template that referenced this pull request May 15, 2024
amannn/next-intl#1017

no longer require locale routing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants