tolk 1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README +60 -0
- data/app/controllers/tolk/application_controller.rb +17 -0
- data/app/controllers/tolk/locales_controller.rb +46 -0
- data/app/controllers/tolk/searches_controller.rb +15 -0
- data/app/helpers/tolk/application_helper.rb +33 -0
- data/app/models/tolk/locale.rb +246 -0
- data/app/models/tolk/phrase.rb +22 -0
- data/app/models/tolk/translation.rb +91 -0
- data/app/views/layouts/tolk/application.html.erb +33 -0
- data/app/views/tolk/locales/all.html.erb +64 -0
- data/app/views/tolk/locales/index.html.erb +35 -0
- data/app/views/tolk/locales/show.atom.builder +11 -0
- data/app/views/tolk/locales/show.html.erb +58 -0
- data/app/views/tolk/searches/_form.html.erb +8 -0
- data/app/views/tolk/searches/show.html.erb +55 -0
- data/config/routes.rb +7 -0
- data/init.rb +8 -0
- data/lib/generators/tolk_migration/templates/migrate/create_tolk_tables.rb +38 -0
- data/lib/generators/tolk_migration/tolk_migration_generator.rb +19 -0
- data/lib/tasks/tolk_tasks.rake +43 -0
- data/lib/tolk.rb +6 -0
- data/lib/tolk/engine.rb +7 -0
- data/lib/tolk/import.rb +47 -0
- data/lib/tolk/sync.rb +74 -0
- data/public/tolk/reset.css +30 -0
- data/public/tolk/screen.css +336 -0
- metadata +122 -0
@@ -0,0 +1,91 @@
|
|
1
|
+
module Tolk
|
2
|
+
class Translation < ActiveRecord::Base
|
3
|
+
set_table_name "tolk_translations"
|
4
|
+
|
5
|
+
scope :containing_text, lambda {|query| where("tolk_translations.text LIKE ?", "%#{query}%") }
|
6
|
+
|
7
|
+
serialize :text
|
8
|
+
validates_presence_of :text, :if => proc {|r| r.primary.blank? && !r.explicit_nil }
|
9
|
+
|
10
|
+
validates_uniqueness_of :phrase_id, :scope => :locale_id
|
11
|
+
|
12
|
+
belongs_to :phrase, :class_name => 'Tolk::Phrase'
|
13
|
+
belongs_to :locale, :class_name => 'Tolk::Locale'
|
14
|
+
|
15
|
+
attr_accessor :force_set_primary_update
|
16
|
+
before_save :set_primary_updated
|
17
|
+
|
18
|
+
before_save :set_previous_text
|
19
|
+
|
20
|
+
attr_accessor :primary
|
21
|
+
before_validation :fix_text_type, :unless => proc {|r| r.primary }
|
22
|
+
|
23
|
+
attr_accessor :explicit_nil
|
24
|
+
before_validation :set_explicit_nil
|
25
|
+
|
26
|
+
def up_to_date?
|
27
|
+
not out_of_date?
|
28
|
+
end
|
29
|
+
|
30
|
+
def out_of_date?
|
31
|
+
primary_updated?
|
32
|
+
end
|
33
|
+
|
34
|
+
def primary_translation
|
35
|
+
@_primary_translation ||= begin
|
36
|
+
if locale && !locale.primary?
|
37
|
+
phrase.translations.primary
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def text=(value)
|
43
|
+
super unless value.to_s == text
|
44
|
+
end
|
45
|
+
|
46
|
+
def value
|
47
|
+
if text.is_a?(String) && /^\d+$/.match(text)
|
48
|
+
text.to_i
|
49
|
+
else
|
50
|
+
text
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def set_explicit_nil
|
57
|
+
if self.text == '~'
|
58
|
+
self.text = nil
|
59
|
+
self.explicit_nil = true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def fix_text_type
|
64
|
+
if primary_translation.present?
|
65
|
+
if self.text.is_a?(String) && !primary_translation.text.is_a?(String)
|
66
|
+
self.text = begin
|
67
|
+
YAML.load(self.text.strip)
|
68
|
+
rescue ArgumentError
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
self.text = nil if primary_translation.text.class != self.text.class
|
75
|
+
end
|
76
|
+
|
77
|
+
true
|
78
|
+
end
|
79
|
+
|
80
|
+
def set_primary_updated
|
81
|
+
self.primary_updated = self.force_set_primary_update ? true : false
|
82
|
+
true
|
83
|
+
end
|
84
|
+
|
85
|
+
def set_previous_text
|
86
|
+
self.previous_text = self.text_was if text_changed?
|
87
|
+
true
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
2
|
+
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<%= Tolk::Locale.primary_locale_name %>" lang="<%= Tolk::Locale.primary_locale_name %>">
|
4
|
+
|
5
|
+
<head>
|
6
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
7
|
+
<title>Tolk</title>
|
8
|
+
|
9
|
+
<% if respond_to?(:sprockets_include_tag) %>
|
10
|
+
<%= sprockets_include_tag %>
|
11
|
+
<% else %>
|
12
|
+
<%= javascript_include_tag :defaults %>
|
13
|
+
<% end %>
|
14
|
+
|
15
|
+
<%= stylesheet_link_tag "/tolk/reset", "/tolk/screen" %>
|
16
|
+
<%= yield :head %>
|
17
|
+
</head>
|
18
|
+
|
19
|
+
<body>
|
20
|
+
<div id="container">
|
21
|
+
<div id="head">
|
22
|
+
<h1><span class="home"><%= link_to "Tolk", tolk_root_path %></span><%= yield :locale %></h1>
|
23
|
+
</div>
|
24
|
+
<%= yield %>
|
25
|
+
</div>
|
26
|
+
|
27
|
+
<script type="text/javascript">
|
28
|
+
$$('td textarea').each(function(element) {
|
29
|
+
element.setStyle({height: element.up('td').measure('height')+'px'});
|
30
|
+
});
|
31
|
+
</script>
|
32
|
+
</body>
|
33
|
+
</html>
|
@@ -0,0 +1,64 @@
|
|
1
|
+
<% content_for :locale do %><span class="locale"><%= @locale.language_name %></span>
|
2
|
+
<% end %>
|
3
|
+
|
4
|
+
<h3 class="switch">Completed translations <span>(<%= link_to 'See phrases missing translation', @locale %>)</span></h3>
|
5
|
+
|
6
|
+
<% if @locale.has_updated_translations? && action_name != 'updated' %>
|
7
|
+
<span class="notice">Some phrases have changed. <%= link_to "Update translations", updated_tolk_locale_path(@locale) %>.</span>
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
<div class="search">
|
11
|
+
<%= render :partial => "tolk/searches/form", :locals => { :locale => @locale } %>
|
12
|
+
</div>
|
13
|
+
|
14
|
+
<div class="translations">
|
15
|
+
<% if @phrases.any? %>
|
16
|
+
<%= form_for @locale do |locale_form| %>
|
17
|
+
<table class="translations">
|
18
|
+
<tr>
|
19
|
+
<th class="translation"><%= @locale.language_name -%></th>
|
20
|
+
<th class="key"><%= Tolk::Locale.primary_language_name -%></th>
|
21
|
+
</tr>
|
22
|
+
<% @phrases.each do |phrase| %>
|
23
|
+
<% if phrase.translations.primary %>
|
24
|
+
<tr>
|
25
|
+
<td class="translation">
|
26
|
+
<%= hidden_field_tag :"translations[][id]", phrase.translation.id %>
|
27
|
+
<%= hidden_field_tag :"translations[][locale_id]", phrase.translation.locale_id %>
|
28
|
+
<%= text_area_tag :"translations[][text]", format_i18n_text_area_value(phrase.translation.text), :class => 'locale',
|
29
|
+
:onfocus => "$(this).up('tr').addClassName('active');", :onblur => "$(this).up('tr').removeClassName('active');" %>
|
30
|
+
</td>
|
31
|
+
<td class="phrase">
|
32
|
+
|
33
|
+
<% if action_name == 'updated' %>
|
34
|
+
<div class="updated">
|
35
|
+
<span class="key">Updated</span>
|
36
|
+
<%= format_i18n_value(phrase.translations.primary.text) -%>
|
37
|
+
</div>
|
38
|
+
<div class="original">
|
39
|
+
<span class="key">Original</span>
|
40
|
+
<%= format_i18n_value(phrase.translations.primary.previous_text) -%>
|
41
|
+
</div>
|
42
|
+
<% else %>
|
43
|
+
<%= format_i18n_value(phrase.translations.primary.text) -%>
|
44
|
+
<% end %>
|
45
|
+
|
46
|
+
<span class="key"><%= phrase.key %></span>
|
47
|
+
</td>
|
48
|
+
</tr>
|
49
|
+
<% end %>
|
50
|
+
<% end %>
|
51
|
+
</table>
|
52
|
+
<div class="table_submit">
|
53
|
+
<p><%= locale_form.submit "Save changes" %></p>
|
54
|
+
</div>
|
55
|
+
<div class="paginate">
|
56
|
+
<%= will_paginate @phrases %>
|
57
|
+
</div>
|
58
|
+
<% end %>
|
59
|
+
<% else %>
|
60
|
+
<p style="text-align: left">There aren't any completed translations for this locale.</p>
|
61
|
+
<p> </p>
|
62
|
+
<% end %>
|
63
|
+
</div>
|
64
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<% content_for :locale do %><span class="locale empty"></span>
|
2
|
+
<% end %>
|
3
|
+
|
4
|
+
<h2>Locales <span>Primary locale is <%= Tolk::Locale.primary_locale.language_name %></span></h2>
|
5
|
+
<% if @locales.any? %>
|
6
|
+
<ul class="locales clearfix">
|
7
|
+
<% @locales.each do |locale| %>
|
8
|
+
<li>
|
9
|
+
<%= link_to locale.language_name, locale %>
|
10
|
+
<% missing_count = locale.count_phrases_without_translation %>
|
11
|
+
<% if missing_count > 0 %>
|
12
|
+
<span class="missing_translations"><%= locale.count_phrases_without_translation %></span>
|
13
|
+
<% end %>
|
14
|
+
</li>
|
15
|
+
<% end %>
|
16
|
+
</ul>
|
17
|
+
|
18
|
+
<% else %>
|
19
|
+
<p>No locales yet.</p>
|
20
|
+
|
21
|
+
<% end %>
|
22
|
+
<%= form_for(Tolk::Locale.new) do |f| %>
|
23
|
+
<div class="submit">
|
24
|
+
<%= f.error_messages %>
|
25
|
+
<p>
|
26
|
+
<%= f.label "Add a new Locale" %>
|
27
|
+
<select id="select_tolk_locale_name" name="tolk_locale[name]">
|
28
|
+
<%= options_for_select tolk_locale_selection %>
|
29
|
+
</select>
|
30
|
+
<%= f.submit 'Add' %>
|
31
|
+
</p>
|
32
|
+
</div>
|
33
|
+
<% end %>
|
34
|
+
|
35
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
atom_feed do |feed|
|
2
|
+
feed.title "Missing Translations for #{@locale.language_name} locale"
|
3
|
+
|
4
|
+
@phrases.each do |phrase|
|
5
|
+
feed.entry(phrase, :url => tolk_locale_url(@locale)) do |entry|
|
6
|
+
entry.title(phrase.key)
|
7
|
+
entry.content(phrase.key)
|
8
|
+
entry.author {|author| author.name("Tolk") }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
<% content_for :locale do %>
|
2
|
+
<span class="locale"><%= @locale.language_name %></span>
|
3
|
+
<% end %>
|
4
|
+
|
5
|
+
<% content_for :head do %>
|
6
|
+
<link rel="alternate" type="application/rss+xml" title="RSS" href="<%= tolk_locale_path(@locale, :format => 'atom') -%>" />
|
7
|
+
<% end %>
|
8
|
+
|
9
|
+
<h3 class="switch">Phrases missing translation (<%= @locale.count_phrases_without_translation %>) <span>(<%= link_to 'See completed translations', all_tolk_locale_path(@locale) %>)</span></h3>
|
10
|
+
|
11
|
+
<% if @locale.has_updated_translations? && action_name != 'updated' %>
|
12
|
+
<span class="notice">Some phrases have changed. <%= link_to "Update translations", updated_tolk_locale_path(@locale) %>.</span>
|
13
|
+
<% end %>
|
14
|
+
|
15
|
+
<div class="search">
|
16
|
+
<%= render :partial => "tolk/searches/form", :locals => { :locale => @locale } %>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<div class="translations">
|
20
|
+
<% if @phrases.any? %>
|
21
|
+
<%= form_for @locale do |locale_form| %>
|
22
|
+
<table class="translations">
|
23
|
+
<tr>
|
24
|
+
<th class="translation"><%= @locale.language_name -%></th>
|
25
|
+
<th class="key"><%= Tolk::Locale.primary_language_name -%></th>
|
26
|
+
</tr>
|
27
|
+
<% @phrases.each do |phrase| %>
|
28
|
+
<tr>
|
29
|
+
<% translation = Tolk::Translation.new(:locale => @locale, :phrase => phrase) %>
|
30
|
+
<td class="translation">
|
31
|
+
<%= hidden_field_tag :"translations[][id]", translation.id, :id => "#{translation.object_id}_id" %>
|
32
|
+
<%= hidden_field_tag :"translations[][phrase_id]", phrase.id, :id => "#{translation.object_id}_phrase_id" %>
|
33
|
+
<%= hidden_field_tag :"translations[][locale_id]", translation.locale_id, :id => "#{translation.object_id}_locale_id" %>
|
34
|
+
<%= text_area_tag :"translations[][text]", format_i18n_text_area_value(translation.text), :class => "locale", :id => "#{translation.object_id}_text", :onfocus => "$(this).up('tr').addClassName('active');", :onblur => "$(this).up('tr').removeClassName('active');" %>
|
35
|
+
</td>
|
36
|
+
<td class="phrase">
|
37
|
+
<% if params[:q].present? -%>
|
38
|
+
<%= highlight(format_i18n_value(phrase.translations.primary.text), params[:q]) -%>
|
39
|
+
<% else -%>
|
40
|
+
<%= format_i18n_value(phrase.translations.primary.text) -%>
|
41
|
+
<% end -%>
|
42
|
+
<span class="key" title="<%= phrase.key %>"><%= truncate(phrase.key, :length => 100) %></span>
|
43
|
+
</td>
|
44
|
+
</tr>
|
45
|
+
<% end %>
|
46
|
+
</table>
|
47
|
+
<div class="table_submit">
|
48
|
+
<p><%= locale_form.submit "Save changes" %></p>
|
49
|
+
</div>
|
50
|
+
<% end %>
|
51
|
+
<div class="paginate">
|
52
|
+
<%= will_paginate @phrases %>
|
53
|
+
</div>
|
54
|
+
<% else %>
|
55
|
+
<p style="text-align: left">There aren't any missing or updated phrases that need translation.</p>
|
56
|
+
<p> </p>
|
57
|
+
<% end %>
|
58
|
+
</div>
|
@@ -0,0 +1,55 @@
|
|
1
|
+
<% content_for :locale do %>
|
2
|
+
<span class="locale"><%= @locale.language_name %></span>
|
3
|
+
<% end %>
|
4
|
+
|
5
|
+
<h3 class="search">
|
6
|
+
<%= render :partial => "form", :locals => { :locale => @locale } %>
|
7
|
+
</h3>
|
8
|
+
|
9
|
+
<div class="search_exits">
|
10
|
+
<%= link_to "Phrases missing translation", @locale %>
|
11
|
+
|
12
|
+
<%= link_to "Completed translations", all_tolk_locale_path(@locale) %>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<div class="translations">
|
16
|
+
<% if @phrases.any? %>
|
17
|
+
<%= form_for @locale do |locale_form| %>
|
18
|
+
<table class="translations">
|
19
|
+
<tr>
|
20
|
+
<th class="translation"><%= @locale.language_name -%></th>
|
21
|
+
<th class="key"><%= Tolk::Locale.primary_language_name -%></th>
|
22
|
+
</tr>
|
23
|
+
<% @phrases.each do |phrase| %>
|
24
|
+
|
25
|
+
<% if translation = phrase.translations.find_by_locale_id(@locale.id) || Tolk::Translation.new(:locale => @locale, :phrase => phrase) %>
|
26
|
+
<tr>
|
27
|
+
<td class="translation">
|
28
|
+
<%= hidden_field_tag :"translations[][id]", translation.id, :id => "#{translation.object_id}_id" %>
|
29
|
+
<%= hidden_field_tag :"translations[][phrase_id]", phrase.id, :id => "#{translation.object_id}_phrase_id" %>
|
30
|
+
<%= hidden_field_tag :"translations[][locale_id]", translation.locale_id, :id => "#{translation.object_id}_locale_id" %>
|
31
|
+
<%= text_area_tag :"translations[][text]", format_i18n_text_area_value(translation.text), :class => "locale", :id => "#{translation.object_id}_text", :onfocus => "$(this).up('tr').addClassName('active');", :onblur => "$(this).up('tr').removeClassName('active');" %>
|
32
|
+
</td>
|
33
|
+
<td class="phrase">
|
34
|
+
<% if params[:q].present? -%>
|
35
|
+
<%= highlight(format_i18n_value(phrase.translations.primary.text), params[:q]) -%>
|
36
|
+
<% else -%>
|
37
|
+
<%= format_i18n_value(phrase.translations.primary.text) -%>
|
38
|
+
<% end -%>
|
39
|
+
<span class="key" title="<%= phrase.key %>"><%= truncate(phrase.key, :length => 100) %></span>
|
40
|
+
</td>
|
41
|
+
</tr>
|
42
|
+
<% end %>
|
43
|
+
<% end %>
|
44
|
+
</table>
|
45
|
+
<div class="table_submit">
|
46
|
+
<p><%= locale_form.submit "Save changes" %></p>
|
47
|
+
</div>
|
48
|
+
<% end %>
|
49
|
+
<div class="paginate">
|
50
|
+
<%= will_paginate @phrases %>
|
51
|
+
</div>
|
52
|
+
<% else %>
|
53
|
+
<p style="text-align: left">No search results.</p>
|
54
|
+
<% end %>
|
55
|
+
</div>
|
data/config/routes.rb
ADDED
data/init.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
class CreateTolkTables < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :tolk_locales do |t|
|
4
|
+
t.string :name
|
5
|
+
t.datetime :created_at
|
6
|
+
t.datetime :updated_at
|
7
|
+
end
|
8
|
+
|
9
|
+
add_index :tolk_locales, :name, :unique => true
|
10
|
+
|
11
|
+
create_table :tolk_phrases do |t|
|
12
|
+
t.text :key
|
13
|
+
t.datetime :created_at
|
14
|
+
t.datetime :updated_at
|
15
|
+
end
|
16
|
+
|
17
|
+
create_table :tolk_translations do |t|
|
18
|
+
t.integer :phrase_id
|
19
|
+
t.integer :locale_id
|
20
|
+
t.text :text
|
21
|
+
t.text :previous_text
|
22
|
+
t.boolean :primary_updated, :default => false
|
23
|
+
t.datetime :created_at
|
24
|
+
t.datetime :updated_at
|
25
|
+
end
|
26
|
+
|
27
|
+
add_index :tolk_translations, [:phrase_id, :locale_id], :unique => true
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.down
|
31
|
+
remove_index :tolk_translations, :column => [:phrase_id, :locale_id]
|
32
|
+
remove_index :tolk_locales, :column => :name
|
33
|
+
|
34
|
+
drop_table :tolk_translations
|
35
|
+
drop_table :tolk_phrases
|
36
|
+
drop_table :tolk_locales
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class TolkMigrationGenerator < Rails::Generators::Base
|
2
|
+
include Rails::Generators::Migration
|
3
|
+
|
4
|
+
source_root File.expand_path("../templates", __FILE__)
|
5
|
+
|
6
|
+
def create_migration_file
|
7
|
+
migration_template "migrate/create_tolk_tables.rb", "db/migrate/create_tolk_tables.rb"
|
8
|
+
end
|
9
|
+
|
10
|
+
# Implement the required interface for Rails::Generators::Migration.
|
11
|
+
def self.next_migration_number(dirname) #:nodoc:
|
12
|
+
next_migration_number = current_migration_number(dirname) + 1
|
13
|
+
if ActiveRecord::Base.timestamped_migrations
|
14
|
+
[Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
|
15
|
+
else
|
16
|
+
"%.3d" % next_migration_number
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
namespace :tolk do
|
2
|
+
desc "Add database tables, copy over the assets, and import existing translations"
|
3
|
+
task :setup => :environment do
|
4
|
+
Rake::Task['tolk:import_assets'].invoke
|
5
|
+
system("rails generate tolk_migration")
|
6
|
+
Rake::Task['db:migrate'].invoke
|
7
|
+
Rake::Task['tolk:sync'].invoke
|
8
|
+
Rake::Task['tolk:import'].invoke
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "Sync Tolk with the default locale's yml file"
|
12
|
+
task :sync => :environment do
|
13
|
+
Tolk::Locale.sync!
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Generate yml files for all the locales defined in Tolk"
|
17
|
+
task :dump_all => :environment do
|
18
|
+
Tolk::Locale.dump_all
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "Imports data all non default locale yml files to Tolk"
|
22
|
+
task :import => :environment do
|
23
|
+
Rake::Task['tolk:sync'].invoke
|
24
|
+
Tolk::Locale.import_secondary_locales
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Show all the keys potentially containing HTML values and no _html postfix"
|
28
|
+
task :html_keys => :environment do
|
29
|
+
bad_translations = Tolk::Locale.primary_locale.translations_with_html
|
30
|
+
bad_translations.each do |bt|
|
31
|
+
puts "#{bt.phrase.key} - #{bt.text}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Copies required assets from tolk to application's public/ directory"
|
36
|
+
task :import_assets do
|
37
|
+
tolk_assets = File.expand_path(File.join(File.dirname(__FILE__), '../../public/tolk'))
|
38
|
+
command = "cp -R #{tolk_assets} #{Rails.root}/public/"
|
39
|
+
puts command
|
40
|
+
system(command)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|