mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
* Switch to using string literals for locale keys Various message keys were being specified with a string template instead of a string literal. They have been switched to use string literals so that the script for detecting unused messages can find them. * Remove unused locale messages A number of unused locale messages have been removed - probably leftover from old UI elements that have since been removed. The `verify_locale_strings` script has been augmented to search the UI for string literals, and match those against the locale message keys in the `en` locale. Any messages without a corresponding string literal are assumed to be unused. The script has also been updated with an optional `--fix` parameter, which will automatically delete any unused messages from locales. 148 unused messages were found in this case, out of a total of about 650 messages. Another 70 messages are _potentially_ unused and require further investigation, but weren't as easy to rule out because they were found in string literals. * Remove additional unused locale messages The following messages were more difficult to rule out because they were present as string literals in the UI. They do appear to be unused as locale keys though.
223 lines
6.5 KiB
JavaScript
223 lines
6.5 KiB
JavaScript
// //////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Locale verification script
|
|
//
|
|
// usage:
|
|
//
|
|
// node app/scripts/verify-locale-strings.js [<locale>] [--fix]
|
|
//
|
|
// This script will validate that locales have no unused messages. It will check
|
|
// the English locale against string literals found under `ui/`, and it will check
|
|
// other locales by comparing them to the English locale.
|
|
//
|
|
// A report will be printed to the console detailing any unused locales, and also
|
|
// any missing messages in the non-English locales.
|
|
//
|
|
// The if the optional '--fix' parameter is given, locales will be automatically
|
|
// updated to remove any unused messages.
|
|
//
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
|
|
const fs = require('fs')
|
|
const path = require('path')
|
|
const { promisify } = require('util')
|
|
const matchAll = require('string.prototype.matchall').getPolyfill()
|
|
const localeIndex = require('../app/_locales/index.json')
|
|
const readdir = promisify(fs.readdir)
|
|
const readFile = promisify(fs.readFile)
|
|
const writeFile = promisify(fs.writeFile)
|
|
|
|
console.log('Locale Verification')
|
|
|
|
let fix = false
|
|
let specifiedLocale
|
|
if (process.argv[2] === '--fix') {
|
|
fix = true
|
|
specifiedLocale = process.argv[3]
|
|
} else {
|
|
specifiedLocale = process.argv[2]
|
|
if (process.argv[3] === '--fix') {
|
|
fix = true
|
|
}
|
|
}
|
|
|
|
main(specifiedLocale, fix)
|
|
.catch(error => {
|
|
console.error(error)
|
|
process.exit(1)
|
|
})
|
|
|
|
async function main (specifiedLocale, fix) {
|
|
if (specifiedLocale) {
|
|
console.log(`Verifying selected locale "${specifiedLocale}":\n\n`)
|
|
const locale = localeIndex.find(localeMeta => localeMeta.code === specifiedLocale)
|
|
const failed = locale.code === 'en' ?
|
|
await verifyEnglishLocale(fix) :
|
|
await verifyLocale(locale, fix)
|
|
if (failed) {
|
|
process.exit(1)
|
|
}
|
|
} else {
|
|
console.log('Verifying all locales:\n\n')
|
|
let failed = await verifyEnglishLocale(fix)
|
|
const localeCodes = localeIndex
|
|
.filter(localeMeta => localeMeta.code !== 'en')
|
|
.map(localeMeta => localeMeta.code)
|
|
|
|
for (const code of localeCodes) {
|
|
const localeFailed = await verifyLocale(code, fix)
|
|
failed = failed || localeFailed
|
|
console.log('\n')
|
|
}
|
|
|
|
if (failed) {
|
|
process.exit(1)
|
|
}
|
|
}
|
|
}
|
|
|
|
function getLocalePath (code) {
|
|
return path.resolve(__dirname, '..', 'app', '_locales', code, 'messages.json')
|
|
}
|
|
|
|
async function getLocale (code) {
|
|
try {
|
|
const localeFilePath = getLocalePath(code)
|
|
const fileContents = await readFile(localeFilePath, 'utf8')
|
|
return JSON.parse(fileContents)
|
|
} catch (e) {
|
|
if (e.code === 'ENOENT') {
|
|
console.log('Locale file not found')
|
|
} else {
|
|
console.log(`Error opening your locale ("${code}") file: `, e)
|
|
}
|
|
process.exit(1)
|
|
}
|
|
}
|
|
|
|
async function writeLocale (code, locale) {
|
|
try {
|
|
const localeFilePath = getLocalePath(code)
|
|
return writeFile(localeFilePath, JSON.stringify(locale, null, 2) + '\n', 'utf8')
|
|
} catch (e) {
|
|
if (e.code === 'ENOENT') {
|
|
console.log('Locale file not found')
|
|
} else {
|
|
console.log(`Error writing your locale ("${code}") file: `, e)
|
|
}
|
|
process.exit(1)
|
|
}
|
|
}
|
|
|
|
async function verifyLocale (code, fix = false) {
|
|
const englishLocale = await getLocale('en')
|
|
const targetLocale = await getLocale(code)
|
|
|
|
const extraItems = compareLocalesForMissingItems({ base: targetLocale, subject: englishLocale })
|
|
const missingItems = compareLocalesForMissingItems({ base: englishLocale, subject: targetLocale })
|
|
|
|
const englishEntryCount = Object.keys(englishLocale).length
|
|
const coveragePercent = 100 * (englishEntryCount - missingItems.length) / englishEntryCount
|
|
|
|
console.log(`Status of **${code}** ${coveragePercent.toFixed(2)}% coverage:`)
|
|
|
|
if (extraItems.length) {
|
|
console.log('\nExtra items that should not be localized:')
|
|
extraItems.forEach(function (key) {
|
|
console.log(` - [ ] ${key}`)
|
|
})
|
|
} else {
|
|
// console.log(` all ${counter} strings declared in your locale ("${code}") were found in the english one`)
|
|
}
|
|
|
|
if (missingItems.length) {
|
|
console.log(`\nMissing items not present in localized file:`)
|
|
missingItems.forEach(function (key) {
|
|
console.log(` - [ ] ${key}`)
|
|
})
|
|
} else {
|
|
// console.log(` all ${counter} english strings were found in your locale ("${code}")!`)
|
|
}
|
|
|
|
if (!extraItems.length && !missingItems.length) {
|
|
console.log('Full coverage : )')
|
|
}
|
|
|
|
if (extraItems.length > 0) {
|
|
if (fix) {
|
|
const newLocale = Object.assign({}, targetLocale)
|
|
for (const item of extraItems) {
|
|
delete newLocale[item]
|
|
}
|
|
await writeLocale(code, newLocale)
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
async function verifyEnglishLocale (fix = false) {
|
|
const englishLocale = await getLocale('en')
|
|
const javascriptFiles = await findJavascriptFiles(path.resolve(__dirname, '..', 'ui'))
|
|
|
|
const regex = /'(\w+)'/g
|
|
const usedMessages = new Set()
|
|
for await (const fileContents of getFileContents(javascriptFiles)) {
|
|
for (const match of matchAll.call(fileContents, regex)) {
|
|
usedMessages.add(match[1])
|
|
}
|
|
}
|
|
|
|
// never consider these messages as unused
|
|
const messageExceptions = ['appName', 'appDescription']
|
|
|
|
const englishMessages = Object.keys(englishLocale)
|
|
const unusedMessages = englishMessages
|
|
.filter(message => !messageExceptions.includes(message) && !usedMessages.has(message))
|
|
|
|
console.log(`Status of **English (en)** ${unusedMessages.length} unused messages:`)
|
|
|
|
if (unusedMessages.length === 0) {
|
|
console.log('Full coverage : )')
|
|
return false
|
|
}
|
|
|
|
console.log(`\nMessages not present in UI:`)
|
|
unusedMessages.forEach(function (key) {
|
|
console.log(` - [ ] ${key}`)
|
|
})
|
|
|
|
if (unusedMessages.length > 0 && fix) {
|
|
const newLocale = Object.assign({}, englishLocale)
|
|
for (const key of unusedMessages) {
|
|
delete newLocale[key]
|
|
}
|
|
await writeLocale('en', newLocale)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
async function findJavascriptFiles (rootDir) {
|
|
const javascriptFiles = []
|
|
const contents = await readdir(rootDir, { withFileTypes: true })
|
|
for (const file of contents) {
|
|
if (file.isDirectory()) {
|
|
javascriptFiles.push(...(await findJavascriptFiles(path.join(rootDir, file.name))))
|
|
} else if (file.isFile() && file.name.endsWith('.js')) {
|
|
javascriptFiles.push(path.join(rootDir, file.name))
|
|
}
|
|
}
|
|
return javascriptFiles
|
|
}
|
|
|
|
async function * getFileContents (filenames) {
|
|
for (const filename of filenames) {
|
|
yield readFile(filename, 'utf8')
|
|
}
|
|
}
|
|
|
|
|
|
function compareLocalesForMissingItems ({ base, subject }) {
|
|
return Object.keys(base).filter((key) => !subject[key])
|
|
}
|