gettext 3.2.9 → 3.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +276 -198
  3. data/Rakefile +7 -4
  4. data/doc/text/news.md +451 -0
  5. data/gettext.gemspec +10 -7
  6. data/lib/gettext/locale_path.rb +17 -9
  7. data/lib/gettext/mo.rb +12 -19
  8. data/lib/gettext/po.rb +4 -4
  9. data/lib/gettext/po_entry.rb +47 -27
  10. data/lib/gettext/po_parser.rb +65 -59
  11. data/lib/gettext/text_domain.rb +1 -1
  12. data/lib/gettext/text_domain_manager.rb +6 -5
  13. data/lib/gettext/tools/msgcat.rb +9 -2
  14. data/lib/gettext/tools/msginit.rb +226 -54
  15. data/lib/gettext/tools/msgmerge.rb +24 -6
  16. data/lib/gettext/tools/parser/erb.rb +9 -1
  17. data/lib/gettext/tools/parser/erubi.rb +88 -0
  18. data/lib/gettext/tools/parser/glade.rb +38 -40
  19. data/lib/gettext/tools/parser/gtk_builder_ui_definitions.rb +129 -0
  20. data/lib/gettext/tools/parser/ruby.rb +288 -221
  21. data/lib/gettext/tools/task.rb +4 -5
  22. data/lib/gettext/tools/xgettext.rb +21 -10
  23. data/lib/gettext/version.rb +3 -3
  24. data/locale/bg/LC_MESSAGES/gettext.mo +0 -0
  25. data/locale/bs/LC_MESSAGES/gettext.mo +0 -0
  26. data/locale/ca/LC_MESSAGES/gettext.mo +0 -0
  27. data/locale/cs/LC_MESSAGES/gettext.mo +0 -0
  28. data/locale/de/LC_MESSAGES/gettext.mo +0 -0
  29. data/locale/el/LC_MESSAGES/gettext.mo +0 -0
  30. data/locale/eo/LC_MESSAGES/gettext.mo +0 -0
  31. data/locale/es/LC_MESSAGES/gettext.mo +0 -0
  32. data/locale/et/LC_MESSAGES/gettext.mo +0 -0
  33. data/locale/fr/LC_MESSAGES/gettext.mo +0 -0
  34. data/locale/hr/LC_MESSAGES/gettext.mo +0 -0
  35. data/locale/hu/LC_MESSAGES/gettext.mo +0 -0
  36. data/locale/it/LC_MESSAGES/gettext.mo +0 -0
  37. data/locale/ja/LC_MESSAGES/gettext.mo +0 -0
  38. data/locale/ko/LC_MESSAGES/gettext.mo +0 -0
  39. data/locale/lv/LC_MESSAGES/gettext.mo +0 -0
  40. data/locale/nb/LC_MESSAGES/gettext.mo +0 -0
  41. data/locale/nl/LC_MESSAGES/gettext.mo +0 -0
  42. data/locale/pt_BR/LC_MESSAGES/gettext.mo +0 -0
  43. data/locale/ru/LC_MESSAGES/gettext.mo +0 -0
  44. data/locale/sr/LC_MESSAGES/gettext.mo +0 -0
  45. data/locale/uk/LC_MESSAGES/gettext.mo +0 -0
  46. data/locale/vi/LC_MESSAGES/gettext.mo +0 -0
  47. data/locale/zh/LC_MESSAGES/gettext.mo +0 -0
  48. data/locale/zh_TW/LC_MESSAGES/gettext.mo +0 -0
  49. data/po/bg/gettext.edit.po +81 -572
  50. data/po/bg/gettext.po +7 -409
  51. data/po/bs/gettext.edit.po +81 -572
  52. data/po/bs/gettext.po +7 -409
  53. data/po/ca/gettext.edit.po +81 -572
  54. data/po/ca/gettext.po +7 -409
  55. data/po/cs/gettext.edit.po +81 -572
  56. data/po/cs/gettext.po +7 -409
  57. data/po/de/gettext.edit.po +81 -572
  58. data/po/de/gettext.po +7 -409
  59. data/po/el/gettext.edit.po +81 -572
  60. data/po/el/gettext.po +7 -409
  61. data/po/eo/gettext.edit.po +81 -572
  62. data/po/eo/gettext.po +7 -409
  63. data/po/es/gettext.edit.po +81 -572
  64. data/po/es/gettext.po +7 -409
  65. data/po/et/gettext.edit.po +81 -572
  66. data/po/et/gettext.po +7 -409
  67. data/po/fr/gettext.edit.po +81 -572
  68. data/po/fr/gettext.po +7 -409
  69. data/po/gettext.pot +93 -645
  70. data/po/hr/gettext.edit.po +81 -572
  71. data/po/hr/gettext.po +7 -409
  72. data/po/hu/gettext.edit.po +81 -572
  73. data/po/hu/gettext.po +7 -409
  74. data/po/it/gettext.edit.po +81 -572
  75. data/po/it/gettext.po +7 -409
  76. data/po/ja/gettext.edit.po +83 -574
  77. data/po/ja/gettext.po +9 -411
  78. data/po/ko/gettext.edit.po +81 -572
  79. data/po/ko/gettext.po +7 -409
  80. data/po/lv/gettext.edit.po +81 -572
  81. data/po/lv/gettext.po +7 -409
  82. data/po/nb/gettext.edit.po +81 -572
  83. data/po/nb/gettext.po +7 -409
  84. data/po/nl/gettext.edit.po +81 -572
  85. data/po/nl/gettext.po +7 -409
  86. data/po/pt_BR/gettext.edit.po +81 -572
  87. data/po/pt_BR/gettext.po +7 -409
  88. data/po/ru/gettext.edit.po +81 -572
  89. data/po/ru/gettext.po +7 -409
  90. data/po/sr/gettext.edit.po +81 -572
  91. data/po/sr/gettext.po +7 -409
  92. data/po/sv/gettext.edit.po +81 -572
  93. data/po/sv/gettext.po +7 -409
  94. data/po/uk/gettext.edit.po +81 -572
  95. data/po/uk/gettext.po +7 -409
  96. data/po/vi/gettext.edit.po +81 -572
  97. data/po/vi/gettext.po +7 -409
  98. data/po/zh/gettext.edit.po +81 -572
  99. data/po/zh/gettext.po +7 -409
  100. data/po/zh_TW/gettext.edit.po +81 -572
  101. data/po/zh_TW/gettext.po +7 -409
  102. data/samples/cgi/po/helloerb1.pot +3 -3
  103. data/samples/cgi/po/helloerb2.pot +3 -3
  104. data/samples/cgi/po/hellolib.pot +3 -3
  105. data/samples/cgi/po/main.pot +3 -3
  106. data/samples/po/bg/hello_gtk_builder.edit.po +15 -0
  107. data/samples/po/bg/hello_gtk_builder.po +12 -0
  108. data/samples/po/bs/hello_gtk_builder.edit.po +15 -0
  109. data/samples/po/bs/hello_gtk_builder.po +12 -0
  110. data/samples/po/ca/hello_gtk_builder.edit.po +15 -0
  111. data/samples/po/ca/hello_gtk_builder.po +12 -0
  112. data/samples/po/cs/hello_gtk_builder.edit.po +15 -0
  113. data/samples/po/cs/hello_gtk_builder.po +12 -0
  114. data/samples/po/de/hello_gtk_builder.edit.po +15 -0
  115. data/samples/po/de/hello_gtk_builder.po +12 -0
  116. data/samples/po/el/hello_gtk_builder.edit.po +15 -0
  117. data/samples/po/el/hello_gtk_builder.po +12 -0
  118. data/samples/po/eo/hello_gtk_builder.edit.po +15 -0
  119. data/samples/po/eo/hello_gtk_builder.po +12 -0
  120. data/samples/po/es/hello_gtk_builder.edit.po +15 -0
  121. data/samples/po/es/hello_gtk_builder.po +12 -0
  122. data/samples/po/fr/hello_gtk_builder.edit.po +15 -0
  123. data/samples/po/fr/hello_gtk_builder.po +12 -0
  124. data/samples/po/hello.pot +3 -3
  125. data/samples/po/hello2.pot +4 -4
  126. data/samples/po/hello_glade2.pot +8 -5
  127. data/samples/po/hello_gtk2.pot +4 -4
  128. data/samples/po/hello_gtk_builder.pot +18 -4
  129. data/samples/po/hello_noop.pot +4 -4
  130. data/samples/po/hello_plural.pot +4 -4
  131. data/samples/po/hello_tk.pot +4 -4
  132. data/samples/po/hr/hello_gtk_builder.edit.po +15 -0
  133. data/samples/po/hr/hello_gtk_builder.po +12 -0
  134. data/samples/po/hu/hello_gtk_builder.edit.po +15 -0
  135. data/samples/po/hu/hello_gtk_builder.po +12 -0
  136. data/samples/po/it/hello_gtk_builder.edit.po +15 -0
  137. data/samples/po/it/hello_gtk_builder.po +12 -0
  138. data/samples/po/ja/hello_gtk_builder.edit.po +15 -0
  139. data/samples/po/ja/hello_gtk_builder.po +12 -0
  140. data/samples/po/ko/hello_gtk_builder.edit.po +15 -0
  141. data/samples/po/ko/hello_gtk_builder.po +12 -0
  142. data/samples/po/lv/hello_gtk_builder.edit.po +15 -0
  143. data/samples/po/lv/hello_gtk_builder.po +12 -0
  144. data/samples/po/nb/hello_gtk_builder.edit.po +15 -0
  145. data/samples/po/nb/hello_gtk_builder.po +12 -0
  146. data/samples/po/nl/hello_gtk_builder.edit.po +15 -0
  147. data/samples/po/nl/hello_gtk_builder.po +12 -0
  148. data/samples/po/pt_BR/hello_gtk_builder.edit.po +15 -0
  149. data/samples/po/pt_BR/hello_gtk_builder.po +12 -0
  150. data/samples/po/ru/hello_gtk_builder.edit.po +15 -0
  151. data/samples/po/ru/hello_gtk_builder.po +12 -0
  152. data/samples/po/sr/hello_gtk_builder.edit.po +15 -0
  153. data/samples/po/sr/hello_gtk_builder.po +12 -0
  154. data/samples/po/sv/hello_gtk_builder.edit.po +15 -0
  155. data/samples/po/sv/hello_gtk_builder.po +12 -0
  156. data/samples/po/uk/hello_gtk_builder.edit.po +15 -0
  157. data/samples/po/uk/hello_gtk_builder.po +12 -0
  158. data/samples/po/vi/hello_gtk_builder.edit.po +15 -0
  159. data/samples/po/vi/hello_gtk_builder.po +12 -0
  160. data/samples/po/zh/hello_gtk_builder.edit.po +15 -0
  161. data/samples/po/zh/hello_gtk_builder.po +12 -0
  162. data/samples/po/zh_TW/hello_gtk_builder.edit.po +15 -0
  163. data/samples/po/zh_TW/hello_gtk_builder.po +12 -0
  164. data/src/po_parser.ry +48 -42
  165. data/test/fixtures/_/backtick.rb +30 -0
  166. data/test/fixtures/_/block_parameter.rb +2 -2
  167. data/test/fixtures/_/double_quote_in_double_quote.rb +2 -2
  168. data/test/fixtures/_/double_quote_in_single_quote.rb +2 -2
  169. data/test/fixtures/_/literal_concatenation_with_continuation_line.rb +2 -2
  170. data/test/fixtures/_/middle_new_line.rb +2 -2
  171. data/test/fixtures/_/multiple_lines_literal.rb +2 -2
  172. data/test/fixtures/_/multiple_messages_in_same_line.rb +2 -2
  173. data/test/fixtures/_/multiple_same_messages.rb +2 -2
  174. data/test/fixtures/_/one_line.rb +2 -2
  175. data/test/fixtures/_/one_new_line.rb +2 -2
  176. data/test/fixtures/_/percent_strings.rb +55 -0
  177. data/test/fixtures/_/pipe.rb +32 -0
  178. data/test/{gettext-test-utils.rb → fixtures/_/quoted_symbol.rb} +13 -23
  179. data/test/fixtures/_.rb +10 -1
  180. data/test/fixtures/erb/case.rhtml +16 -0
  181. data/test/fixtures/erb/minus.rhtml +12 -0
  182. data/test/fixtures/erb/non_ascii.rhtml +1 -0
  183. data/test/fixtures/glade/3.glade +63 -0
  184. data/test/fixtures/gtk_builder_ui_definitions.ui +68 -0
  185. data/test/fixtures/multi_text_domain.rb +12 -12
  186. data/test/fixtures/np_.rb +1 -1
  187. data/test/fixtures/ns_/custom.rb +2 -2
  188. data/test/fixtures/ns_.rb +1 -1
  189. data/test/fixtures/p_.rb +1 -1
  190. data/test/fixtures/ruby/percent_lower_i.rb +1 -0
  191. data/test/fixtures/ruby/percent_lower_w.rb +1 -0
  192. data/test/fixtures/ruby/percent_upper_i.rb +1 -0
  193. data/test/fixtures/ruby/percent_upper_w.rb +1 -0
  194. data/test/fixtures/s_/custom.rb +2 -2
  195. data/test/fixtures/s_.rb +1 -1
  196. data/test/fixtures/simple.rb +5 -1
  197. data/test/fixtures/upper_nn_.rb +77 -0
  198. data/test/helper.rb +99 -0
  199. data/test/locale/fr/LC_MESSAGES/plural_error.mo +0 -0
  200. data/test/locale/fr/LC_MESSAGES/test1.mo +0 -0
  201. data/test/locale/fr_BE/LC_MESSAGES/test1.mo +0 -0
  202. data/test/locale/fr_BE_Foo/LC_MESSAGES/test1.mo +0 -0
  203. data/test/locale/ja/LC_MESSAGES/_.mo +0 -0
  204. data/test/po/_.pot +28 -4
  205. data/test/po/backslash.pot +6 -4
  206. data/test/po/fr/plural_error.po +7 -0
  207. data/test/po/fr/test1.po +7 -0
  208. data/test/po/fr_BE/test1.po +24 -0
  209. data/test/po/fr_BE_Foo/test1.po +20 -0
  210. data/test/po/hello.pot +3 -3
  211. data/test/po/ja/_.edit.po +27 -3
  212. data/test/po/ja/_.po +20 -2
  213. data/test/po/ja/hello.edit.po +0 -1
  214. data/test/po/non_ascii.pot +4 -4
  215. data/test/po/np_.pot +8 -5
  216. data/test/po/ns_.pot +6 -4
  217. data/test/po/p_.pot +3 -3
  218. data/test/po/s_.pot +6 -4
  219. data/test/po/untranslated.pot +4 -4
  220. data/test/run-test.rb +2 -3
  221. data/test/test_class_info.rb +8 -9
  222. data/test/test_gettext.rb +30 -1
  223. data/test/test_locale_path.rb +12 -4
  224. data/test/test_parser.rb +113 -25
  225. data/test/test_po_entry.rb +21 -17
  226. data/test/test_po_parser.rb +49 -4
  227. data/test/test_string.rb +9 -5
  228. data/test/tools/parser/test_glade.rb +91 -0
  229. data/test/tools/parser/test_gtk_builder_ui_definitions.rb +63 -0
  230. data/test/tools/parser/test_ruby.rb +76 -39
  231. data/test/tools/test_msgcat.rb +21 -1
  232. data/test/tools/test_msginit.rb +82 -3
  233. data/test/tools/test_msgmerge.rb +81 -6
  234. data/test/tools/test_xgettext.rb +106 -5
  235. metadata +82 -34
  236. data/lib/gettext/tools/parser/haml.rb +0 -61
  237. /data/test/fixtures/{gladeparser.glade → glade/2.glade} +0 -0
@@ -0,0 +1,129 @@
1
+ # Copyright (C) 2020 Sutou Kouhei <kou@clear-code.com>
2
+ #
3
+ # License: Ruby's or LGPL
4
+ #
5
+ # This library is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Lesser General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require "English"
19
+ require "cgi"
20
+ require "strscan"
21
+
22
+ require "gettext/po_entry"
23
+
24
+ module GetText
25
+ class GtkBuilderUIDefinitionsParser
26
+ @config = {
27
+ :extnames => [".ui", ".glade"]
28
+ }
29
+
30
+ class << self
31
+ # Sets some preferences to parse GtkBuilder UI definitions files.
32
+ # * config: a Hash of the config. It can takes some values below:
33
+ # * :extnames: An Array of target files extension.
34
+ # Default is [".ui", ".glade"].
35
+ def init(config)
36
+ config.each do |k, v|
37
+ @config[k] = v
38
+ end
39
+ end
40
+
41
+ def target?(file) # :nodoc:
42
+ @config[:extnames].each do |extname|
43
+ next unless File.extname(file) == extname
44
+ next unless File.read(file).include?("<interface>")
45
+ return true
46
+ end
47
+ false
48
+ end
49
+
50
+ def parse(path, options={})
51
+ parser = new(path, options)
52
+ parser.parse
53
+ end
54
+ end
55
+
56
+ def initialize(path, options={})
57
+ @path = path
58
+ @options = options
59
+ end
60
+
61
+ def parse # :nodoc:
62
+ File.open(@path) do |file|
63
+ po = []
64
+ start_line_no = nil
65
+ property = nil
66
+ file.each_line do |line|
67
+ case line
68
+ when /<property/
69
+ property = $POSTMATCH
70
+ start_line_no = file.lineno
71
+ if /<\/property>/ =~ property
72
+ property << $PREMATCH
73
+ add_po_entry(po, property, start_line_no)
74
+ property = nil
75
+ end
76
+ when /<\/property>/
77
+ property << $PREMATCH
78
+ add_po_entry(po, property, start_line_no)
79
+ property = nil
80
+ else
81
+ property << line if property
82
+ end
83
+ end
84
+ po
85
+ end
86
+ end
87
+
88
+ private
89
+ def add_po_entry(po, property, line_no)
90
+ raw_attributes, raw_data_and_close_tag = property.split(">", 2)
91
+ raw_data, _close_tag = raw_data_and_close_tag.split("<", 2)
92
+ return if raw_data.empty?
93
+
94
+ attributes = parse_attributes(raw_attributes)
95
+ return unless attributes["translatable"] == "yes"
96
+
97
+ data = CGI.unescapeHTML(raw_data)
98
+ context = attributes["context"]
99
+ if context
100
+ po_entry = POEntry.new(:msgctxt)
101
+ po_entry.msgctxt = context
102
+ else
103
+ po_entry = POEntry.new(:normal)
104
+ end
105
+ po_entry.msgid = data
106
+ po_entry.references << "#{@path}:#{line_no}"
107
+ po << po_entry
108
+ end
109
+
110
+ def parse_attributes(raw_attributes)
111
+ scanner = StringScanner.new(raw_attributes)
112
+ attributes = {}
113
+ loop do
114
+ scanner.scan(/\s*/m)
115
+ break if scanner.eos?
116
+ name = scanner.scan(/[^=]+/)
117
+ break if name.nil?
118
+ break unless scanner.scan(/=/)
119
+ quote = scanner.scan(/["']/)
120
+ break if quote.nil?
121
+ value = scanner.scan(/[^#{Regexp.escape(quote)}]+/m)
122
+ break if value.nil?
123
+ break unless scanner.scan(/#{Regexp.escape(quote)}/)
124
+ attributes[name] = CGI.unescapeHTML(value)
125
+ end
126
+ attributes
127
+ end
128
+ end
129
+ end
@@ -1,8 +1,7 @@
1
- # -*- coding: utf-8 -*-
2
1
  =begin
3
2
  parser/ruby.rb - parser for ruby script
4
3
 
5
- Copyright (C) 2013-2017 Kouhei Sutou <kou@clear-code.com>
4
+ Copyright (C) 2013-2021 Sutou Kouhei <kou@clear-code.com>
6
5
  Copyright (C) 2003-2009 Masao Mutoh
7
6
  Copyright (C) 2005 speakillof
8
7
  Copyright (C) 2001,2002 Yasushi Shoji, Masao Mutoh
@@ -12,162 +11,324 @@
12
11
 
13
12
  =end
14
13
 
15
- require "irb/ruby-lex"
14
+ require "ripper"
16
15
  require "stringio"
17
- require "gettext/po_entry"
18
16
 
19
- require "ripper"
17
+ require "gettext/po_entry"
20
18
 
21
19
  module GetText
22
- class RubyLexX < RubyLex # :nodoc: all
23
- class StringExtractor < Ripper::Filter
20
+ class RubyParser
21
+ class POExtractor < Ripper::Filter
22
+ ID = ["gettext", "_", "N_", "sgettext", "s_"]
23
+ PLURAL_ID = ["ngettext", "n_", "Nn_", "ns_", "nsgettext"]
24
+ MSGCTXT_ID = ["pgettext", "p_"]
25
+ MSGCTXT_PLURAL_ID = ["npgettext", "np_"]
26
+
27
+ attr_accessor :use_comment
28
+ attr_accessor :comment_tag
24
29
  def initialize(*args)
25
- super
30
+ super(*args)
31
+ @start_block = false
32
+ @in_block_arguments = false
33
+ @ignore_next_comma = false
34
+ @need_definition_name = false
35
+ @current_po_entry = nil
36
+ @current_po_entry_nth_attribute = 0
37
+ @use_comment = false
38
+ @comment_tag = nil
39
+ @last_comment = ""
40
+ @reset_comment = false
26
41
  @string_mark_stack = []
42
+ @string_stack = []
27
43
  end
28
44
 
29
- def on_default(event, token, output)
30
- case event
31
- when :on_tstring_content
32
- if @string_mark_stack.last == "\""
33
- output << token.gsub(/\\./) do |data|
34
- case data
35
- when "\\n"
36
- "\n"
37
- when "\\t"
38
- "\t"
39
- when "\\\\"
40
- "\\"
41
- when "\\\""
42
- "\""
43
- when "\\\#"
44
- "#"
45
- else
46
- data
47
- end
48
- end
49
- else
50
- output << token.gsub(/\\./) do |data|
51
- case data
52
- when "\\\\"
53
- "\\"
54
- when "\\'"
55
- "'"
56
- else
57
- data
58
- end
59
- end
45
+ def process_on_op(token, po)
46
+ if @start_block
47
+ @in_block_arguments = (token == "|")
48
+ else
49
+ if @in_block_arguments and token == "|"
50
+ @in_block_arguments = false
60
51
  end
61
- when :on_tstring_beg
62
- unless @string_mark_stack.empty?
63
- output << token
52
+ end
53
+ po
54
+ end
55
+
56
+ def process_on_kw(token, po)
57
+ store_po_entry(po)
58
+ case token
59
+ when "do"
60
+ @start_block = true
61
+ end
62
+ po
63
+ end
64
+
65
+ def process_on_lbrace(token, po)
66
+ store_po_entry(po)
67
+ @start_block = (state == Ripper::EXPR_BEG)
68
+ po
69
+ end
70
+
71
+ def process_on_ident(token, po)
72
+ store_po_entry(po)
73
+
74
+ return po if @in_block_arguments
75
+ return po if state.allbits?(Ripper::EXPR_ENDFN)
76
+
77
+ case token
78
+ when *ID
79
+ @current_po_entry = POEntry.new(:normal)
80
+ when *PLURAL_ID
81
+ @current_po_entry = POEntry.new(:plural)
82
+ when *MSGCTXT_ID
83
+ @current_po_entry = POEntry.new(:msgctxt)
84
+ when *MSGCTXT_PLURAL_ID
85
+ @current_po_entry = POEntry.new(:msgctxt_plural)
86
+ end
87
+ if @current_po_entry
88
+ @current_po_entry.add_comment(@last_comment) unless @last_comment.empty?
89
+ @last_comment = ""
90
+ @current_po_entry.references << "#{filename}:#{lineno}"
91
+ @current_po_entry_nth_attribute = 0
92
+ end
93
+ po
94
+ end
95
+
96
+ def process_on_const(token, po)
97
+ case token
98
+ when "N_", "Nn_"
99
+ # TODO: Check the next token is :on_lparen
100
+ process_on_ident(token, po)
101
+ else
102
+ po
103
+ end
104
+ end
105
+
106
+ def process_on_comment(token, po)
107
+ @last_comment = "" if @reset_comment
108
+ @reset_comment = false
109
+ if @last_comment.empty?
110
+ content = token.gsub(/\A#\s*/, "").chomp
111
+ if comment_to_be_extracted?(content)
112
+ @last_comment << content
64
113
  end
114
+ else
115
+ content = token.gsub(/\A#/, "").chomp
116
+ @last_comment << "\n"
117
+ @last_comment << content
118
+ end
119
+ po
120
+ end
121
+
122
+ def process_on_sp(token, po)
123
+ po
124
+ end
125
+
126
+ def process_on_tstring_beg(token, po)
127
+ if token.start_with?("%Q")
128
+ @string_mark_stack << "\""
129
+ elsif token.start_with?("%q")
130
+ @string_mark_stack << "'"
131
+ elsif token.start_with?("%")
132
+ @string_mark_stack << "\""
133
+ else
65
134
  @string_mark_stack << token
66
- when :on_tstring_end
67
- @string_mark_stack.pop
68
- unless @string_mark_stack.empty?
69
- output << token
135
+ end
136
+ @string_stack << ""
137
+ po
138
+ end
139
+
140
+ def process_on_tstring_content(token, po)
141
+ case @string_mark_stack.last
142
+ when "\"", "`"
143
+ @string_stack.last << token.gsub(/\\./) do |data|
144
+ case data
145
+ when "\\n"
146
+ "\n"
147
+ when "\\t"
148
+ "\t"
149
+ when "\\\\"
150
+ "\\"
151
+ when "\\\""
152
+ "\""
153
+ when "\\\#"
154
+ "#"
155
+ else
156
+ data
157
+ end
70
158
  end
71
159
  else
72
- unless @string_mark_stack.empty?
73
- output << token.to_s
160
+ @string_stack.last << token.gsub(/\\./) do |data|
161
+ case data
162
+ when "\\\\"
163
+ "\\"
164
+ when "\\'"
165
+ "'"
166
+ else
167
+ data
168
+ end
74
169
  end
75
170
  end
76
- output
171
+ po
77
172
  end
78
- end
79
173
 
80
- # Parser#parse resemlbes RubyLex#lex
81
- def parse
82
- until ( (tk = token).kind_of?(RubyToken::TkEND_OF_SCRIPT) && !@continue or tk.nil? )
83
- s = get_readed
84
- if RubyToken::TkSTRING === tk or RubyToken::TkDSTRING === tk
85
- def tk.value
86
- @value
174
+ def process_on_tstring_end(token, po)
175
+ @ignore_next_comma = false
176
+ string_mark = @string_mark_stack.pop
177
+ case string_mark
178
+ when "\"", "'"
179
+ last_string = @string_stack.pop
180
+ if @current_po_entry and last_string
181
+ @current_po_entry[@current_po_entry_nth_attribute] =
182
+ (@current_po_entry[@current_po_entry_nth_attribute] || "") +
183
+ last_string
87
184
  end
185
+ end
186
+ po
187
+ end
88
188
 
89
- def tk.value=(s)
90
- @value = s
91
- end
189
+ def process_on_heredoc_beg(token, po)
190
+ if token.end_with?("'")
191
+ @string_mark_stack << "'"
192
+ else
193
+ @string_mark_stack << "\""
194
+ end
195
+ @string_stack << ""
196
+ po
197
+ end
92
198
 
93
- if @here_header
94
- s = s.sub(/\A.*?\n/, "").sub(/^.*\n\Z/, "")
95
- else
96
- s = StringExtractor.new(s).parse("")
97
- end
199
+ def process_on_heredoc_end(token, po)
200
+ process_on_tstring_end(token, po)
201
+ end
98
202
 
99
- tk.value = s
100
- end
203
+ def process_on_regexp_beg(token, po)
204
+ @string_mark_stack << "\""
205
+ @string_stack << ""
206
+ po
207
+ end
208
+
209
+ def process_on_regexp_end(token, po)
210
+ @string_mark_stack.pop
211
+ @string_stack.pop
212
+ po
213
+ end
101
214
 
102
- if $DEBUG
103
- if tk.is_a? TkSTRING or tk.is_a? TkDSTRING
104
- $stderr.puts("#{tk}: #{tk.value}")
105
- elsif tk.is_a? TkIDENTIFIER
106
- $stderr.puts("#{tk}: #{tk.name}")
107
- else
108
- $stderr.puts(tk)
215
+ def process_on_embexpr_beg(token, po)
216
+ @current_po_entry = nil
217
+ @current_po_entry_nth_attribute = 0
218
+ po
219
+ end
220
+
221
+ def process_on_int(token, po)
222
+ @ignore_next_comma = true
223
+ po
224
+ end
225
+
226
+ def process_on_comma(token, po)
227
+ unless @ignore_next_comma
228
+ if @current_po_entry
229
+ @current_po_entry_nth_attribute += 1
109
230
  end
110
231
  end
232
+ po
233
+ end
111
234
 
112
- yield tk
235
+ def process_on_rparen(token, po)
236
+ store_po_entry(po)
237
+ po
238
+ end
239
+
240
+ def process_on_nl(token, po)
241
+ @reset_comment = true
242
+ po
243
+ end
244
+
245
+ def process_on_symbeg(token, po)
246
+ if token.start_with?("%s") or [":'", ":\""].include?(token)
247
+ @string_mark_stack << ":"
248
+ @string_stack << ""
249
+ end
250
+ po
251
+ end
252
+
253
+ def process_on_backtick(token, po)
254
+ @string_mark_stack << "`"
255
+ @string_stack << ""
256
+ po
113
257
  end
114
- return nil
115
- end
116
258
 
117
- # Original parser does not keep the content of the comments,
118
- # so monkey patching this with new token type and extended
119
- # identify_comment implementation
120
- RubyToken.def_token :TkCOMMENT_WITH_CONTENT, TkVal
259
+ def process_on_symbols_beg(token, po)
260
+ @string_mark_stack << "\""
261
+ @string_stack << ""
262
+ po
263
+ end
121
264
 
122
- def identify_comment
123
- @ltype = "#"
124
- get_readed # skip the hash sign itself
265
+ def process_on_qsymbols_beg(token, po)
266
+ @string_mark_stack << token
267
+ @string_stack << ""
268
+ po
269
+ end
270
+
271
+ def process_on_words_beg(token, po)
272
+ @string_mark_stack << "\""
273
+ @string_stack << ""
274
+ po
275
+ end
125
276
 
126
- while ch = getc
127
- if ch == "\n"
128
- @ltype = nil
129
- ungetc
130
- break
277
+ def process_on_qwords_beg(token, po)
278
+ @string_mark_stack << token
279
+ @string_stack << ""
280
+ po
281
+ end
282
+
283
+ def on_default(event, token, po)
284
+ trace(event, token) do
285
+ process_method = "process_#{event}"
286
+ start_block = @start_block
287
+ if respond_to?(process_method)
288
+ po = __send__(process_method, token, po)
289
+ end
290
+ if start_block and event != :on_sp
291
+ @start_block = false
292
+ end
293
+ po
131
294
  end
132
295
  end
133
- return Token(TkCOMMENT_WITH_CONTENT, get_readed)
134
- end
135
296
 
136
- end
297
+ private
298
+ @@debug = ENV["GETTEXT_RUBY_PARSER_DEBUG"]
299
+ def debug?
300
+ @@debug
301
+ end
137
302
 
138
- # Extends POEntry for RubyParser.
139
- # Implements a sort of state machine to assist the parser.
140
- module POEntryForRubyParser
141
- # Supports parsing by setting attributes by and by.
142
- def set_current_attribute(str)
143
- param = @param_type[@param_number]
144
- raise ParseError, "no more string parameters expected" unless param
145
- set_value(param, str)
146
- end
303
+ def trace(event_name, token)
304
+ if debug?
305
+ status = [
306
+ event_name,
307
+ token,
308
+ state,
309
+ ]
310
+ status << :start_block if @start_block
311
+ status << :in_block_arguments if @in_block_arguments
312
+ pp status
313
+ end
314
+ yield
315
+ end
147
316
 
148
- def init_param
149
- @param_number = 0
150
- self
151
- end
317
+ def store_po_entry(po)
318
+ return if @current_po_entry.nil?
319
+ po << @current_po_entry if @current_po_entry.msgid
320
+ @current_po_entry = nil
321
+ @current_po_entry_nth_attribute = 0
322
+ end
152
323
 
153
- def advance_to_next_attribute
154
- @param_number += 1
155
- end
156
- end
157
- class POEntry
158
- include POEntryForRubyParser
159
- alias :initialize_old :initialize
160
- def initialize(type)
161
- initialize_old(type)
162
- init_param
163
- end
164
- end
324
+ def comment_to_be_extracted?(comment)
325
+ return false unless @use_comment
165
326
 
166
- class RubyParser
167
- ID = ["gettext", "_", "N_", "sgettext", "s_"]
168
- PLURAL_ID = ["ngettext", "n_", "Nn_", "ns_", "nsgettext"]
169
- MSGCTXT_ID = ["pgettext", "p_"]
170
- MSGCTXT_PLURAL_ID = ["npgettext", "np_"]
327
+ return true if @comment_tag.nil?
328
+
329
+ comment.start_with?(@comment_tag)
330
+ end
331
+ end
171
332
 
172
333
  class << self
173
334
  def target?(file) # :nodoc:
@@ -282,106 +443,12 @@ module GetText
282
443
  end
283
444
 
284
445
  def parse_source(source)
285
- po = []
286
- file = StringIO.new(source)
287
- rl = RubyLexX.new
288
- rl.set_input(file)
289
- rl.skip_space = true
290
- #rl.readed_auto_clean_up = true
291
-
292
- po_entry = nil
293
- line_no = nil
294
- last_comment = ""
295
- reset_comment = false
296
- ignore_next_comma = false
297
- rl.parse do |tk|
298
- begin
299
- ignore_current_comma = ignore_next_comma
300
- ignore_next_comma = false
301
- case tk
302
- when RubyToken::TkIDENTIFIER, RubyToken::TkCONSTANT
303
- if store_po_entry(po, po_entry, line_no, last_comment)
304
- last_comment = ""
305
- end
306
- if ID.include?(tk.name)
307
- po_entry = POEntry.new(:normal)
308
- elsif PLURAL_ID.include?(tk.name)
309
- po_entry = POEntry.new(:plural)
310
- elsif MSGCTXT_ID.include?(tk.name)
311
- po_entry = POEntry.new(:msgctxt)
312
- elsif MSGCTXT_PLURAL_ID.include?(tk.name)
313
- po_entry = POEntry.new(:msgctxt_plural)
314
- else
315
- po_entry = nil
316
- end
317
- line_no = tk.line_no.to_s
318
- when RubyToken::TkBITOR
319
- po_entry = nil
320
- when RubyToken::TkSTRING, RubyToken::TkDSTRING
321
- po_entry.set_current_attribute tk.value if po_entry
322
- when RubyToken::TkPLUS, RubyToken::TkNL
323
- #do nothing
324
- when RubyToken::TkINTEGER
325
- ignore_next_comma = true
326
- when RubyToken::TkCOMMA
327
- unless ignore_current_comma
328
- po_entry.advance_to_next_attribute if po_entry
329
- end
330
- else
331
- if store_po_entry(po, po_entry, line_no, last_comment)
332
- po_entry = nil
333
- last_comment = ""
334
- end
335
- end
336
- rescue
337
- $stderr.print "\n\nError"
338
- $stderr.print " parsing #{@path}:#{tk.line_no}\n\t #{source.lines.to_a[tk.line_no - 1]}" if tk
339
- $stderr.print "\n #{$!.inspect} in\n"
340
- $stderr.print $!.backtrace.join("\n")
341
- $stderr.print "\n"
342
- exit 1
343
- end
344
-
345
- case tk
346
- when RubyToken::TkCOMMENT_WITH_CONTENT
347
- last_comment = "" if reset_comment
348
- if last_comment.empty?
349
- comment1 = tk.value.lstrip
350
- if comment_to_be_extracted?(comment1)
351
- last_comment += comment1
352
- end
353
- else
354
- last_comment += "\n"
355
- last_comment += tk.value
356
- end
357
- reset_comment = false
358
- when RubyToken::TkNL
359
- else
360
- reset_comment = true
361
- end
446
+ extractor = POExtractor.new(source, @path)
447
+ if @options.key?(:comment_tag)
448
+ extractor.use_comment = true
449
+ extractor.comment_tag = @options[:comment_tag]
362
450
  end
363
- po
364
- end
365
-
366
- private
367
- def store_po_entry(po, po_entry, line_no, last_comment) #:nodoc:
368
- if po_entry && po_entry.msgid
369
- po_entry.references << @path + ":" + line_no
370
- po_entry.add_comment(last_comment) unless last_comment.empty?
371
- po << po_entry
372
- true
373
- else
374
- false
375
- end
376
- end
377
-
378
- def comment_to_be_extracted?(comment)
379
- return false unless @options.has_key?(:comment_tag)
380
-
381
- tag = @options[:comment_tag]
382
- return true if tag.nil?
383
-
384
- /\A#{Regexp.escape(tag)}/ === comment
451
+ extractor.parse([])
385
452
  end
386
453
  end
387
454
  end