langulator 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +72 -2
- data/langulator.gemspec +1 -1
- data/lib/langulator.rb +35 -9
- data/lib/langulator/untangler.rb +34 -0
- data/spec/fixtures/lang/english.yml +1 -1
- data/spec/langulator_spec.rb +56 -2
- data/spec/untangler_spec.rb +75 -0
- metadata +6 -3
data/README.md
CHANGED
@@ -1,6 +1,20 @@
|
|
1
1
|
# Langulator
|
2
2
|
|
3
|
-
|
3
|
+
Manage your i18n.
|
4
|
+
|
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
|
+
|
7
|
+
## Caveats
|
8
|
+
|
9
|
+
If you need something other than US-ASCII, you may want to consider using ruby 1.9.3.
|
10
|
+
|
11
|
+
In 1.9.2 (patch 290):
|
12
|
+
|
13
|
+
S\xC3\xB8knadstekst
|
14
|
+
|
15
|
+
In 1.9.3:
|
16
|
+
|
17
|
+
Søknadstekst
|
4
18
|
|
5
19
|
## Installation
|
6
20
|
|
@@ -18,7 +32,63 @@ Or install it yourself as:
|
|
18
32
|
|
19
33
|
## Usage
|
20
34
|
|
21
|
-
|
35
|
+
Given that you've written the original translations in English and also need French and German versions, and that you have multiple paths that contain i18n yml files named per the convention `<iso-language-code>.yml`, then this will give you a single output file where each key is listed with the translations immediately below it.
|
36
|
+
|
37
|
+
If you have partial translations in the target languages, these will be included.
|
38
|
+
|
39
|
+
Any extraneous translations (i.e. keys that may have been in use previously but are no longer referenced in the original language) will be discarded.
|
40
|
+
|
41
|
+
Any missing translations will be given keys with an empty spot, ready for translation.
|
42
|
+
|
43
|
+
It handles arbitrarily deep nestings.
|
44
|
+
|
45
|
+
This first version doesn't handle yml files that are namespaced by the language, but I anticipate needing it very soon.
|
46
|
+
|
47
|
+
Also, this first version doesn't actually decompile the finished product. Come back tomorrow for that one.
|
48
|
+
|
49
|
+
e.g.
|
50
|
+
|
51
|
+
Input:
|
52
|
+
|
53
|
+
# en.yml
|
54
|
+
---
|
55
|
+
food:
|
56
|
+
breakfast: Eggs
|
57
|
+
lunch: Sandwich
|
58
|
+
dinner:
|
59
|
+
main_course: Steak
|
60
|
+
desert: Chocolate pudding
|
61
|
+
|
62
|
+
# fr.yml
|
63
|
+
---
|
64
|
+
food:
|
65
|
+
breakfast: Des oeufs
|
66
|
+
thank_you: Merci
|
67
|
+
|
68
|
+
# no de.yml file
|
69
|
+
|
70
|
+
Langulator.write(:language => 'en', :alternates => ['fr', 'de'], :base_path => '**/i18n/', :to => '/tmp/translations.yml')
|
71
|
+
|
72
|
+
Outputs:
|
73
|
+
|
74
|
+
food:
|
75
|
+
breakfast:
|
76
|
+
en: Eggs
|
77
|
+
fr: Des oeufs
|
78
|
+
de:
|
79
|
+
lunch:
|
80
|
+
en: Sandwich
|
81
|
+
fr:
|
82
|
+
de:
|
83
|
+
dinner:
|
84
|
+
main_course:
|
85
|
+
en: Steak
|
86
|
+
fr:
|
87
|
+
de:
|
88
|
+
desert:
|
89
|
+
en: Chocolate pudding
|
90
|
+
fr:
|
91
|
+
de:
|
22
92
|
|
23
93
|
## Contributing
|
24
94
|
|
data/langulator.gemspec
CHANGED
data/lib/langulator.rb
CHANGED
@@ -1,18 +1,44 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'yaml'
|
1
3
|
require 'langulator/loader'
|
2
4
|
require 'langulator/munger'
|
5
|
+
require 'langulator/untangler'
|
3
6
|
|
4
7
|
module Langulator
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
+
class << self
|
9
|
+
def munge(options = {})
|
10
|
+
loader = Loader.new(options)
|
11
|
+
munger = Munger.new(:language => loader.origin, :translations => loader.source_translations, :alternates => loader.destination_translations)
|
8
12
|
|
9
|
-
|
10
|
-
|
13
|
+
munger.munge
|
14
|
+
end
|
15
|
+
|
16
|
+
def compile(options)
|
17
|
+
filename = options[:to]
|
18
|
+
translations = compile(options)
|
19
|
+
write filename, translations
|
20
|
+
end
|
21
|
+
|
22
|
+
def untangle(aggregate, options)
|
23
|
+
Untangler.new(aggregate, options).untangle
|
24
|
+
end
|
25
|
+
|
26
|
+
def decompile(aggregate, options)
|
27
|
+
translations = untangle(aggregate, options)
|
28
|
+
|
29
|
+
translations.each do |language, data|
|
30
|
+
data.each do |path, translation|
|
31
|
+
filename = "#{path}#{language}.yml"
|
32
|
+
write filename, translation
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
11
36
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
37
|
+
private
|
38
|
+
def write(filename, content)
|
39
|
+
File.open(filename, 'w:utf-8') do |file|
|
40
|
+
file.write content.to_yaml
|
41
|
+
end
|
16
42
|
end
|
17
43
|
end
|
18
44
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Langulator
|
2
|
+
class Untangler
|
3
|
+
attr_reader :aggregate, :languages
|
4
|
+
def initialize(aggregate, options = {})
|
5
|
+
@aggregate = aggregate
|
6
|
+
@languages = options[:languages]
|
7
|
+
end
|
8
|
+
|
9
|
+
def untangle
|
10
|
+
untangled = {}
|
11
|
+
languages.each do |language|
|
12
|
+
untangled[language] = extract(language, aggregate)
|
13
|
+
end
|
14
|
+
untangled
|
15
|
+
end
|
16
|
+
|
17
|
+
def extract(language, tangled)
|
18
|
+
untangled = {}
|
19
|
+
tangled.keys.each do |key|
|
20
|
+
values = tangled[key]
|
21
|
+
if translations?(values)
|
22
|
+
untangled[key] = values[language]
|
23
|
+
else
|
24
|
+
untangled[key] = extract(language, values)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
untangled
|
28
|
+
end
|
29
|
+
|
30
|
+
def translations?(values)
|
31
|
+
!values.keys.select {|key| languages.include?(key) }.empty?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/spec/langulator_spec.rb
CHANGED
@@ -4,7 +4,7 @@ require 'langulator'
|
|
4
4
|
describe Langulator do
|
5
5
|
let(:options) { {:base_path => 'spec/fixtures/**/', :origin => 'english', :alternates => ['norsk', 'francais']} }
|
6
6
|
|
7
|
-
let(:
|
7
|
+
let(:aggregate_incomplete) do
|
8
8
|
{
|
9
9
|
"spec/fixtures/" => {
|
10
10
|
"food" => {
|
@@ -28,6 +28,60 @@ describe Langulator do
|
|
28
28
|
end
|
29
29
|
|
30
30
|
it "loads and munges" do
|
31
|
-
Langulator.
|
31
|
+
Langulator.munge(options).should eq(aggregate_incomplete)
|
32
32
|
end
|
33
|
+
|
34
|
+
let(:aggregate_complete) do
|
35
|
+
{
|
36
|
+
"spec/fixtures/" => {
|
37
|
+
"food" => {
|
38
|
+
"breakfast" => {"english" => "yoghurt", "norsk" => "joggurt", "francais" => "yaourt"},
|
39
|
+
"lunch" => {"english" => "sandwich", "norsk" => "smørbrød", "francais" => "sandwich"},
|
40
|
+
"dinner" => {
|
41
|
+
"main_course" => {"english" => "steak", "norsk" => "steak", "francais" => "biffteak"},
|
42
|
+
"side" => {"english" => "baked potato", "norsk" => "bakt potet", "francais" => "pomme de terre au four"},
|
43
|
+
"desert" => {"english" => "chocolate mousse", "norsk" => "sjokolademousse", "francais" => "mousse au chocolat"}
|
44
|
+
}
|
45
|
+
}
|
46
|
+
},
|
47
|
+
"spec/fixtures/lang/" => {
|
48
|
+
"volume" => {
|
49
|
+
"sound" => {"english" => "loud", "norsk" => "høyt", "francais" => "fort"},
|
50
|
+
"liquid" => {"english" => "sloshing", "norsk" => "skvulper", "francais" => "agité"},
|
51
|
+
"hair" => {"english" => "because I'm worth it", "norsk" => "for det er jeg verdt", "francais" => "parce que je le vaux bien"}
|
52
|
+
}
|
53
|
+
}
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
it "untangles" do
|
58
|
+
expected = {
|
59
|
+
"english" => {
|
60
|
+
"spec/fixtures/" => {
|
61
|
+
"food" => {"breakfast" => "yoghurt", "lunch" => "sandwich", "dinner" => {"main_course" => "steak", "side" => "baked potato", "desert" => "chocolate mousse"}}
|
62
|
+
},
|
63
|
+
"spec/fixtures/lang/" => {
|
64
|
+
"volume" => {"sound" => "loud", "liquid" => "sloshing", "hair" => "because I'm worth it"}
|
65
|
+
}
|
66
|
+
},
|
67
|
+
"norsk" => {
|
68
|
+
"spec/fixtures/" => {
|
69
|
+
"food" => {"breakfast" => "joggurt", "lunch" => "smørbrød", "dinner" => {"main_course" => "steak", "side" => "bakt potet", "desert" => "sjokolademousse"}}
|
70
|
+
},
|
71
|
+
"spec/fixtures/lang/" => {
|
72
|
+
"volume" => {"sound" => "høyt", "liquid" => "skvulper", "hair" => "for det er jeg verdt"}
|
73
|
+
}
|
74
|
+
},
|
75
|
+
"francais" => {
|
76
|
+
"spec/fixtures/" => {
|
77
|
+
"food" => {"breakfast" => "yaourt", "lunch" => "sandwich", "dinner" => {"main_course" => "biffteak", "side" => "pomme de terre au four", "desert" => "mousse au chocolat"}}
|
78
|
+
},
|
79
|
+
"spec/fixtures/lang/" => {
|
80
|
+
"volume" => {"sound" => "fort", "liquid" => "agité", "hair" => "parce que je le vaux bien"}
|
81
|
+
}
|
82
|
+
}
|
83
|
+
}
|
84
|
+
Langulator.untangle(aggregate_complete, :languages => ["english", "norsk", "francais"]).should eq(expected)
|
85
|
+
end
|
86
|
+
|
33
87
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'langulator/untangler'
|
3
|
+
|
4
|
+
describe Langulator::Untangler do
|
5
|
+
let(:aggregate) do
|
6
|
+
{
|
7
|
+
:game => {
|
8
|
+
:rock => {
|
9
|
+
:english => "rock",
|
10
|
+
:french => "pierre",
|
11
|
+
},
|
12
|
+
:paper => {
|
13
|
+
:english => "paper",
|
14
|
+
:french => "papier"
|
15
|
+
},
|
16
|
+
:scissors => {
|
17
|
+
:english => "scissors",
|
18
|
+
:french => "ciseau"
|
19
|
+
},
|
20
|
+
:other => {
|
21
|
+
:deeply => {
|
22
|
+
:english => "nested",
|
23
|
+
:french => "imbriqué"
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:english) do
|
31
|
+
{
|
32
|
+
:game => {
|
33
|
+
:rock => "rock",
|
34
|
+
:paper => "paper",
|
35
|
+
:scissors => "scissors",
|
36
|
+
:other => {:deeply => "nested"}
|
37
|
+
}
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
let(:french) do
|
42
|
+
{
|
43
|
+
:game => {
|
44
|
+
:rock => "pierre",
|
45
|
+
:paper => "papier",
|
46
|
+
:scissors => "ciseau",
|
47
|
+
:other => {:deeply => "imbriqué"}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
context "with tangled data" do
|
53
|
+
subject { Langulator::Untangler.new(aggregate, :languages => [:english, :french]) }
|
54
|
+
|
55
|
+
it 'extracts English' do
|
56
|
+
input = {:rock => {:english => "rock"}, :paper => {:english => "paper"}}
|
57
|
+
expected_output = {:rock => "rock", :paper => "paper"}
|
58
|
+
subject.extract(:english, input).should eq(expected_output)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "extracts complicated English" do
|
62
|
+
input = {:a => {:really => {:deeply => {:nested => {:game => {:rock => {:english => "rock"}, :paper => {:english => "paper"}}}}}}}
|
63
|
+
expected_output = {:a => {:really => {:deeply => {:nested => {:game => {:rock => "rock", :paper => "paper"}}}}}}
|
64
|
+
subject.extract(:english, input).should eq(expected_output)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "filters out the english" do
|
68
|
+
subject.untangle[:english].should eq(english)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "filters out the french" do
|
72
|
+
subject.untangle[:french].should eq(french)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
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.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2012-05-03 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &70102603779800 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70102603779800
|
25
25
|
description: Manage, maintain, and munge your i18n files.
|
26
26
|
email:
|
27
27
|
- katrina.owen@gmail.com
|
@@ -40,12 +40,14 @@ files:
|
|
40
40
|
- lib/langulator.rb
|
41
41
|
- lib/langulator/loader.rb
|
42
42
|
- lib/langulator/munger.rb
|
43
|
+
- lib/langulator/untangler.rb
|
43
44
|
- spec/fixtures/english.yml
|
44
45
|
- spec/fixtures/lang/english.yml
|
45
46
|
- spec/fixtures/norsk.yml
|
46
47
|
- spec/langulator_spec.rb
|
47
48
|
- spec/loader_spec.rb
|
48
49
|
- spec/munger_spec.rb
|
50
|
+
- spec/untangler_spec.rb
|
49
51
|
homepage: http://github.com/kytrinyx/langulator
|
50
52
|
licenses: []
|
51
53
|
post_install_message:
|
@@ -77,3 +79,4 @@ test_files:
|
|
77
79
|
- spec/langulator_spec.rb
|
78
80
|
- spec/loader_spec.rb
|
79
81
|
- spec/munger_spec.rb
|
82
|
+
- spec/untangler_spec.rb
|