twine 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -70,7 +70,7 @@ Whitepace in this file is mostly ignored. If you absolutely need to put spaces a
70
70
 
71
71
  ## Usage
72
72
 
73
- Usage: twine COMMAND STRINGS_FILE [INPUT_OR_OUTPUT_PATH] [--lang LANG1,LANG2...] [--tag TAG1,TAG2,TAG3...] [--format FORMAT]
73
+ Usage: twine COMMAND STRINGS_FILE [INPUT_OR_OUTPUT_PATH] [--lang LANG1,LANG2...] [--tags TAG1,TAG2,TAG3...] [--format FORMAT]
74
74
 
75
75
  ### Commands
76
76
 
@@ -78,15 +78,15 @@ Whitepace in this file is mostly ignored. If you absolutely need to put spaces a
78
78
 
79
79
  This command creates an Apple or Android strings file from the master strings data file.
80
80
 
81
- $ twine generate-string-file /path/to/strings.txt values-ja.xml --tag common,app1
82
- $ twine generate-string-file /path/to/strings.txt Localizable.strings --lang ja --tag mytag
81
+ $ twine generate-string-file /path/to/strings.txt values-ja.xml --tags common,app1
82
+ $ twine generate-string-file /path/to/strings.txt Localizable.strings --lang ja --tags mytag
83
83
  $ twine generate-string-file /path/to/strings.txt all-english.strings --lang en
84
84
 
85
85
  #### `generate-all-string-files`
86
86
 
87
87
  This command is a convenient way to call `generate-string-file` multiple times. It uses standard Mac OS X, iOS, and Android conventions to figure out exactly which files to create given a parent directory. For example, if you point it to a parent directory containing `en.lproj`, `fr.lproj`, and `ja.lproj` subdirectories, Twine will create a `Localizable.strings` file of the appropriate language in each of them. This is often the command you will want to execute during the build phase of your project.
88
88
 
89
- $ twine generate-all-string-files /path/to/strings.txt /path/to/project/locales/directory --tag common,app1
89
+ $ twine generate-all-string-files /path/to/strings.txt /path/to/project/locales/directory --tags common,app1
90
90
 
91
91
  #### `consume-string-file`
92
92
 
@@ -101,7 +101,7 @@ This command slurps all of the strings from a `.strings` or `.xml` file and inco
101
101
  This command is a convenient way to generate a zip file containing files created by the `generate-string-file` command. It is often used for creating a single zip containing a large number of strings in all languages which you can then hand off to your translation team.
102
102
 
103
103
  $ twine generate-loc-drop /path/to/strings.txt LocDrop1.zip
104
- $ twine generate-loc-drop /path/to/strings.txt LocDrop2.zip --lang en,fr,ja,ko --tag common,app1
104
+ $ twine generate-loc-drop /path/to/strings.txt LocDrop2.zip --lang en,fr,ja,ko --tags common,app1
105
105
 
106
106
  #### `consume-loc-drop`
107
107
 
data/bin/twine CHANGED
@@ -1,3 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'twine'
3
- Twine::Runner.run(ARGV)
3
+ begin
4
+ Twine::Runner.run(ARGV)
5
+ rescue Twine::Error => e
6
+ STDERR.puts e.message
7
+ abort
8
+ end
@@ -12,8 +12,8 @@ module Twine
12
12
  end
13
13
 
14
14
  def parse_args
15
- parser = OptionParser.new(@args) do |opts|
16
- opts.banner = 'Usage: twine COMMAND STRINGS_FILE [INPUT_OR_OUTPUT_PATH] [--lang LANG1,LANG2...] [--tag TAG1,TAG2,TAG3...] [--format FORMAT]'
15
+ parser = OptionParser.new do |opts|
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
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 iOS and Android string files to ship with our products.'
19
19
  opts.separator ''
@@ -36,7 +36,7 @@ module Twine
36
36
  opts.on('-l', '--lang LANGUAGES', Array, 'The language code(s) to use for the specified action.') do |langs|
37
37
  @options[:languages] = langs
38
38
  end
39
- opts.on('-t', '--tag TAGS', Array, 'The tag(s) to use for the specified action. Only strings with that tag will be processed.') do |tags|
39
+ opts.on('-t', '--tags TAGS', Array, 'The tag(s) to use for the specified action. Only strings with that tag will be processed.') do |tags|
40
40
  @options[:tags] = tags
41
41
  end
42
42
  opts.on('-f', '--format FORMAT', 'The file format to read or write (iOS, Android). Additional formatters can be placed in the formats/ directory.') do |format|
@@ -49,10 +49,16 @@ module Twine
49
49
  end
50
50
  end
51
51
  if !found_format
52
- puts "Invalid format: #{format}"
52
+ STDERR.puts "Invalid format: #{format}"
53
53
  end
54
54
  @options[:format] = lformat
55
55
  end
56
+ opts.on('-a', '--all', 'Normally, when consuming a string file, Twine will ignore any string keys that do not exist in your master file. This flag will force those missing strings to be added to your master file.') do |a|
57
+ @options[:consume_all] = true
58
+ end
59
+ 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|
60
+ @options[:output_path] = o
61
+ end
56
62
  opts.on('-h', '--help', 'Show this message.') do |h|
57
63
  puts opts.help
58
64
  exit
@@ -64,14 +70,14 @@ module Twine
64
70
  opts.separator ''
65
71
  opts.separator 'Examples:'
66
72
  opts.separator ''
67
- opts.separator '> twine generate-string-file strings.txt ko.xml --tag FT'
68
- opts.separator '> twine generate-all-string-files strings.txt Resources/Locales/ --tag FT,FB'
73
+ opts.separator '> twine generate-string-file strings.txt ko.xml --tags FT'
74
+ opts.separator '> twine generate-all-string-files strings.txt Resources/Locales/ --tags FT,FB'
69
75
  opts.separator '> twine consume-string-file strings.txt ja.strings'
70
- opts.separator '> twine generate-loc-drop strings.txt LocDrop5.zip --tag FT,FB --format android --lang de,en,en-GB,ja,ko'
76
+ opts.separator '> twine generate-loc-drop strings.txt LocDrop5.zip --tags FT,FB --format android --lang de,en,en-GB,ja,ko'
71
77
  opts.separator '> twine consume-loc-drop strings.txt LocDrop5.zip'
72
78
  opts.separator '> twine generate-report strings.txt'
73
79
  end
74
- parser.parse!
80
+ parser.parse! @args
75
81
 
76
82
  if @args.length == 0
77
83
  puts parser.help
@@ -81,13 +87,11 @@ module Twine
81
87
  @options[:command] = @args[0]
82
88
 
83
89
  if !VALID_COMMANDS.include? @options[:command]
84
- puts "Invalid command: #{@options[:command]}"
85
- exit
90
+ raise Twine::Error.new "Invalid command: #{@options[:command]}"
86
91
  end
87
92
 
88
93
  if @args.length == 1
89
- puts 'You must specify your strings file.'
90
- exit
94
+ raise Twine::Error.new 'You must specify your strings file.'
91
95
  end
92
96
 
93
97
  @options[:strings_file] = @args[1]
@@ -97,68 +101,54 @@ module Twine
97
101
  if @args.length == 3
98
102
  @options[:output_path] = @args[2]
99
103
  elsif @args.length > 3
100
- puts "Unknown argument: #{@args[3]}"
101
- exit
104
+ raise Twine::Error.new "Unknown argument: #{@args[3]}"
102
105
  else
103
- puts 'Not enough arguments.'
104
- exit
106
+ raise Twine::Error.new 'Not enough arguments.'
105
107
  end
106
108
  if @options[:languages] and @options[:languages].length > 1
107
- puts 'Please only specify a single language for the generate-string-file command.'
108
- exit
109
+ raise Twine::Error.new 'Please only specify a single language for the generate-string-file command.'
109
110
  end
110
111
  when 'generate-all-string-files'
111
112
  if ARGV.length == 3
112
113
  @options[:output_path] = @args[2]
113
114
  elsif @args.length > 3
114
- puts "Unknown argument: #{@args[3]}"
115
- exit
115
+ raise Twine::Error.new "Unknown argument: #{@args[3]}"
116
116
  else
117
- puts 'Not enough arguments.'
118
- exit
117
+ raise Twine::Error.new 'Not enough arguments.'
119
118
  end
120
119
  when 'consume-string-file'
121
120
  if @args.length == 3
122
121
  @options[:input_path] = @args[2]
123
122
  elsif @args.length > 3
124
- puts "Unknown argument: #{@args[3]}"
125
- exit
123
+ raise Twine::Error.new "Unknown argument: #{@args[3]}"
126
124
  else
127
- puts 'Not enough arguments.'
128
- exit
125
+ raise Twine::Error.new 'Not enough arguments.'
129
126
  end
130
127
  if @options[:languages] and @options[:languages].length > 1
131
- puts 'Please only specify a single language for the consume-string-file command.'
132
- exit
128
+ raise Twine::Error.new 'Please only specify a single language for the consume-string-file command.'
133
129
  end
134
130
  when 'generate-loc-drop'
135
131
  if @args.length == 3
136
132
  @options[:output_path] = @args[2]
137
133
  elsif @args.length > 3
138
- puts "Unknown argument: #{@args[3]}"
139
- exit
134
+ raise Twine::Error.new "Unknown argument: #{@args[3]}"
140
135
  else
141
- puts 'Not enough arguments.'
142
- exit
136
+ raise Twine::Error.new 'Not enough arguments.'
143
137
  end
144
138
  if !@options[:format]
145
- puts 'You must specify a format.'
146
- exit
139
+ raise Twine::Error.new 'You must specify a format.'
147
140
  end
148
141
  when 'consume-loc-drop'
149
142
  if @args.length == 3
150
143
  @options[:input_path] = @args[2]
151
144
  elsif @args.length > 3
152
- puts "Unknown argument: #{@args[3]}"
153
- exit
145
+ raise Twine::Error.new "Unknown argument: #{@args[3]}"
154
146
  else
155
- puts 'Not enough arguments.'
156
- exit
147
+ raise Twine::Error.new 'Not enough arguments.'
157
148
  end
158
149
  when 'generate-report'
159
150
  if @args.length > 2
160
- puts "Unknown argument: #{@args[2]}"
161
- exit
151
+ raise Twine::Error.new "Unknown argument: #{@args[2]}"
162
152
  end
163
153
  end
164
154
  end
@@ -1,10 +1,41 @@
1
1
  module Twine
2
2
  module Formatters
3
3
  class Abstract
4
+ attr_accessor :strings
5
+ attr_accessor :options
6
+
4
7
  def self.can_handle_directory?(path)
5
8
  return false
6
9
  end
7
10
 
11
+ def initialize(strings, options)
12
+ @strings = strings
13
+ @options = options
14
+ end
15
+
16
+ def set_translation_for_key(key, lang, value)
17
+ if @strings.strings_map.include?(key)
18
+ @strings.strings_map[key].translations[lang] = value
19
+ elsif @options[:consume_all]
20
+ STDERR.puts "Adding new string '#{key}' to strings data file."
21
+ arr = @strings.sections.select { |s| s.name == 'Uncategorized' }
22
+ current_section = arr ? arr[0] : nil
23
+ if !current_section
24
+ current_section = StringsSection.new('Uncategorized')
25
+ @strings.sections.insert(0, current_section)
26
+ end
27
+ current_row = StringsRow.new(key)
28
+ current_section.rows << current_row
29
+ @strings.strings_map[key] = current_row
30
+ @strings.strings_map[key].translations[lang] = value
31
+ else
32
+ STDERR.puts "Warning: '#{key}' not found in strings data file."
33
+ end
34
+ if !@strings.language_codes.include?(lang)
35
+ @strings.add_language_code(lang)
36
+ end
37
+ end
38
+
8
39
  def default_file_name
9
40
  raise NotImplementedError.new("You must implement default_file_name in your formatter class.")
10
41
  end
@@ -13,15 +44,15 @@ module Twine
13
44
  raise NotImplementedError.new("You must implement determine_language_given_path in your formatter class.")
14
45
  end
15
46
 
16
- def read_file(path, lang, strings)
47
+ def read_file(path, lang)
17
48
  raise NotImplementedError.new("You must implement read_file in your formatter class.")
18
49
  end
19
50
 
20
- def write_file(path, lang, tags, strings)
51
+ def write_file(path, lang)
21
52
  raise NotImplementedError.new("You must implement write_file in your formatter class.")
22
53
  end
23
54
 
24
- def write_all_files(path, tags, strings)
55
+ def write_all_files(path)
25
56
  if !File.directory?(path)
26
57
  raise Twine::Error.new("Directory does not exist: #{path}")
27
58
  end
@@ -29,29 +60,9 @@ module Twine
29
60
  Dir.foreach(path) do |item|
30
61
  lang = determine_language_given_path(item)
31
62
  if lang
32
- write_file(File.join(path, item, default_file_name), lang, tags, strings)
33
- end
34
- end
35
- end
36
-
37
- def row_matches_tags?(row, tags)
38
- if tags == nil || tags.length == 0
39
- return true
40
- end
41
-
42
- if tags != nil && row.tags != nil
43
- tags.each do |tag|
44
- if row.tags.include? tag
45
- return true
46
- end
63
+ write_file(File.join(path, item, default_file_name), lang)
47
64
  end
48
65
  end
49
-
50
- return false
51
- end
52
-
53
- def translated_string_for_row_and_lang(row, lang, default_lang)
54
- row.translations[lang] || row.translations[default_lang]
55
66
  end
56
67
  end
57
68
  end
@@ -21,7 +21,7 @@ module Twine
21
21
  def determine_language_given_path(path)
22
22
  path_arr = path.split(File::SEPARATOR)
23
23
  path_arr.each do |segment|
24
- match = /^values-(.*)$/.match(path_arr)
24
+ match = /^values-(.*)$/.match(segment)
25
25
  if match
26
26
  lang = match[1]
27
27
  lang.sub!('-r', '-')
@@ -32,33 +32,31 @@ module Twine
32
32
  return
33
33
  end
34
34
 
35
- def read_file(path, lang, strings)
35
+ def read_file(path, lang)
36
36
  File.open(path, 'r:UTF-8') do |f|
37
+ current_section = nil
37
38
  doc = REXML::Document.new(f)
38
39
  doc.elements.each('resources/string') do |ele|
39
40
  key = ele.attributes["name"]
40
- if strings.strings_map.include? key
41
- value = ele.text
42
- value.gsub!('\\\'', '\'')
43
- value.gsub!('%s', '%@')
44
- strings.strings_map[key].translations[lang] = value
45
- else
46
- puts "#{key} not found in strings data file."
47
- end
41
+ value = ele.text || ''
42
+ value.gsub!('\\\'', '\'')
43
+ value.gsub!(/\n/, '')
44
+ value.gsub!('%s', '%@')
45
+ set_translation_for_key(key, lang, value)
48
46
  end
49
47
  end
50
48
  end
51
49
 
52
- def write_file(path, lang, tags, strings)
53
- default_lang = strings.language_codes[0]
50
+ def write_file(path, lang)
51
+ default_lang = @strings.language_codes[0]
54
52
  File.open(path, 'w:UTF-8') do |f|
55
53
  f.puts "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Android Strings File -->\n<!-- Generated by Twine -->\n<!-- Language: #{lang} -->"
56
54
  f.write '<resources>'
57
- strings.sections.each do |section|
55
+ @strings.sections.each do |section|
58
56
  printed_section = false
59
57
  section.rows.each do |row|
60
- if row_matches_tags?(row, tags)
61
- unless printed_section
58
+ if row.matches_tags?(options[:tags])
59
+ if !printed_section
62
60
  f.puts ''
63
61
  if section.name && section.name.length > 0
64
62
  section_name = section.name.gsub('--', '—')
@@ -70,7 +68,7 @@ module Twine
70
68
  key = row.key
71
69
  key = CGI.escapeHTML(key)
72
70
 
73
- value = translated_string_for_row_and_lang(row, lang, default_lang)
71
+ value = row.translated_string_for_lang(lang, default_lang)
74
72
  value.gsub!('\'', '\\\\\'')
75
73
  value.gsub!('%@', '%s')
76
74
  value = CGI.escapeHTML(value)
@@ -25,34 +25,30 @@ module Twine
25
25
  return
26
26
  end
27
27
 
28
- def read_file(path, lang, strings)
28
+ def read_file(path, lang)
29
29
  File.open(path, 'r:UTF-8') do |f|
30
30
  while line = f.gets
31
- match = /"((?:[^"\\]|\\.)+)"\s*=\s*"((?:[^"\\]|\\.)*)/.match(line)
31
+ match = /"((?:[^"\\]|\\.)+)"\s*=\s*"((?:[^"\\]|\\.)*)"/.match(line)
32
32
  if match
33
33
  key = match[1]
34
34
  key.gsub!('\\"', '"')
35
- if strings.strings_map.include? key
36
- value = match[2]
37
- value.gsub!('\\"', '"')
38
- strings.strings_map[key].translations[lang] = value
39
- else
40
- puts "#{key} not found in strings data file."
41
- end
35
+ value = match[2]
36
+ value.gsub!('\\"', '"')
37
+ set_translation_for_key(key, lang, value)
42
38
  end
43
39
  end
44
40
  end
45
41
  end
46
42
 
47
- def write_file(path, lang, tags, strings)
48
- default_lang = strings.language_codes[0]
43
+ def write_file(path, lang)
44
+ default_lang = @strings.language_codes[0]
49
45
  File.open(path, 'w:UTF-8') do |f|
50
46
  f.puts "/**\n * iOS Strings File\n * Generated by Twine\n * Language: #{lang}\n */"
51
- strings.sections.each do |section|
47
+ @strings.sections.each do |section|
52
48
  printed_section = false
53
49
  section.rows.each do |row|
54
- if row_matches_tags?(row, tags)
55
- unless printed_section
50
+ if row.matches_tags?(options[:tags])
51
+ if !printed_section
56
52
  f.puts ''
57
53
  if section.name && section.name.length > 0
58
54
  f.puts "/* #{section.name} */"
@@ -63,7 +59,7 @@ module Twine
63
59
  key = row.key
64
60
  key = key.gsub('"', '\\\\"')
65
61
 
66
- value = translated_string_for_row_and_lang(row, lang, default_lang)
62
+ value = row.translated_string_for_lang(lang, default_lang)
67
63
  value = value.gsub('"', '\\\\"')
68
64
 
69
65
  comment = row.comment
@@ -16,13 +16,8 @@ module Twine
16
16
  def run
17
17
  # Parse all CLI arguments.
18
18
  CLI::parse_args(@args, @options)
19
-
20
- begin
21
- read_strings_data
22
- execute_command
23
- rescue Twine::Error => e
24
- puts e.message
25
- end
19
+ read_strings_data
20
+ execute_command
26
21
  end
27
22
 
28
23
  def read_strings_data
@@ -53,7 +48,7 @@ module Twine
53
48
  lang = @options[:languages][0]
54
49
  end
55
50
 
56
- read_write_string_file(@options[:output_path], false, lang, @options[:format], @options[:tags])
51
+ read_write_string_file(@options[:output_path], false, lang)
57
52
  end
58
53
 
59
54
  def generate_all_string_files
@@ -71,7 +66,7 @@ module Twine
71
66
 
72
67
  formatter = formatter_for_format(format)
73
68
 
74
- formatter.write_all_files(@options[:output_path], @options[:tags], @strings)
69
+ formatter.write_all_files(@options[:output_path])
75
70
  end
76
71
 
77
72
  def consume_string_file
@@ -80,15 +75,17 @@ module Twine
80
75
  lang = @options[:languages][0]
81
76
  end
82
77
 
83
- read_write_string_file(@options[:input_path], true, lang, @options[:format], nil)
84
- @strings.write(@options[:strings_file])
78
+ read_write_string_file(@options[:input_path], true, lang)
79
+ output_path = @options[:output_path] || @options[:strings_file]
80
+ @strings.write(output_path)
85
81
  end
86
82
 
87
- def read_write_string_file(path, is_read, lang, format, tags)
83
+ def read_write_string_file(path, is_read, lang)
88
84
  if is_read && !File.file?(path)
89
85
  raise Twine::Error.new("File does not exist: #{path}")
90
86
  end
91
87
 
88
+ format = @options[:format]
92
89
  if !format
93
90
  format = determine_format_given_path(path)
94
91
  end
@@ -108,10 +105,14 @@ module Twine
108
105
  raise Twine::Error.new "Unable to determine language for #{path}"
109
106
  end
110
107
 
108
+ if !@strings.language_codes.include? lang
109
+ @strings.language_codes << lang
110
+ end
111
+
111
112
  if is_read
112
- formatter.read_file(path, lang, @strings)
113
+ formatter.read_file(path, lang)
113
114
  else
114
- formatter.write_file(path, lang, tags, @strings)
115
+ formatter.write_file(path, lang)
115
116
  end
116
117
  end
117
118
 
@@ -162,13 +163,14 @@ module Twine
162
163
  real_path = File.join(dir, entry.name)
163
164
  FileUtils.mkdir_p(File.dirname(real_path))
164
165
  zipfile.extract(entry.name, real_path)
165
- read_write_string_file(real_path, true, nil, nil, @options[:tags])
166
+ read_write_string_file(real_path, true, nil)
166
167
  end
167
168
  end
168
169
  end
169
170
  end
170
-
171
- @strings.write @options[:strings_file]
171
+
172
+ output_path = @options[:output_path] || @options[:strings_file]
173
+ @strings.write(output_path)
172
174
  end
173
175
 
174
176
  def generate_report
@@ -255,7 +257,7 @@ module Twine
255
257
  def formatter_for_format(format)
256
258
  Formatters::FORMATTERS.each do |formatter|
257
259
  if formatter::FORMAT_NAME == format
258
- return formatter.new
260
+ return formatter.new(@strings, @options)
259
261
  end
260
262
  end
261
263
 
@@ -21,6 +21,28 @@ module Twine
21
21
  @tags = nil
22
22
  @translations = {}
23
23
  end
24
+
25
+ def matches_tags?(tags)
26
+ if @tags == nil || @tags.length == 0
27
+ # This row has no tags. Never match
28
+ return false
29
+ elsif tags == nil || tags.length == 0
30
+ # The user did not specify any tags. Everything passes.
31
+ return true
32
+ else
33
+ tags.each do |tag|
34
+ if @tags.include? tag
35
+ return true
36
+ end
37
+ end
38
+ end
39
+
40
+ return false
41
+ end
42
+
43
+ def translated_string_for_lang(lang, default_lang=nil)
44
+ @translations[lang] || @translations[default_lang]
45
+ end
24
46
  end
25
47
 
26
48
  class StringsFile
@@ -87,7 +109,7 @@ module Twine
87
109
  current_row.tags = value.split(',')
88
110
  else
89
111
  if !@language_codes.include? key
90
- @language_codes << key
112
+ add_language_code(key)
91
113
  end
92
114
  current_row.translations[key] = value
93
115
  end
@@ -99,12 +121,6 @@ module Twine
99
121
  raise Twine::Error.new("Unable to parse line #{line_num} of #{path}: #{line}")
100
122
  end
101
123
  end
102
-
103
- # Developer Language
104
- dev_lang = @language_codes[0]
105
- @language_codes.delete(dev_lang)
106
- @language_codes.sort!
107
- @language_codes.insert(0, dev_lang)
108
124
  end
109
125
  end
110
126
 
@@ -120,13 +136,17 @@ module Twine
120
136
  f.puts "[[#{section.name}]]"
121
137
 
122
138
  section.rows.each do |row|
139
+ f.puts "\t[#{row.key}]"
123
140
  value = row.translations[dev_lang]
124
- if value[0,1] == ' ' || value[-1,1] == ' ' || (value[0,1] == '`' && value[-1,1] == '`')
125
- value = '`' + value + '`'
141
+ if !value
142
+ puts "Warning: #{row.key} does not exist in developer language '#{dev_lang}'"
143
+ else
144
+ if value[0,1] == ' ' || value[-1,1] == ' ' || (value[0,1] == '`' && value[-1,1] == '`')
145
+ value = '`' + value + '`'
146
+ end
147
+ f.puts "\t\t#{dev_lang} = #{value}"
126
148
  end
127
149
 
128
- f.puts "\t[#{row.key}]"
129
- f.puts "\t\t#{dev_lang} = #{value}"
130
150
  if row.tags && row.tags.length > 0
131
151
  tag_str = row.tags.join(',')
132
152
  f.puts "\t\ttags = #{tag_str}"
@@ -147,5 +167,17 @@ module Twine
147
167
  end
148
168
  end
149
169
  end
170
+
171
+ def add_language_code(code)
172
+ if @language_codes.length == 0
173
+ @language_codes << code
174
+ elsif !@language_codes.include?(code)
175
+ dev_lang = @language_codes[0]
176
+ @language_codes << code
177
+ @language_codes.delete(dev_lang)
178
+ @language_codes.sort!
179
+ @language_codes.insert(0, dev_lang)
180
+ end
181
+ end
150
182
  end
151
183
  end
@@ -1,3 +1,3 @@
1
1
  module Twine
2
- VERSION = '0.1.1'
2
+ VERSION = '0.1.2'
3
3
  end
@@ -0,0 +1,10 @@
1
+ /**
2
+ * iOS Strings File
3
+ * Generated by Twine
4
+ * Language: en
5
+ */
6
+
7
+ /* My Strings */
8
+ "key1" = "key1-english";
9
+ "key3" = "key3-english";
10
+ "key5" = "A new string";
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Android Strings File -->
3
+ <!-- Generated by Twine -->
4
+ <!-- Language: fr -->
5
+ <resources>
6
+ <string name="key1">key1-french</string>
7
+ <string name="key2">key2-french</string>
8
+ <string name="key3">key3-french</string>
9
+ </resources>
@@ -0,0 +1,16 @@
1
+ [[My Strings]]
2
+ [key1]
3
+ en = key1-english
4
+ tags = tag1
5
+ es = key1-spanish
6
+ fr = key1-french
7
+ [key2]
8
+ en = key2-english
9
+ tags = tag2
10
+ fr = key2-french
11
+ [key3]
12
+ en = key3-english
13
+ tags = tag1,tag2
14
+ es = key3-spanish
15
+ [key4]
16
+ en = key4-english
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Android Strings File -->
3
+ <!-- Generated by Twine -->
4
+ <!-- Language: fr -->
5
+ <resources>
6
+ <!-- My Strings -->
7
+ <string name="key1">key1-french</string>
8
+ <string name="key2">key2-french</string>
9
+ <string name="key3">key3-english</string>
10
+ </resources>
@@ -0,0 +1,9 @@
1
+ /**
2
+ * iOS Strings File
3
+ * Generated by Twine
4
+ * Language: en
5
+ */
6
+
7
+ /* My Strings */
8
+ "key1" = "key1-english";
9
+ "key3" = "key3-english";
@@ -0,0 +1,17 @@
1
+ [[My Strings]]
2
+ [key1]
3
+ en = key1-english
4
+ tags = tag1
5
+ es = key1-spanish
6
+ fr = key1-french
7
+ [key2]
8
+ en = key2-english
9
+ tags = tag2
10
+ fr = key2-french
11
+ [key3]
12
+ en = key3-english
13
+ tags = tag1,tag2
14
+ es = key3-spanish
15
+ fr = key3-french
16
+ [key4]
17
+ en = key4-english
@@ -0,0 +1,20 @@
1
+ [[Uncategorized]]
2
+ [key5]
3
+ en = A new string
4
+
5
+ [[My Strings]]
6
+ [key1]
7
+ en = key1-english
8
+ tags = tag1
9
+ es = key1-spanish
10
+ fr = key1-french
11
+ [key2]
12
+ en = key2-english
13
+ tags = tag2
14
+ fr = key2-french
15
+ [key3]
16
+ en = key3-english
17
+ tags = tag1,tag2
18
+ es = key3-spanish
19
+ [key4]
20
+ en = key4-english
@@ -0,0 +1,40 @@
1
+ require 'test/unit'
2
+ require 'twine'
3
+
4
+ class TwineTest < Test::Unit::TestCase
5
+ def test_generate_string_file_1
6
+ Dir.mktmpdir do |dir|
7
+ output_path = File.join(dir, 'fr.xml')
8
+ Twine::Runner.run(%W(generate-string-file test/fixtures/strings-1.txt #{output_path}))
9
+ assert_equal(File.read('test/fixtures/test-output-1.txt'), File.read(output_path))
10
+ end
11
+ end
12
+
13
+ def test_generate_string_file_2
14
+ Dir.mktmpdir do |dir|
15
+ output_path = File.join(dir, 'en.strings')
16
+ Twine::Runner.run(%W(generate-string-file test/fixtures/strings-1.txt #{output_path} -t tag1))
17
+ assert_equal(File.read('test/fixtures/test-output-2.txt'), File.read(output_path))
18
+ end
19
+ end
20
+
21
+ def test_consume_string_file_1
22
+ Dir.mktmpdir do |dir|
23
+ output_path = File.join(dir, 'strings.txt')
24
+ Twine::Runner.run(%W(consume-string-file test/fixtures/strings-1.txt test/fixtures/fr-1.xml -o #{output_path} -l fr))
25
+ assert_equal(File.read('test/fixtures/test-output-3.txt'), File.read(output_path))
26
+ end
27
+ end
28
+
29
+ def test_consume_string_file_2
30
+ Dir.mktmpdir do |dir|
31
+ output_path = File.join(dir, 'strings.txt')
32
+ Twine::Runner.run(%W(consume-string-file test/fixtures/strings-1.txt test/fixtures/en-1.strings -o #{output_path} -l en -a))
33
+ assert_equal(File.read('test/fixtures/test-output-4.txt'), File.read(output_path))
34
+ end
35
+ end
36
+
37
+ def test_generate_report_1
38
+ Twine::Runner.run(%w(generate-report test/fixtures/strings-1.txt))
39
+ end
40
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: twine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-08 00:00:00.000000000 Z
12
+ date: 2012-02-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rubyzip
16
- requirement: &70309529145320 !ruby/object:Gem::Requirement
16
+ requirement: &70180596581660 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,7 +21,18 @@ dependencies:
21
21
  version: 0.9.5
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70309529145320
24
+ version_requirements: *70180596581660
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &70180596581180 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 0.9.2
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70180596581180
25
36
  description: ! " Twine is a command line tool for managing your strings and their
26
37
  translations.\n \n It is geared toward Mac OS X, iOS, and Android developers.\n"
27
38
  email: twine@mobiata.com
@@ -43,6 +54,14 @@ files:
43
54
  - lib/twine/version.rb
44
55
  - lib/twine.rb
45
56
  - bin/twine
57
+ - test/fixtures/en-1.strings
58
+ - test/fixtures/fr-1.xml
59
+ - test/fixtures/strings-1.txt
60
+ - test/fixtures/test-output-1.txt
61
+ - test/fixtures/test-output-2.txt
62
+ - test/fixtures/test-output-3.txt
63
+ - test/fixtures/test-output-4.txt
64
+ - test/twine_test.rb
46
65
  homepage: https://github.com/mobiata/twine
47
66
  licenses: []
48
67
  post_install_message:
@@ -54,7 +73,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
54
73
  requirements:
55
74
  - - ! '>='
56
75
  - !ruby/object:Gem::Version
57
- version: '0'
76
+ version: 1.8.7
58
77
  required_rubygems_version: !ruby/object:Gem::Requirement
59
78
  none: false
60
79
  requirements:
@@ -67,4 +86,5 @@ rubygems_version: 1.8.11
67
86
  signing_key:
68
87
  specification_version: 3
69
88
  summary: Manage strings and their translations for your iOS and Android projects.
70
- test_files: []
89
+ test_files:
90
+ - test/twine_test.rb