@intlayer/docs 7.3.3 → 7.3.4

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.
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  createdAt: 2025-11-24
3
3
  updatedAt: 2025-11-24
4
- title: コンパイラー型 vs 宣言型 i18n
5
- description: 「マジック」なコンパイラー型国際化と明示的な宣言型コンテンツ管理のアーキテクチャ上のトレードオフを探る。
4
+ title: コンパイラー vs 宣言的 i18n
5
+ description: 「魔法の」コンパイラー ベースの国際化と明示的な宣言的コンテンツ管理の間のアーキテクチャ上のトレードオフを探る。
6
6
  keywords:
7
7
  - Intlayer
8
8
  - 国際化
@@ -12,126 +12,209 @@ keywords:
12
12
  - React
13
13
  - i18n
14
14
  - コンパイラー
15
- - 宣言型
15
+ - 宣言的
16
16
  slugs:
17
17
  - blog
18
18
  - compiler-vs-declarative-i18n
19
19
  ---
20
20
 
21
- # コンパイラー型i18nの賛否
21
+ # コンパイラー ベースの i18n に賛成と反対の理由
22
22
 
23
- もし10年以上ウェブアプリケーションを開発してきたなら、国際化(i18n)が常に摩擦点であったことをご存知でしょう。文字列の抽出、JSONファイルの管理、複数形ルールの扱いなど、誰もやりたがらない作業であることが多いのです。
23
+ 10年以上ウェブアプリケーションを開発しているなら、国際化(i18n)が常に摩擦点であったことをご存知でしょう。文字列の抽出、JSON ファイルの管理、複数形ルールの心配など、誰もやりたがらないタスクであることが多いです。
24
24
 
25
- 最近、「コンパイラー型」i18nツールの新しい波が登場し、この苦痛を解消すると約束しています。魅力的な提案はこうです:**コンポーネント内にテキストを書くだけで、ビルドツールが残りを処理してくれる。** キーもインポートも不要、ただのマジックです。
25
+ 最近、新たな波の**「コンパイラー ベース」i18nツール**が登場し、この面倒を解消すると約束しています。魅力的な提案はこうです:**コンポーネントにテキストを書くだけで、ビルドツールが残りを処理してくれる。** キーもインポートも不要、ただの魔法のように。
26
26
 
27
- しかし、ソフトウェア工学におけるすべての抽象化と同様に、マジックには代償が伴います。
27
+ しかし、ソフトウェア工学におけるすべての抽象化と同様に、魔法には代償があります。
28
28
 
29
- このブログ記事では、宣言型ライブラリからコンパイラー型アプローチへの移行、彼らがもたらす隠れたアーキテクチャ的負債、そしてなぜ「退屈な」方法がプロフェッショナルなアプリケーションにとって依然として最良の方法であるかを探ります。
29
+ このブログ記事では、宣言的ライブラリからコンパイラー ベースのアプローチへの移行、それらがもたらす隠れたアーキテクチャ的負債、そしてなぜ「退屈な」方法がプロフェッショナルなアプリケーションにとって依然として最良の方法である可能性があるのかを探ります。
30
30
 
31
- ## 翻訳の簡単な歴史
31
+ ## 目次
32
32
 
33
- 今の状況を理解するためには、出発点を振り返る必要があります。
33
+ <TOC/>
34
34
 
35
- 2011年から2012年頃、JavaScriptの状況は大きく異なっていました。現在私たちが知っているようなバンドラー(WebpackやVite)は存在しなかったか、まだ初期段階にありました。ブラウザ上でスクリプトをつなぎ合わせていた時代です。この時代に、**i18next**のようなライブラリが誕生しました。
35
+ ## 国際化の簡単な歴史
36
36
 
37
- 彼らは当時可能な唯一の方法で問題を解決しました:**ランタイム辞書**です。巨大なJSONオブジェクトをメモリに読み込み、関数がその場でキーを検索する仕組みでした。これは信頼性が高く、明示的で、どこでも動作しました。
37
+ 今の状況を理解するためには、まず出発点を振り返る必要があります。
38
38
 
39
- そして現在に至ります。私たちはミリ秒単位で抽象構文木(AST)を解析できる強力なコンパイラ(SWCRustベースのバンドラー)を持っています。この力が新しいアイデアを生み出しました:_なぜ私たちは手動でキーを管理しているのか?なぜコンパイラが「Hello World」というテキストを見て、それを置き換えてくれないのか?_
39
+ 2011年から2012年頃、JavaScriptの状況は大きく異なっていました。現在私たちが知っているようなバンドラー(WebpackVite)は存在しなかったか、まだ初期段階にありました。私たちはブラウザ上でスクリプトをつなぎ合わせていました。この時代に、**i18next**のようなライブラリが誕生しました。
40
+
41
+ 彼らは当時可能な唯一の方法で問題を解決しました:**ランタイム辞書**です。巨大なJSONオブジェクトをメモリに読み込み、関数がその場でキーを検索しました。それは信頼性が高く、明示的で、どこでも動作しました。
42
+
43
+ そして今日に至ります。私たちはミリ秒単位で抽象構文木(AST)を解析できる強力なコンパイラ(SWCやRustベースのバンドラー)を持っています。この力が新しいアイデアを生み出しました:_なぜ私たちは手動でキーを管理しているのか?なぜコンパイラが「Hello World」というテキストを見て、それを置き換えてくれないのか?_
40
44
 
41
45
  こうして、コンパイラベースのi18nが誕生しました。
42
46
 
43
- ## コンパイラの魅力(「マジック」アプローチ)
47
+ > **コンパイラベースのi18nの例:**
48
+ >
49
+ > - Paraglide(各メッセージを小さなESM関数にコンパイルするツリーシェイク可能なモジュール。これにより、バンドラーは未使用のロケールやキーを自動的に削除できます。文字列キーのルックアップの代わりに、関数としてメッセージをインポートします。)
50
+ > - LinguiJS(ビルド時に`<Trans>`のようなメッセージマクロをプレーンなJS関数呼び出しに書き換えるマクロから関数へのコンパイラ。ICU/MessageFormat構文を非常に小さなランタイムフットプリントで利用可能です。)
51
+ > - Lingo.dev(Reactアプリケーションのビルド中に翻訳済みコンテンツを直接注入することでローカリゼーションパイプラインの自動化に注力。AIを使った翻訳の自動生成やCI/CDへの直接統合も可能です。)
52
+ > - Wuchale(Svelteを最優先としたプリプロセッサで、.svelteファイル内のインラインテキストを抽出し、ラッパーなしの翻訳関数にコンパイルします。文字列キーを避け、コンテンツ抽出ロジックをメインアプリケーションのランタイムから完全に分離します。)
53
+ > - Intlayer(コンパイラ / 抽出CLIで、コンポーネントを解析し、型付き辞書を生成し、必要に応じてコードを書き換えて明示的なIntlayerコンテンツを使用できます。目的は、宣言的でフレームワークに依存しないコアを維持しつつ、コンパイラによる高速化を実現することです。)
54
+
55
+ > **宣言的i18nの例:**
56
+ >
57
+ > - i18next / react-i18next / next-i18next(ランタイムJSON辞書と豊富なプラグインエコシステムを使用する成熟した業界標準)
58
+ > - react-intl(FormatJSライブラリの一部で、標準的なICUメッセージ構文と厳密なデータフォーマットに焦点を当てています)
59
+ > - next-intl(Next.js向けに最適化されており、App RouterおよびReact Server Componentsとの統合を提供します)
60
+ > - vue-i18n / @nuxt/i18n(コンポーネントレベルの翻訳ブロックと密接なリアクティビティ統合を提供する標準的なVueエコシステムソリューション)
61
+ > - svelte-i18n(リアクティブなランタイム翻訳のためのSvelteストアの軽量ラッパー)
62
+ > - angular-translate(ビルド時のマージではなく、ランタイムのキー検索に依存するレガシーな動的翻訳ライブラリ)
63
+ > - angular-i18n(ビルド時にXLIFFファイルをテンプレートに直接マージするAngularのネイティブな先行処理アプローチ)
64
+ > - Tolgee(宣言型コードとインコンテキストSDKを組み合わせ、UI上での「クリックして翻訳」編集を可能にする)
65
+ > - Intlayer(コンポーネント単位のアプローチで、コンテンツ宣言ファイルを使用し、ネイティブのツリーシェイキングとTypeScript検証を実現)
66
+
67
+ ## Intlayerコンパイラ
68
+
69
+ **Intlayer**は基本的にコンテンツに対して**宣言的アプローチ**を推奨するソリューションですが、開発の高速化や迅速なプロトタイピングを支援するためにコンパイラも含まれています。
70
+
71
+ Intlayerコンパイラは、React、Vue、Svelteコンポーネントおよびその他のJavaScript/TypeScriptファイルのAST(抽象構文木)を走査します。その役割は、ハードコードされた文字列を検出し、それらを専用の`.content`宣言に抽出することです。
72
+
73
+ > 詳細については、ドキュメントをご覧ください: [Intlayer Compiler Docs](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/compiler.md)
74
+
75
+ ## コンパイラーの魅力(「マジック」アプローチ)
44
76
 
45
77
  この新しいアプローチがトレンドになっている理由があります。開発者にとって、その体験は驚くべきものです。
46
78
 
47
79
  ### 1. スピードと「フロー」
48
80
 
49
- 集中しているときに、変数名(`home_hero_title_v2`)を考えるために立ち止まると、フローが途切れてしまいます。コンパイラアプローチでは、`<p>Welcome back</p>` とタイプしてそのまま進めます。摩擦はゼロです。
81
+ 集中しているときに、意味のある変数名(`home_hero_title_v2`)を考えるために立ち止まると、フローが途切れてしまいます。コンパイラーアプローチでは、`<p>Welcome back</p>` とタイプしてそのまま進めます。摩擦はゼロです。
50
82
 
51
83
  ### 2. レガシー救出ミッション
52
84
 
53
- 5,000のコンポーネントがある巨大なコードベースを引き継ぎ、翻訳が一切ないと想像してください。手動のキー管理システムでこれを後付けするのは数ヶ月に及ぶ悪夢です。コンパイラベースのツールは救出戦略として機能し、手動でファイルに触れることなく数千の文字列を瞬時に抽出します。
85
+ 5,000のコンポーネントを持ち、翻訳がまったくない巨大なコードベースを引き継ぐことを想像してください。これを手動のキー ベースのシステムに後付けするのは、数か月に及ぶ悪夢のような作業です。コンパイラベースのツールは救助戦略として機能し、1つのファイルにも手を触れずに何千もの文字列を即座に抽出します。
54
86
 
55
87
  ### 3. AI時代
56
88
 
57
- これは見逃せない現代的な利点です。AIコーディングアシスタント(CopilotやChatGPTのようなもの)は、標準的なJSX/HTMLを自然に生成します。彼らはあなたの特定の翻訳キーのスキーマを知りません。
89
+ これは見逃せない現代的な利点です。AIコーディングアシスタント(CopilotやChatGPTなど)は、標準的なJSX/HTMLを自然に生成します。彼らはあなたの特定の翻訳キーのスキーマを知りません。
90
+
91
+ - **宣言的:** AIの出力をテキストからキーに置き換えるために書き直す必要があります。
92
+ - **コンパイラ:** AIのコードをコピー&ペーストするだけで、そのまま動作します。
93
+
94
+ ## 現実の確認:「マジック」が危険な理由
95
+
96
+ 「マジック」は魅力的ですが、抽象化の漏れが発生します。ビルドツールに人間の意図を理解させることに依存すると、アーキテクチャの脆弱性が生じます。
97
+
98
+ ### ヒューリスティックな脆弱性(推測ゲーム)
99
+
100
+ コンパイラは何がコンテンツで何がコードかを推測しなければなりません。これにより、ツールと「戦う」ことになるエッジケースが発生します。
101
+
102
+ 以下のシナリオを考えてみてください:
103
+
104
+ - `<span className="active"></span>` は抽出されますか?(文字列ですが、おそらくクラス名です)。
105
+ - `<span status="pending"></span>` は抽出されますか?(これはプロップの値です)。
106
+ - `<span>{"Hello World"}</span>` は抽出されますか?(これはJSの式です)。
107
+ - `<span>Hello {name}. How are you?</span>` は抽出されますか?(補間が複雑です)。
108
+ - `<span aria-label="Image of cat"></span>` は抽出されますか?(アクセシビリティ属性は翻訳が必要です)。
109
+ - `<span data-testid="my-element"></span>` は抽出されますか?(テストIDは翻訳すべきではありません)。
110
+ - `<MyComponent errorMessage="An error occurred" />` は抽出されますか?
111
+ - `<p>This is a paragraph{" "}\n containing multiple lines</p>` は抽出されますか?
112
+ - `<p>{getStatusMessage()}</p>` の関数結果は抽出されますか?
113
+ - `<div>{isLoading ? "The page is loading" : <MyComponent/>} </div>` は抽出されますか?
114
+ - `<span>AX-99</span>` のような製品IDは抽出されますか?
58
115
 
59
- - **宣言的(Declarative):** AIの出力をテキストからキーに置き換えるために書き直す必要があります。
60
- - **コンパイラ(Compiler):** AIのコードをコピー&ペーストするだけで、そのまま動作します。
116
+ 最終的には、アプリケーションのロジックを壊さないように、`// ignore-translation` のような特定のコメントや、`data-compiler-ignore="true"` のような特定のプロパティを追加することになります。
61
117
 
62
- ## 現実の確認:なぜ「マジック」は危険なのか
118
+ ### Intlayerはこの複雑さをどのように処理するのか?
63
119
 
64
- 「マジック」は魅力的ですが、その抽象化は漏れが生じます。ビルドツールに人間の意図を理解させることに依存すると、アーキテクチャの脆弱性が生まれます。
120
+ Intlayerは、フィールドが翻訳のために抽出されるべきかを検出するために混合アプローチを使用しており、誤検出を最小限に抑えようとしています。
65
121
 
66
- ### 1. ヒューリスティックの脆弱性(推測ゲーム)
122
+ 1. **AST解析:** 要素のタイプをチェックします(例:`reactNode`、`label`、または`title`プロパティの区別など)。
123
+ 2. **パターン認識:** 文字列が大文字で始まっているか、スペースを含んでいるかを検出し、それがコード識別子ではなく人間が読めるテキストである可能性が高いことを示唆します。
67
124
 
68
- コンパイラは何がコンテンツで何がコードかを推測しなければなりません。
125
+ ### 動的データのハードリミット
69
126
 
70
- - `className="active"` は翻訳されますか?これは文字列です。
71
- - `status="pending"` は翻訳されますか?
72
- - `<MyComponent errorMessage="An error occurred" />` は翻訳されますか?
73
- - `"AX-99"` のような製品IDは翻訳されますか?
127
+ コンパイラの抽出は**静的解析**に依存しています。安定したIDを生成するために、コード内のリテラル文字列を認識する必要があります。
128
+ もしAPIが `server_error` のようなエラーコード文字列を返す場合、コンパイラはビルド時にその文字列が存在することを認識できないため、コンパイラで翻訳することはできません。動的データのために、別途「ランタイム専用」のシステムを構築する必要があります。
74
129
 
75
- 最終的には、コンパイラと「戦う」ことになり、アプリケーションのロジックを壊さないように特定のコメント(例:`// ignore-translation`)を追加することになります。
130
+ ### チャンク分割の欠如
76
131
 
77
- ### 2. 動的データのハードリミット
132
+ 特定のコンパイラはページごとに翻訳をチャンク分割しません。もしコンパイラが言語ごとに大きなJSONファイル(例:`./lang/en.json`、`./lang/fr.json`など)を生成する場合、単一の訪問ページのためにすべてのページのコンテンツを読み込むことになりがちです。また、コンテンツを使用する各コンポーネントは必要以上に多くのコンテンツでハイドレートされる可能性があり、パフォーマンスの問題を引き起こすことがあります。
78
133
 
79
- コンパイラの抽出は**静的解析**に依存しています。安定したIDを生成するためには、コード内にリテラル文字列が存在している必要があります。
80
- もしAPIが `server_error` のようなエラーコード文字列を返す場合、コンパイラはビルド時にその文字列の存在を認識できないため、翻訳できません。動的データ用に「ランタイム専用」の二次的なシステムを構築せざるを得ません。
134
+ また、翻訳を動的に読み込む際には注意が必要です。これが適切に行われないと、現在の言語に加えてすべての言語のコンテンツが読み込まれてしまいます。
81
135
 
82
- ### 3. 「チャンク爆発」とネットワークウォーターフォール
136
+ > 問題を説明するために、10ページと10言語(すべて100%ユニーク)を持つサイトを考えてみてください。すると、99ページ分の追加コンテンツ(10 × 10 - 1)を読み込むことになります。
83
137
 
84
- ツリーシェイキングを可能にするために、コンパイラツールはしばしばコンポーネントごとに翻訳を分割します。
138
+ ### 「チャンク爆発」とネットワークウォーターフォール
85
139
 
86
- - **結果:** 50個の小さなコンポーネントがある単一のページビューで、**50回の別々のHTTPリクエスト**が小さな翻訳断片に対して発生する可能性があります。HTTP/2を使用していても、これはネットワークのウォーターフォールを生み出し、単一の最適化された言語バンドルを読み込む場合と比べて、アプリケーションの動作が遅く感じられます。
140
+ チャンク問題を解決するために、一部のソリューションはコンポーネント単位、あるいはキー単位でのチャンク分割を提供しています。しかし、この問題は部分的にしか解決されていません。これらのソリューションのセールスポイントはしばしば「コンテンツがツリーシェイクされる」という点にあります。
87
141
 
88
- ### 4. ランタイムのパフォーマンスオーバーヘッド
142
+ 確かに、コンテンツを静的に読み込む場合、ソリューションは未使用のコンテンツをツリーシェイクしますが、それでもすべての言語のコンテンツがアプリケーションと共に読み込まれてしまいます。
89
143
 
90
- 翻訳をリアクティブに(言語を切り替えたときに即座に更新されるように)するために、コンパイラはしばしば*すべての*コンポーネントに状態管理フックを注入します。
144
+ では、なぜ動的に読み込まないのでしょうか?確かにその場合、必要以上のコンテンツを読み込むことになりますが、それにはトレードオフがあります。
91
145
 
92
- - **コスト:** もし5,000件のアイテムのリストをレンダリングすると、テキストのためだけに5,000個の `useState` と `useEffect` フックを初期化することになります。これは、宣言的ライブラリ(通常は単一のContextプロバイダーを使用)が節約するメモリとCPUサイクルを消費します。
146
+ コンテンツを動的に読み込むと、各コンテンツがそれぞれ独立したチャンクに分離され、コンポーネントがレンダリングされるときにのみ読み込まれます。つまり、テキストブロックごとに1つのHTTPリクエストが発生します。ページに1,000のテキストブロックがある場合? サーバーに1,000回のHTTPリクエストが送信されます。そして、ダメージを最小限に抑え、アプリケーションの初回レンダリング時間を最適化するために、複数のSuspense境界やSkeleton Loaderを挿入する必要があります。
93
147
 
94
- ## トラップ:ベンダーロックイン
148
+ > 注意: Next.jsとSSRを使用していても、コンポーネントは読み込み後にハイドレートされるため、HTTPリクエストは依然として発生します。
95
149
 
96
- これはおそらく、コンパイラベースのi18nで最も危険な側面です。
150
+ 解決策は?`i18next`、`next-intl`、または `intlayer` のように、スコープ付きのコンテンツ宣言を可能にするソリューションを採用することです。
97
151
 
98
- 宣言的ライブラリでは、ソースコードに明確な意図が含まれています。キーはあなたが所有しています。ライブラリを切り替える場合は、インポートを変更するだけです。
152
+ > 注意: `i18next` と `next-intl` は、バンドルサイズを最適化するために、各ページごとに名前空間やメッセージのインポートを手動で管理する必要があります。`rollup-plugin-visualizer`(vite)、`@next/bundle-analyzer`(next.js)、または `webpack-bundle-analyzer`(React CRA / Angular / など)などのバンドルアナライザーを使用して、未使用の翻訳でバンドルが汚染されていないかを検出することを推奨します。
99
153
 
100
- コンパイラベースのアプローチでは、**ソースコードは単なる英語のテキストです。** 「翻訳ロジック」はビルドプラグインの設定内にのみ存在します。
101
- もしそのライブラリのメンテナンスが停止したり、ライブラリの機能を超えてしまった場合、あなたは行き詰まります。ソースコードに翻訳キーが一切存在しないため、「イジェクト」することは簡単ではありません。移行するには、アプリケーション全体を手動で書き直す必要があります。
154
+ ### ランタイムパフォーマンスのオーバーヘッド
155
+
156
+ 翻訳をリアクティブにするため(言語を切り替えた際に即座に更新されるように)、コンパイラはしばしばすべてのコンポーネントに状態管理のフックを注入します。
157
+
158
+ - **コスト:** もし5,000件のアイテムのリストをレンダリングすると、テキストのためだけに5,000個の `useState` と `useEffect` フックを初期化することになります。Reactはすべての5,000のコンシューマーを同時に特定し、再レンダリングしなければなりません。これにより大規模な「メインスレッド」ブロックが発生し、切り替え時にUIがフリーズします。これは宣言的ライブラリ(通常は単一のContextプロバイダーを使用する)が節約するメモリとCPUサイクルを大量に消費します。
159
+
160
+ > React以外のフレームワークでも同様の問題があることに注意してください。
161
+
162
+ ## 落とし穴:ベンダーロックイン
163
+
164
+ 翻訳キーの抽出や移行を可能にするi18nソリューションを選ぶ際は注意してください。
165
+
166
+ 宣言型ライブラリの場合、ソースコードには明示的に翻訳の意図が含まれています。これらがキーであり、あなたがそれを管理します。ライブラリを変更したい場合は、通常インポートを更新するだけで済みます。
167
+
168
+ コンパイラ方式では、ソースコードは単なる英語のテキストであり、翻訳ロジックの痕跡はありません。すべてはビルドツールの設定に隠されています。そのプラグインがメンテナンスされなくなったり、別のソリューションに変更したい場合、行き詰まる可能性があります。「イジェクト」する簡単な方法はなく、コード内に使えるキーが存在しないため、新しいライブラリ用にすべての翻訳を再生成する必要があるかもしれません。
169
+
170
+ 一部のソリューションは翻訳生成サービスも提供しています。クレジットがなくなったら?翻訳もなくなります。
171
+
172
+ コンパイラはしばしばテキストをハッシュ化します(例:`"Hello World"` -> `x7f2a`)。あなたの翻訳ファイルは `{ "x7f2a": "Hola Mundo" }` のように見えます。罠は、もしライブラリを切り替えた場合、新しいライブラリは `"Hello World"` を見てそのキーを探しますが、翻訳ファイルはハッシュ(`x7f2a`)でいっぱいなので見つかりません。
173
+
174
+ ### プラットフォーム依存
175
+
176
+ コンパイラベースのアプローチを選択することで、基盤となるプラットフォームに縛られることになります。例えば、特定のコンパイラはすべてのバンドラー(Vite、Turbopack、Metroなど)で利用できるわけではありません。これにより、将来の移行が困難になり、すべてのアプリケーションをカバーするために複数のソリューションを採用する必要があるかもしれません。
102
177
 
103
178
  ## もう一方の側面:宣言的アプローチのリスク
104
179
 
105
- 公平を期すために言うと、従来の宣言的な方法も完璧ではありません。独自の「落とし穴」が存在します。
180
+ 公平に言うと、従来の宣言的な方法も完璧ではありません。独自の「落とし穴」があります。
106
181
 
107
182
  1. **ネームスペース地獄:** どのJSONファイルを読み込むか(`common.json`、`dashboard.json`、`footer.json`など)を手動で管理する必要がよくあります。もし一つでも忘れると、ユーザーは生のキーを目にすることになります。
108
- 2. **過剰フェッチ:** 注意深く設定しないと、初回ロード時にすべてのページのすべての翻訳キーを誤って読み込んでしまい、バンドルサイズが膨れ上がることが非常に簡単に起こります。
109
- 3. **同期のズレ(Sync Drift):** コンポーネントが削除された後も、そのコンポーネントで使われていたキーがJSONファイルに残り続けることがよくあります。翻訳ファイルは無限に増え続け、「ゾンビキー」で溢れてしまいます。
183
+ 2. **過剰フェッチ:** 注意深い設定をしないと、初回ロード時にすべてのページのすべての翻訳キーを誤って読み込んでしまい、バンドルサイズが膨れ上がることが非常に簡単に起こります。
184
+ 3. **同期のズレ:** コンポーネントが削除された後も、そのコンポーネントで使用されていたキーがJSONファイルに残り続けることがよくあります。翻訳ファイルは無限に増え続け、「ゾンビキー」で溢れてしまいます。
110
185
 
111
186
  ## Intlayerの中間的アプローチ
112
187
 
113
- ここで、**Intlayer**のようなツールが革新を試みています。Intlayerは、コンパイラが強力である一方で、暗黙のマジックは危険であることを理解しています。
188
+ ここで、**Intlayer**のようなツールが革新を試みています。Intlayerは、コンパイラは強力である一方で、暗黙のマジックは危険であることを理解しています。
114
189
 
115
- Intlayerは独自の**`transform`コマンド**を提供します。隠れたビルドステップで単にマジックを行うのではなく、実際に**コンポーネントのコードを書き換える**ことができます。テキストをスキャンし、コードベース内で明示的なコンテンツ宣言に置き換えます。
190
+ Intlayerは混合アプローチを提供し、宣言的なコンテンツ管理の利点と、開発時間を節約するためのコンパイラとの互換性の両方を享受できるようにしています。
116
191
 
117
- これにより、両方の利点を得られます:
192
+ そして、たとえIntlayerのコンパイラを使用しなくても、Intlayerは`transform`コマンド(VSCode拡張機能からもアクセス可能)を提供しています。隠れたビルドステップで魔法のように処理するのではなく、実際に**コンポーネントのコードを書き換える**ことができます。テキストをスキャンし、コードベース内で明示的なコンテンツ宣言に置き換えます。
118
193
 
119
- 1. **粒度(Granularity):** 翻訳をコンポーネントの近くに保持できるため(モジュール性とツリーシェイキングが向上します)。
120
- 2. **安全性:** 翻訳は隠れたビルド時のマジックではなく、明示的なコードになります。
121
- 3. **ロックインなし:** コードがリポジトリ内で標準的な宣言的構造に変換されるため、webpackプラグイン内にロジックを隠すことはありません。
194
+ これにより、両方の利点を享受できます:
195
+
196
+ 1. **粒度:** 翻訳をコンポーネントの近くに保持できるため(モジュール性とツリーシェイキングが向上します)。
197
+ 2. **安全性:** 翻訳が隠れたビルド時の魔法ではなく、明示的なコードになります。
198
+ 3. **ロックインなし:** コードがリポジトリ内で宣言的な構造に変換されるため、タブキーを押したり、IDEのCopilotを使ってコンテンツ宣言を簡単に生成できます。Webpackプラグインにロジックを隠すことはありません。
122
199
 
123
200
  ## 結論
124
201
 
125
202
  では、どちらを選ぶべきでしょうか?
126
203
 
127
- **もしあなたがジュニア開発者、ソロ創業者、またはMVPを構築している場合:**
128
- コンパイラベースのアプローチは有効な選択肢です。非常に速く開発を進めることができます。ファイル構造やキーについて心配する必要はありません。ただビルドすれば良いのです。技術的負債は「未来のあなた」の問題です。
204
+ **MVPを構築している場合、または迅速に進めたい場合:**
205
+ コンパイラベースのアプローチは有効な選択肢です。非常に速く進めることができます。ファイル構造やキーについて心配する必要はありません。ただ構築するだけです。技術的負債は「未来のあなた」の問題です。
206
+
207
+ **ジュニア開発者である場合、または最適化を気にしない場合:**
208
+ 手動管理を最小限にしたいなら、コンパイラベースのアプローチが最適でしょう。キーや翻訳ファイルを自分で扱う必要はなく、テキストを書くだけでコンパイラが残りを自動化します。これにより、セットアップの手間や手動ステップに伴う一般的なi18nのミスが減ります。
209
+
210
+ **すでに何千ものコンポーネントを含む既存プロジェクトを国際化する場合:**
211
+ コンパイラベースのアプローチは、ここでは実用的な選択肢となり得ます。初期の抽出フェーズは、数週間から数ヶ月に及ぶ手作業を節約できます。ただし、Intlayerの`transform`コマンドのようなツールを使用することを検討してください。これは文字列を抽出し、明示的な宣言的コンテンツ宣言に変換することができます。これにより、自動化のスピードを得ながら、宣言的アプローチの安全性と移植性を維持できます。両方の利点を享受できるのです。すなわち、長期的なアーキテクチャの負債なしに迅速な初期移行が可能になります。
129
212
 
130
- **もしあなたがプロフェッショナルなエンタープライズグレードのアプリケーションを構築している場合:**
131
- マジックは一般的に悪い考えです。コントロールが必要です。
213
+ **プロフェッショナルでエンタープライズグレードのアプリケーションを構築している場合:**
214
+ マジックは一般的に悪い考えです。制御が必要です。
132
215
 
133
216
  - バックエンドからの動的データを扱う必要があります。
134
217
  - 低スペックデバイスでのパフォーマンスを確保する必要があります(フックの爆発を避けるため)。
135
- - 特定のビルドツールに永遠にロックインされないようにする必要があります。
218
+ - 特定のビルドツールに永遠に縛られないようにする必要があります。
136
219
 
137
- プロフェッショナルなアプリケーションの場合、**宣言的コンテンツ管理**(Intlayerや確立されたライブラリのようなもの)が依然としてゴールドスタンダードです。これにより関心事が分離され、アーキテクチャがクリーンに保たれ、アプリケーションが複数言語を扱う能力が「ブラックボックス」コンパイラの推測に依存しないことが保証されます。
220
+ プロフェッショナルなアプリケーションの場合、**宣言的コンテンツ管理**(Intlayerや確立されたライブラリのようなもの)が依然としてゴールドスタンダードです。これにより関心事が分離され、アーキテクチャがクリーンに保たれ、アプリケーションが複数言語に対応する能力が「ブラックボックス」コンパイラに意図を推測させることに依存しなくなります。
@@ -22,116 +22,199 @@ slugs:
22
22
 
23
23
  10년 이상 웹 애플리케이션을 개발해왔다면, 국제화(i18n)가 항상 마찰 지점이었음을 알 것입니다. 문자열 추출, JSON 파일 관리, 복수형 규칙 처리 등 아무도 하고 싶어 하지 않는 작업인 경우가 많습니다.
24
24
 
25
- 최근에 "컴파일러 기반" i18n 도구의 새로운 물결이 등장하여 이 고통을 사라지게 하겠다고 약속하고 있습니다. 그 제안은 매력적입니다: **컴포넌트에 텍스트만 작성하면 빌드 도구가 나머지를 처리해줍니다.** 키도, 임포트도 없이, 그저 마법처럼요.
25
+ 최근에, 이 고통을 사라지게 하겠다고 약속하는 **"컴파일러 기반" i18n 도구**의 새로운 물결이 등장했습니다. 그 제안은 매력적입니다: **컴포넌트에 텍스트만 작성하면, 빌드 도구가 나머지를 처리해줍니다.** 키도, 임포트도 없이, 그저 마법처럼요.
26
26
 
27
27
  하지만 소프트웨어 공학의 모든 추상화가 그렇듯, 마법에는 대가가 따릅니다.
28
28
 
29
- 이 블로그 글에서는 선언적 라이브러리에서 컴파일러 기반 접근법으로의 전환, 그들이 도입하는 숨겨진 아키텍처 부채, 그리고 왜 "지루한" 방식이 여전히 전문 애플리케이션에 가장 좋은 방법일 수 있는지 탐구할 것입니다.
29
+ 이 블로그 포스트에서는 선언적 라이브러리에서 컴파일러 기반 접근법으로의 전환, 그들이 도입하는 숨겨진 아키텍처 부채, 그리고 왜 "지루한" 방식이 여전히 전문 애플리케이션에 가장 좋은 방법일 수 있는지 탐구할 것입니다.
30
30
 
31
- ## 번역의 간략한 역사
31
+ ## 목차
32
32
 
33
- 우리가 현재 어디에 있는지 이해하려면, 우리가 어디서 시작했는지 돌아봐야 합니다.
33
+ <TOC/>
34
34
 
35
- 2011년에서 2012년경, JavaScript 환경은 지금과 매우 달랐습니다. 우리가 알고 있는 번들러들(Webpack, Vite)은 존재하지 않았거나 초기 단계에 불과했습니다. 우리는 브라우저에서 스크립트를 붙여서 사용하고 있었습니다. 이 시기에 **i18next**와 같은 라이브러리가 탄생했습니다.
35
+ ## 국제화의 간략한 역사
36
+
37
+ 우리가 현재 위치를 이해하려면, 시작점을 되돌아봐야 합니다.
38
+
39
+ 2011년에서 2012년경, JavaScript 환경은 매우 달랐습니다. 우리가 알고 있는 번들러들(Webpack, Vite)은 존재하지 않았거나 초기 단계에 불과했습니다. 우리는 브라우저에서 스크립트를 붙여 사용하는 방식이었습니다. 이 시기에 **i18next**와 같은 라이브러리가 탄생했습니다.
36
40
 
37
41
  이들은 당시 가능한 유일한 방법으로 문제를 해결했습니다: **런타임 사전(Runtime Dictionaries)**. 거대한 JSON 객체를 메모리에 로드하고, 함수가 실행 중에 키를 찾아내는 방식이었습니다. 이는 신뢰할 수 있고 명확하며 어디서나 작동했습니다.
38
42
 
39
- 오늘날로 빠르게 넘어가 보면, 우리는 밀리초 단위로 추상 구문 트리(AST)를 파싱할 수 있는 강력한 컴파일러들(SWC, Rust 기반 번들러)을 갖추고 있습니다.강력한 기술은 새로운 아이디어를 탄생시켰습니다: _왜 우리가 수동으로 키를 관리해야 할까? 컴파일러가 "Hello World"라는 텍스트를 보고 바로 교체해주면 안 될까?_
43
+ 시간이 흘러 오늘날에 이르렀습니다. 우리는 밀리초 단위로 추상 구문 트리(Abstract Syntax Trees, AST)를 파싱할 수 있는 강력한 컴파일러들(SWC, Rust 기반 번들러)을 갖추게 되었습니다.힘은 새로운 아이디어를 탄생시켰습니다: _왜 우리가 수동으로 키를 관리해야 할까? 컴파일러가 "Hello World"라는 텍스트를 보고 바로 교체해주면 안 될까?_
40
44
 
41
45
  이렇게 해서 컴파일러 기반 i18n이 탄생했습니다.
42
46
 
47
+ > **컴파일러 기반 i18n의 예:**
48
+ >
49
+ > - Paraglide (각 메시지를 작은 ESM 함수로 컴파일하는 트리 쉐이킹 모듈로, 번들러가 사용하지 않는 로케일과 키를 자동으로 제거할 수 있습니다. 문자열 키 조회 대신 메시지를 함수로 가져옵니다.)
50
+ > - LinguiJS (`<Trans>`와 같은 메시지 매크로를 빌드 시점에 일반 JS 함수 호출로 재작성하는 매크로-투-함수 컴파일러입니다. 매우 작은 런타임 크기로 ICU/MessageFormat 구문을 제공합니다.)
51
+ > - Lingo.dev (React 애플리케이션 빌드 중에 번역된 콘텐츠를 직접 주입하여 로컬라이제이션 파이프라인을 자동화하는 데 중점을 둡니다. AI를 사용해 번역을 자동 생성할 수 있으며 CI/CD에 직접 통합할 수 있습니다.)
52
+ > - Wuchale (Svelte 우선 전처리기로 .svelte 파일 내 인라인 텍스트를 추출하여 래퍼가 없는 번역 함수로 컴파일합니다. 문자열 키를 피하고, 콘텐츠 추출 로직을 메인 애플리케이션 런타임과 완전히 분리합니다.)
53
+ > - Intlayer (컴파일러 / 추출 CLI로 컴포넌트를 파싱하고, 타입이 지정된 사전을 생성하며, 선택적으로 명시적인 Intlayer 콘텐츠를 사용하도록 코드를 재작성할 수 있습니다. 목표는 선언적이고 프레임워크에 구애받지 않는 코어를 유지하면서 컴파일러를 통한 속도를 높이는 것입니다.)
54
+ >
55
+ > **선언적 i18n의 예:**
56
+ >
57
+ > - i18next / react-i18next / next-i18next (런타임 JSON 사전과 광범위한 플러그인 생태계를 사용하는 성숙한 업계 표준)
58
+ > - react-intl (FormatJS 라이브러리의 일부로, 표준 ICU 메시지 구문과 엄격한 데이터 포맷팅에 중점을 둠)
59
+ > - next-intl (Next.js에 최적화되어 App Router 및 React Server Components와 통합됨)
60
+ > - vue-i18n / @nuxt/i18n (컴포넌트 수준의 번역 블록과 강력한 반응성 통합을 제공하는 표준 Vue 생태계 솔루션)
61
+ > - svelte-i18n (반응형 런타임 번역을 위한 Svelte 스토어를 감싼 경량 래퍼)
62
+ > - angular-translate (빌드 타임 병합 대신 런타임 키 조회에 의존하는 레거시 동적 번역 라이브러리)
63
+ > - angular-i18n (빌드 시 XLIFF 파일을 템플릿에 직접 병합하는 Angular의 네이티브 선행 처리 방식)
64
+ > - Tolgee (선언적 코드를 UI 내에서 직접 "클릭하여 번역" 편집이 가능한 인컨텍스트 SDK와 결합)
65
+ > - Intlayer (컴포넌트별 접근 방식으로, 네이티브 트리 쉐이킹과 TypeScript 검증을 가능하게 하는 콘텐츠 선언 파일 사용)
66
+
67
+ ## Intlayer 컴파일러
68
+
69
+ **Intlayer**는 본질적으로 콘텐츠에 대해 **선언적 접근법**을 권장하는 솔루션이지만, 개발 속도를 높이거나 빠른 프로토타이핑을 돕기 위해 컴파일러를 포함하고 있습니다.
70
+
71
+ Intlayer 컴파일러는 React, Vue, Svelte 컴포넌트뿐만 아니라 기타 JavaScript/TypeScript 파일의 AST(추상 구문 트리)를 탐색합니다. 그 역할은 하드코딩된 문자열을 감지하여 전용 `.content` 선언으로 추출하는 것입니다.
72
+
73
+ > 자세한 내용은 문서를 참조하세요: [Intlayer Compiler Docs](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ko/compiler.md)
74
+
43
75
  ## 컴파일러의 매력 (일명 "마법" 접근법)
44
76
 
45
- 이 새로운 접근법이 유행하는 데는 이유가 있습니다. 개발자 입장에서 경험이 놀랍도록 좋기 때문입니다.
77
+ 이 새로운 접근법이 유행하는 데는 이유가 있습니다. 개발자 입장에서 경험이 놀랍게 느껴집니다.
46
78
 
47
79
  ### 1. 속도와 "흐름"
48
80
 
49
- 작업에 몰입해 있을 때 변수 이름(`home_hero_title_v2`)을 생각하느라 멈추면 흐름이 끊깁니다. 컴파일러 접근법에서는 `<p>Welcome back</p>`를 입력하고 계속 진행할 수 있습니다. 마찰이 전혀 없습니다.
81
+ 집중하고 있을 때 의미 있는 변수 이름(`home_hero_title_v2`)을 고민하느라 멈추면 흐름이 끊깁니다. 컴파일러 방식을 사용하면 `<p>Welcome back</p>`를 입력하고 계속 진행할 수 있습니다. 마찰이 전혀 없습니다.
50
82
 
51
- ### 2. 레거시 구조 구출 작전
83
+ ### 2. 레거시 구조 구출 미션
52
84
 
53
- 5,000개의 컴포넌트가 있지만 번역이 전혀 없는 거대한 코드베이스를 상속받았다고 상상해 보세요. 수동 키 기반 시스템으로 이를 개조하는 것은 몇 달이 걸리는 악몽 같은 작업입니다. 컴파일러 기반 도구는 구출 전략으로 작동하여, 단 한 파일도 수동으로 건드리지 않고 수천 개의 문자열을 즉시 추출합니다.
85
+ 거대한 코드베이스를 상속받아 5,000개의 컴포넌트가 있지만 번역이 전혀 없는 상황을 상상해 보세요. 수동 키 기반 시스템으로 이를 개조하는 것은 몇 달이 걸리는 악몽과 같습니다. 컴파일러 기반 도구는 구출 전략으로 작동하여, 단 한 파일도 수동으로 건드리지 않고 수천 개의 문자열을 즉시 추출합니다.
54
86
 
55
87
  ### 3. AI 시대
56
88
 
57
89
  이것은 우리가 간과해서는 안 될 현대적인 이점입니다. AI 코딩 어시스턴트(예: Copilot 또는 ChatGPT)는 자연스럽게 표준 JSX/HTML을 생성합니다. 이들은 여러분의 특정 번역 키 스키마를 알지 못합니다.
58
90
 
59
- - **선언적 방식(Declarative):** AI의 출력을 텍스트에서 키로 교체하기 위해 다시 작성해야 합니다.
60
- - **컴파일러 방식(Compiler):** AI 코드를 복사-붙여넣기만 하면 바로 작동합니다.
91
+ - **선언적(Declarative):** AI의 출력을 텍스트에서 키로 교체하기 위해 다시 작성해야 합니다.
92
+ - **컴파일러(Compiler):** AI 코드를 복사-붙여넣기만 하면 바로 작동합니다.
61
93
 
62
94
  ## 현실 점검: 왜 "마법"이 위험한가
63
95
 
64
- "마법" 매력적이지만, 추상화가 새어나갑니다. 빌드 도구가 인간의 의도를 이해하도록 의존하는 것은 아키텍처적 취약성을 초래합니다.
96
+ "마법" 같은 기능이 매력적이지만, 추상화가 누수됩니다. 빌드 도구에 인간의 의도를 이해하도록 의존하는 것은 아키텍처적 취약성을 초래합니다.
97
+
98
+ ### 휴리스틱 취약성 (추측 게임)
99
+
100
+ 컴파일러는 무엇이 콘텐츠이고 무엇이 코드인지 추측해야 합니다. 이로 인해 도구와 "싸우게" 되는 엣지 케이스가 발생합니다.
101
+
102
+ 다음 시나리오를 고려해 보십시오:
103
+
104
+ - `<span className="active"></span>`가 추출되나요? (문자열이지만, 아마도 클래스입니다).
105
+ - `<span status="pending"></span>`가 추출되나요? (프로퍼티 값입니다).
106
+ - `<span>{"Hello World"}</span>`가 추출되나요? (JS 표현식입니다).
107
+ - `<span>Hello {name}. How are you?</span>`가 추출되나요? (보간법이 복잡합니다).
108
+ - `<span aria-label="Image of cat"></span>`가 추출되나요? (접근성 속성은 번역이 필요합니다).
109
+ - `<span data-testid="my-element"></span>`는 추출되나요? (테스트 ID는 번역해서는 안 됩니다).
110
+ - `<MyComponent errorMessage="An error occurred" />`는 추출되나요?
111
+ - `<p>This is a paragraph{" "}\n containing multiple lines</p>`는 추출되나요?
112
+ - `<p>{getStatusMessage()}</p>` 함수 결과는 추출되나요?
113
+ - `<div>{isLoading ? "The page is loading" : <MyComponent/>} </div>`는 추출되나요?
114
+ - `<span>AX-99</span>`와 같은 제품 ID는 추출되나요?
115
+
116
+ 결국 애플리케이션 로직이 깨지지 않도록 특정 주석(`// ignore-translation` 같은)이나 특정 props(`data-compiler-ignore="true"` 같은)를 추가하게 됩니다.
117
+
118
+ ### Intlayer는 이 복잡성을 어떻게 처리하나요?
119
+
120
+ Intlayer는 필드가 번역을 위해 추출되어야 하는지 감지하기 위해 혼합 방식을 사용하며, 오탐(false positives)을 최소화하려고 시도합니다:
121
+
122
+ 1. **AST 분석:** 요소 타입을 확인합니다 (예: `reactNode`, `label`, 또는 `title` prop을 구분).
123
+ 2. **패턴 인식:** 문자열이 대문자로 시작하거나 공백을 포함하는지 감지하여, 코드 식별자보다는 사람이 읽을 수 있는 텍스트일 가능성이 높다고 판단합니다.
124
+
125
+ ### 동적 데이터의 한계
65
126
 
66
- ### 1. 휴리스틱 취약성 (추측 게임)
127
+ 컴파일러 추출은 **정적 분석**에 의존합니다. 안정적인 ID를 생성하기 위해 코드 내에서 리터럴 문자열을 반드시 확인해야 합니다.
128
+ API가 `server_error`와 같은 에러 코드 문자열을 반환하는 경우, 컴파일러는 빌드 시점에 해당 문자열이 존재하는지 알 수 없기 때문에 컴파일러로 번역할 수 없습니다. 이 경우 동적 데이터 전용의 별도 "런타임 전용" 시스템을 구축해야 합니다.
67
129
 
68
- 컴파일러는 무엇이 콘텐츠이고 무엇이 코드인지 추측해야 합니다.
130
+ ### 청킹 부족
69
131
 
70
- - `className="active"`는 번역되나요? 이것은 문자열입니다.
71
- - `status="pending"`는 번역되나요?
72
- - `<MyComponent errorMessage="An error occurred" />`가 번역되나요?
73
- - `"AX-99"`와 같은 제품 ID가 번역되나요?
132
+ 일부 컴파일러는 페이지별로 번역을 청킹하지 않습니다. 만약 컴파일러가 언어별로 큰 JSON 파일(e.g., `./lang/en.json`, `./lang/fr.json` 등)을 생성한다면, 단일 방문 페이지에 대해 모든 페이지의 콘텐츠를 로드하게 될 가능성이 큽니다. 또한, 콘텐츠를 사용하는 각 컴포넌트는 필요 이상으로 많은 콘텐츠로 하이드레이션되어 성능 문제를 일으킬 수 있습니다.
74
133
 
75
- 결국 컴파일러와 "싸우게" 되며, 애플리케이션 로직이 깨지지 않도록 특정 주석(예: `// ignore-translation`)을 추가하게 됩니다.
134
+ 번역을 동적으로 로드할 때도 주의해야 합니다. 만약 이렇게 하지 않으면, 현재 언어뿐만 아니라 모든 언어의 콘텐츠를 함께 로드하게 됩니다.
76
135
 
77
- ### 2. 동적 데이터의 한계
136
+ > 문제를 설명하기 위해, 10개의 페이지와 10개의 언어(모두 100% 고유한 경우)를 가진 사이트를 생각해 보세요. 이 경우 99개의 추가 페이지 콘텐츠를 로드하게 됩니다 (10 × 10 - 1).
78
137
 
79
- 컴파일러 추출은 **정적 분석**에 의존합니다. 안정적인 ID를 생성하려면 코드 내에 리터럴 문자열이 보여야 합니다.
80
- API가 `server_error`와 같은 에러 코드 문자열을 반환하면, 컴파일러가 빌드 시점에 해당 문자열이 존재하는지 알지 못하기 때문에 번역할 수 없습니다. 동적 데이터 전용의 별도 "런타임 전용" 시스템을 구축해야만 합니다.
138
+ ### "청크 폭발"과 네트워크 워터폴
81
139
 
82
- ### 3. "청크 폭발" 네트워크 워터폴
140
+ 청킹 문제를 해결하기 위해, 일부 솔루션은 컴포넌트별 또는 키별로 청크를 나누는 방식을 제공합니다. 하지만 이 문제는 부분적으로만 해결됩니다. 이러한 솔루션의 판매 포인트는 종종 "당신의 콘텐츠가 트리 쉐이킹된다" 점입니다.
83
141
 
84
- 트리 쉐이킹을 허용하기 위해, 컴파일러 도구는 종종 컴포넌트별로 번역을 분할합니다.
142
+ 실제로 콘텐츠를 정적으로 로드하면, 솔루션은 사용하지 않는 콘텐츠를 트리 쉐이킹하지만, 여전히 모든 언어의 콘텐츠가 애플리케이션과 함께 로드됩니다.
85
143
 
86
- - **결과:** 50개의 작은 컴포넌트가 있는 단일 페이지 뷰는 작은 번역 조각에 대해 **50개의 별도 HTTP 요청**을 발생시킬 수 있습니다. HTTP/2를 사용하더라도, 이는 네트워크 워터폴을 생성하여 단일 최적화된 언어 번들을 로드하는 것보다 애플리케이션이 느리게 느껴지게 만듭니다.
144
+ 그렇다면 동적으로 로드하지 않을까요? 네, 경우 필요한 것보다 많은 콘텐츠를 로드하게 되지만, 이는 단점이 전혀 없는 것은 아닙니다.
87
145
 
88
- ### 4. 런타임 성능 오버헤드
146
+ 콘텐츠를 동적으로 로드하면 콘텐츠 조각이 자체 청크로 분리되어 해당 컴포넌트가 렌더링될 때만 로드됩니다. 이는 텍스트 블록당 하나의 HTTP 요청을 하게 된다는 의미입니다. 페이지에 1,000개의 텍스트 블록이 있다면? → 서버에 1,000개의 HTTP 요청을 보내게 됩니다. 그리고 피해를 줄이고 애플리케이션의 첫 렌더링 시간을 최적화하려면 여러 개의 Suspense 경계나 Skeleton Loader를 삽입해야 합니다.
89
147
 
90
- 번역을 반응형으로 만들어(언어를 전환할 즉시 업데이트되도록) 컴파일러는 종종 _모든_ 컴포넌트에 상태 관리 훅을 주입합니다.
148
+ > 참고: Next.js와 SSR을 사용하더라도 컴포넌트는 로딩 후에 하이드레이션되므로 HTTP 요청은 여전히 발생합니다.
91
149
 
92
- - **비용:** 5,000개의 아이템 리스트를 렌더링하면, 텍스트를 위해 5,000개의 `useState`와 `useEffect` 훅을 초기화하게 됩니다. 이는 선언형 라이브러리(일반적으로 단일 Context 프로바이더를 사용하는)가 절약하는 메모리와 CPU 사이클을 낭비하는 셈입니다.
150
+ 해결책? `i18next`, `next-intl`, 또는 `intlayer`와 같이 범위가 지정된 콘텐츠 선언을 있는 솔루션을 채택하는 것입니다.
93
151
 
94
- ## 함정: 공급업체 종속성 (Vendor Lock-in)
152
+ > 참고: `i18next`와 `next-intl`은 번들 크기를 최적화하기 위해 각 페이지마다 네임스페이스/메시지 임포트를 수동으로 관리해야 합니다. `rollup-plugin-visualizer`(vite), `@next/bundle-analyzer`(next.js), 또는 `webpack-bundle-analyzer`(React CRA / Angular 등)와 같은 번들 분석기를 사용하여 사용하지 않는 번역으로 번들이 오염되고 있는지 감지해야 합니다.
95
153
 
96
- 이는 컴파일러 기반 i18n에서 가장 위험한 측면이라고 할 수 있습니다.
154
+ ### 런타임 성능 오버헤드
97
155
 
98
- 선언형 라이브러리에서는 소스 코드에 명확한 의도가 포함되어 있습니다. 키를 직접 소유합니다. 라이브러리를 변경할 때는 단순히 import만 바꾸면 됩니다.
156
+ 번역을 반응형으로 만들어(언어를 전환할 즉시 업데이트되도록) 컴파일러가 종종 모든 컴포넌트에 상태 관리 훅을 주입합니다.
99
157
 
100
- 컴파일러 기반 접근법에서는 **소스 코드가 단지 영어 텍스트일 뿐입니다.** "번역 로직" 빌드 플러그인의 설정 안에만 존재합니다.
101
- 만약 그 라이브러리가 더 이상 유지되지 않거나, 사용자가 그 라이브러리를 벗어나고 싶어도, 당신은 꼼짝할 수 없습니다. 소스 코드에 번역 키가 전혀 없기 때문에 쉽게 "eject"할 수 없습니다. 마이그레이션을 위해 애플리케이션 전체를 수동으로 다시 작성해야 할 것입니다.
158
+ - **비용:** 5,000개의 아이템 리스트를 렌더링할 경우, 텍스트만을 위해 5,000개의 `useState`와 `useEffect` 훅을 초기화하게 됩니다. React는 모든 5,000명의 소비자를 동시에 식별하고 다시 렌더링해야 합니다. 이로 인해 대규모의 "메인 스레드" 블록이 발생하여 언어 전환 시 UI가 멈추게 됩니다. 이는 선언형 라이브러리(일반적으로 단일 Context 제공자를 사용하는)가 절약하는 메모리와 CPU 사이클을 소모합니다.
102
159
 
103
- ## 다른 측면: 선언적 접근 방식의 위험
160
+ > React 외의 다른 프레임워크에서도 유사한 문제가 발생한다는 점에 유의하세요.
161
+
162
+ ## 함정: 벤더 락인(Vendor Lock-in)
163
+
164
+ 번역 키의 추출 또는 마이그레이션이 가능한 i18n 솔루션을 선택할 때 주의하세요.
165
+
166
+ 선언적 라이브러리의 경우, 소스 코드는 명확하게 번역 의도를 포함합니다: 이것이 바로 키이며, 사용자가 이를 제어합니다. 라이브러리를 변경하고 싶다면 일반적으로 import만 업데이트하면 됩니다.
167
+
168
+ 컴파일러 접근법에서는 소스 코드가 단순한 영어 텍스트일 수 있으며, 번역 로직의 흔적이 없습니다: 모든 것이 빌드 도구 설정에 숨겨져 있습니다. 만약 해당 플러그인이 유지보수되지 않거나 솔루션을 변경하고 싶다면, 꼼짝 못할 수 있습니다. “eject”할 쉬운 방법이 없으며, 코드 내에 사용할 수 있는 키가 없고, 새로운 라이브러리를 위해 모든 번역을 다시 생성해야 할 수도 있습니다.
169
+
170
+ 일부 솔루션은 번역 생성 서비스를 제공하기도 합니다. 크레딧이 없으면? 번역도 없습니다.
171
+
172
+ 컴파일러는 종종 텍스트를 해시합니다(예: `"Hello World"` -> `x7f2a`). 번역 파일은 `{ "x7f2a": "Hola Mundo" }`와 같이 보입니다. 함정: 라이브러리를 변경하면 새 라이브러리는 `"Hello World"`를 보고 해당 키를 찾으려고 합니다. 하지만 번역 파일이 해시(`x7f2a`)로 가득 차 있기 때문에 찾을 수 없습니다.
173
+
174
+ ### 플랫폼 종속성
175
+
176
+ 컴파일러 기반 접근 방식을 선택하면 기본 플랫폼에 종속되게 됩니다. 예를 들어, 특정 컴파일러는 모든 번들러(Vite, Turbopack, Metro 등)에서 사용할 수 없습니다. 이로 인해 향후 마이그레이션이 어려워질 수 있으며, 모든 애플리케이션을 커버하기 위해 여러 솔루션을 채택해야 할 수도 있습니다.
177
+
178
+ ## 반대편: 선언적 접근 방식의 위험
104
179
 
105
180
  공정하게 말하면, 전통적인 선언적 방식도 완벽하지 않습니다. 자체적인 "함정"이 존재합니다.
106
181
 
107
- 1. **네임스페이스 지옥:** 어떤 JSON 파일을 로드할지 (`common.json`, `dashboard.json`, `footer.json`) 수동으로 관리해야 하는 경우가 많습니다. 하나라도 잊으면 사용자는 원시 키를 보게 됩니다.
108
- 2. **과도한 페칭:** 신중한 설정 없이 초기 로드 시 모든 페이지의 모든 번역 키를 실수로 불러와 번들 크기가 불필요하게 커질 수 있습니다.
109
- 3. **동기화 이탈(Sync Drift):** 컴포넌트가 삭제된 후에도 해당 키가 JSON 파일에 남아 있는 경우가 흔합니다. 이로 인해 번역 파일이 무한히 커지며 "좀비 키"로 가득 차게 됩니다.
182
+ 1. **네임스페이스 지옥:** 어떤 JSON 파일을 로드할지 수동으로 관리해야 하는 경우가 많습니다(`common.json`, `dashboard.json`, `footer.json`). 하나라도 잊으면 사용자는 원시 키를 보게 됩니다.
183
+ 2. **과도한 로드:** 신중한 설정 없이 초기 로드 시 모든 페이지의 모든 번역 키를 실수로 불러오는 일이 매우 쉽기 때문에 번들 크기가 불필요하게 커질 수 있습니다.
184
+ 3. **동기화 불일치:** 컴포넌트가 삭제된 후에도 해당 키가 JSON 파일에 남아 있는 경우가 흔합니다. 이로 인해 번역 파일이 무한히 커지며 "좀비 키"로 가득 차게 됩니다.
110
185
 
111
186
  ## Intlayer의 중간 지점
112
187
 
113
- 이것이 바로 **Intlayer**와 같은 도구들이 혁신을 시도하는 부분입니다. Intlayer는 컴파일러가 강력하지만, 암묵적인 마법은 위험하다는 점을 이해합니다.
188
+ 이것이 바로 **Intlayer**와 같은 도구들이 혁신을 시도하는 이유입니다. Intlayer는 컴파일러가 강력하지만 암묵적인 마법은 위험하다는 점을 이해합니다.
114
189
 
115
- Intlayer는 독특한 **`transform` 명령어**를 제공합니다. 숨겨진 빌드 단계에서 마법처럼 처리하는 대신, 실제로 **컴포넌트 코드를 재작성**할 있습니다. 텍스트를 스캔하여 코드베이스 내에서 명시적인 콘텐츠 선언으로 대체합니다.
190
+ Intlayer는 선언적 콘텐츠 관리의 장점과 개발 시간을 절약할 있는 컴파일러 호환성을 모두 누릴있는 혼합 방식을 제공합니다.
116
191
 
117
- 이로써 세계의 장점을 모두 누릴있습니다:
192
+ 그리고 Intlayer 컴파일러를 사용하지 않더라도, Intlayer는 `transform` 명령어를 제공합니다 (VSCode 확장 기능을 통해서도 접근 가능). 숨겨진 빌드 단계에서 마법처럼 처리하는 대신, 실제로 **컴포넌트 코드를 재작성**할 있습니다. 텍스트를 스캔하여 코드베이스 내에서 명시적인 콘텐츠 선언으로 대체합니다.
118
193
 
119
- 1. **세분성(Granularity):** 번역을 컴포넌트 가까이에 유지하여 모듈화와 트리 쉐이킹(tree-shaking)을 개선합니다.
120
- 2. **안전성:** 번역이 숨겨진 빌드 타임 마법이 아니라 명시적인 코드가 됩니다.
121
- 3. **락인 방지:** 코드가 리포지토리 내에서 표준 선언적 구조로 변환되기 때문에 webpack 플러그인에 로직을 숨기지 않습니다.
194
+ 이로써 가지 접근법의 장점을 모두 누릴 있습니다:
195
+
196
+ 1. **세분화:** 번역을 컴포넌트 가까이에 유지하여 모듈화와 트리 쉐이킹을 향상시킵니다.
197
+ 2. **안전성:** 번역이 숨겨진 빌드 타임 마법이 아닌 명시적인 코드가 됩니다.
198
+ 3. **락인 없음:** 코드가 선언적 구조로 변환되어, 탭 키를 누르거나 IDE의 코파일럿을 사용해 콘텐츠 선언을 쉽게 생성할 수 있으며, webpack 플러그인에 로직을 숨기지 않습니다.
122
199
 
123
200
  ## 결론
124
201
 
125
- 그렇다면 어떤 방식을 선택해야 할까요?
202
+ 그렇다면, 어떤 방식을 선택해야 할까요?
203
+
204
+ **MVP를 구축 중이거나 빠르게 진행하고 싶다면:**
205
+ 컴파일러 기반 접근법이 유효한 선택입니다. 매우 빠르게 작업할 수 있습니다. 파일 구조나 키에 대해 걱정할 필요가 없습니다. 그냥 빌드하면 됩니다. 기술 부채는 "미래의 당신"이 해결할 문제입니다.
206
+
207
+ **주니어 개발자이거나 최적화에 신경 쓰지 않는다면:**
208
+ 수동 관리가 가장 적은 방식을 원한다면, 컴파일러 기반 접근법이 아마도 최선입니다. 키나 번역 파일을 직접 다룰 필요 없이 텍스트만 작성하면 컴파일러가 나머지를 자동화합니다. 이는 설정 노력을 줄이고 수동 단계에서 발생하는 일반적인 i18n 실수를 감소시킵니다.
126
209
 
127
- **당신이 주니어 개발자이거나, 1인 창업자이거나, MVP를 만들고 있다면:**
128
- 컴파일러 기반 접근법도 유효한 선택입니다. 매우 빠르게 개발할 수 있습니다. 파일 구조나 키에 대해 걱정할 필요가 없습니다. 그냥 개발하면 됩니다. 기술 부채는 "미래의 당신"해결할 문제입니다.
210
+ **이미 수천 개의 컴포넌트를 리팩토링해야 하는 기존 프로젝트를 국제화하는 경우:**
211
+ 컴파일러 기반 접근 방식은 여기서 실용적인 선택이 수 있습니다. 초기 추출 단계는 수주 또는 수개월에 걸친 수작업을 절약할 있습니다. 그러나 Intlayer의 `transform` 명령어와 같은 도구를 사용하는 것을 고려해 보세요. 도구는 문자열을 추출하고 명시적인 선언적 콘텐츠 선언으로 변환할 수 있습니다. 이를 통해 선언적 접근 방식의 안전성과 이식성을 유지하면서 자동화의 속도를 얻을 수 있습니다. 두 가지 장점을 모두 누릴 수 있습니다: 장기적인 아키텍처 부채 없이 빠른 초기 마이그레이션.
129
212
 
130
- **당신이 전문적이고 엔터프라이즈급 애플리케이션을 개발 중이라면:**
131
- 마법 같은 접근법은 일반적으로 좋지 않습니다. 제어가 필요합니다.
213
+ **전문적이고 엔터프라이즈급 애플리케이션을 구축하는 경우:**
214
+ 마법 같은 방법은 일반적으로 좋지 않습니다. 제어가 필요합니다.
132
215
 
133
216
  - 백엔드에서 오는 동적 데이터를 처리해야 합니다.
134
- - 저사양 기기에서의 성능을 보장해야 합니다 (hook 폭발 방지).
217
+ - 저사양 장치에서의 성능을 보장해야 합니다 ( 폭발 방지).
135
218
  - 특정 빌드 도구에 영원히 종속되지 않도록 해야 합니다.
136
219
 
137
- 전문적인 애플리케이션의 경우, **선언적 콘텐츠 관리**(Intlayer 검증된 라이브러리와 같은) 여전히 최고의 표준입니다. 이는 관심사를 분리하고 아키텍처를 깔끔하게 유지하며, 애플리케이션이 여러 언어를 지원하는 능력이 "블랙박스" 컴파일러가 의도를 추측하는 것에 의존하지 않도록 보장합니다.
220
+ 전문적인 애플리케이션의 경우, **선언적 콘텐츠 관리**(Intlayer 또는 검증된 라이브러리와 같은) 여전히 최고의 표준입니다. 이는 관심사를 분리하고 아키텍처를 깔끔하게 유지하며, 애플리케이션이 여러 언어를 지원하는 능력이 "블랙박스" 컴파일러가 의도를 추측하는 것에 의존하지 않도록 보장합니다.