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

docs: Getting started with all messages on the client side #1046

Merged
merged 7 commits into from
May 8, 2024

Conversation

amannn
Copy link
Owner

@amannn amannn commented May 3, 2024

I know that users are frequently bitten by messages not being automatically available on the client side. We do this in the best interest of pushing performance as much as possible, but I think this has gone a bit too far. Especially when just starting out with an i18n setup, this can be frustrating.

Furthermore, I think for a number of apps this doesn't make a huge difference anyway. The number and length of messages in apps can vary significantly, so it's hard to make assumptions, but here are two examples from apps I'm maintaining:

  1. An emerging one: 88 messages (1,5K gzip)
  2. A more sophisticated one: 317 messages (7,6K gzip)

Apps can of course get much larger and it's important to be able to optimize, but it's questionable how early this is needed.

On top of this, app messages aren't the only place where translations are coming from, but typically they represent a part of the total translatable content (with other parts coming from a CMS, backend APIs, etc.).

We're planning on implementing tree-shaking of messages anyway in #1, I think for the time being we could be a bit more relaxed about the suggestions we give to users.

New getting started docs for the App Router

As part of this, I'd now also consider automatically inheriting formats in Client Components if this is defined in i18n.ts for similar reasoning. This is a breaking change that will be part of #779

Copy link

vercel bot commented May 3, 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 8, 2024 0:32am
next-intl-example-app-router ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 8, 2024 0:32am

@@ -23,7 +23,7 @@ Moving internationalization to the server side unlocks new levels of performance

**Benefits of server-side internationalization:**

1. Your messages never leave the server and don't need to be serialized for the client side
1. Your messages never leave the server and don't need to be passed to the client side
Copy link
Owner Author

Choose a reason for hiding this comment

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

Maybe less scary.

@@ -123,7 +123,9 @@ In regard to performance, async functions and hooks can be used very much interc

## Using internationalization in Client Components

Depending on your situation, you may need to handle internationalization in Client Components as well. There are several options for using translations or other functionality from `next-intl` in Client Components, listed here in order of recommendation.
Depending on your situation, you may need to handle internationalization in Client Components as well. While providing all messages to the client side is typically the easiest way to [get started](/docs/getting-started/app-router#layout) and a reasonable approach for emerging apps, you can be more selective about which messages are passed to the client side to reduce the bundle size of your app.
Copy link
Owner Author

Choose a reason for hiding this comment

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

"emerging" was an attempt at a better phrasing for "smaller apps" :). Might be more accurate anyway, also big apps start out as small ones.

Copy link

Choose a reason for hiding this comment

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

I know that the aim of this docs rewrite is to not scare or overwhelm people with performance implications, but is there a way to somewhere define what is "emerging" and what is considered a bigger app in terms of number of messages?

Perhaps in an expandable section here or in this expandable section in Getting Started.

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 tried to rephrase this again in 30ab630 and included some more details.

Coming back to this, I think even "emerging" is the wrong term here. For some apps, splitting messages will never be a necessity.

I think a particular number of messages doesn't really answer this question here, in the latest commit I tried to focus on pointing you towards metrics and tools that can help you to ensure your app meets your performance goals.

Do you find this helpful?

I've now also removed the expandable section from the getting started docs. I think the comment "Providing all messages to the client side is the easiest way to get started" should be sufficient in terms of setting up an app.

Copy link

Choose a reason for hiding this comment

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

Just had a look at the latest commit. I think that this is much better. In the previous version with "emerging", I might have read that and been worried that I would need to optimise things from the get-go if my app was on a grander scale.

I really like the newer explanation, particularly where you remind users to "always measure before you optimize".

// A Client Component, so that it can use `useFormState` to
// potentially display errors received after submission.
// A Client Component, so that `useFormState` can be used
// to potentially display errors received after submission.
Copy link
Owner Author

Choose a reason for hiding this comment

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

Reads slightly better IMO.

@@ -310,14 +312,12 @@ export default function Counter() {
}
```

In case you prefer to make all messages available to the client side, you can [configure `NextIntlClientProvider` in the root layout](#option-4-providing-all-messages) instead.
Copy link
Owner Author

Choose a reason for hiding this comment

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

This is covered in the very next headline.

<Callout type="warning">
Note that this is a tradeoff in regard to performance (see the bullet points
at the top of this page).
</Callout>
Copy link
Owner Author

Choose a reason for hiding this comment

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

Too dramatic IMO, we already told the user everything relevant at the top of the page :).

Copy link

Choose a reason for hiding this comment

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

Definitely agree, the wording scared me off a bit when I read this for the first time 😁

children,
params: {locale}
}: {
children: React.ReactNode;
params: {locale: string};
}) {
// Providing all messages to the client
// side is the easiest way to get started
const messages = await getMessages();
Copy link
Owner Author

@amannn amannn May 3, 2024

Choose a reason for hiding this comment

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

This component could also use useMessages, but I think we can reasonably suggest implementing LocaleLayout as an async Server Component. A minor benefit is that this ensures that NextIntlClientProvider is rendered from a Server Component now.

While this is a subtle difference from the users' perspective, it enables automatically inheriting configuration from i18n.ts. If the user makes the root layout a Client Component, this wouldn't be possible and more wiring is needed.

children,
params: {locale}
}: {
children: React.ReactNode;
params: {locale: string};
}) {
// Providing all messages to the client
// side is the easiest way to get started
Copy link
Owner Author

Choose a reason for hiding this comment

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

Inline comments are often copied along to user apps, so this could be a reminder when revisiting this code at a later stage of the project.


export default function LocaleLayout({children, params: {locale}}) {
const messages = useMessages();
export default async function LocaleLayout({children, params: {locale}}) {
Copy link
Owner Author

Choose a reason for hiding this comment

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

Similar reasoning as above.


// Async Server Components
import {getMessages} from 'next-intl/server';
const messages = await getMessages();
Copy link
Owner Author

Choose a reason for hiding this comment

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

getMessages might be more common than useMessages. This is in contrast to all other APIs from next-intl, like useTranslations/getTranslations etc.

Due to this, it might be a good idea to briefly mention this API on this page too. To be consistent, I've included them for the other config options below as well.


// Read messages configured via `i18n.ts`. Alternatively,
// messages can be fetched from any other source too.
Copy link
Owner Author

Choose a reason for hiding this comment

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

Reading from other sources shouldn't be relevant here (see also "How can I load messages from remote sources?" above).

@amannn amannn marked this pull request as ready for review May 3, 2024 16:52
@@ -56,8 +56,6 @@ Now, set up the plugin which creates an alias to provide your i18n configuration
<Tabs items={['next.config.mjs', 'next.config.js']}>
<Tab>

If you're using ECMAScript modules for your Next.js config, you can use the plugin as follows:
Copy link
Owner Author

Choose a reason for hiding this comment

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

Not really necessary IMO.

@@ -72,8 +70,6 @@ export default withNextIntl(nextConfig);
</Tab>
<Tab>

If you're using CommonJS for your Next.js config, you can use the plugin as follows:
Copy link
Owner Author

Choose a reason for hiding this comment

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

Same as above.

…ils in a new expandable section in the server/client components docs
@amannn
Copy link
Owner Author

amannn commented May 8, 2024

@lmac-1 I'll merge this in the current form, as I believe this is already quite an improvement and so that I can stack proposed docs from #1017 on top of this. Happy to follow-up though if you find some places that could use rephrasing or clarification!

@amannn amannn merged commit b39fa61 into main May 8, 2024
5 checks passed
@amannn amannn deleted the docs/client-components-first branch May 8, 2024 12:38
Copy link

@lmac-1 lmac-1 left a comment

Choose a reason for hiding this comment

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

I know this is already merged, but just commenting to say that I think that the changes are great. I really like that you've simplified the getting started steps. It should make it easier for users to get up and running in a basic way and can then dive into the docs if they're wanting to understand things more deeply.

I guess my only other comment would be perhaps the App Router example should be updated to match the new getting started steps? Or there could be two examples?

Not sure but just thinking I might be confused if I follow the getting started steps, then want to see a full working example, and can see that it hasn't followed the steps that I did during the initial steps. What do you think?

@@ -148,24 +148,45 @@ export const config = {

### `app/[locale]/layout.tsx` [#layout]

The `locale` that was matched by the middleware is available via the `locale` param and can be used to configure the document language.
The `locale` that was matched by the middleware is available via the `locale` param and can be used to configure the document language. Additionally, we can use this place to pass configuration from `i18n.ts` to Client Components via `NextIntlClientProvider`.
Copy link

Choose a reason for hiding this comment

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

I completely agree. I think that the getting started steps should be as simple as possible to allow users to get things working and then provide links later to allow for more exploration. Otherwise, as you said, users can get a bit distracted reading into things and perhaps confused.

@@ -123,7 +123,9 @@ In regard to performance, async functions and hooks can be used very much interc

## Using internationalization in Client Components

Depending on your situation, you may need to handle internationalization in Client Components as well. There are several options for using translations or other functionality from `next-intl` in Client Components, listed here in order of recommendation.
Depending on your situation, you may need to handle internationalization in Client Components as well. While providing all messages to the client side is typically the easiest way to [get started](/docs/getting-started/app-router#layout) and a reasonable approach for emerging apps, you can be more selective about which messages are passed to the client side to reduce the bundle size of your app.
Copy link

Choose a reason for hiding this comment

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

Just had a look at the latest commit. I think that this is much better. In the previous version with "emerging", I might have read that and been worried that I would need to optimise things from the get-go if my app was on a grander scale.

I really like the newer explanation, particularly where you remind users to "always measure before you optimize".

<Callout type="warning">
Note that this is a tradeoff in regard to performance (see the bullet points
at the top of this page).
</Callout>
Copy link

Choose a reason for hiding this comment

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

Definitely agree, the wording scared me off a bit when I read this for the first time 😁

@amannn
Copy link
Owner Author

amannn commented May 8, 2024

I guess my only other comment would be perhaps the App Router example should be updated to match the new getting started steps?

That's a great point, yes! I've added a follow-up PR to address this here: #1060

Thanks a ton for your feedback here! 🙏

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

Successfully merging this pull request may close these issues.

None yet

2 participants