이 페이지와 원하는 AI 어시스턴트를 사용하여 문서를 요약합니다
버전 기록
- "Initial history"v9.1.32025. 8. 6.
이 페이지의 콘텐츠는 AI를 사용하여 번역되었습니다.
영어 원본 내용의 최신 버전을 보기If you have an idea for improving this documentation, please feel free to contribute by submitting a pull request on GitHub.
GitHub link to the documentationCopy doc Markdown to clipboard
Intlayer를 사용하여 SolidStart 웹사이트 번역하기 | 국제화 (i18n)
목차
이 가이드는 서버 렌더링 SolidStart 애플리케이션을 다룹니다: 로케일 감지는 요청 시 수행되고, 페이지는 올바른 언어로 서버에서 렌더링되며, 검색 엔진에 필요한 <html lang>, hreflang 및 사이트맵 신호가 서버 측에서 내보내집니다.
다른 대안 대신 Intlayer를 선택해야 하는 이유
@solid-primitives/i18n 또는 i18next와 같은 주요 솔루션과 비교하여 Intlayer는 다음과 같은 통합 최적화를 제공하는 솔루션입니다:
Intlayer는 컴포넌트 수준의 콘텐츠 스코핑, 반응형 번역 및 국제화(i18n) 확장에 필요한 모든 기능을 제공하여 Solid와 완벽하게 작동하도록 최적화되어 있습니다.
페이지에 거대한 JSON 파일을 로드하는 대신 필요한 콘텐츠만 로드합니다. Intlayer는 번들 및 페이지 크기를 최대 50%까지 줄이는 데 도움을 줍니다.
애플리케이션의 콘텐츠 범위를 지정하면 대규모 애플리케이션의 유지 보수가 용이해집니다. 전체 콘텐츠 코드베이스를 검토해야 하는 정신적 부담 없이 단일 기능 폴더를 복제하거나 삭제할 수 있습니다. 또한, Intlayer는 콘텐츠의 정확성을 보장하기 위해 완전히 타입 지원됩니다.
AI 제공업체의 비용으로 선택한 LLM을 사용하여 CI/CD 파이프라인에서 번역을 자동화하세요. Intlayer는 콘텐츠 추출을 자동화하는 컴파일러와 백그라운드에서 번역할 수 있도록 돕는 웹 플랫폼도 제공합니다.
거대한 JSON 파일을 컴포넌트에 연결하면 성능 및 반응성 문제가 발생할 수 있습니다. Intlayer는 빌드 시 콘텐츠 로딩을 최적화합니다.
SolidStart 애플리케이션에서 Intlayer를 설정하기 위한 단계별 가이드
의존성 설치
npm을 사용하여 필요한 패키지를 설치합니다:
bash코드 복사코드를 클립보드에 복사
npx intlayer init --interactive--interactive플래그는 선택 사항입니다. AI 에이전트인 경우intlayer-cli init을 사용하세요.이 명령은 환경을 감지하고 필요한 패키지를 설치합니다. 예:
bash코드 복사코드를 클립보드에 복사
npm install intlayer solid-intlayer vite-intlayerintlayer
설정 관리, 번역, 콘텐츠 선언, 트랜스파일링 및 CLI 명령을 위한 국제화 도구를 제공하는 핵심 패키지입니다.
solid-intlayer
Intlayer를 Solid 애플리케이션과 통합하는 패키지입니다. Solid 국제화를 위한 컨텍스트 제공자와 훅을 제공합니다.
vite-intlayer
Intlayer를 Vite 번들러와 통합하기 위한 Vite 플러그인과 사용자의 선호 로케일을 감지하고 쿠키를 관리하며 URL 리디렉션을 처리하는 로케일 라우팅 핸들러를 포함합니다.
vite-intlayer는 빌드 시점뿐만 아니라 서버 측의 관심사입니다: SolidStart의 Nitro 서버가 실행하는 요청 핸들러를 제공합니다.dependencies에 유지하는 것이 안전한 기본값입니다 — Nitro가 핸들러를 인라인화하는 빌드된.output디렉터리를 배포하는 경우에만devDependencies로 이동할 수 있습니다.프로젝트 설정
애플리케이션의 언어를 설정하기 위해 설정 파일을 생성합니다:
intlayer.config.ts코드 복사코드를 클립보드에 복사
import { type IntlayerConfig, Locales } from "intlayer"; const config: IntlayerConfig = { internationalization: { locales: [ Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH, // 기타 로케일 ], defaultLocale: Locales.ENGLISH, }, routing: { mode: "prefix-no-default", }, }; export default config;prefix-no-default를 사용하면 기본 로케일은 접두사가 없는 URL에서 제공됩니다:plaintext코드 복사코드를 클립보드에 복사
/ /about → 영어 (기본 로케일)/fr /fr/about → 프랑스어/es /es/about → 스페인어이 설정 파일을 통해 지역화된 URL, 미들웨어 리디렉션, 쿠키 이름, 콘텐츠 선언의 위치 및 확장자를 설정하고 콘솔에서 Intlayer 로그를 비활성화하는 등의 작업을 수행할 수 있습니다. 사용 가능한 매개변수의 전체 목록은 설정 문서를 참조하세요.
Vite 설정에 Intlayer 통합하기
설정에 Intlayer 플러그인을 추가합니다:
vite.config.ts코드 복사코드를 클립보드에 복사
import { solidStart } from "@solidjs/start/config"; import { nitro } from "nitro/vite"; import { defineConfig } from "vite"; import { intlayer } from "vite-intlayer"; export default defineConfig({ plugins: [solidStart(), nitro(), intlayer()], });intlayer()Vite 플러그인은 콘텐츠 선언 파일을 빌드하고 개발 모드에서 이를 감시하며 애플리케이션 내에 Intlayer 환경 변수를 정의합니다. 또한 성능을 최적화하는 별칭을 제공합니다.로케일 라우팅은 플러그인과 함께 제공됩니다
SolidStart는 Nitro에서 실행되며,
intlayer()는 로케일 라우팅 핸들러를 Nitro의 서버 파이프라인에 직접 등록합니다 (기본적으로true인routing.enableProxy옵션을 통해). 추가로 연결할 작업이 없습니다: 빌드된 서버에서 모든 요청은 루터에 도달하기 전에 검사되며,- 로케일은 URL 접두사,
INTLAYER_LOCALE쿠키, 그리고Accept-Language헤더 순으로 읽힙니다; - 접두사가 없는 URL은 확인된 로케일이 기본 로케일이 아닐 때 해당 지역화된 URL로 리디렉션됩니다 (
/→/fr); - 중복 접두사가 있는 URL은 표준 형식으로 다시 리디렉션됩니다 (
/en/about→/about); - 로케일 쿠키가 응답에 다시 기록됩니다.
- 로케일은 URL 접두사,
콘텐츠 선언하기
번역을 저장하기 위한 콘텐츠 선언을 생성하고 관리합니다:
src/contents/home.content.ts코드 복사코드를 클립보드에 복사
import { type Dictionary, t } from "intlayer"; const homeContent = { key: "home-page", content: { title: t({ en: "Hello world!", fr: "Bonjour le monde !", es: "¡Hola mundo!", }), metaTitle: "SolidStart + Intlayer", metaDescription: t({ en: "A SolidStart application internationalized with Intlayer.", fr: "Une application SolidStart internationalisée avec Intlayer.", es: "Una aplicación SolidStart internacionalizada con Intlayer.", }), documentation: t({ en: "Visit start.solidjs.com to learn how to build SolidStart apps.", fr: "Visitez start.solidjs.com pour apprendre à créer des applications SolidStart.", es: "Visita start.solidjs.com para aprender a crear aplicaciones SolidStart.", }), }, } satisfies Dictionary; export default homeContent;⚠️ SolidStart 전용 주의사항:
src/routes아래의 모든.ts/.tsx파일은 라우트가 되고,.content.ts파일에는 기본 내보내기가 있으므로 페이지로 감지될 수 있습니다. 페이지의 콘텐츠 선언은 라우트 디렉터리 외부에 보관하세요 (src/contents/가 적합합니다). 컴포넌트의 콘텐츠는src/components가 파일 시스템 라우터에 의해 스캔되지 않으므로 같은 위치에 유지할 수 있습니다.콘텐츠 선언은
contentDir디렉터리(기본값./src)에 포함되어 있고 콘텐츠 선언 파일 확장자(기본값.content.{json,ts,tsx,js,jsx,mjs,cjs,md,mdx,yaml,yml})와 일치하는 한 애플리케이션 어디에서나 정의할 수 있습니다.자세한 내용은 콘텐츠 선언 문서를 참조하세요.
지역화된 라우팅 추가하기
이 단계의 목표는 각 언어에 고유한 URL을 부여하는 것입니다. 이것이 검색 엔진이 인덱싱하는 대상입니다.
페이지를 선택적 동적 세그먼트 아래로 이동합니다. SolidStart의 파일 시스템 라우터에서
[[locale]]은:locale?경로 패턴으로 컴파일됩니다:plaintext코드 복사코드를 클립보드에 복사
src/routes/ [[locale]].tsx ← 세그먼트를 검증하는 레이아웃 [[locale]]/ index.tsx → / 및 /fr 및 /es about.tsx → /about 및 /fr/about 및 /es/about [...404].tsx → 기타 모든 요청을 위한 캐치올레이아웃 파일의 유일한 역할은 세그먼트를 설정된 로케일로 제한하는 것입니다:
src/routes/[[locale]].tsx코드 복사코드를 클립보드에 복사
import type { RouteSectionProps } from "@solidjs/router";import { locales } from "intlayer";export const route = { matchFilters: { locale: locales, },};export default function LocaleLayout(props: RouteSectionProps) { return <>{props.children}</>;}@solidjs/router는:locale?을 세그먼트가 있는 패턴과 없는 패턴 두 가지로 확장하고 명확성이 높은 순서대로 시도합니다.matchFilters는 작동하는 설정과 혼란스러운 설정의 차이를 만드는 요소입니다:테이블의 모든 내용 표시테이블을 모달로 열어 모든 데이터를 명확하게 확인
URL matchFilters없음matchFilters있음/fr/about프랑스어 about 페이지 프랑스어 about 페이지 /aboutAbout 페이지 (정적 세그먼트가 우선) About 페이지 /unknown홈 페이지가 locale=unknown으로 조용히 표시됨일치 없음 → 404 캐치올로 넘어감 'prefix-all'라우팅 모드를 사용하는 경우[[locale]]대신[locale](필수)을 권장하며,'no-prefix'또는'search-params'의 경우 세그먼트를 완전히 제거하세요.애플리케이션에 로케일 제공하기
URL은 로케일의 단일 진실 출처(single source of truth)입니다: 미들웨어가 이미 요청을 지역화된 경로로 리디렉션했으므로 루트 레이아웃에서 경로를 읽으면 서버 렌더링과 클라이언트 하이드레이션이 일치하게 되고 클라이언트 측 탐색 시 로케일이 무료로 업데이트됩니다.
src/app.tsx코드 복사코드를 클립보드에 복사
import { MetaProvider } from "@solidjs/meta";import { Router, useLocation } from "@solidjs/router";import { FileRoutes } from "@solidjs/start/router";import { defaultLocale, getHTMLTextDir, getLocaleFromPath } from "intlayer";import { IntlayerProvider } from "solid-intlayer";import { createEffect, type ParentProps, Suspense } from "solid-js";import { isServer } from "solid-js/web";import { Nav } from "~/components/Nav";import "./app.css";const RootLayout = (props: ParentProps) => { const location = useLocation(); const locale = () => getLocaleFromPath(location.pathname) ?? defaultLocale; // 서버는 entry-server.tsx에서 <html>을 렌더링합니다; 로케일 간 클라이언트 측 // 탐색은 속성을 직접 업데이트해야 합니다. createEffect(() => { if (isServer) return; document.documentElement.lang = locale(); document.documentElement.dir = getHTMLTextDir(locale()); }); return ( <MetaProvider> <IntlayerProvider locale={locale()}> <Nav /> <Suspense>{props.children}</Suspense> </IntlayerProvider> </MetaProvider> );};export default function App() { return ( <Router root={RootLayout}> <FileRoutes /> </Router> );}IntlayerProvider는localeprop에 반응하므로 JSX 내부에서 접근자 호출locale()을 전달하는 것으로 충분합니다 — Solid는 이를 게터로 컴파일하며 URL이 변경될 때 전체 트리가 새로운 언어로 다시 렌더링됩니다.서버에서 HTML lang 및 dir 속성 설정하기
<html>엘리먼트는Router외부의entry-server.tsx에 의해 렌더링됩니다. 대신 요청 URL에서 로케일을 읽으세요:src/entry-server.tsx코드 복사코드를 클립보드에 복사
// @refresh reloadimport { createHandler, StartServer } from "@solidjs/start/server";import { defaultLocale, getHTMLTextDir, getLocaleFromPath } from "intlayer";import { getRequestEvent } from "solid-js/web";export default createHandler(() => ( <StartServer document={({ assets, children, scripts }) => { const url = getRequestEvent()?.request.url ?? "/"; const locale = getLocaleFromPath(url) ?? defaultLocale; return ( <html dir={getHTMLTextDir(locale)} lang={locale}> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="icon" href="/favicon.ico" /> {assets} </head> <body> <div id="app">{children}</div> {scripts} </body> </html> ); }} />));크롤러는 이제 첫 번째 바이트에서 올바른 언어를 받습니다:
html코드 복사코드를 클립보드에 복사
<html dir="ltr" lang="fr"></html>페이지에서 Intlayer 활용하기
애플리케이션 전체에서 콘텐츠 사전에 접근합니다:
src/routes/[[locale]]/index.tsx코드 복사코드를 클립보드에 복사
import { Meta, Title } from "@solidjs/meta";import { useIntlayer } from "solid-intlayer";import Counter from "~/components/Counter";export default function Home() { const content = useIntlayer("home-page"); return ( <main> <Title>{content.metaTitle.value}</Title> <Meta content={content.metaDescription.value} name="description" /> <h1>{content.title}</h1> <Counter /> <p>{content.documentation}</p> </main> );}Solid에서
useIntlayer는 반응형 콘텐츠(예:content)를 반환합니다. 해당 속성에 직접 접근할 수 있습니다.alt,title,href,aria-label등과 같은string속성에서 콘텐츠를 사용하려는 경우 다음과 같이 함수의 값을 사용할 수 있습니다:html코드 복사코드를 클립보드에 복사
<img src="{content.image.src.value}" alt="{content.image.value}" /><img src="{content.image.src.toString()}" alt="{content.image.toString()}" /><img src="{String(content.image.src)}" alt="{String(content.image)}" />useIntlayer훅에 대해 자세히 알아보려면 문서를 참조하세요.콘텐츠 노드는 일반 번역에만 국한되지 않습니다. 예를 들어 복수형 카운터:
src/components/Counter.content.ts코드 복사코드를 클립보드에 복사
import { type Dictionary, plural, t } from "intlayer";const counterContent = { key: "counter", content: { clicks: plural({ one: t({ en: "{{count}} click", fr: "{{count}} clic", es: "{{count}} clic", }), other: t({ en: "{{count}} clicks", fr: "{{count}} clics", es: "{{count}} clics", }), }), },} satisfies Dictionary;export default counterContent;src/components/Counter.tsx코드 복사코드를 클립보드에 복사
import { useIntlayer } from "solid-intlayer";import { createSignal } from "solid-js";export default function Counter() { const [count, setCount] = createSignal(0); const content = useIntlayer("counter"); return ( <button onClick={() => setCount(count() + 1)} type="button"> {content.clicks(count())} </button> );}plural()은 활성 로케일에 대해Intl.PluralRules를 통해 카테고리를 선택하므로 두 개 이상의 복수형 형태를 가진 언어도 추가 코드 없이 작동합니다.지역화된 Link 컴포넌트 생성하기
내부 URL에 현재 언어 접두사를 자동으로 붙이는 커스텀
Link컴포넌트를 생성합니다:src/components/LocalizedLink.tsx코드 복사코드를 클립보드에 복사
import { A, type AnchorProps } from "@solidjs/router";import { getLocalizedUrl } from "intlayer";import { useLocale } from "solid-intlayer";import type { ParentComponent } from "solid-js";export const LocalizedLink: ParentComponent<AnchorProps> = (props) => { const { locale } = useLocale(); const isExternal = () => /^[a-z][a-z0-9+.-]*:/i.test(props.href); const localizedHref = () => isExternal() ? props.href : getLocalizedUrl(props.href, locale()); return <A {...props} href={localizedHref()} />;};src/components/Nav.tsx코드 복사코드를 클립보드에 복사
import { useIntlayer } from "solid-intlayer";import type { Component } from "solid-js";import { LocaleSwitcher } from "./LocaleSwitcher";import { LocalizedLink } from "./LocalizedLink";export const Nav: Component = () => { const content = useIntlayer("nav"); return ( <nav> <LocalizedLink href="/">{content.home}</LocalizedLink> <LocalizedLink href="/about">{content.about}</LocalizedLink> <LocaleSwitcher /> </nav> );};이제
href="/about"을 한 번 작성하면 활성 로케일에 따라/about,/fr/about또는/es/about이 생성됩니다 — 페이지 내 어디에서도 수동으로 접두사를 붙일 필요가 없습니다.로케일 스위처 컴포넌트 생성하기
스위처를
<select>대신 실제 앵커로 렌더링합니다: 현재 페이지의 각 언어는 새 탭에서 열 수 있는 크롤링 가능한 링크가 되며, 이는 JavaScript 전용 컨트롤이 제공할 수 없는 장점입니다.getPathWithoutLocale은 현재 경로에서 로케일 세그먼트를 제거하고getLocalizedUrl은 대상 로케일에 맞게 이를 재구성하므로 하드코딩 없이 링크가 라우팅 모드를 따릅니다. 렌더링된 로케일을 변경하는 것은 탐색입니다 —[[locale]]라우트는 URL에서 이를 도출합니다 — 한편setLocale은 선택을INTLAYER_LOCALE쿠키에 유지하므로 나중에 로케일이 없는 URL을 방문할 때 동일한 언어로 해결됩니다.src/components/LocaleSwitcher.tsx코드 복사코드를 클립보드에 복사
import { A, useLocation } from "@solidjs/router"; import { getHTMLTextDir, getLocaleName, getLocalizedUrl, getPathWithoutLocale, } from "intlayer"; import { useIntlayer, useLocale } from "solid-intlayer"; import { type Component, For } from "solid-js"; export const LocaleSwitcher: Component = () => { const content = useIntlayer("locale-switcher"); const location = useLocation(); const { locale, setLocale, availableLocales } = useLocale(); // 현재 표시된 페이지의 표준(로케일 없는) 경로 const pathWithoutLocale = () => getPathWithoutLocale(location.pathname); return ( <div> <button aria-label={content.label.value} popoverTarget="localePopover" type="button" > {getLocaleName(locale())} </button> <div id="localePopover" popover="auto"> <For each={availableLocales}> {(localeItem) => ( <A dir={getHTMLTextDir(localeItem)} // 정확한 일치만 허용하여 기본 로케일 링크가 모든 페이지에서 // 활성화된 것으로 플래그 지정되지 않도록 함 end href={getLocalizedUrl(pathWithoutLocale(), localeItem)} hreflang={localeItem} lang={localeItem} onClick={() => setLocale(localeItem)} // 브라우저의 "뒤로 가기" 버튼이 이전 페이지로 돌아가도록 보장함 replace > {/* 자체 로케일에서의 언어 - 예: Français */} {getLocaleName(localeItem)} </A> )} </For> </div> </div> ); };Solid에서
useLocale의locale은 시그널 접근자(signal accessor)입니다. 현재 값을 반응형으로 읽으려면locale()(괄호 포함)을 사용하세요.getLocaleName(localeItem)은 각 언어를 해당 언어 자체로 렌더링합니다 —English / Français / Español. 두 번째 인수를 전달하면 대신 현재 표시된 언어로 이름을 번역합니다:getLocaleName(localeItem, locale())은 영어로English / French / Spanish, 한국어로영어 / 프랑스어 / 스페인어를 제공합니다.<A>는 현재 URL과 일치하는 링크에 이미aria-current="page"를 설정하므로 추가할 것이 없습니다.replace는 라우터에 의해 렌더링된 속성에서 다시 읽힙니다: 히스토리 항목을 푸시하는 대신 교체하므로 브라우저 "뒤로 가기" 버튼은 이전 언어의 동일한 페이지가 아니라 전환 전에 방문한 페이지로 돌아갑니다.각 링크의
dir및hreflang은 오른쪽에서 왼쪽으로 쓰는 언어 이름을 올바르게 지향하도록 유지하고 보조 기술 및 크롤러에 각 링크가 가리키는 언어를 알려줍니다.useLocale훅에 대해 자세히 알아보려면 문서를 참조하세요.canonical 및 hreflang 링크 내보내기
선택사항hreflang주석은/about,/fr/about및/es/about이 서로 다른 언어로 된 동일한 페이지임을 검색 엔진에 알립니다.getMultilingualUrls는 라우팅 모드에 따라 표준(로케일 없는) 경로에서 이를 도출하므로 아무것도 하드코딩되지 않습니다:src/components/AlternateLinks.tsx코드 복사코드를 클립보드에 복사
import { defaultLocale, getMultilingualUrls, getPathWithoutLocale,} from "intlayer";import { type Component, For } from "solid-js";export type AlternateLinksProps = { /** 렌더링되는 페이지의 절대 URL. */ url: string;};export const AlternateLinks: Component<AlternateLinksProps> = (props) => { const multilingualUrls = () => { const { origin, pathname } = new URL(props.url); return Object.entries( getMultilingualUrls(`${origin}${getPathWithoutLocale(pathname)}`) ); }; const canonicalUrl = () => new URL(props.url).origin + new URL(props.url).pathname; return ( <> <link href={canonicalUrl()} rel="canonical" /> <For each={multilingualUrls()}> {([locale, localizedUrl]) => ( <link href={localizedUrl} hreflang={locale} rel="alternate" /> )} </For> <link href={ multilingualUrls().find(([locale]) => locale === defaultLocale)?.[1] } hreflang="x-default" rel="alternate" /> </> );};이를 요청 URL을 사용할 수 있는 문서 head에 렌더링합니다:
src/entry-server.tsx코드 복사코드를 클립보드에 복사
import { AlternateLinks } from "~/components/AlternateLinks";// … <head> 내부, 다른 meta 태그 옆:<AlternateLinks url={url} />;GET /fr/about은 다음을 내보냅니다:html코드 복사코드를 클립보드에 복사
<link href="https://example.com/fr/about" rel="canonical" /><link href="https://example.com/about" hreflang="en" rel="alternate" /><link href="https://example.com/fr/about" hreflang="fr" rel="alternate" /><link href="https://example.com/es/about" hreflang="es" rel="alternate" /><link href="https://example.com/about" hreflang="x-default" rel="alternate" />@solidjs/meta에 대한 참고 사항: 작성 시점 기준으로@solidjs/meta의<Title>및<Meta>는 하이드레이션 후 클라이언트에 적용되지만 SolidStart v2의 서버 렌더링된<head>에는 내보내지지 않습니다. 상류에서 수정될 때까지 크롤러가 JavaScript 없이 봐야 하는 태그(canonical,hreflang, 필요한 경우title/description)를 위와 같이entry-server.tsx에 직접 렌더링하세요.404 페이지 관리하기
선택사항src/routes루트의 스플랫 라우트는matchFilters에 의해 거부된 잘못된 로케일 접두사를 포함하여 로케일 세그먼트와 일치하지 않은 모든 경로를 캡처합니다. 로케일은 여전히 루트 레이아웃을 통해 URL에서 오므로 404 페이지는 방문자의 언어로 표시됩니다:src/routes/[...404].tsx코드 복사코드를 클립보드에 복사
import { Title } from "@solidjs/meta";import { HttpStatusCode } from "@solidjs/start";import { useIntlayer } from "solid-intlayer";import { LocalizedLink } from "~/components/LocalizedLink";export default function NotFound() { const content = useIntlayer("not-found-page"); return ( <main> <Title>{content.metaTitle.value}</Title> <HttpStatusCode code={404} /> <h1>{content.title}</h1> <LocalizedLink href="/">{content.backHome}</LocalizedLink> </main> );}테이블의 모든 내용 표시테이블을 모달로 열어 모든 데이터를 명확하게 확인
요청 결과 /xx404—xx는 설정된 로케일이 아닙니다/nonexistent기본 로케일의 404/fr/nonexistent프랑스어의 404(Page introuvable)다국어 사이트맵 생성하기
선택사항Intlayer의 사이트맵 생성기는 모든 경로를 로케일당 하나의 항목으로 확장하고 이들 사이의
xhtml:link대체 항목을 연결하므로 라우트는 표준 및 로케일 없는 경로만 나열하면 됩니다.평면 URL만 내보내는 기본 생성기와 달리 Intlayer는 각 페이지의 모든 지역화된 변형 간에 양방향 링크를 연결하여 검색 엔진이 지역화된 URL을 관련시키고 올바른 대상에게 올바른 URL을 제공하도록 돕습니다.
SolidStart는 HTTP 메서드를 내보내는 파일을 API 라우트로 변환하고 경로에서
.ts확장자를 제거합니다 — 따라서src/routes/sitemap.xml.ts는/sitemap.xml에서 제공됩니다:src/routes/sitemap.xml.ts코드 복사코드를 클립보드에 복사
import type { APIEvent } from "@solidjs/start/server"; import { generateSitemap } from "intlayer"; const SITE_URL = process.env.SITE_URL ?? "http://localhost:3000"; export const GET = (_event: APIEvent) => { const sitemap = generateSitemap( [ { path: "/", changefreq: "daily", priority: 1.0 }, { path: "/about", changefreq: "monthly", priority: 0.8 }, ], { siteUrl: SITE_URL } ); return new Response(sitemap, { headers: { "Content-Type": "application/xml" }, }); };output of GET /sitemap.xml코드 복사코드를 클립보드에 복사
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml"> <url> <loc>https://example.com/about</loc> <changefreq>monthly</changefreq> <priority>0.8</priority> <xhtml:link rel="alternate" hreflang="en" href="https://example.com/about"/> <xhtml:link rel="alternate" hreflang="fr" href="https://example.com/fr/about"/> <xhtml:link rel="alternate" hreflang="es" href="https://example.com/es/about"/> <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/about"/> </url></urlset>API 라우트는 선택적 매개변수를 지원하지 않으므로 이 파일을
[[locale]]세그먼트 외부인src/routes루트에 유지하세요. 사이트맵에는 이미 모든 로케일이 포함되어 있습니다.getMultilingualUrls를 사용하여 동일한 방법으로robots.txt를 작성할 수 있으므로Disallow항목이 민감한 경로의 모든 지역화된 철자를 포함하도록 할 수 있습니다:src/routes/robots.txt.ts코드 복사코드를 클립보드에 복사
import { getMultilingualUrls } from "intlayer"; const SITE_URL = process.env.SITE_URL ?? "http://localhost:3000"; const disallowedPaths = ["/admin", "/private"].flatMap((path) => Object.values(getMultilingualUrls(path)) ); export const GET = () => new Response( [ "User-agent: *", "Allow: /", ...disallowedPaths.map((path) => `Disallow: ${path}`), "", `Sitemap: ${SITE_URL}/sitemap.xml`, ].join("\n"), { headers: { "Content-Type": "text/plain" } } );서버 함수에서 로케일 가져오기
선택사항서버 함수 또는 API 라우트 내부에서 현재 로케일에 접근하고 싶을 수 있습니다.
이와 같은 접두사 기반 설정에서는 URL이 권한을 가집니다:
getLocaleFromPath는 요청 URL에서 접두사를 읽습니다.getLocale은 로케일 접두사가 없는 요청에 대한 폴백입니다 —INTLAYER_LOCALE쿠키를 검사하고 그 다음x-intlayer-locale헤더를 검사한 후Accept-Language협상을 진행합니다.src/routes/[[locale]]/index.tsx코드 복사코드를 클립보드에 복사
import { createAsync } from "@solidjs/router";import { getCookie, getIntlayer, getLocale, getLocaleFromPath } from "intlayer";import { getRequestEvent } from "solid-js/web";const loadLocalizedData = async () => { "use server"; const request = getRequestEvent()?.request; const locale = getLocaleFromPath(request?.url) ?? (await getLocale({ // 요청에서 쿠키 가져오기 (기본값: 'INTLAYER_LOCALE') getCookie: (name) => getCookie(name, request?.headers.get("cookie") ?? ""), // 요청에서 헤더 가져오기 (기본값: 'x-intlayer-locale'), // Accept-Language 협상으로 폴백 getHeader: (name) => request?.headers.get(name) ?? undefined, })); // getIntlayer()를 사용하여 컴포넌트 외부에서 콘텐츠 가져오기 const content = getIntlayer("home-page", locale); return { locale, title: String(content.title) };};export default function Page() { const data = createAsync(() => loadLocalizedData()); return <p>{data()?.title}</p>;}여기서
getLocale에만 의존하지 마세요: 로케일 쿠키는 방문자가 언어를 적극적으로 전환할 때만 생성되므로/fr/...에 대한 첫 방문은 기본 로케일로 해결될 수 있습니다.컴포넌트 콘텐츠 추출하기
선택사항기존 코드베이스가 있는 경우 수천 개의 파일을 변환하는 데 시간이 걸릴 수 있습니다.
이 프로세스를 용이하게 하기 위해 Intlayer는 컴포넌트를 변환하고 콘텐츠를 추출할 수 있는 컴파일러 / 추출기를 제안합니다.
설정하려면
intlayer.config.ts파일에compiler섹션을 추가할 수 있습니다:intlayer.config.ts코드 복사코드를 클립보드에 복사
import { type IntlayerConfig } from "intlayer"; const config: IntlayerConfig = { // ... 나머지 설정 compiler: { /** * 컴파일러를 활성화할지 여부를 나타냅니다. */ enabled: true, /** * 출력 파일 경로를 정의합니다 */ output: ({ fileName, extension }) => `./${fileName}${extension}`, /** * 변환 후 컴포넌트를 저장할지 여부를 나타냅니다. * * - `true`인 경우 컴파일러가 디스크의 컴포넌트 파일을 다시 씁니다. 따라서 변환은 영구적이 되며 다음 프로세스에서 컴파일러가 변환을 건너뜁니다. 그렇게 하면 앱을 변환한 후 컴파일러를 제거할 수 있습니다. * * - `false`인 경우 컴파일러가 빌드 출력의 코드에만 `useIntlayer()` 함수 호출을 주입하고 기본 코드베이스는 그대로 유지합니다. 변환은 메모리에서만 수행됩니다. */ saveComponents: false, /** * 사전 키 접두사 */ dictionaryKeyPrefix: "", }, }; export default config;추출기를 실행하여 컴포넌트를 변환하고 콘텐츠를 추출합니다
bash코드 복사코드를 클립보드에 복사
npx intlayer extract5단계에서 설명한 이유로 나중에 생성된 페이지의 콘텐츠 파일을
src/routes외부로 이동하세요.TypeScript 설정하기
Intlayer는 모듈 확장을 사용하여 TypeScript의 이점을 얻고 코드베이스를 더욱 견고하게 만듭니다.
TypeScript 설정에 자동 생성된 타입이 포함되어 있는지 확인하세요:
tsconfig.json코드 복사코드를 클립보드에 복사
{ compilerOptions: { // ... 기존 설정 }, include: [ "src", "*.ts", ".intlayer/**/*.ts", // 자동 생성된 타입 포함 ],}사전 키 및 콘텐츠 경로가 이제 컴파일 시점에 검사됩니다:
tsx코드 복사코드를 클립보드에 복사
useIntlayer("home-page"); // ✅useIntlayer("hom-page"); // ❌ Argument of type '"hom-page"' is not assignable to parameter of type 'keyof __DictionaryRegistry'
설정 확인하기
서버를 빌드하고 시작한 후 다음 요청이 예상대로 동작하는지 확인하세요:
코드를 클립보드에 복사
npm run buildnode .output/server/index.mjs테이블을 모달로 열어 모든 데이터를 명확하게 확인
| 요청 | 예상 응답 |
|---|---|
GET / | 200 — 영어 |
GET / (Accept-Language: fr 포함) | 302 → /fr |
GET / (INTLAYER_LOCALE=es 쿠키 포함) | 302 → /es |
GET /fr | 200 — 프랑스어, <html lang="fr"> |
GET /fr/about | 200 — 프랑스어 about 페이지 |
GET /en/about | 302 → /about (표준 리디렉션) |
GET /xx | 404 |
GET /fr/nonexistent | 404 (프랑스어) |
GET /sitemap.xml | 200 — 다국어 XML 사이트맵 |
페이지를 렌더링하는 행은 vite dev에서도 동일하게 동작합니다. 3개의 리디렉션 행은 핸들러를 직접 미들웨어로 등록하지 않는 한 빌드된 서버에만 적용됩니다 (3단계 참조).
개발 서버를 Bun (bun --bun vite dev) 대신 Node (vite dev)에서 실행하세요: SolidStart의 SSR은 현재 Bun 런타임에서Expected a Response object, but received 'NodeResponse'오류로 실패합니다. 이는 Intlayer와 무관하며 (일반 템플릿에서도 재현됨)vite build가 아닌 개발 서버에만 영향을 미칩니다.
Git 설정
Intlayer에서 생성된 파일을 무시하는 것이 좋습니다. 이를 통해 Git 리포지토리에 커밋하는 것을 방지할 수 있습니다.
이렇게 하려면 .gitignore 파일에 다음 지침을 추가할 수 있습니다:
코드를 클립보드에 복사
# Intlayer에서 생성된 파일 무시.intlayerVS Code 확장 프로그램
Intlayer 개발 경험을 향상시키기 위해 공식 Intlayer VS Code 확장 프로그램을 설치할 수 있습니다.
이 확장 프로그램은 다음을 제공합니다:
- 번역 키 자동 완성.
- 누락된 번역에 대한 실시간 오류 감지.
- 번역된 콘텐츠의 인라인 미리보기.
- 번역을 쉽게 생성하고 업데이트할 수 있는 빠른 작업.
더 알아보기
더 자세히 알아보려면 비주얼 에디터를 구현하거나 CMS를 사용하여 콘텐츠를 외부화할 수 있습니다.