├── LICENSE.txt ├── README.md ├── composer.json └── src ├── Console └── Command │ ├── I18nCsvDiffCommand.php │ └── I18nCsvTranslateCommand.php ├── etc ├── acl.xml ├── adminhtml │ └── system.xml ├── config.xml ├── di.xml └── module.xml └── registration.php /LICENSE.txt: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, Hyvä Themes B.V. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hyvä Themes - Magento translation CSV comparison command 2 | 3 | [![Hyvä Themes](https://gitlab.hyva.io/uploads/-/system/group/avatar/617/hyva-logo-360.png?width=160)](https://hyva.io/) 4 | 5 | ## hyva-themes/magento2-i18n-csv-diff 6 | 7 | ![Supported Magento Versions][ico-compatibility] 8 | 9 | This module adds the `bin/magento i18n:diff-csv` and `i18n:translate-csv` commands. 10 | 11 | ### i18n:diff-csv 12 | 13 | The command takes two CSV files as arguments. 14 | It displays all translations that are present in the first CSV file but not in the second, based on the first colum in the files. 15 | 16 | ### i18n:translate 17 | 18 | The command takes a target language 2-letter ISO code as the argument. 19 | It reads a Magento localization CSV dictionary from stdin, translates it using the DeepL API, and writes it to stdout. 20 | Be sure to check the automatic translations afterwards! 21 | 22 | ## Usage Examples: 23 | 24 | * Collect all Magento and Hyvä strings: 25 | ```sh 26 | bin/magento i18n:collect-phrases vendor/magento/ > magento-strings.csv 27 | bin/magento i18n:collect-phrases vendor/hyva-themes/magento2-default-theme/ > hyva-strings.csv 28 | bin/magento i18n:collect-phrases vendor/hyva-themes/magento2-theme-module/ >> hyva-strings.csv 29 | ``` 30 | * Find all Hyvä specific translations that are not part of native Magento: 31 | ```sh 32 | bin/magento i18n:diff-csv hyva-strings.csv magento-strings.csv 33 | ``` 34 | 35 | * Find all translations that are missing in a Hyvä translation file: 36 | ```sh 37 | bin/magento i18n:diff-csv hyva-strings.csv i18n/de_DE.csv 38 | ``` 39 | * Find all translations that are in a translation file but are not used in Hyvä: 40 | ```sh 41 | bin/magento i18n:diff-csv i18n/de_DE.csv hyva-strings.csv 42 | ``` 43 | * Add new translations to an existing dictionary file. 44 | ```sh 45 | bin/magento i18n:diff-csv hyva-strings.csv i18n/de_DE.csv | bin/magento i18n:translate-csv DE >> i18n/de_DE.csv 46 | ``` 47 | 48 | ## Installation 49 | 50 | 1. Install via composer 51 | ``` 52 | composer require hyva-themes/magento2-i18n-csv-diff 53 | ``` 54 | 2. Enable module 55 | ``` 56 | bin/magento setup:upgrade 57 | ``` 58 | 59 | ## Configuration 60 | 61 | To use the Deep`i18n:translate-csv` command, configure your [API key](https://www.deepl.com/pro-api?cta=header-pro-api/) in the system configuration at *Hyva Themes > Localization CLI > Translation > DeepL API Key*. 62 | 63 | ## License 64 | 65 | The BSD-3-Clause License. Please see [License File](LICENSE.txt) for more information. 66 | 67 | [ico-compatibility]: https://img.shields.io/badge/magento-%202.3%20|%202.4-brightgreen.svg?logo=magento&longCache=true&style=flat-square 68 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hyva-themes/magento2-i18n-csv-diff", 3 | "description": "Magento command to display all translations that are present in the first CSV file but not in the second.", 4 | "require": { 5 | "php": ">=7.4.0" 6 | }, 7 | "type": "magento2-module", 8 | "license": [ 9 | "BSD-3-Clause" 10 | ], 11 | "autoload": { 12 | "files": [ 13 | "src/registration.php" 14 | ], 15 | "psr-4": { 16 | "Hyva\\I18nCsvDiff\\": "src/" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Console/Command/I18nCsvDiffCommand.php: -------------------------------------------------------------------------------- 1 | ignoreStrings = $ignoreStrings; 36 | parent::__construct(); 37 | } 38 | 39 | protected function configure() 40 | { 41 | $this->setName('i18n:diff-csv'); 42 | $this->setDescription('Display all translations that are present in the first CSV file but not in the second.'); 43 | $this->addArgument('file-a', InputArgument::REQUIRED, 'First i18n CSV file'); 44 | $this->addArgument('file-b', InputArgument::REQUIRED, 'Second i18n CSV file'); 45 | } 46 | 47 | protected function execute(InputInterface $input, OutputInterface $output) 48 | { 49 | setlocale(LC_ALL, "en_US.UTF-8"); 50 | $fileA = $input->getArgument('file-a'); 51 | $fileB = $input->getArgument('file-b'); 52 | 53 | $recordsA = $this->readFirstCol($fileA); 54 | sort($recordsA); 55 | 56 | $recordsB = $this->readFirstCol($fileB); 57 | sort($recordsB); 58 | 59 | $diff = diff($recordsA, $recordsB); 60 | 61 | foreach ($diff as $r) { 62 | // display the record twice so the output can be copy&pasted into a translation CSV file 63 | fputcsv(STDOUT, [$r, $r]); 64 | } 65 | return 0; 66 | } 67 | 68 | private function readFirstCol(string $file): array 69 | { 70 | $fileHandle = fopen($file, 'r'); 71 | $records = []; 72 | while (!feof($fileHandle)) { 73 | $row = fgetcsv($fileHandle); 74 | 75 | // ignore empty lines and known invalid record 76 | if ($row && $row[0] !== null && false === search($row[0], $this->ignoreStrings)) { 77 | $records[] = $row[0]; 78 | } 79 | } 80 | return $records; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Console/Command/I18nCsvTranslateCommand.php: -------------------------------------------------------------------------------- 1 | httpClientFactory = $httpClientFactory; 41 | $this->scopeConfig = $scopeConfig; 42 | parent::__construct(); 43 | } 44 | 45 | protected function configure() 46 | { 47 | $this->setName('i18n:translate-csv'); 48 | $this->setDescription( 49 | 'Translate a Magento2 localization CSV to a language using DeepL. ' . 50 | 'Be sure to check the translation afterwards!' 51 | ); 52 | $this->addOption('in', null, InputOption::VALUE_OPTIONAL, 'Input CSV', 'stdin'); 53 | $this->addOption('out', null, InputOption::VALUE_OPTIONAL, 'Input CSV', 'stdout'); 54 | $this->addArgument('target-lang', InputArgument::REQUIRED, 'The target language code (for example NL or DE)'); 55 | } 56 | 57 | protected function execute(InputInterface $input, OutputInterface $output) 58 | { 59 | $inFileHandle = $input->getOption('in') === 'stdin' 60 | ? STDIN 61 | : fopen($input->getOption('in'), 'r'); 62 | $outFileHandle = $input->getOption('out') === 'stdout' 63 | ? STDOUT 64 | : fopen($input->getOption('out'), 'r'); 65 | 66 | $lang = strtoupper($input->getArgument('target-lang')); 67 | 68 | while (!feof($inFileHandle)) { 69 | $row = fgetcsv($inFileHandle); 70 | 71 | // ignore empty lines and known invalid record 72 | if (!$row || $row[0] === null) { 73 | continue; 74 | } 75 | fputcsv($outFileHandle, [$row[0], $this->translate($lang, $row[0])]); 76 | } 77 | 78 | return 0; 79 | } 80 | 81 | private function translate(string $lang, string $phrase): string 82 | { 83 | $key = $this->scopeConfig->getValue('hyva_themes_i18n/translation/deepl_api_key'); 84 | 85 | $client = $this->httpClientFactory->create(); 86 | $client->post('https://api-free.deepl.com/v2/translate', [ 87 | 'tag_handling' => 'xml', 88 | 'split_sentences' => 'nonewlines', 89 | 'source_lang' => 'EN', 90 | 'auth_key' => $key, 91 | 'text' => $phrase, 92 | 'target_lang' => $lang, 93 | ]); 94 | $result = json_decode($client->getBody(), true); 95 | return $result['translations'][0]['text'] ?? $phrase; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/etc/acl.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/etc/adminhtml/system.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 12 | 13 | 14 | 15 | 16 |
18 | 19 | hyva_themes 20 | Hyvy_I18nCsvDiff::config_hyva_i18n 21 | 23 | 24 | 26 | 27 | Magento\Config\Model\Config\Backend\Encrypted 28 | 29 | 30 |
31 |
32 |
33 | -------------------------------------------------------------------------------- /src/etc/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/etc/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 12 | 13 | 14 | 15 | Hyva\I18nCsvDiff\Console\Command\I18nCsvDiffCommand 16 | Hyva\I18nCsvDiff\Console\Command\I18nCsvTranslateCommand 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/registration.php: -------------------------------------------------------------------------------- 1 |