I’ve modified the Node script — translate.js — as such:
const fs = require('fs');
const https = require('https');
const keytar = require('keytar');
const path = require('path');
const TRANSLATOR_REGION_SERVICE = "AzureTranslatorRegion";
const TRANSLATOR_SUBSCRIPTION_KEY_SERVICE = "AzureTranslatorKey";
const baseURL = "https://api.cognitive.microsofttranslator.com";
const apiVersion = "3.0";
async function getCredentials() {
const region = await keytar.getPassword(TRANSLATOR_REGION_SERVICE, "default");
const subscriptionKey = await keytar.getPassword(TRANSLATOR_SUBSCRIPTION_KEY_SERVICE, "default");
if (!region || !subscriptionKey) {
throw new Error("Could not retrieve necessary keys from keychain");
}
return { region, subscriptionKey };
}
function translateText(text, fromLang, toLang, region, subscriptionKey, callback) {
const url = `${baseURL}/translate?api-version=${apiVersion}&from=${fromLang}&to=${toLang}`;
const postData = JSON.stringify([{ "Text": text }]);
const options = {
hostname: 'api.cognitive.microsofttranslator.com',
path: `/translate?api-version=${apiVersion}&from=${fromLang}&to=${toLang}`,
method: 'POST',
headers: {
'Ocp-Apim-Subscription-Key': subscriptionKey,
'Ocp-Apim-Subscription-Region': region,
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postData)
}
};
const req = https.request(options, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
const responseJSON = JSON.parse(data);
const translations = responseJSON[0].translations;
callback(translations[0].text);
});
});
req.on('error', (e) => {
console.error(`Problem with request: ${e.message}`);
callback(null);
});
req.write(postData);
req.end();
}
function removeExtension(filename) {
return filename.substring(0, filename.lastIndexOf('.')) || filename;
}
async function main() {
const args = process.argv;
if (args.length < 3) {
console.log("Usage: node translate.js <file_name>");
return;
}
// Join all arguments that are part of the file name
const fileName = args.slice(2).join(" ");
if (!fs.existsSync(fileName)) {
console.log("File does not exist: " + fileName);
return;
}
try {
const { region, subscriptionKey } = await getCredentials();
fs.readFile(fileName, 'utf8', (err, data) => {
if (err) {
console.log("Failed to read file: " + fileName);
return;
}
translateText(data, "so", "en", region, subscriptionKey, function(translatedContent) {
if (translatedContent) {
const combinedContent = `${translatedContent}\n\n---\n\n${data}`;
// Write the combined content back to the file
fs.writeFile(fileName, combinedContent, 'utf8', (writeErr) => {
if (writeErr) {
console.log("Failed to write translated content to file: " + fileName);
return;
}
console.log("File content translated and updated.");
// Translate the file name
const originalFileName = path.basename(fileName);
const newFileNameBase = removeExtension(originalFileName);
translateText(newFileNameBase, "so", "en", region, subscriptionKey, function(translatedFileName) {
if (translatedFileName) {
const newFileName = path.join(path.dirname(fileName), translatedFileName + path.extname(fileName));
fs.rename(fileName, newFileName, (renameErr) => {
if (renameErr) {
console.log("Failed to rename file: " + fileName);
return;
}
console.log("File renamed to: " + newFileName);
});
} else {
console.log("Failed to translate file name.");
}
});
});
} else {
console.log("Translation of file content failed.");
}
});
});
} catch (error) {
console.error("Error retrieving credentials:", error.message);
}
}
main();
I’ve checked this works as intended.
I’m still struggling to execute the script in a Smart Rule.
I’ve tried:
(() => {
const app = Application('DEVONthink 3');
const selectedRecords = app.selectedRecords();
selectedRecords.forEach(record => {
const path = record.path(); // Get the path of the selected file
if (path) {
const command = `path/to/node /path/to/translate.js "${path}"`;
const doShellScript = Application('System Events').doShellScript;
try {
const result = doShellScript(command);
console.log('Result:', result);
} catch (error) {
console.error('Error:', error);
}
}
});
})();
But no joy. Can you help me get over this final hurdle?