i18n_po_tools 0.9.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,171 @@
1
+ require 'optparse'
2
+
3
+ module I18nPoTools
4
+ class CLI
5
+ def parse_options(options)
6
+ opt_parser = OptionParser.new do |opt|
7
+ opt.banner = "Usage: i18n-po [OPTIONS]"
8
+ opt.separator ""
9
+ opt.separator "Utils to convert translations from source formats to PO/POT Gettex and vise"
10
+ opt.separator "versa. It allows to separate translations from development of apps."
11
+ opt.separator ""
12
+ opt.separator "Supported input/output formats:"
13
+ opt.separator "* iOS and OS X String Resources"
14
+ opt.separator "* Android String XML"
15
+ opt.separator "* Gettext PO/POT"
16
+ opt.separator "* Rails YAML"
17
+ opt.separator "* Basic flat YAML"
18
+ opt.separator "* CVS for easy exchange with other apps"
19
+ opt.separator ""
20
+ opt.separator "Direct converation between any formats supported."
21
+ opt.separator ""
22
+ opt.separator "Rails YAML and PO supports plural forms of messages."
23
+ opt.separator ""
24
+ opt.separator ""
25
+ opt.separator "Options"
26
+
27
+
28
+ opt.on("-i","--input FILENAME","input filename (default STDIN)") do |input_filename|
29
+ options[:input_filename] = input_filename
30
+ end
31
+ opt.on("-b","--base FILENAME","base language input filename (only for po output mode)") do |base_filename|
32
+ options[:base_filename] = base_filename
33
+ end
34
+ opt.on("-o","--output FILENAME","output filename (default STDOUT)") do |output_filename|
35
+ options[:output_filename] = output_filename
36
+ end
37
+
38
+ opt.on("-f","--from FORMAT", FORMATS, "input file format (default is autodetect by ext)", "(#{FORMATS.join(', ')})") do |input_format|
39
+ options[:input_format] = input_format
40
+ end
41
+
42
+ opt.on("-t","--to FORMAT", FORMATS, "output file format (default is autodetect by ext)", "(#{FORMATS.join(', ')})") do |output_format|
43
+ options[:output_format] = output_format
44
+ end
45
+
46
+ opt.on("-l","--language LANGUAGE","input file language (only for po or rails_yaml output mode)") do |language|
47
+ options[:language] = language
48
+ end
49
+
50
+ opt.on("-h","--help","help") do
51
+ puts opt_parser
52
+ exit(0)
53
+ end
54
+
55
+ opt.separator ""
56
+ opt.separator "Examples:"
57
+ opt.separator "1) First import (generation of PO file) from Rails YAML file:"
58
+ opt.separator "i18n-po --input ~/projects/rails_app/config/locales/devise.en.yml --language en \\"
59
+ opt.separator " --base ~/projects/rails_app/config/locales/devise.ru.yml \\"
60
+ opt.separator " --output ~/projects/translations/rails_app/devise.en.po"
61
+ opt.separator ""
62
+ opt.separator "2) Generation of translation template (POT file) from Rails YAML file:"
63
+ opt.separator "i18n-po --input ~/projects/rails_app/config/locales/devise.ru.yml \\"
64
+ opt.separator " --output ~/projects/translations/rails_app/devise.en.pot"
65
+ opt.separator ""
66
+ opt.separator "3) Generation of Rails YAML file from PO file:"
67
+ opt.separator "i18n-po --input ~/projects/translations/rails_app/devise.en.po --language en \\"
68
+ opt.separator " --output ~/projects/rails_app/config/locales/devise.en.yml"
69
+ opt.separator ""
70
+ opt.separator "4) Translation convertation from iOS to Android format:"
71
+ opt.separator "i18n-po --input ~/projects/ios_app/Localizable.strings \\"
72
+ opt.separator " --output ~/projects/android_app/en/strings.xml"
73
+ opt.separator ""
74
+ opt.separator "Homepage: http://github.com/stepin/i18n-po-tools"
75
+ opt.separator ""
76
+ end
77
+
78
+ opt_parser.parse!
79
+
80
+ #default help
81
+ if options.count == 0
82
+ puts opt_parser
83
+ exit(1)
84
+ end
85
+ end
86
+
87
+ def check_options(options)
88
+ if options[:input_format].blank?
89
+ if options[:input_filename].present?
90
+ ext = File.extname(options[:input_filename])[1..-1].to_sym
91
+ options[:input_format] = EXT_TO_FORMAT[ext]
92
+ if options[:input_format].blank?
93
+ STDERR.puts "ERROR: unknown input format, please define it manually."
94
+ exit(1)
95
+ end
96
+ else
97
+ STDERR.puts "ERROR: input format should be defined for STDIN stream."
98
+ exit(1)
99
+ end
100
+ end
101
+
102
+ if options[:output_format].blank?
103
+ if options[:output_filename].present?
104
+ ext = File.extname(options[:output_filename])[1..-1].to_sym
105
+ options[:output_format] = EXT_TO_FORMAT[ext]
106
+ if options[:output_format].blank?
107
+ STDERR.puts "ERROR: unknown output format, please define it manually."
108
+ exit(1)
109
+ end
110
+ else
111
+ STDERR.puts "ERROR: output format should be defined for STDOUT stream."
112
+ exit(1)
113
+ end
114
+ end
115
+
116
+ options[:input_format] = options[:input_format].to_sym
117
+ options[:output_format] = options[:output_format].to_sym
118
+
119
+ if options[:output_format] == :po
120
+ if options[:base_filename].blank?
121
+ STDERR.puts "ERROR: base filename should be defined for po output mode."
122
+ exit(1)
123
+ end
124
+ if options[:language].blank?
125
+ STDERR.puts "ERROR: language should be defined for po output mode."
126
+ exit(1)
127
+ end
128
+ else
129
+ if options[:base_filename].present?
130
+ STDERR.puts "WARNING: base filename is not used in this mode."
131
+ end
132
+ if options[:output_format] == :rails_yaml
133
+ if options[:language].blank?
134
+ STDERR.puts "ERROR: language should be defined for rails_yaml output mode."
135
+ exit(1)
136
+ end
137
+ else
138
+ if options[:language].present?
139
+ STDERR.puts "WARNING: language is not used in this mode."
140
+ end
141
+ end
142
+ end
143
+
144
+
145
+ if options[:input_format] == options[:output_format]
146
+ STDERR.puts "ERROR: same format, just use copy command."
147
+ exit(1)
148
+ end
149
+ end
150
+
151
+ def self.start
152
+ new.start
153
+ end
154
+
155
+ def start
156
+ options = {}
157
+ parse_options(options)
158
+ check_options(options)
159
+
160
+ reader = I18nPoTools::Formats::Factory.get(options[:input_format])
161
+ writer = I18nPoTools::Formats::Factory.get(options[:output_format])
162
+
163
+ input_data = reader.read_with_options(options[:input_filename], options, :input)
164
+ base_data = nil
165
+ if options[:output_format] == :po
166
+ base_data = reader.read_with_options(options[:base_filename], options, :base)
167
+ end
168
+ writer.write_with_options(input_data, base_data, options)
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,34 @@
1
+ class Hash
2
+
3
+ def implode(divider = '.')
4
+ Hash.implode(self, divider)
5
+ end
6
+
7
+ def explode(divider = '.')
8
+ Hash.explode(self, divider)
9
+ end
10
+
11
+ protected
12
+ def self.implode(hash, divider = '.', recursive_key = "")
13
+ hash.each_with_object({}) do |(k, v), ret|
14
+ key = recursive_key + k.to_s
15
+ if v.is_a? Hash
16
+ ret.merge! implode(v, divider, key + divider)
17
+ else
18
+ ret[key] = v
19
+ end
20
+ end
21
+ end
22
+
23
+ def self.explode(hash, divider = '.')
24
+ hash.each_with_object({}) do |(k, v), ret|
25
+ path = k.split(divider)
26
+ h = ret
27
+ path[0...-1].each do |path_part|
28
+ h[path_part] = {} if h[path_part].nil?
29
+ h = h[path_part]
30
+ end
31
+ h[path.last] = v
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,26 @@
1
+ #
2
+ # Languages names with PO pliral rules.
3
+ # Plural rules and keys for Rails in the plurals.rb file.
4
+ #
5
+ # More data from:
6
+ # http://git.openstreetmap.org/rails.git/blob/e2c81091a02ad7e69165a9e30a89aecd2c8db531:/config/languages.yml
7
+ #
8
+ # PO plural rules:
9
+ # http://localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html
10
+ #
11
+ ru:
12
+ english: Russian
13
+ native: Русский
14
+ po_plural_rule: "nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;"
15
+ en:
16
+ english: English
17
+ native: English
18
+ po_plural_rule: "nplurals=2; plural=(n != 1);"
19
+ pl:
20
+ english: Polish
21
+ native: Polski
22
+ po_plural_rule: "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"
23
+ fr:
24
+ english: French
25
+ native: Français
26
+ po_plural_rule: "nplurals=2; plural=(n > 1);"
@@ -0,0 +1,116 @@
1
+ # encoding: utf-8
2
+ #
3
+ # From:
4
+ # https://raw.githubusercontent.com/svenfuchs/i18n/master/test/test_data/locales/plurals.rb
5
+ #
6
+
7
+ {
8
+ :af => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
9
+ :am => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } },
10
+ :ar => { :i18n => { :plural => { :keys => [:zero, :one, :two, :few, :many, :other], :rule => lambda { |n| n == 0 ? :zero : n == 1 ? :one : n == 2 ? :two : [3, 4, 5, 6, 7, 8, 9, 10].include?(n % 100) ? :few : [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99].include?(n % 100) ? :many : :other } } } },
11
+ :az => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
12
+ :be => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } },
13
+ :bg => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
14
+ :bh => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } },
15
+ :bn => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
16
+ :bo => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
17
+ :bs => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } },
18
+ :ca => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
19
+ :cs => { :i18n => { :plural => { :keys => [:one, :few, :other], :rule => lambda { |n| n == 1 ? :one : [2, 3, 4].include?(n) ? :few : :other } } } },
20
+ :cy => { :i18n => { :plural => { :keys => [:one, :two, :many, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : n == 8 || n == 11 ? :many : :other } } } },
21
+ :da => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
22
+ :de => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
23
+ :dz => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
24
+ :el => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
25
+ :en => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
26
+ :eo => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
27
+ :es => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
28
+ :et => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
29
+ :eu => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
30
+ :fa => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
31
+ :fi => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
32
+ :fil => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } },
33
+ :fo => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
34
+ :fr => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n.between?(0, 2) && n != 2 ? :one : :other } } } },
35
+ :fur => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
36
+ :fy => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
37
+ :ga => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } },
38
+ :gl => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
39
+ :gu => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
40
+ :guw => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } },
41
+ :ha => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
42
+ :he => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
43
+ :hi => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } },
44
+ :hr => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } },
45
+ :hu => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
46
+ :id => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
47
+ :is => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
48
+ :it => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
49
+ :iw => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
50
+ :ja => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
51
+ :jv => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
52
+ :ka => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
53
+ :km => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
54
+ :kn => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
55
+ :ko => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
56
+ :ku => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
57
+ :lb => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
58
+ :ln => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } },
59
+ :lt => { :i18n => { :plural => { :keys => [:one, :few, :other], :rule => lambda { |n| n % 10 == 1 && ![11, 12, 13, 14, 15, 16, 17, 18, 19].include?(n % 100) ? :one : [2, 3, 4, 5, 6, 7, 8, 9].include?(n % 10) && ![11, 12, 13, 14, 15, 16, 17, 18, 19].include?(n % 100) ? :few : :other } } } },
60
+ :lv => { :i18n => { :plural => { :keys => [:zero, :one, :other], :rule => lambda { |n| n == 0 ? :zero : n % 10 == 1 && n % 100 != 11 ? :one : :other } } } },
61
+ :mg => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } },
62
+ :mk => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n % 10 == 1 ? :one : :other } } } },
63
+ :ml => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
64
+ :mn => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
65
+ :mo => { :i18n => { :plural => { :keys => [:one, :few, :other], :rule => lambda { |n| n == 1 ? :one : n == 0 ? :few : :other } } } },
66
+ :mr => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
67
+ :ms => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
68
+ :mt => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n == 1 ? :one : n == 0 || [2, 3, 4, 5, 6, 7, 8, 9, 10].include?(n % 100) ? :few : [11, 12, 13, 14, 15, 16, 17, 18, 19].include?(n % 100) ? :many : :other } } } },
69
+ :my => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
70
+ :nah => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
71
+ :nb => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
72
+ :ne => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
73
+ :nl => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
74
+ :nn => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
75
+ :no => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
76
+ :nso => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } },
77
+ :om => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
78
+ :or => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
79
+ :pa => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
80
+ :pap => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
81
+ :pl => { :i18n => { :plural => { :keys => [:one, :few, :other], :rule => lambda { |n| n == 1 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : :other } } } },
82
+ :ps => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
83
+ :pt => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } },
84
+ :"pt-PT" => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
85
+ :ro => { :i18n => { :plural => { :keys => [:one, :few, :other], :rule => lambda { |n| n == 1 ? :one : n == 0 ? :few : :other } } } },
86
+ :ru => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } },
87
+ :se => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } },
88
+ :sh => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } },
89
+ :sk => { :i18n => { :plural => { :keys => [:one, :few, :other], :rule => lambda { |n| n == 1 ? :one : [2, 3, 4].include?(n) ? :few : :other } } } },
90
+ :sl => { :i18n => { :plural => { :keys => [:one, :two, :few, :other], :rule => lambda { |n| n % 100 == 1 ? :one : n % 100 == 2 ? :two : [3, 4].include?(n % 100) ? :few : :other } } } },
91
+ :sma => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } },
92
+ :smi => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } },
93
+ :smj => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } },
94
+ :smn => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } },
95
+ :sms => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } },
96
+ :so => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
97
+ :sq => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
98
+ :sr => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } },
99
+ :sv => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
100
+ :sw => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
101
+ :ta => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
102
+ :te => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
103
+ :th => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
104
+ :ti => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } },
105
+ :tk => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
106
+ :tl => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } },
107
+ :to => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
108
+ :tr => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
109
+ :uk => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } },
110
+ :ur => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
111
+ :vi => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
112
+ :wa => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } },
113
+ :yo => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
114
+ :zh => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } },
115
+ :zu => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }
116
+ }
@@ -0,0 +1,28 @@
1
+ require 'nokogiri'
2
+
3
+ module I18nPoTools
4
+ module Formats
5
+ class AndroidFormat < BaseFormat
6
+ def read(input_filename)
7
+ content = read_file(input_filename)
8
+ doc = Nokogiri.XML(content, nil, 'UTF-8')
9
+ h = {}
10
+ doc.xpath('//resources/string').each do |message_token|
11
+ h[message_token["name"]] = message_token.inner_html
12
+ end
13
+ h
14
+ end
15
+
16
+ def write(output_filename, data)
17
+ content = "<!-- "+banner_msg+" -->\n"
18
+ content += "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
19
+ content += "<resources>\n\n"
20
+ data.each_pair do |k,v|
21
+ content += " <string name=\""+(k.nil? ? '':k)+"\">"+(v.nil? ? '':Nokogiri::HTML.fragment(v).to_html)+"</string>\n"
22
+ end if data.present?
23
+ content += "</resources>\n"
24
+ write_file(output_filename, content)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,57 @@
1
+ module I18nPoTools
2
+ module Formats
3
+ class BaseFormat
4
+ attr_accessor :options
5
+
6
+ def read_file(input_filename)
7
+ if input_filename.present?
8
+ if file_encoding.index('UTF-16')
9
+ mode = "rb:#{file_encoding}"
10
+ else
11
+ mode = "r:#{file_encoding}"
12
+ end
13
+
14
+ File.open(input_filename, mode, &:read)
15
+ else
16
+ $stdin.read
17
+ end
18
+ end
19
+
20
+ def write_file(output_filename, data)
21
+ if output_filename.present?
22
+ File.open(output_filename, "w:"+file_encoding) do |f|
23
+ f << data
24
+ end
25
+ else
26
+ puts data
27
+ end
28
+ end
29
+
30
+ def write_with_options(data, base_data, options)
31
+ @options = options
32
+ write(options[:output_filename], data)
33
+ end
34
+
35
+ def read_with_options(filename, options, mode)
36
+ @options = options
37
+ read(filename)
38
+ end
39
+
40
+ def read(filename)
41
+ raise "Not Implemented"
42
+ end
43
+
44
+ def write(filename, data)
45
+ raise "Not Implemented"
46
+ end
47
+
48
+ def banner_msg
49
+ "Generated by i18n-po #{I18nPoTools::VERSION} on #{Time.now.strftime("%Y-%m-%d %H:%M:%S")} by #{ENV['USER']} on #{`hostname`.gsub("\n",'')}"
50
+ end
51
+
52
+ def file_encoding
53
+ 'UTF-8'
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,18 @@
1
+ require 'yaml'
2
+
3
+ module I18nPoTools
4
+ module Formats
5
+ class BasicFlatYamlFormat < BaseFormat
6
+ def read(input_filename)
7
+ content = read_file(input_filename)
8
+ flat_hash = YAML.load(content)
9
+ end
10
+
11
+ def write(output_filename, data)
12
+ content = "# "+ banner_msg + "\n"
13
+ content += data.to_yaml
14
+ write_file(output_filename, content)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,31 @@
1
+ require 'csv'
2
+
3
+ module I18nPoTools
4
+ module Formats
5
+ class CsvFormat < BaseFormat
6
+ def read(input_filename)
7
+ content = read_file(input_filename)
8
+ h = {}
9
+ first = true
10
+ CSV.parse(content) do |row|
11
+ if first
12
+ first = false
13
+ else
14
+ h[row[0]] = row[1]
15
+ end
16
+ end
17
+ h
18
+ end
19
+
20
+ def write(output_filename, data)
21
+ content = CSV.generate do |csv|
22
+ csv << ["Key","Translation"]
23
+ data.each_pair do |k,v|
24
+ csv << [k,v]
25
+ end if data.present?
26
+ end
27
+ write_file(output_filename, content)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ require 'i18n_po_tools/utils/ios_strings'
2
+
3
+ module I18nPoTools
4
+ module Formats
5
+ class IosFormat < BaseFormat
6
+ def read(input_filename)
7
+ content = read_file(input_filename)
8
+ IosStrings.strings_to_hash(content)
9
+ end
10
+
11
+ def write(output_filename, data)
12
+ content = IosStrings.hash_to_strings(data,banner_msg)
13
+ write_file(output_filename, content)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,115 @@
1
+ require 'get_pomo'
2
+ require 'i18n_po_tools/utils/po'
3
+ require 'i18n_po_tools/utils/data'
4
+ require 'i18n_po_tools/plural_message'
5
+
6
+ module I18nPoTools
7
+ module Formats
8
+ class PoFormat < BaseFormat
9
+ def initialize(options = {})
10
+ @pot = options.fetch(:pot, false)
11
+ end
12
+
13
+ def read(input_filename)
14
+ content = read_file(input_filename)
15
+
16
+ translations = GetPomo::PoFile.parse(content)
17
+ h = {}
18
+ translations.each do |t|
19
+ t.fix_after_read
20
+
21
+ key = t.msgctxt
22
+ if key.blank?
23
+ if t.msgid.present?
24
+ STDERR.puts "WARNING: #{msgid} can not be imported (empty key in the msgctxt field)"
25
+ end
26
+ next
27
+ end
28
+
29
+ if t.plural?
30
+ if @pot
31
+ h[key] = PluralMessage.new(translations: t.msgid)
32
+ STDERR.puts "WARNING: Plural forms for #{key} can be trancated (only 2 forms for msgid, format limitation)"
33
+ else
34
+ h[key] = PluralMessage.new(translations: t.msgstr)
35
+ end
36
+ else
37
+ if @pot
38
+ h[key] = t.msgid
39
+ else
40
+ h[key] = t.msgstr
41
+ end
42
+ end
43
+ end
44
+ h
45
+ end
46
+
47
+ def write_with_options(data, base_data, options)
48
+ @options = options
49
+
50
+ translations = []
51
+ translations << header_entity(@pot)
52
+
53
+ if @pot
54
+ data.each do |k,v|
55
+ translations << message_entity(k,v,nil)
56
+ end
57
+ else
58
+ base_data.each do |k,v|
59
+ translations << message_entity(k,v,data[k])
60
+ end
61
+ end
62
+
63
+ po = GetPomo::PoFile.new(:translations=>translations)
64
+ content = po.to_text
65
+ write_file(options[:output_filename], content)
66
+ end
67
+
68
+ private
69
+ def message_entity(key,base,translated)
70
+ base = base.presence || key
71
+
72
+ msg = GetPomo::Translation.new
73
+ msg.msgctxt = key
74
+ if base.is_a?(PluralMessage)
75
+ msg.msgid = base.to_a
76
+ if translated.present?
77
+ msg.msgstr = translated.is_a?(PluralMessage) ? translated.to_a : [translated]
78
+ else
79
+ msg.msgstr = nil
80
+ end
81
+ else
82
+ msg.msgid = base
83
+ msg.msgstr = translated
84
+ end
85
+ msg.fix_before_save
86
+ msg
87
+ end
88
+
89
+ def header_entity(pot)
90
+ headers = {}
91
+ headers["X-Generator"] = banner_msg
92
+ headers["Project-Id-Version"] = "PACKAGE VERSION"
93
+ headers["POT-Creation-Date"] = Time.now
94
+ headers["PO-Revision-Date"] = "YEAR-MO-DA HO:MI+ZONE"
95
+ headers["Last-Translator"] = "FULL NAME <EMAIL@ADDRESS>"
96
+ unless pot
97
+ headers["Language"] = options[:language]
98
+ language_data = Data.load_languages[options[:language]]
99
+ if language_data.present?
100
+ plural_rule = language_data["po_plural_rule"]
101
+ headers["Plural-Forms"] = plural_rule if plural_rule.present?
102
+ end
103
+ end
104
+ headers["MIME-Version"] = "1.0"
105
+ headers["Content-Type"] = "text/plain; charset=UTF-8"
106
+ headers["Content-Transfer-Encoding"] = "8bit"
107
+
108
+ header = GetPomo::Translation.new
109
+ header.as_header(headers)
110
+ header.fix_before_save
111
+ header
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,32 @@
1
+ require 'yaml'
2
+
3
+ module I18nPoTools
4
+ module Formats
5
+ #NOTE: Yaml preformatted mode (: | ) do not work
6
+ #TODO: plurals support
7
+ class RailsYamlFormat < BaseFormat
8
+ def read(input_filename)
9
+ content = read_file(input_filename)
10
+ rails_yaml = YAML.load(content)
11
+
12
+ locales = rails_yaml.keys
13
+ if locales.length > 1
14
+ raise "Only one language per file supported"
15
+ end
16
+
17
+ flat_hash = rails_yaml[locales.first].implode
18
+ #TODO: plurals
19
+ flat_hash
20
+ end
21
+
22
+ def write(output_filename, data)
23
+ content = "# "+banner_msg+"\n"
24
+
25
+ data_with_locale = {}
26
+ data_with_locale[options[:language]] = data.explode
27
+ content += data_with_locale.to_yaml
28
+ write_file(output_filename, content)
29
+ end
30
+ end
31
+ end
32
+ end