i18n_kit 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|