283 lines
11 KiB
JavaScript
283 lines
11 KiB
JavaScript
"use strict";
|
|
// Copyright 2022 DeepL SE (https://www.deepl.com)
|
|
// Use of this source code is governed by an MIT
|
|
// license that can be found in the LICENSE file.
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.extractGlossaryId = exports.appendCsvDictionaryEntries = exports.appendDictionaryEntries = exports.validateAndAppendTextOptions = exports.appendTextsAndReturnIsSingular = exports.buildURLSearchParams = exports.nonRegionalLanguageCode = exports.standardizeLanguageCode = exports.isFreeAccountAuthKey = exports.toBoolString = exports.isString = exports.timeout = exports.streamToString = exports.streamToBuffer = exports.logInfo = exports.logDebug = void 0;
|
|
const loglevel_1 = __importDefault(require("loglevel"));
|
|
const errors_1 = require("./errors");
|
|
const logger = loglevel_1.default.getLogger('deepl');
|
|
function concatLoggingArgs(args) {
|
|
let detail = '';
|
|
if (args) {
|
|
for (const [key, value] of Object.entries(args)) {
|
|
detail += `, ${key} = ${value}`;
|
|
}
|
|
}
|
|
return detail;
|
|
}
|
|
function logDebug(message, args) {
|
|
logger.debug(message + concatLoggingArgs(args));
|
|
}
|
|
exports.logDebug = logDebug;
|
|
function logInfo(message, args) {
|
|
logger.info(message + concatLoggingArgs(args));
|
|
}
|
|
exports.logInfo = logInfo;
|
|
/**
|
|
* Converts contents of given stream to a Buffer.
|
|
* @private
|
|
*/
|
|
async function streamToBuffer(stream) {
|
|
const chunks = [];
|
|
return new Promise((resolve, reject) => {
|
|
stream.on('data', (chunk) => chunks.push(chunk));
|
|
stream.on('error', (err) => reject(err));
|
|
stream.on('end', () => resolve(Buffer.concat(chunks)));
|
|
});
|
|
}
|
|
exports.streamToBuffer = streamToBuffer;
|
|
/**
|
|
* Converts contents of given stream to a string using UTF-8 encoding.
|
|
* @private
|
|
*/
|
|
async function streamToString(stream) {
|
|
return (await streamToBuffer(stream)).toString('utf8');
|
|
}
|
|
exports.streamToString = streamToString;
|
|
// Wrap setTimeout() with Promise
|
|
const timeout = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
exports.timeout = timeout;
|
|
/**
|
|
* Returns true if the given argument is a string.
|
|
* @param arg Argument to check.
|
|
*/
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
function isString(arg) {
|
|
return typeof arg === 'string';
|
|
}
|
|
exports.isString = isString;
|
|
/**
|
|
* Returns '1' if the given arg is truthy, '0' otherwise.
|
|
* @param arg Argument to check.
|
|
*/
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
function toBoolString(arg) {
|
|
return arg ? '1' : '0';
|
|
}
|
|
exports.toBoolString = toBoolString;
|
|
/**
|
|
* Returns true if the specified DeepL Authentication Key is associated with a free account,
|
|
* otherwise false.
|
|
* @param authKey The authentication key to check.
|
|
* @return True if the key is associated with a free account, otherwise false.
|
|
*/
|
|
function isFreeAccountAuthKey(authKey) {
|
|
return authKey.endsWith(':fx');
|
|
}
|
|
exports.isFreeAccountAuthKey = isFreeAccountAuthKey;
|
|
/**
|
|
* Changes the upper- and lower-casing of the given language code to match ISO 639-1 with an
|
|
* optional regional code from ISO 3166-1.
|
|
* For example, input 'EN-US' returns 'en-US'.
|
|
* @param langCode String containing language code to standardize.
|
|
* @return Standardized language code.
|
|
*/
|
|
function standardizeLanguageCode(langCode) {
|
|
if (!isString(langCode) || langCode.length === 0) {
|
|
throw new errors_1.DeepLError('langCode must be a non-empty string');
|
|
}
|
|
const [lang, region] = langCode.split('-', 2);
|
|
return (region === undefined ? lang.toLowerCase() : `${lang.toLowerCase()}-${region.toUpperCase()}`);
|
|
}
|
|
exports.standardizeLanguageCode = standardizeLanguageCode;
|
|
/**
|
|
* Removes the regional variant from a language, for example inputs 'en' and 'en-US' both return
|
|
* 'en'.
|
|
* @param langCode String containing language code to convert.
|
|
* @return Language code with regional variant removed.
|
|
*/
|
|
function nonRegionalLanguageCode(langCode) {
|
|
if (!isString(langCode) || langCode.length === 0) {
|
|
throw new errors_1.DeepLError('langCode must be a non-empty string');
|
|
}
|
|
return langCode.split('-', 2)[0].toLowerCase();
|
|
}
|
|
exports.nonRegionalLanguageCode = nonRegionalLanguageCode;
|
|
/**
|
|
* Joins given TagList with commas to form a single comma-delimited string.
|
|
* @private
|
|
*/
|
|
function joinTagList(tagList) {
|
|
if (isString(tagList)) {
|
|
return tagList;
|
|
}
|
|
else {
|
|
return tagList.join(',');
|
|
}
|
|
}
|
|
/**
|
|
* Validates and prepares URLSearchParams for arguments common to text and document translation.
|
|
* @private
|
|
*/
|
|
function buildURLSearchParams(sourceLang, targetLang, formality, glossary, extraRequestParameters) {
|
|
targetLang = standardizeLanguageCode(targetLang);
|
|
if (sourceLang !== null) {
|
|
sourceLang = standardizeLanguageCode(sourceLang);
|
|
}
|
|
if (glossary !== undefined && sourceLang === null) {
|
|
throw new errors_1.DeepLError('sourceLang is required if using a glossary');
|
|
}
|
|
if (targetLang === 'en') {
|
|
throw new errors_1.DeepLError("targetLang='en' is deprecated, please use 'en-GB' or 'en-US' instead.");
|
|
}
|
|
else if (targetLang === 'pt') {
|
|
throw new errors_1.DeepLError("targetLang='pt' is deprecated, please use 'pt-PT' or 'pt-BR' instead.");
|
|
}
|
|
const searchParams = new URLSearchParams({
|
|
target_lang: targetLang,
|
|
});
|
|
if (sourceLang !== null) {
|
|
searchParams.append('source_lang', sourceLang);
|
|
}
|
|
if (formality !== undefined) {
|
|
const formalityStr = String(formality).toLowerCase();
|
|
searchParams.append('formality', formalityStr);
|
|
}
|
|
if (glossary !== undefined) {
|
|
if (!isString(glossary)) {
|
|
if (glossary.glossaryId === undefined) {
|
|
throw new errors_1.DeepLError('glossary option should be a string containing the Glossary ID or a GlossaryInfo object.');
|
|
}
|
|
glossary = glossary.glossaryId;
|
|
}
|
|
searchParams.append('glossary_id', glossary);
|
|
}
|
|
if (extraRequestParameters !== undefined) {
|
|
for (const paramName in extraRequestParameters) {
|
|
searchParams.append(paramName, extraRequestParameters[paramName]);
|
|
}
|
|
}
|
|
return searchParams;
|
|
}
|
|
exports.buildURLSearchParams = buildURLSearchParams;
|
|
/**
|
|
* Validates and appends texts to HTTP request parameters, and returns whether a single text
|
|
* argument was provided.
|
|
* @param data Parameters for HTTP request.
|
|
* @param texts User-supplied texts to be checked.
|
|
* @return True if only a single text was provided.
|
|
* @private
|
|
*/
|
|
function appendTextsAndReturnIsSingular(data, texts) {
|
|
const singular = !Array.isArray(texts);
|
|
if (singular) {
|
|
if (!isString(texts) || texts.length === 0) {
|
|
throw new errors_1.DeepLError('texts parameter must be a non-empty string or array of non-empty strings');
|
|
}
|
|
data.append('text', texts);
|
|
}
|
|
else {
|
|
for (const text of texts) {
|
|
if (!isString(text) || text.length === 0) {
|
|
throw new errors_1.DeepLError('texts parameter must be a non-empty string or array of non-empty strings');
|
|
}
|
|
data.append('text', text);
|
|
}
|
|
}
|
|
return singular;
|
|
}
|
|
exports.appendTextsAndReturnIsSingular = appendTextsAndReturnIsSingular;
|
|
/**
|
|
* Validates and appends text options to HTTP request parameters.
|
|
* @param data Parameters for HTTP request.
|
|
* @param options Options for translate text request.
|
|
* Note the formality and glossaryId options are handled separately, because these options
|
|
* overlap with the translateDocument function.
|
|
* @private
|
|
*/
|
|
function validateAndAppendTextOptions(data, options) {
|
|
if (!options) {
|
|
return;
|
|
}
|
|
if (options.splitSentences !== undefined) {
|
|
options.splitSentences = options.splitSentences.toLowerCase();
|
|
if (options.splitSentences === 'on' || options.splitSentences === 'default') {
|
|
data.append('split_sentences', '1');
|
|
}
|
|
else if (options.splitSentences === 'off') {
|
|
data.append('split_sentences', '0');
|
|
}
|
|
else {
|
|
data.append('split_sentences', options.splitSentences);
|
|
}
|
|
}
|
|
if (options.preserveFormatting !== undefined) {
|
|
data.append('preserve_formatting', toBoolString(options.preserveFormatting));
|
|
}
|
|
if (options.tagHandling !== undefined) {
|
|
data.append('tag_handling', options.tagHandling);
|
|
}
|
|
if (options.outlineDetection !== undefined) {
|
|
data.append('outline_detection', toBoolString(options.outlineDetection));
|
|
}
|
|
if (options.context !== undefined) {
|
|
data.append('context', options.context);
|
|
}
|
|
if (options.modelType !== undefined) {
|
|
data.append('model_type', options.modelType);
|
|
}
|
|
if (options.nonSplittingTags !== undefined) {
|
|
data.append('non_splitting_tags', joinTagList(options.nonSplittingTags));
|
|
}
|
|
if (options.splittingTags !== undefined) {
|
|
data.append('splitting_tags', joinTagList(options.splittingTags));
|
|
}
|
|
if (options.ignoreTags !== undefined) {
|
|
data.append('ignore_tags', joinTagList(options.ignoreTags));
|
|
}
|
|
}
|
|
exports.validateAndAppendTextOptions = validateAndAppendTextOptions;
|
|
/**
|
|
* Appends glossary dictionaries to HTTP request parameters.
|
|
* @param data URL-encoded parameters for a HTTP request.
|
|
* @param dictionaries Glossary dictionaries to append.
|
|
* @private
|
|
*/
|
|
function appendDictionaryEntries(data, dictionaries) {
|
|
dictionaries.forEach((dict, index) => {
|
|
data.append(`dictionaries[${index}].source_lang`, dict.sourceLangCode);
|
|
data.append(`dictionaries[${index}].target_lang`, dict.targetLangCode);
|
|
data.append(`dictionaries[${index}].entries`, dict.entries.toTsv());
|
|
data.append(`dictionaries[${index}].entries_format`, 'tsv');
|
|
});
|
|
}
|
|
exports.appendDictionaryEntries = appendDictionaryEntries;
|
|
/**
|
|
* Appends a glossary dictionary with CSV entries to HTTP request parameters.
|
|
* @param data URL-encoded parameters for a HTTP request.
|
|
* @param sourceLanguageCode Source language code of the dictionary.
|
|
* @param targetLanguageCode Target language code of the dictionary.
|
|
* @param csvContent CSV-formatted string containing the dictionary entries.
|
|
* @private
|
|
*/
|
|
function appendCsvDictionaryEntries(data, sourceLanguageCode, targetLanguageCode, csvContent) {
|
|
data.append('dictionaries[0].source_lang', sourceLanguageCode);
|
|
data.append('dictionaries[0].target_lang', targetLanguageCode);
|
|
data.append('dictionaries[0].entries', csvContent);
|
|
data.append('dictionaries[0].entries_format', 'csv');
|
|
}
|
|
exports.appendCsvDictionaryEntries = appendCsvDictionaryEntries;
|
|
/**
|
|
* Extract the glossary ID from the argument.
|
|
* @param glossary The glossary as a string, GlossaryInfo, or MultilingualGlossaryInfo.
|
|
* @private
|
|
*/
|
|
function extractGlossaryId(glossary) {
|
|
return typeof glossary === 'string' ? glossary : glossary.glossaryId;
|
|
}
|
|
exports.extractGlossaryId = extractGlossaryId;
|