-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.js
282 lines (248 loc) · 8.82 KB
/
index.js
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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/**
* Create Pseudo Translated JSON from Any Other JSON
* It does not matter how deep the JSON is, as long as it's composed of objects
* and key value pairs. All values of key values pairs are pseudo translated,
* but the keys and overall structure are maintained
*
* Intended to be used on Angular-Translate JSON files with namespace support
*
* @author Michael Lage <[email protected]>
* @date September 23, 2015
*
* Pesudo Translation Code Inspired From:
* @see http://www.pseudolocalize.com
*/
'use strict';
// Dependencies
var gutil = require('gulp-util');
var PluginError = gutil.PluginError;
var traverse = require('traverse');
// Go!
var PLUGIN_NAME = 'gulp-pseudo-translate-angular-json';
var config = {
increasePercent: 0
};
var tCC = 0; // translated characters count
var openMode = false; // flag for protect
var stopTranslatingString = false;
var openModeException = false;
var extraWords = " lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc eget urna laoreet, accumsan felis at, dapibus elit. In ut tempus mauris. Sed eget sagittis arcu, in condimentum purus. Curabitur vitae congue elit.";
// Translate Line & Check For Padding Config
function pseudoLine(translatedLine) {
// Set flags to false
stopTranslatingString = false;
openMode = false;
openModeException = false;
tCC = 0;
var pseudoTranslatedLine = pseudoWords(translatedLine);
// To add padding or not ? Better not if it's an id link
if (! stopTranslatingString) {
if (tCC > 0) {
var extraLength = Math.round(tCC * config.increasePercent / 100.0);
pseudoTranslatedLine += pseudoWords(extraWords.substr(0, extraLength));
}
}
return pseudoTranslatedLine;
}
/**
* [protect description]
* @description Look for things to be skipped from Pseudo Translation
* @param {[type]} i [description]
* @param {[type]} text [description]
* @return {[type]} [description]
*/
function protect(i, text) {
/**
* Stop pseudoWords from destroying these:
*
* {{ SettingsCtrl.ProfileService.profile.mismatchedTravelNames.length }}
* {{trophiesCount, select, 1{Trophy} other{Trophies}}} (only touch the plualized forms...!)
* <a href=\"/web/agreement\" target=\"_blank\">User Agreement</a> (dont touch stuff inside < >)
* @:common.home
*/
/**
* @todo build support for more complicated ngMessageFormat strings:
{{recipients.length, plural, offset:1
=0 {{{sender.name}} gave no gifts (\#=#)}
=1 {{{sender.name}} gave one gift to {{recipients[0].name}} (\#=#)}
one {{{sender.name}} gave {{recipients[0].name}} and one other person a gift (\#=#)}
other {{{sender.name}} gave {{recipients[0].name}} and # other people a gift (\#=#)}
}}
Currently, it works pretty well, except "one" & "other" gets translated into pseudo:
{{recipients.length, plural, offset:1 =0 {{{sender.name}} ϱáƲè ñô ϱïƒƭƨ (\\#=#)} =1 {{{sender.name}} ϱáƲè ôñè ϱïƒƭ ƭô {{recipients[0].name}} (\\#=#)} ôñè {{{sender.name}} ϱáƲè {{recipients[0].name}} áñδ ôñè ôƭλèř ƥèřƨôñ á ϱïƒƭ (\\#=#)} ôƭλèř {{{sender.name}} ϱáƲè {{recipients[0].name}} áñδ # ôƭλèř ƥèôƥℓè á ϱïƒƭ (\\#=#)} }}
*/
// Take a look at the next character, how important is it, to the context of the moment ?
if (openMode) {
// look for closing tags
switch(text.charAt(i)) {
case '>':
openMode = false;
break;
case '}':
if (openModeException) {
openModeException = false;
} else {
openMode = ! checkForInterpolateExpression(i, text, false);
}
break;
case '{':
// maybe special case of no open mode
openModeException = checkForNgMessageFormat(i, text);
break;
}
} else {
// look for opening tags
switch(text.charAt(i)) {
case '<':
openMode = true;
break;
case '{':
openMode = checkForInterpolateExpression(i, text, true);
break;
case '@':
stopTranslatingString = checkForLinkedIds(i, text);
}
}
}
function checkForNgMessageFormat(i, text) {
// we need the next character to NOT be {, and there is at least } before the end of the text
if (text.charAt(i+1) !== '{' && text.charAt(i-1) !== '{') {
return true;
}
// {{openMode, select, 1{Test} other{Tests}}} test~
return false;
}
/**
* [checkForLinkedIds description]
* @param {[type]} i [description]
* @param {[type]} text [description]
* @return {[type]} [description]
*/
function checkForLinkedIds(i, text) {
if (text.charAt(i+1) === ':') {
return true;
}
return false;
}
/**
* [checkForInterpolateExpression description]
* @param {[type]} i [description]
* @param {[type]} text [description]
* @param {[type]} start [description]
* @return {[type]} [description]
* @description
* Checks the next character in the text. Depending on 'start', it will look for either the
* end or beginning of an interpolate expression, {{ var }}. The idea is we want to protect the inside
* of these expression so they evaluate normamly in javascript. Hence the need to maintain openMode
*
* When openMode is true, we are in an Interpolate expression that must not be converted into pseudo
*/
function checkForInterpolateExpression(i, text, start) {
var checkFor = start ? '{' : '}';
if (text.charAt(i+1) === checkFor)
{
return true;
}
return false;
}
/**
* [pseudoLetter description]
* @description Translate Letter to Pseudo Letter
* @param {[type]} c [description]
* @return {[type]} [description]
*/
function pseudoLetter(c) {
switch (c) {
case 'a': c = 'á'; tCC++; break;
case 'b': c = 'β'; tCC++; break;
case 'c': c = 'ç'; tCC++; break;
case 'd': c = 'δ'; tCC++; break;
case 'e': c = 'è'; tCC++; break;
case 'f': c = 'ƒ'; tCC++; break;
case 'g': c = 'ϱ'; tCC++; break;
case 'h': c = 'λ'; tCC++; break;
case 'i': c = 'ï'; tCC++; break;
case 'j': c = 'J'; tCC++; break;
case 'k': c = 'ƙ'; tCC++; break;
case 'l': c = 'ℓ'; tCC++; break;
case 'm': c = '₥'; tCC++; break;
case 'n': c = 'ñ'; tCC++; break;
case 'o': c = 'ô'; tCC++; break; // One Óñè
case 'p': c = 'ƥ'; tCC++; break;
case 'q': c = '9'; tCC++; break; // Many Máñ¥
case 'r': c = 'ř'; tCC++; break;
case 's': c = 'ƨ'; tCC++; break;
case 't': c = 'ƭ'; tCC++; break;
case 'u': c = 'ú'; tCC++; break;
case 'v': c = 'Ʋ'; tCC++; break;
case 'w': c = 'ω'; tCC++; break;
case 'x': c = 'ж'; tCC++; break;
case 'y': c = '¥'; tCC++; break;
case 'z': c = 'ƺ'; tCC++; break;
case 'A': c = 'Â'; tCC++; break;
case 'B': c = 'ß'; tCC++; break;
case 'C': c = 'Ç'; tCC++; break;
case 'D': c = 'Ð'; tCC++; break;
case 'E': c = 'É'; tCC++; break;
case 'F': c = 'F'; tCC++; break;
case 'G': c = 'G'; tCC++; break;
case 'H': c = 'H'; tCC++; break;
case 'I': c = 'Ì'; tCC++; break;
case 'J': c = 'J'; tCC++; break;
case 'K': c = 'K'; tCC++; break;
case 'L': c = '£'; tCC++; break;
case 'M': c = 'M'; tCC++; break;
case 'N': c = 'N'; tCC++; break;
case 'O': c = 'Ó'; tCC++; break;
case 'P': c = 'Þ'; tCC++; break;
case 'Q': c = 'Q'; tCC++; break;
case 'R': c = 'R'; tCC++; break;
case 'S': c = '§'; tCC++; break;
case 'T': c = 'T'; tCC++; break;
case 'U': c = 'Û'; tCC++; break;
case 'V': c = 'V'; tCC++; break;
case 'W': c = 'W'; tCC++; break;
case 'X': c = 'X'; tCC++; break;
case 'Y': c = 'Ý'; tCC++; break;
case 'Z': c = 'Z'; tCC++; break;
}
return c;
}
// Actual Translation. Goes through string, char by char,
// replacing regular letters with pseudo versions
function pseudoWords(text) {
var translated = '';
// Loop through string chars
for (var i = 0; i < text.length; i++) {
// Look for: {{ }} < >
// To set or unset openMode
if (!stopTranslatingString) {
protect(i, text);
}
if (stopTranslatingString || (openMode && !openModeException)) {
translated += text.charAt(i);
} else {
translated += pseudoLetter(text.charAt(i));
}
}
return translated;
}
// Plugin level function(dealing with files)
function pseudoTranslator(json, conf) {
if (!json) {
throw new PluginError(PLUGIN_NAME, 'JSON Translatable Data Is Missing');
}
// setup config
config = conf || config;
// Run your recursive function here to translate all values for all key-value pairs
// found in 'json'
return traverse(json).map(function(line) {
if (typeof line === 'object') {
// skip objects
} else {
this.update(pseudoLine(line));
}
});
}
// Exporting the plugin main function
module.exports = pseudoTranslator;