@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.
- package/blog/ar/i18n_using_next-i18next.md +1 -1
- package/blog/ar/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/de/i18n_using_next-i18next.md +1 -1
- package/blog/de/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/en/i18n_using_next-i18next.md +1 -1
- package/blog/en/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/en-GB/i18n_using_next-i18next.md +1 -1
- package/blog/en-GB/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/es/i18n_using_next-i18next.md +1 -1
- package/blog/es/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/fr/i18n_using_next-i18next.md +1 -1
- package/blog/fr/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/hi/i18n_using_next-i18next.md +1 -1
- package/blog/id/i18n_using_next-i18next.md +1 -1
- package/blog/id/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/it/i18n_using_next-i18next.md +1 -1
- package/blog/it/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/ja/i18n_using_next-i18next.md +1 -1
- package/blog/ja/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/ko/i18n_using_next-i18next.md +1 -1
- package/blog/ko/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/pl/i18n_using_next-i18next.md +1 -1
- package/blog/pl/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/pt/i18n_using_next-i18next.md +1 -1
- package/blog/pt/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/ru/i18n_using_next-i18next.md +1 -1
- package/blog/ru/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/blog/tr/i18n_using_next-i18next.md +1 -1
- package/blog/uk/i18n_using_next-i18next.md +1 -1
- package/blog/uk/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/vi/i18n_using_next-i18next.md +1 -1
- package/blog/vi/next-i18next_vs_next-intl_vs_intlayer.md +2 -2
- package/blog/zh/i18n_using_next-i18next.md +1 -1
- package/blog/zh/next-i18next_vs_next-intl_vs_intlayer.md +1 -1
- package/docs/ar/bundle_optimization.md +454 -0
- package/docs/ar/intlayer_with_next-i18next.md +1 -1
- package/docs/ar/intlayer_with_next-intl.md +1 -1
- package/docs/ar/intlayer_with_tanstack+solid.md +24 -5
- package/docs/ar/intlayer_with_tanstack.md +45 -68
- package/docs/bn/bundle_optimization.md +454 -0
- package/docs/cs/bundle_optimization.md +454 -0
- package/docs/de/bundle_optimization.md +454 -0
- package/docs/de/intlayer_with_next-i18next.md +1 -1
- package/docs/de/intlayer_with_next-intl.md +1 -1
- package/docs/de/intlayer_with_tanstack+solid.md +24 -5
- package/docs/de/intlayer_with_tanstack.md +45 -68
- package/docs/en/bundle_optimization.md +36 -8
- package/docs/en/intlayer_with_next-i18next.md +1 -1
- package/docs/en/intlayer_with_next-intl.md +1 -1
- package/docs/en/intlayer_with_tanstack+solid.md +24 -5
- package/docs/en/intlayer_with_tanstack.md +45 -68
- package/docs/en-GB/bundle_optimization.md +454 -0
- package/docs/en-GB/intlayer_with_next-i18next.md +1 -1
- package/docs/en-GB/intlayer_with_next-intl.md +1 -1
- package/docs/en-GB/intlayer_with_tanstack+solid.md +24 -5
- package/docs/en-GB/intlayer_with_tanstack.md +47 -70
- package/docs/es/bundle_optimization.md +454 -0
- package/docs/es/intlayer_with_next-i18next.md +1 -1
- package/docs/es/intlayer_with_next-intl.md +1 -1
- package/docs/es/intlayer_with_tanstack+solid.md +24 -5
- package/docs/es/intlayer_with_tanstack.md +45 -68
- package/docs/fr/bundle_optimization.md +454 -0
- package/docs/fr/intlayer_with_next-i18next.md +1 -1
- package/docs/fr/intlayer_with_next-intl.md +1 -1
- package/docs/fr/intlayer_with_tanstack+solid.md +24 -5
- package/docs/fr/intlayer_with_tanstack.md +45 -68
- package/docs/hi/bundle_optimization.md +454 -0
- package/docs/hi/intlayer_with_next-i18next.md +1 -1
- package/docs/hi/intlayer_with_next-intl.md +1 -1
- package/docs/hi/intlayer_with_tanstack+solid.md +24 -5
- package/docs/hi/intlayer_with_tanstack.md +45 -68
- package/docs/id/bundle_optimization.md +454 -0
- package/docs/id/intlayer_with_next-i18next.md +1 -1
- package/docs/id/intlayer_with_next-intl.md +1 -1
- package/docs/id/intlayer_with_tanstack+solid.md +24 -5
- package/docs/id/intlayer_with_tanstack.md +45 -68
- package/docs/it/bundle_optimization.md +454 -0
- package/docs/it/intlayer_with_next-i18next.md +1 -1
- package/docs/it/intlayer_with_next-intl.md +1 -1
- package/docs/it/intlayer_with_tanstack+solid.md +24 -5
- package/docs/it/intlayer_with_tanstack.md +45 -68
- package/docs/ja/bundle_optimization.md +454 -0
- package/docs/ja/intlayer_with_next-i18next.md +1 -1
- package/docs/ja/intlayer_with_next-intl.md +1 -1
- package/docs/ja/intlayer_with_tanstack+solid.md +24 -5
- package/docs/ja/intlayer_with_tanstack.md +45 -36
- package/docs/ko/bundle_optimization.md +454 -0
- package/docs/ko/intlayer_with_next-i18next.md +1 -1
- package/docs/ko/intlayer_with_next-intl.md +1 -1
- package/docs/ko/intlayer_with_tanstack+solid.md +24 -5
- package/docs/ko/intlayer_with_tanstack.md +45 -68
- package/docs/nl/bundle_optimization.md +454 -0
- package/docs/pl/bundle_optimization.md +454 -0
- package/docs/pl/intlayer_with_next-i18next.md +1 -1
- package/docs/pl/intlayer_with_next-intl.md +1 -1
- package/docs/pl/intlayer_with_tanstack+solid.md +24 -5
- package/docs/pl/intlayer_with_tanstack.md +45 -68
- package/docs/pt/bundle_optimization.md +454 -0
- package/docs/pt/intlayer_with_next-i18next.md +1 -1
- package/docs/pt/intlayer_with_next-intl.md +1 -1
- package/docs/pt/intlayer_with_tanstack+solid.md +24 -5
- package/docs/pt/intlayer_with_tanstack.md +45 -68
- package/docs/ru/bundle_optimization.md +454 -0
- package/docs/ru/intlayer_with_next-i18next.md +1 -1
- package/docs/ru/intlayer_with_next-intl.md +1 -1
- package/docs/ru/intlayer_with_tanstack+solid.md +24 -5
- package/docs/ru/intlayer_with_tanstack.md +45 -68
- package/docs/tr/bundle_optimization.md +454 -0
- package/docs/tr/intlayer_with_next-i18next.md +1 -1
- package/docs/tr/intlayer_with_next-intl.md +1 -1
- package/docs/tr/intlayer_with_tanstack+solid.md +24 -5
- package/docs/tr/intlayer_with_tanstack.md +45 -68
- package/docs/uk/bundle_optimization.md +454 -0
- package/docs/uk/intlayer_with_next-i18next.md +1 -1
- package/docs/uk/intlayer_with_next-intl.md +1 -1
- package/docs/uk/intlayer_with_tanstack+solid.md +24 -5
- package/docs/uk/intlayer_with_tanstack.md +45 -68
- package/docs/ur/bundle_optimization.md +454 -0
- package/docs/vi/bundle_optimization.md +454 -0
- package/docs/vi/intlayer_with_next-i18next.md +1 -1
- package/docs/vi/intlayer_with_next-intl.md +1 -1
- package/docs/vi/intlayer_with_tanstack+solid.md +24 -5
- package/docs/vi/intlayer_with_tanstack.md +45 -68
- package/docs/zh/bundle_optimization.md +454 -0
- package/docs/zh/intlayer_with_next-i18next.md +1 -1
- package/docs/zh/intlayer_with_next-intl.md +1 -1
- package/docs/zh/intlayer_with_tanstack+solid.md +24 -5
- package/docs/zh/intlayer_with_tanstack.md +45 -68
- 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
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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 {
|
|
368
|
+
import type { StripLocalePrefix } from "@/components/localized-link";
|
|
381
369
|
import type { FileRouteTypes } from "@/routeTree.gen";
|
|
382
370
|
|
|
383
|
-
type
|
|
384
|
-
|
|
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
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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
|
|
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
|
-
{
|
|
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
|
-
?
|
|
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
|
|
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
|
|
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
|
-
{
|
|
532
|
+
{ name: "description", content: metaContent.meta.description },
|
|
514
533
|
],
|
|
515
534
|
};
|
|
516
535
|
},
|