diff --git a/package.json b/package.json index ff012aae..802e0ff1 100644 --- a/package.json +++ b/package.json @@ -46,13 +46,15 @@ "license": "MPL-2.0", "repository": "mozilla/send", "availableLanguages": [ - "en-US", - "zh-TW", - "zh-CN", "cs", + "cy", + "de", + "dsb", + "en-US", + "es-MX", "fr", "fy-NL", - "de", + "hsb", "hu", "it", "ja", @@ -60,19 +62,14 @@ "ms", "nb-NO", "nn-NO", - "pt-PT", "pt-BR", + "pt-PT", "ru", "sk", "sl", - "dsb", - "hsb", - "es-CL", - "es-ES", - "es-MX", "sv-SE", - "tr", - "cy" + "zh-CN", + "zh-TW" ], "scripts": { "build": "npm-run-all build:*", @@ -82,9 +79,14 @@ "build:l10n": "cp node_modules/l20n/dist/web/l20n.min.js public", "dev": "npm run build && npm start", "format": "prettier '{frontend/src/,scripts/,server/,test/**/!(bundle)}*.js' 'public/*.css' --single-quote --write", + "get-prod-locales": "node scripts/get-prod-locales", + "get-prod-locales:write": "npm run get-prod-locales -- --write", "lint": "npm-run-all lint:*", "lint:css": "stylelint 'public/*.css'", "lint:js": "eslint .", + "lint-locales": "node scripts/lint-locales", + "lint-locales:dev": "npm run lint-locales", + "lint-locales:prod": "npm run lint-locales -- --production", "start": "node server/server", "test": "npm-run-all test:*", "test:unit": "mocha test/unit", diff --git a/scripts/.eslintrc.yml b/scripts/.eslintrc.yml new file mode 100644 index 00000000..dfaf2328 --- /dev/null +++ b/scripts/.eslintrc.yml @@ -0,0 +1,6 @@ +rules: + node/shebang: off + security/detect-child-process: off + + no-console: off + no-process-exit: off diff --git a/scripts/get-prod-locales.js b/scripts/get-prod-locales.js new file mode 100755 index 00000000..27c86e26 --- /dev/null +++ b/scripts/get-prod-locales.js @@ -0,0 +1,49 @@ +#!/usr/bin/env node + +const cp = require('child_process'); +const { promisify } = require('util'); +const fs = require('fs'); +const pkg = require('../package.json'); + +const availableLanguages = pkg.availableLanguages.sort(); +const exec = promisify(cp.exec); + +const arrayDiff = (current, package) => + current.filter(locale => !package.includes(locale)); + +const cmd = 'compare-locales l10n.toml . `ls public/locales` --data=json'; + +exec(cmd) + .then(({ stdout }) => JSON.parse(stdout)) + .then(({ summary }) => { + const locales = Object.keys(summary) + .filter(locale => { + const loc = summary[locale]; + const hasMissing = loc.hasOwnProperty('missing'); + const hasErrors = loc.hasOwnProperty('errors'); + return !hasMissing && !hasErrors; + }) + .sort(); + + if (locales.join(',') !== availableLanguages.join(',')) { + const missingLanguages = arrayDiff(locales, availableLanguages); + + console.log('current 100%:', JSON.stringify(locales)); + console.log('package.json:', JSON.stringify(availableLanguages)); + console.log('missing prod:', JSON.stringify(missingLanguages)); + + if (process.argv.includes('--write')) { + const pkgPath = require.resolve('../package.json'); + pkg.availableLanguages = locales; + const str = JSON.stringify(pkg, null, 2) + '\n'; + console.log('Updating /package.json availableLanguages'); + fs.writeFileSync(pkgPath, str, 'utf-8'); + } + } else { + console.log('Production locales are up to date!'); + } + }) + .catch(err => { + console.error(err); + process.exit(1); + }); diff --git a/scripts/lint-locales.js b/scripts/lint-locales.js new file mode 100644 index 00000000..fbcf84ef --- /dev/null +++ b/scripts/lint-locales.js @@ -0,0 +1,51 @@ +#!/usr/bin/env node + +const cp = require('child_process'); +const { promisify } = require('util'); +const pkg = require('../package.json'); +const conf = require('../server/config'); + +const exec = promisify(cp.exec); +const cmd = `compare-locales l10n.toml . ${getLocales()} --data=json`; + +console.log(cmd); + +exec(cmd) + .then(({ stdout }) => JSON.parse(stdout)) + .then(({ details }) => filterErrors(details)) + .then(results => { + if (results.length) { + results.forEach(({ locale, data }) => { + console.log(locale); + data.forEach(msg => console.log(`- ${msg}`)); + console.log(''); + }); + process.exit(2); + } + }) + .catch(err => { + console.error(err); + process.exit(1); + }); + +function filterErrors(details) { + return Object.keys(details) + .sort() + .map(locale => { + const data = details[locale] + .filter(item => item.hasOwnProperty('error')) + .map(({ error }) => error); + return { locale, data }; + }) + .filter(({ data }) => data.length); +} + +function getLocales() { + // If we're in a "production" env (or passed the `--production` flag), only + // check the locales from the package.json file's `availableLanguages` array. + if (conf.env === 'production' || process.argv.includes('--production')) { + return pkg.availableLanguages.sort().join(' '); + } + // Lint all the locales. + return '`ls public/locales`'; +}