message.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. // Copyright 2013 ChaiShushan <chaishushan{AT}gmail.com>. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package po
  5. import (
  6. "bytes"
  7. "fmt"
  8. "io"
  9. "strconv"
  10. "strings"
  11. )
  12. // A PO file is made up of many entries,
  13. // each entry holding the relation between an original untranslated string
  14. // and its corresponding translation.
  15. //
  16. // See http://www.gnu.org/software/gettext/manual/html_node/PO-Files.html
  17. type Message struct {
  18. Comment // Coments
  19. MsgContext string // msgctxt context
  20. MsgId string // msgid untranslated-string
  21. MsgIdPlural string // msgid_plural untranslated-string-plural
  22. MsgStr string // msgstr translated-string
  23. MsgStrPlural []string // msgstr[0] translated-string-case-0
  24. }
  25. type byMessages []Message
  26. func (d byMessages) Len() int {
  27. return len(d)
  28. }
  29. func (d byMessages) Less(i, j int) bool {
  30. if d[i].Comment.less(&d[j].Comment) {
  31. return true
  32. }
  33. if a, b := d[i].MsgContext, d[j].MsgContext; a != b {
  34. return a < b
  35. }
  36. if a, b := d[i].MsgId, d[j].MsgId; a != b {
  37. return a < b
  38. }
  39. if a, b := d[i].MsgIdPlural, d[j].MsgIdPlural; a != b {
  40. return a < b
  41. }
  42. return false
  43. }
  44. func (d byMessages) Swap(i, j int) {
  45. d[i], d[j] = d[j], d[i]
  46. }
  47. func (p *Message) readPoEntry(r *lineReader) (err error) {
  48. *p = Message{}
  49. if err = r.skipBlankLine(); err != nil {
  50. return
  51. }
  52. defer func(oldPos int) {
  53. newPos := r.currentPos()
  54. if newPos != oldPos && err == io.EOF {
  55. err = nil
  56. }
  57. }(r.currentPos())
  58. if err = p.Comment.readPoComment(r); err != nil {
  59. return
  60. }
  61. for {
  62. var s string
  63. if s, _, err = r.currentLine(); err != nil {
  64. return
  65. }
  66. if p.isInvalidLine(s) {
  67. err = fmt.Errorf("gettext: line %d, %v", r.currentPos(), "invalid line")
  68. return
  69. }
  70. if reComment.MatchString(s) || reBlankLine.MatchString(s) {
  71. return
  72. }
  73. if err = p.readMsgContext(r); err != nil {
  74. return
  75. }
  76. if err = p.readMsgId(r); err != nil {
  77. return
  78. }
  79. if err = p.readMsgIdPlural(r); err != nil {
  80. return
  81. }
  82. if err = p.readMsgStrOrPlural(r); err != nil {
  83. return
  84. }
  85. }
  86. }
  87. func (p *Message) readMsgContext(r *lineReader) (err error) {
  88. var s string
  89. if s, _, err = r.currentLine(); err != nil {
  90. return
  91. }
  92. if !reMsgContext.MatchString(s) {
  93. return
  94. }
  95. p.MsgContext, err = p.readString(r)
  96. return
  97. }
  98. func (p *Message) readMsgId(r *lineReader) (err error) {
  99. var s string
  100. if s, _, err = r.currentLine(); err != nil {
  101. return
  102. }
  103. if !reMsgId.MatchString(s) {
  104. return
  105. }
  106. p.MsgId, err = p.readString(r)
  107. return
  108. }
  109. func (p *Message) readMsgIdPlural(r *lineReader) (err error) {
  110. var s string
  111. if s, _, err = r.currentLine(); err != nil {
  112. return
  113. }
  114. if !reMsgIdPlural.MatchString(s) {
  115. return
  116. }
  117. p.MsgIdPlural, err = p.readString(r)
  118. return nil
  119. }
  120. func (p *Message) readMsgStrOrPlural(r *lineReader) (err error) {
  121. var s string
  122. if s, _, err = r.currentLine(); err != nil {
  123. return
  124. }
  125. if !reMsgStr.MatchString(s) && !reMsgStrPlural.MatchString(s) {
  126. return
  127. }
  128. if reMsgStrPlural.MatchString(s) {
  129. left, right := strings.Index(s, `[`), strings.LastIndex(s, `]`)
  130. idx, _ := strconv.Atoi(s[left+1 : right])
  131. s, err = p.readString(r)
  132. if n := len(p.MsgStrPlural); (idx + 1) > n {
  133. p.MsgStrPlural = append(p.MsgStrPlural, make([]string, (idx+1)-n)...)
  134. }
  135. p.MsgStrPlural[idx] = s
  136. } else {
  137. p.MsgStr, err = p.readString(r)
  138. }
  139. return nil
  140. }
  141. func (p *Message) readString(r *lineReader) (msg string, err error) {
  142. var s string
  143. if s, _, err = r.readLine(); err != nil {
  144. return
  145. }
  146. msg += decodePoString(s)
  147. for {
  148. if s, _, err = r.readLine(); err != nil {
  149. return
  150. }
  151. if !reStringLine.MatchString(s) {
  152. r.unreadLine()
  153. break
  154. }
  155. msg += decodePoString(s)
  156. }
  157. return
  158. }
  159. // String returns the po format entry string.
  160. func (p Message) String() string {
  161. var buf bytes.Buffer
  162. fmt.Fprintf(&buf, "%s", p.Comment.String())
  163. fmt.Fprintf(&buf, "msgid %s", encodePoString(p.MsgId))
  164. if p.MsgIdPlural != "" {
  165. fmt.Fprintf(&buf, "msgid_plural %s", encodePoString(p.MsgIdPlural))
  166. }
  167. if p.MsgStr != "" {
  168. fmt.Fprintf(&buf, "msgstr %s", encodePoString(p.MsgStr))
  169. }
  170. for i := 0; i < len(p.MsgStrPlural); i++ {
  171. fmt.Fprintf(&buf, "msgstr[%d] %s", i, encodePoString(p.MsgStrPlural[i]))
  172. }
  173. return buf.String()
  174. }