@@ -39,6 +39,9 @@ import {
3939 TranslationMemoryJob ,
4040 TranslationMemorySegments ,
4141 TranslationMemorySegmentsOptions ,
42+ CreateTranslationMemoryImportOptions ,
43+ ImportTranslationMemoryOptions ,
44+ WaitOptions ,
4245 LanguageResource ,
4346 LanguageResourceName ,
4447 GetLanguagesForResourceOptions ,
@@ -80,6 +83,7 @@ import {
8083 extractStyleProfileId ,
8184 extractTranslationMemoryId ,
8285 isString ,
86+ streamToBuffer ,
8387 logInfo ,
8488 resolveExtraBodyParameters ,
8589 setExtraBodyParameters ,
@@ -1289,16 +1293,17 @@ export class DeepLClient extends Translator {
12891293 *
12901294 * @param fileName: Name of the TMX file to import, for example 'legal.tmx'.
12911295 * @param contentLength: Size of the TMX file in bytes.
1292- * @param options: Optional contentType (defaults to 'application/xml') and displayName for the
1293- * resulting translation memory (defaults to the file name).
1296+ * @param options: Optional contentType (defaults to 'application/xml'), displayName for the
1297+ * resulting translation memory (defaults to the file name) and extraBodyParameters, see
1298+ * {@link CreateTranslationMemoryImportOptions}.
12941299 * @returns {Promise<TranslationMemoryImport> } The job ID and upload URL.
12951300 *
12961301 * @throws {DeepLError } If any error occurs while communicating with the DeepL API.
12971302 */
12981303 async createTranslationMemoryImport (
12991304 fileName : string ,
13001305 contentLength : number ,
1301- options ?: { contentType ?: string ; displayName ?: string } ,
1306+ options ?: CreateTranslationMemoryImportOptions ,
13021307 ) : Promise < TranslationMemoryImport > {
13031308 if ( ! fileName ) {
13041309 throw new ArgumentError ( 'fileName must not be empty' ) ;
@@ -1322,7 +1327,7 @@ export class DeepLClient extends Translator {
13221327 const { statusCode, content } = await this . httpClient . sendRequestWithBackoff < string > (
13231328 'POST' ,
13241329 '/v3/translation_memories/import' ,
1325- { jsonBody } ,
1330+ { jsonBody : addExtraBodyParametersToJson ( jsonBody , options ) } ,
13261331 ) ;
13271332
13281333 await checkStatusCode ( statusCode , content ) ;
@@ -1403,12 +1408,15 @@ export class DeepLClient extends Translator {
14031408 /**
14041409 * Retrieves the status of a translation memory import or export job.
14051410 *
1406- * @param jobId : ID of the job to query.
1411+ * @param job : ID of the job to query, or a TranslationMemoryJob object .
14071412 * @returns {Promise<TranslationMemoryJob> } The current status of the job.
14081413 *
14091414 * @throws {DeepLError } If any error occurs while communicating with the DeepL API.
14101415 */
1411- async getTranslationMemoryJob ( jobId : string ) : Promise < TranslationMemoryJob > {
1416+ async getTranslationMemoryJob (
1417+ job : string | TranslationMemoryJob ,
1418+ ) : Promise < TranslationMemoryJob > {
1419+ const jobId = typeof job === 'string' ? job : job ?. jobId ;
14121420 if ( ! jobId ) {
14131421 throw new ArgumentError ( 'jobId must not be empty' ) ;
14141422 }
@@ -1422,30 +1430,31 @@ export class DeepLClient extends Translator {
14221430 }
14231431
14241432 /**
1425- * Polls a translation memory job until it finishes, sleeping between requests , and resolves
1426- * with the final status .
1433+ * Polls a translation memory job every 5 seconds until it completes , and resolves with the
1434+ * completed job .
14271435 *
14281436 * Note that an import job keeps reporting 'awaiting_input' for a while after its file has been
14291437 * uploaded, because the API detects the upload asynchronously. That status, and statuses this
14301438 * library does not know, are polled through. A job whose file is never uploaded does not finish
1431- * on its own, so pass timeoutMs when that is a possibility.
1439+ * on its own, so set a timeout when that is a possibility.
14321440 *
1433- * @param jobId: ID of the job to wait for.
1434- * @param timeoutMs: (Optional) Maximum time to wait in milliseconds before rejecting. Note that
1435- * this is not accurate to the millisecond, as the job is only polled every 5 seconds.
1436- * @returns {Promise<TranslationMemoryJob> } The job once it has finished.
1441+ * @param job: ID of the job to wait for, or a TranslationMemoryJob object.
1442+ * @param options: (Optional) WaitOptions with timeoutMs, or the timeout in milliseconds as a
1443+ * number: maximum time to wait before rejecting. Note that this is not accurate to the
1444+ * millisecond, as the job is only polled every 5 seconds.
1445+ * @returns {Promise<TranslationMemoryJob> } The completed job.
14371446 *
1438- * @throws {DeepLError } If the job fails, the timeout is exceeded, or any error occurs while
1439- * communicating with the DeepL API.
1447+ * @throws {DeepLError } If the job fails or expires (with the job's error message), the timeout
1448+ * is exceeded, or any error occurs while communicating with the DeepL API.
14401449 */
1441- async isTranslationMemoryJobComplete (
1442- jobId : string ,
1443- timeoutMs ?: number ,
1450+ async waitUntilTranslationMemoryJobDone (
1451+ job : string | TranslationMemoryJob ,
1452+ options ?: WaitOptions | number ,
14441453 ) : Promise < TranslationMemoryJob > {
1454+ const timeoutMs = typeof options === 'number' ? options : options ?. timeoutMs ;
14451455 const deadline = timeoutMs !== undefined ? Date . now ( ) + timeoutMs : undefined ;
1446- let job = await this . getTranslationMemoryJob ( jobId ) ;
1447- // Unknown statuses and a missing result are polled through; the timeout bounds the wait.
1448- while ( ! DeepLClient . isJobFinished ( job ) ) {
1456+ let current = await this . getTranslationMemoryJob ( job ) ;
1457+ while ( ! current . done ( ) && current . ok ( ) ) {
14491458 let sleepMs = 5000 ;
14501459 if ( deadline !== undefined ) {
14511460 const remainingMs = deadline - Date . now ( ) ;
@@ -1458,13 +1467,25 @@ export class DeepLClient extends Translator {
14581467 }
14591468 await timeout ( sleepMs ) ;
14601469 logInfo ( `Rechecking translation memory job status after sleeping for ${ sleepMs } ms.` ) ;
1461- job = await this . getTranslationMemoryJob ( jobId ) ;
1470+ current = await this . getTranslationMemoryJob ( current . jobId ) ;
14621471 }
1463- const result = job . results [ 0 ] ;
1464- if ( result . status !== 'completed' ) {
1465- throw new DeepLError ( result . errorMessage || `Job ${ result . status } ` ) ;
1472+ if ( ! current . ok ( ) ) {
1473+ throw new DeepLError ( current . result ?. errorMessage || `Job ${ current . status } ` ) ;
14661474 }
1467- return job ;
1475+ return current ;
1476+ }
1477+
1478+ /**
1479+ * Polls a translation memory job until it completes, and resolves with the completed job.
1480+ *
1481+ * @deprecated Use {@link waitUntilTranslationMemoryJobDone} instead. Will be removed in the
1482+ * next major version.
1483+ */
1484+ async isTranslationMemoryJobComplete (
1485+ job : string | TranslationMemoryJob ,
1486+ timeoutMs ?: number | WaitOptions ,
1487+ ) : Promise < TranslationMemoryJob > {
1488+ return this . waitUntilTranslationMemoryJobDone ( job , timeoutMs ) ;
14681489 }
14691490
14701491 /**
@@ -1476,7 +1497,7 @@ export class DeepLClient extends Translator {
14761497 * @throws {DeepLError } If the job has no download URL, or any error occurs while downloading.
14771498 */
14781499 async downloadTranslationMemoryExport (
1479- job : TranslationMemoryJob ,
1500+ job : Omit < TranslationMemoryJob , 'done' | 'ok' > ,
14801501 outputFile : string | fs . WriteStream ,
14811502 ) : Promise < void > {
14821503 const downloadUrl = job . results [ 0 ] ?. downloadUrl ;
@@ -1563,32 +1584,47 @@ export class DeepLClient extends Translator {
15631584 * Note that the API detects the upload asynchronously, so the job keeps reporting
15641585 * 'awaiting_input' for a while (usually well under a minute) before it completes.
15651586 *
1566- * @param inputPath : Path of the TMX file to import.
1587+ * @param input : Path of the TMX file to import, or a Buffer or ReadStream with its content .
15671588 * @param options: Optional displayName for the resulting translation memory (defaults to the
1568- * file name), contentType (defaults to 'application/xml'), and timeoutMs limiting how long to
1569- * wait for the import to finish.
1589+ * file name), contentType (defaults to 'application/xml'), fileName (required for a Buffer or
1590+ * stream unless displayName is set), extraBodyParameters, and timeoutMs limiting how long to
1591+ * wait for the import to finish, see {@link ImportTranslationMemoryOptions}.
15701592 * @returns {Promise<TranslationMemoryJob> } The completed import job; its result carries the
15711593 * new translation memory ID.
15721594 *
15731595 * @throws {DeepLError } If the import fails, the timeout is exceeded, or any error occurs while
15741596 * communicating with the DeepL API.
15751597 */
15761598 async importTranslationMemoryFromFilepath (
1577- inputPath : string ,
1578- options ?: { displayName ?: string ; contentType ?: string ; timeoutMs ?: number } ,
1599+ input : string | Buffer | fs . ReadStream ,
1600+ options ?: ImportTranslationMemoryOptions ,
15791601 ) : Promise < TranslationMemoryJob > {
1580- const fileBuffer = await fs . promises . readFile ( inputPath ) ;
1581- const created = await this . createTranslationMemoryImport (
1582- path . basename ( inputPath ) ,
1583- fileBuffer . length ,
1584- { displayName : options ?. displayName , contentType : options ?. contentType } ,
1585- ) ;
1602+ let fileBuffer : Buffer ;
1603+ let fileName : string | undefined ;
1604+ if ( isString ( input ) ) {
1605+ fileBuffer = await fs . promises . readFile ( input ) ;
1606+ fileName = options ?. fileName ?? path . basename ( input ) ;
1607+ } else {
1608+ fileName = options ?. fileName ?? options ?. displayName ;
1609+ if ( ! fileName ) {
1610+ throw new ArgumentError (
1611+ 'options.fileName or options.displayName must be specified unless using an input file path' ,
1612+ ) ;
1613+ }
1614+ fileBuffer = input instanceof fs . ReadStream ? await streamToBuffer ( input ) : input ;
1615+ }
1616+ const created = await this . createTranslationMemoryImport ( fileName , fileBuffer . length , {
1617+ displayName : options ?. displayName ,
1618+ contentType : options ?. contentType ,
1619+ extraBodyParameters : options ?. extraBodyParameters ,
1620+ extraRequestParameters : options ?. extraRequestParameters ,
1621+ } ) ;
15861622 await this . uploadTranslationMemoryFile (
15871623 created ,
15881624 fileBuffer ,
15891625 options ?. contentType ?? 'application/xml' ,
15901626 ) ;
1591- return this . isTranslationMemoryJobComplete ( created . jobId , options ?. timeoutMs ) ;
1627+ return this . waitUntilTranslationMemoryJobDone ( created . jobId , options ) ;
15921628 }
15931629
15941630 /**
@@ -1597,7 +1633,8 @@ export class DeepLClient extends Translator {
15971633 *
15981634 * @param translationMemory: Translation memory ID, or TranslationMemoryInfo object.
15991635 * @param outputFile: String containing output file path, or a WriteStream to store file data.
1600- * @param options: Optional timeoutMs limiting how long to wait for the export to finish.
1636+ * @param options: Optional WaitOptions with timeoutMs limiting how long to wait for the export
1637+ * to finish.
16011638 * @returns {Promise<TranslationMemoryJob> } The completed export job.
16021639 *
16031640 * @throws {DeepLError } If the export fails, the timeout is exceeded, or any error occurs while
@@ -1606,23 +1643,14 @@ export class DeepLClient extends Translator {
16061643 async exportTranslationMemoryToFilepath (
16071644 translationMemory : TranslationMemoryId | TranslationMemoryInfo ,
16081645 outputFile : string | fs . WriteStream ,
1609- options ?: { timeoutMs ?: number } ,
1646+ options ?: WaitOptions ,
16101647 ) : Promise < TranslationMemoryJob > {
16111648 const created = await this . createTranslationMemoryExport ( translationMemory ) ;
1612- const job = await this . isTranslationMemoryJobComplete ( created . jobId , options ?. timeoutMs ) ;
1649+ const job = await this . waitUntilTranslationMemoryJobDone ( created . jobId , options ) ;
16131650 await this . downloadTranslationMemoryExport ( job , outputFile ) ;
16141651 return job ;
16151652 }
16161653
1617- /**
1618- * True once the job has completed, failed or expired.
1619- * @private
1620- */
1621- private static isJobFinished ( job : TranslationMemoryJob ) : boolean {
1622- const status = job . results [ 0 ] ?. status ;
1623- return status === 'completed' || status === 'failed' || status === 'expired' ;
1624- }
1625-
16261654 /**
16271655 * Creates a new style rule.
16281656 *
0 commit comments