3030 ResultTooLarge ,
3131 materialize_export ,
3232)
33+ from app .i18n import DEFAULT_LANGUAGE , t
3334from app .settings import settings
3435from app .storage import generate_signed_url
3536
@@ -118,6 +119,7 @@ async def create_thread(
118119 thread_create = ThreadCreate (
119120 title = thread_payload .title ,
120121 user_id = user_id ,
122+ language = thread_payload .language ,
121123 )
122124
123125 return await database .create_thread (thread_create )
@@ -169,13 +171,17 @@ async def send_message(
169171 running_runs : RunningRuns ,
170172 user_id : UserID ,
171173) -> StreamingResponse :
172- await _authorize_thread (database , thread_id , user_id )
174+ thread = await _authorize_thread (database , thread_id , user_id )
173175
174176 run_id = str (uuid .uuid4 ())
175177
176178 config = ConfigDict (
177179 run_id = run_id ,
178- configurable = {"thread_id" : thread_id , "user_id" : user_id },
180+ configurable = {
181+ "thread_id" : thread_id ,
182+ "user_id" : user_id ,
183+ "language" : thread .language ,
184+ },
179185 )
180186
181187 message_create = MessageCreate (
@@ -196,6 +202,7 @@ async def send_message(
196202 thread_id = thread_id ,
197203 user_message = message ,
198204 model_uri = settings .MODEL_URI ,
205+ language = thread .language ,
199206 queue = queue ,
200207 ),
201208 name = f"run_agent:{ run_id } " ,
@@ -220,28 +227,18 @@ def _cleanup(task: asyncio.Task): # pragma: no cover
220227 )
221228
222229
223- # User-facing details the frontend surfaces to the end user when a download fails.
224- RESULTS_EXPIRED_DETAIL = "Estes resultados não estão mais disponíveis para download."
225-
226- RESULTS_TOO_LARGE_DETAIL = (
227- "Estes resultados são grandes demais para baixar em um único arquivo."
228- )
229-
230- # Fallback base name when a query's slug yields nothing filesystem-safe.
231- DEFAULT_EXPORT_FILENAME = "resultados"
232-
233-
234- def _sanitize_filename (slug : str ) -> str :
230+ def _sanitize_filename (slug : str , fallback : str ) -> str :
235231 """Sanitize a query's slug into a safe base filename.
236232
237233 Args:
238234 slug (str): The query's slug.
235+ fallback (str): Base name to use when the slug yields nothing filesystem-safe.
239236
240237 Returns:
241238 str: A filesystem-safe base filename, without extension.
242239 """
243240 filename = re .sub (r"[^\w-]+" , "_" , slug ).strip ("_" )
244- return filename or DEFAULT_EXPORT_FILENAME
241+ return filename or fallback
245242
246243
247244@router .post ("/messages/{message_id}/exports" )
@@ -263,6 +260,11 @@ async def export_message_results(
263260
264261 message = await _authorize_message (database , message_id , user_id )
265262
263+ # The thread's language localizes the download-failure details below. _authorize_message
264+ # already validated the thread exists and is owned; re-read it to read its language.
265+ thread = await database .get_thread (message .thread_id )
266+ language = thread .language if thread else DEFAULT_LANGUAGE
267+
266268 query_handle = await database .get_query_handle (message .id , query_ref )
267269
268270 if query_handle is None :
@@ -277,18 +279,20 @@ async def export_message_results(
277279 query_ref = query_handle .query_ref ,
278280 destination_table = query_handle .destination_table ,
279281 file_format = file_format ,
280- filename = _sanitize_filename (query_handle .slug ),
282+ filename = _sanitize_filename (
283+ query_handle .slug , t ("default_export_filename" , language )
284+ ),
281285 message_id = str (message .id ),
282286 )
283287 except ResultTableExpired as e :
284288 raise HTTPException (
285289 status_code = status .HTTP_410_GONE ,
286- detail = RESULTS_EXPIRED_DETAIL ,
290+ detail = t ( "results_expired" , language ) ,
287291 ) from e
288292 except ResultTooLarge as e :
289293 raise HTTPException (
290294 status_code = status .HTTP_400_BAD_REQUEST ,
291- detail = RESULTS_TOO_LARGE_DETAIL ,
295+ detail = t ( "results_too_large" , language ) ,
292296 ) from e
293297
294298 signed_url = generate_signed_url (
0 commit comments