@intlayer/docs 6.1.4 → 6.1.6-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/blog/ar/next-i18next_vs_next-intl_vs_intlayer.md +1366 -75
  2. package/blog/ar/nextjs-multilingual-seo-comparison.md +364 -0
  3. package/blog/de/next-i18next_vs_next-intl_vs_intlayer.md +1288 -72
  4. package/blog/de/nextjs-multilingual-seo-comparison.md +362 -0
  5. package/blog/en/intlayer_with_next-i18next.mdx +431 -0
  6. package/blog/en/intlayer_with_next-intl.mdx +335 -0
  7. package/blog/en/next-i18next_vs_next-intl_vs_intlayer.md +583 -336
  8. package/blog/en/nextjs-multilingual-seo-comparison.md +360 -0
  9. package/blog/en-GB/next-i18next_vs_next-intl_vs_intlayer.md +1144 -37
  10. package/blog/en-GB/nextjs-multilingual-seo-comparison.md +360 -0
  11. package/blog/es/next-i18next_vs_next-intl_vs_intlayer.md +1236 -64
  12. package/blog/es/nextjs-multilingual-seo-comparison.md +363 -0
  13. package/blog/fr/next-i18next_vs_next-intl_vs_intlayer.md +1142 -75
  14. package/blog/fr/nextjs-multilingual-seo-comparison.md +362 -0
  15. package/blog/hi/nextjs-multilingual-seo-comparison.md +363 -0
  16. package/blog/it/next-i18next_vs_next-intl_vs_intlayer.md +1130 -55
  17. package/blog/it/nextjs-multilingual-seo-comparison.md +363 -0
  18. package/blog/ja/next-i18next_vs_next-intl_vs_intlayer.md +1150 -76
  19. package/blog/ja/nextjs-multilingual-seo-comparison.md +362 -0
  20. package/blog/ko/next-i18next_vs_next-intl_vs_intlayer.md +1139 -73
  21. package/blog/ko/nextjs-multilingual-seo-comparison.md +362 -0
  22. package/blog/pt/next-i18next_vs_next-intl_vs_intlayer.md +1143 -76
  23. package/blog/pt/nextjs-multilingual-seo-comparison.md +362 -0
  24. package/blog/ru/next-i18next_vs_next-intl_vs_intlayer.md +1150 -74
  25. package/blog/ru/nextjs-multilingual-seo-comparison.md +370 -0
  26. package/blog/tr/next-i18next_vs_next-intl_vs_intlayer.md +2 -0
  27. package/blog/tr/nextjs-multilingual-seo-comparison.md +362 -0
  28. package/blog/zh/next-i18next_vs_next-intl_vs_intlayer.md +1152 -75
  29. package/blog/zh/nextjs-multilingual-seo-comparison.md +394 -0
  30. package/dist/cjs/generated/blog.entry.cjs +16 -0
  31. package/dist/cjs/generated/blog.entry.cjs.map +1 -1
  32. package/dist/cjs/generated/docs.entry.cjs +16 -0
  33. package/dist/cjs/generated/docs.entry.cjs.map +1 -1
  34. package/dist/esm/generated/blog.entry.mjs +16 -0
  35. package/dist/esm/generated/blog.entry.mjs.map +1 -1
  36. package/dist/esm/generated/docs.entry.mjs +16 -0
  37. package/dist/esm/generated/docs.entry.mjs.map +1 -1
  38. package/dist/types/generated/blog.entry.d.ts +1 -0
  39. package/dist/types/generated/blog.entry.d.ts.map +1 -1
  40. package/dist/types/generated/docs.entry.d.ts +1 -0
  41. package/dist/types/generated/docs.entry.d.ts.map +1 -1
  42. package/docs/ar/component_i18n.md +186 -0
  43. package/docs/ar/vs_code_extension.md +48 -109
  44. package/docs/de/component_i18n.md +186 -0
  45. package/docs/de/vs_code_extension.md +46 -107
  46. package/docs/en/component_i18n.md +186 -0
  47. package/docs/en/interest_of_intlayer.md +2 -2
  48. package/docs/en/intlayer_with_nextjs_14.md +18 -1
  49. package/docs/en/intlayer_with_nextjs_15.md +18 -1
  50. package/docs/en/vs_code_extension.md +24 -114
  51. package/docs/en-GB/component_i18n.md +186 -0
  52. package/docs/en-GB/vs_code_extension.md +42 -103
  53. package/docs/es/component_i18n.md +182 -0
  54. package/docs/es/vs_code_extension.md +53 -114
  55. package/docs/fr/component_i18n.md +186 -0
  56. package/docs/fr/vs_code_extension.md +50 -111
  57. package/docs/hi/component_i18n.md +186 -0
  58. package/docs/hi/vs_code_extension.md +49 -110
  59. package/docs/it/component_i18n.md +186 -0
  60. package/docs/it/vs_code_extension.md +50 -111
  61. package/docs/ja/component_i18n.md +186 -0
  62. package/docs/ja/vs_code_extension.md +50 -111
  63. package/docs/ko/component_i18n.md +186 -0
  64. package/docs/ko/vs_code_extension.md +48 -109
  65. package/docs/pt/component_i18n.md +186 -0
  66. package/docs/pt/vs_code_extension.md +46 -107
  67. package/docs/ru/component_i18n.md +186 -0
  68. package/docs/ru/vs_code_extension.md +48 -109
  69. package/docs/tr/component_i18n.md +186 -0
  70. package/docs/tr/vs_code_extension.md +54 -115
  71. package/docs/zh/component_i18n.md +186 -0
  72. package/docs/zh/vs_code_extension.md +51 -105
  73. package/package.json +11 -11
  74. package/src/generated/blog.entry.ts +16 -0
  75. package/src/generated/docs.entry.ts +16 -0
@@ -0,0 +1,335 @@
1
+ ```bash
2
+ .
3
+ ├── locales
4
+ │ ├── en
5
+ │ │ ├── common.json
6
+ │ │ └── about.json
7
+ │ ├── fr
8
+ │ │ ├── common.json
9
+ │ │ └── about.json
10
+ │ └── es
11
+ │ ├── common.json
12
+ │ └── about.json
13
+ └── src
14
+ ├── i18n.ts
15
+ ├── middleware.ts
16
+ ├── app
17
+ │ └── [locale]
18
+ │ ├── layout.tsx
19
+ │ └── about
20
+ │ └── page.tsx
21
+ └── components
22
+ ├── ClientComponentExample.tsx
23
+ └── ServerComponent.tsx
24
+ ```
25
+
26
+ #### Setup and Loading Content
27
+
28
+ As mentioned previously, you must optimize how each JSON file is imported into your code.
29
+ How the library handles content loading is important.
30
+
31
+ ```tsx fileName="src/i18n.ts"
32
+ import { getRequestConfig } from 'next-intl/server';
33
+ import { notFound } from 'next/navigation';
34
+
35
+ export const locales = ['en', 'fr', 'es'] as const;
36
+ export const defaultLocale = 'en' as const;
37
+
38
+ async function loadMessages(locale: string) {
39
+ // Load only the namespaces your layout/pages need
40
+ const [common, about] = await Promise.all([
41
+ import(`../locales/${locale}/common.json`).then((m) => m.default),
42
+ import(`../locales/${locale}/about.json`).then((m) => m.default),
43
+ ]);
44
+
45
+ return { common, about } as const;
46
+ }
47
+
48
+ export default getRequestConfig(async ({ locale }) => {
49
+ if (!locales.includes(locale as any)) notFound();
50
+
51
+ return {
52
+ messages: await loadMessages(locale),
53
+ };
54
+ });
55
+ ```
56
+
57
+ ```tsx fileName="src/app/[locale]/layout.tsx"
58
+ import type { ReactNode } from 'react';
59
+ import { locales } from '@/i18n';
60
+ import {
61
+ getLocaleDirection,
62
+ unstable_setRequestLocale,
63
+ } from 'next-intl/server';
64
+
65
+ export const dynamic = 'force-static';
66
+
67
+ export function generateStaticParams() {
68
+ return locales.map((locale) => ({ locale }));
69
+ }
70
+
71
+ export default function LocaleLayout({
72
+ children,
73
+ params,
74
+ }: {
75
+ children: ReactNode;
76
+ params: Promise<{ locale: string }>;
77
+ }) {
78
+ const { locale } = await params;
79
+
80
+ // Set the active request locale for this server render (RSC)
81
+ unstable_setRequestLocale(locale);
82
+
83
+ const dir = getLocaleDirection(locale);
84
+
85
+ return (
86
+ <html lang={locale} dir={dir}>
87
+ <body>{children}</body>
88
+ </html>
89
+ );
90
+ }
91
+ ```
92
+
93
+ ```tsx fileName="src/app/[locale]/about/page.tsx"
94
+ import { getTranslations, getMessages, getFormatter } from 'next-intl/server';
95
+ import { NextIntlClientProvider } from 'next-intl';
96
+ import pick from 'lodash/pick';
97
+ import ServerComponent from '@/components/ServerComponent';
98
+ import ClientComponentExample from '@/components/ClientComponentExample';
99
+
100
+ export const dynamic = 'force-static';
101
+
102
+ export default async function AboutPage({
103
+ params,
104
+ }: {
105
+ params: Promise<{ locale: string }>;
106
+ }) {
107
+ const { locale } = await params;
108
+
109
+ // Messages are loaded server-side. Push only what's needed to the client.
110
+ const messages = await getMessages();
111
+ const clientMessages = pick(messages, ['common', 'about']);
112
+
113
+ // Strictly server-side translations/formatting
114
+ const tAbout = await getTranslations('about');
115
+ const tCounter = await getTranslations('about.counter');
116
+ const format = await getFormatter();
117
+
118
+ const initialFormattedCount = format.number(0);
119
+
120
+ return (
121
+ <NextIntlClientProvider locale={locale} messages={clientMessages}>
122
+ <main>
123
+ <h1>{tAbout('title')}</h1>
124
+ <ClientComponentExample />
125
+ <ServerComponent
126
+ formattedCount={initialFormattedCount}
127
+ label={tCounter('label')}
128
+ increment={tCounter('increment')}
129
+ />
130
+ </main>
131
+ </NextIntlClientProvider>
132
+ );
133
+ }
134
+ ```
135
+
136
+ ### Usage in a client component
137
+
138
+ Let's take an example of a client component rendering a counter.
139
+
140
+ **Translations (shape reused; load them into next-intl messages as you prefer)**
141
+
142
+ ```json fileName="locales/en/about.json"
143
+ {
144
+ "counter": {
145
+ "label": "Counter",
146
+ "increment": "Increment"
147
+ }
148
+ }
149
+ ```
150
+
151
+ ```json fileName="locales/fr/about.json"
152
+ {
153
+ "counter": {
154
+ "label": "Compteur",
155
+ "increment": "Incrémenter"
156
+ }
157
+ }
158
+ ```
159
+
160
+ **Client component**
161
+
162
+ ```tsx fileName="src/components/ClientComponentExample.tsx"
163
+ 'use client';
164
+
165
+ import React, { useState } from 'react';
166
+ import { useTranslations, useFormatter } from 'next-intl';
167
+
168
+ const ClientComponentExample = () => {
169
+ // Scope directly to the nested object
170
+ const t = useTranslations('about.counter');
171
+ const format = useFormatter();
172
+ const [count, setCount] = useState(0);
173
+
174
+ return (
175
+ <div>
176
+ <p>{format.number(count)}</p>
177
+ <button
178
+ aria-label={t('label')}
179
+ onClick={() => setCount((count) => count + 1)}
180
+ >
181
+ {t('increment')}
182
+ </button>
183
+ </div>
184
+ );
185
+ };
186
+ ```
187
+
188
+ > Don't forget to add "about" message on the page client message
189
+ > (only include the namespaces your client actually needs).
190
+
191
+ ### Usage in a server component
192
+
193
+ We will take the case of a UI component. This component is a server component, and should be able to be inserted as a child of a client component. (page (server component) -> client component -> server component). As this component can be inserted as a child of a client component, it cannot be async.
194
+
195
+ ```tsx fileName="src/components/ServerComponent.tsx"
196
+ type ServerComponentProps = {
197
+ count: number;
198
+ t: (key: string) => string;
199
+ formatter: Intl.NumberFormat;
200
+ };
201
+
202
+ const ServerComponent = ({ t, count, formatter }: ServerComponentProps) => {
203
+ const formatted = formatter.format(count);
204
+
205
+ return (
206
+ <div>
207
+ <p>{formatted}</p>
208
+ <button aria-label={t('label')}>{t('increment')}</button>
209
+ </div>
210
+ );
211
+ };
212
+ ```
213
+
214
+ > As the server component cannot be async, you need to pass the translations and formatter function as props.
215
+ >
216
+ > In your page / layout:
217
+ >
218
+ > - `import { getTranslations, getFormatter } from "next-intl/server";`
219
+ > - `const t = await getTranslations("about.counter");`
220
+ > - `const formatter = await getFormatter().then((formatter) => formatter.number());`
221
+
222
+ ```tsx fileName="src/app/[locale]/about/layout.tsx"
223
+ import type { Metadata } from 'next';
224
+ import { locales, defaultLocale } from '@/i18n';
225
+ import { getTranslations } from 'next-intl/server';
226
+
227
+ function localizedPath(locale: string, path: string) {
228
+ return locale === defaultLocale ? path : '/' + locale + path;
229
+ }
230
+
231
+ export async function generateMetadata({
232
+ params,
233
+ }: {
234
+ params: { locale: string };
235
+ }): Promise<Metadata> {
236
+ const { locale } = params;
237
+ const t = await getTranslations({ locale, namespace: 'about' });
238
+
239
+ const url = '/about';
240
+ const languages = Object.fromEntries(
241
+ locales.map((locale) => [locale, localizedPath(locale, url)])
242
+ );
243
+
244
+ return {
245
+ title: t('title'),
246
+ description: t('description'),
247
+ alternates: {
248
+ canonical: localizedPath(locale, url),
249
+ languages: { ...languages, 'x-default': url },
250
+ },
251
+ };
252
+ }
253
+
254
+ // ... Rest of the page code
255
+ ```
256
+
257
+ ```tsx fileName="src/app/sitemap.ts"
258
+ import type { MetadataRoute } from 'next';
259
+ import { locales, defaultLocale } from '@/i18n';
260
+
261
+ const origin = 'https://example.com';
262
+
263
+ const formatterLocalizedPath = (locale: string, path: string) =>
264
+ locale === defaultLocale ? origin + path : origin + '/' + locale + path;
265
+
266
+ export default function sitemap(): MetadataRoute.Sitemap {
267
+ const aboutLanguages = Object.fromEntries(
268
+ locales.map((l) => [l, formatterLocalizedPath(l, '/about')])
269
+ );
270
+
271
+ return [
272
+ {
273
+ url: formatterLocalizedPath(defaultLocale, '/about'),
274
+ lastModified: new Date(),
275
+ changeFrequency: 'monthly',
276
+ priority: 0.7,
277
+ alternates: { languages: aboutLanguages },
278
+ },
279
+ ];
280
+ }
281
+ ```
282
+
283
+ ```tsx fileName="src/app/robots.ts"
284
+ import type { MetadataRoute } from 'next';
285
+ import { locales, defaultLocale } from '@/i18n';
286
+
287
+ const origin = 'https://example.com';
288
+ const withAllLocales = (path: string) => [
289
+ path,
290
+ ...locales
291
+ .filter((locale) => locale !== defaultLocale)
292
+ .map((locale) => '/' + locale + path),
293
+ ];
294
+
295
+ export default function robots(): MetadataRoute.Robots {
296
+ const disallow = [
297
+ ...withAllLocales('/dashboard'),
298
+ ...withAllLocales('/admin'),
299
+ ];
300
+
301
+ return {
302
+ rules: { userAgent: '*', allow: ['/'], disallow },
303
+ host: origin,
304
+ sitemap: origin + '/sitemap.xml',
305
+ };
306
+ }
307
+ ```
308
+
309
+ ### Middleware for locale routing
310
+
311
+ Add a middleware to handle locale detection and routing:
312
+
313
+ ```ts fileName="src/middleware.ts"
314
+ import createMiddleware from 'next-intl/middleware';
315
+ import { locales, defaultLocale } from '@/i18n';
316
+
317
+ export default createMiddleware({
318
+ locales: [...locales],
319
+ defaultLocale,
320
+ localeDetection: true,
321
+ });
322
+
323
+ export const config = {
324
+ // Skip API, Next internals and static assets
325
+ matcher: ['/((?!api|_next|.*\\..*).*)'],
326
+ };
327
+ ```
328
+
329
+ ### Best practices
330
+
331
+ - **Set html `lang` and `dir`**: In `src/app/[locale]/layout.tsx`, compute `dir` via `getLocaleDirection(locale)` and set `<html lang={locale} dir={dir}>`.
332
+ - **Split messages by namespace**: Organize JSON per locale and namespace (e.g., `common.json`, `about.json`).
333
+ - **Minimize client payload**: On pages, send only required namespaces to `NextIntlClientProvider` (e.g., `pick(messages, ['common', 'about'])`).
334
+ - **Prefer static pages**: Export `export const dynamic = 'force-static'` and generate static params for all `locales`.
335
+ - **Synchronous server components**: Keep server components sync by passing precomputed strings (translated labels, formatted numbers) rather than async calls or non-serializable functions.