# gt-next: General Translation Next.js SDK: Locale Aliases and SEO URL: https://generaltranslation.com/en-US/docs/next/guides/locale-aliases.mdx --- title: Locale Aliases and SEO description: Use custom locale aliases for URL routing while maintaining BCP 47 compliance for search engines --- Locale aliases let you use custom locale codes in your URLs (e.g. `/cn/` instead of `/zh/`) while keeping your SEO metadata compliant with the [BCP 47 standard](https://www.w3.org/International/articles/language-tags/) that search engines expect. ## Why aliases? BCP 47 locale codes like `zh` (Chinese) or `zh-Hant` (Traditional Chinese) are the standard for identifying languages on the web. However, you might want different codes in your URL paths for branding, readability, or regional reasons — for example, using `/cn/` instead of `/zh/` for your Chinese audience. GT supports this through **custom mapping** in your `gt.config.json`. The alias is used for routing and URL paths, while the canonical BCP 47 code is used wherever search engines need it. **SEO requirement:** Search engines only recognize [BCP 47 locale codes](https://www.w3.org/International/articles/language-tags/). Using non-standard codes like `cn` in `hreflang` attributes or `` will cause search engines to ignore your locale signals. ## Setup ### Step 1: Configure custom mapping Add a `customMapping` entry to your `gt.config.json` for each alias: ```json title="gt.config.json" { "defaultLocale": "en-US", "locales": ["en-US", "cn", "ja", "zh-Hant"], "customMapping": { "cn": { "code": "zh", "name": "Mandarin" } } } ``` Here, `cn` is the alias used in URLs and middleware routing, and `zh` is the canonical BCP 47 code. ### Step 2: Use middleware as normal The [middleware](/docs/next/guides/middleware) and `[locale]` dynamic route work with your alias codes out of the box. Users visiting `/cn/about` will be served Chinese content — no special handling needed for routing. ## BCP 47 compliance for SEO While aliases work seamlessly for routing, there are three places where you **must** use the canonical BCP 47 code instead of the alias: 1. The `lang` attribute on your `` tag 2. Alternate link tags in your page metadata 3. Alternate entries in your sitemap GT provides the `resolveCanonicalLocale()` method to convert aliases back to their BCP 47 codes. You can access it via `getGTClass` from `gt-next/server`: ```ts import { getGTClass } from 'gt-next/server'; const gtInstance = getGTClass(); const canonicalLocale = gtInstance.resolveCanonicalLocale('cn'); // Returns: "zh" ``` For non-aliased locales, `resolveCanonicalLocale()` returns the input unchanged: ```ts gtInstance.resolveCanonicalLocale('ja'); // "ja" gtInstance.resolveCanonicalLocale('en-US'); // "en-US" ``` ### 1. HTML `lang` attribute The `` attribute tells browsers and search engines what language the page is in. It must be a valid BCP 47 code. In your root layout, resolve the locale before passing it to the `` tag: ```tsx title="app/[locale]/layout.tsx" import { getGTClass } from 'gt-next/server'; export default function RootLayout({ children, params, }: { children: React.ReactNode; params: { locale: string }; }) { const gtInstance = getGTClass(); const canonicalLocale = gtInstance.resolveCanonicalLocale(params.locale); return ( {children} ); } ``` Without this, a page at `/cn/about` would incorrectly render ``, which search engines don't recognize. ### 2. Metadata alternates Alternate links tell search engines which versions of a page exist in other languages. The `hreflang` attribute must use BCP 47 codes. ```tsx title="app/[locale]/layout.tsx" import type { Metadata } from 'next'; import { getGTClass } from 'gt-next/server'; export async function generateMetadata({ params, }: { params: { locale: string }; }): Promise { const gtInstance = getGTClass(); const locales = ['en-US', 'cn', 'ja', 'zh-Hant']; // Build alternates with canonical BCP 47 codes as keys const languages: Record = {}; for (const locale of locales) { const canonical = gtInstance.resolveCanonicalLocale(locale); languages[canonical] = `https://example.com/${locale}`; } // Add x-default for the default locale languages['x-default'] = 'https://example.com'; return { alternates: { canonical: `https://example.com/${params.locale}`, languages, }, }; } ``` This produces correct `` tags in the page head: ```html ``` Notice that the `hreflang` values use canonical codes (`zh`, not `cn`), while the `href` URLs still use the alias paths (`/cn/`). ### 3. Sitemap alternates If you use a [dynamic sitemap](https://nextjs.org/docs/app/api-reference/file-conventions/metadata/sitemap), apply the same pattern: ```ts title="app/sitemap.ts" import type { MetadataRoute } from 'next'; import { getGTClass } from 'gt-next/server'; export default function sitemap(): MetadataRoute.Sitemap { const gtInstance = getGTClass(); const locales = ['en-US', 'cn', 'ja', 'zh-Hant']; const baseUrl = 'https://example.com'; const pages = ['', '/about', '/pricing']; return pages.map((page) => { // Build language alternates with canonical codes const languages: Record = {}; for (const locale of locales) { const canonical = gtInstance.resolveCanonicalLocale(locale); languages[canonical] = `${baseUrl}/${locale}${page}`; } return { url: `${baseUrl}${page}`, lastModified: new Date(), alternates: { languages, }, }; }); } ``` This generates sitemap XML with proper `hreflang` attributes: ```xml https://example.com ``` ## Common mistakes | Mistake | Impact | Fix | |---|---|---| | Using alias code in `` | Search engines can't identify the page language | Use `resolveCanonicalLocale()` for the `lang` attribute | | Using alias code in `hreflang` | Search engines ignore the alternate link | Use `resolveCanonicalLocale()` for `hreflang` values | | Missing `x-default` alternate | No fallback for users whose language isn't listed | Add `x-default` pointing to your default locale URL | | Inconsistent alternates between HTML and sitemap | Conflicting signals confuse crawlers | Use `resolveCanonicalLocale()` in both places | ## Next steps - Learn about [middleware](/docs/next/guides/middleware) for locale-based URL routing - See [`resolveCanonicalLocale`](/docs/core/class/methods/locales/resolve-canonical-locale) API reference - Configure [`customMapping`](/docs/cli/reference/config) in your `gt.config.json`