Lines 91.48% 43 / 47
Methods 40.00% 2 / 5
Classes 0.00% 0 / 1
Covered by tests of size
Name Lines Methods CRAP
 getStatistics 93.10% 27 / 29 0.00% 0 / 1 6.01
 getLanguageStatistics 100.00% 2 / 2 100.00% 1 / 1 1
 getMissingKeys 100.00% 5 / 5 100.00% 1 / 1 2
 getTranslationKeys 85.71% 6 / 7 0.00% 0 / 1 2.01
 extractLanguageCode 75.00% 3 / 4 0.00% 0 / 1 2.06
22readonly class TranslationStatistics
23{
24    private const string REFERENCE_LANGUAGE = 'en';
25
26    /**
27     * Returns statistics about all available translations.
28     *
29     * @return array<string, array<string, mixed>>
30     */
31    public function getStatistics(): array
32    {
33        $statistics = [];
34        $translationsDir = PMF_TRANSLATION_DIR;
35        $referenceKeys = $this->getTranslationKeys(self::REFERENCE_LANGUAGE);
36        $totalReferenceKeys = count($referenceKeys);
37
38        $languageFiles = glob($translationsDir . '/language_*.php');
39
40        if ($languageFiles === false) {
41            return $statistics;
42        }
43
44        foreach ($languageFiles as $languageFile) {
45            $language = $this->extractLanguageCode($languageFile);
46
47            if ($language === null) {
48                continue;
49            }
50
51            $translationKeys = $this->getTranslationKeys($language);
52            $totalKeys = count($translationKeys);
53            $missingKeys = array_diff($referenceKeys, $translationKeys);
54            $missingCount = count($missingKeys);
55            $translatedCount = $totalKeys;
56
57            if ($language !== self::REFERENCE_LANGUAGE) {
58                $translatedCount = $totalReferenceKeys - $missingCount;
59            }
60
61            $statistics[$language] = [
62                'language_code' => $language,
63                'total_keys' => $totalKeys,
64                'translated_keys' => $translatedCount,
65                'missing_keys' => $missingCount,
66                'completion_percentage' => $totalReferenceKeys > 0
67                    ? round(($translatedCount / $totalReferenceKeys) * 100, precision: 2)
68                    : 0.0,
69            ];
70        }
71
72        uasort($statistics, static fn($a, $b): int => $b['completion_percentage'] <=> $a['completion_percentage']);
73
74        return $statistics;
75    }
76
77    /**
78     * Returns detailed statistics for a specific language.
79     *
80     * @return array<string, mixed>|null
81     */
82    public function getLanguageStatistics(string $language): ?array
83    {
84        $statistics = $this->getStatistics();
85
86        return $statistics[$language] ?? null;
87    }
88
89    /**
90     * Returns the list of missing translation keys for a specific language.
91     *
92     * @return array<int, string>
93     */
94    public function getMissingKeys(string $language): array
95    {
96        if ($language === self::REFERENCE_LANGUAGE) {
97            return [];
98        }
99
100        $referenceKeys = $this->getTranslationKeys(self::REFERENCE_LANGUAGE);
101        $languageKeys = $this->getTranslationKeys($language);
102
103        return array_values(array_diff($referenceKeys, $languageKeys));
104    }
105
106    /**
107     * Returns all translation keys for a specific language.
108     *
109     * @return array<int, string>
110     */
111    private function getTranslationKeys(string $language): array
112    {
113        $translationsDir = PMF_TRANSLATION_DIR;
114        $file = $translationsDir . '/language_' . $language . '.php';
115
116        if (!file_exists($file)) {
117            return [];
118        }
119
120        $PMF_LANG = [];
121        include $file;
122
123        return array_map(strval(...), array_keys($PMF_LANG));
124    }
125
126    /**
127     * Extracts the language code from a translation file path.
128     */
129    private function extractLanguageCode(string $filePath): ?string
130    {
131        $matches = [];
132        if (preg_match(pattern: '/language_([a-z_]+)\.php$/', subject: $filePath, matches: $matches)) {
133            return $matches[1];
134        }
135
136        return null;
137    }
138}