l10nizer 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +50 -0
- data/Rakefile +54 -0
- data/bin/l10nizer +32 -0
- data/lib/l10nizer/grammar.treetop +53 -0
- data/lib/l10nizer/keygen.rb +41 -0
- data/lib/l10nizer/node.rb +108 -0
- data/lib/l10nizer/parser.rb +20 -0
- data/lib/l10nizer/processor.rb +29 -0
- data/lib/l10nizer/version.rb +9 -0
- data/lib/l10nizer.rb +2 -0
- data/test/samples/input.html.erb +34 -0
- data/test/samples/output.html.erb +34 -0
- data/test/test_key_generator.rb +68 -0
- data/test/test_processor.rb +162 -0
- metadata +97 -0
data/README.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
L10nizer
|
2
|
+
========
|
3
|
+
|
4
|
+
Automagic _ex post facto_ localisation for Rails templates.
|
5
|
+
|
6
|
+
What it does
|
7
|
+
------------
|
8
|
+
|
9
|
+
Processes all your `html.erb` templates, extracts text, replaces it with `t()` calls, and generates a YAML file of localisations.
|
10
|
+
|
11
|
+
For example, given a file `app/views/things/show.html.erb` with this content:
|
12
|
+
|
13
|
+
<div class="thing">
|
14
|
+
<h1>Some heading</h1>
|
15
|
+
<p>This thing is called <%= h(@thing.name)</p>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
l10nizer will change it to:
|
19
|
+
|
20
|
+
<div class="thing">
|
21
|
+
<h1><%= t("things.some_heading") %></h1>
|
22
|
+
<p><%= t("things.this_thing_is_called_a", :a => (h(@thing.name))) %></p>
|
23
|
+
</div>
|
24
|
+
|
25
|
+
and generate the following entries in `config/locales/l10nized.yml`:
|
26
|
+
|
27
|
+
things:
|
28
|
+
some_heading: Some heading
|
29
|
+
this_thing_is_called_a: This thing is called {{a}}
|
30
|
+
|
31
|
+
You can then use `l10nized.yml` as a basis for the localisation file for your current locale, e.g. `en_GB.yml`.
|
32
|
+
|
33
|
+
Usage
|
34
|
+
-----
|
35
|
+
|
36
|
+
From within a Rails application directory:
|
37
|
+
|
38
|
+
l10nizer
|
39
|
+
|
40
|
+
Specifying the application path explicitly:
|
41
|
+
|
42
|
+
l10nizer /path/to/my/rails/app
|
43
|
+
|
44
|
+
Limitations
|
45
|
+
-----------
|
46
|
+
|
47
|
+
* Perhaps ironically for a _localisation_ utility, l10nizer assumes that your templates are written in English or generally in ASCII, and ignores non-alphanumeric content when generating localisation keys. This could be fixed by modifying or replacing the L10nizer::KeyGenerator class.
|
48
|
+
* L10nizer takes no position on HTML entities or escaping. You __will__ need to review the changes it makes.
|
49
|
+
* Similarly, pluralisation is outside the scope of this application and will require attention.
|
50
|
+
* Strings that should be single entities but which contain HTML will be broken into multiple localisation strings.
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "rake/gempackagetask"
|
3
|
+
require "rake/testtask"
|
4
|
+
require "lib/l10nizer/version"
|
5
|
+
|
6
|
+
task :default => [:test]
|
7
|
+
|
8
|
+
Rake::TestTask.new("test") do |t|
|
9
|
+
t.libs << "test"
|
10
|
+
t.pattern = "test/**/test_*.rb"
|
11
|
+
t.verbose = true
|
12
|
+
end
|
13
|
+
|
14
|
+
task :default => :test
|
15
|
+
|
16
|
+
require "rake/testtask"
|
17
|
+
Rake::TestTask.new do |t|
|
18
|
+
t.libs << "test"
|
19
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
20
|
+
t.verbose = true
|
21
|
+
end
|
22
|
+
|
23
|
+
spec = Gem::Specification.new do |s|
|
24
|
+
s.name = "l10nizer"
|
25
|
+
s.version = L10nizer::VERSION::STRING
|
26
|
+
s.summary = "Automatically extract strings from ERB templates and replace with calls to t()"
|
27
|
+
s.author = "Paul Battley"
|
28
|
+
s.email = "pbattley@gmail.com"
|
29
|
+
|
30
|
+
s.has_rdoc = false
|
31
|
+
|
32
|
+
s.files = %w(Rakefile README.md) + Dir.glob("{bin,test,lib}/**/*")
|
33
|
+
s.executables = FileList["bin/**"].map { |f| File.basename(f) }
|
34
|
+
|
35
|
+
s.require_paths = ["lib"]
|
36
|
+
|
37
|
+
s.add_dependency("treetop", ">= 1.2.6")
|
38
|
+
s.add_dependency("polyglot", ">= 0.2.5")
|
39
|
+
|
40
|
+
s.add_development_dependency("thoughtbot-shoulda")
|
41
|
+
end
|
42
|
+
|
43
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
44
|
+
pkg.gem_spec = spec
|
45
|
+
|
46
|
+
# Generate the gemspec file for github.
|
47
|
+
file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
|
48
|
+
File.open(file, "w") {|f| f << spec.to_ruby }
|
49
|
+
end
|
50
|
+
|
51
|
+
desc 'Clear out generated packages'
|
52
|
+
task :clean => [:clobber_package] do
|
53
|
+
rm "#{spec.name}.gemspec"
|
54
|
+
end
|
data/bin/l10nizer
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "l10nizer"
|
3
|
+
require "yaml"
|
4
|
+
require "fileutils"
|
5
|
+
|
6
|
+
Dir.chdir(ARGV.first) if ARGV.first
|
7
|
+
templates = Dir["app/views/**/*.html.erb"]
|
8
|
+
raise "Can't find any templates in app/views." unless templates.any?
|
9
|
+
|
10
|
+
keygen = L10nizer::KeyGenerator.new
|
11
|
+
l10ns = {}
|
12
|
+
|
13
|
+
templates.each do |path|
|
14
|
+
keygen.namespace = path.split("/")[2]
|
15
|
+
source = File.read(path)
|
16
|
+
l10nizer = L10nizer::Processor.new(source, keygen)
|
17
|
+
l10ns.merge!(l10nizer.l10ns)
|
18
|
+
File.open(path, "w") do |f|
|
19
|
+
f << l10nizer.reformed
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
l10ns = l10ns.inject({}){ |hash, (key, value)|
|
24
|
+
parts = key.split(".")
|
25
|
+
parts[0 .. -2].inject(hash){ |h, k| h[k] ||= {} }[parts.last] = value
|
26
|
+
hash
|
27
|
+
}
|
28
|
+
|
29
|
+
FileUtils.mkdir_p("config/locales")
|
30
|
+
File.open("config/locales/l10nized.yml", "w") do |f|
|
31
|
+
f << l10ns.to_yaml
|
32
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
grammar HtmlErb
|
2
|
+
rule document
|
3
|
+
(html_comment / javascript / style / erb_control / whitespace / text / tag)* <Document>
|
4
|
+
end
|
5
|
+
|
6
|
+
rule html_comment
|
7
|
+
"<!--" ([^\-] / "-" [^\-] / "--" [^>])* "-->"
|
8
|
+
end
|
9
|
+
|
10
|
+
rule javascript
|
11
|
+
"<script" ([^<] / "<" [^/] / "</" [^s] / "</s" [^c] / "</sc" [^r] / "</scr" [^i] / "</scri" [^p] / "</scrip" [^t] / "</script" [^>"])* "</script>"
|
12
|
+
end
|
13
|
+
|
14
|
+
rule style
|
15
|
+
"<style" ([^<] / "<" [^/] / "</" [^s] / "</s" [^t] / "</st" [^y] / "</sty" [^l] / "</styl" [^e] / "</style" [^>"])* "</style>"
|
16
|
+
end
|
17
|
+
|
18
|
+
rule erb_control
|
19
|
+
"<%" [^=] ruby_code "%>"
|
20
|
+
end
|
21
|
+
|
22
|
+
rule tag
|
23
|
+
"<" (erb_control / erb_eval / [^>])+ ">"
|
24
|
+
end
|
25
|
+
|
26
|
+
rule whitespace
|
27
|
+
[\s]+
|
28
|
+
end
|
29
|
+
|
30
|
+
rule optional_whitespace
|
31
|
+
[\s]*
|
32
|
+
end
|
33
|
+
|
34
|
+
rule text
|
35
|
+
(optional_whitespace (erb_eval / inline_markup / word))+ <Text>
|
36
|
+
end
|
37
|
+
|
38
|
+
rule inline_markup
|
39
|
+
"<" "/"? ("em" / "strong") [^>]* ">"
|
40
|
+
end
|
41
|
+
|
42
|
+
rule word
|
43
|
+
[^<\s]+ <Word>
|
44
|
+
end
|
45
|
+
|
46
|
+
rule erb_eval
|
47
|
+
"<%=" ruby_code "%>" <Eval>
|
48
|
+
end
|
49
|
+
|
50
|
+
rule ruby_code
|
51
|
+
(("%" [^>]) / [^%])+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module L10nizer
|
2
|
+
class KeyGenerator
|
3
|
+
attr_accessor :namespace
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@seen = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(string)
|
10
|
+
provisional = [make_safe(namespace), make_safe(string)].compact * "."
|
11
|
+
|
12
|
+
until try(provisional, string)
|
13
|
+
provisional.sub!(/(?:_\d+)?$/){ |m| "_" + m.to_i.succ.to_s }
|
14
|
+
end
|
15
|
+
|
16
|
+
return provisional
|
17
|
+
end
|
18
|
+
|
19
|
+
def try(key, string)
|
20
|
+
if [nil, string].include?(@seen[key])
|
21
|
+
@seen[key] = string
|
22
|
+
true
|
23
|
+
else
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def make_safe(string)
|
29
|
+
return nil if string.nil?
|
30
|
+
safe = string.
|
31
|
+
downcase.
|
32
|
+
gsub(/&[a-z0-9]{1,20};/, ""). # entities
|
33
|
+
gsub(/<[^>]*>/, ""). # html
|
34
|
+
gsub(/[^a-z0-9]+/, "_"). # non alphanumeric
|
35
|
+
slice(0, 40).
|
36
|
+
gsub(/^_|_$/, "") # leading/trailing _
|
37
|
+
safe = "unknown" if safe.empty?
|
38
|
+
safe
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module L10nizer
|
2
|
+
class NodeWrapperFactory
|
3
|
+
def self.wrap(node, keygen=nil)
|
4
|
+
case node
|
5
|
+
when HtmlErb::Text
|
6
|
+
TextNode.new(node, keygen)
|
7
|
+
when HtmlErb::Eval
|
8
|
+
EvalNode.new(node, keygen)
|
9
|
+
when HtmlErb::Word
|
10
|
+
WordNode.new(node)
|
11
|
+
else
|
12
|
+
BasicNode.new(node)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class BasicNode
|
18
|
+
def initialize(node, keygen=nil)
|
19
|
+
@node = node
|
20
|
+
@keygen = keygen
|
21
|
+
end
|
22
|
+
|
23
|
+
def l10n
|
24
|
+
{}
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
@node.text_value
|
29
|
+
end
|
30
|
+
|
31
|
+
def evaluated?
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
def string?
|
36
|
+
false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class TextNode < BasicNode
|
41
|
+
def l10n
|
42
|
+
_, text = vars_and_text
|
43
|
+
text ? {key => text} : {}
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_s
|
47
|
+
vars, _ = vars_and_text
|
48
|
+
return super unless vars
|
49
|
+
|
50
|
+
params = ['"' + key + '"']
|
51
|
+
vars.each_with_index do |v, i|
|
52
|
+
params << %{:#{variable_name(i)} => (#{v})}
|
53
|
+
end
|
54
|
+
|
55
|
+
%{<%= t(#{params * ", "}) %>}
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def children
|
60
|
+
@node.children.map{ |e| NodeWrapperFactory.wrap(e) }
|
61
|
+
end
|
62
|
+
|
63
|
+
def variable_name(index)
|
64
|
+
("a" .. "z").to_a[index]
|
65
|
+
end
|
66
|
+
|
67
|
+
def vars_and_text
|
68
|
+
@vars_and_text ||= (
|
69
|
+
if children.all?{ |c| c.evaluated? } || !children.any?{ |c| c.string? }
|
70
|
+
[]
|
71
|
+
else
|
72
|
+
l10n = ""
|
73
|
+
vars = []
|
74
|
+
children.each do |e|
|
75
|
+
if e.evaluated?
|
76
|
+
l10n << "{{#{variable_name(vars.length)}}}"
|
77
|
+
vars << e.to_s
|
78
|
+
else
|
79
|
+
l10n << e.to_s
|
80
|
+
end
|
81
|
+
end
|
82
|
+
[vars, l10n]
|
83
|
+
end
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
def key
|
88
|
+
_, text = vars_and_text
|
89
|
+
@key ||= @keygen.call(text)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class EvalNode < BasicNode
|
94
|
+
def to_s
|
95
|
+
super[/\A<%=\s*(.*?)\s*%>\Z/, 1]
|
96
|
+
end
|
97
|
+
|
98
|
+
def evaluated?
|
99
|
+
true
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class WordNode < BasicNode
|
104
|
+
def string?
|
105
|
+
true
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "treetop"
|
2
|
+
require "polyglot"
|
3
|
+
require "l10nizer/grammar"
|
4
|
+
|
5
|
+
module HtmlErb
|
6
|
+
class Document < Treetop::Runtime::SyntaxNode
|
7
|
+
end
|
8
|
+
|
9
|
+
class Text < Treetop::Runtime::SyntaxNode
|
10
|
+
def children
|
11
|
+
elements.map{ |e| e.elements }.flatten
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Eval < Treetop::Runtime::SyntaxNode
|
16
|
+
end
|
17
|
+
|
18
|
+
class Word < Treetop::Runtime::SyntaxNode
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "l10nizer/parser"
|
2
|
+
require "l10nizer/node"
|
3
|
+
|
4
|
+
module L10nizer
|
5
|
+
class Processor
|
6
|
+
def initialize(html, keygen)
|
7
|
+
@html = html
|
8
|
+
@keygen = keygen
|
9
|
+
end
|
10
|
+
|
11
|
+
def l10ns
|
12
|
+
processed.inject({}){ |hash, node| hash.merge(node.l10n) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def reformed
|
16
|
+
processed.map{ |e| e.to_s }.join
|
17
|
+
end
|
18
|
+
|
19
|
+
def processed
|
20
|
+
unless @processed
|
21
|
+
kcode = $KCODE
|
22
|
+
$KCODE = "n"
|
23
|
+
@processed = HtmlErbParser.new.parse(@html).elements.map{ |e| NodeWrapperFactory.wrap(e, @keygen) }
|
24
|
+
$KCODE = kcode
|
25
|
+
end
|
26
|
+
@processed
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/l10nizer.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
<li id="action_<%=h thing.whatsit.id %>" class="item underway">
|
2
|
+
<div class="wrap">
|
3
|
+
<div class="placeholder">
|
4
|
+
<%- if thing.whatsit.identity? -%>
|
5
|
+
<%- if thing.whatsit.identity.processed? -%>
|
6
|
+
<%= image_tag thing.whatsit.identity.url(:medium) %>
|
7
|
+
<%- else -%>
|
8
|
+
<%= t("general.processing_image") %>
|
9
|
+
<%- end -%>
|
10
|
+
<%- end -%>
|
11
|
+
</div>
|
12
|
+
<h3>
|
13
|
+
<% link_to(thing) do %>
|
14
|
+
<span><%=h thing.whatsit.name %></span><%=h thing.short_address %>
|
15
|
+
<% end %>
|
16
|
+
</h3>
|
17
|
+
</div>
|
18
|
+
<div class="details">
|
19
|
+
<div class="format">
|
20
|
+
<h4 class="obfuscate">Skills</h4>
|
21
|
+
<ul class="skills">
|
22
|
+
<% thing.whatsit.skills.each do |skill| %>
|
23
|
+
<li class="skill">
|
24
|
+
<%= skill_icon(skill, :small) %>
|
25
|
+
</li>
|
26
|
+
<% end %>
|
27
|
+
<li class="engagement">
|
28
|
+
<%= engagement_icon(4, :small) %>
|
29
|
+
</li>
|
30
|
+
</ul>
|
31
|
+
<%=h truncate(thing.whatsit.short_description, :length => 130) %>
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
</li>
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<li id="action_<%=h thing.whatsit.id %>" class="item underway">
|
2
|
+
<div class="wrap">
|
3
|
+
<div class="placeholder">
|
4
|
+
<%- if thing.whatsit.identity? -%>
|
5
|
+
<%- if thing.whatsit.identity.processed? -%>
|
6
|
+
<%= image_tag thing.whatsit.identity.url(:medium) %>
|
7
|
+
<%- else -%>
|
8
|
+
<%= t("general.processing_image") %>
|
9
|
+
<%- end -%>
|
10
|
+
<%- end -%>
|
11
|
+
</div>
|
12
|
+
<h3>
|
13
|
+
<% link_to(thing) do %>
|
14
|
+
<span><%=h thing.whatsit.name %></span><%=h thing.short_address %>
|
15
|
+
<% end %>
|
16
|
+
</h3>
|
17
|
+
</div>
|
18
|
+
<div class="details">
|
19
|
+
<div class="format">
|
20
|
+
<h4 class="obfuscate"><%= t("skills") %></h4>
|
21
|
+
<ul class="skills">
|
22
|
+
<% thing.whatsit.skills.each do |skill| %>
|
23
|
+
<li class="skill">
|
24
|
+
<%= skill_icon(skill, :small) %>
|
25
|
+
</li>
|
26
|
+
<% end %>
|
27
|
+
<li class="engagement">
|
28
|
+
<%= engagement_icon(4, :small) %>
|
29
|
+
</li>
|
30
|
+
</ul>
|
31
|
+
<%=h truncate(thing.whatsit.short_description, :length => 130) %>
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
</li>
|
@@ -0,0 +1,68 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
2
|
+
|
3
|
+
require "test/unit"
|
4
|
+
require "l10nizer/keygen"
|
5
|
+
require "shoulda"
|
6
|
+
|
7
|
+
class KeyGeneratorTest < Test::Unit::TestCase
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@keygen = L10nizer::KeyGenerator.new
|
11
|
+
end
|
12
|
+
|
13
|
+
context "without namespacing" do
|
14
|
+
should "generate keys based on string" do
|
15
|
+
assert_equal "foo_bar_baz_a", @keygen.call("Foo bar baz {{a}}")
|
16
|
+
end
|
17
|
+
|
18
|
+
should "truncate exceptionally long keys" do
|
19
|
+
long = "blah_" * 20
|
20
|
+
short = "blah_blah_blah_blah_blah_blah_blah_blah"
|
21
|
+
assert_equal short, @keygen.call(long)
|
22
|
+
end
|
23
|
+
|
24
|
+
should "reuse key for same text" do
|
25
|
+
assert_equal @keygen.call("the same"), @keygen.call("the same")
|
26
|
+
end
|
27
|
+
|
28
|
+
should "prevent duplicate keys for different texts" do
|
29
|
+
assert_equal "a_thing", @keygen.call("a thing")
|
30
|
+
assert_equal "a_thing_1", @keygen.call("A thing")
|
31
|
+
assert_equal "a_thing_2", @keygen.call("A Thing")
|
32
|
+
end
|
33
|
+
|
34
|
+
should "generate non empty keys for punctuation" do
|
35
|
+
assert_not_equal "", @keygen.call("<>!@#%#.,")
|
36
|
+
end
|
37
|
+
|
38
|
+
should "skip entities in keys" do
|
39
|
+
assert_equal "foo_bar", @keygen.call("foo ' bar")
|
40
|
+
end
|
41
|
+
|
42
|
+
should "skip inline markup in keys" do
|
43
|
+
assert_equal "foo_bar", @keygen.call("foo <strong>bar</strong>")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "with namespacing" do
|
48
|
+
setup do
|
49
|
+
@keygen.namespace = "ns1"
|
50
|
+
end
|
51
|
+
|
52
|
+
should "prepend namespace" do
|
53
|
+
assert_equal "ns1.foo", @keygen.call("Foo")
|
54
|
+
end
|
55
|
+
|
56
|
+
should "prevent duplicate keys for different texts" do
|
57
|
+
assert_equal "ns1.a_thing", @keygen.call("a thing")
|
58
|
+
assert_equal "ns1.a_thing_1", @keygen.call("A thing")
|
59
|
+
assert_equal "ns1.a_thing_2", @keygen.call("A Thing")
|
60
|
+
end
|
61
|
+
|
62
|
+
should "check duplication by namespace" do
|
63
|
+
assert_equal "ns1.a_thing", @keygen.call("a thing")
|
64
|
+
@keygen.namespace = "ns2"
|
65
|
+
assert_equal "ns2.a_thing", @keygen.call("A thing")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
2
|
+
|
3
|
+
require "test/unit"
|
4
|
+
require "l10nizer/processor"
|
5
|
+
require "shoulda"
|
6
|
+
|
7
|
+
class ProcessorTest < Test::Unit::TestCase
|
8
|
+
|
9
|
+
class DumbKeyGenerator
|
10
|
+
def call(string)
|
11
|
+
string.downcase.gsub(/[^a-z0-9]+/, "_").gsub(/^_|_$/, "")[0, 40]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context "when finding text" do
|
16
|
+
setup do
|
17
|
+
html = "just some text"
|
18
|
+
@l10nizer = L10nizer::Processor.new(html, DumbKeyGenerator.new)
|
19
|
+
end
|
20
|
+
|
21
|
+
should "pass key to t()" do
|
22
|
+
expected = %{<%= t("just_some_text") %>}
|
23
|
+
assert_equal expected, @l10nizer.reformed
|
24
|
+
end
|
25
|
+
|
26
|
+
should "extract l10n strings" do
|
27
|
+
expected = {"just_some_text" => "just some text"}
|
28
|
+
assert_equal expected, @l10nizer.l10ns
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "when interpolating inline eval" do
|
33
|
+
setup do
|
34
|
+
html = "String <%= 27 %> with <%= 42 %>"
|
35
|
+
@l10nizer = L10nizer::Processor.new(html, DumbKeyGenerator.new)
|
36
|
+
end
|
37
|
+
|
38
|
+
should "pass values to t()" do
|
39
|
+
expected = %{<%= t("string_a_with_b", :a => (27), :b => (42)) %>}
|
40
|
+
assert_equal expected, @l10nizer.reformed
|
41
|
+
end
|
42
|
+
|
43
|
+
should "extract l10n strings" do
|
44
|
+
expected = {"string_a_with_b" => "String {{a}} with {{b}}"}
|
45
|
+
assert_equal expected, @l10nizer.l10ns
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when interpolating in multiple text strings" do
|
50
|
+
setup do
|
51
|
+
html = "<p>String <%= 27 %> with <%= 42 %></p><p>Another <%= 'x' %></p>"
|
52
|
+
@l10nizer = L10nizer::Processor.new(html, DumbKeyGenerator.new)
|
53
|
+
end
|
54
|
+
|
55
|
+
should "pass values to t() reusing placeholder variables" do
|
56
|
+
expected = %{<p><%= t("string_a_with_b", :a => (27), :b => (42)) %></p><p><%= t("another_a", :a => ('x')) %></p>}
|
57
|
+
assert_equal expected, @l10nizer.reformed
|
58
|
+
end
|
59
|
+
|
60
|
+
should "extract l10n strings with placeholder variables" do
|
61
|
+
expected = {"string_a_with_b" => "String {{a}} with {{b}}", "another_a" => "Another {{a}}"}
|
62
|
+
assert_equal expected, @l10nizer.l10ns
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
should "not try to localise inline eval on its own" do
|
67
|
+
html = "<p><%= 27 %></p>"
|
68
|
+
l10nizer = L10nizer::Processor.new(html, DumbKeyGenerator.new)
|
69
|
+
assert_equal html, l10nizer.reformed
|
70
|
+
end
|
71
|
+
|
72
|
+
should "not try to localise an HTML comment" do
|
73
|
+
html = "<!-- <p><%= 27 %></p> --> <p> <!-- fooo --> </p>"
|
74
|
+
l10nizer = L10nizer::Processor.new(html, DumbKeyGenerator.new)
|
75
|
+
assert_equal html, l10nizer.reformed
|
76
|
+
end
|
77
|
+
|
78
|
+
should "not try to localise Javascript" do
|
79
|
+
html = "<script>var a = 3;</script> <script>var b = 'b';</script>"
|
80
|
+
l10nizer = L10nizer::Processor.new(html, DumbKeyGenerator.new)
|
81
|
+
assert_equal html, l10nizer.reformed
|
82
|
+
end
|
83
|
+
|
84
|
+
should "not try to localise inline styles" do
|
85
|
+
html = %{<style type="text/css">\nhtml.js .nojs {display: none; background:#fff!important;}\n</style>}
|
86
|
+
l10nizer = L10nizer::Processor.new(html, DumbKeyGenerator.new)
|
87
|
+
assert_equal html, l10nizer.reformed
|
88
|
+
end
|
89
|
+
|
90
|
+
should "not try to localise control inside a tag" do
|
91
|
+
html = %{<div class="user-skills block <% unless @user.skills.any? %>blank<% end %>">}
|
92
|
+
l10nizer = L10nizer::Processor.new(html, DumbKeyGenerator.new)
|
93
|
+
assert_equal html, l10nizer.reformed
|
94
|
+
end
|
95
|
+
|
96
|
+
context "when string contains inline markup" do
|
97
|
+
setup do
|
98
|
+
html = "<p>String with <strong>strong</strong> and <em>emphasised</em> text</p>"
|
99
|
+
@l10nizer = L10nizer::Processor.new(html, lambda{ "key" })
|
100
|
+
end
|
101
|
+
|
102
|
+
should "include that markup in text" do
|
103
|
+
expected = "String with <strong>strong</strong> and <em>emphasised</em> text"
|
104
|
+
assert_equal [expected], @l10nizer.l10ns.values
|
105
|
+
end
|
106
|
+
|
107
|
+
should "use only one localisation" do
|
108
|
+
expected = %{<p><%= t("key") %></p>}
|
109
|
+
assert_equal expected, @l10nizer.reformed
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
should "not consider <span> to be inline markup" do
|
114
|
+
html = %{foo <span>bar</span>}
|
115
|
+
l10nizer = L10nizer::Processor.new(html, DumbKeyGenerator.new)
|
116
|
+
assert_equal ["bar", "foo"], l10nizer.l10ns.values.sort
|
117
|
+
end
|
118
|
+
|
119
|
+
context "when parsing a sample document" do
|
120
|
+
setup do
|
121
|
+
@html = File.read(File.join(File.dirname(__FILE__), "samples", "input.html.erb"))
|
122
|
+
@expected = File.read(File.join(File.dirname(__FILE__), "samples", "output.html.erb"))
|
123
|
+
@l10nizer = L10nizer::Processor.new(@html, DumbKeyGenerator.new)
|
124
|
+
end
|
125
|
+
|
126
|
+
should "replace embedded text" do
|
127
|
+
actual = @l10nizer.reformed
|
128
|
+
#@expected.split(/\n/).zip(actual.split(/\n/)).each do |a, b|
|
129
|
+
# puts a, b unless a == b
|
130
|
+
#end
|
131
|
+
assert_equal @expected, actual
|
132
|
+
end
|
133
|
+
|
134
|
+
should "extract l10n strings" do
|
135
|
+
expected = {"skills" => "Skills"}
|
136
|
+
assert_equal expected, @l10nizer.l10ns
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context "when $KCODE is 'UTF8'" do
|
141
|
+
setup do
|
142
|
+
@kcode = $KCODE
|
143
|
+
$KCODE = "UTF8"
|
144
|
+
end
|
145
|
+
|
146
|
+
teardown do
|
147
|
+
$KCODE = @kcode
|
148
|
+
end
|
149
|
+
|
150
|
+
should "parse multi-byte characters in strings" do
|
151
|
+
html = "<p>We’ve</p>"
|
152
|
+
l10nizer = L10nizer::Processor.new(html, DumbKeyGenerator.new)
|
153
|
+
assert_equal ["We’ve"], l10nizer.l10ns.values
|
154
|
+
end
|
155
|
+
|
156
|
+
should "not change $KCODE" do
|
157
|
+
l10nizer = L10nizer::Processor.new("", DumbKeyGenerator.new)
|
158
|
+
ignore = l10nizer.l10ns.values
|
159
|
+
assert_equal "UTF8", $KCODE
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: l10nizer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.9
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Paul Battley
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-11 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: treetop
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.2.6
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: polyglot
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.2.5
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: thoughtbot-shoulda
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
description:
|
46
|
+
email: pbattley@gmail.com
|
47
|
+
executables:
|
48
|
+
- l10nizer
|
49
|
+
extensions: []
|
50
|
+
|
51
|
+
extra_rdoc_files: []
|
52
|
+
|
53
|
+
files:
|
54
|
+
- Rakefile
|
55
|
+
- README.md
|
56
|
+
- bin/l10nizer
|
57
|
+
- test/samples/output.html.erb
|
58
|
+
- test/samples/input.html.erb
|
59
|
+
- test/test_key_generator.rb
|
60
|
+
- test/test_processor.rb
|
61
|
+
- lib/l10nizer/grammar.treetop
|
62
|
+
- lib/l10nizer/node.rb
|
63
|
+
- lib/l10nizer/parser.rb
|
64
|
+
- lib/l10nizer/processor.rb
|
65
|
+
- lib/l10nizer/keygen.rb
|
66
|
+
- lib/l10nizer/version.rb
|
67
|
+
- lib/l10nizer.rb
|
68
|
+
has_rdoc: true
|
69
|
+
homepage:
|
70
|
+
licenses: []
|
71
|
+
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: "0"
|
82
|
+
version:
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: "0"
|
88
|
+
version:
|
89
|
+
requirements: []
|
90
|
+
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 1.3.5
|
93
|
+
signing_key:
|
94
|
+
specification_version: 3
|
95
|
+
summary: Automatically extract strings from ERB templates and replace with calls to t()
|
96
|
+
test_files: []
|
97
|
+
|