雙語網站需要路由模型,而不只是語言切換
A bilingual site needs a route model, not just a language switch
kaahn.com 的雙語網站以明確路由映射連結頁面身份、語言與 URL,為土耳其語和英語版本設定穩定網址。網站在建置時於各網址生成可讀 HTML,並以 canonical URL 和語言替代關係標示頁面版本;語言切換按頁面身份前往另一語言的對應頁面。這是實作案例,並非搜尋排名提升的證據。
A language switch changes what a visitor sees. A route model decides what a visitor can share, where navigation leads and which language is present in the HTML before browser code runs.
For a small bilingual site, these decisions can live in one explicit mapping. The current kaahn.com implementation uses separate Turkish and English addresses and generates readable HTML for each. The useful engineering lesson is the relationship between content identity, language and URL. This is an implementation case study, not evidence of a ranking increase.
Give each page a stable identity
Consider an About page. Its Turkish address is /hakkimda/; its English address is /en/about/. Those are two language versions of the same subject. Switching from the English About page should lead to the Turkish About page, rather than restarting at the Turkish home page.
The same relationship is useful in navigation, breadcrumbs and a sitemap. Copying paths independently into each template makes it easy to update one place and leave an old address somewhere else. A shared mapping makes the relationship inspectable:
pages = {
"about": {"tr": "/hakkimda/", "en": "/en/about/"},
"projects": {"tr": "/projeler/", "en": "/en/projects/"},
}
seen = set()
for page, languages in pages.items():
assert set(languages) == {"tr", "en"}, page
for language, path in languages.items():
assert path.startswith("/") and path.endswith("/"), path
assert path not in seen, f"Duplicate route: {path}"
seen.add(path)
assert pages["about"]["tr"] == "/hakkimda/"
This simplified example validates route shape, language coverage and collisions. It does not validate the translation itself. A publishing check still needs to confirm that the English page actually contains English prose, including its title, description and navigation labels.
The site’s production build extracts the shared header and footer, selects a page body, applies the corresponding translations and writes an HTML file at each address. Browser JavaScript can add interaction afterwards. The main heading and body do not depend on a language toggle running successfully.
Google describes crawling, rendering and indexing as distinct stages for JavaScript pages. Preparing stable content at build time makes the published file straightforward to inspect. It does not imply that every client-rendered application is unsuitable for search, or that replacing a framework guarantees better results.
Model two different relationships
A canonical URL identifies the preferred address for a page. A language alternative identifies another language version. Treating these as the same relationship creates confusing metadata.
For this site, the English About page has an English self-canonical. Both language versions declare the same Turkish and English alternatives. The Turkish page is the default-language alternative. A shortened example of the English page’s metadata is:
<link rel="canonical" href="https://kaahn.com/en/about/">
<link rel="alternate" hreflang="tr" href="https://kaahn.com/hakkimda/">
<link rel="alternate" hreflang="en" href="https://kaahn.com/en/about/">
<link rel="alternate" hreflang="x-default" href="https://kaahn.com/hakkimda/">
Google’s localized-versions guidance calls for reciprocal relationships. Inspecting only the English file is therefore insufficient. The Turkish file must identify both versions too, and every target should resolve to the intended page.
This also explains why a language switch should use the current page identity. Guessing an English address by adding /en/ would fail for translated slugs such as hakkimda and about. The mapping already knows the correct pair.
Treat publication as a contract
A successful generator run proves only that files were produced. The server must serve those files at their public addresses, and the relationships between them must remain correct.
| Check | Expected result | Failure it exposes |
|---|---|---|
| Inspect the initial HTML | Heading, body and navigation are present | Content exists only in browser state |
| Open both language versions | Each points to both alternatives | A missing or mismatched language pair |
| Follow internal links | Final URLs preserve the intended language | Lost context or unnecessary redirects |
| Compare sitemap and published routes | Only published canonical addresses appear | Draft or obsolete addresses exposed |
For scheduled content, the sitemap and navigation should use the same publication decision as the page generator. Otherwise a future article can leak into a sitemap before its page exists, or appear as a link that leads to a missing page.
The design fits a small portfolio with relatively stable content. Concurrent editing, per-user pages and updates that must appear within seconds introduce different requirements. Those requirements may justify a CMS or runtime rendering. Establishing page identity and language first makes that later change easier to reason about.
Sources and original version
- Original article on kaahn.com
- Published English About page and Turkish counterpart
- Google: localized versions
- Google: JavaScript SEO basics
AI disclosure: This article was drafted by an AI agent from the site’s implementation and the linked documentation. It does not report a search-ranking experiment.
來源:DEV Community · dev.to