@@ -61,6 +61,7 @@ const lang = langIdx !== -1 ? args[langIdx + 1] : null;
6161
6262const flagAll = args . includes ( '--all' ) ;
6363const flagIncremental = args . includes ( '--incremental' ) ;
64+ const flagSync = args . includes ( '--sync' ) ;
6465const flagApiSpecs = args . includes ( '--api-specs' ) ;
6566const flagSidebarsOnly = args . includes ( '--sidebars' ) ;
6667const flagMigrateHashes = args . includes ( '--migrate-hashes' ) ;
@@ -107,8 +108,8 @@ if ((flagResume || fileId || fileIds || sidebarName || platform) && !lang) {
107108 process . exit ( 1 ) ;
108109}
109110
110- // Single-file, multi-id, or incremental → use sync (immediate) API; otherwise Batch API
111- const syncMode = fileId != null || fileIds != null || flagIncremental ;
111+ // Single-file, multi-id, incremental, or --sync → use sync (immediate) API; otherwise Batch API
112+ const syncMode = fileId != null || fileIds != null || flagIncremental || flagSync ;
112113
113114// ---------------------------------------------------------------------------
114115// Locale discovery
@@ -803,6 +804,9 @@ async function translateFileWithSections(client, file, systemPrompt, localesDir,
803804// Batch translation (default, --all, --platform)
804805// ---------------------------------------------------------------------------
805806
807+ // Max files per batch chunk — smaller chunks complete faster and give intermediate progress.
808+ const BATCH_CHUNK_SIZE = 40 ;
809+
806810async function translateBatch ( client , files , systemPrompt , localesDir , tag , lang ) {
807811 const hashesDir = path . join ( localesDir , '.hashes' ) ;
808812
@@ -831,45 +835,65 @@ async function translateBatch(client, files, systemPrompt, localesDir, tag, lang
831835
832836 if ( batchFiles . length === 0 ) return ;
833837
834- console . log ( `${ tag } Reading source files...` ) ;
835- const requests = await Promise . all (
836- batchFiles . map ( async ( file ) => {
837- const basename = path . basename ( file , '.mdx' ) ;
838- const content = await fs . readFile ( file , 'utf-8' ) ;
839- return {
840- custom_id : basename ,
841- params : {
842- model : 'claude-sonnet-4-6' ,
843- max_tokens : 8192 ,
844- system : systemPrompt ,
845- messages : [ { role : 'user' , content } ] ,
846- } ,
847- } ;
848- } )
849- ) ;
838+ // Split into chunks so large platforms don't sit in one huge queue entry.
839+ const chunks = [ ] ;
840+ for ( let i = 0 ; i < batchFiles . length ; i += BATCH_CHUNK_SIZE ) {
841+ chunks . push ( batchFiles . slice ( i , i + BATCH_CHUNK_SIZE ) ) ;
842+ }
850843
851- console . log ( `${ tag } Submitting batch of ${ requests . length } requests...` ) ;
852- const batch = await client . messages . batches . create ( { requests } ) ;
853- const batchId = batch . id ;
844+ for ( let c = 0 ; c < chunks . length ; c ++ ) {
845+ const chunk = chunks [ c ] ;
846+ const chunkLabel = chunks . length > 1 ? ` (chunk ${ c + 1 } /${ chunks . length } )` : '' ;
847+
848+ console . log ( `${ tag } Reading source files${ chunkLabel } ...` ) ;
849+ const requests = await Promise . all (
850+ chunk . map ( async ( file ) => {
851+ const basename = path . basename ( file , '.mdx' ) ;
852+ const content = await fs . readFile ( file , 'utf-8' ) ;
853+ return {
854+ custom_id : basename ,
855+ params : {
856+ model : 'claude-sonnet-4-6' ,
857+ max_tokens : 8192 ,
858+ system : systemPrompt ,
859+ messages : [ { role : 'user' , content } ] ,
860+ } ,
861+ } ;
862+ } )
863+ ) ;
854864
855- await fs . writeFile ( BATCH_ID_FILE , batchId , 'utf-8' ) ;
856- console . log ( `${ tag } Batch submitted: ${ batchId } ` ) ;
857- console . log ( `${ tag } Batch ID saved to .translate-batch-id` ) ;
858- console . log ( `${ tag } Use --resume <batchId> to retrieve results if this process is interrupted.` ) ;
865+ console . log ( `${ tag } Submitting batch of ${ requests . length } requests${ chunkLabel } ...` ) ;
866+ const batch = await client . messages . batches . create ( { requests } ) ;
867+ const batchId = batch . id ;
859868
860- const fileMap = Object . fromEntries ( batchFiles . map ( f => [ path . basename ( f , '.mdx' ) , f ] ) ) ;
861- await waitAndRetrieve ( client , batchId , fileMap , localesDir , tag , lang ) ;
869+ await fs . writeFile ( BATCH_ID_FILE , batchId , 'utf-8' ) ;
870+ console . log ( `${ tag } Batch submitted: ${ batchId } ` ) ;
871+ console . log ( `${ tag } Batch ID saved to .translate-batch-id` ) ;
872+ console . log ( `${ tag } Use --resume <batchId> to retrieve results if this process is interrupted.` ) ;
873+
874+ const fileMap = Object . fromEntries ( chunk . map ( f => [ path . basename ( f , '.mdx' ) , f ] ) ) ;
875+ await waitAndRetrieve ( client , batchId , fileMap , localesDir , tag , lang ) ;
876+ }
862877}
863878
864879async function waitAndRetrieve ( client , batchId , fileMap , localesDir , tag , lang ) {
865880 console . log ( `${ tag } Polling batch status every 30 seconds...` ) ;
866881
882+ const startedAt = Date . now ( ) ;
867883 let batch = await client . messages . batches . retrieve ( batchId ) ;
884+ // Warn threshold: 15 min base + 30s per request, capped at 60 min.
885+ const requestCount = Object . keys ( fileMap ) . length ;
886+ const warnAfterMin = Math . min ( 60 , 15 + Math . ceil ( requestCount * 0.5 ) ) ;
868887 while ( batch . processing_status !== 'ended' ) {
869888 const counts = batch . request_counts ?? { } ;
889+ const elapsedMin = Math . floor ( ( Date . now ( ) - startedAt ) / 60000 ) ;
890+ const elapsed = elapsedMin >= 1 ? ` (${ elapsedMin } m elapsed)` : '' ;
891+ const warning = elapsedMin >= warnAfterMin && ( counts . succeeded ?? 0 ) === 0
892+ ? ` ⚠️ no progress in ${ warnAfterMin } m — API may be slow or use --sync to bypass batch`
893+ : '' ;
870894 console . log (
871895 ` Status: ${ batch . processing_status } — processing: ${ counts . processing ?? '?' } , ` +
872- `succeeded: ${ counts . succeeded ?? '?' } , errored: ${ counts . errored ?? '?' } `
896+ `succeeded: ${ counts . succeeded ?? '?' } , errored: ${ counts . errored ?? '?' } ${ elapsed } ${ warning } `
873897 ) ;
874898 await sleep ( 30_000 ) ;
875899 batch = await client . messages . batches . retrieve ( batchId ) ;
0 commit comments