twine 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9c61dcacafc740f0edf24674f331c77c83b1d94b
4
+ data.tar.gz: e45d63d180e8d396194d1f86d408acefb6813886
5
+ SHA512:
6
+ metadata.gz: 05f6f055d5eb781de1173a0556921e286381a0effbe328a8be5d586405fc10b0a25fc2e1c235f58a9ddbd96264f3fd3a1f27bc965386302e9522c1806184b961
7
+ data.tar.gz: ef330b79c5c17f265823e2646b04f30e7a415cf39569148968f5708cee40c7b2108dc9de64f5facfced1d8ec35d6e7a1ed129d3677234ab38f5a6b10126cfb4f
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Twine
2
2
 
3
- Twine is a command line tool for managing your strings and their translations. These strings are all stored in a master text file and then Twine uses this file to import and export strings in a variety of file types, including iOS and Mac OS X `.strings` files, Android `.xml` files, and [jquery-localize][jquerylocalize] `.json` files. This allows individuals and companies to easily share strings across multiple projects, as well as export strings in any format the user wants.
3
+ Twine is a command line tool for managing your strings and their translations. These strings are all stored in a master text file and then Twine uses this file to import and export strings in a variety of file types, including iOS and Mac OS X `.strings` files, Android `.xml` files, gettext `.po` files, and [jquery-localize][jquerylocalize] `.json` files. This allows individuals and companies to easily share strings across multiple projects, as well as export strings in any format the user wants.
4
4
 
5
5
  ## Install
6
6
 
@@ -74,6 +74,7 @@ Twine currently supports the following formats for outputting strings:
74
74
 
75
75
  * [iOS and OS X String Resources][applestrings] (format: apple)
76
76
  * [Android String Resources][androidstrings] (format: android)
77
+ * [Gettext PO Files][gettextpo] (format: gettext)
77
78
  * [jquery-localize Language Files][jquerylocalize] (format: jquery)
78
79
 
79
80
  If you would like to enable twine to create language files in another format, create an appropriate formatter in `lib/twine/formatters`.
@@ -156,9 +157,21 @@ It is easy to incorporate Twine right into your iOS and OS X app build processes
156
157
 
157
158
  Now, whenever you build your application, Xcode will automatically invoke Twine to make sure that your `.strings` files are up-to-date.
158
159
 
160
+ ## Contributors
161
+
162
+ Many thanks to all of the contributors to the Twine project, including:
163
+
164
+ * [Ishitoya Kentaro](https://github.com/kent013)
165
+ * [Kevin Everets](https://github.com/keverets)
166
+ * [Kevin Wood](https://github.com/kwood)
167
+ * [Mohammad Hejazi](https://github.com/MohammadHejazi)
168
+ * [Robert Guo](http://www.robertguo.me/)
169
+
170
+
159
171
  [rubyzip]: http://rubygems.org/gems/rubyzip
160
172
  [git]: http://git-scm.org/
161
173
  [INI]: http://en.wikipedia.org/wiki/INI_file
162
174
  [applestrings]: http://developer.apple.com/documentation/Cocoa/Conceptual/LoadingResources/Strings/Strings.html
163
175
  [androidstrings]: http://developer.android.com/guide/topics/resources/string-resource.html
176
+ [gettextpo]: http://www.gnu.org/savannah-checkouts/gnu/gettext/manual/html_node/PO-Files.html
164
177
  [jquerylocalize]: https://github.com/coderifous/jquery-localize
data/lib/twine/cli.rb CHANGED
@@ -15,7 +15,7 @@ module Twine
15
15
  parser = OptionParser.new do |opts|
16
16
  opts.banner = 'Usage: twine COMMAND STRINGS_FILE [INPUT_OR_OUTPUT_PATH] [--lang LANG1,LANG2...] [--tags TAG1,TAG2,TAG3...] [--format FORMAT]'
17
17
  opts.separator ''
18
- opts.separator 'The purpose of this script is to convert back and forth between multiple data formats, allowing us to treat our strings (and translations) as data stored in a text file. We can then use the data file to create drops for the localization team, consume similar drops returned by the localization team, generate reports on the strings, as well as create formatted string files to ship with your products. Twine currently supports iOS, OS X, Android, and jquery-localize string files.'
18
+ opts.separator 'The purpose of this script is to convert back and forth between multiple data formats, allowing us to treat our strings (and translations) as data stored in a text file. We can then use the data file to create drops for the localization team, consume similar drops returned by the localization team, generate reports on the strings, as well as create formatted string files to ship with your products. Twine currently supports iOS, OS X, Android, gettext, and jquery-localize string files.'
19
19
  opts.separator ''
20
20
  opts.separator 'Commands:'
21
21
  opts.separator ''
@@ -64,6 +64,9 @@ module Twine
64
64
  opts.on('-o', '--output-file OUTPUT_FILE', 'Write the new strings database to this file instead of replacing the original file. This flag is only useful when running the consume-string-file or consume-loc-drop commands.') do |o|
65
65
  @options[:output_path] = o
66
66
  end
67
+ opts.on('-n', '--file-name FILE_NAME', 'When running the generate-all-string-files command, this flag may be used to overwrite the default file name of the format.') do |n|
68
+ @options[:file_name] = n
69
+ end
67
70
  opts.on('-d', '--developer-language LANG', 'When writing the strings data file, set the specified language as the "developer language". In practice, this just means that this language will appear first in the strings data file.') do |d|
68
71
  @options[:developer_language] = d
69
72
  end
@@ -1,10 +1,12 @@
1
1
  require 'twine/formatters/abstract'
2
2
  require 'twine/formatters/android'
3
3
  require 'twine/formatters/apple'
4
+ require 'twine/formatters/flash'
5
+ require 'twine/formatters/gettext'
4
6
  require 'twine/formatters/jquery'
5
7
 
6
8
  module Twine
7
9
  module Formatters
8
- FORMATTERS = [Formatters::Apple, Formatters::Android, Formatters::JQuery]
10
+ FORMATTERS = [Formatters::Apple, Formatters::Android, Formatters::Gettext, Formatters::JQuery, Formatters::Flash]
9
11
  end
10
12
  end
@@ -57,6 +57,9 @@ module Twine
57
57
  # 1) use "s" instead of "@" for substituting strings
58
58
  str.gsub!(/%([0-9\$]*)@/, '%\1s')
59
59
 
60
+ # 1a) escape strings that begin with a lone "@"
61
+ str.sub!(/^@ /, '\\@ ')
62
+
60
63
  # 2) if there is more than one substitution in a string, make sure they are numbered
61
64
  substituteCount = 0
62
65
  startFound = false
@@ -148,10 +151,11 @@ module Twine
148
151
  raise Twine::Error.new("Directory does not exist: #{path}")
149
152
  end
150
153
 
154
+ file_name = @options[:file_name] || default_file_name
151
155
  Dir.foreach(path) do |item|
152
156
  lang = determine_language_given_path(item)
153
157
  if lang
154
- write_file(File.join(path, item, default_file_name), lang)
158
+ write_file(File.join(path, item, file_name), lang)
155
159
  end
156
160
  end
157
161
  end
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
- require 'CGI'
2
+ require 'cgi'
3
3
  require 'rexml/document'
4
4
 
5
5
  module Twine
@@ -74,9 +74,6 @@ module Twine
74
74
  else
75
75
  value = ""
76
76
  end
77
- if @options[:tags]
78
- set_tags_for_key(key, @options[:tags])
79
- end
80
77
  set_translation_for_key(key, lang, value)
81
78
  if comment and comment.length > 0 and !comment.start_with?("SECTION:")
82
79
  set_comment_for_key(key, comment)
@@ -0,0 +1,110 @@
1
+ module Twine
2
+ module Formatters
3
+ class Flash < Abstract
4
+ FORMAT_NAME = 'flash'
5
+ EXTENSION = '.properties'
6
+ DEFAULT_FILE_NAME = 'resources.properties'
7
+
8
+ def self.can_handle_directory?(path)
9
+ return false
10
+ end
11
+
12
+ def default_file_name
13
+ return DEFAULT_FILE_NAME
14
+ end
15
+
16
+ def determine_language_given_path(path)
17
+ return
18
+ end
19
+
20
+ def read_file(path, lang)
21
+ encoding = Twine::Encoding.encoding_for_path(path)
22
+ sep = nil
23
+ if !encoding.respond_to?(:encode)
24
+ # This code is not necessary in 1.9.3 and does not work as it did in 1.8.7.
25
+ if encoding.end_with? 'LE'
26
+ sep = "\x0a\x00"
27
+ elsif encoding.end_with? 'BE'
28
+ sep = "\x00\x0a"
29
+ else
30
+ sep = "\n"
31
+ end
32
+ end
33
+
34
+ if encoding.index('UTF-16')
35
+ mode = "rb:#{encoding}"
36
+ else
37
+ mode = "r:#{encoding}"
38
+ end
39
+
40
+ File.open(path, mode) do |f|
41
+ last_comment = nil
42
+ while line = (sep) ? f.gets(sep) : f.gets
43
+ if encoding.index('UTF-16')
44
+ if line.respond_to? :encode!
45
+ line.encode!('UTF-8')
46
+ else
47
+ require 'iconv'
48
+ line = Iconv.iconv('UTF-8', encoding, line).join
49
+ end
50
+ end
51
+ match = /((?:[^"\\]|\\.)+)\s*=\s*((?:[^"\\]|\\.)*)/.match(line)
52
+ if match
53
+ key = match[1]
54
+ value = match[2]
55
+ value.gsub!(/\{[0-9]\}/, '%@')
56
+ set_translation_for_key(key, lang, value)
57
+ if last_comment
58
+ set_comment_for_key(key, last_comment)
59
+ end
60
+ end
61
+ if @options[:consume_comments]
62
+ match = /#(.*)/.match(line)
63
+ if match
64
+ last_comment = match[1]
65
+ else
66
+ last_comment = nil
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ def write_file(path, lang)
74
+ default_lang = @strings.language_codes[0]
75
+ encoding = @options[:output_encoding] || 'UTF-8'
76
+ File.open(path, "w:#{encoding}") do |f|
77
+ f.puts "## Flash Strings File\n## Generated by Twine #{Twine::VERSION}\n## Language: #{lang}\n"
78
+ @strings.sections.each do |section|
79
+ printed_section = false
80
+ section.rows.each do |row|
81
+ if row.matches_tags?(@options[:tags], @options[:untagged])
82
+ f.puts ''
83
+ if !printed_section
84
+ if section.name && section.name.length > 0
85
+ f.print "## #{section.name} ##\n\n"
86
+ end
87
+ printed_section = true
88
+ end
89
+
90
+ key = row.key
91
+ value = row.translated_string_for_lang(lang, default_lang)
92
+ if value
93
+ placeHolderNumber = -1
94
+ value = value.gsub(/%[d@]/) { placeHolderNumber += 1; '{%d}' % placeHolderNumber }
95
+
96
+ comment = row.comment
97
+ if comment && comment.length > 0
98
+ f.print "# #{comment}\n"
99
+ end
100
+
101
+ f.print "#{key}=#{value}"
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,100 @@
1
+ module Twine
2
+ module Formatters
3
+ class Gettext < Abstract
4
+ FORMAT_NAME = 'gettext'
5
+ EXTENSION = '.po'
6
+ DEFAULT_FILE_NAME = 'strings.po'
7
+
8
+ def self.can_handle_directory?(path)
9
+ Dir.entries(path).any? { |item| /^.+\.po$/.match(item) }
10
+ end
11
+
12
+ def default_file_name
13
+ return DEFAULT_FILE_NAME
14
+ end
15
+
16
+ def determine_language_given_path(path)
17
+ path_arr = path.split(File::SEPARATOR)
18
+ path_arr.each do |segment|
19
+ match = /(..)\.po$/.match(segment)
20
+ if match
21
+ return match[1]
22
+ end
23
+ end
24
+
25
+ return
26
+ end
27
+
28
+ def read_file(path, lang)
29
+ comment_regex = /#.? *"(.*)"$/
30
+ key_regex = /msgctxt *"(.*)"$/
31
+ value_regex = /msgstr *"(.*)"$/m
32
+ File.open(path, 'r:UTF-8') do |f|
33
+ while item = f.gets("\n\n")
34
+ key = nil
35
+ value = nil
36
+ comment = nil
37
+
38
+ for line in item.split(/\r?\n/)
39
+ comment_match = comment_regex.match(line)
40
+ if comment_match
41
+ comment = comment_match[1]
42
+ end
43
+ key_match = key_regex.match(line)
44
+ if key_match
45
+ key = key_match[1].gsub('\\"', '"')
46
+ end
47
+ value_match = value_regex.match(line)
48
+ if value_match
49
+ value = value_match[1].gsub('\\"', '"')
50
+ end
51
+ end
52
+ if key and key.length > 0 and value and value.length > 0
53
+ set_translation_for_key(key, lang, value)
54
+ if comment and comment.length > 0
55
+ set_comment_for_key(key, comment)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ def write_file(path, lang)
63
+ default_lang = @strings.language_codes[0]
64
+ encoding = @options[:output_encoding] || 'UTF-8'
65
+ File.open(path, "w:#{encoding}") do |f|
66
+ f.puts "msgid \"\"\nmsgstr \"\"\n\"Language: #{lang}\\n\"\n\"X-Generator: Twine #{Twine::VERSION}\\n\"\n\n"
67
+ @strings.sections.each do |section|
68
+ printed_section = false
69
+ section.rows.each do |row|
70
+ if row.matches_tags?(@options[:tags], @options[:untagged])
71
+ basetrans = row.translated_string_for_lang(default_lang)
72
+
73
+ if basetrans
74
+ key = row.key
75
+ key = key.gsub('"', '\\\\"')
76
+
77
+ comment = row.comment
78
+ if comment
79
+ comment = comment.gsub('"', '\\\\"')
80
+ end
81
+
82
+ if comment && comment.length > 0
83
+ f.print "#. \"#{comment}\"\n"
84
+ end
85
+
86
+ f.print "msgctxt \"#{key}\"\nmsgid \"#{basetrans}\"\n"
87
+ value = row.translated_string_for_lang(lang)
88
+ if value
89
+ value = value.gsub('"', '\\\\"')
90
+ end
91
+ f.print "msgstr \"#{value}\"\n\n"
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
data/lib/twine/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Twine
2
- VERSION = '0.3.2'
2
+ VERSION = '0.4.0'
3
3
  end
@@ -0,0 +1,16 @@
1
+ msgid ""
2
+ msgstr ""
3
+ "Language: en\n"
4
+ "X-Generator: Twine\n"
5
+
6
+ msgctxt "key1"
7
+ msgid "key1-english"
8
+ msgstr "key1-english"
9
+
10
+ msgctxt "key3"
11
+ msgid "key3-english"
12
+ msgstr ""
13
+
14
+ msgctxt "key5"
15
+ msgid "A new string"
16
+ msgstr "A new string"
@@ -0,0 +1,5 @@
1
+ [[My Strings]]
2
+ [parameterized_string]
3
+ en = The %@ brown fox jumps over the %@ dog %d times.
4
+ [percentage_string]
5
+ en = This product is %d%% off.
@@ -0,0 +1,14 @@
1
+ msgid ""
2
+ msgstr ""
3
+ "Language: en\n"
4
+ "X-Generator: Twine <%= Twine::VERSION %>\n"
5
+
6
+ #. "This is a comment"
7
+ msgctxt "key1"
8
+ msgid "key1-english"
9
+ msgstr "key1-english"
10
+
11
+ msgctxt "key3"
12
+ msgid "key3-english"
13
+ msgstr "key3-english"
14
+
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Android Strings File -->
3
+ <!-- Generated by Twine <%= Twine::VERSION %> -->
4
+ <!-- Language: en -->
5
+ <resources>
6
+ <!-- SECTION: My Strings -->
7
+ <string name="parameterized_string">The %1$s brown fox jumps over the %2$s dog %3$d times.</string>
8
+ <string name="percentage_string">This product is %d%% off.</string>
9
+ </resources>
data/test/twine_test.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'ERB'
1
+ require 'erb'
2
2
  require 'rubygems'
3
3
  require 'test/unit'
4
4
  require 'twine'
@@ -36,6 +36,22 @@ class TwineTest < Test::Unit::TestCase
36
36
  end
37
37
  end
38
38
 
39
+ def test_generate_string_file_5
40
+ Dir.mktmpdir do |dir|
41
+ output_path = File.join(dir, 'en.po')
42
+ Twine::Runner.run(%W(generate-string-file test/fixtures/strings-1.txt #{output_path} -t tag1))
43
+ assert_equal(ERB.new(File.read('test/fixtures/test-output-7.txt')).result, File.read(output_path))
44
+ end
45
+ end
46
+
47
+ def test_generate_string_file_6
48
+ Dir.mktmpdir do |dir|
49
+ output_path = File.join(dir, 'en.xml')
50
+ Twine::Runner.run(%W(generate-string-file test/fixtures/strings-3.txt #{output_path}))
51
+ assert_equal(ERB.new(File.read('test/fixtures/test-output-8.txt')).result, File.read(output_path))
52
+ end
53
+ end
54
+
39
55
  def test_consume_string_file_1
40
56
  Dir.mktmpdir do |dir|
41
57
  output_path = File.join(dir, 'strings.txt')
@@ -60,6 +76,14 @@ class TwineTest < Test::Unit::TestCase
60
76
  end
61
77
  end
62
78
 
79
+ def test_consume_string_file_4
80
+ Dir.mktmpdir do |dir|
81
+ output_path = File.join(dir, 'strings.txt')
82
+ Twine::Runner.run(%W(consume-string-file test/fixtures/strings-1.txt test/fixtures/en-1.po -o #{output_path} -l en -a))
83
+ assert_equal(File.read('test/fixtures/test-output-4.txt'), File.read(output_path))
84
+ end
85
+ end
86
+
63
87
  def test_generate_report_1
64
88
  Twine::Runner.run(%w(generate-report test/fixtures/strings-1.txt))
65
89
  end
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: twine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
5
- prerelease:
4
+ version: 0.4.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Sebastian Celis
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-09-13 00:00:00.000000000 Z
11
+ date: 2013-05-17 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rubyzip
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
@@ -22,7 +20,6 @@ dependencies:
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ~>
28
25
  - !ruby/object:Gem::Version
@@ -30,7 +27,6 @@ dependencies:
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: rake
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ~>
36
32
  - !ruby/object:Gem::Version
@@ -38,13 +34,14 @@ dependencies:
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ~>
44
39
  - !ruby/object:Gem::Version
45
40
  version: 0.9.2
46
- description: ! " Twine is a command line tool for managing your strings and their
47
- translations.\n\n It is geared toward Mac OS X, iOS, and Android developers.\n"
41
+ description: |2
42
+ Twine is a command line tool for managing your strings and their translations.
43
+
44
+ It is geared toward Mac OS X, iOS, and Android developers.
48
45
  email: twine@mobiata.com
49
46
  executables:
50
47
  - twine
@@ -59,6 +56,8 @@ files:
59
56
  - lib/twine/formatters/abstract.rb
60
57
  - lib/twine/formatters/android.rb
61
58
  - lib/twine/formatters/apple.rb
59
+ - lib/twine/formatters/flash.rb
60
+ - lib/twine/formatters/gettext.rb
62
61
  - lib/twine/formatters/jquery.rb
63
62
  - lib/twine/formatters.rb
64
63
  - lib/twine/runner.rb
@@ -67,40 +66,43 @@ files:
67
66
  - lib/twine.rb
68
67
  - bin/twine
69
68
  - test/fixtures/en-1.json
69
+ - test/fixtures/en-1.po
70
70
  - test/fixtures/en-1.strings
71
71
  - test/fixtures/fr-1.xml
72
72
  - test/fixtures/strings-1.txt
73
73
  - test/fixtures/strings-2.txt
74
+ - test/fixtures/strings-3.txt
74
75
  - test/fixtures/test-output-1.txt
75
76
  - test/fixtures/test-output-2.txt
76
77
  - test/fixtures/test-output-3.txt
77
78
  - test/fixtures/test-output-4.txt
78
79
  - test/fixtures/test-output-5.txt
79
80
  - test/fixtures/test-output-6.txt
81
+ - test/fixtures/test-output-7.txt
82
+ - test/fixtures/test-output-8.txt
80
83
  - test/twine_test.rb
81
84
  homepage: https://github.com/mobiata/twine
82
85
  licenses: []
86
+ metadata: {}
83
87
  post_install_message:
84
88
  rdoc_options: []
85
89
  require_paths:
86
90
  - lib
87
91
  required_ruby_version: !ruby/object:Gem::Requirement
88
- none: false
89
92
  requirements:
90
- - - ! '>='
93
+ - - '>='
91
94
  - !ruby/object:Gem::Version
92
95
  version: 1.8.7
93
96
  required_rubygems_version: !ruby/object:Gem::Requirement
94
- none: false
95
97
  requirements:
96
- - - ! '>='
98
+ - - '>='
97
99
  - !ruby/object:Gem::Version
98
100
  version: '0'
99
101
  requirements: []
100
102
  rubyforge_project:
101
- rubygems_version: 1.8.23
103
+ rubygems_version: 2.0.0
102
104
  signing_key:
103
- specification_version: 3
105
+ specification_version: 4
104
106
  summary: Manage strings and their translations for your iOS and Android projects.
105
107
  test_files:
106
108
  - test/twine_test.rb