@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
|
@@ -225,9 +225,7 @@ function RootDocument({ children }: { children: ReactNode }) {
|
|
|
225
225
|
<HeadContent />
|
|
226
226
|
</head>
|
|
227
227
|
<body>
|
|
228
|
-
<IntlayerProvider locale={locale}>
|
|
229
|
-
{children}
|
|
230
|
-
</IntlayerProvider>
|
|
228
|
+
<IntlayerProvider locale={locale}>{children}</IntlayerProvider>
|
|
231
229
|
<Scripts />
|
|
232
230
|
</body>
|
|
233
231
|
</html>
|
|
@@ -326,30 +324,20 @@ import { getPrefix } from "intlayer";
|
|
|
326
324
|
|
|
327
325
|
export const LOCALE_ROUTE = "{-$locale}" as const;
|
|
328
326
|
|
|
329
|
-
|
|
330
|
-
export type RemoveLocaleParam<T> = T extends string
|
|
331
|
-
? RemoveLocaleFromString<T>
|
|
332
|
-
: T;
|
|
327
|
+
export type To = StripLocalePrefix<LinkComponentProps["to"]>;
|
|
333
328
|
|
|
334
|
-
export type
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
329
|
+
export type StripLocalePrefix<T extends string | undefined> = T extends
|
|
330
|
+
| `/${typeof LOCALE_ROUTE}/`
|
|
331
|
+
| `/${typeof LOCALE_ROUTE}`
|
|
332
|
+
? "/"
|
|
333
|
+
: T extends `/${typeof LOCALE_ROUTE}/${infer Rest}`
|
|
334
|
+
? `/${Rest}`
|
|
335
|
+
: T;
|
|
338
336
|
|
|
339
337
|
type LocalizedLinkProps = {
|
|
340
338
|
to?: To;
|
|
341
339
|
} & Omit<LinkComponentProps, "to">;
|
|
342
340
|
|
|
343
|
-
// Helper
|
|
344
|
-
type RemoveAll<
|
|
345
|
-
S extends string,
|
|
346
|
-
Sub extends string,
|
|
347
|
-
> = S extends `${infer H}${Sub}${infer T}` ? RemoveAll<`${H}${T}`, Sub> : S;
|
|
348
|
-
|
|
349
|
-
type RemoveLocaleFromString<S extends string> = CollapseDoubleSlashes<
|
|
350
|
-
RemoveAll<S, typeof LOCALE_ROUTE>
|
|
351
|
-
>;
|
|
352
|
-
|
|
353
341
|
export const LocalizedLink: FC<LocalizedLinkProps> = (props) => {
|
|
354
342
|
const { locale } = useLocale();
|
|
355
343
|
const { localePrefix } = getPrefix(locale);
|
|
@@ -378,26 +366,26 @@ Quindi possiamo creare un hook `useLocalizedNavigate` per la navigazione program
|
|
|
378
366
|
import { useNavigate } from "@tanstack/react-router";
|
|
379
367
|
import { getPrefix } from "intlayer";
|
|
380
368
|
import { useLocale } from "react-intlayer";
|
|
381
|
-
import {
|
|
369
|
+
import type { StripLocalePrefix } from "@/components/localized-link";
|
|
382
370
|
import type { FileRouteTypes } from "@/routeTree.gen";
|
|
383
371
|
|
|
384
|
-
type
|
|
385
|
-
|
|
386
|
-
| `/${typeof LOCALE_ROUTE}/`
|
|
387
|
-
? "/"
|
|
388
|
-
: T extends `/${typeof LOCALE_ROUTE}/${infer Rest}`
|
|
389
|
-
? `/${Rest}`
|
|
390
|
-
: never;
|
|
372
|
+
type NavigateFn = ReturnType<typeof useNavigate>;
|
|
373
|
+
type BaseNavigateOptions = Parameters<NavigateFn>[0];
|
|
391
374
|
|
|
392
375
|
type LocalizedTo = StripLocalePrefix<FileRouteTypes["to"]>;
|
|
393
376
|
|
|
394
|
-
type
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
377
|
+
export type LocalizedNavigateOptions = Omit<
|
|
378
|
+
BaseNavigateOptions,
|
|
379
|
+
"to" | "params"
|
|
380
|
+
> & {
|
|
381
|
+
to: LocalizedTo;
|
|
382
|
+
params?: Omit<NonNullable<BaseNavigateOptions["params"]>, "locale">;
|
|
399
383
|
};
|
|
400
384
|
|
|
385
|
+
type LocalizedNavigate = (
|
|
386
|
+
options: LocalizedNavigateOptions
|
|
387
|
+
) => ReturnType<NavigateFn>;
|
|
388
|
+
|
|
401
389
|
export const useLocalizedNavigate = () => {
|
|
402
390
|
const navigate = useNavigate();
|
|
403
391
|
|
|
@@ -444,38 +432,6 @@ import { useLocalizedNavigate } from "@/hooks/useLocalizedNavigate";
|
|
|
444
432
|
|
|
445
433
|
export const Route = createFileRoute("/{-$locale}/")({
|
|
446
434
|
component: RouteComponent,
|
|
447
|
-
head: ({ params }) => {
|
|
448
|
-
const { locale } = params;
|
|
449
|
-
const path = "/"; // The path for this route
|
|
450
|
-
|
|
451
|
-
const metaContent = getIntlayer("app", locale);
|
|
452
|
-
|
|
453
|
-
return {
|
|
454
|
-
links: [
|
|
455
|
-
// Canonical link: Points to the current localized page
|
|
456
|
-
{ rel: "canonical", href: getLocalizedUrl(path, locale) },
|
|
457
|
-
|
|
458
|
-
// Hreflang: Tell Google about all localized versions
|
|
459
|
-
...localeMap(({ locale: mapLocale }) => ({
|
|
460
|
-
rel: "alternate",
|
|
461
|
-
hrefLang: mapLocale,
|
|
462
|
-
href: getLocalizedUrl(path, mapLocale),
|
|
463
|
-
})),
|
|
464
|
-
|
|
465
|
-
// x-default: For users in unmatched languages
|
|
466
|
-
// Define the default fallback locale (usually your primary language)
|
|
467
|
-
{
|
|
468
|
-
rel: "alternate",
|
|
469
|
-
hrefLang: "x-default",
|
|
470
|
-
href: getLocalizedUrl(path, defaultLocale),
|
|
471
|
-
},
|
|
472
|
-
],
|
|
473
|
-
meta: [
|
|
474
|
-
{ title: metaContent.title },
|
|
475
|
-
{ name: "description", content: metaContent.meta.description },
|
|
476
|
-
],
|
|
477
|
-
};
|
|
478
|
-
},
|
|
479
435
|
});
|
|
480
436
|
|
|
481
437
|
function RouteComponent() {
|
|
@@ -630,12 +586,33 @@ export const Route = createFileRoute("/{-$locale}/")({
|
|
|
630
586
|
component: RouteComponent,
|
|
631
587
|
head: ({ params }) => {
|
|
632
588
|
const { locale } = params;
|
|
633
|
-
const
|
|
589
|
+
const path = "/"; // The path for this route
|
|
590
|
+
|
|
591
|
+
const metaContent = getIntlayer("app", locale);
|
|
634
592
|
|
|
635
593
|
return {
|
|
594
|
+
links: [
|
|
595
|
+
// Canonical link: Points to the current localized page
|
|
596
|
+
{ rel: "canonical", href: getLocalizedUrl(path, locale) },
|
|
597
|
+
|
|
598
|
+
// Hreflang: Tell Google about all localized versions
|
|
599
|
+
...localeMap(({ locale: mapLocale }) => ({
|
|
600
|
+
rel: "alternate",
|
|
601
|
+
hrefLang: mapLocale,
|
|
602
|
+
href: getLocalizedUrl(path, mapLocale),
|
|
603
|
+
})),
|
|
604
|
+
|
|
605
|
+
// x-default: For users in unmatched languages
|
|
606
|
+
// Define the default fallback locale (usually your primary language)
|
|
607
|
+
{
|
|
608
|
+
rel: "alternate",
|
|
609
|
+
hrefLang: "x-default",
|
|
610
|
+
href: getLocalizedUrl(path, defaultLocale),
|
|
611
|
+
},
|
|
612
|
+
],
|
|
636
613
|
meta: [
|
|
637
614
|
{ title: metaContent.title },
|
|
638
|
-
{
|
|
615
|
+
{ name: "description", content: metaContent.meta.description },
|
|
639
616
|
],
|
|
640
617
|
};
|
|
641
618
|
},
|
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2025-11-25
|
|
3
|
+
updatedAt: 2026-04-08
|
|
4
|
+
title: i18nバンドルサイズの最適化とパフォーマンス
|
|
5
|
+
description: 国際化(i18n)コンテンツを最適化することで、アプリケーションのバンドルサイズを削減します。Intlayerを使用して辞書のツリーシェイキングや遅延読み込みを活用する方法を学びましょう。
|
|
6
|
+
keywords:
|
|
7
|
+
- バンドル最適化
|
|
8
|
+
- コンテンツ自動化
|
|
9
|
+
- 動的コンテンツ
|
|
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: "ビルド構成に `minify` と `purge` オプションを追加"
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# i18nバンドルサイズの最適化とパフォーマンス
|
|
25
|
+
|
|
26
|
+
JSONファイルに依存する従来のi18nソリューションで最も一般的な課題の1つは、コンテンツサイズの管理です。開発者が手動でコンテンツを名前空間に分離しない場合、ユーザーは特定のページを表示するためだけに、全ページおよび全言語の翻訳をダウンロードすることになりかねません。
|
|
27
|
+
|
|
28
|
+
例えば、10ページが10言語に翻訳されたアプリケーションでは、ユーザーが必要なのは**1ページ**(現在の言語の現在のページ)だけであるにもかかわらず、100ページ分のコンテンツをダウンロードすることになる可能性があります。これは帯域幅の無駄遣いと読み込み時間の低下につながります。
|
|
29
|
+
|
|
30
|
+
**Intlayerは、ビルド時の最適化を通じてこの問題を解決します。** コードを分析してコンポーネントごとに実際に使用されている辞書を検出し、必要なコンテンツのみをバンドルに再注入します。
|
|
31
|
+
|
|
32
|
+
## 目次
|
|
33
|
+
|
|
34
|
+
<TOC />
|
|
35
|
+
|
|
36
|
+
## バンドルのスキャン
|
|
37
|
+
|
|
38
|
+
バンドルを分析することは、「重い」JSONファイルやコード分割の機会を特定するための第一歩です。これらのツールは、アプリケーションのコンパイル済みコードの視覚的なツリーマップを生成し、どのライブラリが最もスペースを消費しているかを正確に把握できるようにします。
|
|
39
|
+
|
|
40
|
+
<Tabs>
|
|
41
|
+
<Tab value="vite">
|
|
42
|
+
|
|
43
|
+
### Vite / Rollup
|
|
44
|
+
|
|
45
|
+
Viteは内部でRollupを使用しています。`rollup-plugin-visualizer`プラグインは、グラフ内のすべてのモジュールのサイズを示すインタラクティブなHTMLファイルを生成します。
|
|
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, // ブラウザでレポートを自動的に開く
|
|
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
|
+
App RouterとTurbopackを使用しているプロジェクトの場合、Next.jsは追加の依存関係を必要としない組み込みの実験的なアナライザーを提供しています。
|
|
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
|
+
Next.jsでデフォルトのWebpackバンドラーを使用している場合は、公式のバンドルアナライザーを使用してください。ビルド中に環境変数を設定することでトリガーされます。
|
|
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
|
+
// Next.jsの設定
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**使用方法:**
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
ANALYZE=true npm run build
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
</Tab>
|
|
130
|
+
<Tab value="Webpack (CRA / Angular / etc)">
|
|
131
|
+
|
|
132
|
+
### 標準Webpack
|
|
133
|
+
|
|
134
|
+
Create React App (ejected)、Angular、またはカスタムのWebpack設定の場合は、業界標準の `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
|
+
## 仕組み
|
|
170
|
+
|
|
171
|
+
Intlayerは**コンポーネント単位のアプローチ**を採用しています。グローバルなJSONファイルとは異なり、コンテンツはコンポーネントの隣または内部で定義されます。ビルドプロセス中、Intlayerは以下を行います:
|
|
172
|
+
|
|
173
|
+
1. コードを**分析**して `useIntlayer` の呼び出しを見つけます。
|
|
174
|
+
2. 対応する辞書コンテンツを**構築**します。
|
|
175
|
+
3. 設定に基づいて `useIntlayer` の呼び出しを最適化されたコードに**置き換え**ます。
|
|
176
|
+
|
|
177
|
+
これにより、以下が保証されます:
|
|
178
|
+
|
|
179
|
+
- コンポーネントがインポートされていない場合、そのコンテンツはバンドルに含まれません(デッドコード削除)。
|
|
180
|
+
- コンポーネントが遅延読み込み(lazy loading)される場合、そのコンテンツも遅延読み込みされます。
|
|
181
|
+
|
|
182
|
+
## プラットフォーム別のセットアップ
|
|
183
|
+
|
|
184
|
+
<Tabs>
|
|
185
|
+
<Tab value="nextjs">
|
|
186
|
+
|
|
187
|
+
### Next.js
|
|
188
|
+
|
|
189
|
+
Next.jsはビルドにSWCを使用するため、変換を処理するために `@intlayer/swc` プラグインが必要です。
|
|
190
|
+
|
|
191
|
+
> SWCプラグインはNext.jsにおいてまだ実験段階であるため、このプラグインはデフォルトではインストールされません。将来的に変更される可能性があります。
|
|
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
|
+
インストールされると、Intlayerは自動的にプラグインを検出して使用します。
|
|
210
|
+
|
|
211
|
+
</Tab>
|
|
212
|
+
<Tab value="vite">
|
|
213
|
+
|
|
214
|
+
### Vite
|
|
215
|
+
|
|
216
|
+
Viteは `vite-intlayer` の依存関係として含まれている `@intlayer/babel` プラグインを使用します。最適化はデフォルトで有効になっています。他に行うことはありません。
|
|
217
|
+
|
|
218
|
+
</Tab>
|
|
219
|
+
<Tab value="webpack">
|
|
220
|
+
|
|
221
|
+
### Webpack
|
|
222
|
+
|
|
223
|
+
WebpackでIntlayerを使用したバンドル最適化を有効にするには、適切なBabel (`@intlayer/babel`) または SWC (`@intlayer/swc`) プラグインをインストールして構成する必要があります。
|
|
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
|
+
## 構成
|
|
256
|
+
|
|
257
|
+
`intlayer.config.ts` の `build` プロパティを介して、Intlayerがバンドルを最適化する方法を制御できます。
|
|
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
|
+
* バンドルサイズを削減するために辞書を縮小(minify)します。
|
|
273
|
+
*/
|
|
274
|
+
minify: true;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* 辞書内の未使用のキーを削除(purge)します。
|
|
278
|
+
*/
|
|
279
|
+
purge: true;
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* ビルド時にTypeScriptの型チェックを行うかどうかを指定します。
|
|
283
|
+
*/
|
|
284
|
+
checkTypes: false;
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
export default config;
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
> ほとんどの場合、`optimize` オプションはデフォルトのままにすることをお勧めします。
|
|
292
|
+
|
|
293
|
+
> 構成の詳細はドキュメントを参照してください: [構成](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/configuration.md)
|
|
294
|
+
|
|
295
|
+
### ビルドオプション
|
|
296
|
+
|
|
297
|
+
`build` 構成オブジェクトでは、以下のオプションが利用可能です:
|
|
298
|
+
|
|
299
|
+
| プロパティ | 型 | デフォルト | 説明 |
|
|
300
|
+
| :------------- | :-------- | :---------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
301
|
+
| **`optimize`** | `boolean` | `undefined` | ビルド最適化を有効にするかどうかを制御します。`true` の場合、Intlayerは辞書の呼び出しを最適化された注入に置き換えます。`false` の場合、最適化は無効になります。本番環境では `true` に設定するのが理想的です。 |
|
|
302
|
+
| **`minify`** | `boolean` | `false` | バンドルサイズを削減するために辞書を縮小するかどうか。 |
|
|
303
|
+
| **`purge`** | `boolean` | `false` | 辞書内の未使用のキーを削除するかどうか。 |
|
|
304
|
+
|
|
305
|
+
### ミニフィケーション(縮小化)
|
|
306
|
+
|
|
307
|
+
辞書をミニファイすることで、不要な空白やコメントを削除し、JSONコンテンツのサイズを削減します。これは大きな辞書の場合に特に有効です。
|
|
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
|
+
> 注意: `optimize` が無効な場合、またはビジュアルエディタが有効な場合(エディタが編集のために完全なコンテンツを必要とするため)、ミニフィケーションは無視されます。
|
|
322
|
+
|
|
323
|
+
### パージング(未使用キーの削除)
|
|
324
|
+
|
|
325
|
+
パージングにより、コード内で実際に使用されているキーのみが最終的な辞書バンドルに含まれるようになります。アプリケーションのすべての部分で使用されていないキーが多数含まれる大きな辞書がある場合、これによりバンドルサイズを大幅に削減できます。
|
|
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
|
+
> 注意: `optimize` が無効な場合、パージングは無視されます。
|
|
340
|
+
|
|
341
|
+
### インポートモード
|
|
342
|
+
|
|
343
|
+
複数のページとロケールを含む大規模なアプリケーションでは、JSONファイルがバンドルサイズの大部分を占める可能性があります。Intlayerでは、辞書の読み込み方法を制御できます。
|
|
344
|
+
|
|
345
|
+
インポートモードは、`intlayer.config.ts` ファイルでグローバルにデフォルト設定を定義できます。
|
|
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
|
+
また、`.content.{{ts|tsx|js|jsx|mjs|cjs|json|jsonc|json5}}` ファイル内の各辞書に対しても定義できます。
|
|
360
|
+
|
|
361
|
+
```ts
|
|
362
|
+
import { type Dictionary, t } from "intlayer";
|
|
363
|
+
|
|
364
|
+
const appContent: Dictionary = {
|
|
365
|
+
key: "app",
|
|
366
|
+
importMode: "dynamic", // デフォルトのインポートモードを上書き
|
|
367
|
+
content: {
|
|
368
|
+
// ...
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
export default appContent;
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
| プロパティ | 型 | デフォルト | 説明 |
|
|
376
|
+
| :--------------- | :--------------------------------- | :--------- | :-------------------------------------------------------------------------------------------------------------- |
|
|
377
|
+
| **`importMode`** | `'static'`, `'dynamic'`, `'fetch'` | `'static'` | **非推奨**: 代わりに `dictionary.importMode` を使用してください。辞書の読み込み方法を決定します(詳細は後述)。 |
|
|
378
|
+
|
|
379
|
+
`importMode` 設定は、辞書コンテンツがどのようにコンポーネントに注入されるかを規定します。
|
|
380
|
+
`intlayer.config.ts` ファイルの `dictionary` オブジェクトの下でグローバルに定義することも、特定の辞書の `.content.ts` ファイルで上書きすることもできます。
|
|
381
|
+
|
|
382
|
+
### 1. 静的モード (`default`)
|
|
383
|
+
|
|
384
|
+
静的モードでは、Intlayerは `useIntlayer` を `useDictionary` に置き換え、辞書をJavaScriptバンドルに直接注入します。
|
|
385
|
+
|
|
386
|
+
- **メリット:** 即時レンダリング(同期)、ハイドレーション中に追加のネットワークリクエストが発生しない。
|
|
387
|
+
- **デメリット:** バンドルに、その特定のコンポーネントで使用可能な**すべての**言語の翻訳が含まれる。
|
|
388
|
+
- **最適なケース:** シングルページアプリケーション(SPA)。
|
|
389
|
+
|
|
390
|
+
**変換後のコード例:**
|
|
391
|
+
|
|
392
|
+
```tsx
|
|
393
|
+
// 元のコード
|
|
394
|
+
const content = useIntlayer("my-key");
|
|
395
|
+
|
|
396
|
+
// 最適化されたコード(静的)
|
|
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. 動的モード
|
|
410
|
+
|
|
411
|
+
動的モードでは、Intlayerは `useIntlayer` を `useDictionaryAsync` に置き換えます。これは `import()`(Suspenseのような仕組み)を使用して、現在のロケールのJSONを具体的に遅延読み込みします。
|
|
412
|
+
|
|
413
|
+
- **メリット:** **ロケールレベルのツリーシェイキング。** 英語版を表示しているユーザーは英語版の辞書のみをダウンロードし、フランス語版が読み込まれることはありません。
|
|
414
|
+
- **デメリット:** ハイドレーション中にコンポーネントごとにネットワークリクエスト(アセットの取得)が発生する。
|
|
415
|
+
- **最適なケース:** バンドルサイズが非常に重要で、多くの言語、大きなテキストブロック、記事などをサポートする場合。
|
|
416
|
+
|
|
417
|
+
**変換後のコード例:**
|
|
418
|
+
|
|
419
|
+
```tsx
|
|
420
|
+
// 元のコード
|
|
421
|
+
const content = useIntlayer("my-key");
|
|
422
|
+
|
|
423
|
+
// 最適化されたコード(動的)
|
|
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
|
+
> `importMode: 'dynamic'` を使用する場合、1ページに `useIntlayer` を使用するコンポーネントが100個あると、ブラウザは100回の個別のフェッチを試みます。このリクエストの「ウォーターフォール」を避けるために、アトムコンポーネントごとに1つではなく、ページセクションごとに1つの辞書といったように、コンテンツをより少ない `.content` ファイルにグループ化してください。
|
|
437
|
+
|
|
438
|
+
### 3. フェッチモード
|
|
439
|
+
|
|
440
|
+
動的モードと同様に動作しますが、まずIntlayer Live Sync APIから辞書の取得を試みます。API呼び出しが失敗するか、コンテンツがライブアップデート対象でない場合は、動的インポートにフォールバックします。
|
|
441
|
+
|
|
442
|
+
> 詳細はCMSドキュメントを参照してください: [CMS](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/intlayer_CMS.md)
|
|
443
|
+
|
|
444
|
+
> フェッチモードでは、パージとミニフィケーションは使用できません。
|
|
445
|
+
|
|
446
|
+
## まとめ: 静的 vs 動的
|
|
447
|
+
|
|
448
|
+
| 特徴 | 静的モード | 動的モード |
|
|
449
|
+
| :------------------------- | :----------------------------------- | :------------------------------------ |
|
|
450
|
+
| **JSバンドルサイズ** | 最大(コンポーネントの全言語を含む) | 最小(コードのみ、コンテンツなし) |
|
|
451
|
+
| **初期読み込み** | 即時(バンドル内にコンテンツあり) | わずかな遅延(JSONをフェッチ) |
|
|
452
|
+
| **ネットワークリクエスト** | 0 回(追加リクエストなし) | 辞書ごとに 1 回 |
|
|
453
|
+
| **ツリーシェイキング** | コンポーネントレベル | コンポーネントレベル + ロケールレベル |
|
|
454
|
+
| **最適なユースケース** | UIコンポーネント、小規模アプリ | テキストが多いページ、多言語対応 |
|
|
@@ -256,7 +256,7 @@ export default function LocaleLayout({
|
|
|
256
256
|
params: { locale: string };
|
|
257
257
|
}) {
|
|
258
258
|
const locale: Locale = (locales as readonly string[]).includes(params.locale)
|
|
259
|
-
?
|
|
259
|
+
? params.locale
|
|
260
260
|
: defaultLocale;
|
|
261
261
|
|
|
262
262
|
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),
|
|
@@ -194,9 +194,7 @@ const RootComponent: ParentComponent = (props) => {
|
|
|
194
194
|
</head>
|
|
195
195
|
<body>
|
|
196
196
|
<IntlayerProvider locale={locale}>
|
|
197
|
-
<Suspense>
|
|
198
|
-
{props.children}
|
|
199
|
-
</Suspense>
|
|
197
|
+
<Suspense>{props.children}</Suspense>
|
|
200
198
|
</IntlayerProvider>
|
|
201
199
|
<Scripts />
|
|
202
200
|
</body>
|
|
@@ -506,12 +504,33 @@ export const Route = createFileRoute("/{-$locale}/")({
|
|
|
506
504
|
component: RouteComponent,
|
|
507
505
|
head: ({ params }) => {
|
|
508
506
|
const { locale } = params;
|
|
509
|
-
const
|
|
507
|
+
const path = "/"; // The path for this route
|
|
508
|
+
|
|
509
|
+
const metaContent = getIntlayer("app", locale);
|
|
510
510
|
|
|
511
511
|
return {
|
|
512
|
+
links: [
|
|
513
|
+
// Canonical link: Points to the current localized page
|
|
514
|
+
{ rel: "canonical", href: getLocalizedUrl(path, locale) },
|
|
515
|
+
|
|
516
|
+
// Hreflang: Tell Google about all localized versions
|
|
517
|
+
...localeMap(({ locale: mapLocale }) => ({
|
|
518
|
+
rel: "alternate",
|
|
519
|
+
hrefLang: mapLocale,
|
|
520
|
+
href: getLocalizedUrl(path, mapLocale),
|
|
521
|
+
})),
|
|
522
|
+
|
|
523
|
+
// x-default: For users in unmatched languages
|
|
524
|
+
// Define the default fallback locale (usually your primary language)
|
|
525
|
+
{
|
|
526
|
+
rel: "alternate",
|
|
527
|
+
hrefLang: "x-default",
|
|
528
|
+
href: getLocalizedUrl(path, defaultLocale),
|
|
529
|
+
},
|
|
530
|
+
],
|
|
512
531
|
meta: [
|
|
513
532
|
{ title: metaContent.title },
|
|
514
|
-
{
|
|
533
|
+
{ name: "description", content: metaContent.meta.description },
|
|
515
534
|
],
|
|
516
535
|
};
|
|
517
536
|
},
|