Created
August 17, 2011 08:56
-
-
Save amardaxini/1151126 to your computer and use it in GitHub Desktop.
redis i18n
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<tr> | |
<td><%[email protected](".").drop(1) %></td> | |
<td><%= @value%></td> | |
</tr> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This class is helpful for redis i18n implementation | |
# | |
#--- | |
# en: | |
# text_menu_contacts: "Contacts" | |
# text_menu_accounts: "Accounts" | |
# text_menu_opportunity: "Opportunities" | |
# errors: | |
# template: | |
# header: "%{count} errors prohibited this %{model} from being saved" | |
class DynamicI18n | |
#$redis = Redis.new | |
# yaml to redis for i18n | |
# file name is "/abc/en.yml" | |
def self.i18n_yaml_to_redis(file_name) | |
# for picking up initial key for iterating | |
# TODO may be it can be automated further | |
i18n_key = file_name.split(".").first.split("/").last | |
#i18n_key = "en" | |
if File.exist?(file_name) | |
i18n_yml = YAML::load(File.open(file_name)) | |
i18n_yml[i18n_key].each do |k,v| | |
# if yaml data contain further nested then create nestedkey or add key to redis | |
if i18n_yml[i18n_key][k].is_a?(Hash) | |
self.add_value_to_redis("#{i18n_key}",i18n_yml[i18n_key]) | |
else | |
create("#{i18n_key}.#{k}","#{v}") | |
end | |
end | |
end | |
end | |
# en. | |
# errors: | |
# template: | |
# header: "%{count} errors prohibited this %{model} from being saved" | |
# THis method generates folloeing key and set its respective value | |
#en.errors.template.header="%{count} errors prohibited this %{model} from being saved" | |
def self.add_value_to_redis(i18n_key,k) | |
k.each do |key,value| | |
if k[key].is_a?(Hash) | |
self.add_value_to_redis(i18n_key+"."+key,k[key]) | |
else | |
create("#{i18n_key}.#{key}","#{value}") | |
end | |
end | |
end | |
# Add to en(master) it will add to all company | |
# Company is a redis hash which contain company id or company name as per requirement | |
# and its i18n value liek us_20 | |
# any data is added to master it will find all the respective company and add data | |
def self.i18n_add_to_master(key,val) | |
create("#{key}","#{val}") | |
key = key.split(".").drop(1) | |
# find out all company and add to all company | |
# like after create callback | |
company_array = $redis.hgetall("company") | |
company_array.each do |ckey,cvalue| | |
create("#{cvalue}.#{key}",val) | |
end | |
end | |
# for removing data from master and company | |
def self.i18n_remove_from_master(key) | |
remove("#{key}") | |
#key = en.abc.xyz | |
key = key.split(".").drop(1) | |
# key abc.xyz | |
company_array = $redis.hgetall("company") | |
company_array.each do |ckey,cvalue| | |
destroy("#{cvalue}.#{key}") | |
end | |
end | |
# add to redis | |
def self.create(key,value) | |
$redis.set(key,value) | |
end | |
# remove from redis | |
def self.destroy(key) | |
$redis.del(key) | |
end | |
# get data from redis | |
def self.find(key) | |
$redis.get(key) | |
end | |
#This contain name = "company name or id",value en_20 | |
def self.add_company(name,value) | |
$redis.hset("company",name,value) | |
end | |
#This contain name = "company name or id" | |
def self.remove_company(name) | |
$redis.hdel("company",name) | |
end | |
# find out word and replace it | |
def self.search_and_replace_word(search_value,replace_value,i18n="*") | |
i18n_keys = find_all(i18n) | |
i18n_keys.each do |i18n_key| | |
value = $redis.get(i18n_key) | |
value.gsub!(/\b#{search_value}\b/,replace_value) | |
$redis.set(i18n_key,value) | |
end | |
end | |
# add master clone to another company | |
# i18n_key is us_20 | |
def self.clone_master_data_for_company(i18n_key) | |
# find out all master keys i.e en | |
master_keys= find_all("en.*") | |
master_keys.each do |key| | |
clone_key = "#{i18n_key}.#{key.split('.').drop(1)}" | |
create(clone_key,$redis.get(key)) | |
end | |
end | |
def self.find_all(key) | |
$redis.keys(key) | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class DynamicI18nsController < ApplicationController | |
before_filter :load_redis | |
layout 'admin' | |
def index | |
@companies = @redis.hgetall("company").to_a | |
#companies =Company.find(:all,companies.keys).collect{|c| [c.name,c.dynamic_label.file_name]} | |
# @companies = Company.find(companies.keys) | |
if params[:company].present? | |
@company_key = params[:company] | |
else | |
@company_key = "en" | |
end | |
@translations = @redis.keys("#{@company_key}.*") | |
end | |
def add | |
@key = params[:i18n]+"."+params[:key] | |
if params[:i18n] == "en" | |
DynamicI18n.i18n_add_to_master(@key,params[:value]) | |
else | |
DynamicI18n.create(@key,params[:value]) | |
end | |
@value = DynamicI18n.find(@key) | |
respond_to do |format| | |
format.js | |
end | |
end | |
def modify | |
DynamicI18n.create(params[:id],params[:value]) | |
render :text=>DynamicI18n.find(params[:id]) | |
end | |
def remove | |
DynamicI18n.destroy(params[:key]) | |
end | |
def replace_word | |
if params[:company] | |
search_key = "#{params[:company]}.*" | |
company_key = params[:company] | |
else | |
search_key = "*" | |
company_key = "en" | |
end | |
DynamicI18n.search_and_replace_word(params[:search_value],params[:replace_value],search_key) | |
redirect_to dynamic_i18ns_path(:company=>company_key) | |
end | |
private | |
def load_redis | |
@redis = $redis | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Assuming redis on default localhost and port 5678 | |
module I18nBackend | |
def self.connect | |
$redis = Redis.new | |
I18n.backend = I18n::Backend::Chain.new(I18n::Backend::KeyValue.new($redis), I18n.backend) | |
end | |
end | |
if defined?(PhusionPassenger) | |
PhusionPassenger.on_event(:starting_worker_process) do |forked| | |
if forked | |
# We're in smart spawning mode. If we're not, we do not need to do anything | |
I18nBackend.connect | |
end | |
end | |
else | |
I18nBackend.connect | |
end | |
#TRANSLATION_STORE = Redis.new | |
#I18n.backend =I18n::Backend::KeyValue.new(TRANSLATION_STORE) | |
#I18n.backend = I18n::Backend::Chain.new(I18n::Backend::KeyValue.new(TRANSLATION_STORE), I18n.backend) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%= javascript_include_tag "jquery.jeditable.js" %> | |
<div> | |
<% form_tag('/dynamic_i18ns',:method=>:get,:id=>"company_select_form") do %> | |
<%= select_tag :company,"<option value=en>Master Data</option>"+options_for_select(@companies,:selected=>@company_key )%> | |
<% end %> | |
<% form_tag('/dynamic_i18ns/replace_word',:id=>"replace_form") do %> | |
<strong>Search</strong> <%= text_field_tag :search_value %> | |
<strong>Replace</strong> <%=text_field_tag :replace_value %> | |
<%= hidden_field_tag :company,@company_key %> | |
<%= submit_tag "Find & Replace" %> | |
<% end %> | |
<strong>Key</strong> <%= text_field_tag :i18n_key %> | |
<strong>Value</strong> <%=text_field_tag :i18n_value %> | |
<%= submit_tag "Add" ,:id=>"add_i18n"%> | |
</div> | |
<table> | |
<thead> | |
<tr> | |
<th> Key </th> | |
<th> ORIGINAL </th> | |
<th> Values </th> | |
</tr> | |
<tbody id="i18n_table_body"> | |
<% @translations.each do |key| %> | |
<tr> | |
<td> | |
<%i18n_key = key.split(".").drop(1)%> | |
<%= key.split(".").drop(1) %> | |
</td> | |
<td> <%= @redis["en.#{i18n_key}"]%></td> | |
<td class="edit_in_place" id="<%= key %>" ><%= @redis[key]%></td> | |
</tr> | |
<% end %> | |
</tbody> | |
</thead> | |
</table> | |
<script type="text/javascript"> | |
function in_line_edit() | |
{ | |
jQuery('.edit_in_place').editable('/dynamic_i18ns/modify', { | |
indicator : 'Saving...', | |
tooltip : 'Click to edit...', | |
id:$(this).attr('id'), | |
submit : 'OK', | |
width: '150', | |
cancel: "cancel" | |
}); | |
} | |
in_line_edit(); | |
$("#company").live("change",function(){ | |
$("#company_select_form").submit(); | |
}); | |
$("#add_i18n").live("click",function(){ | |
$.ajax({ | |
type: "POST", | |
url: "/dynamic_i18ns/add", | |
data: "key="+$("#i18n_key").val()+"&value="+$("#i18n_value").val()+"&i18n="+"<%= @company_key %>", | |
success: function(html){ | |
/*html content response of city_list.xxx */ | |
$("#i18n_table_body").append(html); | |
$("#i18n_key").val(""); | |
$("#i18n_value").val(""); | |
} | |
}); | |
}); | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
I18n implementation using redis | |
Reference | |
https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale | |
http://ruby-i18n.org/ | |
http://railscasts.com/episodes/138-i18n | |
http://redis.io/ | |
http://redis.io/commands | |
https://github.com/ezmobius/redis-rb | |
Older Implementation | |
When company is created they can choose I18n and if they want customize version of labeling they can do it. | |
dynamic_label model is used to store i18n name like "us". | |
if it is customize then new file is created with i18nname_company_id like us_20 | |
and i18n is reloaded again or required server to be again restart after changing data every time. | |
Current Implementation | |
Instead of file we used redis database to handle this kind of stuff. | |
This help to allow change data and reflect imidiately to client. | |
We are useing reids-rb gem (ruby interface for redis). | |
There are mainly 3 files to do this job | |
1) config/initializers/i18n_backend.rb | |
It helps us to connect redis server and setting I18n Backend with redis | |
More info view railscasts | |
2) app/models/dynamic_i18n.rb | |
This is like model. | |
It helps us to CRUD Operation to redis and many more. | |
3) app/controllers/dynamic_i18ns_controller.rb | |
There are certain Scenario we need to handle like | |
What about older data? How we migrate older data? what if new company is added? | |
Older data is migrate to redis by using rake task | |
rake locale_to_redis | |
This task do following things | |
1) Initially add en and us locale file to redis | |
2) Find out all dynamic label of each company | |
3) If label is customized and its respective file is present then | |
It adds data to redis | |
Add company to redis hash("company") (company_id=>"#{i18nname}_#{company_id}" | |
4) If label is customized and file is not present or no customizaion is present then | |
It copies master data i.e en data to respective company with suffix with _company_id("us_20") | |
Add company to redis hash("company") (company_id=>"#{i18nname}_#{company_id}" | |
Update dynamic_label i18n name with #{i18nname}_#{company_id} | |
What if new company is added ? | |
It copies master data i.e en data to respective company with suffix with _company_id("us_20") | |
Add company to redis hash("company") (company_id=>"#{i18nname}_#{company_id}" | |
What happens if we added new data to master? | |
If data is added to master it will imediately reflected all existing company data. | |
TODO above things you need to start redis server | |
Download redis and install(http://redis.io/download) | |
start redis server using redis-server | |
If you want to start redis on background | |
nohup path_of_redis-server >> /dev/null 2>&1 & | |
In Production if we are using passenger and forking method is smart | |
then passenger spawn a new process with same redis connection | |
so mutex issue comes in picture to handle this each time process is forked add new redis connection | |
If this is not done passenger spawn process hike cpu and memory usage abruptly | |
to understand more on this | |
http://ryreitsma.blogspot.com/2011/07/redis-and-phusion-passenger-reconnect.html | |
https://github.com/ezmobius/redis-rb/issues/117 | |
http://www.modrails.com/documentation/Users%20guide%20Apache.html#_example_1_memcached_connection_sharing_harmful | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment