/var/www/html_nl/wp-content/plugins/loco-translate/src/admin/init/InitPoController.php


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
<?php
/**
 * pre-msginit function. Prepares arguments for creating a new PO language file
 */
class Loco_admin_init_InitPoController extends Loco_admin_bundle_BaseController {
    
    
    
/**
     * {@inheritdoc}
     */
    
public function init(){
        
parent::init();
        
$this->enqueueStyle('poinit');
        
//
        
$bundle $this->getBundle();
        
$this->set('title'__('New language','loco-translate').' &lsaquo; '.$bundle );
    }


    
/**
     * {@inheritdoc}
     */
    
public function getHelpTabs(){
        return  [
            
__('Overview') => $this->viewSnippet('tab-init-po'),
        ];
    }


    
/**
     * Sort to the left the best option for saving new translation files.
     * This favours custom locations, with system secondary. No other locations will be pre-selected. 
     * @param Loco_mvc_ViewParams[] $choices
     * @return Loco_mvc_ViewParams|null
     */
    
private function sortPreferred( array $choices ){
        
usort$choices, [__CLASS__,'_onSortPreferred'] );
        foreach( 
$choices as $choice ){
            if( 
$choice['disabled'] || $choice['copying'] || $choice['exists']){
                continue;
            }
            
// avoid promoting the Author location
            
$t $choice['dirtype'];
            if( 
'theme' === $t || 'plugin' === $t){
                continue;
            }
            
// pre-select this. It should be a writable custom or system file.
            
return $choice;
        }
        return 
null;
    }


    
/**
     * @internal
     * @return int
     */
    
public static function _onSortPreferredLoco_mvc_ViewParams $aLoco_mvc_ViewParams $b ){
        
$x self::scoreFileChoice($a);
        
$y self::scoreFileChoice($b);
        return 
$x === $y : ( $x $y ? -);
    }
    
    
    
/**
     * Score an individual file choice for sorting preferred
     * @return int
     */
    
private static function scoreFileChoiceLoco_mvc_ViewParams $p ){
        
$score 0;
        if( 
$p['writable'] ){
            
$score++;
        }
        if( 
$p['disabled'] ){
            
$score -= 2;
        }
        if( 
$p['systype'] ){
            
$score--;
        }
        return 
$score;
    }


    
/**
     * @internal
     * @param int $a
     * @param int $b
     * @return int
     */
    
private static function compareLocationKeys$a$b ){
        static 
$order = ['custom' => 4'wplang' => 3'theme' => 2'plugin' => 2'other' => ];
        
$x $order[$a];
        
$y $order[$b];
        return 
$x === $y : ( $x $y ? -);
    }


    
/**
     * {@inheritdoc}
     */
    
public function render(){
        
        
$breadcrumb $this->prepareNavigation();
        
// "new" tab is confusing when no project-scope navigation
        // $this->get('tabs')->add( __('New PO','loco-translate'), '', true );
        
        // bundle mandatory, but project optional
        
$bundle $this->getBundle();

        try {
            
$project $this->getProject();
            
$slug $project->getSlug();
            
$domain = (string) $project->getDomain();
            
// translators: %s refers to the slug/handle of a theme or plugin
            
$subhead sprintf__('Initializing new translations in "%s"','loco-translate'), $slug?:$domain );
        }
        catch( 
Loco_error_Exception $e ){
            
$project null;
            
$subhead __('Initializing new translations in unknown set','loco-translate');
        }

        
$title __('New language','loco-translate');
        
$this->set('subhead'$subhead );
        
        
// navigate up to bundle listing page 
        
$breadcrumb->add$title );
        
$this->set'breadcrumb'$breadcrumb );
        
        
// default locale is a placeholder
        
$localeTag '';
        
$locale = new Loco_Locale('zxx');
        
$content_dir untrailingslashitloco_constant('WP_CONTENT_DIR') );
        
$extracting = (bool) $this->get('extract');
        
$copying false;
        
$sourcedir '';
        
        
// Permit using any provided file a template instead of POT
        
if( $potpath $this->get('path') ){
            
$potfile = new Loco_fs_LocaleFile($potpath);
            
$potfile->normalize($content_dir);
            if( ! 
$potfile->exists() ){
                throw new 
Loco_error_Exception('Forced template argument must exist');
            }
            
$copying true;
            
$sourcedir $potfile->dirname();
            
// forced source could be a POT (although UI would normally prevent it)
            
if( $potfile->getSuffix() ){
                
$locale $potfile->getLocale();
                if( 
$locale->isValid() ){
                    
$this->set('sourceLocale'$locale );
                    
$localeTag = (string) $locale;
                }
            }
        }
        
// else project not configured. UI should prevent this by not offering msginit
        
else if( ! $project ){
            throw new 
Loco_error_Exception('Cannot add new language to unconfigured set');
        }
        
// else POT file may or may not be known, and may or may not exist
        
else {
            
$potfile $project->getPot();
        }

        
$locales = [];
        
$installed = [];
        
$api = new Loco_api_WordPressTranslations;
        
$prefs Loco_data_Preferences::get();
        
// pull installed list first, this will include en_US and any non-standard languages installed
        
foreach( $api->getInstalledCore() as $tag ){
            
$tmp Loco_Locale::parse($tag);
            if( 
$tmp->isValid() && $prefs->has_locale($tmp) ){
                
$tag = (string) $tmp;
                
// We may not have names for these, so just the language tag will show
                
$installed[$tag] = new Loco_mvc_ViewParams( [
                    
'value' => $tag,
                    
'icon'  => $tmp->getIcon(),
                    
'label' => $tmp->ensureName($api),
                    
'selected' => $tag === $localeTag ' selected' ''
                
] );
            }
        }
        
// pull the same list of "available" languages as used in WordPress settings
        
foreach( $api->getAvailableCore() as $tag => $tmp ){
            if( ! 
array_key_exists($tag,$installed) && $prefs->has_locale($tmp) ){
                
$locales[$tag] = new Loco_mvc_ViewParams( [
                    
'value' => $tag,
                    
'icon'  => $tmp->getIcon(),
                    
'label' => $tmp->ensureName($api),
                    
'selected' => $tag === $localeTag ' selected' ''
                
] );
            }
        }

        
// two locale lists built for "installed" and "available" dropdowns. Else populate custom field.
        
$this->set('locales'$locales );
        
$this->set('installed'$installed );
        if( 
$localeTag && ! array_key_exists($localeTag,$installed) && ! array_key_exists($localeTag,$locales) ){
            
$this->set('custom'$localeTag );
        }

        
// Critical that user selects the correct save location:
        
if( $project ){
            
$filechoice $project->initLocaleFiles$locale );
        }
        
// without configured project we will only allow save to same location
        
else {
            
$filechoice = new Loco_fs_FileList;
        }
        
        
        
// Define suitable prompts for extracting from source, creating a template, or (preferred) copy an existing PO.
        
$prompts = [];
        if( ! 
$copying ){
            
$args array_intersect_key$_GET,['bundle'=>'','domain'=>'']);
            
// link to extract a new template if none exists
            
$prompts['xgettext'] = new Loco_mvc_ViewParams( [ 
                
'link' => Loco_mvc_AdminRouter::generate$this->get('type' ).'-xgettext'$args ),
                
'text' => __('Create template','loco-translate'),
            ] );
            
// link to direct extraction here, if not already doing
            
$prompts['skip'] = new Loco_mvc_ViewParams( [
                
'link' => Loco_mvc_AdminRouter::generate$this->get('_route'), $args + ['extract'=>'1'] ),
                
'text' => __('Skip template','loco-translate'),
            ] );
            
// link to copy an existing PO file, if there's an installed one
            /* @var Loco_fs_LocaleFile $pofile */
            
foreach( $project->findLocaleFiles('po') as $pofile ){
                
$dir = new Loco_fs_LocaleDirectory$pofile->dirname() );
                if( 
'wplang' !== $dir->getTypeId() ){
                    continue;
                }
                
// we don't know yet what locale the user will select, so pick first PO.
                
$args array_intersect_key$_GET,['bundle'=>'','domain'=>'']);
                
$args['path'] = $pofile->getRelativePath($content_dir);
                
$prompts['copy'] = new Loco_mvc_FileParams( [
                    
'link' => Loco_mvc_AdminRouter::generate$this->get('type' ).'-msginit'$args ),
                    
'text' => __('Copy PO file','loco-translate'),
                ], 
$pofile );
                break;
            }
        }


        
// show information about POT file if we are initializing from template
        
if( $potfile && $potfile->exists() ){
            
$meta Loco_gettext_Metadata::load($potfile);
            
$total $meta->getTotal();
            
// translators: 1: Number of strings; 2: Name of POT file; e.g. "100 strings found in file.pot"
            
$summary sprintf_n('%1$s string found in %2$s','%1$s strings found in %2$s',$total,'loco-translate'), number_format($total), $potfile->basename() );
            
$this->set'pot', new Loco_mvc_ViewParams( [
                
'name' => $potfile->basename(),
                
'path' => $meta->getPath(false),
            ] ) );
            
// if copying an existing PO file, we can fairly safely establish the correct prefixing
            
if( $copying ){
                
$prefix $potfile->getPrefix();
                
$poname $prefix sprintf('%s-%s.po',$prefix,$locale) : sprintf('%s.po',$locale);
                
$pofile = new Loco_fs_LocaleFile$poname );
                
$pofile->normalize$potfile->dirname() );
                
$filechoice->add$pofile );
            }
        }
        
// else not initializing from template- prompt to copy existing PO file
        
else if( array_key_exists('copy',$prompts) && ! $extracting ){
            
$this->set('copy'$prompts['copy'] );
            
$this->set('skip'$prompts['skip'] );
            
$this->set('ext'$prompts['xgettext'] );
            return 
$this->view('admin/init/init-copy');
        }
        
// else prompt to extract from source (advanced).
        
else if( Loco_data_Settings::get()->pot_expected ){
            
// if allowing source extraction without warning show brief description of source files
            
if( $extracting || === Loco_data_Settings::get()->pot_expected ){
                
// Tokenizer required for string extraction
                
if( ! loco_check_extension('tokenizer') ){
                    return 
$this->view('admin/errors/no-tokenizer');
                }
                
$nfiles count$project->findSourceFiles() );
                
// translators: Were %s is number of source files that will be scanned
                
$summary sprintf_n('%s source file will be scanned for translatable strings','%s source files will be scanned for translatable strings',$nfiles,'loco-translate'), number_format_i18n($nfiles) );
                
$extracting true;
            }
            
// else prompt for template creation before continuing
            
else {
                
// POT could still be defined, it might just not exist yet
                
if( $potfile ){
                    
$this->set('pot'Loco_mvc_FileParams::create($potfile) );
                }
                
// else offer assignment of a new file
                
else {
                    
$this->set'conf', new Loco_mvc_ViewParams( [
                        
'link' => Loco_mvc_AdminRouter::generate$this->get('type').'-conf'array_intersect_key($_GET,['bundle'=>'']) ),
                        
'text' => __('Assign template','loco-translate'),
                    ] ) );
                }
                
$this->set('ext'$prompts['xgettext'] );
                
$this->set('skip'$prompts['skip'] );
                return 
$this->view('admin/init/init-prompt');
            }
        }
        else {
            throw new 
Loco_error_Exception('Plugin settings disallow missing templates');
        }
        
$this->set'summary'$summary );
        
        
// group established locations into types (official, etc..)
        // there is no point checking whether any of these file exist, because we don't know what language will be chosen yet.
        
$sortable = [];
        
$locations = [];
        
$fs_failure null;
        
/* @var Loco_fs_LocaleFile $pofile */
        
foreach( $filechoice as $pofile ){
            
$parent = new Loco_fs_LocaleDirectory$pofile->dirname() );
            
$systype $parent->getUpdateType();
            
$typeId $parent->getTypeId();
            if( ! isset(
$locations[$typeId]) ){
                
$locations[$typeId] = new Loco_mvc_ViewParams( [
                    
'label' => $parent->getTypeLabel$typeId ),
                    
'paths' => [],
                ] );
            }
            
// folder may be unwritable (requiring connect to create file) or may be denied under security settings
            
try {
                
$context $parent->getWriteContext()->authorize();
                
$writable $context->writable();
                
$disabled false;
            }
            catch( 
Loco_error_WriteException $e ){
                
$fs_failure $e->getMessage();
                
$writable false;
                
$disabled true;
            }
            
$suffix $pofile->getSuffix().'.po';
            
$choice = new Loco_mvc_ViewParams(  [
                
'checked' => '',
                
'writable' => $writable,
                
'disabled' => $disabled,
                
'systype' => $systype,
                
'dirtype' => $typeId,
                
'parent' => Loco_mvc_FileParams::create($parent),
                
'hidden' => $pofile->getRelativePath($content_dir),
                
'holder' => str_replace$suffix'<span>{locale}</span>.po'$pofile->basename() ),
                
'copying' => $sourcedir === $parent->getPath(),
                
'exists' => $copying && $pofile->exists(),
            ] );
            
$sortable[] = $choice;
            
$locations[$typeId]['paths'][] = $choice;
        }

        
// display locations in runtime preference order
        
uksort$locations, [__CLASS__,'compareLocationKeys'] );
        
$this->set'locations'$locations );

        
// pre-select best option, excluding the current source if copying
        
$preferred $this->sortPreferred$sortable );
        if( 
$preferred instanceof ArrayAccess ){
            
$preferred['checked'] = 'checked';
        }
        
// else show total lock message. probably file mods disallowed
        
else if( $fs_failure ){
            
$this->set('fsLocked'$fs_failure );
        }
        
        
// hidden fields to pass through to Ajax endpoint
        
$this->set('hidden', new Loco_mvc_HiddenFields( [
            
'action' => 'loco_json',
            
'route' => 'msginit',
            
'loco-nonce' => $this->setNonce('msginit')->value,
            
'type' => $bundle->getType(),
            
'bundle' => $bundle->getHandle(),
            
'domain' => $project $project->getId() : '',
            
'source' => $potpath,
        ] ) );
        
        
$this->set('help', new Loco_mvc_ViewParams( [
            
'href' => apply_filters('loco_external','https://localise.biz/wordpress/plugin/manual/msginit'),
            
'text' => __("What's this?",'loco-translate'),
        ] ) );
        
        
// add a prompt if there's a better option for creating a new PO
        
if( ! $copying ){
            if( 
array_key_exists('copy',$prompts) ){
                
$prompt $prompts['copy'];
                
// translators: %s will be replaced with a PO file name
                
$prompt['text'] = sprintf__('Copy %s instead','loco-translate'), $prompt->__get('name') );
            }
            else {
                
$prompt $prompts['xgettext'];
                
$prompt['text'] = __('Create template instead','loco-translate');
            }
            if( 
$extracting ){
                
$prompt['title'] = __("You're creating translations directly from source code",'loco-translate');
                
$this->set('prompt',$prompt);
            }
            else if( 
array_key_exists('copy',$prompts) ){
                
$prompt['title'] = __("You're creating translations from a POT file",'loco-translate');
                
$this->set('prompt',$prompt);
            }
        }

        
// file system prompts will be handled when paths are selected (i.e. we don't have one yet)
        
$this->prepareFsConnect'create''' );
        
        
$this->enqueueScript('poinit');
        return 
$this->view('admin/init/init-po');
    }

}