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:
- in which coffeescript file I should put the
$("time.timeago").timeago()call and how I should write it if what I have is incorrect - whether
timeagolibraries are included in rails-jquery - 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?
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.timeagotags. Anyway, hopefully this is of interest to someone.Setup
Gemfile
config/initializers/timeago.rb
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
app/views/layouts/application.html.haml
Implementation
app/views/microposts/_micropost.html.haml
Code for app/views/shared/_feed_item.html.haml is the same: just substitute out
micropostforfeed_item.i18n
config/locales/en.yml
config/locales/it.yml
config/locales/ja.yml
Strings for
shared.feed_itemin the i18n files are the same.I couldn’t find a way to use
t()dynamically with the result oftimeago_tag(egt('.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_tagandtime_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 thetime_posted_suffixkey. 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
:itand:enlocales,timeago‘s default translations get used, and:entranslations display as expected. So, I did get what I consider an good solution to this problem with therails-timeagogem.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
config/environments/production.rb
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.