i18n_tyml 0.1.0

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.
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: