# General Translation Knowledge Base: How do I translate SEO titles and meta descriptions?
URL: https://generaltranslation.com/en-US/kb/translate-seo-metadata.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Write each language version's title and meta description in that language, using the words that market searches for, which often differ from a literal translation. Then localize the tags around them, including Open Graph, canonical, hreflang, html lang, and structured data. General Translation's gt-next translates titles and descriptions inside generateMetadata with getGT.
Category: Multilingual SEO
Last updated: 2026-10-28
Authors: General Translation Team
Published: 2026-10-28

Give every language version of a page its own title and meta description, written in that page's language with the terms people in that market search for. Then make the tags around them agree: a canonical URL that points to the page itself, hreflang alternates, the `lang` attribute, the Open Graph locale, and translated structured data. Search engines build each result's title and snippet from these, and Google can replace a title whose language or script doesn't match the page.

## What to localize on each page

Most of these live in the `<head>` or in attributes, so they don't show up when someone reviews the rendered page.

| Element | Where it lives | What to do |
| --- | --- | --- |
| Title | `<title>` | Rewrite for the market's search terms, in the page's language and script |
| Meta description | `<meta name="description">` | Translate per page; Google may show it as the snippet |
| Social previews | `og:title`, `og:description`, `twitter:title`, `twitter:description` | Translate along with the title and description |
| Open Graph locale | `og:locale`, `og:locale:alternate` | Set the page's locale as `language_TERRITORY`, such as `pt_BR`, and list the other versions |
| Share image | `og:image`, `og:image:alt` | Translate the alt text; use a localized image when the image contains text |
| Canonical URL | `<link rel="canonical">` | Point each version at itself |
| Hreflang alternates | `<link rel="alternate" hreflang>` | List every version, including the page itself (see [what is hreflang](/kb/what-is-hreflang)) |
| Document language | `<html lang>` | Set the page's locale code; browsers and screen readers use it |
| Structured data | JSON-LD | Translate names and descriptions, and set `inLanguage` |
| Images | `<img alt>`, file names | Translate alt text, and the file names of localized images |
| URL slug | The path | Optional: `/de/preise` instead of `/de/pricing` (see [localized slugs](/kb/multilingual-url-structure#localized-slugs)) |

The [Open Graph protocol](https://ogp.me/) defines `og:locale` in the `language_TERRITORY` format, defaulting to `en_US`, and says a page that sets `og:image` should also set `og:image:alt`. For JSON-LD, schema.org's [`inLanguage`](https://schema.org/inLanguage) takes a BCP 47 code, and Google's [structured data guidelines](https://developers.google.com/search/docs/appearance/structured-data/sd-policies) say markup must describe content visible on the page, so a German page's markup carries the German name and description. Google's [image guidance](https://developers.google.com/search/docs/appearance/google-images) uses alt text to understand an image and suggests translating the file names of localized images too.

## How search engines use translated metadata

A search result's title and snippet come from your metadata, but not only from it:

- **Titles.** Google's [title link guide](https://developers.google.com/search/docs/appearance/title-link) asks for a `<title>` in the same language and writing system as the page's main content. When they don't match, such as a Hindi page with an English or transliterated title, Google may generate a title link from the page instead.
- **Descriptions.** Google [builds snippets primarily from the page content](https://developers.google.com/search/docs/appearance/snippet) and uses the meta description when it describes the page better. It recommends a unique description for each page, which on a multilingual site means one per page per language.
- **The page itself comes first.** Google treats localized versions as duplicates only when the [main content stays untranslated](https://developers.google.com/search/docs/specialty/international/localized-versions), and it ignores `lang` and hreflang when detecting a page's language. Translated metadata can't stand in for a translated page.

## Common mistakes

### Translating keywords word for word

People in each market search with their own words. British shoppers look for trainers where Americans look for sneakers, and a mobile phone is a "celular" in Brazil, a "telemóvel" in Portugal, and a "Handy" in Germany. A literal translation of an English title can miss the term people in that market type. Pick the target term per locale (Search Console's Performance report, filtered by country, shows the queries that already reach you), then make every title and description use it.

### Reusing English length limits

Google sets no character limit on titles or descriptions; it [truncates them to fit the device width](https://developers.google.com/search/docs/appearance/title-link). A character budget written for English doesn't transfer to other languages. W3C's article on [text size in translation](https://www.w3.org/International/articles/article-text-size) cites IBM guidelines in which English text of 31 to 50 characters, a typical title, averages 140 to 160 percent of its length once translated, and notes that each Chinese and Korean character takes about the width of two Latin characters. Put the main term first, so truncation cuts the end of the title and the keyword survives.

### Pointing every canonical at the source language

A translated page whose canonical points at the English page tells Google the translation is a duplicate. Google's [duplicate URL guidance](https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls) recommends a canonical in the same language when you use hreflang. (See [self-referencing canonicals](/kb/what-is-hreflang#should-hreflang-pages-use-self-referencing-canonicals).)

### Forgetting the tags nobody sees

Open Graph tags, JSON-LD, and image alt text don't appear on the rendered page, so a reviewer reading the German page in a browser won't notice they're still English. Check the page source, or a social preview, for each locale before launch.

## Ways to produce translated metadata

| Approach | Market-specific terms | When the source copy changes | Length | Review |
| --- | --- | --- | --- | --- |
| Write it by hand per market | Chosen through keyword research | Someone updates every language | The writer checks it | Built in |
| Machine translation (Google Translate, DeepL) | Translates the source wording; DeepL glossaries can pin terms | Paste by hand, or automate with their APIs | No control | A separate step |
| A raw AI API (OpenAI API, Claude API) | Follows instructions, such as a term to use per market | Rerun your script | Can be asked for a limit | A separate step |
| General Translation | Glossary terms per locale, Custom Prompts, and per-string `$context` | [`npx gt translate`](/docs/cli/reference/commands/translate) or Locadex translates changed strings | `$maxChars` sets a ceiling | Dashboard Translation Editor |

Write metadata by hand for the few pages that bring most of your search traffic in each priority market, where a researched title is worth the time. Use machine translation for a first draft someone will edit. Use General Translation when titles and descriptions live in your code or CMS and should be retranslated every time the source changes, then hand-edit the pages that matter most in the Dashboard.

## How to implement it

### In a Next.js app

In the App Router, metadata comes from `generateMetadata`, so translate the strings it returns. This page uses General Translation's [`getGT`](/docs/react/nextjs/reference/functions/get-gt), which returns a translation function for the request's locale, and assumes locale-prefixed routes such as `/de/pricing`:

```tsx title="app/[locale]/pricing/page.tsx"
import type { Metadata } from 'next';
import { getGT, getLocale } from 'gt-next/server';

const LOCALES = ['en', 'de', 'fr', 'pt-BR'];
const pageUrl = (locale: string) => `https://example.com/${locale}/pricing`;

// BCP 47 (pt-BR) to Open Graph's language_TERRITORY (pt_BR).
// maximize() fills in the likely region for a bare code: de becomes de-Latn-DE.
function ogLocale(locale: string) {
  const { language, region } = new Intl.Locale(locale).maximize();
  return `${language}_${region}`;
}

export async function generateMetadata(): Promise<Metadata> {
  const gt = await getGT();
  const locale = await getLocale();

  // $context tells the translator what the string is.
  const title = gt('Pricing for teams of every size', {
    $context: 'SEO title of the pricing page',
  });
  const description = gt(
    'Compare plans, see what each one includes, and start for free.',
    { $context: 'Meta description of the pricing page' },
  );

  return {
    title,
    description,
    alternates: { canonical: pageUrl(locale) }, // add hreflang alternates here too
    openGraph: {
      title,
      description,
      url: pageUrl(locale),
      locale: ogLocale(locale),
      alternateLocale: LOCALES.filter((l) => l !== locale).map(ogLocale),
      images: [
        {
          url: `https://example.com/og/pricing-${locale}.png`, // one image per locale if it contains text
          alt: gt('Plan comparison table', { $context: 'Alt text of the share image' }),
        },
      ],
    },
    twitter: { card: 'summary_large_image', title, description },
  };
}
```

Built with Next.js 16.4.3 and `gt-next` 11.4.16, `/de/pricing` rendered these locale-specific tags, alongside the title, description, and Twitter tags:

```html title="/de/pricing"
<html lang="de">
<link rel="canonical" href="https://example.com/de/pricing"/>
<meta property="og:url" content="https://example.com/de/pricing"/>
<meta property="og:locale" content="de_DE"/>
<meta property="og:image" content="https://example.com/og/pricing-de.png"/>
<meta property="og:locale:alternate" content="en_US"/>
<meta property="og:locale:alternate" content="fr_FR"/>
<meta property="og:locale:alternate" content="pt_BR"/>
```

The `lang` attribute comes from the `[locale]` layout. Locale routing, the hreflang alternates, and the sitemap are covered in [How do I set up SEO for a multilingual Next.js app?](/kb/multilingual-nextjs-seo), which builds `alternates.languages` from a record of published locales.

### On a static site

Static site generators usually take a page's title and description from its front matter, so each translated file carries its own. In Docusaurus, for example, the [`description` front matter](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-docs) becomes both `<meta name="description">` and `og:description`, and translated docs live under `i18n/[locale]/docusaurus-plugin-content-docs/current/`. Translate the front matter in the same pass as the body.

### In a CMS or site builder

Store SEO fields per locale, the same way as the page body, so each language gets its own title and description. For a site on WordPress or Shopify that you don't build in code, a proxy such as Weglot [translates titles, meta descriptions, and structured data](https://support.weglot.com/article/293-how-does-weglot-manage-the-seo) along with the page. (See [General Translation vs. Weglot](/kb/general-translation-vs-weglot).)

## How General Translation translates SEO metadata

In the Next.js App Router, General Translation's `gt-next` translates the strings you pass to [`getGT`](/docs/react/nextjs/reference/functions/get-gt) inside `generateMetadata`. Pass string literals so the CLI can find them, and run [`npx gt translate`](/docs/cli/reference/commands/translate) before `next build`; when you change the English title, the next build translates the new text into every locale. On the page above, [`npx gt generate`](/docs/cli/reference/commands/generate), the CLI's offline template command, lists the title, description, and alt text as source strings.

Two [translation options](/docs/react/reference/types/inline-translation-options) help with metadata. `$context` tells the translation what the string is, such as "SEO title of the pricing page." `$maxChars` asks for a translation within a character budget, and when a translation still runs over, `gt-next` cuts it at the limit with an ellipsis at render, so treat it as a ceiling, not a pixel-accurate limit. It's optional: Google sets no fixed length, and a ceiling can cut a good translation short, so add one only where your own layout, such as a share card or a CMS field, has a hard cap.

For market-specific keywords, add the term to a [Context Group](/docs/platform/dashboard/guides/defining-context-for-translations): a Glossary entry holds its translation per locale, and a Custom Prompt can apply to a single locale. General Translation applies both to new translations, whether titles, descriptions, or body copy. Review the result in the [Dashboard Translation Editor](/docs/platform/dashboard/guides/reviewing-translations) before publishing.

In our test on a new project with no Glossary (`gt` 2.26.11 and `gt-next` 11.4.16), a running-shoes category page written in `en-US` came back with each market's own term. The title and description went through [`getGT`](/docs/react/nextjs/reference/functions/get-gt), each with a `$context` naming the page, an online shoe store's running-shoes category, and the built pages rendered:

```html title="Rendered metadata"
<!-- /en-US/running (source) -->
<title>Running Sneakers for Trail and Road</title>
<meta name="description" content="Shop lightweight running sneakers with cushioned soles for long runs on trails and city streets."/>

<!-- /en-GB/running -->
<title>Running Trainers for Trail and Road</title>
<meta name="description" content="Shop lightweight running trainers with cushioned soles for long runs on trails and city streets."/>

<!-- /de/running -->
<title>Laufschuhe für Gelände und Straße</title>
<meta name="description" content="Entdecke leichte Laufschuhe mit gut dämpfenden Sohlen für lange Läufe auf Trails und Straßen."/>
```

The British page says trainers, and the German title uses Laufschuhe, the German word for running shoes ([Duden](https://www.duden.de/rechtschreibung/Laufschuh)), instead of a word-for-word "Lauf-Sneaker." The same title without `$context` came back identical, so for a string this clear, the target locale was enough. The German title is also shorter than the English one, 33 characters against 35, so measure each locale instead of assuming every translation runs longer. Two things still need a person: the German description addresses the reader informally (_Entdecke_), so add a Custom Prompt for German if your brand uses the formal _Sie_; and a model's term is a judgment about search behavior, so check it against the queries Search Console shows for that country and pin it in a Glossary when it matters.

Outside app code:

- **Payload CMS:** with `gt-payload`, fields rendered by Payload's SEO plugin get [recommended lengths](/docs/integrations/payload/reference/supported-content#character-limits) of 60 characters for the meta title and 150 for the meta description. A longer translation triggers a request for a shorter one; it's only cut, at a word boundary with the locale's ellipsis, when the field also sets `maxLength`.
- **Mintlify:** Locadex [translates frontmatter](/docs/integrations/mintlify/reference/supported-content) such as page titles and descriptions, so a docs site's SEO metadata exists in every language.

Ramp localizes its dashboard, landing page, and iOS app into 11+ locales with General Translation. (See [production deployments](https://generaltranslation.com/enterprise).)

## Key takeaways

- Translate every title and meta description into the page's own language and script; Google may replace a title that doesn't match the page.
- Choose keywords per market, such as trainers in `en-GB` and sneakers in `en-US`, instead of translating the English term.
- Don't reuse English character limits: Google truncates by device width, and translation length varies by language and string, so measure each locale.
- Localize the tags around the title too: a self-referencing canonical, hreflang, `lang`, `og:locale`, alt text, and JSON-LD.
- In Next.js, General Translation's [`getGT`](/docs/react/nextjs/reference/functions/get-gt) translates metadata in `generateMetadata`, with `$context` for intent and an optional `$maxChars` ceiling for layouts with a hard cap.

## Related questions

- [How do I set up SEO for a multilingual Next.js app?](https://generaltranslation.com/en-US/kb/multilingual-nextjs-seo.mdx): Give each language its own prefixed URL, such as /en/about and /es/about, then publish html lang, a self-referencing canonical, hreflang alternates with x-default, a translated title and description, and a sitemap entry for each published language. Mark unpublished versions noindex. General Translation's gt-next routes each language to its own URL and translates titles with getGT.
- [What is hreflang, and how do I set it up?](https://generaltranslation.com/en-US/kb/what-is-hreflang.mdx): Hreflang tells Google which URLs are language or regional versions of the same page, so it can show the right one in search results. Add it with HTML link tags, HTTP Link headers, or a sitemap, make every link reciprocal, and use valid codes such as en-GB, not en-UK.
- [How do I create a sitemap for a multilingual website?](https://generaltranslation.com/en-US/kb/multilingual-sitemap.mdx): List every language version of each page as its own url entry. If the sitemap carries your hreflang, give each entry xhtml:link alternates for every version, including itself, plus an optional x-default, and split files at 50,000 URLs or 50MB. General Translation's gt-next gives each locale its own URL for the sitemap to list.
- [Subdirectory vs. subdomain vs. ccTLD for multilingual SEO](https://generaltranslation.com/en-US/kb/multilingual-url-structure.mdx): Use subdirectories such as example.com/de/ when one product shares a site and release process. Only a ccTLD signals a country, subdomains allow separate hosting, Google discourages ?lang= parameters, and it treats vanity domains such as .io and .ai as generic.
- [How do I make a website multilingual?](https://generaltranslation.com/en-US/kb/make-website-multilingual.mdx): Pick where translations live, in a proxy, CMS plugin, site builder, or your own code, then give each language its own URL, as Google recommends. Translate full pages plus titles and meta descriptions, link the versions with hreflang, and offer a language switcher instead of auto-redirecting visitors.

## Previous and next questions

- Previous question: [How do I translate PO and POT files?](https://generaltranslation.com/en-US/kb/translate-po-files.mdx)
- Next question: [How do I translate YAML locale files?](https://generaltranslation.com/en-US/kb/translate-yaml-files.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/en-US/kb/sitemap.md) for every article.
