Overwrite the default Parsley translation for validations in rails - ruby-on-rails

I already have all my error messages in all the languages I need them in rails config locales, so how do I change the default message for parsley required (and other validators) from
This field is required
And then how to add messages for a different language?

easy :)
Parsley.addMessage('en', 'required', "something else");
Parsley.addMessage('en', 'maxlength', 'something else max is %s')
Parsley.addCatalog('zh-HANS', {required: "其他的东西"}, true);
this adds the new language 'zh-HANS' to the catalog, with one translation and the final paramater true says to change Parsley to that locale, so you would say false if you were just populating a bunch of different locals. The dedicated method to change to the locale is Parsley.setLocale('zh-HANS')
Rails part
(in case you are using rails and have a similar situation, read on.)
regarding rails and getting the translations into the javascript, I like to use data attributes, so if the I18n.locale is zh-HANS I would add a div with some id like
%div#parsley-translations{data:{'current-locale'=> I18n.locale, required: I18n.t('forms.errors.required'), 'maxlength'=>I18n.t('forms.errors.maxlength')}}
then in the javascript
var translationData = $('#parsley-translations').data();
// now use AddLocale with all the keys and values from translationData for the translationData.currentLocale - Will adjust this once I have tested and got it working

Related

Intercept and handle missing translations in helper

I have a helper function that generates useful HTML markup. My functions are usually called with some parameters, including some text translated with I18n my_helper( t(:translation_symbol) ).
I use I18n t translation helper for that, which (if left untouched) outputs some <span class="translation missing">...</span> for texts without translation
I was wondering how to best "intercept" this translation missing so
I can render them appropriately in my helpers
Normal behavior outside my helpers
In my views I should be able to call both
<%= my_helper(t(:my_text)) %> # My helper needs to handle missing translations
<%= t(:my_text) %> # I want default I18n markup (or a custom one) '<span class="translation missing">fr.my_text</span>
My helper
def my_helper(text=nil)
# Suppose my_text doesn't have a translation, text will have value
# => '<span class="translation-missing">fr.my_text</span>'
if translation_missing?(text)
# Code to handle missing translation
doSomething(remove_translation_missing_markup(text))
notify_translation_missing
else
doSomething(text)
end
end
def translation_missing?(text)
# Uses some regex pattern to detect '<span class="translation-missing">'
end
def remove_translation_missing_markup(text)
# Uses some regex pattern to extract my_text from '<span class="translation-missing">fr.my_text</span>'
end
Is there a better way around this ? I feel bad about using a dirty regex solution.
EDIT : extra requirements
No additional markup on views : I don't want to look at my files individually to add raise: true or default: xxx for every translation. If there is a need to change the behavior everywhere I can override the t method.
In my helpers, I need a convenient way to manipulate, just the translated text if the translation was found, but for missing translations, I need to easily be able to extract the full path of the translation (fr.namespace.translation_symbol), and the original translation symbol (translation_symbol), so I can add myself translation_missing in my custom markup.
EDIT2 : I am thinking of something like that
Override t helper to rescue I18n::MissingTranslationData => e
If the exception is raised, create and return a custom object that
If rendered in a html.erb, will output the usual <span class="translation..."
Has useful fields (translation path, translation_string) that I can reuse in my helpers
I think you can leverage the fact that I18n can be configured to raise an exception when translation missing. You can either set ActionView::Base.raise_on_missing_translations = true to globally raise the exception upon missing translations or you can pass :raise => true option to the t translation helper.
Update: since you are already using t helpers in your views and you don't want to change them, I think the only option for you is overriding the t helper in your ApplicationHelper:
# ApplicationHelper
def t(key, options = {})
# try to translate as usual
I18n.t(key, options.merge(raise: true)
rescue I18n::MissingTranslationData => e
# do whatever you want on translation missing, e.g. send notification to someone
# ...
"Oh-oh!"
end
This helper either returns the translated text or "oh-oh" if translation missing. And it should work as-is in your current views.
Since missing translations seems to be a pain point in your app, rather than have your app code be constantly on the lookout for whether a translation exists or not, if possible I would advocate ensuring that you always have translations for every potential call to t, regardless of locale (this is all on the assumption that the :translation_symbol keys in your calls to my_helper(t(:translation_symbol)) are all static, kept in your config/locales directory, and aren't generated dynamically).
You can do this using the I18n-tasks gem to:
Ensure your app does not have missing or unused keys, so you should be able to delete the missing translation handling parts of your helper methods
Make your test suite/build process fail if there are any missing or unused translations, so no one else that touches the codebase accidentally sneaks any through. See instructions on copying over their RSpec test.
Why not set the default option to the translate method as follows:
<%= t(:my_text, default: "not here") %>
See http://guides.rubyonrails.org/i18n.html#defaults for details about setting defaults for the I18n.translate method.

Add translation to I18N dynamically

I have added humanized-money-accessors as described here: Decimals and commas when entering a number into a Ruby on Rails form
Now I have two attributes in my model for the same type of data: the original version and the human-readable version. The problem: Since I am using activerecord-translation-yml-files, I have to put in the same translation for original attribute and the humanized_attribute, because my forms show the name of thie humanized_attribute, but on validation errors, the name of the original attribute is shown.
Is it possible to add translations dynamically? This way I could add the translation for the humanized-version of the field when the humanized_accessor-class-method is called, getting the original translation string from the yml file, instead of writing both of them (with the same value) into the yml-file, just to have more DRY.
This is dependent on the I18n gem's internal API not changing but it is possible using I18n.backend.store_translations.
This contrived example demonstrates:
I18n.with_locale(:fake_locale) { I18n.t('some_word') }
=> "translation missing: fake_locale.some_word"
I18n.backend.store_translations(:fake_locale, some_word: 'fake translation')
I18n.with_locale(:fake_locale) { I18n.t('some_word') }
=> "fake translation"
Important: This is only done in memory. Some persistence or re-generation mechanism is necessary to prevent these from disappearing when you redeploy/restart the server.
You might want to check out globalize3 gem. You have railscast tutorial http://railscasts.com/episodes/338-globalize3?view=asciicast.

Keeping track of changes using rails - "changed?"

I am building a multi lingual website, using ruby on rails, where part of the content is supposed to be user generated and they are supposed to be able to create different versions of it for all languages. The language support is handled by i18n gem.
Part of their content is created using Markdown through http://daringfireball.net/projects/markdown/basics .
In my database I save: object.content_markdown_en, object.content_html_en, object.content_markdown_sv, object.content_html_sv and so on for the different locales.
Now if a user changes the content, new html is supposed to be generated. But it seems unnecessary to regenerate the html for all locales if he only made changes in one of the languages.
I thought there might be some way to use something like
if object.content_markdown_[locale]_changed?
generate_new_html
end
that can be run in a loop for all possible locales. But I can't find any nice ways of doing this.
How about:
[:en, :sv].each do |locale|
if object.send("content_markdown_#{ locale }_changed?".to_sym)
send("generate_new_#{ locale }_html".to_sym)
end
end
You can use send to call methods by name:
object.send("content_markdown_#{locale}_changed?".to_sym)
Your loop would look like this:
%w(en sv).each { |locale|
if object.send("content_markdown_#{locale}_changed?".to_sym)
generate_new_html
end
}
However, using a separate translation table might be a better approach.

Hook into Rails' I18n

I've written a little library to hold translated content in model attributes. All you have to do is add the following to a model:
class Page < ActiveRecord::Base
i18n_attributes :title, :content
end
By convention, the data is written to the real attributes i18n_title and i18n_content as a hash (or hstore hash for Postgres). And a number of getters and setters give you access to "localized virtual attributes":
page = Page.new
page.title_en = 'Hello'
page.title_es = 'Hola'
page.i18n_title # => { en: "Hello", es: "Hola" }
I18n.locale = :es
page.title # => "Hola"
page.title_en # => "Hello"
You can use these virtual attributes in forms as well, but there's a downside: The form builder uses I18n to get the label and translate attribute validation errors. And it does of course look for keys such as activerecord.attributes.page.title_en if you use title_enin the form.
It would be very cumbersome to replicate the same translation for every available_locale in the locales/en.yml etc files:
activerecord:
attributes:
page:
title_en: "Title"
title_es: "Title"
...
What I'd like to do is execute some code after Rails has loaded all locales in the boot process and then clone translations for these keys. Is there a way to do this? Maybe a hook which gets called after the translations have been loaded from the YAML files? (The translations are not yet loaded when my lib loads.)
Or do you see another way to tackle this problem? (I've tried to alias I18n.translate, but I'm afraid this might cause major headache in the future.)
Thanks for your hints!
Although you dropped this approach, please let me share my thoughts:
I don't think it is incredible useful to add other locale strings into a translation file for a specific localization. Since a config/locales/$locale.yml usually starts (at least in my case) with
$locale:
...
there is no need for activerecord.attributes.page.title_es in an English localization file. I'd just put it in es.yml as activerecord.attributes.page.title.
I mean: isn't that the whole purpose of separate localization files? (Or from the developer/translator point of view: In which file should I search for .title_es, in en.yml, es.yml or both?)

translate database fields with rails

I know about the built-in I18n in Rails, but how can I select a database field per locale?
My model structure is something like this:
title #default (englisch)
title_de #(german)
title_it #(italian)
In my template I want to be able to write only
<%= #model.title %>
and should get the value in the right language.
Is there a plugin or a solution to use different fields per different locale settings with a structure like mine?
Although your db architecture (different locales hardcoded as table columns) seems wrong to me, I think you can achieve what you want by adding a pseudo-field to your model, something along:
# example not tested
class MyModel < ActiveRecord::Base
def localized_title(locale)
locale = locale == 'en' ? '' : '_' + locale
read_attribute("title#{locale}".to_sym")
end
end
Or, provided that you somehow make your current locale visible to your models, you can similarly overwrite the default title accessor method.
Edit: You can take a look at http://github.com/iain/translatable_columns, it seems pretty much compatible with your architecture....
Try using:
http://github.com/joshmh/globalize2
It may require renaming your columns (to a different standard).
Nowadays the best way to translate active record models fields is using https://github.com/shioyama/mobility

Resources