twine 0.9.1 → 0.10.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.
- checksums.yaml +4 -4
- data/README.md +56 -69
- data/lib/twine.rb +11 -3
- data/lib/twine/cli.rb +375 -155
- data/lib/twine/formatters.rb +0 -5
- data/lib/twine/formatters/abstract.rb +43 -43
- data/lib/twine/formatters/android.rb +58 -59
- data/lib/twine/formatters/apple.rb +3 -3
- data/lib/twine/formatters/django.rb +15 -21
- data/lib/twine/formatters/flash.rb +17 -20
- data/lib/twine/formatters/gettext.rb +11 -15
- data/lib/twine/formatters/jquery.rb +8 -11
- data/lib/twine/formatters/tizen.rb +4 -4
- data/lib/twine/output_processor.rb +15 -15
- data/lib/twine/placeholders.rb +26 -6
- data/lib/twine/runner.rb +95 -95
- data/lib/twine/{stringsfile.rb → twine_file.rb} +53 -48
- data/lib/twine/version.rb +1 -1
- data/test/{command_test_case.rb → command_test.rb} +5 -5
- data/test/fixtures/{consume_loc_drop.zip → consume_localization_archive.zip} +0 -0
- data/test/fixtures/formatter_django.po +3 -1
- data/test/test_abstract_formatter.rb +40 -40
- data/test/test_cli.rb +313 -211
- data/test/test_consume_localization_archive.rb +27 -0
- data/test/{test_consume_string_file.rb → test_consume_localization_file.rb} +19 -19
- data/test/test_formatters.rb +108 -43
- data/test/{test_generate_all_string_files.rb → test_generate_all_localization_files.rb} +18 -18
- data/test/{test_generate_loc_drop.rb → test_generate_localization_archive.rb} +14 -14
- data/test/{test_generate_string_file.rb → test_generate_localization_file.rb} +18 -18
- data/test/test_output_processor.rb +26 -26
- data/test/test_placeholders.rb +44 -9
- data/test/test_twine_definition.rb +111 -0
- data/test/test_twine_file.rb +58 -0
- data/test/test_validate_twine_file.rb +61 -0
- data/test/twine_file_dsl.rb +12 -12
- data/test/{twine_test_case.rb → twine_test.rb} +1 -1
- metadata +23 -23
- data/test/test_consume_loc_drop.rb +0 -27
- data/test/test_strings_file.rb +0 -58
- data/test/test_strings_row.rb +0 -47
- data/test/test_validate_strings_file.rb +0 -61
data/lib/twine/formatters.rb
CHANGED
@@ -3,11 +3,11 @@ require 'fileutils'
|
|
3
3
|
module Twine
|
4
4
|
module Formatters
|
5
5
|
class Abstract
|
6
|
-
attr_accessor :
|
6
|
+
attr_accessor :twine_file
|
7
7
|
attr_accessor :options
|
8
8
|
|
9
9
|
def initialize
|
10
|
-
@
|
10
|
+
@twine_file = TwineFile.new
|
11
11
|
@options = {}
|
12
12
|
end
|
13
13
|
|
@@ -20,7 +20,7 @@ module Twine
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def can_handle_directory?(path)
|
23
|
-
|
23
|
+
Dir.entries(path).any? { |item| /^.+#{Regexp.escape(extension)}$/.match(item) }
|
24
24
|
end
|
25
25
|
|
26
26
|
def default_file_name
|
@@ -30,47 +30,47 @@ module Twine
|
|
30
30
|
def set_translation_for_key(key, lang, value)
|
31
31
|
value = value.gsub("\n", "\\n")
|
32
32
|
|
33
|
-
if @
|
34
|
-
|
35
|
-
reference = @
|
33
|
+
if @twine_file.definitions_by_key.include?(key)
|
34
|
+
definition = @twine_file.definitions_by_key[key]
|
35
|
+
reference = @twine_file.definitions_by_key[definition.reference_key] if definition.reference_key
|
36
36
|
|
37
37
|
if !reference or value != reference.translations[lang]
|
38
|
-
|
38
|
+
definition.translations[lang] = value
|
39
39
|
end
|
40
40
|
elsif @options[:consume_all]
|
41
|
-
Twine::stderr.puts "Adding new
|
42
|
-
current_section = @
|
41
|
+
Twine::stderr.puts "Adding new definition '#{key}' to twine file."
|
42
|
+
current_section = @twine_file.sections.find { |s| s.name == 'Uncategorized' }
|
43
43
|
unless current_section
|
44
|
-
current_section =
|
45
|
-
@
|
44
|
+
current_section = TwineSection.new('Uncategorized')
|
45
|
+
@twine_file.sections.insert(0, current_section)
|
46
46
|
end
|
47
|
-
|
48
|
-
current_section.
|
47
|
+
current_definition = TwineDefinition.new(key)
|
48
|
+
current_section.definitions << current_definition
|
49
49
|
|
50
50
|
if @options[:tags] && @options[:tags].length > 0
|
51
|
-
|
51
|
+
current_definition.tags = @options[:tags]
|
52
52
|
end
|
53
53
|
|
54
|
-
@
|
55
|
-
@
|
54
|
+
@twine_file.definitions_by_key[key] = current_definition
|
55
|
+
@twine_file.definitions_by_key[key].translations[lang] = value
|
56
56
|
else
|
57
|
-
Twine::stderr.puts "Warning: '#{key}' not found in
|
57
|
+
Twine::stderr.puts "Warning: '#{key}' not found in twine file."
|
58
58
|
end
|
59
|
-
if !@
|
60
|
-
@
|
59
|
+
if !@twine_file.language_codes.include?(lang)
|
60
|
+
@twine_file.add_language_code(lang)
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
64
|
def set_comment_for_key(key, comment)
|
65
65
|
return unless @options[:consume_comments]
|
66
66
|
|
67
|
-
if @
|
68
|
-
|
67
|
+
if @twine_file.definitions_by_key.include?(key)
|
68
|
+
definition = @twine_file.definitions_by_key[key]
|
69
69
|
|
70
|
-
reference = @
|
70
|
+
reference = @twine_file.definitions_by_key[definition.reference_key] if definition.reference_key
|
71
71
|
|
72
72
|
if !reference or comment != reference.raw_comment
|
73
|
-
|
73
|
+
definition.comment = comment
|
74
74
|
end
|
75
75
|
end
|
76
76
|
end
|
@@ -88,35 +88,35 @@ module Twine
|
|
88
88
|
end
|
89
89
|
|
90
90
|
def format_file(lang)
|
91
|
-
output_processor = Processors::OutputProcessor.new(@
|
92
|
-
|
91
|
+
output_processor = Processors::OutputProcessor.new(@twine_file, @options)
|
92
|
+
processed_twine_file = output_processor.process(lang)
|
93
93
|
|
94
|
-
return nil if
|
94
|
+
return nil if processed_twine_file.definitions_by_key.empty?
|
95
95
|
|
96
96
|
header = format_header(lang)
|
97
97
|
result = ""
|
98
98
|
result += header + "\n" if header
|
99
|
-
result += format_sections(
|
99
|
+
result += format_sections(processed_twine_file, lang)
|
100
100
|
end
|
101
101
|
|
102
102
|
def format_header(lang)
|
103
103
|
end
|
104
104
|
|
105
|
-
def format_sections(
|
106
|
-
sections =
|
105
|
+
def format_sections(twine_file, lang)
|
106
|
+
sections = twine_file.sections.map { |section| format_section(section, lang) }
|
107
107
|
sections.compact.join("\n")
|
108
108
|
end
|
109
109
|
|
110
110
|
def format_section_header(section)
|
111
111
|
end
|
112
112
|
|
113
|
-
def
|
114
|
-
|
113
|
+
def should_include_definition(definition, lang)
|
114
|
+
return !definition.translation_for_lang(lang).nil?
|
115
115
|
end
|
116
116
|
|
117
117
|
def format_section(section, lang)
|
118
|
-
|
119
|
-
return if
|
118
|
+
definitions = section.definitions.select { |definition| should_include_definition(definition, lang) }
|
119
|
+
return if definitions.empty?
|
120
120
|
|
121
121
|
result = ""
|
122
122
|
|
@@ -125,22 +125,22 @@ module Twine
|
|
125
125
|
result += "\n#{section_header}" if section_header
|
126
126
|
end
|
127
127
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
result +=
|
128
|
+
definitions.map! { |definition| format_definition(definition, lang) }
|
129
|
+
definitions.compact! # remove nil definitions
|
130
|
+
definitions.map! { |definition| "\n#{definition}" } # prepend newline
|
131
|
+
result += definitions.join
|
132
132
|
end
|
133
133
|
|
134
|
-
def
|
135
|
-
[format_comment(
|
134
|
+
def format_definition(definition, lang)
|
135
|
+
[format_comment(definition, lang), format_key_value(definition, lang)].compact.join
|
136
136
|
end
|
137
137
|
|
138
|
-
def format_comment(
|
138
|
+
def format_comment(definition, lang)
|
139
139
|
end
|
140
140
|
|
141
|
-
def format_key_value(
|
142
|
-
value =
|
143
|
-
key_value_pattern % { key: format_key(
|
141
|
+
def format_key_value(definition, lang)
|
142
|
+
value = definition.translation_for_lang(lang)
|
143
|
+
key_value_pattern % { key: format_key(definition.key.dup), value: format_value(value.dup) }
|
144
144
|
end
|
145
145
|
|
146
146
|
def key_value_pattern
|
@@ -7,16 +7,6 @@ module Twine
|
|
7
7
|
class Android < Abstract
|
8
8
|
include Twine::Placeholders
|
9
9
|
|
10
|
-
LANG_MAPPINGS = Hash[
|
11
|
-
'zh-rCN' => 'zh-Hans',
|
12
|
-
'zh-rHK' => 'zh-Hant',
|
13
|
-
'en-rGB' => 'en-UK',
|
14
|
-
'zh' => 'zh-Hans',
|
15
|
-
'in' => 'id',
|
16
|
-
'nb' => 'no'
|
17
|
-
# TODO: spanish
|
18
|
-
]
|
19
|
-
|
20
10
|
def format_name
|
21
11
|
'android'
|
22
12
|
end
|
@@ -30,24 +20,20 @@ module Twine
|
|
30
20
|
end
|
31
21
|
|
32
22
|
def default_file_name
|
33
|
-
|
23
|
+
'strings.xml'
|
34
24
|
end
|
35
25
|
|
36
26
|
def determine_language_given_path(path)
|
37
27
|
path_arr = path.split(File::SEPARATOR)
|
38
28
|
path_arr.each do |segment|
|
39
29
|
if segment == 'values'
|
40
|
-
return @
|
30
|
+
return @twine_file.language_codes[0]
|
41
31
|
else
|
42
32
|
# The language is defined by a two-letter ISO 639-1 language code, optionally followed by a two letter ISO 3166-1-alpha-2 region code (preceded by lowercase "r").
|
43
33
|
# see http://developer.android.com/guide/topics/resources/providing-resources.html#AlternativeResources
|
44
34
|
match = /^values-([a-z]{2}(-r[a-z]{2})?)$/i.match(segment)
|
45
|
-
|
46
|
-
|
47
|
-
lang = LANG_MAPPINGS.fetch(lang, lang)
|
48
|
-
lang.sub!('-r', '-')
|
49
|
-
return lang
|
50
|
-
end
|
35
|
+
|
36
|
+
return match[1].sub('-r', '-') if match
|
51
37
|
end
|
52
38
|
end
|
53
39
|
|
@@ -55,7 +41,7 @@ module Twine
|
|
55
41
|
end
|
56
42
|
|
57
43
|
def output_path_for_language(lang)
|
58
|
-
"values
|
44
|
+
"values-#{lang}"
|
59
45
|
end
|
60
46
|
|
61
47
|
def set_translation_for_key(key, lang, value)
|
@@ -69,35 +55,23 @@ module Twine
|
|
69
55
|
end
|
70
56
|
|
71
57
|
def read(io, lang)
|
72
|
-
|
73
|
-
|
74
|
-
comment_regex = /<!-- (.*) -->/
|
75
|
-
value_regex = /<string name="\w+">(.*)<\/string>/
|
76
|
-
key = nil
|
77
|
-
value = nil
|
58
|
+
document = REXML::Document.new io, :compress_whitespace => %w{ string }
|
59
|
+
|
78
60
|
comment = nil
|
61
|
+
document.root.children.each do |child|
|
62
|
+
if child.is_a? REXML::Comment
|
63
|
+
content = child.string.strip
|
64
|
+
comment = content if content.length > 0 and not content.start_with?("SECTION:")
|
65
|
+
elsif child.is_a? REXML::Element
|
66
|
+
next unless child.name == 'string'
|
79
67
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
value = value_match ? value_match[1] : ""
|
88
|
-
|
89
|
-
set_translation_for_key(key, lang, value)
|
90
|
-
if comment and comment.length > 0 and !comment.start_with?("SECTION:")
|
91
|
-
set_comment_for_key(key, comment)
|
92
|
-
end
|
93
|
-
comment = nil
|
94
|
-
end
|
95
|
-
|
96
|
-
comment_match = comment_regex.match(line)
|
97
|
-
if comment_match
|
98
|
-
comment = comment_match[1]
|
99
|
-
end
|
100
|
-
end
|
68
|
+
key = child.attributes['name']
|
69
|
+
|
70
|
+
set_translation_for_key(key, lang, child.text)
|
71
|
+
set_comment_for_key(key, comment) if comment
|
72
|
+
|
73
|
+
comment = nil
|
74
|
+
end
|
101
75
|
end
|
102
76
|
end
|
103
77
|
|
@@ -105,7 +79,7 @@ module Twine
|
|
105
79
|
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Android Strings File -->\n<!-- Generated by Twine #{Twine::VERSION} -->\n<!-- Language: #{lang} -->"
|
106
80
|
end
|
107
81
|
|
108
|
-
def format_sections(
|
82
|
+
def format_sections(twine_file, lang)
|
109
83
|
result = '<resources>'
|
110
84
|
|
111
85
|
result += super + "\n"
|
@@ -117,27 +91,52 @@ module Twine
|
|
117
91
|
"\t<!-- SECTION: #{section.name} -->"
|
118
92
|
end
|
119
93
|
|
120
|
-
def format_comment(
|
121
|
-
"\t<!-- #{
|
94
|
+
def format_comment(definition, lang)
|
95
|
+
"\t<!-- #{definition.comment.gsub('--', '—')} -->\n" if definition.comment
|
122
96
|
end
|
123
97
|
|
124
98
|
def key_value_pattern
|
125
99
|
"\t<string name=\"%{key}\">%{value}</string>"
|
126
100
|
end
|
127
101
|
|
128
|
-
def
|
129
|
-
#
|
130
|
-
# 1) apostrophes and quotes must be escaped with a backslash
|
102
|
+
def escape_value(value)
|
103
|
+
# escape double and single quotes, & signs and tags
|
131
104
|
value = escape_quotes(value)
|
132
105
|
value.gsub!("'", "\\\\'")
|
133
|
-
|
134
|
-
value
|
135
|
-
|
136
|
-
|
137
|
-
# 4) escape non resource identifier @ signs (http://developer.android.com/guide/topics/resources/accessing-resources.html#ResourcesFromXml)
|
106
|
+
value.gsub!(/&/, '&')
|
107
|
+
value.gsub!('<', '<')
|
108
|
+
|
109
|
+
# escape non resource identifier @ signs (http://developer.android.com/guide/topics/resources/accessing-resources.html#ResourcesFromXml)
|
138
110
|
resource_identifier_regex = /@(?!([a-z\.]+:)?[a-z+]+\/[a-zA-Z_]+)/ # @[<package_name>:]<resource_type>/<resource_name>
|
139
|
-
value.gsub
|
140
|
-
|
111
|
+
value.gsub(resource_identifier_regex, '\@')
|
112
|
+
end
|
113
|
+
|
114
|
+
# see http://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling
|
115
|
+
# however unescaped HTML markup like in "Welcome to <b>Android</b>!" is stripped when retrieved with getString() (http://stackoverflow.com/questions/9891996/)
|
116
|
+
def format_value(value)
|
117
|
+
value = value.dup
|
118
|
+
|
119
|
+
# convert placeholders (e.g. %@ -> %s)
|
120
|
+
value = convert_placeholders_from_twine_to_android(value)
|
121
|
+
|
122
|
+
# capture xliff tags and replace them with a placeholder
|
123
|
+
xliff_tags = []
|
124
|
+
value.gsub! /<xliff:g.+?<\/xliff:g>/ do
|
125
|
+
xliff_tags << $&
|
126
|
+
'TWINE_XLIFF_TAG_PLACEHOLDER'
|
127
|
+
end
|
128
|
+
|
129
|
+
# escape everything outside xliff tags
|
130
|
+
value = escape_value(value)
|
131
|
+
|
132
|
+
# put xliff tags back into place
|
133
|
+
xliff_tags.each do |xliff_tag|
|
134
|
+
# escape content of xliff tags
|
135
|
+
xliff_tag.gsub! /(<xliff:g.*?>)(.*)(<\/xliff:g>)/ do "#{$1}#{escape_value($2)}#{$3}" end
|
136
|
+
value.sub! 'TWINE_XLIFF_TAG_PLACEHOLDER', xliff_tag
|
137
|
+
end
|
138
|
+
|
139
|
+
# replace beginning and end spaces with \u0020. Otherwise Android strips them.
|
141
140
|
value.gsub(/\A *| *\z/) { |spaces| '\u0020' * spaces.length }
|
142
141
|
end
|
143
142
|
|
@@ -14,7 +14,7 @@ module Twine
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def default_file_name
|
17
|
-
|
17
|
+
'Localizable.strings'
|
18
18
|
end
|
19
19
|
|
20
20
|
def determine_language_given_path(path)
|
@@ -73,8 +73,8 @@ module Twine
|
|
73
73
|
"\"%{key}\" = \"%{value}\";\n"
|
74
74
|
end
|
75
75
|
|
76
|
-
def format_comment(
|
77
|
-
"/* #{
|
76
|
+
def format_comment(definition, lang)
|
77
|
+
"/* #{definition.comment.gsub('*/', '* /')} */\n" if definition.comment
|
78
78
|
end
|
79
79
|
|
80
80
|
def format_key(key)
|
@@ -9,23 +9,17 @@ module Twine
|
|
9
9
|
'.po'
|
10
10
|
end
|
11
11
|
|
12
|
-
def can_handle_directory?(path)
|
13
|
-
Dir.entries(path).any? { |item| /^.+\.po$/.match(item) }
|
14
|
-
end
|
15
|
-
|
16
12
|
def default_file_name
|
17
|
-
|
13
|
+
'strings.po'
|
18
14
|
end
|
19
15
|
|
20
16
|
def determine_language_given_path(path)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
28
|
-
|
17
|
+
path_arr = path.split(File::SEPARATOR)
|
18
|
+
path_arr.each do |segment|
|
19
|
+
match = /(..)\.po$/.match(segment)
|
20
|
+
return match[1] if match
|
21
|
+
end
|
22
|
+
|
29
23
|
return
|
30
24
|
end
|
31
25
|
|
@@ -63,26 +57,26 @@ module Twine
|
|
63
57
|
end
|
64
58
|
|
65
59
|
def format_file(lang)
|
66
|
-
@default_lang = @
|
60
|
+
@default_lang = @twine_file.language_codes[0]
|
67
61
|
result = super
|
68
62
|
@default_lang = nil
|
69
63
|
result
|
70
64
|
end
|
71
65
|
|
72
66
|
def format_header(lang)
|
73
|
-
"##\n # Django Strings File\n # Generated by Twine #{Twine::VERSION}\n # Language: #{lang}\n"
|
67
|
+
"##\n # Django Strings File\n # Generated by Twine #{Twine::VERSION}\n # Language: #{lang}\nmsgid \"\"\nmsgstr \"\"\n\"Content-Type: text/plain; charset=UTF-8\\n\""
|
74
68
|
end
|
75
69
|
|
76
70
|
def format_section_header(section)
|
77
71
|
"#--------- #{section.name} ---------#\n"
|
78
72
|
end
|
79
73
|
|
80
|
-
def
|
81
|
-
[format_comment(
|
74
|
+
def format_definition(definition, lang)
|
75
|
+
[format_comment(definition, lang), format_base_translation(definition), format_key_value(definition, lang)].compact.join
|
82
76
|
end
|
83
77
|
|
84
|
-
def format_base_translation(
|
85
|
-
base_translation =
|
78
|
+
def format_base_translation(definition)
|
79
|
+
base_translation = definition.translations[@default_lang]
|
86
80
|
"# base translation: \"#{base_translation}\"\n" if base_translation
|
87
81
|
end
|
88
82
|
|
@@ -91,8 +85,8 @@ module Twine
|
|
91
85
|
"msgstr \"%{value}\"\n"
|
92
86
|
end
|
93
87
|
|
94
|
-
def format_comment(
|
95
|
-
"#. #{escape_quotes(
|
88
|
+
def format_comment(definition, lang)
|
89
|
+
"#. #{escape_quotes(definition.comment)}\n" if definition.comment
|
96
90
|
end
|
97
91
|
|
98
92
|
def format_key(key)
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Twine
|
2
2
|
module Formatters
|
3
3
|
class Flash < Abstract
|
4
|
+
include Twine::Placeholders
|
5
|
+
|
4
6
|
def format_name
|
5
7
|
'flash'
|
6
8
|
end
|
@@ -9,16 +11,18 @@ module Twine
|
|
9
11
|
'.properties'
|
10
12
|
end
|
11
13
|
|
12
|
-
def can_handle_directory?(path)
|
13
|
-
return false
|
14
|
-
end
|
15
|
-
|
16
14
|
def default_file_name
|
17
|
-
|
15
|
+
'resources.properties'
|
18
16
|
end
|
19
17
|
|
20
18
|
def determine_language_given_path(path)
|
21
|
-
|
19
|
+
# match two-letter language code, optionally followed by a two letter region code
|
20
|
+
path.split(File::SEPARATOR).reverse.find { |segment| segment =~ /^([a-z]{2}(-[a-z]{2})?)$/i }
|
21
|
+
end
|
22
|
+
|
23
|
+
def set_translation_for_key(key, lang, value)
|
24
|
+
value = convert_placeholders_from_flash_to_twine(value)
|
25
|
+
super(key, lang, value)
|
22
26
|
end
|
23
27
|
|
24
28
|
def read(io, lang)
|
@@ -28,23 +32,17 @@ module Twine
|
|
28
32
|
if match
|
29
33
|
key = match[1]
|
30
34
|
value = match[2].strip
|
31
|
-
|
35
|
+
|
32
36
|
set_translation_for_key(key, lang, value)
|
33
|
-
if last_comment
|
34
|
-
set_comment_for_key(key, last_comment)
|
35
|
-
end
|
37
|
+
set_comment_for_key(key, last_comment) if last_comment
|
36
38
|
end
|
37
39
|
|
38
40
|
match = /# *(.*)/.match(line)
|
39
|
-
|
40
|
-
last_comment = match[1]
|
41
|
-
else
|
42
|
-
last_comment = nil
|
43
|
-
end
|
41
|
+
last_comment = match ? match[1] : nil
|
44
42
|
end
|
45
43
|
end
|
46
44
|
|
47
|
-
def format_sections(
|
45
|
+
def format_sections(twine_file, lang)
|
48
46
|
super + "\n"
|
49
47
|
end
|
50
48
|
|
@@ -56,8 +54,8 @@ module Twine
|
|
56
54
|
"## #{section.name} ##\n"
|
57
55
|
end
|
58
56
|
|
59
|
-
def format_comment(
|
60
|
-
"# #{
|
57
|
+
def format_comment(definition, lang)
|
58
|
+
"# #{definition.comment}\n" if definition.comment
|
61
59
|
end
|
62
60
|
|
63
61
|
def key_value_pattern
|
@@ -65,8 +63,7 @@ module Twine
|
|
65
63
|
end
|
66
64
|
|
67
65
|
def format_value(value)
|
68
|
-
|
69
|
-
value.gsub(/%[d@]/) { placeHolderNumber += 1; '{%d}' % placeHolderNumber }
|
66
|
+
convert_placeholders_from_twine_to_flash(value)
|
70
67
|
end
|
71
68
|
end
|
72
69
|
end
|