translate-rails3 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
@@ -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
@@ -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')
@@ -0,0 +1 @@
1
+ <%= t(:'category_html_erb.key1') %>
@@ -0,0 +1,5 @@
1
+ <script>
2
+ document.createElement('li');
3
+ </script>
4
+
5
+ <%= t(:'category_rhtml.key1') %>
@@ -0,0 +1 @@
1
+ I18n.t('js.alert')
@@ -0,0 +1,179 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'fileutils'
3
+
4
+ describe Translate::Keys do
5
+ before(:each) do
6
+ I18n.stub!(:default_locale).and_return(:en)
7
+ @keys = Translate::Keys.new
8
+ Translate::Storage.stub!(:root_dir).and_return(i18n_files_dir)
9
+ end
10
+
11
+ describe "to_a" do
12
+ it "extracts keys from I18n lookups in .rb, .html.erb, and .rhtml files" do
13
+ @keys.to_a.map(&:to_s).sort.should == ['article.key1', 'article.key2', 'article.key3', 'article.key4', 'article.key5',
14
+ 'category_erb.key1', 'category_html_erb.key1', 'category_rhtml.key1', 'js.alert']
15
+ end
16
+ end
17
+
18
+ describe "to_hash" do
19
+ it "return a hash with I18n keys and file lists" do
20
+ @keys.to_hash[:'article.key3'].should == ["vendor/plugins/translate/spec/files/translate/app/models/article.rb"]
21
+ end
22
+ end
23
+
24
+ describe "i18n_keys" do
25
+ before(:each) do
26
+ I18n.backend.send(:init_translations) unless I18n.backend.initialized?
27
+ end
28
+
29
+ it "should return all keys in the I18n backend translations hash" do
30
+ I18n.backend.should_receive(:translations).and_return(translations)
31
+ @keys.i18n_keys(:en).should == ['articles.new.page_title', 'categories.flash.created', 'empty', 'home.about']
32
+ end
33
+
34
+ describe "untranslated_keys" do
35
+ before(:each) do
36
+ I18n.backend.stub!(:translations).and_return(translations)
37
+ end
38
+
39
+ it "should return a hash with keys with missing translations in each locale" do
40
+ @keys.untranslated_keys.should == {
41
+ :sv => ['articles.new.page_title', 'categories.flash.created', 'empty']
42
+ }
43
+ end
44
+ end
45
+
46
+ describe "missing_keys" do
47
+ before(:each) do
48
+ @file_path = File.join(i18n_files_dir, "config", "locales", "en.yml")
49
+ Translate::File.new(@file_path).write({
50
+ :en => {
51
+ :home => {
52
+ :page_title => false,
53
+ :intro => {
54
+ :one => "intro one",
55
+ :other => "intro other"
56
+ }
57
+ }
58
+ }
59
+ })
60
+ end
61
+
62
+ after(:each) do
63
+ FileUtils.rm(@file_path)
64
+ end
65
+
66
+ it "should return a hash with keys that are not in the locale file" do
67
+ @keys.stub!(:files).and_return({
68
+ :'home.page_title' => "app/views/home/index.rhtml",
69
+ :'home.intro' => 'app/views/home/index.rhtml',
70
+ :'home.signup' => "app/views/home/_signup.rhtml",
71
+ :'about.index.page_title' => "app/views/about/index.rhtml"
72
+ })
73
+ @keys.missing_keys.should == {
74
+ :'home.signup' => "app/views/home/_signup.rhtml",
75
+ :'about.index.page_title' => "app/views/about/index.rhtml"
76
+ }
77
+ end
78
+ end
79
+
80
+ describe "contains_key?" do
81
+ it "works" do
82
+ hash = {
83
+ :foo => {
84
+ :bar => {
85
+ :baz => false
86
+ }
87
+ }
88
+ }
89
+ Translate::Keys.contains_key?(hash, "").should be_false
90
+ Translate::Keys.contains_key?(hash, "foo").should be_true
91
+ Translate::Keys.contains_key?(hash, "foo.bar").should be_true
92
+ Translate::Keys.contains_key?(hash, "foo.bar.baz").should be_true
93
+ Translate::Keys.contains_key?(hash, :"foo.bar.baz").should be_true
94
+ Translate::Keys.contains_key?(hash, "foo.bar.baz.bla").should be_false
95
+ end
96
+ end
97
+
98
+ describe "translated_locales" do
99
+ before(:each) do
100
+ I18n.stub!(:default_locale).and_return(:en)
101
+ I18n.stub!(:available_locales).and_return([:sv, :no, :en, :root])
102
+ end
103
+
104
+ it "returns all avaiable except :root and the default" do
105
+ Translate::Keys.translated_locales.should == [:sv, :no]
106
+ end
107
+ end
108
+
109
+ describe "to_deep_hash" do
110
+ it "convert shallow hash with dot separated keys to deep hash" do
111
+ Translate::Keys.to_deep_hash(shallow_hash).should == deep_hash
112
+ end
113
+ end
114
+
115
+ describe "to_shallow_hash" do
116
+ it "converts a deep hash to a shallow one" do
117
+ Translate::Keys.to_shallow_hash(deep_hash).should == shallow_hash
118
+ end
119
+ end
120
+
121
+ ##########################################################################
122
+ #
123
+ # Helper Methods
124
+ #
125
+ ##########################################################################
126
+
127
+ def translations
128
+ {
129
+ :en => {
130
+ :home => {
131
+ :about => "This site is about making money"
132
+ },
133
+ :articles => {
134
+ :new => {
135
+ :page_title => "New Article"
136
+ }
137
+ },
138
+ :categories => {
139
+ :flash => {
140
+ :created => "Category created"
141
+ }
142
+ },
143
+ :empty => nil
144
+ },
145
+ :sv => {
146
+ :home => {
147
+ :about => false
148
+ }
149
+ }
150
+ }
151
+ end
152
+ end
153
+
154
+ def shallow_hash
155
+ {
156
+ 'pressrelease.label.one' => "Pressmeddelande",
157
+ 'pressrelease.label.other' => "Pressmeddelanden",
158
+ 'article' => "Artikel",
159
+ 'category' => ''
160
+ }
161
+ end
162
+
163
+ def deep_hash
164
+ {
165
+ :pressrelease => {
166
+ :label => {
167
+ :one => "Pressmeddelande",
168
+ :other => "Pressmeddelanden"
169
+ }
170
+ },
171
+ :article => "Artikel",
172
+ :category => ''
173
+ }
174
+ end
175
+
176
+ def i18n_files_dir
177
+ File.join(ENV['PWD'], "spec", "files", "translate")
178
+ end
179
+ end