I'd like to be able to generate a complete list of all the I18n keys and values for a locale including the full keys. In other words if I have these files:
config/locales/en.yml
en:
greeting:
polite: "Good evening"
informal: "What's up?"
config/locales/second.en.yml
en:
farewell:
polite: "Goodbye"
informal: "Later"
I want the following output:
greeting.polite: "Good evening"
greeting.informal: "What's up?"
farewell.polite: "Goodbye"
farewell.informal: "Later"
How do I do this?
Once loaded into memory it's just a big Hash, which you can format any way you want. to access it you can do this:
I18n.backend.send(:translations)[:en]
To get a list of available translations (created by you or maybe by plugins and gems)
I18n.available_locales
Nick Gorbikoff's answer was a start but did not emit the output I wanted as described in the question. I ended up writing my own script get_translations to do it, below.
#!/usr/bin/env ruby
require 'pp'
require './config/environment.rb'
def print_translations(prefix, x)
if x.is_a? Hash
if (not prefix.empty?)
prefix += "."
end
x.each {|key, value|
print_translations(prefix + key.to_s, value)
}
else
print prefix + ": "
PP.singleline_pp x
puts ""
end
end
I18n.translate(:foo)
translations_hash = I18n.backend.send(:translations)
print_translations("", translations_hash)
Here's a working version of a method you can use to achieve your desired output
def print_tr(data,prefix="")
if data.kind_of?(Hash)
data.each do |key,value|
print_tr(value, prefix.empty? ? key : "#{prefix}.#{key}")
end
else
puts "#{prefix}: #{data}"
end
end
Usage:
$ data = YAML.load_file('config/locales/second.en.yml')
$ print_tr(data)
=>
en.farewell.polite: "Goodbye"
en.farewell.informal: "Later"
Related
Let's say you have a YML file like this:
en:
thanksgiving:
turkey: 'Turkey'
stuffing: 'Stuffing'
christmas:
ham: 'Bring ham'
thanksgiving:
beer: 'lots of beer'
and you want to use ruby to basically read, consolidate and rewrite that YML like this:
en:
thanksgiving:
turkey: 'Turkey'
stuffing: 'Stuffing'
beer: 'lots of beer'
christmas:
ham: 'Bring ham'
what is the most best / most efficient way of making this happen?
You need to parse the file to get the node representation:
yml = YAML.parse( open('c:\temp\foo.yml'))
The yml variable contains the whole structure. Example: Typing the following prints the actual entire file content
pp yml
After inspecting the result I was able to write a safe serializer. Add a file called config/initializers/yaml.rb
module YAML
def YAML.safe_load(file_name)
YAML::safe_load_node(YAML::parse(IO.read(file_name)))
end
def YAML.safe_load_node(input)
case input.kind
when :map
{}.tap do |h|
input.value.each do |key, node|
k,v = key.value, YAML::safe_load_node(node)
if (v.is_a?(Hash) and h[k].is_a?(Hash))
h[k] = h[k].merge(v)
elsif (v.is_a?(Array) and h[k].is_a?(Array))
h[k] = h[k] + v
else
h[k] = v
end
end
end
when :seq
input.value.map{|node| YAML::safe_load_node(node)}
when :scalar
input.value
end
end
end
Now in the rails console:
>> y YAML::safe_load('c:/temp/test.yml')
---
en:
christmas:
ham: Bring ham
thanksgiving:
turkey: Turkey
stuffing: Stuffing
beer: lots of beer
It's been a very long time since I've used ruby for things like this but, I forget how to open a file, look for a string, and print what ruby finds. Here is what I have:
#!/usr/bin/env ruby
f = File.new("file.txt")
text = f.read
if text =~ /string/ then
puts test
end
I want to determine what the "document root" (routes) is in config/routes.rb
If I print the string, it prints the file.
I feel dumb that I don't remember what this is, but I need to know.
Hopefully, I can make it print this:
# Route is:
blah blah blah blah
File.open 'file.txt' do |file|
file.find { |line| line =~ /regexp/ }
end
That will return the first line that matches the regular expression. If you want all matching lines, change find to find_all.
It's also more efficient. It iterates over the lines one at a time, without loading the entire file into memory.
Also, the grep method can be used:
File.foreach('file.txt').grep /regexp/
The simplest way to get the root is to do:
rake routes | grep root
If you want to do it in Ruby, I would go with:
File.open("config/routes.rb") do |f|
f.each_line do |line|
if line =~ /root/
puts "Found root: #{line}"
end
end
end
Inside text you have the whole file as a string, you can either match against it using a .match with regexp or as Dave Newton suggested you can just iterate over each line and check.
Something such as:
f.each_line { |line|
if line =~ /string/ then
puts line
end
}
So following is what I would like to do, be able to access I18n locale as a constant.For example:
if (I18n.locale == I18n.locales.en)
puts "You are using viewing page in english"
end
Is there a way to access these constants (I18n.locales.en is just example to clarify)? I can always write
if (I18n.locale.to_s == "en")
but I would like to avoid that. Since code is less readable with that approach.
You could shorten your second statement to be if (I18n.locale == :en) rather than converting to a string, however that's still missing the point of using Rails' locale support. For example, with this yml file:
en:
hello: "Hello world!"
fr:
hello: "Bonjour tout le monde !"
You should just be able to do this without any conditional statements:
puts I18n.translate(:hello)
and it would give you the appropriate translation.
If you really wanted to be able to do something like your first example, then you could override method_missing on the Symbol class. If you did this:
class Symbol
def method_missing(method_name, *arguments)
if method_name.to_s[-1,1] == "?"
self.to_s == method_name.to_s[0..-2]
else
super
end
end
end
Then you could do this:
if I18n.locale.en?
puts "english"
end
Similar question, but for java, Keeping i18n resources synced
How to keep the i18n yaml locals' keys in sync? i.e. when a key is added to en.yml, how to get those to nb.yml or ru.yml?
if I add the key my_label: "some text in english" next to my_title: "a title" I'd like to get this to my other locals I specify, as I can't do all translations and it should fall back to english in other languages
e.g en.yml
somegroup:
my_tile: "a title in english"
my_label: "some text in english"
othergroup:
...
I'd like go issue a command and get the whole key and value inject into the norwegian translation and the corresponding position, if missing. Then git diff would show all translations to do in this language.
nb.yml
somegroup:
my_tile: "En tittel på norsk"
+ my_label: "some text in english"
othergroup:
...
Are there any gems that do something like this? If you think it's a good idea, maybe I should take the time to make it myself. Other approaches?
Try the i18n_translation_spawner gem, it could be helpful.
I will check i18n_translation_spawner gem. In case that someone needs a not-so-fast but do the job, i use this script:
First we extend the Hash class in order to support deep_merge and to replace all their leaf values with some string.
require 'yaml'
class Hash
def deep_merge(hash)
target = dup
hash.keys.each do |key|
if hash[key].is_a? Hash and self[key].is_a? Hash
target[key] = target[key].deep_merge(hash[key])
next
end
target[key] = hash[key]
end
target
end
def fill_all_values value
each_key do |key|
if self[key].is_a?(String)
store(key,value)
else
self[key].fill_all_values value
end
end
end
end
Now we can use our merger of translations:
def merge_yaml_i18n_files(locale_code_A,locale_code_B,untranslated_message)
hash_A = YAML.load_file("i18n/#{locale_code_A}.yml")
hash_B = YAML.load_file("i18n/#{locale_code_B}.yml")
hash_A_ut = Marshal.load(Marshal.dump(hash_A))
hash_A_ut.fill_all_values(untranslated_message)
hash_B_ut = Marshal.load(Marshal.dump(hash_B))
hash_B_ut.fill_all_values(untranslated_message)
hash_A = hash_B_ut.deep_merge(hash_A)
hash_B = hash_A_ut.deep_merge(hash_B)
puts hash_A.to_yaml
puts hash_B.to_yaml
end
And finally, we call this method with:
merge_yaml_i18n_files('en','es','untranslated')
If we apply this function in the following i18n files:
es.yaml
test:
hello: Hola
only_es: abc
en.yaml
test:
hello: Hello
only_en: def
The result will be:
es.yaml
test:
hello: Hola
only_en: untranslated
only_es: abc
en.yaml
test:
hello: Hello
only_en: def
only_es: untranslated
You can use i18n-tasks gem for this.
It scans calls such as I18n.t('some.key') and provides reports on key usage, missing, and unused keys. It can also can pre-fill missing keys, including from Google Translate, and it can remove unused keys as well.
ActiveSupport offers the nice method to_sentence. Thus,
require 'active_support'
[1,2,3].to_sentence # gives "1, 2, and 3"
[1,2,3].to_sentence(:last_word_connector => ' and ') # gives "1, 2 and 3"
it's good that you can change the last word connector, because I prefer not to have the extra comma. but it takes so much extra text: 44 characters instead of 11!
the question: what's the most ruby-like way to change the default value of :last_word_connector to ' and '?
Well, it's localizable so you could just specify a default 'en' value of ' and ' for support.array.last_word_connector
See:
from: conversion.rb
def to_sentence(options = {})
...
default_last_word_connector = I18n.translate(:'support.array.last_word_connector', :locale => options[:locale])
...
end
Step by step guide:
First, Create a rails project
rails i18n
Next, edit your en.yml file: vim config/locales/en.yml
en:
support:
array:
last_word_connector: " and "
Finally, it works:
Loading development environment (Rails 2.3.3)
>> [1,2,3].to_sentence
=> "1, 2 and 3"
As an answer to how to override a method in general, a post here gives a nice way of doing it. It doesn't suffer from the same problems as the alias technique, as there isn't a leftover "old" method.
Here how you could use that technique with your original problem (tested with ruby 1.9)
class Array
old_to_sentence = instance_method(:to_sentence)
define_method(:to_sentence) { |options = {}|
options[:last_word_connector] ||= " and "
old_to_sentence.bind(self).call(options)
}
end
You might also want read up on UnboundMethod if the above code is confusing. Note that old_to_sentence goes out of scope after the end statement, so it isn't a problem for future uses of Array.
class Array
alias_method :old_to_sentence, :to_sentence
def to_sentence(args={})
a = {:last_word_connector => ' and '}
a.update(args) if args
old_to_sentence(a)
end
end