missing_t 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/CHANGELOG +5 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +43 -0
- data/Rakefile +22 -0
- data/VERSION +1 -0
- data/bin/missing_t +19 -0
- data/init.rb +4 -0
- data/lib/missing_t.rb +206 -0
- data/missing_t.gemspec +57 -0
- data/spec/missing_t_spec.rb +217 -0
- data/spec/spec_helper.rb +1 -0
- data/tasks/missing_t.rake +6 -0
- data/todos.markdown +8 -0
- metadata +81 -0
data/.gitignore
ADDED
data/CHANGELOG
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 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/README.markdown
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Missing T
|
2
|
+
|
3
|
+
Missing T provides an easy way to see which internationalized messages lack their translations in a ruby project that uses I18n (e.g Rails apps). Instead of going through the translation files manually, you just call Missing T which gives you a list of all missing translation strings. By default it searches for all languages that you have translation files for. If you given it an option it will only search for translations in that language.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Missing T comes packaged as a gem, so you install it via the normal procedure:
|
8
|
+
|
9
|
+
$ gem install missing_t
|
10
|
+
|
11
|
+
Also, if you prefer to use it as a plugin to Rails project, you can simply do the following:
|
12
|
+
|
13
|
+
$ ./script/plugin install git://github.com/balinterdi/missing_t.git
|
14
|
+
|
15
|
+
## Running
|
16
|
+
|
17
|
+
To find all the messages without translations, you have to be in your project directory and then launch missing_t in the most simple way imaginable:
|
18
|
+
|
19
|
+
$ missing_t
|
20
|
+
|
21
|
+
You should see all messages that don't have translations on the screen, broken down per file, e.g:
|
22
|
+
|
23
|
+
app/views/users/new.html.erb:
|
24
|
+
fr.users.name
|
25
|
+
fr.users.city_of_residence
|
26
|
+
es.users.age
|
27
|
+
|
28
|
+
app/helpers/user_helper.rb:
|
29
|
+
fr.users.travels
|
30
|
+
|
31
|
+
__NOTE__ If no language code is provided, the script will determine which languages need to have translations by gathering all language codes in the localization files and assuming that if there is at least one translation defined for a language then all translations should be defined for it.
|
32
|
+
|
33
|
+
If you wish to see all the lacking translations for a certain language, just provide its language code as a parameter:
|
34
|
+
|
35
|
+
$ missing_t fr
|
36
|
+
|
37
|
+
## Epilogue
|
38
|
+
|
39
|
+
That's all about it, let me know if you find any bugs or have suggestions as to what else should the script do. If you wish you can directly contact me at balint.erdi@gmail.com.
|
40
|
+
|
41
|
+
[http://github.com/balinterdi/missing_t/](http://github.com/balinterdi/missing_t/)
|
42
|
+
|
43
|
+
Copyright (c) 2009 Balint Erdi, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'jeweler'
|
5
|
+
Jeweler::Tasks.new do |gemspec|
|
6
|
+
gemspec.name = "missing_t"
|
7
|
+
gemspec.summary = "See all the missing I18n translations in your Rails project"
|
8
|
+
gemspec.description = <<-EOF
|
9
|
+
With missing_t you can easily find all the missing i18n translations in your Rails project.
|
10
|
+
EOF
|
11
|
+
gemspec.email = "balint.erdi@gmail.com"
|
12
|
+
gemspec.homepage = "http://github.com/balinterdi/missing_t"
|
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
|
+
task :default => :spec
|
21
|
+
|
22
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
data/bin/missing_t
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
|
4
|
+
|
5
|
+
require 'missing_t'
|
6
|
+
|
7
|
+
@missing_t = MissingT.new
|
8
|
+
|
9
|
+
@missing_t = MissingT.new
|
10
|
+
@missing_t.instance_eval do
|
11
|
+
parse_options(ARGV)
|
12
|
+
find_missing_translations(ARGV[0]).each do |file, queries|
|
13
|
+
puts
|
14
|
+
puts "#{file}:"
|
15
|
+
puts
|
16
|
+
queries.each { |q| puts " #{red(q)}" }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
data/init.rb
ADDED
data/lib/missing_t.rb
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "optparse"
|
3
|
+
require "ostruct"
|
4
|
+
require "forwardable"
|
5
|
+
|
6
|
+
class Hash
|
7
|
+
def has_nested_key?(key)
|
8
|
+
h = self
|
9
|
+
key.to_s.split('.').each do |segment|
|
10
|
+
return false unless h.key?(segment)
|
11
|
+
h = h[segment]
|
12
|
+
end
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
# idea snatched from deep_merge in Rails source code
|
17
|
+
def deep_safe_merge(other_hash)
|
18
|
+
self.merge(other_hash) do |key, oldval, newval|
|
19
|
+
oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
|
20
|
+
newval = newval.to_hash if newval.respond_to?(:to_hash)
|
21
|
+
if oldval.class.to_s == 'Hash'
|
22
|
+
if newval.class.to_s == 'Hash'
|
23
|
+
oldval.deep_safe_merge(newval)
|
24
|
+
else
|
25
|
+
oldval
|
26
|
+
end
|
27
|
+
else
|
28
|
+
newval
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def deep_safe_merge!(other_hash)
|
34
|
+
replace(deep_safe_merge(other_hash))
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
module Helpers
|
40
|
+
# snatched from rspec source
|
41
|
+
def colour(text, colour_code)
|
42
|
+
"#{colour_code}#{text}\e[0m"
|
43
|
+
end
|
44
|
+
|
45
|
+
def green(text); colour(text, "\e[32m"); end
|
46
|
+
def red(text); colour(text, "\e[31m"); end
|
47
|
+
def magenta(text); colour(text, "\e[35m"); end
|
48
|
+
def yellow(text); colour(text, "\e[33m"); end
|
49
|
+
def blue(text); colour(text, "\e[34m"); end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
class MissingT
|
54
|
+
include Helpers
|
55
|
+
extend Forwardable
|
56
|
+
def_delegators :@translations, :[]
|
57
|
+
|
58
|
+
# attr_reader :translations
|
59
|
+
|
60
|
+
def initialize
|
61
|
+
@translations = Hash.new
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse_options(args)
|
65
|
+
@options = OpenStruct.new
|
66
|
+
@options.prefix = nil
|
67
|
+
opts = OptionParser.new do |opts|
|
68
|
+
opts.on("-f", "--file FILE_OR_DIR",
|
69
|
+
"look for missing translations in files under FILE_OR_DIR",
|
70
|
+
"(if a file is given, only look in that file)") do |path|
|
71
|
+
@options.path = path
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
76
|
+
puts opts
|
77
|
+
exit
|
78
|
+
end
|
79
|
+
|
80
|
+
opts.on_tail("--version", "Show version") do
|
81
|
+
puts "0.1.2"
|
82
|
+
exit
|
83
|
+
end
|
84
|
+
|
85
|
+
opts.parse!(args)
|
86
|
+
end
|
87
|
+
|
88
|
+
# NOTE: this method is needed
|
89
|
+
# because attr_reader :translations
|
90
|
+
# does not seem to be stubbable
|
91
|
+
def translations
|
92
|
+
@translations
|
93
|
+
end
|
94
|
+
|
95
|
+
def add_translations(trs)
|
96
|
+
translations.deep_safe_merge!(trs)
|
97
|
+
end
|
98
|
+
|
99
|
+
def collect_translations
|
100
|
+
locales_pathes = ["config/locales/**/*.yml", "vendor/plugins/**/config/locales/**/*yml", "vendor/plugins/**/locale/**/*yml"]
|
101
|
+
locales_pathes.each do |path|
|
102
|
+
Dir.glob(path) do |file|
|
103
|
+
add_translations(translations_in_file(file))
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def hashify(strings)
|
109
|
+
h = Hash.new
|
110
|
+
strings.map { |s| s.split('.') }.
|
111
|
+
each do |segmented_string|
|
112
|
+
root = h
|
113
|
+
segmented_string.each do |segment|
|
114
|
+
root[segment] ||= {}
|
115
|
+
root = root[segment]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
h
|
119
|
+
end
|
120
|
+
|
121
|
+
def translations_in_file(yaml_file)
|
122
|
+
open(yaml_file) { |f| YAML.load(f.read) }
|
123
|
+
end
|
124
|
+
|
125
|
+
def files_with_i18n_queries
|
126
|
+
if path = @options.path
|
127
|
+
path = path[0...-1] if path[-1..-1] == '/'
|
128
|
+
[ Dir.glob("#{path}/**/*.erb"), Dir.glob("#{path}/**/*.rb") ]
|
129
|
+
else
|
130
|
+
[ Dir.glob("app/**/*.erb"),
|
131
|
+
Dir.glob("app/**/controllers/**/*.rb"),
|
132
|
+
Dir.glob("app/**/helpers/**/*.rb")]
|
133
|
+
end.flatten
|
134
|
+
end
|
135
|
+
|
136
|
+
def get_content_of_file_with_i18n_queries(file)
|
137
|
+
f = open(File.expand_path(file), "r")
|
138
|
+
content = f.read()
|
139
|
+
f.close()
|
140
|
+
content
|
141
|
+
end
|
142
|
+
|
143
|
+
def extract_i18n_queries(file)
|
144
|
+
i18n_query_pattern = /[^\w]+(?:I18n\.translate|I18n\.t|translate|t)\s*\((.*?)[,\)]/
|
145
|
+
i18n_query_no_parens_pattern = /[^\w]+(?:I18n\.translate|I18n\.t|translate|t)\s+(['"])(.*?)\1/
|
146
|
+
file_content = get_content_of_file_with_i18n_queries(file)
|
147
|
+
file_content.scan(i18n_query_pattern).map { |match| match.first.gsub(/['"\s]/, '') }.
|
148
|
+
concat(file_content.scan(i18n_query_no_parens_pattern).map { |match| match[1].gsub(/['"\s]/, '') })
|
149
|
+
end
|
150
|
+
|
151
|
+
def collect_translation_queries
|
152
|
+
queries = {}
|
153
|
+
files_with_i18n_queries.each do |file|
|
154
|
+
queries_in_file = extract_i18n_queries(file)
|
155
|
+
unless queries_in_file.empty?
|
156
|
+
queries[file] = queries_in_file
|
157
|
+
end
|
158
|
+
end
|
159
|
+
queries
|
160
|
+
#TODO: remove duplicate queries across files
|
161
|
+
end
|
162
|
+
|
163
|
+
def has_translation?(lang, query)
|
164
|
+
t = translations
|
165
|
+
i18n_label(lang, query).split('.').each do |segment|
|
166
|
+
return false unless segment =~ /#\{.*\}/ or (t.respond_to?(:key?) and t.key?(segment))
|
167
|
+
t = t[segment]
|
168
|
+
end
|
169
|
+
true
|
170
|
+
end
|
171
|
+
|
172
|
+
def get_missing_translations(queries, lang=nil)
|
173
|
+
missing = {}
|
174
|
+
languages = lang.nil? ? translations.keys : [lang]
|
175
|
+
languages.each do |l|
|
176
|
+
get_missing_translations_for_lang(queries, l).each do |file, qs|
|
177
|
+
missing[file] ||= []
|
178
|
+
missing[file].concat(qs).uniq!
|
179
|
+
end
|
180
|
+
end
|
181
|
+
missing
|
182
|
+
end
|
183
|
+
|
184
|
+
def find_missing_translations(lang=nil)
|
185
|
+
collect_translations
|
186
|
+
get_missing_translations(collect_translation_queries, lang)
|
187
|
+
end
|
188
|
+
|
189
|
+
private
|
190
|
+
def get_missing_translations_for_lang(queries, lang)
|
191
|
+
queries.map do |file, queries_in_file|
|
192
|
+
queries_with_no_translation = queries_in_file.select { |q| !has_translation?(lang, q) }
|
193
|
+
if queries_with_no_translation.empty?
|
194
|
+
nil
|
195
|
+
else
|
196
|
+
[file, queries_with_no_translation.map { |q| i18n_label(lang, q) }]
|
197
|
+
end
|
198
|
+
end.compact
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
def i18n_label(lang, query)
|
203
|
+
"#{lang}.#{query}"
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
data/missing_t.gemspec
ADDED
@@ -0,0 +1,57 @@
|
|
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{missing_t}
|
8
|
+
s.version = "0.2.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{2010-09-11}
|
13
|
+
s.default_executable = %q{missing_t}
|
14
|
+
s.description = %q{ With missing_t you can easily find all the missing i18n translations in your Rails project.
|
15
|
+
}
|
16
|
+
s.email = %q{balint.erdi@gmail.com}
|
17
|
+
s.executables = ["missing_t"]
|
18
|
+
s.extra_rdoc_files = [
|
19
|
+
"README.markdown"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".gitignore",
|
23
|
+
"CHANGELOG",
|
24
|
+
"MIT-LICENSE",
|
25
|
+
"README.markdown",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"bin/missing_t",
|
29
|
+
"init.rb",
|
30
|
+
"lib/missing_t.rb",
|
31
|
+
"missing_t.gemspec",
|
32
|
+
"spec/missing_t_spec.rb",
|
33
|
+
"spec/spec_helper.rb",
|
34
|
+
"tasks/missing_t.rake",
|
35
|
+
"todos.markdown"
|
36
|
+
]
|
37
|
+
s.homepage = %q{http://github.com/balinterdi/missing_t}
|
38
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
39
|
+
s.require_paths = ["lib"]
|
40
|
+
s.rubygems_version = %q{1.3.7}
|
41
|
+
s.summary = %q{See all the missing I18n translations in your Rails project}
|
42
|
+
s.test_files = [
|
43
|
+
"spec/missing_t_spec.rb",
|
44
|
+
"spec/spec_helper.rb"
|
45
|
+
]
|
46
|
+
|
47
|
+
if s.respond_to? :specification_version then
|
48
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
49
|
+
s.specification_version = 3
|
50
|
+
|
51
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
52
|
+
else
|
53
|
+
end
|
54
|
+
else
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
@@ -0,0 +1,217 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "spec"
|
3
|
+
require "mocha"
|
4
|
+
|
5
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
6
|
+
|
7
|
+
# use mocha for mocking instead of
|
8
|
+
# Rspec's own mock framework
|
9
|
+
Spec::Runner.configure do |config|
|
10
|
+
config.mock_with :mocha
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "MissingT" do
|
14
|
+
before do
|
15
|
+
@missing_t = MissingT.new
|
16
|
+
@es_translations = {"es"=>
|
17
|
+
{"zoo"=>{"elephant"=>"elefante", "bear"=>"oso", "lion"=>"leon", "bee" => "abeja"},
|
18
|
+
"lamp"=>"lampa",
|
19
|
+
"book"=>"libro",
|
20
|
+
"handkerchief"=>"panuelo",
|
21
|
+
"pen" => "boli"}}
|
22
|
+
@fr_translations = {"fr"=>
|
23
|
+
{"zoo"=>{"elephant"=>"elephant", "bear"=>"ours", "lion"=>"lion", "wasp" => "guepe"},
|
24
|
+
"lamp"=>"lampe",
|
25
|
+
"book"=>"livre",
|
26
|
+
"handkerchief"=>"mouchoir",
|
27
|
+
"mother" => "mere"}}
|
28
|
+
|
29
|
+
@other_es_translations = { "es" => {"zoo" => {}}}
|
30
|
+
@yet_other_es_translations = { "es" => {"zoo" => {"monkey" => "mono", "horse" => "caballo"}}}
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "adding translations" do
|
34
|
+
before do
|
35
|
+
@missing_t.add_translations(@es_translations)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should pick up the new translations" do
|
39
|
+
@missing_t.translations.should == @es_translations
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should correctly merge different translations" do
|
43
|
+
@missing_t.add_translations(@fr_translations)
|
44
|
+
@missing_t["fr"]["zoo"].should have_key("wasp")
|
45
|
+
@missing_t["fr"].should have_key("mother")
|
46
|
+
@missing_t["es"]["zoo"].should have_key("bee")
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should not overwrite translations keys" do
|
50
|
+
@missing_t.add_translations(@other_es_translations)
|
51
|
+
@missing_t["es"]["zoo"].should have_key("bear")
|
52
|
+
@missing_t["es"]["zoo"].should have_key("bee")
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should add the new translations even if they contain keys already in the translations hash" do
|
56
|
+
@missing_t.add_translations(@yet_other_es_translations)
|
57
|
+
@missing_t["es"]["zoo"].should have_key("monkey")
|
58
|
+
@missing_t["es"]["zoo"].should have_key("bear")
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "hashification" do
|
64
|
+
before do
|
65
|
+
queries = ["zoo.bee", "zoo.departments.food", "zoo.departments.qa", "lamp", "mother", "mother.maiden_name"]
|
66
|
+
@queries_hash = @missing_t.hashify(queries)
|
67
|
+
@h = { "fr" => { "book" => "livre", "zoo" => {"elephant" => "elephant"} } }
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should find a nested key and return it" do
|
71
|
+
@h.should have_nested_key('fr.zoo.elephant')
|
72
|
+
@h.should have_nested_key('fr.book')
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should return false when it does not have a nested key" do
|
76
|
+
@h.should_not have_nested_key('fr.zoo.seal')
|
77
|
+
@h.should_not have_nested_key('xxx')
|
78
|
+
end
|
79
|
+
|
80
|
+
it "an empty hash should not have any nested keys" do
|
81
|
+
{}.should_not have_nested_key(:puppy)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should turn strings to hash keys along their separators (dots)" do
|
85
|
+
["zoo", "lamp", "mother"].all? { |k| @queries_hash.key?(k) }.should == true
|
86
|
+
["bee", "departments"].all? { |k| @queries_hash["zoo"].key?(k) }.should == true
|
87
|
+
@queries_hash["zoo"]["departments"].should have_key("food")
|
88
|
+
@queries_hash["zoo"]["departments"].should have_key("qa")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "the i18n query extracion" do
|
93
|
+
before do
|
94
|
+
metaclass = class << @missing_t; self; end
|
95
|
+
metaclass.instance_eval do
|
96
|
+
define_method :get_content_of_file_with_i18n_queries do |content|
|
97
|
+
content
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should correctly extract the I18n.t type of messages" do
|
103
|
+
content = <<-EOS
|
104
|
+
<div class="title_gray"><span><%= I18n.t("anetcom.member.projects.new.page_title") %></span></div>
|
105
|
+
EOS
|
106
|
+
@missing_t.extract_i18n_queries(content).should == ["anetcom.member.projects.new.page_title"]
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should correctly extract the I18n.t type of messages not right after the <%= mark" do
|
110
|
+
content = <<-EOS
|
111
|
+
<%= submit_tag I18n.t('anetcom.member.projects.new.create_project'), :class => 'button' %>
|
112
|
+
EOS
|
113
|
+
@missing_t.extract_i18n_queries(content).should == ["anetcom.member.projects.new.create_project"]
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should correctly extract the I18n.t type of messages from a link_to" do
|
117
|
+
# honestly, I am not sure anymore why this qualifies as a sep. test case
|
118
|
+
# but I am sure there was something special about this one :)
|
119
|
+
content = <<-EOS
|
120
|
+
<%= link_to I18n.t("tog_headlines.admin.publish"), publish_admin_headlines_story_path(story), :class => 'button' %>
|
121
|
+
EOS
|
122
|
+
@missing_t.extract_i18n_queries(content).should == ["tog_headlines.admin.publish"]
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should correctly extract the I18n.t type of messages with an argument in the message" do
|
126
|
+
content = <<-EOS
|
127
|
+
:html => {:title => I18n.t("tog_social.sharing.share_with", :name => shared.name)}
|
128
|
+
EOS
|
129
|
+
@missing_t.extract_i18n_queries(content).should == ["tog_social.sharing.share_with"]
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should correctly extract the I18n.translate type of messages" do
|
133
|
+
content = <<-EOS
|
134
|
+
<div class="title_gray"><span><%= I18n.translate("anetcom.member.projects.new.page_title") %></span></div>
|
135
|
+
EOS
|
136
|
+
@missing_t.extract_i18n_queries(content).should == ["anetcom.member.projects.new.page_title"]
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should correctly extract the t type of messages" do
|
140
|
+
content = <<-EOS
|
141
|
+
<div class="title_gray"><span><%= t("anetcom.member.projects.new.page_title") %></span></div>
|
142
|
+
EOS
|
143
|
+
@missing_t.extract_i18n_queries(content).should == ["anetcom.member.projects.new.page_title"]
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should find several messages on the same line" do
|
147
|
+
content = <<-EOS
|
148
|
+
<div class="title_gray"><span><%= t("anetcom.member.projects.new.page_title") %></span><span>t("anetcom.member.projects.new.page_size")</span></div>
|
149
|
+
EOS
|
150
|
+
@missing_t.extract_i18n_queries(content).should == ["anetcom.member.projects.new.page_title", "anetcom.member.projects.new.page_size"]
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should find messages with a parens-less call" do
|
154
|
+
content = <<-EOS
|
155
|
+
<div class="title_gray"><span><%= t "anetcom.member.projects.new.page_title" %></span></div>
|
156
|
+
EOS
|
157
|
+
@missing_t.extract_i18n_queries(content).should == ["anetcom.member.projects.new.page_title"]
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should not extract a function call that just ends in t" do
|
161
|
+
content = <<-EOS
|
162
|
+
<div class="title_gray"><span><%= at(3) %></span></div>
|
163
|
+
EOS
|
164
|
+
@missing_t.extract_i18n_queries(content).should == []
|
165
|
+
end
|
166
|
+
|
167
|
+
it "should find and correctly extract a dynamic key translation message" do
|
168
|
+
# @missing_t.stubs(:get_content_of_file_with_i18n_queries).returns(content)
|
169
|
+
content = %q(<div class="title_gray"><span><%= I18n.t("mycompany.welcome.#{key}") %></span></div>)
|
170
|
+
@missing_t.extract_i18n_queries(content).should == [%q(mycompany.welcome.#{key})]
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
describe "finding missing translations" do
|
176
|
+
before do
|
177
|
+
@t_queries = { :fake_file => ["mother", "zoo.bee", "zoo.wasp", "pen"] }
|
178
|
+
@missing_t.stubs(:translations).returns(@fr_translations.merge(@es_translations))
|
179
|
+
# @missing_t.stubs(:collect_translation_queries).returns(@t_queries)
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should return true if it has a translation given in the I18n form" do
|
183
|
+
@missing_t.has_translation?("fr", "zoo.wasp").should == true
|
184
|
+
@missing_t.has_translation?("es", "pen").should == true
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should return false if it does not have a translation given in the I18n form" do
|
188
|
+
@missing_t.has_translation?("fr", "zoo.bee").should == false
|
189
|
+
@missing_t.has_translation?("es", "mother").should == false
|
190
|
+
end
|
191
|
+
|
192
|
+
describe "of dynamic message strings" do
|
193
|
+
it "should return true if it has a translation that matches the fix parts" do
|
194
|
+
@missing_t.has_translation?("fr", %q(zoo.#{animal})).should == true
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should return false if it does not have a translation that matches all the fix parts" do
|
198
|
+
@missing_t.has_translation?("fr", %q(household.#{animal})).should == false
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should correctly get missing translations for a spec. language" do
|
203
|
+
miss_entries = @missing_t.get_missing_translations(@t_queries, "fr").map{ |e| e[1] }.flatten
|
204
|
+
miss_entries.should include("fr.pen")
|
205
|
+
miss_entries.should include("fr.zoo.bee")
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should correctly get missing translations" do
|
209
|
+
miss_entries = @missing_t.get_missing_translations(@t_queries).map{ |e| e[1] }.flatten
|
210
|
+
miss_entries.should include("fr.zoo.bee")
|
211
|
+
miss_entries.should include("fr.pen")
|
212
|
+
miss_entries.should include("es.zoo.wasp")
|
213
|
+
miss_entries.should include("es.mother")
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "lib", "missing_t")
|
data/todos.markdown
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
DONE
|
2
|
+
----
|
3
|
+
|
4
|
+
* for message strings with dynamic parts, check if there is any translation that matches the non-dynamic part. Only output the message if none found. E.g
|
5
|
+
|
6
|
+
user.friendships.make.#{action}
|
7
|
+
|
8
|
+
will output user.friendships.make.#{action} if there are no translation strings that match user.friendships.make. (no matter what comes after the make.)
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: missing_t
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Balint Erdi
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-09-11 00:00:00 +02:00
|
19
|
+
default_executable: missing_t
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: " With missing_t you can easily find all the missing i18n translations in your Rails project.\n"
|
23
|
+
email: balint.erdi@gmail.com
|
24
|
+
executables:
|
25
|
+
- missing_t
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- README.markdown
|
30
|
+
files:
|
31
|
+
- .gitignore
|
32
|
+
- CHANGELOG
|
33
|
+
- MIT-LICENSE
|
34
|
+
- README.markdown
|
35
|
+
- Rakefile
|
36
|
+
- VERSION
|
37
|
+
- bin/missing_t
|
38
|
+
- init.rb
|
39
|
+
- lib/missing_t.rb
|
40
|
+
- missing_t.gemspec
|
41
|
+
- spec/missing_t_spec.rb
|
42
|
+
- spec/spec_helper.rb
|
43
|
+
- tasks/missing_t.rake
|
44
|
+
- todos.markdown
|
45
|
+
has_rdoc: true
|
46
|
+
homepage: http://github.com/balinterdi/missing_t
|
47
|
+
licenses: []
|
48
|
+
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options:
|
51
|
+
- --charset=UTF-8
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
hash: 3
|
69
|
+
segments:
|
70
|
+
- 0
|
71
|
+
version: "0"
|
72
|
+
requirements: []
|
73
|
+
|
74
|
+
rubyforge_project:
|
75
|
+
rubygems_version: 1.3.7
|
76
|
+
signing_key:
|
77
|
+
specification_version: 3
|
78
|
+
summary: See all the missing I18n translations in your Rails project
|
79
|
+
test_files:
|
80
|
+
- spec/missing_t_spec.rb
|
81
|
+
- spec/spec_helper.rb
|