index.js 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. const { mo, po } = require('gettext-parser')
  2. const getPluralFunction = require('./plural-forms')
  3. const defaultOptions = {
  4. defaultCharset: null,
  5. forceContext: false,
  6. pluralFunction: null,
  7. pluralVariablePattern: /%(?:\((\w+)\))?\w/,
  8. replacements: [
  9. {
  10. pattern: /[\\{}#]/g,
  11. replacement: '\\$&'
  12. },
  13. {
  14. pattern: /%(\d+)(?:\$\w)?/g,
  15. replacement: (_, n) => `{${n - 1}}`
  16. },
  17. {
  18. pattern: /%\((\w+)\)\w/g,
  19. replacement: '{$1}'
  20. },
  21. {
  22. pattern: /%\w/g,
  23. replacement: function () { return `{${this.n++}}` },
  24. state: { n: 0 }
  25. },
  26. {
  27. pattern: /%%/g,
  28. replacement: '%'
  29. }
  30. ],
  31. verbose: false
  32. }
  33. const getMessageFormat = (
  34. { pluralFunction, pluralVariablePattern, replacements, verbose },
  35. { msgid, msgid_plural, msgstr }
  36. ) => {
  37. if (!msgid || !msgstr) return null
  38. if (!msgstr[0]) {
  39. if (verbose) console.warn('Translation not found:', msgid)
  40. msgstr[0] = msgid
  41. }
  42. if (msgid_plural) {
  43. if (!pluralFunction) throw new Error('Plural-Forms not defined')
  44. for (let i = 1; i < pluralFunction.cardinal.length; ++i) {
  45. if (!msgstr[i]) {
  46. if (verbose) console.warn('Plural translation not found:', msgid, i)
  47. msgstr[i] = msgid_plural
  48. }
  49. }
  50. }
  51. msgstr = msgstr.map(str => (
  52. replacements.reduce((str, { pattern, replacement, state }) => {
  53. if (state) replacement = replacement.bind(Object.assign({}, state))
  54. return str.replace(pattern, replacement)
  55. }, str)
  56. ))
  57. if (msgid_plural) {
  58. const m = msgid_plural.match(pluralVariablePattern)
  59. const pv = m && m[1] || '0'
  60. const pc = pluralFunction.cardinal.map((c, i) => `${c}{${msgstr[i]}}`)
  61. return `{${pv}, plural, ${pc.join(' ')}}`
  62. }
  63. return msgstr[0]
  64. }
  65. const convert = (parse, input, options) => {
  66. options = Object.assign({}, defaultOptions, options)
  67. const { headers, translations } = parse(input, options.defaultCharset)
  68. if (!options.pluralFunction) {
  69. options.pluralFunction = getPluralFunction(headers['plural-forms'])
  70. }
  71. let hasContext = false
  72. for (const context in translations) {
  73. if (context) hasContext = true
  74. const data = translations[context]
  75. for (const id in data) {
  76. const mf = getMessageFormat(options, data[id])
  77. if (mf) data[id] = mf
  78. else delete data[id]
  79. }
  80. }
  81. return {
  82. headers,
  83. pluralFunction: options.pluralFunction,
  84. translations: hasContext || options.forceContext ? translations : translations['']
  85. }
  86. }
  87. module.exports = {
  88. parseMo: (input, options) => convert(mo.parse, input, options),
  89. parsePo: (input, options) => convert(po.parse, input, options)
  90. }