comment.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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. // Comment represents every message's comments.
  13. type Comment struct {
  14. StartLine int // comment start line
  15. TranslatorComment string // # translator-comments // TrimSpace
  16. ExtractedComment string // #. extracted-comments
  17. ReferenceFile []string // #: src/msgcmp.c:338 src/po-lex.c:699
  18. ReferenceLine []int // #: src/msgcmp.c:338 src/po-lex.c:699
  19. Flags []string // #, fuzzy,c-format,range:0..10
  20. PrevMsgContext string // #| msgctxt previous-context
  21. PrevMsgId string // #| msgid previous-untranslated-string
  22. }
  23. func (p *Comment) less(q *Comment) bool {
  24. if p.StartLine != 0 || q.StartLine != 0 {
  25. return p.StartLine < q.StartLine
  26. }
  27. if a, b := len(p.ReferenceFile), len(q.ReferenceFile); a != b {
  28. return a < b
  29. }
  30. for i := 0; i < len(p.ReferenceFile); i++ {
  31. if a, b := p.ReferenceFile[i], q.ReferenceFile[i]; a != b {
  32. return a < b
  33. }
  34. if a, b := p.ReferenceLine[i], q.ReferenceLine[i]; a != b {
  35. return a < b
  36. }
  37. }
  38. return false
  39. }
  40. func (p *Comment) readPoComment(r *lineReader) (err error) {
  41. *p = Comment{}
  42. if err = r.skipBlankLine(); err != nil {
  43. return err
  44. }
  45. defer func(oldPos int) {
  46. newPos := r.currentPos()
  47. if newPos != oldPos && err == io.EOF {
  48. err = nil
  49. }
  50. }(r.currentPos())
  51. p.StartLine = r.currentPos() + 1
  52. for {
  53. var s string
  54. if s, _, err = r.currentLine(); err != nil {
  55. return
  56. }
  57. if len(s) == 0 || s[0] != '#' {
  58. return
  59. }
  60. if err = p.readTranslatorComment(r); err != nil {
  61. return
  62. }
  63. if err = p.readExtractedComment(r); err != nil {
  64. return
  65. }
  66. if err = p.readReferenceComment(r); err != nil {
  67. return
  68. }
  69. if err = p.readFlagsComment(r); err != nil {
  70. return
  71. }
  72. if err = p.readPrevMsgContext(r); err != nil {
  73. return
  74. }
  75. if err = p.readPrevMsgId(r); err != nil {
  76. return
  77. }
  78. }
  79. }
  80. func (p *Comment) readTranslatorComment(r *lineReader) (err error) {
  81. const prefix = "# " // .,:|
  82. for {
  83. var s string
  84. if s, _, err = r.readLine(); err != nil {
  85. return err
  86. }
  87. if len(s) < 1 || s[0] != '#' {
  88. r.unreadLine()
  89. return nil
  90. }
  91. if len(s) >= 2 {
  92. switch s[1] {
  93. case '.', ',', ':', '|':
  94. r.unreadLine()
  95. return nil
  96. }
  97. }
  98. if p.TranslatorComment != "" {
  99. p.TranslatorComment += "\n"
  100. }
  101. p.TranslatorComment += strings.TrimSpace(s[1:])
  102. }
  103. }
  104. func (p *Comment) readExtractedComment(r *lineReader) (err error) {
  105. const prefix = "#."
  106. for {
  107. var s string
  108. if s, _, err = r.readLine(); err != nil {
  109. return err
  110. }
  111. if len(s) < len(prefix) || s[:len(prefix)] != prefix {
  112. r.unreadLine()
  113. return nil
  114. }
  115. if p.ExtractedComment != "" {
  116. p.ExtractedComment += "\n"
  117. }
  118. p.ExtractedComment += strings.TrimSpace(s[len(prefix):])
  119. }
  120. }
  121. func (p *Comment) readReferenceComment(r *lineReader) (err error) {
  122. const prefix = "#:"
  123. for {
  124. var s string
  125. if s, _, err = r.readLine(); err != nil {
  126. return err
  127. }
  128. if len(s) < len(prefix) || s[:len(prefix)] != prefix {
  129. r.unreadLine()
  130. return nil
  131. }
  132. ss := strings.Split(strings.TrimSpace(s[len(prefix):]), " ")
  133. for i := 0; i < len(ss); i++ {
  134. idx := strings.Index(ss[i], ":")
  135. if idx <= 0 {
  136. continue
  137. }
  138. name := strings.TrimSpace(ss[i][:idx])
  139. line, _ := strconv.Atoi(strings.TrimSpace(ss[i][idx+1:]))
  140. p.ReferenceFile = append(p.ReferenceFile, name)
  141. p.ReferenceLine = append(p.ReferenceLine, line)
  142. }
  143. }
  144. }
  145. func (p *Comment) readFlagsComment(r *lineReader) (err error) {
  146. const prefix = "#,"
  147. for {
  148. var s string
  149. if s, _, err = r.readLine(); err != nil {
  150. return err
  151. }
  152. if len(s) < len(prefix) || s[:len(prefix)] != prefix {
  153. r.unreadLine()
  154. return nil
  155. }
  156. ss := strings.Split(strings.TrimSpace(s[len(prefix):]), ",")
  157. for i := 0; i < len(ss); i++ {
  158. p.Flags = append(p.Flags, strings.TrimSpace(ss[i]))
  159. }
  160. }
  161. }
  162. func (p *Comment) readPrevMsgContext(r *lineReader) (err error) {
  163. var s string
  164. if s, _, err = r.currentLine(); err != nil {
  165. return
  166. }
  167. if !rePrevMsgContextComments.MatchString(s) {
  168. return
  169. }
  170. p.PrevMsgContext, err = p.readString(r)
  171. return
  172. }
  173. func (p *Comment) readPrevMsgId(r *lineReader) (err error) {
  174. var s string
  175. if s, _, err = r.currentLine(); err != nil {
  176. return
  177. }
  178. if !rePrevMsgIdComments.MatchString(s) {
  179. return
  180. }
  181. p.PrevMsgId, err = p.readString(r)
  182. return
  183. }
  184. func (p *Comment) readString(r *lineReader) (msg string, err error) {
  185. var s string
  186. if s, _, err = r.readLine(); err != nil {
  187. return
  188. }
  189. msg += decodePoString(s)
  190. for {
  191. if s, _, err = r.readLine(); err != nil {
  192. return
  193. }
  194. if !reStringLineComments.MatchString(s) {
  195. r.unreadLine()
  196. break
  197. }
  198. msg += decodePoString(s)
  199. }
  200. return
  201. }
  202. // GetFuzzy gets the fuzzy flag.
  203. func (p *Comment) GetFuzzy() bool {
  204. for _, s := range p.Flags {
  205. if s == "fuzzy" {
  206. return true
  207. }
  208. }
  209. return false
  210. }
  211. // SetFuzzy sets the fuzzy flag.
  212. func (p *Comment) SetFuzzy(fuzzy bool) {
  213. //
  214. }
  215. // String returns the po format comment string.
  216. func (p Comment) String() string {
  217. var buf bytes.Buffer
  218. if p.TranslatorComment != "" {
  219. ss := strings.Split(p.TranslatorComment, "\n")
  220. for i := 0; i < len(ss); i++ {
  221. fmt.Fprintf(&buf, "# %s\n", ss[i])
  222. }
  223. }
  224. if p.ExtractedComment != "" {
  225. ss := strings.Split(p.ExtractedComment, "\n")
  226. for i := 0; i < len(ss); i++ {
  227. fmt.Fprintf(&buf, "#. %s\n", ss[i])
  228. }
  229. }
  230. if a, b := len(p.ReferenceFile), len(p.ReferenceLine); a != 0 && a == b {
  231. fmt.Fprintf(&buf, "#:")
  232. for i := 0; i < len(p.ReferenceFile); i++ {
  233. fmt.Fprintf(&buf, " %s:%d", p.ReferenceFile[i], p.ReferenceLine[i])
  234. }
  235. fmt.Fprintf(&buf, "\n")
  236. }
  237. if len(p.Flags) != 0 {
  238. fmt.Fprintf(&buf, "#, %s", p.Flags[0])
  239. for i := 1; i < len(p.Flags); i++ {
  240. fmt.Fprintf(&buf, ", %s", p.Flags[i])
  241. }
  242. fmt.Fprintf(&buf, "\n")
  243. }
  244. if p.PrevMsgContext != "" {
  245. s := encodeCommentPoString(p.PrevMsgContext)
  246. fmt.Fprintf(&buf, "#| msgctxt %s\n", s)
  247. }
  248. if p.PrevMsgId != "" {
  249. s := encodeCommentPoString(p.PrevMsgId)
  250. fmt.Fprintf(&buf, "#| msgid %s\n", s)
  251. }
  252. return buf.String()
  253. }