Project updates from Template

This commit contains the latest updates from the generic Website template, which rework the compression and localization:

- Reworked the compression and localization in the Infrastructure package. (3c568e4)
- Adopted the reworked compression and localization in the Website service. (08cf3e3)

The template commit that only touched the root README (53676ed) was left out, as this project no longer carries that file.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-20 12:45:58 +02:00
co-authored by Claude Fable 5.1
parent 65b62681eb
commit 916df7e2f0
24 changed files with 426 additions and 218 deletions
@@ -0,0 +1,41 @@
import Foundation
import HTTPTypes
import Hummingbird
import Localization
public extension Negotiate {
// MARK: Methods
/// The language to serve a request in.
///
/// The `lang` query parameter wins as a deliberate override; failing that, a leading path segment naming a supported language pins it, so an
/// unrouted path under a language's prefix answers in that language. Anything naming no supported language is ignored, leaving the
/// `Accept-Language` header and its fallback to the default.
///
/// Called where the answer is used rather than stamped onto every request on the way past: page routes that pin their language by URL never ask.
/// - Parameter request: the request to negotiate for.
/// - Returns: the identifier of the supported language to serve.
func callAsFunction(
for request: Request
) -> String {
let requested = request.uri.queryParameters[.Parameter.language].map(String.init)
?? request.uri.path.split(separator: "/").first.map(String.init)
return callAsFunction(
requested: requested,
acceptLanguage: request.headers[.acceptLanguage]
)
}
}
// MARK: - Constants
private extension Substring {
/// A namespace for the query parameters the negotiation reads.
enum Parameter {
/// The query parameter carrying an explicit language choice; the site's language switcher appends it to the current path.
static let language: Substring = "lang"
}
}
@@ -7,9 +7,11 @@ extension String {
/// The default `Content-Security-Policy`.
///
/// Restricts every resource to the site's own origin (`default-src 'self'`), blocks plugins (`object-src 'none'`), pins the document
/// base URL (`base-uri 'self'`), and forbids framing (`frame-ancestors 'none'`). No inline-style exception is included, so pages must
/// link external stylesheets.
public static let contentSecurityPolicy = "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'"
/// base URL (`base-uri 'self'`), holds form submissions to the origin (`form-action 'self'`), and forbids framing
/// (`frame-ancestors 'none'`). No inline-style exception is included, so pages must link external stylesheets.
///
/// `form-action` is named outright because it inherits from nothing: `default-src` does not cover it, however tight.
public static let contentSecurityPolicy = "default-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'"
/// The default `X-Content-Type-Options` (disables MIME sniffing).
public static let contentTypeOptions = "nosniff"
/// The default `X-Frame-Options` (forbids framing the page).