translate-rails3 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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