Passed
Pull Request — master (#81)
by Dante
01:06
created

GettextCommand::execute()   B

Complexity

Conditions 8
Paths 64

Size

Total Lines 47
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 31
c 0
b 0
f 0
nc 64
nop 2
dl 0
loc 47
rs 8.1795
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * BEdita, API-first content management framework
6
 * Copyright 2022 Atlas Srl, Chialab Srl
7
 *
8
 * This file is part of BEdita: you can redistribute it and/or modify
9
 * it under the terms of the GNU Lesser General Public License as published
10
 * by the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
14
 */
15
16
namespace BEdita\I18n\Command;
17
18
use BEdita\I18n\Filesystem\File;
19
use BEdita\I18n\Filesystem\Gettext;
20
use BEdita\I18n\Filesystem\Paths;
21
use BEdita\I18n\Filesystem\Ttag;
22
use Cake\Command\Command;
23
use Cake\Console\Arguments;
24
use Cake\Console\ConsoleIo;
25
use Cake\Console\ConsoleOptionParser;
26
use Cake\Core\Configure;
27
28
/**
29
 * Gettext command.
30
 */
31
class GettextCommand extends Command
32
{
33
    /**
34
     * @var int
35
     */
36
    public const CODE_CHANGES = 2;
37
38
    /**
39
     * The Po results
40
     *
41
     * @var array
42
     */
43
    protected $poResult = [];
44
45
    /**
46
     * The template paths
47
     *
48
     * @var array
49
     */
50
    protected $templatePaths = [];
51
52
    /**
53
     * The locale path
54
     *
55
     * @var string
56
     */
57
    protected $localePath = '';
58
59
    /**
60
     * The name of default domain if not specified. Used for pot and po file names.
61
     *
62
     * @var string
63
     */
64
    protected $defaultDomain = 'default';
65
66
    /**
67
     * The locales to generate.
68
     *
69
     * @var array
70
     */
71
    protected $locales = [];
72
73
    /**
74
     * @inheritDoc
75
     */
76
    public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser
77
    {
78
        return parent::buildOptionParser($parser)
79
            ->setDescription([
80
                'Create or update i18n po/pot files',
81
                '',
82
                '`bin/cake gettext`: update files for current app',
83
                '`bin/cake gettext -app <app path>`: update files for the app',
84
                '`bin/cake gettext -plugin <plugin name>`: update files for the plugin',
85
            ])
86
            ->addOption('app', [
87
                'help' => 'The app path, for i18n update.',
88
                'short' => 'a',
89
                'required' => false,
90
            ])
91
            ->addOption('plugin', [
92
                'help' => 'The plugin name, for i18n update.',
93
                'short' => 'p',
94
                'required' => false,
95
            ])
96
            ->addOption('plugins', [
97
                'help' => 'All plugins',
98
                'required' => false,
99
                'boolean' => true,
100
            ])
101
            ->addOption('ci', [
102
                'help' => 'Run in CI mode. Exit with error if PO files are changed.',
103
                'required' => false,
104
                'boolean' => true,
105
            ])
106
            ->addOption('locales', [
107
                'help' => 'Comma separated list of locales to generate. Leave empty to use configuration `I18n.locales`',
108
                'short' => 'l',
109
                'default' => implode(',', array_keys((array)Configure::read('I18n.locales'))),
110
            ]);
111
    }
112
113
    /**
114
     * Get po result.
115
     *
116
     * @return array
117
     */
118
    public function getPoResult(): array
119
    {
120
        return $this->poResult;
121
    }
122
123
    /**
124
     * Get templatePaths.
125
     *
126
     * @return array
127
     */
128
    public function getTemplatePaths(): array
129
    {
130
        return $this->templatePaths;
131
    }
132
133
    /**
134
     * Get localePath
135
     *
136
     * @return string
137
     */
138
    public function getLocalePath(): string
139
    {
140
        return $this->localePath;
141
    }
142
143
    /**
144
     * Update gettext po files.
145
     *
146
     * @param \Cake\Console\Arguments $args The command arguments.
147
     * @param \Cake\Console\ConsoleIo $io The console io
148
     * @return null|void|int The exit code or null for success
149
     */
150
    public function execute(Arguments $args, ConsoleIo $io)
151
    {
152
        $resCmd = [];
153
        exec('which msgmerge 2>&1', $resCmd);
154
        if (empty($resCmd[0])) {
155
            $io->abort('ERROR: msgmerge not available. Please install gettext utilities.');
156
        }
157
158
        $io->out('Updating .pot and .po files...');
159
        $this->locales = array_filter(explode(',', $args->getOption('locales')));
0 ignored issues
show
Bug introduced by
It seems like $args->getOption('locales') can also be of type boolean and null; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

159
        $this->locales = array_filter(explode(',', /** @scrutinizer ignore-type */ $args->getOption('locales')));
Loading history...
160
        Paths::setup($this->templatePaths, $this->localePath, $this->defaultDomain, $args->getOptions());
161
        foreach ($this->templatePaths as $path) {
162
            $io->out(sprintf('Search in: %s', $path));
163
            File::parseDir($path, $this->defaultDomain, $this->poResult);
164
        }
165
166
        $io->out('Creating master .pot file');
167
        $result = Gettext::writeMasterPot($this->localePath, $this->poResult);
168
        foreach ($result['info'] as $info) {
169
            $io->out($info);
170
        }
171
        $hasChanges = $result['updated'];
172
173
        $io->out('Extracting ttag translations from javascript files');
174
        $result = Ttag::extract($this->localePath, $args->getOption('plugin'));
0 ignored issues
show
Bug introduced by
It seems like $args->getOption('plugin') can also be of type boolean; however, parameter $plugin of BEdita\I18n\Filesystem\Ttag::extract() does only seem to accept null|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

174
        $result = Ttag::extract($this->localePath, /** @scrutinizer ignore-type */ $args->getOption('plugin'));
Loading history...
175
        foreach ($result['info'] as $info) {
176
            $io->out($info);
177
        }
178
        $io->out(sprintf('Ttag extracted: %s', $result['extracted']));
179
180
        $io->hr();
181
        $io->out('Merging master .pot with current .po files');
182
        $io->hr();
183
184
        $io->out('Writing po files');
185
        $result = Gettext::writePoFiles($this->locales, $this->localePath, $this->poResult);
186
        foreach ($result['info'] as $info) {
187
            $io->out($info);
188
        }
189
190
        $io->out('Done');
191
192
        if ($args->getOption('ci') && $hasChanges) {
193
            return GettextCommand::CODE_CHANGES;
194
        }
195
196
        return GettextCommand::CODE_SUCCESS;
197
    }
198
}
199