React Router v7에서 페이지 메타데이터를 번역하는 방법

검색 및 소셜을 위한 메타데이터 번역

문제

페이지 메타데이터(제목 및 설명)는 페이지 자체 외부인 브라우저 탭, 북마크, 검색 결과 및 소셜 미디어 미리보기에 표시됩니다. 이 메타데이터가 페이지의 언어와 일치하지 않으면 불협화음이 발생합니다. 영어 제목이 있는 스페인어 페이지는 사용자가 콘텐츠를 보기도 전에 혼란을 줍니다. 검색 엔진은 이러한 불일치를 페이지가 제대로 현지화되지 않았거나 품질이 낮다는 신호로 해석하여 언어별 결과에서 순위가 낮아질 수 있습니다. 사용자는 페이지가 자신의 언어가 아니라고 가정하고 로드되기 전에 페이지를 떠날 수 있습니다.

솔루션

라우트 모듈에서 meta 함수를 내보내 현재 언어와 일치하도록 페이지 메타데이터를 번역합니다. Message Descriptors와 함께 react-intl의 formatMessage API를 사용하여 제목 및 설명 문자열을 번역하고, 메타데이터가 페이지 콘텐츠와 동일한 번역 리소스를 사용하도록 합니다. 이렇게 하면 브라우저 탭, 검색 결과 및 페이지 자체에 표시되는 내용 간에 모든 것이 일관되게 유지됩니다.

단계

1. 컴포넌트 외부에서 intl에 액세스하는 헬퍼 생성

intl 객체는 formatMessage를 제공하며 컴포넌트에서는 useIntl 훅을 통해 액세스하거나 React가 아닌 환경에서는 createIntl로 직접 생성할 수 있습니다. meta 함수는 React 컴포넌트 트리 외부에서 실행되므로 메시지에서 intl 인스턴스를 빌드하는 헬퍼를 생성합니다.

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

const cache = createIntlCache();

export function createIntlForLocale(
  locale: string,
  messages: Record<string, string>,
) {
  return createIntl(
    {
      locale,
      messages,
    },
    cache,
  );
}

이 헬퍼는 React 컴포넌트뿐만 아니라 모든 함수에서 메시지를 포맷할 수 있는 intl 인스턴스를 생성합니다.

2. 상위 라우트 로더에서 메시지 로드

라우트 로더는 컴포넌트가 loaderData props를 통해 액세스하는 데이터를 반환합니다. 하위 라우트에서 사용할 수 있도록 상위 라우트에서 번역 메시지를 로드합니다.

import type { Route } from "./+types/root";

export async function loader({ request }: Route.LoaderArgs) {
  const url = new URL(request.url);
  const locale = url.pathname.split("/")[1] || "en";

  const messages = await import(`../translations/${locale}.json`);

  return {
    locale,
    messages: messages.default,
  };
}

meta 함수는 일치하는 모든 라우트의 로더 데이터를 포함하는 matches 매개변수를 받아, 상위 로더 데이터를 하위 라우트 meta 함수에서 접근할 수 있게 합니다.

3. 메타데이터를 번역하는 meta 함수 내보내기

메타 설명자 객체 배열을 반환하는 meta 함수를 라우트 모듈에서 내보냅니다. matches에서 상위 로더 데이터에 접근하고 intl 헬퍼를 사용하여 문자열을 번역합니다.

import type { Route } from "./+types/product";
import { createIntlForLocale } from "~/utils/intl";

export function meta({ matches }: Route.MetaArgs) {
  const rootMatch = matches.find((match) => match.id === "root");
  const { locale, messages } = rootMatch?.data || {
    locale: "en",
    messages: {},
  };

  const intl = createIntlForLocale(locale, messages);

  return [
    {
      title: intl.formatMessage({
        id: "product.meta.title",
        defaultMessage: "Product Details",
      }),
    },
    {
      name: "description",
      content: intl.formatMessage({
        id: "product.meta.description",
        defaultMessage: "View detailed information about this product",
      }),
    },
  ];
}

formatMessage 함수는 id와 defaultMessage를 포함하는 Message Descriptor를 받아 현재 로케일에 대한 번역된 문자열을 반환합니다.

4. 메시지 파일에 번역된 메타데이터 문자열 추가

formatMessage가 찾을 수 있도록 각 로케일의 메시지 파일에 메타데이터 번역 키를 추가합니다.

{
  "product.meta.title": "Détails du produit",
  "product.meta.description": "Voir les informations détaillées sur ce produit"
}

사용자가 이 라우트로 이동하면 루트 레이아웃의 Meta 컴포넌트가 라우트 meta 내보내기로 생성된 모든 메타 태그를 렌더링하여 페이지 언어와 일치하는 번역된 제목과 설명을 표시합니다.