Project updates from Template (#1)

This PR contains the latest updates from the generic Website template, which have been added while working on #loud-amsterdam.

Reviewed-on: #1
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
This commit was merged in pull request #1.
This commit is contained in:
2026-09-04 13:40:35 +00:00
committed by javier
parent 08b4d80064
commit 65b62681eb
60 changed files with 2347 additions and 326 deletions
@@ -25,15 +25,18 @@ public struct RootController<Context: LocalizedRequestContext> {
/// Creates a root controller.
/// - Parameters:
/// - assetVersion: the version token appended to the page's asset URLs, or `nil` (the default) to leave them unversioned.
/// - siteOrigin: the public origin the page derives its canonical URL and language alternates from, or `nil` (the default) to omit them.
/// - analytics: the analytics tracker the landing page embeds, or `nil` (the default) to omit it.
public init(
assetVersion: String? = nil,
siteOrigin: String? = nil,
analytics: Analytics? = nil
) {
self.responses = .init(bundle: .module) {
IndexPage(
locale: $0,
assetVersion: assetVersion,
siteOrigin: siteOrigin,
analytics: analytics
)
}
@@ -55,6 +58,15 @@ extension RootController: RouterController {
use: index
)
// Every non-default language answers under a prefix of its own, so the `hreflang` alternates the page advertises
// resolve and a crawler can index each edition at a stable URL. A single-language catalog adds none.
for language in Language.all where !language.isDefault {
routes.get(
.init(language.path(IndexPage.path)),
use: index(in: language)
)
}
return routes
}
@@ -84,6 +96,23 @@ private extension RootController {
)
}
/// Builds the handler serving the landing page in one fixed language, for the routes carrying the language in their path.
///
/// The path *is* the language choice, so the negotiated context language is ignored: a prefixed URL answers in its language for every
/// visitor and every crawler alike, which is what lets a search engine index it as that edition.
/// - Parameter language: the language the route serves.
/// - Returns: the handler answering requests for that edition of the page.
func index(
in language: Language
) -> @Sendable (Request, Context) -> Response {
{ request, _ in
responses.response(
for: language.identifier,
request: request
)
}
}
}
// MARK: - Constants
@@ -91,7 +120,7 @@ private extension RootController {
private extension RouterPath {
/// A namespace for the ``RootController`` route paths.
enum Root {
/// The path of the landing page.
static let index: RouterPath = "/"
/// The path of the landing page; the page builds its canonical URL from the same constant.
static let index: RouterPath = .init(IndexPage.path)
}
}