amatsuda-i18n_generators 0.0.5
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.
- data/MIT-LICENSE +20 -0
- data/README +56 -0
- data/Rakefile +22 -0
- data/generators/i18n/USAGE +13 -0
- data/generators/i18n/i18n_generator.rb +41 -0
- data/generators/i18n/templates/base.yml +0 -0
- data/generators/i18n/templates/i18n_config.rb +5 -0
- data/generators/i18n/templates/models.yml +16 -0
- data/generators/i18n_locales/USAGE +12 -0
- data/generators/i18n_locales/i18n_locales_command.rb +91 -0
- data/generators/i18n_locales/i18n_locales_generator.rb +9 -0
- data/generators/i18n_locales/lib/cldr.rb +125 -0
- data/generators/i18n_locales/lib/yaml.rb +141 -0
- data/generators/i18n_models/USAGE +9 -0
- data/generators/i18n_models/i18n_models_command.rb +48 -0
- data/generators/i18n_models/i18n_models_generator.rb +9 -0
- data/generators/i18n_models/lib/translator.rb +21 -0
- data/spec/cldr_spec.rb +52 -0
- data/spec/data/cldr/ja.html +3112 -0
- data/spec/data/yml/active_record/en-US.yml +54 -0
- data/spec/yaml_spec.rb +39 -0
- metadata +81 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 [name of plugin creator]
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
I18n generator
|
2
|
+
======
|
3
|
+
|
4
|
+
This plugin generates a config file and locale files for Rails 2.2 i18n feature.
|
5
|
+
|
6
|
+
|
7
|
+
Installation
|
8
|
+
=======
|
9
|
+
|
10
|
+
As a gem
|
11
|
+
|
12
|
+
% sudo gem install amatsuda-i18n_generators -s http://gems.github.com
|
13
|
+
|
14
|
+
As a Rails plugin
|
15
|
+
|
16
|
+
% ./script/plugin install git://github.com/amatsuda/i18n_generators.git
|
17
|
+
|
18
|
+
|
19
|
+
Example
|
20
|
+
=======
|
21
|
+
|
22
|
+
% ./script/generate i18n ja-JP (de-AT, pt-BR, etc.)
|
23
|
+
|
24
|
+
This will generate following files.
|
25
|
+
|
26
|
+
config/initializers/i18n_config.rb
|
27
|
+
lib/locale/action_view_ja-JP.yml
|
28
|
+
lib/locale/active_record_ja-JP.yml
|
29
|
+
lib/locale/active_support_ja-JP.yml
|
30
|
+
|
31
|
+
|
32
|
+
Requirement
|
33
|
+
=======
|
34
|
+
|
35
|
+
This plugin requires gettext gem ( http://rubyforge.org/projects/gettext/ )
|
36
|
+
and sufficient speed of the Internet connection.
|
37
|
+
|
38
|
+
|
39
|
+
Caution
|
40
|
+
=======
|
41
|
+
|
42
|
+
Current version of this plugin is very very very very roughly implemented just for experiment.
|
43
|
+
Every time you execute the generate command, the plugin connects to the Unicode CLDR website and scrapes the huge amount of its contents.
|
44
|
+
Please be careful not to harm your network environment and CLDR website by running this plugin too many times.
|
45
|
+
|
46
|
+
|
47
|
+
Todo
|
48
|
+
=======
|
49
|
+
|
50
|
+
* More accurate format tranlation.
|
51
|
+
* Download the XML data file via CLDR website and store in tmp directory.
|
52
|
+
* Merging with the existing locale file.
|
53
|
+
* Currency sign
|
54
|
+
|
55
|
+
|
56
|
+
Copyright (c) 2008 Akira Matsuda, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the i18n_generator plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Generate documentation for the i18n_generator plugin.'
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = 'I18nGenerator'
|
19
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
20
|
+
rdoc.rdoc_files.include('README')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
Description:
|
2
|
+
Generates a config file and locale files and a model/attributes translation file for Rails i18n feature.
|
3
|
+
|
4
|
+
Example:
|
5
|
+
./script/generate i18n locale_name (ja-JP, de-AT, etc.)
|
6
|
+
|
7
|
+
This will create:
|
8
|
+
config/initializers/i18n_config.rb
|
9
|
+
lib/locale/action_view_ja-JP.yml
|
10
|
+
lib/locale/active_record_ja-JP.yml
|
11
|
+
lib/locale/active_support_ja-JP.yml
|
12
|
+
lib/locale/models_ja-JP.yml
|
13
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rails_generator'
|
2
|
+
require 'rails_generator/commands'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'gettext'
|
5
|
+
|
6
|
+
class I18nGenerator < Rails::Generator::NamedBase
|
7
|
+
attr_reader :locale_name, :cldr, :generate_models_only, :generate_locales_only
|
8
|
+
|
9
|
+
def initialize(runtime_args, runtime_options = {})
|
10
|
+
super
|
11
|
+
unless name =~ /[a-zA-Z]{2}[-_][a-zA-Z]{2}/
|
12
|
+
puts 'ERROR: Wrong locale format. Please input in ??-?? format.'
|
13
|
+
exit
|
14
|
+
end
|
15
|
+
@locale_name = "#{name[0..1].downcase}-#{name[3..4].upcase}"
|
16
|
+
GetText.bindtextdomain 'rails'
|
17
|
+
GetText.locale = @locale_name
|
18
|
+
|
19
|
+
@cldr = CldrDocument.new @locale_name
|
20
|
+
end
|
21
|
+
|
22
|
+
def manifest
|
23
|
+
record do |m|
|
24
|
+
m.directory 'lib/locale'
|
25
|
+
unless self.generate_models_only
|
26
|
+
m.template 'i18n:i18n_config.rb', 'config/initializers/i18n_config.rb', :assigns => {:locale_name => @locale_name}
|
27
|
+
m.active_support_yaml
|
28
|
+
m.active_record_yaml
|
29
|
+
m.action_view_yaml
|
30
|
+
end
|
31
|
+
unless self.generate_locales_only
|
32
|
+
m.models_yaml
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
require File.join(File.dirname(__FILE__), '../i18n_locales/i18n_locales_command')
|
39
|
+
require File.join(File.dirname(__FILE__), '../i18n_models/i18n_models_command')
|
40
|
+
Rails::Generator::Commands::Create.send :include, I18nGenerator::Generator::Commands::Create
|
41
|
+
|
File without changes
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<%= locale_name %>:
|
2
|
+
activerecord:
|
3
|
+
models:
|
4
|
+
<% models.each do |model| -%>
|
5
|
+
<%= "#{model.english_name}: #{model.translated_name}" %>
|
6
|
+
<% end -%>
|
7
|
+
attributes:
|
8
|
+
<% models.each do |model| -%>
|
9
|
+
<%= "#{model.english_name}:" %>
|
10
|
+
<% model.content_columns.each do |col| -%>
|
11
|
+
<% unless %w[created_at updated_at].include?(col.name) -%>
|
12
|
+
<%= "#{col.name}: #{col.translated_name}" %>
|
13
|
+
<% end -%>
|
14
|
+
<% end -%>
|
15
|
+
<% end -%>
|
16
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Description:
|
2
|
+
Generates a config file and locale files for Rails i18n feature.
|
3
|
+
|
4
|
+
Example:
|
5
|
+
./script/generate i18n locale_name (ja-JP, de-AT, etc.)
|
6
|
+
|
7
|
+
This will create:
|
8
|
+
config/initializers/i18n_config.rb
|
9
|
+
lib/locale/action_view_ja-JP.yml
|
10
|
+
lib/locale/active_record_ja-JP.yml
|
11
|
+
lib/locale/active_support_ja-JP.yml
|
12
|
+
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'rails_generator'
|
2
|
+
require 'rails_generator/commands'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'gettext'
|
5
|
+
require File.join(File.dirname(__FILE__), 'lib/yaml')
|
6
|
+
require File.join(File.dirname(__FILE__), 'lib/cldr')
|
7
|
+
include I18nLocalesGeneratorModule
|
8
|
+
|
9
|
+
module I18nGenerator::Generator
|
10
|
+
module Commands #:nodoc:
|
11
|
+
module Create
|
12
|
+
def active_support_yaml
|
13
|
+
open_yaml('active_support') do |yaml|
|
14
|
+
yaml[locale_name].descendant_nodes do |node|
|
15
|
+
v = cldr.lookup(node.path)
|
16
|
+
node.value = v if !v.nil? && !(v.is_a?(Array) && v.any? {|e| e.nil?})
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def active_record_yaml
|
22
|
+
open_yaml('active_record') do |yaml|
|
23
|
+
yaml[locale_name]['activerecord']['errors']['messages'].children.each do |node|
|
24
|
+
unless node.value.nil?
|
25
|
+
node.value = transfer_format('%{fn} ' + node.value.gsub('{{count}}', '%d')) do |v|
|
26
|
+
GetText._(v)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def action_view_yaml
|
34
|
+
open_yaml('action_view') do |yaml|
|
35
|
+
yaml[locale_name]['datetime']['distance_in_words'].children.each do |node|
|
36
|
+
if !node.value.nil?
|
37
|
+
node.value = GetText._(node.value)
|
38
|
+
elsif ((children = node.children).size == 2) && (children.map(&:key) == %w[one other])
|
39
|
+
children['one'].value, children['other'].value = translate_one_and_other(children.map(&:value))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
yaml[locale_name]['activerecord']['errors']['template'].children.each do |node|
|
43
|
+
if !node.value.nil?
|
44
|
+
node.value = if node.value == 'There were problems with the following fields:'
|
45
|
+
GetText.n_('There was a problem with the following field:', node.value, 2)
|
46
|
+
else
|
47
|
+
GetText._(node.value)
|
48
|
+
end
|
49
|
+
elsif ((children = node.children).size == 2) && (children.map(&:key) == %w[one other])
|
50
|
+
children['one'].value, children['other'].value = if children['one'].value == '1 error prohibited this {{model}} from being saved'
|
51
|
+
translate_one_and_other(['%{num} error prohibited this {{model}} from being saved', children['other']])
|
52
|
+
else
|
53
|
+
translate_one_and_other(children.map(&:value))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
yaml[locale_name]['number'].descendant_nodes do |node|
|
58
|
+
v = cldr.lookup(node.path)
|
59
|
+
node.value = v if !v.nil? && !(v.is_a?(Array) && v.any? {|e| e.nil?})
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
def open_yaml(filename_base)
|
66
|
+
original_yml = I18n.load_path.detect {|lp| lp =~ /\/lib\/#{filename_base}\/locale\/en-US\.yml$/}
|
67
|
+
doc = YamlDocument.new(original_yml, locale_name)
|
68
|
+
yield doc
|
69
|
+
file('i18n:base.yml', "lib/locale/#{filename_base}_#{locale_name}.yml") do |f|
|
70
|
+
doc.to_s
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def transfer_format(args)
|
75
|
+
vals = if args.is_a?(Array)
|
76
|
+
args.map {|v| v.gsub('{{count}}', '%d').gsub('{{model}}', '%{record}')}
|
77
|
+
else
|
78
|
+
args.gsub('{{count}}', '%d').gsub('{{model}}', '%{record}')
|
79
|
+
end
|
80
|
+
result = yield vals
|
81
|
+
result.gsub(/^%\{fn\}/, '').gsub('%d', '{{count}}').gsub('%{record}', '{{model}}').gsub('%{num}', '{{count}}').strip
|
82
|
+
end
|
83
|
+
|
84
|
+
def translate_one_and_other(values)
|
85
|
+
values = values.map {|v| v.is_a?(Node) ? v.value : v}
|
86
|
+
[transfer_format(values) {|v| GetText.n_(v.first, v.second, 1)}, transfer_format(values) {|v| GetText.n_(v.first, v.second, 2)}]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
@@ -0,0 +1,125 @@
|
|
1
|
+
$KCODE = 'U'
|
2
|
+
|
3
|
+
require 'open-uri'
|
4
|
+
|
5
|
+
module I18nLocalesGeneratorModule
|
6
|
+
class CldrDocument
|
7
|
+
def initialize(locale_name)
|
8
|
+
@locale_name = locale_name
|
9
|
+
@summaries = [load_cldr_data(locale_name.tr('-', '_')), load_cldr_data(locale_name.sub(/-.*/, ''))]
|
10
|
+
end
|
11
|
+
|
12
|
+
def lookup(path)
|
13
|
+
case path.sub(/^\/#{@locale_name}\//, '')
|
14
|
+
when 'date/formats/default'
|
15
|
+
convert_date_pattern search('calendar-gregorian', 'pattern', 'date-medium')
|
16
|
+
when 'date/formats/short'
|
17
|
+
convert_date_pattern search('calendar-gregorian', 'pattern', 'date-short')
|
18
|
+
when 'date/formats/long'
|
19
|
+
convert_date_pattern search('calendar-gregorian', 'pattern', 'date-full')
|
20
|
+
when 'date/day_names'
|
21
|
+
[search('calendar-gregorian', 'day', 'sunday:format-wide'),
|
22
|
+
search('calendar-gregorian', 'day', 'monday:format-wide'),
|
23
|
+
search('calendar-gregorian', 'day', 'tuesday:format-wide'),
|
24
|
+
search('calendar-gregorian', 'day', 'wednesday:format-wide'),
|
25
|
+
search('calendar-gregorian', 'day', 'thursday:format-wide'),
|
26
|
+
search('calendar-gregorian', 'day', 'friday:format-wide'),
|
27
|
+
search('calendar-gregorian', 'day', 'saturday:format-wide')]
|
28
|
+
when 'date/abbr_day_names'
|
29
|
+
[search('calendar-gregorian', 'day', 'sunday:format-abbreviated'),
|
30
|
+
search('calendar-gregorian', 'day', 'monday:format-abbreviated'),
|
31
|
+
search('calendar-gregorian', 'day', 'tuesday:format-abbreviated'),
|
32
|
+
search('calendar-gregorian', 'day', 'wednesday:format-abbreviated'),
|
33
|
+
search('calendar-gregorian', 'day', 'thursday:format-abbreviated'),
|
34
|
+
search('calendar-gregorian', 'day', 'friday:format-abbreviated'),
|
35
|
+
search('calendar-gregorian', 'day', 'saturday:format-abbreviated')]
|
36
|
+
when 'date/month_names'
|
37
|
+
['~',
|
38
|
+
search('calendar-gregorian', 'month', '[01]-format-wide'),
|
39
|
+
search('calendar-gregorian', 'month', '[02]-format-wide'),
|
40
|
+
search('calendar-gregorian', 'month', '[03]-format-wide'),
|
41
|
+
search('calendar-gregorian', 'month', '[04]-format-wide'),
|
42
|
+
search('calendar-gregorian', 'month', '[05]-format-wide'),
|
43
|
+
search('calendar-gregorian', 'month', '[06]-format-wide'),
|
44
|
+
search('calendar-gregorian', 'month', '[07]-format-wide'),
|
45
|
+
search('calendar-gregorian', 'month', '[08]-format-wide'),
|
46
|
+
search('calendar-gregorian', 'month', '[09]-format-wide'),
|
47
|
+
search('calendar-gregorian', 'month', '[10]-format-wide'),
|
48
|
+
search('calendar-gregorian', 'month', '[11]-format-wide'),
|
49
|
+
search('calendar-gregorian', 'month', '[12]-format-wide')]
|
50
|
+
when 'date/abbr_month_names'
|
51
|
+
['~',
|
52
|
+
search('calendar-gregorian', 'month', '[01]-format-abbreviated'),
|
53
|
+
search('calendar-gregorian', 'month', '[02]-format-abbreviated'),
|
54
|
+
search('calendar-gregorian', 'month', '[03]-format-abbreviated'),
|
55
|
+
search('calendar-gregorian', 'month', '[04]-format-abbreviated'),
|
56
|
+
search('calendar-gregorian', 'month', '[05]-format-abbreviated'),
|
57
|
+
search('calendar-gregorian', 'month', '[06]-format-abbreviated'),
|
58
|
+
search('calendar-gregorian', 'month', '[07]-format-abbreviated'),
|
59
|
+
search('calendar-gregorian', 'month', '[08]-format-abbreviated'),
|
60
|
+
search('calendar-gregorian', 'month', '[09]-format-abbreviated'),
|
61
|
+
search('calendar-gregorian', 'month', '[10]-format-abbreviated'),
|
62
|
+
search('calendar-gregorian', 'month', '[11]-format-abbreviated'),
|
63
|
+
search('calendar-gregorian', 'month', '[12]-format-abbreviated')]
|
64
|
+
# when 'date/order'
|
65
|
+
when 'time/formats/default'
|
66
|
+
format = search('calendar-gregorian', 'pattern', 'date+time')
|
67
|
+
date = convert_date_pattern search('calendar-gregorian', 'pattern', 'date-medium')
|
68
|
+
time = convert_date_pattern search('calendar-gregorian', 'pattern', 'time-medium')
|
69
|
+
(format.nil? || date.nil? || time.nil?) ? nil : format.sub('{0}', time).sub('{1}', date)
|
70
|
+
when 'time/formats/short'
|
71
|
+
format = search('calendar-gregorian', 'pattern', 'date+time')
|
72
|
+
date = convert_date_pattern search('calendar-gregorian', 'pattern', 'date-short')
|
73
|
+
time = convert_date_pattern search('calendar-gregorian', 'pattern', 'time-short')
|
74
|
+
(format.nil? || date.nil? || time.nil?) ? nil : format.sub('{0}', time).sub('{1}', date)
|
75
|
+
when 'time/formats/long'
|
76
|
+
format = search('calendar-gregorian', 'pattern', 'date+time')
|
77
|
+
date = convert_date_pattern search('calendar-gregorian', 'pattern', 'date-full')
|
78
|
+
time = convert_date_pattern search('calendar-gregorian', 'pattern', 'time-full')
|
79
|
+
(format.nil? || date.nil? || time.nil?) ? nil : format.sub('{0}', time).sub('{1}', date)
|
80
|
+
when 'time/am'
|
81
|
+
search('calendar-gregorian', 'day-period', 'am')
|
82
|
+
when 'time/pm'
|
83
|
+
search('calendar-gregorian', 'day-period', 'pm')
|
84
|
+
# action_view
|
85
|
+
when 'number/format/separator'
|
86
|
+
search('number', 'symbol', 'decimal')
|
87
|
+
when 'number/format/separator'
|
88
|
+
search('number', 'symbol', 'group')
|
89
|
+
when 'number/currency/format/format'
|
90
|
+
format = search('number', 'currency', 'name-pattern/other')
|
91
|
+
format.nil? ? nil : format.tr(' ', '').sub('{0}', '%u').sub('{1}', '%n')
|
92
|
+
else
|
93
|
+
nil
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
def load_cldr_data(locale_name)
|
99
|
+
cldr = begin
|
100
|
+
OpenURI.open_uri("http://www.unicode.org/cldr/data/charts/summary/#{locale_name}.html").read
|
101
|
+
rescue
|
102
|
+
puts "WARNING: Couldn't find locale data for #{locale_name} on the web."
|
103
|
+
''
|
104
|
+
end
|
105
|
+
cldr.split("\n").grep(/^<tr>/).delete_if {|r| r =~ /^<tr><td>\d*<\/td><td class='[gn]'>names<\/td>/}
|
106
|
+
end
|
107
|
+
|
108
|
+
def search(n1, n2, g)
|
109
|
+
pattern = Regexp.new /<tr><td>\d*<\/td><td class='[ng]'>#{Regexp.quote(n1)}<\/td><td class='[ng]'>#{Regexp.quote(n2)}<\/td><td class='[ng]'>#{Regexp.quote(g)}<\/td>/
|
110
|
+
extract_pattern = /<td class='v'>(?:<span.*?>)?(.*?)(?:<\/span>)?<\/td><td>/
|
111
|
+
_, value = *extract_pattern.match(@summaries[0].grep(pattern).first)
|
112
|
+
return value unless value.nil?
|
113
|
+
_, value = *extract_pattern.match(@summaries[1].grep(pattern).first)
|
114
|
+
value
|
115
|
+
end
|
116
|
+
|
117
|
+
def convert_date_pattern(val)
|
118
|
+
return val if val.nil?
|
119
|
+
val = val.sub('MMMM', '%B').sub('MMM', '%b')
|
120
|
+
val = val.sub('yyyy', '%Y').sub('yy', '%y').sub('MM', '%m').sub('M', '%m').sub(/dd|d/, '%d')
|
121
|
+
val.sub('EEEEE', '%a').sub('EEEE', '%A').sub('H', '%H').sub('mm', '%M').sub('ss', '%S').sub('v', '%Z')
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module I18nLocalesGeneratorModule
|
4
|
+
class YamlDocument
|
5
|
+
def initialize(yml_path, locale_name)
|
6
|
+
@locale_name, @nodes = locale_name, []
|
7
|
+
File.read(yml_path).each_with_index do |line_text, i|
|
8
|
+
n = Node.new(self, i, line_text.chomp)
|
9
|
+
@nodes << (n.key == 'en-US' ? Node.new(self, i, "#{locale_name}:") : n)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def next
|
14
|
+
@current_line += 1
|
15
|
+
return false if @current_line == @nodes.size
|
16
|
+
@nodes[@current_line].is_blank_or_comment? ? self.next : @nodes[@current_line]
|
17
|
+
end
|
18
|
+
|
19
|
+
def prev
|
20
|
+
return false if @current_line == 0
|
21
|
+
@current_line -= 1
|
22
|
+
@nodes[@current_line].is_blank_or_comment? ? self.prev : @nodes[@current_line]
|
23
|
+
end
|
24
|
+
|
25
|
+
def parent_of(child)
|
26
|
+
@current_line = child.line
|
27
|
+
while n = self.prev
|
28
|
+
return n if n.indent_level == child.indent_level - 2
|
29
|
+
end
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def children_of(parent)
|
34
|
+
nodes = Nodes.new
|
35
|
+
@current_line = parent.line
|
36
|
+
while n = self.next
|
37
|
+
if n.indent_level < parent.indent_level + 2
|
38
|
+
break
|
39
|
+
elsif n.indent_level == parent.indent_level + 2
|
40
|
+
nodes << n
|
41
|
+
end
|
42
|
+
end
|
43
|
+
nodes
|
44
|
+
end
|
45
|
+
|
46
|
+
def [](node_name)
|
47
|
+
@current_line = @nodes.detect {|n| (n.indent_level == 0) && (n.key == node_name)}.line
|
48
|
+
@nodes[@current_line]
|
49
|
+
end
|
50
|
+
|
51
|
+
def document
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
def path
|
56
|
+
''
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_s
|
60
|
+
@nodes.inject('') do |ret, n|
|
61
|
+
ret << n.text + "\n"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class Node
|
67
|
+
attr_reader :line, :indent_level
|
68
|
+
|
69
|
+
def initialize(doc, line_index, text)
|
70
|
+
@doc, @line, @text = doc, line_index, text
|
71
|
+
@text =~ /(^\s*)/
|
72
|
+
@indent_level = $1.nil? ? 0 : $1.size
|
73
|
+
@yaml = YAML.load(@text + ' ')
|
74
|
+
end
|
75
|
+
|
76
|
+
def parent
|
77
|
+
@parent ||= @doc.parent_of self
|
78
|
+
end
|
79
|
+
|
80
|
+
def children
|
81
|
+
@children ||= @doc.children_of(self)
|
82
|
+
end
|
83
|
+
|
84
|
+
def [](node_name)
|
85
|
+
children.detect {|c| c.key == node_name}
|
86
|
+
end
|
87
|
+
|
88
|
+
def key
|
89
|
+
@yaml.is_a?(Hash) ? @yaml.keys.first : nil
|
90
|
+
end
|
91
|
+
|
92
|
+
def value
|
93
|
+
@yaml.is_a?(Hash) ? @yaml.values.first : nil
|
94
|
+
end
|
95
|
+
|
96
|
+
def value=(val)
|
97
|
+
if @yaml[self.key] != val
|
98
|
+
@yaml[self.key] = val
|
99
|
+
@changed = true
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def text
|
104
|
+
if @changed
|
105
|
+
v = if self.value.is_a?(Array)
|
106
|
+
"[#{self.value * ', '}]"
|
107
|
+
else
|
108
|
+
%Q["#{self.value}"]
|
109
|
+
end
|
110
|
+
"#{' ' * self.indent_level}#{self.key}: #{v}"
|
111
|
+
else
|
112
|
+
@text
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def changed?
|
117
|
+
@changed
|
118
|
+
end
|
119
|
+
|
120
|
+
def is_blank_or_comment?
|
121
|
+
@text.sub(/#.*$/, '').gsub(/\s/, '').empty?
|
122
|
+
end
|
123
|
+
|
124
|
+
def path
|
125
|
+
@path ||= "#{self.parent.path}/#{self.key}"
|
126
|
+
end
|
127
|
+
|
128
|
+
def descendant_nodes(&block)
|
129
|
+
yield self if self.value
|
130
|
+
self.children.each {|child| child.descendant_nodes(&block)} if self.children
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class Nodes < Array
|
135
|
+
def[](index)
|
136
|
+
return detect {|node| node.key == index} if index.is_a?(String)
|
137
|
+
super
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rails_generator'
|
2
|
+
require 'rails_generator/commands'
|
3
|
+
require File.join(File.dirname(__FILE__), 'lib/translator')
|
4
|
+
include I18nModelsGeneratorModule
|
5
|
+
|
6
|
+
module I18nGenerator::Generator
|
7
|
+
module Commands #:nodoc:
|
8
|
+
module Create
|
9
|
+
def models_yaml
|
10
|
+
models = Dir.chdir("#{RAILS_ROOT}/app/models/") do
|
11
|
+
Dir["**/*.rb"].map {|m| m.sub(/\.rb$/, '').capitalize.constantize}
|
12
|
+
end
|
13
|
+
I18n.locale = locale_name
|
14
|
+
lang = locale_name.sub(/-.*$/, '')
|
15
|
+
models.each do |model|
|
16
|
+
model_name = model.class_name.underscore
|
17
|
+
registered_t_name = I18n.t("activerecord.models.#{model_name}", :default => model_name)
|
18
|
+
|
19
|
+
model.class_eval <<-END
|
20
|
+
def self.english_name
|
21
|
+
"#{model_name}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.translated_name
|
25
|
+
"#{registered_t_name != model_name ? registered_t_name : Translator.translate(model_name, lang)}"
|
26
|
+
end
|
27
|
+
END
|
28
|
+
model.content_columns.each do |col|
|
29
|
+
next if %w[created_at updated_at].include? col.name
|
30
|
+
registered_t_name = I18n.t("activerecord.attributes.#{model_name}.#{col.name}", :default => col.name)
|
31
|
+
col.class_eval <<-END
|
32
|
+
def translated_name
|
33
|
+
"#{registered_t_name != col.name ? registered_t_name : Translator.translate(col.name, lang)}"
|
34
|
+
end
|
35
|
+
END
|
36
|
+
end
|
37
|
+
end
|
38
|
+
generate_yaml(locale_name, models)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
def generate_yaml(locale_name, models)
|
43
|
+
template 'i18n:models.yml', "lib/locale/models_#{locale_name}.yml", :assigns => {:locale_name => locale_name, :models => models}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
module I18nModelsGeneratorModule
|
4
|
+
class Translator
|
5
|
+
#TODO
|
6
|
+
def self.translatable?(lang)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.translate(word, lang)
|
10
|
+
begin
|
11
|
+
json = OpenURI.open_uri("http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=#{word}&langpair=en%7C#{lang}").read
|
12
|
+
result = ActiveSupport::JSON.decode(json)
|
13
|
+
return result['responseData']['translatedText'] if result['responseStatus'] == 200
|
14
|
+
rescue e
|
15
|
+
puts %Q[failed to translate "#{word}" into "#{lang}" language.]
|
16
|
+
word
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|