@intlayer/docs 5.7.6 → 5.7.8
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/dist/cjs/generated/docs.entry.cjs +44 -238
- package/dist/cjs/generated/docs.entry.cjs.map +1 -1
- package/dist/esm/generated/docs.entry.mjs +44 -238
- package/dist/esm/generated/docs.entry.mjs.map +1 -1
- package/dist/types/generated/docs.entry.d.ts +1 -2
- package/dist/types/generated/docs.entry.d.ts.map +1 -1
- package/docs/ar/CI_CD.md +67 -41
- package/docs/ar/intlayer_with_tanstack.md +457 -0
- package/docs/ar/packages/next-intlayer/index.md +0 -1
- package/docs/ar/packages/react-intlayer/index.md +0 -1
- package/docs/de/CI_CD.md +63 -37
- package/docs/de/intlayer_with_tanstack.md +458 -0
- package/docs/de/packages/next-intlayer/index.md +0 -1
- package/docs/de/packages/react-intlayer/index.md +0 -1
- package/docs/en/CI_CD.md +51 -27
- package/docs/en/intlayer_with_tanstack.md +452 -0
- package/docs/en/packages/next-intlayer/index.md +0 -1
- package/docs/en/packages/react-intlayer/index.md +0 -1
- package/docs/en-GB/CI_CD.md +58 -32
- package/docs/en-GB/intlayer_with_tanstack.md +457 -0
- package/docs/en-GB/packages/next-intlayer/index.md +0 -1
- package/docs/en-GB/packages/react-intlayer/index.md +0 -1
- package/docs/es/CI_CD.md +68 -42
- package/docs/es/intlayer_with_tanstack.md +435 -0
- package/docs/es/packages/next-intlayer/index.md +0 -1
- package/docs/es/packages/react-intlayer/index.md +0 -1
- package/docs/fr/intlayer_with_tanstack.md +435 -0
- package/docs/fr/packages/next-intlayer/index.md +0 -1
- package/docs/fr/packages/react-intlayer/index.md +0 -1
- package/docs/hi/CI_CD.md +69 -44
- package/docs/hi/intlayer_with_tanstack.md +438 -0
- package/docs/hi/packages/next-intlayer/index.md +0 -1
- package/docs/hi/packages/react-intlayer/index.md +0 -1
- package/docs/it/CI_CD.md +67 -41
- package/docs/it/intlayer_with_tanstack.md +457 -0
- package/docs/it/packages/next-intlayer/index.md +0 -1
- package/docs/it/packages/react-intlayer/index.md +0 -1
- package/docs/ja/CI_CD.md +67 -41
- package/docs/ja/intlayer_with_tanstack.md +457 -0
- package/docs/ja/packages/next-intlayer/index.md +0 -1
- package/docs/ja/packages/react-intlayer/index.md +0 -1
- package/docs/ko/CI_CD.md +63 -37
- package/docs/ko/intlayer_with_tanstack.md +457 -0
- package/docs/ko/packages/next-intlayer/index.md +0 -1
- package/docs/ko/packages/react-intlayer/index.md +0 -1
- package/docs/pt/CI_CD.md +67 -41
- package/docs/pt/intlayer_with_tanstack.md +457 -0
- package/docs/pt/packages/next-intlayer/index.md +0 -1
- package/docs/pt/packages/react-intlayer/index.md +0 -1
- package/docs/ru/CI_CD.md +70 -44
- package/docs/ru/intlayer_with_tanstack.md +458 -0
- package/docs/ru/packages/next-intlayer/index.md +0 -1
- package/docs/ru/packages/react-intlayer/index.md +0 -1
- package/docs/zh/CI_CD.md +62 -36
- package/docs/zh/intlayer_with_tanstack.md +435 -0
- package/docs/zh/packages/next-intlayer/index.md +0 -1
- package/docs/zh/packages/react-intlayer/index.md +0 -1
- package/package.json +9 -9
- package/src/generated/docs.entry.ts +44 -238
- package/docs/ar/packages/next-intlayer/useIntlayerAsync.md +0 -237
- package/docs/ar/packages/react-intlayer/useIntlayerAsync.md +0 -252
- package/docs/de/packages/next-intlayer/useIntlayerAsync.md +0 -262
- package/docs/de/packages/react-intlayer/useIntlayerAsync.md +0 -256
- package/docs/en/packages/next-intlayer/useIntlayerAsync.md +0 -239
- package/docs/en/packages/react-intlayer/useIntlayerAsync.md +0 -254
- package/docs/en-GB/packages/next-intlayer/useIntlayerAsync.md +0 -237
- package/docs/en-GB/packages/react-intlayer/useIntlayerAsync.md +0 -257
- package/docs/es/packages/next-intlayer/useIntlayerAsync.md +0 -240
- package/docs/es/packages/react-intlayer/useIntlayerAsync.md +0 -276
- package/docs/fr/packages/next-intlayer/useIntlayerAsync.md +0 -238
- package/docs/fr/packages/react-intlayer/useIntlayerAsync.md +0 -252
- package/docs/hi/packages/next-intlayer/useIntlayerAsync.md +0 -237
- package/docs/hi/packages/react-intlayer/useIntlayerAsync.md +0 -252
- package/docs/it/packages/next-intlayer/useIntlayerAsync.md +0 -237
- package/docs/it/packages/react-intlayer/useIntlayerAsync.md +0 -252
- package/docs/ja/packages/next-intlayer/useIntlayerAsync.md +0 -237
- package/docs/ja/packages/react-intlayer/useIntlayerAsync.md +0 -268
- package/docs/ko/packages/next-intlayer/useIntlayerAsync.md +0 -260
- package/docs/ko/packages/react-intlayer/useIntlayerAsync.md +0 -271
- package/docs/pt/packages/next-intlayer/useIntlayerAsync.md +0 -238
- package/docs/pt/packages/react-intlayer/useIntlayerAsync.md +0 -252
- package/docs/ru/packages/next-intlayer/useIntlayerAsync.md +0 -237
- package/docs/ru/packages/react-intlayer/useIntlayerAsync.md +0 -252
- package/docs/zh/packages/next-intlayer/useIntlayerAsync.md +0 -239
- package/docs/zh/packages/react-intlayer/useIntlayerAsync.md +0 -257
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2025-08-11
|
|
3
|
+
updatedAt: 2025-08-11
|
|
4
|
+
title: TanStack Start (React)에서 Intlayer 시작하기
|
|
5
|
+
description: Intlayer를 사용하여 TanStack Start 앱에 i18n 추가하기 — 컴포넌트 수준 사전, 지역화된 URL, SEO 친화적 메타데이터.
|
|
6
|
+
keywords:
|
|
7
|
+
- 국제화
|
|
8
|
+
- 문서
|
|
9
|
+
- Intlayer
|
|
10
|
+
- TanStack Start
|
|
11
|
+
- TanStack Router
|
|
12
|
+
- React
|
|
13
|
+
- i18n
|
|
14
|
+
- 자바스크립트
|
|
15
|
+
slugs:
|
|
16
|
+
- doc
|
|
17
|
+
- environment
|
|
18
|
+
- tanstack-start
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# Intlayer와 TanStack Start (React)로 국제화(i18n) 시작하기
|
|
22
|
+
|
|
23
|
+
## Intlayer란 무엇인가요?
|
|
24
|
+
|
|
25
|
+
**Intlayer**는 React 앱을 위한 오픈 소스 i18n 툴킷입니다. 다음을 제공합니다:
|
|
26
|
+
|
|
27
|
+
- **TypeScript 안전성을 갖춘 컴포넌트 로컬 사전**.
|
|
28
|
+
- **동적 메타데이터 및 라우트** (SEO 준비 완료).
|
|
29
|
+
- **런타임 로케일 전환** (로케일 감지/유지 도우미 포함).
|
|
30
|
+
- **Vite 플러그인** 빌드 타임 변환 및 개발자 경험 향상용.
|
|
31
|
+
|
|
32
|
+
이 가이드는 Intlayer를 **TanStack Start** 프로젝트에 연결하는 방법을 보여줍니다 (TanStack Start는 내부적으로 Vite와 TanStack Router를 라우팅/SSR에 사용합니다).
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 1단계: 의존성 설치
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# npm
|
|
40
|
+
npm i intlayer react-intlayer
|
|
41
|
+
npm i -D vite-intlayer
|
|
42
|
+
|
|
43
|
+
# pnpm
|
|
44
|
+
pnpm add intlayer react-intlayer
|
|
45
|
+
pnpm add -D vite-intlayer
|
|
46
|
+
|
|
47
|
+
# yarn
|
|
48
|
+
yarn add intlayer react-intlayer
|
|
49
|
+
yarn add -D vite-intlayer
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- **intlayer**: 코어 (설정, 사전, CLI/변환).
|
|
53
|
+
- **react-intlayer**: React용 `<IntlayerProvider>` 및 훅.
|
|
54
|
+
- **vite-intlayer**: Vite 플러그인, 선택적 미들웨어 포함 (로케일 감지/리다이렉트용, 개발 및 SSR/미리보기에서 작동; 프로덕션 SSR 시 `dependencies`로 이동).
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 2단계: Intlayer 구성
|
|
59
|
+
|
|
60
|
+
프로젝트 루트에 `intlayer.config.ts` 파일을 생성하세요:
|
|
61
|
+
|
|
62
|
+
```ts fileName="intlayer.config.ts"
|
|
63
|
+
import { Locales, type IntlayerConfig } from "intlayer";
|
|
64
|
+
|
|
65
|
+
const config: IntlayerConfig = {
|
|
66
|
+
internationalization: {
|
|
67
|
+
locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
|
|
68
|
+
defaultLocale: Locales.ENGLISH,
|
|
69
|
+
},
|
|
70
|
+
// contentDir, contentFileExtensions, 미들웨어 옵션 등도 조정할 수 있습니다.
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export default config;
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
CommonJS/ESM 변형은 `cjs`/`mjs`를 선호하는 경우 원본 문서와 동일합니다.
|
|
77
|
+
|
|
78
|
+
> 전체 구성 참조는 Intlayer의 구성 문서를 참고하세요.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 3단계: Vite 플러그인 추가 (및 선택적 미들웨어)
|
|
83
|
+
|
|
84
|
+
**TanStack Start는 Vite를 사용하므로**, Intlayer의 플러그인을 `vite.config.ts`에 추가하세요:
|
|
85
|
+
|
|
86
|
+
```ts fileName="vite.config.ts"
|
|
87
|
+
import { defineConfig } from "vite";
|
|
88
|
+
import react from "@vitejs/plugin-react-swc";
|
|
89
|
+
import { intlayerPlugin, intLayerMiddlewarePlugin } from "vite-intlayer";
|
|
90
|
+
|
|
91
|
+
export default defineConfig({
|
|
92
|
+
plugins: [
|
|
93
|
+
react(),
|
|
94
|
+
intlayerPlugin(),
|
|
95
|
+
// 선택 사항이지만 로케일 감지, 쿠키 및 리디렉션에 권장됩니다:
|
|
96
|
+
intLayerMiddlewarePlugin(),
|
|
97
|
+
],
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
> SSR을 배포하는 경우, 미들웨어가 프로덕션에서 실행되도록 `vite-intlayer`를 `dependencies`로 이동하세요.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 4단계: 콘텐츠 선언하기
|
|
106
|
+
|
|
107
|
+
사전(dictionary)을 기본 `contentDir`인 `./src` 아래 아무 곳에나 배치하세요. 예시:
|
|
108
|
+
|
|
109
|
+
```tsx fileName="src/app.content.tsx"
|
|
110
|
+
import { t, type Dictionary } from "intlayer";
|
|
111
|
+
import type { ReactNode } from "react";
|
|
112
|
+
|
|
113
|
+
const appContent = {
|
|
114
|
+
key: "app",
|
|
115
|
+
content: {
|
|
116
|
+
viteLogo: t({
|
|
117
|
+
ko: "Vite 로고",
|
|
118
|
+
en: "Vite logo",
|
|
119
|
+
fr: "Logo Vite",
|
|
120
|
+
es: "Logo Vite",
|
|
121
|
+
}),
|
|
122
|
+
reactLogo: t({
|
|
123
|
+
ko: "React 로고",
|
|
124
|
+
en: "React logo",
|
|
125
|
+
fr: "Logo React",
|
|
126
|
+
es: "Logo React",
|
|
127
|
+
}),
|
|
128
|
+
title: t({
|
|
129
|
+
ko: "TanStack Start + React",
|
|
130
|
+
en: "TanStack Start + React",
|
|
131
|
+
fr: "TanStack Start + React",
|
|
132
|
+
es: "TanStack Start + React",
|
|
133
|
+
}),
|
|
134
|
+
count: t({
|
|
135
|
+
ko: "카운트는 ",
|
|
136
|
+
en: "count is ",
|
|
137
|
+
fr: "le compte est ",
|
|
138
|
+
es: "el recuento es ",
|
|
139
|
+
}),
|
|
140
|
+
edit: t<ReactNode>({
|
|
141
|
+
ko: (
|
|
142
|
+
<>
|
|
143
|
+
<code>src/routes/index.tsx</code>를 편집하고 저장하여 HMR을
|
|
144
|
+
테스트하세요
|
|
145
|
+
</>
|
|
146
|
+
),
|
|
147
|
+
en: (
|
|
148
|
+
<>
|
|
149
|
+
Edit <code>src/routes/index.tsx</code> and save to test HMR
|
|
150
|
+
</>
|
|
151
|
+
),
|
|
152
|
+
fr: (
|
|
153
|
+
<>
|
|
154
|
+
Éditez <code>src/routes/index.tsx</code> et enregistrez pour tester
|
|
155
|
+
HMR
|
|
156
|
+
</>
|
|
157
|
+
),
|
|
158
|
+
es: (
|
|
159
|
+
<>
|
|
160
|
+
Edita <code>src/routes/index.tsx</code> y guarda para probar HMR
|
|
161
|
+
</>
|
|
162
|
+
),
|
|
163
|
+
}),
|
|
164
|
+
readTheDocs: t({
|
|
165
|
+
en: "Click the logos to learn more",
|
|
166
|
+
fr: "Cliquez sur les logos pour en savoir plus",
|
|
167
|
+
es: "Haz clic en los logotipos para saber más",
|
|
168
|
+
}),
|
|
169
|
+
},
|
|
170
|
+
} satisfies Dictionary;
|
|
171
|
+
|
|
172
|
+
export default appContent;
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
JSON/ESM/CJS 변형은 원본 문서와 동일하게 작동합니다.
|
|
176
|
+
|
|
177
|
+
> TSX 콘텐츠인가요? 설정에 따라 `import React from "react"`를 잊지 마세요.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 5단계: TanStack Start를 Intlayer로 감싸기
|
|
182
|
+
|
|
183
|
+
TanStack Start에서는 **루트 경로(root route)**가 프로바이더를 설정하기에 적합한 위치입니다.
|
|
184
|
+
|
|
185
|
+
```tsx fileName="src/routes/__root.tsx"
|
|
186
|
+
import {
|
|
187
|
+
Outlet,
|
|
188
|
+
createRootRoute,
|
|
189
|
+
Link as RouterLink,
|
|
190
|
+
} from "@tanstack/react-router";
|
|
191
|
+
import { IntlayerProvider, useIntlayer } from "react-intlayer";
|
|
192
|
+
|
|
193
|
+
function AppShell() {
|
|
194
|
+
// 최상위에서 사전을 사용하는 예:
|
|
195
|
+
const content = useIntlayer("app");
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
<div>
|
|
199
|
+
<nav className="flex gap-3 p-3">
|
|
200
|
+
<RouterLink to="/">홈</RouterLink>
|
|
201
|
+
<RouterLink to="/about">소개</RouterLink>
|
|
202
|
+
</nav>
|
|
203
|
+
<main className="p-6">
|
|
204
|
+
<h1>{content.title}</h1>
|
|
205
|
+
<Outlet />
|
|
206
|
+
</main>
|
|
207
|
+
</div>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export const Route = createRootRoute({
|
|
212
|
+
component: () => (
|
|
213
|
+
<IntlayerProvider>
|
|
214
|
+
<AppShell />
|
|
215
|
+
</IntlayerProvider>
|
|
216
|
+
),
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
그런 다음 페이지에서 콘텐츠를 사용하세요:
|
|
221
|
+
|
|
222
|
+
```tsx fileName="src/routes/index.tsx"
|
|
223
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
224
|
+
import { useIntlayer } from "react-intlayer";
|
|
225
|
+
import reactLogo from "../assets/react.svg";
|
|
226
|
+
|
|
227
|
+
export const Route = createFileRoute("/")({
|
|
228
|
+
component: () => {
|
|
229
|
+
const content = useIntlayer("app");
|
|
230
|
+
return (
|
|
231
|
+
<>
|
|
232
|
+
<button>{content.count}0</button>
|
|
233
|
+
<p>{content.edit}</p>
|
|
234
|
+
<img
|
|
235
|
+
src={reactLogo}
|
|
236
|
+
alt={content.reactLogo.value}
|
|
237
|
+
width={48}
|
|
238
|
+
height={48}
|
|
239
|
+
/>
|
|
240
|
+
<p className="opacity-70">{content.readTheDocs}</p>
|
|
241
|
+
</>
|
|
242
|
+
);
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
> 문자열 속성(`alt`, `title`, `aria-label` 등)은 `.value`가 필요합니다:
|
|
248
|
+
>
|
|
249
|
+
> ```jsx
|
|
250
|
+
> <img alt={c.reactLogo.value} />
|
|
251
|
+
> ```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## (선택 사항) 6단계: 로케일 전환 (클라이언트)
|
|
256
|
+
|
|
257
|
+
```tsx fileName="src/components/LocaleSwitcher.tsx"
|
|
258
|
+
import { Locales } from "intlayer";
|
|
259
|
+
import { useLocale } from "react-intlayer";
|
|
260
|
+
|
|
261
|
+
export function LocaleSwitcher() {
|
|
262
|
+
const { setLocale } = useLocale();
|
|
263
|
+
return (
|
|
264
|
+
<div className="flex gap-2">
|
|
265
|
+
<button onClick={() => setLocale(Locales.ENGLISH)}>영어</button>
|
|
266
|
+
<button onClick={() => setLocale(Locales.FRENCH)}>프랑스어</button>
|
|
267
|
+
<button onClick={() => setLocale(Locales.SPANISH)}>스페인어</button>
|
|
268
|
+
</div>
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## (선택 사항) 7단계: 지역화된 라우팅 (SEO 친화적 URL)
|
|
276
|
+
|
|
277
|
+
TanStack Start에는 **두 가지 좋은 패턴**이 있습니다. 하나를 선택하세요.
|
|
278
|
+
|
|
279
|
+
동적 세그먼트 폴더 `src/routes/$locale/`를 생성하여 URL이 `/:locale/...` 형식이 되도록 합니다. `$locale` 레이아웃에서 `params.locale`을 검증하고, `<IntlayerProvider locale=...>`를 설정한 후 `<Outlet />`을 렌더링합니다. 이 방법은 간단하지만, 나머지 라우트들은 `$locale` 하위에 마운트되며, 기본 로케일 접두사를 원하지 않는 경우 별도의 접두사 없는 트리가 필요합니다.
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## (선택 사항) 8단계: 로케일 전환 시 URL 업데이트
|
|
284
|
+
|
|
285
|
+
패턴 A (basepath)에서는 로케일을 전환하면 **다른 basepath로 이동**하는 것을 의미합니다:
|
|
286
|
+
|
|
287
|
+
```tsx fileName="src/components/LocaleSwitcherNavigate.tsx"
|
|
288
|
+
import { useRouter } from "@tanstack/react-router";
|
|
289
|
+
import { Locales, getLocalizedUrl } from "intlayer";
|
|
290
|
+
import { useLocale } from "react-intlayer";
|
|
291
|
+
|
|
292
|
+
export function LocaleSwitcherNavigate() {
|
|
293
|
+
const router = useRouter();
|
|
294
|
+
const { locale, setLocale } = useLocale();
|
|
295
|
+
|
|
296
|
+
const change = async (next: Locales) => {
|
|
297
|
+
if (next === locale) return;
|
|
298
|
+
const nextPath = getLocalizedUrl(
|
|
299
|
+
window.location.pathname + window.location.search,
|
|
300
|
+
next
|
|
301
|
+
);
|
|
302
|
+
await router.navigate({ to: nextPath }); // 기록을 유지합니다
|
|
303
|
+
setLocale(next);
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
return (
|
|
307
|
+
<div className="flex gap-2">
|
|
308
|
+
<button onClick={() => change(Locales.ENGLISH)}>English</button>
|
|
309
|
+
<button onClick={() => change(Locales.FRENCH)}>Français</button>
|
|
310
|
+
<button onClick={() => change(Locales.SPANISH)}>Español</button>
|
|
311
|
+
</div>
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## (선택 사항) 9단계: `<html lang>` 및 `dir` (TanStack Start 문서)
|
|
319
|
+
|
|
320
|
+
TanStack Start는 사용자 정의가 가능한 **Document**(루트 HTML 셸)를 제공합니다. 접근성 및 SEO를 위해 `lang`과 `dir`을 설정하세요:
|
|
321
|
+
|
|
322
|
+
```tsx fileName="src/routes/__root.tsx" {4,15}
|
|
323
|
+
import { Outlet, createRootRoute } from "@tanstack/react-router";
|
|
324
|
+
import { IntlayerProvider } from "react-intlayer";
|
|
325
|
+
import { getHTMLTextDir } from "intlayer";
|
|
326
|
+
|
|
327
|
+
function Document({
|
|
328
|
+
locale,
|
|
329
|
+
children,
|
|
330
|
+
}: {
|
|
331
|
+
locale: string;
|
|
332
|
+
children: React.ReactNode;
|
|
333
|
+
}) {
|
|
334
|
+
return (
|
|
335
|
+
<html lang={locale} dir={getHTMLTextDir(locale)}>
|
|
336
|
+
<head>
|
|
337
|
+
<meta charSet="utf-8" />
|
|
338
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
339
|
+
{/* ... */}
|
|
340
|
+
</head>
|
|
341
|
+
<body>{children}</body>
|
|
342
|
+
</html>
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export const Route = createRootRoute({
|
|
347
|
+
component: () => (
|
|
348
|
+
<IntlayerProvider>
|
|
349
|
+
{/* 서버에서 로케일을 계산하는 경우 Document에 전달하세요; 그렇지 않으면 클라이언트가 하이드레이션 후 수정합니다 */}
|
|
350
|
+
<Document locale={document?.documentElement?.lang || "en"}>
|
|
351
|
+
<Outlet />
|
|
352
|
+
</Document>
|
|
353
|
+
</IntlayerProvider>
|
|
354
|
+
),
|
|
355
|
+
});
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
클라이언트 측 수정을 위해, 다음과 같은 작은 훅을 유지할 수도 있습니다:
|
|
359
|
+
|
|
360
|
+
```tsx fileName="src/hooks/useI18nHTMLAttributes.tsx"
|
|
361
|
+
import { useEffect } from "react";
|
|
362
|
+
import { useLocale } from "react-intlayer";
|
|
363
|
+
import { getHTMLTextDir } from "intlayer";
|
|
364
|
+
|
|
365
|
+
export const useI18nHTMLAttributes = () => {
|
|
366
|
+
const { locale } = useLocale();
|
|
367
|
+
useEffect(() => {
|
|
368
|
+
document.documentElement.lang = locale; // 문서의 언어 속성을 현재 로케일로 설정
|
|
369
|
+
document.documentElement.dir = getHTMLTextDir(locale); // 문서의 텍스트 방향 설정
|
|
370
|
+
}, [locale]);
|
|
371
|
+
};
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## (선택 사항) 10단계: 지역화된 Link 컴포넌트
|
|
377
|
+
|
|
378
|
+
TanStack Router는 `<Link/>`를 제공하지만, 내부 URL에 자동으로 접두사를 붙이는 일반 `<a>` 태그가 필요할 경우:
|
|
379
|
+
|
|
380
|
+
```tsx fileName="src/components/Link.tsx"
|
|
381
|
+
import { getLocalizedUrl } from "intlayer";
|
|
382
|
+
import {
|
|
383
|
+
forwardRef,
|
|
384
|
+
type AnchorHTMLAttributes,
|
|
385
|
+
type DetailedHTMLProps,
|
|
386
|
+
} from "react";
|
|
387
|
+
import { useLocale } from "react-intlayer";
|
|
388
|
+
|
|
389
|
+
export interface LinkProps
|
|
390
|
+
extends DetailedHTMLProps<
|
|
391
|
+
AnchorHTMLAttributes<HTMLAnchorElement>,
|
|
392
|
+
HTMLAnchorElement
|
|
393
|
+
> {}
|
|
394
|
+
|
|
395
|
+
const isExternal = (href?: string) => /^https?:\/\//.test(href ?? "");
|
|
396
|
+
|
|
397
|
+
export const Link = forwardRef<HTMLAnchorElement, LinkProps>(
|
|
398
|
+
({ href, children, ...props }, ref) => {
|
|
399
|
+
const { locale } = useLocale();
|
|
400
|
+
const hrefI18n =
|
|
401
|
+
href && !isExternal(href) ? getLocalizedUrl(href, locale) : href;
|
|
402
|
+
return (
|
|
403
|
+
<a href={hrefI18n} ref={ref} {...props}>
|
|
404
|
+
{children}
|
|
405
|
+
</a>
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
);
|
|
409
|
+
Link.displayName = "Link";
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
> 패턴 A(basepath)를 사용하는 경우, TanStack의 `<Link to="/about" />`는 이미 `basepath`를 통해 `/fr/about`로 해결되므로, 커스텀 링크는 선택 사항입니다.
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## TypeScript
|
|
417
|
+
|
|
418
|
+
Intlayer가 생성한 타입을 포함하세요:
|
|
419
|
+
|
|
420
|
+
```json5 fileName="tsconfig.json"
|
|
421
|
+
{
|
|
422
|
+
"include": ["src", ".intlayer/**/*.ts"],
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## Git
|
|
429
|
+
|
|
430
|
+
Intlayer가 생성한 아티팩트를 무시하세요:
|
|
431
|
+
|
|
432
|
+
```gitignore
|
|
433
|
+
.intlayer
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## VS 코드 확장
|
|
439
|
+
|
|
440
|
+
- **Intlayer VS 코드 확장** → 자동완성, 오류, 인라인 미리보기, 빠른 작업.
|
|
441
|
+
마켓플레이스: `intlayer.intlayer-vs-code-extension`
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## 더 나아가기
|
|
446
|
+
|
|
447
|
+
- 비주얼 에디터
|
|
448
|
+
- CMS 모드
|
|
449
|
+
- 엣지/어댑터에서의 로케일 감지
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
453
|
+
## 문서 이력
|
|
454
|
+
|
|
455
|
+
| 버전 | 날짜 | 변경 사항 |
|
|
456
|
+
| ----- | ---------- | ------------------------ |
|
|
457
|
+
| 1.0.0 | 2025-08-11 | TanStack Start 적응 추가 |
|
|
@@ -280,7 +280,6 @@ Intlayer는 Next.js 애플리케이션의 국제화를 돕기 위한 다양한
|
|
|
280
280
|
- [`useIntlayer()`](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ko/packages/next-intlayer/useIntlayer.md)
|
|
281
281
|
- [`useDictionary()`](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ko/packages/next-intlayer/useDictionary.md)
|
|
282
282
|
- [`useLocale()`](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ko/packages/next-intlayer/useLocale.md)
|
|
283
|
-
- [`useIntlayerAsync()`](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ko/packages/next-intlayer/useIntlayerAsync.md)
|
|
284
283
|
|
|
285
284
|
## 문서 이력
|
|
286
285
|
|
|
@@ -280,7 +280,6 @@ Intlayer는 React 애플리케이션을 국제화하는 데 도움이 되는 다
|
|
|
280
280
|
- [`useIntlayer()`](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ko/packages/react-intlayer/useIntlayer.md)
|
|
281
281
|
- [`useDictionary()`](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ko/packages/react-intlayer/useDictionary.md)
|
|
282
282
|
- [`useLocale()`](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ko/packages/react-intlayer/useLocale.md)
|
|
283
|
-
- [`useIntlayerAsync()`](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ko/packages/react-intlayer/useIntlayerAsync.md)
|
|
284
283
|
|
|
285
284
|
## 문서 이력
|
|
286
285
|
|
package/docs/pt/CI_CD.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
createdAt: 2025-05-20
|
|
3
|
-
updatedAt: 2025-
|
|
3
|
+
updatedAt: 2025-08-13
|
|
4
4
|
title: Integração CI/CD
|
|
5
5
|
description: Aprenda como integrar o Intlayer em seu pipeline CI/CD para gerenciamento e implantação automatizados de conteúdo.
|
|
6
6
|
keywords:
|
|
@@ -19,13 +19,13 @@ slugs:
|
|
|
19
19
|
|
|
20
20
|
# Geração Automática de Traduções em um Pipeline CI/CD
|
|
21
21
|
|
|
22
|
-
O Intlayer permite a geração automática de traduções para seus arquivos de declaração de conteúdo. Existem várias maneiras de
|
|
22
|
+
O Intlayer permite a geração automática de traduções para seus arquivos de declaração de conteúdo. Existem várias maneiras de realizar isso dependendo do seu fluxo de trabalho.
|
|
23
23
|
|
|
24
24
|
## Usando o CMS
|
|
25
25
|
|
|
26
|
-
Com o Intlayer, você pode adotar um fluxo de trabalho onde apenas um único idioma é declarado localmente, enquanto todas as traduções são gerenciadas remotamente através do CMS. Isso permite que o conteúdo e as traduções fiquem completamente desacoplados da base de código, oferecendo mais flexibilidade para os editores de conteúdo e possibilitando recarregamento dinâmico do conteúdo (sem necessidade de reconstruir a aplicação para aplicar as
|
|
26
|
+
Com o Intlayer, você pode adotar um fluxo de trabalho onde apenas um único idioma é declarado localmente, enquanto todas as traduções são gerenciadas remotamente através do CMS. Isso permite que o conteúdo e as traduções fiquem completamente desacoplados da base de código, oferecendo mais flexibilidade para os editores de conteúdo e possibilitando o recarregamento dinâmico do conteúdo (sem necessidade de reconstruir a aplicação para aplicar as alterações).
|
|
27
27
|
|
|
28
|
-
###
|
|
28
|
+
### Configuração de Exemplo
|
|
29
29
|
|
|
30
30
|
```ts fileName="intlayer.config.ts"
|
|
31
31
|
import { Locales, type IntlayerConfig } from "intlayer";
|
|
@@ -37,7 +37,7 @@ const config: IntlayerConfig = {
|
|
|
37
37
|
defaultLocale: Locales.ENGLISH,
|
|
38
38
|
},
|
|
39
39
|
editor: {
|
|
40
|
-
dictionaryPriorityStrategy: "distant_first", //
|
|
40
|
+
dictionaryPriorityStrategy: "distant_first", // Conteúdo remoto tem prioridade
|
|
41
41
|
|
|
42
42
|
applicationURL: process.env.APPLICATION_URL, // URL da aplicação usada pelo CMS
|
|
43
43
|
|
|
@@ -45,7 +45,7 @@ const config: IntlayerConfig = {
|
|
|
45
45
|
clientSecret: process.env.INTLAYER_CLIENT_SECRET,
|
|
46
46
|
},
|
|
47
47
|
ai: {
|
|
48
|
-
applicationContext: "
|
|
48
|
+
applicationContext: "This is a test application", // Ajuda a garantir a geração consistente de traduções
|
|
49
49
|
},
|
|
50
50
|
};
|
|
51
51
|
|
|
@@ -56,9 +56,9 @@ Para saber mais sobre o CMS, consulte a [documentação oficial](https://github.
|
|
|
56
56
|
|
|
57
57
|
## Usando Husky
|
|
58
58
|
|
|
59
|
-
Você pode integrar a geração de traduções
|
|
59
|
+
Você pode integrar a geração de traduções no seu fluxo de trabalho local do Git usando o [Husky](https://typicode.github.io/husky/).
|
|
60
60
|
|
|
61
|
-
###
|
|
61
|
+
### Configuração de Exemplo
|
|
62
62
|
|
|
63
63
|
```ts fileName="intlayer.config.ts"
|
|
64
64
|
import { Locales, type IntlayerConfig } from "intlayer";
|
|
@@ -77,7 +77,7 @@ const config: IntlayerConfig = {
|
|
|
77
77
|
provider: "openai",
|
|
78
78
|
apiKey: process.env.OPENAI_API_KEY, // Use sua própria chave API
|
|
79
79
|
|
|
80
|
-
applicationContext: "
|
|
80
|
+
applicationContext: "This is a test application", // Ajuda a garantir a geração consistente de traduções
|
|
81
81
|
},
|
|
82
82
|
};
|
|
83
83
|
|
|
@@ -86,7 +86,7 @@ export default config;
|
|
|
86
86
|
|
|
87
87
|
```bash fileName=".husky/pre-push"
|
|
88
88
|
npx intlayer build # Para garantir que os dicionários estejam atualizados
|
|
89
|
-
npx intlayer fill --unpushed --mode fill #
|
|
89
|
+
npx intlayer fill --unpushed --mode fill # Apenas preenche o conteúdo faltante, não atualiza os existentes
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
> Para mais informações sobre os comandos CLI do Intlayer e seu uso, consulte a [documentação CLI](https://github.com/aymericzip/intlayer/blob/main/docs/docs/pt/intlayer_cli.md).
|
|
@@ -105,68 +105,94 @@ npx intlayer fill --base-dir ./app2 --unpushed --mode fill
|
|
|
105
105
|
|
|
106
106
|
## Usando GitHub Actions
|
|
107
107
|
|
|
108
|
-
Intlayer fornece um comando CLI para preenchimento automático e revisão do conteúdo do dicionário. Isso pode ser integrado ao seu fluxo de trabalho CI/CD usando GitHub Actions.
|
|
108
|
+
O Intlayer fornece um comando CLI para preenchimento automático e revisão do conteúdo do dicionário. Isso pode ser integrado ao seu fluxo de trabalho CI/CD usando GitHub Actions.
|
|
109
109
|
|
|
110
110
|
```yaml fileName=".github/workflows/intlayer-translate.yml"
|
|
111
|
-
name: Intlayer
|
|
111
|
+
name: Preenchimento Automático Intlayer
|
|
112
|
+
# Condições de gatilho para este fluxo de trabalho
|
|
112
113
|
on:
|
|
113
|
-
push:
|
|
114
|
-
branches: [ main ]
|
|
115
|
-
paths:
|
|
116
|
-
- 'src/**'
|
|
117
114
|
pull_request:
|
|
118
|
-
branches:
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
branches:
|
|
116
|
+
- "main"
|
|
117
|
+
|
|
118
|
+
permissions:
|
|
119
|
+
contents: write
|
|
120
|
+
pull-requests: write
|
|
122
121
|
|
|
123
122
|
concurrency:
|
|
124
|
-
group:
|
|
123
|
+
group: "autofill-${{ github.ref }}"
|
|
125
124
|
cancel-in-progress: true
|
|
126
125
|
|
|
127
126
|
jobs:
|
|
128
127
|
autofill:
|
|
129
128
|
runs-on: ubuntu-latest
|
|
130
129
|
env:
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
130
|
+
# OpenAI
|
|
131
|
+
AI_MODEL: openai
|
|
132
|
+
AI_PROVIDER: gpt-5-mini
|
|
133
|
+
AI_API_KEY: ${{ secrets.AI_API_KEY }}
|
|
134
134
|
|
|
135
135
|
steps:
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
# Passo 1: Obter o código mais recente do repositório
|
|
137
|
+
- name: ⬇️ Checkout do repositório
|
|
138
|
+
uses: actions/checkout@v4
|
|
138
139
|
with:
|
|
139
|
-
persist-credentials: true
|
|
140
|
+
persist-credentials: true # Manter credenciais para criação de PRs
|
|
141
|
+
fetch-depth: 0 # Obter histórico completo do git para análise de diferenças
|
|
140
142
|
|
|
143
|
+
# Passo 2: Configurar ambiente Node.js
|
|
141
144
|
- name: 🟢 Configurar Node.js
|
|
142
|
-
uses: actions/setup-node@
|
|
145
|
+
uses: actions/setup-node@v4
|
|
143
146
|
with:
|
|
144
|
-
node-version: 20
|
|
147
|
+
node-version: 20 # Usar Node.js 20 LTS para estabilidade
|
|
145
148
|
|
|
149
|
+
# Passo 3: Instalar dependências do projeto
|
|
146
150
|
- name: 📦 Instalar dependências
|
|
147
|
-
run: npm
|
|
151
|
+
run: npm install
|
|
152
|
+
|
|
153
|
+
# Passo 4: Instalar Intlayer CLI globalmente para gerenciamento de traduções
|
|
154
|
+
- name: 📦 Instalar Intlayer
|
|
155
|
+
run: npm install -g intlayer-cli
|
|
148
156
|
|
|
157
|
+
# Passo 5: Construir o projeto Intlayer para gerar arquivos de tradução
|
|
149
158
|
- name: ⚙️ Construir projeto Intlayer
|
|
150
159
|
run: npx intlayer build
|
|
151
160
|
|
|
161
|
+
# Passo 6: Usar IA para preencher automaticamente traduções faltantes
|
|
152
162
|
- name: 🤖 Preencher automaticamente traduções faltantes
|
|
153
|
-
run: npx intlayer fill --git-diff --mode fill
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
163
|
+
run: npx intlayer fill --git-diff --mode fill --provider $AI_PROVIDER --model $AI_MODEL --api-key $AI_API_KEY
|
|
164
|
+
|
|
165
|
+
# Passo 7: Verificar se há alterações e comitá-las
|
|
166
|
+
- name: � Verificar alterações
|
|
167
|
+
id: check-changes
|
|
168
|
+
run: |
|
|
169
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
170
|
+
echo "has-changes=true" >> $GITHUB_OUTPUT
|
|
171
|
+
else
|
|
172
|
+
echo "has-changes=false" >> $GITHUB_OUTPUT
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
# Passo 8: Comitar e enviar alterações se existirem
|
|
176
|
+
- name: 📤 Comitar e enviar alterações
|
|
177
|
+
if: steps.check-changes.outputs.has-changes == 'true'
|
|
178
|
+
run: |
|
|
179
|
+
git config --local user.email "action@github.com"
|
|
180
|
+
git config --local user.name "GitHub Action"
|
|
181
|
+
git add .
|
|
182
|
+
git commit -m "chore: auto-fill missing translations [skip ci]"
|
|
183
|
+
git push origin HEAD:${{ github.head_ref }}
|
|
162
184
|
```
|
|
163
185
|
|
|
186
|
+
Para configurar as variáveis de ambiente, vá para GitHub → Configurações → Segredos e variáveis → Ações e adicione o segredo .
|
|
187
|
+
|
|
164
188
|
> Assim como para o Husky, no caso de um monorepo, você pode usar o argumento `--base-dir` para tratar sequencialmente cada app.
|
|
165
189
|
|
|
166
190
|
> Por padrão, o argumento `--git-diff` filtra os dicionários que incluem alterações da base (padrão `origin/main`) para o branch atual (padrão: `HEAD`).
|
|
167
191
|
|
|
168
192
|
> Para mais informações sobre os comandos do Intlayer CLI e seu uso, consulte a [documentação do CLI](https://github.com/aymericzip/intlayer/blob/main/docs/docs/pt/intlayer_cli.md).
|
|
169
193
|
|
|
170
|
-
## Histórico
|
|
194
|
+
## Histórico do Documento
|
|
171
195
|
|
|
172
|
-
|
|
196
|
+
| Versão | Data | Alterações |
|
|
197
|
+
| ------ | ---------- | ----------------- |
|
|
198
|
+
| 5.5.10 | 2025-06-29 | Histórico inicial |
|