t9n 0.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.markdown +1 -0
- data/generators/t9n/t9n_generator.rb +57 -0
- data/generators/t9n/templates/initializer.rb +8 -0
- data/generators/t9n/templates/language.rb +3 -0
- data/generators/t9n/templates/migrations/create_languages.rb +941 -0
- data/generators/t9n/templates/migrations/create_translations.rb +20 -0
- data/generators/t9n/templates/translation.rb +3 -0
- data/init.rb +1 -0
- data/install.rb +1 -0
- data/lib/t9n.rb +16 -0
- data/lib/t9n/active_record/translatable_attribute.rb +120 -0
- data/lib/t9n/backend/active_record.rb +179 -0
- data/lib/t9n/backend/exception_handler.rb +24 -0
- data/lib/t9n/backend/extension.rb +25 -0
- data/lib/t9n/models/language.rb +41 -0
- data/lib/t9n/models/translation.rb +84 -0
- data/lib/t9n/util.rb +95 -0
- data/lib/tasks/t9n.rake +18 -0
- data/rails/init.rb +1 -0
- metadata +86 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
module Perfectline
|
2
|
+
module T9n
|
3
|
+
module Models
|
4
|
+
module Translation
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.__send__(:include, Associations)
|
8
|
+
base.__send__(:include, Scopes)
|
9
|
+
base.__send__(:include, Validations)
|
10
|
+
base.__send__(:include, InstanceMethods)
|
11
|
+
|
12
|
+
base.extend(ClassMethods)
|
13
|
+
end
|
14
|
+
|
15
|
+
module Associations
|
16
|
+
|
17
|
+
def self.included(base)
|
18
|
+
base.class_eval do
|
19
|
+
belongs_to :translatable, :polymorphic => true, :readonly => true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
module Scopes
|
26
|
+
|
27
|
+
def self.included(base)
|
28
|
+
base.class_eval do
|
29
|
+
named_scope :untranslated, :conditions => "value IS NULL OR value = ''"
|
30
|
+
named_scope :translated, :conditions => "value IS NOT NULL AND value != ''"
|
31
|
+
named_scope :orphans, :conditions => "translatable_id IS NULL"
|
32
|
+
|
33
|
+
named_scope :locale, lambda{ |locale| {:conditions => {:locale => locale}} }
|
34
|
+
named_scope :for, lambda{ |object| {:conditions => {:translatable_id => object.id, :translatable_type => object.class.name}} }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
module Validations
|
41
|
+
|
42
|
+
def self.included(base)
|
43
|
+
base.class_eval do
|
44
|
+
validates_presence_of :locale
|
45
|
+
validates_presence_of :key
|
46
|
+
validates_length_of :key, :maximum => 255
|
47
|
+
validates_uniqueness_of :key, :scope => [:locale, :translatable_id, :translatable_type]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
module ClassMethods
|
54
|
+
|
55
|
+
def available_locales
|
56
|
+
find(:all, :select => "DISTINCT locale", :order => :locale).map(&:locale)
|
57
|
+
end
|
58
|
+
|
59
|
+
def lookup(key)
|
60
|
+
translated.find(:all, :conditions => ["key = ? OR key ILIKE ?", key, "#{key}.%"], :order => :key)
|
61
|
+
end
|
62
|
+
|
63
|
+
def cache_key(locale, key, object)
|
64
|
+
if object.nil?
|
65
|
+
return "#{locale}:#{key}"
|
66
|
+
else
|
67
|
+
return "#{locale}:#{key}:#{object.class.name}_#{object.id}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
module InstanceMethods
|
74
|
+
|
75
|
+
def empty?
|
76
|
+
value.blank? || value.nil?
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/t9n/util.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
module Perfectline
|
5
|
+
module T9n
|
6
|
+
class Util
|
7
|
+
|
8
|
+
# sync non-default languages from the default one
|
9
|
+
def self.populate
|
10
|
+
::Translation.locale(I18n.default_locale).each do |t|
|
11
|
+
::Translation.available_locales.reject!{ |locale| I18n.default_locale?(locale) }.each do |locale|
|
12
|
+
unless ::Translation.locale(locale).exists?({:key => t.key, :translatable_id => t.translatable_id, :translatable_type => t.translatable_type})
|
13
|
+
::Translation.create!(
|
14
|
+
:locale => locale,
|
15
|
+
:key => t.key,
|
16
|
+
:value => nil,
|
17
|
+
:translatable_id => t.translatable_id,
|
18
|
+
:translatable_type => t.translatable_type
|
19
|
+
)
|
20
|
+
puts "Translation::Populate created a new translation in :#{locale} with key '#{t.key}'"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# translate from google
|
27
|
+
def self.google_translate
|
28
|
+
I18n.available_locales.reject!{ |locale| I18n.default_locale?(locale) }.each do |locale|
|
29
|
+
::Translation.locale(locale).untranslated.each do |translation|
|
30
|
+
|
31
|
+
unless self.needs_human_eyes?(translation.value)
|
32
|
+
interpolation = translation.value.scan(/\{\{(.*?)\}\}/).flatten
|
33
|
+
|
34
|
+
if interpolation.empty?
|
35
|
+
translation.update_attribute(:value, Perfectline::T9n::GoogleTranslator.translate(translation.value, locale, I18n.default_locale))
|
36
|
+
else
|
37
|
+
placeholders = {}
|
38
|
+
|
39
|
+
# sub out interpolations
|
40
|
+
interpolation.each do |argument|
|
41
|
+
placeholder = Time.now.to_i
|
42
|
+
translation.value.gsub!("{{#{argument}}}", placeholder)
|
43
|
+
placeholders[placeholder] = argument
|
44
|
+
end
|
45
|
+
|
46
|
+
# translate
|
47
|
+
translated = Perfectline::T9n::GoogleTranslator.translate(translation.value, locale, I18n.default_locale)
|
48
|
+
placeholders.each{|value, argument| translated.gsub!(value, "{{#{argument}}}")}
|
49
|
+
|
50
|
+
translation.update_attribute(:value, translated)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def self.needs_human_eyes?(value)
|
61
|
+
return true if value.index('%') # date formats
|
62
|
+
return true if value =~ /^---(.*)\n/ # YAML
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
class GoogleTranslator
|
68
|
+
|
69
|
+
def self.translate(text, to, from='en')
|
70
|
+
|
71
|
+
base = 'http://ajax.googleapis.com/ajax/services/language/translate'
|
72
|
+
|
73
|
+
# assemble query params
|
74
|
+
params = {
|
75
|
+
:langpair => "#{from}|#{to}",
|
76
|
+
:q => text,
|
77
|
+
:v => 1.0
|
78
|
+
}
|
79
|
+
|
80
|
+
query = params.map{ |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join('&')
|
81
|
+
|
82
|
+
# send get request
|
83
|
+
response = Net::HTTP.get_response( URI.parse( "#{base}?#{query}" ) )
|
84
|
+
json = JSON.parse( response.body )
|
85
|
+
|
86
|
+
if json['responseStatus'] == 200
|
87
|
+
json['responseData']['translatedText']
|
88
|
+
else
|
89
|
+
raise StandardError, response['responseDetails']
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/tasks/t9n.rake
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
namespace :translations do
|
2
|
+
|
3
|
+
desc "Clear translations cache"
|
4
|
+
task :clear => :environment do
|
5
|
+
I18n.backend.cache_clear!
|
6
|
+
end
|
7
|
+
|
8
|
+
desc "Create empty translations keys from the default locale into other available locales"
|
9
|
+
task :populate => :environment do
|
10
|
+
Perfectline::T9n::Util.populate
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "Uses google to translate untranslated values. No interpolation"
|
14
|
+
task :google => :environment do
|
15
|
+
Perfectline::T9n::Util.google_translate
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 't9n'
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: t9n
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tanel Suurhans
|
8
|
+
- Tarmo Lehtpuu
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2010-01-29 00:00:00 +02:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: activesupport
|
18
|
+
type: :runtime
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
version:
|
26
|
+
description: A custom AR backend for I18n, translatable model columns and virtual attributes, languages database with integration and some utilities.
|
27
|
+
email:
|
28
|
+
- tanel.suurhans@perfectline.ee
|
29
|
+
- tarmo.lehtpuu@perfectline.ee
|
30
|
+
executables: []
|
31
|
+
|
32
|
+
extensions: []
|
33
|
+
|
34
|
+
extra_rdoc_files:
|
35
|
+
- README.markdown
|
36
|
+
files:
|
37
|
+
- MIT-LICENSE
|
38
|
+
- README.markdown
|
39
|
+
- generators/t9n/t9n_generator.rb
|
40
|
+
- generators/t9n/templates/initializer.rb
|
41
|
+
- generators/t9n/templates/language.rb
|
42
|
+
- generators/t9n/templates/migrations/create_languages.rb
|
43
|
+
- generators/t9n/templates/migrations/create_translations.rb
|
44
|
+
- generators/t9n/templates/translation.rb
|
45
|
+
- init.rb
|
46
|
+
- install.rb
|
47
|
+
- lib/t9n.rb
|
48
|
+
- lib/t9n/active_record/translatable_attribute.rb
|
49
|
+
- lib/t9n/backend/active_record.rb
|
50
|
+
- lib/t9n/backend/exception_handler.rb
|
51
|
+
- lib/t9n/backend/extension.rb
|
52
|
+
- lib/t9n/models/language.rb
|
53
|
+
- lib/t9n/models/translation.rb
|
54
|
+
- lib/t9n/util.rb
|
55
|
+
- lib/tasks/t9n.rake
|
56
|
+
- rails/init.rb
|
57
|
+
has_rdoc: true
|
58
|
+
homepage: http://perfectline.co.uk
|
59
|
+
licenses: []
|
60
|
+
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options:
|
63
|
+
- --charset=UTF-8
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: "0"
|
71
|
+
version:
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: "0"
|
77
|
+
version:
|
78
|
+
requirements: []
|
79
|
+
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 1.3.5
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: Translation package including I18n AR backend, translatable attributes and Language database by PerfectLine
|
85
|
+
test_files: []
|
86
|
+
|