2 |
26 | <% if params[:group] && params[:group] == 'deleted' %>
27 |
28 | You can delete those translations from database as application doesn't use them anymore.
29 |
30 | <% end %>
31 |
32 | <% @keys.each do |key| %>
33 |
34 |
35 | <%-
36 | parts = key.split('.').map(&:humanize)
37 | if params[:key].present?
38 | parts.delete_at(0)
39 | elsif parts.size > 1
40 | parts[0] = link_to(parts[0], translations_path(:group => params[:group], :key => parts[0].underscore), :title => "Go to section " + parts[0])
41 | end
42 | %>
43 | <%= parts.join(' > ').html_safe %>
44 |
45 |
46 | <% if params[:group] && params[:group] == 'deleted' %>
47 | <%= link_to 'destroy', translation_path(:id => key.gsub('.','-')),
48 | :remote => true,
49 | :method => :delete,
50 | :confirm => 'Are you sure? This operation is irreversible.',
51 | :class => 'button warning',
52 | :title => 'Clear form database as translation no longer exists in default language file.'
53 | %>
54 | <% end %>
55 | <% Translator.locales.each do |locale| %>
56 | <%= render :partial => "form", :locals => {:locale => locale, :key => key} %>
57 | <% end %>
58 |
59 | <% end %>
60 |
61 | <% unless @total_pages < 2 %>
62 | <% (1..@total_pages).each do |p| %>
63 | <%= link_to_unless_current p, translations_path(:group => params[:group],
64 | :search => params[:search],
65 | :key => params[:key],
66 | :translated => params[:translated],
67 | :page => p), :class => "button small" %>
68 | <% end %>
69 | <% end %>
70 |
71 |
72 |
80 |
81 |
87 |
88 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | Translator::Engine.routes.draw do
2 | resources :translations do
3 | collection do
4 | get :export
5 | post :import
6 | end
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/lib/translator.rb:
--------------------------------------------------------------------------------
1 | require 'translator/engine' if defined?(Rails) && Rails::VERSION::STRING.to_f >= 3.1
2 |
3 | module Translator
4 | class << self
5 | attr_accessor :auth_handler, :current_store, :framework_keys
6 | attr_reader :simple_backend
7 | attr_writer :layout_name
8 | end
9 |
10 | @framework_keys = ["date.formats.default", "date.formats.short", "date.formats.long",
11 | "time.formats.default", "time.formats.short", "time.formats.long", "time.am", "time.pm",
12 | "support.array.words_connector", "support.array.two_words_connector", "support.array.last_word_connector",
13 | "errors.format", "errors.messages.inclusion", "errors.messages.exclusion", "errors.messages.invalid",
14 | "errors.messages.confirmation", "errors.messages.accepted", "errors.messages.empty",
15 | "errors.messages.blank", "errors.messages.too_long", "errors.messages.too_short", "errors.messages.wrong_length",
16 | "errors.messages.not_a_number", "errors.messages.not_an_integer", "errors.messages.greater_than",
17 | "errors.messages.greater_than_or_equal_to", "errors.messages.equal_to", "errors.messages.less_than",
18 | "errors.messages.less_than_or_equal_to", "errors.messages.odd", "errors.messages.even", "errors.required", "errors.blank",
19 | "number.format.separator", "number.format.delimiter", "number.currency.format.format", "number.currency.format.unit",
20 | "number.currency.format.separator", "number.currency.format.delimiter", "number.percentage.format.delimiter",
21 | "number.precision.format.delimiter", "number.human.format.delimiter", "number.human.storage_units.format",
22 | "number.human.storage_units.units.byte.one", "number.human.storage_units.units.byte.other",
23 | "number.human.storage_units.units.kb", "number.human.storage_units.units.mb", "number.human.storage_units.units.gb",
24 | "number.human.storage_units.units.tb", "number.human.decimal_units.format", "number.human.decimal_units.units.unit",
25 | "number.human.decimal_units.units.thousand", "number.human.decimal_units.units.million",
26 | "number.human.decimal_units.units.billion", "number.human.decimal_units.units.trillion",
27 | "number.human.decimal_units.units.quadrillion", "datetime.distance_in_words.half_a_minute",
28 | "datetime.distance_in_words.less_than_x_seconds.one", "datetime.distance_in_words.less_than_x_seconds.other",
29 | "datetime.distance_in_words.x_seconds.one", "datetime.distance_in_words.x_seconds.other",
30 | "datetime.distance_in_words.less_than_x_minutes.one", "datetime.distance_in_words.less_than_x_minutes.other",
31 | "datetime.distance_in_words.x_minutes.one", "datetime.distance_in_words.x_minutes.other",
32 | "datetime.distance_in_words.about_x_hours.one", "datetime.distance_in_words.about_x_hours.other",
33 | "datetime.distance_in_words.x_days.one", "datetime.distance_in_words.x_days.other",
34 | "datetime.distance_in_words.about_x_months.one", "datetime.distance_in_words.about_x_months.other",
35 | "datetime.distance_in_words.x_months.one", "datetime.distance_in_words.x_months.other",
36 | "datetime.distance_in_words.about_x_years.one", "datetime.distance_in_words.about_x_years.other",
37 | "datetime.distance_in_words.over_x_years.one", "datetime.distance_in_words.over_x_years.other",
38 | "datetime.distance_in_words.almost_x_years.one", "datetime.distance_in_words.almost_x_years.other",
39 | "datetime.prompts.year", "datetime.prompts.month", "datetime.prompts.day", "datetime.prompts.hour",
40 | "datetime.prompts.minute", "datetime.prompts.second", "helpers.select.prompt", "helpers.submit.create",
41 | "helpers.submit.update", "helpers.submit.submit"]
42 |
43 | def self.setup_backend(simple_backend)
44 | @simple_backend = simple_backend
45 |
46 | I18n::Backend::Chain.new(I18n::Backend::KeyValue.new(@current_store), @simple_backend)
47 | end
48 |
49 | def self.locales
50 | @simple_backend.available_locales
51 | end
52 |
53 | def self.keys_for_strings(options = {})
54 | @simple_backend.available_locales
55 |
56 | flat_translations = {}
57 | flatten_keys nil, Translator.get_translations, flat_translations
58 | flat_translations = flat_translations.delete_if {|k,v| !v.is_a?(String) }
59 | store_keys = Translator.current_store.keys.map {|k| k.sub(/^[a-z0-9\-_]*\./i, '')}
60 |
61 | keys = if options[:group].to_s == "deleted"
62 | store_keys - flat_translations.keys
63 | else
64 | store_keys + flat_translations.keys
65 | end.uniq
66 |
67 | if options[:filter]
68 | keys = keys.select {|k| k[0, options[:filter].size] == options[:filter]}
69 | end
70 |
71 | case options[:group].to_s
72 | when "framework"
73 | keys.select! {|k| @framework_keys.include?(k) }
74 | when "application"
75 | keys -= @framework_keys
76 | end
77 |
78 | keys || []
79 | end
80 |
81 | def self.layout_name
82 | @layout_name || "translator"
83 | end
84 |
85 | private
86 |
87 | def self.get_translations
88 | @simple_backend.instance_variable_get("@translations")[:en]
89 | end
90 |
91 | def self.flatten_keys(current_key, hash, dest_hash)
92 | hash.each do |key, value|
93 | full_key = [current_key, key].compact.join('.')
94 | if value.kind_of?(Hash)
95 | flatten_keys full_key, value, dest_hash
96 | else
97 | dest_hash[full_key] = value
98 | end
99 | end
100 | hash
101 | end
102 | end
103 |
104 |
--------------------------------------------------------------------------------
/lib/translator/engine.rb:
--------------------------------------------------------------------------------
1 | module Translator
2 | class Engine < Rails::Engine
3 | isolate_namespace Translator
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/spec/acceptance/acceptance_helper.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + "/../spec_helper"
2 | require 'capybara/rails'
3 |
4 | Capybara.default_driver = :selenium
5 |
6 | RSpec.configure do |config|
7 | config.include Capybara::DSL
8 | end
9 |
10 | RSpec.configuration.include Capybara, :type => :acceptance
11 |
12 | # Put your acceptance spec helpers inside /spec/acceptance/support
13 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
14 |
15 | def autoconfirm
16 | page.evaluate_script("window.alert = function(msg) { return true; }")
17 | page.evaluate_script("window.confirm = function(msg) { return true; }")
18 | end
19 |
20 | def sign_in_as_admin
21 | Admin.create! admin_attributes(:email => "a@b.com", :password => "testtest")
22 | visit "/admins/sign_in"
23 | fill_in "Email", :with => "a@b.com"
24 | fill_in "Password", :with => "testtest"
25 | click_button "Sign in"
26 | end
27 |
28 | def sign_in_as_superadmin
29 | admin = Admin.create! admin_attributes(:email => "super@super.com", :password => "super")
30 | admin.superadmin = true
31 | admin.save!
32 | visit "/admins/sign_in"
33 | fill_in "Email", :with => "super@super.com"
34 | fill_in "Password", :with => "super"
35 | click_button "Sign in"
36 | end
37 |
38 |
--------------------------------------------------------------------------------
/spec/acceptance/mongodb_spec.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | require File.dirname(__FILE__) + '/acceptance_helper'
3 | require File.dirname(__FILE__) + '/translations_management'
4 |
5 | feature "Translations management with MongoDB", %q{
6 | In order to show user app in different translate
7 | As a app admin
8 | I want to
9 | } do
10 |
11 | background do
12 | conn = Mongo::Connection.new.db("translator_test").collection("translations")
13 | Translator.current_store = Translator::MongoStore.new(conn)
14 | I18n.backend = Translator.setup_backend(I18n::Backend::Simple.new)
15 | Translator.current_store.clear_database
16 | visit translations_path
17 | end
18 |
19 | it_should_behave_like "translations_management"
20 | end
21 |
22 |
--------------------------------------------------------------------------------
/spec/acceptance/redis_spec.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | require File.dirname(__FILE__) + '/acceptance_helper'
3 | require File.dirname(__FILE__) + '/translations_management'
4 |
5 | feature "Translations management with Redis", %q{
6 | In order to show user app in different translate
7 | As a app admin
8 | I want to
9 | } do
10 |
11 | background do
12 | Translator.current_store = Translator::RedisStore.new(Redis.new)
13 | I18n.backend = Translator.setup_backend(I18n::Backend::Simple.new)
14 | Translator.current_store.clear_database
15 | visit translations_path
16 | end
17 |
18 | it_should_behave_like "translations_management"
19 | end
20 |
21 |
--------------------------------------------------------------------------------
/spec/acceptance/support/helpers.rb:
--------------------------------------------------------------------------------
1 | # Put helper methods you need to be available in all tests here.
2 | def translations_path
3 | '/translations'
4 | end
5 |
6 |
--------------------------------------------------------------------------------
/spec/acceptance/translations_management.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | #
3 | shared_examples_for "translations_management" do
4 | scenario "see translations keys specified in main language yaml file" do
5 | page.should have_content "Date > Formats > Default"
6 | end
7 |
8 | scenario "see translations provided in language files" do
9 | visit root_path
10 | page.should have_content "Hello world!"
11 | visit root_path(:locale => "pl")
12 | page.should have_content "Witaj, Świecie"
13 | end
14 |
15 | scenario "editing translations" do
16 | visit translations_path + "/?utf8=✓&search=&key=&group=application&translated=&commit=Submit"
17 | within :css, "#pl-hello-world" do
18 | fill_in "value", :with => "Elo ziomy"
19 | click_button "Save"
20 | end
21 |
22 | within :css, "#en-hello-world" do
23 | fill_in "value", :with => "Yo hommies"
24 | click_button "Save"
25 | end
26 |
27 | visit root_path
28 | page.should have_content("Yo hommies")
29 | visit root_path(:locale => "pl")
30 | page.should have_content("Elo ziomy")
31 | end
32 |
33 | scenario "see only all translations by default, app ones after selecting from dropdown" do
34 | page.should have_content("Date > Formats")
35 | select "Application", from: "group"
36 | click_button "Submit"
37 | page.should_not have_content("Date > Formats")
38 | page.should have_content("World")
39 | end
40 |
41 | scenario "paginate translations, 50 on every page" do
42 | page.should have_content("Date > Formats")
43 | click_link "2"
44 | page.should_not have_content("Date > Formats")
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/spec/controllers/translations_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | require 'spec_helper'
3 |
4 | RSpec.describe Translator::TranslationsController do
5 |
6 | before :all do
7 | conn = Mongo::Connection.new.db("translator_test").collection("translations")
8 | Translator.current_store = Translator::MongoStore.new(conn)
9 | I18n.backend = Translator.setup_backend(I18n::Backend::Simple.new)
10 | end
11 |
12 | after :all do
13 | Translator.auth_handler = nil
14 | end
15 |
16 | describe "GET index" do
17 | context "auth_handler" do
18 | it "responds OK with no auth proc" do
19 | get :index, use_route: '/'
20 | expect(response.status).to eq(200)
21 | end
22 |
23 | it "responds 401 with auth proc defined" do
24 | Translator.auth_handler = proc {
25 | head 401
26 | }
27 | get :index, use_route: '/'
28 | expect(response.status).to eq(401)
29 | end
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/spec/dummy/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require File.expand_path('../config/application', __FILE__)
5 | require 'rake'
6 |
7 | Dummy::Application.load_tasks
8 |
--------------------------------------------------------------------------------
/spec/dummy/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | before_filter :set_locale
3 | protect_from_forgery
4 |
5 | def set_locale
6 | I18n.locale = params[:locale] || :en
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/spec/dummy/app/controllers/welcome_controller.rb:
--------------------------------------------------------------------------------
1 | class WelcomeController < ApplicationController
2 | def index; end
3 | end
4 |
--------------------------------------------------------------------------------
/spec/dummy/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |