Automatic Translate Addon For Loco Translate - Version 1.0.2

Version Description

Download this release

Release Info

Developer Narinder singh
Plugin Icon 128x128 Automatic Translate Addon For Loco Translate
Version 1.0.2
Comparing to
See all releases

Code changes from version 1.0 to 1.0.2

assets/css/atlt-admin-feedback-notice.css ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .cool-feedback-notice-wrapper.notice.notice-info.is-dismissible {
2
+ padding: 5px;
3
+ display: inline-block;
4
+ width: 100%;
5
+ }
6
+ .cool-feedback-notice-wrapper .logo_container {
7
+ width:80px;
8
+ display:inline-block;
9
+ margin-right: 10px;
10
+ vertical-align: top;
11
+ }
12
+ .cool-feedback-notice-wrapper .logo_container img {
13
+ width:100%;
14
+ height:auto;
15
+ }
16
+ .cool-feedback-notice-wrapper .message_container {
17
+ width: calc(100% - 120px);
18
+ display: inline-block;
19
+ margin: 0;
20
+ vertical-align: top;
21
+ }
22
+ .cool-feedback-notice-wrapper ul li {
23
+ float: left;
24
+ margin: 0px 5px;
25
+ }
26
+ .clrfix{
27
+ clear:both;
28
+ }
assets/images/atlt-logo.png ADDED
Binary file
assets/images/screenshot-1.gif ADDED
Binary file
assets/js/atlt-admin-feedback-notice.js ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function ($) {
2
+ $('.atlt_dismiss_notice').on('click', function (event) {
3
+ var $this = $(this);
4
+ var wrapper=$this.parents('.cool-feedback-notice-wrapper');
5
+ var ajaxURL=wrapper.data('ajax-url');
6
+ var ajaxCallback=wrapper.data('ajax-callback');
7
+
8
+ $.post(ajaxURL, { 'action':ajaxCallback }, function( data ) {
9
+ wrapper.slideUp('fast');
10
+ }, "json");
11
+
12
+ });
13
+ });
assets/js/loco-js-editor.js ADDED
@@ -0,0 +1,810 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Script for PO file editor pages
3
+ */
4
+ !function( window, $ ){
5
+
6
+ var loco = window.locoScope,
7
+ conf = window.locoConf,
8
+ syncParams = null,
9
+ saveParams = null,
10
+
11
+
12
+ // UI translation
13
+ translator = loco.l10n,
14
+ sprintf = loco.string.sprintf,
15
+
16
+ // PO file data
17
+ locale = conf.locale,
18
+ messages = loco.po.init( locale ).wrap( conf.powrap ),
19
+ template = ! locale,
20
+
21
+ // form containing action buttons
22
+ elForm = document.getElementById('loco-actions'),
23
+ filePath = conf.popath,
24
+ syncPath = conf.potpath,
25
+
26
+ // file system connect when file is locked
27
+ elFilesys = document.getElementById('loco-fs'),
28
+ fsConnect = elFilesys && loco.fs.init( elFilesys ),
29
+
30
+ // prevent all write operations if readonly mode
31
+ readonly = conf.readonly,
32
+ editable = ! readonly,
33
+
34
+ // Editor components
35
+ editor,
36
+ saveButton,
37
+ innerDiv = document.getElementById('loco-editor-inner');
38
+
39
+ /**
40
+ *
41
+ */
42
+ function doSyncAction( callback ){
43
+ function onSuccess( result ){
44
+ var info = [],
45
+ doc = messages,
46
+ exp = result.po,
47
+ src = result.pot,
48
+ pot = loco.po.init().load( exp ),
49
+ done = doc.merge( pot ),
50
+ nadd = done.add.length,
51
+ ndel = done.del.length,
52
+ t = translator;
53
+ // reload even if unchanged, cos indexes could be off
54
+ editor.load( doc );
55
+ // Show summary
56
+ if( nadd || ndel ){
57
+ if( src ){
58
+ // Translators: Where %s is the name of the POT template file. Message appears after sync
59
+ info.push( sprintf( t._('Merged from %s'), src ) );
60
+ }
61
+ else {
62
+ // Translators: Message appears after sync operation
63
+ info.push( t._('Merged from source code') );
64
+ }
65
+ // Translators: Summary of new strings after running in-editor Sync
66
+ nadd && info.push( sprintf( t._n('1 new string added','%s new strings added', nadd ), nadd ) );
67
+ // Translators: Summary of existing strings that no longer exist after running in-editor Sync
68
+ ndel && info.push( sprintf( t._n('1 obsolete string removed','%s obsolete strings removed', ndel ), ndel ) );
69
+ // editor thinks it's saved, but we want the UI to appear otherwise
70
+ $(innerDiv).trigger('poUnsaved',[]);
71
+ updateStatus();
72
+ // debug info in lieu of proper merge confirmation:
73
+ window.console && debugMerge( console, done );
74
+ }
75
+ else if( src ){
76
+ // Translators: Message appears after sync operation when nothing has changed. %s refers to a POT file.
77
+ info.push( sprintf( t._('Already up to date with %s'), src ) );
78
+ }
79
+ else {
80
+ // Translators: Message appears after sync operation when nothing has changed
81
+ info.push( t._('Already up to date with source code') );
82
+ }
83
+ loco.notices.success( info.join('. ') );
84
+ $(innerDiv).trigger('poMerge',[result]);
85
+ // done sync
86
+ callback && callback();
87
+ }
88
+ loco.ajax.post( 'sync', syncParams, onSuccess, callback );
89
+ }
90
+
91
+
92
+
93
+ function debugMerge( console, result ){
94
+ var i = -1, t = result.add.length;
95
+ while( ++i < t ){
96
+ console.log(' + '+result.add[i].source() );
97
+ }
98
+ i = -1, t = result.del.length;
99
+ while( ++i < t ){
100
+ console.log(' - '+result.del[i].source() );
101
+ }
102
+ }
103
+
104
+
105
+ /**
106
+ * Post full editor contents to "posave" endpoint
107
+ */
108
+ function doSaveAction( callback ){
109
+ function onSuccess( result ){
110
+ callback && callback();
111
+ editor.save( true );
112
+ // Update saved time update
113
+ $('#loco-po-modified').text( result.datetime||'[datetime error]' );
114
+ }
115
+ saveParams.locale = String( messages.locale() || '' );
116
+ if( fsConnect ){
117
+ fsConnect.applyCreds( saveParams );
118
+ }
119
+ // adding PO source last for easier debugging in network inspector
120
+ saveParams.data = String( messages );
121
+ loco.ajax.post( 'save', saveParams, onSuccess, callback );
122
+ }
123
+
124
+
125
+ function saveIfDirty(){
126
+ editor.dirty && doSaveAction();
127
+ }
128
+
129
+
130
+
131
+ function onUnloadWarning(){
132
+ // Translators: Warning appears when user tries to refresh or navigate away when editor work is unsaved
133
+ return translator._("Your changes will be lost if you continue without saving");
134
+ }
135
+
136
+
137
+
138
+ function registerSaveButton( button ){
139
+ saveButton = button;
140
+ // enables and disable according to save/unsave events
141
+ editor
142
+ .on('poUnsaved', function(){
143
+ enable();
144
+ $(button).addClass( 'button-primary loco-flagged' );
145
+ } )
146
+ .on('poSave', function(){
147
+ disable();
148
+ $(button).removeClass( 'button-primary loco-flagged' );
149
+ } )
150
+ ;
151
+ function disable(){
152
+ button.disabled = true;
153
+ }
154
+ function enable(){
155
+ button.disabled = false;
156
+ }
157
+ function think(){
158
+ disable();
159
+ $(button).addClass('loco-loading');
160
+ }
161
+ function unthink(){
162
+ enable();
163
+ $(button).removeClass('loco-loading');
164
+ }
165
+ saveParams = $.extend( { path: filePath }, conf.project||{} );
166
+
167
+ $(button).click( function(event){
168
+ event.preventDefault();
169
+ think();
170
+ doSaveAction( unthink );
171
+ return false;
172
+ } );
173
+ return true;
174
+ };
175
+
176
+
177
+
178
+ function registerSyncButton( button ){
179
+ var project = conf.project;
180
+ if( project ){
181
+ function disable(){
182
+ button.disabled = true;
183
+ }
184
+ function enable(){
185
+ button.disabled = false;
186
+ }
187
+ function think(){
188
+ disable();
189
+ $(button).addClass('loco-loading');
190
+ }
191
+ function unthink(){
192
+ enable();
193
+ $(button).removeClass('loco-loading');
194
+ }
195
+ // Only permit sync when document is saved
196
+ editor
197
+ .on('poUnsaved', function(){
198
+ disable();
199
+ } )
200
+ .on('poSave', function(){
201
+ enable();
202
+ } )
203
+ ;
204
+ // params for sync end point
205
+ syncParams = {
206
+ bundle: project.bundle,
207
+ domain: project.domain,
208
+ type: template ? 'pot' : 'po',
209
+ sync: syncPath||''
210
+ };
211
+ // enable syncing on button click
212
+ $(button)
213
+ .click( function(event){
214
+ event.preventDefault();
215
+ think();
216
+ doSyncAction( unthink );
217
+ return false;
218
+ } )
219
+ //.attr('title', syncPath ? sprintf( translator._('Update from %s'), syncPath ) : translator._('Update from source code') )
220
+ ;
221
+ enable();
222
+ }
223
+ return true;
224
+ }
225
+
226
+
227
+
228
+ function registerFuzzyButton( button ){
229
+ var toggled = false,
230
+ enabled = false
231
+ ;
232
+ function redraw( message, state ){
233
+ // fuzziness only makes sense when top-level string is translated
234
+ var allowed = message && message.translated(0) || false;
235
+ if( enabled !== allowed ){
236
+ button.disabled = ! allowed;
237
+ enabled = allowed;
238
+ }
239
+ // toggle on/off according to new fuzziness
240
+ if( state !== toggled ){
241
+ $(button)[ state ? 'addClass' : 'removeClass' ]('inverted');
242
+ toggled = state;
243
+ }
244
+ }
245
+ // state changes depending on whether an asset is selected and is fuzzy
246
+ editor
247
+ .on('poSelected', function( event, message ){
248
+ redraw( message, message && message.fuzzy() || false );
249
+ } )
250
+ .on( 'poEmpty', function( event, blank, message, pluralIndex ){
251
+ if( 0 === pluralIndex && blank === enabled ){
252
+ redraw( message, toggled );
253
+ }
254
+ } )
255
+ .on( 'poFuzzy', function( event, message, newState ){
256
+ redraw( message, newState );
257
+ } )
258
+ ;
259
+ // click toggles current state
260
+ $(button).click( function( event ){
261
+ event.preventDefault();
262
+ editor.fuzzy( ! editor.fuzzy() );
263
+ return false;
264
+ } );
265
+ return true;
266
+ };
267
+
268
+
269
+
270
+ function registerRevertButton( button ){
271
+ // No need for revert when document is saved
272
+ editor
273
+ .on('poUnsaved', function(){
274
+ button.disabled = false;
275
+ } )
276
+ .on('poSave', function(){
277
+ button.disabled = true;
278
+ } )
279
+ ;
280
+ // handling unsaved state prompt with onbeforeunload, see below
281
+ $(button).click( function( event ){
282
+ event.preventDefault();
283
+ location.reload();
284
+ return false;
285
+ } );
286
+ return true;
287
+ };
288
+
289
+
290
+
291
+ function registerInvisiblesButton( button ){
292
+ var $button = $(button);
293
+ button.disabled = false;
294
+ editor.on('poInvs', function( event, state ){
295
+ $button[ state ? 'addClass' : 'removeClass' ]('inverted');
296
+ });
297
+ $button.click( function( event ){
298
+ event.preventDefault();
299
+ editor.setInvs( ! editor.getInvs() );
300
+ return false;
301
+ } );
302
+ locoScope.tooltip.init($button);
303
+ return true;
304
+ }
305
+
306
+
307
+
308
+ function registerCodeviewButton( button ){
309
+ var $button = $(button);
310
+ button.disabled = false;
311
+ $button.click( function(event){
312
+ event.preventDefault();
313
+ var state = ! editor.getMono();
314
+ editor.setMono( state );
315
+ $button[ state ? 'addClass' : 'removeClass' ]('inverted');
316
+ return false;
317
+ } );
318
+ locoScope.tooltip.init($button);
319
+ return true;
320
+ };
321
+
322
+
323
+
324
+ function registerAddButton( button ){
325
+ button.disabled = false;
326
+ $(button).click( function( event ){
327
+ event.preventDefault();
328
+ // Need a placeholder guaranteed to be unique for new items
329
+ var i = 1, baseid, msgid, regex = /(\d+)$/;
330
+ msgid = baseid = 'New message';
331
+ while( messages.get( msgid ) ){
332
+ i = regex.exec(msgid) ? Math.max(i,RegExp.$1) : i;
333
+ msgid = baseid+' '+( ++i );
334
+ }
335
+ editor.add( msgid );
336
+ return false;
337
+ } );
338
+ return true;
339
+ };
340
+
341
+
342
+
343
+ function registerDelButton( button ){
344
+ button.disabled = false;
345
+ $(button).click( function(event){
346
+ event.preventDefault();
347
+ editor.del();
348
+ return false;
349
+ } );
350
+ return true;
351
+ };
352
+
353
+
354
+
355
+ function registerDownloadButton( button, id ){
356
+ button.disabled = false;
357
+ $(button).click( function( event ){
358
+ var form = button.form,
359
+ path = filePath;
360
+ // swap out path
361
+ if( 'binary' === id ){
362
+ path = path.replace(/\.po$/,'.mo');
363
+ }
364
+ form.path.value = path;
365
+ form.source.value = messages.toString();
366
+ // allow form to submit
367
+ return true;
368
+ } );
369
+ return true;
370
+ }
371
+
372
+
373
+ // event handler that stops dead
374
+ function noop( event ){
375
+ event.preventDefault();
376
+ return false;
377
+ }
378
+
379
+
380
+ /*/ dummy function for enabling buttons that do nothing (or do something inherently)
381
+ function registerNoopButton( button ){
382
+ return true;
383
+ }*/
384
+
385
+
386
+
387
+ /**
388
+ * Update status message above editor.
389
+ * This is dynamic version of PHP Loco_gettext_Metadata::getProgressSummary
390
+ * TODO implement progress bar, not just text.
391
+ */
392
+ function updateStatus(){
393
+ var t = translator,
394
+ stats = editor.stats(),
395
+ total = stats.t,
396
+ fuzzy = stats.f,
397
+ empty = stats.u,
398
+ // Translators: Shows total string count at top of editor
399
+ stext = sprintf( t._n('1 string','%s strings',total ), total.format(0) ),
400
+ extra = [];
401
+ if( locale ){
402
+ // Translators: Shows percentage translated at top of editor
403
+ stext = sprintf( t._('%s%% translated'), stats.p.replace('%','') ) +', '+ stext;
404
+ // Translators: Shows number of fuzzy strings at top of editor
405
+ fuzzy && extra.push( sprintf( t._('%s fuzzy'), fuzzy.format(0) ) );
406
+ // Translators: Shows number of untranslated strings at top of editor
407
+ empty && extra.push( sprintf( t._('%s untranslated'), empty.format(0) ) );
408
+ if( extra.length ){
409
+ stext += ' ('+extra.join(', ')+')';
410
+ }
411
+ }
412
+ $('#loco-po-status').text( stext );
413
+ window.locoEditorStats = {totalWords:stats.t, totalTranslated:stats.p} ;
414
+
415
+ }
416
+
417
+
418
+
419
+ /**
420
+ * Enable text filtering
421
+ */
422
+ function initSearchFilter( elSearch ){
423
+ editor.searchable( loco.fulltext.init() );
424
+ // prep search text field
425
+ elSearch.disabled = false;
426
+ elSearch.value = '';
427
+ function showValidFilter( numFound ){
428
+ $(elSearch.parentNode)[ numFound || null == numFound ? 'removeClass' : 'addClass' ]('invalid');
429
+ }
430
+ var listener = loco.watchtext( elSearch, function( value ){
431
+ var numFound = editor.filter( value, true );
432
+ showValidFilter( numFound );
433
+ } );
434
+ editor
435
+ .on( 'poFilter', function( event, value, numFound ){
436
+ listener.val( value||'' );
437
+ showValidFilter( numFound );
438
+ } )
439
+ .on( 'poMerge', function( event, result ){
440
+ var value = listener.val();
441
+ value && editor.filter( value );
442
+ } )
443
+ ;
444
+ }
445
+
446
+
447
+
448
+ // resize function fits editor to screen, accounting for headroom and touching bottom of screen.
449
+ var resize = function(){
450
+ function top( el, ancestor ){
451
+ var y = el.offsetTop||0;
452
+ while( ( el = el.offsetParent ) && el !== ancestor ){
453
+ y += el.offsetTop||0;
454
+ }
455
+ return y;
456
+ }
457
+ var fixHeight,
458
+ minHeight = parseInt($(innerDiv).css('min-height')||0)
459
+ ;
460
+ return function(){
461
+ var padBottom = 20,
462
+ topBanner = top( innerDiv, document.body ),
463
+ winHeight = window.innerHeight,
464
+ setHeight = Math.max( minHeight, winHeight - topBanner - padBottom )
465
+ ;
466
+ if( fixHeight !== setHeight ){
467
+ innerDiv.style.height = String(setHeight)+'px';
468
+ fixHeight = setHeight;
469
+ }
470
+ };
471
+ }();
472
+
473
+ // ensure outer resize is handled before editor's internal resize
474
+ resize();
475
+ $(window).resize( resize );
476
+
477
+ // initialize editor
478
+ innerDiv.innerHTML = '';
479
+ editor = loco.po.ed
480
+ .init( innerDiv )
481
+ .localise( translator )
482
+ ;
483
+ loco.po.kbd
484
+ .init( editor )
485
+ .add( 'save', saveIfDirty )
486
+ .enable('copy','clear','enter','next','prev','fuzzy','save','invis')
487
+ ;
488
+
489
+ // initialize toolbar button actions
490
+ var buttons = {
491
+ // help: registerNoopButton,
492
+ save: editable && registerSaveButton,
493
+ sync: editable && registerSyncButton,
494
+ revert: registerRevertButton,
495
+ // editor mode togglers
496
+ invs: registerInvisiblesButton,
497
+ code: registerCodeviewButton,
498
+ // downloads / post-throughs
499
+ source: registerDownloadButton,
500
+ binary: template ? null : registerDownloadButton
501
+ };
502
+ // POT only
503
+ if( template ){
504
+ buttons.add = editable && registerAddButton;
505
+ buttons.del = editable && registerDelButton;
506
+ }
507
+ // PO only
508
+ else {
509
+ buttons.fuzzy = registerFuzzyButton;
510
+ };
511
+ $('#loco-toolbar').find('button').each( function(i,el){
512
+ var id = el.getAttribute('data-loco'), register = buttons[id];
513
+ register && register(el,id) || $(el).hide();
514
+ } );
515
+
516
+ // disable submit on dummy form
517
+ $(elForm).submit( noop );
518
+
519
+ // enable text filtering
520
+ initSearchFilter( document.getElementById('loco-search') );
521
+
522
+ // editor event behaviours
523
+ editor
524
+ .on('poUnsaved', function(){
525
+ window.onbeforeunload = onUnloadWarning;
526
+ } )
527
+ .on('poSave', function(){
528
+ updateStatus();
529
+ window.onbeforeunload = null;
530
+ } )
531
+ .on( 'poUpdate', updateStatus );
532
+ ;
533
+
534
+ // load raw message data
535
+ messages.load( conf.podata );
536
+
537
+ // ready to render editor
538
+ editor.load( messages );
539
+
540
+ // locale should be cast to full object once set in editor
541
+ if( locale = editor.targetLocale ){
542
+ locale.isRTL() && $(innerDiv).addClass('trg-rtl');
543
+ }
544
+ // enable template mode when no target locale
545
+ else {
546
+ editor.unlock();
547
+ }
548
+
549
+
550
+ /*
551
+ |--------------------------------------------------------------------------
552
+ | Auto Translator Custom Code
553
+ |--------------------------------------------------------------------------
554
+ */
555
+
556
+ function createEncodedString(allStringText){
557
+ const queryString=allStringText.map((item)=>{
558
+ return "&text="+ encodeURIComponent(item.source);
559
+ }).join(",");
560
+
561
+ return queryString;
562
+ }
563
+
564
+ function addAutoTranslationBtn(){
565
+ if($("#loco-toolbar").find("#cool-auto-translate-btn").length>0){
566
+ $("#loco-toolbar").find("#cool-auto-translate-btn").remove();
567
+ }
568
+
569
+ if( ATLT == '' || ATLT["api_key"] == '' || ATLT["api_key"]["atlt_api-key"]=='' ){
570
+ $("#loco-toolbar").find("#loco-actions")
571
+ .append('<fieldset><button title="Add API key to enable this feature." id="cool-auto-translate-btn" disabled class="button has-icon icon-translate">Auto Translate</button> <a style="font-size:9px;display:block;margin-left:8px;" target="_blank" href="https://tech.yandex.com/translate/">Get Free API Key</a></fieldset>');
572
+ return;
573
+ } else if( window.locoEditorStats.totalTranslated != "100%" && window.locoEditorStats.totalWords > 0 ){
574
+ $("#loco-toolbar").find("#loco-actions")
575
+ .append('<fieldset><button id="cool-auto-translate-btn" class="button has-icon icon-translate">Auto Translate</button> <a style="font-size:9px;display:block;margin-left:8px;" target="_blank" href="http://translate.yandex.com/">Powered by Yandex.Translate</a></fieldset>');
576
+ } else if( window.locoEditorStats.totalWords == 0){
577
+ return;
578
+ } else {
579
+ $("#loco-toolbar").find("#loco-actions")
580
+ .append('<fieldset><button id="cool-auto-translate-btn" class="button has-icon icon-translate" disabled>Translated</button></fieldset>');
581
+ }
582
+ }
583
+
584
+ $(document).ready(function(){
585
+ const locoRawData=conf.podata;
586
+
587
+ if(locoRawData!=undefined && locoRawData.length>0 ){
588
+
589
+ addAutoTranslationBtn();
590
+
591
+ }
592
+ let translationObj = {};
593
+
594
+ $(document)
595
+ // Bind translate function to translate button
596
+ .on("click", "#cool-auto-translate-btn", function() {
597
+
598
+ $(this).text('...Translating');
599
+ // const targetLang=conf.locale.lang;
600
+
601
+ const apiKey = ATLT["api_key"]["lat_api-key"];
602
+
603
+
604
+ if(locoRawData!=undefined && locoRawData.length>0 && apiKey!='' ){
605
+ let savedIndex = 0;
606
+ let untranslatedTextArr=locoRawData.filter((item,index)=>{
607
+
608
+ if(item.target===undefined || item.target=="" && savedIndex<=90){
609
+ savedIndex++;
610
+ return true;
611
+ }
612
+
613
+ });
614
+ translationObj = {
615
+ sourceLang:'en',
616
+ targetLang:conf['locale']["lang"],
617
+ textToTranslateArr:untranslatedTextArr,
618
+ orginalArr:conf.podata,
619
+ apiKey:apiKey,
620
+ thisBtn:$(this),
621
+ saveBtn: $('[data-loco="save"]')
622
+ };
623
+ if (translationObj.targetLang !== null && translationObj.textToTranslateArr !== null) {
624
+ atlt_translate(translationObj);
625
+
626
+ // load raw message data
627
+
628
+ }
629
+
630
+ }
631
+
632
+ });
633
+
634
+ });
635
+
636
+ // Translate
637
+ function atlt_translate(data) {
638
+ atlt_ajax_translation_request(data, "POST").success(function(
639
+ resp
640
+ ) {
641
+ const json_resp = JSON.parse(resp);
642
+ if( json_resp == false ){
643
+ alert('Unable to make request to the server at the moment. Try again later.');
644
+ window.location.reload();
645
+ }else if( typeof json_resp['code'] === undefined || json_resp['code'] != 200 ){
646
+ switch(json_resp['code']){
647
+ case 401:
648
+ alert('Yendex API Key is invalid!');
649
+ break;
650
+ case 402:
651
+ alert('Provided Yendex API Key has been blocked!');
652
+ break;
653
+ case 404:
654
+ alert('Exceeded the daily limit for Yendex API on the amount of translated text.');
655
+ break;
656
+ case 422:
657
+ alert('The text cannot be translated by Yendex API.');
658
+ break;
659
+ case 501:
660
+ alert('Yendex API does not support the specified translation direction.');
661
+ break;
662
+ default:
663
+ alert(json_resp['message']);
664
+ }
665
+ return;
666
+ }
667
+
668
+ var response = json_resp['text'][0].split('],![');
669
+
670
+ if(response!==undefined){
671
+ for(i=0;i< response.length;i++){
672
+ var text = response[i];
673
+
674
+ if( data.textToTranslateArr[i] === undefined ){
675
+ break;
676
+ }
677
+ text = text.replace(/\\"/g,'"', text.trim() );
678
+ text = text.replace(/\\'/g,"'", text.trim() );
679
+ if( i==0 && response.length >1){
680
+ data.textToTranslateArr[i].target= text.substring(2,text.length-1);
681
+
682
+ } else if( i==0 && response.length <= 1 ){
683
+ data.textToTranslateArr[i].target= text.substring(2,text.length-2);
684
+
685
+ } else if( i == response.length-1 ){
686
+ data.textToTranslateArr[i].target = text.substring(1,text.length-2);
687
+ } else {
688
+ data.textToTranslateArr[i].target = text.substring(1,text.length-1);
689
+
690
+ }
691
+
692
+ }
693
+ }
694
+ var mergeTranslatedText = atlt_arrayUnique(data['textToTranslateArr'].concat(data['orginalArr']) ),
695
+ textForTranslation = data['textToTranslateArr'],
696
+ Emptytargets = []
697
+ for(var x=0; x<textForTranslation.length;++x){
698
+ if(textForTranslation[x].target !='' ){
699
+ Emptytargets[x]=textForTranslation[x].source;
700
+ }
701
+ }
702
+
703
+ messages = loco.po.init( locale ).wrap( conf.powrap );
704
+ // ready to render editor
705
+ messages.load(mergeTranslatedText);
706
+
707
+ // editor event behaviours
708
+ editor
709
+ .on('poUnsaved', function(){
710
+ window.onbeforeunload = onUnloadWarning;
711
+ } )
712
+ .on('poSave', function(){
713
+ updateStatus();
714
+ window.onbeforeunload = null;
715
+ } )
716
+ .on( 'poUpdate', updateStatus );
717
+
718
+ // ready to render editor
719
+ editor.load(messages);
720
+ data.saveBtn.addClass( 'button-primary loco-flagged ' ).removeAttr("disabled");
721
+ updateStatus();
722
+
723
+ data.thisBtn.text('Translated');
724
+ data.thisBtn.attr('disabled','disabled');
725
+
726
+ // run through DOM and mark *(STAR) for newly translated
727
+ for(var x=0;x<=Emptytargets.length;x++){
728
+ var source = Emptytargets[x];
729
+ jQuery("#po-list-tbody div[for='po-list-col-source'] div").filter(function(index){
730
+ return jQuery(this).text() == source
731
+ }).addClass('po-unsaved');
732
+ }
733
+ });
734
+ }
735
+
736
+ // Abstract API request function
737
+ function makeApiRequest(data, type) {
738
+ // send text to translate
739
+ url = "https://translate.yandex.net/api/v1.5/tr.json/translate";
740
+ url += "?key=" + data.apiKey;
741
+ url += "&lang="+data.sourceLang+'-'+ data.targetLang;
742
+ url += createEncodedString(data.textToTranslateArr);
743
+
744
+ // Return response from API
745
+ console.log(createEncodedString(data.textToTranslateArr))
746
+ const newArr=data.textToTranslateArr.map((item)=>{
747
+ if(item.source!==undefined){
748
+ return obj={text:item.source};
749
+ }
750
+ });
751
+ console.log( newArr);
752
+ return $.ajax({
753
+ url: url,
754
+ type: type || "GET",
755
+ dataType:'jsonp',
756
+ data:newArr,
757
+ crossDomain:true,
758
+ headers: {
759
+ "Content-Type": "application/json",
760
+ Accept: "application/json"
761
+ }
762
+ });
763
+ }
764
+
765
+ function atlt_arrayUnique(array) {
766
+ var a = array.concat();
767
+ for(var i=0; i<a.length; ++i) {
768
+ for(var j=i+1; j<a.length; ++j) {
769
+ if(a[i] === a[j])
770
+ a.splice(j--, 1);
771
+ }
772
+ }
773
+
774
+ return a;
775
+ }
776
+
777
+ function atlt_ajax_translation_request(data,type){
778
+
779
+ const newArr=data.textToTranslateArr.map((item)=>{
780
+ if(item.source!==undefined){
781
+ return obj='['+item.source+']';
782
+ }
783
+ });
784
+ return jQuery.ajax({
785
+ url: ajaxurl,
786
+ type:'POST',
787
+ data: {'action':'atlt_translation',
788
+ 'sourceLan':data.sourceLang,
789
+ 'targetLan':data.targetLang,
790
+ 'data':newArr
791
+ },
792
+
793
+ });
794
+ }
795
+
796
+ // refresh page on save only if there are pending translation
797
+ jQuery(document).on('poSave',function(){
798
+ if( locoEditorStats.totalTranslated != "100%" ){
799
+ setTimeout(function(){ window.location.reload(); }, 500);
800
+ }
801
+ })
802
+
803
+ // ok, editor ready
804
+ updateStatus();
805
+
806
+ // clean up
807
+ //delete window.locoConf;
808
+ //conf = buttons = null;
809
+
810
+ }( window, jQuery );
automatic-translator-addon-for-loco-translate.php CHANGED
@@ -2,7 +2,7 @@
2
  /*
3
  Plugin Name: Loco Automatic Translate Addon
4
  Description: Auto language translator add-on for Loco Translate plugin to translate plugins and themes translation files into any language via fully automatic machine translations via Yendex Translate API.
5
- Version: 1.0
6
  License: GPL2
7
  Text Domain:atlt
8
  Domain Path:languages
@@ -12,7 +12,7 @@ Author URI: https://coolplugins.net/
12
 
13
  /**
14
  * @package Loco Automatic Translate Addon
15
- * @version 1.0
16
  */
17
  if (!defined('ABSPATH')) {
18
  die('WordPress Environment Not Found!');
@@ -34,6 +34,9 @@ class LocoAutoTranslate
34
  add_action('plugins_loaded', array($this, 'atlt_check_required_loco_plugin'));
35
  add_action( 'admin_enqueue_scripts', array( $this, 'atlt_enqueue_scripts') );
36
  add_action('wp_ajax_atlt_translation', array($this, 'atlt_translate_words'), 100);
 
 
 
37
  }
38
 
39
 
@@ -47,6 +50,11 @@ class LocoAutoTranslate
47
  require_once ATLT_PATH . 'includes/class.settings-api.php';
48
  require_once ATLT_PATH . 'includes/atlt-settings.php';
49
  new Atlt_Settings_Panel();
 
 
 
 
 
50
  }
51
 
52
  /*
@@ -64,8 +72,8 @@ class LocoAutoTranslate
64
  if (empty($DATA)) {
65
  die(json_encode(array('code' => 900, 'message' => 'Empty request')));
66
  }
67
- $data = '[' . implode('], [', $DATA) . ']';
68
-
69
  $HOST = 'https://translate.yandex.net/api/v1.5/tr.json/translate?key=' . $KEY . '&lang=' . $lang;
70
  $response = wp_remote_post($HOST, array('body' => array('text' => $data)));
71
 
@@ -120,7 +128,7 @@ class LocoAutoTranslate
120
  function atlt_enqueue_scripts(){
121
 
122
  wp_deregister_script('loco-js-editor');
123
- wp_register_script( 'loco-js-editor', ATLT_URL.'js/loco-js-editor.js', array('loco-js-min-admin'),false, true);
124
 
125
  if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'file-edit') {
126
  wp_enqueue_script('loco-js-editor');
@@ -137,8 +145,13 @@ class LocoAutoTranslate
137
  public function atlt_activate(){
138
  $plugin_info = get_plugin_data(__FILE__, true, true);
139
  update_option('atlt_version', $plugin_info['Version'] );
 
 
 
 
140
  }
141
 
 
142
  /*
143
  |-------------------------------------------------------
144
  | Plugin deactivation
2
  /*
3
  Plugin Name: Loco Automatic Translate Addon
4
  Description: Auto language translator add-on for Loco Translate plugin to translate plugins and themes translation files into any language via fully automatic machine translations via Yendex Translate API.
5
+ Version: 1.0.2
6
  License: GPL2
7
  Text Domain:atlt
8
  Domain Path:languages
12
 
13
  /**
14
  * @package Loco Automatic Translate Addon
15
+ * @version 1.0.2
16
  */
17
  if (!defined('ABSPATH')) {
18
  die('WordPress Environment Not Found!');
34
  add_action('plugins_loaded', array($this, 'atlt_check_required_loco_plugin'));
35
  add_action( 'admin_enqueue_scripts', array( $this, 'atlt_enqueue_scripts') );
36
  add_action('wp_ajax_atlt_translation', array($this, 'atlt_translate_words'), 100);
37
+
38
+ // delete_option("atlt-installDate");
39
+ // delete_option("atlt-ratingDiv");
40
  }
41
 
42
 
50
  require_once ATLT_PATH . 'includes/class.settings-api.php';
51
  require_once ATLT_PATH . 'includes/atlt-settings.php';
52
  new Atlt_Settings_Panel();
53
+
54
+ if ( is_admin() ) {
55
+ require_once ATLT_PATH . "includes/atlt-feedback-notice.php";
56
+ new atltFeedbackNotice();
57
+ }
58
  }
59
 
60
  /*
72
  if (empty($DATA)) {
73
  die(json_encode(array('code' => 900, 'message' => 'Empty request')));
74
  }
75
+ $data = '[' . implode('],![', $DATA) . ']';
76
+
77
  $HOST = 'https://translate.yandex.net/api/v1.5/tr.json/translate?key=' . $KEY . '&lang=' . $lang;
78
  $response = wp_remote_post($HOST, array('body' => array('text' => $data)));
79
 
128
  function atlt_enqueue_scripts(){
129
 
130
  wp_deregister_script('loco-js-editor');
131
+ wp_register_script( 'loco-js-editor', ATLT_URL.'assets/js/loco-js-editor.js', array('loco-js-min-admin'),false, true);
132
 
133
  if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'file-edit') {
134
  wp_enqueue_script('loco-js-editor');
145
  public function atlt_activate(){
146
  $plugin_info = get_plugin_data(__FILE__, true, true);
147
  update_option('atlt_version', $plugin_info['Version'] );
148
+ update_option("atlt-installDate",date('Y-m-d h:i:s') );
149
+ update_option("atlt-ratingDiv","no");
150
+
151
+
152
  }
153
 
154
+
155
  /*
156
  |-------------------------------------------------------
157
  | Plugin deactivation
includes/atlt-feedback-notice.php ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if (!class_exists('atltFeedbackNotice')) {
4
+ class atltFeedbackNotice {
5
+ /**
6
+ * The Constructor
7
+ */
8
+ public function __construct() {
9
+ // register actions
10
+
11
+ if(is_admin()){
12
+ add_action( 'admin_notices',array($this,'atlt_admin_notice_for_reviews'));
13
+ add_action( 'admin_print_scripts', array($this, 'atlt_load_script' ) );
14
+ add_action( 'wp_ajax_atlt_dismiss_notice',array($this,'atlt_dismiss_review_notice' ) );
15
+ }
16
+ }
17
+
18
+ /**
19
+ * Load script to dismiss notices.
20
+ *
21
+ * @return void
22
+ */
23
+ public function atlt_load_script() {
24
+ wp_register_script( 'atlt-feedback-notice-script', ATLT_URL. 'assets/js/atlt-admin-feedback-notice.js', array( 'jquery' ),null, true );
25
+ wp_enqueue_script( 'atlt-feedback-notice-script' );
26
+ wp_register_style( 'atlt-feedback-notice-styles',ATLT_URL.'assets/css/atlt-admin-feedback-notice.css' );
27
+ wp_enqueue_style( 'atlt-feedback-notice-styles' );
28
+ }
29
+ // ajax callback for review notice
30
+ public function atlt_dismiss_review_notice(){
31
+ $rs=update_option( 'atlt-ratingDiv','yes' );
32
+ echo json_encode( array("success"=>"true") );
33
+ exit;
34
+ }
35
+ // admin notice
36
+ public function atlt_admin_notice_for_reviews(){
37
+
38
+ if( !current_user_can( 'update_plugins' ) ){
39
+ return;
40
+ }
41
+ // get installation dates and rated settings
42
+ $installation_date = get_option( 'atlt-installDate' );
43
+ $alreadyRated =get_option( 'atlt-ratingDiv' )!=false?get_option( 'atlt-ratingDiv'):"no";
44
+ // check user already rated
45
+ if( $alreadyRated=="yes") {
46
+ return;
47
+ }
48
+
49
+ // grab plugin installation date and compare it with current date
50
+ $display_date = date( 'Y-m-d h:i:s' );
51
+ $install_date= new DateTime( $installation_date );
52
+ $current_date = new DateTime( $display_date );
53
+ $difference = $install_date->diff($current_date);
54
+ $diff_days= $difference->days;
55
+ // check if installation days is greator then week
56
+ if (isset($diff_days) && $diff_days>=3) {
57
+ echo $this->atlt_create_notice_content();
58
+ }
59
+ }
60
+
61
+ // generated review notice HTML
62
+ function atlt_create_notice_content(){
63
+
64
+ $ajax_url=admin_url( 'admin-ajax.php' );
65
+ $ajax_callback='atlt_dismiss_notice';
66
+ $wrap_cls="notice notice-info is-dismissible";
67
+ $img_path=ATLT_URL.'assets/images/atlt-logo.png';
68
+ /// $plugin_info = get_plugin_data( ATLT_PATH , true, true );
69
+ $p_name='Loco Automatic Translate Addon';
70
+ $like_it_text='Rate Now! ★★★★★';
71
+ $already_rated_text=esc_html__( 'I already rated it', 'atlt' );
72
+ $not_like_it_text=esc_html__( 'No, not good enough, i do not like to rate it!', 'atlt' );
73
+ $p_link=esc_url('https://wordpress.org/support/plugin/automatic-translator-addon-for-loco-translate/reviews/#new-post');
74
+ $pro_url=esc_url('https://eventscalendartemplates.com/');
75
+
76
+ $message="Thanks for using <b>$p_name</b> - WordPress plugin. We hope it meets your expectations! <br/>Please give us a quick rating, it works as a boost for us to keep working on more <a href='https://coolplugins.net' target='_blank'><strong>Cool Plugins</strong></a>!<br/>";
77
+
78
+ $html='<div data-ajax-url="%8$s" data-ajax-callback="%9$s" class="cool-feedback-notice-wrapper %1$s">
79
+ <div class="logo_container"><a href="%5$s"><img src="%2$s" alt="%3$s"></a></div>
80
+ <div class="message_container">%4$s
81
+ <div class="callto_action">
82
+ <ul>
83
+ <li class="love_it"><a href="%5$s" class="like_it_btn button button-primary" target="_new" title="%6$s">%6$s</a></li>
84
+ <li class="already_rated"><a href="javascript:void(0);" class="already_rated_btn button atlt_dismiss_notice" title="%7$s">%7$s</a></li>
85
+ </ul>
86
+ <div class="clrfix"></div>
87
+ </div>
88
+ </div>
89
+ </div>';
90
+
91
+ return sprintf($html,
92
+ $wrap_cls,
93
+ $img_path,
94
+ $p_name,
95
+ $message,
96
+ $p_link,
97
+ $like_it_text,
98
+ $already_rated_text,
99
+ $ajax_url,// 8
100
+ $ajax_callback//9
101
+
102
+ );
103
+
104
+ }
105
+
106
+ } //class end
107
+
108
+ }
109
+
110
+
111
+
includes/atlt-settings.php CHANGED
@@ -43,13 +43,8 @@ if( !class_exists( 'Atlt_Settings_Panel' ) ){
43
 
44
  array(
45
  'id' => $this->PREFIX.'register',
46
- 'title' => __('Loco Auto Translator Settings', 'cmb2'),
47
- )
48
- /* array(
49
- 'id' => $this->PREFIX.'_license_registration',
50
- 'title' => __('Registration', 'cmb2'),
51
- ), */
52
-
53
  );
54
  return $sections;
55
  }
@@ -74,6 +69,22 @@ if( !class_exists( 'Atlt_Settings_Panel' ) ){
74
  'type' => 'text',
75
  'placeholder'=>__('Enter API Key','cmb2'),
76
  'default' => '',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  )
78
  )
79
 
@@ -86,6 +97,26 @@ if( !class_exists( 'Atlt_Settings_Panel' ) ){
86
  return get_submit_button('Save');
87
 
88
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  /*
90
  |---------------------------------------------------
91
  | Add settings page to wordpress menu
43
 
44
  array(
45
  'id' => $this->PREFIX.'register',
46
+ 'title' => __('Loco Automatic Translate Addon Settings', 'cmb2'),
47
+ ),
 
 
 
 
 
48
  );
49
  return $sections;
50
  }
69
  'type' => 'text',
70
  'placeholder'=>__('Enter API Key','cmb2'),
71
  'default' => '',
72
+ ),
73
+ array(
74
+ 'name' => $this->PREFIX.'rating',
75
+ 'id' => $this->PREFIX.'rating',
76
+ 'label' => 'Please share your valuable feedback.',
77
+ 'desc' => $this->rate_now(),
78
+ 'type' => 'html',
79
+ 'default' => '',
80
+ ),
81
+ array(
82
+ 'name' => $this->PREFIX.'screenshort',
83
+ 'id' => $this->PREFIX.'screenshort',
84
+ 'label' => 'Usage instructions',
85
+ 'desc' => $this->screenshort(),
86
+ 'type' => 'html',
87
+ 'default' => '',
88
  )
89
  )
90
 
97
  return get_submit_button('Save');
98
 
99
  }
100
+
101
+ public function rate_now(){
102
+ $like_it_text='Rate Now! ★★★★★';
103
+ $p_link=esc_url('https://wordpress.org/support/plugin/automatic-translator-addon-for-loco-translate/reviews/#new-post');
104
+ $ajax_url=admin_url( 'admin-ajax.php' );
105
+ $html ='<p>Thanks for using Loco Automatic Translate Addon - WordPress plugin. We hope it meets your expectations!
106
+ Please give us a quick rating, it works as a boost for us to keep working on more <a href="https://coolplugins.net/">Cool Plugins!</a></p>
107
+ <a href="'.$p_link.'" class="like_it_btn button button-primary" target="_new" title="'.$like_it_text.'">'.$like_it_text.'</a>
108
+ ';
109
+ return $html;
110
+ }
111
+
112
+ public function screenshort(){
113
+
114
+ $src = ATLT_URL .'assets/images/screenshot-1.gif';
115
+
116
+ $html = '<img src="'.$src.'" width="100%">';
117
+
118
+ return $html;
119
+ }
120
  /*
121
  |---------------------------------------------------
122
  | Add settings page to wordpress menu
readme.txt CHANGED
@@ -1,9 +1,9 @@
1
  === Loco Automatic Translate Addon ===
2
  Contributors:narinder-singh,satindersingh,coolplugins
3
  Tags:loco,translate,translation,translator,localization,language,translations
4
- Requires at least:4.0
5
  Tested up to:5.2.1
6
- Stable tag:1.0
7
  License:GPLv2 or later
8
  License URI:http://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -56,5 +56,7 @@ As per Yendex API terms to use:- The volume of the text translated: 1,000,000 ch
56
  2. Translations Using Yendex API
57
 
58
  == Changelog ==
59
- Version 1.0 |13 June 2019
 
 
60
  - New: Initial Plugin Release
1
  === Loco Automatic Translate Addon ===
2
  Contributors:narinder-singh,satindersingh,coolplugins
3
  Tags:loco,translate,translation,translator,localization,language,translations
4
+ Requires at least:4.5
5
  Tested up to:5.2.1
6
+ Stable tag:1.0.2
7
  License:GPLv2 or later
8
  License URI:http://www.gnu.org/licenses/gpl-2.0.html
9
 
56
  2. Translations Using Yendex API
57
 
58
  == Changelog ==
59
+ Version 1.0.2 | 08 July 2019
60
+ - Fixed:Translations issues with Chinese language.
61
+ Version 1.0 | 08 June 2019
62
  - New: Initial Plugin Release