nodejs-i18n
Version:
Minimalistic internationalization using gettext style for NodeJS
154 lines (142 loc) • 4 kB
JavaScript
const fs = require('fs')
const path = require('path')
const readline = require('readline')
const esprima = require('esprima')
const gettextParser = require('gettext-parser')
const argv = require('minimist')(process.argv)
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
})
const whiteList = [
'String',
'Template'
]
const prefixs = [
'i18n',
'_'
]
const comments = []
let n = 0
let check = false
let identifier = []
const po = {
charset: 'utf-8',
headers: {
'content-type': 'text/plain; charset=UTF-8',
'content-transfer-encoding': 'UTF-8'
},
translations: { '': {}}
}
rl.on('line', (line) => {
line = line.replace(/^\.\//g, '')
const file = path.resolve(process.cwd() + '/' + line)
const code = fs.readFileSync(file).toString()
let tokens = esprima.tokenize(code, {
loc: true,
comment: true
})
tokens = tokens.reduce((total, current, index) => {
if (argv.all && !check) {
check = true
}
if (current.type == 'LineComment' && current.value.match(/i18n/i)) {
comments.push(current)
} else if (current.type == 'Identifier' && prefixs.includes(current.value)) {
check = true
} else if (check) {
if (current.type == 'Identifier' && argv.value) {
if (identifier[n]) {
identifier[n] += `.${current.value}`
} else {
identifier[n] = `${current.value}`
}
}
if (whiteList.includes(current.type) && typeof check == 'boolean') {
check = current.type
}
if (!whiteList.includes(current.type)) {
if (check == whiteList[0] && current.type == 'Punctuator' && current.value == ')') {
check = false
}
return total
}
if (check == whiteList[0]) { //String
total.push(current)
//check = false
} else if (current.value.startsWith('}')) {
const old = total[total.length-1]
total[total.length-1].value = `${old.value}${n}${current.value}`
total[total.length-1].loc.end = current.loc.end
n++
if (argv.value) {
identifier[n] = false
}
} else if (current.value.startsWith('`') && !total[total.length-1].value.endsWith('${')) {
n = 0
if (argv.value) {
identifier = [false]
}
total.push(current)
}
if (check == current.type && (
total[total.length-1].value.endsWith('"') ||
total[total.length-1].value.endsWith('\'') ||
total[total.length-1].value.endsWith('`')
)) {
const comment = comments[comments.length-1]
if (comment) {
const commentLine = comment.loc.start.line
const codeLine = total[total.length-1].loc.start.line
if (commentLine == codeLine || commentLine+1 == codeLine) {
total[total.length-1].comment = comment.value.replace(/[#\s]*i18n[\s:]*/i, '')
}
} else if (argv.value) {
total[total.length-1].comment = ''
}
if (argv.value) {
identifier.map((value, index) => {
if (value) {
if (index > 0) {
total[total.length-1].comment += '\n'
}
total[total.length-1].comment += `\${${index}} = "${value}"`
}
})
}
check = false
}
}
return total
}, [{value: ''}])
tokens = tokens.filter(t => t.value != '')
tokens.map((token) => {
token.value = token.value.replace(/['"`](.*)['"`]/s, '$1')
if (Object.keys(po.translations['']).includes(token.value)) {
po.translations[''][token.value].comments.reference += (
`\n${line}:${token.loc.start.line}:${token.loc.start.column}`
)
if (token.comment) {
po.translations[''][token.value].comments.extracted += '\n' + token.comment
}
} else {
po.translations[''][token.value] = {
msgid: token.value,
comments: {
reference: `${line}:${token.loc.start.line}:${token.loc.start.column}`
},
msgstr: []
}
if (token.comment) {
po.translations[''][token.value].comments.extracted = token.comment
}
}
})
})
rl.on('close', async (line) => {
console.log(
gettextParser.po.compile(po).toString().trim().replace(/\\\\/sg, '\\')
)
})