i18n_tyml 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 47e8bbaaab4574efa9947e3b2754cb592fc5cee4
4
+ data.tar.gz: 0cf63c876e223019c086a34b9d1f151f17c2cc5c
5
+ SHA512:
6
+ metadata.gz: 7fb4aa855ff70b47831d3969688fdb487b1260b519e8d586df58d10cd017af41c98b89da39974e2c46cf0adb35a68319f1f0d1a10433cf75e55acaa0aea8f818
7
+ data.tar.gz: 83048ec30caa158a77e1808cf98d10d58e9a3646d388cbd543b981638f34fa0cd00c4f79acbb11d169f79e64a6c42e494ac38b64f6eb6fb2e23efb97b7088a8d
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in i18n_tyml.gemspec
4
+ gemspec
5
+ gem 'byebug'
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 William
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # I18nTyml
2
+
3
+ Use this gem to find the missing translation by parsing I18n.t, and write missing values to yml file.
4
+
5
+ It should be used with [i15r](https://github.com/balinterdi/i15r)
6
+
7
+ The idea and part code is borrowed from (http://willbradley.name/2013/03/21/rails_localization_i18n_tools/)
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'i18n_tyml', :github => 'wingice/i18n_tyml'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install i18n_tyml
24
+
25
+
26
+
27
+ ## Usage
28
+
29
+ cd your project home
30
+
31
+ ```
32
+ 1. i15r app/views/xxxx/xxx.html.erb
33
+ 2. i18n_tyml app/views/xxxx/xxx.html.erb
34
+ ```
35
+
36
+ ## Contributing
37
+
38
+ 1. Fork it ( https://github.com/wingice/i18n_tyml/fork )
39
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
40
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
41
+ 4. Push to the branch (`git push origin my-new-feature`)
42
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/i18n_tyml ADDED
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # USAGE:
4
+ # Run and redirect output to a file for best results.
5
+ # example: ruby pull-defaults.rb > my-results.yml
6
+ # For now, you'll have to add the root en: element and increase
7
+ # the indent level for it to be a valid locale YML.
8
+ # Uncomment lines 160 and 161 (the ones that call
9
+ # replace_content_of_file_with_i18n_queries) to have this
10
+ # script remove :default strings from i18n tags it finds.
11
+
12
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
13
+
14
+
15
+ require "yaml"
16
+ require "optparse"
17
+ require "forwardable"
18
+ require 'active_support/all'
19
+ require 'nokogiri'
20
+ require 'i18n_tyml'
21
+
22
+ def parse_options(args)
23
+ args << "--help" if args.empty?
24
+ options = {}
25
+ options[:path] = args[-1]
26
+
27
+ opts = OptionParser.new do |ops|
28
+ ops.banner = "Usage: i18n_tyml [options] <path_or_file>, run in project home directory."
29
+ ops.on("-u", "--update", "Update the view files, remove the defaults part") do
30
+ options[:remove_default] = true
31
+ end
32
+ end
33
+
34
+ opts.on_tail("-h", "--help", "Show this message") do
35
+ puts opts
36
+ exit
37
+ end
38
+
39
+ opts.on_tail("--version", "Show version") do
40
+ puts I18nTyml::VERSION
41
+ exit
42
+ end
43
+
44
+ opts.parse!(args)
45
+ options
46
+ end
47
+
48
+ translation_mgr = I18nTyml::TranslationManager.new(parse_options(ARGV))
49
+ missing_values = translation_mgr.search_missing().values
50
+
51
+ missing_message_strings = missing_values.map { |ms|
52
+ ms.map { |mv|
53
+ #puts "STRINGS: "+mv.first.inspect+" - "+mv[1]
54
+ mr = mv.first.gsub(/\./, '^')
55
+ #mr.gsub(/./, '^')
56
+ unless(mv[1].nil?)
57
+ mr += "^"+mv[1]
58
+ end
59
+ mr = mr.split('^').reverse.inject {|a, n| {n => a}}
60
+ #mr = hashify([mv.first],mv[1])
61
+ #puts "STRINGShash: "+mr.inspect
62
+ mr
63
+ }
64
+ }
65
+
66
+ missing = missing_message_strings.each_with_object({}) do |h, all_message_strings|
67
+ #puts "HHH: "+h.inspect
68
+ h.each do |r|
69
+ all_message_strings.deep_safe_merge!(r)
70
+ end
71
+ end
72
+
73
+ puts missing.to_yaml
data/i18n_tyml.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'i18n_tyml/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "i18n_tyml"
8
+ spec.version = I18nTyml::VERSION
9
+ spec.authors = ["William"]
10
+ spec.email = ["wingicelee@hotmail.com"]
11
+ spec.summary = %q{Parse I18n.t function call from view files, and output with yaml format.}
12
+ spec.description = %q{Parse I18n.t function call from view files, and output with yaml format..}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec"
24
+ end
@@ -0,0 +1,14 @@
1
+ module ColorOutput
2
+ # snatched from rspec source
3
+ def colour(text, colour_code)
4
+ "#{colour_code}#{text}\e[0m"
5
+ end
6
+
7
+ def green(text); colour(text, "\e[32m"); end
8
+ def red(text); colour(text, "\e[31m"); end
9
+ def magenta(text); colour(text, "\e[35m"); end
10
+ def yellow(text); colour(text, "\e[33m"); end
11
+ def blue(text); colour(text, "\e[34m"); end
12
+
13
+ end
14
+
@@ -0,0 +1,31 @@
1
+ class Hash
2
+ def has_nested_key?(key)
3
+ h = self
4
+ key.to_s.split('.').each do |segment|
5
+ return false unless h.respond_to?(:key?) && h.key?(segment)
6
+ h = h[segment]
7
+ end
8
+ true
9
+ end
10
+
11
+ # idea snatched from deep_merge in Rails source code
12
+ def deep_safe_merge(other_hash)
13
+ self.merge(other_hash) do |key, oldval, newval|
14
+ oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
15
+ newval = newval.to_hash if newval.respond_to?(:to_hash)
16
+ if oldval.class.to_s == 'Hash'
17
+ if newval.class.to_s == 'Hash'
18
+ oldval.deep_safe_merge(newval)
19
+ else
20
+ oldval
21
+ end
22
+ else
23
+ newval
24
+ end
25
+ end
26
+ end
27
+
28
+ def deep_safe_merge!(other_hash)
29
+ replace(deep_safe_merge(other_hash))
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ module I18nTyml
2
+ class RubyI18nParser
3
+
4
+ I18N_DEFAULT_PATTERN = /I18n\.t\(["']([\w\.]*?)["'][^"']*default[^"']*?["']([^"']*?)["']/
5
+
6
+ def self.get_locale_ref_array(file_content)
7
+ file_content.scan(I18N_DEFAULT_PATTERN)
8
+ end
9
+
10
+ def self.remove_default(file_content)
11
+ file_content.gsub(I18N_DEFAULT_PATTERN, 'I18n.t("\1"')
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,3 @@
1
+ module I18nTyml
2
+ VERSION = "0.1.0"
3
+ end
data/lib/i18n_tyml.rb ADDED
@@ -0,0 +1,111 @@
1
+ require "i18n_tyml/version"
2
+ require "i18n_tyml/hash_deep_merge"
3
+ require "i18n_tyml/ruby_i18n_parser"
4
+
5
+ module I18nTyml
6
+ class TranslationManager
7
+
8
+ def initialize(options)
9
+ @dest_path = options[:path]
10
+ @remove_default = options[:remove_default]
11
+ @existed_translations = Hash.new
12
+ end
13
+
14
+ def search_missing(lang=nil)
15
+ collect_translations
16
+
17
+ languages = lang ? [lang] : existed_trans.keys
18
+ get_missing(collect_trans_in_erb(), languages )
19
+ end
20
+
21
+ private
22
+
23
+ def existed_trans
24
+ @existed_translations
25
+ end
26
+
27
+ def add_translations(trs)
28
+ existed_trans.deep_safe_merge!(trs)
29
+ end
30
+
31
+
32
+ def collect_translations
33
+ locales_pathes = ["config/locales/**/*.yml",
34
+ "vendor/plugins/**/config/locales/**/*yml",
35
+ "vendor/plugins/**/locale/**/*yml"]
36
+ locales_pathes.each do |path|
37
+ Dir.glob(path) do |file|
38
+ file_content = translations_in_file(file)
39
+ add_translations(file_content) if file_content
40
+ end
41
+ end
42
+ end
43
+
44
+ def collect_trans_in_erb
45
+ target_files().each_with_object({}) do |file, trans|
46
+ trs_in_file = translation_refs_in(file)
47
+ if trs_in_file.any?
48
+ trans[file] = trs_in_file
49
+ end
50
+ end
51
+ end
52
+
53
+ def target_files
54
+ path = @dest_path
55
+ path = path[0...-1] if path[-1..-1] == '/'
56
+ [path, Dir.glob("#{path}/**/*.erb"), Dir.glob("#{path}/**/*.rb") ].flatten
57
+ end
58
+
59
+
60
+ def read_content_of_file(file)
61
+ File.read(File.expand_path(file))
62
+ end
63
+
64
+ def write_content_to_file(file, output)
65
+ File.open(file, "w") { |f| f.puts output }
66
+ end
67
+
68
+
69
+ def translation_refs_in(file)
70
+ file_content = read_content_of_file(file)
71
+ results = I18nTyml::RubyI18nParser.get_locale_ref_array(file_content)
72
+ write_content_to_file(file, I18nTyml::RubyI18nParser.remove_default(file_content)) if @remove_default
73
+ results
74
+ end
75
+
76
+ def add_translations(trs)
77
+ existed_trans.deep_safe_merge!(trs)
78
+ end
79
+
80
+ def translations_in_file(yaml_file)
81
+ open(yaml_file) { |f| YAML.load(f.read) }
82
+ end
83
+
84
+
85
+ def get_missing(trans_in_code, languages)
86
+ languages.each_with_object({}) do |lang, missing|
87
+ get_missing_for_lang(trans_in_code, lang).each do |file, translation_refs|
88
+ missing[file] ||= []
89
+ missing[file].concat(translation_refs).uniq!
90
+ end
91
+ end
92
+ end
93
+
94
+ def get_missing_for_lang(trans_in_code, lang)
95
+ trans_in_code.map do |file, trans_with_defaults|
96
+ unmatched = trans_with_defaults.select {|q| has_trans?(lang, q[0]) == false }
97
+ [file, unmatched.map { |q| [i18n_label(lang, q[0]), q[1]] }] if unmatched.size > 0
98
+ end.compact
99
+ end
100
+
101
+
102
+ def has_trans?(lang, i18n_key)
103
+ existed_trans[lang].has_nested_key?(i18n_key)
104
+ end
105
+
106
+ def i18n_label(lang, query)
107
+ "#{lang}.#{query}"
108
+ end
109
+ end
110
+ end
111
+
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+ require 'i18n_tyml/ruby_i18n_parser'
3
+
4
+ describe I18nTyml::RubyI18nParser do
5
+ let(:erb_1) { <<-END
6
+ <% provide(:title, "Edit") %>
7
+ <h1><%= I18n.t("companies.edit.edit", :default => "Edit") %></h1>
8
+
9
+ <div class="row">
10
+ <div class="col-md-8 col-md-offset-2 well">
11
+ <%= simple_form_for @company do |f| %>
12
+ <%= render 'shared/error_messages',:object =>@company %>
13
+ <%= f.input :name %>
14
+ <%= f.input :description, :as => :text , input_html: { :rows => 6} %>
15
+ <div>
16
+ <%= f.submit I18n.t("companies.edit.update_company", :default => 'Update Company'), :class =>'btn btn-primary' %>
17
+ <%= link_to I18n.t("common.back", :default => 'Back'), :back, class:'btn btn-default' %>
18
+ </div>
19
+ <% end %>
20
+ </div>
21
+ </div>
22
+
23
+ END
24
+ }
25
+
26
+ describe "Extract the I18n.t call" do
27
+ it {
28
+ expect(I18nTyml::RubyI18nParser.get_locale_ref_array(erb_1)).to eq [["companies.edit.edit", "Edit"],
29
+ ["companies.edit.update_company", "Update Company"],
30
+ ["common.back", "Back"]]
31
+ }
32
+ end
33
+ describe "remove_default" do
34
+ it "should remove default part from I18n.t funcation call" do
35
+ result_str = <<-END
36
+ <% provide(:title, "Edit") %>
37
+ <h1><%= I18n.t("companies.edit.edit") %></h1>
38
+
39
+ <div class="row">
40
+ <div class="col-md-8 col-md-offset-2 well">
41
+ <%= simple_form_for @company do |f| %>
42
+ <%= render 'shared/error_messages',:object =>@company %>
43
+ <%= f.input :name %>
44
+ <%= f.input :description, :as => :text , input_html: { :rows => 6} %>
45
+ <div>
46
+ <%= f.submit I18n.t("companies.edit.update_company"), :class =>'btn btn-primary' %>
47
+ <%= link_to I18n.t("common.back"), :back, class:'btn btn-default' %>
48
+ </div>
49
+ <% end %>
50
+ </div>
51
+ </div>
52
+
53
+ END
54
+ expect(I18nTyml::RubyI18nParser.remove_default(erb_1)).to eq result_str
55
+ end
56
+ end
57
+ end
58
+
@@ -0,0 +1,9 @@
1
+ current_dir = File.expand_path(File.dirname(__FILE__))
2
+ $:.unshift File.join(current_dir, "..", "lib")
3
+
4
+
5
+ Dir["#{current_dir}/support/**/*.rb"].each do |file|
6
+ require file
7
+ end
8
+
9
+
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: i18n_tyml
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - William
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Parse I18n.t function call from view files, and output with yaml format..
56
+ email:
57
+ - wingicelee@hotmail.com
58
+ executables:
59
+ - i18n_tyml
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - bin/i18n_tyml
69
+ - i18n_tyml.gemspec
70
+ - lib/i18n_tyml.rb
71
+ - lib/i18n_tyml/color_output.rb
72
+ - lib/i18n_tyml/hash_deep_merge.rb
73
+ - lib/i18n_tyml/ruby_i18n_parser.rb
74
+ - lib/i18n_tyml/version.rb
75
+ - spec/ruby_i18n_parser_spec.rb
76
+ - spec/spec_helper.rb
77
+ homepage: ''
78
+ licenses:
79
+ - MIT
80
+ metadata: {}
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 2.2.2
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: Parse I18n.t function call from view files, and output with yaml format.
101
+ test_files:
102
+ - spec/ruby_i18n_parser_spec.rb
103
+ - spec/spec_helper.rb
104
+ has_rdoc: