diff --git a/Changes.md b/Changes.md index b5d10599..b35c79a3 100644 --- a/Changes.md +++ b/Changes.md @@ -1,3 +1,11 @@ +2.10.0 +----------- + +- I18n for web UI. Please submit translations of `web/locales/en.yml` for +your own language. [#811] +- 'sinatra', 'slim' and 'i18n' are now gem dependencies for Sidekiq. + + 2.9.0 ----------- diff --git a/lib/sidekiq/version.rb b/lib/sidekiq/version.rb index 81499b20..ef471443 100644 --- a/lib/sidekiq/version.rb +++ b/lib/sidekiq/version.rb @@ -1,3 +1,3 @@ module Sidekiq - VERSION = "2.9.0" + VERSION = "2.10.0" end diff --git a/lib/sidekiq/web.rb b/lib/sidekiq/web.rb index 442b6853..a9db8776 100644 --- a/lib/sidekiq/web.rb +++ b/lib/sidekiq/web.rb @@ -1,18 +1,28 @@ require 'sinatra/base' require 'slim' require 'sidekiq/paginator' +require 'i18n' module Sidekiq class Web < Sinatra::Base include Sidekiq::Paginator dir = File.expand_path(File.dirname(__FILE__) + "/../../web") + I18n.load_path += Dir[File.join(dir, 'locales', '*.yml').to_s] + set :public_folder, "#{dir}/assets" set :views, "#{dir}/views" set :root, "#{dir}/public" set :slim, :pretty => true helpers do + def get_locale + (request.env["HTTP_ACCEPT_LANGUAGE"] || 'en')[0,2] + end + + def t(msg, options={}) + I18n.t(msg, options.merge(:locale => get_locale)) + end def reset_worker_list Sidekiq.redis do |conn| diff --git a/sidekiq.gemspec b/sidekiq.gemspec index 7b2ab584..f9092a8f 100644 --- a/sidekiq.gemspec +++ b/sidekiq.gemspec @@ -19,9 +19,10 @@ Gem::Specification.new do |gem| gem.add_dependency 'connection_pool', '~> 1.0' gem.add_dependency 'celluloid', '~> 0.12.0' gem.add_dependency 'multi_json', '~> 1' + gem.add_dependency 'sinatra' + gem.add_dependency 'slim' + gem.add_dependency 'i18n' gem.add_development_dependency 'minitest', '~> 4' - gem.add_development_dependency 'sinatra' - gem.add_development_dependency 'slim' gem.add_development_dependency 'rake' gem.add_development_dependency 'actionmailer', '~> 3' gem.add_development_dependency 'activerecord', '~> 3' diff --git a/web/locales/en.yml b/web/locales/en.yml new file mode 100644 index 00000000..dc73cf8c --- /dev/null +++ b/web/locales/en.yml @@ -0,0 +1,58 @@ +en: + Dashboard: Dashboard + Status: Status + Time: Time + Namespace: Namespace + Realtime: Real-time + History: History + Busy: Busy + Processed: Processed + Failed: Failed + Scheduled: Scheduled + Retries: Retries + Enqueued: Enqueued + Worker: Worker + Workers: Workers + LivePoll: Live Poll + StopPolling: Stop Polling + Queue: Queue + Class: Class + Job: Job + Arguments: Arguments + Started: Started + ShowAll: Show All + CurrentMessagesInQueue: "Current messages in %{queue}" + Delete: Delete + AreYouSureDeleteJob: Are you sure you want to delete this job? + AreYouSureDeleteQueue: Are you sure you want to delete the %{queue} queue? + Queues: Queues + Size: Size + Actions: Actions + NextRetry: Next Retry + RetryCount: Retry Count + RetryNow: Retry Now + LastRetry: Last Retry + OriginallyFailed: Originally Failed + AreYouSure: Are you sure? + DeleteAll: Delete All + RetryAll: Retry All + NoRetriesFound: No retries were found + Error: Error + ErrorClass: Error Class + ErrorMessage: Error Message + ErrorBacktrace: Error Backtrace + GoBack: ← Back + NoScheduledFound: No scheduled jobs were found + When: When + ScheduledJobs: Scheduled Jobs + idle: idle + active: active + Version: Version + Connections: Connections + MemoryUsage: Memory Usage + PeakMemoryUsage: Peak Memory Usage + Uptime: Uptime (days) + OneWeek: 1 week + OneMonth: 1 month + ThreeMonths: 3 months + SixMonths: 6 months diff --git a/web/views/_nav.slim b/web/views/_nav.slim old mode 100755 new mode 100644 index cc8d22a2..69d3ed1e --- a/web/views/_nav.slim +++ b/web/views/_nav.slim @@ -6,7 +6,7 @@ - tabs.each do |title, url| - if url == '' li class="#{(current_path == url) ? 'active':''}" - a href='#{{root_path}}#{{url}}' #{title} + a href='#{{root_path}}#{{url}}' = t(title) - else li class="#{(current_path =~ Regexp.new(url)) ? 'active':''}" - a href='#{{root_path}}#{{url}}' #{title} + a href='#{{root_path}}#{{url}}' = t(title) diff --git a/web/views/_paging.slim b/web/views/_paging.slim old mode 100755 new mode 100644 diff --git a/web/views/_status.slim b/web/views/_status.slim old mode 100755 new mode 100644 index f7dd0dd2..4bba99ef --- a/web/views/_status.slim +++ b/web/views/_status.slim @@ -1,3 +1,3 @@ span.status i class="status-sprite status-#{current_status}" - == current_status + == t(current_status) diff --git a/web/views/_summary.slim b/web/views/_summary.slim old mode 100755 new mode 100644 index 7cee24f6..0b885151 --- a/web/views/_summary.slim +++ b/web/views/_summary.slim @@ -1,19 +1,19 @@ ul.unstyled.summary li.processed span.count #{number_with_delimiter(stats.processed)} - span.desc Processed + span.desc = t('Processed') li.failed span.count #{number_with_delimiter(stats.failed)} - span.desc Failed + span.desc = t('Failed') li.busy span.count #{number_with_delimiter(workers.size)} - span.desc Busy + span.desc = t('Busy') li.scheduled span.count #{number_with_delimiter(stats.scheduled_size)} - span.desc Scheduled + span.desc = t('Scheduled') li.retries span.count #{number_with_delimiter(stats.retry_size)} - span.desc Retries + span.desc = t('Retries') li.enqueued span.count #{number_with_delimiter(stats.enqueued)} - span.desc Enqueued + span.desc = t('Enqueued') diff --git a/web/views/_workers.slim b/web/views/_workers.slim old mode 100755 new mode 100644 index 8bf8423f..d85b99e7 --- a/web/views/_workers.slim +++ b/web/views/_workers.slim @@ -1,10 +1,10 @@ table class="workers table table-hover table-bordered table-striped table-white" thead - th Worker - th Queue - th Class - th Arguments - th Started + th = t('Worker') + th = t('Queue') + th = t('Class') + th = t('Arguments') + th = t('Started') - workers.each_with_index do |(worker, msg), index| tr td= worker @@ -14,7 +14,7 @@ table class="workers table table-hover table-bordered table-striped table-white" td - if msg['payload']['args'].to_s.size > 100 = msg['payload']['args'].inspect[0..100] + "... " - button data-toggle="collapse" data-target="#worker_#{index}" class="btn btn-mini" Show All + button data-toggle="collapse" data-target="#worker_#{index}" class="btn btn-mini" = t('ShowAll') .toggle[id="worker_#{index}" style="display: none;max-width: 750px;"]= msg['payload']['args'] - else = msg['payload']['args'] diff --git a/web/views/dashboard.slim b/web/views/dashboard.slim index bafc6bfd..a0a4d23d 100644 --- a/web/views/dashboard.slim +++ b/web/views/dashboard.slim @@ -1,20 +1,20 @@ script type="text/javascript" src="#{{root_path}}javascripts/dashboard.js" h3 - | Dashboard + = t('Dashboard') span.beacon .ring .dot -h5 Real-time +h5 = t('Realtime') #realtime h5 - span.history-heading History - a href="#{{root_path}}?days=7" class="history-graph #{{"active" if params[:days] == "7"}}" 1 week - a href="#{{root_path}}" class="history-graph #{{"active" if params[:days].nil? || params[:days] == "30"}}" 1 month - a href="#{{root_path}}?days=90" class="history-graph #{{"active" if params[:days] == "90"}}" 3 month - a href="#{{root_path}}?days=180" class="history-graph #{{"active" if params[:days] == "180"}}" 6 month + span.history-heading = t('History') + a href="#{{root_path}}?days=7" class="history-graph #{{"active" if params[:days] == "7"}}" = t('OneWeek') + a href="#{{root_path}}" class="history-graph #{{"active" if params[:days].nil? || params[:days] == "30"}}" = t('OneMonth') + a href="#{{root_path}}?days=90" class="history-graph #{{"active" if params[:days] == "90"}}" = t('ThreeMonths') + a href="#{{root_path}}?days=180" class="history-graph #{{"active" if params[:days] == "180"}}" = t('SixMonths') #history data-processed="#{Sidekiq.dump_json(@processed_history)}" data-failed="#{Sidekiq.dump_json(@failed_history)}" data-update-url="#{{root_path}}dashboard/stats" br @@ -23,24 +23,24 @@ h5 Redis - if @redis_info.fetch("redis_version", nil) .stat h3.redis_version= @redis_info.fetch("redis_version") - p Version + p = t('Version') - if @redis_info.fetch("uptime_in_days", nil) .stat h3.uptime_in_days= @redis_info.fetch("uptime_in_days") - p Uptime (days) + p = t('Uptime') - if @redis_info.fetch("connected_clients", nil) .stat h3.connected_clients= @redis_info.fetch("connected_clients") - p Connections + p = t('Connections') - if @redis_info.fetch("used_memory_human", nil) .stat h3.used_memory_human= @redis_info.fetch("used_memory_human") - p Memory Usage + p = t('MemoryUsage') - if @redis_info.fetch("used_memory_peak_human", nil) .stat h3.used_memory_peak_human= @redis_info.fetch("used_memory_peak_human") - p Peak Memory Usage \ No newline at end of file + p = t('PeakMemoryUsage') diff --git a/web/views/index.slim b/web/views/index.slim old mode 100755 new mode 100644 index af780472..3078673b --- a/web/views/index.slim +++ b/web/views/index.slim @@ -1,10 +1,10 @@ .row.header .span7 - h3 Workers + h3 = t('Workers') == slim :_workers - if workers.size > 0 .row .span2.pull-right form action="#{root_path}reset" method="post" - button.btn.btn-primary.btn-block type="submit" title="If you kill -9 Sidekiq, this table can fill up with old data." Clear Worker List + button.btn.btn-primary.btn-block type="submit" title="#{t('Kill9Warning')}" = t('ClearWorkerList') diff --git a/web/views/layout.slim b/web/views/layout.slim old mode 100755 new mode 100644 index 924e6446..7ae909db --- a/web/views/layout.slim +++ b/web/views/layout.slim @@ -23,26 +23,26 @@ html li p.navbar-text Redis: #{location} li - p.navbar-text Time: #{Time.now.utc.strftime('%H:%M:%S UTC')} + p.navbar-text #{t('Time')}: #{Time.now.utc.strftime('%H:%M:%S UTC')} - if namespace li - p.navbar-text Namespace: #{namespace} + p.navbar-text #{t('Namespace')}: #{namespace} #page .container .row .span2.summary_bar h4 - span.title Status + span.title = t('Status') == slim :_status == slim :_summary - unless current_path == '' .row .span2 - if params[:poll] - a#live-poll.btn.btn-block.btn-primary.active href='#{{root_path}}#{{current_path}}' Stop Polling + a#live-poll.btn.btn-block.btn-primary.active href='#{{root_path}}#{{current_path}}' = t('StopPolling') - else - a#live-poll.btn.btn-block.btn-primary href='#{{root_path}}#{{current_path}}?poll=true' Live Poll + a#live-poll.btn.btn-block.btn-primary href='#{{root_path}}#{{current_path}}?poll=true' = t('LivePoll') .span10 == yield diff --git a/web/views/queue.slim b/web/views/queue.slim old mode 100755 new mode 100644 index af1b96f5..715c21ec --- a/web/views/queue.slim +++ b/web/views/queue.slim @@ -1,15 +1,14 @@ header.row .span5 h3 - | Current messages in - span.title #{@name} + == t('CurrentMessagesInQueue', :queue => @name) .span4 == slim :_paging, :locals => { :url => "#{root_path}queues/#{@name}" } table class="queue table table-hover table-bordered table-striped" thead - th Class - th Arguments + th = t('Class') + th = t('Arguments') th - @messages.each_with_index do |msg, index| tr @@ -17,12 +16,12 @@ table class="queue table table-hover table-bordered table-striped" td - if msg['args'] and msg['args'].to_s.size > 100 = msg['args'].inspect[0..100] + "... " - button data-toggle="collapse" data-target="#worker_#{index}" class="btn btn-mini" Show All + button data-toggle="collapse" data-target="#worker_#{index}" class="btn btn-mini" = t('ShowAll') .toggle[id="worker_#{index}" style="display: none;"]= msg['args'] - else = msg['args'] td form action="#{root_path}queues/#{@name}/delete" method="post" input name="key_val" value="#{Sidekiq.dump_json(msg)}" type="hidden" - input.btn.btn-danger.btn-mini type="submit" name="delete" value="Delete" data-confirm="Are you sure you want to delete this job?" + input.btn.btn-danger.btn-mini type="submit" name="delete" value="#{t('Delete')}" data-confirm="#{t('AreYouSure')}" == slim :_paging, :locals => { :url => "#{root_path}queues/#{@name}" } diff --git a/web/views/queues.slim b/web/views/queues.slim old mode 100755 new mode 100644 index f9f4f8ef..1b930213 --- a/web/views/queues.slim +++ b/web/views/queues.slim @@ -1,10 +1,10 @@ -h3 Queues +h3 = t('Queues') table class="queues table table-hover table-bordered table-striped table-white" thead - th Queue - th Size - th Actions + th = t('Queue') + th = t('Size') + th = t('Actions') - @queues.each do |queue, size| tr td @@ -12,4 +12,4 @@ table class="queues table table-hover table-bordered table-striped table-white" td= number_with_delimiter(size) td width="20%" form action="#{root_path}queues/#{queue}" method="post" - input.btn.btn-danger.btn-small type="submit" name="delete" value="Delete" data-confirm="Are you sure you want to delete the #{queue} queue?" \ No newline at end of file + input.btn.btn-danger.btn-small type="submit" name="delete" value="#{t('Delete')}" data-confirm="#{t('AreYouSureDeleteQueue', :queue => queue)}" diff --git a/web/views/retries.slim b/web/views/retries.slim old mode 100755 new mode 100644 index 2c295b9a..44649985 --- a/web/views/retries.slim +++ b/web/views/retries.slim @@ -1,6 +1,6 @@ header.row .span5 - h3 Retries + h3 = t('Retries') .span4 - if @retries.size > 0 == slim :_paging, :locals => { :url => "#{root_path}retries" } @@ -12,11 +12,11 @@ header.row tr th width="20px" input type="checkbox" class="check_all" - th width="25%" Next Retry - th width="11%" Retry count - th Queue - th Worker - th Args + th width="25%" = t('NextRetry') + th width="11%" = t('RetryCount') + th = t('Queue') + th = t('Worker') + th = t('Arguments') - @retries.each do |msg, score| tr td @@ -28,13 +28,13 @@ header.row a href="#{root_path}queues/#{msg['queue']}" #{msg['queue']} td= msg['class'] td= display_args(msg['args']) - input.btn.btn-primary.btn-small.pull-left type="submit" name="retry" value="Retry Now" - input.btn.btn-danger.btn-small.pull-left type="submit" name="delete" value="Delete" + input.btn.btn-primary.btn-small.pull-left type="submit" name="retry" value="#{t('RetryNow')}" + input.btn.btn-danger.btn-small.pull-left type="submit" name="delete" value="#{t('Delete')}" form action="#{root_path}retries/all/delete" method="post" - input.btn.btn-danger.btn-small.pull-right type="submit" name="delete" value="Delete All" data-confirm="Are you sure?" + input.btn.btn-danger.btn-small.pull-right type="submit" name="delete" value="#{t('DeleteAll')}" data-confirm="#{t('AreYouSure')}" form action="#{root_path}retries/all/retry" method="post" - input.btn.btn-danger.btn-small.pull-right type="submit" name="retry" value="Retry All" data-confirm="Are you sure?" + input.btn.btn-danger.btn-small.pull-right type="submit" name="retry" value="#{t('RetryAll')}" data-confirm="#{t('AreYouSure')}" - else - .alert.alert-success No retries were found + .alert.alert-success = t('NoRetriesFound') diff --git a/web/views/retry.slim b/web/views/retry.slim old mode 100755 new mode 100644 index a8842d43..e334b8b8 --- a/web/views/retry.slim +++ b/web/views/retry.slim @@ -1,55 +1,55 @@ header - h3 Job + h3 = t('Job') table class="retry table table-bordered table-striped" tbody tr - th Queue + th = t('Queue') td a href="#{root_path}queues/#{@retry['queue']}" #{@retry['queue']} tr - th Job Class + th = t('Class') td code= @retry['class'] tr - th Job Arguments + th = t('Arguments') td code= display_args(@retry['args'], 1000) tr - th Job ID + th JID td code= @retry.jid - if @retry['retry_count'] > 0 tr - th Retry Count + th = t('RetryCount') td= @retry['retry_count'] tr - th Last Retry + th = t('LastRetry') td== relative_time(@retry['retried_at'].is_a?(Numeric) ? Time.at(@retry['retried_at']) : Time.parse(@retry['retried_at'])) - else tr - th Originally Failed + th = t('OriginallyFailed') td== relative_time(@retry['failed_at'].is_a?(Numeric) ? Time.at(@retry['failed_at']) : Time.parse(@retry['failed_at'])) tr - th Next Retry + th = t('NextRetry') td== relative_time(Time.at(@retry.score)) -h3 Error +h3 = t('Error') table class="error table table-bordered table-striped" tbody tr - th Error Class + th = t('ErrorClass') td code= @retry['error_class'] tr - th Error Message + th = t('ErrorMessage') td= @retry['error_message'] - if !@retry['error_backtrace'].nil? tr - th Error Backtrace + th = t('ErrorBacktrace') td code== @retry['error_backtrace'].join("
") form.form-horizontal action="#{root_path}retries/#{job_params(@retry, @retry.score)}" method="post" - a.btn href="#{root_path}retries" ← Back - input.btn.btn-primary type="submit" name="retry" value="Retry Now" - input.btn.btn-danger type="submit" name="delete" value="Delete" + a.btn href="#{root_path}retries" = t('GoBack') + input.btn.btn-primary type="submit" name="retry" value="#{t('RetryNow')}" + input.btn.btn-danger type="submit" name="delete" value="#{t('Delete')}" diff --git a/web/views/scheduled.slim b/web/views/scheduled.slim old mode 100755 new mode 100644 index 66931acd..a390c1f4 --- a/web/views/scheduled.slim +++ b/web/views/scheduled.slim @@ -1,6 +1,6 @@ header.row .span5 - h3 Scheduled Jobs + h3 = t('ScheduledJobs') .span4 - if @scheduled.size > 0 == slim :_paging, :locals => { :url => "#{root_path}scheduled" } @@ -12,10 +12,10 @@ header.row thead th width="20px" input type="checkbox" class="check_all" - th width="25%" When - th width="10%" Queue - th Worker - th Args + th width="25%" = t('When') + th width="10%" = t('Queue') + th = t('Worker') + th = t('Arguments') - @scheduled.each do |msg, score| tr td @@ -25,6 +25,6 @@ header.row a href="#{root_path}queues/#{msg['queue']}" #{msg['queue']} td= msg['class'] td= display_args(msg['args']) - input.btn.btn-danger.pull-right type="submit" name="delete" value="Delete" + input.btn.btn-danger.pull-right type="submit" name="delete" value="#{t('Delete')}" - else - .alert.alert-success No scheduled jobs were found + .alert.alert-success = t('NoScheduledFound')