Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Js translations #147

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ translating app by non-technicals.

Highly inspired by Copycopter by thoughtbot.

[![travis status](https://travis-ci.org/prograils/lit.svg)](https://travis-ci.org/prograils/lit)
[![travis status](https://api.travis-ci.org/3eggert/lit.svg?branch=js_translations)](https://api.travis-ci.org/3eggert/lit.svg?branch=js_translations)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please revert that

Suggested change
[![travis status](https://api.travis-ci.org/3eggert/lit.svg?branch=js_translations)](https://api.travis-ci.org/3eggert/lit.svg?branch=js_translations)
[![travis status](https://travis-ci.org/prograils/lit.svg)](https://travis-ci.org/prograils/lit)


### Features

Expand Down
5 changes: 5 additions & 0 deletions app/controllers/lit/dashboard_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,10 @@ class DashboardController < ::Lit::ApplicationController
def index
@locales = Lit::Locale.ordered.visible
end

def clear_usage_data
Lit::LocalizationKey.where.not(used_last_at: nil).update_all(usage_count: 0, used_last_at: nil)
redirect_to root_path
end
end
end
10 changes: 10 additions & 0 deletions app/controllers/lit/localization_keys_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ def index
get_localization_keys
end

def not_used
@scope = @scope.not_used
get_localization_keys
end

def used
@scope = @scope.used
get_localization_keys
end

def not_translated
@scope = @scope.not_completed
get_localization_keys
Expand Down
11 changes: 11 additions & 0 deletions app/jobs/lit/persit_global_hits_counters_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Lit
if defined?(::ActiveJob)
class PersitGlobalHitsCountersJob < ::ActiveJob::Base
queue_as :default

def perform(update_array)
PersitGlobalHitsCountersService.new(update_array).execute
end
end
end
end
2 changes: 2 additions & 0 deletions app/models/lit/localization_key.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ class LocalizationKey < Lit::Base
attr_accessor :interpolated_key

## SCOPES
scope :used, -> { where.not(used_last_at: nil) }
scope :not_used, -> { where(used_last_at: nil) }
scope :completed, -> { where(is_completed: true) }
scope :not_completed, -> { where(is_completed: false) }
scope :starred, -> { where(is_starred: true) }
Expand Down
12 changes: 12 additions & 0 deletions app/services/persit_global_hits_counters_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class PersitGlobalHitsCountersService
def initialize(update_array)
@update_array = update_array
end

def execute
@update_array.each do |a|
Lit::LocalizationKey.find(a[0]).update_columns(usage_count: a[1], used_last_at: Time.now)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as per my comment to Lit::Cache

Suggested change
Lit::LocalizationKey.find(a[0]).update_columns(usage_count: a[1], used_last_at: Time.now)
Lit::LocalizationKey.where(id: a[0]).update_all('usage_count = usage_count + ?, user_last_at = ?', a[1], Time.now)

end
end

end
2 changes: 2 additions & 0 deletions app/views/lit/dashboard/index.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<strong>All localization keys</strong> <%= Lit::LocalizationKey.active.count(:id) %><br/>
<strong>Used localization keys</strong> <%= Lit::LocalizationKey.where.not(used_last_at: nil).count(:id) %><br/>
<%= button_to("clear usage data", {action: "clear_usage_data"}, data: {confirm: "Are you sure you want to clear the usage data?" }) %>
<% @locales.each do |l| %>
<strong><%= EmojiFlag.new(l.locale) %> <%= I18n.t("lit.locale_to_languages.#{l.locale}", :default=>l.locale) %>:</strong> <span title="<%= "#{l.changed_localizations_count}/#{l.all_localizations_count}" %>"><%= l.translated_percentage %>%</span><br/>
<% end %>
Expand Down
8 changes: 7 additions & 1 deletion app/views/lit/localization_keys/_localizations_list.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
<tr class="localization_key_row" data-id="<%= lk.id %>">
<td>
<strong><%= lk.localization_key %></strong>
<span class="badge"><%= Lit.init.cache.get_global_hits_counter(lk.localization_key) %></span>
<% if lk.used_last_at.present? %>
<span class="badge"><%= "#{I18n.t('lit.used', default: 'used')} #{lk.usage_count} #{I18n.t('lit.times_since', default: 'times since')} #{l(Lit::LocalizationKey.maximum(:used_last_at), :format => :short4rb)}, #{I18n.t('lit.times_since', default: 'last use')} #{l(lk.used_last_at, :format => :short4rb)}" %></span>
<% elsif Lit::LocalizationKey.maximum(:used_last_at).present? %>
<span class="badge"><%= "#{I18n.t('lit.not_used', default: 'not used since')} #{l(Lit::LocalizationKey.maximum(:used_last_at), :format => :short4rb)}" %></span>
<% else %>
<span class="badge"><%= "#{I18n.t('lit.not_used', default: 'not used since')}" %></span>
<% end %>
<div class="localization_keys_options">
<% if Lit.store_request_info %>
<%= link_to '#', class: 'request_info_link title', title: 'Show / hide request' do %>
Expand Down
14 changes: 13 additions & 1 deletion app/views/lit/localization_keys/_sidebar.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,19 @@
</div>
<% end %>
<ul class="nav nav-pills nav-stacked">
<li class="<%= "active" if params[:action]=='not_translated' %>">
<li class="<%= "active" if params[:action]=='not_used' %>">
<%= link_to lit.not_used_localization_keys_path do -%>
<%= draw_icon 'times-circle' %>
not used
<% end %>
</li>
<li class="<%= "active" if params[:action]=='used' %>">
<%= link_to lit.used_localization_keys_path do -%>
<%= draw_icon 'check-circle' %>
used
<% end %>
</li>
<li class="<%= "active" if params[:action]=='not_translated' %>">
<%= link_to lit.not_translated_localization_keys_path do -%>
<%= draw_icon 'pencil' %>
not translated
Expand Down
11 changes: 11 additions & 0 deletions app/views/lit/localization_keys/not_used.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<h3><%= I18n.t('lit.not_used_header', default: 'Not used localization keys') %></h3>

<%= render 'localizations_list', available_locales: I18n.backend.available_locales %>

<% if defined?(Kaminari) %>
<%= paginate @localization_keys, :theme=>"lit" %>
<% elsif defined?(WillPaginate) %>
<%= will_paginate @localization_keys %>
<% end %>

<%= render 'sidebar' %>
11 changes: 11 additions & 0 deletions app/views/lit/localization_keys/used.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<h3><%= I18n.t('lit.used_header', default: 'Used localization keys') %></h3>

<%= render 'localizations_list', available_locales: I18n.backend.available_locales %>

<% if defined?(Kaminari) %>
<%= paginate @localization_keys, :theme=>"lit" %>
<% elsif defined?(WillPaginate) %>
<%= will_paginate @localization_keys %>
<% end %>

<%= render 'sidebar' %>
4 changes: 4 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
get :starred
get :find_localization
get :not_translated
get :not_used
get :used
get :visited_again
end
resources :localizations, only: [:edit, :update, :show] do
Expand All @@ -50,6 +52,8 @@
end
end

post 'dashboard/clear_usage_data', to: 'dashboard#clear_usage_data', as: "dashboard_clear_usage_data"

resource :cloud_translation, only: :show

root to: 'dashboard#index'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class LitAddUsageCountAndUsedLastAtToLitLocalizationKeys < Rails::VERSION::MAJOR >= 5 ?
ActiveRecord::Migration[4.2] :
ActiveRecord::Migration
def change
add_column :lit_localization_keys, :usage_count, :integer, index: true
add_column :lit_localization_keys, :used_last_at, :datetime, index: true
end
end
7 changes: 5 additions & 2 deletions lib/generators/lit/install/templates/initializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@
# For more information please check the README.md
Lit.store_request_info = false

# Initialize lit
Lit.init
# Persist global_hits_counter every 1000 lookups, set to nil or comment out to disable
Lit.persit_global_hits_count = 1000

# Initialize lit unless it ist disabled by setting the SKIP_LIT enviroment variable. Disabling is usefull to speed up the startup, for example to execute migrations
Lit.init unless ENV["SKIP_LIT"] == "1"


1 change: 1 addition & 0 deletions lib/lit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module Lit
mattr_accessor :all_translations_are_html_safe
mattr_accessor :set_last_updated_at_upon_creation
mattr_accessor :store_request_info
mattr_accessor :persit_global_hits_count

class << self
attr_accessor :loader
Expand Down
15 changes: 15 additions & 0 deletions lib/lit/cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ module Lit
class Cache
def initialize
@hits_counter = Lit.get_key_value_engine
@hits = 0
@request_info_store = Lit.get_key_value_engine
@hits_counter_working = true
@keys = nil
Expand Down Expand Up @@ -143,6 +144,18 @@ def get_global_hits_counter(key)
@hits_counter['global_hits_counter.' + key]
end

def persit_global_hits_counters
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Over time this will grow - I suggest clearing that with every call of #persit_global_hits_counters and starting from 0 again. Then PersitGlobalHitsCountersService should make db sum count with added value, rather than set it. It will also make hits counter independent of redis (ie. redis can be flushed every now and then).

update_array = []
@hits_counter.each do |k,v|
if k.match?(/^global_hits_counter/)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be works considering adding new method to Redis and Hash adapter, that will return all keys matching given criteria, ie. redis has this already built in: https://redis.io/commands/keys

localization_key = find_localization_key(k.gsub("global_hits_counter.", ""))
update_array << [localization_key.id, @hits_counter[k] + localization_key.usage_count.to_i]
#localization_key.update_columns(usage_count: @hits_counter[k] + localization_key.usage_count.to_i, used_last_at: Time.now)
end
end
PersitGlobalHitsCountersJob.perform_later(update_array)
end

def get_hits_counter(key)
@hits_counter['hits_counter.' + key]
end
Expand Down Expand Up @@ -310,6 +323,8 @@ def find_or_create_localization_key(key_without_locale)
def update_hits_count(key)
return unless @hits_counter_working
key_without_locale = split_key(key).last
@hits += 1
persit_global_hits_counters if Lit.persit_global_hits_count.present? && (@hits%Lit.persit_global_hits_count) == 0
@hits_counter.incr('hits_counter.' + key)
@hits_counter.incr('global_hits_counter.' + key_without_locale)
end
Expand Down
16 changes: 11 additions & 5 deletions lib/lit/export.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module Lit
class Export
def self.call(locale_keys:, format:, include_hits_count: false)
raise ArgumentError, "format must be yaml or csv" if %i[yaml csv].exclude?(format)
raise ArgumentError, "format must be yaml, json_js or csv" if %i[json_js yaml csv].exclude?(format)
Lit.loader.cache.load_all_translations
localizations_scope = Lit::Localization.active
if locale_keys.present?
Expand All @@ -19,7 +19,10 @@ def self.call(locale_keys:, format:, include_hits_count: false)
when :yaml
exported_keys = nested_string_keys_to_hash(db_localizations)
exported_keys.to_yaml
when :csv
when :json_js
exported_keys = nested_string_keys_to_hash(db_localizations, "javascript")
"var js_locale= " + exported_keys.to_json
when :csv
relevant_locales = locale_keys.presence || I18n.available_locales.map(&:to_s)
CSV.generate do |csv|
csv << ['key', *relevant_locales, ('hits' if include_hits_count)].compact
Expand Down Expand Up @@ -55,7 +58,7 @@ def self.call(locale_keys:, format:, include_hits_count: false)
end
end

private_class_method def self.nested_string_keys_to_hash(db_localizations)
private_class_method def self.nested_string_keys_to_hash(db_localizations, key_selector=nil)
# http://subtech.g.hatena.ne.jp/cho45/20061122
deep_proc = proc do |_k, s, o|
if s.is_a?(Hash) && o.is_a?(Hash)
Expand All @@ -66,8 +69,11 @@ def self.call(locale_keys:, format:, include_hits_count: false)
nested_keys = {}
db_localizations.sort.each do |k, v|
key_parts = k.to_s.split('.')
converted = key_parts.reverse.reduce(v) { |a, n| { n => a } }
nested_keys.merge!(converted, &deep_proc)
if (key_selector.present? && key_parts[1] == key_selector) || key_selector.nil?
key_parts = key_parts.drop(2) if key_selector.present?
converted = key_parts.reverse.reduce(v) { |a, n| { n => a } }
nested_keys.merge!(converted, &deep_proc)
end
end
nested_keys
end
Expand Down