From 0c8d81d3e1214c27c46b755d533a5dbaee521003 Mon Sep 17 00:00:00 2001 From: John Koster Date: Sun, 9 Jul 2023 12:47:58 -0500 Subject: [PATCH] =?UTF-8?q?Hello,=20universe.=20=F0=9F=A5=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/FUNDING.yml | 1 + .gitignore | 3 + LICENSE.md | 21 ++++ README.md | 33 ++++++ composer.json | 23 +++++ src/AntlersExporter.php | 63 ++++++++++++ src/Extractor/AntlersFileFinder.php | 19 ++++ src/Extractor/AntlersParser.php | 102 +++++++++++++++++++ src/Extractor/TranslationStringExtractor.php | 36 +++++++ src/ServiceProvider.php | 13 +++ 10 files changed, 314 insertions(+) create mode 100644 .github/FUNDING.yml create mode 100644 .gitignore create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 composer.json create mode 100644 src/AntlersExporter.php create mode 100644 src/Extractor/AntlersFileFinder.php create mode 100644 src/Extractor/AntlersParser.php create mode 100644 src/Extractor/TranslationStringExtractor.php create mode 100644 src/ServiceProvider.php diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..728df2a --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: JohnathonKoster diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0199b2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +vendor +mix-manifest.json diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..1e51b5e --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1d31d3b --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# Antlers Translation Strings Exporter + +This package extends the capabilities of the [kkomelin/laravel-translatable-string-exporter](https://github.com/kkomelin/laravel-translatable-string-exporter) package, allowing you to export translation strings for both Blade and Antlers templates. + +## How to Install + +This package can be installed by running the following command from the root of your project: + +``` bash +composer require stillat/antlers-translation-strings-exporter --dev +``` + +## How to Use + +Since this package adds additional functionality to [kkomelin/laravel-translatable-string-exporter](https://github.com/kkomelin/laravel-translatable-string-exporter), the usage steps (and configuration) guides are the same. + +At a high level, you can export translation strings by running the following Artisan command from the root of your project: + +``` +php artisan translatable:export +``` + +For full usage and configuration steps, please refer to [kkomelin/laravel-translatable-string-exporter](https://github.com/kkomelin/laravel-translatable-string-exporter). + +## Credits + +This package simply adds additional functionality to the following package, which does all of the hard work: + +https://github.com/kkomelin/laravel-translatable-string-exporter + +## License + +This package is free software released under the MIT License. diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..92a233a --- /dev/null +++ b/composer.json @@ -0,0 +1,23 @@ +{ + "name": "stillat/antlers-translation-strings-exporter", + "autoload": { + "psr-4": { + "Stillat\\AntlersTranslationStringsExporter\\": "src" + } + }, + "extra": { + "statamic": { + "name": "Antlers Translation Strings Exporter", + "description": "Provides utilities for exporting translation strings from Antlers and Blade template files." + }, + "laravel": { + "providers": [ + "Stillat\\AntlersTranslationStringsExporter\\ServiceProvider" + ] + } + }, + "require": { + "kkomelin/laravel-translatable-string-exporter": "^1.21", + "statamic/cms": "^4" + } +} diff --git a/src/AntlersExporter.php b/src/AntlersExporter.php new file mode 100644 index 0000000..3536b80 --- /dev/null +++ b/src/AntlersExporter.php @@ -0,0 +1,63 @@ +bladeExtractor = new StringExtractor(); + $this->antlersExtractor = new TranslationStringExtractor(); + } + + public function export(string $language) + { + $bladeStrings = $this->bladeExtractor->extract(); + $antlersStrings = $this->antlersExtractor->extract(); + $new_strings = array_merge($bladeStrings, $antlersStrings); + + // Do all the things the package normally does. + + $language_path = IO::languageFilePath($language); + + // Read existing translation file for the chosen language. + $existing_strings = IO::readTranslationFile($language_path); + + // Get the persistent strings. + $persistent_strings_path = + IO::languageFilePath(self::PERSISTENT_STRINGS_FILENAME_WO_EXT); + $persistent_strings = IO::readTranslationFile($persistent_strings_path); + + // Add persistent strings to the export if enabled. + $new_strings = $this->addPersistentStringsIfEnabled($new_strings, $persistent_strings); + + // Merge old an new translations preserving existing translations and persistent strings. + $resulting_strings = $this->mergeStrings($new_strings, $existing_strings, $persistent_strings); + + // Exclude translation keys if enabled through the config. + $resulting_strings = $this->excludeTranslationKeysIfEnabled($resulting_strings, $language); + + // Wisely sort the translations if enabled through the config. + $sorted_strings = $this->advancedSortIfEnabled($resulting_strings); + + // Prepare JSON string and dump it to the translation file. + $content = JSON::jsonEncode($sorted_strings); + IO::write($content, $language_path); + } +} diff --git a/src/Extractor/AntlersFileFinder.php b/src/Extractor/AntlersFileFinder.php new file mode 100644 index 0000000..7841470 --- /dev/null +++ b/src/Extractor/AntlersFileFinder.php @@ -0,0 +1,19 @@ +patterns = []; + + foreach (Engine::EXTENSIONS as $extension) { + $this->patterns[] = '*.'.$extension; + } + } +} diff --git a/src/Extractor/AntlersParser.php b/src/Extractor/AntlersParser.php new file mode 100644 index 0000000..47b330c --- /dev/null +++ b/src/Extractor/AntlersParser.php @@ -0,0 +1,102 @@ +documentParser = new DocumentParser(); + + if (NodeTypeAnalyzer::$environmentDetails == null) { + $envDetails = new EnvironmentDetails(); + + $envDetails->setTagNames(app()->make('statamic.tags')->keys()->all()); + $envDetails->setModifierNames(app()->make('statamic.modifiers')->keys()->all()); + + NodeTypeAnalyzer::$environmentDetails = $envDetails; + } + } + + public function parse($file) + { + $this->documentParser->parse(file_get_contents($file)); + + $result = collect($this->documentParser->getNodes())->where(function ($node) { + return $node instanceof AntlersNode; + }); + + $keys = []; + + /** @var AntlersNode $node */ + foreach ($result as $node) { + if (! empty($node->processedInterpolationRegions)) { + $keys = array_merge($keys, $this->locateInterpolatedTranslationKeys($node)); + } + + if (in_array($node->name->name, $this->antlersTranslationTags)) { + $keyParam = $this->getParameterByName($node, 'key'); + + if ($keyParam == null || $keyParam->isModifierParameter || $keyParam->isVariableReference) { + $keys[] = $node->name->methodPart; + + continue; + } + + $keys[] = $keyParam->value; + } + } + + return $keys; + } + + private function locateInterpolatedTranslationKeys(AntlersNode $rootNode, $foundKeys = []) + { + foreach ($rootNode->processedInterpolationRegions as $interpolationRegion) { + /** @var AntlersNode $node */ + $node = $interpolationRegion[0]; + + if (! empty($node->processedInterpolationRegions)) { + $foundKeys = array_merge($foundKeys, $this->locateInterpolatedTranslationKeys($node, $foundKeys)); + } + + if (in_array($node->name->name, $this->antlersTranslationTags)) { + $keyParam = $this->getParameterByName($node, 'key'); + + if ($keyParam == null || $keyParam->isModifierParameter || $keyParam->isVariableReference) { + $foundKeys[] = $node->name->methodPart; + + continue; + } + + $foundKeys[] = $keyParam->value; + } + } + + return $foundKeys; + } + + private function getParameterByName(AntlersNode $node, $paramName) + { + if ($node->hasParameters) { + foreach ($node->parameters as $parameter) { + if ($parameter->name == $paramName) { + return $parameter; + } + } + } + + return null; + } +} diff --git a/src/Extractor/TranslationStringExtractor.php b/src/Extractor/TranslationStringExtractor.php new file mode 100644 index 0000000..4b3e823 --- /dev/null +++ b/src/Extractor/TranslationStringExtractor.php @@ -0,0 +1,36 @@ +finder = new AntlersFileFinder(); + $this->parser = new AntlersParser(); + } + + public function extract() + { + $strings = []; + + $files = $this->finder->find(); + foreach ($files as $file) { + $strings = array_merge($strings, $this->parser->parse($file)); + } + + return $this->formatArray($strings); + } +} diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php new file mode 100644 index 0000000..ced0046 --- /dev/null +++ b/src/ServiceProvider.php @@ -0,0 +1,13 @@ +app->singleton('translatable-string-exporter-exporter', function ($app) { + return $app->make(AntlersExporter::class); + }); + } +}