Question

Running my finished Rails Tutorial app through Rails Best Practices got me a warning to not use the time_ago_in_words method as "it's too expensive to calculate the time on the server side", so they provide some example code to change your implementation to use the timeago jQuery plugin. Sounded like it would make an interesting change, so I attempted it. My original code that used time_ago_in_words is in two places:

app/views/shared/_feed_item.html.haml

# ...
%span.timestamp
  = t('.time_posted_ago', time: time_ago_in_words(feed_item.created_at) )

app/views/microposts/_micropost.html.haml

# ...
%span.timestamp
  = t('.time_posted_ago', time: time_ago_in_words(micropost.created_at))

I changed the code to use timeago as follows, using the HTML5 time tag:

app/views/shared/_feed_item.html.haml

# ...
%time.timeago{ datetime: feed_item.created_at.getutc.iso8601 }
  = t('.time_posted_ago', time: feed_item.created_at.to_s)

app/views/microposts/_micropost.html.haml

# ...
%time.timeago{ datetime: micropost.created_at.getutc.iso8601 }
  = t('.time_posted_ago', time: micropost.created_at.to_s)

Which, I thought, only left me to add the timeago() method to my coffeescript file:

app/assets/javascripts/application.js.coffee

$(document).ready ->
  $("time.timeago").timeago()

This didn't work, so I tried moving the statement to microposts.js.coffee and users.js.coffee, with and without $(document).ready -> and still no go. I then thought that perhaps the required libraries for timeago aren't included by default in jquery-rails, so I added them:

app/assets/javascripts/application.js.coffee

//= require jquery
//= require jquery_ujs
//= require jquery.min
//= require jquery.timeago
//= require bootstrap
//= require_tree .

Adding the jquery.min.js and jquery.timeago.js declarations here didn't work, and I'm also assuming my syntax is off since they busted my other page elements that rely on javascript.

So, as far as I understand, I think I have the implementation code correct in my haml files, but I just don't know:

  1. in which coffeescript file I should put the $("time.timeago").timeago() call and how I should write it if what I have is incorrect
  2. whether timeago libraries are included in rails-jquery
  3. if they're not, how to include them properly

After spending a lot of time on this, I did end up trying out the rails-timeago gem, which, as well as having some nice helpers, did work in getting the time to display properly. However, it didn't seem to play well with the rails t() method implementation above in order to just get the time in words and then interpolate it in to an i18n string.

So, how can I get the desired effect of time_ago_in_words using the timeago JQuery plugin in a Rails 3.2 app with i18n?

Was it helpful?

Solution

I managed to hack together a solution using the rails-timeago gem. It's not optimal, but it seems to get the desired timeago effect, which is quite nice. I'd like to know how to get the same effect in Rails using just the timeago jQuery plugin and the %time.timeago tags. Anyway, hopefully this is of interest to someone.

Setup

Gemfile

gem 'rails-timeago', '1.3.0'

config/initializers/timeago.rb

Rails::Timeago.default_options limit: proc { 20.days.ago }, nojs: true
Rails::Timeago.locales = [:en, :it, :ja]

Create app/assets/javascripts/locales directory to put customizations to the timeago i18n strings. For completeness sake, I took the three locales I wanted for my app from the jQuery-timeago Github repository and put them under the directory, but only actually ended up changing the ja locale file.

app/assets/javascripts/application.js.coffee

# ...
//= require jquery
//= require jquery_ujs
//= require rails-timeago

app/views/layouts/application.html.haml

# ...
%head
  # ...
  = javascript_include_tag "application"
  = timeago_script_tag

Implementation

app/views/microposts/_micropost.html.haml

# ...
%span.timestamp
  = t('.time_posted_prefix')
  %time<
    = timeago_tag(micropost.created_at)
    = t('.time_posted_suffix')

Code for app/views/shared/_feed_item.html.haml is the same: just substitute out micropost for feed_item.

i18n

config/locales/en.yml

microposts:
  micropost:
    time_posted_prefix: Posted
    time_posted_suffix: ""

config/locales/it.yml

microposts:
  micropost:
    time_posted_prefix: Pubblicato
    time_posted_suffix: ""

config/locales/ja.yml

microposts:
  micropost:
    time_posted_prefix: ""
    time_posted_suffix: に投稿された。

Strings for shared.feed_item in the i18n files are the same.

I couldn't find a way to use t() dynamically with the result of timeago_tag (eg t('.time_posted_ago', time: timeago_tag(micropost.created_at))), so I put the result between a prefix and a suffix. timeago provides time-related prefixes/suffixes, but not ones related to "posting", so they were added above.
In order to get rid of the space between the output of timeago_tag and time_posted_suffix (specifically for Japanese), I wrapped those two tags in a %time< tag with a < to remove all whitespace within the tag. It's a small thing, but it was annoying for me so I wanted to put it in, but it's by no means necessary if you aren't using languages that would need a value in the time_posted_suffix key. I'm also assuming there's a better way to do this.

Issues

I originally had jquery.timeago.en.js, jquery.timeago.it.js, and jquery.timeago.ja.js under the app/assets/javascripts/locales directory, but I had an issue where the en file didn't get loaded for the en locale; instead, the last file alphabetically in the directory, which was ja, would be used with the en locale. So, I renamed the en file to jquery.timeago.xx.js, which got English to display. I tried moving the en file outside the locales directory, but it never seemed to get picked up, so it became xx.

Update

The i18n issues I had are now solved. See this Github issue for details. In summary, I got rid of my app/assets/javascripts/locales directory and put just jquery.timeago.ja.js, the only file I really wanted to customize, under the lib/assets/javascripts/locales directory, meaning that for :it and :en locales, timeago's default translations get used, and :en translations display as expected. So, I did get what I consider an good solution to this problem with the rails-timeago gem.

Update 2

This solution currently works locally, but does not deploy correctly to Heroku due to asset precompilation issues...

Update 3

Found a config solution that seems to work locally and with Heroku:

config/initializers/timeago.rb

Rails::Timeago.default_options limit: proc { 20.days.ago }, nojs: true
Rails::Timeago.locales = [:en, :it, :ja]
Rails::Timeago.map_locale "en", "jquery.timeago.js"  # default
Rails::Timeago.map_locale "it", "locales/jquery.timeago.it.js"  # default
Rails::Timeago.map_locale "ja", "timeago/jquery.timeago.ja.js"  # customized

config/environments/production.rb

# ...
config.assets.precompile += %w( jquery.timeago.js locales/jquery.timeago.it.js timeago/jquery.timeago.ja.js )

And, in this case, the only custom file needed is app/assets/javascripts/timeago/jquery.timeago.ja.js

Further details can be found at the related Github issue.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top