@intlayer/docs 8.6.10 → 8.7.0-canary.1

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 (129) hide show
  1. package/blog/ar/i18n_using_next-i18next.md +1 -1
  2. package/blog/ar/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
  3. package/blog/de/i18n_using_next-i18next.md +1 -1
  4. package/blog/de/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
  5. package/blog/en/i18n_using_next-i18next.md +1 -1
  6. package/blog/en/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
  7. package/blog/en-GB/i18n_using_next-i18next.md +1 -1
  8. package/blog/en-GB/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
  9. package/blog/es/i18n_using_next-i18next.md +1 -1
  10. package/blog/es/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
  11. package/blog/fr/i18n_using_next-i18next.md +1 -1
  12. package/blog/fr/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
  13. package/blog/hi/i18n_using_next-i18next.md +1 -1
  14. package/blog/id/i18n_using_next-i18next.md +1 -1
  15. package/blog/id/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
  16. package/blog/it/i18n_using_next-i18next.md +1 -1
  17. package/blog/it/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
  18. package/blog/ja/i18n_using_next-i18next.md +1 -1
  19. package/blog/ja/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
  20. package/blog/ko/i18n_using_next-i18next.md +1 -1
  21. package/blog/ko/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
  22. package/blog/pl/i18n_using_next-i18next.md +1 -1
  23. package/blog/pl/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
  24. package/blog/pt/i18n_using_next-i18next.md +1 -1
  25. package/blog/pt/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
  26. package/blog/ru/i18n_using_next-i18next.md +1 -1
  27. package/blog/ru/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
  28. package/blog/tr/i18n_using_next-i18next.md +1 -1
  29. package/blog/uk/i18n_using_next-i18next.md +1 -1
  30. package/blog/uk/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
  31. package/blog/vi/i18n_using_next-i18next.md +1 -1
  32. package/blog/vi/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
  33. package/blog/zh/i18n_using_next-i18next.md +1 -1
  34. package/blog/zh/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
  35. package/docs/ar/bundle_optimization.md +454 -0
  36. package/docs/ar/intlayer_with_next-i18next.md +1 -1
  37. package/docs/ar/intlayer_with_next-intl.md +1 -1
  38. package/docs/ar/intlayer_with_tanstack+solid.md +24 -5
  39. package/docs/ar/intlayer_with_tanstack.md +45 -68
  40. package/docs/bn/bundle_optimization.md +454 -0
  41. package/docs/cs/bundle_optimization.md +454 -0
  42. package/docs/de/bundle_optimization.md +454 -0
  43. package/docs/de/intlayer_with_next-i18next.md +1 -1
  44. package/docs/de/intlayer_with_next-intl.md +1 -1
  45. package/docs/de/intlayer_with_tanstack+solid.md +24 -5
  46. package/docs/de/intlayer_with_tanstack.md +45 -68
  47. package/docs/en/bundle_optimization.md +36 -8
  48. package/docs/en/intlayer_with_next-i18next.md +1 -1
  49. package/docs/en/intlayer_with_next-intl.md +1 -1
  50. package/docs/en/intlayer_with_tanstack+solid.md +24 -5
  51. package/docs/en/intlayer_with_tanstack.md +45 -68
  52. package/docs/en-GB/bundle_optimization.md +454 -0
  53. package/docs/en-GB/intlayer_with_next-i18next.md +1 -1
  54. package/docs/en-GB/intlayer_with_next-intl.md +1 -1
  55. package/docs/en-GB/intlayer_with_tanstack+solid.md +24 -5
  56. package/docs/en-GB/intlayer_with_tanstack.md +47 -70
  57. package/docs/es/bundle_optimization.md +454 -0
  58. package/docs/es/intlayer_with_next-i18next.md +1 -1
  59. package/docs/es/intlayer_with_next-intl.md +1 -1
  60. package/docs/es/intlayer_with_tanstack+solid.md +24 -5
  61. package/docs/es/intlayer_with_tanstack.md +45 -68
  62. package/docs/fr/bundle_optimization.md +454 -0
  63. package/docs/fr/intlayer_with_next-i18next.md +1 -1
  64. package/docs/fr/intlayer_with_next-intl.md +1 -1
  65. package/docs/fr/intlayer_with_tanstack+solid.md +24 -5
  66. package/docs/fr/intlayer_with_tanstack.md +45 -68
  67. package/docs/hi/bundle_optimization.md +454 -0
  68. package/docs/hi/intlayer_with_next-i18next.md +1 -1
  69. package/docs/hi/intlayer_with_next-intl.md +1 -1
  70. package/docs/hi/intlayer_with_tanstack+solid.md +24 -5
  71. package/docs/hi/intlayer_with_tanstack.md +45 -68
  72. package/docs/id/bundle_optimization.md +454 -0
  73. package/docs/id/intlayer_with_next-i18next.md +1 -1
  74. package/docs/id/intlayer_with_next-intl.md +1 -1
  75. package/docs/id/intlayer_with_tanstack+solid.md +24 -5
  76. package/docs/id/intlayer_with_tanstack.md +45 -68
  77. package/docs/it/bundle_optimization.md +454 -0
  78. package/docs/it/intlayer_with_next-i18next.md +1 -1
  79. package/docs/it/intlayer_with_next-intl.md +1 -1
  80. package/docs/it/intlayer_with_tanstack+solid.md +24 -5
  81. package/docs/it/intlayer_with_tanstack.md +45 -68
  82. package/docs/ja/bundle_optimization.md +454 -0
  83. package/docs/ja/intlayer_with_next-i18next.md +1 -1
  84. package/docs/ja/intlayer_with_next-intl.md +1 -1
  85. package/docs/ja/intlayer_with_tanstack+solid.md +24 -5
  86. package/docs/ja/intlayer_with_tanstack.md +45 -36
  87. package/docs/ko/bundle_optimization.md +454 -0
  88. package/docs/ko/intlayer_with_next-i18next.md +1 -1
  89. package/docs/ko/intlayer_with_next-intl.md +1 -1
  90. package/docs/ko/intlayer_with_tanstack+solid.md +24 -5
  91. package/docs/ko/intlayer_with_tanstack.md +45 -68
  92. package/docs/nl/bundle_optimization.md +454 -0
  93. package/docs/pl/bundle_optimization.md +454 -0
  94. package/docs/pl/intlayer_with_next-i18next.md +1 -1
  95. package/docs/pl/intlayer_with_next-intl.md +1 -1
  96. package/docs/pl/intlayer_with_tanstack+solid.md +24 -5
  97. package/docs/pl/intlayer_with_tanstack.md +45 -68
  98. package/docs/pt/bundle_optimization.md +454 -0
  99. package/docs/pt/intlayer_with_next-i18next.md +1 -1
  100. package/docs/pt/intlayer_with_next-intl.md +1 -1
  101. package/docs/pt/intlayer_with_tanstack+solid.md +24 -5
  102. package/docs/pt/intlayer_with_tanstack.md +45 -68
  103. package/docs/ru/bundle_optimization.md +454 -0
  104. package/docs/ru/intlayer_with_next-i18next.md +1 -1
  105. package/docs/ru/intlayer_with_next-intl.md +1 -1
  106. package/docs/ru/intlayer_with_tanstack+solid.md +24 -5
  107. package/docs/ru/intlayer_with_tanstack.md +45 -68
  108. package/docs/tr/bundle_optimization.md +454 -0
  109. package/docs/tr/intlayer_with_next-i18next.md +1 -1
  110. package/docs/tr/intlayer_with_next-intl.md +1 -1
  111. package/docs/tr/intlayer_with_tanstack+solid.md +24 -5
  112. package/docs/tr/intlayer_with_tanstack.md +45 -68
  113. package/docs/uk/bundle_optimization.md +454 -0
  114. package/docs/uk/intlayer_with_next-i18next.md +1 -1
  115. package/docs/uk/intlayer_with_next-intl.md +1 -1
  116. package/docs/uk/intlayer_with_tanstack+solid.md +24 -5
  117. package/docs/uk/intlayer_with_tanstack.md +45 -68
  118. package/docs/ur/bundle_optimization.md +454 -0
  119. package/docs/vi/bundle_optimization.md +454 -0
  120. package/docs/vi/intlayer_with_next-i18next.md +1 -1
  121. package/docs/vi/intlayer_with_next-intl.md +1 -1
  122. package/docs/vi/intlayer_with_tanstack+solid.md +24 -5
  123. package/docs/vi/intlayer_with_tanstack.md +45 -68
  124. package/docs/zh/bundle_optimization.md +454 -0
  125. package/docs/zh/intlayer_with_next-i18next.md +1 -1
  126. package/docs/zh/intlayer_with_next-intl.md +1 -1
  127. package/docs/zh/intlayer_with_tanstack+solid.md +24 -5
  128. package/docs/zh/intlayer_with_tanstack.md +45 -68
  129. package/package.json +7 -7
@@ -224,9 +224,7 @@ function RootDocument({ children }: { children: ReactNode }) {
224
224
  <HeadContent />
225
225
  </head>
226
226
  <body>
227
- <IntlayerProvider locale={locale}>
228
- {children}
229
- </IntlayerProvider>
227
+ <IntlayerProvider locale={locale}>{children}</IntlayerProvider>
230
228
  <Scripts />
231
229
  </body>
232
230
  </html>
@@ -325,30 +323,20 @@ import { getPrefix } from "intlayer";
325
323
 
326
324
  export const LOCALE_ROUTE = "{-$locale}" as const;
327
325
 
328
- // मुख्य उपयोगिता
329
- export type RemoveLocaleParam<T> = T extends string
330
- ? RemoveLocaleFromString<T>
331
- : T;
326
+ export type To = StripLocalePrefix<LinkComponentProps["to"]>;
332
327
 
333
- export type To = RemoveLocaleParam<LinkComponentProps["to"]>;
334
-
335
- type CollapseDoubleSlashes<S extends string> =
336
- S extends `${infer H}//${infer T}` ? CollapseDoubleSlashes<`${H}/${T}`> : S;
328
+ export type StripLocalePrefix<T extends string | undefined> = T extends
329
+ | `/${typeof LOCALE_ROUTE}/`
330
+ | `/${typeof LOCALE_ROUTE}`
331
+ ? "/"
332
+ : T extends `/${typeof LOCALE_ROUTE}/${infer Rest}`
333
+ ? `/${Rest}`
334
+ : T;
337
335
 
338
336
  type LocalizedLinkProps = {
339
337
  to?: To;
340
338
  } & Omit<LinkComponentProps, "to">;
341
339
 
342
- // सहायक
343
- type RemoveAll<
344
- S extends string,
345
- Sub extends string,
346
- > = S extends `${infer H}${Sub}${infer T}` ? RemoveAll<`${H}${T}`, Sub> : S;
347
-
348
- type RemoveLocaleFromString<S extends string> = CollapseDoubleSlashes<
349
- RemoveAll<S, typeof LOCALE_ROUTE>
350
- >;
351
-
352
340
  export const LocalizedLink: FC<LocalizedLinkProps> = (props) => {
353
341
  const { locale } = useLocale();
354
342
  const { localePrefix } = getPrefix(locale);
@@ -377,26 +365,26 @@ export const LocalizedLink: FC<LocalizedLinkProps> = (props) => {
377
365
  import { useNavigate } from "@tanstack/react-router";
378
366
  import { getPrefix } from "intlayer";
379
367
  import { useLocale } from "react-intlayer";
380
- import { LOCALE_ROUTE } from "@/components/localized-link";
368
+ import type { StripLocalePrefix } from "@/components/localized-link";
381
369
  import type { FileRouteTypes } from "@/routeTree.gen";
382
370
 
383
- type StripLocalePrefix<T extends string> = T extends
384
- | `/${typeof LOCALE_ROUTE}`
385
- | `/${typeof LOCALE_ROUTE}/`
386
- ? "/"
387
- : T extends `/${typeof LOCALE_ROUTE}/${infer Rest}`
388
- ? `/${Rest}`
389
- : never;
371
+ type NavigateFn = ReturnType<typeof useNavigate>;
372
+ type BaseNavigateOptions = Parameters<NavigateFn>[0];
390
373
 
391
374
  type LocalizedTo = StripLocalePrefix<FileRouteTypes["to"]>;
392
375
 
393
- type LocalizedNavigate = {
394
- (to: LocalizedTo): ReturnType<ReturnType<typeof useNavigate>>;
395
- (
396
- opts: { to: LocalizedTo } & Record<string, unknown>
397
- ): ReturnType<ReturnType<typeof useNavigate>>;
376
+ export type LocalizedNavigateOptions = Omit<
377
+ BaseNavigateOptions,
378
+ "to" | "params"
379
+ > & {
380
+ to: LocalizedTo;
381
+ params?: Omit<NonNullable<BaseNavigateOptions["params"]>, "locale">;
398
382
  };
399
383
 
384
+ type LocalizedNavigate = (
385
+ options: LocalizedNavigateOptions
386
+ ) => ReturnType<NavigateFn>;
387
+
400
388
  export const useLocalizedNavigate = () => {
401
389
  const navigate = useNavigate();
402
390
 
@@ -443,38 +431,6 @@ import { useLocalizedNavigate } from "@/hooks/useLocalizedNavigate";
443
431
 
444
432
  export const Route = createFileRoute("/{-$locale}/")({
445
433
  component: RouteComponent,
446
- head: ({ params }) => {
447
- const { locale } = params;
448
- const path = "/"; // The path for this route
449
-
450
- const metaContent = getIntlayer("app", locale);
451
-
452
- return {
453
- links: [
454
- // Canonical link: Points to the current localized page
455
- { rel: "canonical", href: getLocalizedUrl(path, locale) },
456
-
457
- // Hreflang: Tell Google about all localized versions
458
- ...localeMap(({ locale: mapLocale }) => ({
459
- rel: "alternate",
460
- hrefLang: mapLocale,
461
- href: getLocalizedUrl(path, mapLocale),
462
- })),
463
-
464
- // x-default: For users in unmatched languages
465
- // Define the default fallback locale (usually your primary language)
466
- {
467
- rel: "alternate",
468
- hrefLang: "x-default",
469
- href: getLocalizedUrl(path, defaultLocale),
470
- },
471
- ],
472
- meta: [
473
- { title: metaContent.title },
474
- { name: "description", content: metaContent.meta.description },
475
- ],
476
- };
477
- },
478
434
  });
479
435
 
480
436
  function RouteComponent() {
@@ -629,12 +585,33 @@ export const Route = createFileRoute("/{-$locale}/")({
629
585
  component: RouteComponent,
630
586
  head: ({ params }) => {
631
587
  const { locale } = params;
632
- const metaContent = getIntlayer("page-metadata", locale);
588
+ const path = "/"; // The path for this route
589
+
590
+ const metaContent = getIntlayer("app", locale);
633
591
 
634
592
  return {
593
+ links: [
594
+ // Canonical link: Points to the current localized page
595
+ { rel: "canonical", href: getLocalizedUrl(path, locale) },
596
+
597
+ // Hreflang: Tell Google about all localized versions
598
+ ...localeMap(({ locale: mapLocale }) => ({
599
+ rel: "alternate",
600
+ hrefLang: mapLocale,
601
+ href: getLocalizedUrl(path, mapLocale),
602
+ })),
603
+
604
+ // x-default: For users in unmatched languages
605
+ // Define the default fallback locale (usually your primary language)
606
+ {
607
+ rel: "alternate",
608
+ hrefLang: "x-default",
609
+ href: getLocalizedUrl(path, defaultLocale),
610
+ },
611
+ ],
635
612
  meta: [
636
613
  { title: metaContent.title },
637
- { content: metaContent.description, name: "description" },
614
+ { name: "description", content: metaContent.meta.description },
638
615
  ],
639
616
  };
640
617
  },
@@ -0,0 +1,454 @@
1
+ ---
2
+ createdAt: 2025-11-25
3
+ updatedAt: 2026-04-08
4
+ title: Mengoptimalkan Ukuran Bundle i18n & Performa
5
+ description: Kurangi ukuran bundle aplikasi dengan mengoptimalkan konten internasionalisasi (i18n). Pelajari cara memanfaatkan tree shaking dan lazy loading untuk kamus dengan Intlayer.
6
+ keywords:
7
+ - Optimasi Bundle
8
+ - Otomasi Konten
9
+ - Konten Dinamis
10
+ - Intlayer
11
+ - Next.js
12
+ - JavaScript
13
+ - React
14
+ slugs:
15
+ - doc
16
+ - concept
17
+ - bundle-optimization
18
+ history:
19
+ - version: 8.7.0
20
+ date: 2026-04-08
21
+ changes: "Tambahkan opsi `minify` dan `purge` ke konfigurasi build"
22
+ ---
23
+
24
+ # Mengoptimalkan Ukuran Bundle i18n & Performa
25
+
26
+ Salah satu tantangan paling umum dengan solusi i18n tradisional yang mengandalkan file JSON adalah mengelola ukuran konten. Jika pengembang tidak memisahkan konten ke dalam namespace secara manual, pengguna sering kali akhirnya mengunduh terjemahan untuk setiap halaman dan berpotensi untuk setiap bahasa hanya untuk melihat satu halaman.
27
+
28
+ Misalnya, aplikasi dengan 10 halaman yang diterjemahkan ke dalam 10 bahasa dapat mengakibatkan pengguna mengunduh konten dari 100 halaman, padahal mereka hanya membutuhkan **satu** (halaman saat ini dalam bahasa saat ini). Hal ini menyebabkan pemborosan bandwidth dan waktu pemuatan yang lebih lambat.
29
+
30
+ **Intlayer menyelesaikan masalah ini melalui optimasi waktu build.** Ini menganalisis kode Anda untuk mendeteksi kamus mana yang sebenarnya digunakan per komponen dan menyuntikkan kembali hanya konten yang diperlukan ke dalam bundle Anda.
31
+
32
+ ## Daftar Isi
33
+
34
+ <TOC />
35
+
36
+ ## Pindai bundle Anda
37
+
38
+ Menganalisis bundle Anda adalah langkah pertama dalam mengidentifikasi file JSON yang "berat" dan peluang pemisahan kode (code-splitting). Alat-alat ini menghasilkan peta pohon (treemap) visual dari kode aplikasi Anda yang telah dikompilasi, memungkinkan Anda untuk melihat dengan tepat pustaka mana yang menghabiskan ruang paling banyak.
39
+
40
+ <Tabs>
41
+ <Tab value="vite">
42
+
43
+ ### Vite / Rollup
44
+
45
+ Vite menggunakan Rollup di balik layar. Plugin `rollup-plugin-visualizer` menghasilkan file HTML interaktif yang menunjukkan ukuran setiap modul dalam grafik Anda.
46
+
47
+ ```bash
48
+ npm install -D rollup-plugin-visualizer
49
+ ```
50
+
51
+ ```typescript fileName="vite.config.ts"
52
+ import { defineConfig } from "vite";
53
+ import { visualizer } from "rollup-plugin-visualizer";
54
+
55
+ export default defineConfig({
56
+ plugins: [
57
+ visualizer({
58
+ open: true, // Buka laporan secara otomatis di browser Anda
59
+ filename: "stats.html",
60
+ gzipSize: true,
61
+ brotliSize: true,
62
+ }),
63
+ ],
64
+ });
65
+ ```
66
+
67
+ </Tab>
68
+ <Tab value="nextjs (turbopack)">
69
+
70
+ ### Next.js (Turbopack)
71
+
72
+ Untuk proyek yang menggunakan App Router dan Turbopack, Next.js menyediakan penganalisis eksperimental bawaan yang tidak memerlukan dependensi tambahan.
73
+
74
+ ```bash packageManager='npm'
75
+ npx next experimental-analyze
76
+ ```
77
+
78
+ ```bash packageManager='yarn'
79
+ yarn next experimental-analyze
80
+ ```
81
+
82
+ ```bash packageManager='pnpm'
83
+ pnpm next experimental-analyze
84
+ ```
85
+
86
+ ```bash packageManager='bun'
87
+ bun next experimental-analyze
88
+ ```
89
+
90
+ </Tab>
91
+ <Tab value="nextjs (Webpack)">
92
+
93
+ ### Next.js (Webpack)
94
+
95
+ Jika Anda menggunakan bundler Webpack default di Next.js, gunakan penganalisis bundle resmi. Picu dengan menetapkan variabel lingkungan selama build Anda.
96
+
97
+ ```bash packageManager='npm'
98
+ npm install -D @next/bundle-analyzer
99
+ ```
100
+
101
+ ```bash packageManager='yarn'
102
+ yarn add -D @next/bundle-analyzer
103
+ ```
104
+
105
+ ```bash packageManager='pnpm'
106
+ pnpm add -D @next/bundle-analyzer
107
+ ```
108
+
109
+ ```bash packageManager='bun'
110
+ bun add -d @next/bundle-analyzer
111
+ ```
112
+
113
+ ```javascript fileName="next.config.js"
114
+ const withBundleAnalyzer = require("@next/bundle-analyzer")({
115
+ enabled: process.env.ANALYZE === "true",
116
+ });
117
+
118
+ module.exports = withBundleAnalyzer({
119
+ // Konfigurasi Next.js Anda
120
+ });
121
+ ```
122
+
123
+ **Penggunaan:**
124
+
125
+ ```bash
126
+ ANALYZE=true npm run build
127
+ ```
128
+
129
+ </Tab>
130
+ <Tab value="Webpack (CRA / Angular / etc)">
131
+
132
+ ### Webpack Standar
133
+
134
+ Untuk Create React App (ejected), Angular, atau setup Webpack kustom, gunakan standar industri `webpack-bundle-analyzer`.
135
+
136
+ ```bash packageManager='npm'
137
+ npm install -D webpack-bundle-analyzer
138
+ ```
139
+
140
+ ```bash packageManager='yarn'
141
+ yarn add -D webpack-bundle-analyzer
142
+ ```
143
+
144
+ ```bash packageManager='pnpm'
145
+ pnpm add -D webpack-bundle-analyzer
146
+ ```
147
+
148
+ ```bash packageManager='bun'
149
+ bun add -d webpack-bundle-analyzer
150
+ ```
151
+
152
+ ```typescript fileName="webpack.config.ts
153
+ import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
154
+
155
+ export default {
156
+ plugins: [
157
+ new BundleAnalyzerPlugin({
158
+ analyzerMode: "static",
159
+ reportFilename: "bundle-analyzer.html",
160
+ openAnalyzer: false,
161
+ }),
162
+ ],
163
+ };
164
+ ```
165
+
166
+ </Tab>
167
+ </Tabs>
168
+
169
+ ## Cara Kerjanya
170
+
171
+ Intlayer menggunakan **pendekatan per-komponen**. Tidak seperti file JSON global, konten Anda didefinisikan bersama atau di dalam komponen Anda. Selama proses build, Intlayer akan:
172
+
173
+ 1. **Menganalisis** kode Anda untuk menemukan panggilan `useIntlayer`.
174
+ 2. **Membangun** konten kamus yang sesuai.
175
+ 3. **Mengganti** panggilan `useIntlayer` dengan kode yang dioptimalkan berdasarkan konfigurasi Anda.
176
+
177
+ Ini memastikan bahwa:
178
+
179
+ - Jika komponen tidak diimpor, kontennya tidak disertakan dalam bundle (Dead Code Elimination).
180
+ - Jika komponen dimuat secara lambat (lazy-loaded), kontennya juga dimuat secara lambat.
181
+
182
+ ## Setup Berdasarkan Platform
183
+
184
+ <Tabs>
185
+ <Tab value="nextjs">
186
+
187
+ ### Next.js
188
+
189
+ Next.js memerlukan plugin `@intlayer/swc` untuk menangani transformasi, karena Next.js menggunakan SWC untuk build.
190
+
191
+ > Plugin ini tidak diinstal secara default karena plugin SWC masih eksperimental untuk Next.js. Ini mungkin berubah di masa mendatang.
192
+
193
+ ```bash packageManager="npm"
194
+ npm install -D @intlayer/swc
195
+ ```
196
+
197
+ ```bash packageManager="yarn"
198
+ yarn add -D @intlayer/swc
199
+ ```
200
+
201
+ ```bash packageManager="pnpm"
202
+ pnpm add -D @intlayer/swc
203
+ ```
204
+
205
+ ```bash packageManager="bun"
206
+ bun add -d @intlayer/swc
207
+ ```
208
+
209
+ Setelah Diinstal. Intlayer akan secara otomatis mendeteksi dan menggunakan plugin tersebut.
210
+
211
+ </Tab>
212
+ <Tab value="vite">
213
+
214
+ ### Vite
215
+
216
+ Vite menggunakan plugin `@intlayer/babel` yang disertakan sebagai dependensi dari `vite-intlayer`. Optimasi diaktifkan secara default. Tidak ada hal lain yang perlu dilakukan.
217
+
218
+ </Tab>
219
+ <Tab value="webpack">
220
+
221
+ ### Webpack
222
+
223
+ Untuk mengaktifkan optimasi bundle dengan Intlayer di Webpack, Anda perlu menginstal dan mengonfigurasi plugin Babel (`@intlayer/babel`) atau SWC (`@intlayer/swc`) yang sesuai.
224
+
225
+ ```bash packageManager="npm"
226
+ npm install -D @intlayer/babel
227
+ ```
228
+
229
+ ```bash packageManager="yarn"
230
+ yarn add -D @intlayer/babel
231
+ ```
232
+
233
+ ```bash packageManager="pnpm"
234
+ pnpm add -D @intlayer/babel
235
+ ```
236
+
237
+ ```bash packageManager="bun"
238
+ bun add -d @intlayer/babel
239
+ ```
240
+
241
+ ```typescript fileName="babel.config.js"
242
+ const {
243
+ getOptimizePluginOptions,
244
+ intlayerOptimizeBabelPlugin,
245
+ } = require("@intlayer/babel");
246
+
247
+ module.exports = {
248
+ plugins: [[intlayerOptimizeBabelPlugin, getOptimizePluginOptions()]],
249
+ };
250
+ ```
251
+
252
+ </Tab>
253
+ </Tabs>
254
+
255
+ ## Konfigurasi
256
+
257
+ Anda dapat mengontrol cara Intlayer mengoptimalkan bundle Anda melalui properti `build` di `intlayer.config.ts` Anda.
258
+
259
+ ```typescript fileName="intlayer.config.ts"
260
+ import { Locales, type IntlayerConfig } from "intlayer";
261
+
262
+ const config: IntlayerConfig = {
263
+ internationalization: {
264
+ locales: [Locales.ENGLISH, Locales.FRENCH],
265
+ defaultLocale: Locales.ENGLISH,
266
+ },
267
+ dictionary: {
268
+ importMode: "dynamic",
269
+ },
270
+ build: {
271
+ /**
272
+ * Minifikasi kamus untuk memperkecil ukuran bundle.
273
+ */
274
+ minify: true;
275
+
276
+ /**
277
+ * Hapus (purge) kunci yang tidak digunakan dalam kamus
278
+ */
279
+ purge: true;
280
+
281
+ /**
282
+ * Menunjukkan apakah build harus memeriksa tipe TypeScript
283
+ */
284
+ checkTypes: false;
285
+ },
286
+ };
287
+
288
+ export default config;
289
+ ```
290
+
291
+ > Mempertahankan opsi default untuk `optimize` direkomendasikan dalam sebagian besar kasus.
292
+
293
+ > Lihat dok konfigurasi untuk detail lebih lanjut: [Konfigurasi](https://github.com/aymericzip/intlayer/blob/main/docs/docs/id/configuration.md)
294
+
295
+ ### Opsi Build
296
+
297
+ Opsi berikut tersedia di bawah objek konfigurasi `build`:
298
+
299
+ | Properti | Tipe | Default | Deskripsi |
300
+ | :------------- | :-------- | :---------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
301
+ | **`optimize`** | `boolean` | `undefined` | Mengontrol apakah optimasi build diaktifkan. Jika `true`, Intlayer mengganti panggilan kamus dengan suntikan yang dioptimalkan. Jika `false`, optimasi dinonaktifkan. Idealnya diatur ke `true` di produksi. |
302
+ | **`minify`** | `boolean` | `false` | Apakah akan meminimalkan kamus untuk memperkecil ukuran bundle. |
303
+ | **`purge`** | `boolean` | `false` | Apakah akan menghapus kunci yang tidak digunakan dalam kamus. |
304
+
305
+ ### Minifikasi
306
+
307
+ Meminimalkan kamus akan menghapus spasi putih yang tidak perlu, komentar, dan mengurangi ukuran konten JSON. Ini sangat berguna untuk kamus besar.
308
+
309
+ ```typescript fileName="intlayer.config.ts"
310
+ import type { IntlayerConfig } from "intlayer";
311
+
312
+ const config: IntlayerConfig = {
313
+ build: {
314
+ minify: true,
315
+ },
316
+ };
317
+
318
+ export default config;
319
+ ```
320
+
321
+ > Catatan: Minifikasi diabaikan jika `optimize` dinonaktifkan atau jika Editor Visual diaktifkan (karena editor memerlukan konten lengkap untuk memungkinkan pengeditan).
322
+
323
+ ### Purging
324
+
325
+ Purging memastikan bahwa hanya kunci yang benar-benar digunakan dalam kode Anda yang disertakan dalam bundle kamus akhir. Ini dapat secara signifikan mengurangi ukuran bundle Anda jika Anda memiliki kamus besar dengan banyak kunci yang tidak digunakan di setiap bagian aplikasi Anda.
326
+
327
+ ```typescript fileName="intlayer.config.ts"
328
+ import type { IntlayerConfig } from "intlayer";
329
+
330
+ const config: IntlayerConfig = {
331
+ build: {
332
+ purge: true,
333
+ },
334
+ };
335
+
336
+ export default config;
337
+ ```
338
+
339
+ > Catatan: Purging diabaikan jika `optimize` dinonaktifkan.
340
+
341
+ ### Mode Impor
342
+
343
+ Untuk aplikasi besar, termasuk beberapa halaman dan lokal, JSON Anda dapat mewakili bagian yang signifikan dari ukuran bundle Anda. Intlayer memungkinkan Anda untuk mengontrol bagaimana kamus dimuat.
344
+
345
+ Mode impor dapat didefinisikan secara default secara global dalam file `intlayer.config.ts` Anda.
346
+
347
+ ```typescript fileName="intlayer.config.ts"
348
+ import type { IntlayerConfig } from "intlayer";
349
+
350
+ const config: IntlayerConfig = {
351
+ build: {
352
+ minify: true,
353
+ },
354
+ };
355
+
356
+ export default config;
357
+ ```
358
+
359
+ Serta untuk setiap kamus dalam file `.content.{{ts|tsx|js|jsx|mjs|cjs|json|jsonc|json5}}` Anda.
360
+
361
+ ```ts
362
+ import { type Dictionary, t } from "intlayer";
363
+
364
+ const appContent: Dictionary = {
365
+ key: "app",
366
+ importMode: "dynamic", // Menimpa mode impor default
367
+ content: {
368
+ // ...
369
+ },
370
+ };
371
+
372
+ export default appContent;
373
+ ```
374
+
375
+ | Properti | Tipe | Default | Deskripsi |
376
+ | :--------------- | :--------------------------------- | :--------- | :---------------------------------------------------------------------------------------------------------------------- |
377
+ | **`importMode`** | `'static'`, `'dynamic'`, `'fetch'` | `'static'` | **Usang**: Gunakan `dictionary.importMode` sebagai gantinya. Menentukan bagaimana kamus dimuat (lihat detail di bawah). |
378
+
379
+ Pengaturan `importMode` menentukan bagaimana konten kamus disuntikkan ke dalam komponen Anda.
380
+ Anda dapat mendefinisikannya secara global dalam file `intlayer.config.ts` di bawah objek `dictionary`, atau Anda dapat menimpanya untuk kamus tertentu dalam file `.content.ts` nya.
381
+
382
+ ### 1. Mode Statis (`default`)
383
+
384
+ Dalam mode statis, Intlayer mengganti `useIntlayer` dengan `useDictionary` dan menyuntikkan kamus langsung ke dalam bundle JavaScript.
385
+
386
+ - **Kelebihan:** Rendering instan (sinkron), nol permintaan jaringan ekstra selama hidrasi.
387
+ - **Kekurangan:** Bundle menyertakan terjemahan untuk **semua** bahasa yang tersedia untuk komponen tertentu tersebut.
388
+ - **Terbaik untuk:** Single Page Applications (SPA).
389
+
390
+ **Contoh Kode yang Ditransformasi:**
391
+
392
+ ```tsx
393
+ // Kode Anda
394
+ const content = useIntlayer("my-key");
395
+
396
+ // Kode yang dioptimalkan (Statis)
397
+ const content = useDictionary({
398
+ key: "my-key",
399
+ content: {
400
+ nodeType: "translation",
401
+ translation: {
402
+ en: "My title",
403
+ fr: "Mon titre",
404
+ },
405
+ },
406
+ });
407
+ ```
408
+
409
+ ### 2. Mode Dinamis
410
+
411
+ Dalam mode dinamis, Intlayer mengganti `useIntlayer` dengan `useDictionaryAsync`. Ini menggunakan `import()` (mekanisme seperti Suspense) untuk memuat secara lambat (lazy-load) khususnya JSON untuk lokal saat ini.
412
+
413
+ - **Kelebihan:** **Locale-level tree shaking.** Pengguna yang melihat versi bahasa Inggris _hanya_ akan mengunduh kamus bahasa Inggris. Kamus bahasa Prancis tidak pernah dimuat.
414
+ - **Kekurangan:** Memicu permintaan jaringan (pengambilan aset) per komponen selama hidrasi.
415
+ - **Terbaik untuk:** Blok teks besar, artikel, atau aplikasi yang mendukung banyak bahasa di mana ukuran bundle sangat penting.
416
+
417
+ **Contoh Kode yang Ditransformasi:**
418
+
419
+ ```tsx
420
+ // Kode Anda
421
+ const content = useIntlayer("my-key");
422
+
423
+ // Kode yang dioptimalkan (Dinamis)
424
+ const content = useDictionaryAsync({
425
+ en: () =>
426
+ import(".intlayer/dynamic_dictionary/my-key/en.json").then(
427
+ (mod) => mod.default
428
+ ),
429
+ fr: () =>
430
+ import(".intlayer/dynamic_dictionary/my-key/fr.json").then(
431
+ (mod) => mod.default
432
+ ),
433
+ });
434
+ ```
435
+
436
+ > Saat menggunakan `importMode: 'dynamic'`, jika Anda memiliki 100 komponen yang menggunakan `useIntlayer` pada satu halaman, browser akan mencoba 100 pengambilan terpisah. Untuk menghindari "air terjun" permintaan ini, kelompokkan konten ke dalam lebih sedikit file `.content` (misalnya, satu kamus per bagian halaman) daripada satu per komponen atom.
437
+
438
+ ### 3. Mode Fetch
439
+
440
+ Berperilaku mirip dengan mode Dinamis tetapi mencoba mengambil kamus dari Intlayer Live Sync API terlebih dahulu. Jika panggilan API gagal atau konten tidak ditandai untuk pembaruan langsung, ia akan kembali ke impor dinamis.
441
+
442
+ > Lihat dok CMS untuk detail lebih lanjut: [CMS](https://github.com/aymericzip/intlayer/blob/main/docs/docs/id/intlayer_CMS.md)
443
+
444
+ > Dalam mode fetch, pembersihan (purge) dan minifikasi tidak dapat digunakan.
445
+
446
+ ## Ringkasan: Statis vs Dinamis
447
+
448
+ | Fitur | Mode Statis | Mode Dinamis |
449
+ | :--------------------------- | :------------------------------------------------- | :---------------------------------------- |
450
+ | **Ukuran Bundle JS** | Lebih besar (termasuk semua bahasa untuk komponen) | Terkecil (hanya kode, tanpa konten) |
451
+ | **Pemuatan Awal** | Instan (Konten ada dalam bundle) | Sedikit tertunda (Mengambil JSON) |
452
+ | **Permintaan Jaringan** | 0 permintaan ekstra | 1 permintaan per kamus |
453
+ | **Tree Shaking** | Tingkat komponen | Tingkat komponen + Tingkat lokal |
454
+ | **Kasus Penggunaan Terbaik** | Komponen UI, Aplikasi Kecil | Halaman dengan banyak teks, Banyak Bahasa |
@@ -257,7 +257,7 @@ export default function LocaleLayout({
257
257
  params: { locale: string };
258
258
  }) {
259
259
  const locale: Locale = (locales as readonly string[]).includes(params.locale)
260
- ? (params.locale as any)
260
+ ? params.locale
261
261
  : defaultLocale;
262
262
 
263
263
  const dir = isRtl(locale) ? "rtl" : "ltr";
@@ -103,7 +103,7 @@ async function loadMessages(locale: string) {
103
103
  }
104
104
 
105
105
  export default getRequestConfig(async ({ locale }) => {
106
- if (!locales.includes(locale as any)) notFound();
106
+ if (!locales.includes(locale)) notFound();
107
107
 
108
108
  return {
109
109
  messages: await loadMessages(locale),
@@ -193,9 +193,7 @@ const RootComponent: ParentComponent = (props) => {
193
193
  </head>
194
194
  <body>
195
195
  <IntlayerProvider locale={locale}>
196
- <Suspense>
197
- {props.children}
198
- </Suspense>
196
+ <Suspense>{props.children}</Suspense>
199
197
  </IntlayerProvider>
200
198
  <Scripts />
201
199
  </body>
@@ -505,12 +503,33 @@ export const Route = createFileRoute("/{-$locale}/")({
505
503
  component: RouteComponent,
506
504
  head: ({ params }) => {
507
505
  const { locale } = params;
508
- const metaContent = getIntlayer("page-metadata", locale);
506
+ const path = "/"; // The path for this route
507
+
508
+ const metaContent = getIntlayer("app", locale);
509
509
 
510
510
  return {
511
+ links: [
512
+ // Canonical link: Points to the current localized page
513
+ { rel: "canonical", href: getLocalizedUrl(path, locale) },
514
+
515
+ // Hreflang: Tell Google about all localized versions
516
+ ...localeMap(({ locale: mapLocale }) => ({
517
+ rel: "alternate",
518
+ hrefLang: mapLocale,
519
+ href: getLocalizedUrl(path, mapLocale),
520
+ })),
521
+
522
+ // x-default: For users in unmatched languages
523
+ // Define the default fallback locale (usually your primary language)
524
+ {
525
+ rel: "alternate",
526
+ hrefLang: "x-default",
527
+ href: getLocalizedUrl(path, defaultLocale),
528
+ },
529
+ ],
511
530
  meta: [
512
531
  { title: metaContent.title },
513
- { content: metaContent.description, name: "description" },
532
+ { name: "description", content: metaContent.meta.description },
514
533
  ],
515
534
  };
516
535
  },