이 페이지와 원하는 AI 어시스턴트를 사용하여 문서를 요약합니다
버전 기록
- "초기 버전"v9.5.102026. 9. 26.
이 페이지의 콘텐츠는 AI를 사용하여 번역되었습니다.
영어 원본 내용의 최신 버전을 보기이 문서를 개선할 아이디어가 있으시면 GitHub에 풀 리퀘스트를 제출하여 자유롭게 기여해 주세요.
문서에 대한 GitHub 링크문서의 Markdown을 클립보드에 복사
2026년 Paraglide JS를 사용하여 TanStack Start 애플리케이션을 국제화하는 방법
목차
Paraglide JS란 무엇인가요?
Paraglide JS(inlang 제공)는 컴파일러 기반 i18n 라이브러리입니다. JSON 객체에서 키를 검색하는 런타임을 제공하는 대신, 각 메시지를 타입이 지정된 JavaScript 함수(m.about_title())로 컴파일합니다. 사용되지 않는 메시지는 번들러에 의해 제거(트리 셰이킹)될 수 있으며, 키에 오타가 있으면 컴파일 에러가 발생합니다.
Paraglide는 공식 TanStack Router 예제에서 사용되는 i18n 방식이며, 세 가지 요소를 통해 TanStack Start와 통합됩니다:
- 메시지와 런타임을
src/paraglide로 컴파일하는 Vite 플러그인 - 각 요청의 로케일을 확인하는 서버 미들웨어
- 지역화된 URL(
/fr/about)을 라우트 트리(/about)로 매핑하여 별도의$locale세그먼트가 필요 없도록 하는 라우터 재작성(rewrite)
이 가이드에서는 이 세 가지를 모두 설정한 다음, Paraglide가 개발자에게 맡기는 나머지 작업들(lang 및 dir, 언어 전환기, 번역된 메타데이터, canonical, x-default를 포함한 hreflang, Open Graph, JSON-LD, 사이트맵, robots.txt, 사전 렌더링 및 지역화된 404 페이지)까지 모두 다룹니다.
다른 스택을 찾고 계신가요? TanStack Start + use-intl 가이드, TanStack Start + Lingui 가이드, 또는 TanStack Start + Intlayer 가이드를 확인하세요.
두 컴파일러 기반 접근 방식을 비교하고 싶으신가요? Intlayer는 Paraglide보다 더 가벼운가요? 문서를 읽어보세요.
TanStack Start에서의 Paraglide 벤치마크 결과
i18n 벤치마크는 모든 주요 라이브러리를 사용하여 동일한 10페이지, 10개 로케일 TanStack Start 앱을 실행하고 브라우저가 실제로 다운로드하는 양을 측정합니다.
동적 JSON 로드
런타임에 번역을 지연 로드합니다
범위가 지정된 JSON (네임스페이싱)
페이지별 번역 네임스페이스
I18n 성능 벤치마크
이 측정항목은 무엇인가요?
국제화 라이브러리 번들의 총 gzip 압축 크기입니다. 여기에는 트리 쉐이킹 및 미니피케이션 후의 프로바이더 및 콘텐츠 검색 로직만 포함됩니다.
왜 중요한가요?
라이브러리 크기가 작으면 초기 JavaScript 페이로드가 줄어들어 클라이언트에서 다운로드 및 실행 시간이 빨라집니다.
보기 형식
2026-09-26에 측정된 @inlang/paraglide-js@2.15.1의 주요 수치 (gzip):
테이블을 모달로 열어 모든 데이터를 명확하게 확인
| 설정 | 라이브러리 크기 | 페이지당 JS | 타 로케일 누출 | 타 페이지 누출 | 페이지 로드 |
|---|---|---|---|---|---|
| i18n 미적용 (기본 앱) | - | 111.0 KB | 0% | 0% | 15.7 ms |
| Paraglide JS | 1.8 KB | 125.1 KB | 49.7% | 0% | 22.1 ms |
react-intlayer | 4.5 KB | 126.8 KB | 0% | 0% | 14.8 ms |
use-intl | 75.9 KB | 128.7 KB | 0% | 0% | 17.4 ms |
| Lingui | 56.7 KB | 120.2 KB | 8.6% | 0% | 21.9 ms |
주요 시사점:
- 런타임이 매우 작고, 다른 페이지의 번역이 누출되지 않습니다. 런타임은 설정에 맞게 생성되며, 메시지는 사용되는 곳에서 직접 임포트됩니다.
- 로케일 데이터 누출이 발생합니다. 각 메시지 함수에 모든 로케일이 포함되어 있으므로, 한 페이지에 전달되는 번역 문자열의 약 절반이 방문자가 사용하지 않는 언어로 구성됩니다. 로케일을 추가할수록 이 비율은 더 커집니다.
- 페이지 로드 속도가 비교군 중 가장 느립니다. 이는 부분적으로 로케일을 React 컨텍스트에서 읽지 않고 매 호출 시마다 전략을 통해 확인하기 때문입니다.
전체 데이터 확인하기: TanStack Start 벤치마크 리포트 및 벤치마크 저장소.
TanStack Start에서의 기능 비교
Paraglide JS와 TanStack Start에서 흔히 사용되는 다른 라이브러리 간의 비교:
테이블을 모달로 열어 모든 데이터를 명확하게 확인
| 기능 | react-intlayer (Intlayer) | use-intl | Paraglide JS | Lingui |
|---|---|---|---|---|
| 컴포넌트 인근 번역 파일 배치 | ✅ 동위 배치 (Co-located) | ❌ 중앙 집중식 JSON | ❌ 로케일당 하나의 JSON 파일 | ⚠️ 컴포넌트 내 원본 텍스트 |
| TypeScript 통합 | ✅ 자동 생성된 타입 | ✅ AppConfig 활용 | ✅ 타입이 지정된 메시지 함수 | ⚠️ 매크로만 지원 |
| 누락된 번역 감지 | ✅ 타입 에러 및 빌드 경고 | ⚠️ 런타임 폴백 | ⚠️ 기본 로케일로 폴백 | ⚠️ 원본 텍스트로 폴백 |
| 리치 콘텐츠 (JSX, Markdown) | ✅ 직접 지원 | ⚠️ t.rich 태그 지원 | ⚠️ 문자열 | ✅ <Trans> 내부 JSX |
| 지역화된 라우팅 | ✅ 내장 기능 | ❌ 수동 {-$locale} | ✅ urlPatterns + 라우터 재작성 | ❌ 수동 {-$locale} |
| 새로고침 없는 언어 전환 | ✅ 지원 | ✅ 지원 | ❌ 전체 페이지 새로고침 | ✅ 지원 |
| 복수형 (Pluralization) | ✅ 열거형 기반 | ✅ ICU | ✅ 변형(Variants) | ✅ ICU |
| ICU MessageFormat | ✅ format: "icu" 지원 | ✅ 기본 지원 | ⚠️ inlang 플러그인 필요 | ✅ 기본 지원 |
| 콘텐츠 포맷 | ✅ .ts, .json, .md, .yaml 등 | ⚠️ .json | ⚠️ inlang JSON | ✅ PO, JSON, CSV |
| AI 번역 | ✅ 자체 프로바이더 및 API 키 | ❌ 미지원 | ❌ 미지원 | ❌ 미지원 |
| 시각적 에디터 / CMS | ✅ 로컬 에디터 + 선택적 CMS | ❌ 외부 플랫폼 | ⚠️ inlang 생태계 앱 | ❌ 외부 플랫폼 |
| SEO 헬퍼 (hreflang, 사이트맵) | ✅ 내장 기능 | ❌ 수동 구현 | ⚠️ 지역화된 URL 지원, 나머지 수동 | ❌ 수동 구현 |
| 런타임 크기 (gzip, 벤치마크) | 4.5 KB | 75.9 KB | 1.8 KB | 56.7 KB |
| 누출, 최적 설정 (로케일 / 페이지) | 0% / 0% | 0% / 0% | 49.7% / 0% | 8.6% / 0% |
| CI에서의 누락 번역 검사 | ✅ npx intlayer test | ⚠️ 내장 기능 없음 | ⚠️ 내장 기능 없음 | ✅ lingui compile --strict |
런타임 크기 및 누출 수치는 TanStack Start 벤치마크를 기반으로 합니다. 누출은 각 라이브러리의 최적 설정에서 측정되었습니다.
기타 TanStack Start 가이드: Lingui, use-intl, 및 Intlayer.
준수해야 할 모범 사례
- 서버에서 확인된 로케일을 바탕으로
<html>에lang및dir을 설정하세요. - 모든 언어 버전이 색인될 수 있도록 접두사 전략(
/fr/about)을 통해 로케일당 하나의 URL을 유지하세요. - URL이 신뢰할 수 있는 단일 출처가 되고 크롤러가 요청한 페이지를 정확히 받을 수 있도록 로케일 전략에서
url을 첫 번째로 배치하세요. - 함수 이름으로 깔끔하게 매핑되는 단일 레벨의 설명적인 메시지 키(
about_title)를 사용하세요. - 생성된 파일의 머지 충돌을 방지하기 위해 생성된
src/paraglide폴더가 아닌messages/*.json만 커밋하세요. - 메타데이터를 번역하고, 모든 페이지에
canonical,hreflang및x-default를 선언하세요. - 다국어 사이트맵과 robots.txt를 생성하고, 모든 로케일을 사전 렌더링하세요.
- 크롤러가 모든 언어를 발견할 수 있도록 언어 전환기에 실제 링크 태그(
<a>)를 사용하세요.
국제화 및 SEO 가이드와 hreflang 가이드를 참조하세요.
TanStack Start 애플리케이션에서 Paraglide JS를 설정하는 단계별 가이드
생성할 프로젝트 구조는 다음과 같습니다:
코드를 클립보드에 복사
라우터 재작성이 라우트 매칭 전에 접두사를 제거하므로 별도의 $locale 폴더가 필요 없다는 점에 주목하세요.
종속성 설치
TanStack Start 프로젝트에서 시작한 다음, Paraglide를 초기화합니다. init 명령은
project.inlang/settings.json, 최초의messages/en.json을 생성하고 패키지를 설치합니다.bash코드 복사코드를 클립보드에 복사
- @inlang/paraglide-js: 컴파일러 및 Vite 플러그인입니다. 런타임 패키지는 설치할 필요가 없습니다. 런타임 코드가 프로젝트 내부로 직접 생성됩니다.
로케일 구성
project.inlang/settings.json은 로케일을 관리하는 단일 소스입니다. 메시지 포맷 플러그인은 로케일당 하나의 JSON 파일을 읽습니다.project.inlang/settings.json코드 복사코드를 클립보드에 복사
Vite 플러그인 및 URL 전략 구성
플러그인은 변경 사항이 있을 때마다 메시지를 컴파일합니다. TanStack Start에서는 세 가지 옵션이 중요합니다:
strategy: 로케일을 읽어올 순서가 지정된 목록입니다.url을 첫 번째로 설정하면 URL이 신뢰할 수 있는 소스가 됩니다.cookie와preferredLanguage는 URL로 결정할 수 없을 때 미들웨어에서 사용됩니다.urlPatterns: 로케일이 URL에 매핑되는 방식입니다. 일치하는 첫 번째 패턴이 적용되므로 기본이 아닌 로케일을 먼저 나열합니다. 여기서는 기본 로케일에 접두사를 붙이지 않고(/about), 다른 로케일에는 접두사를 붙입니다(/fr/about).outputStructure: "message-modules": 메시지당 하나의 모듈로 분리하여 페이지에서 임포트하지 않는 메시지를 번들러가 제거할 수 있도록 합니다.
vite.config.ts코드 복사코드를 클립보드에 복사
생성된 폴더를
.gitignore에 추가합니다. 이 폴더는dev및build시 자동으로 다시 빌드됩니다:.gitignore코드 복사코드를 클립보드에 복사
번역 파일 생성
각 키는
src/paraglide/messages에서 내보내지는 함수가 됩니다. 계층 구조가 없는 snake_case 키를 사용하면 함수 이름을 가장 깔끔하게 유지할 수 있습니다. 변수는{name}플레이스홀더를 사용합니다.messages/en.json코드 복사코드를 클립보드에 복사
messages/fr.json코드 복사코드를 클립보드에 복사
복수형(Plural)은 inlang 메시지 포맷의 변형(variants) 구문을 사용합니다:
messages/en.json코드 복사코드를 클립보드에 복사
서버 미들웨어 추가
미들웨어는 지정한 전략에 따라 각 요청의 로케일을 확인하고,
AsyncLocalStorage범위를 통해 전체 서버 렌더링 동안getLocale()에서 해당 로케일을 사용할 수 있도록 합니다. 이를 통해 서로 다른 언어로 들어오는 동시 요청을 안전하게 처리할 수 있습니다.TanStack Start에서는 기본 서버 엔트리를 래핑합니다:
src/server.ts코드 복사코드를 클립보드에 복사
라우터에서 지역화된 URL 재작성
TanStack Router의
rewrite옵션은 라우터의 경계에서 URL을 변환합니다:- 입력(input):
/fr/about은 라우트 매칭 전에/about으로 비지역화(de-localize)되므로 단일about.tsx라우트가 모든 언어를 처리할 수 있습니다. - 출력(output): 생성되는 모든
href(링크, 리다이렉트, 네비게이션)는 현재 활성화된 로케일에 맞게 지역화되므로 프랑스어 페이지에서<Link to="/about">는/fr/about을 렌더링합니다.
src/router.tsx코드 복사코드를 클립보드에 복사
링크가 rewrite에 의해 자동으로 지역화되므로 커스텀
LocalizedLink컴포넌트가 필요하지 않습니다. 평소처럼 TanStack Router의Link를 사용하면 됩니다.- 입력(input):
루트 문서 생성
getLocale()은 서버에서는 미들웨어가 확인한 로케일을 반환하고 브라우저에서는 URL의 로케일을 반환하므로, 서버 HTML과 하이드레이션 이후의lang및dir이 동일하게 유지됩니다.src/i18n/config.ts코드 복사코드를 클립보드에 복사
src/routes/__root.tsx코드 복사코드를 클립보드에 복사
페이지에서 번역 활용하기
메시지는 일반 함수입니다.
m을 가져와서 함수를 호출하고 변수를 객체로 전달하기만 하면 됩니다. 변수를 포함한 모든 것이 타입으로 보호됩니다.src/routes/index.tsx코드 복사코드를 클립보드에 복사
src/routes/about.tsx코드 복사코드를 클립보드에 복사
메시지 함수는 명시적인 로케일도 지원합니다:
m.about_title({}, { locale: "fr" }). 이는 이메일 발송처럼 요청과 다른 언어로 렌더링해야 하는 서버 코드에서 유용합니다.콘텐츠 언어 변경하기
선택사항크롤러가 모든 언어를 발견할 수 있도록
localizeHref를 사용하여 전환기를 링크로 렌더링하세요.setLocale은 쿠키에 선택 사항을 저장하고 새 언어로 페이지를 다시 로드합니다. 메시지 함수가 React 상태를 구독하지 않고 호출될 때마다 로케일을 읽기 때문에 전체 페이지를 다시 로드하는 것이 Paraglide의 기본 동작입니다.src/components/LocaleSwitcher.tsx코드 복사코드를 클립보드에 복사
메타데이터 국제화
선택사항모든 페이지가 다음 항목들을 제공하면 각 언어 버전이 독립적으로 검색 순위를 확보할 수 있습니다:
- 번역된
<title>및description - 자기 자신을 가리키는 canonical URL
- 로케일당 하나의
hreflang대체 링크 및x-default - Open Graph
og:locale,og:locale:alternate및og:url inLanguage가 포함된 JSON-LD
Paraglide의
localizeUrl은urlPatterns를 기반으로 대체 URL을 생성하므로 실제 라우팅과 불일치할 염려가 없습니다:src/i18n/seo.ts코드 복사코드를 클립보드에 복사
- 번역된
사이트맵 국제화
선택사항다국어 사이트맵은 모든 로케일의 모든 URL을 나열하며, 각 항목은
xhtml:link를 사용하여 모든 대체 언어 버전을 선언합니다:src/routes/sitemap[.]xml.ts코드 복사코드를 클립보드에 복사
robots.txt 국제화
선택사항비공개 라우트는 모든 언어로 존재하므로
Disallow규칙은 모든 지역화된 경로를 포함해야 합니다. 스타터 템플릿이 생성한public/robots.txt가 있다면 삭제한 다음, 라우트를 통해 제공하세요:src/routes/robots[.]txt.ts코드 복사코드를 클립보드에 복사
모든 로케일 사전 렌더링
선택사항TanStack Start가 모든 언어 버전을 사전 렌더링하도록 각 페이지의 지역화된 경로를 나열합니다.
localizeHref는 브라우저 종속성이 없는 생성된 코드이므로vite.config.ts에서 실행할 수 있지만, 최초 컴파일 이후에만 파일이 존재합니다. 아래와 같이 경로를 수동으로 나열하면 이러한 순서 문제를 방지할 수 있습니다:vite.config.ts코드 복사코드를 클립보드에 복사
언어 전환기가 실제 링크를 렌더링하기 때문에,
crawlLinks: true옵션은 목록에 누락된 페이지도 자동으로 찾아냅니다.지역화된 404 페이지 처리
선택사항라우터 재작성을 적용하면
/fr/does-not-exist가/does-not-exist로 매칭되지만getLocale()은 여전히fr을 반환하므로, 7단계의 루트notFoundComponent가 프랑스어로 렌더링됩니다. catch-all 라우트를 구성하여 깊은 경로도 여기에 도달하도록 합니다. 페이지에noindex를 표시하세요. React 19는<meta>태그를 자동으로<head>로 끌어올립니다.src/components/NotFound.tsx코드 복사코드를 클립보드에 복사
src/routes/$.tsx코드 복사코드를 클립보드에 복사
서버 함수에서 로케일 접근하기
선택사항서버 함수는 Paraglide 미들웨어 범위 내에서 실행되므로
getLocale()을 서버 함수에서도 동일하게 사용할 수 있습니다:src/server/sendWelcomeEmail.ts코드 복사코드를 클립보드에 복사
Intlayer와 비교
선택사항Paraglide와 Intlayer는 모두 빌드 타임에 콘텐츠를 컴파일하고 가능한 한 적은 런타임을 제공한다는 동일한 아이디어를 따르기 때문에 두 라이브러리 간의 직접적인 드롭인 어댑터는 없습니다. 차이점은 브라우저에 도달하는 데이터와 콘텐츠 구성 방식에 있습니다:
- 로케일 관리: Intlayer는 로케일별로 동적 딕셔너리를 로드하여 벤치마크 기준 로케일 누출이 0%인 반면, Paraglide의 각 메시지 함수는 모든 로케일을 포함하므로 49.7%의 로케일 누출이 발생합니다.
- 콘텐츠 구성: 콘텐츠를 각 컴포넌트 옆의
.content.ts파일에 둘 수도 있고, 중앙 집중식 파일로 관리할 수도 있습니다. 컴포넌트별 vs 중앙 집중식 i18n 문서를 참고하세요. - 언어 전환: 콘텐츠가 React 컨텍스트에서 읽히므로 로케일을 전환해도 페이지 새로고침 없이 리렌더링됩니다.
- 생성된 코드:
src내부에 코드가 생성되지 않으므로 커밋 전에 다시 생성할 필요가 없습니다.
Paraglide가 아닌 다른 라이브러리에서 마이그레이션하는 경우, 호환 어댑터를 사용하면
use-intl,next-intl,react-i18next,react-intl, Lingui API를 그대로 유지하면서 런타임만 교체할 수 있습니다.Intlayer는 Paraglide보다 더 가벼운가요? 및 Intlayer TanStack Start 가이드를 확인하세요.
Intlayer를 활용하여 번역 자동화하기
선택사항Paraglide는 번역을 렌더링하지만 번역을 생성하는 데는 도움이 되지 않습니다. Intlayer는 무료이며 오픈 소스로, Paraglide 프로젝트에서도 유용한 도구들을 제공합니다:
- 자체 API 키와 프로바이더를 사용하여 AI로 번역하세요. 자동 완성(auto fill) 및 CLI를 참고하세요.
- JSON 동기화 플러그인으로 JSON 파일을 신뢰할 수 있는 소스로 유지하세요.
- CI에서 누락된 번역을 테스트하세요. 번역 테스트 가이드를 참고하세요.
- scan 명령어를 사용하여 배포된 사이트에서 누락된
hreflang, 잘못된 canonical URL 및 로케일 누출을 스캔하세요.
자주 묻는 질문
좋은 선택입니다. 공식 TanStack Router 예제에서 사용되고 있으며, 벤치마크에서 가장 작은 런타임 크기(~1.8 KB gzip)를 기록했고 메시지가 완벽하게 타입으로 보호됩니다. 다만, 모든 메시지 함수에 모든 로케일이 포함되어 있어 다른 언어 방문자에게 약 절반의 번역 문자열이 누출된다는 점과 언어 전환 시 페이지가 새로고침된다는 단점이 있습니다.
아니요. 라우터 rewrite가 라우트 매칭 전에 로케일 접두사를 제거하고 생성된 링크에 다시 추가하므로 단일 about.tsx 파일로 /about, /fr/about, /es/about을 모두 처리할 수 있습니다.
메시지 함수는 호출될 때 로케일을 읽으며 React 상태를 구독하지 않기 때문입니다. 따라서 setLocale은 기본적으로 페이지를 다시 로드하여 모든 메시지가 새 언어로 다시 렌더링되도록 합니다. { reload: false }를 전달할 수 있지만, 이 경우 직접 컴포넌트 트리를 다시 렌더링해야 합니다.
커밋하지 않는 것이 좋습니다. 이 폴더는 dev 및 build 시마다 다시 생성되며, 이를 커밋하면 생성된 파일에서 머지 충돌이 발생할 수 있습니다. 대신 messages/*.json과 project.inlang/settings.json을 커밋하세요.
라우트의 head()에서 localizeUrl을 사용하여 로케일당 하나의 절대 URL을 빌드하고 기본 로케일을 가리키는 x-default를 추가하세요. 10단계에서 재사용 가능한 헬퍼를 제공하며, 11단계에서는 동일한 대체 링크를 사이트맵에 추가합니다.
outputStructure: "message-modules"를 사용할 때 사용하지 않는 메시지는 제거되므로 다른 페이지의 콘텐츠는 누출되지 않습니다. 그러나 사용하지 않는 로케일은 제거되지 않습니다. 각 메시지 함수에 모든 번역이 포함되어 있기 때문에 벤치마크에서 49.7%의 로케일 누출이 측정됩니다.
댓글
아직 댓글이 없습니다. 첫 번째로 의견을 나눠보세요.
