HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
File: D:/HostingSpaces/SBogers10/gggg.komma.nl/vendor/komma/kms/tests/Unit/TranslationFilesTest.php
<?php

namespace Tests\Unit;

use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Str;
use Illuminate\Translation\FileLoader;
use Komma\KMS\Core\Translator;
use Tests\TestCase;

/**
 * Class TranslationFiles
 *
 * Tests translation files for the other languages contains the same keys
 *
 * @package Tests\Unit
 */
class TranslationFilesTest extends TestCase
{

    private Translator $trans;
    private Filesystem $fileSystem;

    private array $translationKeys = [];

    private array $languageFunctionPatterns = [
        "/__\('(.*?)'[),]/",
        "/trans\('(.*?)'[),]/",
        "/lang\('(.*?)'[),]/"
    ];

    private array $skippableStartKeys = [
        'routes',
        'KMS::prevent_navigation', // Skipped because of array usage
        'KMS::validation.min',
        'KMS::attributes/components.types',
        'KMS::calendar',
        'KMS::global',
        'KMS::outofdate',
        'attributes/components.types', // Skipped because of generic usage
        'passwords.',
        'roles.',
        'transfer.',
        'validation.',
        'sidebarMenu.',
        'auth.validation.throttled',
    ];

    private array $skippableEndKeys = [
        '.section.new',
        '.section.title',
    ];

    public function __construct($name = null, array $data = [], $dataName = '')
    {
        parent::__construct($name, $data, $dataName);
        $this->fileSystem = new Filesystem();

        $loader = new FileLoader($this->fileSystem, realpath('./resources/lang'));
        $this->trans = new Translator($loader, 'nl');
    }

    /**
     * Setup the test environment.
     *
     * @throws \Exception
     */
    protected function setUp(): void
    {
        parent::setUp();

        $localLanguageFolder = realpath('./resources/lang/nl');
        $this->assertFileExists($localLanguageFolder);

        $fileSystem = new Filesystem();
        $languageFiles = $fileSystem->allFiles($localLanguageFolder);

        foreach ($languageFiles as $languageFile) {

            $fileName = $languageFile->getRelativePathname();
            $translationFileKey = Str::replaceLast('.' . $languageFile->getExtension(), '', $fileName);

            $this->getKeysFromArray($translationFileKey);
        }
    }

    /**
     * Recurring function for grabbing the translations out of an array till it's a string
     *
     * @param  string  $translationParentKey
     * @throws \Exception
     */
    private function getKeysFromArray(string $translationParentKey)
    {
        foreach ($this->trans->get($translationParentKey, [], 'nl') as $translationKey => $translationValue) {
            if(is_array($translationValue)) {
                $this->getKeysFromArray($translationParentKey . '.' . $translationKey);
                continue;
            }

            $this->assertIsString($translationValue);
            $this->translationKeys[] = $translationParentKey . '.' . $translationKey;
        }
    }

    /**
     * @group TranslationFilesTest
     * @test
     * @throws \Exception
     */
    public function checkUsageOfTranslations()
    {

        $fileList = [];
        $this->addFilesContainLanguageFunctionsOutOfDirectory($fileList, realpath('./src'));
        $this->addFilesContainLanguageFunctionsOutOfDirectory($fileList, realpath('./resources'));

        // Create a list of all the used translation keys
        $usedTranslationKeys = $this->createUsedTranslationKeysList($fileList);

        // Copy the translations keys so we track if they are used
        $unusedTranslationsKeys = $this->translationKeys;
        $resolvedTranslationKeys = [];

        // Loop through the used translations and validate that they are defined.
        foreach ($usedTranslationKeys as $usedTranslationKey ) {

//            echo $key . ': ' . $usedTranslationKey.PHP_EOL;

            // Skip translations keys starting with, contains variables or already resolved keys
            if(in_array($usedTranslationKey, $this->skippableStartKeys) || Str::contains($usedTranslationKey, ['.$', '. $', '($']) || in_array($usedTranslationKey, $resolvedTranslationKeys)) continue;

            // Store original found translation key for the resolvedTranslationKeys
            // Note: We kept append in the bottom to maintain the same flow as in the boiler plates
            $originalTranslationKey = $usedTranslationKey;

            // Remove the KMS:: prefix from the key
            if(Str::startsWith($usedTranslationKey, 'KMS::')) $usedTranslationKey = Str::replaceFirst('KMS::', '', $usedTranslationKey);

            // Validate that the translation is found
            $this->assertContains($usedTranslationKey, $this->translationKeys, 'Found translation "' . $usedTranslationKey .'" is not defined in the languages files. Or possible with "KMS::"');

            // Remove this translation key from the unusedTranslationsKeys because it is used
            unset($unusedTranslationsKeys[array_search($usedTranslationKey, $unusedTranslationsKeys)]);

            // Append the resolve translation, in case it is used multiple times
            $resolvedTranslationKeys[] = $originalTranslationKey;
        }

        $unusedTranslationErrors = collect();

        // Loop through the remaining defined translations keys
        foreach ($unusedTranslationsKeys as $unusedTranslationsKey) {

            // Dont show through errors for the skippable start or end keys
            if(Str::startsWith($unusedTranslationsKey, $this->skippableStartKeys)) continue;
            if(Str::startsWith('KMS::' . $unusedTranslationsKey, $this->skippableStartKeys)) continue;
            if(Str::endsWith($unusedTranslationsKey, $this->skippableEndKeys)) continue;

            $unusedTranslationErrors->push((object) [
                'message' => 'The translation key "' . $unusedTranslationsKey .'" is not used within the application. If this is not correct and if used in a undetectable way, then exclude it from this test.',
            ]);
        }

        if($unusedTranslationErrors->isNotEmpty()) {
            echo PHP_EOL;
            foreach ($unusedTranslationErrors as $unusedTranslationError) echo $unusedTranslationError->message . PHP_EOL;
            echo PHP_EOL;
            $this->assertEmpty($unusedTranslationErrors->count(), 'Not defined translations are used in the application or you should extends skippable keys.');
        }
    }

    /**
     * Map all the files out of the index into the file list
     *
     * @param  array  $fileList
     * @param  string  $directoryPath
     */
    private function addFilesContainLanguageFunctionsOutOfDirectory(array &$fileList, string $directoryPath)
    {

//        echo 'Directory indexed: "' . $directoryPath . '"' . PHP_EOL;
        $directory = new \DirectoryIterator($directoryPath);

        foreach ($directory as $file) {

            // Exclude itself
            if($file->isDot()) continue;

            // If directory then search within that directory
            if($file->isDir()) {
                $this->addFilesContainLanguageFunctionsOutOfDirectory($fileList, $file->getPathname());
                continue;
            }

            $fileContent = file_get_contents($file->getPathname());
            if(!Str::contains($fileContent, ['__(', 'trans(', '@lang('])) continue;

//            echo 'File added that contains translation function: "' . $file->getPathname() . '"' . PHP_EOL ;
            $fileList[] = $file->getPathname();
        }
    }

    /**
     * Generate a list of used translations
     *
     * @param  array  $fileList
     * @return array
     */
    private function createUsedTranslationKeysList(array $fileList): array
    {
        $usedTranslationKeys = [];

        foreach ($fileList as $localizedFile)
        {
            $fileContent = file_get_contents($localizedFile);

            foreach ($this->languageFunctionPatterns as $pattern) {
                preg_match_all($pattern, $fileContent, $matches);
                $usedTranslationKeys = array_merge($usedTranslationKeys, $matches[1]);
            }
        }

        return $usedTranslationKeys;
    }

}