serve language variants under a prefix
The default locale stays at the root; every other language is the same key under
/{lang}/ (ADR-0009). /en/… is never live and redirects to the root form so the URL
space cannot fork. Lookup now takes a language and reports which one it served,
following requested → default → any rather than 404ing when a translation is
missing.
That is the second routing case, so the resolver is extracted to resolve.go and
the mux keeps one entry: URL shape is the resolver's business. A leading segment
counts as a language only when some bundle is written in it, so an unknown prefix
is a 404 rather than a stripped path — and a section may not be named after a
language in use, now recorded in content-model.md.
Because the served variant can differ from the URL requested, Page gained
.Canonical (the variant actually served) and .Alternates for hreflang. A theme
must never build a path, so both come from the engine.
Evidence: /bn/pages/about/ serves the Bengali body with lang="bn" and canonical
/bn/pages/about/; /bn/posts/hello-world/ falls back to English with canonical
/posts/hello-world/; /en/pages/about/ 301s to /pages/about/; /fr/… is 404.
This commit is contained in:
@@ -167,11 +167,51 @@ func TestSiteLookupFindsDefaultVariant(t *testing.T) {
|
||||
if site.Len() != 2 {
|
||||
t.Fatalf("indexed %d bundles, want 2", site.Len())
|
||||
}
|
||||
b, ok := site.Lookup("pages/about")
|
||||
if !ok || b.Title != "About" {
|
||||
t.Fatalf("Lookup gave %+v %v, want the English variant", b, ok)
|
||||
b, served, ok := site.Lookup("pages/about", "en")
|
||||
if !ok || b.Title != "About" || served != "en" {
|
||||
t.Fatalf("Lookup gave %+v %q %v, want the English variant", b, served, ok)
|
||||
}
|
||||
if _, ok := site.Lookup("pages/missing"); ok {
|
||||
if _, _, ok := site.Lookup("pages/missing", "en"); ok {
|
||||
t.Error("Lookup invented a bundle")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupFallsBackThroughLanguages(t *testing.T) {
|
||||
fsys := fstest.MapFS{
|
||||
"content/pages/about.md": {Data: []byte("---\ntitle: About\n---\n")},
|
||||
"content/pages/about.bn.md": {Data: []byte("---\ntitle: পরিচিতি\n---\n")},
|
||||
"content/posts/only.bn.md": {Data: []byte("---\ntitle: শুধু\n---\n")},
|
||||
"content/posts/plain.md": {Data: []byte("---\ntitle: Plain\n---\n")},
|
||||
}
|
||||
bundles, err := Scan(fsys)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
site := NewSite(bundles)
|
||||
cases := []struct{ key, want, served string }{
|
||||
{"pages/about", "পরিচিতি", "bn"}, // the requested language exists
|
||||
{"posts/plain", "Plain", "en"}, // falls back to the default
|
||||
{"posts/only", "শুধু", "bn"}, // no default either: any variant beats a 404
|
||||
}
|
||||
for _, c := range cases {
|
||||
b, served, ok := site.Lookup(c.key, "bn")
|
||||
if !ok || b.Title != c.want || served != c.served {
|
||||
t.Errorf("Lookup(%q, bn) = %q/%q ok=%v, want %q/%q", c.key, b.Title, served, ok, c.want, c.served)
|
||||
}
|
||||
}
|
||||
if got := site.Variants("pages/about"); len(got) != 2 || got[0] != "bn" || got[1] != "en" {
|
||||
t.Errorf("Variants = %v, want [bn en]", got)
|
||||
}
|
||||
if !site.HasLang("bn") || site.HasLang("fr") {
|
||||
t.Error("HasLang misreported")
|
||||
}
|
||||
}
|
||||
|
||||
func TestURLPrefixesOnlyNonDefaultLanguages(t *testing.T) {
|
||||
cases := map[string]string{"en": "/pages/about/", "": "/pages/about/", "bn": "/bn/pages/about/"}
|
||||
for lang, want := range cases {
|
||||
if got := URL("pages/about", lang); got != want {
|
||||
t.Errorf("URL(pages/about, %q) = %q, want %q", lang, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user