i15r 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/CHANGELOG +5 -0
- data/MIT-LICENSE +20 -0
- data/Manifest +12 -0
- data/README.markdown +55 -0
- data/Rakefile +20 -0
- data/VERSION +1 -0
- data/bin/i15r +11 -0
- data/i15r.gemspec +71 -0
- data/init.rb +4 -0
- data/lib/i15r.rb +4 -0
- data/lib/i15r/base.rb +143 -0
- data/lib/i15r/pattern_matcher.rb +4 -0
- data/lib/i15r/pattern_matchers/base.rb +35 -0
- data/lib/i15r/pattern_matchers/rails_helper_matcher.rb +69 -0
- data/lib/i15r/pattern_matchers/tag_attribute_matcher.rb +21 -0
- data/lib/i15r/pattern_matchers/tag_content_matcher.rb +20 -0
- data/spec/i15r_spec.rb +130 -0
- data/spec/pattern_matcher_spec.rb +8 -0
- data/spec/rails_helper_matcher_spec.rb +45 -0
- data/spec/spec.opts +4 -0
- data/spec/tag_attribute_matcher_spec.rb +15 -0
- data/spec/tag_content_matcher_spec.rb +38 -0
- data/tasks/i15r.rake +8 -0
- data/todos.markdown +36 -0
- metadata +83 -0
data/.gitignore
ADDED
data/CHANGELOG
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Balint Erdi (balint.erdi@gmail.com)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# I15r
|
2
|
+
|
3
|
+
## Summary
|
4
|
+
|
5
|
+
I15r (Internationalizer) searches for all the non-i18n texts in erb views in the given files/directory and replaces them with I18n messages. The message string is based on the file in which the text was found and the text itself that was replaced.
|
6
|
+
|
7
|
+
E.g
|
8
|
+
|
9
|
+
(in file app/views/users/new.html.erb)
|
10
|
+
<label for="user-name">Name</label>
|
11
|
+
<input type="text" id="user-name" name="user[name]" />
|
12
|
+
|
13
|
+
will be replaced by:
|
14
|
+
|
15
|
+
(in file app/views/users/new.html.erb)
|
16
|
+
<label for="user-name"><%= I18n.t("users.new.name") %></label>
|
17
|
+
<input type="text" id="user-name" name="user[name]" />
|
18
|
+
|
19
|
+
and
|
20
|
+
|
21
|
+
(in file app/views/member/users/edit.html.erb)
|
22
|
+
<label for="user-name">Name</label>
|
23
|
+
<input type="text" id="user-name" name="user[name]" />
|
24
|
+
|
25
|
+
will be replaced by
|
26
|
+
|
27
|
+
(in file app/views/member/users/edit.html.erb)
|
28
|
+
<label for="user-name"><%= I18n.t("member.users.edit.name") %></label>
|
29
|
+
<input type="text" id="user-name" name="user[name]" />
|
30
|
+
|
31
|
+
## Installation
|
32
|
+
|
33
|
+
gem install i15r --source http://gemcutter.org
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
### Convert a single file
|
38
|
+
|
39
|
+
i15r path/leading/to/view
|
40
|
+
|
41
|
+
### Convert all files in a directory (deep search)
|
42
|
+
|
43
|
+
i15r path/leading/to/directory
|
44
|
+
|
45
|
+
All files with an erb suffix will be converted.
|
46
|
+
|
47
|
+
### Dry run
|
48
|
+
|
49
|
+
By default, i15r overwrites all the source files with the i18n message strings it generates. If you first want to see what would be replaced, you should do:
|
50
|
+
|
51
|
+
i15r app/views/users -p
|
52
|
+
|
53
|
+
## Licensing, contribution
|
54
|
+
|
55
|
+
The source code of this gem can be found at [http://github.com/balinterdi/i15r/](http://github.com/balinterdi/i15r/). It is released under the MIT-LICENSE, so you can basically do anything with it. However, if you think your modifications only make the tool better, please send a pull request or patch and I will consider merging in your changes. Any suggestions or feedback are welcome to <balint@bucionrails.com>.
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'jeweler'
|
5
|
+
Jeweler::Tasks.new do |gemspec|
|
6
|
+
gemspec.name = "i15r"
|
7
|
+
gemspec.summary = "The internationalizer. Makes your Ruby app international"
|
8
|
+
gemspec.description = <<-EOF
|
9
|
+
The internationalizer. Replaces plain text strings in your views and replaces them with I18n message strings so you only have to provide the translations.
|
10
|
+
EOF
|
11
|
+
gemspec.email = "balint.erdi@gmail.com"
|
12
|
+
gemspec.homepage = "http://github.com/balinterdi/i15r"
|
13
|
+
gemspec.authors = ["Balint Erdi"]
|
14
|
+
end
|
15
|
+
Jeweler::GemcutterTasks.new
|
16
|
+
rescue LoadError
|
17
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
18
|
+
end
|
19
|
+
|
20
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.3.0
|
data/bin/i15r
ADDED
data/i15r.gemspec
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{i15r}
|
8
|
+
s.version = "0.3.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Balint Erdi"]
|
12
|
+
s.date = %q{2009-12-08}
|
13
|
+
s.default_executable = %q{i15r}
|
14
|
+
s.description = %q{ The internationalizer. Replaces plain text strings in your views and replaces them with I18n message strings so you only have to provide the translations.
|
15
|
+
}
|
16
|
+
s.email = %q{balint.erdi@gmail.com}
|
17
|
+
s.executables = ["i15r"]
|
18
|
+
s.extra_rdoc_files = [
|
19
|
+
"README.markdown"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".gitignore",
|
23
|
+
"CHANGELOG",
|
24
|
+
"MIT-LICENSE",
|
25
|
+
"Manifest",
|
26
|
+
"README.markdown",
|
27
|
+
"Rakefile",
|
28
|
+
"VERSION",
|
29
|
+
"bin/i15r",
|
30
|
+
"i15r.gemspec",
|
31
|
+
"init.rb",
|
32
|
+
"lib/i15r.rb",
|
33
|
+
"lib/i15r/base.rb",
|
34
|
+
"lib/i15r/pattern_matcher.rb",
|
35
|
+
"lib/i15r/pattern_matchers/base.rb",
|
36
|
+
"lib/i15r/pattern_matchers/rails_helper_matcher.rb",
|
37
|
+
"lib/i15r/pattern_matchers/tag_attribute_matcher.rb",
|
38
|
+
"lib/i15r/pattern_matchers/tag_content_matcher.rb",
|
39
|
+
"spec/i15r_spec.rb",
|
40
|
+
"spec/pattern_matcher_spec.rb",
|
41
|
+
"spec/rails_helper_matcher_spec.rb",
|
42
|
+
"spec/spec.opts",
|
43
|
+
"spec/tag_attribute_matcher_spec.rb",
|
44
|
+
"spec/tag_content_matcher_spec.rb",
|
45
|
+
"tasks/i15r.rake",
|
46
|
+
"todos.markdown"
|
47
|
+
]
|
48
|
+
s.homepage = %q{http://github.com/balinterdi/i15r}
|
49
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
50
|
+
s.require_paths = ["lib"]
|
51
|
+
s.rubygems_version = %q{1.3.5}
|
52
|
+
s.summary = %q{The internationalizer. Makes your Ruby app international}
|
53
|
+
s.test_files = [
|
54
|
+
"spec/i15r_spec.rb",
|
55
|
+
"spec/pattern_matcher_spec.rb",
|
56
|
+
"spec/rails_helper_matcher_spec.rb",
|
57
|
+
"spec/tag_attribute_matcher_spec.rb",
|
58
|
+
"spec/tag_content_matcher_spec.rb"
|
59
|
+
]
|
60
|
+
|
61
|
+
if s.respond_to? :specification_version then
|
62
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
63
|
+
s.specification_version = 3
|
64
|
+
|
65
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
66
|
+
else
|
67
|
+
end
|
68
|
+
else
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
data/init.rb
ADDED
data/lib/i15r.rb
ADDED
data/lib/i15r/base.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
require "optparse"
|
2
|
+
require "ostruct"
|
3
|
+
|
4
|
+
class AppFolderNotFound < Exception; end
|
5
|
+
|
6
|
+
module I15R
|
7
|
+
class Base
|
8
|
+
attr_reader :options
|
9
|
+
|
10
|
+
def self.get_i18n_message_string(text, prefix)
|
11
|
+
key = text.strip.downcase.gsub(/\s/, '_').gsub(/[\W]/, '')
|
12
|
+
indent = ""
|
13
|
+
(0..prefix.split(".").size).each { |i| indent = " " + indent }
|
14
|
+
# silenced_if_testing do
|
15
|
+
# puts "#{indent}#{key}: #{text}"
|
16
|
+
# end
|
17
|
+
"#{prefix}.#{key}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@options = OpenStruct.new
|
22
|
+
@options.prefix = nil
|
23
|
+
@options
|
24
|
+
end
|
25
|
+
|
26
|
+
def parse_options(args)
|
27
|
+
opts = OptionParser.new do |opts|
|
28
|
+
opts.banner = "Usage: ruby i15r.rb [options] <path_to_internationalize>"
|
29
|
+
opts.on("--prefix PREFIX",
|
30
|
+
"apply PREFIX to generated I18n messages instead of deriving it from the path") do |prefix|
|
31
|
+
@options.prefix = prefix
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
if args.length.zero?
|
36
|
+
puts opts.banner
|
37
|
+
exit
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on_tail("-p", "--pretend", "Do not write the files, just show what would be replaced") do
|
41
|
+
@options.dry_run = true
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
45
|
+
puts opts
|
46
|
+
exit
|
47
|
+
end
|
48
|
+
|
49
|
+
opts.on_tail("--version", "Show version") do
|
50
|
+
puts "0.0.1"
|
51
|
+
exit
|
52
|
+
end
|
53
|
+
|
54
|
+
opts.parse!(args)
|
55
|
+
# @options
|
56
|
+
end
|
57
|
+
|
58
|
+
def prefix
|
59
|
+
@options.prefix
|
60
|
+
end
|
61
|
+
|
62
|
+
def dry_run?
|
63
|
+
!!@options.dry_run
|
64
|
+
end
|
65
|
+
|
66
|
+
def file_path_to_message_prefix(file)
|
67
|
+
segments = File.expand_path(file).split('/').select { |segment| !segment.empty? }
|
68
|
+
subdir = %w(views helpers controllers models).find do |app_subdir|
|
69
|
+
segments.index(app_subdir)
|
70
|
+
end
|
71
|
+
if subdir.nil?
|
72
|
+
raise AppFolderNotFound, "No app. subfolders were found to determine prefix. Path is #{File.expand_path(file)}"
|
73
|
+
end
|
74
|
+
first_segment_index = segments.index(subdir) + 1
|
75
|
+
file_name_without_extensions = segments.last.split('.')[0..0]
|
76
|
+
path_segments = segments.slice(first_segment_index...-1)
|
77
|
+
(path_segments + file_name_without_extensions).join('.')
|
78
|
+
end
|
79
|
+
|
80
|
+
def get_content_from(file)
|
81
|
+
File.read(File.expand_path(file))
|
82
|
+
end
|
83
|
+
|
84
|
+
def write_content_to(file, content)
|
85
|
+
open(File.expand_path(file), "w") { |f| f.write(content) }
|
86
|
+
end
|
87
|
+
|
88
|
+
def show_diff(plain_row, i9l_row)
|
89
|
+
silenced_if_testing do
|
90
|
+
$stdout.puts "- #{plain_row}"
|
91
|
+
$stdout.puts "+ #{i9l_row}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def internationalize_file(file)
|
96
|
+
text = get_content_from(file)
|
97
|
+
prefix = self.prefix || file_path_to_message_prefix(file)
|
98
|
+
i18ned_text = sub_plain_strings(text, prefix)
|
99
|
+
write_content_to(file, i18ned_text) unless dry_run?
|
100
|
+
end
|
101
|
+
|
102
|
+
def display_indented_header(prefix)
|
103
|
+
silenced_if_testing do
|
104
|
+
puts "en:"
|
105
|
+
end
|
106
|
+
prefix_parts = prefix.split(".").each_with_index do |p, i|
|
107
|
+
p = "#{p}:"
|
108
|
+
(0..i).each { |i| p = " " + p }
|
109
|
+
silenced_if_testing do
|
110
|
+
puts "#{p}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def sub_plain_strings(text, prefix)
|
116
|
+
#TODO: find out how to display diff rows
|
117
|
+
I15R::PatternMatchers::Base.run(text, prefix) do |plain_row, i9l_row|
|
118
|
+
show_diff(plain_row, i9l_row)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def internationalize!(path)
|
123
|
+
files = path =~ /.erb$/ ? [path] : Dir.glob("#{path}/**/*.erb")
|
124
|
+
files.each { |file| internationalize_file(file) }
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
def silenced_if_testing
|
129
|
+
if testing?
|
130
|
+
orig_stdout = $stdout
|
131
|
+
$stdout = File.new('/dev/null', 'w')
|
132
|
+
end
|
133
|
+
yield
|
134
|
+
if testing?
|
135
|
+
$stdout = orig_stdout
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def testing?
|
140
|
+
$testing
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module I15R
|
2
|
+
module PatternMatchers
|
3
|
+
class Base
|
4
|
+
# def self.inherited(descendant)
|
5
|
+
# descendant.methods(false).grep /^match/ do |matcher_method|
|
6
|
+
# descendant.method(matcher_method).call
|
7
|
+
# end
|
8
|
+
# end
|
9
|
+
|
10
|
+
def self.matches(&block)
|
11
|
+
@@matchers ||= []
|
12
|
+
@@matchers.push(block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.run(text, prefix)
|
16
|
+
i18ned_text = text
|
17
|
+
@@matchers.each do |matcher|
|
18
|
+
m = matcher.call(text, prefix)
|
19
|
+
unless m.nil?
|
20
|
+
plain_row, i18ned_row = m
|
21
|
+
yield plain_row, i18ned_row if block_given?
|
22
|
+
i18ned_text.gsub!(plain_row, i18ned_row)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
i18ned_text
|
26
|
+
end
|
27
|
+
|
28
|
+
#TODO: use method_added to add to matchers so that
|
29
|
+
# matchers do not have to be explicitly registered.
|
30
|
+
def self.register_matcher(matcher_method)
|
31
|
+
self.method(matcher_method).call
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'i15r/base'
|
2
|
+
|
3
|
+
module I15R
|
4
|
+
module PatternMatchers
|
5
|
+
class RailsHelperMatcher < Base
|
6
|
+
|
7
|
+
def self.match_link_to_title
|
8
|
+
patt = /^(.*)<%=\s*link_to\s+['"](.*?)['"]\s*,(.*)%>(.*)$/
|
9
|
+
matches do |text, prefix|
|
10
|
+
if m = patt.match(text)
|
11
|
+
i18n_string = I15R::Base.get_i18n_message_string(m[2], prefix)
|
12
|
+
i18ned_row = %(#{m[1]}<%= link_to I18n.t("#{i18n_string}"),#{m[3]}%>#{m[4]})
|
13
|
+
[m[0], i18ned_row]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
register_matcher :match_link_to_title
|
18
|
+
|
19
|
+
def self.match_label_helper_text
|
20
|
+
patt = /^(.*)<%=(.*)\.label(.*),\s*['"](.*?)['"]\s*%>(.*)$/
|
21
|
+
matches do |text, prefix|
|
22
|
+
if m = patt.match(text)
|
23
|
+
i18n_string = I15R::Base.get_i18n_message_string(m[4], prefix)
|
24
|
+
i18ned_row = %(#{m[1]}<%=#{m[2]}.label#{m[3]}, I18n.t("#{i18n_string}") %>#{m[5]})
|
25
|
+
[m[0], i18ned_row]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
register_matcher :match_label_helper_text
|
30
|
+
|
31
|
+
def self.match_label_tag_helper_text
|
32
|
+
patt = /^(.*)<%=(.*)label_tag (.*),\s*['"](.*?)['"]\s*%>(.*)$/
|
33
|
+
matches do |text, prefix|
|
34
|
+
if m = patt.match(text)
|
35
|
+
i18n_string = I15R::Base.get_i18n_message_string(m[4], prefix)
|
36
|
+
i18ned_row = %(#{m[1]}<%=#{m[2]}label_tag #{m[3]}, I18n.t("#{i18n_string}") %>#{m[5]})
|
37
|
+
[m[0], i18ned_row]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
register_matcher :match_label_tag_helper_text
|
42
|
+
|
43
|
+
def self.match_submit_helper_text
|
44
|
+
patt = /^(.*)<%=(.*)\.submit\s*['"](.*?)['"]\s*%>(.*)$/
|
45
|
+
matches do |text, prefix|
|
46
|
+
if m = patt.match(text)
|
47
|
+
i18n_string = I15R::Base.get_i18n_message_string(m[3], prefix)
|
48
|
+
i18ned_row = %(#{m[1]}<%=#{m[2]}.submit I18n.t("#{i18n_string}") %>#{m[4]})
|
49
|
+
[m[0], i18ned_row]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
register_matcher :match_submit_helper_text
|
54
|
+
|
55
|
+
def self.match_submit_tag_helper_text
|
56
|
+
patt = /^(.*)<%=\s*submit_tag\s*['"](.*?)['"]\s*%>(.*)$/
|
57
|
+
matches do |text, prefix|
|
58
|
+
if m = patt.match(text)
|
59
|
+
i18n_string = I15R::Base.get_i18n_message_string(m[2], prefix)
|
60
|
+
i18ned_row = %(#{m[1]}<%= submit_tag I18n.t("#{i18n_string}") %>#{m[3]})
|
61
|
+
[m[0], i18ned_row]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
register_matcher :match_submit_tag_helper_text
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'i15r/base'
|
2
|
+
|
3
|
+
module I15R
|
4
|
+
module PatternMatchers
|
5
|
+
class TagAttributeMatcher < Base
|
6
|
+
|
7
|
+
def self.match_title_attribute
|
8
|
+
patt = /^(.*)(<a\s+.*title=)['"](.*?)['"](.*)/
|
9
|
+
matches do |text, prefix|
|
10
|
+
if m = patt.match(text)
|
11
|
+
i18n_string = I15R::Base.get_i18n_message_string(m[3], prefix)
|
12
|
+
i18ned_row = %(#{m[1]}#{m[2]}"<%= I18n.t("#{i18n_string}") %>"#{m[4]})
|
13
|
+
[m[0], i18ned_row]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
register_matcher :match_title_attribute
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'i15r/base'
|
2
|
+
|
3
|
+
module I15R
|
4
|
+
module PatternMatchers
|
5
|
+
class TagContentMatcher < Base
|
6
|
+
def self.match_tag_content
|
7
|
+
patt = /^(.*)>(\s*)(\w[\s\w:'"!?\.,]+)\s*<\/(.*)$/
|
8
|
+
matches do |text, prefix|
|
9
|
+
if m = patt.match(text)
|
10
|
+
i18n_string = I15R::Base.get_i18n_message_string(m[3], prefix)
|
11
|
+
ending_punctuation = m[3][/([?.!:\s]*)$/, 1]
|
12
|
+
i18ned_row = %(#{m[1]}>#{m[2]}<%= I18n.t("#{i18n_string}") %>#{ending_punctuation.to_s}</#{m[4]})
|
13
|
+
[m[0], i18ned_row]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
register_matcher :match_tag_content
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/spec/i15r_spec.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
# puts %(#{File.join(File.expand_path(File.dirname(__FILE__)), "..", "lib", "i15r")})
|
2
|
+
$:.unshift File.join(File.expand_path(File.dirname(__FILE__)), "..", "lib")
|
3
|
+
|
4
|
+
require "i15r"
|
5
|
+
require "fakefs"
|
6
|
+
|
7
|
+
$testing = true
|
8
|
+
|
9
|
+
describe I15R::Base do
|
10
|
+
|
11
|
+
before do
|
12
|
+
@i15r = I15R::Base.new
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "converting file paths to message prefixes" do
|
16
|
+
|
17
|
+
it "should correctly work from app root for views" do
|
18
|
+
@i15r.file_path_to_message_prefix("app/views/users/new.html.erb").should == "users.new"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should correctly work from app root for helpers" do
|
22
|
+
@i15r.file_path_to_message_prefix("app/helpers/users_helper.rb").should == "users_helper"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should correctly work from app root for controllers" do
|
26
|
+
@i15r.file_path_to_message_prefix("app/controllers/users_controller.rb").should == "users_controller"
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should correctly work from app root for models" do
|
30
|
+
@i15r.file_path_to_message_prefix("app/models/user.rb").should == "user"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should correctly work from app root for deep dir. structures" do
|
34
|
+
@i15r.file_path_to_message_prefix("app/views/member/session/users/new.html.erb").should == "member.session.users.new"
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should raise if path does not contain any Rails app directories" do
|
38
|
+
path = "projects/doodle.rb"
|
39
|
+
lambda { @i15r.file_path_to_message_prefix(path) }.should raise_error(AppFolderNotFound)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "dry_run?" do
|
44
|
+
it "should return true when in dry run mode" do
|
45
|
+
@i15r.options.dry_run = true
|
46
|
+
@i15r.dry_run?.should == true
|
47
|
+
end
|
48
|
+
it "should return false when not in dry run mode" do
|
49
|
+
@i15r.options.dry_run = false
|
50
|
+
@i15r.dry_run?.should == false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "turning plain messages into i18n message strings" do
|
55
|
+
|
56
|
+
it "should downcase a single word" do
|
57
|
+
I15R::Base.get_i18n_message_string("Name", "users.new").should == "users.new.name"
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should replace spaces with underscores" do
|
61
|
+
I15R::Base.get_i18n_message_string("New name", "users.index").should == "users.index.new_name"
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "when substituting the plain contents with i18n message strings" do
|
67
|
+
before do
|
68
|
+
@i15r.options.prefix = nil
|
69
|
+
@file_path = "app/views/users/new.html.erb"
|
70
|
+
File.open(@file_path, "w") { |f| f.write("<label for=\"user-name\">Name</label>") }
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "and in dry-run mode" do
|
74
|
+
before do
|
75
|
+
@i15r.stub!(:dry_run?).and_return(true)
|
76
|
+
end
|
77
|
+
it "should not touch any files" do
|
78
|
+
@i15r.should_not_receive(:write_content_to)
|
79
|
+
@i15r.internationalize_file(@file_path)
|
80
|
+
end
|
81
|
+
it "should display the diff" do
|
82
|
+
@i15r.should_receive(:show_diff)
|
83
|
+
@i15r.internationalize_file(@file_path)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "and not in dry-run mode" do
|
88
|
+
before do
|
89
|
+
@i15r.stub!(:dry_run?).and_return(false)
|
90
|
+
end
|
91
|
+
it "should write the files" do
|
92
|
+
@i15r.should_receive(:write_content_to)
|
93
|
+
@i15r.internationalize_file(@file_path)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "when no prefix option was given" do
|
99
|
+
it "should correctly internationalize messages using a prefix derived from the path" do
|
100
|
+
message_prefix = "users.new"
|
101
|
+
plain_snippet = <<-EOS
|
102
|
+
<label for="user-name">Name</label>
|
103
|
+
<input type="text" id="user-name" name="user[name]" />
|
104
|
+
EOS
|
105
|
+
i18ned_snippet = <<-EOS
|
106
|
+
<label for="user-name"><%= I18n.t("#{message_prefix}.name") %></label>
|
107
|
+
<input type="text" id="user-name" name="user[name]" />
|
108
|
+
EOS
|
109
|
+
@i15r.sub_plain_strings(plain_snippet, message_prefix).should == i18ned_snippet
|
110
|
+
end
|
111
|
+
end # "when no prefix option was given"
|
112
|
+
|
113
|
+
describe "when an explicit prefix option was given" do
|
114
|
+
it "should correctly internationalize messages using the prefix" do
|
115
|
+
prefix_option = "mysite"
|
116
|
+
plain_snippet = <<-EOS
|
117
|
+
<label for="user-name">Name</label>
|
118
|
+
<input type="text" id="user-name" name="user[name]" />
|
119
|
+
EOS
|
120
|
+
i18ned_snippet = <<-EOS
|
121
|
+
<label for="user-name"><%= I18n.t("#{prefix_option}.name") %></label>
|
122
|
+
<input type="text" id="user-name" name="user[name]" />
|
123
|
+
EOS
|
124
|
+
|
125
|
+
@i15r.sub_plain_strings(plain_snippet, prefix_option).should == i18ned_snippet
|
126
|
+
end
|
127
|
+
|
128
|
+
end # "when an explicit prefix option was given"
|
129
|
+
|
130
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
$:.unshift File.join(File.expand_path(File.dirname(__FILE__)), "..", "lib")
|
2
|
+
|
3
|
+
require 'i15r/pattern_matcher'
|
4
|
+
require "spec"
|
5
|
+
|
6
|
+
# FIXME: since matcher blocks are added and executed in the Base class,
|
7
|
+
# tests are not independent: they are coupled through the Base's run method
|
8
|
+
describe I15R::PatternMatchers::RailsHelperMatcher do
|
9
|
+
it "should replace a title in a link_to helper" do
|
10
|
+
plain = %(want a <p class="highlighted"><%= link_to 'New user', new_user_path %>?</p>)
|
11
|
+
i18ned = %(want a <p class="highlighted"><%= link_to I18n.t("users.index.new_user"), new_user_path %>?</p>)
|
12
|
+
I15R::PatternMatchers::RailsHelperMatcher.run(plain, "users.index").should == i18ned
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should replace a title in a link_to helper with html attributes" do
|
16
|
+
plain = %(<p><%= link_to "Create a new user", new_user_path, { :class => "add" } -%></p>)
|
17
|
+
i18ned = %(<p><%= link_to I18n.t("users.index.create_a_new_user"), new_user_path, { :class => "add" } -%></p>)
|
18
|
+
I15R::PatternMatchers::RailsHelperMatcher.run(plain, "users.index").should == i18ned
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should replace the label text in a label helper" do
|
22
|
+
plain = %(<%= f.label :name, "Name" %>)
|
23
|
+
i18ned = %(<%= f.label :name, I18n.t("users.new.name") %>)
|
24
|
+
I15R::PatternMatchers::RailsHelperMatcher.run(plain, "users.new").should == i18ned
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should replace the label text of a label_tag helper" do
|
28
|
+
plain = %(<%= label_tag :name, "Name" %>)
|
29
|
+
i18ned = %(<%= label_tag :name, I18n.t("users.new.name") %>)
|
30
|
+
I15R::PatternMatchers::RailsHelperMatcher.run(plain, "users.new").should == i18ned
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should replace the title of a submit helper in a form builder" do
|
34
|
+
plain = %(<%= f.submit "Create user" %>)
|
35
|
+
i18ned = %(<%= f.submit I18n.t("users.new.create_user") %>)
|
36
|
+
I15R::PatternMatchers::RailsHelperMatcher.run(plain, "users.new").should == i18ned
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should replace the title of a submit_tag helper" do
|
40
|
+
plain = %(<%= submit_tag "Create user" %>)
|
41
|
+
i18ned = %(<%= submit_tag I18n.t("users.new.create_user") %>)
|
42
|
+
I15R::PatternMatchers::RailsHelperMatcher.run(plain, "users.new").should == i18ned
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
$:.unshift File.join(File.expand_path(File.dirname(__FILE__)), "..", "lib")
|
2
|
+
|
3
|
+
require 'i15r/pattern_matcher'
|
4
|
+
require "spec"
|
5
|
+
|
6
|
+
# FIXME: since matcher blocks are added and executed in the Base class, that's what should be called
|
7
|
+
# in the tests. That's not elegant and tests are not independent: they are coupled through the Base's run method
|
8
|
+
describe I15R::PatternMatchers::TagAttributeMatcher do
|
9
|
+
it "should replace a link's title" do
|
10
|
+
plain = %(Site root\nThis is it: <a title="site root" href="/"><img src="site_logo.png" /></a>)
|
11
|
+
i18ned = %(Site root\nThis is it: <a title="<%= I18n.t("users.new.site_root") %>" href="/"><img src="site_logo.png" /></a>)
|
12
|
+
I15R::PatternMatchers::TagAttributeMatcher.run(plain, "users.new").should == i18ned
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
$:.unshift File.join(File.expand_path(File.dirname(__FILE__)), "..", "lib")
|
2
|
+
|
3
|
+
require 'i15r/pattern_matcher'
|
4
|
+
require "spec"
|
5
|
+
|
6
|
+
describe I15R::PatternMatchers::TagContentMatcher do
|
7
|
+
|
8
|
+
it "should replace a single word" do
|
9
|
+
plain = %(<label for="user-name">Name</label>)
|
10
|
+
i18ned = %(<label for="user-name"><%= I18n.t("users.new.name") %></label>)
|
11
|
+
I15R::PatternMatchers::TagContentMatcher.run(plain, "users.new").should == i18ned
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should replace several words" do
|
15
|
+
plain = %(<label for="user-name">Earlier names</label>)
|
16
|
+
i18ned = %(<label for="user-name"><%= I18n.t("users.new.earlier_names") %></label>)
|
17
|
+
I15R::PatternMatchers::TagContentMatcher.run(plain, "users.new").should == i18ned
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should remove punctuation from plain strings" do
|
21
|
+
plain = %(<label for="user-name">Got friends? A friend's name</label>)
|
22
|
+
i18ned = %(<label for="user-name"><%= I18n.t("users.new.got_friends_a_friends_name") %></label>)
|
23
|
+
I15R::PatternMatchers::TagContentMatcher.run(plain, "users.new").should == i18ned
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should not remove punctuation outside plain strings" do
|
27
|
+
plain = %(<label for="user-name">A friend's name:</label>)
|
28
|
+
i18ned = %(<label for="user-name"><%= I18n.t("users.new.a_friends_name") %>:</label>)
|
29
|
+
I15R::PatternMatchers::TagContentMatcher.run(plain, "users.new").should == i18ned
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should preserve whitespace in the content part of the tag" do
|
33
|
+
plain = %(<label for="user-name"> Name </label>)
|
34
|
+
i18ned = %(<label for="user-name"> <%= I18n.t("users.new.name") %> </label>)
|
35
|
+
I15R::PatternMatchers::TagContentMatcher.run(plain, "users.new").should == i18ned
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/tasks/i15r.rake
ADDED
data/todos.markdown
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
### 0.1
|
2
|
+
|
3
|
+
* DONE bug: In messages ending in punctuation (e.g No tags.) the punctuation is removed when replaced (e.g <%= I18n.t("users.show.no_tags") %>)
|
4
|
+
|
5
|
+
### 0.1.1
|
6
|
+
|
7
|
+
* DONE bug: indenting whitespace is removed around replaced messages
|
8
|
+
This is easy to fix, the whitespace has to be readded just like the punctuation
|
9
|
+
|
10
|
+
<% tags = @photo.tag_list %>
|
11
|
+
Tags:
|
12
|
+
<% if !tags.blank? %>
|
13
|
+
|
14
|
+
becomes
|
15
|
+
|
16
|
+
<% tags = @photo.tag_list %><%= I18n.t("mysite.tags") %><% if !tags.blank? %>
|
17
|
+
|
18
|
+
### 0.2
|
19
|
+
|
20
|
+
* DONE title of links have to be internationailzed, e.g: <a title="Go back" href="...">
|
21
|
+
* DONE <%= f.label :body, "Question" %>
|
22
|
+
* DONE <%= label_tag :body, "Question" %>
|
23
|
+
* DONE <%= f.submit "Submit question" %>
|
24
|
+
* DONE <%= submit_tag "Submit question" %>
|
25
|
+
* DONE think about which rails helpers could be given text that needs to be internationalized (currently only link_to is scanned)
|
26
|
+
* DONE suppress printing of yaml strings (change by Alberto) when running the specs
|
27
|
+
* DONE make it possible to run i15r on several files
|
28
|
+
|
29
|
+
### 0.?
|
30
|
+
|
31
|
+
* dry-run option: only show the diffs but do not actually overwrite the files
|
32
|
+
* make prefix not replace the inferred message string prefix, but really act as a prefix. (e.g if anetcom is given as a prefix, and the inferred message string is 'users.new' it should not be 'anetcom.name', but 'anetcom.users.new.name')
|
33
|
+
* handle non-ascii characters properly (UTF-8 support) when replacing them. Mañana should become I18n.t("prefix.manana"), for instance. That is, regular expressions should be utf-8 aware.
|
34
|
+
* write the necessary i18n strings back into the yaml file
|
35
|
+
* make interactive mode possible. the user is asked about each message to be replaced. he can choose to replace it, skip it or even edit it. Ideally the message would be shown in its context.
|
36
|
+
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: i15r
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Balint Erdi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-08 00:00:00 +01:00
|
13
|
+
default_executable: i15r
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: " The internationalizer. Replaces plain text strings in your views and replaces them with I18n message strings so you only have to provide the translations.\n"
|
17
|
+
email: balint.erdi@gmail.com
|
18
|
+
executables:
|
19
|
+
- i15r
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.markdown
|
24
|
+
files:
|
25
|
+
- .gitignore
|
26
|
+
- CHANGELOG
|
27
|
+
- MIT-LICENSE
|
28
|
+
- Manifest
|
29
|
+
- README.markdown
|
30
|
+
- Rakefile
|
31
|
+
- VERSION
|
32
|
+
- bin/i15r
|
33
|
+
- i15r.gemspec
|
34
|
+
- init.rb
|
35
|
+
- lib/i15r.rb
|
36
|
+
- lib/i15r/base.rb
|
37
|
+
- lib/i15r/pattern_matcher.rb
|
38
|
+
- lib/i15r/pattern_matchers/base.rb
|
39
|
+
- lib/i15r/pattern_matchers/rails_helper_matcher.rb
|
40
|
+
- lib/i15r/pattern_matchers/tag_attribute_matcher.rb
|
41
|
+
- lib/i15r/pattern_matchers/tag_content_matcher.rb
|
42
|
+
- spec/i15r_spec.rb
|
43
|
+
- spec/pattern_matcher_spec.rb
|
44
|
+
- spec/rails_helper_matcher_spec.rb
|
45
|
+
- spec/spec.opts
|
46
|
+
- spec/tag_attribute_matcher_spec.rb
|
47
|
+
- spec/tag_content_matcher_spec.rb
|
48
|
+
- tasks/i15r.rake
|
49
|
+
- todos.markdown
|
50
|
+
has_rdoc: true
|
51
|
+
homepage: http://github.com/balinterdi/i15r
|
52
|
+
licenses: []
|
53
|
+
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options:
|
56
|
+
- --charset=UTF-8
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: "0"
|
70
|
+
version:
|
71
|
+
requirements: []
|
72
|
+
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 1.3.5
|
75
|
+
signing_key:
|
76
|
+
specification_version: 3
|
77
|
+
summary: The internationalizer. Makes your Ruby app international
|
78
|
+
test_files:
|
79
|
+
- spec/i15r_spec.rb
|
80
|
+
- spec/pattern_matcher_spec.rb
|
81
|
+
- spec/rails_helper_matcher_spec.rb
|
82
|
+
- spec/tag_attribute_matcher_spec.rb
|
83
|
+
- spec/tag_content_matcher_spec.rb
|