forked from justinklemm/i18n-strings-files
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.coffee
197 lines (171 loc) · 6.53 KB
/
index.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
fs = require('fs')
Iconv = require('iconv').Iconv
i18nStringsFiles = ->
i18nStringsFiles.prototype.readFile = (file, options, callback) ->
encoding = null
wantsComments = false
# check if encoding was excluded and callback specified as 2nd param
if typeof callback == "undefined" and typeof options == "function"
callback = options
encoding = null
else if typeof options == "string" # for backward compatibility
encoding = options
else if typeof options == "object"
encoding = options['encoding']
wantsComments = options['wantsComments']
# read passed in file
fs.readFile file, (err, buffer) =>
# if there's an error, callback with it and return
if err then return callback?(err, null)
# convert buffer from file into utf-8 string, then parse
str = @convertBufferToString(buffer, encoding)
data = @parse(str, wantsComments)
# callback with parsed object
callback?(null, data)
i18nStringsFiles.prototype.readFileSync = (file, options) ->
encoding = null
wantsComments = false
if typeof options == 'string'
encoding = options
else if typeof options == 'object'
encoding = options['encoding']
wantsComments = options['wantsComments']
# read the passed in file and convert to utf-8 string
buffer = fs.readFileSync(file)
str = @convertBufferToString(buffer, encoding)
# pass file contents string to parse() and return
return @parse(str, wantsComments)
i18nStringsFiles.prototype.writeFile = (file, data, options, callback) ->
encoding = null
wantsComments = false
# check if encoding was excluded and callback specified as 2nd param
if typeof callback == "undefined" and typeof options == "function"
callback = options
encoding = null
else if typeof options == "string" # for backward compatibility
encoding = options
else if typeof options == "object"
encoding = options['encoding']
wantsComments = options['wantsComments']
# build string and convert from utf-8 to output buffer
str = @compile(data, options)
buffer = @convertStringToBuffer(str, encoding)
# write buffer to file
fs.writeFile file, buffer, (err) =>
# callback with any errors
callback?(err)
i18nStringsFiles.prototype.writeFileSync = (file, data, options) ->
encoding = null
wantsComments = false
if typeof options == 'string'
encoding = options
else if typeof options == 'object'
encoding = options['encoding']
wantsComments = options['wantsComments']
# build string and convert from utf-8 to output buffer
str = @compile(data, options)
buffer = @convertStringToBuffer(str, encoding)
# write buffer to file
return fs.writeFileSync(file, buffer)
i18nStringsFiles.prototype.convertBufferToString = (buffer, encoding) ->
# if no encoding is passed in, default to utf-16 (as recommended by Apple)
if !encoding then encoding = 'UTF-16'
# convert buffer to utf-8 string and return
iconv = new Iconv(encoding, 'UTF-8')
return iconv.convert(buffer).toString('utf8')
i18nStringsFiles.prototype.convertStringToBuffer = (str, encoding) ->
# if no encoding is passed in, default to utf-16 (as recommended by Apple)
if !encoding then encoding = 'UTF-16'
# convert string from utf-8 to buffer in output encoding
iconv = new Iconv('UTF-8', encoding)
return iconv.convert(str)
i18nStringsFiles.prototype.parse = (input, wantsComments) ->
# if wantsComments is not specified, default to false
if !wantsComments then wantsComments = false
# patterns used for parsing
reAssign = /[^\\]" = "/
reLineEnd = /";$/
reCommentEnd = /\*\/$/
# holds resulting hash
result = {}
# splt into lines
lines = input.split("\n")
# previous comment
currentComment = ''
nextLineIsComment = false
# process line by line
lines.forEach (line) ->
# strip extra whitespace
line = line.trim()
# normalize spacing around assignment operator
line = line.replace(/([^\\])("\s*=\s*")/g, "$1\" = \"")
# remove any space between final quote and semi-colon
line = line.replace(/"\s+;/g, '";')
# check if starts with '/*', store it in previousComment var
if nextLineIsComment
if line.search(reCommentEnd) == -1
currentComment += '\n' + line.trim()
else
nextLineIsComment = false
currentComment += '\n' + line.substr(0, line.search(reCommentEnd)).trim()
else if line.substr(0, 2) == '/*'
if line.search(reCommentEnd) == -1
nextLineIsComment = true
currentComment = line.substr(2).trim()
else
nextLineIsComment = false
currentComment = line.substr(2, line.search(reCommentEnd)-2).trim()
# check for first quote, assignment operator, and final semi-colon
if line.substr(0, 1) != '"' or line.search(reAssign) == -1 or line.search(reLineEnd) == -1 then return
# get msgid
msgid = line
msgid = msgid.substr(1)
msgid = msgid.substr(0, msgid.search(reAssign) + 1)
# get msg str
msgstr = line
msgstr = msgstr.substr(msgstr.search(reAssign) + 6)
msgstr = msgstr.substr(0, msgstr.search(reLineEnd))
# convert escaped quotes
msgid = msgid.replace(/\\"/g, "\"")
msgstr = msgstr.replace(/\\"/g, "\"")
# convert escaped new lines
msgid = msgid.replace(/\\n/g, "\n")
msgstr = msgstr.replace(/\\n/g, "\n")
# store values in object
if !wantsComments then result[msgid] = msgstr
else
val = { 'text': msgstr }
if currentComment
val['comment'] = currentComment
currentComment = ''
result[msgid] = val
# return resulting object
return result
i18nStringsFiles.prototype.compile = (data, wantsComments) ->
# if wantsComments is not specified, default to false
if !wantsComments then wantsComments = false
# make sure data is an object
if typeof data != "object" then return ""
# output string
output = ""
# loop through hash
for msgid, val of data
msgstr = ''
comment = null
if typeof val == 'string' then msgstr = val
else
if val.hasOwnProperty('text') then msgstr = val['text']
if wantsComments and val.hasOwnProperty('comment') then comment = val['comment']
# escape quotes in msgid, msgstr
msgid = msgid.replace(/"/g, "\\\"")
msgstr = msgstr.replace(/"/g, "\\\"")
# escape new lines in msgid, msgstr
msgid = msgid.replace(/\n/g, "\\n")
msgstr = msgstr.replace(/\r?\n/g, "\\n")
# add comment if available
if comment then output = output + "/* " + comment + " */\n"
# add line to output
output = output + "\"" + msgid + "\" = \"" + msgstr + "\";\n"
# return output string
return output
module.exports = new i18nStringsFiles