missing_i18n 0.0.1

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.
Files changed (85) hide show
  1. checksums.yaml +15 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +68 -0
  4. data/Rakefile +40 -0
  5. data/app/assets/javascripts/missing_i18n/application.js +11 -0
  6. data/app/assets/stylesheets/missing_i18n/application.css.erb +36 -0
  7. data/app/controllers/missing_i18n/application_controller.rb +7 -0
  8. data/app/controllers/missing_i18n/locales_controller.rb +17 -0
  9. data/app/models/missing_i18n/locale.rb +90 -0
  10. data/app/views/layouts/missing_i18n/application.html.erb +14 -0
  11. data/app/views/missing_i18n/locales/_locale.html.erb +5 -0
  12. data/app/views/missing_i18n/locales/index.html.erb +19 -0
  13. data/app/views/missing_i18n/locales/show.html.erb +15 -0
  14. data/config/routes.rb +4 -0
  15. data/lib/missing_i18n/engine.rb +5 -0
  16. data/lib/missing_i18n/version.rb +3 -0
  17. data/lib/missing_i18n.rb +29 -0
  18. data/script/rails +8 -0
  19. data/test/dummy/README.rdoc +261 -0
  20. data/test/dummy/Rakefile +7 -0
  21. data/test/dummy/app/assets/javascripts/application.js +15 -0
  22. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  23. data/test/dummy/app/controllers/application_controller.rb +3 -0
  24. data/test/dummy/app/helpers/application_helper.rb +2 -0
  25. data/test/dummy/app/views/layouts/application.html.erb +12 -0
  26. data/test/dummy/config/application.rb +61 -0
  27. data/test/dummy/config/boot.rb +10 -0
  28. data/test/dummy/config/database.yml +25 -0
  29. data/test/dummy/config/environment.rb +5 -0
  30. data/test/dummy/config/environments/development.rb +37 -0
  31. data/test/dummy/config/environments/production.rb +67 -0
  32. data/test/dummy/config/environments/test.rb +37 -0
  33. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  34. data/test/dummy/config/initializers/inflections.rb +15 -0
  35. data/test/dummy/config/initializers/mime_types.rb +5 -0
  36. data/test/dummy/config/initializers/secret_token.rb +7 -0
  37. data/test/dummy/config/initializers/session_store.rb +8 -0
  38. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  39. data/test/dummy/config/locales/en.yml +6 -0
  40. data/test/dummy/config/locales/es.yml +9 -0
  41. data/test/dummy/config/routes.rb +4 -0
  42. data/test/dummy/config.ru +4 -0
  43. data/test/dummy/db/development.sqlite3 +0 -0
  44. data/test/dummy/log/development.log +3702 -0
  45. data/test/dummy/public/404.html +26 -0
  46. data/test/dummy/public/422.html +26 -0
  47. data/test/dummy/public/500.html +25 -0
  48. data/test/dummy/public/favicon.ico +0 -0
  49. data/test/dummy/script/rails +6 -0
  50. data/test/dummy/tmp/cache/assets/C37/460/sprockets%2F2c97f7629d838469e984005b45012f74 +0 -0
  51. data/test/dummy/tmp/cache/assets/C57/090/sprockets%2Ffb76e34f34542c751920190a976075c7 +0 -0
  52. data/test/dummy/tmp/cache/assets/C7A/AE0/sprockets%2F805bd3ef634444b405484a146381c6f2 +0 -0
  53. data/test/dummy/tmp/cache/assets/C85/6B0/sprockets%2F721d069a93d238f5396bf503b5b15594 +0 -0
  54. data/test/dummy/tmp/cache/assets/C8A/340/sprockets%2F7455c226fe9a93805641c9d1e38453a5 +0 -0
  55. data/test/dummy/tmp/cache/assets/C9A/670/sprockets%2F982543409941ce9c29dc44a78f4c0988 +0 -0
  56. data/test/dummy/tmp/cache/assets/CB3/D00/sprockets%2F00a9467f9e3685b6eb224e03379b28b2 +0 -0
  57. data/test/dummy/tmp/cache/assets/CE0/370/sprockets%2Fa39a8740f0b61611a6f9b9756fd9b211 +0 -0
  58. data/test/dummy/tmp/cache/assets/CE3/230/sprockets%2F218faa25364da35b59b68638b73ca567 +0 -0
  59. data/test/dummy/tmp/cache/assets/D0B/A40/sprockets%2F57f31167761f8b26bc3f1b0167bb4e3f +0 -0
  60. data/test/dummy/tmp/cache/assets/D14/CC0/sprockets%2F413b47ad449f6c649da1a76de9c52740 +0 -0
  61. data/test/dummy/tmp/cache/assets/D1A/BA0/sprockets%2F8f568f3083278618c50c82d1bbcf6dc5 +0 -0
  62. data/test/dummy/tmp/cache/assets/D20/D90/sprockets%2F7a9daf4472f16222b989f0f69c9820eb +0 -0
  63. data/test/dummy/tmp/cache/assets/D27/130/sprockets%2Fc3fee9098e52c52368f4eb9722ef9182 +0 -0
  64. data/test/dummy/tmp/cache/assets/D32/080/sprockets%2F8b1af3a2afe11e950164bec706f64191 +0 -0
  65. data/test/dummy/tmp/cache/assets/D3D/400/sprockets%2Ff3a0fc08891cc4e1e05f219c8827dd40 +0 -0
  66. data/test/dummy/tmp/cache/assets/D4A/250/sprockets%2F0ad7a77a58513a939a16f7b87fd742fc +0 -0
  67. data/test/dummy/tmp/cache/assets/D54/650/sprockets%2F95c28117b88ef818d27e1d2f9ca8bc58 +0 -0
  68. data/test/dummy/tmp/cache/assets/D64/6C0/sprockets%2Fe5bf1f16b921a3c2050657e8c3fcc51e +0 -0
  69. data/test/dummy/tmp/cache/assets/D74/6F0/sprockets%2Fd70538680abddce9109f2c6ef271ff07 +0 -0
  70. data/test/dummy/tmp/cache/assets/D76/F70/sprockets%2F46dfa9faaf2928d59b22da4442dd1957 +0 -0
  71. data/test/dummy/tmp/cache/assets/D86/640/sprockets%2F286d910145a2ea0cc8cda9acb442c06a +0 -0
  72. data/test/dummy/tmp/cache/assets/D8F/560/sprockets%2F057d1cb22221eecd76f281af2f6bd43c +0 -0
  73. data/test/dummy/tmp/cache/assets/DA2/EB0/sprockets%2Fcb0b147573bea528d5de16ce97c65a7f +0 -0
  74. data/test/dummy/tmp/cache/assets/DA5/7B0/sprockets%2F3cb24755d374c7f515eef2bfd548cde2 +0 -0
  75. data/test/dummy/tmp/cache/assets/DC9/9F0/sprockets%2Fb6cf7b756a0d77db1e530ad74072dbef +0 -0
  76. data/test/dummy/tmp/pids/server.pid +1 -0
  77. data/test/integration/navigation_test.rb +10 -0
  78. data/test/missing_i18n_test.rb +7 -0
  79. data/test/test_helper.rb +15 -0
  80. data/vendor/assets/images/glyphicons-halflings-white.png +0 -0
  81. data/vendor/assets/images/glyphicons-halflings.png +0 -0
  82. data/vendor/assets/javascripts/bootstrap.min.js +6 -0
  83. data/vendor/assets/javascripts/jquery-1.8.3.min.js +2 -0
  84. data/vendor/assets/stylesheets/bootstrap.min.css +9 -0
  85. metadata +217 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YmIwNDA0MmM3ODk5YTY2NDk2YmFiMDEyY2I5YzQyYjY5ZWM3ZDE3Yw==
5
+ data.tar.gz: !binary |-
6
+ MjU4YjNjMGY0ZTBkYTM5MGE5YjExZjU3ZjJiMTgxMDJjM2MxZGVhYQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NGY3MDM0MjAzZWQwNzUyMGQxYjEyNjY1ZDhmYWViMTAzYmYwMmIzZTFmYmFh
10
+ MGI1NWU3Y2NjYTRiODQ5M2YwYWY1Njc0YTg1MWMyY2FlMzIyYjNkODg3ZTg5
11
+ MDY0YjNkNmJhZmJhNDlkNmYwMjlhNDFhODBkYTM5YTE1MDBkNzA=
12
+ data.tar.gz: !binary |-
13
+ YTE3M2Q2OWFkMTJhMjIxYmU3N2I3Mzg0ZmNmZTAyMGE1YzY3OThkMmY1Yzg2
14
+ NjgxODU2MmQyMWU1NDUxZjY3MTE5OTVjNGY3ZWYzMmJhZWMwOTY2NjFiOTQ2
15
+ N2Y4YjE5N2VkNDkwOWU5OGNmY2RmODc2ZWIwNDFiZGNjYmIyODM=
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # missing_i18n
2
+
3
+ This is a [rails 3 mountable engine](http://railscasts.com/episodes/277-mountable-engines).
4
+
5
+ It allows you to see which locales have missing entries. You probably want to use it in your development/staging environment.
6
+
7
+ ## Installation
8
+
9
+ On your Gemfile:
10
+
11
+ gem 'missing_i18n'
12
+
13
+ On your `config/routes.rb`:
14
+
15
+ unless Rails.env.production? # optional, but recommended
16
+ mount MissingI18n::Engine, :path => '/missing_i18n'
17
+ end
18
+
19
+ ## Customization & scope filtering
20
+
21
+ You can override MissingI18n' only view by creating any of the following files:
22
+
23
+ * `app/views/missing_i18n/locales/index.html.erb`
24
+ * `app/views/missing_i18n/locales/show.html.erb`
25
+
26
+ You can similarly override MissingI18n controller by defining `app/views/missing_i18n/locales_controller.rb`.
27
+
28
+ Finally, you will probably want to add "ignored paths" to `missing_i18n`, for example for avoiding certain groups of prefixes.
29
+
30
+ To ignore (for example) the entries inserted by the [Faker](http://faker.rubyforge.org/), create an initializer file with this text:
31
+
32
+ # config/initializers/missing_i18n.rb
33
+
34
+ if defined? MissingI18n
35
+ MissingI18n.ignored_scopes += ['faker']
36
+ end
37
+
38
+ You can add more than one scope on that array. Also, you can add dot-separated scopes (`'foo.bar.baz'`) to filter more precisely.
39
+
40
+ Notice that `missing_i18n` [ignores some scopes by default](https://github.com/kikito/missing_i18n/blob/master/lib/missing_i18n.rb). These are scopes missing in some
41
+ locales of [rails-i18n](https://github.com/svenfuchs/rails-i18n), and in general it is safe to ignore those. If you need to include them in the list, you can; just
42
+ set `MissingI18n.ignored_scopes` to whatever you want; just use `=` instead of `+=`:
43
+
44
+ # config/initializers/missing_i18n.rb
45
+
46
+ if defined? MissingI18n
47
+ MissingI18n.ignored_scopes = ['my.scopes', 'which.dont.include', 'the.default.ones']
48
+ end
49
+
50
+ Everything will work as long as `MissingI18n.ignored_scopes` is an array of strings.
51
+
52
+ ## Tests
53
+
54
+ This engine has no tests. I'm basically using non-public methods from the i18n gem, so it's extremely brittle and coupled with the implementation details of i18n.
55
+
56
+ However, you can try the dummy app by executing:
57
+
58
+ cd test/dummy
59
+ rails s
60
+
61
+ If you then go to http://localhost:3000, you should see some differences between the es and en locales. Try modifying the files inside `test/dummy/config/locales` and
62
+ refreshing the page.
63
+
64
+ Feel free to send me pull requests with tests if you want!
65
+
66
+
67
+
68
+
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'MissingI18n'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+ require 'rake/testtask'
31
+
32
+ Rake::TestTask.new(:test) do |t|
33
+ t.libs << 'lib'
34
+ t.libs << 'test'
35
+ t.pattern = 'test/**/*_test.rb'
36
+ t.verbose = false
37
+ end
38
+
39
+
40
+ task :default => :test
@@ -0,0 +1,11 @@
1
+ //= require jquery-1.8.3.min
2
+ //= require_tree .
3
+
4
+ jQuery(function($) {
5
+ $('.locales').on('click', 'li', function() {
6
+ var $li = $(this);
7
+ $('iframe').attr('src', $li.find('a').attr('href'));
8
+ $li.parent().find('.active').removeClass('active');
9
+ $li.addClass('active');
10
+ });
11
+ });
@@ -0,0 +1,36 @@
1
+ /*
2
+ *= require bootstrap.min
3
+ *= require_self
4
+ *= require_tree .
5
+ */
6
+
7
+ body {
8
+ height: 100%;
9
+ }
10
+
11
+ iframe {
12
+ min-height: 95%;
13
+ width: 100%;
14
+ border: none;
15
+ }
16
+
17
+ .locales li {
18
+ list-style-type: none;
19
+ }
20
+
21
+ .locales .active,
22
+ .locales .active:hover {
23
+ background: #ccc;
24
+ }
25
+
26
+ .locales li {
27
+ cursor: pointer;
28
+ }
29
+
30
+ [class^="icon-"], [class*=" icon-"] {
31
+ background-image: url(<%=asset_path "glyphicons-halflings.png"%>);
32
+ }
33
+
34
+ .icon-white {
35
+ background-image: url(<%=asset_path "glyphicons-halflings-white.png"%>);
36
+ }
@@ -0,0 +1,7 @@
1
+ module MissingI18n
2
+ class ApplicationController < ActionController::Base
3
+ def index
4
+ @locales = Locale.all
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,17 @@
1
+ module MissingI18n
2
+ class LocalesController < ApplicationController
3
+ def index
4
+ @locales = Locale.all
5
+ end
6
+
7
+ def show
8
+ @locale = Locale.new(params[:id])
9
+ respond_to do |format|
10
+ format.html
11
+ format.yaml {
12
+ render :text => @locale.to_yaml, :content_type => 'text/yaml'
13
+ }
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,90 @@
1
+ module MissingI18n
2
+ class Locale #plain old ruby object
3
+
4
+ @@merged_translations = nil
5
+ @@i18n_translations = nil
6
+ @@all = nil
7
+
8
+ attr_accessor :id
9
+
10
+ def initialize(id)
11
+ self.id = id.to_s
12
+ end
13
+
14
+ def to_yaml(ignored_scopes = MissingI18n.ignored_scopes)
15
+ {self.id => missing_i18n(ignored_scopes)}.to_yaml
16
+ end
17
+
18
+ def translations
19
+ self.class.i18n_translations[self.id]
20
+ end
21
+
22
+ def missing_i18n(ignored_scopes = MissingI18n.ignored_scopes)
23
+ diff = hash_deep_diff(self.class.merged_translations, self.translations)
24
+ return hash_filter(diff, [], ignored_scopes)
25
+ end
26
+
27
+ def self.all
28
+ @@all ||= I18n.available_locales.collect{ |id| new(id) }.sort_by(&:id)
29
+ end
30
+
31
+ def self.merged_translations
32
+ unless @@merged_translations
33
+ @@merged_translations = {}
34
+ i18n_translations.each do |locale, translations|
35
+ @@merged_translations = translations.deep_merge(@@merged_translations)
36
+ end
37
+ end
38
+ @@merged_translations
39
+ end
40
+
41
+ def self.i18n_translations
42
+ unless @@i18n_translations
43
+ I18n.backend.send :init_translations
44
+ @@i18n_translations = hash_deep_stringify_keys(I18n.backend.send(:translations))
45
+ end
46
+ @@i18n_translations
47
+ end
48
+
49
+ private
50
+
51
+
52
+ def hash_filter(hash, scope, ignored_scopes)
53
+ return hash unless hash.is_a? Hash
54
+ result = {}
55
+ hash.each do |key, value|
56
+ new_scope = scope.dup << key
57
+ unless new_scope.join('.').in?(ignored_scopes)
58
+ new_value = hash_filter(value, new_scope, ignored_scopes)
59
+ result[key] = new_value unless new_value.blank?
60
+ end
61
+ end
62
+ result
63
+ end
64
+
65
+ # assumption big contains small completely
66
+ def hash_deep_diff(big, small)
67
+ return big unless small.is_a? Hash
68
+ result = {}
69
+ big.each do |key, value|
70
+ if small[key].present?
71
+ if value.is_a?(Hash)
72
+ diff = hash_deep_diff(value, small[key])
73
+ result[key] = diff unless diff.empty?
74
+ end
75
+ else
76
+ result[key] = value
77
+ end
78
+ end
79
+ result
80
+ end
81
+
82
+ def self.hash_deep_stringify_keys(hash)
83
+ return hash unless hash.is_a? Hash
84
+ hash.each_with_object({}) do |(key,value), result|
85
+ result[key.to_s] = hash_deep_stringify_keys(value)
86
+ end
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>missing_i18n</title>
5
+ <%= stylesheet_link_tag "missing_i18n/application", :media => "all" %>
6
+ <%= javascript_include_tag "missing_i18n/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,5 @@
1
+ <h2><%= link_to locale.name, self.send("missing_i18n_#{locale.name}_path") %>
2
+ <small>(<%= link_to 'yaml', self.send("missing_i18n_#{locale.name}_path", :format => :yaml) %>)</small></h2>
3
+ <pre>
4
+ <%= locale.to_yaml %>
5
+ </pre>
@@ -0,0 +1,19 @@
1
+ <div class="container">
2
+ <h1>missing_i18n
3
+ <%= link_to locales_path, class: 'btn btn-primary refresh' do %>
4
+ <i class="icon-refresh icon-white"></i>
5
+ Refresh
6
+ <% end %>
7
+ </h1>
8
+
9
+ <ul class="locales inline">
10
+ <% @locales.each_with_index do |locale, index| %>
11
+ <li class="<%= 'active' if index == 0 %>">
12
+ <%= link_to(locale.id, locale_path(locale.id), target: 'locale') %>
13
+ </li>
14
+ <% end %>
15
+ </ul>
16
+
17
+ <iframe name="locale" id="locale" src="<%= @locales[0].present? ? locale_path(@locales[0].id) : 'about:blank' %>"></iframe>
18
+ </div>
19
+
@@ -0,0 +1,15 @@
1
+ <h2><%= @locale.id %></h2>
2
+
3
+ <pre>
4
+ <%= @locale.to_yaml %>
5
+ </pre>
6
+
7
+ <p class="pull-right">
8
+ <%= link_to locale_path(@locale.id, :format => :yaml),
9
+ :class => 'btn btn-inverse btn-small',
10
+ :target => '_blank' do %>
11
+ <i class="icon-download icon-white"></i>
12
+ yaml
13
+ <% end %>
14
+ </p>
15
+
data/config/routes.rb ADDED
@@ -0,0 +1,4 @@
1
+ MissingI18n::Engine.routes.draw do
2
+ get '/' => 'locales#index', :as => :locales
3
+ get ':id' => 'locales#show', :as => :locale
4
+ end
@@ -0,0 +1,5 @@
1
+ module MissingI18n
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace MissingI18n
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module MissingI18n
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,29 @@
1
+ require "missing_i18n/engine"
2
+
3
+ module MissingI18n
4
+ DEFAULT_IGNORED_SCOPES = %w{
5
+ activemodel.errors.messages
6
+ activemodel.errors.template.header
7
+ activerecord.errors.messages
8
+ activerecord.errors.template.header
9
+ datetime.distance_in_words
10
+ datetime.prompts
11
+ errors.messages
12
+ errors.template.header
13
+ helpers.button
14
+ helpers.select
15
+ helpers.submit
16
+ i18n
17
+ number
18
+ support.array
19
+ time
20
+ }
21
+
22
+ def self.ignored_scopes=(new_ignored_scopes)
23
+ @@ignored_scopes = new_ignored_scopes
24
+ end
25
+
26
+ def self.ignored_scopes
27
+ @@ignored_scopes ||= DEFAULT_IGNORED_SCOPES.clone
28
+ end
29
+ end
data/script/rails ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
5
+ ENGINE_PATH = File.expand_path('../../lib/missing_i18n/engine', __FILE__)
6
+
7
+ require 'rails/all'
8
+ require 'rails/engine/commands'