For example:
I18n.t('something')
should output only
something
if the translation missing.
It is possible:
See section 4.1.2 Defaults at Rails Internationalization (I18n) API.
I18n.t :missing, :default => 'Not here'
# => 'Not here'
On rails 4 you can change the exception handler.
Add the following to config/initializers/i18n.rb:
module I18n
class MissingTranslationExceptionHandler < ExceptionHandler
def call(exception, locale, key, options)
if exception.is_a?(MissingTranslation)
key
else
super
end
end
end
end
I18n.exception_handler = I18n::MissingTranslationExceptionHandler.new
Now on you views you can just do:
<p><%= t "Not translated!" %></p>
Guide on the subject: http://guides.rubyonrails.org/i18n.html#using-different-exception-handlers
side-note:
this might help figuring out what Rails thinks the current scope is (e.g. when using ".something")
http://unixgods.org/~tilo/Rails/which_l10n_strings_is_rails_trying_to_lookup.html
this way you can better avoid having missing translations because of incorrect placing of translations strings in the L10n-file / incorrect keys
David's answer is the right solution to the question, another (more verbose) way to do it is to rescue and return the key:
def translate_nicely(key)
begin
I18n.translate!(key)
rescue
key
end
end
nope, not possible. If you use I18 you need to have a file that corresponds to the language otherwise I18n will complain.
Of course you can set the default language in your environment.rb file. Should be near the bottom and you can set this for whatever language you want but in your locales/ folder you will need to have a corresponding yml translation.
Related
In my Rails 4 app, I implemented a custom validator named LinkValidator on my Post model:
class LinkValidator < ActiveModel::Validator
def validate(record)
if record.format == "Link"
if extract_link(record.copy).blank?
record.errors[:copy] << 'Please make sure the copy of this post includes a link.'
end
end
end
end
Everything works fine, except that currently, the message displayed is:
1 error prohibited this post from being saved:
Copy Please make sure the copy of this post includes a link.
How can remove the word "copy" from the above message?
I tried record.errors << '...' instead of record.errors[:copy] << '...' in the validator, but then the validation no longer works.
Any idea?
Unfortunately currently the format of full_messages for errors is controlled with a single I18n key errors.format, hence any change to it will have a global consequences.
Common option is to attach error to the base rather than to the attribute, as full messages for base errors do not include attribute human name. Personally I don't like this solution for number of reason, main being that if the validation error is caused by a field A, it should be attach to field A. It just makes sense. Period.
There is no good fix for this problem though. Dirty solution is to use monkey patching. Place this code in a new file in your config/initializers folder:
module ActiveModel
class Errors
def full_message(attribute, message)
return message if attribute == :base
attr_name = attribute.to_s.tr('.', '_').humanize
attr_name = #base.class.human_attribute_name(attribute, :default => attr_name)
klass = #base.class
I18n.t(:"#{klass.i18n_scope}.error_format.#{klass.model_name.i18n_key}.#{attribute}", {
:default => [:"errors.format", "%{attribute} %{message}"],
:attribute => attr_name,
:message => message
})
end
end
end
This preserves the behaviour of full_messages (as per rails 4.0), however it allows you to override the format of full_message for particular model attribute. So you can just add this bit somewhere in your translations:
activerecord:
error_format:
post:
copy: "%{message}"
I honestly dislike the fact that there is no clean way to do this, that probably deserves a new gem.
I'm trying to translate an app into Japanese and everything was going smoothly until I put it into production.
As cache_classes is now true any translation within a model reverts to the default locale.
I know I'm probably supposed to target the translations directly in the yml file but I'm not sure how I would do that for the following simplified code:
class TimeseriesForecast < ActiveRecord::Base
##field_names = {
:location_name => I18n.t('forecast_timeseries.location_name'),
:local_date_time => I18n.t('forecast_timeseries.local_date_time'),
:zulu_date_time => I18n.t('forecast_timeseries.zulu_date_time'),
:temp_mean => I18n.t('forecast_timeseries.temp_mean')
}
end
Many thanks
Your I18n.t() call is evaluated at compile time since you are defining class variables, not instance variables. You need to put your call to I18n.t where they will be evaluated at runtime.
But if you want to translate ActiveRecord field names, use human_attribute_name and provide your translations via YML. You do not need to manually provide translations, Rails handles it all for you automatically.
The respective documentation is at http://guides.rubyonrails.org/i18n.html Chapter 5.1.
Don't use I18n.t or translate method in your models. You can do this instead:
In your model
Use something like this to add internationalized errors to the name attribute of your model (Check documentation: ActiveModel/Errors/method-i-add):
self.errors.add(:name, :your_error_key)
# The error key could be something like :wrong_name
NOTE: Sometimes you won't even need to add errors with errors.add method. For example if you add validations in your model with somethind like this:
validates :name, presence: true
Rails will add an error with the key :blank (the key depens on the validation type). In other words rails internally will issue self.errors.add(:name, :blank)
In your locale
Then in your locale.jp.yml can use any of this (just one):
activerecord.errors.models.[model_name].attributes.[attribute_name]
activerecord.errors.models.[model_name]
activerecord.errors.messages
errors.attributes.[attribute_name]
errors.messages
In your case replace [model_name] with timeseries_forecast and [attribute_name] with your_error_key
For example:
en:
errors:
messages:
your_error_key: "Your error message in english"
Don't think you're improving performance by caching the names in the class. Make it a method instead.
class TimeseriesForecast < ActiveRecord::Base
def self.field_names
{ :location_name => I18n.t('forecast_timeseries.location_name'),
:local_date_time => I18n.t('forecast_timeseries.local_date_time'),
:zulu_date_time => I18n.t('forecast_timeseries.zulu_date_time'),
:temp_mean => I18n.t('forecast_timeseries.temp_mean') }
end
end
# usage
TimeseriesForecast.field_names
Better yet, return just the actual fields and do the translation in the view, if you're gonna be strict MVC about it (some Rails methods - like collection_select - make it harder to do that though, hence the suggestion above).
I have a problem with a Rails 2.3.8 application. I'm using rails i18n to make the site in different languages. Everything works perfect, everywhere, except one place.
After successful signup, I do:
flash[:notice] = t 'path.to.locale.key'
Just as I do everywhere else.
But that renders the following:
translation missing: 'locale.path.to.locale.key' not found
It seems it's not loading the current locale (or else it will say 'en', or 'es', or whatever instead of 'locale').
Any idea that could be causing this?
Thanks
Maybe you overwrite it somewhere down that yml file. Maybe you did too many nesting. Maybe that key has subkeys.
Delete everything from that locale.yml and place only that message and see if it works.
The problem you are having happens to me every now and then, and it's always something I messed up in yml file.
Try setting a default locale in your ApplicationController, for example with a before_filter:
I18n.locale = params[:locale] || 'en'
Well, this happened to me in mailer classes after I upgraded to Rails 4.1. It was working correctly on Rails 3 and there was no change on yml files. Somehow i18n did not see the default locale.
So I've added this line on mailer class to fix out.
I18n.locale = I18n.default_locale
class ProviderMailer < ActionMailer::Base
include Resque::Mailer
default from: APP_CONFIG.mailer.from
def registration_email(provider)
#provider = provider
I18n.locale = I18n.default_locale
#provider_url = "#{APP_CONFIG.base_url}/hizmetsgl/#{provider['_id']}"
#howto_url = "#{APP_CONFIG.base_url}/hizmetverenler"
mail(to: provider["business_email"], subject: t('provider_mailer.registration_email.subject'))
end
end
My question is very similar to this one: How do I add a method to a ruby gem without editing the gem source?. However, this question is almost a year old and the solution that was chosen isn't the cleanest, not to me at least.
The person who provided the answer offered 3 suggestions. The first suggestion was chosen as the answer, but I would really like to figure out how to do it the second way.
I need to override an instance method of a class that is defined by a Gem. More specifically, it is the SessionSerializer class in 1.1.2 Devise. The issue is that Devise doesn't respect non-standard primary key names. It always uses id. You can see that in warden_compat.rb on Line 30, it uses the following to find a model by it's ID:
klass.constantize.find(:first, :conditions => { :id => id })
In my case, the name of my id column is application_user_id, so it is obvious that this won't work. Devise has fixed this issue in 1.1.3, however, I cannot use 1.1.3 because the Devise LDAP Authenticatable plugin does not support 1.1.3.
So here's what I've done instead. I should mention first that I tested this fix by editing the Gem source directly, so now I simply want to move it into my project.
Created a session_serializer.rb file in lib/warden/ (i.e., lib/warden/session_serializer.rb), reopened the Warden::SessionSerializer class, and redefined the deserialize method.
Modified application.rb to include lib/ in config.autoload_paths
config.autoload_paths += ["#{config.root}/lib"]
However, this doesn't seem to do the trick. It is still using the same code that is defined in the Gem source. So I have couple questions that I hope that can be answered:
Questions
What am I doing wrong here?
Does Rails load files of the paths defined in config.autoload_paths before Gems, or is it the other way around?
Thanks for the help in advance!
lib/warden/session_serializer.rb
module Warden
class SessionSerializer
def deserialize(keys)
klass, id = keys
if klass.is_a?(Class)
raise "Devise changed how it stores objects in session. If you are seeing this message, " <<
"you can fix it by changing one character in your cookie secret, forcing all previous " <<
"cookies to expire, or cleaning up your database sessions if you are using a db store."
end
# NOTE: Original line code. Notice that it uses an :id symbol. It doesn't respect the primary key that explicity defined in the model
# klass.constantize.find(:first, :conditions => { :id => id })
# NOTE: THIS IS THE FIX
klass.constantize.find(:first, :conditions => { :application_user_id => id })
rescue NameError => e
if e.message =~ /uninitialized constant/
Rails.logger.debug "Trying to deserialize invalid class #{klass}"
nil
else
raise
end
end
end
end
I would create a file called warden.rb in initializers directory and put the monkey patch code inside the file. I use this technique often in my projects to patch a gem.
To put the patch under the lib directory, do the following:
config.autoload_paths += ["#{config.root}/lib/warden"]
PS: I know you have tried this, but it looks like your path is not correct.
PPS To understand the Rails 2.3 load sequence refer to this code.
Have you read:
http://guides.rubyonrails.org/configuring.html
?
I'm pretty sure I saw on a Rails related site something along the lines of:
def my_function(*opts)
opts.require_keys(:first, :second, :third)
end
And if one of the keys in require_keys weren't specified, or if there were keys that weren't specified, an exception was raised. I've been looking through ActiveSupport and I guess I might be looking for something like the inverse of except.
I like to try and use as much of the framework as possible compared to writing my own code, that's the reason I'm asking when I know how to make the same functionality on my own. :)
At the moment I'm doing it through the normal merge routine and making sure that I have what I need with some IFs.
I think the method you're thinking of is assert_valid_keys (documentation here) but this only raises an exception if any unexpected keys exist in the hash, not if any of the specified keys are missing. i.e. if a hash is being used to pass options to a method it can be used to check for invalid options not for required options.
You can do this yourself relatively easily. As was stated in an earlier answer, half your work is done for you in assert_valid_keys. You can roll your own method to do the rest.
def my_function( *opts )
opts.require_and_assert_keys( :first, :second, :third )
end
create lib/hash_extensions.rb with the following:
class Hash
def require_and_assert_keys( *required_keys )
assert_valid_keys( keys )
missing_keys = required_keys.inject(missing=[]) do |missing, key|
has_key?( key ) ? missing : missing.push( key )
end
raise( ArgumentError, "Missing key(s): #{missing_keys.join( ", ")}" ) unless missing_keys.empty?
end
end
finally, in config/environment.rb, add this to make it work:
require 'hash_extensions'