diff --git a/CHANGELOG.md b/CHANGELOG.md index 851fc9c..c7315da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## 3.4.1 - 2026-09-27 +### Added +- Hungarian language support (hu_HU) with .pot, .po, and .mo files + ### Changed +- credited sandros & Rarst as authors and updated repository homepage - adjusted requirements to `Requires at least: 6.2` to support ClassicPress 2.x and WordPress 6.2+ - updated documentation with Hungarian translation diff --git a/composer.json b/composer.json index de27da4..4db7c56 100644 --- a/composer.json +++ b/composer.json @@ -3,16 +3,20 @@ "description": "Light WordPress profiler.", "keywords" : ["wordpress", "performance"], "type" : "wordpress-plugin", - "homepage" : "https://github.com/Rarst/laps", + "homepage" : "https://git.sandros.hu/sandros/laps", "license" : "MIT", "authors" : [ + { + "name" : "sandros", + "homepage": "https://git.sandros.hu/sandros" + }, { "name" : "Andrey Savchenko", "homepage": "https://www.Rarst.net/" } ], "support" : { - "issues": "https://github.com/Rarst/laps/issues" + "issues": "https://git.sandros.hu/sandros/laps/issues" }, "autoload" : { "psr-4": { diff --git a/languages/laps-hu_HU.mo b/languages/laps-hu_HU.mo new file mode 100644 index 0000000..058df21 Binary files /dev/null and b/languages/laps-hu_HU.mo differ diff --git a/languages/laps-hu_HU.po b/languages/laps-hu_HU.po new file mode 100644 index 0000000..fab1bfd --- /dev/null +++ b/languages/laps-hu_HU.po @@ -0,0 +1,150 @@ +# Hungarian translation for Laps. +# Copyright (C) 2026 sandros & Rarst +# This file is distributed under the MIT license. +# +msgid "" +msgstr "" +"Project-Id-Version: Laps 3.4.1\n" +"Report-Msgid-Bugs-To: https://git.sandros.hu/sandros/laps/issues\n" +"POT-Creation-Date: 2026-09-27 23:05+0200\n" +"PO-Revision-Date: 2026-09-27 23:06+0200\n" +"Last-Translator: sandros \n" +"Language-Team: Hungarian \n" +"Language: hu_HU\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. Description of the plugin +#: laps.php:5 +msgid "Light WordPress profiler." +msgstr "Könnyűsúlyú WordPress profilozó." + +#. translators: %s: page load time in seconds +#: src/Manager/Toolbar_Manager.php:51 +msgid "Lap: %ss" +msgstr "Kör: %ss" + +#. translators: %s: PHP version +#: src/Record/Collector/Core_Load_Collector.php:40 +msgid "PHP Load – %s" +msgstr "PHP betöltés – %s" + +#: src/Record/Collector/Core_Load_Collector.php:48 +msgid "OPcache" +msgstr "OPcache" + +#: src/Record/Collector/Core_Load_Collector.php:51 +msgid "Core Load" +msgstr "WordPress mag betöltése" + +#: src/Record/Collector/Core_Load_Collector.php:51 +msgid "Core & MU Plugins Load" +msgstr "WordPress mag és MU bővítmények betöltése" + +#: src/Record/Collector/Hook_Collector.php:46 +msgid "Network & MU Plugins Load" +msgstr "Hálózati és MU bővítmények betöltése" + +#: src/Event/Core_Events.php:16 +msgid "Plugins Load" +msgstr "Bővítmények betöltése" + +#: src/Event/Core_Events.php:17 +msgid "Plugins Loaded Hook" +msgstr "Plugins Loaded hook" + +#: src/Event/Core_Events.php:18 +msgid "Themes Load" +msgstr "Sablonok betöltése" + +#: src/Event/Core_Events.php:19 +msgid "Init Hook" +msgstr "Init hook" + +#: src/Event/Core_Events.php:20 +msgid "WP Loaded Hook" +msgstr "WP Loaded hook" + +#: src/Event/Core_Events.php:21 +msgid "Query Setup" +msgstr "Lekérdezés előkészítése" + +#: src/Event/Core_Events.php:22 +msgid "Template Loader" +msgstr "Sablon betöltő" + +#: src/Event/Core_Events.php:23 +msgid "Admin Init Hook" +msgstr "Admin Init hook" + +#: src/Event/Core_Events.php:24 +msgid "Admin Menu" +msgstr "Admin menü" + +#: src/Event/Core_Events.php:25 +msgid "Admin Print Scripts" +msgstr "Admin scriptek betöltése" + +#: src/Event/Core_Events.php:26 +msgid "Head Hook" +msgstr "Fejléc hook (wp_head)" + +#: src/Event/Core_Events.php:27 +msgid "Main Loop" +msgstr "Fő ciklus (loop)" + +#: src/Event/Core_Events.php:28 +msgid "Sidebar" +msgstr "Oldalsáv (sidebar)" + +#: src/Event/Core_Events.php:29 +msgid "Toolbar" +msgstr "Eszköztár (toolbar)" + +#: src/Event/Core_Events.php:30 +msgid "Footer Hook" +msgstr "Lábléc hook (wp_footer)" + +#. translators: 1: file path, 2: line number +#: src/Formatter/Hook_Formatter.php:94 +msgid "closure from %1$s:%2$d" +msgstr "closure innen: %1$s:%2$d" + +#. translators: 1: file path, 2: line number +#: src/Formatter/Hook_Formatter.php:107 +msgid "anonymous class from %1$s:%2$d" +msgstr "névtelen osztály innen: %1$s:%2$d" + +#: src/Event/Extension_Events.php:28 +msgid "Beaver Builder Post Content" +msgstr "Beaver Builder bejegyzéstartalom" + +#: src/Event/Extension_Events.php:33 +msgid "Beaver Builder Module" +msgstr "Beaver Builder modul" + +#: src/Event/Extension_Events.php:40 +msgid "Beaver Builder Top Bar" +msgstr "Beaver Builder felső sáv" + +#: src/Event/Extension_Events.php:41 +msgid "Beaver Builder Header" +msgstr "Beaver Builder fejléc" + +#: src/Event/Extension_Events.php:42 +msgid "Beaver Builder Page Content" +msgstr "Beaver Builder oldaltartalom" + +#: src/Event/Extension_Events.php:43 +msgid "Beaver Builder Post" +msgstr "Beaver Builder bejegyzés" + +#: src/Event/Extension_Events.php:44 +msgid "Beaver Builder Footer Widgets" +msgstr "Beaver Builder lábléc widgetek" + +#: src/Event/Extension_Events.php:45 +msgid "Beaver Builder Footer" +msgstr "Beaver Builder lábléc" diff --git a/languages/laps.pot b/languages/laps.pot new file mode 100644 index 0000000..56a1fd4 --- /dev/null +++ b/languages/laps.pot @@ -0,0 +1,148 @@ +# Copyright (C) 2026 sandros & Rarst +# This file is distributed under the MIT license. +msgid "" +msgstr "" +"Project-Id-Version: Laps 3.4.1\n" +"Report-Msgid-Bugs-To: https://git.sandros.hu/sandros/laps/issues\n" +"POT-Creation-Date: 2026-09-27 23:05+0200\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2026-YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. Description of the plugin +#: laps.php:5 +msgid "Light WordPress profiler." +msgstr "" + +#. translators: %s: page load time in seconds +#: src/Manager/Toolbar_Manager.php:51 +msgid "Lap: %ss" +msgstr "" + +#. translators: %s: PHP version +#: src/Record/Collector/Core_Load_Collector.php:40 +msgid "PHP Load – %s" +msgstr "" + +#: src/Record/Collector/Core_Load_Collector.php:48 +msgid "OPcache" +msgstr "" + +#: src/Record/Collector/Core_Load_Collector.php:51 +msgid "Core Load" +msgstr "" + +#: src/Record/Collector/Core_Load_Collector.php:51 +msgid "Core & MU Plugins Load" +msgstr "" + +#: src/Record/Collector/Hook_Collector.php:46 +msgid "Network & MU Plugins Load" +msgstr "" + +#: src/Event/Core_Events.php:16 +msgid "Plugins Load" +msgstr "" + +#: src/Event/Core_Events.php:17 +msgid "Plugins Loaded Hook" +msgstr "" + +#: src/Event/Core_Events.php:18 +msgid "Themes Load" +msgstr "" + +#: src/Event/Core_Events.php:19 +msgid "Init Hook" +msgstr "" + +#: src/Event/Core_Events.php:20 +msgid "WP Loaded Hook" +msgstr "" + +#: src/Event/Core_Events.php:21 +msgid "Query Setup" +msgstr "" + +#: src/Event/Core_Events.php:22 +msgid "Template Loader" +msgstr "" + +#: src/Event/Core_Events.php:23 +msgid "Admin Init Hook" +msgstr "" + +#: src/Event/Core_Events.php:24 +msgid "Admin Menu" +msgstr "" + +#: src/Event/Core_Events.php:25 +msgid "Admin Print Scripts" +msgstr "" + +#: src/Event/Core_Events.php:26 +msgid "Head Hook" +msgstr "" + +#: src/Event/Core_Events.php:27 +msgid "Main Loop" +msgstr "" + +#: src/Event/Core_Events.php:28 +msgid "Sidebar" +msgstr "" + +#: src/Event/Core_Events.php:29 +msgid "Toolbar" +msgstr "" + +#: src/Event/Core_Events.php:30 +msgid "Footer Hook" +msgstr "" + +#. translators: 1: file path, 2: line number +#: src/Formatter/Hook_Formatter.php:94 +msgid "closure from %1$s:%2$d" +msgstr "" + +#. translators: 1: file path, 2: line number +#: src/Formatter/Hook_Formatter.php:107 +msgid "anonymous class from %1$s:%2$d" +msgstr "" + +#: src/Event/Extension_Events.php:28 +msgid "Beaver Builder Post Content" +msgstr "" + +#: src/Event/Extension_Events.php:33 +msgid "Beaver Builder Module" +msgstr "" + +#: src/Event/Extension_Events.php:40 +msgid "Beaver Builder Top Bar" +msgstr "" + +#: src/Event/Extension_Events.php:41 +msgid "Beaver Builder Header" +msgstr "" + +#: src/Event/Extension_Events.php:42 +msgid "Beaver Builder Page Content" +msgstr "" + +#: src/Event/Extension_Events.php:43 +msgid "Beaver Builder Post" +msgstr "" + +#: src/Event/Extension_Events.php:44 +msgid "Beaver Builder Footer Widgets" +msgstr "" + +#: src/Event/Extension_Events.php:45 +msgid "Beaver Builder Footer" +msgstr "" diff --git a/laps.php b/laps.php index 8b3cb38..89b7dee 100644 --- a/laps.php +++ b/laps.php @@ -2,14 +2,16 @@ /* Plugin Name: Laps -Plugin URI: https://github.com/Rarst/laps +Plugin URI: https://git.sandros.hu/sandros/laps Description: Light WordPress profiler. -Author: Andrey “Rarst” Savchenko +Author: sandros & Rarst +Author URI: https://git.sandros.hu/sandros/laps Version: 3.4.1 -Author URI: https://www.rarst.net/ Requires at least: 6.2 Requires PHP: 7.4 Update URI: false +Text Domain: laps +Domain Path: /languages License: MIT */ @@ -25,6 +27,10 @@ if ( PHP_VERSION_ID < 70400 ) { trigger_error( 'Laps requires PHP version >=7.4.', E_USER_ERROR ); } +add_action( 'plugins_loaded', function (): void { + load_plugin_textdomain( 'laps', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); +} ); + $laps = new \Rarst\Laps\Plugin(); $laps->run(); diff --git a/src/Formatter/Hook_Formatter.php b/src/Formatter/Hook_Formatter.php index ed894eb..15876a4 100644 --- a/src/Formatter/Hook_Formatter.php +++ b/src/Formatter/Hook_Formatter.php @@ -4,89 +4,76 @@ declare( strict_types=1 ); namespace Rarst\Laps\Formatter; /** - * Formatter to process callbacks from a hook instance. + * Formatter for WordPress hook callbacks. */ class Hook_Formatter { - /** @var array $truncate_paths Paths to truncate from includes. */ - protected $truncate_paths = []; + /** @var array $truncate_paths List of base paths to strip for readability. */ + protected $truncate_paths; /** - * Set up object properties. + * Set up truncate paths. */ public function __construct() { - // TODO inject paths instead of reaching for global constants. - $this->truncate_paths = [ - wp_normalize_path( WP_CONTENT_DIR ), + $this->truncate_paths = array_filter( [ wp_normalize_path( ABSPATH ), - 'wp-admin/', - ]; + wp_normalize_path( WP_CONTENT_DIR ), + ] ); } /** - * @psalm-type Callback = string|object|array - * @psalm-type AcceptedArgs = int - * @psalm-type Hook = array{function: Callback, accepted_args: AcceptedArgs} - * @psalm-type Priority = array-key - * @psalm-type Hooks = array> + * Convert hook callbacks array to a list of readable names. * - * @param \WP_Hook|array $hook Hook instance. - * @psalm-param \WP_Hook|Hooks $hook + * @param array $hook Array of callbacks, registered for the hook. * * @return array */ - public function format( $hook ): array { - - $callbacks = []; - - if ( $hook instanceof \WP_Hook ) { - /** @psalm-var Hooks $hook->callbacks */ - $hook = $hook->callbacks; - } + public function format( array $hook ): array { if ( empty( $hook ) ) { - return $callbacks; + return []; } - ksort( $hook ); + /** + * Empty priorities produce empty arrays in PHP 7.2 and unpack operator doesn't accept empty array in 7.3. + * + * @see https://wiki.php.net/rfc/compact-empty-array-de-optimization + * @see https://bugs.php.net/bug.php?id=76045 + */ + $hook = array_filter( $hook ); + if ( empty( $hook ) ) { + return []; + } + + /** @var array $functions */ $functions = array_merge( ...$hook ); - foreach ( $functions as $function ) { - $callback = $this->get_callback_name( $function['function'], $function['accepted_args'] ); - - if ( false !== strpos( $callback, 'Hook_Collector' ) ) { - continue; - } - - $callbacks[] = $callback; - } - - return $callbacks; + return array_map( [ $this, 'format_callback' ], $functions ); } /** - * @param string|object|array $callback Hook callback. - * @param int $args Number of accepted arguments. + * Convert callback data array to a readable name. + * + * @param array $callback_data Callback data, registered for the hook. + * + * @return string */ - protected function get_callback_name( $callback, int $args ): string { + protected function format_callback( array $callback_data ): string { - switch ( gettype( $callback ) ) { - case 'object': - $name = $this->get_class_name( $callback ); - break; + $callback = $callback_data['function']; + $args = $callback_data['accepted_args']; - case 'array': - /** @psalm-var array{0: object|string, 1: string} $callback */ - $class = is_string( $callback[0] ) - ? $callback[0] . '::' - : $this->get_class_name( $callback[0] ) . '->'; - $name = $class . $callback[1]; - break; - - default: - $name = $callback; + if ( is_string( $callback ) ) { + $name = $callback; + } elseif ( is_array( $callback ) ) { + $class = is_object( $callback[0] ) ? $this->get_class_name( $callback[0] ) : $callback[0]; + $name = $class . '::' . $callback[1]; + } elseif ( is_object( $callback ) ) { + $name = $this->get_class_name( $callback ); + } else { + $name = 'unknown'; } return $name . ( ( 1 === $args ) ? '' : "({$args})" ); @@ -102,14 +89,24 @@ class Hook_Formatter { if ( $object instanceof \Closure ) { $class = new \ReflectionFunction( $object ); - return 'closure from ' . $this->shorten_path( $class->getFileName() ) . ':' . $class->getStartLine(); + return sprintf( + /* translators: 1: file path, 2: line number */ + __( 'closure from %1$s:%2$d', 'laps' ), + $this->shorten_path( (string) $class->getFileName() ), + (int) $class->getStartLine() + ); } $class = new \ReflectionClass( $object ); $name = $class->getName(); if ( 0 === strpos( $name, 'class@anonymous' ) ) { - return 'anonymous class from ' . $this->shorten_path( $class->getFileName() ) . ':' . $class->getStartLine(); + return sprintf( + /* translators: 1: file path, 2: line number */ + __( 'anonymous class from %1$s:%2$d', 'laps' ), + $this->shorten_path( (string) $class->getFileName() ), + (int) $class->getStartLine() + ); } return $name; @@ -121,14 +118,16 @@ class Hook_Formatter { * @return string */ protected function shorten_path( string $path ): string { + $path = wp_normalize_path( $path ); $path = str_replace( $this->truncate_paths, '', $path ); - if ( ':' === $path[1] ) { + + if ( 0 === strpos( $path, './' ) ) { $path = substr( $path, 2 ); } + $path = ltrim( $path, '/' ); return $path; } } - diff --git a/src/Manager/Toolbar_Manager.php b/src/Manager/Toolbar_Manager.php index a340244..f02470c 100644 --- a/src/Manager/Toolbar_Manager.php +++ b/src/Manager/Toolbar_Manager.php @@ -47,7 +47,8 @@ class Toolbar_Manager { $wp_admin_bar->add_node( [ 'id' => 'laps', - 'title' => sprintf( 'Lap: %ss', round( microtime( true ) - $timestart, 3 ) ), + /* translators: %s: page load time in seconds */ + 'title' => sprintf( __( 'Lap: %ss', 'laps' ), round( microtime( true ) - $timestart, 3 ) ), ] ); $wp_admin_bar->add_node( [ diff --git a/src/Record/Collector/Core_Load_Collector.php b/src/Record/Collector/Core_Load_Collector.php index a8f8189..179eb44 100644 --- a/src/Record/Collector/Core_Load_Collector.php +++ b/src/Record/Collector/Core_Load_Collector.php @@ -36,7 +36,8 @@ class Core_Load_Collector implements Record_Collector_Interface { $request_time = filter_var( $_SERVER['REQUEST_TIME_FLOAT'], FILTER_VALIDATE_FLOAT ); - $php = 'PHP Load – ' . PHP_VERSION; + /* translators: %s: PHP version */ + $php = sprintf( __( 'PHP Load – %s', 'laps' ), PHP_VERSION ); if ( $this->opcache_api_available() ) { /** @@ -44,10 +45,10 @@ class Core_Load_Collector implements Record_Collector_Interface { * @see https://github.com/php/php-src/issues/8799 */ $zend_status = @opcache_get_status(); - $php .= empty( $zend_status['opcache_enabled'] ) ? '' : ' – OPcache'; + $php .= empty( $zend_status['opcache_enabled'] ) ? '' : ' – ' . __( 'OPcache', 'laps' ); } - $load = $this->network_activated ? 'Core Load' : 'Core & MU Plugins Load'; + $load = $this->network_activated ? __( 'Core Load', 'laps' ) : __( 'Core & MU Plugins Load', 'laps' ); /** * @var float $request_time diff --git a/src/Record/Record.php b/src/Record/Record.php index 4f3c575..b4a66db 100644 --- a/src/Record/Record.php +++ b/src/Record/Record.php @@ -52,8 +52,9 @@ class Record implements Record_Interface { if ( empty( $this->description ) ) { $duration = round( $this->get_duration() * 1000 ); + $name = __( $this->name, 'laps' ); - return "{$this->name} – {$duration} ms"; + return "{$name} – {$duration} ms"; } return $this->description; diff --git a/src/Record/Stopwatch_Record.php b/src/Record/Stopwatch_Record.php index ad053df..81d1924 100644 --- a/src/Record/Stopwatch_Record.php +++ b/src/Record/Stopwatch_Record.php @@ -45,8 +45,9 @@ class Stopwatch_Record implements Record_Interface { $duration = round( $this->stopwatch_event->getDuration() ); $memory = round( $this->stopwatch_event->getMemory() / 1024 / 1024, 2 ); $description = '' !== $this->description ? "
{$this->description}" : ''; + $name = __( $this->name, 'laps' ); - return "{$this->name} – {$duration} ms – {$memory} MB{$description}"; + return "{$name} – {$duration} ms – {$memory} MB{$description}"; } /** diff --git a/tests/LapsTestCase.php b/tests/LapsTestCase.php index f382090..d5f5a4a 100644 --- a/tests/LapsTestCase.php +++ b/tests/LapsTestCase.php @@ -13,6 +13,7 @@ class LapsTestCase extends TestCase { protected function setUp():void { parent::setUp(); Monkey\setUp(); + Monkey\Functions\stubTranslationFunctions(); } protected function tearDown(): void {