1
0
Fork 0
mirror of https://github.com/mperham/sidekiq.git synced 2022-11-09 13:52:34 -05:00

Merge pull request #811 from mperham/i18n

Localize web UI
This commit is contained in:
Mike Perham 2013-04-04 21:08:18 -07:00
commit 0bac7902ac
18 changed files with 158 additions and 82 deletions

View file

@ -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 2.9.0
----------- -----------

View file

@ -1,3 +1,3 @@
module Sidekiq module Sidekiq
VERSION = "2.9.0" VERSION = "2.10.0"
end end

View file

@ -1,18 +1,28 @@
require 'sinatra/base' require 'sinatra/base'
require 'slim' require 'slim'
require 'sidekiq/paginator' require 'sidekiq/paginator'
require 'i18n'
module Sidekiq module Sidekiq
class Web < Sinatra::Base class Web < Sinatra::Base
include Sidekiq::Paginator include Sidekiq::Paginator
dir = File.expand_path(File.dirname(__FILE__) + "/../../web") 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 :public_folder, "#{dir}/assets"
set :views, "#{dir}/views" set :views, "#{dir}/views"
set :root, "#{dir}/public" set :root, "#{dir}/public"
set :slim, :pretty => true set :slim, :pretty => true
helpers do 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 def reset_worker_list
Sidekiq.redis do |conn| Sidekiq.redis do |conn|

View file

@ -19,9 +19,10 @@ Gem::Specification.new do |gem|
gem.add_dependency 'connection_pool', '~> 1.0' gem.add_dependency 'connection_pool', '~> 1.0'
gem.add_dependency 'celluloid', '~> 0.12.0' gem.add_dependency 'celluloid', '~> 0.12.0'
gem.add_dependency 'multi_json', '~> 1' 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 'minitest', '~> 4'
gem.add_development_dependency 'sinatra'
gem.add_development_dependency 'slim'
gem.add_development_dependency 'rake' gem.add_development_dependency 'rake'
gem.add_development_dependency 'actionmailer', '~> 3' gem.add_development_dependency 'actionmailer', '~> 3'
gem.add_development_dependency 'activerecord', '~> 3' gem.add_development_dependency 'activerecord', '~> 3'

58
web/locales/en.yml Normal file
View file

@ -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 <span class='title'>%{queue}</span>"
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

4
web/views/_nav.slim Executable file → Normal file
View file

@ -6,7 +6,7 @@
- tabs.each do |title, url| - tabs.each do |title, url|
- if url == '' - if url == ''
li class="#{(current_path == url) ? 'active':''}" li class="#{(current_path == url) ? 'active':''}"
a href='#{{root_path}}#{{url}}' #{title} a href='#{{root_path}}#{{url}}' = t(title)
- else - else
li class="#{(current_path =~ Regexp.new(url)) ? 'active':''}" li class="#{(current_path =~ Regexp.new(url)) ? 'active':''}"
a href='#{{root_path}}#{{url}}' #{title} a href='#{{root_path}}#{{url}}' = t(title)

0
web/views/_paging.slim Executable file → Normal file
View file

2
web/views/_status.slim Executable file → Normal file
View file

@ -1,3 +1,3 @@
span.status span.status
i class="status-sprite status-#{current_status}" i class="status-sprite status-#{current_status}"
== current_status == t(current_status)

12
web/views/_summary.slim Executable file → Normal file
View file

@ -1,19 +1,19 @@
ul.unstyled.summary ul.unstyled.summary
li.processed li.processed
span.count #{number_with_delimiter(stats.processed)} span.count #{number_with_delimiter(stats.processed)}
span.desc Processed span.desc = t('Processed')
li.failed li.failed
span.count #{number_with_delimiter(stats.failed)} span.count #{number_with_delimiter(stats.failed)}
span.desc Failed span.desc = t('Failed')
li.busy li.busy
span.count #{number_with_delimiter(workers.size)} span.count #{number_with_delimiter(workers.size)}
span.desc Busy span.desc = t('Busy')
li.scheduled li.scheduled
span.count #{number_with_delimiter(stats.scheduled_size)} span.count #{number_with_delimiter(stats.scheduled_size)}
span.desc Scheduled span.desc = t('Scheduled')
li.retries li.retries
span.count #{number_with_delimiter(stats.retry_size)} span.count #{number_with_delimiter(stats.retry_size)}
span.desc Retries span.desc = t('Retries')
li.enqueued li.enqueued
span.count #{number_with_delimiter(stats.enqueued)} span.count #{number_with_delimiter(stats.enqueued)}
span.desc Enqueued span.desc = t('Enqueued')

12
web/views/_workers.slim Executable file → Normal file
View file

@ -1,10 +1,10 @@
table class="workers table table-hover table-bordered table-striped table-white" table class="workers table table-hover table-bordered table-striped table-white"
thead thead
th Worker th = t('Worker')
th Queue th = t('Queue')
th Class th = t('Class')
th Arguments th = t('Arguments')
th Started th = t('Started')
- workers.each_with_index do |(worker, msg), index| - workers.each_with_index do |(worker, msg), index|
tr tr
td= worker td= worker
@ -14,7 +14,7 @@ table class="workers table table-hover table-bordered table-striped table-white"
td td
- if msg['payload']['args'].to_s.size > 100 - if msg['payload']['args'].to_s.size > 100
= msg['payload']['args'].inspect[0..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'] .toggle[id="worker_#{index}" style="display: none;max-width: 750px;"]= msg['payload']['args']
- else - else
= msg['payload']['args'] = msg['payload']['args']

View file

@ -1,20 +1,20 @@
script type="text/javascript" src="#{{root_path}}javascripts/dashboard.js" script type="text/javascript" src="#{{root_path}}javascripts/dashboard.js"
h3 h3
| Dashboard = t('Dashboard')
span.beacon span.beacon
.ring .ring
.dot .dot
h5 Real-time h5 = t('Realtime')
#realtime #realtime
h5 h5
span.history-heading History span.history-heading = t('History')
a href="#{{root_path}}?days=7" class="history-graph #{{"active" if params[:days] == "7"}}" 1 week 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"}}" 1 month 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"}}" 3 month 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"}}" 6 month 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" #history data-processed="#{Sidekiq.dump_json(@processed_history)}" data-failed="#{Sidekiq.dump_json(@failed_history)}" data-update-url="#{{root_path}}dashboard/stats"
br br
@ -23,24 +23,24 @@ h5 Redis
- if @redis_info.fetch("redis_version", nil) - if @redis_info.fetch("redis_version", nil)
.stat .stat
h3.redis_version= @redis_info.fetch("redis_version") h3.redis_version= @redis_info.fetch("redis_version")
p Version p = t('Version')
- if @redis_info.fetch("uptime_in_days", nil) - if @redis_info.fetch("uptime_in_days", nil)
.stat .stat
h3.uptime_in_days= @redis_info.fetch("uptime_in_days") h3.uptime_in_days= @redis_info.fetch("uptime_in_days")
p Uptime (days) p = t('Uptime')
- if @redis_info.fetch("connected_clients", nil) - if @redis_info.fetch("connected_clients", nil)
.stat .stat
h3.connected_clients= @redis_info.fetch("connected_clients") h3.connected_clients= @redis_info.fetch("connected_clients")
p Connections p = t('Connections')
- if @redis_info.fetch("used_memory_human", nil) - if @redis_info.fetch("used_memory_human", nil)
.stat .stat
h3.used_memory_human= @redis_info.fetch("used_memory_human") 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) - if @redis_info.fetch("used_memory_peak_human", nil)
.stat .stat
h3.used_memory_peak_human= @redis_info.fetch("used_memory_peak_human") h3.used_memory_peak_human= @redis_info.fetch("used_memory_peak_human")
p Peak Memory Usage p = t('PeakMemoryUsage')

4
web/views/index.slim Executable file → Normal file
View file

@ -1,10 +1,10 @@
.row.header .row.header
.span7 .span7
h3 Workers h3 = t('Workers')
== slim :_workers == slim :_workers
- if workers.size > 0 - if workers.size > 0
.row .row
.span2.pull-right .span2.pull-right
form action="#{root_path}reset" method="post" 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')

10
web/views/layout.slim Executable file → Normal file
View file

@ -23,26 +23,26 @@ html
li li
p.navbar-text Redis: #{location} p.navbar-text Redis: #{location}
li 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 - if namespace
li li
p.navbar-text Namespace: #{namespace} p.navbar-text #{t('Namespace')}: #{namespace}
#page #page
.container .container
.row .row
.span2.summary_bar .span2.summary_bar
h4 h4
span.title Status span.title = t('Status')
== slim :_status == slim :_status
== slim :_summary == slim :_summary
- unless current_path == '' - unless current_path == ''
.row .row
.span2 .span2
- if params[:poll] - 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 - 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 .span10
== yield == yield

11
web/views/queue.slim Executable file → Normal file
View file

@ -1,15 +1,14 @@
header.row header.row
.span5 .span5
h3 h3
| Current messages in == t('CurrentMessagesInQueue', :queue => @name)
span.title #{@name}
.span4 .span4
== slim :_paging, :locals => { :url => "#{root_path}queues/#{@name}" } == slim :_paging, :locals => { :url => "#{root_path}queues/#{@name}" }
table class="queue table table-hover table-bordered table-striped" table class="queue table table-hover table-bordered table-striped"
thead thead
th Class th = t('Class')
th Arguments th = t('Arguments')
th th
- @messages.each_with_index do |msg, index| - @messages.each_with_index do |msg, index|
tr tr
@ -17,12 +16,12 @@ table class="queue table table-hover table-bordered table-striped"
td td
- if msg['args'] and msg['args'].to_s.size > 100 - if msg['args'] and msg['args'].to_s.size > 100
= msg['args'].inspect[0..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'] .toggle[id="worker_#{index}" style="display: none;"]= msg['args']
- else - else
= msg['args'] = msg['args']
td td
form action="#{root_path}queues/#{@name}/delete" method="post" form action="#{root_path}queues/#{@name}/delete" method="post"
input name="key_val" value="#{Sidekiq.dump_json(msg)}" type="hidden" 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}" } == slim :_paging, :locals => { :url => "#{root_path}queues/#{@name}" }

10
web/views/queues.slim Executable file → Normal file
View file

@ -1,10 +1,10 @@
h3 Queues h3 = t('Queues')
table class="queues table table-hover table-bordered table-striped table-white" table class="queues table table-hover table-bordered table-striped table-white"
thead thead
th Queue th = t('Queue')
th Size th = t('Size')
th Actions th = t('Actions')
- @queues.each do |queue, size| - @queues.each do |queue, size|
tr tr
td td
@ -12,4 +12,4 @@ table class="queues table table-hover table-bordered table-striped table-white"
td= number_with_delimiter(size) td= number_with_delimiter(size)
td width="20%" td width="20%"
form action="#{root_path}queues/#{queue}" method="post" 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?" input.btn.btn-danger.btn-small type="submit" name="delete" value="#{t('Delete')}" data-confirm="#{t('AreYouSureDeleteQueue', :queue => queue)}"

22
web/views/retries.slim Executable file → Normal file
View file

@ -1,6 +1,6 @@
header.row header.row
.span5 .span5
h3 Retries h3 = t('Retries')
.span4 .span4
- if @retries.size > 0 - if @retries.size > 0
== slim :_paging, :locals => { :url => "#{root_path}retries" } == slim :_paging, :locals => { :url => "#{root_path}retries" }
@ -12,11 +12,11 @@ header.row
tr tr
th width="20px" th width="20px"
input type="checkbox" class="check_all" input type="checkbox" class="check_all"
th width="25%" Next Retry th width="25%" = t('NextRetry')
th width="11%" Retry count th width="11%" = t('RetryCount')
th Queue th = t('Queue')
th Worker th = t('Worker')
th Args th = t('Arguments')
- @retries.each do |msg, score| - @retries.each do |msg, score|
tr tr
td td
@ -28,13 +28,13 @@ header.row
a href="#{root_path}queues/#{msg['queue']}" #{msg['queue']} a href="#{root_path}queues/#{msg['queue']}" #{msg['queue']}
td= msg['class'] td= msg['class']
td= display_args(msg['args']) td= display_args(msg['args'])
input.btn.btn-primary.btn-small.pull-left type="submit" name="retry" value="Retry Now" 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="Delete" input.btn.btn-danger.btn-small.pull-left type="submit" name="delete" value="#{t('Delete')}"
form action="#{root_path}retries/all/delete" method="post" 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" 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 - else
.alert.alert-success No retries were found .alert.alert-success = t('NoRetriesFound')

32
web/views/retry.slim Executable file → Normal file
View file

@ -1,55 +1,55 @@
header header
h3 Job h3 = t('Job')
table class="retry table table-bordered table-striped" table class="retry table table-bordered table-striped"
tbody tbody
tr tr
th Queue th = t('Queue')
td td
a href="#{root_path}queues/#{@retry['queue']}" #{@retry['queue']} a href="#{root_path}queues/#{@retry['queue']}" #{@retry['queue']}
tr tr
th Job Class th = t('Class')
td td
code= @retry['class'] code= @retry['class']
tr tr
th Job Arguments th = t('Arguments')
td td
code= display_args(@retry['args'], 1000) code= display_args(@retry['args'], 1000)
tr tr
th Job ID th JID
td td
code= @retry.jid code= @retry.jid
- if @retry['retry_count'] > 0 - if @retry['retry_count'] > 0
tr tr
th Retry Count th = t('RetryCount')
td= @retry['retry_count'] td= @retry['retry_count']
tr 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'])) td== relative_time(@retry['retried_at'].is_a?(Numeric) ? Time.at(@retry['retried_at']) : Time.parse(@retry['retried_at']))
- else - else
tr 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'])) td== relative_time(@retry['failed_at'].is_a?(Numeric) ? Time.at(@retry['failed_at']) : Time.parse(@retry['failed_at']))
tr tr
th Next Retry th = t('NextRetry')
td== relative_time(Time.at(@retry.score)) td== relative_time(Time.at(@retry.score))
h3 Error h3 = t('Error')
table class="error table table-bordered table-striped" table class="error table table-bordered table-striped"
tbody tbody
tr tr
th Error Class th = t('ErrorClass')
td td
code= @retry['error_class'] code= @retry['error_class']
tr tr
th Error Message th = t('ErrorMessage')
td= @retry['error_message'] td= @retry['error_message']
- if !@retry['error_backtrace'].nil? - if !@retry['error_backtrace'].nil?
tr tr
th Error Backtrace th = t('ErrorBacktrace')
td td
code== @retry['error_backtrace'].join("<br/>") code== @retry['error_backtrace'].join("<br/>")
form.form-horizontal action="#{root_path}retries/#{job_params(@retry, @retry.score)}" method="post" form.form-horizontal action="#{root_path}retries/#{job_params(@retry, @retry.score)}" method="post"
a.btn href="#{root_path}retries" ← Back a.btn href="#{root_path}retries" = t('GoBack')
input.btn.btn-primary type="submit" name="retry" value="Retry Now" input.btn.btn-primary type="submit" name="retry" value="#{t('RetryNow')}"
input.btn.btn-danger type="submit" name="delete" value="Delete" input.btn.btn-danger type="submit" name="delete" value="#{t('Delete')}"

14
web/views/scheduled.slim Executable file → Normal file
View file

@ -1,6 +1,6 @@
header.row header.row
.span5 .span5
h3 Scheduled Jobs h3 = t('ScheduledJobs')
.span4 .span4
- if @scheduled.size > 0 - if @scheduled.size > 0
== slim :_paging, :locals => { :url => "#{root_path}scheduled" } == slim :_paging, :locals => { :url => "#{root_path}scheduled" }
@ -12,10 +12,10 @@ header.row
thead thead
th width="20px" th width="20px"
input type="checkbox" class="check_all" input type="checkbox" class="check_all"
th width="25%" When th width="25%" = t('When')
th width="10%" Queue th width="10%" = t('Queue')
th Worker th = t('Worker')
th Args th = t('Arguments')
- @scheduled.each do |msg, score| - @scheduled.each do |msg, score|
tr tr
td td
@ -25,6 +25,6 @@ header.row
a href="#{root_path}queues/#{msg['queue']}" #{msg['queue']} a href="#{root_path}queues/#{msg['queue']}" #{msg['queue']}
td= msg['class'] td= msg['class']
td= display_args(msg['args']) 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 - else
.alert.alert-success No scheduled jobs were found .alert.alert-success = t('NoScheduledFound')