twine 1.0.2 → 1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +45 -6
- data/lib/twine/cli.rb +24 -2
- data/lib/twine/formatters/abstract.rb +10 -3
- data/lib/twine/formatters/android.rb +21 -12
- data/lib/twine/formatters/apple.rb +1 -1
- data/lib/twine/formatters/django.rb +7 -16
- data/lib/twine/formatters/flash.rb +0 -5
- data/lib/twine/formatters/gettext.rb +4 -14
- data/lib/twine/formatters/jquery.rb +5 -10
- data/lib/twine/placeholders.rb +7 -1
- data/lib/twine/plugin.rb +2 -1
- data/lib/twine/runner.rb +33 -14
- data/lib/twine/twine_file.rb +1 -1
- data/lib/twine/version.rb +1 -1
- data/test/fixtures/consume_localization_archive.zip +0 -0
- data/test/fixtures/formatter_django.po +5 -6
- data/test/fixtures/formatter_gettext.po +2 -2
- data/test/fixtures/formatter_gettext_quotes.po +10 -0
- data/test/test_cli.rb +27 -3
- data/test/test_consume_localization_archive.rb +13 -7
- data/test/test_formatters.rb +203 -11
- data/test/test_generate_all_localization_files.rb +24 -14
- data/test/test_generate_localization_archive.rb +9 -4
- data/test/test_placeholders.rb +21 -0
- data/test/test_validate_twine_file.rb +8 -0
- data/test/twine_test.rb +2 -2
- metadata +17 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d8667f50ad7147b8b7edac0d82ce542c2ecc973604c8334e878513b6acaa9a14
|
4
|
+
data.tar.gz: 1ceb2b2b18dea8a3585bd91a1b427b30ebd9c888817dcf7140b3d211cd9e157c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a961953ec7b74510c10e17114b84c227bac43117a869ba4254d7410a16ea9413e4133a2c9f099c3d67922fd6e1632d841f366e79d28687e61f73861721dea43a
|
7
|
+
data.tar.gz: 954b69b0d6e0fadec38cf145e0e1772eb1fde0259337b45ffac172d41b6f6c031b33373c768cfbb48ad3e9cfb1565dc9e91db8e825023421fdc30098439a4d86
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Twine
|
2
2
|
|
3
|
-
[![Continuous Integration by CircleCI](https://circleci.com/gh/
|
3
|
+
[![Continuous Integration by CircleCI](https://circleci.com/gh/scelis/twine.svg?style=shield)](https://circleci.com/gh/scelis/twine)
|
4
4
|
|
5
5
|
Twine is a command line tool for managing your strings and their translations. These are all stored in a master text file and then Twine uses this file to import and export localization files in a variety of types, including iOS and Mac OS X `.strings` files, Android `.xml` files, gettext `.po` files, and [jquery-localize][jquerylocalize] `.json` files. This allows individuals and companies to easily share translations across multiple projects, as well as export localization files in any format the user wants.
|
6
6
|
|
@@ -80,7 +80,7 @@ Twine currently supports the following output formats:
|
|
80
80
|
* [Android String Resources][androidstrings] (format: android)
|
81
81
|
* HTML tags will be escaped by replacing `<` with `<`
|
82
82
|
* Tags inside `<![CDATA[` won't be escaped.
|
83
|
-
* Supports [basic styling][androidstyling]
|
83
|
+
* Supports [basic styling][androidstyling] according to [Android documentation](https://developer.android.com/guide/topics/resources/string-resource.html#StylingWithHTML). All of the documented tags are supported, in addition to `<a>` links.
|
84
84
|
* These tags will *not* be escaped if the string doesn't contain placeholders. You can reference them directly in your layouts or by using [`getText()`](https://developer.android.com/reference/android/content/res/Resources.html#getText(int)) to read them programatically.
|
85
85
|
* These tags *will* be escaped if the string contains placeholders. You can use [`getString()`](https://developer.android.com/reference/android/content/res/Resources.html#getString(int,%20java.lang.Object...)) combined with [`fromHtml`](https://developer.android.com/reference/android/text/Html.html#fromHtml(java.lang.String)) as shown in the [documentation][androidstyling] to display them.
|
86
86
|
* See [\#212](https://github.com/scelis/twine/issues/212) for details.
|
@@ -150,7 +150,7 @@ This command validates that the Twine data file can be parsed, contains no dupli
|
|
150
150
|
The easiest way to create your first Twine data file is to run the [`consume-all-localization-files`](#consume-all-localization-files) command. The one caveat is to first create a blank file to use as your starting point. Then, just point the `consume-all-localization-files` command at a directory in your project containing all of your localization files.
|
151
151
|
|
152
152
|
$ touch twine.txt
|
153
|
-
$ twine consume-all-localization-files twine.txt Resources/Locales --developer-language en --consume-all --consume-comments
|
153
|
+
$ twine consume-all-localization-files twine.txt Resources/Locales --developer-language en --consume-all --consume-comments --format apple/android/gettext/jquery/django/tizen/flash
|
154
154
|
|
155
155
|
## Twine and Your Build Process
|
156
156
|
|
@@ -174,7 +174,10 @@ Now, whenever you build your application, Xcode will automatically invoke Twine
|
|
174
174
|
|
175
175
|
### Android Studio/Gradle
|
176
176
|
|
177
|
-
|
177
|
+
#### Standard
|
178
|
+
|
179
|
+
Add the following code to `app/build.gradle`:
|
180
|
+
|
178
181
|
```
|
179
182
|
task generateLocalizations {
|
180
183
|
String script = 'if hash twine 2>/dev/null; then twine generate-localization-file twine.txt ./src/main/res/values/generated_strings.xml; fi'
|
@@ -183,10 +186,46 @@ task generateLocalizations {
|
|
183
186
|
args '-c', script
|
184
187
|
}
|
185
188
|
}
|
189
|
+
|
190
|
+
preBuild {
|
191
|
+
dependsOn generateLocalizations
|
192
|
+
}
|
186
193
|
```
|
187
194
|
|
188
|
-
|
195
|
+
#### Using [jruby](http://jruby.org)
|
189
196
|
|
197
|
+
With this approach, developers do not need to manually install ruby, gem, or twine.
|
198
|
+
|
199
|
+
Add the following code to `app/build.gradle`:
|
200
|
+
|
201
|
+
```
|
202
|
+
buildscript {
|
203
|
+
repositories { jcenter() }
|
204
|
+
|
205
|
+
dependencies {
|
206
|
+
/* NOTE: Set your prefered version of jruby here. */
|
207
|
+
classpath "com.github.jruby-gradle:jruby-gradle-plugin:1.5.0"
|
208
|
+
}
|
209
|
+
}
|
210
|
+
|
211
|
+
apply plugin: 'com.github.jruby-gradle.base'
|
212
|
+
|
213
|
+
dependencies {
|
214
|
+
/* NOTE: Set your prefered version of twine here. */
|
215
|
+
jrubyExec 'rubygems:twine:1.0.3'
|
216
|
+
}
|
217
|
+
|
218
|
+
task generateLocalizations (type: JRubyExec) {
|
219
|
+
dependsOn jrubyPrepare
|
220
|
+
jrubyArgs '-S'
|
221
|
+
script "twine"
|
222
|
+
scriptArgs 'generate-localization-file', 'twine.txt', './src/main/res/values/generated_strings.xml'
|
223
|
+
}
|
224
|
+
|
225
|
+
preBuild {
|
226
|
+
dependsOn generateLocalizations
|
227
|
+
}
|
228
|
+
```
|
190
229
|
|
191
230
|
## User Interface
|
192
231
|
|
@@ -224,4 +263,4 @@ Many thanks to all of the contributors to the Twine project, including:
|
|
224
263
|
[djangopo]: https://docs.djangoproject.com/en/dev/topics/i18n/translation/
|
225
264
|
[tizen]: https://developer.tizen.org/documentation/articles/localization
|
226
265
|
[flash]: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/resources/IResourceManager.html#getString()
|
227
|
-
[printf]: https://en.wikipedia.org/wiki/Printf_format_string
|
266
|
+
[printf]: https://en.wikipedia.org/wiki/Printf_format_string
|
data/lib/twine/cli.rb
CHANGED
@@ -45,6 +45,14 @@ module Twine
|
|
45
45
|
files are UTF-16 without BOM, you need to specify if it's UTF-16LE or UTF16-BE.
|
46
46
|
DESC
|
47
47
|
},
|
48
|
+
escape_all_tags: {
|
49
|
+
switch: ['--[no-]escape-all-tags'],
|
50
|
+
description: <<-DESC,
|
51
|
+
Always escape all HTML tags. By default the Android formatter will ONLY escape styling tags, if a
|
52
|
+
string also contains placeholders. This flag enforces that styling tags are escaped regardless of
|
53
|
+
placeholders.
|
54
|
+
DESC
|
55
|
+
},
|
48
56
|
file_name: {
|
49
57
|
switch: ['-n', '--file-name FILE_NAME'],
|
50
58
|
description: 'This flag may be used to overwrite the default file name of the format.'
|
@@ -78,6 +86,10 @@ module Twine
|
|
78
86
|
switch: ['-p', '--[no-]pedantic'],
|
79
87
|
description: 'When validating a Twine file, perform additional checks that go beyond pure validity (like presence of tags).'
|
80
88
|
},
|
89
|
+
quiet: {
|
90
|
+
switch: ['-q', '--[no-]quiet'],
|
91
|
+
description: 'Suppress all console output except error messages.'
|
92
|
+
},
|
81
93
|
tags: {
|
82
94
|
switch: ['-t', '--tags TAG1,TAG2,TAG3', Array],
|
83
95
|
description: <<-DESC,
|
@@ -107,9 +119,11 @@ module Twine
|
|
107
119
|
optional_options: [
|
108
120
|
:developer_language,
|
109
121
|
:encoding,
|
122
|
+
:escape_all_tags,
|
110
123
|
:format,
|
111
124
|
:include,
|
112
125
|
:languages,
|
126
|
+
:quiet,
|
113
127
|
:tags,
|
114
128
|
:untagged,
|
115
129
|
:validate
|
@@ -128,9 +142,11 @@ module Twine
|
|
128
142
|
:create_folders,
|
129
143
|
:developer_language,
|
130
144
|
:encoding,
|
145
|
+
:escape_all_tags,
|
131
146
|
:file_name,
|
132
147
|
:format,
|
133
148
|
:include,
|
149
|
+
:quiet,
|
134
150
|
:tags,
|
135
151
|
:untagged,
|
136
152
|
:validate
|
@@ -146,7 +162,9 @@ module Twine
|
|
146
162
|
optional_options: [
|
147
163
|
:developer_language,
|
148
164
|
:encoding,
|
165
|
+
:escape_all_tags,
|
149
166
|
:include,
|
167
|
+
:quiet,
|
150
168
|
:tags,
|
151
169
|
:untagged,
|
152
170
|
:validate
|
@@ -164,6 +182,7 @@ module Twine
|
|
164
182
|
:format,
|
165
183
|
:languages,
|
166
184
|
:output_path,
|
185
|
+
:quiet,
|
167
186
|
:tags
|
168
187
|
],
|
169
188
|
option_validation: Proc.new { |options|
|
@@ -183,6 +202,7 @@ module Twine
|
|
183
202
|
:encoding,
|
184
203
|
:format,
|
185
204
|
:output_path,
|
205
|
+
:quiet,
|
186
206
|
:tags
|
187
207
|
],
|
188
208
|
example: 'twine consume-all-localization-files twine.txt Resources/Locales/ --developer-language en --tags DefaultTag1,DefaultTag2'
|
@@ -197,6 +217,7 @@ module Twine
|
|
197
217
|
:encoding,
|
198
218
|
:format,
|
199
219
|
:output_path,
|
220
|
+
:quiet,
|
200
221
|
:tags
|
201
222
|
],
|
202
223
|
example: 'twine consume-localization-archive twine.txt LocDrop5.zip'
|
@@ -206,7 +227,8 @@ module Twine
|
|
206
227
|
arguments: [:twine_file],
|
207
228
|
optional_options: [
|
208
229
|
:developer_language,
|
209
|
-
:pedantic
|
230
|
+
:pedantic,
|
231
|
+
:quiet
|
210
232
|
],
|
211
233
|
example: 'twine validate-twine-file twine.txt'
|
212
234
|
}
|
@@ -227,7 +249,7 @@ module Twine
|
|
227
249
|
|
228
250
|
mapped_command = DEPRECATED_COMMAND_MAPPINGS[command]
|
229
251
|
if mapped_command
|
230
|
-
Twine::
|
252
|
+
Twine::stdout.puts "WARNING: Twine commands names have changed. `#{command}` is now `#{mapped_command}`. The old command is deprecated and will soon stop working. For more information please check the documentation at https://github.com/mobiata/twine"
|
231
253
|
command = mapped_command
|
232
254
|
end
|
233
255
|
|
@@ -3,6 +3,8 @@ require 'fileutils'
|
|
3
3
|
module Twine
|
4
4
|
module Formatters
|
5
5
|
class Abstract
|
6
|
+
LANGUAGE_CODE_WITH_OPTIONAL_REGION_CODE = "[a-z]{2}(?:-[A-Za-z]{2})?"
|
7
|
+
|
6
8
|
attr_accessor :twine_file
|
7
9
|
attr_accessor :options
|
8
10
|
|
@@ -38,7 +40,7 @@ module Twine
|
|
38
40
|
definition.translations[lang] = value
|
39
41
|
end
|
40
42
|
elsif @options[:consume_all]
|
41
|
-
Twine::
|
43
|
+
Twine::stdout.puts "Adding new definition '#{key}' to twine file."
|
42
44
|
current_section = @twine_file.sections.find { |s| s.name == 'Uncategorized' }
|
43
45
|
unless current_section
|
44
46
|
current_section = TwineSection.new('Uncategorized')
|
@@ -54,7 +56,7 @@ module Twine
|
|
54
56
|
@twine_file.definitions_by_key[key] = current_definition
|
55
57
|
@twine_file.definitions_by_key[key].translations[lang] = value
|
56
58
|
else
|
57
|
-
Twine::
|
59
|
+
Twine::stdout.puts "WARNING: '#{key}' not found in twine file."
|
58
60
|
end
|
59
61
|
if !@twine_file.language_codes.include?(lang)
|
60
62
|
@twine_file.add_language_code(lang)
|
@@ -76,7 +78,12 @@ module Twine
|
|
76
78
|
end
|
77
79
|
|
78
80
|
def determine_language_given_path(path)
|
79
|
-
|
81
|
+
only_language_and_region = /^#{LANGUAGE_CODE_WITH_OPTIONAL_REGION_CODE}$/i
|
82
|
+
basename = File.basename(path, File.extname(path))
|
83
|
+
return basename if basename =~ only_language_and_region
|
84
|
+
return basename if @twine_file.language_codes.include? basename
|
85
|
+
|
86
|
+
path.split(File::SEPARATOR).reverse.find { |segment| segment =~ only_language_and_region }
|
80
87
|
end
|
81
88
|
|
82
89
|
def output_path_for_language(lang)
|
@@ -37,11 +37,15 @@ module Twine
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
return
|
40
|
+
return super
|
41
41
|
end
|
42
42
|
|
43
43
|
def output_path_for_language(lang)
|
44
|
-
|
44
|
+
if lang == @twine_file.language_codes[0]
|
45
|
+
"values"
|
46
|
+
else
|
47
|
+
"values-#{lang}".gsub(/-(\p{Lu})/, '-r\1')
|
48
|
+
end
|
45
49
|
end
|
46
50
|
|
47
51
|
def set_translation_for_key(key, lang, value)
|
@@ -56,7 +60,7 @@ module Twine
|
|
56
60
|
|
57
61
|
def read(io, lang)
|
58
62
|
document = REXML::Document.new io, :compress_whitespace => %w{ string }
|
59
|
-
|
63
|
+
document.context[:attribute_quote] = :quote
|
60
64
|
comment = nil
|
61
65
|
document.root.children.each do |child|
|
62
66
|
if child.is_a? REXML::Comment
|
@@ -67,7 +71,8 @@ module Twine
|
|
67
71
|
|
68
72
|
key = child.attributes['name']
|
69
73
|
|
70
|
-
|
74
|
+
content = child.children.map(&:to_s).join
|
75
|
+
set_translation_for_key(key, lang, content)
|
71
76
|
set_comment_for_key(key, comment) if comment
|
72
77
|
|
73
78
|
comment = nil
|
@@ -108,22 +113,26 @@ module Twine
|
|
108
113
|
|
109
114
|
# http://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling
|
110
115
|
def escape_value(value)
|
111
|
-
inside_cdata = /<\!\[CDATA\[((?!\]\]>).)*$/
|
112
|
-
|
116
|
+
inside_cdata = /<\!\[CDATA\[((?!\]\]>).)*$/ # opening CDATA tag ('<![CDATA[') not followed by a closing tag (']]>')
|
117
|
+
inside_opening_tag = /<(a|font|span|p)\s?((?!>).)*$/ # tag start ('<a ', '<font ', '<span ' or '<p ') not followed by a '>'
|
113
118
|
|
114
119
|
# escape double and single quotes and & signs
|
115
|
-
value = gsub_unless(value, '"', '\\"') { |substring| substring =~ inside_cdata || substring =~
|
120
|
+
value = gsub_unless(value, '"', '\\"') { |substring| substring =~ inside_cdata || substring =~ inside_opening_tag }
|
116
121
|
value = gsub_unless(value, "'", "\\'") { |substring| substring =~ inside_cdata }
|
117
|
-
value = gsub_unless(value, /&/, '&') { |substring| substring =~ inside_cdata || substring =~
|
122
|
+
value = gsub_unless(value, /&/, '&') { |substring| substring =~ inside_cdata || substring =~ inside_opening_tag }
|
118
123
|
|
119
124
|
# if `value` contains a placeholder, escape all angle brackets
|
120
125
|
# if not, escape opening angle brackes unless it's a supported styling tag
|
121
126
|
# https://github.com/scelis/twine/issues/212
|
122
127
|
# https://stackoverflow.com/questions/3235131/#18199543
|
123
|
-
if number_of_twine_placeholders(value) > 0
|
124
|
-
|
125
|
-
|
126
|
-
|
128
|
+
if number_of_twine_placeholders(value) > 0 or @options[:escape_all_tags]
|
129
|
+
# matches all `<` but <![CDATA
|
130
|
+
angle_bracket = /<(?!(\/?(\!\[CDATA)))/
|
131
|
+
else
|
132
|
+
# matches all '<' but <b>, <em>, <i>, <cite>, <dfn>, <big>, <small>, <font>, <tt>, <s>,
|
133
|
+
# <strike>, <del>, <u>, <super>, <sub>, <ul>, <li>, <br>, <div>, <span>, <p>, <a>
|
134
|
+
# and <![CDATA
|
135
|
+
angle_bracket = /<(?!(\/?(b|em|i|cite|dfn|big|small|font|tt|s|strike|del|u|super|sub|ul|li|br|div|span|p|a|\!\[CDATA)))/
|
127
136
|
end
|
128
137
|
value = gsub_unless(value, angle_bracket, '<') { |substring| substring =~ inside_cdata }
|
129
138
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Twine
|
2
2
|
module Formatters
|
3
|
+
# For a description of the .po file format, see https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html
|
3
4
|
class Django < Abstract
|
4
5
|
def format_name
|
5
6
|
'django'
|
@@ -13,22 +14,11 @@ module Twine
|
|
13
14
|
'strings.po'
|
14
15
|
end
|
15
16
|
|
16
|
-
def determine_language_given_path(path)
|
17
|
-
path_arr = path.split(File::SEPARATOR)
|
18
|
-
path_arr.each do |segment|
|
19
|
-
match = /([a-z]{2}(-[A-Za-z]{2})?)\.po$/.match(segment)
|
20
|
-
return match[1] if match
|
21
|
-
end
|
22
|
-
|
23
|
-
return
|
24
|
-
end
|
25
|
-
|
26
17
|
def read(io, lang)
|
27
|
-
comment_regex =
|
28
|
-
key_regex =
|
29
|
-
value_regex =
|
18
|
+
comment_regex = /^\s*#\. *"?(.*)"?$/
|
19
|
+
key_regex = /^msgid *"(.*)"$/
|
20
|
+
value_regex = /^msgstr *"(.*)"$/m
|
30
21
|
|
31
|
-
last_comment = nil
|
32
22
|
while line = io.gets
|
33
23
|
comment_match = comment_regex.match(line)
|
34
24
|
if comment_match
|
@@ -64,11 +54,12 @@ module Twine
|
|
64
54
|
end
|
65
55
|
|
66
56
|
def format_header(lang)
|
67
|
-
|
57
|
+
# see https://www.gnu.org/software/trans-coord/manual/gnun/html_node/PO-Header.html for details
|
58
|
+
"# Django Strings File\n# Generated by Twine #{Twine::VERSION}\n# Language: #{lang}\nmsgid \"\"\nmsgstr \"\"\n\"Content-Type: text/plain; charset=UTF-8\\n\""
|
68
59
|
end
|
69
60
|
|
70
61
|
def format_section_header(section)
|
71
|
-
"
|
62
|
+
"# --------- #{section.name} --------- #\n"
|
72
63
|
end
|
73
64
|
|
74
65
|
def format_definition(definition, lang)
|
@@ -15,11 +15,6 @@ module Twine
|
|
15
15
|
'resources.properties'
|
16
16
|
end
|
17
17
|
|
18
|
-
def determine_language_given_path(path)
|
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
18
|
def set_translation_for_key(key, lang, value)
|
24
19
|
value = convert_placeholders_from_flash_to_twine(value)
|
25
20
|
super(key, lang, value)
|
@@ -15,16 +15,6 @@ module Twine
|
|
15
15
|
'strings.po'
|
16
16
|
end
|
17
17
|
|
18
|
-
def determine_language_given_path(path)
|
19
|
-
path_arr = path.split(File::SEPARATOR)
|
20
|
-
path_arr.each do |segment|
|
21
|
-
match = /([a-z]{2}(-[A-Za-z]{2})?)\.po$/.match(segment)
|
22
|
-
return match[1] if match
|
23
|
-
end
|
24
|
-
|
25
|
-
return
|
26
|
-
end
|
27
|
-
|
28
18
|
def read(io, lang)
|
29
19
|
comment_regex = /#.? *"(.*)"$/
|
30
20
|
key_regex = /msgctxt *"(.*)"$/
|
@@ -65,7 +55,7 @@ module Twine
|
|
65
55
|
end
|
66
56
|
|
67
57
|
def format_header(lang)
|
68
|
-
"msgid \"\"\nmsgstr \"\"\n\"Language: #{lang}
|
58
|
+
"msgid \"\"\nmsgstr \"\"\n\"Language: #{lang}\"\n\"X-Generator: Twine #{Twine::VERSION}\"\n"
|
69
59
|
end
|
70
60
|
|
71
61
|
def format_section_header(section)
|
@@ -86,15 +76,15 @@ module Twine
|
|
86
76
|
end
|
87
77
|
|
88
78
|
def format_key(key)
|
89
|
-
"msgctxt \"#{key}\"\n"
|
79
|
+
"msgctxt \"#{escape_quotes(key)}\"\n"
|
90
80
|
end
|
91
81
|
|
92
82
|
def format_base_translation(definition)
|
93
|
-
"msgid \"#{definition.translations[@default_lang]}\"\n"
|
83
|
+
"msgid \"#{escape_quotes(definition.translations[@default_lang])}\"\n"
|
94
84
|
end
|
95
85
|
|
96
86
|
def format_value(value)
|
97
|
-
"msgstr \"#{value}\"\n"
|
87
|
+
"msgstr \"#{escape_quotes(value)}\"\n"
|
98
88
|
end
|
99
89
|
end
|
100
90
|
end
|
@@ -14,22 +14,17 @@ module Twine
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def determine_language_given_path(path)
|
17
|
-
|
18
|
-
|
19
|
-
match = /^((.+)-)?([^-]+)\.json$/.match(segment)
|
20
|
-
if match
|
21
|
-
return match[3]
|
22
|
-
end
|
23
|
-
end
|
17
|
+
match = /^.+-([^-]{2})\.json$/.match File.basename(path)
|
18
|
+
return match[1] if match
|
24
19
|
|
25
|
-
return
|
20
|
+
return super
|
26
21
|
end
|
27
22
|
|
28
23
|
def read(io, lang)
|
29
24
|
begin
|
30
25
|
require "json"
|
31
26
|
rescue LoadError
|
32
|
-
raise Twine::Error.new "You must run
|
27
|
+
raise Twine::Error.new "You must run `gem install json` in order to read or write jquery-localize files."
|
33
28
|
end
|
34
29
|
|
35
30
|
json = JSON.load(io)
|
@@ -46,7 +41,7 @@ module Twine
|
|
46
41
|
|
47
42
|
def format_sections(twine_file, lang)
|
48
43
|
sections = twine_file.sections.map { |section| format_section(section, lang) }
|
49
|
-
sections.delete_if
|
44
|
+
sections.delete_if(&:empty?)
|
50
45
|
sections.join(",\n\n")
|
51
46
|
end
|
52
47
|
|