TanStack Start v1에서 페이지 메타데이터를 번역하는 방법

검색 및 소셜 미디어를 위한 메타데이터 번역

문제

페이지 메타데이터(제목 및 설명)는 브라우저 탭, 북마크, 검색 결과, 소셜 미디어 미리보기에 표시됩니다. 메타데이터 언어가 페이지 콘텐츠 언어와 일치하지 않으면 사용자는 페이지를 보기도 전에 불편한 불일치를 경험하게 됩니다. 스페인어 페이지가 검색 결과에서 영어 제목으로 표시되면 낮은 현지화 품질을 나타냅니다. 검색 엔진은 이러한 불일치를 순위 신호로 해석하여 언어별 검색 결과에서 가시성을 낮출 수 있습니다.

이러한 불일치는 발견 단계에서 사용자 경험을 저해합니다. 선호하는 언어로 검색하는 사용자는 메타데이터가 일치하기를 기대하며, 불일치는 첫 클릭 전에 신뢰를 무너뜨립니다.

해결 방법

페이지 콘텐츠와 동일한 번역 리소스를 사용하여 페이지 메타데이터를 번역하세요. 라우트 구성에서 번역된 문자열에 액세스하고 현지화된 제목 및 설명 메타데이터를 반환하는 head 함수를 정의하세요. 이를 통해 브라우저 크롬, 검색 결과 및 렌더링된 페이지에 표시되는 내용 간의 일관성을 보장합니다.

head 함수 내에서 react-intl의 메시지 포맷팅을 사용하면 메타데이터가 번역 워크플로와 동기화된 상태를 유지하고 로케일이 변경될 때 자동으로 업데이트됩니다.

단계

1. React 컴포넌트 외부에서 메시지를 포맷하는 헬퍼 생성

head 함수는 훅을 사용할 수 없는 React 컴포넌트 트리 외부에서 실행됩니다. react-intl의 createIntl를 사용하여 메시지를 포맷하는 유틸리티를 생성하세요.

import { createIntl, createIntlCache } from "react-intl";

const cache = createIntlCache();

export function formatMetadataMessage(
  locale: string,
  messages: Record<string, string>,
  id: string,
  values?: Record<string, string | number>,
): string {
  const intl = createIntl({ locale, messages }, cache);
  return intl.formatMessage({ id }, values);
}

이 헬퍼는 head 함수와 같은 비React 컨텍스트에서 사용할 수 있도록 필요에 따라 intl 인스턴스를 생성합니다.

2. 메타데이터 번역 키 정의

현지화된 메타데이터가 필요한 각 페이지에 대해 번역 파일에 제목 및 설명 키를 추가합니다.

export const enMessages = {
  "page.home.title": "Welcome to Our Site",
  "page.home.description": "Discover amazing content in your language",
  "page.about.title": "About Us",
  "page.about.description": "Learn more about our mission and team",
};

export const esMessages = {
  "page.home.title": "Bienvenido a Nuestro Sitio",
  "page.home.description": "Descubre contenido increíble en tu idioma",
  "page.about.title": "Acerca de Nosotros",
  "page.about.description": "Conoce más sobre nuestra misión y equipo",
};

이러한 키는 컴포넌트 번역과 동일한 패턴을 따르며, 모든 로컬라이즈된 콘텐츠를 한 곳에 유지합니다.

3. 라우트에 head 함수 추가

createFileRoutehead 옵션을 사용하여 번역된 메타데이터를 반환하세요. 라우트 파라미터 또는 로더 데이터에서 현재 로케일에 액세스한 다음 헬퍼를 사용하여 메시지를 포맷하세요.

import { createFileRoute } from "@tanstack/react-router";
import { formatMetadataMessage } from "../utils/formatMetadataMessage";
import { enMessages, esMessages } from "../i18n/messages";

const messagesByLocale = {
  en: enMessages,
  es: esMessages,
};

export const Route = createFileRoute("/$locale/about")({
  head: ({ params }) => {
    const locale = params.locale || "en";
    const messages = messagesByLocale[locale] || messagesByLocale.en;

    return {
      meta: [
        {
          title: formatMetadataMessage(locale, messages, "page.about.title"),
        },
        {
          name: "description",
          content: formatMetadataMessage(
            locale,
            messages,
            "page.about.description",
          ),
        },
      ],
    };
  },
  component: AboutPage,
});

function AboutPage() {
  return <div>About content</div>;
}

head 함수는 라우트 매칭 중에 실행되며 TanStack Start가 문서 헤드에 렌더링하는 메타데이터 객체를 반환합니다.

4. 동적 메타데이터에 로더 데이터 사용

메타데이터가 가져온 데이터에 의존하는 경우 head 함수에서 loaderData에 액세스하여 동적 콘텐츠를 번역된 템플릿과 결합하세요.

import { createFileRoute } from "@tanstack/react-router";
import { formatMetadataMessage } from "../utils/formatMetadataMessage";
import { enMessages, esMessages } from "../i18n/messages";

const messagesByLocale = {
  en: enMessages,
  es: esMessages,
};

export const Route = createFileRoute("/$locale/posts/$postId")({
  loader: async ({ params }) => {
    const post = await fetchPost(params.postId);
    return { post };
  },
  head: ({ params, loaderData }) => {
    const locale = params.locale || "en";
    const messages = messagesByLocale[locale] || messagesByLocale.en;
    const { post } = loaderData;

    return {
      meta: [
        {
          title: formatMetadataMessage(locale, messages, "page.post.title", {
            title: post.title,
          }),
        },
        {
          name: "description",
          content: post.excerpt,
        },
      ],
    };
  },
  component: PostPage,
});

function PostPage() {
  const { post } = Route.useLoaderData();
  return <article>{post.content}</article>;
}

로더는 head 함수가 실행되기 전에 데이터를 가져오므로 동적 값을 번역된 메타데이터 템플릿에 삽입할 수 있습니다.