diff --git a/development/download-translations.js b/development/download-translations.js index 84666f2d15..7877153248 100644 --- a/development/download-translations.js +++ b/development/download-translations.js @@ -1,13 +1,13 @@ -const pathUtil = require('path'); -const fs = require('fs'); +const pathUtil = require("path"); +const fs = require("fs"); const { - transifexApi, - ORGANIZATION_NAME, - PROJECT_NAME, - RUNTIME_RESOURCE, - METADATA_RESOURCE, - SOURCE_LOCALE -} = require('./transifex-common'); + transifexApi, + ORGANIZATION_NAME, + PROJECT_NAME, + RUNTIME_RESOURCE, + METADATA_RESOURCE, + SOURCE_LOCALE, +} = require("./transifex-common"); /** * @template T @@ -15,16 +15,16 @@ const { * @returns {T} obj but keys are sorted */ const withSortedKeyOrder = (obj) => { - const result = {}; - for (const key of Object.keys(obj).sort()) { - const value = obj[key]; - if (typeof value === 'object') { - result[key] = withSortedKeyOrder(obj); - } else { - result[key] = value; - } + const result = {}; + for (const key of Object.keys(obj).sort()) { + const value = obj[key]; + if (typeof value === "object") { + result[key] = withSortedKeyOrder(obj); + } else { + result[key] = value; } - return result; + } + return result; }; /** @@ -32,17 +32,19 @@ const withSortedKeyOrder = (obj) => { * @returns {Promise>} */ const getResourceStatistics = async (resource) => { - const iterator = transifexApi.resource_language_stats.filter({ - project: `o:${ORGANIZATION_NAME}:p:${PROJECT_NAME}`, - resource: `o:${ORGANIZATION_NAME}:p:${PROJECT_NAME}:r:${resource}` - }).all(); - const locales = {}; - for await (const languageData of iterator) { - const localeCode = languageData.id.match(/\bl:([\w\d-]+)/)[1]; - const translatedStrings = languageData.attributes.translated_strings; - locales[localeCode] = translatedStrings; - } - return withSortedKeyOrder(locales); + const iterator = transifexApi.resource_language_stats + .filter({ + project: `o:${ORGANIZATION_NAME}:p:${PROJECT_NAME}`, + resource: `o:${ORGANIZATION_NAME}:p:${PROJECT_NAME}:r:${resource}`, + }) + .all(); + const locales = {}; + for await (const languageData of iterator) { + const localeCode = languageData.id.match(/\bl:([\w\d-]+)/)[1]; + const translatedStrings = languageData.attributes.translated_strings; + locales[localeCode] = translatedStrings; + } + return withSortedKeyOrder(locales); }; /** @@ -50,15 +52,15 @@ const getResourceStatistics = async (resource) => { * @returns {object} JSON with string values. */ const removeDeveloperComments = (strings) => { - const result = {}; - for (const [key, value] of Object.entries(strings)) { - if (typeof value.string === 'string') { - result[key] = value.string; - } else { - result[key] = removeDeveloperComments(value); - } + const result = {}; + for (const [key, value] of Object.entries(strings)) { + if (typeof value.string === "string") { + result[key] = value.string; + } else { + result[key] = removeDeveloperComments(value); } - return result; + } + return result; }; /** @@ -66,15 +68,15 @@ const removeDeveloperComments = (strings) => { * @returns {object} */ const removeEmptyTranslations = (strings) => { - const result = {}; - for (const [key, value] of Object.entries(strings)) { - if (typeof value === 'object') { - result[key] = removeEmptyTranslations(value); - } else if (value !== '') { - result[key] = value; - } + const result = {}; + for (const [key, value] of Object.entries(strings)) { + if (typeof value === "object") { + result[key] = removeEmptyTranslations(value); + } else if (value !== "") { + result[key] = value; } - return result; + } + return result; }; /** @@ -83,19 +85,22 @@ const removeEmptyTranslations = (strings) => { * @returns {object} */ const removeUnchangedTranslations = (source, translated) => { - const result = {}; - for (const [key, translatedValue] of Object.entries(translated)) { - const sourceValue = source[key]; - if (typeof translatedValue === 'object') { - const recursiveResult = removeUnchangedTranslations(sourceValue, translatedValue); - if (Object.keys(recursiveResult).length > 0) { - result[key] = recursiveResult; - } - } else if (translatedValue !== sourceValue) { - result[key] = translatedValue; - } + const result = {}; + for (const [key, translatedValue] of Object.entries(translated)) { + const sourceValue = source[key]; + if (typeof translatedValue === "object") { + const recursiveResult = removeUnchangedTranslations( + sourceValue, + translatedValue + ); + if (Object.keys(recursiveResult).length > 0) { + result[key] = recursiveResult; + } + } else if (translatedValue !== sourceValue) { + result[key] = translatedValue; } - return result; + } + return result; }; /** @@ -104,42 +109,45 @@ const removeUnchangedTranslations = (source, translated) => { * @returns {Promise} */ const downloadTranslatedResource = async (resource, locale) => { - let urlToDownload; - if (locale === SOURCE_LOCALE) { - urlToDownload = await transifexApi.ResourceStringsAsyncDownload.download({ - resource: { - data: { - id: `o:${ORGANIZATION_NAME}:p:${PROJECT_NAME}:r:${resource}`, - type: 'resources' - } - }, - }); - } else { - urlToDownload = await transifexApi.ResourceTranslationsAsyncDownload.download({ - mode: 'onlytranslated', - resource: { - data: { - id: `o:${ORGANIZATION_NAME}:p:${PROJECT_NAME}:r:${resource}`, - type: 'resources' - } - }, - language: { - data: { - id: `l:${locale}`, - type: 'languages' - } - } - }); - } - - const translationsResponse = await fetch(urlToDownload, { - signal: AbortSignal.timeout(10000 * 60) + let urlToDownload; + if (locale === SOURCE_LOCALE) { + urlToDownload = await transifexApi.ResourceStringsAsyncDownload.download({ + resource: { + data: { + id: `o:${ORGANIZATION_NAME}:p:${PROJECT_NAME}:r:${resource}`, + type: "resources", + }, + }, }); - const rawTranslations = await translationsResponse.json(); - const withoutDeveloperComments = removeDeveloperComments(rawTranslations); - const withoutEmptyTranslations = removeEmptyTranslations(withoutDeveloperComments); - const sortedTranslations = withSortedKeyOrder(withoutEmptyTranslations); - return sortedTranslations; + } else { + urlToDownload = + await transifexApi.ResourceTranslationsAsyncDownload.download({ + mode: "onlytranslated", + resource: { + data: { + id: `o:${ORGANIZATION_NAME}:p:${PROJECT_NAME}:r:${resource}`, + type: "resources", + }, + }, + language: { + data: { + id: `l:${locale}`, + type: "languages", + }, + }, + }); + } + + const translationsResponse = await fetch(urlToDownload, { + signal: AbortSignal.timeout(10000 * 60), + }); + const rawTranslations = await translationsResponse.json(); + const withoutDeveloperComments = removeDeveloperComments(rawTranslations); + const withoutEmptyTranslations = removeEmptyTranslations( + withoutDeveloperComments + ); + const sortedTranslations = withSortedKeyOrder(withoutEmptyTranslations); + return sortedTranslations; }; /** @@ -147,47 +155,60 @@ const downloadTranslatedResource = async (resource, locale) => { * @returns {Promise} */ const downloadAllResourceTranslations = async (resource) => { - const transifexStatistics = await getResourceStatistics(resource); - const localesToFetch = []; - for (const [locale, translatedStringCount] of Object.entries(transifexStatistics)) { - if (translatedStringCount > 0) { - localesToFetch.push(locale); - } + const transifexStatistics = await getResourceStatistics(resource); + const localesToFetch = []; + for (const [locale, translatedStringCount] of Object.entries( + transifexStatistics + )) { + if (translatedStringCount > 0) { + localesToFetch.push(locale); } + } - const entries = await Promise.all(localesToFetch.map(async (locale) => { - const translatedStrings = await downloadTranslatedResource(resource, locale); - return [locale, translatedStrings]; - })); + const entries = await Promise.all( + localesToFetch.map(async (locale) => { + const translatedStrings = await downloadTranslatedResource( + resource, + locale + ); + return [locale, translatedStrings]; + }) + ); - const sourceStrings = entries.find(i => i[0] === SOURCE_LOCALE)[1]; - const result = {}; - for (const [locale, strings] of entries) { - if (locale !== SOURCE_LOCALE) { - const withoutUnchangedStrings = removeUnchangedTranslations(sourceStrings, strings); - const normalizedLocale = locale.toLowerCase().replace(/_/g, '-'); - result[normalizedLocale] = withoutUnchangedStrings; - } + const sourceStrings = entries.find((i) => i[0] === SOURCE_LOCALE)[1]; + const result = {}; + for (const [locale, strings] of entries) { + if (locale !== SOURCE_LOCALE) { + const withoutUnchangedStrings = removeUnchangedTranslations( + sourceStrings, + strings + ); + const normalizedLocale = locale.toLowerCase().replace(/_/g, "-"); + result[normalizedLocale] = withoutUnchangedStrings; } - return result; + } + return result; }; const run = async () => { - console.log('This is going to take a while.'); + console.log("This is going to take a while."); - const [ - runtimeStrings, - metadataStrings - ] = await Promise.all([ - downloadAllResourceTranslations(RUNTIME_RESOURCE), - downloadAllResourceTranslations(METADATA_RESOURCE), - ]); + const [runtimeStrings, metadataStrings] = await Promise.all([ + downloadAllResourceTranslations(RUNTIME_RESOURCE), + downloadAllResourceTranslations(METADATA_RESOURCE), + ]); - fs.writeFileSync(pathUtil.join(__dirname, '../translations/extension-runtime.json'), JSON.stringify(runtimeStrings, null, 4)); - fs.writeFileSync(pathUtil.join(__dirname, '../translations/extension-metadata.json'), JSON.stringify(metadataStrings, null, 4)); + fs.writeFileSync( + pathUtil.join(__dirname, "../translations/extension-runtime.json"), + JSON.stringify(runtimeStrings, null, 4) + ); + fs.writeFileSync( + pathUtil.join(__dirname, "../translations/extension-metadata.json"), + JSON.stringify(metadataStrings, null, 4) + ); }; run().catch((err) => { - console.error(err); - process.exit(1); + console.error(err); + process.exit(1); }); diff --git a/development/transifex-common.js b/development/transifex-common.js index 971f91377c..6dc9ab9468 100644 --- a/development/transifex-common.js +++ b/development/transifex-common.js @@ -1,46 +1,46 @@ -const {transifexApi} = require('@transifex/api'); +const { transifexApi } = require("@transifex/api"); const TOKEN = process.env.TRANSIFEX_TOKEN; if (!TOKEN) { - console.error('Missing TRANSIFEX_TOKEN.'); - process.exit(1); + console.error("Missing TRANSIFEX_TOKEN."); + process.exit(1); } const ORGANIZATION_NAME = process.env.TRANSIFEX_ORGANIZATION; if (!ORGANIZATION_NAME) { - console.error('Missing TRANSIFEX_ORGANIZATION.'); - process.exit(1); + console.error("Missing TRANSIFEX_ORGANIZATION."); + process.exit(1); } const PROJECT_NAME = process.env.TRANSIFEX_PROJECT; if (!PROJECT_NAME) { - console.error('Missing TRANSIFEX_PROJECT.'); - process.exit(1); + console.error("Missing TRANSIFEX_PROJECT."); + process.exit(1); } const RUNTIME_RESOURCE = process.env.TRANSIFEX_RUNTIME_RESOURCE; if (!RUNTIME_RESOURCE) { - console.error('Missing TRANSIFEX_RUNTIME_RESOURCE.'); - process.exit(1); + console.error("Missing TRANSIFEX_RUNTIME_RESOURCE."); + process.exit(1); } const METADATA_RESOURCE = process.env.TRANSIFEX_METADATA_RESOURCE; if (!METADATA_RESOURCE) { - console.error('Missing TRANSIFEX_METADATA_RESOURCE.'); - process.exit(1); + console.error("Missing TRANSIFEX_METADATA_RESOURCE."); + process.exit(1); } -const SOURCE_LOCALE = 'en'; +const SOURCE_LOCALE = "en"; transifexApi.setup({ - auth: TOKEN + auth: TOKEN, }); module.exports = { - transifexApi, - ORGANIZATION_NAME, - PROJECT_NAME, - RUNTIME_RESOURCE, - METADATA_RESOURCE, - SOURCE_LOCALE + transifexApi, + ORGANIZATION_NAME, + PROJECT_NAME, + RUNTIME_RESOURCE, + METADATA_RESOURCE, + SOURCE_LOCALE, }; diff --git a/development/upload-translations.js b/development/upload-translations.js index 90ce1d2adb..d35c02301e 100644 --- a/development/upload-translations.js +++ b/development/upload-translations.js @@ -1,68 +1,68 @@ const { - transifexApi, - ORGANIZATION_NAME, - PROJECT_NAME, - RUNTIME_RESOURCE, - METADATA_RESOURCE -} = require('./transifex-common'); + transifexApi, + ORGANIZATION_NAME, + PROJECT_NAME, + RUNTIME_RESOURCE, + METADATA_RESOURCE, +} = require("./transifex-common"); const Builder = require("./builder"); const uploadRuntimeStrings = async (strings) => { - if ( - typeof strings['lab/text@_Animated Text'].string !== 'string' || - typeof strings['lab/text@_Animated Text'].developer_comment !== 'string' || - Object.keys(strings).length < 500 - ) { - throw new Error('Sanity check failed.'); - } + if ( + typeof strings["lab/text@_Animated Text"].string !== "string" || + typeof strings["lab/text@_Animated Text"].developer_comment !== "string" || + Object.keys(strings).length < 500 + ) { + throw new Error("Sanity check failed."); + } - await transifexApi.ResourceStringsAsyncUpload.upload({ - resource: { - data: { - id: `o:${ORGANIZATION_NAME}:p:${PROJECT_NAME}:r:${RUNTIME_RESOURCE}`, - type: 'resources' - } - }, - content: JSON.stringify(strings) - }); + await transifexApi.ResourceStringsAsyncUpload.upload({ + resource: { + data: { + id: `o:${ORGANIZATION_NAME}:p:${PROJECT_NAME}:r:${RUNTIME_RESOURCE}`, + type: "resources", + }, + }, + content: JSON.stringify(strings), + }); }; const uploadMetadataStrings = async (strings) => { - if ( - typeof strings['lab/text@name'].string !== 'string' || - typeof strings['lab/text@name'].developer_comment !== 'string' || - Object.keys(strings).length < 100 - ) { - throw new Error('Sanity check failed.'); - } + if ( + typeof strings["lab/text@name"].string !== "string" || + typeof strings["lab/text@name"].developer_comment !== "string" || + Object.keys(strings).length < 100 + ) { + throw new Error("Sanity check failed."); + } - await transifexApi.ResourceStringsAsyncUpload.upload({ - resource: { - data: { - id: `o:${ORGANIZATION_NAME}:p:${PROJECT_NAME}:r:${METADATA_RESOURCE}`, - type: 'resources' - } - }, - content: JSON.stringify(strings) - }); + await transifexApi.ResourceStringsAsyncUpload.upload({ + resource: { + data: { + id: `o:${ORGANIZATION_NAME}:p:${PROJECT_NAME}:r:${METADATA_RESOURCE}`, + type: "resources", + }, + }, + content: JSON.stringify(strings), + }); }; const run = async () => { - console.log('Building...'); - const builder = new Builder(); - const build = builder.build(); + console.log("Building..."); + const builder = new Builder(); + const build = builder.build(); - console.log('Generating strings...'); - const l10n = build.generateL10N(); + console.log("Generating strings..."); + const l10n = build.generateL10N(); - console.log('Uploading runtime strings...'); - await uploadRuntimeStrings(l10n['extension-runtime']); + console.log("Uploading runtime strings..."); + await uploadRuntimeStrings(l10n["extension-runtime"]); - console.log('Uploading metadata strings...'); - await uploadMetadataStrings(l10n['extension-metadata']); + console.log("Uploading metadata strings..."); + await uploadMetadataStrings(l10n["extension-metadata"]); }; run().catch((err) => { - console.error(err); - process.exit(1); + console.error(err); + process.exit(1); });