mirror of
https://github.com/scratchfoundation/scratch-l10n.git
synced 2024-12-23 06:02:42 -05:00
30b1975e94
* Fixed a bug in the push script (tags were not saved as correct structured json, so they weren’t getting put into transifex) * extended FreshDesk API with functions to update knowledge base. Functions automatically try to create the item if it isn’t found for updating * tx-pull-help-names: pulls category and folder name translations from transifex and updates them in Freshdesk (Note, since we don’t send people into the knowledge base, these aren’t really public, but the actual folders and categories have to exist to be able to save articles) * tx-pull-help-articles: pull article translations from Transifex and update the Freshdesk KB. * help-utils: utility functions for the tx-pull-help-* scripts. Handles limiting the number of things happening in parallel - currently two languages may be processed at the same time.
146 lines
5.2 KiB
JavaScript
Executable file
146 lines
5.2 KiB
JavaScript
Executable file
#!/usr/bin/env babel-node
|
|
|
|
/**
|
|
* @fileoverview
|
|
* Script get Knowledge base articles from Freshdesk and push them to transifex.
|
|
*/
|
|
|
|
const args = process.argv.slice(2);
|
|
|
|
const usage = `
|
|
Pull knowledge base articles from Freshdesk and push to scratch-help project on transifex. Usage:
|
|
node tx-push-help.js
|
|
NOTE:
|
|
FRESHDESK_TOKEN environment variable needs to be set to a FreshDesk API key with
|
|
access to the Knowledge Base.
|
|
TX_TOKEN environment variable needs to be set with a Transifex API token. See
|
|
the Localization page on the GUI wiki for information about setting up Transifex.
|
|
`;
|
|
// Fail immediately if the API tokens are not defined, or there any argument
|
|
if (!process.env.TX_TOKEN || !process.env.FRESHDESK_TOKEN || args.length > 0) {
|
|
process.stdout.write(usage);
|
|
process.exit(1);
|
|
}
|
|
|
|
import transifex from 'transifex';
|
|
import FreshdeskApi from './freshdesk-api.js';
|
|
|
|
const FD = new FreshdeskApi('https://mitscratch.freshdesk.com', process.env.FRESHDESK_TOKEN);
|
|
const TX_PROJECT = 'scratch-help';
|
|
const TX = new transifex({
|
|
project_slug: TX_PROJECT,
|
|
credential: 'api:' + process.env.TX_TOKEN
|
|
});
|
|
|
|
const categoryNames = {};
|
|
const folderNames = {};
|
|
|
|
/**
|
|
* Generate a transifex id from the name and id field of an objects. Remove spaces and '/'
|
|
* from the name and append '.<id>' Transifex ids (slugs) have a max length of 50. Use at most
|
|
* 30 characters of the name to allow for Freshdesk id, and a suffix like '_json'
|
|
* @param {object} item data from Freshdesk that includes the name and id of a category or folder
|
|
* @return {string} generated transifex id
|
|
*/
|
|
const makeTxId = item => {
|
|
return `${item.name.replace(/[ /]/g, '').slice(0, 30)}_${item.id}`;
|
|
};
|
|
|
|
const txPushResource = (name, articles, type) => {
|
|
const resourceData = {
|
|
slug: name,
|
|
name: name,
|
|
priority: 0, // default to normal priority
|
|
i18n_type: type,
|
|
content: '{}'
|
|
};
|
|
TX.resourceCreateMethod(TX_PROJECT, resourceData, (err) => {
|
|
// ignore already created error report others
|
|
if (err && err.response.statusCode !== 400) {
|
|
process.stdout.write(`Transifex Error: ${err.message}\n`);
|
|
process.stdout.write(
|
|
`Transifex Error ${err.response.statusCode.toString()}: ${err.response.body}\n`);
|
|
process.exitCode = 1;
|
|
return;
|
|
}
|
|
// update Transifex with English source
|
|
TX.uploadSourceLanguageMethod(TX_PROJECT, name,
|
|
{content: JSON.stringify(articles, null, 2)}, (err1) => {
|
|
if (err1) {
|
|
process.stdout.write(`Transifex Error:${err1.name}, ${err1.message}\n`);
|
|
process.stdout.write(`Transifex Error:${err1.toString()}\n`);
|
|
process.exitCode = 1;
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* get a flattened list of folders
|
|
* @param {category} categories array of categories the folders belong to
|
|
* @return {Promise} flattened list of folders
|
|
*/
|
|
const getFolders = async (categories) => {
|
|
let categoryFolders = await Promise.all( // eslint-disable-line no-undef
|
|
categories.map(category => FD.listFolders(category))
|
|
);
|
|
return [].concat(...categoryFolders);
|
|
};
|
|
|
|
const PUBLISHED = 2; // in Freshdesk, draft status = 1, and published = 2
|
|
const saveArticles = (folder) => {
|
|
FD.listArticles(folder)
|
|
.then(json => {
|
|
let txArticles = json.reduce((strings, current) => {
|
|
if (current.status === PUBLISHED) {
|
|
strings[`${current.id}`] = {
|
|
title: {
|
|
string: current.title
|
|
},
|
|
description: {
|
|
string: current.description
|
|
}
|
|
};
|
|
if (current.tags.length > 0) {
|
|
strings[`${current.id}`].tags = {string: current.tags.toString()};
|
|
}
|
|
}
|
|
return strings;
|
|
}, {});
|
|
process.stdout.write(`Push ${folder.name} articles to Transifex\n`);
|
|
txPushResource(`${makeTxId(folder)}_json`, txArticles, 'STRUCTURED_JSON');
|
|
});
|
|
};
|
|
const getArticles = async (folders) => {
|
|
return Promise.all(folders.map(folder => saveArticles(folder))); // eslint-disable-line no-undef
|
|
};
|
|
|
|
const syncSources = async () => {
|
|
let status = 0;
|
|
status = await FD.listCategories()
|
|
.then(json => {
|
|
// save category names for translation
|
|
for (let cat of json.values()) {
|
|
categoryNames[`${makeTxId(cat)}`] = cat.name;
|
|
}
|
|
return json;
|
|
})
|
|
.then(getFolders)
|
|
.then(data => {
|
|
data.forEach(item => {
|
|
folderNames[`${makeTxId(item)}`] = item.name;
|
|
});
|
|
process.stdout.write('Push category and folder names to Transifex\n');
|
|
txPushResource('categoryNames_json', categoryNames, 'KEYVALUEJSON');
|
|
txPushResource('folderNames_json', folderNames, 'KEYVALUEJSON');
|
|
return data;
|
|
})
|
|
.then(getArticles)
|
|
.catch((e) => {
|
|
process.stdout.write(`Error:${e.message}\n`);
|
|
return 1;
|
|
});
|
|
process.exitCode = status;
|
|
};
|
|
|
|
syncSources();
|