const { mo, po } = require('gettext-parser') const getPluralFunction = require('./plural-forms') const defaultOptions = { defaultCharset: null, forceContext: false, pluralFunction: null, pluralVariablePattern: /%(?:\((\w+)\))?\w/, replacements: [ { pattern: /[\\{}#]/g, replacement: '\\$&' }, { pattern: /%(\d+)(?:\$\w)?/g, replacement: (_, n) => `{${n - 1}}` }, { pattern: /%\((\w+)\)\w/g, replacement: '{$1}' }, { pattern: /%\w/g, replacement: function () { return `{${this.n++}}` }, state: { n: 0 } }, { pattern: /%%/g, replacement: '%' } ], verbose: false } const getMessageFormat = ( { pluralFunction, pluralVariablePattern, replacements, verbose }, { msgid, msgid_plural, msgstr } ) => { if (!msgid || !msgstr) return null if (!msgstr[0]) { if (verbose) console.warn('Translation not found:', msgid) msgstr[0] = msgid } if (msgid_plural) { if (!pluralFunction) throw new Error('Plural-Forms not defined') for (let i = 1; i < pluralFunction.cardinal.length; ++i) { if (!msgstr[i]) { if (verbose) console.warn('Plural translation not found:', msgid, i) msgstr[i] = msgid_plural } } } msgstr = msgstr.map(str => ( replacements.reduce((str, { pattern, replacement, state }) => { if (state) replacement = replacement.bind(Object.assign({}, state)) return str.replace(pattern, replacement) }, str) )) if (msgid_plural) { const m = msgid_plural.match(pluralVariablePattern) const pv = m && m[1] || '0' const pc = pluralFunction.cardinal.map((c, i) => `${c}{${msgstr[i]}}`) return `{${pv}, plural, ${pc.join(' ')}}` } return msgstr[0] } const convert = (parse, input, options) => { options = Object.assign({}, defaultOptions, options) const { headers, translations } = parse(input, options.defaultCharset) if (!options.pluralFunction) { options.pluralFunction = getPluralFunction(headers['plural-forms']) } let hasContext = false for (const context in translations) { if (context) hasContext = true const data = translations[context] for (const id in data) { const mf = getMessageFormat(options, data[id]) if (mf) data[id] = mf else delete data[id] } } return { headers, pluralFunction: options.pluralFunction, translations: hasContext || options.forceContext ? translations : translations[''] } } module.exports = { parseMo: (input, options) => convert(mo.parse, input, options), parsePo: (input, options) => convert(po.parse, input, options) }