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,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: []