translate-rails3-plus 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,178 @@
1
+ require 'yaml'
2
+
3
+ class Hash
4
+ def deep_merge(other)
5
+ # deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
6
+ merger = proc { |key, v1, v2| (Hash === v1 && Hash === v2) ? v1.merge(v2, &merger) : v2 }
7
+ merge(other, &merger)
8
+ end
9
+
10
+ def set(keys, value)
11
+ key = keys.shift
12
+ if keys.empty?
13
+ self[key] = value
14
+ else
15
+ self[key] ||= {}
16
+ self[key].set keys, value
17
+ end
18
+ end
19
+
20
+ if ENV['SORT']
21
+ # copy of ruby's to_yaml method, prepending sort.
22
+ # before each so we get an ordered yaml file
23
+ def to_yaml( opts = {} )
24
+ YAML::quick_emit( self, opts ) do |out|
25
+ out.map( taguri, to_yaml_style ) do |map|
26
+ sort.each do |k, v| #<- Adding sort.
27
+ map.add( k, v )
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ namespace :translate do
36
+ desc "Show untranslated keys for locale LOCALE"
37
+ task :untranslated => :environment do
38
+ from_locale = I18n.default_locale
39
+ untranslated = Translate::Keys.new.untranslated_keys
40
+
41
+ messages = []
42
+ untranslated.each do |locale, keys|
43
+ keys.each do |key|
44
+ from_text = I18n.backend.send(:lookup, from_locale, key)
45
+ messages << "#{locale}.#{key} (#{from_locale}.#{key}='#{from_text}')"
46
+ end
47
+ end
48
+
49
+ if messages.present?
50
+ messages.each { |m| puts m }
51
+ else
52
+ puts "No untranslated keys"
53
+ end
54
+ end
55
+
56
+ desc "Show I18n keys that are missing in the config/locales/default_locale.yml YAML file"
57
+ task :missing => :environment do
58
+ missing = Translate::Keys.new.missing_keys.inject([]) do |keys, (key, filename)|
59
+ keys << "#{key} in \t #{filename} is missing"
60
+ end
61
+ puts missing.present? ? missing.join("\n") : "No missing translations in the default locale file"
62
+ end
63
+
64
+ desc "Remove all translation texts that are no longer present in the locale they were translated from"
65
+ task :remove_obsolete_keys => :environment do
66
+ I18n.backend.send(:init_translations)
67
+ master_locale = ENV['LOCALE'] || I18n.default_locale
68
+ Translate::Keys.translated_locales.each do |locale|
69
+ texts = {}
70
+ Translate::Keys.new.i18n_keys(locale).each do |key|
71
+ if I18n.backend.send(:lookup, master_locale, key).to_s.present?
72
+ texts[key] = I18n.backend.send(:lookup, locale, key)
73
+ end
74
+ end
75
+ I18n.backend.send(:translations)[locale] = nil # Clear out all current translations
76
+ I18n.backend.store_translations(locale, Translate::Keys.to_deep_hash(texts))
77
+ Translate::Storage.new(locale).write_to_file
78
+ end
79
+ end
80
+
81
+ desc "Merge I18n keys from log/translations.yml into config/locales/*.yml (for use with the Rails I18n TextMate bundle)"
82
+ task :merge_keys => :environment do
83
+ I18n.backend.send(:init_translations)
84
+ new_translations = YAML::load(IO.read(File.join(Rails.root, "log", "translations.yml")))
85
+ raise("Can only merge in translations in single locale") if new_translations.keys.size > 1
86
+ locale = new_translations.keys.first
87
+
88
+ overwrites = false
89
+ Translate::Keys.to_shallow_hash(new_translations[locale]).keys.each do |key|
90
+ new_text = key.split(".").inject(new_translations[locale]) { |hash, sub_key| hash[sub_key] }
91
+ existing_text = I18n.backend.send(:lookup, locale.to_sym, key)
92
+ if existing_text && new_text != existing_text
93
+ puts "ERROR: key #{key} already exists with text '#{existing_text.inspect}' and would be overwritten by new text '#{new_text}'. " +
94
+ "Set environment variable OVERWRITE=1 if you really want to do this."
95
+ overwrites = true
96
+ end
97
+ end
98
+
99
+ if !overwrites || ENV['OVERWRITE']
100
+ I18n.backend.store_translations(locale, new_translations[locale])
101
+ Translate::Storage.new(locale).write_to_file
102
+ end
103
+ end
104
+
105
+ desc "Apply Google translate to auto translate all texts in locale ENV['FROM'] to locale ENV['TO']"
106
+ task :google => :environment do
107
+ raise "Please specify FROM and TO locales as environment variables" if ENV['FROM'].blank? || ENV['TO'].blank?
108
+
109
+ # Depends on httparty gem
110
+ # http://www.robbyonrails.com/articles/2009/03/16/httparty-goes-foreign
111
+ class GoogleApi
112
+ include HTTParty
113
+ base_uri 'ajax.googleapis.com'
114
+ def self.translate(string, to, from)
115
+ tries = 0
116
+ begin
117
+ get("/ajax/services/language/translate",
118
+ :query => {:langpair => "#{from}|#{to}", :q => string, :v => 1.0},
119
+ :format => :json)
120
+ rescue
121
+ tries += 1
122
+ puts("SLEEPING - retrying in 5...")
123
+ sleep(5)
124
+ retry if tries < 10
125
+ end
126
+ end
127
+ end
128
+
129
+ I18n.backend.send(:init_translations)
130
+
131
+ start_at = Time.now
132
+ translations = {}
133
+ Translate::Keys.new.i18n_keys(ENV['FROM']).each do |key|
134
+ from_text = I18n.backend.send(:lookup, ENV['FROM'], key).to_s
135
+ to_text = I18n.backend.send(:lookup, ENV['TO'], key)
136
+ if !from_text.blank? && to_text.blank?
137
+ print "#{key}: '#{from_text[0, 40]}' => "
138
+ if !translations[from_text]
139
+ response = GoogleApi.translate(from_text, ENV['TO'], ENV['FROM'])
140
+ translations[from_text] = response["responseData"] && response["responseData"]["translatedText"]
141
+ end
142
+ if !(translation = translations[from_text]).blank?
143
+ translation.gsub!(/\(\(([a-z_.]+)\)\)/i, '{{\1}}')
144
+ # Google translate sometimes replaces {{foobar}} with (()) foobar. We skip these
145
+ if translation !~ /\(\(\)\)/
146
+ puts "'#{translation[0, 40]}'"
147
+ I18n.backend.store_translations(ENV['TO'].to_sym, Translate::Keys.to_deep_hash({key => translation}))
148
+ else
149
+ puts "SKIPPING since interpolations were messed up: '#{translation[0,40]}'"
150
+ end
151
+ else
152
+ puts "NO TRANSLATION - #{response.inspect}"
153
+ end
154
+ end
155
+ end
156
+
157
+ puts "\nTime elapsed: #{(((Time.now - start_at) / 60) * 10).to_i / 10.to_f} minutes"
158
+ Translate::Storage.new(ENV['TO'].to_sym).write_to_file
159
+ end
160
+
161
+ desc "List keys that have changed I18n texts between YAML file ENV['FROM_FILE'] and YAML file ENV['TO_FILE']. Set ENV['VERBOSE'] to see changes"
162
+ task :changed => :environment do
163
+ from_hash = Translate::Keys.to_shallow_hash(Translate::File.new(ENV['FROM_FILE']).read)
164
+ to_hash = Translate::Keys.to_shallow_hash(Translate::File.new(ENV['TO_FILE']).read)
165
+ from_hash.each do |key, from_value|
166
+ if (to_value = to_hash[key]) && to_value != from_value
167
+ key_without_locale = key[/^[^.]+\.(.+)$/, 1]
168
+ if ENV['VERBOSE']
169
+ puts "KEY: #{key_without_locale}"
170
+ puts "FROM VALUE: '#{from_value}'"
171
+ puts "TO VALUE: '#{to_value}'"
172
+ else
173
+ puts key_without_locale
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,35 @@
1
+ require 'fileutils'
2
+
3
+ class Translate::File
4
+ attr_accessor :path
5
+
6
+ def initialize(path)
7
+ self.path = path
8
+ end
9
+
10
+ def write(keys)
11
+ FileUtils.mkdir_p File.dirname(path)
12
+ File.open(path, "w") do |file|
13
+ file.puts keys_to_yaml(Translate::File.deep_stringify_keys(keys))
14
+ end
15
+ end
16
+
17
+ def read
18
+ File.exists?(path) ? YAML::load(IO.read(path)) : {}
19
+ end
20
+
21
+ # Stringifying keys for prettier YAML
22
+ def self.deep_stringify_keys(hash)
23
+ hash.inject({}) { |result, (key, value)|
24
+ value = deep_stringify_keys(value) if value.is_a? Hash
25
+ result[(key.to_s rescue key) || key] = value
26
+ result
27
+ }
28
+ end
29
+
30
+ private
31
+ def keys_to_yaml(keys)
32
+ # Using ya2yaml, if available, for UTF8 support
33
+ keys.respond_to?(:ya2yaml) ? keys.ya2yaml(:escape_as_utf8 => true) : keys.to_yaml
34
+ end
35
+ end
@@ -0,0 +1,185 @@
1
+ require 'pathname'
2
+
3
+ class Translate::Keys
4
+ # Allows keys extracted from lookups in files to be cached
5
+ def self.files
6
+ @@files ||= Translate::Keys.new.files
7
+ end
8
+
9
+ # Allows flushing of the files cache
10
+ def self.files=(files)
11
+ @@files = files
12
+ end
13
+
14
+ def files
15
+ @files ||= extract_files
16
+ end
17
+ alias_method :to_hash, :files
18
+
19
+ def keys
20
+ files.keys
21
+ end
22
+ alias_method :to_a, :keys
23
+
24
+ def i18n_keys(locale)
25
+ I18n.backend.send(:init_translations) unless I18n.backend.initialized?
26
+ Translate::Keys.to_shallow_hash(I18n.backend.send(:translations)[locale.to_sym]).keys.sort
27
+ end
28
+
29
+ def untranslated_keys
30
+ Translate::Keys.translated_locales.inject({}) do |missing, locale|
31
+ missing[locale] = i18n_keys(I18n.default_locale).map do |key|
32
+ I18n.backend.send(:lookup, locale, key).nil? ? key : nil
33
+ end.compact
34
+ missing
35
+ end
36
+ end
37
+
38
+ def missing_keys
39
+ locale = I18n.default_locale; yaml_keys = {}
40
+ yaml_keys = Translate::Storage.file_paths(locale).inject({}) do |keys, path|
41
+ keys = keys.deep_merge(Translate::File.new(path).read[locale.to_s])
42
+ end
43
+ files.reject { |key, file| self.class.contains_key?(yaml_keys, key) }
44
+ end
45
+
46
+ def self.translated_locales
47
+ I18n.available_locales.reject { |locale| [:root, I18n.default_locale.to_sym].include?(locale) }
48
+ end
49
+
50
+ # Checks if a nested hash contains the keys in dot separated I18n key.
51
+ #
52
+ # Example:
53
+ #
54
+ # hash = {
55
+ # :foo => {
56
+ # :bar => {
57
+ # :baz => 1
58
+ # }
59
+ # }
60
+ # }
61
+ #
62
+ # contains_key?("foo", key) # => true
63
+ # contains_key?("foo.bar", key) # => true
64
+ # contains_key?("foo.bar.baz", key) # => true
65
+ # contains_key?("foo.bar.baz.bla", key) # => false
66
+ #
67
+ def self.contains_key?(hash, key)
68
+ keys = key.to_s.split(".")
69
+ return false if keys.empty?
70
+ !keys.inject(HashWithIndifferentAccess.new(hash)) do |memo, key|
71
+ memo.is_a?(Hash) ? memo.try(:[], key) : nil
72
+ end.nil?
73
+ end
74
+
75
+ # Convert something like:
76
+ #
77
+ # {
78
+ # :pressrelease => {
79
+ # :label => {
80
+ # :one => "Pressmeddelande"
81
+ # }
82
+ # }
83
+ # }
84
+ #
85
+ # to:
86
+ #
87
+ # {'pressrelease.label.one' => "Pressmeddelande"}
88
+ #
89
+ def self.to_shallow_hash(hash)
90
+ hash.inject({}) do |shallow_hash, (key, value)|
91
+ if value.is_a?(Hash)
92
+ to_shallow_hash(value).each do |sub_key, sub_value|
93
+ shallow_hash[[key, sub_key].join(".")] = sub_value
94
+ end
95
+ else
96
+ shallow_hash[key.to_s] = value
97
+ end
98
+ shallow_hash
99
+ end
100
+ end
101
+
102
+ # Convert something like:
103
+ #
104
+ # {'pressrelease.label.one' => "Pressmeddelande"}
105
+ #
106
+ # to:
107
+ #
108
+ # {
109
+ # :pressrelease => {
110
+ # :label => {
111
+ # :one => "Pressmeddelande"
112
+ # }
113
+ # }
114
+ # }
115
+ def self.to_deep_hash(hash)
116
+ hash.inject({}) do |deep_hash, (key, value)|
117
+ keys = key.to_s.split('.').reverse
118
+ leaf_key = keys.shift
119
+ key_hash = keys.inject({leaf_key.to_sym => value}) { |hash, key| {key.to_sym => hash} }
120
+ deep_merge!(deep_hash, key_hash)
121
+ deep_hash
122
+ end
123
+ end
124
+
125
+ # deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
126
+ def self.deep_merge!(hash1, hash2)
127
+ merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
128
+ hash1.merge!(hash2, &merger)
129
+ end
130
+
131
+ # Convert something like:
132
+ #
133
+ # {'0' => "elem 1", '1' => "elem 2"}
134
+ #
135
+ # to:
136
+ #
137
+ # ["elem 1", "elem 2"]
138
+ #
139
+ def self.arraylize(input_hash)
140
+ input_hash.inject([]) do |constructed_array, (key, value)|
141
+ constructed_array << value
142
+ constructed_array
143
+ end
144
+ end
145
+
146
+ private
147
+
148
+ def extract_files
149
+ files_to_scan.inject(HashWithIndifferentAccess.new) do |files, file|
150
+ keys = IO.read(file)
151
+ if keys.respond_to? "encode"
152
+ keys = keys.encode("UTF-8").force_encoding("UTF-8")
153
+ end
154
+ error_count = 0
155
+ begin
156
+ encoded_keys = keys.scan(i18n_lookup_pattern)
157
+ rescue => e
158
+ unless error_count > 1
159
+ if keys.respond_to? 'encode!'
160
+ keys.encode!('utf-8', 'utf-8', :invalid => :replace)
161
+ end
162
+ error_count += 1
163
+ retry
164
+ else
165
+ puts "cannot fix: #{e} on : #{file}"
166
+ end
167
+ end
168
+ encoded_keys.flatten.map(&:to_sym).each do |key|
169
+ files[key] ||= []
170
+ path = Pathname.new(File.expand_path(file)).relative_path_from(Pathname.new(Rails.root)).to_s
171
+ files[key] << path if !files[key].include?(path)
172
+ end
173
+ files
174
+ end
175
+ end
176
+
177
+ def i18n_lookup_pattern
178
+ /\b(?:I18n\.t|I18n\.translate|t)(?:\s|\():?'([a-z0-9_]+.[a-z0-9_.]+)'\)?/
179
+ end
180
+
181
+ def files_to_scan
182
+ Dir.glob(File.join(Translate::Storage.root_dir, "{app,config,lib}", "**","*.{rb,erb,rhtml}")) +
183
+ Dir.glob(File.join(Translate::Storage.root_dir, "public", "javascripts", "**","*.js"))
184
+ end
185
+ end
@@ -0,0 +1,35 @@
1
+ class Translate::Log
2
+ attr_accessor :from_locale, :to_locale, :keys
3
+
4
+ def initialize(from_locale, to_locale, keys)
5
+ self.from_locale = from_locale
6
+ self.to_locale = to_locale
7
+ self.keys = keys
8
+ end
9
+
10
+ def write_to_file
11
+ current_texts = File.exists?(file_path) ? file.read : {}
12
+ current_texts.merge!(from_texts)
13
+ file.write(current_texts)
14
+ end
15
+
16
+ def read
17
+ file.read
18
+ end
19
+
20
+ private
21
+ def file
22
+ @file ||= Translate::File.new(file_path)
23
+ end
24
+
25
+ def from_texts
26
+ Translate::File.deep_stringify_keys(Translate::Keys.to_deep_hash(keys.inject({}) do |hash, key|
27
+ hash[key] = I18n.backend.send(:lookup, from_locale, key)
28
+ hash
29
+ end))
30
+ end
31
+
32
+ def file_path
33
+ File.join(Rails.root, "config", "locales", "log", "from_#{from_locale}_to_#{to_locale}.yml")
34
+ end
35
+ end
@@ -0,0 +1,11 @@
1
+ module Translate
2
+ class Routes
3
+ def self.translation_ui(map)
4
+ map.with_options(:controller => 'translate') do |t|
5
+ t.translate_list 'translate'
6
+ t.translate 'translate/translate', :action => 'translate'
7
+ t.translate_reload 'translate/reload', :action => 'reload'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,28 @@
1
+ class Translate::Storage
2
+ attr_accessor :locale
3
+
4
+ def initialize(locale)
5
+ self.locale = locale.to_sym
6
+ end
7
+
8
+ def write_to_file
9
+ Translate::File.new(file_path).write(keys)
10
+ end
11
+
12
+ def self.file_paths(locale)
13
+ Dir.glob(File.join(root_dir, "config", "locales", "**","#{locale}.yml"))
14
+ end
15
+
16
+ def self.root_dir
17
+ Rails.root
18
+ end
19
+
20
+ private
21
+ def keys
22
+ {locale => I18n.backend.send(:translations)[locale]}
23
+ end
24
+
25
+ def file_path
26
+ File.join(Translate::Storage.root_dir, "config", "locales", "#{locale}.yml")
27
+ end
28
+ end
data/lib/translate.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'ya2yaml'
2
+
3
+ module Translate
4
+ class Engine < Rails::Engine
5
+ end if defined?(Rails) && Rails::VERSION::MAJOR == 3
6
+
7
+ class << self
8
+ # For configuring Google Translate API key
9
+ attr_accessor :api_key
10
+ # For configuring Bing Application id
11
+ attr_accessor :app_id
12
+ end
13
+ end
14
+
15
+ Dir[File.join(File.dirname(__FILE__), "translate", "*.rb")].each do |file|
16
+ require file
17
+ end
@@ -0,0 +1,130 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
3
+
4
+ describe TranslateController do
5
+ describe "index" do
6
+ before(:each) do
7
+ controller.stub!(:per_page).and_return(1)
8
+ I18n.backend.stub!(:translations).and_return(i18n_translations)
9
+ I18n.backend.instance_eval { @initialized = true }
10
+ keys = mock(:keys)
11
+ keys.stub!(:i18n_keys).and_return(['vendor.foobar'])
12
+ Translate::Keys.should_receive(:new).and_return(keys)
13
+ Translate::Keys.should_receive(:files).and_return(files)
14
+ I18n.stub!(:available_locales).and_return([:en, :sv])
15
+ I18n.stub!(:default_locale).and_return(:sv)
16
+ end
17
+
18
+ it "shows sorted paginated keys from the translate from locale and extracted keys by default" do
19
+ get_page :index
20
+ assigns(:from_locale).should == :sv
21
+ assigns(:to_locale).should == :en
22
+ assigns(:files).should == files
23
+ assigns(:keys).sort.should == ['articles.new.page_title', 'home.page_title', 'vendor.foobar']
24
+ assigns(:paginated_keys).should == ['articles.new.page_title']
25
+ end
26
+
27
+ it "can be paginated with the page param" do
28
+ get_page :index, :page => 2
29
+ assigns(:files).should == files
30
+ assigns(:paginated_keys).should == ['home.page_title']
31
+ end
32
+
33
+ it "accepts a key_pattern param with key_type=starts_with" do
34
+ get_page :index, :key_pattern => 'articles', :key_type => 'starts_with'
35
+ assigns(:files).should == files
36
+ assigns(:paginated_keys).should == ['articles.new.page_title']
37
+ assigns(:total_entries).should == 1
38
+ end
39
+
40
+ it "accepts a key_pattern param with key_type=contains" do
41
+ get_page :index, :key_pattern => 'page_', :key_type => 'contains'
42
+ assigns(:files).should == files
43
+ assigns(:total_entries).should == 2
44
+ assigns(:paginated_keys).should == ['articles.new.page_title']
45
+ end
46
+
47
+ it "accepts a filter=untranslated param" do
48
+ get_page :index, :filter => 'untranslated'
49
+ assigns(:total_entries).should == 2
50
+ assigns(:paginated_keys).should == ['articles.new.page_title']
51
+ end
52
+
53
+ it "accepts a filter=translated param" do
54
+ get_page :index, :filter => 'translated'
55
+ assigns(:total_entries).should == 1
56
+ assigns(:paginated_keys).should == ['vendor.foobar']
57
+ end
58
+
59
+ it "accepts a filter=changed param" do
60
+ log = mock(:log)
61
+ old_translations = {:home => {:page_title => "Skapar ny artikel"}}
62
+ log.should_receive(:read).and_return(Translate::File.deep_stringify_keys(old_translations))
63
+ Translate::Log.should_receive(:new).with(:sv, :en, {}).and_return(log)
64
+ get_page :index, :filter => 'changed'
65
+ assigns(:total_entries).should == 1
66
+ assigns(:keys).should == ["home.page_title"]
67
+ end
68
+
69
+ def i18n_translations
70
+ HashWithIndifferentAccess.new({
71
+ :en => {
72
+ :vendor => {
73
+ :foobar => "Foo Baar"
74
+ }
75
+ },
76
+ :sv => {
77
+ :articles => {
78
+ :new => {
79
+ :page_title => "Skapa ny artikel"
80
+ }
81
+ },
82
+ :home => {
83
+ :page_title => "Välkommen till I18n"
84
+ },
85
+ :vendor => {
86
+ :foobar => "Fobar"
87
+ }
88
+ }
89
+ })
90
+ end
91
+
92
+ def files
93
+ HashWithIndifferentAccess.new({
94
+ :'home.page_title' => ["app/views/home/index.rhtml"],
95
+ :'general.back' => ["app/views/articles/new.rhtml", "app/views/categories/new.rhtml"],
96
+ :'articles.new.page_title' => ["app/views/articles/new.rhtml"]
97
+ })
98
+ end
99
+ end
100
+
101
+ describe "translate" do
102
+ it "should store translations to I18n backend and then write them to a YAML file" do
103
+ session[:from_locale] = :sv
104
+ session[:to_locale] = :en
105
+ translations = {
106
+ :articles => {
107
+ :new => {
108
+ :title => "New Article"
109
+ }
110
+ },
111
+ :category => "Category"
112
+ }
113
+ key_param = {'articles.new.title' => "New Article", "category" => "Category"}
114
+ I18n.backend.should_receive(:store_translations).with(:en, translations)
115
+ storage = mock(:storage)
116
+ storage.should_receive(:write_to_file)
117
+ Translate::Storage.should_receive(:new).with(:en).and_return(storage)
118
+ log = mock(:log)
119
+ log.should_receive(:write_to_file)
120
+ Translate::Log.should_receive(:new).with(:sv, :en, key_param.keys).and_return(log)
121
+ post :translate, "key" => key_param
122
+ response.should be_redirect
123
+ end
124
+ end
125
+
126
+ def get_page(*args)
127
+ get(*args)
128
+ response.should be_success
129
+ end
130
+ end
data/spec/file_spec.rb ADDED
@@ -0,0 +1,54 @@
1
+ require 'fileutils'
2
+ require File.dirname(__FILE__) + '/spec_helper'
3
+
4
+ describe Translate::File do
5
+ describe "write" do
6
+ before(:each) do
7
+ @file = Translate::File.new(file_path)
8
+ end
9
+
10
+ after(:each) do
11
+ FileUtils.rm(file_path)
12
+ end
13
+
14
+ it "writes all I18n messages for a locale to YAML file" do
15
+ @file.write(translations)
16
+ @file.read.should == Translate::File.deep_stringify_keys(translations)
17
+ end
18
+
19
+ def translations
20
+ {
21
+ :en => {
22
+ :article => {
23
+ :title => "One Article"
24
+ },
25
+ :category => "Category"
26
+ }
27
+ }
28
+ end
29
+ end
30
+
31
+ describe "deep_stringify_keys" do
32
+ it "should convert all keys in a hash to strings" do
33
+ Translate::File.deep_stringify_keys({
34
+ :en => {
35
+ :article => {
36
+ :title => "One Article"
37
+ },
38
+ :category => "Category"
39
+ }
40
+ }).should == {
41
+ "en" => {
42
+ "article" => {
43
+ "title" => "One Article"
44
+ },
45
+ "category" => "Category"
46
+ }
47
+ }
48
+ end
49
+ end
50
+
51
+ def file_path
52
+ File.join(File.dirname(__FILE__), "files", "en.yml")
53
+ end
54
+ end
@@ -0,0 +1,12 @@
1
+ class Article < ActiveRecord::Base
2
+ def validate
3
+ # t('li')
4
+ errors.add_to_base([t(:'article.key1') + "#{t('article.key2')}"])
5
+ I18n.t 'article.key3'
6
+ I18n.t 'article.key3'
7
+ I18n.t :'article.key4'
8
+ I18n.translate :'article.key5'
9
+ 'bla bla t' + "blubba bla" + ' foobar'
10
+ 'bla bla t ' + "blubba bla" + ' foobar'
11
+ end
12
+ end
@@ -0,0 +1 @@
1
+ <%= t(:'category_erb.key1') %>
@@ -0,0 +1 @@
1
+ t(:'category_html.key1')