Automatic Translate Addon For Loco Translate - Version 1.0

Version Description

Download this release

Release Info

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

Version 1.0

automatic-translator-addon-for-loco-translate.php ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
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
9
+ Author:Cool Plugins
10
+ Author URI: https://coolplugins.net/
11
+ */
12
+
13
+ /**
14
+ * @package Loco Automatic Translate Addon
15
+ * @version 1.0
16
+ */
17
+ if (!defined('ABSPATH')) {
18
+ die('WordPress Environment Not Found!');
19
+ }
20
+
21
+ define('ATLT_FILE', __FILE__);
22
+ define('ATLT_URL', plugin_dir_url(ATLT_FILE));
23
+ define('ATLT_PATH', plugin_dir_path(ATLT_FILE));
24
+
25
+ class LocoAutoTranslate
26
+ {
27
+
28
+ public function __construct()
29
+ {
30
+
31
+ register_activation_hook( ATLT_FILE, array( $this, 'atlt_activate' ) );
32
+ register_deactivation_hook( ATLT_FILE, array( $this, 'atlt_deactivate' ) );
33
+ add_action('init', array($this, 'atlt_include_files'));
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
+
40
+ /*
41
+ |----------------------------------------------------------------------
42
+ | required php files
43
+ |----------------------------------------------------------------------
44
+ */
45
+ public function atlt_include_files()
46
+ {
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
+ /*
53
+ |----------------------------------------------------------------------
54
+ | AJAX called to this function for translation
55
+ |----------------------------------------------------------------------
56
+ */
57
+ public function atlt_translate_words()
58
+ {
59
+
60
+ $api_key = get_option('atlt_register');
61
+ $KEY = $api_key['atlt_api-key'];
62
+ $lang = $_REQUEST['sourceLan'] . '-' . $_REQUEST['targetLan'];
63
+ $DATA = $_REQUEST['data'];
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
+
72
+ if (is_wp_error($response)) {
73
+ return $response; // Bail early
74
+ }
75
+
76
+ $body = wp_remote_retrieve_body($response);
77
+ $data = json_decode($body);
78
+
79
+ die(json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
80
+
81
+ }
82
+
83
+ /*
84
+ |----------------------------------------------------------------------
85
+ | check if required "Loco Translate" plugin is active
86
+ | also register the plugin text domain
87
+ |----------------------------------------------------------------------
88
+ */
89
+ public function atlt_check_required_loco_plugin()
90
+ {
91
+
92
+ if (!function_exists('loco_plugin_self')) {
93
+ add_action('admin_notices', array($this, 'atlt_plugin_required_admin_notice'));
94
+ }
95
+ load_plugin_textdomain('atlt', false, basename(dirname(__FILE__)) . '/languages/');
96
+
97
+ }
98
+
99
+ /*
100
+ |----------------------------------------------------------------------
101
+ | Notice to 'Admin' if "Loco Translate" is not active
102
+ |----------------------------------------------------------------------
103
+ */
104
+ public function atlt_plugin_required_admin_notice()
105
+ {
106
+ if (current_user_can('activate_plugins')) {
107
+ $url = 'plugin-install.php?tab=plugin-information&plugin=loco-translate&TB_iframe=true';
108
+ $title = "Loco Translate";
109
+ $plugin_info = get_plugin_data(__FILE__, true, true);
110
+ echo '<div class="error"><p>' . sprintf(__('In order to use <strong>%s</strong> plugin, please install and activate the latest version of <a href="%s" class="thickbox" title="%s">%s</a>', 'atlt'), $plugin_info['Name'], esc_url($url), esc_attr($title), esc_attr($title)) . '.</p></div>';
111
+ deactivate_plugins(__FILE__);
112
+ }
113
+ }
114
+
115
+ /*
116
+ |------------------------------------------------------------------------
117
+ | Enqueue required JS file
118
+ |------------------------------------------------------------------------
119
+ */
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');
127
+ wp_localize_script('loco-js-editor', 'ATLT', array('api_key' => get_option('atlt_register')));
128
+ }
129
+
130
+ }
131
+
132
+ /*
133
+ |------------------------------------------------------
134
+ | Plugin activation
135
+ |------------------------------------------------------
136
+ */
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
145
+ |-------------------------------------------------------
146
+ */
147
+ public function atlt_deactivate(){
148
+
149
+ }
150
+
151
+ }
152
+ new LocoAutoTranslate();
includes/atlt-settings.php ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ |------------------------------------------------------------------------
4
+ | Settings panel
5
+ |------------------------------------------------------------------------
6
+ */
7
+ if( !class_exists( 'Atlt_Settings_Panel' ) ){
8
+ class Atlt_Settings_Panel {
9
+
10
+ public $settings_api;
11
+ public $PREFIX;
12
+
13
+ public function __construct(){
14
+ $this->settings_api = new Atlt_Settings_API;
15
+ $this->PREFIX = 'atlt_';
16
+
17
+ add_action('admin_init', array($this, 'admin_init' ) );
18
+ add_action('admin_menu', array( $this, 'admin_menu' ),100 );
19
+ add_action('admin_notices', array( $this, 'missing_api_key') );
20
+ }
21
+
22
+
23
+ /*
24
+ |------------------------------------------
25
+ | Initialize settings section
26
+ |------------------------------------------
27
+ */
28
+ public function admin_init(){
29
+
30
+ $this->settings_api->set_sections( $this->get_settings_sections() );
31
+ $this->settings_api->set_fields( $this->get_settings_fields() );
32
+ $this->settings_api->admin_init();
33
+ }
34
+
35
+ /*
36
+ |--------------------------------------------------------------------
37
+ | Create multiple section in settings page using array in $sections
38
+ |--------------------------------------------------------------------
39
+ */
40
+ public function get_settings_sections()
41
+ {
42
+ $sections = array(
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
+ }
56
+
57
+ /*
58
+ |--------------------------------------------------------------------
59
+ | return all settings fields to be initialized in settings page
60
+ |--------------------------------------------------------------------
61
+ */
62
+ public function get_settings_fields()
63
+ {
64
+ $settings_fields = array(
65
+
66
+ $this->PREFIX.'register' => array(
67
+
68
+ array(
69
+ 'name' => $this->PREFIX.'api-key',
70
+ 'id' => $this->PREFIX.'api-key',
71
+ 'class' => $this->PREFIX.'settings-field',
72
+ 'label' => 'Yendex API Key:',
73
+ 'desc' => '<a target="_blank" href="https://tech.yandex.com/translate/">Get Free Yendex API Key</a><br/>'.$this->welcome_tab(),
74
+ 'type' => 'text',
75
+ 'placeholder'=>__('Enter API Key','cmb2'),
76
+ 'default' => '',
77
+ )
78
+ )
79
+
80
+ );
81
+ return $settings_fields;
82
+ }
83
+
84
+ public function welcome_tab(){
85
+ //$this->ce_get_option($this->PREFIX.'-api-key');
86
+ return get_submit_button('Save');
87
+
88
+ }
89
+ /*
90
+ |---------------------------------------------------
91
+ | Add settings page to wordpress menu
92
+ |---------------------------------------------------
93
+ */
94
+ public function admin_menu()
95
+ {
96
+ add_submenu_page( 'loco','Loco Auto Translator', 'Loco Auto Translator', 'manage_options', 'loco-atlt', array($this, 'atlt_settings_page'));
97
+ }
98
+
99
+ public function atlt_settings_page(){
100
+
101
+ $this->settings_api->show_navigation();
102
+ $this->settings_api->show_forms('Save',false);
103
+
104
+ }
105
+
106
+ /*
107
+ |---------------------------------------------------------
108
+ | Gather settings field-values like get_options()
109
+ |---------------------------------------------------------
110
+ */
111
+ public function ce_get_option($option, $default = '')
112
+ {
113
+
114
+ $section = $this->PREFIX.'register';
115
+ $options = get_option($section);
116
+
117
+ if (isset($options[$option])) {
118
+ return $options[$option];
119
+ }
120
+
121
+ return $default;
122
+ }
123
+
124
+ /*
125
+ |-----------------------------------------------------------
126
+ | Show message in case of no api-key is saved
127
+ |-----------------------------------------------------------
128
+ */
129
+ public function missing_api_key(){
130
+
131
+ $api_key = $this->ce_get_option( $this->PREFIX.'api-key');
132
+
133
+ if( isset( $api_key ) && !empty( $api_key ) ){
134
+ return;
135
+ }
136
+
137
+ // Show API message only in translation editor page
138
+ if( isset($_REQUEST['action']) && $_REQUEST['action'] == 'file-edit' ){
139
+ $plugin_info = get_plugin_data( ATLT_FILE , true, true );
140
+
141
+ $message = sprintf('You must provide an %s to use the functionality of <strong>%s</strong>','<a href="'.admin_url('admin.php?page=loco-atlt').'">API key</a>',$plugin_info['Name']);
142
+
143
+ $translation = __($message,'atlt');
144
+
145
+ $HTML = '<div class="notice notice-warning inline is-dismissible"><p>'.$translation.'</p></div>';
146
+ echo $HTML;
147
+ }else if( isset( $_REQUEST['page'] ) && $_REQUEST['page'] == 'loco-atlt' ){
148
+
149
+ $message = sprintf('Get a free API KEY from %s and save it below to enable the Auto Translation feature.','<a href="https://tech.yandex.com/translate/" target="_blank">Yandex.com</a>');
150
+
151
+ $translation = __($message,'atlt');
152
+
153
+ $HTML = '<div class="notice notice-warning inline is-dismissible"><p>'.$translation.'</p></div>';
154
+
155
+ echo $HTML;
156
+ }
157
+ }
158
+
159
+ }
160
+
161
+ }
includes/class.settings-api.php ADDED
@@ -0,0 +1,649 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( !class_exists( 'Atlt_Settings_API' ) ):
4
+ class Atlt_Settings_API {
5
+
6
+ /**
7
+ * settings sections array
8
+ *
9
+ * @var array
10
+ */
11
+ protected $settings_sections = array();
12
+
13
+ /**
14
+ * Settings fields array
15
+ *
16
+ * @var array
17
+ */
18
+ protected $settings_fields = array();
19
+
20
+ public function __construct() {
21
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
22
+ }
23
+
24
+ /**
25
+ * Enqueue scripts and styles
26
+ */
27
+ function admin_enqueue_scripts() {
28
+ wp_enqueue_style( 'wp-color-picker' );
29
+
30
+ wp_enqueue_media();
31
+ wp_enqueue_script( 'wp-color-picker' );
32
+ wp_enqueue_script( 'jquery' );
33
+ }
34
+
35
+ /**
36
+ * Set settings sections
37
+ *
38
+ * @param array $sections setting sections array
39
+ */
40
+ function set_sections( $sections ) {
41
+ $this->settings_sections = $sections;
42
+
43
+ return $this;
44
+ }
45
+
46
+ /**
47
+ * Add a single section
48
+ *
49
+ * @param array $section
50
+ */
51
+ function add_section( $section ) {
52
+ $this->settings_sections[] = $section;
53
+
54
+ return $this;
55
+ }
56
+
57
+ /**
58
+ * Set settings fields
59
+ *
60
+ * @param array $fields settings fields array
61
+ */
62
+ function set_fields( $fields ) {
63
+ $this->settings_fields = $fields;
64
+
65
+ return $this;
66
+ }
67
+
68
+ function add_field( $section, $field ) {
69
+ $defaults = array(
70
+ 'name' => '',
71
+ 'label' => '',
72
+ 'desc' => '',
73
+ 'type' => 'text'
74
+ );
75
+
76
+ $arg = wp_parse_args( $field, $defaults );
77
+ $this->settings_fields[$section][] = $arg;
78
+
79
+ return $this;
80
+ }
81
+
82
+ /**
83
+ * Initialize and registers the settings sections and fileds to WordPress
84
+ *
85
+ * Usually this should be called at `admin_init` hook.
86
+ *
87
+ * This function gets the initiated settings sections and fields. Then
88
+ * registers them to WordPress and ready for use.
89
+ */
90
+ function admin_init() {
91
+ //register settings sections
92
+ foreach ( $this->settings_sections as $section ) {
93
+ if ( false == get_option( $section['id'] ) ) {
94
+ add_option( $section['id'] );
95
+ }
96
+
97
+ if ( isset($section['desc']) && !empty($section['desc']) ) {
98
+ $section['desc'] = '<div class="inside">' . $section['desc'] . '</div>';
99
+ $callback = function() use ( $section ) {
100
+ echo str_replace( '"', '\"', $section['desc'] );
101
+ };
102
+ } else if ( isset( $section['callback'] ) ) {
103
+ $callback = $section['callback'];
104
+ } else {
105
+ $callback = null;
106
+ }
107
+
108
+ add_settings_section( $section['id'], $section['title'], $callback, $section['id'] );
109
+ }
110
+
111
+ //register settings fields
112
+ foreach ( $this->settings_fields as $section => $field ) {
113
+ foreach ( $field as $option ) {
114
+
115
+ $name = $option['name'];
116
+ $type = isset( $option['type'] ) ? $option['type'] : 'text';
117
+ $label = isset( $option['label'] ) ? $option['label'] : '';
118
+ $callback = isset( $option['callback'] ) ? $option['callback'] : array( $this, 'callback_' . $type );
119
+
120
+ $args = array(
121
+ 'id' => $name,
122
+ 'class' => isset( $option['class'] ) ? $option['class'] : $name,
123
+ 'label_for' => "{$section}[{$name}]",
124
+ 'desc' => isset( $option['desc'] ) ? $option['desc'] : '',
125
+ 'name' => $label,
126
+ 'section' => $section,
127
+ 'size' => isset( $option['size'] ) ? $option['size'] : null,
128
+ 'options' => isset( $option['options'] ) ? $option['options'] : '',
129
+ 'std' => isset( $option['default'] ) ? $option['default'] : '',
130
+ 'sanitize_callback' => isset( $option['sanitize_callback'] ) ? $option['sanitize_callback'] : '',
131
+ 'type' => $type,
132
+ 'placeholder' => isset( $option['placeholder'] ) ? $option['placeholder'] : '',
133
+ 'min' => isset( $option['min'] ) ? $option['min'] : '',
134
+ 'max' => isset( $option['max'] ) ? $option['max'] : '',
135
+ 'step' => isset( $option['step'] ) ? $option['step'] : '',
136
+ );
137
+
138
+ add_settings_field( "{$section}[{$name}]", $label, $callback, $section, $section, $args );
139
+ }
140
+ }
141
+
142
+ // creates our settings in the options table
143
+ foreach ( $this->settings_sections as $section ) {
144
+ register_setting( $section['id'], $section['id'], array( $this, 'sanitize_options' ) );
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Get field description for display
150
+ *
151
+ * @param array $args settings field args
152
+ */
153
+ public function get_field_description( $args ) {
154
+ if ( ! empty( $args['desc'] ) ) {
155
+ $desc = sprintf( '<p class="description">%s</p>', $args['desc'] );
156
+ } else {
157
+ $desc = '';
158
+ }
159
+
160
+ return $desc;
161
+ }
162
+
163
+ /**
164
+ * Displays a text field for a settings field
165
+ *
166
+ * @param array $args settings field args
167
+ */
168
+ function callback_text( $args ) {
169
+
170
+ $value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
171
+ $size = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
172
+ $type = isset( $args['type'] ) ? $args['type'] : 'text';
173
+ $placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="' . $args['placeholder'] . '"';
174
+
175
+ $html = sprintf( '<input type="%1$s" class="%2$s-text" id="%3$s[%4$s]" name="%3$s[%4$s]" value="%5$s"%6$s/>', $type, $size, $args['section'], $args['id'], $value, $placeholder );
176
+ $html .= $this->get_field_description( $args );
177
+
178
+ echo $html;
179
+ }
180
+
181
+ /**
182
+ * Displays a url field for a settings field
183
+ *
184
+ * @param array $args settings field args
185
+ */
186
+ function callback_url( $args ) {
187
+ $this->callback_text( $args );
188
+ }
189
+
190
+ /**
191
+ * Displays a number field for a settings field
192
+ *
193
+ * @param array $args settings field args
194
+ */
195
+ function callback_number( $args ) {
196
+ $value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
197
+ $size = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
198
+ $type = isset( $args['type'] ) ? $args['type'] : 'number';
199
+ $placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="' . $args['placeholder'] . '"';
200
+ $min = ( $args['min'] == '' ) ? '' : ' min="' . $args['min'] . '"';
201
+ $max = ( $args['max'] == '' ) ? '' : ' max="' . $args['max'] . '"';
202
+ $step = ( $args['step'] == '' ) ? '' : ' step="' . $args['step'] . '"';
203
+
204
+ $html = sprintf( '<input type="%1$s" class="%2$s-number" id="%3$s[%4$s]" name="%3$s[%4$s]" value="%5$s"%6$s%7$s%8$s%9$s/>', $type, $size, $args['section'], $args['id'], $value, $placeholder, $min, $max, $step );
205
+ $html .= $this->get_field_description( $args );
206
+
207
+ echo $html;
208
+ }
209
+
210
+ /**
211
+ * Displays a checkbox for a settings field
212
+ *
213
+ * @param array $args settings field args
214
+ */
215
+ function callback_checkbox( $args ) {
216
+
217
+ $value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
218
+
219
+ $html = '<fieldset>';
220
+ $html .= sprintf( '<label for="wpuf-%1$s[%2$s]">', $args['section'], $args['id'] );
221
+ $html .= sprintf( '<input type="hidden" name="%1$s[%2$s]" value="off" />', $args['section'], $args['id'] );
222
+ $html .= sprintf( '<input type="checkbox" class="checkbox" id="wpuf-%1$s[%2$s]" name="%1$s[%2$s]" value="on" %3$s />', $args['section'], $args['id'], checked( $value, 'on', false ) );
223
+ $html .= sprintf( '%1$s</label>', $args['desc'] );
224
+ $html .= '</fieldset>';
225
+
226
+ echo $html;
227
+ }
228
+
229
+ /**
230
+ * Displays a multicheckbox for a settings field
231
+ *
232
+ * @param array $args settings field args
233
+ */
234
+ function callback_multicheck( $args ) {
235
+
236
+ $value = $this->get_option( $args['id'], $args['section'], $args['std'] );
237
+ $html = '<fieldset>';
238
+ $html .= sprintf( '<input type="hidden" name="%1$s[%2$s]" value="" />', $args['section'], $args['id'] );
239
+ foreach ( $args['options'] as $key => $label ) {
240
+ $checked = isset( $value[$key] ) ? $value[$key] : '0';
241
+ $html .= sprintf( '<label for="wpuf-%1$s[%2$s][%3$s]">', $args['section'], $args['id'], $key );
242
+ $html .= sprintf( '<input type="checkbox" class="checkbox" id="wpuf-%1$s[%2$s][%3$s]" name="%1$s[%2$s][%3$s]" value="%3$s" %4$s />', $args['section'], $args['id'], $key, checked( $checked, $key, false ) );
243
+ $html .= sprintf( '%1$s</label><br>', $label );
244
+ }
245
+
246
+ $html .= $this->get_field_description( $args );
247
+ $html .= '</fieldset>';
248
+
249
+ echo $html;
250
+ }
251
+
252
+ /**
253
+ * Displays a radio button for a settings field
254
+ *
255
+ * @param array $args settings field args
256
+ */
257
+ function callback_radio( $args ) {
258
+
259
+ $value = $this->get_option( $args['id'], $args['section'], $args['std'] );
260
+ $html = '<fieldset>';
261
+
262
+ foreach ( $args['options'] as $key => $label ) {
263
+ $html .= sprintf( '<label for="wpuf-%1$s[%2$s][%3$s]">', $args['section'], $args['id'], $key );
264
+ $html .= sprintf( '<input type="radio" class="radio" id="wpuf-%1$s[%2$s][%3$s]" name="%1$s[%2$s]" value="%3$s" %4$s />', $args['section'], $args['id'], $key, checked( $value, $key, false ) );
265
+ $html .= sprintf( '%1$s</label><br>', $label );
266
+ }
267
+
268
+ $html .= $this->get_field_description( $args );
269
+ $html .= '</fieldset>';
270
+
271
+ echo $html;
272
+ }
273
+
274
+ /**
275
+ * Displays a selectbox for a settings field
276
+ *
277
+ * @param array $args settings field args
278
+ */
279
+ function callback_select( $args ) {
280
+
281
+ $value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
282
+ $size = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
283
+ $html = sprintf( '<select class="%1$s" name="%2$s[%3$s]" id="%2$s[%3$s]">', $size, $args['section'], $args['id'] );
284
+
285
+ foreach ( $args['options'] as $key => $label ) {
286
+ $html .= sprintf( '<option value="%s"%s>%s</option>', $key, selected( $value, $key, false ), $label );
287
+ }
288
+
289
+ $html .= sprintf( '</select>' );
290
+ $html .= $this->get_field_description( $args );
291
+
292
+ echo $html;
293
+ }
294
+
295
+ /**
296
+ * Displays a textarea for a settings field
297
+ *
298
+ * @param array $args settings field args
299
+ */
300
+ function callback_textarea( $args ) {
301
+
302
+ $value = esc_textarea( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
303
+ $size = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
304
+ $placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="'.$args['placeholder'].'"';
305
+
306
+ $html = sprintf( '<textarea rows="5" cols="55" class="%1$s-text" id="%2$s[%3$s]" name="%2$s[%3$s]"%4$s>%5$s</textarea>', $size, $args['section'], $args['id'], $placeholder, $value );
307
+ $html .= $this->get_field_description( $args );
308
+
309
+ echo $html;
310
+ }
311
+
312
+ /**
313
+ * Displays the html for a settings field
314
+ *
315
+ * @param array $args settings field args
316
+ * @return string
317
+ */
318
+ function callback_html( $args ) {
319
+ echo $this->get_field_description( $args );
320
+ }
321
+
322
+ /**
323
+ * Displays a rich text textarea for a settings field
324
+ *
325
+ * @param array $args settings field args
326
+ */
327
+ function callback_wysiwyg( $args ) {
328
+
329
+ $value = $this->get_option( $args['id'], $args['section'], $args['std'] );
330
+ $size = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : '500px';
331
+
332
+ echo '<div style="max-width: ' . $size . ';">';
333
+
334
+ $editor_settings = array(
335
+ 'teeny' => true,
336
+ 'textarea_name' => $args['section'] . '[' . $args['id'] . ']',
337
+ 'textarea_rows' => 10
338
+ );
339
+
340
+ if ( isset( $args['options'] ) && is_array( $args['options'] ) ) {
341
+ $editor_settings = array_merge( $editor_settings, $args['options'] );
342
+ }
343
+
344
+ wp_editor( $value, $args['section'] . '-' . $args['id'], $editor_settings );
345
+
346
+ echo '</div>';
347
+
348
+ echo $this->get_field_description( $args );
349
+ }
350
+
351
+ /**
352
+ * Displays a file upload field for a settings field
353
+ *
354
+ * @param array $args settings field args
355
+ */
356
+ function callback_file( $args ) {
357
+
358
+ $value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
359
+ $size = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
360
+ $id = $args['section'] . '[' . $args['id'] . ']';
361
+ $label = isset( $args['options']['button_label'] ) ? $args['options']['button_label'] : __( 'Choose File' );
362
+
363
+ $html = sprintf( '<input type="text" class="%1$s-text wpsa-url" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s"/>', $size, $args['section'], $args['id'], $value );
364
+ $html .= '<input type="button" class="button wpsa-browse" value="' . $label . '" />';
365
+ $html .= $this->get_field_description( $args );
366
+
367
+ echo $html;
368
+ }
369
+
370
+ /**
371
+ * Displays a password field for a settings field
372
+ *
373
+ * @param array $args settings field args
374
+ */
375
+ function callback_password( $args ) {
376
+
377
+ $value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
378
+ $size = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
379
+
380
+ $html = sprintf( '<input type="password" class="%1$s-text" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s"/>', $size, $args['section'], $args['id'], $value );
381
+ $html .= $this->get_field_description( $args );
382
+
383
+ echo $html;
384
+ }
385
+
386
+ /**
387
+ * Displays a color picker field for a settings field
388
+ *
389
+ * @param array $args settings field args
390
+ */
391
+ function callback_color( $args ) {
392
+
393
+ $value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
394
+ $size = isset( $args['size'] ) && !is_null( $args['size'] ) ? $args['size'] : 'regular';
395
+
396
+ $html = sprintf( '<input type="text" class="%1$s-text wp-color-picker-field" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s" data-default-color="%5$s" />', $size, $args['section'], $args['id'], $value, $args['std'] );
397
+ $html .= $this->get_field_description( $args );
398
+
399
+ echo $html;
400
+ }
401
+
402
+
403
+ /**
404
+ * Displays a select box for creating the pages select box
405
+ *
406
+ * @param array $args settings field args
407
+ */
408
+ function callback_pages( $args ) {
409
+
410
+ $dropdown_args = array(
411
+ 'selected' => esc_attr($this->get_option($args['id'], $args['section'], $args['std'] ) ),
412
+ 'name' => $args['section'] . '[' . $args['id'] . ']',
413
+ 'id' => $args['section'] . '[' . $args['id'] . ']',
414
+ 'echo' => 0
415
+ );
416
+ $html = wp_dropdown_pages( $dropdown_args );
417
+ echo $html;
418
+ }
419
+
420
+ /**
421
+ * Sanitize callback for Settings API
422
+ *
423
+ * @return mixed
424
+ */
425
+ function sanitize_options( $options ) {
426
+
427
+ if ( !$options ) {
428
+ return $options;
429
+ }
430
+
431
+ foreach( $options as $option_slug => $option_value ) {
432
+ $sanitize_callback = $this->get_sanitize_callback( $option_slug );
433
+
434
+ // If callback is set, call it
435
+ if ( $sanitize_callback ) {
436
+ $options[ $option_slug ] = call_user_func( $sanitize_callback, $option_value );
437
+ continue;
438
+ }
439
+ }
440
+
441
+ return $options;
442
+ }
443
+
444
+ /**
445
+ * Get sanitization callback for given option slug
446
+ *
447
+ * @param string $slug option slug
448
+ *
449
+ * @return mixed string or bool false
450
+ */
451
+ function get_sanitize_callback( $slug = '' ) {
452
+ if ( empty( $slug ) ) {
453
+ return false;
454
+ }
455
+
456
+ // Iterate over registered fields and see if we can find proper callback
457
+ foreach( $this->settings_fields as $section => $options ) {
458
+ foreach ( $options as $option ) {
459
+ if ( $option['name'] != $slug ) {
460
+ continue;
461
+ }
462
+
463
+ // Return the callback name
464
+ return isset( $option['sanitize_callback'] ) && is_callable( $option['sanitize_callback'] ) ? $option['sanitize_callback'] : false;
465
+ }
466
+ }
467
+
468
+ return false;
469
+ }
470
+
471
+ /**
472
+ * Get the value of a settings field
473
+ *
474
+ * @param string $option settings field name
475
+ * @param string $section the section name this field belongs to
476
+ * @param string $default default text if it's not found
477
+ * @return string
478
+ */
479
+ function get_option( $option, $section, $default = '' ) {
480
+
481
+ $options = get_option( $section );
482
+
483
+ if ( isset( $options[$option] ) ) {
484
+ return $options[$option];
485
+ }
486
+
487
+ return $default;
488
+ }
489
+
490
+ /**
491
+ * Show navigations as tab
492
+ *
493
+ * Shows all the settings section labels as tab
494
+ */
495
+ function show_navigation() {
496
+ $html = '<h2 class="nav-tab-wrapper">';
497
+
498
+ $count = count( $this->settings_sections );
499
+
500
+ // don't show the navigation if only one section exists
501
+ if ( $count === 1 ) {
502
+ return;
503
+ }
504
+
505
+ foreach ( $this->settings_sections as $tab ) {
506
+ $html .= sprintf( '<a href="#%1$s" class="nav-tab" id="%1$s-tab">%2$s</a>', $tab['id'], $tab['title'] );
507
+ }
508
+
509
+ $html .= '</h2>';
510
+
511
+ echo $html;
512
+ }
513
+
514
+ /**
515
+ * Show the section settings forms
516
+ *
517
+ * This function displays every sections in a different form
518
+ */
519
+ function show_forms($savebtn='',$showSaveBtn=true) {
520
+ ?>
521
+ <div class="metabox-holder">
522
+ <?php foreach ( $this->settings_sections as $form ) { ?>
523
+ <div id="<?php echo $form['id']; ?>" class="group" style="display: none;">
524
+ <form method="post" action="options.php">
525
+ <?php
526
+ do_action( 'wsa_form_top_' . $form['id'], $form );
527
+ settings_fields( $form['id'] );
528
+ do_settings_sections( $form['id'] );
529
+ do_action( 'wsa_form_bottom_' . $form['id'], $form );
530
+ if ( isset( $this->settings_fields[ $form['id'] ] ) ):
531
+ ?>
532
+ <div style="padding-left: 10px">
533
+ <?php if($showSaveBtn==true) submit_button($savebtn); ?>
534
+ </div>
535
+ <?php endif; ?>
536
+ </form>
537
+ </div>
538
+ <?php } ?>
539
+ </div>
540
+ <?php
541
+ $this->script();
542
+ }
543
+
544
+ /**
545
+ * Tabbable JavaScript codes & Initiate Color Picker
546
+ *
547
+ * This code uses localstorage for displaying active tabs
548
+ */
549
+ function script() {
550
+ ?>
551
+ <script>
552
+ jQuery(document).ready(function($) {
553
+ //Initiate Color Picker
554
+ $('.wp-color-picker-field').wpColorPicker();
555
+
556
+ // Switches option sections
557
+ $('.group').hide();
558
+ var activetab = '';
559
+ if (typeof(localStorage) != 'undefined' ) {
560
+ activetab = localStorage.getItem("activetab");
561
+ }
562
+
563
+ //if url has section id as hash then set it as active or override the current local storage value
564
+ if(window.location.hash){
565
+ activetab = window.location.hash;
566
+ if (typeof(localStorage) != 'undefined' ) {
567
+ localStorage.setItem("activetab", activetab);
568
+ }
569
+ }
570
+
571
+ if (activetab != '' && $(activetab).length ) {
572
+ $(activetab).fadeIn();
573
+ } else {
574
+ $('.group:first').fadeIn();
575
+ }
576
+ $('.group .collapsed').each(function(){
577
+ $(this).find('input:checked').parent().parent().parent().nextAll().each(
578
+ function(){
579
+ if ($(this).hasClass('last')) {
580
+ $(this).removeClass('hidden');
581
+ return false;
582
+ }
583
+ $(this).filter('.hidden').removeClass('hidden');
584
+ });
585
+ });
586
+
587
+ if (activetab != '' && $(activetab + '-tab').length ) {
588
+ $(activetab + '-tab').addClass('nav-tab-active');
589
+ }
590
+ else {
591
+ $('.nav-tab-wrapper a:first').addClass('nav-tab-active');
592
+ }
593
+ $('.nav-tab-wrapper a').click(function(evt) {
594
+ $('.nav-tab-wrapper a').removeClass('nav-tab-active');
595
+ $(this).addClass('nav-tab-active').blur();
596
+ var clicked_group = $(this).attr('href');
597
+ if (typeof(localStorage) != 'undefined' ) {
598
+ localStorage.setItem("activetab", $(this).attr('href'));
599
+ }
600
+ $('.group').hide();
601
+ $(clicked_group).fadeIn();
602
+ evt.preventDefault();
603
+ });
604
+
605
+ $('.wpsa-browse').on('click', function (event) {
606
+ event.preventDefault();
607
+
608
+ var self = $(this);
609
+
610
+ // Create the media frame.
611
+ var file_frame = wp.media.frames.file_frame = wp.media({
612
+ title: self.data('uploader_title'),
613
+ button: {
614
+ text: self.data('uploader_button_text'),
615
+ },
616
+ multiple: false
617
+ });
618
+
619
+ file_frame.on('select', function () {
620
+ attachment = file_frame.state().get('selection').first().toJSON();
621
+ self.prev('.wpsa-url').val(attachment.url).change();
622
+ });
623
+
624
+ // Finally, open the modal
625
+ file_frame.open();
626
+ });
627
+ });
628
+ </script>
629
+ <?php
630
+ $this->_style_fix();
631
+ }
632
+
633
+ function _style_fix() {
634
+ global $wp_version;
635
+
636
+ if (version_compare($wp_version, '3.8', '<=')):
637
+ ?>
638
+ <style type="text/css">
639
+ /** WordPress 3.8 Fix **/
640
+ .form-table th { padding: 20px 10px; }
641
+ #wpbody-content .metabox-holder { padding-top: 5px; }
642
+ </style>
643
+ <?php
644
+ endif;
645
+ }
646
+
647
+ }
648
+
649
+ endif;
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 );
readme.txt ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
10
+ Automatic language translator add-on for Loco Translate plugin to translate WordPress plugins and themes translation files.
11
+
12
+ == Description ==
13
+
14
+ **Automatic Machine Translator Addon For Loco Translate**
15
+
16
+ Install this plugin along with the famous **[Loco Translate](https://wordpress.org/plugins/loco-translate/)** plugin (**900,000+ Active Installations**) and automatically machine translate any WordPress plugin or theme translation files into any language.
17
+
18
+ = Plugin Features: =
19
+ * One-click translate any plugin or theme all translatable strings.
20
+ * Check auto translations inside Loco build-in editor to manually edit any machine translated string.
21
+ * This plugin **[Powered by Yandex.Translate](http://translate.yandex.com/)** to auto translate plugin / theme language(po) files. Grab free api key:- [https://translate.yandex.com/developers/keys](https://translate.yandex.com/developers/keys)
22
+
23
+ > This plugin is not developed by or affiliated with the "Loco Translate". It is a third party addon that provides automatic machine translations to quickly translate your theme or plugin language files.
24
+
25
+ > Special thanks to famous **[Loco Translate](https://wordpress.org/plugins/loco-translate/)** plugin authors for creating an awesome plugin for translations.
26
+
27
+ > All automatic translations will machine translations, powered by Yendex Translate API, so we don't guarantee 100% correctness, please check all translated text carefully before making it live on your production site.
28
+
29
+ == Installation ==
30
+
31
+ 1. Install **Loco Automatic Translate Addon** from the WordPress.org repository or by uploading plugin-zip unzipped folder to the **/wp-content/plugins** directory. You must also install **[Loco Translate](https://wordpress.org/plugins/loco-translate/)** free plugin if you want to use this addon.
32
+
33
+ 2. Activate the plugin through **Plugins >> Installed Plugin** menu in WordPress
34
+
35
+ 3. After plugin activation you need to insert Yendex API key inside **Loco Translate >> Loco Auto Translator**. You can grab a free api key at here:- [https://translate.yandex.com/developers/keys](https://translate.yandex.com/developers/keys)
36
+
37
+ 4. While editing any plugin or theme language file using Loco build-in editor, you will find an auto translator button at top to quickly translate all translatable strings with one-click.
38
+
39
+ == Frequently Asked Questions ==
40
+
41
+ = How it works? =
42
+
43
+ This plugin works as an addon for **Loco Translate** plugin. First you need to install and activate free version of "Loco translate" then install this addon and use one-click machine translations (supported by Yendex API).
44
+
45
+ = Are you using any language translation API? =
46
+
47
+ **[Yendex Translate API](https://tech.yandex.com/translate/)**, you can read more about its terms to use at here - https://yandex.com/legal/translate_api/ Also grab a free API key at here:- [https://translate.yandex.com/developers/keys](https://translate.yandex.com/developers/keys)
48
+
49
+ = Is there any translation limit? =
50
+
51
+ As per Yendex API terms to use:- The volume of the text translated: 1,000,000 characters per day but not more than 10,000,000 per month.
52
+
53
+ == Screenshots ==
54
+
55
+ 1. Automatic Loco Translate Addon
56
+ 2. Translations Using Yendex API
57
+
58
+ == Changelog ==
59
+ Version 1.0 |13 June 2019
60
+ - New: Initial Plugin Release