langulator 0.0.4 → 0.0.5
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/README.md +35 -6
- data/bin/langulator +3 -0
- data/features/compile_aggregate.feature +13 -0
- data/features/decompile_aggregate.feature +13 -0
- data/features/fixtures/en.yml +8 -0
- data/features/fixtures/fr.yml +10 -0
- data/features/fixtures/in.yml +38 -0
- data/features/fixtures/lang/en.yml +5 -0
- data/features/fixtures/lang/fr.yml +5 -0
- data/features/step_definitions/aggregate_steps.rb +76 -0
- data/features/support/env.rb +13 -0
- data/features/support/hooks.rb +20 -0
- data/langulator.gemspec +3 -1
- data/lib/langulator.rb +6 -3
- data/lib/langulator/aggregate_translation.rb +67 -0
- data/lib/langulator/cli.rb +30 -0
- data/lib/langulator/individual_translation.rb +8 -0
- data/lib/langulator/individual_translations.rb +115 -0
- data/lib/langulator/translation.rb +44 -0
- data/spec/aggregate_translation_spec.rb +45 -0
- data/spec/fixtures/whatever.yml +3 -0
- data/spec/individual_translation_spec.rb +13 -0
- data/spec/individual_translations_spec.rb +58 -0
- data/spec/translation_interface.rb +7 -0
- data/spec/translation_spec.rb +59 -0
- metadata +63 -10
- data/lib/langulator/aggregate.rb +0 -124
- data/lib/langulator/loader.rb +0 -44
- data/spec/aggregate_spec.rb +0 -141
- data/spec/loader_spec.rb +0 -46
data/README.md
CHANGED
@@ -4,7 +4,7 @@ Manage your i18n.
|
|
4
4
|
|
5
5
|
Or rather: compile it into a single managable file and give it to the translator, so you don't have to deal with it.
|
6
6
|
|
7
|
-
For example, given that you've written the original translations in English and also need French and Norwegian versions, and that you have multiple paths that contain i18n yml files
|
7
|
+
For example, given that you've written the original translations in English and also need French and Norwegian versions, and that you have multiple paths that contain i18n yml files (`<language>.yml`), then this will give you a single output file where each key is listed with the translations immediately below it.
|
8
8
|
|
9
9
|
If you have partial translations in the target languages, these will be included.
|
10
10
|
|
@@ -20,7 +20,7 @@ If you need something other than US-ASCII, you may want to consider using ruby 1
|
|
20
20
|
|
21
21
|
In 1.9.2 (patch 290):
|
22
22
|
|
23
|
-
|
23
|
+
S\xC3\xB8knadstekst
|
24
24
|
|
25
25
|
In 1.9.3:
|
26
26
|
|
@@ -43,13 +43,17 @@ Or install it yourself as:
|
|
43
43
|
|
44
44
|
## Usage
|
45
45
|
|
46
|
-
###
|
46
|
+
### In Ruby
|
47
|
+
|
48
|
+
#### Compile
|
47
49
|
|
48
50
|
* load individual translations
|
49
51
|
* combine into aggregated translations
|
50
52
|
* write to an aggregate file
|
51
53
|
|
52
|
-
|
54
|
+
```
|
55
|
+
Langulator.compile(:source_language => :en, :target_languages => [:fr, :no], :base_path => '**/i18n/', :to => '/tmp/translations.yml')
|
56
|
+
```
|
53
57
|
|
54
58
|
Input:
|
55
59
|
|
@@ -93,13 +97,16 @@ Outputs:
|
|
93
97
|
fr:
|
94
98
|
no:
|
95
99
|
|
96
|
-
|
100
|
+
#### Decompile
|
97
101
|
|
98
102
|
* load an aggregate file
|
99
103
|
* separate into individual translations
|
100
104
|
* write to individual translation files
|
101
105
|
|
102
|
-
|
106
|
+
|
107
|
+
```
|
108
|
+
Langulator.decompile(:from => './tmp/translations.yml')
|
109
|
+
```
|
103
110
|
|
104
111
|
Input:
|
105
112
|
|
@@ -152,6 +159,28 @@ Output:
|
|
152
159
|
main_course: Steak
|
153
160
|
desert: Sjokolademousse
|
154
161
|
|
162
|
+
### CLI
|
163
|
+
|
164
|
+
```
|
165
|
+
$~: langulator help
|
166
|
+
|
167
|
+
$~: langulator help compile
|
168
|
+
|
169
|
+
$~: langulator help decompile
|
170
|
+
```
|
171
|
+
|
172
|
+
#### Compile
|
173
|
+
|
174
|
+
```
|
175
|
+
$~: langulator compile -s en -t fr nb-no -p 'config/locales/**/' -f ./tmp/translations.yml
|
176
|
+
```
|
177
|
+
|
178
|
+
#### Decompile
|
179
|
+
|
180
|
+
```
|
181
|
+
$~: langulator decompile -f ./tmp/translations.yml
|
182
|
+
```
|
183
|
+
|
155
184
|
## TODO
|
156
185
|
|
157
186
|
* handle yml files that are namespaced by the language, e.g.
|
data/bin/langulator
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
Feature: Aggregate individual translation files
|
2
|
+
|
3
|
+
Combines any number of source translation files into a single file,
|
4
|
+
where all the translations for a single key are grouped for easy translation.
|
5
|
+
|
6
|
+
@compile
|
7
|
+
Scenario: Compile aggregate
|
8
|
+
Given a website in English, French, and Norwegian
|
9
|
+
And there is a translation file for English
|
10
|
+
And there is an outdated translation file for French
|
11
|
+
And there is no translation file for Norwegian
|
12
|
+
When the aggregate file is compiled
|
13
|
+
Then the output file collects the translation keys for easy translation
|
@@ -0,0 +1,13 @@
|
|
1
|
+
Feature: Separate an aggregate into individual i18n files
|
2
|
+
|
3
|
+
Takes an aggregate translation file and writes the translated keys into
|
4
|
+
their respective i18n files at their original locations.
|
5
|
+
|
6
|
+
@decompile
|
7
|
+
Scenario: Decompile aggregate
|
8
|
+
Given a website in English, French, and Norwegian
|
9
|
+
And there is an aggregate translation file
|
10
|
+
When the aggregate file is decompiled
|
11
|
+
Then the English translations should be complete
|
12
|
+
And the French translations should be complete
|
13
|
+
And the Norwegian translations should be complete
|
@@ -0,0 +1,38 @@
|
|
1
|
+
---
|
2
|
+
features/fixtures/:
|
3
|
+
food:
|
4
|
+
breakfast:
|
5
|
+
:english: yoghurt
|
6
|
+
:norsk: joggurt
|
7
|
+
:francais: yaourt
|
8
|
+
lunch:
|
9
|
+
:english: sandwich
|
10
|
+
:norsk: smørbrød
|
11
|
+
:francais: sandwich
|
12
|
+
dinner:
|
13
|
+
main_course:
|
14
|
+
:english: steak
|
15
|
+
:norsk: steak
|
16
|
+
:francais: biffteak
|
17
|
+
side:
|
18
|
+
:english: baked potato
|
19
|
+
:norsk: bakt potet
|
20
|
+
:francais: pomme de terre au four
|
21
|
+
desert:
|
22
|
+
:english: chocolate mousse
|
23
|
+
:norsk: sjokolademousse
|
24
|
+
:francais: mousse au chocolat
|
25
|
+
features/fixtures/lang/:
|
26
|
+
volume:
|
27
|
+
sound:
|
28
|
+
:english: loud
|
29
|
+
:norsk: høyt
|
30
|
+
:francais: haut
|
31
|
+
liquid:
|
32
|
+
:english: full
|
33
|
+
:norsk: fullt
|
34
|
+
:francais: plein
|
35
|
+
hair:
|
36
|
+
:english: because I'm worth it
|
37
|
+
:norsk: for det er jeg verdt
|
38
|
+
:francais: parce que je le vaux bien
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
Given /a website in English, French, and Norwegian$/ do
|
4
|
+
end
|
5
|
+
|
6
|
+
Given /^there is a translation file for English$/ do
|
7
|
+
end
|
8
|
+
|
9
|
+
Given /^there is an outdated translation file for French$/ do
|
10
|
+
end
|
11
|
+
|
12
|
+
Given /^there is no translation file for Norwegian$/ do
|
13
|
+
end
|
14
|
+
|
15
|
+
Given /^there is an aggregate translation file$/ do
|
16
|
+
end
|
17
|
+
|
18
|
+
When /the aggregate file is compiled/ do
|
19
|
+
Langulator.compile(:source_language => :en, :target_languages => [:fr, :no], :base_path => 'features/fixtures/**/', :to => AGGREGATE_FILE)
|
20
|
+
end
|
21
|
+
|
22
|
+
When /^the aggregate file is decompiled$/ do
|
23
|
+
# Using full language name rather than the iso-code to avoid
|
24
|
+
# overwriting the individual files used for input in the compile feature.
|
25
|
+
# Ideally I should just use different languages, but I don't speak any others.
|
26
|
+
Langulator.decompile(:languages => [:english, :francais, :norsk], :from => INFILE)
|
27
|
+
end
|
28
|
+
|
29
|
+
Then /^the output file collects the translation keys for easy translation$/ do
|
30
|
+
expected = {
|
31
|
+
"features/fixtures/" => {
|
32
|
+
"food" => {
|
33
|
+
"breakfast" => {:en => "yoghurt", :no => nil, :fr => "yaourt"},
|
34
|
+
"lunch" => {:en => "sandwich", :no => nil, :fr => "sandwich"},
|
35
|
+
"dinner" => {
|
36
|
+
"main_course" => {:en => "steak", :no => nil, :fr => "biffteak"},
|
37
|
+
"side" => {:en => "baked potato", :no => nil, :fr => nil},
|
38
|
+
"desert" => {:en => "chocolate mousse", :no => nil, :fr => "mousse au chocolat"}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
},
|
42
|
+
"features/fixtures/lang/" => {
|
43
|
+
"volume" => {
|
44
|
+
"sound" => {:en => "loud", :no => nil, :fr => "haut"},
|
45
|
+
"liquid" => {:en => "full", :no => nil, :fr => "plein"},
|
46
|
+
"hair" => {:en => "because I'm worth it", :no => nil, :fr => "parce que je le vaux bien"}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
YAML.load(File.read(AGGREGATE_FILE)).should eq(expected)
|
51
|
+
end
|
52
|
+
|
53
|
+
Then /^the English translations should be complete$/ do
|
54
|
+
expected = {"food"=>{"breakfast"=>"yoghurt", "lunch"=>"sandwich", "dinner"=>{"main_course"=>"steak", "side"=>"baked potato", "desert"=>"chocolate mousse"}}}
|
55
|
+
english = YAML.load(File.read('features/fixtures/english.yml')).should eq(expected)
|
56
|
+
|
57
|
+
expected = {"volume"=>{"sound"=>"loud", "liquid"=>"full", "hair"=>"because I'm worth it"}}
|
58
|
+
nested_english = YAML.load(File.read('features/fixtures/lang/english.yml')).should eq(expected)
|
59
|
+
end
|
60
|
+
|
61
|
+
Then /^the French translations should be complete$/ do
|
62
|
+
expected = {"food"=>{"breakfast"=>"yaourt", "lunch"=>"sandwich", "dinner"=>{"main_course"=>"biffteak", "side"=>"pomme de terre au four", "desert"=>"mousse au chocolat"}}}
|
63
|
+
french = YAML.load(File.read('features/fixtures/francais.yml')).should eq(expected)
|
64
|
+
|
65
|
+
expected = {"volume"=>{"sound"=>"haut", "liquid"=>"plein", "hair"=>"parce que je le vaux bien"}}
|
66
|
+
nested_french = YAML.load(File.read('features/fixtures/lang/francais.yml')).should eq(expected)
|
67
|
+
end
|
68
|
+
|
69
|
+
Then /^the Norwegian translations should be complete$/ do
|
70
|
+
expected = {"food"=>{"breakfast"=>"joggurt", "lunch"=>"smørbrød", "dinner"=>{"main_course"=>"steak", "side"=>"bakt potet", "desert"=>"sjokolademousse"}}}
|
71
|
+
norwegian = YAML.load(File.read('features/fixtures/norsk.yml')).should eq(expected)
|
72
|
+
|
73
|
+
expected = {"volume"=>{"sound"=>"høyt", "liquid"=>"fullt", "hair"=>"for det er jeg verdt"}}
|
74
|
+
nested_norwegian = YAML.load(File.read('features/fixtures/lang/norsk.yml')).should eq(expected)
|
75
|
+
end
|
76
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
$:.unshift "#{File.dirname(__FILE__)}/../../lib/"
|
2
|
+
|
3
|
+
require 'langulator'
|
4
|
+
|
5
|
+
path = 'features/fixtures/'
|
6
|
+
|
7
|
+
AGGREGATE_FILE = "#{path}out.yml"
|
8
|
+
|
9
|
+
INFILE = "#{path}in.yml"
|
10
|
+
|
11
|
+
individual = ['english', 'lang/english', 'francais', 'lang/francais', 'norsk', 'lang/norsk']
|
12
|
+
INDIVIDUAL_FILES = individual.map {|f| "#{path}#{f}.yml"}
|
13
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
def cleanup(file)
|
2
|
+
FileUtils.rm(file) if File.exists? file
|
3
|
+
end
|
4
|
+
|
5
|
+
Before('@compile') do
|
6
|
+
cleanup AGGREGATE_FILE
|
7
|
+
end
|
8
|
+
|
9
|
+
After('@compile') do
|
10
|
+
cleanup AGGREGATE_FILE
|
11
|
+
end
|
12
|
+
|
13
|
+
Before('@decompile') do
|
14
|
+
INDIVIDUAL_FILES.each {|f| cleanup f}
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
After('@decompile') do
|
19
|
+
INDIVIDUAL_FILES.each {|f| cleanup f}
|
20
|
+
end
|
data/langulator.gemspec
CHANGED
@@ -12,7 +12,9 @@ Gem::Specification.new do |gem|
|
|
12
12
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
13
13
|
gem.name = "langulator"
|
14
14
|
gem.require_paths = ["lib"]
|
15
|
-
gem.version = "0.0.
|
15
|
+
gem.version = "0.0.5"
|
16
16
|
|
17
17
|
gem.add_development_dependency "rspec"
|
18
|
+
gem.add_development_dependency "cucumber"
|
19
|
+
gem.add_runtime_dependency "thor"
|
18
20
|
end
|
data/lib/langulator.rb
CHANGED
@@ -1,16 +1,19 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'yaml'
|
3
|
-
require 'langulator/
|
3
|
+
require 'langulator/translation'
|
4
|
+
require 'langulator/aggregate_translation'
|
5
|
+
require 'langulator/individual_translation'
|
6
|
+
require 'langulator/individual_translations'
|
4
7
|
|
5
8
|
module Langulator
|
6
9
|
|
7
10
|
class << self
|
8
11
|
def compile(options)
|
9
|
-
|
12
|
+
IndividualTranslations.new(options).compile
|
10
13
|
end
|
11
14
|
|
12
15
|
def decompile(options)
|
13
|
-
|
16
|
+
AggregateTranslation.new(:location => options[:from]).decompile
|
14
17
|
end
|
15
18
|
end
|
16
19
|
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Langulator
|
2
|
+
class AggregateTranslation < Translation
|
3
|
+
|
4
|
+
attr_writer :individual_translations
|
5
|
+
|
6
|
+
def languages
|
7
|
+
@languages ||= detect_languages
|
8
|
+
end
|
9
|
+
|
10
|
+
def decompile
|
11
|
+
individual_translations.each(&:write)
|
12
|
+
end
|
13
|
+
|
14
|
+
def individual_translations
|
15
|
+
@individual_translations ||= extract_individual_translations
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def extract_individual_translations
|
21
|
+
i18ns = IndividualTranslations.new
|
22
|
+
languages.each do |language|
|
23
|
+
i18ns << extract_translations(language)
|
24
|
+
end
|
25
|
+
i18ns
|
26
|
+
end
|
27
|
+
|
28
|
+
def extract_translations(language)
|
29
|
+
extract(language, translations).map do |path, values|
|
30
|
+
IndividualTranslation.new(:path => path, :base_filename => language, :translations => values)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def extract(language, aggregate)
|
35
|
+
separated = {}
|
36
|
+
aggregate.keys.each do |key|
|
37
|
+
values = aggregate[key]
|
38
|
+
if translations?(values)
|
39
|
+
separated[key] = values[language]
|
40
|
+
else
|
41
|
+
separated[key] = extract(language, values)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
separated
|
45
|
+
end
|
46
|
+
|
47
|
+
def translations?(values)
|
48
|
+
!values.keys.select {|key| languages.include?(key) }.empty?
|
49
|
+
end
|
50
|
+
|
51
|
+
def detect_languages(dictionary = translations, detected = [])
|
52
|
+
dictionary.each do |key, values|
|
53
|
+
if values.is_a?(Hash)
|
54
|
+
detect_languages(values, detected)
|
55
|
+
else
|
56
|
+
language = key.to_sym
|
57
|
+
# The languages are always listed together.
|
58
|
+
# If we're seeing a duplicate language, then we're done
|
59
|
+
return detected if detected.include?(language)
|
60
|
+
|
61
|
+
detected << language
|
62
|
+
end
|
63
|
+
end
|
64
|
+
return detected
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Langulator
|
4
|
+
|
5
|
+
class CLI < Thor
|
6
|
+
|
7
|
+
desc "compile", "Combine individual i18n files into a single aggregate"
|
8
|
+
method_option :source_language, :type => :string, :aliases => "-s", :required => true, :desc => "Which i18n files have the required keys? Expects i18n files to be in format <language>.yml"
|
9
|
+
method_option :target_languages, :type => :array, :aliases => "-t", :required => true, :desc => "The target languages"
|
10
|
+
method_option :base_path, :type => :string, :aliases => "-p", :default => '**/i18n/', :desc => "The path used to glob for .yml files matching the desired languages"
|
11
|
+
method_option :file, :type => :string, :aliases => "-f", :default => './tmp/translations.yml', :desc => "The target path of the aggregate file"
|
12
|
+
def compile
|
13
|
+
compile_options = {
|
14
|
+
:source_language => options[:source_language].to_sym,
|
15
|
+
:target_languages => options[:target_languages].map(&:to_sym),
|
16
|
+
:base_path => options[:base_path],
|
17
|
+
:to => options[:file]
|
18
|
+
}
|
19
|
+
Langulator.compile(compile_options)
|
20
|
+
end
|
21
|
+
|
22
|
+
method_option :file, :type => :string, :aliases => "-f", :default => './tmp/translations.yml', :desc => "The path of the aggregate file"
|
23
|
+
desc "decompile", "Split an aggregate into its respective i18n files"
|
24
|
+
def decompile
|
25
|
+
Langulator.decompile(:from => options[:file])
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Langulator
|
2
|
+
class IndividualTranslations
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
attr_reader :translations, :aggregate_location, :base_path, :source_language, :target_languages
|
6
|
+
def initialize(options = {})
|
7
|
+
@aggregate_location = options[:to]
|
8
|
+
@base_path = options[:base_path]
|
9
|
+
@source_language = options[:source_language]
|
10
|
+
@target_languages = options[:target_languages]
|
11
|
+
end
|
12
|
+
|
13
|
+
def each
|
14
|
+
translations.each { |translation| yield translation }
|
15
|
+
end
|
16
|
+
|
17
|
+
def compile
|
18
|
+
aggregate.write
|
19
|
+
end
|
20
|
+
|
21
|
+
def <<(*objects)
|
22
|
+
@translations ||= []
|
23
|
+
objects.flatten.each do |obj|
|
24
|
+
translations << obj
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def in(*languages)
|
29
|
+
select {|translation| languages.include?(translation.language)}
|
30
|
+
end
|
31
|
+
|
32
|
+
def source_translations
|
33
|
+
return [] unless source_language
|
34
|
+
|
35
|
+
self.in(source_language)
|
36
|
+
end
|
37
|
+
|
38
|
+
def target_translations
|
39
|
+
return [] unless target_languages
|
40
|
+
|
41
|
+
self.in(*target_languages)
|
42
|
+
end
|
43
|
+
|
44
|
+
def translations
|
45
|
+
@translations ||= load_translations
|
46
|
+
end
|
47
|
+
|
48
|
+
def aggregate
|
49
|
+
@aggregate ||= combine
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def load_translations
|
55
|
+
i18ns = []
|
56
|
+
[source_language, *target_languages].each do |language|
|
57
|
+
paths.each do |path|
|
58
|
+
i18ns << IndividualTranslation.new(:path => path, :base_filename => language)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
i18ns
|
62
|
+
end
|
63
|
+
|
64
|
+
def paths
|
65
|
+
@paths ||= locations.map {|location| location.gsub("#{source_language}.yml", '')}
|
66
|
+
end
|
67
|
+
|
68
|
+
def locations
|
69
|
+
@locations ||= Dir.glob("#{base_path}#{source_language}.yml")
|
70
|
+
end
|
71
|
+
|
72
|
+
def combine
|
73
|
+
dictionary = initialize_aggregate
|
74
|
+
|
75
|
+
target_translations.each do |i18n|
|
76
|
+
dictionary[i18n.path] = insert(i18n.language, i18n.translations, dictionary[i18n.path])
|
77
|
+
end
|
78
|
+
|
79
|
+
AggregateTranslation.new(:location => aggregate_location, :translations => dictionary)
|
80
|
+
end
|
81
|
+
|
82
|
+
def initialize_aggregate
|
83
|
+
dict = {}
|
84
|
+
source_translations.each do |i18n|
|
85
|
+
dict[i18n.path] = remap(i18n.language, i18n.translations)
|
86
|
+
end
|
87
|
+
dict
|
88
|
+
end
|
89
|
+
|
90
|
+
def remap(language, source)
|
91
|
+
target = {}
|
92
|
+
source.each do |key, value|
|
93
|
+
target[key] ||= {}
|
94
|
+
if value.is_a?(Hash)
|
95
|
+
target[key] = remap(language, value)
|
96
|
+
else
|
97
|
+
target[key][language] = value
|
98
|
+
end
|
99
|
+
end
|
100
|
+
target
|
101
|
+
end
|
102
|
+
|
103
|
+
def insert(language, source, target)
|
104
|
+
target.dup.each do |key, value|
|
105
|
+
if value.is_a?(Hash)
|
106
|
+
insert(language, (source || {})[key], value)
|
107
|
+
else
|
108
|
+
target[language] = source
|
109
|
+
end
|
110
|
+
end
|
111
|
+
target
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Langulator
|
2
|
+
class Translation
|
3
|
+
|
4
|
+
attr_reader :location
|
5
|
+
def initialize(options = {})
|
6
|
+
@path = options[:path]
|
7
|
+
if options[:base_filename]
|
8
|
+
@filename = "#{options[:base_filename]}.yml"
|
9
|
+
end
|
10
|
+
@location = options[:location] || "#{@path}#{@filename}"
|
11
|
+
@translations = options[:translations]
|
12
|
+
end
|
13
|
+
|
14
|
+
def path
|
15
|
+
@path ||= location[/^(.*\/)/, 0] || "./"
|
16
|
+
end
|
17
|
+
|
18
|
+
def filename
|
19
|
+
@filename ||= location.gsub(path, '')
|
20
|
+
end
|
21
|
+
|
22
|
+
def translations
|
23
|
+
unless @translations
|
24
|
+
begin
|
25
|
+
@translations = read
|
26
|
+
rescue Errno::ENOENT => e
|
27
|
+
@translations = {}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
@translations
|
31
|
+
end
|
32
|
+
|
33
|
+
def read
|
34
|
+
YAML.load(File.read(location))
|
35
|
+
end
|
36
|
+
|
37
|
+
def write
|
38
|
+
File.open(location, 'w:utf-8') do |file|
|
39
|
+
file.write translations.to_yaml
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'langulator/translation'
|
2
|
+
require 'langulator/aggregate_translation'
|
3
|
+
require 'langulator/individual_translation'
|
4
|
+
require 'langulator/individual_translations'
|
5
|
+
require 'translation_interface'
|
6
|
+
|
7
|
+
describe Langulator::AggregateTranslation do
|
8
|
+
subject { Langulator::AggregateTranslation.new(:location => 'spec/fixtures/translations.yml') }
|
9
|
+
|
10
|
+
it_behaves_like "a translation"
|
11
|
+
|
12
|
+
its(:languages) { should eq([:klingon, :lolcode]) }
|
13
|
+
|
14
|
+
let(:combined) do
|
15
|
+
{
|
16
|
+
"spec/fixtures/" => {
|
17
|
+
"words" => {
|
18
|
+
"affirmative" => {
|
19
|
+
:klingon => "HISlaH",
|
20
|
+
:lolcode => "YA RLY"
|
21
|
+
},
|
22
|
+
"negative" => {
|
23
|
+
:klingon => "ghobe'",
|
24
|
+
:lolcode => "NO WAI"
|
25
|
+
},
|
26
|
+
"hello" => {
|
27
|
+
:klingon => "nuqneH",
|
28
|
+
:lolcode => "O HAI"
|
29
|
+
}
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
let(:klingon) do
|
36
|
+
dictionary = {"words" => {"affirmative" => "HISlaH", "negative" => "ghobe'", "hello" => "nuqneH"}}
|
37
|
+
Langulator::IndividualTranslation.new(:location => "spec/fixtures/klingon.yml", :translations => dictionary)
|
38
|
+
end
|
39
|
+
|
40
|
+
let(:lolcode) do
|
41
|
+
dictionary = {"words" => {"affirmative" => "YA RLY", "negative" => "NO WAI", "hello" => "O HAI"}}
|
42
|
+
Langulator::IndividualTranslation.new(:location => "spec/fixtures/lolcode.yml", :translations => dictionary)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'langulator/translation'
|
2
|
+
require 'langulator/individual_translation'
|
3
|
+
require 'translation_interface'
|
4
|
+
|
5
|
+
describe Langulator::IndividualTranslation do
|
6
|
+
|
7
|
+
subject { Langulator::IndividualTranslation.new(:location => 'spec/fixtures/english.yml') }
|
8
|
+
|
9
|
+
it_behaves_like "a translation"
|
10
|
+
|
11
|
+
its(:language) { should eq(:english) }
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'langulator/translation'
|
2
|
+
require 'langulator/individual_translation'
|
3
|
+
require 'langulator/individual_translations'
|
4
|
+
|
5
|
+
describe Langulator::IndividualTranslations do
|
6
|
+
|
7
|
+
describe "collection" do
|
8
|
+
subject { Langulator::IndividualTranslations.new(:source_language => :en, :target_languages => [:fr]) }
|
9
|
+
|
10
|
+
let(:english) { stub(:english, :language => :en, :translations => {"stuff" => "whatever"}) }
|
11
|
+
let(:english2) { stub(:english2, :language => :en, :translations => {"more" => "stuff"}) }
|
12
|
+
let(:french) { stub(:french, :language => :fr, :translations => {"truc" => "machin"}) }
|
13
|
+
let(:french2) { stub(:french2, :language => :fr, :translations => {"autre" => "bidule"}) }
|
14
|
+
|
15
|
+
before(:each) do
|
16
|
+
subject << english
|
17
|
+
subject << english2
|
18
|
+
subject << french
|
19
|
+
subject << french2
|
20
|
+
end
|
21
|
+
|
22
|
+
it "can accumulate many at a time" do
|
23
|
+
subject << [stub(:language => :nl), stub(:language => :cz)]
|
24
|
+
subject.map(&:language).uniq.should eq([:en, :fr, :nl, :cz])
|
25
|
+
end
|
26
|
+
|
27
|
+
it "can iterate through them" do
|
28
|
+
subject.map(&:language).should eq([:en, :en, :fr, :fr])
|
29
|
+
end
|
30
|
+
|
31
|
+
it "selects a language" do
|
32
|
+
subject.in(:en).should eq([english, english2])
|
33
|
+
end
|
34
|
+
|
35
|
+
it "provides the source_translations" do
|
36
|
+
subject.source_translations.should eq([english, english2])
|
37
|
+
end
|
38
|
+
|
39
|
+
it "provides the target translations" do
|
40
|
+
subject.target_translations.should eq([french, french2])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "loading from a path" do
|
45
|
+
let(:options) { {:source_language => :english, :target_languages => [:norsk], :base_path => 'spec/**/', :to => 'spec/fixtures/out.yml'} }
|
46
|
+
subject { Langulator::IndividualTranslations.new(options) }
|
47
|
+
|
48
|
+
its(:source_language) { should eq(:english) }
|
49
|
+
its(:target_languages) { should eq([:norsk]) }
|
50
|
+
its(:base_path) { should eq('spec/**/') }
|
51
|
+
its(:aggregate_location) { should eq('spec/fixtures/out.yml') }
|
52
|
+
|
53
|
+
it "loads the actual individual translations" do
|
54
|
+
subject.map(&:language).uniq.should eq([:english, :norsk])
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
shared_examples_for "a translation" do
|
2
|
+
it { subject.should respond_to(:filename) }
|
3
|
+
it { subject.should respond_to(:location) }
|
4
|
+
it { subject.should respond_to(:path) }
|
5
|
+
it { subject.should respond_to(:translations) }
|
6
|
+
it { subject.should respond_to(:write) }
|
7
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'langulator/translation'
|
3
|
+
require 'translation_interface'
|
4
|
+
|
5
|
+
describe Langulator::Translation do
|
6
|
+
subject { Langulator::Translation.new(:location => 'spec/fixtures/whatever.yml') }
|
7
|
+
|
8
|
+
it_behaves_like 'a translation'
|
9
|
+
|
10
|
+
its(:path) { should eq('spec/fixtures/') }
|
11
|
+
its(:location) { should eq('spec/fixtures/whatever.yml') }
|
12
|
+
its(:filename) { should eq('whatever.yml') }
|
13
|
+
|
14
|
+
describe "with a hyphen" do
|
15
|
+
subject { Langulator::Translation.new(:location => 'spec/fixtures/nb-no.yml') }
|
16
|
+
its(:filename) { should eq('nb-no.yml') }
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "in the current directory" do
|
20
|
+
subject { Langulator::Translation.new(:location => 'whatever.yml') }
|
21
|
+
its(:path) { should eq('./') }
|
22
|
+
its(:filename) { should eq('whatever.yml') }
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'with a missing file' do
|
26
|
+
subject { Langulator::Translation.new(:location => 'spec/fixtures/does_not_exist.yml') }
|
27
|
+
its(:path) { should eq('spec/fixtures/') }
|
28
|
+
its(:filename) { should eq('does_not_exist.yml') }
|
29
|
+
its(:translations) { should eq({}) }
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "with a path and base filename" do
|
33
|
+
subject { Langulator::Translation.new(:path => 'spec/fixtures/', :base_filename => 'whatever') }
|
34
|
+
its(:path) { should eq('spec/fixtures/') }
|
35
|
+
its(:location) { should eq('spec/fixtures/whatever.yml') }
|
36
|
+
its(:filename) { should eq('whatever.yml') }
|
37
|
+
end
|
38
|
+
|
39
|
+
its(:translations) { should eq({"some" => {"stuff" => "yadda yadda"}}) }
|
40
|
+
|
41
|
+
describe "with provided translations" do
|
42
|
+
subject { Langulator::Translation.new(:location => './nonexistant.yml', :translations => {"do" => "something"}) }
|
43
|
+
its(:translations) { should eq({"do" => "something"}) }
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#write" do
|
47
|
+
let(:location) { 'spec/fixtures/xy.yml' }
|
48
|
+
subject { Langulator::Translation.new(:location => location, :translations => {"some" => "stuff"}) }
|
49
|
+
|
50
|
+
before(:each) { FileUtils.rm(location) if File.exists?(location) }
|
51
|
+
after(:each) { FileUtils.rm(location) if File.exists?(location) }
|
52
|
+
|
53
|
+
it "outputs the content to the given location" do
|
54
|
+
subject.write
|
55
|
+
|
56
|
+
Langulator::Translation.new(:location => location).translations.should eq({"some" => "stuff"})
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: langulator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-05-
|
12
|
+
date: 2012-05-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &70268228073700 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,29 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70268228073700
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: cucumber
|
27
|
+
requirement: &70268228072740 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70268228072740
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: thor
|
38
|
+
requirement: &70268228071780 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70268228071780
|
25
47
|
description: Manage, maintain, and munge your i18n files.
|
26
48
|
email:
|
27
49
|
- katrina.owen@gmail.com
|
@@ -36,16 +58,33 @@ files:
|
|
36
58
|
- README.md
|
37
59
|
- Rakefile
|
38
60
|
- bin/langulator
|
61
|
+
- features/compile_aggregate.feature
|
62
|
+
- features/decompile_aggregate.feature
|
63
|
+
- features/fixtures/en.yml
|
64
|
+
- features/fixtures/fr.yml
|
65
|
+
- features/fixtures/in.yml
|
66
|
+
- features/fixtures/lang/en.yml
|
67
|
+
- features/fixtures/lang/fr.yml
|
68
|
+
- features/step_definitions/aggregate_steps.rb
|
69
|
+
- features/support/env.rb
|
70
|
+
- features/support/hooks.rb
|
39
71
|
- langulator.gemspec
|
40
72
|
- lib/langulator.rb
|
41
|
-
- lib/langulator/
|
42
|
-
- lib/langulator/
|
43
|
-
-
|
73
|
+
- lib/langulator/aggregate_translation.rb
|
74
|
+
- lib/langulator/cli.rb
|
75
|
+
- lib/langulator/individual_translation.rb
|
76
|
+
- lib/langulator/individual_translations.rb
|
77
|
+
- lib/langulator/translation.rb
|
78
|
+
- spec/aggregate_translation_spec.rb
|
44
79
|
- spec/fixtures/english.yml
|
45
80
|
- spec/fixtures/lang/english.yml
|
46
81
|
- spec/fixtures/norsk.yml
|
47
82
|
- spec/fixtures/translations.yml
|
48
|
-
- spec/
|
83
|
+
- spec/fixtures/whatever.yml
|
84
|
+
- spec/individual_translation_spec.rb
|
85
|
+
- spec/individual_translations_spec.rb
|
86
|
+
- spec/translation_interface.rb
|
87
|
+
- spec/translation_spec.rb
|
49
88
|
homepage: http://github.com/kytrinyx/langulator
|
50
89
|
licenses: []
|
51
90
|
post_install_message:
|
@@ -71,9 +110,23 @@ signing_key:
|
|
71
110
|
specification_version: 3
|
72
111
|
summary: Tasks to keep your i18n files managable.
|
73
112
|
test_files:
|
74
|
-
-
|
113
|
+
- features/compile_aggregate.feature
|
114
|
+
- features/decompile_aggregate.feature
|
115
|
+
- features/fixtures/en.yml
|
116
|
+
- features/fixtures/fr.yml
|
117
|
+
- features/fixtures/in.yml
|
118
|
+
- features/fixtures/lang/en.yml
|
119
|
+
- features/fixtures/lang/fr.yml
|
120
|
+
- features/step_definitions/aggregate_steps.rb
|
121
|
+
- features/support/env.rb
|
122
|
+
- features/support/hooks.rb
|
123
|
+
- spec/aggregate_translation_spec.rb
|
75
124
|
- spec/fixtures/english.yml
|
76
125
|
- spec/fixtures/lang/english.yml
|
77
126
|
- spec/fixtures/norsk.yml
|
78
127
|
- spec/fixtures/translations.yml
|
79
|
-
- spec/
|
128
|
+
- spec/fixtures/whatever.yml
|
129
|
+
- spec/individual_translation_spec.rb
|
130
|
+
- spec/individual_translations_spec.rb
|
131
|
+
- spec/translation_interface.rb
|
132
|
+
- spec/translation_spec.rb
|
data/lib/langulator/aggregate.rb
DELETED
@@ -1,124 +0,0 @@
|
|
1
|
-
require 'langulator/loader'
|
2
|
-
|
3
|
-
module Langulator
|
4
|
-
class Aggregate
|
5
|
-
|
6
|
-
class << self
|
7
|
-
def from_file(options)
|
8
|
-
translations = YAML.load(File.read(options[:from]))
|
9
|
-
new options.merge(:aggregate_translations => translations)
|
10
|
-
end
|
11
|
-
|
12
|
-
def from_files(options)
|
13
|
-
loader = Loader.new(options)
|
14
|
-
new options.merge(:individual_translations => loader.translations)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
attr_reader :languages, :source_language, :target_languages, :aggregate_file_path
|
19
|
-
def initialize(options = {})
|
20
|
-
@aggregate_file_path = options[:to]
|
21
|
-
@aggregate = options[:aggregate_translations]
|
22
|
-
@individual_translations = options[:individual_translations]
|
23
|
-
@source_language = options[:source_language]
|
24
|
-
@target_languages = Array(options[:target_languages])
|
25
|
-
@languages = options[:languages] || [source_language] + target_languages
|
26
|
-
end
|
27
|
-
|
28
|
-
def individual_translations
|
29
|
-
@individual_translations ||= separate
|
30
|
-
end
|
31
|
-
|
32
|
-
def aggregate
|
33
|
-
@aggregate ||= combine
|
34
|
-
end
|
35
|
-
|
36
|
-
def extract(language, tangled)
|
37
|
-
separated = {}
|
38
|
-
tangled.keys.each do |key|
|
39
|
-
values = tangled[key]
|
40
|
-
if translations?(values)
|
41
|
-
separated[key] = values[language]
|
42
|
-
else
|
43
|
-
separated[key] = extract(language, values)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
separated
|
47
|
-
end
|
48
|
-
|
49
|
-
def compile
|
50
|
-
write(aggregate_file_path, aggregate)
|
51
|
-
end
|
52
|
-
|
53
|
-
def decompile
|
54
|
-
individual_translations.each do |language, translations|
|
55
|
-
translations.each do |path, translation|
|
56
|
-
filename = "#{path}#{language}.yml"
|
57
|
-
write filename, translation
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
# TODO: find a good name for this
|
63
|
-
def to_aggregate(language, translations)
|
64
|
-
dictionary = {}
|
65
|
-
translations.each do |key, value|
|
66
|
-
dictionary[key] ||= {}
|
67
|
-
if value.is_a?(Hash)
|
68
|
-
dictionary[key] = to_aggregate(language, value)
|
69
|
-
else
|
70
|
-
dictionary[key][language] = value
|
71
|
-
end
|
72
|
-
end
|
73
|
-
dictionary
|
74
|
-
end
|
75
|
-
|
76
|
-
def insert(language, translations, dictionary)
|
77
|
-
dictionary.dup.each do |key, value|
|
78
|
-
if value.is_a?(Hash)
|
79
|
-
insert(language, (translations || {})[key], value)
|
80
|
-
else
|
81
|
-
dictionary[language] = translations
|
82
|
-
end
|
83
|
-
end
|
84
|
-
dictionary
|
85
|
-
end
|
86
|
-
|
87
|
-
private
|
88
|
-
|
89
|
-
def translations?(values)
|
90
|
-
!values.keys.select {|key| languages.include?(key) }.empty?
|
91
|
-
end
|
92
|
-
|
93
|
-
def write(filename, content)
|
94
|
-
File.open(filename, 'w:utf-8') do |file|
|
95
|
-
file.write content.to_yaml
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def separate
|
100
|
-
separated = {}
|
101
|
-
languages.each do |language|
|
102
|
-
separated[language] = extract(language, aggregate)
|
103
|
-
end
|
104
|
-
separated
|
105
|
-
end
|
106
|
-
|
107
|
-
def combine
|
108
|
-
source_translations = individual_translations[source_language]
|
109
|
-
|
110
|
-
target_translations = {}
|
111
|
-
target_languages.each do |language|
|
112
|
-
target_translations[language] = individual_translations[language]
|
113
|
-
end
|
114
|
-
|
115
|
-
dictionary = to_aggregate(source_language, source_translations)
|
116
|
-
target_translations.each do |language, translations|
|
117
|
-
dictionary = insert(language, translations, dictionary)
|
118
|
-
end
|
119
|
-
|
120
|
-
dictionary
|
121
|
-
end
|
122
|
-
|
123
|
-
end
|
124
|
-
end
|
data/lib/langulator/loader.rb
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
module Langulator
|
2
|
-
class Loader
|
3
|
-
|
4
|
-
attr_reader :base_path, :languages
|
5
|
-
def initialize(options = {})
|
6
|
-
@base_path = options[:base_path]
|
7
|
-
@languages = [options[:source_language]] + options[:target_languages]
|
8
|
-
end
|
9
|
-
|
10
|
-
def paths
|
11
|
-
unless @paths
|
12
|
-
filename = "#{languages.first}.yml"
|
13
|
-
@paths = Dir.glob("#{base_path}#{filename}").map {|file| file.gsub(filename, '') }.sort
|
14
|
-
end
|
15
|
-
@paths
|
16
|
-
end
|
17
|
-
|
18
|
-
def translations
|
19
|
-
translations = {}
|
20
|
-
languages.each do |language|
|
21
|
-
translations[language] = load_translations(language)
|
22
|
-
end
|
23
|
-
translations
|
24
|
-
end
|
25
|
-
|
26
|
-
def load_translations(language)
|
27
|
-
translations = {}
|
28
|
-
paths.each do |path|
|
29
|
-
translations[path] = read_translations(path, language)
|
30
|
-
end
|
31
|
-
translations
|
32
|
-
end
|
33
|
-
|
34
|
-
def read_translations(path, language)
|
35
|
-
file = "#{path}#{language}.yml"
|
36
|
-
if File.exists?(file)
|
37
|
-
YAML.load(File.read(file))
|
38
|
-
else
|
39
|
-
{}
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
end
|
44
|
-
end
|
data/spec/aggregate_spec.rb
DELETED
@@ -1,141 +0,0 @@
|
|
1
|
-
require 'langulator/aggregate'
|
2
|
-
|
3
|
-
describe Langulator::Aggregate do
|
4
|
-
|
5
|
-
let(:aggregate) do
|
6
|
-
{
|
7
|
-
"spec/fixtures/" => {
|
8
|
-
"words" => {
|
9
|
-
"affirmative" => {
|
10
|
-
:klingon => "HISlaH",
|
11
|
-
:lolcode => "YA RLY"
|
12
|
-
},
|
13
|
-
"negative" => {
|
14
|
-
:klingon => "ghobe'",
|
15
|
-
:lolcode => "NO WAI"
|
16
|
-
},
|
17
|
-
"hello" => {
|
18
|
-
:klingon => "nuqneH",
|
19
|
-
:lolcode => "O HAI"
|
20
|
-
}
|
21
|
-
}
|
22
|
-
}
|
23
|
-
}
|
24
|
-
end
|
25
|
-
|
26
|
-
let(:klingon) { {"spec/fixtures/" => {"words" => {"affirmative" => "HISlaH", "negative" => "ghobe'", "hello" => "nuqneH"}}} }
|
27
|
-
let(:lolcode) { {"spec/fixtures/" => {"words" => {"affirmative" => "YA RLY", "negative" => "NO WAI", "hello" => "O HAI"}}} }
|
28
|
-
|
29
|
-
describe "combining individual translations" do
|
30
|
-
let(:klingon_as_aggregate) do
|
31
|
-
{
|
32
|
-
"spec/fixtures/" => {
|
33
|
-
"words" => {
|
34
|
-
"affirmative" => { :klingon => "HISlaH" },
|
35
|
-
"negative" => { :klingon => "ghobe'" },
|
36
|
-
"hello" => { :klingon => "nuqneH" }
|
37
|
-
}
|
38
|
-
}
|
39
|
-
}
|
40
|
-
end
|
41
|
-
|
42
|
-
let(:outfile) { 'spec/fixtures/output.yml' }
|
43
|
-
|
44
|
-
let(:compile_options) do
|
45
|
-
{
|
46
|
-
:source_language => :klingon,
|
47
|
-
:target_languages => :lolcode,
|
48
|
-
:individual_translations => {:klingon => klingon, :lolcode => lolcode},
|
49
|
-
:to => outfile
|
50
|
-
}
|
51
|
-
end
|
52
|
-
|
53
|
-
subject { Langulator::Aggregate.new(compile_options) }
|
54
|
-
|
55
|
-
its(:source_language) { should eq(:klingon) }
|
56
|
-
its(:target_languages) { should eq([:lolcode]) }
|
57
|
-
its(:languages) { should eq([:klingon, :lolcode]) }
|
58
|
-
its(:individual_translations) { should eq({:klingon => klingon, :lolcode => lolcode}) }
|
59
|
-
its(:aggregate_file_path) { should eq(outfile) }
|
60
|
-
its(:aggregate) { should eq(aggregate) }
|
61
|
-
|
62
|
-
it "remappes the source language to initialize aggregate" do
|
63
|
-
subject.to_aggregate(:klingon, subject.individual_translations[:klingon]).should eq(klingon_as_aggregate)
|
64
|
-
end
|
65
|
-
|
66
|
-
it "inserts a target language into the aggregate" do
|
67
|
-
subject.insert(:lolcode, lolcode, klingon_as_aggregate).should eq(aggregate)
|
68
|
-
end
|
69
|
-
|
70
|
-
it "loads an aggregate" do
|
71
|
-
subject = Langulator::Aggregate.from_file(:from => 'spec/fixtures/translations.yml', :languages => [:klingon, :lolcode])
|
72
|
-
subject.aggregate.should eq(aggregate)
|
73
|
-
end
|
74
|
-
|
75
|
-
context "writing an aggregate" do
|
76
|
-
before(:each) do
|
77
|
-
FileUtils.rm(outfile) if File.exists? outfile
|
78
|
-
end
|
79
|
-
|
80
|
-
after(:each) do
|
81
|
-
FileUtils.rm(outfile) if File.exists? outfile
|
82
|
-
end
|
83
|
-
|
84
|
-
it "compiles" do
|
85
|
-
subject.compile
|
86
|
-
YAML.load(File.read(outfile)).should eq(aggregate)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
describe "de-aggregating translations" do
|
92
|
-
subject { Langulator::Aggregate.new(:languages => [:english]) }
|
93
|
-
|
94
|
-
it 'extracts English' do
|
95
|
-
input = {:rock => {:english => "rock"}, :paper => {:english => "paper"}}
|
96
|
-
expected_output = {:rock => "rock", :paper => "paper"}
|
97
|
-
subject.extract(:english, input).should eq(expected_output)
|
98
|
-
end
|
99
|
-
|
100
|
-
it "extracts complicated English" do
|
101
|
-
input = {:a => {:really => {:deeply => {:nested => {:game => {:rock => {:english => "rock"}, :paper => {:english => "paper"}}}}}}}
|
102
|
-
expected_output = {:a => {:really => {:deeply => {:nested => {:game => {:rock => "rock", :paper => "paper"}}}}}}
|
103
|
-
subject.extract(:english, input).should eq(expected_output)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
context "with aggregated data" do
|
108
|
-
subject { Langulator::Aggregate.new(:aggregate_translations => aggregate, :languages => [:klingon, :lolcode]) }
|
109
|
-
|
110
|
-
it "filters out the klingon" do
|
111
|
-
subject.individual_translations[:klingon].should eq(klingon)
|
112
|
-
end
|
113
|
-
|
114
|
-
it "filters out the lolcode" do
|
115
|
-
subject.individual_translations[:lolcode].should eq(lolcode)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
context "writing individual translations" do
|
120
|
-
let(:klingon_file) { "spec/fixtures/klingon.yml" }
|
121
|
-
let(:lolcode_file) { "spec/fixtures/lolcode.yml" }
|
122
|
-
|
123
|
-
subject { Langulator::Aggregate.new(:aggregate_translations => aggregate, :languages => [:klingon, :lolcode]) }
|
124
|
-
|
125
|
-
before(:each) do
|
126
|
-
FileUtils.rm(klingon_file) if File.exists? klingon_file
|
127
|
-
FileUtils.rm(lolcode_file) if File.exists? lolcode_file
|
128
|
-
end
|
129
|
-
|
130
|
-
after(:each) do
|
131
|
-
FileUtils.rm(klingon_file) if File.exists? klingon_file
|
132
|
-
FileUtils.rm(lolcode_file) if File.exists? lolcode_file
|
133
|
-
end
|
134
|
-
|
135
|
-
it "decompiles" do
|
136
|
-
subject.decompile
|
137
|
-
YAML.load(File.read(klingon_file)).should eq(klingon["spec/fixtures/"])
|
138
|
-
YAML.load(File.read(lolcode_file)).should eq(lolcode["spec/fixtures/"])
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
data/spec/loader_spec.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require 'yaml'
|
3
|
-
require 'langulator/loader'
|
4
|
-
|
5
|
-
describe Langulator::Loader do
|
6
|
-
|
7
|
-
let(:english) do
|
8
|
-
{
|
9
|
-
"spec/fixtures/" => {
|
10
|
-
"food" => {
|
11
|
-
"breakfast" => "yoghurt",
|
12
|
-
"lunch" => "sandwich",
|
13
|
-
"dinner" => {"main_course" => "steak", "side" => "baked potato", "desert" => "chocolate mousse"}
|
14
|
-
}
|
15
|
-
},
|
16
|
-
"spec/fixtures/lang/" => {
|
17
|
-
"volume" => {
|
18
|
-
"sound" => "loud",
|
19
|
-
"liquid" => "sloshing",
|
20
|
-
"hair" => "because I'm worth it"
|
21
|
-
}
|
22
|
-
}
|
23
|
-
}
|
24
|
-
end
|
25
|
-
|
26
|
-
let(:norwegian) do
|
27
|
-
{
|
28
|
-
"spec/fixtures/" => {
|
29
|
-
"food" => {
|
30
|
-
"lunch" => "smørbrød"
|
31
|
-
}
|
32
|
-
},
|
33
|
-
"spec/fixtures/lang/" => {}
|
34
|
-
}
|
35
|
-
end
|
36
|
-
|
37
|
-
let(:french) do
|
38
|
-
{
|
39
|
-
"spec/fixtures/" => {},
|
40
|
-
"spec/fixtures/lang/" => {}
|
41
|
-
}
|
42
|
-
end
|
43
|
-
|
44
|
-
subject { Langulator::Loader.new(:base_path => 'spec/fixtures/**/', :source_language => 'english', :target_languages => ['norsk', 'francais']) }
|
45
|
-
its(:translations) { should eq({'english' => english, 'norsk' => norwegian, 'francais' => french}) }
|
46
|
-
end
|