@@ -2,20 +2,102 @@ import { defineMiddleware } from 'astro:middleware'
22import { useRuntimeConfig } from './config/app'
33import { paraglideMiddleware } from './paraglide/server.js'
44import { defaultLocale , type Locales } from './services/locale'
5+ import {
6+ isRuntimeTranslationEligiblePath ,
7+ runtimeTranslationLocales ,
8+ stripRuntimeLocalePrefix ,
9+ translateHtmlResponseWithCloudflareAI ,
10+ type RuntimeTranslationLocale ,
11+ } from './services/runtimeTranslation'
12+
13+ interface RuntimeEnv {
14+ AI ?: {
15+ run ( model : string , payload : Record < string , unknown > ) : Promise < unknown >
16+ }
17+ }
18+
19+ const buildTranslationState = (
20+ locale : RuntimeTranslationLocale ,
21+ options : {
22+ enabled : boolean
23+ isTranslated : boolean
24+ showSelector : boolean
25+ sourcePath : string
26+ } ,
27+ ) => ( {
28+ availableLocales : runtimeTranslationLocales ,
29+ enabled : options . enabled ,
30+ isTranslated : options . isTranslated ,
31+ requestedLocale : locale ,
32+ showSelector : options . showSelector ,
33+ sourcePath : options . sourcePath ,
34+ } )
35+
36+ export const onRequest = defineMiddleware ( async ( context , next ) => {
37+ const runtimeConfig = useRuntimeConfig ( )
38+ const { locale : requestedRuntimeLocale , pathname : sourcePath } = stripRuntimeLocalePrefix ( context . url . pathname )
39+ const runtimeEnv = ( ( context . locals as { runtime ?: { env ?: RuntimeEnv } } ) . runtime ?. env || { } ) as RuntimeEnv
40+ const isLocalRequest = [ '127.0.0.1' , 'localhost' ] . includes ( context . url . hostname )
41+ const translationEnabled = Boolean ( runtimeEnv . AI ) && ! isLocalRequest
42+ const eligiblePath = isRuntimeTranslationEligiblePath ( sourcePath )
43+ const showSelector = translationEnabled && eligiblePath
44+ const shouldTranslate = Boolean ( requestedRuntimeLocale ) && requestedRuntimeLocale !== defaultLocale && translationEnabled && eligiblePath
45+ const localeForUi : RuntimeTranslationLocale = shouldTranslate && requestedRuntimeLocale ? requestedRuntimeLocale : defaultLocale
46+ const search = context . url . search || ''
547
6- export const onRequest = defineMiddleware ( ( context , next ) => {
748 // When Astro pre-renders during `astro build`, there is no real request.
849 // Skip the Paraglide middleware so we don't touch unavailable request headers.
950 // Use context.isPrerendered which is the reliable way to detect prerendering
1051 if ( context . isPrerendered ) {
11- context . locals . locale = ( context . currentLocale || defaultLocale ) as Locales
12- context . locals . runtimeConfig = useRuntimeConfig ( )
52+ context . locals . locale = defaultLocale as Locales
53+ context . locals . runtimeConfig = runtimeConfig
54+ context . locals . translation = buildTranslationState ( defaultLocale , {
55+ enabled : false ,
56+ isTranslated : false ,
57+ showSelector : false ,
58+ sourcePath,
59+ } )
1360 return next ( )
1461 }
1562
16- return paraglideMiddleware ( context . request , async ( ) => {
17- context . locals . locale = ( context . currentLocale || defaultLocale ) as Locales
18- context . locals . runtimeConfig = useRuntimeConfig ( )
19- return await next ( )
63+ if ( requestedRuntimeLocale === defaultLocale ) {
64+ return Response . redirect ( new URL ( `${ sourcePath } ${ search } ` , context . url ) , 302 )
65+ }
66+
67+ if ( requestedRuntimeLocale && ( ! translationEnabled || ! eligiblePath ) ) {
68+ return Response . redirect ( new URL ( `${ sourcePath } ${ search } ` , context . url ) , 302 )
69+ }
70+
71+ const requestForRender = shouldTranslate ? new Request ( new URL ( `${ sourcePath } ${ search } ` , context . url ) , context . request ) : context . request
72+
73+ return paraglideMiddleware ( requestForRender , async ( ) => {
74+ context . locals . locale = defaultLocale as Locales
75+ context . locals . runtimeConfig = runtimeConfig
76+ context . locals . translation = buildTranslationState ( localeForUi , {
77+ enabled : translationEnabled ,
78+ isTranslated : shouldTranslate ,
79+ showSelector,
80+ sourcePath,
81+ } )
82+
83+ const renderedResponse = shouldTranslate ? await next ( requestForRender ) : await next ( )
84+ if ( ! shouldTranslate || ! runtimeEnv . AI ) {
85+ return renderedResponse
86+ }
87+
88+ const fallbackResponse = renderedResponse . clone ( )
89+
90+ try {
91+ return await translateHtmlResponseWithCloudflareAI ( {
92+ ai : runtimeEnv . AI ,
93+ locale : requestedRuntimeLocale as RuntimeTranslationLocale ,
94+ requestUrl : context . url ,
95+ response : renderedResponse ,
96+ sourcePath,
97+ } )
98+ } catch ( error ) {
99+ console . error ( `Runtime translation failed for ${ context . request . url } ` , error )
100+ return fallbackResponse
101+ }
20102 } )
21103} )
0 commit comments