Passed
Branch feature/code-analysis (eb61a8)
by Jonathan
06:01
created

loadedMyArtJaubTranslations()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 7
c 1
b 0
f 0
dl 0
loc 13
rs 10
cc 4
nc 2
nop 0
1
<?php
2
3
/**
4
 * webtrees-mod-translationtool: MyArtJaub Translation Tool Module for webtrees
5
 *
6
 * @package MyArtJaub\Webtrees\Module
7
 * @subpackage TranslationTool
8
 * @author Jonathan Jaubart <[email protected]>
9
 * @copyright Copyright (c) 2016-2020, Jonathan Jaubart
10
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License, version 3
11
 */
12
13
declare(strict_types=1);
14
15
namespace MyArtJaub\Webtrees\Module\TranslationTool\Model;
16
17
use Fisharebest\Webtrees\I18N;
18
use Gettext\Translation;
19
use Illuminate\Support\Collection;
20
use MyArtJaub\Webtrees\Module\TranslationTool\Services\SourceCodeService;
21
use ReflectionClass;
22
23
/**
24
 * Translations Analyzer
25
 * Extract translations from the code, and analize the translations list.
26
 */
27
class TranslationsAnalyzer
28
{
29
30
   
31
    
32
    /** @var SourceCodeService $sourcecode_service */
33
    private $sourcecode_service;
34
    
35
    /**
36
     * List of items to be translated found in the code.
37
     * @var Collection $strings_to_translate
38
     */
39
    private $strings_to_translate;
40
    
41
    /**
42
     * List of translations loaded through the standard I18N library.
43
     * @var Collection $loaded_translations
44
     */
45
    private $loaded_translations;
46
    
47
    /**
48
     * List of translations loaded within the MyArtJaub modules
49
     * @var Collection $maj_translations
50
     */
51
    private $maj_translations;
52
    
53
    /**
54
     * List of paths for source code
55
     * @var Collection $source_code_paths
56
     */
57
    private $source_code_paths;
58
    
59
    /**
60
     * Reference language code
61
     * @var string $language
62
     */
63
    private $language;
64
    
65
    /**
66
     * Constructor for TranslationAnalyzer
67
     *
68
     * @param SourceCodeService $sourcecode_service
69
     * @param Collection $code_paths
70
     * @param string $language
71
     */
72
    public function __construct(SourceCodeService $sourcecode_service, Collection $code_paths, string $language = null)
73
    {
74
        $this->sourcecode_service = $sourcecode_service;
75
        $this->language = $language ?? I18N::locale()->languageTag();
76
        $this->source_code_paths = $code_paths;
77
    }
78
    
79
    /******************************
80
     *  Data retrieval functions  *
81
     ******************************/
82
    
83
    /**
84
     * Compute the key for a given GetText Translation entry, dealing with context \x04 and plural \x00 cases.
85
     *
86
     * @param Translation $translation
87
     * @return string
88
     */
89
    private function getTranslationKey(Translation $translation): string
90
    {
91
        $key = $translation->getOriginal();
92
        if ($translation->getPlural() !== null && strlen($translation->getPlural()) > 0) {
93
            $key .= I18N::PLURAL . $translation->getPlural();
94
        }
95
        if ($translation->getContext() !== null && strlen($translation->getContext()) > 0) {
96
            $key = $translation->getContext() . I18N::CONTEXT . $key;
97
        }
98
        return $key;
99
    }
100
    
101
    /**
102
     * Returns the strings tagged for translation in the source code.
103
     * The returned structure is an associative Collection with :
104
     *      - key: MD5 hash of the Translation key
105
     *      - value: array [
106
     *              0 => GetText Translations Header (including domain)
107
     *              1 => GetTex Translation
108
     *          ]
109
     *
110
     * @return Collection
111
     */
112
    private function stringsToTranslate(): Collection
113
    {
114
        if ($this->strings_to_translate === null) {
115
            $strings_to_translate_list = $this->sourcecode_service->findStringsToTranslate($this->source_code_paths);
116
            
117
            $this->strings_to_translate = new Collection();
118
            foreach ($strings_to_translate_list as $translations) {
119
                foreach ($translations as $translation) {
120
                    $key = md5($this->getTranslationKey($translation));
121
                    $this->strings_to_translate->put($key, [$translations->getHeaders(), $translation]);
122
                }
123
            }
124
        }
125
        return $this->strings_to_translate;
126
    }
127
    
128
    /**
129
     * Returns the list of translations loaded through the standard I18N library.
130
     * The returned structure is an associative Collection with :
131
     *      - key: Original translation key
132
     *      - value: Translated string
133
     *
134
     * @return Collection
135
     */
136
    private function loadedTranslations(): Collection
137
    {
138
        if ($this->loaded_translations === null) {
139
            $I18N_class = new ReflectionClass('\\Fisharebest\\Webtrees\\I18N');
140
            $translator_property = $I18N_class->getProperty('translator');
141
            $translator_property->setAccessible(true);
142
            $wt_translator = $translator_property->getValue();
143
            
144
            $translator_class = new ReflectionClass(get_class($wt_translator));
145
            $translations_property = $translator_class->getProperty('translations');
146
            $translations_property->setAccessible(true);
147
            $this->loaded_translations = collect($translations_property->getValue($wt_translator));
148
        }
149
        return $this->loaded_translations;
150
    }
151
    
152
    /**
153
     * Returns the list of translations loaded in MyArtJaub modules.
154
     * The returned structure is an associative Collection with :
155
     *      - key: MD5 hash of the translation key
156
     *      - value: array [
157
     *              0 => Module name
158
     *              1 => Translation key
159
     *          ]
160
     *
161
     * @return Collection
162
     */
163
    private function loadedMyArtJaubTranslations(): Collection
164
    {
165
        if ($this->maj_translations === null) {
166
            $maj_translations_list = $this->sourcecode_service->listMyArtJaubTranslations($this->language);
167
            
168
            $this->maj_translations = new Collection();
169
            foreach ($maj_translations_list as $module => $maj_mod_translations) {
170
                foreach (array_keys($maj_mod_translations) as $maj_mod_translation) {
171
                    $this->maj_translations->put(md5((string) $maj_mod_translation), [$module, $maj_mod_translation]);
172
                }
173
            }
174
        }
175
        return $this->maj_translations;
176
    }
177
    
178
    /*************************
179
     *  Analyzer functions   *
180
     *************************/
181
    
182
    
183
    /**
184
     * Returns the translations missing through the standard I18N.
185
     * The returned array is composed of items with the structure:
186
     *      - array [
187
     *              0 => GetText Translations Header (including domain)
188
     *              1 => GetTex Translation
189
     *          ]
190
     *
191
     * @return array
192
     */
193
    public function missingTranslations(): array
194
    {
195
        $missing_translations = array();
196
        foreach ($this->stringsToTranslate() as $translation_info) {
197
            list(, $translation) = $translation_info;
198
            if (!$this->loadedTranslations()->has($this->getTranslationKey($translation))) {
199
                $missing_translations[] = $translation_info;
200
            }
201
        }
202
        
203
        return $missing_translations;
204
    }
205
    
206
    /**
207
     * Returns the translations defined in the MaJ modules, but not actually used in the code.
208
     * The returned array is composed of items with the structure:
209
     *      - array [
210
     *              0 => Module name
211
     *              1 => Translation key
212
     *          ]
213
     *
214
     * @return array
215
     */
216
    public function nonUsedMajTranslations(): array
217
    {
218
        $removed_translations = array();
219
        $strings_to_translate_list = $this->stringsToTranslate();
220
        foreach ($this->loadedMyArtJaubTranslations() as $msgid => $translation_info) {
221
            if (!$strings_to_translate_list->has($msgid)) {
222
                $removed_translations[] = $translation_info;
223
            }
224
        }
225
        return $removed_translations;
226
    }
227
    
228
    /**
229
     * Get some statistics about the translations data.
230
     * Returns an array with the statistics:
231
     *      nbTranslations : total number of translations
232
     *      nbTranslationsFound: total number of translations found in the code
233
     *      nbMajTranslations: total number of translations loaded in the MyArtJaub modules
234
     *
235
     * @return array
236
     */
237
    public function translationsStatictics(): array
238
    {
239
        return array(
240
            'nbTranslations' => $this->loadedTranslations()->count(),
241
            'nbTranslationsFound' => $this->stringsToTranslate()->count(),
242
            'nbMajTranslations' => $this->loadedMyArtJaubTranslations()->count()
243
        );
244
    }
245
}
246