twine 0.9.1 → 0.10.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 +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
|