i18n-tasks
i18n-tasks copied to clipboard
Model Custom Validators
I would like to use "standard" approach in custom validators of my model ... like that:
errors.add(:base, :my_custom_error_message)
So it is automatically placed into the scope: en.activerecord.errors.models.my_model.attributes.base.my_custom_error_message
Can I somehow config the i18n-tasks to handle this translations?
Would love this feature too!! 👍
Hello,
I was trying to find something for that as well, without success. So, I ended up by writing a custom scanner for that. I am not at all a rails nor ruby expert, so use it wisely and every update on this code is more than welcome. For now, it just fit my need.
There is a big regex matcher to find anything like (because I needed it to harmonize code...):
errors.add :unit_price, :should_be_something
errors.add(:unit_price, :should_be_something)
errors.add(:unit_price, :should_be_something, range: range)
errors.add(:unit_price, I18n.t("activerecord.errors.line_item.attributes.unit_price.should_be_something)
errors.add(:unit_price, "Hard-coded error message")
# ./lib/scan_active_record_errors.rb
require 'i18n/tasks/scanners/file_scanner'
class ScanActiveRecordsErrors < I18n::Tasks::Scanners::FileScanner
include I18n::Tasks::Scanners::OccurrenceFromPosition
# @return [Array<[absolute key, Results::Occurrence]>]
def scan_file(path)
text = read_file(path)
text.scan(/^\s*errors.add(?:\():([\w]*), (?:I18n.t\(){0,1}(?::){0,1}(?:"){0,1}([^"\r\n\),]*)/).map do |type, message|
if type == "base"
attribute_or_message = "messages"
else
attribute_or_message = "attributes.#{type}"
end
occurrence = occurrence_from_position(
path, text, Regexp.last_match.offset(0).first)
model = File.basename(path, ".rb") #.split('/').last
# p "================"
# p type
# p message
# p ["activerecord.errors.models.%s.%s.%s" % [model, attribute_or_message, message], occurrence]
# p "================"
["activerecord.errors.models.%s.%s.%s" % [model, attribute_or_message, message], occurrence]
end
end
end
I18n::Tasks.add_scanner 'ScanActiveRecordsErrors'
and add it to your i18n-tasks.yml
# ./config/i18n-tasks.yml
<% require './lib/scan_active_record_errors.rb' %>
Would anyone have the skill to write a similar scanner for extracting form labels for resources? The documentation for ActionView::Helpers::FormHelper states:
The text of label will default to the attribute name unless a translation is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
So you should be able to create a translation for a title label on a post resource like this:
app/views/posts/new.html.erb
<% form_for @post do |f| %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.submit %>
<% end %>
config/locales/en.yml
en:
helpers:
label:
post:
title: 'Customized title'
or config/locales/en.yml
en:
activerecord:
attributes:
post:
title: 'Customized title'
It would be nice to be able to extract anything of the type .label :[attribute]
or .label(:[model], :[attribute])
to either one of the above solutions, whether using helpers.label.[model].[attribute]
or activerecord.attributes.[model].[attribute]
.
This may be a bit complex, because it means succeeding in identifying the [model] in all cases, and perhaps skipping cases where the label text is explicitly defined in a hard-coded string or with an I18n.t
method (seeing this will already be dealt with by i18n-tasks add-missing
).
There are many different possible scenarios:
activerecord:
attributes:
post:
cost: "Total cost"
label(:post, :cost)
# => <label for="post_cost">Total cost</label>
label(:post, :title, "A short title")
# => <label for="post_title">A short title</label>
label(:post, :title, "A short title", class: "title_label")
# => <label for="post_title" class="title_label">A short title</label>
label(:post, :privacy, "Public Post", value: "public")
# => <label for="post_privacy_public">Public Post</label>
label(:post, :cost) do |translation|
content_tag(:span, translation, class: "cost_label")
end
# => <label for="post_cost"><span class="cost_label">Total cost</span></label>
label(:post, :cost) do |builder|
content_tag(:span, builder.translation, class: "cost_label")
end
# => <label for="post_cost"><span class="cost_label">Total cost</span></label>
label(:post, :terms) do
raw('Accept <a href="/terms">Terms</a>.')
end
# => <label for="post_terms">Accept <a href="/terms">Terms</a>.</label>
Well I've actually come up with a solution which does not handle all use cases, it does however handle the basic use case for what's generated from the scaffolding generator, basically forms and labels of this type: <%= form.label :username %>
:
require 'i18n/tasks/scanners/file_scanner'
class ScanResourceFormLabels < I18n::Tasks::Scanners::FileScanner
include I18n::Tasks::Scanners::OccurrenceFromPosition
# @return [Array<[absolute key, Results::Occurrence]>]
def scan_file(path)
text = read_file(path)
text.scan(/^\s*<%= f(?:orm){0,1}.label :(.*) %>$/).map do |attribute|
occurrence = occurrence_from_position(
path, text, Regexp.last_match.offset(0).first)
model = File.dirname(path).split('/').last
# p "================"
# p model
# p attribute
# p ["activerecord.attributes.%s.%s" % [model.singularize, attribute.first], occurrence]
# p "================"
["activerecord.attributes.%s.%s" % [model.singularize, attribute.first], occurrence]
end
end
end
I18n::Tasks.add_scanner 'ScanResourceFormLabels'