i18n_po_tools 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,32 @@
1
+ require 'i18n_po_tools/formats/base_format'
2
+ require 'i18n_po_tools/formats/rails_yaml_format'
3
+ require 'i18n_po_tools/formats/basic_flat_yaml_format'
4
+ require 'i18n_po_tools/formats/po_format'
5
+ require 'i18n_po_tools/formats/ios_format'
6
+ require 'i18n_po_tools/formats/android_format'
7
+ require 'i18n_po_tools/formats/csv_format'
8
+
9
+ module I18nPoTools
10
+ module Formats
11
+ class Factory
12
+ def self.get(format)
13
+ case format
14
+ when :rails_yaml
15
+ RailsYamlFormat.new
16
+ when :flat_yaml
17
+ BasicFlatYamlFormat.new
18
+ when :po
19
+ PoFormat.new
20
+ when :pot
21
+ PoFormat.new(:pot => true)
22
+ when :ios
23
+ IosFormat.new
24
+ when :android
25
+ AndroidFormat.new
26
+ when :csv
27
+ CsvFormat.new
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,18 @@
1
+ module I18nPoTools
2
+ class PluralMessage
3
+
4
+ attr_accessor :translations
5
+
6
+ def initialize(options = {})
7
+ @translations = options.fetch(:translations,[])
8
+ end
9
+
10
+ def to_a
11
+ translations
12
+ end
13
+
14
+ def to_s
15
+ translations.first.to_s
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,25 @@
1
+ require 'yaml'
2
+
3
+ module I18nPoTools
4
+ class Data
5
+ def self.load_plurals
6
+ filename = relative_filename('../data/plurals.rb')
7
+ load_config_rb(filename)
8
+ end
9
+
10
+ def self.load_languages
11
+ filename = relative_filename('../data/languages.yml')
12
+ YAML.load_file(filename)
13
+ end
14
+
15
+ private
16
+ def self.relative_filename(filename)
17
+ dirname = File.expand_path(File.dirname(__FILE__))
18
+ File.join(dirname, filename)
19
+ end
20
+
21
+ def self.load_config_rb(filename)
22
+ eval(File.open(filename).read)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,83 @@
1
+ # Format description:
2
+ # https://developer.apple.com/library/mac/documentation/macosx/conceptual/bpinternational/Articles/StringsFiles.html
3
+ #
4
+ # Example:
5
+ # /* Insert Element menu item */
6
+ # "Insert Element" = "Insert Element";
7
+ # "Windows must have at least %d columns and %d rows." =
8
+ # "Les fenêtres doivent être composes au minimum de %d colonnes et %d lignes.";
9
+ # "File %@ not found." = "Le fichier %@ n’existe pas.";
10
+ # "%@ Error! %@ failed!" = "%2$@ blah blah, %1$@ blah!";
11
+ # "File \"%@\" cannot be opened" = " ... ";
12
+ #
13
+ # Localizable.strings is a default filename.
14
+ # It is recommended that you save strings files using the UTF-16 encoding.
15
+ #
16
+ # Use /usr/bin/plutil -lint (part of Mac OS X) to check for syntax errors.
17
+ #
18
+ class IosStrings
19
+ DEFAULT_FILE_NAME = 'Localizable.strings'
20
+ EXTENSION = '.strings'
21
+
22
+ # From https://github.com/mobiata/twine/blob/master/lib/twine/encoding.rb
23
+ def self.encoding_for_path(path)
24
+ File.open(path, 'rb') do |f|
25
+ begin
26
+ a = f.readbyte
27
+ b = f.readbyte
28
+ if (a == 0xfe && b == 0xff)
29
+ return 'UTF-16BE'
30
+ elsif (a == 0xff && b == 0xfe)
31
+ return 'UTF-16LE'
32
+ end
33
+ rescue EOFError
34
+ end
35
+ end
36
+
37
+ 'UTF-8'
38
+ end
39
+
40
+ def self.strings_to_hash(data)
41
+ data = data.encode('UTF-8')
42
+ h = {}
43
+
44
+ #remove comments
45
+ data.gsub!(/\/\*.*\*\//,'') # /* lala */
46
+ data.gsub!(/^\s*\/\/.*/,'') # // lala
47
+
48
+ regexp = /"((?:[^"\\]|\\.)+)"\s*=\s*"((?:[^"\\]|\\.)*)"/
49
+ data.split("\n").each do |line|
50
+ match = regexp.match(line)
51
+ next unless match
52
+
53
+ key = unescape(match[1])
54
+ value = unescape(match[2])
55
+
56
+ h[key] = value
57
+ end
58
+
59
+ h
60
+ end
61
+
62
+ def self.hash_to_strings(h, comment = '')
63
+ content = ""
64
+ content = "/* "+comment.gsub('*/','* /')+" */\n" if comment.present?
65
+
66
+ h.each_pair do |k,v|
67
+ key = escape(k)
68
+ value = escape(v)
69
+ content += %Q{"#{key}" = "#{value}";\n}
70
+ end unless h.nil?
71
+
72
+ content
73
+ end
74
+
75
+ private
76
+ def self.escape(s)
77
+ (s||"").gsub('"','\"')
78
+ end
79
+
80
+ def self.unescape(s)
81
+ (s||"").gsub('\"','"')
82
+ end
83
+ end
@@ -0,0 +1,164 @@
1
+ require 'fileutils'
2
+ require 'yaml'
3
+
4
+ module I18nPoTools
5
+ class LocfileUtils
6
+ attr_accessor :translations_dir,
7
+ :dirname,
8
+ :project_name,
9
+ :template_locales,
10
+ :locales,
11
+ :locales_indexes,
12
+ :translation_files,
13
+ :src_filename_template,
14
+ :pot_filename_path,
15
+ :po_filename_path,
16
+ :verbose
17
+
18
+ def initialize(dirname)
19
+ @translations_dir = ENV["I18N_YML_TRANSLATIONS_DIR"]
20
+ @verbose = true
21
+
22
+ @dirname = File.expand_path(dirname)
23
+ config = YAML.load_file(File.join(dirname, 'Locfile'))
24
+
25
+ @project_name = config["project_name"]
26
+ @template_locales = config["template_locales"]
27
+ @locales = config["locales"]
28
+ @locales_indexes = config["locales_indexes"]
29
+
30
+ @translation_files = config["translation_files"]
31
+ @src_filename_template = config["src_filename_template"]
32
+ @pot_filename_path = config["pot_filename_path"].gsub('%{project_name}', project_name)
33
+ @po_filename_path = config["po_filename_path"].gsub('%{project_name}', project_name)
34
+ end
35
+
36
+ def my_command(cmd)
37
+ puts cmd if verbose
38
+ system(cmd)
39
+ end
40
+
41
+ def create_dirs()
42
+ template_locales.each do |locale|
43
+ name = File.join(translations_dir, pot_filename_path.gsub('%{locale}',locale))
44
+ my_command("mkdir -p #{name}")
45
+ end
46
+ locales.each do |(locale, base_locale)|
47
+ name = File.join(translations_dir, po_filename_path.gsub('%{locale}',locale))
48
+ my_command("mkdir -p #{name}")
49
+ end
50
+ end
51
+
52
+ def generate_pot(locale)
53
+ src_path = File.join(dirname, src_filename_template_with_locale(locale))
54
+ pot_path = File.join(translations_dir, pot_filename_path.gsub('%{locale}',locale))
55
+
56
+ translation_files.each_pair do |src_name, pot_name|
57
+ src_filename = src_path.gsub('%{src_filename}',src_name)
58
+ pot_filename = File.join(pot_path, pot_name+".pot")
59
+
60
+ cmds = []
61
+ cmds << "i18n-po "\
62
+ "--input #{src_filename} "\
63
+ "--output #{pot_filename} "\
64
+ "#{generate_pot_extra(locale, src_filename, pot_filename)}"
65
+ my_command( cmds.join(';') )
66
+ end
67
+ end
68
+
69
+ def merge_pot(locale, base_locale, pot_name)
70
+ pot_filename = File.join(translations_dir, pot_filename_path.gsub('%{locale}',base_locale),pot_name+".pot")
71
+ po_filename = File.join(translations_dir, po_filename_path.gsub('%{locale}',locale),pot_name+".po")
72
+ my_command("msgmerge -U #{po_filename} #{pot_filename}")
73
+ end
74
+
75
+ def merge_pot_all()
76
+ locales.each do |(locale, base_locale)|
77
+ translation_files.each_pair do |src_name, pot_name|
78
+ merge_pot(locale, base_locale, pot_name)
79
+ end
80
+ end
81
+ end
82
+
83
+ def convert_from_po_to_src(locale)
84
+ src_path = File.join(dirname, src_filename_template_with_locale(locale))
85
+ po_path = File.join(translations_dir, po_filename_path.gsub('%{locale}',locale))
86
+
87
+ translation_files.each_pair do |src_name, pot_name|
88
+ src_filename = src_path.gsub('%{src_filename}',src_name)
89
+ po_filename = File.join(po_path, pot_name+".po")
90
+
91
+ cmds = []
92
+ cmds << "i18n-po "\
93
+ "--output #{src_filename} "\
94
+ "--input #{po_filename} "\
95
+ "#{convert_from_po_to_src_extra(locale, src_filename, po_filename)}"
96
+ my_command( cmds.join(';') )
97
+ end
98
+ end
99
+
100
+ def convert_from_src_to_po(locale, base_locale)
101
+ base_path = File.join(dirname, src_filename_template_with_locale(base_locale))
102
+ src_path = File.join(dirname, src_filename_template_with_locale(locale))
103
+ po_path = File.join(translations_dir, po_filename_path.gsub('%{locale}',locale))
104
+
105
+ translation_files.each_pair do |src_name, pot_name|
106
+ base_filename = base_path.gsub('%{src_filename}', src_name)
107
+ src_filename = src_path.gsub('%{src_filename}', src_name)
108
+ po_filename = File.join(po_path, pot_name+".po")
109
+
110
+ cmds = []
111
+ cmds << "i18n-po "\
112
+ "--output #{po_filename} "\
113
+ "--input #{src_filename} "\
114
+ "--language #{locale} "\
115
+ "--base #{base_filename} "\
116
+ "#{convert_from_src_to_po_extra(locale, base_locale, base_filename, src_filename, po_filename)}"
117
+ my_command( cmds.join(';') )
118
+ end
119
+ end
120
+
121
+ def src_filename_template_with_locale(locale)
122
+ src_filename_template.gsub('%{locale}',locale)
123
+ end
124
+
125
+ def generate_pot_extra(locale, src_filename, pot_filename)
126
+ ''
127
+ end
128
+ def convert_from_po_to_src_extra(locale, src_filename, po_filename)
129
+ ''
130
+ end
131
+ def convert_from_src_to_po_extra(locale, base_locale, base_filename, src_filename, po_filename)
132
+ ''
133
+ end
134
+
135
+ def export_messages()
136
+ create_dirs()
137
+ template_locales.each do |locale|
138
+ generate_pot(locale)
139
+ end
140
+
141
+ #merge new strigns to po files
142
+ puts
143
+ puts
144
+ puts "WARNING:"
145
+ puts "Ensure that currently translators do not update your project translations."
146
+ puts
147
+ puts "Press [Enter] key to start export new strings from POT to PO-files..."
148
+ gets
149
+ merge_pot_all()
150
+ end
151
+
152
+ def import_translations()
153
+ locales.each do |(locale, base_locale)|
154
+ convert_from_po_to_src(locale)
155
+ end
156
+ end
157
+
158
+ def export_initial_translations()
159
+ locales.each do |(locale, base_locale)|
160
+ convert_from_src_to_po(locale, base_locale)
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,87 @@
1
+ #
2
+ # Fixes for pomo gem
3
+ #
4
+ module GetPomo
5
+ class Translation
6
+ def add_comment(value)
7
+ @comment += "\n" if @comment.present?
8
+ @comment += value.strip
9
+ end
10
+
11
+ def as_header(headers)
12
+ self.msgctxt = nil
13
+ self.msgid = ""
14
+ self.msgstr = headers.map{|(k,v)| k.to_s+": "+v.to_s+"\\n\n" }.join('')
15
+ end
16
+
17
+ def fix_after_read
18
+ self.msgctxt = unescape(@msgctxt)
19
+
20
+ self.msgid = if @msgid.is_a?(Array)
21
+ @msgid.map do |msgid|
22
+ unescape(msgid)
23
+ end
24
+ else
25
+ unescape(@msgid)
26
+ end
27
+
28
+ self.msgstr = if @msgstr.is_a?(Array)
29
+ @msgstr.map do |msgstr|
30
+ unescape(msgstr)
31
+ end
32
+ else
33
+ unescape(@msgstr)
34
+ end
35
+ end
36
+
37
+ def fix_before_save
38
+ self.msgctxt = escape(@msgctxt)
39
+
40
+ self.msgid = if @msgid.is_a?(Array)
41
+ @msgid.map do |msgid|
42
+ wrap_and_escape(msgid)
43
+ end
44
+ else
45
+ wrap_and_escape(@msgid)
46
+ end
47
+
48
+ self.msgstr = if @msgstr.is_a?(Array)
49
+ @msgstr.map do |msgstr|
50
+ wrap_and_escape(msgstr)
51
+ end
52
+ else
53
+ wrap_and_escape(@msgstr)
54
+ end
55
+ end
56
+
57
+ private
58
+ def escape(str)
59
+ return nil if str.nil?
60
+ return str if header?
61
+ #TODO: better \ escapes?
62
+ str.gsub('"','\"').gsub('\\','\\\\\\')
63
+ end
64
+
65
+ def unescape(str)
66
+ return nil if str.nil?
67
+ str.gsub('\\\\','\\').gsub('\"','"')
68
+ end
69
+
70
+ def wrap_and_escape(str, line_width = 80)
71
+ return nil if str.nil?
72
+
73
+ lines = str.split(/\n|\r\n/).map do |line|
74
+ if line.length > line_width
75
+ line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1 \n")[0...-2]
76
+ else
77
+ line
78
+ end
79
+ end.join("\n").split("\n")
80
+ if lines.count > 1
81
+ "\"\n\""+lines.map{|line| escape(line) }.join("\"\n\"")
82
+ else
83
+ escape(str)
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,3 @@
1
+ module I18nPoTools
2
+ VERSION = "0.9.0"
3
+ end
@@ -0,0 +1,24 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'active_support'
4
+ require 'active_support/core_ext/object/blank'
5
+
6
+ dir = File.expand_path(File.dirname(__FILE__))
7
+ $:.unshift(dir) unless $:.include?(dir)
8
+
9
+ require "i18n_po_tools/version"
10
+ require "i18n_po_tools/core_ext/dotted_hash"
11
+ require "i18n_po_tools/formats"
12
+
13
+ module I18nPoTools
14
+ FORMATS = %w[rails_yaml flat_yaml po pot ios android csv]
15
+
16
+ EXT_TO_FORMAT = {
17
+ :yml => :rails_yaml,
18
+ :po => :po,
19
+ :pot => :pot,
20
+ :strings => :ios,
21
+ :xml => :android,
22
+ :csv => :csv
23
+ }
24
+ end
metadata ADDED
@@ -0,0 +1,185 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: i18n_po_tools
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Igor Stepin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: get_pomo
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.9.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 0.9.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 4.1.6
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 4.1.6
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: gem-release
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: ! 'Utils to convert translations from source formats to PO/POT Gettex
98
+ and vise versa.
99
+
100
+ It allows to separate translations from development of apps.
101
+
102
+
103
+ Supported input/output formats:
104
+
105
+ * iOS and OS X String Resources
106
+
107
+ * Android String XML
108
+
109
+ * Gettext PO/POT
110
+
111
+ * Rails YAML
112
+
113
+ * Basic flat YAML
114
+
115
+ * CVS for easy exchange with other apps
116
+
117
+
118
+ Direct converation between any formats supported.
119
+
120
+
121
+ Rails YAML and PO supports plural forms of messages.
122
+
123
+ '
124
+ email:
125
+ - igor_for_os@stepin.name
126
+ executables:
127
+ - i18n-po
128
+ extensions: []
129
+ extra_rdoc_files: []
130
+ files:
131
+ - .gitignore
132
+ - Design.md
133
+ - Design.ru.md
134
+ - Gemfile
135
+ - LICENSE
136
+ - Rakefile
137
+ - Readme.md
138
+ - Readme.ru.md
139
+ - bin/i18n-po
140
+ - i18n_yml_tools.gemspec
141
+ - lib/i18n_po_tools.rb
142
+ - lib/i18n_po_tools/cli.rb
143
+ - lib/i18n_po_tools/core_ext/dotted_hash.rb
144
+ - lib/i18n_po_tools/data/languages.yml
145
+ - lib/i18n_po_tools/data/plurals.rb
146
+ - lib/i18n_po_tools/formats.rb
147
+ - lib/i18n_po_tools/formats/android_format.rb
148
+ - lib/i18n_po_tools/formats/base_format.rb
149
+ - lib/i18n_po_tools/formats/basic_flat_yaml_format.rb
150
+ - lib/i18n_po_tools/formats/csv_format.rb
151
+ - lib/i18n_po_tools/formats/ios_format.rb
152
+ - lib/i18n_po_tools/formats/po_format.rb
153
+ - lib/i18n_po_tools/formats/rails_yaml_format.rb
154
+ - lib/i18n_po_tools/plural_message.rb
155
+ - lib/i18n_po_tools/utils/data.rb
156
+ - lib/i18n_po_tools/utils/ios_strings.rb
157
+ - lib/i18n_po_tools/utils/locfile.rb
158
+ - lib/i18n_po_tools/utils/po.rb
159
+ - lib/i18n_po_tools/version.rb
160
+ homepage: http://github.com/stepin/i18n_po_tools
161
+ licenses:
162
+ - MIT
163
+ metadata: {}
164
+ post_install_message:
165
+ rdoc_options: []
166
+ require_paths:
167
+ - lib
168
+ required_ruby_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ! '>='
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ required_rubygems_version: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ! '>='
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ requirements: []
179
+ rubyforge_project:
180
+ rubygems_version: 2.1.11
181
+ signing_key:
182
+ specification_version: 4
183
+ summary: Utils to convert translations from source formats to PO/POT Gettex and vise
184
+ versa.
185
+ test_files: []