From 0fda85e9cce05cd682ea2f4d383b0daf4d4ada62 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 3 Nov 2025 13:47:45 +0000 Subject: [PATCH 1/6] Initial plan From 9f137a95aa7ce38702268102dc360474b6015597 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 3 Nov 2025 14:09:33 +0000 Subject: [PATCH 2/6] Implement POT file order preservation in update-po command Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- features/updatepo.feature | 52 +++++++++++++++++++++++++++++++++++++++ src/UpdatePoCommand.php | 32 +++++++++++++++++++++++- 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/features/updatepo.feature b/features/updatepo.feature index 6aba5e30..85e86f94 100644 --- a/features/updatepo.feature +++ b/features/updatepo.feature @@ -465,3 +465,55 @@ Feature: Update existing PO files from a POT file """ "X-Domain: foo-plugin\n" """ + + Scenario: Keeps POT file order of translations + Given an empty foo-plugin directory + And a foo-plugin/foo-plugin.pot file: + """ + # Copyright (C) 2018 Foo Plugin + # This file is distributed under the same license as the Foo Plugin package. + msgid "" + msgstr "" + "Project-Id-Version: Foo Plugin\n" + "Content-Type: text/plain; charset=UTF-8\n" + "X-Domain: foo-plugin\n" + + #: foo-plugin.php:1 + msgid "First string" + msgstr "" + + #: foo-plugin.php:10 + msgid "Second string" + msgstr "" + + #: foo-plugin.php:20 + msgid "Third string" + msgstr "" + """ + And a foo-plugin/foo-plugin-de_DE.po file: + """ + # Copyright (C) 2018 Foo Plugin + # This file is distributed under the same license as the Foo Plugin package. + msgid "" + msgstr "" + "Project-Id-Version: Foo Plugin\n" + "Content-Type: text/plain; charset=UTF-8\n" + "Language: de_DE\n" + "X-Domain: foo-plugin\n" + + #: foo-plugin.php:20 + msgid "Third string" + msgstr "Dritte Zeichenfolge" + + #: foo-plugin.php:1 + msgid "First string" + msgstr "Erste Zeichenfolge" + """ + + When I run `wp i18n update-po foo-plugin/foo-plugin.pot foo-plugin/foo-plugin-de_DE.po` + Then STDOUT should be: + """ + Success: Updated 1 file. + """ + And STDERR should be empty + And the foo-plugin/foo-plugin-de_DE.po file should match /First string.*Second string.*Third string/s diff --git a/src/UpdatePoCommand.php b/src/UpdatePoCommand.php index 5af6b996..db3dc7b5 100644 --- a/src/UpdatePoCommand.php +++ b/src/UpdatePoCommand.php @@ -45,6 +45,33 @@ class UpdatePoCommand extends WP_CLI_Command { * * @throws WP_CLI\ExitException */ + /** + * Reorder translations to match the POT file order. + * + * @param Translations $po_translations The merged PO translations. + * @param Translations $pot_translations The POT translations (source of truth for order). + * + * @return Translations Translations object with entries in POT file order. + */ + private function reorder_translations( Translations $po_translations, Translations $pot_translations ) { + $ordered = new Translations(); + + // Copy headers from the merged PO translations. + foreach ( $po_translations->getHeaders() as $name => $value ) { + $ordered->setHeader( $name, $value ); + } + + // Add translations in POT file order. + foreach ( $pot_translations as $pot_entry ) { + $po_entry = $po_translations->find( $pot_entry ); + if ( $po_entry ) { + $ordered[] = $po_entry->getClone(); + } + } + + return $ordered; + } + public function __invoke( $args, $assoc_args ) { $source = realpath( $args[0] ); if ( ! $source || ! is_file( $source ) ) { @@ -87,7 +114,10 @@ public function __invoke( $args, $assoc_args ) { Merge::ADD | Merge::REMOVE | Merge::COMMENTS_THEIRS | Merge::EXTRACTED_COMMENTS_THEIRS | Merge::REFERENCES_THEIRS | Merge::DOMAIN_OVERRIDE ); - if ( ! $po_translations->toPoFile( $file->getPathname() ) ) { + // Reorder translations to match POT file order. + $ordered_translations = $this->reorder_translations( $po_translations, $pot_translations ); + + if ( ! $ordered_translations->toPoFile( $file->getPathname() ) ) { WP_CLI::warning( sprintf( 'Could not update file %s', $file->getPathname() ) ); continue; } From 1b436fb2ba6000e365973eeb985492ba1b71a44f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 3 Nov 2025 14:15:19 +0000 Subject: [PATCH 3/6] Fix docblock placement issue Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- src/UpdatePoCommand.php | 54 ++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/UpdatePoCommand.php b/src/UpdatePoCommand.php index db3dc7b5..fc9b01bf 100644 --- a/src/UpdatePoCommand.php +++ b/src/UpdatePoCommand.php @@ -45,33 +45,6 @@ class UpdatePoCommand extends WP_CLI_Command { * * @throws WP_CLI\ExitException */ - /** - * Reorder translations to match the POT file order. - * - * @param Translations $po_translations The merged PO translations. - * @param Translations $pot_translations The POT translations (source of truth for order). - * - * @return Translations Translations object with entries in POT file order. - */ - private function reorder_translations( Translations $po_translations, Translations $pot_translations ) { - $ordered = new Translations(); - - // Copy headers from the merged PO translations. - foreach ( $po_translations->getHeaders() as $name => $value ) { - $ordered->setHeader( $name, $value ); - } - - // Add translations in POT file order. - foreach ( $pot_translations as $pot_entry ) { - $po_entry = $po_translations->find( $pot_entry ); - if ( $po_entry ) { - $ordered[] = $po_entry->getClone(); - } - } - - return $ordered; - } - public function __invoke( $args, $assoc_args ) { $source = realpath( $args[0] ); if ( ! $source || ! is_file( $source ) ) { @@ -127,4 +100,31 @@ public function __invoke( $args, $assoc_args ) { WP_CLI::success( sprintf( 'Updated %d %s.', $result_count, Utils\pluralize( 'file', $result_count ) ) ); } + + /** + * Reorder translations to match the POT file order. + * + * @param Translations $po_translations The merged PO translations. + * @param Translations $pot_translations The POT translations (source of truth for order). + * + * @return Translations Translations object with entries in POT file order. + */ + private function reorder_translations( Translations $po_translations, Translations $pot_translations ) { + $ordered = new Translations(); + + // Copy headers from the merged PO translations. + foreach ( $po_translations->getHeaders() as $name => $value ) { + $ordered->setHeader( $name, $value ); + } + + // Add translations in POT file order. + foreach ( $pot_translations as $pot_entry ) { + $po_entry = $po_translations->find( $pot_entry ); + if ( $po_entry ) { + $ordered[] = $po_entry->getClone(); + } + } + + return $ordered; + } } From f29e9cd530f91510e65d09af5919c13af6a70ea4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 3 Nov 2025 14:30:33 +0000 Subject: [PATCH 4/6] Fix Behat test syntax to use correct step definition Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- features/updatepo.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/updatepo.feature b/features/updatepo.feature index 85e86f94..95c2a253 100644 --- a/features/updatepo.feature +++ b/features/updatepo.feature @@ -516,4 +516,4 @@ Feature: Update existing PO files from a POT file Success: Updated 1 file. """ And STDERR should be empty - And the foo-plugin/foo-plugin-de_DE.po file should match /First string.*Second string.*Third string/s + And the contents of the foo-plugin/foo-plugin-de_DE.po file should match /First string.*Second string.*Third string/s From 2caa01c67e6198244ffd12a0557e4728a0aec1dd Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sat, 20 Dec 2025 22:43:48 +0100 Subject: [PATCH 5/6] Fix test --- features/updatepo.feature | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/features/updatepo.feature b/features/updatepo.feature index 0018a768..87c0cd18 100644 --- a/features/updatepo.feature +++ b/features/updatepo.feature @@ -488,6 +488,10 @@ Feature: Update existing PO files from a POT file #: foo-plugin.php:1 msgid "Some string" msgstr "" + + #: foo-plugin.php:2 + msgid "Some other string" + msgstr "" """ And a foo-plugin/foo-plugin-de_DE.po file: """ @@ -578,4 +582,4 @@ Feature: Update existing PO files from a POT file Success: Updated 1 file. """ And STDERR should be empty - And the contents of the foo-plugin/foo-plugin-de_DE.po file should match /First string.*Second string.*Third string/s \ No newline at end of file + And the contents of the foo-plugin/foo-plugin-de_DE.po file should match /First string.*Second string.*Third string/s From 99d5bab48992abc4d15f9ef787b46fc8a704d84f Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sat, 20 Dec 2025 23:00:27 +0100 Subject: [PATCH 6/6] Fix order --- src/UpdatePoCommand.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/UpdatePoCommand.php b/src/UpdatePoCommand.php index 243465b7..9cb2aae8 100644 --- a/src/UpdatePoCommand.php +++ b/src/UpdatePoCommand.php @@ -87,13 +87,13 @@ public function __invoke( $args, $assoc_args ) { Merge::ADD | Merge::REMOVE | Merge::COMMENTS_THEIRS | Merge::EXTRACTED_COMMENTS_THEIRS | Merge::REFERENCES_THEIRS | Merge::DOMAIN_OVERRIDE ); - // Reorder translations to match POT file order. - $ordered_translations = $this->reorder_translations( $po_translations, $pot_translations ); - // Update PO-Revision-Date to current date and time in UTC. // Uses gmdate() for consistency across different server timezones. $po_translations->setHeader( 'PO-Revision-Date', gmdate( 'Y-m-d\TH:i:sP' ) ); + // Reorder translations to match POT file order. + $ordered_translations = $this->reorder_translations( $po_translations, $pot_translations ); + if ( ! $ordered_translations->toPoFile( $file->getPathname() ) ) { WP_CLI::warning( sprintf( 'Could not update file %s', $file->getPathname() ) ); continue;