scratch-l10n 5.0.308 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +6 -0
  3. package/dist/l10n.js +3 -0
  4. package/dist/l10n.js.map +1 -1
  5. package/dist/localeData.js +3 -0
  6. package/dist/localeData.js.map +1 -1
  7. package/dist/supportedLocales.js +3 -0
  8. package/dist/supportedLocales.js.map +1 -1
  9. package/package.json +24 -22
  10. package/scripts/{build-data.mjs → build-data.mts} +15 -6
  11. package/scripts/{build-i18n-src.js → build-i18n-src.mts} +16 -11
  12. package/scripts/lib/concurrent.mts +37 -0
  13. package/scripts/lib/freshdesk-api.mts +322 -0
  14. package/scripts/lib/help-utils.mts +221 -0
  15. package/{lib/progress-logger.mjs → scripts/lib/progress-logger.mts} +10 -5
  16. package/scripts/lib/transifex-formats.mts +53 -0
  17. package/scripts/lib/transifex-objects.mts +143 -0
  18. package/scripts/lib/transifex.mts +284 -0
  19. package/scripts/lib/validate.mts +107 -0
  20. package/scripts/tsconfig.json +20 -0
  21. package/scripts/tx-pull-editor.mts +74 -0
  22. package/scripts/{tx-pull-help-articles.js → tx-pull-help-articles.mts} +5 -13
  23. package/scripts/{tx-pull-help-names.js → tx-pull-help-names.mts} +5 -13
  24. package/scripts/{tx-pull-locale-articles.js → tx-pull-locale-articles.mts} +5 -13
  25. package/scripts/{tx-pull-www.mjs → tx-pull-www.mts} +16 -29
  26. package/scripts/{tx-push-help.mjs → tx-push-help.mts} +39 -37
  27. package/scripts/{tx-push-src.js → tx-push-src.mts} +13 -20
  28. package/scripts/update-translations.sh +2 -2
  29. package/scripts/{validate-extension-inputs.mjs → validate-extension-inputs.mts} +20 -10
  30. package/scripts/{validate-translations.mjs → validate-translations.mts} +7 -12
  31. package/scripts/{validate-www.mjs → validate-www.mts} +15 -13
  32. package/src/supported-locales.mjs +3 -0
  33. package/www/scratch-website.about-l10njson/nn.json +1 -1
  34. package/www/scratch-website.splash-l10njson/mi.json +2 -2
  35. package/www/scratch-website.teacher-faq-l10njson/cy.json +1 -1
  36. package/.github/PULL_REQUEST_TEMPLATE.md +0 -75
  37. package/.github/workflows/ci-cd.yml +0 -55
  38. package/.github/workflows/commitlint.yml +0 -12
  39. package/.github/workflows/daily-help-update.yml +0 -40
  40. package/.github/workflows/daily-tx-pull.yml +0 -54
  41. package/.github/workflows/signature-assistant.yml +0 -31
  42. package/lib/batch.js +0 -15
  43. package/lib/transifex.js +0 -242
  44. package/lib/validate.mjs +0 -48
  45. package/scripts/freshdesk-api.js +0 -149
  46. package/scripts/help-utils.js +0 -190
  47. package/scripts/tx-pull-editor.mjs +0 -83
@@ -1,190 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * @file
5
- * Helper functions for syncing Freshdesk knowledge base articles with Transifex
6
- */
7
-
8
- const FreshdeskApi = require('./freshdesk-api.js')
9
- const fs = require('fs')
10
- const fsPromises = fs.promises
11
- const mkdirp = require('mkdirp')
12
- const { txPull, txResourcesObjects, txAvailableLanguages } = require('../lib/transifex.js')
13
-
14
- const FD = new FreshdeskApi('https://mitscratch.freshdesk.com', process.env.FRESHDESK_TOKEN)
15
- const TX_PROJECT = 'scratch-help'
16
-
17
- const freshdeskLocale = locale => {
18
- // map between Transifex locale and Freshdesk. Two letter codes are usually fine
19
- const localeMap = {
20
- es_419: 'es-LA',
21
- ja: 'ja-JP',
22
- 'ja-Hira': 'ja-JP',
23
- lv: 'lv-LV',
24
- nb: 'nb-NO',
25
- nn: 'nb-NO',
26
- pt: 'pt-PT',
27
- pt_BR: 'pt-BR',
28
- ru: 'ru-RU',
29
- sv: 'sv-SE',
30
- zh_CN: 'zh-CN',
31
- zh_TW: 'zh-TW',
32
- }
33
- return localeMap[locale] || locale
34
- }
35
-
36
- /**
37
- * Pull metadata from Transifex for the scratch-help project
38
- * @returns {Promise} results array containing:
39
- * languages: array of supported languages
40
- * folders: array of tx resources corresponding to Freshdesk folders
41
- * names: array of tx resources corresponding to the Freshdesk metadata
42
- */
43
- exports.getInputs = async () => {
44
- const resources = await txResourcesObjects(TX_PROJECT)
45
- const languages = await txAvailableLanguages(TX_PROJECT)
46
- // there are three types of resources differentiated by the file type
47
- const folders = resources.filter(resource => resource.i18n_type === 'STRUCTURED_JSON')
48
- const names = resources.filter(resource => resource.i18n_type === 'KEYVALUEJSON')
49
- // ignore the yaml type because it's not possible to update via API
50
-
51
- return Promise.all([languages, folders, names])
52
- }
53
-
54
- /*
55
- * internal function to serialize saving category and folder name translations to avoid Freshdesk rate limit
56
- */
57
- const serializeNameSave = async (json, resource, locale) => {
58
- for (const [key, value] of Object.entries(json)) {
59
- // key is of the form <name>_<id>
60
- const words = key.split('_')
61
- const id = words[words.length - 1]
62
- let status = 0
63
- if (resource.name === 'categoryNames_json') {
64
- status = await FD.updateCategoryTranslation(id, freshdeskLocale(locale), { name: value })
65
- }
66
- if (resource.name === 'folderNames_json') {
67
- status = await FD.updateFolderTranslation(id, freshdeskLocale(locale), { name: value })
68
- }
69
- if (status === -1) {
70
- process.exitCode = 1
71
- }
72
- }
73
- }
74
-
75
- /**
76
- * Internal function serialize Freshdesk requests to avoid getting rate limited
77
- * @param {object} json object with keys corresponding to article ids
78
- * @param {string} locale language code
79
- * @returns {Promise} [description]
80
- */
81
- const serializeFolderSave = async (json, locale) => {
82
- // json is a map of articles:
83
- // {
84
- // <id>: {
85
- // title: {string: <title-value>},
86
- // description: {string: <description-value>},
87
- // tags: {string: <comma separated strings} // optional
88
- // },
89
- // <id>: {
90
- // title: {string: <title-value>},
91
- // description: {string: <description-value>},
92
- // tags: {string: <comma separated strings} // optional
93
- // }
94
- // }
95
- for (const [id, value] of Object.entries(json)) {
96
- const body = {
97
- title: value.title.string,
98
- description: value.description.string,
99
- status: 2, // set status to published
100
- }
101
- if (Object.prototype.hasOwnProperty.call(value, 'tags')) {
102
- const tags = value.tags.string.split(',')
103
- const validTags = tags.filter(tag => tag.length < 33)
104
- if (validTags.length !== tags.length) {
105
- process.stdout.write(`Warning: tags too long in ${id} for ${locale}\n`)
106
- }
107
- body.tags = validTags
108
- }
109
- const status = await FD.updateArticleTranslation(id, freshdeskLocale(locale), body)
110
- if (status === -1) {
111
- // eslint-disable-next-line require-atomic-updates -- I promise that `process` won't change across `await`
112
- process.exitCode = 1
113
- }
114
- }
115
- return 0
116
- }
117
-
118
- /**
119
- * Process Transifex resource corresponding to a Knowledge base folder on Freshdesk
120
- * @param {object} folder Transifex resource json corresponding to a KB folder
121
- * @param {string} locale locale to pull and submit to Freshdesk
122
- * @returns {Promise} [description]
123
- */
124
- exports.localizeFolder = async (folder, locale) => {
125
- txPull(TX_PROJECT, folder.slug, locale, { mode: 'default' })
126
- .then(data => {
127
- serializeFolderSave(data, locale)
128
- })
129
- .catch(e => {
130
- process.stdout.write(`Error processing ${folder.slug}, ${locale}: ${e.message}\n`)
131
- process.exitCode = 1 // not ok
132
- })
133
- }
134
-
135
- /**
136
- * Save Transifex resource corresponding to a Knowledge base folder locally for debugging
137
- * @param {object} folder Transifex resource json corresponding to a KB folder
138
- * @param {string} locale locale to pull and save
139
- * @returns {Promise} [description]
140
- */
141
- exports.debugFolder = async (folder, locale) => {
142
- mkdirp.sync('tmpDebug')
143
- txPull(TX_PROJECT, folder.slug, locale, { mode: 'default' })
144
- .then(data => {
145
- fsPromises.writeFile(`tmpDebug/${folder.slug}_${locale}.json`, JSON.stringify(data, null, 2))
146
- })
147
- .catch(e => {
148
- process.stdout.write(`Error processing ${folder.slug}, ${locale}: ${e.message}\n`)
149
- process.exitCode = 1 // not ok
150
- })
151
- }
152
-
153
- /**
154
- * Process KEYVALUEJSON resources from scratch-help on transifex
155
- * Category and Folder names are stored as plain json
156
- * @param {object} resource Transifex resource json for either CategoryNames or FolderNames
157
- * @param {string} locale locale to pull and submit to Freshdesk
158
- * @returns {Promise} [description]
159
- */
160
- exports.localizeNames = async (resource, locale) => {
161
- txPull(TX_PROJECT, resource.slug, locale, { mode: 'default' })
162
- .then(data => {
163
- serializeNameSave(data, resource, locale)
164
- })
165
- .catch(e => {
166
- process.stdout.write(`Error saving ${resource.slug}, ${locale}: ${e.message}\n`)
167
- process.exitCode = 1 // not ok
168
- })
169
- }
170
-
171
- const BATCH_SIZE = 2
172
- /*
173
- * save resource items in batches to reduce rate limiting errors
174
- * @param {object} item Transifex resource json, used for 'slug'
175
- * @param {array} languages Array of languages to save
176
- * @param {function} saveFn Async function to use to save the item
177
- * @return {Promise}
178
- */
179
- exports.saveItem = async (item, languages, saveFn) => {
180
- const saveLanguages = languages.filter(l => l !== 'en') // exclude English from update
181
- let batchedPromises = Promise.resolve()
182
- for (let i = 0; i < saveLanguages.length; i += BATCH_SIZE) {
183
- batchedPromises = batchedPromises
184
- .then(() => Promise.all(saveLanguages.slice(i, i + BATCH_SIZE).map(l => saveFn(item, l))))
185
- .catch(err => {
186
- process.stdout.write(`Error saving item:${err.message}\n${JSON.stringify(item, null, 2)}\n`)
187
- process.exitCode = 1 // not ok
188
- })
189
- }
190
- }
@@ -1,83 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * @file
4
- * Script to pull translations from transifex and generate the editor-msgs file.
5
- * Expects that the project and resource have already been defined in Transifex, and that
6
- * the person running the script has the the TX_TOKEN environment variable set to an api
7
- * token that has developer access.
8
- */
9
- import fs from 'fs'
10
- import path from 'path'
11
- import { batchMap } from '../lib/batch.js'
12
- import { txPull } from '../lib/transifex.js'
13
- import { validateTranslations } from '../lib/validate.mjs'
14
- import locales, { localeMap } from '../src/supported-locales.mjs'
15
-
16
- /**
17
- * @file
18
- * Script to pull translations from transifex and generate the editor-msgs file.
19
- * Expects that the project and resource have already been defined in Transifex, and that
20
- * the person running the script has the the TX_TOKEN environment variable set to an api
21
- * token that has developer access.
22
- */
23
-
24
- const args = process.argv.slice(2)
25
-
26
- const usage = `
27
- Pull supported language translations from Transifex. Usage:
28
- node tx-pull-editor.js tx-project tx-resource path
29
- tx-project: project on Transifex (e.g., scratch-editor)
30
- tx-resource: resource within the project (e.g., interface)
31
- path: where to put the downloaded json files
32
- NOTE: TX_TOKEN environment variable needs to be set with a Transifex API token. See
33
- the Localization page on the GUI wiki for information about setting up Transifex.
34
- `
35
-
36
- // Fail immediately if the TX_TOKEN is not defined
37
- if (!process.env.TX_TOKEN || args.length < 3) {
38
- process.stdout.write(usage)
39
- process.exit(1)
40
- }
41
-
42
- // Globals
43
- const PROJECT = args[0]
44
- const RESOURCE = args[1]
45
- const OUTPUT_DIR = path.resolve(args[2])
46
- const MODE = 'reviewed'
47
- const CONCURRENCY_LIMIT = 36
48
-
49
- const getLocaleData = async function (locale) {
50
- const txLocale = localeMap[locale] || locale
51
- const data = await txPull(PROJECT, RESOURCE, txLocale, MODE)
52
- return {
53
- locale: locale,
54
- translations: data,
55
- }
56
- }
57
-
58
- const pullTranslations = async function () {
59
- try {
60
- const values = await batchMap(Object.keys(locales), CONCURRENCY_LIMIT, getLocaleData)
61
- const source = values.find(elt => elt.locale === 'en').translations
62
- values.forEach(translation => {
63
- validateTranslations({ locale: translation.locale, translations: translation.translations }, source)
64
- // if translation has message & description, we only want the message
65
- const txs = {}
66
- for (const key of Object.keys(translation.translations)) {
67
- const tx = translation.translations[key]
68
- if (tx.message) {
69
- txs[key] = tx.message
70
- } else {
71
- txs[key] = tx
72
- }
73
- }
74
- const file = JSON.stringify(txs, null, 4)
75
- fs.writeFileSync(`${OUTPUT_DIR}/${translation.locale}.json`, file)
76
- })
77
- } catch (err) {
78
- process.stdout.write(err.message)
79
- process.exit(1)
80
- }
81
- }
82
-
83
- pullTranslations()