twine 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +12 -0
- data/lib/twine.rb +21 -0
- data/lib/twine/cli.rb +68 -91
- data/lib/twine/formatters/abstract.rb +133 -82
- data/lib/twine/formatters/android.rb +49 -67
- data/lib/twine/formatters/apple.rb +33 -47
- data/lib/twine/formatters/django.rb +38 -48
- data/lib/twine/formatters/flash.rb +25 -40
- data/lib/twine/formatters/gettext.rb +37 -44
- data/lib/twine/formatters/jquery.rb +31 -33
- data/lib/twine/formatters/tizen.rb +38 -55
- data/lib/twine/output_processor.rb +57 -0
- data/lib/twine/placeholders.rb +54 -0
- data/lib/twine/runner.rb +78 -115
- data/lib/twine/stringsfile.rb +83 -60
- data/lib/twine/version.rb +1 -1
- data/test/command_test_case.rb +12 -0
- data/test/fixtures/consume_loc_drop.zip +0 -0
- data/test/fixtures/formatter_android.xml +15 -0
- data/test/fixtures/formatter_apple.strings +20 -0
- data/test/fixtures/formatter_django.po +28 -0
- data/test/fixtures/formatter_flash.properties +15 -0
- data/test/fixtures/formatter_gettext.po +26 -0
- data/test/fixtures/formatter_jquery.json +7 -0
- data/test/fixtures/formatter_tizen.xml +15 -0
- data/test/fixtures/gettext_multiline.po +10 -0
- data/test/fixtures/twine_accent_values.txt +13 -0
- data/test/test_abstract_formatter.rb +152 -0
- data/test/test_cli.rb +288 -0
- data/test/test_consume_loc_drop.rb +27 -0
- data/test/test_consume_string_file.rb +53 -0
- data/test/test_formatters.rb +236 -0
- data/test/test_generate_all_string_files.rb +44 -0
- data/test/test_generate_loc_drop.rb +44 -0
- data/test/test_generate_string_file.rb +51 -0
- data/test/test_output_processor.rb +85 -0
- data/test/test_placeholders.rb +86 -0
- data/test/test_strings_file.rb +58 -0
- data/test/test_strings_row.rb +47 -0
- data/test/test_validate_strings_file.rb +55 -0
- data/test/twine_file_dsl.rb +46 -0
- data/test/twine_test_case.rb +44 -0
- metadata +80 -37
- data/test/fixtures/en-1.json +0 -5
- data/test/fixtures/en-1.po +0 -16
- data/test/fixtures/en-1.strings +0 -10
- data/test/fixtures/en-2.po +0 -23
- data/test/fixtures/en-3.xml +0 -8
- data/test/fixtures/fr-1.xml +0 -10
- data/test/fixtures/strings-1.txt +0 -17
- data/test/fixtures/strings-2.txt +0 -5
- data/test/fixtures/strings-3.txt +0 -5
- data/test/fixtures/test-json-line-breaks/consumed.txt +0 -5
- data/test/fixtures/test-json-line-breaks/generated.json +0 -3
- data/test/fixtures/test-json-line-breaks/line-breaks.json +0 -3
- data/test/fixtures/test-json-line-breaks/line-breaks.txt +0 -4
- data/test/fixtures/test-output-1.txt +0 -12
- data/test/fixtures/test-output-10.txt +0 -9
- data/test/fixtures/test-output-11.txt +0 -9
- data/test/fixtures/test-output-12.txt +0 -12
- data/test/fixtures/test-output-2.txt +0 -12
- data/test/fixtures/test-output-3.txt +0 -18
- data/test/fixtures/test-output-4.txt +0 -21
- data/test/fixtures/test-output-5.txt +0 -4
- data/test/fixtures/test-output-6.txt +0 -10
- data/test/fixtures/test-output-7.txt +0 -16
- data/test/fixtures/test-output-8.txt +0 -9
- data/test/fixtures/test-output-9.txt +0 -21
- data/test/twine_test.rb +0 -134
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 46fb29dd5d1ec899c427901e72e40c081fa8c6c4
|
4
|
+
data.tar.gz: 3faf58c634c4d1903fda10a1ece3cace658dc761
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ec4b6098a24520cba05e4bb2bd0ea16602c9b52382abfeb45e9bf0308a4b1c03287251160cdec49cb26f44ccd69ff96d17f5dd0c9d56b7612068435c750dfb2
|
7
|
+
data.tar.gz: b1a85e5a3fb56e20a790e7beb8649784f5ffab42f4e8b6db7132680a634275caad8ab698a09369d9b46821c873bf8a51c416f941337d1b101ad079f6bd6d2960
|
data/README.md
CHANGED
@@ -27,6 +27,10 @@ Twine stores all of its strings in a single file. The format of this file is a s
|
|
27
27
|
|
28
28
|
Each grouping section contains N string definitions. These string definitions start with the string key placed within a single pair of square brackets. This string definition then contains a number of key-value pairs, including a comment, a comma-separated list of tags (which are used by Twine to select a subset of strings), and all of the translations.
|
29
29
|
|
30
|
+
### Placeholders
|
31
|
+
|
32
|
+
Twine supports [`printf` style placeholders](https://en.wikipedia.org/wiki/Printf_format_string) with one peculiarity: `@` is used for strings instead of `s`. This is because Twine started out as a tool for iOS and OS X projects.
|
33
|
+
|
30
34
|
### Tags
|
31
35
|
|
32
36
|
Tags are used by Twine as a way to only work with a subset of your strings at any given point in time. Each string can be assigned zero or more tags which are separated by commas. Tags are optional, though highly recommended. You can get a list of all strings currently missing tags by executing the `validate-strings-file` command.
|
@@ -35,6 +39,10 @@ Tags are used by Twine as a way to only work with a subset of your strings at an
|
|
35
39
|
|
36
40
|
Whitepace in this file is mostly ignored. If you absolutely need to put spaces at the beginning or end of your translated string, you can wrap the entire string in a pair of `` ` `` characters. If your actual string needs to start *and* end with a grave accent, you can wrap it in another pair of `` ` `` characters. See the example, below.
|
37
41
|
|
42
|
+
### References
|
43
|
+
|
44
|
+
If you want a key to inherit the values of another key, you can use a reference. Any property not specified for a key will be taken from the reference.
|
45
|
+
|
38
46
|
### Example
|
39
47
|
|
40
48
|
[[General]]
|
@@ -57,6 +65,9 @@ Whitepace in this file is mostly ignored. If you absolutely need to put spaces a
|
|
57
65
|
en = The network is currently unavailable.
|
58
66
|
tags = app1
|
59
67
|
comment = An error describing when the device can not connect to the internet.
|
68
|
+
[dismiss_error]
|
69
|
+
ref = yes
|
70
|
+
en = Dismiss
|
60
71
|
|
61
72
|
[[Escaping Example]]
|
62
73
|
[list_item_separator]
|
@@ -216,6 +227,7 @@ Many thanks to all of the contributors to the Twine project, including:
|
|
216
227
|
* [Kevin Wood](https://github.com/kwood)
|
217
228
|
* [Mohammad Hejazi](https://github.com/MohammadHejazi)
|
218
229
|
* [Robert Guo](http://www.robertguo.me/)
|
230
|
+
* [sebastianludwig](https://github.com/sebastianludwig)
|
219
231
|
* [Sergey Pisarchik](https://github.com/SergeyPisarchik)
|
220
232
|
* [Shai Shamir](https://github.com/pichirichi)
|
221
233
|
|
data/lib/twine.rb
CHANGED
@@ -1,10 +1,31 @@
|
|
1
1
|
module Twine
|
2
|
+
@@stdout = STDOUT
|
3
|
+
@@stderr = STDERR
|
4
|
+
|
5
|
+
def self.stdout
|
6
|
+
@@stdout
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.stdout=(out)
|
10
|
+
@@stdout = out
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.stderr
|
14
|
+
@@stderr
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.stderr=(err)
|
18
|
+
@@stderr = err
|
19
|
+
end
|
20
|
+
|
2
21
|
class Error < StandardError
|
3
22
|
end
|
4
23
|
|
5
24
|
require 'twine/plugin'
|
6
25
|
require 'twine/cli'
|
7
26
|
require 'twine/encoding'
|
27
|
+
require 'twine/output_processor'
|
28
|
+
require 'twine/placeholders'
|
8
29
|
require 'twine/formatters'
|
9
30
|
require 'twine/runner'
|
10
31
|
require 'twine/stringsfile'
|
data/lib/twine/cli.rb
CHANGED
@@ -1,17 +1,19 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
|
3
3
|
module Twine
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
4
|
+
module CLI
|
5
|
+
NEEDED_COMMAND_ARGUMENTS = {
|
6
|
+
'generate-string-file' => 3,
|
7
|
+
'generate-all-string-files' => 3,
|
8
|
+
'consume-string-file' => 3,
|
9
|
+
'consume-all-string-files' => 3,
|
10
|
+
'generate-loc-drop' => 3,
|
11
|
+
'consume-loc-drop' => 3,
|
12
|
+
'validate-strings-file' => 2
|
13
|
+
}
|
13
14
|
|
14
|
-
def
|
15
|
+
def self.parse(args)
|
16
|
+
options = {}
|
15
17
|
parser = OptionParser.new do |opts|
|
16
18
|
opts.banner = 'Usage: twine COMMAND STRINGS_FILE [INPUT_OR_OUTPUT_PATH] [--lang LANG1,LANG2...] [--tags TAG1,TAG2,TAG3...] [--format FORMAT]'
|
17
19
|
opts.separator ''
|
@@ -27,7 +29,7 @@ module Twine
|
|
27
29
|
opts.separator ''
|
28
30
|
opts.separator 'consume-all-string-files -- Slurps all of the strings from a directory into the specified STRINGS_FILE. If you have some files returned to you by your translators you can use this command to incorporate all of their changes. This script will attempt to guess both the language and the format given the filename and extension. For example, "ja.strings" will assume that the file is a Japanese iOS strings file.'
|
29
31
|
opts.separator ''
|
30
|
-
opts.separator 'generate-loc-drop -- Generates a zip archive of strings files in any format. The purpose of this command is to create a very simple archive that can be handed off to a translation team. The translation team can unzip the archive, translate all of the strings in the archived files, zip everything back up, and then hand that final archive back to be consumed by the consume-loc-drop command.
|
32
|
+
opts.separator 'generate-loc-drop -- Generates a zip archive of strings files in any format. The purpose of this command is to create a very simple archive that can be handed off to a translation team. The translation team can unzip the archive, translate all of the strings in the archived files, zip everything back up, and then hand that final archive back to be consumed by the consume-loc-drop command.'
|
31
33
|
opts.separator ''
|
32
34
|
opts.separator 'consume-loc-drop -- Consumes an archive of translated files. This archive should be in the same format as the one created by the generate-loc-drop command.'
|
33
35
|
opts.separator ''
|
@@ -36,48 +38,56 @@ module Twine
|
|
36
38
|
opts.separator 'General Options:'
|
37
39
|
opts.separator ''
|
38
40
|
opts.on('-l', '--lang LANGUAGES', Array, 'The language code(s) to use for the specified action.') do |langs|
|
39
|
-
|
41
|
+
options[:languages] = langs
|
40
42
|
end
|
41
43
|
opts.on('-t', '--tags TAGS', Array, 'The tag(s) to use for the specified action. Only strings with that tag will be processed. Do not specify any tags to match all strings in the strings data file.') do |tags|
|
42
|
-
|
44
|
+
options[:tags] = tags
|
43
45
|
end
|
44
46
|
opts.on('-u', '--untagged', 'If you have specified tags using the --tags flag, then only those tags will be selected. If you also want to select all strings that are untagged, then you can specify this option to do so.') do |u|
|
45
|
-
|
46
|
-
end
|
47
|
-
formats = []
|
48
|
-
Formatters.formatters.each do |formatter|
|
49
|
-
formats << formatter::FORMAT_NAME
|
47
|
+
options[:untagged] = true
|
50
48
|
end
|
49
|
+
formats = Formatters.formatters.map { |f| f::FORMAT_NAME }
|
51
50
|
opts.on('-f', '--format FORMAT', "The file format to read or write (#{formats.join(', ')}). Additional formatters can be placed in the formats/ directory.") do |format|
|
52
|
-
|
53
|
-
|
54
|
-
STDERR.puts "Invalid format: #{format}"
|
51
|
+
unless formats.include?(format.downcase)
|
52
|
+
raise Twine::Error.new "Invalid format: #{format}"
|
55
53
|
end
|
56
|
-
|
54
|
+
options[:format] = format.downcase
|
57
55
|
end
|
58
56
|
opts.on('-a', '--consume-all', 'Normally, when consuming a string file, Twine will ignore any string keys that do not exist in your master file.') do |a|
|
59
|
-
|
57
|
+
options[:consume_all] = true
|
58
|
+
end
|
59
|
+
opts.on('-i', '--include SET', "This flag will determine which strings are included when generating strings files. It's possible values:",
|
60
|
+
" all: All strings both translated and untranslated for the specified language are included. This is the default value.",
|
61
|
+
" translated: Only translated strings are included.",
|
62
|
+
" untranslated: Only untranslated strings are included.") do |set|
|
63
|
+
unless ['all', 'translated', 'untranslated'].include?(set.downcase)
|
64
|
+
raise Twine::Error.new "Invalid include flag: #{set}"
|
65
|
+
end
|
66
|
+
options[:include] = set.downcase
|
60
67
|
end
|
61
|
-
|
62
|
-
|
68
|
+
unless options[:include]
|
69
|
+
options[:include] = 'all'
|
63
70
|
end
|
64
71
|
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
|
-
|
72
|
+
options[:output_path] = o
|
66
73
|
end
|
67
74
|
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
|
-
|
75
|
+
options[:file_name] = n
|
76
|
+
end
|
77
|
+
opts.on('-r', '--create-folders', "When running the generate-all-string-files command, this flag may be used to create output folders for all languages, if they don't exist yet. As a result all languages will be exported, not only the ones where an output folder already exists.") do |r|
|
78
|
+
options[:create_folders] = true
|
69
79
|
end
|
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|
|
71
|
-
|
80
|
+
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. When generating files this language will be used as default language and its translations will be used if a key is not localized for the output language.') do |d|
|
81
|
+
options[:developer_language] = d
|
72
82
|
end
|
73
83
|
opts.on('-c', '--consume-comments', 'Normally, when consuming a string file, Twine will ignore all comments in the file. With this flag set, any comments encountered will be read and parsed into the strings data file. This is especially useful when creating your first strings data file from an existing project.') do |c|
|
74
|
-
|
84
|
+
options[:consume_comments] = true
|
75
85
|
end
|
76
|
-
opts.on('-e', '--encoding ENCODING', 'Twine defaults to encoding all output files in UTF-8. This flag will tell Twine to use an alternate encoding for these files. For example, you could use this to write Apple .strings files in UTF-16. This flag
|
77
|
-
|
86
|
+
opts.on('-e', '--encoding ENCODING', 'Twine defaults to encoding all output files in UTF-8. This flag will tell Twine to use an alternate encoding for these files. For example, you could use this to write Apple .strings files in UTF-16. This flag is currently only supported in Ruby 1.9.3 or greater.') do |e|
|
87
|
+
unless "".respond_to? :encode
|
78
88
|
raise Twine::Error.new "The --encoding flag is only supported on Ruby 1.9.3 or greater."
|
79
89
|
end
|
80
|
-
|
90
|
+
options[:output_encoding] = e
|
81
91
|
end
|
82
92
|
opts.on('-h', '--help', 'Show this message.') do |h|
|
83
93
|
puts opts.help
|
@@ -98,89 +108,56 @@ module Twine
|
|
98
108
|
opts.separator '> twine consume-loc-drop strings.txt LocDrop5.zip'
|
99
109
|
opts.separator '> twine validate-strings-file strings.txt'
|
100
110
|
end
|
101
|
-
parser.parse!
|
111
|
+
parser.parse! args
|
102
112
|
|
103
|
-
if
|
113
|
+
if args.length == 0
|
104
114
|
puts parser.help
|
105
115
|
exit
|
106
116
|
end
|
107
117
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
raise Twine::Error.new "Invalid command: #{@options[:command]}"
|
118
|
+
number_of_needed_arguments = NEEDED_COMMAND_ARGUMENTS[args[0]]
|
119
|
+
unless number_of_needed_arguments
|
120
|
+
raise Twine::Error.new "Invalid command: #{args[0]}"
|
112
121
|
end
|
122
|
+
options[:command] = args[0]
|
113
123
|
|
114
|
-
if
|
124
|
+
if args.length < 2
|
115
125
|
raise Twine::Error.new 'You must specify your strings file.'
|
116
126
|
end
|
127
|
+
options[:strings_file] = args[1]
|
117
128
|
|
118
|
-
|
129
|
+
if args.length < number_of_needed_arguments
|
130
|
+
raise Twine::Error.new 'Not enough arguments.'
|
131
|
+
elsif args.length > number_of_needed_arguments
|
132
|
+
raise Twine::Error.new "Unknown argument: #{args[number_of_needed_arguments]}"
|
133
|
+
end
|
119
134
|
|
120
|
-
case
|
135
|
+
case options[:command]
|
121
136
|
when 'generate-string-file'
|
122
|
-
|
123
|
-
|
124
|
-
elsif @args.length > 3
|
125
|
-
raise Twine::Error.new "Unknown argument: #{@args[3]}"
|
126
|
-
else
|
127
|
-
raise Twine::Error.new 'Not enough arguments.'
|
128
|
-
end
|
129
|
-
if @options[:languages] and @options[:languages].length > 1
|
137
|
+
options[:output_path] = args[2]
|
138
|
+
if options[:languages] and options[:languages].length > 1
|
130
139
|
raise Twine::Error.new 'Please only specify a single language for the generate-string-file command.'
|
131
140
|
end
|
132
141
|
when 'generate-all-string-files'
|
133
|
-
|
134
|
-
@options[:output_path] = @args[2]
|
135
|
-
elsif @args.length > 3
|
136
|
-
raise Twine::Error.new "Unknown argument: #{@args[3]}"
|
137
|
-
else
|
138
|
-
raise Twine::Error.new 'Not enough arguments.'
|
139
|
-
end
|
142
|
+
options[:output_path] = args[2]
|
140
143
|
when 'consume-string-file'
|
141
|
-
|
142
|
-
|
143
|
-
elsif @args.length > 3
|
144
|
-
raise Twine::Error.new "Unknown argument: #{@args[3]}"
|
145
|
-
else
|
146
|
-
raise Twine::Error.new 'Not enough arguments.'
|
147
|
-
end
|
148
|
-
if @options[:languages] and @options[:languages].length > 1
|
144
|
+
options[:input_path] = args[2]
|
145
|
+
if options[:languages] and options[:languages].length > 1
|
149
146
|
raise Twine::Error.new 'Please only specify a single language for the consume-string-file command.'
|
150
147
|
end
|
151
148
|
when 'consume-all-string-files'
|
152
|
-
|
153
|
-
@options[:input_path] = @args[2]
|
154
|
-
elsif @args.length > 3
|
155
|
-
raise Twine::Error.new "Unknown argument: #{@args[3]}"
|
156
|
-
else
|
157
|
-
raise Twine::Error.new 'Not enough arguments.'
|
158
|
-
end
|
149
|
+
options[:input_path] = args[2]
|
159
150
|
when 'generate-loc-drop'
|
160
|
-
|
161
|
-
if
|
162
|
-
@options[:output_path] = @args[2]
|
163
|
-
elsif @args.length > 3
|
164
|
-
raise Twine::Error.new "Unknown argument: #{@args[3]}"
|
165
|
-
else
|
166
|
-
raise Twine::Error.new 'Not enough arguments.'
|
167
|
-
end
|
168
|
-
if !@options[:format]
|
151
|
+
options[:output_path] = args[2]
|
152
|
+
if !options[:format]
|
169
153
|
raise Twine::Error.new 'You must specify a format.'
|
170
154
|
end
|
171
155
|
when 'consume-loc-drop'
|
172
|
-
|
173
|
-
@options[:input_path] = @args[2]
|
174
|
-
elsif @args.length > 3
|
175
|
-
raise Twine::Error.new "Unknown argument: #{@args[3]}"
|
176
|
-
else
|
177
|
-
raise Twine::Error.new 'Not enough arguments.'
|
178
|
-
end
|
156
|
+
options[:input_path] = args[2]
|
179
157
|
when 'validate-strings-file'
|
180
|
-
if @args.length > 2
|
181
|
-
raise Twine::Error.new "Unknown argument: #{@args[2]}"
|
182
|
-
end
|
183
158
|
end
|
159
|
+
|
160
|
+
return options
|
184
161
|
end
|
185
162
|
end
|
186
163
|
end
|
@@ -1,8 +1,10 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
1
3
|
module Twine
|
2
4
|
module Formatters
|
3
5
|
class Abstract
|
4
|
-
|
5
|
-
|
6
|
+
attr_reader :strings
|
7
|
+
attr_reader :options
|
6
8
|
|
7
9
|
def self.can_handle_directory?(path)
|
8
10
|
return false
|
@@ -11,70 +13,23 @@ module Twine
|
|
11
13
|
def initialize(strings, options)
|
12
14
|
@strings = strings
|
13
15
|
@options = options
|
16
|
+
@output_processor = Processors::OutputProcessor.new @strings, @options
|
14
17
|
end
|
15
18
|
|
16
|
-
def iosify_substitutions(str)
|
17
|
-
# use "@" instead of "s" for substituting strings
|
18
|
-
str.gsub!(/%([0-9\$]*)s/, '%\1@')
|
19
|
-
return str
|
20
|
-
end
|
21
|
-
|
22
|
-
def androidify_substitutions(str)
|
23
|
-
# 1) use "s" instead of "@" for substituting strings
|
24
|
-
str.gsub!(/%([0-9\$]*)@/, '%\1s')
|
25
|
-
|
26
|
-
# 1a) escape strings that begin with a lone "@"
|
27
|
-
str.sub!(/^@ /, '\\@ ')
|
28
|
-
|
29
|
-
# 2) if there is more than one substitution in a string, make sure they are numbered
|
30
|
-
substituteCount = 0
|
31
|
-
startFound = false
|
32
|
-
str.each_char do |c|
|
33
|
-
if startFound
|
34
|
-
if c == "%"
|
35
|
-
# ignore as this is a literal %
|
36
|
-
elsif c.match(/\d/)
|
37
|
-
# leave the string alone if it already has numbered substitutions
|
38
|
-
return str
|
39
|
-
else
|
40
|
-
substituteCount += 1
|
41
|
-
end
|
42
|
-
startFound = false
|
43
|
-
elsif c == "%"
|
44
|
-
startFound = true
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
if substituteCount > 1
|
49
|
-
currentSub = 1
|
50
|
-
startFound = false
|
51
|
-
newstr = ""
|
52
|
-
str.each_char do |c|
|
53
|
-
if startFound
|
54
|
-
if !(c == "%")
|
55
|
-
newstr = newstr + "#{currentSub}$"
|
56
|
-
currentSub += 1
|
57
|
-
end
|
58
|
-
startFound = false
|
59
|
-
elsif c == "%"
|
60
|
-
startFound = true
|
61
|
-
end
|
62
|
-
newstr = newstr + c
|
63
|
-
end
|
64
|
-
return newstr
|
65
|
-
else
|
66
|
-
return str
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
19
|
def set_translation_for_key(key, lang, value)
|
20
|
+
value = value.gsub("\n", "\\n")
|
21
|
+
|
71
22
|
if @strings.strings_map.include?(key)
|
72
|
-
@strings.strings_map[key]
|
23
|
+
row = @strings.strings_map[key]
|
24
|
+
reference = @strings.strings_map[row.reference_key] if row.reference_key
|
25
|
+
|
26
|
+
if !reference or value != reference.translations[lang]
|
27
|
+
row.translations[lang] = value
|
28
|
+
end
|
73
29
|
elsif @options[:consume_all]
|
74
|
-
|
75
|
-
|
76
|
-
current_section
|
77
|
-
if !current_section
|
30
|
+
Twine::stderr.puts "Adding new string '#{key}' to strings data file."
|
31
|
+
current_section = @strings.sections.find { |s| s.name == 'Uncategorized' }
|
32
|
+
unless current_section
|
78
33
|
current_section = StringsSection.new('Uncategorized')
|
79
34
|
@strings.sections.insert(0, current_section)
|
80
35
|
end
|
@@ -82,13 +37,13 @@ module Twine
|
|
82
37
|
current_section.rows << current_row
|
83
38
|
|
84
39
|
if @options[:tags] && @options[:tags].length > 0
|
85
|
-
|
40
|
+
current_row.tags = @options[:tags]
|
86
41
|
end
|
87
42
|
|
88
43
|
@strings.strings_map[key] = current_row
|
89
44
|
@strings.strings_map[key].translations[lang] = value
|
90
45
|
else
|
91
|
-
|
46
|
+
Twine::stderr.puts "Warning: '#{key}' not found in strings data file."
|
92
47
|
end
|
93
48
|
if !@strings.language_codes.include?(lang)
|
94
49
|
@strings.add_language_code(lang)
|
@@ -96,8 +51,16 @@ module Twine
|
|
96
51
|
end
|
97
52
|
|
98
53
|
def set_comment_for_key(key, comment)
|
54
|
+
return unless @options[:consume_comments]
|
55
|
+
|
99
56
|
if @strings.strings_map.include?(key)
|
100
|
-
@strings.strings_map[key]
|
57
|
+
row = @strings.strings_map[key]
|
58
|
+
|
59
|
+
reference = @strings.strings_map[row.reference_key] if row.reference_key
|
60
|
+
|
61
|
+
if !reference or comment != reference.raw_comment
|
62
|
+
row.comment = comment
|
63
|
+
end
|
101
64
|
end
|
102
65
|
end
|
103
66
|
|
@@ -109,38 +72,126 @@ module Twine
|
|
109
72
|
raise NotImplementedError.new("You must implement determine_language_given_path in your formatter class.")
|
110
73
|
end
|
111
74
|
|
75
|
+
def output_path_for_language(lang)
|
76
|
+
lang
|
77
|
+
end
|
78
|
+
|
112
79
|
def read_file(path, lang)
|
113
80
|
raise NotImplementedError.new("You must implement read_file in your formatter class.")
|
114
81
|
end
|
115
82
|
|
116
|
-
def
|
117
|
-
|
83
|
+
def format_file(strings, lang)
|
84
|
+
header = format_header(lang)
|
85
|
+
result = ""
|
86
|
+
result += header + "\n" if header
|
87
|
+
result += format_sections(strings, lang)
|
118
88
|
end
|
119
89
|
|
120
|
-
def
|
121
|
-
|
122
|
-
|
90
|
+
def format_header(lang)
|
91
|
+
end
|
92
|
+
|
93
|
+
def format_sections(strings, lang)
|
94
|
+
sections = strings.sections.map { |section| format_section(section, lang) }
|
95
|
+
sections.join("\n")
|
96
|
+
end
|
97
|
+
|
98
|
+
def format_section_header(section)
|
99
|
+
end
|
100
|
+
|
101
|
+
def format_section(section, lang)
|
102
|
+
rows = section.rows.dup
|
103
|
+
|
104
|
+
result = ""
|
105
|
+
unless rows.empty?
|
106
|
+
if section.name && section.name.length > 0
|
107
|
+
section_header = format_section_header(section)
|
108
|
+
result += "\n#{section_header}" if section_header
|
109
|
+
end
|
123
110
|
end
|
124
111
|
|
112
|
+
rows.map! { |row| format_row(row, lang) }
|
113
|
+
rows.compact! # remove nil entries
|
114
|
+
rows.map! { |row| "\n#{row}" } # prepend newline
|
115
|
+
result += rows.join
|
116
|
+
end
|
117
|
+
|
118
|
+
def row_pattern
|
119
|
+
"%{comment}%{key_value}"
|
120
|
+
end
|
121
|
+
|
122
|
+
def format_row(row, lang)
|
123
|
+
return nil unless row.translated_string_for_lang(lang)
|
124
|
+
|
125
|
+
result = row_pattern.scan(/%\{([a-z_]+)\}/).flatten
|
126
|
+
result.map! { |element| send("format_#{element}".to_sym, row, lang) }
|
127
|
+
result.flatten.join
|
128
|
+
end
|
129
|
+
|
130
|
+
def format_comment(row, lang)
|
131
|
+
end
|
132
|
+
|
133
|
+
def format_key_value(row, lang)
|
134
|
+
value = row.translated_string_for_lang(lang)
|
135
|
+
key_value_pattern % { key: format_key(row.key.dup), value: format_value(value.dup) }
|
136
|
+
end
|
137
|
+
|
138
|
+
def key_value_pattern
|
139
|
+
raise NotImplementedError.new("You must implement key_value_pattern in your formatter class.")
|
140
|
+
end
|
141
|
+
|
142
|
+
def format_key(key)
|
143
|
+
key
|
144
|
+
end
|
145
|
+
|
146
|
+
def format_value(value)
|
147
|
+
value
|
148
|
+
end
|
149
|
+
|
150
|
+
def escape_quotes(text)
|
151
|
+
text.gsub('"', '\\\\"')
|
152
|
+
end
|
153
|
+
|
154
|
+
def write_file(path, lang)
|
155
|
+
encoding = @options[:output_encoding] || 'UTF-8'
|
156
|
+
|
157
|
+
processed_strings = @output_processor.process(lang)
|
158
|
+
|
159
|
+
File.open(path, "w:#{encoding}") do |f|
|
160
|
+
f.puts format_file(processed_strings, lang)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def write_all_files(path)
|
125
165
|
file_name = @options[:file_name] || default_file_name
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
166
|
+
if @options[:create_folders]
|
167
|
+
@strings.language_codes.each do |lang|
|
168
|
+
output_path = File.join(path, output_path_for_language(lang))
|
169
|
+
|
170
|
+
FileUtils.mkdir_p(output_path)
|
171
|
+
|
172
|
+
write_file(File.join(output_path, file_name), lang)
|
130
173
|
end
|
131
|
-
|
132
|
-
|
174
|
+
else
|
175
|
+
language_written = false
|
176
|
+
Dir.foreach(path) do |item|
|
177
|
+
next if item == "." or item == ".."
|
178
|
+
|
179
|
+
item = File.join(path, item)
|
180
|
+
next unless File.directory?(item)
|
181
|
+
|
133
182
|
lang = determine_language_given_path(item)
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
183
|
+
next unless lang
|
184
|
+
|
185
|
+
write_file(File.join(item, file_name), lang)
|
186
|
+
language_written = true
|
187
|
+
end
|
188
|
+
|
189
|
+
if !language_written
|
190
|
+
raise Twine::Error.new("Failed to generate any files: No languages found at #{path}")
|
138
191
|
end
|
139
|
-
end
|
140
|
-
if langs_written.empty?
|
141
|
-
raise Twine::Error.new("Failed to generate any files: No languages found at #{path}")
|
142
192
|
end
|
143
193
|
end
|
194
|
+
|
144
195
|
end
|
145
196
|
end
|
146
197
|
end
|