i18n_kit 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +32 -0
- data/LICENSE.txt +20 -0
- data/README.textile +145 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/i18n_kit.gemspec +76 -0
- data/lib/i18n_kit/comparer.rb +41 -0
- data/lib/i18n_kit/document.rb +100 -0
- data/lib/i18n_kit/importer/excel_xml.rb +63 -0
- data/lib/i18n_kit/importer/yaml.rb +24 -0
- data/lib/i18n_kit/importer.rb +14 -0
- data/lib/i18n_kit.rb +5 -0
- data/lib/yamlator.rb +60 -0
- data/spec/assets/excel_export.xml +190 -0
- data/spec/assets/fi_hierarchical.yml +22 -0
- data/spec/compare_spec.rb +29 -0
- data/spec/document_spec.rb +32 -0
- data/spec/importer_excel_xml_spec.rb +80 -0
- data/spec/importer_yml_spec.rb +20 -0
- data/spec/spec_helper.rb +46 -0
- metadata +178 -0
data/.document
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.2)
|
5
|
+
git (1.2.5)
|
6
|
+
jeweler (1.6.2)
|
7
|
+
bundler (~> 1.0)
|
8
|
+
git (>= 1.2.5)
|
9
|
+
rake
|
10
|
+
nokogiri (1.4.4)
|
11
|
+
rake (0.8.7)
|
12
|
+
rcov (0.9.9)
|
13
|
+
rspec (2.3.0)
|
14
|
+
rspec-core (~> 2.3.0)
|
15
|
+
rspec-expectations (~> 2.3.0)
|
16
|
+
rspec-mocks (~> 2.3.0)
|
17
|
+
rspec-core (2.3.1)
|
18
|
+
rspec-expectations (2.3.0)
|
19
|
+
diff-lcs (~> 1.1.2)
|
20
|
+
rspec-mocks (2.3.0)
|
21
|
+
ya2yaml (0.30)
|
22
|
+
|
23
|
+
PLATFORMS
|
24
|
+
ruby
|
25
|
+
|
26
|
+
DEPENDENCIES
|
27
|
+
bundler (~> 1.0.0)
|
28
|
+
jeweler (~> 1.6.2)
|
29
|
+
nokogiri
|
30
|
+
rcov
|
31
|
+
rspec (~> 2.3.0)
|
32
|
+
ya2yaml
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 funkensturm.
|
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.textile
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
h2. I18nKit
|
2
|
+
|
3
|
+
You can import XML files that were exported using Excel, or standard i18n hierarchical YML files. Both will be normalized as I18nKit::Document objects so that they can be compared with each other. You need implement the comparison method yourself, however. I18nKit only provides the Iterator.
|
4
|
+
|
5
|
+
h3. Importing
|
6
|
+
|
7
|
+
Say you have an Excel file like this
|
8
|
+
|
9
|
+
<pre>
|
10
|
+
1 | key | english | swedish | german | comments |
|
11
|
+
2 | customer.invoice.create | Create invoice | Skapa intyg | Rechnung anlegen | |
|
12
|
+
3 | customer.invoice.delete | Delete invoice | Radera intyg | Rechnung löschen | Wow... |
|
13
|
+
</pre>
|
14
|
+
|
15
|
+
and you export it as XML. Then you can import it with I18nKit like this:
|
16
|
+
|
17
|
+
<pre>
|
18
|
+
require 'i18n_kit'
|
19
|
+
documents = I18nKit::Importer::ExcelXML.new('my_export.xml').parse
|
20
|
+
|
21
|
+
# Or use the shortcut:
|
22
|
+
documents = I18nKit.import_excel("'my_export.xml')
|
23
|
+
</pre>
|
24
|
+
|
25
|
+
you could also pass the file as a String:
|
26
|
+
|
27
|
+
<pre>
|
28
|
+
documents = I18nKit::Importer::ExcelXML.new(File.read('my_export.xml')).parse
|
29
|
+
</pre>
|
30
|
+
|
31
|
+
The first row will be used to identify the locale language. Note that there are more options for how to import Excel files depending on whether there is a headline or not. Or whether you want to import only the first X columns, like so:
|
32
|
+
|
33
|
+
<pre>
|
34
|
+
importer = I18nKit::Importer::ExcelXML.new(XML_PATH, :ignore_headline => true, :locales => [:sv, :en, :fi])
|
35
|
+
</pre>
|
36
|
+
|
37
|
+
That will ignore the last column (comments) and use "sv" instead of "swedish", for example.
|
38
|
+
|
39
|
+
At any rate, @documents@ will hold an Array of @I18nKit::Document@ objects. Each representing one language. You might want to give them names so that you can compare them better later:
|
40
|
+
|
41
|
+
<pre>
|
42
|
+
documents[0] = 'Excel: English'
|
43
|
+
documents[1] = 'Excel: Swedish'
|
44
|
+
documents[2] = 'Excel: German'
|
45
|
+
|
46
|
+
# Note: By default they're called :excel_en, :excel_sv, etc...
|
47
|
+
</pre>
|
48
|
+
|
49
|
+
Now let's import a YML file from your Rails project:
|
50
|
+
|
51
|
+
<pre>
|
52
|
+
more_documents = I18nKit::Importer::YAML.new('config/locales/fi.yml').parse
|
53
|
+
more_documents[0].name = 'My Rails App: Finnish'
|
54
|
+
|
55
|
+
# Or just use the shortcut:
|
56
|
+
I18nKit.import_yaml("config/locales/fi.yml")
|
57
|
+
|
58
|
+
# Note: The default document name is: :yaml_fi
|
59
|
+
</pre>
|
60
|
+
|
61
|
+
What can you do with a @I18nKit::Document@ entity? If you used the Excel import, then you have e.g. these instance methods:
|
62
|
+
|
63
|
+
<pre>
|
64
|
+
document.name => 'Excel: German'
|
65
|
+
document.locale => 'de'
|
66
|
+
document.to_flat_hash => { 'customer.invoice.create' => 'Rechnung anlegen',
|
67
|
+
'customer.invoice.delete' => 'Rechnung löschen' }
|
68
|
+
document.keys => ['customer.invoice.create', 'customer.invoice.delete']
|
69
|
+
</pre>
|
70
|
+
|
71
|
+
You can do the same with files imported from YML, but additionally you have:
|
72
|
+
|
73
|
+
<pre>
|
74
|
+
document.to_hierarchical_hash => { 'customer' => { 'invoice' => { 'create' => 'Rechnung anlegen', etc... } } }
|
75
|
+
document.to_i18n_hash => { 'de' => { 'customer' => { 'invoice' => { etc... } } }
|
76
|
+
|
77
|
+
# You can even access and update values directly:
|
78
|
+
|
79
|
+
document['customer.invoice.create'] => 'Rechnung anlegen'
|
80
|
+
document['customer.invoice.create'] = 'New Value'
|
81
|
+
|
82
|
+
document.write_i18n_file('path/to/output_file.yml')
|
83
|
+
</pre>
|
84
|
+
|
85
|
+
As you may notice, the whole idea is to update a Rails app according to a Excel file that is out-of-sync. In the future there could be lots more handy features, but for now, you can perform some good deal of tasks a lot easier than manually. For instance, you could quickly compare two YML I18n files with each other and automate some synchronization.
|
86
|
+
|
87
|
+
@I18nKit:Comparer@ will compare each @I18nKit::Document@ with each document:
|
88
|
+
|
89
|
+
<pre>
|
90
|
+
# Loading an Excel file and a YML file
|
91
|
+
documents = ::I18nKit::Importer::ExcelXML.new(XML_PATH, :ignore_headline => true, :locales => [:sv, :en, :fi]).parse +
|
92
|
+
::I18nKit::Importer::YAML.new(YML_FI_HIERARCHICAL_PATH).parse
|
93
|
+
|
94
|
+
# Giving the Documents smart names
|
95
|
+
documents[0].name = 'First'
|
96
|
+
documents[1].name = 'Second'
|
97
|
+
documents[2].name = 'Third'
|
98
|
+
documents[3].name = 'Fourth'
|
99
|
+
|
100
|
+
# Load the comparer
|
101
|
+
comparer = I18nKit::Comparer.new(documents)
|
102
|
+
|
103
|
+
# Iterate the comparison
|
104
|
+
comparer.documents_to_compare.each do |left, right|
|
105
|
+
puts "#{left.name} <=> #{right.name}"
|
106
|
+
end
|
107
|
+
</pre>
|
108
|
+
|
109
|
+
will output:
|
110
|
+
|
111
|
+
<pre>
|
112
|
+
First <=> Second
|
113
|
+
First <=> Third
|
114
|
+
First <=> Fourth
|
115
|
+
Second <=> Third
|
116
|
+
Second <=> Fourth
|
117
|
+
Third <=> Fourth
|
118
|
+
</pre>
|
119
|
+
|
120
|
+
You can also access individual documents by their name by using [] on the Comparer:
|
121
|
+
|
122
|
+
<pre>
|
123
|
+
comparer[:excel_sv].keys.each do |key|
|
124
|
+
if comparer[:yaml_sv].has_key?(key)
|
125
|
+
# Do something because :excel_sv has a key that also exists in :yaml_sv
|
126
|
+
end
|
127
|
+
end
|
128
|
+
</pre>
|
129
|
+
|
130
|
+
Of course you can then compare them as you wish and merge them in some specific way (depending on your needs).
|
131
|
+
|
132
|
+
h3. Development
|
133
|
+
|
134
|
+
Fork the i18n_kit project from Github and run the tests with
|
135
|
+
|
136
|
+
<pre>
|
137
|
+
bundle install
|
138
|
+
rake spec
|
139
|
+
</pre>
|
140
|
+
|
141
|
+
h4. Copyright
|
142
|
+
|
143
|
+
External library in @lib/yamlator.rb@: YAMLator by Henrik Nyh "http://henrik.nyh.se":http://henrik.nyh.se 2010-02-03 under the MIT license.
|
144
|
+
|
145
|
+
All other files are as well free for all under the MIT license. See LICENSE.txt for further details.
|
data/Rakefile
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "i18n_kit"
|
18
|
+
gem.homepage = "http://github.com/funkensturm/i18n_kit"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{A small tool to import I18n files from Excel and YML and compare them}
|
21
|
+
gem.description = %Q{You can import XML files that were exported using Excel, or standard i18n hierarchical YML files. Both will be normalized as I18nKit::Document objects so that they can be compared with each other. You need implement the comparison method yourself, however. I18nKit only provides the Iterator. )}
|
22
|
+
gem.email = "commanda.keen@gmail.com"
|
23
|
+
gem.authors = ["funkensturm."]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rspec/core'
|
29
|
+
require 'rspec/core/rake_task'
|
30
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
31
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
32
|
+
end
|
33
|
+
|
34
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
35
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
36
|
+
spec.rcov = true
|
37
|
+
end
|
38
|
+
|
39
|
+
task :default => :spec
|
40
|
+
|
41
|
+
require 'rake/rdoctask'
|
42
|
+
Rake::RDocTask.new do |rdoc|
|
43
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
44
|
+
|
45
|
+
rdoc.rdoc_dir = 'rdoc'
|
46
|
+
rdoc.title = "i18n_kit #{version}"
|
47
|
+
rdoc.rdoc_files.include('README*')
|
48
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
49
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/i18n_kit.gemspec
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{i18n_kit}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["funkensturm."]
|
12
|
+
s.date = %q{2011-06-07}
|
13
|
+
s.description = %q{You can import XML files that were exported using Excel, or standard i18n hierarchical YML files. Both will be normalized as I18nKit::Document objects so that they can be compared with each other. You need implement the comparison method yourself, however. I18nKit only provides the Iterator. )}
|
14
|
+
s.email = %q{commanda.keen@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.textile"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"LICENSE.txt",
|
24
|
+
"README.textile",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"i18n_kit.gemspec",
|
28
|
+
"lib/i18n_kit.rb",
|
29
|
+
"lib/i18n_kit/comparer.rb",
|
30
|
+
"lib/i18n_kit/document.rb",
|
31
|
+
"lib/i18n_kit/importer.rb",
|
32
|
+
"lib/i18n_kit/importer/excel_xml.rb",
|
33
|
+
"lib/i18n_kit/importer/yaml.rb",
|
34
|
+
"lib/yamlator.rb",
|
35
|
+
"spec/assets/excel_export.xml",
|
36
|
+
"spec/assets/fi_hierarchical.yml",
|
37
|
+
"spec/compare_spec.rb",
|
38
|
+
"spec/document_spec.rb",
|
39
|
+
"spec/importer_excel_xml_spec.rb",
|
40
|
+
"spec/importer_yml_spec.rb",
|
41
|
+
"spec/spec_helper.rb"
|
42
|
+
]
|
43
|
+
s.homepage = %q{http://github.com/funkensturm/i18n_kit}
|
44
|
+
s.licenses = ["MIT"]
|
45
|
+
s.require_paths = ["lib"]
|
46
|
+
s.rubygems_version = %q{1.5.3}
|
47
|
+
s.summary = %q{A small tool to import I18n files from Excel and YML and compare them}
|
48
|
+
|
49
|
+
if s.respond_to? :specification_version then
|
50
|
+
s.specification_version = 3
|
51
|
+
|
52
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
53
|
+
s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
|
54
|
+
s.add_runtime_dependency(%q<ya2yaml>, [">= 0"])
|
55
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
|
56
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
57
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.6.2"])
|
58
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
59
|
+
else
|
60
|
+
s.add_dependency(%q<nokogiri>, [">= 0"])
|
61
|
+
s.add_dependency(%q<ya2yaml>, [">= 0"])
|
62
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
63
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
64
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
|
65
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
66
|
+
end
|
67
|
+
else
|
68
|
+
s.add_dependency(%q<nokogiri>, [">= 0"])
|
69
|
+
s.add_dependency(%q<ya2yaml>, [">= 0"])
|
70
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
71
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
72
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
|
73
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'i18n_kit/importer'
|
2
|
+
require 'i18n_kit/document'
|
3
|
+
require 'yamlator'
|
4
|
+
|
5
|
+
module I18nKit
|
6
|
+
class Comparer
|
7
|
+
attr_accessor :documents
|
8
|
+
|
9
|
+
def initialize(documents)
|
10
|
+
@documents = documents
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](document_name)
|
14
|
+
@documents.each { |doc| return doc if doc.name == document_name }
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def compare
|
19
|
+
documents_to_compare.each do |left, right|
|
20
|
+
#puts "#{left.name} <=> #{right.name}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def documents_to_compare(&block)
|
25
|
+
docs = @documents.dup
|
26
|
+
pairs = []
|
27
|
+
while base = docs.shift do
|
28
|
+
docs.each do |doc|
|
29
|
+
#break if doc == docs.last
|
30
|
+
if block_given?
|
31
|
+
yield(base, doc)
|
32
|
+
else
|
33
|
+
pairs << [base, doc]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
pairs
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require "ya2yaml" # Dumps with unescaped UTF-8.
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
module I18nKit
|
5
|
+
class Document
|
6
|
+
attr_accessor :locale, :name
|
7
|
+
|
8
|
+
def initialize(opts={})
|
9
|
+
@name = opts.fetch(:name, :undefined)
|
10
|
+
# Each Document can only have one locale
|
11
|
+
@locale = opts.fetch(:locale, :undefined)
|
12
|
+
# This is the hierarchical format that is commonly used in standard I18n
|
13
|
+
# Note that the root node (i.e. 'en') is *not* supposed to be included
|
14
|
+
@hash = opts.fetch(:hash, {})
|
15
|
+
# This is a flattened version of the hierarchical hash
|
16
|
+
# There is again no locale at the beginning of the keys (i.e. 'one.two' instead of 'en.one.two')
|
17
|
+
@flat_hash = if opts.has_key?(:flat_hash)
|
18
|
+
opts[:flat_hash]
|
19
|
+
else
|
20
|
+
{}
|
21
|
+
end
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def []=(key, value)
|
26
|
+
yamlator = YAMLator.new(@hash)
|
27
|
+
yamlator[key] = value
|
28
|
+
@hash = yamlator.hash
|
29
|
+
@flat_hash = {}
|
30
|
+
end
|
31
|
+
|
32
|
+
def [](chain)
|
33
|
+
to_flat_hash[chain]
|
34
|
+
end
|
35
|
+
|
36
|
+
def has_key?(key)
|
37
|
+
to_flat_hash.has_key?(key)
|
38
|
+
end
|
39
|
+
|
40
|
+
def keys
|
41
|
+
to_flat_hash.keys
|
42
|
+
end
|
43
|
+
|
44
|
+
def values
|
45
|
+
to_flat_hash.values
|
46
|
+
end
|
47
|
+
|
48
|
+
# Without locale name as root
|
49
|
+
def to_hierarchical_hash
|
50
|
+
@hash
|
51
|
+
end
|
52
|
+
|
53
|
+
# With locale name as root
|
54
|
+
def to_i18n_hash
|
55
|
+
{ @locale => @hash }
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_i18n_yaml
|
59
|
+
to_i18n_hash.ya2yaml
|
60
|
+
end
|
61
|
+
|
62
|
+
def write_i18n_file(path)
|
63
|
+
File.open(path, 'w') do |file|
|
64
|
+
file.write to_i18n_yaml
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_flat_hash
|
69
|
+
# On demand flattening
|
70
|
+
populate_flat_hash(@hash) if @flat_hash.size == 0 and @hash.size > 1
|
71
|
+
@flat_hash
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_flat_yaml
|
75
|
+
to_flat_hash.ya2yaml
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_s
|
79
|
+
@name
|
80
|
+
end
|
81
|
+
|
82
|
+
protected
|
83
|
+
|
84
|
+
def populate_flat_hash(data, root=[])
|
85
|
+
data.each do |key, value|
|
86
|
+
if value.is_a?(Hash)
|
87
|
+
new_root = root + [key]
|
88
|
+
populate_flat_hash(value, new_root)
|
89
|
+
else
|
90
|
+
data.each do |k, v|
|
91
|
+
flat_key = [root] + [k]
|
92
|
+
@flat_hash[flat_key.join('.')] = v
|
93
|
+
end
|
94
|
+
return
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'i18n_kit/document'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
module I18nKit
|
5
|
+
module Importer
|
6
|
+
class ExcelXML
|
7
|
+
attr_accessor :locales, :nokodoc, :ignore_headline
|
8
|
+
attr_reader :path_or_string, :hash
|
9
|
+
|
10
|
+
def initialize(path_or_string, opts={})
|
11
|
+
@path_or_string = path_or_string
|
12
|
+
@locales = opts.fetch(:locales, [])
|
13
|
+
@ignore_headline = opts.fetch(:ignore_headline, false)
|
14
|
+
@nokodoc = if File.file?(@path_or_string)
|
15
|
+
Nokogiri::XML(File.open(@path_or_string))
|
16
|
+
else
|
17
|
+
Nokogiri::XML(@path_or_string)
|
18
|
+
end
|
19
|
+
@result = {}
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
# This method uses Nokogiri to parse the XML data
|
24
|
+
def parse
|
25
|
+
# First, we're loading all Rows of the sheet
|
26
|
+
@nokodoc.css('Row').each_with_index do |row, row_index|
|
27
|
+
# In each row, we load the columns (i.e. row cells)
|
28
|
+
columns = row.css('Cell > Data')
|
29
|
+
# We skip empty rows (i.e. rows with no or just one column)
|
30
|
+
next unless columns.size > 1
|
31
|
+
# The first row might be a header column containing the names of the columns
|
32
|
+
# In case the locales are unknown and we're in the header row, let's derive the locales from this header row
|
33
|
+
next if @ignore_headline && row_index == 0
|
34
|
+
if @locales == [] && row_index == 0
|
35
|
+
# Going through the cells of the header row
|
36
|
+
columns.each_with_index do |column, column_index|
|
37
|
+
# The first column is expected to contain the key, all others we interpret as locale name
|
38
|
+
@locales << column.text.to_sym unless column_index == 0
|
39
|
+
end
|
40
|
+
else
|
41
|
+
# This is every row except the header row
|
42
|
+
# The key is expected to be in the first column
|
43
|
+
key = columns[0].text
|
44
|
+
# Now let's read the translations for this key. I.e. all columns except the first one
|
45
|
+
locales.each_with_index do |locale, locale_index|
|
46
|
+
locale = locale.to_s
|
47
|
+
@result[locale] ||= {}
|
48
|
+
@result[locale][key] = (columns[locale_index+1] == nil ? '' : columns[locale_index+1].text)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
documents = []
|
54
|
+
@result.each { |locale, hash|
|
55
|
+
documents << ::I18nKit::Document.new(:locale => locale, :flat_hash => hash)
|
56
|
+
documents.last.name = "excel_#{locale}".to_sym
|
57
|
+
}
|
58
|
+
documents
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'i18n_kit/document'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module I18nKit
|
5
|
+
module Importer
|
6
|
+
class YAML
|
7
|
+
attr_reader :hash, :locale
|
8
|
+
|
9
|
+
def initialize(path_or_string, opts={})
|
10
|
+
raw = File.file?(path_or_string) ? File.read(path_or_string) : path_or_string
|
11
|
+
@hash = ::YAML.load(raw)
|
12
|
+
@locale = @hash.keys.first
|
13
|
+
@name = "yaml_#{@locale}".to_sym
|
14
|
+
@hash = @hash.values.first # Removing the root node
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def parse
|
19
|
+
[::I18nKit::Document.new :locale => @locale, :hash => @hash, :name => @name]
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'i18n_kit/importer/excel_xml'
|
2
|
+
require 'i18n_kit/importer/yaml'
|
3
|
+
|
4
|
+
module I18nKit
|
5
|
+
|
6
|
+
def self.import_excel(*args)
|
7
|
+
Importer::ExcelXML.new(*args).parse
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.import_yaml(*args)
|
11
|
+
Importer::YAML.new(*args).parse
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
data/lib/i18n_kit.rb
ADDED
data/lib/yamlator.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# YAMLator by Henrik Nyh <http://henrik.nyh.se> 2010-02-03 under the MIT license.
|
2
|
+
# Helps you update Rails i18n YAML files programmatically, to be used e.g. for
|
3
|
+
# editor extraction tools.
|
4
|
+
|
5
|
+
$KCODE = 'u'
|
6
|
+
|
7
|
+
require "yaml"
|
8
|
+
require "rubygems"
|
9
|
+
require "ya2yaml" # Dumps with unescaped UTF-8.
|
10
|
+
|
11
|
+
class YAMLator
|
12
|
+
attr_reader :hash
|
13
|
+
|
14
|
+
def initialize(hash)
|
15
|
+
#@data = File.file?(path_or_string) ? File.read(path_or_string) : path_or_string
|
16
|
+
@hash = hash
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_yaml
|
20
|
+
#[preamble, yaml].join
|
21
|
+
yaml
|
22
|
+
end
|
23
|
+
|
24
|
+
def []=(key, value)
|
25
|
+
chain = key.split('.')
|
26
|
+
this_hash = @hash
|
27
|
+
chain.each_with_index do |part, index|
|
28
|
+
is_last = index==chain.length-1
|
29
|
+
key_this_far = chain[0..index].join('.')
|
30
|
+
|
31
|
+
case this_hash[part]
|
32
|
+
when Hash
|
33
|
+
raise("trying to add a string to a hash key in use: #{key_this_far.inspect}") if is_last
|
34
|
+
# Uncomment the following two lines if you would like to prevent overwriting existing keys
|
35
|
+
#when String
|
36
|
+
# raise("trying to add to a string key in use: #{key_this_far.inspect}")
|
37
|
+
else
|
38
|
+
this_hash[part] = is_last ? value : {}
|
39
|
+
end
|
40
|
+
this_hash = this_hash[part]
|
41
|
+
end
|
42
|
+
value
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# Comments and blank lines in the beginning of the file.
|
48
|
+
def preamble
|
49
|
+
#@data[/\A(\s*(#.*?)?\n)+/]
|
50
|
+
end
|
51
|
+
|
52
|
+
def yaml
|
53
|
+
@hash.ya2yaml
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
#y = YAMLator.new(DATA)
|
59
|
+
#y['sv.some.foo.bar.baz'] = "boink"
|
60
|
+
#puts y.to_yaml
|
@@ -0,0 +1,190 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<?mso-application progid="Excel.Sheet"?>
|
3
|
+
|
4
|
+
<Workbook xmlns:c="urn:schemas-microsoft-com:office:component:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:x2="http://schemas.microsoft.com/office/excel/2003/xml" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:x="urn:schemas-microsoft-com:office:excel">
|
5
|
+
|
6
|
+
<OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
|
7
|
+
<Colors>
|
8
|
+
<Color>
|
9
|
+
<Index>3</Index>
|
10
|
+
<RGB>#c0c0c0</RGB>
|
11
|
+
</Color>
|
12
|
+
<Color>
|
13
|
+
<Index>4</Index>
|
14
|
+
<RGB>#dd0806</RGB>
|
15
|
+
</Color>
|
16
|
+
<Color>
|
17
|
+
<Index>5</Index>
|
18
|
+
<RGB>#ff0000</RGB>
|
19
|
+
</Color>
|
20
|
+
</Colors>
|
21
|
+
</OfficeDocumentSettings>
|
22
|
+
|
23
|
+
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
|
24
|
+
<WindowHeight>9000</WindowHeight>
|
25
|
+
<WindowWidth>13860</WindowWidth>
|
26
|
+
<WindowTopX>240</WindowTopX>
|
27
|
+
<WindowTopY>75</WindowTopY>
|
28
|
+
<ProtectStructure>False</ProtectStructure>
|
29
|
+
<ProtectWindows>False</ProtectWindows>
|
30
|
+
</ExcelWorkbook>
|
31
|
+
|
32
|
+
<Styles>
|
33
|
+
<Style ss:ID="Default" ss:Name="Default">
|
34
|
+
<Font ss:FontName="Arial1"/>
|
35
|
+
</Style>
|
36
|
+
<Style ss:ID="Result" ss:Name="Result">
|
37
|
+
<Font ss:Bold="1" ss:FontName="Arial1" ss:Italic="1" ss:Underline="Single"/>
|
38
|
+
</Style>
|
39
|
+
<Style ss:ID="Result2" ss:Name="Result2">
|
40
|
+
<Font ss:Bold="1" ss:FontName="Arial1" ss:Italic="1" ss:Underline="Single"/>
|
41
|
+
<NumberFormat ss:Format="Currency"/>
|
42
|
+
</Style>
|
43
|
+
<Style ss:ID="Heading" ss:Name="Heading">
|
44
|
+
<Font ss:Bold="1" ss:FontName="Arial1" ss:Italic="1" ss:Size="16"/>
|
45
|
+
</Style>
|
46
|
+
<Style ss:ID="Heading1" ss:Name="Heading1">
|
47
|
+
<Alignment ss:Rotate="90"/>
|
48
|
+
<Font ss:Bold="1" ss:FontName="Arial1" ss:Italic="1" ss:Size="16"/>
|
49
|
+
</Style>
|
50
|
+
<Style ss:ID="co1"/>
|
51
|
+
<Style ss:ID="co2"/>
|
52
|
+
<Style ss:ID="co3"/>
|
53
|
+
<Style ss:ID="co4"/>
|
54
|
+
<Style ss:ID="co5"/>
|
55
|
+
<Style ss:ID="ta1"/>
|
56
|
+
<Style ss:ID="ce1">
|
57
|
+
<Font ss:Bold="1" ss:FontName="Arial2" ss:Size="10"/>
|
58
|
+
<NumberFormat ss:Format="General"/>
|
59
|
+
</Style>
|
60
|
+
<Style ss:ID="ce2">
|
61
|
+
<Font ss:FontName="Arial1" ss:Size="10"/>
|
62
|
+
<NumberFormat ss:Format="General"/>
|
63
|
+
</Style>
|
64
|
+
<Style ss:ID="ce3">
|
65
|
+
<Alignment ss:Vertical="Automatic" ss:WrapText="1" ss:Indent="0" ss:Rotate="0"/>
|
66
|
+
<Font ss:Bold="1" ss:FontName="Arial2" ss:Size="10"/>
|
67
|
+
<NumberFormat ss:Format="General"/>
|
68
|
+
</Style>
|
69
|
+
<Style ss:ID="ce4">
|
70
|
+
<Alignment ss:Vertical="Automatic" ss:WrapText="1" ss:Indent="0" ss:Rotate="0"/>
|
71
|
+
<Font ss:FontName="Arial1" ss:Size="10"/>
|
72
|
+
<NumberFormat ss:Format="General"/>
|
73
|
+
</Style>
|
74
|
+
<Style ss:ID="ce5">
|
75
|
+
<Alignment ss:Vertical="Automatic" ss:WrapText="1" ss:Indent="0" ss:Rotate="0"/>
|
76
|
+
</Style>
|
77
|
+
<Style ss:ID="ce6">
|
78
|
+
<Alignment ss:Vertical="Automatic" ss:WrapText="1" ss:Indent="0" ss:Rotate="0"/>
|
79
|
+
<Font ss:Color="#dd0806" ss:FontName="Arial1" ss:Size="10"/>
|
80
|
+
<NumberFormat ss:Format="General"/>
|
81
|
+
</Style>
|
82
|
+
<Style ss:ID="ce7">
|
83
|
+
<Font ss:Bold="1" ss:Color="#dd0806" ss:FontName="Arial1" ss:Size="10"/>
|
84
|
+
<NumberFormat ss:Format="General"/>
|
85
|
+
</Style>
|
86
|
+
<Style ss:ID="ce8">
|
87
|
+
<Font ss:Color="#dd0806" ss:FontName="Arial1" ss:Size="10"/>
|
88
|
+
</Style>
|
89
|
+
<Style ss:ID="ce9">
|
90
|
+
<Font ss:Color="#dd0806" ss:FontName="Arial1" ss:Size="10"/>
|
91
|
+
<NumberFormat ss:Format="General"/>
|
92
|
+
</Style>
|
93
|
+
<Style ss:ID="ta_extref"/>
|
94
|
+
<Style ss:ID="T1">
|
95
|
+
<Font ss:Color="#dd0806" ss:FontName="Arial1" ss:Size="10" ss:VerticalAlign="Subscript"/>
|
96
|
+
</Style>
|
97
|
+
<Style ss:ID="T2">
|
98
|
+
<Font ss:FontName="Arial1" ss:Size="10" ss:VerticalAlign="Subscript"/>
|
99
|
+
</Style>
|
100
|
+
<Style ss:ID="T3">
|
101
|
+
<Font ss:FontName="Times New Roman" ss:Size="12" ss:VerticalAlign="Subscript"/>
|
102
|
+
</Style>
|
103
|
+
</Styles>
|
104
|
+
|
105
|
+
<ss:Worksheet ss:Name="Translations">
|
106
|
+
|
107
|
+
<Table ss:StyleID="ta1">
|
108
|
+
<Column ss:Width="232.1568"/>
|
109
|
+
<Column ss:Width="232.1568"/>
|
110
|
+
<Column ss:Width="199.8144"/>
|
111
|
+
<Column ss:Width="285.8472"/>
|
112
|
+
<Column ss:Width="171.1008"/>
|
113
|
+
<Column ss:Span="1018" ss:Width="63.8352"/>
|
114
|
+
|
115
|
+
<Row ss:Height="11.988">
|
116
|
+
<Cell ss:StyleID="ce1">
|
117
|
+
<Data ss:Type="String">nyckel</Data>
|
118
|
+
</Cell>
|
119
|
+
<Cell ss:StyleID="ce3">
|
120
|
+
<Data ss:Type="String">svenska</Data>
|
121
|
+
</Cell>
|
122
|
+
<Cell ss:StyleID="ce3">
|
123
|
+
<Data ss:Type="String">engelska</Data>
|
124
|
+
</Cell>
|
125
|
+
<Cell ss:StyleID="ce3">
|
126
|
+
<Data ss:Type="String">finska</Data>
|
127
|
+
</Cell>
|
128
|
+
<Cell ss:StyleID="ce7">
|
129
|
+
<Data ss:Type="String">GM Kommentar</Data>
|
130
|
+
</Cell>
|
131
|
+
<Cell ss:Index="1024"/>
|
132
|
+
</Row>
|
133
|
+
|
134
|
+
<Row ss:Height="11.988">
|
135
|
+
<Cell ss:StyleID="ce2">
|
136
|
+
<Data ss:Type="String">base_helper.all</Data>
|
137
|
+
</Cell>
|
138
|
+
<Cell ss:StyleID="ce4">
|
139
|
+
<Data ss:Type="String">Alla</Data>
|
140
|
+
</Cell>
|
141
|
+
<Cell ss:StyleID="ce4">
|
142
|
+
<Data ss:Type="String">All</Data>
|
143
|
+
</Cell>
|
144
|
+
<Cell ss:StyleID="ce4">
|
145
|
+
<Data ss:Type="String">Kaikki</Data>
|
146
|
+
</Cell>
|
147
|
+
<Cell ss:Index="1024"/>
|
148
|
+
</Row>
|
149
|
+
|
150
|
+
<Row ss:Height="11.988">
|
151
|
+
<Cell ss:StyleID="ce2">
|
152
|
+
<Data ss:Type="String">base_helper.time_left</Data>
|
153
|
+
</Cell>
|
154
|
+
<Cell ss:StyleID="ce4">
|
155
|
+
<Data ss:Type="String">%{time} kvar</Data>
|
156
|
+
</Cell>
|
157
|
+
<Cell ss:StyleID="ce4">
|
158
|
+
<Data ss:Type="String">%{time} left</Data>
|
159
|
+
</Cell>
|
160
|
+
<Cell ss:StyleID="ce4">
|
161
|
+
<Data ss:Type="String">%{time} jäljellä</Data>
|
162
|
+
</Cell>
|
163
|
+
<Cell ss:Index="1024"/>
|
164
|
+
</Row>
|
165
|
+
|
166
|
+
<Row ss:Height="11.988">
|
167
|
+
<Cell ss:StyleID="ce2">
|
168
|
+
<Data ss:Type="String">support.select.prompt</Data>
|
169
|
+
</Cell>
|
170
|
+
<Cell ss:StyleID="ce4">
|
171
|
+
<Data ss:Type="String">Välj</Data>
|
172
|
+
</Cell>
|
173
|
+
<Cell ss:StyleID="ce4">
|
174
|
+
<Data ss:Type="String">Please select</Data>
|
175
|
+
</Cell>
|
176
|
+
<Cell ss:StyleID="ce4">
|
177
|
+
<Data ss:Type="String">Valitse</Data>
|
178
|
+
</Cell>
|
179
|
+
<Cell ss:StyleID="ce4">
|
180
|
+
<Data ss:Type="String">This is a comment</Data>
|
181
|
+
</Cell>
|
182
|
+
<Cell ss:Index="1024"/>
|
183
|
+
</Row>
|
184
|
+
|
185
|
+
</Table>
|
186
|
+
|
187
|
+
<x:WorksheetOptions/>
|
188
|
+
|
189
|
+
</ss:Worksheet>
|
190
|
+
</Workbook>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
fi:
|
2
|
+
activerecord:
|
3
|
+
attributes:
|
4
|
+
account:
|
5
|
+
account_type: "Pankkitilin tyyppi"
|
6
|
+
number: Tilinumero
|
7
|
+
auction:
|
8
|
+
estimate: Lähtöhinta
|
9
|
+
estimate_upper: "Lähtöhinta alkaen"
|
10
|
+
reserve_price: Pohjahinta
|
11
|
+
percentage_fee:
|
12
|
+
manual_unspecified: ~
|
13
|
+
percent_thereafter: "%{percent} sen jälkeen"
|
14
|
+
percent_to_limit: "%{percent} - %{limit}"
|
15
|
+
seller_invoice:
|
16
|
+
description_with_vat: "Toimeksiantolaskelma #%{ocr}"
|
17
|
+
description_without_vat: "Toimeksiantolaskelma (ilman alv) #%{ocr}"
|
18
|
+
shared:
|
19
|
+
actions:
|
20
|
+
add: Lisää
|
21
|
+
log_in: "Kirjaudu sisään"
|
22
|
+
order: Tilaa
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
describe "I18nKit Comparer Test" do
|
5
|
+
|
6
|
+
it "should know which documents to compare to one another" do
|
7
|
+
one = I18nKit::Document.new
|
8
|
+
two = I18nKit::Document.new
|
9
|
+
three = I18nKit::Document.new
|
10
|
+
four = I18nKit::Document.new
|
11
|
+
array = [one, two, three, four]
|
12
|
+
comparer = I18nKit::Comparer.new array
|
13
|
+
comparer.documents.should == array
|
14
|
+
comparer.documents_to_compare.should == [[one, two], [one, three], [one, four], [two, three], [two, four], [three, four]]
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should compare" do
|
18
|
+
documents = ::I18nKit::Importer::ExcelXML.new(XML_PATH, :ignore_headline => true, :locales => [:sv, :en, :fi]).parse +
|
19
|
+
::I18nKit::Importer::YAML.new(YML_FI_HIERARCHICAL_PATH).parse
|
20
|
+
documents[0].name = 'First'
|
21
|
+
documents[1].name = 'Second'
|
22
|
+
documents[2].name = 'Third'
|
23
|
+
documents[3].name = 'Fourth'
|
24
|
+
comparer = I18nKit::Comparer.new(documents)
|
25
|
+
# TODO: test automated comparison in the future :)
|
26
|
+
# comparer.compare
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
describe "I18nKit Document Test" do
|
5
|
+
|
6
|
+
it "should store an hierarchical hash" do
|
7
|
+
document = ::I18nKit::Importer::YAML.new(YML_FI_HIERARCHICAL_PATH).parse.first
|
8
|
+
document.to_hierarchical_hash.should == YAML.load(File.read(YML_FI_HIERARCHICAL_PATH)).values.first
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should flatten a hierarchical hash" do
|
12
|
+
document = ::I18nKit::Importer::YAML.new(YML_FI_HIERARCHICAL_PATH).parse.first
|
13
|
+
document.to_flat_hash.should == YML_FI_FLAT
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should spill out a yaml file without locale as root node" do
|
17
|
+
document = ::I18nKit::Importer::YAML.new(YML_FI_HIERARCHICAL_PATH).parse.first
|
18
|
+
YAML.load(document.to_flat_yaml).should == YML_FI_FLAT # Hard to test because Hashes are unordered when exported to YML
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should export proper i18n yaml files" do
|
22
|
+
document = ::I18nKit::Importer::YAML.new(YML_FI_HIERARCHICAL_PATH).parse.first
|
23
|
+
document.to_i18n_hash.should == YAML.load(File.read(YML_FI_HIERARCHICAL_PATH))
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should replace a value for a given chain" do
|
27
|
+
document = ::I18nKit::Importer::YAML.new(YML_FI_HIERARCHICAL_PATH).parse.first
|
28
|
+
document['activerecord.attributes.account.number'] = 'New Value'
|
29
|
+
document.to_i18n_hash['fi']['activerecord']['attributes']['account']['number'].should == 'New Value'
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "I18nKit ExcelXML Importer Test" do
|
4
|
+
|
5
|
+
it "should import a file at a given path and load it with Nokogiri" do
|
6
|
+
importer = ::I18nKit::Importer::ExcelXML.new(XML_PATH)
|
7
|
+
importer.path_or_string.should == XML_PATH
|
8
|
+
importer.nokodoc.should be_an_instance_of Nokogiri::XML::Document
|
9
|
+
importer.nokodoc.children.size.should be > 0
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should import a String and load it with Nokogiri" do
|
13
|
+
importer = ::I18nKit::Importer::ExcelXML.new File.read(XML_PATH)
|
14
|
+
importer.path_or_string.should == File.read(XML_PATH)
|
15
|
+
importer.nokodoc.should be_an_instance_of Nokogiri::XML::Document
|
16
|
+
importer.nokodoc.children.size.should be > 0
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should know how to parse an .xml file" do
|
20
|
+
documents = ::I18nKit::Importer::ExcelXML.new(XML_PATH).parse
|
21
|
+
documents.should be_an_instance_of Array
|
22
|
+
documents.each do |doc|
|
23
|
+
doc.should be_an_instance_of I18nKit::Document
|
24
|
+
case doc.locale
|
25
|
+
when 'svenska'
|
26
|
+
doc.to_flat_hash.should == XML_HASH_SV
|
27
|
+
when "engelska"
|
28
|
+
doc.to_flat_hash.should == XML_HASH_EN
|
29
|
+
when "finska"
|
30
|
+
doc.to_flat_hash.should == XML_HASH_FI
|
31
|
+
when "GM Kommentar"
|
32
|
+
doc.to_flat_hash.should == XML_HASH_COMMENTS
|
33
|
+
else
|
34
|
+
flunk 'There is one translation too much'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should know how to parse an .xml file with manually defined column names" do
|
40
|
+
importer = ::I18nKit::Importer::ExcelXML.new(XML_PATH, :ignore_headline => true, :locales => [:sv, :en, :fi, :comment])
|
41
|
+
documents = importer.parse
|
42
|
+
documents.should be_an_instance_of Array
|
43
|
+
documents.each do |doc|
|
44
|
+
doc.should be_an_instance_of I18nKit::Document
|
45
|
+
case doc.locale
|
46
|
+
when "sv"
|
47
|
+
doc.to_flat_hash.should == XML_HASH_SV
|
48
|
+
when "en"
|
49
|
+
doc.to_flat_hash.should == XML_HASH_EN
|
50
|
+
when "fi"
|
51
|
+
doc.to_flat_hash.should == XML_HASH_FI
|
52
|
+
when "comment"
|
53
|
+
doc.to_flat_hash.should == XML_HASH_COMMENTS
|
54
|
+
else
|
55
|
+
flunk 'There is one translation too much'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should know how to parse an .xml file with a subset of manually defined column names" do
|
61
|
+
importer = ::I18nKit::Importer::ExcelXML.new(XML_PATH, :ignore_headline => true, :locales => [:sv, :en, :fi])
|
62
|
+
documents = importer.parse
|
63
|
+
documents.should be_an_instance_of Array
|
64
|
+
documents.each do |doc|
|
65
|
+
doc.should be_an_instance_of I18nKit::Document
|
66
|
+
case doc.locale
|
67
|
+
when "sv"
|
68
|
+
doc.to_flat_hash.should == XML_HASH_SV
|
69
|
+
when "en"
|
70
|
+
doc.to_flat_hash.should == XML_HASH_EN
|
71
|
+
when "fi"
|
72
|
+
doc.to_flat_hash.should == XML_HASH_FI
|
73
|
+
else
|
74
|
+
flunk 'There is one translation too much'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "I18nKit Importer Test" do
|
4
|
+
|
5
|
+
it "should identify the locale of a standard Rails i18n yml file" do
|
6
|
+
documents = ::I18nKit::Importer::YAML.new(YML_FI_HIERARCHICAL_PATH).parse
|
7
|
+
documents.should be_an_instance_of Array
|
8
|
+
documents.size.should == 1
|
9
|
+
documents.first.should be_an_instance_of I18nKit::Document
|
10
|
+
documents.first.locale.should == 'fi'
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should chop of the root of imported yaml files" do
|
14
|
+
importer = ::I18nKit::Importer::YAML.new(YML_FI_HIERARCHICAL_PATH)
|
15
|
+
hierarchical_hash = YAML.load(File.read(YML_FI_HIERARCHICAL_PATH)).values.first
|
16
|
+
importer.hash.should == hierarchical_hash
|
17
|
+
importer.parse.first.to_hierarchical_hash.should == hierarchical_hash
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'i18n_kit'
|
5
|
+
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
7
|
+
# in ./support/ and its subdirectories.
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
XML_PATH = File.join(File.dirname(__FILE__), 'assets', 'excel_export.xml')
|
11
|
+
|
12
|
+
XML_HASH_FI = { "support.select.prompt" => "Valitse",
|
13
|
+
"base_helper.time_left" => "%{time} j\303\244ljell\303\244",
|
14
|
+
"base_helper.all" => "Kaikki" }
|
15
|
+
|
16
|
+
XML_HASH_SV = { "support.select.prompt" => "V\303\244lj",
|
17
|
+
"base_helper.time_left" => "%{time} kvar",
|
18
|
+
"base_helper.all" => "Alla" }
|
19
|
+
|
20
|
+
XML_HASH_EN = { "support.select.prompt" => "Please select",
|
21
|
+
"base_helper.time_left" => "%{time} left",
|
22
|
+
"base_helper.all" => "All" }
|
23
|
+
|
24
|
+
XML_HASH_COMMENTS = { "support.select.prompt" => "This is a comment",
|
25
|
+
"base_helper.time_left" => "",
|
26
|
+
"base_helper.all" => "" }
|
27
|
+
|
28
|
+
YML_FI_HIERARCHICAL_PATH = File.join(File.dirname(__FILE__), 'assets', 'fi_hierarchical.yml')
|
29
|
+
|
30
|
+
YML_FI_FLAT = { "activerecord.attributes.account.account_type" => "Pankkitilin tyyppi",
|
31
|
+
"activerecord.attributes.account.number" => "Tilinumero",
|
32
|
+
"activerecord.attributes.auction.estimate" => "L\303\244ht\303\266hinta",
|
33
|
+
"activerecord.attributes.auction.estimate_upper" => "L\303\244ht\303\266hinta alkaen",
|
34
|
+
"activerecord.attributes.auction.reserve_price" => "Pohjahinta",
|
35
|
+
"percentage_fee.manual_unspecified" => nil,
|
36
|
+
"percentage_fee.percent_thereafter" => "%{percent} sen j\303\244lkeen",
|
37
|
+
"percentage_fee.percent_to_limit" => "%{percent} - %{limit}",
|
38
|
+
"seller_invoice.description_without_vat" => "Toimeksiantolaskelma (ilman alv) #%{ocr}",
|
39
|
+
"seller_invoice.description_with_vat" => "Toimeksiantolaskelma #%{ocr}",
|
40
|
+
"shared.actions.add" => "Lis\303\244\303\244",
|
41
|
+
"shared.actions.order" => "Tilaa",
|
42
|
+
"shared.actions.log_in" => "Kirjaudu sis\303\244\303\244n" }
|
43
|
+
|
44
|
+
RSpec.configure do |config|
|
45
|
+
|
46
|
+
end
|
metadata
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: i18n_kit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- funkensturm.
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-06-07 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
prerelease: false
|
23
|
+
type: :runtime
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
name: nokogiri
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
prerelease: false
|
37
|
+
type: :runtime
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
name: ya2yaml
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
prerelease: false
|
51
|
+
type: :development
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ~>
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 2
|
60
|
+
- 3
|
61
|
+
- 0
|
62
|
+
version: 2.3.0
|
63
|
+
name: rspec
|
64
|
+
version_requirements: *id003
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
prerelease: false
|
67
|
+
type: :development
|
68
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ~>
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
hash: 23
|
74
|
+
segments:
|
75
|
+
- 1
|
76
|
+
- 0
|
77
|
+
- 0
|
78
|
+
version: 1.0.0
|
79
|
+
name: bundler
|
80
|
+
version_requirements: *id004
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
prerelease: false
|
83
|
+
type: :development
|
84
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
hash: 11
|
90
|
+
segments:
|
91
|
+
- 1
|
92
|
+
- 6
|
93
|
+
- 2
|
94
|
+
version: 1.6.2
|
95
|
+
name: jeweler
|
96
|
+
version_requirements: *id005
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
prerelease: false
|
99
|
+
type: :development
|
100
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
hash: 3
|
106
|
+
segments:
|
107
|
+
- 0
|
108
|
+
version: "0"
|
109
|
+
name: rcov
|
110
|
+
version_requirements: *id006
|
111
|
+
description: You can import XML files that were exported using Excel, or standard i18n hierarchical YML files. Both will be normalized as I18nKit::Document objects so that they can be compared with each other. You need implement the comparison method yourself, however. I18nKit only provides the Iterator. )
|
112
|
+
email: commanda.keen@gmail.com
|
113
|
+
executables: []
|
114
|
+
|
115
|
+
extensions: []
|
116
|
+
|
117
|
+
extra_rdoc_files:
|
118
|
+
- LICENSE.txt
|
119
|
+
- README.textile
|
120
|
+
files:
|
121
|
+
- .document
|
122
|
+
- Gemfile
|
123
|
+
- Gemfile.lock
|
124
|
+
- LICENSE.txt
|
125
|
+
- README.textile
|
126
|
+
- Rakefile
|
127
|
+
- VERSION
|
128
|
+
- i18n_kit.gemspec
|
129
|
+
- lib/i18n_kit.rb
|
130
|
+
- lib/i18n_kit/comparer.rb
|
131
|
+
- lib/i18n_kit/document.rb
|
132
|
+
- lib/i18n_kit/importer.rb
|
133
|
+
- lib/i18n_kit/importer/excel_xml.rb
|
134
|
+
- lib/i18n_kit/importer/yaml.rb
|
135
|
+
- lib/yamlator.rb
|
136
|
+
- spec/assets/excel_export.xml
|
137
|
+
- spec/assets/fi_hierarchical.yml
|
138
|
+
- spec/compare_spec.rb
|
139
|
+
- spec/document_spec.rb
|
140
|
+
- spec/importer_excel_xml_spec.rb
|
141
|
+
- spec/importer_yml_spec.rb
|
142
|
+
- spec/spec_helper.rb
|
143
|
+
has_rdoc: true
|
144
|
+
homepage: http://github.com/funkensturm/i18n_kit
|
145
|
+
licenses:
|
146
|
+
- MIT
|
147
|
+
post_install_message:
|
148
|
+
rdoc_options: []
|
149
|
+
|
150
|
+
require_paths:
|
151
|
+
- lib
|
152
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
hash: 3
|
158
|
+
segments:
|
159
|
+
- 0
|
160
|
+
version: "0"
|
161
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
162
|
+
none: false
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
hash: 3
|
167
|
+
segments:
|
168
|
+
- 0
|
169
|
+
version: "0"
|
170
|
+
requirements: []
|
171
|
+
|
172
|
+
rubyforge_project:
|
173
|
+
rubygems_version: 1.5.3
|
174
|
+
signing_key:
|
175
|
+
specification_version: 3
|
176
|
+
summary: A small tool to import I18n files from Excel and YML and compare them
|
177
|
+
test_files: []
|
178
|
+
|