lit 0.0.4.3 → 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.
Files changed (46) hide show
  1. data/README.md +27 -35
  2. data/app/assets/javascripts/lit/localizations.js.coffee +8 -1
  3. data/app/assets/stylesheets/lit/application.css +4 -0
  4. data/app/controllers/lit/api/v1/base_controller.rb +19 -0
  5. data/app/controllers/lit/api/v1/locales_controller.rb +14 -0
  6. data/app/controllers/lit/api/v1/localization_keys_controller.rb +16 -0
  7. data/app/controllers/lit/api/v1/localizations_controller.rb +18 -0
  8. data/app/controllers/lit/application_controller.rb +1 -1
  9. data/app/controllers/lit/incomming_localizations_controller.rb +42 -0
  10. data/app/controllers/lit/localization_keys_controller.rb +2 -2
  11. data/app/controllers/lit/localizations_controller.rb +8 -2
  12. data/app/controllers/lit/sources_controller.rb +60 -0
  13. data/app/helpers/lit/localizations_helper.rb +1 -1
  14. data/app/helpers/lit/sources_helper.rb +4 -0
  15. data/app/models/lit/incomming_localization.rb +60 -0
  16. data/app/models/lit/locale.rb +3 -2
  17. data/app/models/lit/localization.rb +33 -3
  18. data/app/models/lit/localization_key.rb +2 -1
  19. data/app/models/lit/localization_version.rb +12 -0
  20. data/app/models/lit/source.rb +87 -0
  21. data/app/views/layouts/lit/_navigation.html.erb +3 -0
  22. data/app/views/lit/incomming_localizations/index.html.erb +47 -0
  23. data/app/views/lit/localization_keys/_localization_row.html.erb +11 -0
  24. data/app/views/lit/localization_keys/index.html.erb +12 -5
  25. data/app/views/lit/localizations/_form.html.erb +9 -1
  26. data/app/views/lit/localizations/_previous_versions_rows.html.erb +15 -0
  27. data/app/views/lit/localizations/previous_versions.js.erb +3 -0
  28. data/app/views/lit/localizations/update.js.erb +2 -2
  29. data/app/views/lit/sources/_form.html.erb +35 -0
  30. data/app/views/lit/sources/edit.html.erb +6 -0
  31. data/app/views/lit/sources/index.html.erb +36 -0
  32. data/app/views/lit/sources/new.html.erb +5 -0
  33. data/app/views/lit/sources/show.html.erb +18 -0
  34. data/config/routes.rb +32 -1
  35. data/db/migrate/20130921071304_create_lit_localization_versions.rb +11 -0
  36. data/db/migrate/20130923162141_create_lit_sources.rb +12 -0
  37. data/db/migrate/20130924151910_create_lit_incomming_localizations.rb +21 -0
  38. data/lib/generators/lit/install/templates/initializer.rb +34 -0
  39. data/lib/generators/lit/install_generator.rb +73 -0
  40. data/lib/lit.rb +2 -0
  41. data/lib/lit/adapters/hash_storage.rb +2 -2
  42. data/lib/lit/adapters/redis_storage.rb +25 -5
  43. data/lib/lit/cache.rb +30 -22
  44. data/lib/lit/i18n_backend.rb +9 -7
  45. data/lib/lit/version.rb +1 -1
  46. metadata +42 -2
@@ -1,28 +1,35 @@
1
1
  module Lit
2
2
  class Localization < ActiveRecord::Base
3
+ serialize :translated_value
4
+ serialize :default_value
3
5
 
4
6
  ## SCOPES
5
7
  scope :changed, proc{ where(:is_changed=>true) }
8
+ # @HACK: dirty, find a way to round date to full second
9
+ scope :after, proc{|dt| where('updated_at >= ?', dt+1.second) }
6
10
 
7
11
  ## ASSOCIATIONS
8
12
  belongs_to :locale
9
13
  belongs_to :localization_key
14
+ has_many :localization_versions, dependent: :destroy
15
+ has_many :versions, class_name: '::Lit::LocalizationVersion'
10
16
 
11
17
  ## VALIDATIONS
12
18
  validates :locale_id,
13
19
  :presence=>true
14
20
 
15
- if ::Rails::VERSION::MAJOR<4
21
+ unless defined?(::ActionController::StrongParameters)
16
22
  ## ACCESSIBLE
17
23
  attr_accessible :translated_value, :locale_id
18
24
  end
19
25
 
20
26
  ## BEFORE & AFTER
21
27
  before_update :update_is_changed
28
+ before_update :create_version
22
29
  after_update :mark_localization_key_completed
23
30
 
24
31
  def to_s
25
- self.value
32
+ self.get_value
26
33
  end
27
34
 
28
35
  def full_key
@@ -30,7 +37,23 @@ module Lit
30
37
  end
31
38
 
32
39
  def get_value
33
- is_changed? ? self.translated_value : self.default_value
40
+ (is_changed? && (not self.translated_value.nil?)) ? self.translated_value : self.default_value
41
+ end
42
+
43
+ def value
44
+ get_value
45
+ end
46
+
47
+ def localization_key_str
48
+ self.localization_key.localization_key
49
+ end
50
+
51
+ def locale_str
52
+ self.locale.locale
53
+ end
54
+
55
+ def last_change
56
+ self.updated_at.to_s(:db)
34
57
  end
35
58
 
36
59
  private
@@ -41,5 +64,12 @@ module Lit
41
64
  def mark_localization_key_completed
42
65
  self.localization_key.mark_completed!
43
66
  end
67
+
68
+ def create_version
69
+ if self.translated_value.present? and (not self.translated_value.nil?)
70
+ l = self.localization_versions.new
71
+ l.translated_value = self.translated_value_was || self.default_value
72
+ end
73
+ end
44
74
  end
45
75
  end
@@ -7,6 +7,7 @@ module Lit
7
7
  scope :not_completed, proc{ where(:is_completed=>false) }
8
8
  scope :starred, proc{ where(:is_starred=>true) }
9
9
  scope :ordered, proc{ order('localization_key asc') }
10
+ scope :after, proc{|dt| where('updated_at >= ?', dt) }
10
11
 
11
12
  ## ASSOCIATIONS
12
13
  has_many :localizations, :dependent=>:destroy
@@ -16,7 +17,7 @@ module Lit
16
17
  :presence=>true,
17
18
  :uniqueness=>true
18
19
 
19
- if ::Rails::VERSION::MAJOR<4
20
+ unless defined?(::ActionController::StrongParameters)
20
21
  ## ACCESSIBLE
21
22
  attr_accessible :localization_key
22
23
  end
@@ -0,0 +1,12 @@
1
+ module Lit
2
+ class LocalizationVersion < ActiveRecord::Base
3
+ serialize :translated_value
4
+
5
+ ## ASSOCIATIONS
6
+ belongs_to :localization
7
+
8
+ def to_s
9
+ self.translated_value
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,87 @@
1
+ require 'net/http'
2
+ module Lit
3
+ class Source < ActiveRecord::Base
4
+ LOCALES_PATH = "/api/v1/locales.json"
5
+ LOCALIZATION_KEYS_PATH = "/api/v1/localization_keys.json"
6
+ LOCALIZATIONS_PATH = "/api/v1/localizations.json"
7
+ LAST_CHANGE_PATH = "/api/v1/last_change.json"
8
+
9
+ ## ASSOCIATIONS
10
+ has_many :incomming_localizations
11
+
12
+ ## VALIDATIONS
13
+ validates :api_key, :identifier, :url,
14
+ :presence=>true
15
+ validates :url,
16
+ :format=>{:with=>/\Ahttps?:\/\/.*\/.*[^\/]\Z/i}
17
+
18
+ unless defined?(::ActionController::StrongParameters)
19
+ attr_accessible :api_key, :identifier, :url
20
+ end
21
+
22
+ ## BEFORE & AFTER
23
+ after_validation :check_if_url_is_valid
24
+
25
+
26
+ def get_last_change
27
+ result = get_from_remote(LAST_CHANGE_PATH)
28
+ result["last_change"] unless result.nil?
29
+ end
30
+
31
+ def synchronize
32
+ after = self.last_updated_at.nil? ? nil : self.last_updated_at.to_s(:db)
33
+ result = get_from_remote(LOCALIZATIONS_PATH, {:after=>after})
34
+ unless result.nil?
35
+ if result.is_a?(Array)
36
+ result.each do |r|
37
+ il = IncommingLocalization.new
38
+ if ::Rails::VERSION::MAJOR<4
39
+ il = IncommingLocalization.where(:incomming_id=>r["id"]).first_or_initialize
40
+ else
41
+ il = IncommingLocalization.find_or_initialize_by(:incomming_id=>r["id"])
42
+ end
43
+ il.source = self
44
+ il.locale_str = r["locale_str"]
45
+ il.locale = Locale.where(:locale=>il.locale_str).first
46
+ il.localization_key_str = r["localization_key_str"]
47
+ il.localization_key = LocalizationKey.where(:localization_key=>il.localization_key_str).first
48
+ il.save!
49
+ IncommingLocalization.where(:id=>il.id).update_all ['translated_value=?', r["value"]]
50
+ end
51
+ lc = get_last_change
52
+ lc = DateTime.parse(lc) unless lc.nil?
53
+ self.last_updated_at = lc || Time.now
54
+ self.save
55
+ end
56
+ end
57
+ end
58
+
59
+ private
60
+ def check_if_url_is_valid
61
+ if self.errors.empty? && (self.new_record? || self.url_changed?)
62
+ self.errors.add(:url, "is not accessible") if get_last_change.nil?
63
+ end
64
+ end
65
+
66
+ def get_from_remote(path, query_values={})
67
+ result = nil
68
+ #begin
69
+ uri = URI(self.url+path)
70
+ query_values.each do |k,v|
71
+ params = URI.decode_www_form(uri.query || []) << [k, v]
72
+ uri.query = URI.encode_www_form(params)
73
+ end
74
+ req = Net::HTTP::Get.new(uri.request_uri)
75
+ req.add_field("Authorization", %(Token token="#{self.api_key}"))
76
+ res = Net::HTTP.new(uri.host, uri.port).start do |http|
77
+ http.request(req)
78
+ end
79
+ if res.is_a?(Net::HTTPSuccess)
80
+ result = JSON.parse(res.body)
81
+ end
82
+ #rescue
83
+ #end
84
+ result
85
+ end
86
+ end
87
+ end
@@ -1,11 +1,14 @@
1
1
  <%= link_to "Lost in translation", root_path, :class=>'brand' %>
2
2
  <ul class="nav">
3
3
  <li><%= link_to "Translate!", lit.localization_keys_path %></li>
4
+ <li><%= link_to "Synchronize", lit.sources_path %></li>
4
5
  <li class="dropdown">
5
6
  <a href="#" class="dropdown-toggle" data-toggle="dropdown">
6
7
  Settings
7
8
  <b class="caret"></b>
8
9
  </a>
10
+ <ul class="dropdown-menu">
11
+ </ul>
9
12
  <ul class="dropdown-menu">
10
13
  <li><%= link_to "Locales", lit.locales_path %></li>
11
14
  </ul>
@@ -0,0 +1,47 @@
1
+ <h1>Incomming localizations</h1>
2
+ <div class="row">
3
+ <span class="pull-right">
4
+ <%= link_to accept_all_source_incomming_localizations_path(@source), :class=>"btn btn-success", :data=>{:confirm=>t('lit.common.you_sure', :default=>"Are you sure?")} do %>
5
+ <%= t('lit.common.accept_all', :default=>"Accept all") %>
6
+ <% end %>
7
+ <%= link_to reject_all_source_incomming_localizations_path(@source), :class=>"btn btn-danger", :method=>:post, :data=>{:confirm=>t('lit.common.you_sure', :default=>"Are you sure?")} do %>
8
+ <%= t('lit.common.reject_all', :default=>"Reject all") %>
9
+ <% end %>
10
+ </span>
11
+ </div>
12
+
13
+ <table class="table table-bordered table-striped">
14
+ <tr>
15
+ <th>Current value</th>
16
+ <th>Imported value</th>
17
+ <th width="200px"></th>
18
+ </tr>
19
+ <% @incomming_localizations.each do |il| %>
20
+ <tr>
21
+ <td colspan="3">
22
+ <%= image_tag "lit/famfamfam_flags/#{il.locale_str[0,2]}.png" %>
23
+ <strong><%= il.full_key %></strong>
24
+ </td>
25
+ </tr>
26
+ <tr>
27
+ <td>
28
+ <% if il.localization %>
29
+ <%= render :partial=>"/lit/localization_keys/localization_row", :locals=>{:localization=>il.localization.get_value} %>
30
+ <% end %>
31
+ </td>
32
+ <Td>
33
+ <%= render :partial=>"/lit/localization_keys/localization_row", :locals=>{:localization=>il.get_value} %>
34
+ </td>
35
+ <td>
36
+ <%= link_to accept_source_incomming_localization_path(@source, il), :class=>"btn btn-success" do %>
37
+ <%= draw_icon "ok", :class=>"icon-white" %>
38
+ <%= t('lit.common.accept', :default=>"Accept") %>
39
+ <% end %>
40
+ <%= link_to source_incomming_localization_path(@source, il), :class=>"btn btn-danger", :method=>:delete, :data=>{:confirm=>t('lit.common.you_sure', :default=>"Are you sure?")} do %>
41
+ <%= draw_icon "remove", :class=>"icon-white" %>
42
+ <%= t('lit.common.reject', :default=>"Reject") %>
43
+ <% end %>
44
+ </td>
45
+ </tr>
46
+ <% end %>
47
+ </table>
@@ -0,0 +1,11 @@
1
+ <%- la = localization -%>
2
+ <% if la.is_a?(Array) %>
3
+ <ul>
4
+ <% la.each do |l| %>
5
+ <li><%= l %></li>
6
+ <% end %>
7
+ </ul>
8
+ <% else %>
9
+ <%= la %>
10
+ <% end %>
11
+
@@ -17,20 +17,28 @@
17
17
  <div class="detail_wrapper">
18
18
  <table class="table table-bordered table-striped">
19
19
  <%- I18n.backend.available_locales.each do |locale| %>
20
+ <%- localization = lk.localizations.where(:locale_id=>Lit.init.cache.find_locale(locale).id).first %>
20
21
  <tr>
21
- <%- localization = lk.localizations.where(:locale_id=>Lit.init.cache.find_locale(locale).id).first %>
22
22
  <% unless localization %>
23
23
  <% Lit.init.cache.refresh_key("#{locale}.#{lk.localization_key}") %>
24
24
  <%- localization = lk.localizations.where(:locale_id=>Lit.init.cache.find_locale(locale).id).first %>
25
25
  <% end %>
26
- <td class="localization_row" data-id="<%= localization.id%>" data-edit="<%= edit_localization_key_localization_path(lk, localization) %>" data-editing=0 data-content="<%= ejs(Lit.init.cache["#{locale}.#{lk.localization_key}"])%>">
27
- <%= Lit.init.cache["#{locale}.#{lk.localization_key}"] %>
26
+ <td class="localization_row" data-id="<%= localization.id%>" data-edit="<%= edit_localization_key_localization_path(lk, localization) %>" data-editing=0 data-content="">
27
+ <%= render :partial=>"localization_row", :locals=>{:localization=>Lit.init.cache["#{locale}.#{lk.localization_key}"]} %>
28
28
  </td>
29
29
  <td class="locale_row">
30
30
  <%= image_tag "lit/famfamfam_flags/#{locale[0,2]}.png" %>
31
31
  <%= locale %>
32
+ <% if localization %>
33
+ <%= link_to lit.previous_versions_localization_key_localization_path(lk, localization), :class=>"show_prev_versions #{'hidden' if localization.versions.empty?}", :remote=>true do %>
34
+ <%= draw_icon 'random', :title=>t('lit.common.previous_versions', :default=>"Previous versions") %>
35
+ <% end %>
36
+ <% end %>
32
37
  </td>
33
38
  </tr>
39
+ <tr class="hidden localization_versions_row" data-id="<%= localization.id %>">
40
+ <td colspan="2" class="localization_versions"></td>
41
+ </tr>
34
42
  <% end %>
35
43
  </table>
36
44
  </div>
@@ -39,9 +47,8 @@
39
47
  <% end %>
40
48
 
41
49
  </table>
42
- <% begin %>
50
+ <% if defined?(Kaminari) %>
43
51
  <%= paginate @localization_keys, :theme=>"lit" %>
44
- <% rescue %>
45
52
  <% end %>
46
53
  <% content_for(:sidebar) do %>
47
54
  <div class="well">
@@ -1,6 +1,14 @@
1
1
  <%= form_for [@localization_key, @localization], :html=>{:remote=>true} do |f| %>
2
2
  <%= f.label :translated_value %>
3
- <%= f.text_area :translated_value, :size=>"40x2", :class=>"input-xxlarge" %>
3
+ <% if @localization.translated_value.is_a?(Array) %>
4
+ <ul style="list-style: none;">
5
+ <% @localization.translated_value.each do |l| %>
6
+ <li><%= text_field_tag 'localization[translated_value][]', l, :class=>"input-xlarge" %></li>
7
+ <% end %>
8
+ </ul>
9
+ <% else %>
10
+ <%= f.text_area :translated_value, :size=>"40x2", :class=>"input-xxlarge" %>
11
+ <% end %>
4
12
  <button class="btn" type="submit"><%= t('lit.common.update', :default=>"Update") %></button>
5
13
  <button class="btn cancel"><%= t('lit.common.cancel', :default=>'Cancel') %></button>
6
14
  <% end %>
@@ -0,0 +1,15 @@
1
+ <span class="pull-right"><%= draw_icon 'remove', :class=>"close_versions" %></span>
2
+ <table class="table table-condensed">
3
+ <tr>
4
+ <th><%= t('lit.common.previous_versions') %></th>
5
+ <th width="25%"><%= t('lit.common.created_at') %></th>
6
+ </tr>
7
+ <% @versions.each do |v| %>
8
+ <tr>
9
+ <td>
10
+ <%= render :partial=>"/lit/localization_keys/localization_row", :locals=>{:localization=>v.translated_value} %>
11
+ </td>
12
+ <td><%= v.created_at %></td>
13
+ </tr>
14
+ <% end %>
15
+ </table>
@@ -0,0 +1,3 @@
1
+ var row = $('.localization_versions_row[data-id="<%= @localization.id %>"]');
2
+ row.removeClass('hidden');
3
+ row.children('td').html("<%= j render(:partial=>'previous_versions_rows', :locals=>{:localization=>@localization, :versions=>@versions}) %>");
@@ -1,4 +1,4 @@
1
1
  var $row = $('td.localization_row[data-id="<%= @localization.id %>"]');
2
2
  $row.data('editing', 0);
3
- $row.data('content', "<%= ejs @localization.get_value %>");
4
- $row.html("<%= @localization.get_value %>");
3
+ $row.html("<%= ejs render(:partial=>"/lit/localization_keys/localization_row", :locals=>{:localization=>@localization.translated_value }) %>");
4
+ $row.siblings().find('.show_prev_versions').removeClass('hidden');
@@ -0,0 +1,35 @@
1
+ <%= form_for(@source, :class=>"form-horizontal") do |f| %>
2
+ <% if @source.errors.any? %>
3
+ <div id="error_explanation">
4
+ <h2><%= pluralize(@source.errors.count, "error") %> prohibited this source from being saved:</h2>
5
+
6
+ <ul>
7
+ <% @source.errors.full_messages.each do |msg| %>
8
+ <li><%= msg %></li>
9
+ <% end %>
10
+ </ul>
11
+ </div>
12
+ <% end %>
13
+ <div class="control-group">
14
+ <%= f.label :identifier, :class=>"control-label" %>
15
+ <div class="controls">
16
+ <%= f.text_field :identifier %>
17
+ </div>
18
+ </div>
19
+ <div class="control-group">
20
+ <%= f.label :url, :class=>"control-label" %>
21
+ <div class="controls">
22
+ <%= f.text_field :url, :placeholder=>"http://your_other_env.host/lit" %>
23
+ </div>
24
+ </div>
25
+ <div class="control-group">
26
+ <%= f.label :api_key, :class=>"control-label" %>
27
+ <div class="controls">
28
+ <%= f.text_field :api_key %>
29
+ </div>
30
+ </div>
31
+
32
+ <div class="actions">
33
+ <%= f.submit t('lit.common.save'), :class=>"btn" %>
34
+ </div>
35
+ <% end %>
@@ -0,0 +1,6 @@
1
+ <h1>Editing source</h1>
2
+
3
+ <%= render 'form' %>
4
+
5
+ <%= link_to 'Show', @source %> |
6
+ <%= link_to 'Back', sources_path %>
@@ -0,0 +1,36 @@
1
+ <h1>Synchronization sources</h1>
2
+
3
+ <table class="table">
4
+ <tr>
5
+ <th>Identifier</th>
6
+ <th>Last synchronization</th>
7
+ <th>Last change (remote)</th>
8
+ <th width="80px"></th>
9
+ </tr>
10
+
11
+ <% @sources.each do |source| %>
12
+ <tr>
13
+ <td><%= link_to source.identifier, lit.source_path(source) %></td>
14
+ <td><%= source.last_updated_at.to_s(:db) unless source.last_updated_at.nil? %></td>
15
+ <td><%= source.get_last_change %></td>
16
+ <td>
17
+ <%= link_to lit.synchronize_source_path(source), :title=>t('lit.common.synchronize', :default=>"Synchronize") do %>
18
+ <%= draw_icon 'refresh' %>
19
+ <% end %>
20
+ <%= link_to lit.source_path(source), :title=>t('lit.common.show', :default=>"Show") do %>
21
+ <%= draw_icon 'zoom-in' %>
22
+ <% end %>
23
+ <%= link_to lit.edit_source_path(source), :title=>t('lit.common.edit', :default=>"Edit") do %>
24
+ <%= draw_icon 'pencil' %>
25
+ <% end %>
26
+ <%= link_to lit.source_path(source), :method => :delete, :data => { :confirm => 'Are you sure?' }, :title=>t('lit.common.delete', :default=>"Delete") do %>
27
+ <%= draw_icon 'trash' %>
28
+ <% end %>
29
+ </td>
30
+ </tr>
31
+ <% end %>
32
+ </table>
33
+
34
+ <br />
35
+
36
+ <%= link_to 'New Source', new_source_path %>
@@ -0,0 +1,5 @@
1
+ <h1>New source</h1>
2
+
3
+ <%= render 'form' %>
4
+
5
+ <%= link_to 'Back', sources_path %>