Hreflang タグと Next.js: 118 ページで 30 言語をいかにして展開したか

夜寝られなくなった数字をシェアしたい: 3,540。それは 30 言語かける 118 ページだ。Deluxe Astrology でこれらのすべてを同時にローンチしていたら、Google は何千ものシン、未翻訳、またはマシン生成のゴミのようなページをインデックスしていただろう。ランキングは急落していた。代わりに、言語を段階的にロールアウトし、Claude Haiku を言語あたり約 $22 でバッチ翻訳に使用し、Winston AI スコアリングで品質をゲートする二層翻訳ゲーティングシステムを構築した。すべてが Next.js の next-intl ミドルウェア、ロケール対応の正規 URL、HTML head と XML サイトマップの両方の hreflang タグで実行される。これはその全体的な詳細だ -- すべてのミドルウェア設定、すべてのサイトマップエントリ、すべてのコスト計算。

目次

Hreflang Tags in Next.js: How We Ship 30 Languages Across 118 Pages

2025 年に Hreflang タグがまだ重要な理由

Google の言語検出は改善されている。認めるぞ。だが「改善」は「解決」を意味しない。地域別バリアント -- pt-BR vs pt-PT、または zh-CN vs zh-TW を考えてみてください -- を実行している場合、Google には明示的なシグナルが必要だ。hreflang がなければ、ブラジルの Portuguese ページがポルトガルをターゲットしたコンテンツを共食いし、その逆も起こる。

データが語るもの:

  • 多言語サイトの 60% 以上が hreflang 設定エラーを抱えている (出典: Ahrefs の国際 SEO 監査に関する研究)
  • 適切な hreflang 実装は、4-6 週間以内にターゲットされた市場でクリックスルー率を 20-30% 引き上げることができる
  • hreflang がないサイトは言語バージョン間の予測不可能なランキング回転を経験し、パフォーマンス追跡をほぼ不可能にする

Deluxe Astrology では、30 言語をターゲットしており、それぞれ異なるコンテンツがある。地域別バリアントではなく -- 実際の異なる言語だ。それは検索結果で正しいバージョンを見つける必要のある 30 の異なるオーディエンスだ。ここで hreflang はオプションではない。それは基盤だ。

ほとんどのガイドが見落とすもの: HTML <head> XML サイトマップの両方で hreflang が必要だ。一方または他方ではなく。両方だ。Google は複数のソースから hreflang を処理することを確認しており、ここでの冗長性は無駄ではない -- それは保険だ。

3,540 ページ問題

アーキテクチャ全体を形成した数学を説明しよう。

Deluxe Astrology は以下を持っている:

  • 118 ページ (コアコンテンツページ)
  • 41 翻訳ネームスペース (翻訳可能文字列の論理的グループ)
  • 39 ロケール対応 API ルート
  • 30 ターゲット言語

30 × 118 = 3,540 の総ページバリアント。

1 日目にすべての 3,540 ページをローンチしたら、こういったことが起こる:

  1. ほとんどのページは英語フォールバックテキストを非英語 URL パスで含む。Google はこれをシン/重複コンテンツとして見る。
  2. Googlebot は何千の低品質ページをインデックスして、クロール予算を消費する。
  3. サイトの全体的な品質シグナルが低下し、良い英語ページもドラッグされる。
  4. 未翻訳ページに来たユーザーはすぐにバウンスする。

これは理論ではない。Weglot または同様のツールをプラグインして 20 言語のスイッチを一晩で入れたクライアントサイトでこれが起こるのを見たことがある。トラフィックは上がらず下がった。

解決策: すべての言語を一度にローンチしない。ゲートする。

二層翻訳ゲーティングシステム

30 言語を根本的に異なるローンチ戦略を持つ 2 つの層に分割した。

層 1: TRANSLATED_LOCALES

これらはネイティブスピーカーまたは検証済みのバイリンガルによって手動で確認された、完全に翻訳されたコアページを持つ 15 言語だ。

// config/locales.ts
export const TRANSLATED_LOCALES = [
  'en', 'es', 'fr', 'de', 'it', 'pt-BR', 'ja', 'ko',
  'zh-CN', 'zh-TW', 'ru', 'ar', 'hi', 'tr', 'nl'
] as const;

これら 15 言語は以下を得る:

  • すべての 118 ページにわたる完全な hreflang タグ
  • XML サイトマップへの含める
  • インデックス可能、正規 URL
  • ロケール対応スキーママークアップ

層 2: DYNAMIC_TRANSLATED_LOCALES

残りの 15 言語は英語のみのプレースホルダーとして開始する。それらはインデックスされない。hreflang エントリを取得しない。サイトマップに存在しない。

export const DYNAMIC_TRANSLATED_LOCALES = [
  'pl', 'sv', 'da', 'fi', 'no', 'cs', 'ro', 'hu',
  'el', 'th', 'vi', 'id', 'ms', 'uk', 'bg'
] as const;

export const ALL_LOCALES = [
  ...TRANSLATED_LOCALES,
  ...DYNAMIC_TRANSLATED_LOCALES
] as const;

層 2 の言語が翻訳パイプラインを完成させる -- Claude Haiku バッチ翻訳、Winston AI 品質ゲート、オプションで人間によるレビュー -- 時、それは層 1 にアップグレードする。hreflang エントリ、サイトマップ含める、インデックス指令は自動的に更新される。

// utils/locale-status.ts
export function isLocaleReady(locale: string): boolean {
  // すべての必要なネームスペースが
  // 翻訳を持つかチェック
  // Winston AI スコア >= 95%
  const status = getTranslationStatus(locale);
  return status.completedNamespaces >= REQUIRED_NAMESPACES
    && status.minQualityScore >= 0.95;
}

export function getIndexableLocales(): string[] {
  return ALL_LOCALES.filter(isLocaleReady);
}

これが重要な洞察だ: 呼びかけhreflang 実装は 動的 である必要がある。ビルド時にハードコードされた静的リストにできない (まあ、ロケールがアップグレードする時にリビルドするなら可能だが、ISR を使用してそれを行う):

Hreflang Tags in Next.js: How We Ship 30 Languages Across 118 Pages - architecture

Next-intl ミドルウェア設定

ミドルウェアはロケール検出、ルーティング、ゲーティングロジックすべてが収束するところだ。実際の middleware.ts を見ていこう:

// middleware.ts
import createMiddleware from 'next-intl/middleware';
import { NextRequest, NextResponse } from 'next/server';
import { ALL_LOCALES, TRANSLATED_LOCALES } from './config/locales';

const intlMiddleware = createMiddleware({
  locales: ALL_LOCALES,
  defaultLocale: 'en',
  localePrefix: 'always',
  localeDetection: true
});

export default function middleware(request: NextRequest) {
  const pathname = request.nextUrl.pathname;
  
  // パスからロケールを抽出
  const pathnameLocale = ALL_LOCALES.find(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
  );

  // ロケールが層 2 で、まだ準備ができていない場合、
  // コンテンツを提供するが noindex ヘッダーを追加
  if (
    pathnameLocale &&
    !TRANSLATED_LOCALES.includes(pathnameLocale) &&
    !isLocaleReady(pathnameLocale)
  ) {
    const response = intlMiddleware(request);
    response.headers.set('X-Robots-Tag', 'noindex, nofollow');
    return response;
  }

  return intlMiddleware(request);
}

export const config = {
  matcher: ['/((?!api|_next|_vercel|.*\\..*).*)'] 
};

ここで注意すべきことがいくつか:

  1. localePrefix: 'always' -- すべての URL がロケールプレフィックスを取得する。 /en/horoscope, /de/horoskop など。曖昧性がない。これは hreflang にとって重要だ。すべての代替 URL は異なり予測可能である必要があるため。

  2. 層 2 noindex -- 未翻訳のロケールはまだレンダリングされる (これらの地域のユーザーはまだ閲覧できる) が、noindex ヘッダーを取得する。Google はそれらに対してクロール予算を無駄にしない。

  3. マッチャー -- API ルート、Next.js インターナル、スタティックファイルを除外する。39 個のロケール対応 API ルートは独自のロケール処理を持つ。

同様のものを構築している場合、Next.js 開発アプローチと ミドルウェアがアーキテクチャにどのように適合するかについて詳しく書いている。

HTML Head の Hreflang 実装

Next.js 14+ with the App Router は generateMetadata 関数を与える。これは HTML <head> に hreflang タグが行く場所だ。

// app/[locale]/[...slug]/page.tsx
import { getIndexableLocales } from '@/utils/locale-status';
import { getLocalizedSlug } from '@/utils/slugs';

type Props = {
  params: { locale: string; slug: string[] };
};

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const { locale, slug } = params;
  const baseUrl = 'https://deluxeastrology.com';
  const pagePath = slug ? `/${slug.join('/')}` : '';
  const indexableLocales = getIndexableLocales();

  // 言語代替を構築 -- インデックス可能なロケールのみ
  const languages: Record<string, string> = {};
  
  for (const loc of indexableLocales) {
    const localizedSlug = await getLocalizedSlug(pagePath, loc);
    languages[loc] = `${baseUrl}/${loc}${localizedSlug}`;
  }

  // x-default は英語を指す
  languages['x-default'] = `${baseUrl}/en${pagePath}`;

  return {
    title: await getLocalizedTitle(pagePath, locale),
    alternates: {
      canonical: `${baseUrl}/${locale}${pagePath}`,
      languages
    }
  };
}

これは次のような HTML を生成する:

<link rel="canonical" href="https://deluxeastrology.com/de/horoskop" />
<link rel="alternate" hreflang="en" href="https://deluxeastrology.com/en/horoscope" />
<link rel="alternate" hreflang="de" href="https://deluxeastrology.com/de/horoskop" />
<link rel="alternate" hreflang="fr" href="https://deluxeastrology.com/fr/horoscope" />
<!-- ... インデックス可能な 12 言語以上 ... -->
<link rel="alternate" hreflang="x-default" href="https://deluxeastrology.com/en/horoscope" />

2 つの重要な詳細:

  1. 正規 URL はロケール固有だ。 ドイツのページの正規は英語ではなく、ドイツの URL だ。各言語バージョンは独自の正規ページだ。
  2. x-default は常に存在する。 英語を指す。Google はユーザーの言語を hreflang エントリのいずれかと照合できない場合、x-default はフォールバックだ。

Hreflang エントリを含むサイトマップ生成

HTML <head> hreflang は必要だが十分ではない。3,540 個の潜在的なページバリアントを持つサイトでは、XML サイトマップに hreflang も必要だ。理由はこうだ: Google はすべてのページを最初にクロールすることなく、サイトマップから hreflang 関係を発見できる。

// app/sitemap.ts
import { MetadataRoute } from 'next';
import { getIndexableLocales } from '@/utils/locale-status';
import { getAllPages } from '@/utils/pages';
import { getLocalizedSlug } from '@/utils/slugs';

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const baseUrl = 'https://deluxeastrology.com';
  const indexableLocales = getIndexableLocales();
  const pages = await getAllPages(); // 118 ページ定義を返す

  const entries: MetadataRoute.Sitemap = [];

  for (const page of pages) {
    for (const locale of indexableLocales) {
      const localizedSlug = await getLocalizedSlug(page.path, locale);
      const url = `${baseUrl}/${locale}${localizedSlug}`;

      // このページの代替を構築
      const alternates: Record<string, string> = {};
      for (const altLocale of indexableLocales) {
        const altSlug = await getLocalizedSlug(page.path, altLocale);
        alternates[altLocale] = `${baseUrl}/${altLocale}${altSlug}`;
      }
      alternates['x-default'] = `${baseUrl}/en${page.path}`;

      entries.push({
        url,
        lastModified: page.updatedAt,
        changeFrequency: page.changeFreq || 'weekly',
        priority: locale === 'en' ? 0.9 : 0.8,
        alternates: {
          languages: alternates
        }
      });
    }
  }

  return entries;
}

これは XML を生成する:

<url>
  <loc>https://deluxeastrology.com/de/horoskop</loc>
  <lastmod>2025-01-15</lastmod>
  <xhtml:link rel="alternate" hreflang="en" href="https://deluxeastrology.com/en/horoscope"/>
  <xhtml:link rel="alternate" hreflang="de" href="https://deluxeastrology.com/de/horoskop"/>
  <xhtml:link rel="alternate" hreflang="fr" href="https://deluxeastrology.com/fr/horoscope"/>
  <xhtml:link rel="alternate" hreflang="x-default" href="https://deluxeastrology.com/en/horoscope"/>
</url>

15 のインデックス可能なロケールと 118 ページで、それは 1,770 のサイトマップエントリだ。管理可能だ。すべての 30 言語が準備できたら、それは 3,540 になるだろう。それでも Google の 50,000 URL サイトマップ制限内だが、私たちはロケール毎のサイトマップに分割して Google Search Console モニタリングをより清潔にしている。

翻訳パイプライン: Claude Haiku + Winston AI

ここで経済学が興味深くなる。118 ページを 41 のネームスペースにわたって 30 言語に翻訳する必要があった。プロの人間による翻訳は金銭的に最高だろうが、予算の計算は残酷だ。

パイプライン

  1. 抽出 -- 41 個のネームスペースからすべての翻訳可能な文字列を構造化 JSON に引き出す
  2. 翻訳 -- ドメイン (占星術)、トーン、ターゲットオーディエンスに関するコンテキスト付きで Claude Haiku (Anthropic の高速で安価なモデル) を通じてバッチ処理する
  3. 品質ゲート -- 翻訳されたコンテンツを Winston AI のコンテンツ検出と品質スコアリングで実行する。閾値: 95%+ またはリジェクト。
  4. 人間によるレビュー -- 高価値ページ (ホームページ、ランディングページ、マネーページ) がネイティブスピーカーによる手動レビューを取得する
  5. アップグレード -- すべてのネームスペースが品質ゲートをパスすると、ロケールは DYNAMIC_TRANSLATED_LOCALES から TRANSLATED_LOCALES に移動する
// scripts/translate-locale.ts
async function translateLocale(targetLocale: string) {
  const namespaces = await getNamespaces(); // 41 ネームスペース
  
  for (const ns of namespaces) {
    const sourceStrings = await loadNamespace('en', ns);
    
    const translated = await claude.messages.create({
      model: 'claude-3-haiku-20240307',
      max_tokens: 4096,
      system: `占星術コンテンツを専門とするプロの翻訳者です。
               英語から${getLanguageName(targetLocale)}に翻訳してください。
               占星術の用語精度を維持してください。
               {name} と {date} のようなすべての補間変数を保持してください。`,
      messages: [{
        role: 'user',
        content: `これらの JSON キー値ペアを翻訳してください。有効な JSON のみを返してください:\n${JSON.stringify(sourceStrings, null, 2)}`
      }]
    });

    const qualityScore = await winstonAI.analyze(translated.content);
    
    if (qualityScore >= 0.95) {
      await saveNamespace(targetLocale, ns, translated.content);
    } else {
      await flagForReview(targetLocale, ns, translated.content, qualityScore);
    }
  }
}

Claude Haiku との言語あたりのコストは、すべての 118 ページを 41 ネームスペースにわたって約 $22 になる。それはほとんどトークンコストだ -- Haiku は入力トークン 100 万あたり $0.25、出力トークン 100 万あたり $1.25 (2025 年の価格) で非常に安い。

コスト比較: 当社のアプローチと代替案

これはテーブルが Deluxe Astrology チームを説得した:

アプローチ 30 言語のコスト 継続的なコスト 品質 ローンチまでの時間
Claude Haiku + Winston AI ~$660 合計 ($22/言語) $0 (ワンタイム) 95%+ 品質ゲート、主要ページの人間によるレビュー 2-3 週間ローリング
Weglot $0 セットアップ $699/月 ($8,388/年) 機械翻訳、編集可能 インスタントだがリスク
プロの翻訳者 $150K-$300K ($5K-10K/言語) $2K-5K/言語 更新用 最高品質 3-6 ヶ月
DeepL API ~$400 合計 $0 (ワンタイム) 良いが品質ゲートなし 1-2 週間
Google Translate API ~$300 合計 $0 (ワンタイム) ニッチコンテンツの品質が低い 1 週間

率直に言おう: 30 言語の Claude Haiku 翻訳の $660 合計は、ほぼ疑わしいほど安い。落とし穴は品質ゲート (Winston AI) と人間によるレビュー層が必要であることだ。これらのコストが含まれても -- Winston AI API 呼び出しで $50-100、高価値ページの人間によるレビューで $500-1,000 -- あなたはまだ $2,000 合計以下だ。Weglot の $699/月と比較してみてください。3 ヶ月未満で回収される。

Weglot と同様のサービスの本当のキラー: すべてを一度に翻訳する。ゲートなし。ページあたりの品質管理なし。スイッチを入れると突然 Google は 3,540 ページを見る、その多くは平凡な機械翻訳だ。当社のアプローチは外科的なやり方である。

このような規模のプロジェクトについて、当社がどのようにアプローチするかについてはさらに詳しく説明している。価格設定ページ -- 翻訳パイプラインは大規模な ヘッドレス CMS 開発 アーキテクチャのコンポーネントだ。

ロケール対応のスキーママークアップ

このつはほぼ全員を驚かせる。構造化データはページ言語と一致する必要がある。英語 FAQ スキーマを持つドイツのページは Google のページ言語理解を混乱させる。

// utils/schema.ts
export function generateFAQSchema(
  faqs: Array<{ question: string; answer: string }>,
  locale: string
) {
  return {
    '@context': 'https://schema.org',
    '@type': 'FAQPage',
    'inLanguage': locale, // 重要: ページロケールと一致する必要がある
    'mainEntity': faqs.map((faq) => ({
      '@type': 'Question',
      'name': faq.question, // ターゲット言語で記述する必要がある
      'acceptedAnswer': {
        '@type': 'Answer',
        'text': faq.answer // ターゲット言語で記述する必要がある
      }
    }))
  };
}

inLanguage をサポートするすべてのスキーマ型はそれを使用する必要がある。Deluxe Astrology では、これが含まれる:

  • FAQPage -- ターゲット言語での質問と回答
  • Article -- inLanguage がロケールと一致
  • WebPage -- inLanguage プロパティ
  • BreadcrumbList -- ターゲット言語のパンくずリスト名

単に目に見えるコンテンツを翻訳して構造化データを忘れる。Google は両方を読む。

ランキングを急落させる一般的な間違い

x-default hreflang がない

私はこれを絶えず見かける。サイトはすべての言語に hreflang を実装するが x-default を忘れる。これなしで、Google はユーザーの言語がバージョンのいずれかと一致しない場合のフォールバックがない。常にそれを含める。常にプライマリ言語 (通常は英語) を指す。

URL vs コンテンツのロケール不一致

URL が /fr/horoscope と言っているが、ページコンテンツが英語である場合、翻訳がロードされていないか、フォールバックしているため、Google はこれをソフト 404 またはシンコンテンツとしてフラグを付ける。これは正確に当社が二層ゲーティングシステムを構築した理由だ -- ページはフランス語コンテンツを持つまでフランス語 URL を取得しない。

すべての言語を一度にローンチ

これを既に叩いているが、繰り返す価値がある。30 言語を同時にローンチすることは国際 SEO の最も一般的な間違いだ。翻訳が完璧でも、Google が一晩中に何千もの新しいページをクロール、インデックス、評価するよう求めている。3-5 言語のバッチでロールアウトする。GSC でインデックス作成を監視する。その後さらに追加する。

相互的でない hreflang タグ

ページ A (英語) が hreflang でページ B (ドイツ語) を指す場合、ページ B はページ A に指し戻す必要がある。この相互的なリンクがない場合、Google は hreflang を無視する。これらを動的に生成する場合 (当社が行う場合)、相互的性は自動だ。しかし手動で管理している場合は、定期的に監査する。

自己参照 hreflang がない

すべてのページは独自の hreflang セットに自分自身を含める必要がある。ドイツのページは hreflang="de" を自分を指すもので一覧表示する必要がある。これは手動実装では見落としやすい。

hreflang が 1 つの場所のみ

<head> またはサイトマップのみに hreflang を入れることは間違いだ。両方を使用する。ベルトとサスペンダー。Google は両方のソースを処理し、いずれかがクロール対象にならない場合、他方がバックアップとして機能する。

このスケールのプロジェクトについて、経験豊かなチームを持つことはこれらの落とし穴を避けるのに役立つ。多言語ビルドを計画している場合、私たちはアプローチについて 話し合う ことを喜んでしている。

FAQ

言語の違いのみ (地域別ではない) の場合、hreflang タグが必要ですか?

はい。Google の言語検出は 2025 年に改善されているが、hreflang は依然として検索エンジンにどの言語バージョンを提供するかを伝えるための決定的なシグナルだ。それらなしで、Google は単に英語バージョンがより多くのバックリンクを持っているため、単に英語ページをフランス語話者に表示する危険がある。hreflang は 10+ の言語がある場合さらに重要になる -- 言語間のカニバライゼーション確率は規模が増加するにつれて劇的に増加する。

単一ページの hreflang エントリが多すぎるのはいくつですか?

Google は公式な制限を発表していないが、実践的なテストでは、単一ページあたり 50 言語バリアント以上を超えると、収益逓減が見られ、たまに解析問題が起こる。30 言語セットアップでは、各ページは 31 の hreflang エントリ (30 言語 + x-default) を持ち、安全ゾーン内に十分にある。50+ の地域と言語の組み合わせを扱っている場合、<head> サイズを管理可能に保つために XML サイトマップアプローチのみの使用を検討する。

HTML head、XML サイトマップ、HTTP ヘッダーのどれを使用する必要がありますか?

Next.js アプリケーションについて、HTML <head> と XML サイトマップ両方を使用する。HTTP ヘッダーは主に PDF のような非 HTML リソースに役立つ。HTML <head> アプローチはクロール時に処理され、最速のシグナルを与える。サイトマップはバックアップとして機能し、Google が まだクロールしていない代替ページを発見する。単一の方法に依存することはお勧めしない。

2025 年にフルウェブサイトを AI で翻訳するコストはいくらですか?

Claude Haiku を使用して、41 のネームスペースにわたって 118 ページを翻訳すると、言語あたり約 $22 になる。30 言語で、それは約 $660 合計だ。Winston AI 品質ゲーティングを $50-100 程度で追加し、高価値ページの人間によるレビューをオプションで $500-1,000 で追加すると、オール・イン・コストは $2,000 未満だ。Weglot の $699/月または専門翻訳サービスの $5,000-10,000 言語と比較する。

すべてを一度に翻訳する代わりに、二層翻訳ゲーティングシステムを使用する理由は何ですか?

Google はシンコンテンツを負の品質シグナルとして扱い、ドメイン全体をドラッグダウンさせる。30 言語をローンチしても、15 のみが品質翻訳を持つ場合、これら 15 の不十分に翻訳された言語は約 1,770 の低品質ページを作成する。二層システムは 95%+ 品質閾値を満たすページのみがインデックスされることを保証する。言語は品質ゲートを通過するにつれて層 2 から層 1 にアップグレードし、ロールアウト全体を通じてドメイン権限を保護する。

ロケールが部分的に翻訳されている場合、翻訳されていないページをどのように処理しますか?

いくつかのネームスペースが翻訳されているがそれ以外が翻訳されていないロケールについて、英語コンテンツにフォールバックし、ミドルウェア経由で noindex メタタグを追加する。URL はまだ解決する (ユーザーはアクセスできる) が、Google は混在言語ページをインデックスしない。すべての必要なネームスペースが品質ゲートをパスすると、noindex タグは削除され、hreflang エントリが追加される。これは部分翻訳がインデックスを汚染するのを防ぐ。

AI 翻訳に使用すべき品質スコア閾値は何ですか?

Winston AI で 95%+ の品質スコア閾値を使用する。それ以下のものは人間によるレビューまたは調整されたプロンプトでの再翻訳のためにフラグが付けられる。実際には、Claude Haiku は最初のパス上のネームスペースバッチの約 85% で 95%+ を達成する。残りの 15% は通常、ドメイン固有の用語 (直接翻訳されない占星術の用語) または複雑な文構造のために失敗する。90% 閾値はかなり不器用なフレーズを通す。

Hreflang を持つ多言語サイトのために Next.js の代わりに Astro を使用できますか?

絶対に。Astro は Astro 4.0+ の時点で優れた i18n サポートをビルトインしており、その静的生成モデルは実際に hreflang 実装を簡略化する。すべての URL がビルド時に既知であるため。両方のフレームワークを使用した多言語プロジェクトを構築してきた。大量の動的コンテンツと API ルート (Deluxe Astrology の 39 のロケール対応エンドポイントのような) を持つサイトでは、Next.js が最適なフィットだ。対話的でしょり、コンテンツが多いサイトでは、Astro 開発 がより高速でパフォーマンスを発揮できる。hreflang の原則は、フレームワークに関係なく同じだ。