Executing a calculation on an array of symbols - ruby-on-rails

Working with and array of symbols
> I18n.available_locales
[:en, :it, :fr, :de]
> I18n.locale
:en
running an array calculation
> I18n.available_locales - I18n.locale
returns the error:
TypeError (no implicit conversion of Symbol into Array)
one cannot operate on the component to calculate with to transform it into an array
> I18n.locale.to_a
NoMethodError (undefined method `to_a' for :en:Symbol)
So what is the way to execute the calculation if the end-intent is to
<% I18n.inactive_locales.each do |locale| %>
<li><%= link_to locale.to_s, { locale: locale } %></li>
<% end %>

Make the second operand an array too, like this (for example):
I18n.available_locales - [I18n.locale]
So what is the way to execute the calculation if the end-intent is to
I'd probably do it inline (this way you can avoid patching I18n)
<% I18n.available_locales.each do |locale| %>
<% next if locale == I18n.locale %>
<li><%= link_to locale.to_s, { locale: locale } %></li>
<% end %>

It's because operator - needs an array
try
2.2.10 :003 > [:a,:b,:c] - [:a]
=> [:b, :c]
In your case would be something like
I18n.available_locales - [I18n.locale]

In such cases, you can use Array() method
Array(I18n.locale)
#=> [:en]
Array([I18n.locale])
#=> [:en]
Array(nil)
#=> []
I18n.available_locales - Array(I18n.locale)
#=> [:it, :fr, :de]
Reference: https://ruby-doc.org/core-2.6/Array.html

Related

Output integer as phone number format in view

I am trying to show show this as a formatted phone number like: xxx-xxx-xxxx instead of just a number string like it currently is ..any help would be great!
<% if #post.phone.present? %>
<h4>Phone: <small> <%= #post.phone %><br></h4>
<% end %>
You can use number_to_phone helper, like this:
<% if #post.phone.present? %>
<h4>Phone: <small> <%= number_to_phone #post.phone %><br></h4>
<% end %>
By default, it formats the phone number as xxx-xxx-xxxx :
2.1.1 :009 > number_to_phone(1235551234)
=> "123-555-1234"
2.1.1 :010 > number_to_phone("1235551234")
=> "123-555-1234"
Use the phone gem. https://github.com/carr/phone
pn = Phoner::Phone.parse('+385915125486')
pn.to_s # => "+385915125486"
pn.format("%A/%f-%l") # => "091/512-5486"
pn.format("+ %c (%a) %n") # => "+ 385 (91) 5125486"
pn.format(:europe) # => "+385 (0) 91 512 5486"
pn.format(:us) # => "(234) 123-4567"
pn.format(:default_with_extension) # => "+3851234567x143"

rails check_box value using translation I18n

just wondering how I should do internationalization in rails when using a checkbox in a normal (very simple) form;
This is what I tried:
view/form file:
<%= f.check_box :do_you_agree, {}, t('submissions.yes'), t('submissions.no') %>
<%= f.check_box :do_you_agree, {}, I18n.t('submissions.yes'), I18n.t('submissions.no') %>
<%= f.check_box :do_you_agree, {}, "#{t('submissions.yes')}", "#{t('submissions.no')}" %>
...and all them return:
translation_missing in HTML.
When I use :
<%= f.check_box :do_you_agree, {}, 'NO', 'YES' %>
...everything is ok!
The YML file is ok. Many thanks.
In your config/application.rb you must have the line config.i18n.default_locale = :es (:es for spanish, as an example)
And the yml must be like this:
es:
submissions:
'yes': Si
'no': No
Edit: Make the yes/no keys explicit strings. And it will works.
Why? Because Rails does yaml conversion with psych gem. And psych return true and false with all those values (see the links).
Then, if you define a locales this way:
es:
submissions:
yes: Si
no: No
at the rails console you get:
irb> I18n.t('submissions')
=> {true=>"Si",false=>"No"}
irb> I18n.t('submissions')[true]
=> "Si"
irb> I18n.t('submissions.true')
=> "translation missing: es.submissions.true"

dealing with empty elements in ruby hash

I am returning some data from a web service to a rails page. However sometimes an element is present, sometimes it's not. This seems like it would deal with the case when the videos element is not present, but it throws an error:
<% if #data['videos'].present? && !#data['videos'].empty? %>
<div class="tab-pane map" id="video"></div>
<% end %>
Here's the error:
undefined method `empty?' for nil:NilClass
How do I properly ignore the element if it is not present?
You should use .present? instead of .empty?
1.9.3p489 :003 > ''.present?
=> false
1.9.3p489 :004 > ''.empty?
=> true
1.9.3p489 :005 > nil.present?
=> false
1.9.3p489 :006 > nil.empty?
NoMethodError: undefined method `empty?' for nil:NilClass
In your case:
<% if #data['videos'].present? %>
<div class="tab-pane map" id="video"></div>
<% end %>
Additionnal note:
.blank? is the exact opposite of .present?:
class Object
def blank?
respond_to?(:empty?) ? empty? : !self
end
def present?
!blank?
end
Another note:
The method .presence is quite usefull (it returns the object if it responds true to .present?):
1.9.3p489 :008 > params = { name: '' }
=> {:name=>""}
1.9.3p489 :009 > user_name = params[:username].presence || 'DefaultName'
=> "DefaultName"
Remove following part from the condition used in code
&& !#data['videos'].empty?

How do I recursively flatten a YAML file into a JSON object where keys are dot separated strings?

For example if I have YAML file with
en:
questions:
new: 'New Question'
other:
recent: 'Recent'
old: 'Old'
This would end up as a json object like
{
'questions.new': 'New Question',
'questions.other.recent': 'Recent',
'questions.other.old': 'Old'
}
Since the question is about using YAML files for i18n on a Rails app, it's worth noting that the i18n gem provides a helper module I18n::Backend::Flatten that flattens translations exactly like this:
test.rb:
require 'yaml'
require 'json'
require 'i18n'
yaml = YAML.load <<YML
en:
questions:
new: 'New Question'
other:
recent: 'Recent'
old: 'Old'
YML
include I18n::Backend::Flatten
puts JSON.pretty_generate flatten_translations(nil, yaml, nil, false)
Output:
$ ruby test.rb
{
"en.questions.new": "New Question",
"en.questions.other.recent": "Recent",
"en.questions.other.old": "Old"
}
require 'yaml'
yml = %Q{
en:
questions:
new: 'New Question'
other:
recent: 'Recent'
old: 'Old'
}
yml = YAML.load(yml)
translations = {}
def process_hash(translations, current_key, hash)
hash.each do |new_key, value|
combined_key = [current_key, new_key].delete_if { |k| k.blank? }.join('.')
if value.is_a?(Hash)
process_hash(translations, combined_key, value)
else
translations[combined_key] = value
end
end
end
process_hash(translations, '', yml['en'])
p translations
#Ryan's recursive answer is the way to go, I just made it a little more Rubyish:
yml = YAML.load(yml)['en']
def flatten_hash(my_hash, parent=[])
my_hash.flat_map do |key, value|
case value
when Hash then flatten_hash( value, parent+[key] )
else [(parent+[key]).join('.'), value]
end
end
end
p flatten_hash(yml) #=> ["questions.new", "New Question", "questions.other.recent", "Recent", "questions.other.old", "Old"]
p Hash[*flatten_hash(yml)] #=> {"questions.new"=>"New Question", "questions.other.recent"=>"Recent", "questions.other.old"=>"Old"}
Then to get it into json format you just need to require 'json' and call the to_json method on the hash.

I18n: How to check if a translation key/value pairs is missing?

I am using Ruby on Rails 3.1.0 and the I18n gem. I (am implementing a plugin and) I would like to check at runtime if the I18n is missing a translation key/value pairs and, if so, to use a custom string. That is, I have:
validates :link_url,
:format => {
:with => REGEX,
:message => I18n.t(
'custom_invalid_format',
:scope => 'activerecord.errors.messages'
)
}
If in the .yml file there is not the following code
activerecord:
errors:
messages:
custom_invalid_format: This is the test error message 1
I would like to use the This is the test error message 2. Is it possible? If so, how can I make that?
BTW: For performance reasons, is it advisable to check at runtime if the translation key/value pairs is present?
You could pass a :default parameter to I18n.t:
I18n.t :missing, :default => 'Not here'
# => 'Not here'
You can read more about it here.
I just had the same question and I want to compute an automatic string in case the translation is missing. If I use the :default option I have to compute the automatic string every time even when the translation is not missing. So I searched for another solution.
You can add the option :raise => true or use I18n.translate! instead of I18n.translate. If no translation can be found an exception is raised.
begin
I18n.translate!('this.key.should.be.translated', :raise => true)
rescue I18n::MissingTranslationData
do_some_resource_eating_text_generation_here
end
I don't know how to this at runtime but you can use rake to find it out. You'll have create your own rake task for that. Here's one:
namespace :i18n do
desc "Find and list translation keys that do not exist in all locales"
task :missing_keys => :environment do
def collect_keys(scope, translations)
full_keys = []
translations.to_a.each do |key, translations|
new_scope = scope.dup << key
if translations.is_a?(Hash)
full_keys += collect_keys(new_scope, translations)
else
full_keys << new_scope.join('.')
end
end
return full_keys
end
# Make sure we've loaded the translations
I18n.backend.send(:init_translations)
puts "#{I18n.available_locales.size} #{I18n.available_locales.size == 1 ? 'locale' : 'locales'} available: #{I18n.available_locales.to_sentence}"
# Get all keys from all locales
all_keys = I18n.backend.send(:translations).collect do |check_locale, translations|
collect_keys([], translations).sort
end.flatten.uniq
puts "#{all_keys.size} #{all_keys.size == 1 ? 'unique key' : 'unique keys'} found."
missing_keys = {}
all_keys.each do |key|
I18n.available_locales.each do |locale|
I18n.locale = locale
begin
result = I18n.translate(key, :raise => true)
rescue I18n::MissingInterpolationArgument
# noop
rescue I18n::MissingTranslationData
if missing_keys[key]
missing_keys[key] << locale
else
missing_keys[key] = [locale]
end
end
end
end
puts "#{missing_keys.size} #{missing_keys.size == 1 ? 'key is missing' : 'keys are missing'} from one or more locales:"
missing_keys.keys.sort.each do |key|
puts "'#{key}': Missing from #{missing_keys[key].join(', ')}"
end
end
end
put the given in a .rake file in your lib/tasks directory and execute:
rake i18n:missing_keys
Information source is here and code on github here.
If you wish to pass variable to the message like This is the test error message {variable}
This is possible using variable in language file like below.
# app/views/home/index.html.erb
<%=t 'greet_username', :user => "Bill", :message => "Goodbye" %>
# config/locales/en.yml
en:
greet_username: "%{message}, %{user}!"
More description you can find here.

Resources