import Foundation import HTTPTypes import Hummingbird import Localization /// Resolves the visitor's preferred language and records it on the request context. /// /// Placed ahead of the localized responders in the middleware chain, it reads the request's `Accept-Language` header, negotiates the best supported /// match (falling back to the default language), and stores it on the context's ``LocalizedRequestContext/language``. /// /// The request is otherwise passed through untouched — the URL and routing are not affected — so each page is served at its existing path and varies its /// content by header. public struct LocalizationMiddleware { // MARK: Properties /// Negotiates the request's language from its `Accept-Language` header. private let negotiate: Negotiate // MARK: Initializers /// Creates a localization middleware that negotiates against the given bundle's String Catalog languages. /// - Parameter bundle: the bundle whose String Catalog names the supported languages. public init( bundle: Bundle ) { self.negotiate = .init(bundle: bundle) } } // MARK: - RouterMiddleware extension LocalizationMiddleware: RouterMiddleware { // MARK: Functions /// Negotiates the request's language and records it on the context before passing it down. /// - Parameters: /// - request: the incoming request. /// - context: the context the request is resolved against. /// - next: the next responder in the middleware chain. /// - Returns: the downstream response. /// - Throws: any error thrown downstream. public func handle( _ request: Request, context: Context, next: (Request, Context) async throws -> Response ) async throws -> Response { var context = context context.language = negotiate( acceptLanguage: request.headers[.acceptLanguage] ) return try await next(request, context) } }