I am using Ruby on Rails 3.1 and I would like to know how to correctly handle internationalization related to partial template files. That is, ...
... in my app/views/users/flag.html.erb file I have:
<%= t('.test_key1') %>
<%= render :partial => "/users/flag_form" %>
... in my app/views/users/_flag_form.html.erb file I have:
<%= t('.test_key2') %>
If in my config/locales/views/users/en.yml file (note: I am organizing files as stated in the official RoR guide) I use
en:
users:
flag:
test_key1: Test 1 text
test_key2: Test 2 text
the Test 1 text is displayed in the "main" template (app/views/users/flag.html.erb) but the Test 2 text isn't for the partial template (app/views/users/_flag_form.html.erb). How could\should I solve this issue so to properly display the Test 2 text?
config/locales/en.yml
en:
users:
flag:
test_key1: Test 1 text
flag_form:
test_key2: Test 2 text
app/views/users/flag.html.erb
<%= t('.test_key1') %>
<%= render :partial => "/users/flag_form" %>
app/views/users/_flag_form.html.erb
<%= t('.test_key2') %>
NB:
Rails path to the view must match YAML path to the symbol. You need to create an entry at YAML file that matches the name of the view. Omit the trailing underscore since it's a partial.
Read more about lazy lookups
One way would be to using scope, instead of "lazy loading" using the full stop.
Something like this should work:
I18n.t :test_key2, :scope => 'users.flag'
or use:
I18n.t "users.flag.test_key2"
Lastly, you could even pass it to the partial as in
<%= render :partial => "/users/flag_form", :locals => { :test_key => t('.test_key1') } %>
You should also checkout the appendix section on this website as it might be listing something that I am missing:
https://web.archive.org/web/20120619002316/http://www.unixgods.org/~tilo/Rails/where_is_Rails_trying_to_lookup_L10N_strings.html
I wrote this. What do you think about it?
def translate_for_partials key, *args
I18n.t("#{params[:controller].gsub('/', '.')}.#{params[:action]}.#{key}", *args)
end
Is that bad to make such a method ?
Related
I'm trying to create a situation where one user makes message templates and another one can plug in values. I'm using the best_in_place gem, which will allow a user to edit the message on the show page.
The problem is this. When I call the message, with the required erb to make the gem work, it treats all of this as a regular string, not as ruby.
This is unclear, I'm sorry.
Here's the code.
#announcement.content = "The <%= best_in_place #announcement, :train %> is arriving in five minutes."
/show.html.erb
<%= #announcement.content %>
I want it to put "The click to set train is arriving in five minutes." and if the user clicks where it says "click to set train," a text field will open for them to edit (this is something the best-in-place gem does).
Instead, it puts "The <%= best_in_place #announcement, :train %> is arriving in five minutes."
I understand why it is doing this, but I don't know how to make it instead interpret the ruby I'm trying to pass in.
Ideas?
Use regular old string interpolation:
#announcement.content = "The #{best_in_place #announcement, :train} is arriving in five minutes."
You can use ERB to render any ERB template string. In this case something like:
<%= ERB.new(#announcement.content).result %>
Although you likely won't have access to all your Rails helpers, etc.
The Rails way to do this:
#announcement.content_type = :arriving
Later:
<%= render(partial: #announcement.content_type)
In _arriving.erb:
The <%= best_in_place #announcement, :train %> is arriving in five minutes.
TL;DR: ERB is not Ruby, and Rails uses both at different times.
You want simple Ruby string interpolation here:
#announcement.content = "The #{best_in_place #announcement, :train} is arriving in five minutes."
This is unclear, I'm sorry.
Not to worry, the Rails framework throws so many different new concepts at you it can be frustrating for newcomers.
Start from this: the Ruby framework builds the answer to the user's browser from a collection of resources Each file is evaluated by an interpreter for its own language. The trick is: look at the extension.
Files ending in .coffee will be compiled into javascript, files ending in .scss will become CSS, and in the same way files ending in .erb will yield HTML.
ERB is a language composed of mostly HTML already, plus a tag that allows you to interpolate Ruby. ERB stands for Embedded Ruby.
What about files ending in .rb, like the file in which you (surely) are evaluating #announcement.content = "The <%= best_in_place[...]" (a controller, I guess)?
Well, that's just pure Ruby :) that's why the ERB interpolation syntax <%= ... > is not recognized.
What you want to do in the controller, is (as you're trying to do) preparing the data for the view. The ruby in the <%= ... > tag in ERB will have access to the controller's instance variables, i.e. the variables with an # in front defined in the controller. But to define those, inside the controller, you should rely on Ruby alone.
Take-home message:
Be aware of which language you are writing in at each moment. For example:
# show.html.erb
<p>Here is ERB, which will be interpreted straight into HTML</p>
<% "Inside the '<% ...' tag is Ruby, but results won't show up in the HTML because there's no '<%='."%>
<% which_language = "Ruby" # Even variable assignments, and comments, do work %>
<%= "Inside the '<%=' tag, you're writing and interpolating #{which_language} :)" %>
I think the fact that I wasn't clear made it hard to answer this question.
What I'm doing is transforming user-inputted text (using a method in the model, called by the controller) to replace certain keywords with erb tags that call the best_in_place plugin. In my view, when presenting this content to another user, I wanted to call this content, which is saved as an attribute in the database, in such a way that it would render correctly for the other user to have the best_in_place functionality active.
Here's what I ended up doing. It is working, but if you have better ideas, please let me know.
In the announcements#create view, the user creates an announcement with certain pre-defined blocks of bracketed text as well as free-input text. For example, they might write "[train] is leaving from [platform] in [time] minutes."
When they hit save, the controller's create action calls the construct_message method from the model. It looks like this:
def construct_message(msg)
msg.gsub! '[train]', '<%= best_in_place #announcement, :train_id, :as => :select, collection: Train::list_trains, place_holder: "Click here to set train." %>' #note: list_trains and list_platforms are methods on the model, not really important...
msg.gsub! '[platform]', '<%= best_in_place #announcement, :platform_id, :as => select, collection: Platform::list_platforms, placeholder: "Click here to set platform." %>'
msg.gsub! '[time]', '<%= best_in_place #announcement, :number_of_minutes, placeholder: "Click here to set." %>'
end
Then, when I want to show that attribute in my view, I'm using render :inline, like this.
on announcements/:id
<p id="notice"><%= notice %></p>
<p>
<strong>Content:</strong>
<% announcement = #announcement %>
<%= render :inline => announcement.content, locals: { :announcement => announcement } %>
</p>
This allows the erb call that I wrote into the attribute to be functional.
Also note that I'm choosing to use a local rather than instance variable here; this is because in announcements#index, I also render this text and the table there uses local variables.
I've been trying to render a underscore.js template, that is just like ERB on some haml content.
But as the templates grow I dont want to do more like this
%script{:type => "text/template", :id => "my_template"}
:plain
<div><%= my_js_value %></div>
.....(other content)...
and instead of that I want to use partials to render inside it I wish to do something like:
%script{:type => "text/template", :id => "my_template"}
=render :file => "mytemplate.txt"
But that rendering tried to bind the ERB on it, and I've got errors for my_js_value
The only way I get to do this rendering is through that way:
%script{:type => "text/template", :id => "my_template"}
=render :text => File.read("#{Rails.root}/app/views/mycontroller/mytemplate.txt")
The last one worked for me, but I was looking for something better than that.
What do you suggest me to do instead of reading a file? Isn't there a "raw" option for render?
BTW this is on a rails 2.3.14 app
I don't know HAML to well yet but there is a raw option for rails. You can find the details at the link below:
<%= raw #user.name %>
http://api.rubyonrails.org/classes/ActionView/Helpers/OutputSafetyHelper.html
I've made a small gist of it but basically what I'd like to do is
<%= render "this is a #{ I18n.translate(:a_string) }" %>
Saving snippets and/or entire views_templates (like views/mailers/some_model/regards.haml) in a database will allow me to building hierakies of templated views entirely from the web-interface - and saving the designers the round-trip to uploading files to the server, or in less pompous circumstances, having users edit minor screw-ups in templates themselves.
The above example does in no way portray the size of the problem - but point to the heart of it: how to render a template usually sitting in the filesystem, now being a text attribute on a Template model in the database.
You could render a partial for a generic template:
<%= render :partial => 'shared/string' %>
Then the partial template does the string rendering:
In shared/_string:
<%= "this is a #{ I18n.translate(:a_string) }" %>
Or you could have the database lookup for stored string done in the render call, and pass it in as a parameter:
<%= render :partial => 'shared/string', :locals => {:a_string => String.find(:string_id)} %>
In shared/_string:
<%= a_string %>
Or if your string contains ERB that needs to be executed try:
<%= ERB.new(a_string).result %>
Rendering a partial like this shouldn't cause the DoubleRender error.
The answer - at least to my question - is rather convoluted, and requires a great deal of digging into the innards of the Rails stack! I'm merely copy-cat'ing here: go see the complete answer in José Valím's book, Crafting Rails Applications!
With Rails 3.2.1 (possibly even before) templates (as in eg. app/views/posts/show.haml) are 'found' by something called a Resolver, and it is possible to add ones own Resolver - which I did :)
I added a
class ViewTemplate < ActiveRecord::Base
class Resolver < ActionView::Resolver
def find_templates(name, prefix, partial, details)
conditions = {
:path => normalize_path(name, prefix),
:locale => normalize_array(details[:locale]).first,
:display_format => normalize_array(details[:formats]).first,
:handler => normalize_array(details[:handlers]),
:partial => partial || false
}
ViewTemplate.where(conditions).map do |record|
initialize_template(record)
end
end
end
end
and then I told my ApplicationController to look at my own path first:
class ApplicationController < ActionController::Base
append_view_path ViewTemplate::Resolver.new
end
and finally I added a record to my ViewTemplate with
ViewTemplate.create( content: '=render "form"', path: 'posts/edit', display_format: 'html', handler: 'haml' )
and replaced the contents of my views/layouts/application.haml with:
= render 'posts/edit'
and huzzah :)
(well, more or less - there are of cause issues like variables, and scopes - but hey, nothing is perfect)
I want to reformat some helpers in my Rails views. I want a syntax similar to:
<%=
box :option => 'value' do |b|
b.header "Header of box #1"
%>
Content of Box#1
<%
end
%>
The b.header call is optional.
How would I structure my code to allow this? I guess it's something similar to fields_for in Rails.
You could always look at the source for form_for / fields_for?
so i have this code:
<% form_tag(:action => 'find') do%>
Product name:
<%= text_field("cars_", "name", :size => "30") %>
<input type ="submit" value="Find"/>
<%end%>
upon pressing the button I want it to complete the method (def find) found in the controller but its requesting the html.erb file:
Template is missing
Missing template cars/find.erb in view
path H:\Documents and
Settings/owner/My
Documents/NetBeansProjects/RailsApplication5/app/views
in the find def (found in controller)
def find
#car = Car.new(params[:car_])
end
What is the last line of your find method? Generally, if you don't specify the template to render in your controller method, Rails attempts to find a template with the same name as the method. That is why it is saying it can't find cars/find.erb. Without seeing the code in your find action, it is hard to give a better answer.
Your find method should be doing some searching, not initializing with the parameters. I recommend checking out something like thinking sphinx or searchlogic if you want to do searching.
I believe that your code is executing the find action. However, after it finds the car object, it needs to write that into a template that shows the results of your search. By convention, rails looks for a file called find.html.erb in the view folder for that controller. So, the error message you are seeing means that Rails has executed the line of code in your action and is now trying to generate some HTML to send back to the browser
If you create a simple file in the view folder for that controller with contents:
<%= #car.name %>
You should see the results.
However, your code is a bit confusing to me as I don't know why a find method would create a new Car object. I would expect something like this:
def find
#car = Car.find_by_name(params[:name])
end
I would also expect that your form would be more like:
<% form_tag(:action => 'find') do%>
Product name:
<%= text_field_tag("name", :size => "30") %>
<%= submit_tag "find" %>
<%end%>