gettext_i18n_rails_js 0.0.9 → 1.0.0

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.
@@ -0,0 +1,100 @@
1
+ # -*- coding: UTF-8 -*-
2
+ #
3
+ # Copyright (c) 2012-2015 Dropmysite.com <https://dropmyemail.com>
4
+ # Copyright (c) 2015 Webhippie <http://www.webhippie.de>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #
25
+
26
+ require "gettext/tools/xgettext"
27
+ require "gettext_i18n_rails/gettext_hooks"
28
+ require_relative "base"
29
+
30
+ module GettextI18nRailsJs
31
+ module Parser
32
+ module Javascript
33
+ include Base
34
+ extend self
35
+
36
+ def target?(file)
37
+ [
38
+ ".js",
39
+ ".coffee"
40
+ ].include? ::File.extname(file)
41
+ end
42
+
43
+ protected
44
+
45
+ def collect_for(value)
46
+ ::File.new(
47
+ value
48
+ ).each_line.each_with_index.collect do |line, idx|
49
+ line.scan(invoke_regex).collect do |function, arguments|
50
+ yield(function, arguments, idx + 1)
51
+ end
52
+ end.inject(:+).compact
53
+ end
54
+
55
+ def invoke_regex
56
+ #
57
+ # * Matches the function call grouping the method used (__, n__, N__)
58
+ # * A parenthesis to start the arguments to the function.
59
+ # * There may be many arguments to the same function call
60
+ # * Then the last, or only argument to the function call.
61
+ # * Function call closing parenthesis
62
+ #
63
+
64
+ /
65
+ (\b[snN]?#{gettext_function})
66
+ \(
67
+ (
68
+ (#{arg_regex},)*
69
+ #{arg_regex}
70
+ )?
71
+ \)
72
+ /x
73
+ end
74
+
75
+ def arg_regex
76
+ #
77
+ # * Some whitespace
78
+ # * A token inside the argument list, like a single quoted string
79
+ # * Double quote string, both support escapes
80
+ # * A number, variable name, or called function lik: 33, foo, Foo.bar()
81
+ # * More whitespace
82
+ #
83
+
84
+ /
85
+ \s*
86
+ (
87
+ '(?:[^'\\]|\\.)*?'|
88
+ "(?:[^"\\]|\\.)*?"|
89
+ [a-zA-Z0-9_\.()]*?
90
+ )
91
+ \s*
92
+ /x
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+ GettextI18nRails::GettextHooks.add_parser(
99
+ GettextI18nRailsJs::Parser::Javascript
100
+ )
@@ -1,3 +1,40 @@
1
+ # -*- coding: UTF-8 -*-
2
+ #
3
+ # Copyright (c) 2012-2015 Dropmysite.com <https://dropmyemail.com>
4
+ # Copyright (c) 2015 Webhippie <http://www.webhippie.de>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #
25
+
1
26
  module GettextI18nRailsJs
2
- VERSION = "0.0.9"
27
+ class Version
28
+ MAJOR = 1
29
+ MINOR = 0
30
+ PATCH = 0
31
+
32
+ PRE = nil
33
+
34
+ class << self
35
+ def to_s
36
+ [MAJOR, MINOR, PATCH, PRE].compact.join(".")
37
+ end
38
+ end
39
+ end
3
40
  end
@@ -1,7 +1,153 @@
1
- if RUBY_PLATFORM != 'java'
2
- dir = File.dirname(__FILE__)
3
- require File.join(dir, '../gettext_i18n_rails_js/js_and_coffee_parser')
4
- require File.join(dir, '../gettext_i18n_rails_js/handlebars_parser')
5
- require 'gettext_i18n_rails/tasks'
6
- require File.join(dir, '../gettext_i18n_rails_js/tasks')
1
+ # -*- coding: UTF-8 -*-
2
+ #
3
+ # Copyright (c) 2012-2015 Dropmysite.com <https://dropmyemail.com>
4
+ # Copyright (c) 2015 Webhippie <http://www.webhippie.de>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #
25
+
26
+ require "gettext_i18n_rails/tasks"
27
+
28
+ namespace :gettext do
29
+ desc "Convert PO files to JS files"
30
+ task po_to_json: :environment do
31
+ GettextI18nRailsJs::Parser::Javascript
32
+ .gettext_function = config[:javascript_function]
33
+
34
+ GettextI18nRailsJs::Parser::Handlebars
35
+ .gettext_function = config[:handlebars_function]
36
+
37
+ if files_list.empty?
38
+ puts "Couldn't find PO files in #{locale_path}, run 'rake gettext:find'"
39
+ else
40
+ files_list.each do |input|
41
+ # Language is used for filenames, while language code is used as the
42
+ # in-app language code. So for instance, simplified chinese will live
43
+ # in app/assets/locale/zh_CN/app.js but inside the file the language
44
+ # will be referred to as locales['zh-CN']. This is to adapt to the
45
+ # existing gettext_rails convention.
46
+
47
+ language = input.dirname.basename.to_s
48
+ language_code = language.gsub("_", "-")
49
+
50
+ destination = output_path.join(language)
51
+ destination.mkpath
52
+
53
+ json = PoToJson.new(
54
+ input.to_s
55
+ ).generate_for_jed(
56
+ language_code,
57
+ config[:jed_options].symbolize_keys
58
+ )
59
+
60
+ destination.join("app.js").open("w") do |f|
61
+ f.rewind
62
+ f.write(json)
63
+ end
64
+
65
+ puts "Created app.js in #{destination}"
66
+ end
67
+
68
+ puts
69
+ puts "All files created, make sure they are being added to your assets."
70
+ puts "If they are not, you can add them with this line (configurable):"
71
+ puts
72
+ puts "//= require_tree ./locale"
73
+ puts "//= require gettext/all"
74
+ puts
75
+ end
76
+ end
77
+
78
+ def files_list
79
+ Pathname.glob(
80
+ ::File.join(
81
+ locale_path,
82
+ "**",
83
+ "*.po"
84
+ )
85
+ )
86
+ end
87
+
88
+ def output_path
89
+ Rails.root.join(
90
+ config[:output_path]
91
+ )
92
+ end
93
+
94
+ def config
95
+ @config ||= begin
96
+ file = Rails.root.join(
97
+ "config",
98
+ "gettext_i18n_rails_js.yml"
99
+ )
100
+
101
+ defaults = {
102
+ output_path: File.join(
103
+ "app",
104
+ "assets",
105
+ "javascripts",
106
+ "locale"
107
+ ),
108
+ handlebars_function: "__",
109
+ javascript_function: "__",
110
+ jed_options: {
111
+ pretty: false
112
+ }
113
+ }
114
+
115
+ if file.exist?
116
+ yaml = YAML.load_file(file) || {}
117
+
118
+ defaults.deep_merge(
119
+ yaml
120
+ ).with_indifferent_access
121
+ else
122
+ defaults.with_indifferent_access
123
+ end
124
+ end
125
+ end
126
+
127
+ # Required for gettext to filter the files
128
+ def files_to_translate
129
+ folders = [
130
+ "app",
131
+ "lib",
132
+ "config",
133
+ locale_path
134
+ ].join(",")
135
+
136
+ exts = [
137
+ "rb",
138
+ "erb",
139
+ "haml",
140
+ "slim",
141
+ "rhtml",
142
+ "js",
143
+ "coffee",
144
+ "handlebars",
145
+ "hbs",
146
+ "mustache"
147
+ ].join(",")
148
+
149
+ Dir.glob(
150
+ "{#{folders}}/**/*.{#{exts}}"
151
+ )
152
+ end
7
153
  end
@@ -0,0 +1,15 @@
1
+ pepito = ->
2
+ string = "this" + __('json') + 'should be translated'
3
+ alert(n__('item', 'items', 3))
4
+
5
+ example_template( __('Hello {yourname}'), { yourname: 'bob' })
6
+
7
+ object =
8
+ one: N__('new-trans')
9
+ two: s__('namespaced', 'trans')
10
+
11
+ "#{ __("Hello\nBuddy") }"
12
+
13
+ """
14
+ #{ __("Multi-line") }
15
+ """
@@ -0,0 +1,11 @@
1
+ <div class="content-main">
2
+ <div class="content-header">
3
+ <label>{{__ "Locale"}}</label>
4
+ <h1>{{__ "Profile"}}</h1>
5
+ <div class="btn-toolbar pull-right">
6
+ <div class="btn-group">
7
+ <a href="#" {{action "updateUser" target="controller"}} class="btn btn-success">{{__ "Update" "Updates" 2}}</a>
8
+ </div>
9
+ </div>
10
+ </div>
11
+ </div>
@@ -0,0 +1,12 @@
1
+ function pepito() {
2
+ var string = "this" + __('json') + 'should be translated';
3
+ alert(n__('item', 'items', 3));
4
+ }
5
+
6
+ example_template(__('Hello {yourname}'), { yourname: 'bob' });
7
+
8
+ object = {
9
+ one: N__('new-trans'),
10
+ two: s__('namespaced', 'trans')
11
+ three: __("Hello\nBuddy")
12
+ }
@@ -0,0 +1,301 @@
1
+ # -*- coding: UTF-8 -*-
2
+ #
3
+ # Copyright (c) 2012-2015 Dropmysite.com <https://dropmyemail.com>
4
+ # Copyright (c) 2015 Webhippie <http://www.webhippie.de>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #
25
+
26
+ require "spec_helper"
27
+
28
+ describe GettextI18nRailsJs::Parser::Handlebars do
29
+ let(:parser) { GettextI18nRailsJs::Parser::Handlebars }
30
+
31
+ describe "#target?" do
32
+ it "targets .handlebars" do
33
+ expect(parser.target?("foo/bar/xxx.handlebars")).to be_truthy
34
+ end
35
+
36
+ it "targets .handlebars.erb" do
37
+ expect(parser.target?("foo/bar/xxx.handlebars.erb")).to be_truthy
38
+ end
39
+ it "targets .hbs" do
40
+ expect(parser.target?("foo/bar/xxx.hbs")).to be_truthy
41
+ end
42
+
43
+ it "targets .hbs.erb" do
44
+ expect(parser.target?("foo/bar/xxx.hbs.erb")).to be_truthy
45
+ end
46
+
47
+ it "targets .mustache" do
48
+ expect(parser.target?("foo/bar/xxx.mustache")).to be_truthy
49
+ end
50
+
51
+ it "targets .mustache.erb" do
52
+ expect(parser.target?("foo/bar/xxx.mustache.erb")).to be_truthy
53
+ end
54
+
55
+ it "does not target cows" do
56
+ expect(parser.target?("foo/bar/xxx.cows")).to be_falsey
57
+ end
58
+ end
59
+
60
+ describe "#parse" do
61
+ it "finds plural messages" do
62
+ content = <<-EOF
63
+ <div>{{n__ "xxxx" "yyyy\" "zzzz" some_count}}</div>
64
+ EOF
65
+
66
+ with_file content do |path|
67
+ expect(parser.parse(path, [])).to(
68
+ eq(
69
+ [
70
+ ["xxxx\000yyyy\000zzzz", "#{path}:1"]
71
+ ]
72
+ )
73
+ )
74
+ end
75
+ end
76
+
77
+ it "finds namespaced messages" do
78
+ content = <<-EOF
79
+ <div>{{__ "xxxx", "yyyy"}}</div>
80
+ EOF
81
+
82
+ with_file content do |path|
83
+ expect(parser.parse(path, [])).to(
84
+ eq(
85
+ [
86
+ ["xxxx\004yyyy", "#{path}:1"]
87
+ ]
88
+ )
89
+ )
90
+ end
91
+ end
92
+
93
+ it "finds simple messages" do
94
+ content = <<-EOF
95
+ <div>{{__ "blah"}}</div>
96
+ EOF
97
+
98
+ with_file content do |path|
99
+ expect(parser.parse(path, [])).to(
100
+ eq(
101
+ [
102
+ ["blah", "#{path}:1"]
103
+ ]
104
+ )
105
+ )
106
+ end
107
+ end
108
+
109
+ # it "finds messages with newlines/tabs" do
110
+ # content = <<-EOF
111
+ # bla = __("xxxx\n\tfoo")
112
+ # EOF
113
+
114
+ # with_file content do |path|
115
+ # expect(parser.parse(path, [])).to(
116
+ # eq(
117
+ # [
118
+ # ["xxxx\n\tfoo", "#{path}:1"]
119
+ # ]
120
+ # )
121
+ # )
122
+ # end
123
+ # end
124
+
125
+ # it "finds messages with newlines/tabs (single quotes)" do
126
+ # content = <<-EOF
127
+ # bla = __('xxxx\n\tfoo')
128
+ # EOF
129
+
130
+ # with_file content do |path|
131
+ # parser.parse(path, []).should == [
132
+ # ["xxxx\n\tfoo", "#{path}:1"]
133
+ # ]
134
+ # end
135
+ # end
136
+
137
+ # it "finds interpolated multi-line messages" do
138
+ # content = <<-EOF
139
+ # """ Parser should grab
140
+ # #{ __("This") } __("known bug")
141
+ # """
142
+ # EOF
143
+
144
+ # with_file content do |path|
145
+ # expect(parser.parse(path, [])).to(
146
+ # eq(
147
+ # [
148
+ # ["This", "#{path}:3"],
149
+ # ["known bug", "#{path}:3"]
150
+ # ]
151
+ # )
152
+ # )
153
+ # end
154
+ # end
155
+
156
+ # it "finds strings that use some templating" do
157
+ # content = <<-EOF
158
+ # __("hello {yourname}")
159
+ # EOF
160
+
161
+ # with_file content do |path|
162
+ # expect(parser.parse(path, [])).to(
163
+ # eq(
164
+ # [
165
+ # ["hello {yourname}", "#{path}:1"]
166
+ # ]
167
+ # )
168
+ # )
169
+ # end
170
+ # end
171
+
172
+ # it "finds strings that use escaped strings" do
173
+ # content = <<-EOF
174
+ # __("hello \"dude\"") + __('how is it \'going\' ')
175
+ # EOF
176
+
177
+ # with_file content do |path|
178
+ # expect(parser.parse(path, [])).to(
179
+ # eq(
180
+ # [
181
+ # ["hello \"dude\"", "#{path}:1"],
182
+ # ["how is it 'going' ", "#{path}:1"]
183
+ # ]
184
+ # )
185
+ # )
186
+ # end
187
+ # end
188
+
189
+ # it "does not capture a false positive" do
190
+ # content = <<-EOF
191
+ # bla = this_should_not_be_registered__("xxxx", "yyyy")
192
+ # EOF
193
+
194
+ # with_file content do |path|
195
+ # expect(parser.parse(path, [])).to(
196
+ # eq(
197
+ # []
198
+ # )
199
+ # )
200
+ # end
201
+ # end
202
+
203
+ it "does not find nonstring messages" do
204
+ content = <<-EOF
205
+ <div>{{__ bar}}</div>
206
+ EOF
207
+
208
+ with_file content do |path|
209
+ expect(parser.parse(path, [])).to(
210
+ eq(
211
+ []
212
+ )
213
+ )
214
+ end
215
+ end
216
+
217
+ it "does not parse internal parentheses" do
218
+ content = <<-EOF
219
+ <div>
220
+ {{__ "text (which is great) and parentheses()"}}
221
+ {{__ "foobar"}}
222
+ </div>
223
+ EOF
224
+
225
+ with_file content do |path|
226
+ expect(parser.parse(path, [])).to(
227
+ eq(
228
+ [
229
+ ["text (which is great) and parentheses()", "#{path}:1"],
230
+ ["foobar", "#{path}:1"]
231
+ ]
232
+ )
233
+ )
234
+ end
235
+ end
236
+
237
+ # it "does not parse internal functions" do
238
+ # content = <<-EOF
239
+ # bla = n__("items (single)", "i (more)", item.count()) + __('foobar')
240
+ # EOF
241
+
242
+ # with_file content do |path|
243
+ # parser.parse(path, []).should == [
244
+ # ["items (single)\000i (more)", "#{path}:1"],
245
+ # ['foobar', "#{path}:1"]
246
+ # ]
247
+ # end
248
+ # end
249
+ end
250
+
251
+ describe "parses handlebars files" do
252
+ let(:example) do
253
+ File.expand_path(
254
+ "../../../fixtures/example.handlebars",
255
+ __FILE__
256
+ )
257
+ end
258
+
259
+ let(:parsed_example) do
260
+ parser.parse(example, [])
261
+ end
262
+
263
+ it "parses all translations" do
264
+ expect(parsed_example.collect(&:first)).to(
265
+ eq(["Locale", "Profile", "Update\004Updates"])
266
+ )
267
+ end
268
+
269
+ it "parses the file paths" do
270
+ parsed_example.collect(&:last).each do |path|
271
+ expect(path).to end_with("fixtures/example.handlebars:1")
272
+ end
273
+ end
274
+
275
+ it "accepts changing the translate method" do
276
+ parser.gettext_function = "gettext"
277
+
278
+ content = <<-EOF
279
+ <div>
280
+ {{gettext \"Hello {yourname}\"}}
281
+ <span>
282
+ {{ngettext \"item\", \"items\", 44}}
283
+ </span>
284
+ </div>
285
+ EOF
286
+
287
+ with_file content do |path|
288
+ expect(parser.parse(path, [])).to(
289
+ eq(
290
+ [
291
+ ["Hello {yourname}", "#{path}:1"],
292
+ ["item\000items", "#{path}:1"]
293
+ ]
294
+ )
295
+ )
296
+ end
297
+
298
+ parser.gettext_function = "__"
299
+ end
300
+ end
301
+ end