DEV Community

Image Splitting Field Notes
Image Splitting Field Notes

Posted on

Building a Multilingual Game Guide in Astro Without Duplicate-Content Pages

Multilingual SEO pages are easy to generate and surprisingly easy to get wrong.

If every locale is a literal translation of one template, readers get awkward copy and search engines get a thin cluster of near-duplicates. If every language becomes a completely separate page, editorial updates drift and technical SEO becomes hard to audit.

I ran into that trade-off while building an Astro field guide for a newly released game. One guide query — the location and use of a specific key — needed an English page plus localized versions for Japanese, Spanish, French, German, and Traditional Chinese.

Here is the implementation pattern I used.

1. Keep page structure in Astro and editorial content in data

The page component owns the stable structure:

  • breadcrumb
  • answer summary
  • quick facts
  • route steps
  • FAQ
  • sources
  • related guides

Localized content lives in a typed JSON collection. Each entry carries its own locale, route, title, description, headings, steps, FAQ answers, and source note.

The dynamic route stays small:

export function getStaticPaths() {
  return guideData.guides.map((guide) => ({
    params: { locale: guide.route.split("/")[1] },
    props: { guide },
  }));
}

const { guide } = Astro.props;
Enter fullscreen mode Exit fullscreen mode

This gives editors one predictable content contract while allowing each locale to use natural terminology instead of inheriting English sentence structure.

It also makes omissions visible. If a localized page is missing a route step, source note, or FAQ answer, that is a data problem rather than a hidden template branch.

2. Build one complete hreflang cluster

The English page lives at the canonical query URL. Localized versions use stable locale folders.

const alternates = [
  { hrefLang: "en-US", path: "/blue-key-location/" },
  ...allGuides.map((item) => ({
    hrefLang: item.locale,
    path: item.route,
  })),
];
Enter fullscreen mode Exit fullscreen mode

The shared layout turns those entries into absolute alternate links using the production site URL. It also emits an x-default URL that points to the English guide.

Every page in the cluster references the same set of alternates. This matters more than simply placing a language switcher in the visible navigation: the HTML metadata needs to describe the relationship consistently in both directions.

Each page also receives its own self-referencing canonical. Localized pages never canonicalize back to English, because they are intended to stand as distinct language results.

3. Generate structured data from the same content

The article data already contains the information needed for several useful schema types, so I generate JSON-LD from the same source instead of maintaining a second SEO-only copy.

The page emits:

  • Article for the guide itself
  • BreadcrumbList for navigation context
  • FAQPage from the visible questions and answers
  • HowTo only when the page contains an ordered route

The conditional HowTo block is important. Adding process schema to every article would make the markup less accurate.

const routeSection = guide.sections.find(
  (section) => section.steps?.length
);

const howTo = routeSection && {
  "@context": "https://schema.org",
  "@type": "HowTo",
  name: routeSection.heading,
  step: routeSection.steps.map((step, index) => ({
    "@type": "HowToStep",
    position: index + 1,
    name: step,
    text: step,
  })),
};
Enter fullscreen mode Exit fullscreen mode

Because the visible guide and JSON-LD share one data source, a route correction updates both.

4. Localize search intent, not just sentences

The most useful change was editorial rather than technical.

A guide page is not merely a translation target. Players in different languages may use different names for the same landmark, key item, platform feature, or region. A literal translation can be grammatically correct while still missing the phrase players actually search.

Each locale therefore owns:

  • its native title and meta description
  • its preferred landmark terminology
  • short answer wording
  • UI labels
  • source and update notes

The underlying route remains evidence-aligned across languages, but the phrasing is written for the reader in that locale.

I also keep dated evidence labels visible. Game information changes after release, so “verified on” is more honest and maintainable than presenting every fact as permanent.

5. Make production URLs part of the build

The site is static, but canonical URLs, hreflang links, sitemap entries, and JSON-LD still need the real production origin.

The build uses a single environment variable:

SITE_URL=https://beastofreincarnation.co npm run build
npm run check
Enter fullscreen mode Exit fullscreen mode

That removes hard-coded development origins and makes the generated output portable across static hosts.

After building, I check the rendered HTML rather than trusting the component source:

  • canonical is absolute and self-referencing
  • every alternate URL returns 200
  • the cluster is reciprocal
  • x-default resolves
  • JSON-LD parses
  • the page is not accidentally noindex

Live example

The finished query cluster is visible in the Beast of Reincarnation Blue Key location guide. The English page links to five localized versions, and each version is generated from the same Astro page shape while retaining locale-specific copy.

The pattern is intentionally small: one component, one content contract, one alternate cluster, and one build-time origin. That has been easier to review than either duplicating page files or building a translation layer inside the component.

What I would reuse on another project

For another multilingual content site, I would keep the same boundaries:

  1. Put layout and schema generation in components.
  2. Put claims, steps, labels, and sources in typed content.
  3. Give every locale a real canonical URL.
  4. Generate a complete reciprocal hreflang cluster.
  5. Localize the query language, not only the prose.
  6. Validate rendered production HTML after every content expansion.

The goal is not to manufacture more URLs. It is to make each localized URL useful enough to deserve being indexed.

Disclosure

I used AI assistance to organize and edit this article. I verified the implementation details against the live Astro project and its rendered production output before publishing.

Top comments (0)