Files
flatpark/tests/test_i18n_negotiate.sh
T
KomhandClaude Opus 5 d6818d655c feat(site): send / to the reader's locale at the edge
A Chinese reader arriving at flatpark.org had to find the globe menu before
seeing a word of Chinese. Negotiate it instead, on the one URL that is
ambiguous: a Pages Function on `/` reads Accept-Language and 302s to
`/zh-Hans/` when that is what the browser asks for.

Scoped to the root on purpose. English lives on the bare paths here, so every
other URL is already unambiguous and stays a plain static asset — which keeps
the canonical English pages out of any redirect and keeps Function invocations
off the rest of the site. `_routes.json` pins that.

An explicit pick from the language switcher wins over the browser: the switcher
writes a `lang` cookie and the function prefers it, so choosing English on a
Chinese browser is not undone on the next visit to the homepage.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-08-14 09:01:32 +08:00

61 lines
2.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Root-path language negotiation: which locale `/` serves for a given browser.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
. "$ROOT/tests/lib/assert.sh"
command -v node >/dev/null 2>&1 || { echo "test_i18n_negotiate: SKIP (no node)"; exit 0; }
out="$(node --input-type=module -e "
import { cookieLocale, localeForRequest, preferredLocale } from '$ROOT/site/src/i18n/negotiate.mjs';
const lines = [
// Nothing to go on -> English, which is what crawlers get (they send no header).
'empty=' + preferredLocale(''),
'missing=' + preferredLocale(undefined),
'wildcard=' + preferredLocale('*'),
// The plain cases.
'en=' + preferredLocale('en-US,en;q=0.9'),
'zh-cn=' + preferredLocale('zh-CN,zh;q=0.9,en;q=0.8'),
'zh-bare=' + preferredLocale('zh'),
// Traditional regions have no locale of their own yet, so they land on the
// only Chinese we ship rather than on English.
'zh-tw=' + preferredLocale('zh-TW,zh;q=0.9'),
'zh-hant=' + preferredLocale('zh-Hant'),
// A language we do not ship at all stays English.
'ja=' + preferredLocale('ja-JP,ja;q=0.9'),
// q-order decides, not the order the tags appear in.
'q-order=' + preferredLocale('en;q=0.3,zh-CN;q=0.9'),
'q-zero=' + preferredLocale('zh-CN;q=0,ja;q=0.5'),
// Case and stray whitespace are the browser's business, not ours.
'sloppy=' + preferredLocale(' ZH-hans-cn ; q=0.8 , en ; q=0.2 '),
// An explicit pick from the switcher outranks the browser, both ways.
'cookie-en=' + localeForRequest({ acceptLanguage: 'zh-CN', cookieHeader: 'theme=dark; lang=en' }),
'cookie-zh=' + localeForRequest({ acceptLanguage: 'en-US', cookieHeader: 'lang=zh-Hans' }),
// A cookie naming something we do not ship is ignored, not trusted.
'cookie-junk=' + localeForRequest({ acceptLanguage: 'zh-CN', cookieHeader: 'lang=../etc' }),
'cookie-none=' + cookieLocale('theme=dark'),
];
console.log(lines.join('\n'));
")"
get() { printf '%s\n' "$out" | grep "^$1=" | cut -d= -f2-; }
assert_eq "$(get empty)" "en"
assert_eq "$(get missing)" "en"
assert_eq "$(get wildcard)" "en"
assert_eq "$(get en)" "en"
assert_eq "$(get zh-cn)" "zh-Hans"
assert_eq "$(get zh-bare)" "zh-Hans"
assert_eq "$(get zh-tw)" "zh-Hans"
assert_eq "$(get zh-hant)" "zh-Hans"
assert_eq "$(get ja)" "en"
assert_eq "$(get q-order)" "zh-Hans"
assert_eq "$(get q-zero)" "en"
assert_eq "$(get sloppy)" "zh-Hans"
assert_eq "$(get cookie-en)" "en"
assert_eq "$(get cookie-zh)" "zh-Hans"
assert_eq "$(get cookie-junk)" "zh-Hans"
assert_eq "$(get cookie-none)" "null"
echo "test_i18n_negotiate: ok"