vocab 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/lib/vocab/application.rb +24 -3
  2. data/lib/vocab/cleaner/android.rb +15 -0
  3. data/lib/vocab/cleaner/base.rb +14 -0
  4. data/lib/vocab/cleaner/rails.rb +141 -0
  5. data/lib/vocab/cleaner.rb +7 -0
  6. data/lib/vocab/converter/base.rb +17 -0
  7. data/lib/vocab/converter/rails.rb +86 -0
  8. data/lib/vocab/converter.rb +6 -0
  9. data/lib/vocab/extractor/rails.rb +26 -10
  10. data/lib/vocab/merger/android.rb +39 -3
  11. data/lib/vocab/merger/rails.rb +18 -0
  12. data/lib/vocab/translator/rails.rb +4 -0
  13. data/lib/vocab/version.rb +1 -1
  14. data/lib/vocab.rb +9 -0
  15. data/spec/cleaner/android_spec.rb +0 -0
  16. data/spec/cleaner/base_spec.rb +19 -0
  17. data/spec/cleaner/rails_spec.rb +79 -0
  18. data/spec/converter/base_spec.rb +23 -0
  19. data/spec/converter/rails_spec.rb +32 -0
  20. data/spec/data/android/locales/values/strings.xml +1 -1
  21. data/spec/data/android/locales/values-fr/strings.xml +21 -0
  22. data/spec/data/rails/full/cn.full.yml +0 -0
  23. data/spec/data/rails/full/en.expected.yml +4 -0
  24. data/spec/data/rails/full/en.full.yml +4 -0
  25. data/spec/data/rails/full/es.expected.yml +8 -0
  26. data/spec/data/rails/full/es.full.yml +8 -0
  27. data/spec/data/rails/full/fr.expected.yml +5 -0
  28. data/spec/data/rails/full/fr.full.yml +11 -0
  29. data/spec/data/rails/full/test.diff.yml +4 -0
  30. data/spec/data/rails/xml/in_file.xml +16 -0
  31. data/spec/data/rails/xml/in_file.yml +19 -0
  32. data/spec/data/rails/xml/out_file_expected.xml +16 -0
  33. data/spec/data/rails/xml/out_file_expected.yml +19 -0
  34. data/spec/data/rails/xml/test_file.xml +16 -0
  35. data/spec/data/rails/xml/test_file.yml +19 -0
  36. data/spec/extractor/android_spec.rb +2 -2
  37. data/spec/extractor/rails_spec.rb +15 -0
  38. data/spec/merger/android_spec.rb +13 -0
  39. data/spec/merger/rails_spec.rb +7 -0
  40. data/spec/validator/android_spec.rb +2 -2
  41. data/vocab.gemspec +4 -0
  42. metadata +142 -11
@@ -18,12 +18,16 @@ module Vocab
18
18
  options = OpenStruct.new
19
19
  parser = OptionParser.new
20
20
 
21
- parser.banner = 'Usage: vocab [-h] command [platform] [file]'
21
+ parser.banner = 'Usage: vocab [-h] command [platform] [type] [path]'
22
22
  parser.on( '-h', '--help', 'Show this usage message' ) { options.help = true }
23
23
  parser.separator ""
24
24
  parser.separator " vocab init"
25
25
  parser.separator " vocab extract rails"
26
+ parser.separator " vocab extract rails all"
26
27
  parser.separator " vocab extract android"
28
+ parser.separator " vocab clean rails"
29
+ parser.separator " vocab convert rails xml2yml <infile>"
30
+ parser.separator " vocab convert rails yml2xml <infile>"
27
31
  parser.separator " vocab merge rails"
28
32
  parser.separator " vocab merge android"
29
33
  parser.separator " vocab validate android"
@@ -33,12 +37,29 @@ module Vocab
33
37
  commands = parser.parse( ARGV )
34
38
  options.command = commands[0]
35
39
  options.platform = commands[1]
36
- options.path = commands[2]
40
+ options.type = commands[2]
41
+ options.path = commands[3]
37
42
 
38
43
  if( options.command == 'init' )
39
44
  Vocab::Settings.create
45
+ elsif( options.command == 'clean' && options.platform == 'rails' )
46
+ Cleaner::Rails.clean
47
+ elsif( options.command == 'clean' && options.platform == 'android' )
48
+ Cleaner::Android.clean
49
+ elsif( options.command == 'convert' && options.platform == 'rails' )
50
+ if options.type == 'xml2yml'
51
+ Converter::Rails.convert_xml_to_yml( options.path )
52
+ elsif options.type = 'yml2xml'
53
+ Converter::Rails.convert_yml_to_xml( options.path )
54
+ else
55
+ puts parser.help
56
+ end
40
57
  elsif( options.command == 'extract' && options.platform == 'rails' )
41
- Extractor::Rails.extract
58
+ if options.type == 'all'
59
+ Extractor::Rails.extract_all
60
+ else
61
+ Extractor::Rails.extract
62
+ end
42
63
  elsif( options.command == 'extract' && options.platform == 'android' )
43
64
  Extractor::Android.extract
44
65
  elsif( options.command == 'merge' && options.platform == 'rails' )
@@ -0,0 +1,15 @@
1
+ module Vocab
2
+ module Cleaner
3
+ class Android < Base
4
+ class << self
5
+ def clean_file( file )
6
+ # TODO: implement
7
+ end
8
+
9
+ def files_to_clean
10
+ return Dir.glob( "*.full.xml" )
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module Vocab
2
+ module Cleaner
3
+ class Base
4
+ class << self
5
+ def clean
6
+ files_to_clean.each do |file|
7
+ Vocab.ui.say( "Cleaning file: #{file}" )
8
+ clean_file( file )
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,141 @@
1
+ # Cleans full translation files ending in "full.yml" in the vocab root directory by:
2
+ # - removing empty keys
3
+ # - replacing HTML codes with the corresponding UTF-8 characters (e.g. &gt; --> > )
4
+ # - replacing \x[XX]\x[XX] codes with characters
5
+ # - removing keys that shouldn't be translated (specified in a blacklist)
6
+
7
+ module Vocab
8
+ module Cleaner
9
+ class Rails < Base
10
+ FULL_SUFFIX = 'full.yml'
11
+ DIFF_SUFFIX = 'diff.yml'
12
+ CLEAN_SUFFIX = 'clean.yml'
13
+ BLACKLIST = [
14
+ 'devise',
15
+ 'active_record',
16
+ 'activerecord',
17
+ 'number',
18
+ 'datetime'
19
+ ]
20
+ WINDOWS_TO_UTF8 = {
21
+ "\\xC2\\x80" => "\xe2\x82\xac", # EURO SIGN
22
+ "\\xC2\\x82" => "\xe2\x80\x9a", # SINGLE LOW-9 QUOTATION MARK
23
+ "\\xC2\\x83" => "\xc6\x92", # LATIN SMALL LETTER F WITH HOOK
24
+ "\\xC2\\x84" => "\xe2\x80\x9e", # DOUBLE LOW-9 QUOTATION MARK
25
+ "\\xC2\\x85" => "\xe2\x80\xa6", # HORIZONTAL ELLIPSIS
26
+ "\\xC2\\x86" => "\xe2\x80\xa0", # DAGGER
27
+ "\\xC2\\x87" => "\xe2\x80\xa1", # DOUBLE DAGGER
28
+ "\\xC2\\x88" => "\xcb\x86", # MODIFIER LETTER CIRCUMFLEX ACCENT
29
+ "\\xC2\\x89" => "\xe2\x80\xb0", # PER MILLE SIGN
30
+ "\\xC2\\x8A" => "\xc5\xa0", # LATIN CAPITAL LETTER S WITH CARON
31
+ "\\xC2\\x8B" => "\xe2\x80\xb9", # SINGLE LEFT-POINTING ANGLE QUOTATION
32
+ "\\xC2\\x8C" => "\xc5\x92", # LATIN CAPITAL LIGATURE OE
33
+ "\\xC2\\x8E" => "\xc5\xbd", # LATIN CAPITAL LETTER Z WITH CARON
34
+ "\\xC2\\x91" => "\xe2\x80\x98", # LEFT SINGLE QUOTATION MARK
35
+ "\\xC2\\x92" => "\xe2\x80\x99", # RIGHT SINGLE QUOTATION MARK
36
+ "\\xC2\\x93" => "\xe2\x80\x9c", # LEFT DOUBLE QUOTATION MARK
37
+ "\\xC2\\x94" => "\xe2\x80\x9d", # RIGHT DOUBLE QUOTATION MARK
38
+ "\\xC2\\x95" => "\xe2\x80\xa2", # BULLET
39
+ "\\xC2\\x96" => "\xe2\x80\x93", # EN DASH
40
+ "\\xC2\\x97" => "\xe2\x80\x94", # EM DASH
41
+
42
+ "\\xC2\\x98" => "\xcb\x9c", # SMALL TILDE
43
+ "\\xC2\\x99" => "\xe2\x84\xa2", # TRADE MARK SIGN
44
+ "\\xC2\\x9A" => "\xc5\xa1", # LATIN SMALL LETTER S WITH CARON
45
+ "\\xC2\\x9B" => "\xe2\x80\xba", # SINGLE RIGHT-POINTING ANGLE QUOTATION
46
+ "\\xC2\\x9C" => "\xc5\x93", # LATIN SMALL LIGATURE OE
47
+ "\\xC2\\x9E" => "\xc5\xbe", # LATIN SMALL LETTER Z WITH CARON
48
+ "\\xC2\\x9F" => "\xc5\xb8" # LATIN CAPITAL LETTER Y WITH DIAERESIS
49
+ }
50
+
51
+ class << self
52
+
53
+ def clean_file( file )
54
+ @file = file
55
+ @locale_name = File.basename( @file, '.yml' )
56
+ @clean_dir = File.dirname( @file )
57
+ @clean_name = "#{@clean_dir}/#{@locale_name}.#{CLEAN_SUFFIX}"
58
+
59
+ replace_codes
60
+ clean_yaml
61
+ end
62
+
63
+ def files_to_clean ( dir = Vocab.root )
64
+ return ( Dir.glob( "#{dir}/*.#{FULL_SUFFIX}" ) + Dir.glob( "#{dir}/*.#{DIFF_SUFFIX}" ) )
65
+ end
66
+
67
+ private
68
+ def replace_codes
69
+ translation = File.read( @file )
70
+
71
+ cleaned_text = replace_html_codes( translation )
72
+ cleaned_text = replace_windows_codes( cleaned_text )
73
+
74
+ cleaned_file = File.open( @clean_name, 'w' )
75
+ cleaned_file.puts( cleaned_text )
76
+ cleaned_file.close
77
+ end
78
+
79
+ def replace_html_codes( translation )
80
+ entity_matcher = /&.+?;/
81
+ coder = HTMLEntities.new( :expanded )
82
+
83
+ cleaned_text = translation.gsub( entity_matcher ) do |entity|
84
+ entity == "&quot;" ? "\\\"" : coder.decode( entity )
85
+ end
86
+
87
+ return cleaned_text
88
+ end
89
+
90
+ def replace_windows_codes( translation )
91
+ WINDOWS_TO_UTF8.each { |windows_code,utf8_code| translation.gsub!( windows_code, utf8_code ) }
92
+ return translation
93
+ end
94
+
95
+ def clean_yaml
96
+ # TODO: use psych
97
+ original_engine = YAML::ENGINE.yamler
98
+ YAML::ENGINE.yamler='syck'
99
+ translation_hash = YAML.load( File.open( @clean_name, 'r' ))
100
+
101
+ cleaned_file = File.open( @clean_name, 'w' )
102
+ keys = clean_keys( translation_hash )
103
+ cleaned_file.puts( replace_hex_codes( keys ) )
104
+ cleaned_file.close
105
+
106
+ ensure
107
+ YAML::ENGINE.yamler=original_engine
108
+
109
+ end
110
+
111
+ def replace_hex_codes( keys )
112
+ # Using ya2yaml, if available, for UTF8 support
113
+ keys.respond_to?( :ya2yaml ) ? keys.ya2yaml( :escape_as_utf8 => true ) : keys.to_yaml
114
+ end
115
+
116
+ def clean_keys( hash )
117
+ hash.inject({}) { |result, (key, value)|
118
+ if valid_key?( key )
119
+ value = clean_keys(value) if value.is_a? Hash
120
+ unless value.to_s.empty? or value == {}
121
+ result[(key.to_s rescue key) || key] = value
122
+ end
123
+ end
124
+ result
125
+ }
126
+ end
127
+
128
+ def valid_key?( key )
129
+ BLACKLIST.each do |prefix|
130
+ if key.to_s.include?( prefix )
131
+ return false
132
+ end
133
+ end
134
+ return true
135
+ end
136
+
137
+
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,7 @@
1
+ module Vocab
2
+ module Cleaner
3
+ autoload :Base, 'vocab/cleaner/base'
4
+ autoload :Rails, 'vocab/cleaner/rails'
5
+ autoload :Android, 'vocab/cleaner/android'
6
+ end
7
+ end
@@ -0,0 +1,17 @@
1
+ module Vocab
2
+ module Converter
3
+ class Base
4
+ class << self
5
+ def convert_xml_to_yml( path = nil)
6
+ Vocab.ui.say( "No conversion available" )
7
+ return nil
8
+ end
9
+
10
+ def convert_yml_to_xml( path = nil)
11
+ Vocab.ui.say( "No conversion available" )
12
+ return nil
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,86 @@
1
+ module Vocab
2
+ module Converter
3
+ class Rails < Base
4
+ SEPARATOR_ESCAPE_CHAR = "\001"
5
+
6
+ class << self
7
+ def convert_xml_to_yml( file = nil )
8
+ xml_root = 'hash'
9
+ keys = xml_to_yml_keys( Hash.from_xml( File.read( file ) )[ xml_root ] )
10
+ unwound_keys = unwind_keys( keys )
11
+ out_dir = File.dirname( file )
12
+ out_file = "#{out_dir}/#{File.basename( file, '.xml' ) + '.yml'}"
13
+ File.open( out_file, 'w' ) {|f| f.puts( ( keys_to_yaml( unwound_keys ) ) ) }
14
+ end
15
+
16
+ def xml_to_yml_keys( hash )
17
+ hash.inject( {} ) { |result, ( key, value )|
18
+ value = xml_to_yml_keys( value ) if value.is_a? Hash
19
+ yml_key = key.gsub( "-", "_" )
20
+ result[ yml_key ] = value
21
+ result
22
+ }
23
+ end
24
+
25
+ def unwind_keys( hash, separator = "." )
26
+ result = {}
27
+ hash.each do |key, value|
28
+ keys = key.to_s.split( separator )
29
+ curr = result
30
+ curr = curr[ keys.shift ] ||= {} while keys.size > 1
31
+ curr[ keys.shift ] = value
32
+ end
33
+
34
+ return result
35
+ end
36
+
37
+ def wind_keys( hash, separator = nil, subtree = false, prev_key = nil, result = {}, orig_hash = hash )
38
+ separator ||= I18n.default_separator
39
+
40
+ hash.each_pair do |key, value|
41
+ key = escape_default_separator( key, separator )
42
+ curr_key = [ prev_key, key ].compact.join( separator ).to_sym
43
+
44
+ if value.is_a?( Hash )
45
+ result[ curr_key ] = value if subtree
46
+ wind_keys( value, separator, subtree, curr_key, result, orig_hash )
47
+ else
48
+ result[ unescape_default_separator( curr_key ) ] = value
49
+ end
50
+ end
51
+
52
+ return result
53
+ end
54
+
55
+ def keys_to_yaml( keys )
56
+ # Using ya2yaml, if available, for UTF8 support
57
+ keys.respond_to?( :ya2yaml ) ? keys.ya2yaml( :escape_as_utf8 => true ) : keys.to_yaml
58
+ end
59
+
60
+ def keys_to_xml( keys )
61
+ raise "to_xml is broken" unless keys.respond_to?( :to_xml )
62
+ keys.to_xml
63
+ end
64
+
65
+ def convert_yml_to_xml( file = nil )
66
+ xml_root = 'hash'
67
+ yml = YAML.load_file( file )
68
+ wound_keys = wind_keys( yml )
69
+
70
+ out_dir = File.dirname( file )
71
+ out_file = "#{out_dir}/#{File.basename( file, '.yml' ) + '.xml'}"
72
+ File.open( out_file, 'w' ) { |f| f.puts( ( keys_to_xml( wound_keys ) ) ) }
73
+ end
74
+
75
+ def unescape_default_separator( key, separator = nil )
76
+ key.to_s.tr( SEPARATOR_ESCAPE_CHAR, separator || I18n.default_separator ).to_sym
77
+ end
78
+
79
+ def escape_default_separator( key, separator = nil )
80
+ key.to_s.tr( separator || I18n.default_separator, SEPARATOR_ESCAPE_CHAR )
81
+ end
82
+
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,6 @@
1
+ module Vocab
2
+ module Converter
3
+ autoload :Base, 'vocab/converter/base'
4
+ autoload :Rails, 'vocab/converter/rails'
5
+ end
6
+ end
@@ -3,20 +3,22 @@ module Vocab
3
3
  class Rails < Base
4
4
  DIFF = 'en.yml'
5
5
  FULL = 'en.full.yml'
6
+ DIFF_SUFFIX = 'diff.yml'
7
+ FULL_SUFFIX = 'full.yml'
6
8
 
7
9
  class << self
8
- def write_diff( strings, plurals, path )
9
- path ||= "#{Vocab.root}/#{DIFF}"
10
+ def write_diff( strings, plurals, path, locale = :en )
11
+ path ||= "#{Vocab.root}/#{locale}.#{DIFF_SUFFIX}"
10
12
  write( strings, path )
11
13
  end
12
14
 
13
- def write_full( strings, plurals, path )
14
- path ||= "#{Vocab.root}/#{FULL}"
15
+ def write_full( strings, plurals, path, locale = :en )
16
+ path ||= "#{Vocab.root}/#{locale}.#{FULL_SUFFIX}"
15
17
  write( strings, path )
16
18
  end
17
19
 
18
- def write( translations, path )
19
- data = hasherize( translations ).to_yaml
20
+ def write( translations, path, locale = :en )
21
+ data = hasherize( translations, locale ).to_yaml
20
22
  File.open( path, "w+" ) { |f| f.write( data ) }
21
23
  Vocab.ui.say( "Extracted to #{path}" )
22
24
  end
@@ -56,16 +58,30 @@ module Vocab
56
58
  return {}
57
59
  end
58
60
 
59
- def translations( dir )
61
+ def extract_all( locales_root = nil, result_dir = nil )
62
+ locales_root ||= "#{Vocab.root}/config/locales"
63
+ result_dir ||= Vocab.root
64
+
60
65
  translator = Vocab::Translator::Rails.new
66
+ translator.load_dir( locales_root )
67
+
68
+ translator.available_locales.each do |locale|
69
+ strings = translations( locales_root, locale )
70
+ path = "#{result_dir}/#{locale}.full.yml"
71
+ write( strings, path, locale )
72
+ end
73
+ end
74
+
75
+ def translations( dir, locale = :en )
76
+ translator = Vocab::Translator::Rails.new( locale )
61
77
  translator.load_dir( dir )
62
78
  return translator.flattened_translations( :prefix => true )
63
79
  end
64
80
 
65
- def hasherize( diff )
66
- translator = Vocab::Translator::Rails.new
81
+ def hasherize( diff, locale = :en )
82
+ translator = Vocab::Translator::Rails.new( locale )
67
83
  diff.each do |key, value|
68
- key = key.to_s.gsub!( /^en\./, '' )
84
+ key = key.to_s.gsub!( /^#{locale.to_s}\./, '' )
69
85
  translator.store( key, value )
70
86
  end
71
87
  return translator.translations( :prefix => true )
@@ -1,10 +1,17 @@
1
1
  module Vocab
2
2
  module Merger
3
3
  class Android < Base
4
+ FORMAT_PATTERN = /%(.+?)\b/
5
+ ARG_PATTERN = /\$(.+?)\b/
4
6
 
5
7
  def initialize( locales_dir = nil, updates_dir = nil )
6
8
  @locales_dir = locales_dir || 'res'
7
9
  @updates_dir = updates_dir || 'tmp/translations'
10
+ @english_path = "#{@locales_dir}/values/strings.xml"
11
+ if File.exists?( @english_path )
12
+ @english_strings = english_strings
13
+ @english_plurals = english_plurals
14
+ end
8
15
  end
9
16
 
10
17
  def merge_file( path )
@@ -17,17 +24,17 @@ module Vocab
17
24
  keys = string_keys
18
25
  current = current_strings_for_locale( path )
19
26
  updates = update_strings_for_locale( path )
20
- return translation_hash( keys, current, updates, path )
27
+ return translation_hash( keys, current, updates, path, :string_format_changed? )
21
28
  end
22
29
 
23
30
  def plurals( path )
24
31
  keys = plural_keys
25
32
  current = current_plurals_for_locale( path )
26
33
  updates = update_plurals_for_locale( path )
27
- return translation_hash( keys, current, updates, path )
34
+ return translation_hash( keys, current, updates, path, :plural_format_changed? )
28
35
  end
29
36
 
30
- def translation_hash( keys, current, updates, path )
37
+ def translation_hash( keys, current, updates, path, format_checker = :string_format_changed? )
31
38
  translation = {}
32
39
  keys.each do |key|
33
40
  next if Vocab::Translator::Base.ignore_key?( key )
@@ -35,6 +42,7 @@ module Vocab
35
42
  value = updates[ key ] || current[ key ]
36
43
  if value
37
44
  translation[ key ] = value
45
+ check_matching_format_strings( key, value, path, format_checker )
38
46
  else
39
47
  Vocab.ui.warn( "No translation found for key #{key} while merging #{path}" )
40
48
  end
@@ -43,6 +51,34 @@ module Vocab
43
51
  return translation
44
52
  end
45
53
 
54
+ def english_strings
55
+ return Vocab::Translator::Android.hash_from_xml( @english_path )
56
+ end
57
+
58
+ def english_plurals
59
+ return Vocab::Translator::Android.plurals_from_xml( @english_path )
60
+ end
61
+
62
+ def check_matching_format_strings( key, new_value, path, format_checker )
63
+ send( format_checker, key, new_value, path)
64
+ end
65
+
66
+ def plural_format_changed?( key, new_value, path )
67
+ new_value.each do |inner_key,inner_value|
68
+ if ( @english_plurals[ key ][ inner_key ].to_s.scan( FORMAT_PATTERN ) != inner_value.to_s.scan( FORMAT_PATTERN ) ) ||
69
+ ( @english_plurals[ key ][ inner_key ].to_s.scan( ARG_PATTERN ) != inner_value.to_s.scan( ARG_PATTERN ) )
70
+ Vocab.ui.warn( "Format string mismatch for key #{key}, quantity #{inner_key} while merging #{path}. \n English: #{@english_plurals[ key ][ inner_key ]} \n Translation: #{new_value[ inner_key ]}" )
71
+ end
72
+ end
73
+ end
74
+
75
+ def string_format_changed?( key, new_value, path )
76
+ if ( @english_strings[ key ].to_s.scan( FORMAT_PATTERN ) != new_value.to_s.scan( FORMAT_PATTERN ) ) ||
77
+ ( @english_strings[ key ].to_s.scan( ARG_PATTERN ) != new_value.to_s.scan( ARG_PATTERN ) )
78
+ Vocab.ui.warn( "Format string mismatch for key #{key} while merging #{path}. \n English: #{@english_strings[ key ]} \n Translation: #{new_value}" )
79
+ end
80
+ end
81
+
46
82
  def string_keys
47
83
  return Vocab::Translator::Android.string_keys( @locales_dir )
48
84
  end
@@ -1,6 +1,7 @@
1
1
  module Vocab
2
2
  module Merger
3
3
  class Rails < Base
4
+ INTERPOLATION_PATTERN = /%{(.+?)}/
4
5
 
5
6
  def initialize( locales_dir = nil, updates_dir = nil )
6
7
  @locales_dir = locales_dir || 'config/locales'
@@ -13,6 +14,8 @@ module Vocab
13
14
 
14
15
  # list of keys that need to be in the translated file
15
16
  keys = Vocab::Merger::Rails.keys_for_file( locales_path )
17
+ english = Vocab::Merger::Rails.load_english( locales_path )
18
+
16
19
 
17
20
  # existing translations already in the file
18
21
  locales_translator = translator( locales_path )
@@ -27,6 +30,7 @@ module Vocab
27
30
  updates_translator = translator( update_path )
28
31
  updates = updates_translator.flattened_translations
29
32
 
33
+
30
34
  # apply updated keys to locales hash
31
35
  keys.each do |key|
32
36
  next if Vocab::Translator::Base.ignore_key?( key )
@@ -34,6 +38,7 @@ module Vocab
34
38
  value = updates[ key ] || locales[ key ]
35
39
  if value
36
40
  locales_translator.store( key, value )
41
+ check_matching_interpolations( key, english[ key ], value, locales_path )
37
42
  else
38
43
  Vocab.ui.warn( "No translation found for key #{key} while merging #{locales_path}" )
39
44
  end
@@ -42,6 +47,12 @@ module Vocab
42
47
  locales_translator.write_file( locales_path )
43
48
  end
44
49
 
50
+ def check_matching_interpolations( key, old_value, new_value, locales_path )
51
+ if old_value.to_s.scan( INTERPOLATION_PATTERN ) != new_value.to_s.scan( INTERPOLATION_PATTERN )
52
+ Vocab.ui.warn( "Interpolation mismatch for key #{key} while merging #{locales_path}. \n English: #{old_value} Translation: #{new_value}" )
53
+ end
54
+ end
55
+
45
56
  def self.keys_for_file( path )
46
57
  en_path = Vocab::Translator::Rails.en_equivalent_path( path )
47
58
  translator = Vocab::Translator::Rails.new
@@ -49,6 +60,13 @@ module Vocab
49
60
  return translator.flattened_translations.keys
50
61
  end
51
62
 
63
+ def self.load_english( path )
64
+ en_path = Vocab::Translator::Rails.en_equivalent_path( path )
65
+ translator = Vocab::Translator::Rails.new
66
+ translator.load_file( en_path )
67
+ return translator.flattened_translations
68
+ end
69
+
52
70
  def translatable?( path )
53
71
  if File.basename( path ) == 'en.yml'
54
72
  Vocab.ui.warn( "can't translate english file #{path}" )
@@ -14,6 +14,10 @@ module Vocab
14
14
  @locale = locale
15
15
  end
16
16
 
17
+ def available_locales
18
+ return @backend.available_locales
19
+ end
20
+
17
21
  def load_dir( dir )
18
22
  I18n.load_path = Dir.glob( "#{dir}/**/*.{yml,rb}" )
19
23
  load_translations
data/lib/vocab/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Vocab
2
- VERSION = "0.0.6"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/vocab.rb CHANGED
@@ -2,10 +2,19 @@ require 'fileutils'
2
2
  require 'i18n'
3
3
  require 'pathname'
4
4
  require 'nokogiri'
5
+ require 'htmlentities'
6
+ require 'yaml'
7
+ require 'ya2yaml'
8
+ require 'active_support/core_ext'
9
+ require 'builder'
10
+
11
+
5
12
 
6
13
  require 'vocab/application'
7
14
  require 'vocab/extractor'
8
15
  require 'vocab/translator'
16
+ require 'vocab/cleaner'
17
+ require 'vocab/converter'
9
18
  require 'vocab/merger'
10
19
  require 'vocab/settings'
11
20
  require 'vocab/ui'
File without changes
@@ -0,0 +1,19 @@
1
+ require "spec_helper"
2
+
3
+ describe "Vocab::Cleaner::Base" do
4
+
5
+ describe 'clean' do
6
+
7
+ it 'cleans a list of files' do
8
+ files = [ 'es.yml', 'en.yml' ]
9
+ Vocab::Cleaner::Base.should_receive( :files_to_clean ).and_return( files )
10
+
11
+ files.each do |file|
12
+ Vocab::Cleaner::Base.should_receive( :clean_file ).with( file )
13
+ Vocab.ui.should_receive( :say ).with( "Cleaning file: #{file}" )
14
+ end
15
+ Vocab::Cleaner::Base.clean
16
+ end
17
+
18
+ end
19
+ end