The problem is bigger than a language button

A TR/EN switch makes an interface readable in two languages. It does not, by itself, decide which language a shared link opens, what the initial HTML contains, or where the contact link on an English project page should lead. Those decisions form the address model of a portfolio. They deserve to be explicit even when the site has only a few page types.

The kaahn.com build defines a path for each page and language. Its Turkish profile lives at /hakkimda/ and its English counterpart at /en/about/. The generator reads those pairs from a central mapping. A language switch can therefore preserve the current subject instead of sending every visitor back to the other language’s home page.

Prepare the content when publishing

The source design for this site also supports a single-page preview. The production build extracts the shared header and footer, selects a page body, applies its translations and writes an HTML file for each address. Titles and descriptions come from the corresponding language record. Browser code can add interaction afterwards; the main text does not need a language-switching script to become readable.

Google describes crawling, rendering and indexing as separate stages for JavaScript pages. The engineering choice here is to prepare relatively stable content in advance, making the published file directly inspectable. This is a description of the implementation, not a measured ranking improvement or a general claim that client-rendered applications are always slower.

Make one route map serve several outputs

This simplified example assigns two paths to one content identity. Navigation, language switches and the sitemap can consume that same record. Independently typing each path into several templates creates more places for an old URL to survive. A shared mapping reduces that risk, while a missing translation should still cause a visible publishing error rather than silently substituting another language.

pages = {
    "about": {"tr": "/hakkimda/", "en": "/en/about/"},
    "projects": {"tr": "/projeler/", "en": "/en/projects/"},
}
for page, languages in pages.items():
    assert set(languages) == {"tr", "en"}
    for language, path in languages.items():
        assert path.startswith("/") and path.endswith("/")

Separate the canonical address from language alternatives

A translated page identifying its preferred address and that page identifying its translation are different relationships. Here, the page record produces both a self-referencing canonical URL and Turkish, English and default-language alternatives. Pointing every English canonical at a Turkish page would conflict with the decision to maintain a distinct English version.

Google’s localized-versions guidance requires reciprocal language relationships. Checking only the Turkish file is therefore insufficient: open both generated files and compare their alternate targets. Also inspect the visible prose. Correct language metadata cannot compensate for a page whose body is accidentally left in the other language.

A release acceptance table

These checks are repeatable publishing criteria, not a search-position forecast. A successful build and Google choosing a page for a query are different events. The production server must also serve the expected file at its public address with an appropriate HTTP status.

CheckExpected resultFailure caught
Page with JavaScript disabledHeading, body and links remain readableContent exists only in browser state
Language pairBoth pages reference each otherMissing or mismatched alternative
Internal linksFinal address in the intended languageLost language context or needless redirects
SitemapContains published page addressesDraft or obsolete URLs exposed

Know when the design stops fitting

Concurrent editors, per-user content and updates that must appear within seconds introduce different requirements. They may justify a content system or runtime rendering. For a small portfolio, a content record, two languages and inspectable static output provide a legible starting point. The first design question is where a page’s identity and language live; choosing another framework comes after that question.

Sources