diff --git a/Makefile b/Makefile index 5b9871614..8ab9bee70 100644 --- a/Makefile +++ b/Makefile @@ -114,6 +114,17 @@ endif @$(PHP) ./cli/manipulate.translation.php --action add --key $(key) --value "$(value)" @echo Key added. +.PHONY: i18n-move-key +i18n-move-key: ## Move an existing key into a new location +ifndef key + $(error To move a key, you need to provide one in the "key" variable) +endif +ifndef new-key + $(error To specify a location to move the key to, you need to provide it in the "new-key" variable) +endif + @$(PHP) ./cli/manipulate.translation.php --action move --key $(key) --new-key "$(new-key)" + @echo Key moved. + .PHONY: i18n-add-language i18n-add-language: ## Add a new supported language ifndef lang diff --git a/cli/i18n/I18nData.php b/cli/i18n/I18nData.php index 981c19e62..7d2e39a95 100644 --- a/cli/i18n/I18nData.php +++ b/cli/i18n/I18nData.php @@ -98,7 +98,7 @@ class I18nData { */ public function addLanguage(string $language, ?string $reference = null): void { if (array_key_exists($language, $this->data)) { - throw new Exception('The selected language already exist.'); + throw new Exception('The selected language already exists.'); } if (!is_string($reference) || !array_key_exists($reference, $this->data)) { $reference = static::REFERENCE_LANGUAGE; @@ -221,7 +221,7 @@ class I18nData { } if ($this->isKnown($key)) { - throw new Exception('The selected key already exist.'); + throw new Exception('The selected key already exists.'); } $parentKey = $this->getParentKey($key); @@ -248,6 +248,29 @@ class I18nData { } } + /** + * Move an existing key into a new location + * @throws Exception + */ + public function moveKey(string $key, string $newKey): void { + if (!$this->isKnown($key) && !$this->isKnown($this->getEmptySibling($key))) { + throw new Exception('The selected key does not exist'); + } + if ($this->isKnown($newKey)) { + throw new Exception('Cannot move key to a location that already exists.'); + } + + $keyPrefix = $this->isParent($key) ? $key . '.' : $key; + foreach ($this->getAvailableLanguages() as $language) { + foreach ($this->data[$language][$this->getFilenamePrefix($key)] as $k => $v) { + if (str_starts_with($k, $keyPrefix)) { + $this->data[$language][$this->getFilenamePrefix($newKey)][str_replace($key, $newKey, $k)] = $v; + unset($this->data[$language][$this->getFilenamePrefix($key)][$k]); + } + } + } + } + /** * Add a value for a key for the selected language. * diff --git a/cli/manipulate.translation.php b/cli/manipulate.translation.php index 1c4a30ca6..59f28ec78 100755 --- a/cli/manipulate.translation.php +++ b/cli/manipulate.translation.php @@ -9,6 +9,7 @@ require_once dirname(__DIR__) . '/constants.php'; $cliOptions = new class extends CliOptionsParser { public string $action; public string $key; + public string $newKey; public string $value; public string $language; public string $originLanguage; @@ -18,6 +19,7 @@ $cliOptions = new class extends CliOptionsParser { public function __construct() { $this->addRequiredOption('action', (new CliOption('action', 'a'))); $this->addOption('key', (new CliOption('key', 'k'))); + $this->addOption('newKey', (new CliOption('new-key', 'n'))); $this->addOption('value', (new CliOption('value', 'v'))); $this->addOption('language', (new CliOption('language', 'l'))); $this->addOption('originLanguage', (new CliOption('origin-language', 'o'))); @@ -56,6 +58,14 @@ switch ($cliOptions->action) { exit; } break; + case 'move': + if (isset($cliOptions->key) && isset($cliOptions->newKey)) { + $i18nData->moveKey($cliOptions->key, $cliOptions->newKey); + } else { + error('You need to specify the key to move and its new location.'); + exit; + } + break; case 'delete': if (isset($cliOptions->key)) { $i18nData->removeKey($cliOptions->key); @@ -131,7 +141,7 @@ DESCRIPTION Manipulate translation files. -a, --action=ACTION - select the action to perform. Available actions are add, delete, + select the action to perform. Available actions are add, move, delete, exist, format, ignore, and ignore_unmodified. This option is mandatory. -k, --key=KEY select the key to work on. -v, --value=VAL select the value to set. @@ -176,6 +186,9 @@ Example 10: check if a key exist. Example 11: add a new file to all languages php $file -a add -k my_file.php -HELP; + +Example 12:\tmove an existing key into a new location + php $file -a move -k my_key -n new_location +HELP, PHP_EOL; exit(); } diff --git a/docs/en/internationalization.md b/docs/en/internationalization.md index a2accd2d7..c40285c4a 100644 --- a/docs/en/internationalization.md +++ b/docs/en/internationalization.md @@ -83,7 +83,7 @@ make i18n-ignore-key lang=fr key=index.about.version This command adds an IGNORE comment on the translation so the key can be considered as translated. -## Add/remove/update a key +## Add/remove/update/rename a key If you’re developing a new part of the application, you might want to declare a new translation key. Your first impulse would be to add the key to each file manually: don’t do that, it’s very painful. We provide another command: @@ -107,6 +107,11 @@ make i18n-update-key key=the.key.to.change value='The new string in English' The key will simply be removed and added back with the new value. +If you want to move/rename a key, you can use: +```sh +make i18n-move-key key=the.key.to.move new-key=new.location.of.the.key +``` + ## How to access a translation programmatically To access these translations, you must use the `_t()` function (which is a shortcut for `Minz_Translate::t()`). Code example: diff --git a/tests/cli/i18n/I18nDataTest.php b/tests/cli/i18n/I18nDataTest.php index 02ad7ea9a..5533ce166 100644 --- a/tests/cli/i18n/I18nDataTest.php +++ b/tests/cli/i18n/I18nDataTest.php @@ -34,6 +34,16 @@ final class I18nDataTest extends \PHPUnit\Framework\TestCase { ]; } + public function testMoveKey(): void { + $data = new I18nData($this->referenceData); + $value = $data->getData()['en']['file2.php']['file2.l1.l2.k2']; + self::assertTrue($data->isKnown('file2.l1.l2.k2')); + self::assertFalse($data->isKnown('file2.l1.nkl2')); + $data->moveKey('file2.l1.l2.k2', 'file2.l1.nkl2'); + self::assertFalse($data->isKnown('file2.l1.l2.k2')); + self::assertTrue($data->isKnown('file2.l1.nkl2')); + } + public function testConstructWhenReferenceOnly(): void { $data = new I18nData($this->referenceData); self::assertSame($this->referenceData, $data->getData()); @@ -302,7 +312,7 @@ final class I18nDataTest extends \PHPUnit\Framework\TestCase { public function testAddLanguageWhenLanguageExists(): void { $this->expectException(\Exception::class); - $this->expectExceptionMessage('The selected language already exist.'); + $this->expectExceptionMessage('The selected language already exists.'); $data = new I18nData($this->referenceData); $data->addLanguage('en'); } @@ -430,7 +440,7 @@ final class I18nDataTest extends \PHPUnit\Framework\TestCase { public function testAddKeyWhenKeyExists(): void { $this->expectException(\Exception::class); - $this->expectExceptionMessage('The selected key already exist.'); + $this->expectExceptionMessage('The selected key already exists.'); $data = new I18nData($this->referenceData); $data->addKey('file2.l1.l2.k1', 'value'); }