From 59a4cde9ed94c84343f22951a06fcf8f2438cdf3 Mon Sep 17 00:00:00 2001 From: Randell Dawson <5313213+RandellDawson@users.noreply.github.com> Date: Tue, 23 Feb 2021 23:45:02 -0700 Subject: [PATCH] feat(tool): Script to mark translated strings on Crowdin as "Done" (#41241) --- .../mark-translated-strings-as-done.js | 54 +++++++++++ tools/crowdin/utils/get-languages.js | 17 ++++ tools/crowdin/utils/strings.js | 90 ++++++++++++++++++- 3 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 tools/crowdin/one-off-scripts/mark-translated-strings-as-done.js create mode 100644 tools/crowdin/utils/get-languages.js diff --git a/tools/crowdin/one-off-scripts/mark-translated-strings-as-done.js b/tools/crowdin/one-off-scripts/mark-translated-strings-as-done.js new file mode 100644 index 0000000000..a643e45197 --- /dev/null +++ b/tools/crowdin/one-off-scripts/mark-translated-strings-as-done.js @@ -0,0 +1,54 @@ +/* +This one-off script can be used to make any string in a Crowdin project to be marked as "Done" in case a workflow +change inadvertently causes strings, which already have translations (and even an approved translation), to be +reverted to "To Do". + +Specifying a projectId in the .env file allows the script to find any string with at least one translation, +adds a new temporary translation to it, and then delete the newly added translation. It is the addition of a +new translation that switches the status back to "Done" in the Crowdsourcing view on Crowdin. + +*/ + +require('dotenv').config({ path: `${__dirname}/../.env` }); + +const getLanguages = require('../utils/get-languages'); + +const { + addTranslation, + deleteTranslation, + getLanguageTranslations +} = require('../utils/strings'); + +const markTranslatedStringsAsDone = async projectId => { + console.log('starting script...'); + const languageIds = await getLanguages(projectId); + for (let languageId of languageIds) { + const translations = await getLanguageTranslations({ + projectId, + languageId + }); + if (translations && translations.length) { + console.log( + `${languageId} has ${translations.length} strings with at least one translation` + ); + for (let translation of translations) { + const { stringId } = translation.data; + const newTranslation = await addTranslation( + projectId, + stringId, + languageId, + 'this is a camperbot test translation' + ); + if (newTranslation && newTranslation.id); + console.log( + `added new translation (translationId: ${newTranslation.id}) for stringId: ${stringId}` + ); + await deleteTranslation(projectId, newTranslation.id); + } + } + } + console.log('complete'); +}; + +const projectId = process.env.CROWDIN_PROJECT_ID; +markTranslatedStringsAsDone(projectId); diff --git a/tools/crowdin/utils/get-languages.js b/tools/crowdin/utils/get-languages.js new file mode 100644 index 0000000000..cdf4901a0a --- /dev/null +++ b/tools/crowdin/utils/get-languages.js @@ -0,0 +1,17 @@ +const authHeader = require('./auth-header'); +const makeRequest = require('./make-request'); + +const getLanguages = async projectId => { + let headers = { ...authHeader }; + const endPoint = `projects/${projectId}?limit=500`; + const response = await makeRequest({ method: 'get', endPoint, headers }); + if (response.data && response.data.targetLanguageIds.length) { + return response.data.targetLanguageIds; + } else { + const { error, errors } = response; + console.error(error ? error : errors); + return null; + } +}; + +module.exports = getLanguages; diff --git a/tools/crowdin/utils/strings.js b/tools/crowdin/utils/strings.js index b29e0321a5..2268e68aa7 100644 --- a/tools/crowdin/utils/strings.js +++ b/tools/crowdin/utils/strings.js @@ -128,9 +128,97 @@ const updateFileString = async ({ } }; +const getStringTranslations = async ({ projectId, stringId, languageId }) => { + let headers = { ...authHeader }; + let done = false; + let offset = 0; + let translations = []; + while (!done) { + let endPoint = `projects/${projectId}/translations?stringId=${stringId}&languageId=${languageId}&limit=500&offset=${offset}`; + const response = await makeRequest({ method: 'get', endPoint, headers }); + if (response.data) { + if (response.data.length) { + translations = [...translations, ...response.data]; + offset += 500; + } else { + done = true; + return translations; + } + } else { + const { error, errors } = response; + console.error( + error ? JSON.stringify(error, null, 2) : JSON.stringify(errors, null, 2) + ); + } + } + return null; +}; + +const deleteTranslation = async (projectId, translationId) => { + let headers = { ...authHeader }; + const endPoint = `projects/${projectId}/translations/${translationId}`; + await makeRequest({ + method: 'delete', + endPoint, + headers + }); + console.log(`Deleted translationId ${translationId} from Crowdin project`); + return null; +}; + +const addTranslation = async (projectId, stringId, languageId, text) => { + let headers = { ...authHeader }; + const endPoint = `projects/${projectId}/translations`; + const body = { + stringId, + languageId, + text + }; + const response = await makeRequest({ + method: 'post', + endPoint, + headers, + body + }); + if (response.data) { + return response.data; + } + return null; +}; + +const getLanguageTranslations = async ({ projectId, languageId }) => { + let headers = { ...authHeader }; + let done = false; + let offset = 0; + let translations = []; + while (!done) { + let endPoint = `projects/${projectId}/languages/${languageId}/translations?limit=500&offset=${offset}`; + const response = await makeRequest({ method: 'get', endPoint, headers }); + if (response.data) { + if (response.data.length) { + translations = [...translations, ...response.data]; + offset += 500; + } else { + done = true; + return translations; + } + } else { + const { error, errors } = response; + console.error( + error ? JSON.stringify(error, null, 2) : JSON.stringify(errors, null, 2) + ); + } + } + return null; +}; + module.exports = { getStrings, updateString, updateFileStrings, - updateFileString + updateFileString, + getStringTranslations, + addTranslation, + deleteTranslation, + getLanguageTranslations };