Procházet zdrojové kódy

Add new translate action: `move`, `make i18n-move-key` (#8214)

So that renaming something like `conf.shortcut.toggle_sidebar` to `conf.shortcut.toggle_aside` can be done easily even after already having added `conf.shortcut.toggle_sidebar` and translated it in multiple languages.

Example of usage:
```console
./cli/manipulate.translation.php -a move -k conf.shortcut.toggle_sidebar -n conf.shortcut.toggle_aside
```
```console
make i18n-move-key key="conf.shortcut.toggle_sidebar" new-key="conf.shortcut.toggle_aside"
```

The key will be moved and all values/states will be kept.
Inverle před 4 měsíci
rodič
revize
b9abe70690

+ 11 - 0
Makefile

@@ -114,6 +114,17 @@ endif
 	@$(PHP) ./cli/manipulate.translation.php --action add --key $(key) --value "$(value)"
 	@$(PHP) ./cli/manipulate.translation.php --action add --key $(key) --value "$(value)"
 	@echo Key added.
 	@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
 .PHONY: i18n-add-language
 i18n-add-language: ## Add a new supported language
 i18n-add-language: ## Add a new supported language
 ifndef lang
 ifndef lang

+ 25 - 2
cli/i18n/I18nData.php

@@ -98,7 +98,7 @@ class I18nData {
 	 */
 	 */
 	public function addLanguage(string $language, ?string $reference = null): void {
 	public function addLanguage(string $language, ?string $reference = null): void {
 		if (array_key_exists($language, $this->data)) {
 		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)) {
 		if (!is_string($reference) || !array_key_exists($reference, $this->data)) {
 			$reference = static::REFERENCE_LANGUAGE;
 			$reference = static::REFERENCE_LANGUAGE;
@@ -221,7 +221,7 @@ class I18nData {
 		}
 		}
 
 
 		if ($this->isKnown($key)) {
 		if ($this->isKnown($key)) {
-			throw new Exception('The selected key already exist.');
+			throw new Exception('The selected key already exists.');
 		}
 		}
 
 
 		$parentKey = $this->getParentKey($key);
 		$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.
 	 * Add a value for a key for the selected language.
 	 *
 	 *

+ 15 - 2
cli/manipulate.translation.php

@@ -9,6 +9,7 @@ require_once dirname(__DIR__) . '/constants.php';
 $cliOptions = new class extends CliOptionsParser {
 $cliOptions = new class extends CliOptionsParser {
 	public string $action;
 	public string $action;
 	public string $key;
 	public string $key;
+	public string $newKey;
 	public string $value;
 	public string $value;
 	public string $language;
 	public string $language;
 	public string $originLanguage;
 	public string $originLanguage;
@@ -18,6 +19,7 @@ $cliOptions = new class extends CliOptionsParser {
 	public function __construct() {
 	public function __construct() {
 		$this->addRequiredOption('action', (new CliOption('action', 'a')));
 		$this->addRequiredOption('action', (new CliOption('action', 'a')));
 		$this->addOption('key', (new CliOption('key', 'k')));
 		$this->addOption('key', (new CliOption('key', 'k')));
+		$this->addOption('newKey', (new CliOption('new-key', 'n')));
 		$this->addOption('value', (new CliOption('value', 'v')));
 		$this->addOption('value', (new CliOption('value', 'v')));
 		$this->addOption('language', (new CliOption('language', 'l')));
 		$this->addOption('language', (new CliOption('language', 'l')));
 		$this->addOption('originLanguage', (new CliOption('origin-language', 'o')));
 		$this->addOption('originLanguage', (new CliOption('origin-language', 'o')));
@@ -56,6 +58,14 @@ switch ($cliOptions->action) {
 			exit;
 			exit;
 		}
 		}
 		break;
 		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':
 	case 'delete':
 		if (isset($cliOptions->key)) {
 		if (isset($cliOptions->key)) {
 			$i18nData->removeKey($cliOptions->key);
 			$i18nData->removeKey($cliOptions->key);
@@ -131,7 +141,7 @@ DESCRIPTION
 	Manipulate translation files.
 	Manipulate translation files.
 
 
 	-a, --action=ACTION
 	-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.
 				exist, format, ignore, and ignore_unmodified. This option is mandatory.
 	-k, --key=KEY		select the key to work on.
 	-k, --key=KEY		select the key to work on.
 	-v, --value=VAL		select the value to set.
 	-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
 Example 11:	add a new file to all languages
 	php $file -a add -k my_file.php
 	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();
 	exit();
 }
 }

+ 6 - 1
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.
 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:
 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.
 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
 ## 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:
 To access these translations, you must use the `_t()` function (which is a shortcut for `Minz_Translate::t()`). Code example:

+ 12 - 2
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 {
 	public function testConstructWhenReferenceOnly(): void {
 		$data = new I18nData($this->referenceData);
 		$data = new I18nData($this->referenceData);
 		self::assertSame($this->referenceData, $data->getData());
 		self::assertSame($this->referenceData, $data->getData());
@@ -302,7 +312,7 @@ final class I18nDataTest extends \PHPUnit\Framework\TestCase {
 
 
 	public function testAddLanguageWhenLanguageExists(): void {
 	public function testAddLanguageWhenLanguageExists(): void {
 		$this->expectException(\Exception::class);
 		$this->expectException(\Exception::class);
-		$this->expectExceptionMessage('The selected language already exist.');
+		$this->expectExceptionMessage('The selected language already exists.');
 		$data = new I18nData($this->referenceData);
 		$data = new I18nData($this->referenceData);
 		$data->addLanguage('en');
 		$data->addLanguage('en');
 	}
 	}
@@ -430,7 +440,7 @@ final class I18nDataTest extends \PHPUnit\Framework\TestCase {
 
 
 	public function testAddKeyWhenKeyExists(): void {
 	public function testAddKeyWhenKeyExists(): void {
 		$this->expectException(\Exception::class);
 		$this->expectException(\Exception::class);
-		$this->expectExceptionMessage('The selected key already exist.');
+		$this->expectExceptionMessage('The selected key already exists.');
 		$data = new I18nData($this->referenceData);
 		$data = new I18nData($this->referenceData);
 		$data->addKey('file2.l1.l2.k1', 'value');
 		$data->addKey('file2.l1.l2.k1', 'value');
 	}
 	}