Skip to content

Conversation

@Squidly271
Copy link
Contributor

@Squidly271 Squidly271 commented Oct 28, 2025

Summary by CodeRabbit

  • New Features

    • Added a translation extractor that scans plugin files and generates separate JS and PHP/HTML .po catalogs with location references and progress reporting.
  • Internationalization

    • Added a comprehensive JavaScript translations catalog.
    • Standardized translation formatting across many UI strings (positional placeholders, sprintf, plural handling).
  • Bug Fixes / Chores

    • Multiple wording, punctuation and typo fixes, minor session/formatting tweaks, and added Name metadata to several pages.

✏️ Tip: You can customize this high-level summary in your review settings.

@Squidly271 Squidly271 marked this pull request as draft October 28, 2025 16:10
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 28, 2025

Walkthrough

Adds a new PHP extractor that scans plugin files to collect gettext-like strings into JS and PHP/HTML maps with file:line locations and writes translations_js.po and translations_php.po. Also applies widespread i18n sprintf/placeholder conversions, punctuation/typo fixes, a session_write_close tweak, and many menu/page metadata additions.

Changes

Cohort / File(s) Summary of Changes
New extraction script
emhttp/plugins/extract_translations.php
New standalone PHP script: recursively enumerates plugin files (configurable extension sets; excludes itself, dynamix/javascript/dynamix.js, ace paths), detects patterns _(...), _('...')/_("..."), tr('...')/tr("..."), and ngettext('singular','plural',$n), computes file:line locations, aggregates per-string locations into separate JS and PHP/HTML maps, escapes entries and writes translations_js.po and translations_php.po.
Generated JS translation catalog (new)
emhttp/plugins/translations_js.po
New PO file: headers and many msgid entries (empty msgstr) with multiple source location references.
VM manager sprintf/spacing & label fixes
emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php, .../scripts/VMAjaxCall.php, .../scripts/VMClone.php, .../VMTemplates.page, .../templates/Custom.form.php
Swapped embedded-variable translations for sprintf with %s placeholders; adjusted error message spacing; wrapped vCPU button labels with _() and fixed minor typos/whitespace.
Localization conversions (sprintf/numbered placeholders)
emhttp/plugins/dynamix/ArrayOperation.page, emhttp/plugins/dynamix/DashStats.page, emhttp/plugins/dynamix.docker.manager/scripts/dockerupdate, emhttp/plugins/dynamix.plugin.manager/scripts/languagecheck, emhttp/plugins/dynamix.plugin.manager/scripts/plugincheck, emhttp/plugins/dynamix/include/DashboardApps.php, emhttp/plugins/dynamix/nchan/device_list, emhttp/plugins/dynamix/nchan/update_2, emhttp/plugins/dynamix.docker.manager/include/DockerContainers.php
Converted many translation usages to sprintf and/or positional placeholders (%1$s, %2$s), moved variable interpolation out of literal translations, and improved plural handling in some places.
Swal/dialog punctuation and wording fixes
emhttp/plugins/dynamix/DeviceInfo.page, .../NewPerms.page, .../SysDrivers.page, .../dynamix.docker.manager/include/CreateDocker.php, .../VMTemplates.page
Adjusted placement of question marks and small wording/punctuation changes in dialog titles and confirmation prompts; UI text only.
Session & formatting tweak
emhttp/plugins/dynamix/include/Translations.php
Added session_write_close() immediately after session_start() and minor whitespace/formatting edits.
Menu/page metadata additions
emhttp/plugins/dynamix.docker.manager/Docker.page, emhttp/plugins/dynamix.plugin.manager/Plugins.page, emhttp/plugins/dynamix.vm.manager/VMs.page, emhttp/plugins/dynamix/Dashboard.page, emhttp/plugins/dynamix/Device.page, emhttp/plugins/dynamix/Favorites.page, emhttp/plugins/dynamix/Flash.page, emhttp/plugins/dynamix/Main.page, emhttp/plugins/dynamix/Settings.page, emhttp/plugins/dynamix/Share.page, emhttp/plugins/dynamix/Shares.page, emhttp/plugins/dynamix/Tools.page
Added Name="..." attributes to multiple .page files (menu/page metadata); no behavior changes.
Title localization removals / static labels
emhttp/plugins/dynamix/Eth0.page, emhttp/plugins/dynamix/EthX.page, emhttp/plugins/dynamix/Wireless.page
Replaced translatable title entries with static strings (e.g., "Interface eth0", "Interface ethX", "Wireless wlan0").
Spelling / docs / minor text fixes
emhttp/plugins/dynamix/PHPsettings.page, emhttp/plugins/dynamix/include/Markdown.php, .../MarkdownExtra.php
Corrected typos and documentation comments (e.g., "overide"→"override", "begining"→"beginning", "automaticaly"→"automatically"); no behavior changes.
Small functional-localization tweaks
emhttp/plugins/dynamix/include/CPUset.php, emhttp/plugins/dynamix/nchan/device_list, emhttp/plugins/dynamix.docker.manager/include/DockerContainers.php
Localized button text and adjusted translation formatting to use placeholders/_() where appropriate; no control-flow changes.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Dev as Developer
    participant Extractor as extract_translations.php
    participant FS as Filesystem
    participant Parser as PatternMatcher
    participant MapJS as JS Map
    participant MapPHP as PHP/HTML Map
    participant Writer as PO Writer

    Dev->>Extractor: run script
    Extractor->>FS: enumerate files (apply extension filters, exclude paths)
    loop per file
        Extractor->>FS: read file content
        alt unreadable
            Extractor-->>Dev: warn and skip
        else
            Extractor->>Parser: match patterns _(…), _('…')/("…"), tr('…')/("…"), ngettext(...)
            alt file is JS
                Parser->>MapJS: record string with file:line
            else
                Parser->>MapPHP: record string with file:line
            end
        end
    end
    Extractor->>Writer: build translations_js.po from MapJS
    Writer->>FS: write translations_js.po
    Extractor->>Writer: build translations_php.po from MapPHP
    Writer->>FS: write translations_php.po
    Extractor-->>Dev: print totals and status
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

  • Review focus:
    • extract_translations.php: regex correctness, file exclusion rules, accurate line-number calculation, JS vs PHP/HTML classification, aggregation of locations, PO escaping and file writes.
    • Plural extraction (ngettext) handling and correct generation of msgid_plural / msgstr[n].
    • Spot-check converted sprintf/positional placeholder changes for translation semantics.

Poem

(_/)
(•ᴥ•) I hopped through files with carrot-powered glee,
I gathered msgids from JS and PHP, each line where they be.
Two little PO sacks ready, locales waiting to sing,
I twitch my nose, drop a carrot — translations take wing.
🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 69.23% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Refactor: Translations' accurately reflects the main focus of the pull request, which comprehensively updates translation handling across the codebase.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

@Squidly271 Squidly271 changed the title First draft Refactor: Translations Oct 28, 2025
@github-actions
Copy link

github-actions bot commented Oct 28, 2025

🔧 PR Test Plugin Available

A test plugin has been generated for this PR that includes the modified files.

Version: 2025.12.09.2305
Build: View Workflow Run

📥 Installation Instructions:

Install via Unraid Web UI:

  1. Go to Plugins → Install Plugin
  2. Copy and paste this URL:
https://preview.dl.unraid.net/pr-plugins/pr-2440/webgui-pr-2440.plg
  1. Click Install

Alternative: Direct Download

⚠️ Important Notes:

  • Testing only: This plugin is for testing PR changes
  • Backup included: Original files are automatically backed up
  • Easy removal: Files are restored when plugin is removed
  • Conflicts: Remove this plugin before installing production updates

📝 Modified Files:

Click to expand file list
emhttp/languages/en_US/helptext.txt
emhttp/plugins/dynamix.docker.manager/Docker.page
emhttp/plugins/dynamix.docker.manager/include/CreateDocker.php
emhttp/plugins/dynamix.docker.manager/include/DockerContainers.php
emhttp/plugins/dynamix.docker.manager/scripts/dockerupdate
emhttp/plugins/dynamix.plugin.manager/Plugins.page
emhttp/plugins/dynamix.plugin.manager/scripts/languagecheck
emhttp/plugins/dynamix.plugin.manager/scripts/plugincheck
emhttp/plugins/dynamix.vm.manager/VMTemplates.page
emhttp/plugins/dynamix.vm.manager/VMs.page
emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php
emhttp/plugins/dynamix.vm.manager/scripts/VMAjaxCall.php
emhttp/plugins/dynamix.vm.manager/scripts/VMClone.php
emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php
emhttp/plugins/dynamix/ArrayOperation.page
emhttp/plugins/dynamix/DashStats.page
emhttp/plugins/dynamix/Dashboard.page
emhttp/plugins/dynamix/Device.page
emhttp/plugins/dynamix/DeviceInfo.page
emhttp/plugins/dynamix/Eth0.page
emhttp/plugins/dynamix/EthX.page
emhttp/plugins/dynamix/Favorites.page
emhttp/plugins/dynamix/Flash.page
emhttp/plugins/dynamix/Main.page
emhttp/plugins/dynamix/NewPerms.page
emhttp/plugins/dynamix/PHPsettings.page
emhttp/plugins/dynamix/Settings.page
emhttp/plugins/dynamix/Share.page
emhttp/plugins/dynamix/Shares.page
emhttp/plugins/dynamix/SysDrivers.page
emhttp/plugins/dynamix/Tools.page
emhttp/plugins/dynamix/Wireless.page
emhttp/plugins/dynamix/include/CPUset.php
emhttp/plugins/dynamix/include/DashboardApps.php
emhttp/plugins/dynamix/include/Markdown.php
emhttp/plugins/dynamix/include/MarkdownExtra.php
emhttp/plugins/dynamix/include/Translations.php
emhttp/plugins/dynamix/nchan/device_list
emhttp/plugins/dynamix/nchan/update_2
emhttp/plugins/extract_translations.php
emhttp/plugins/translations_js.po
emhttp/plugins/translations_php.po

🔄 To Remove:

Navigate to Plugins → Installed Plugins and remove webgui-pr-2440, or run:

plugin remove webgui-pr-2440

🤖 This comment is automatically generated and will be updated with each new push to this PR.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
emhttp/plugins/extract_translations.php (3)

40-61: Consider protection against symlink loops.

The recursive directory scanning doesn't guard against symlink loops, which could cause infinite recursion if the directory structure contains circular symbolic links.

Add symlink detection or use a visited-path tracker:

 function scanDirectory($dir, $extensions, $pattern1, $pattern2, &$translations1, &$translations2, &$fileCount1, &$fileCount2) {
+    // Resolve to real path to detect symlink loops
+    $realPath = realpath($dir);
+    if ($realPath === false) {
+        return;
+    }
+    
     $items = scandir($dir);
     
     foreach ($items as $item) {
         if ($item === '.' || $item === '..') {
             continue;
         }
         
         $path = $dir . '/' . $item;
         
         if (is_dir($path)) {
+            // Skip if this is a symlink to avoid potential loops
+            if (is_link($path)) {
+                continue;
+            }
             // Recursively scan subdirectories
             scanDirectory($path, $extensions, $pattern1, $pattern2, $translations1, $translations2, $fileCount1, $fileCount2);
         }
     }
 }

66-71: Consider file size limits for memory safety.

Reading entire files with file_get_contents() without size checks could cause memory exhaustion on very large files.

Add a file size check before reading:

 function processFile($filePath, $pattern1, $pattern2, &$translations1, &$translations2, &$fileCount1, &$fileCount2) {
+    // Skip files larger than 10MB to prevent memory issues
+    $fileSize = filesize($filePath);
+    if ($fileSize === false || $fileSize > 10 * 1024 * 1024) {
+        echo "Warning: Skipping large or unreadable file: $filePath\n";
+        return;
+    }
+    
     $content = file_get_contents($filePath);
     if ($content === false) {
         echo "Warning: Could not read file: $filePath\n";
         return;
     }

112-112: Remove unused variable.

The variable $temp is assigned but never used. The return value of preg_match_all() is sufficient.

Based on static analysis.

Apply this diff:

-            $specialChars = preg_match_all('/[\/\^\$\*\+\?\{\}\[\]\\\|]/', $text, $temp);
+            $specialChars = preg_match_all('/[\/\^\$\*\+\?\{\}\[\]\\\|]/', $text);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a593173 and 029e478.

📒 Files selected for processing (1)
  • emhttp/plugins/extract_translations.php (1 hunks)
🧰 Additional context used
🪛 PHPMD (2.15.0)
emhttp/plugins/extract_translations.php

112-112: Avoid unused local variables such as '$temp'. (undefined)

(UnusedLocalVariable)

🔇 Additional comments (3)
emhttp/plugins/extract_translations.php (3)

105-108: Quote stripping doesn't verify matching quote types.

The logic strips quotes if the text starts and ends with a quote character, but doesn't verify they're the same type. For example, "text' would incorrectly have quotes stripped.

Apply this diff to ensure matching quote types:

             // Remove surrounding quotes if present
-            if ((substr($text, 0, 1) === '"' && substr($text, -1) === '"') ||
-                (substr($text, 0, 1) === "'" && substr($text, -1) === "'")) {
+            $firstChar = substr($text, 0, 1);
+            $lastChar = substr($text, -1);
+            if (($firstChar === '"' && $lastChar === '"') ||
+                ($firstChar === "'" && $lastChar === "'")) {
                 $text = substr($text, 1, -1);
             }

Note: The current logic actually does verify matching types (both conditions check start AND end), so this is already correct. This comment can be ignored.


140-174: LGTM!

The pattern 2 processing correctly extracts text between quotes, handles offset-based line number calculation, unescapes quotes, and filters empty strings. The use of stripslashes() is appropriate for translation strings that may contain escaped characters.


218-279: LGTM!

The main execution flow provides clear progress feedback, generates comprehensive statistics, and handles both success and error cases appropriately. The sample entries output is helpful for verification.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
emhttp/plugins/extract_translations.php (1)

19-19: Pattern still fails with nested parentheses.

The regex (?:[^)]|\\.)*? will still fail on strings like _(error (code 123))_ because [^)] stops at the first ) regardless of nesting level. The existing review comment on this remains valid.

🧹 Nitpick comments (2)
emhttp/plugins/extract_translations.php (2)

56-56: Consider adding case-insensitive extension matching for robustness.

The in_array($ext, $fileExtensions) check is case-sensitive. While the codebase currently maintains strict lowercase extension conventions (verified: all 662 files use lowercase), applying strtolower($ext) would protect against future mixed-case filenames and improve defensive programming.

$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));

110-113: Pattern 1 regex is intentionally permissive, making quote removal defensive rather than necessary.

The regex /_\(((?:[^)]|\\.)*?)\)_/s allows any content inside parentheses, including quotes. While the codebase currently uses only bare text like _(Update)_, the quote removal at lines 110-113 serves as a defensive measure. To make intent clearer, either add a comment explaining why quotes might occur or restrict the regex to /_\(([^'"\)]*(?:\\.)*?)\)_/s if only bare text is intended.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 029e478 and a3c9185.

📒 Files selected for processing (1)
  • emhttp/plugins/extract_translations.php (1 hunks)
🧰 Additional context used
🪛 PHPMD (2.15.0)
emhttp/plugins/extract_translations.php

117-117: Avoid unused local variables such as '$temp'. (undefined)

(UnusedLocalVariable)

🔇 Additional comments (5)
emhttp/plugins/extract_translations.php (5)

66-90: LGTM!

The file processing logic correctly handles self-exclusion, error cases, and progress reporting for both patterns.


162-162: stripslashes depends on pattern 2 correctly capturing escaped quotes.

Once pattern 2 is updated to handle escaped quotes (as suggested earlier), stripslashes() will correctly unescape the captured text. Ensure this change is coordinated with the regex fix.


207-207: Backslash escaping is correctly implemented.

The addcslashes($text, "\\\"\n\r\t") properly escapes backslashes for .po format, addressing the previous review concern about missing backslash escape.


222-284: LGTM!

The main execution flow provides clear progress reporting, statistics, and sample output. Error cases are handled with informative messages.


13-13: __DIR__ correctly resolves to the plugin directory structure.

The script is located at emhttp/plugins/extract_translations.php, and __DIR__ correctly resolves to emhttp/plugins/. The recursive scanDirectory() function will properly traverse subdirectories (such as emhttp/plugins/dynamix/include/) and extract translation strings from supported file extensions (js, html, htm, php, page). The .po output files are written to the same directory as the script. This configuration is appropriate and intentional for the extraction workflow.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (4)
emhttp/plugins/extract_translations.php (4)

146-146: Remove unused variable $temp.

The $temp variable in the preg_match_all call is never used. The return value (match count) is already assigned to $specialChars.

Based on static analysis hints.

Apply this diff:

-            $specialChars = preg_match_all('/[\/\^\$\*\+\?\{\}\[\]\\\|]/', $text, $temp);
+            $specialChars = preg_match_all('/[\/\^\$\*\+\?\{\}\[\]\\\|]/', $text);

27-27: Pattern may still fail with unescaped nested parentheses.

While the pattern now handles escaped characters with \\., it will still fail on text containing unescaped nested parentheses, such as _(error (code 123))_, where it will stop at the first ).

For a robust solution, consider implementing a simple parser that tracks parenthesis depth or use a PCRE recursive pattern if nested structures are expected in translation strings.


30-30: Pattern doesn't handle escaped quotes.

The pattern (?:(?!\1).)* will fail on strings containing escaped quotes, such as _("text with \" escaped"). The negative lookahead stops at any occurrence of the quote character, even if it's escaped.

Apply this diff to handle escaped quotes:

-$pattern2 = '/_\((["\'])((?:(?!\1).)*)\1\)/s';
+$pattern2 = '/_\((["\'])((?:[^\\\\]|\\\\.)*)?\1\)/s';

This pattern matches either non-backslash characters or a backslash followed by any character, allowing proper handling of escaped quotes.


51-52: Add error handling for scandir failure.

scandir() returns false on error (e.g., permission denied), which would cause a fatal error in the foreach loop at line 54.

Apply this diff to handle errors gracefully:

 function scanDirectory($dir, $extensions, $jsExtensions, $phpExtensions, $pattern1, $pattern2, $pattern3, &$translationsJS, &$translationsPHP, &$fileCountJS, &$fileCountPHP) {
-    $items = scandir($dir);
+    $items = @scandir($dir);
+    if ($items === false) {
+        echo "Warning: Could not read directory: $dir\n";
+        return;
+    }
     
     foreach ($items as $item) {
🧹 Nitpick comments (3)
emhttp/plugins/extract_translations.php (3)

19-19: Remove commented debug code.

The commented alternative base directory path appears to be leftover debug/testing code and should be removed before merging.

-//$baseDir = "/Volumes/GitHub/community.applications/source/community.applications/usr/local/emhttp/plugins/community.applications";

77-77: Remove unused parameter $phpExtensions.

The $phpExtensions parameter is never used in this function. The file type is determined by checking if the extension is in $jsExtensions (line 90), making this parameter redundant.

Based on static analysis hints.

Apply this diff:

-function processFile($filePath, $ext, $jsExtensions, $phpExtensions, $pattern1, $pattern2, $pattern3, &$translationsJS, &$translationsPHP, &$fileCountJS, &$fileCountPHP) {
+function processFile($filePath, $ext, $jsExtensions, $pattern1, $pattern2, $pattern3, &$translationsJS, &$translationsPHP, &$fileCountJS, &$fileCountPHP) {

And update the call at line 68:

-                processFile($path, $ext, $jsExtensions, $phpExtensions, $pattern1, $pattern2, $pattern3, $translationsJS, $translationsPHP, $fileCountJS, $fileCountPHP);
+                processFile($path, $ext, $jsExtensions, $pattern1, $pattern2, $pattern3, $translationsJS, $translationsPHP, $fileCountJS, $fileCountPHP);

213-247: Consider extracting common logic to reduce duplication.

The processPattern3 function is nearly identical to processPattern2 (lines 174-208), differing only in the pattern parameter. Consider extracting the common processing logic into a shared helper function to improve maintainability.

For example:

function processQuotedPattern($filePath, $content, $pattern, &$translations) {
    $matches = [];
    $count = preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE);
    $foundCount = 0;
    
    if ($count > 0) {
        foreach ($matches[2] as $match) {
            $text = trim($match[0]);
            $offset = $match[1];
            
            $beforeMatch = substr($content, 0, $offset);
            $lineNumber = substr_count($beforeMatch, "\n") + 1;
            
            $text = stripslashes($text);
            
            if (empty($text)) {
                continue;
            }
            
            if (!isset($translations[$text])) {
                $translations[$text] = [];
            }
            $translations[$text][] = $filePath . ':' . $lineNumber;
            $foundCount++;
        }
    }
    
    return $foundCount;
}

Then both functions can call this shared implementation.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a3c9185 and 4312b66.

📒 Files selected for processing (2)
  • emhttp/plugins/extract_translations.php (1 hunks)
  • emhttp/plugins/translations_js.po (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • emhttp/plugins/translations_js.po
🧰 Additional context used
🪛 PHPMD (2.15.0)
emhttp/plugins/extract_translations.php

77-77: Avoid unused parameters such as '$phpExtensions'. (undefined)

(UnusedFormalParameter)


146-146: Avoid unused local variables such as '$temp'. (undefined)

(UnusedLocalVariable)

🔇 Additional comments (2)
emhttp/plugins/extract_translations.php (2)

252-288: LGTM! Proper .po format with backslash escaping.

The .po file generation correctly:

  • Includes proper headers with charset encoding
  • Sorts translations alphabetically for consistency
  • Escapes backslashes and special characters (line 275 correctly includes \\ in the escape list)
  • Includes location references for each translation
  • Handles file write errors

290-352: LGTM! Clear execution flow with helpful progress reporting.

The main execution properly:

  • Scans the directory tree for translation strings
  • Reports detailed statistics for both JavaScript and PHP/HTML files
  • Generates separate .po files for each language category
  • Provides sample output and error messages
  • Handles cases where no translations are found

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (4)
emhttp/plugins/extract_translations.php (4)

154-154: Remove unused variable $temp.

The $temp variable in preg_match_all('/[\/\^\$\*\+\?\{\}\[\]\\\|]/', $text, $temp) is never used. The return value (match count) is already assigned to $specialChars.

Apply this diff:

-            $specialChars = preg_match_all('/[\/\^\$\*\+\?\{\}\[\]\\\|]/', $text, $temp);
+            $specialChars = preg_match_all('/[\/\^\$\*\+\?\{\}\[\]\\\|]/', $text);

Note: A past review comment indicated this was addressed in commit 4312b66, but the issue still appears in the current code.

Based on static analysis hints.


30-30: Pattern doesn't handle escaped quotes.

The pattern (?:(?!\1).)* will fail on strings containing escaped quotes, such as _("text with \" escaped"). The negative lookahead stops at any occurrence of the quote character, even if it's escaped.

Apply this diff to handle escaped quotes:

-$pattern2 = '/_\((["\'])((?:(?!\1).)*)\1\)/s';
+$pattern2 = '/_\((["\'])((?:[^\\\\]|\\\\.)*)?\1\)/s';

This pattern matches either non-backslash characters or a backslash followed by any character, allowing proper handling of escaped quotes.


33-33: Pattern doesn't handle escaped quotes.

Pattern 3 has the same issue as Pattern 2. It will fail on strings containing escaped quotes, such as tr("text with \" escaped").

Apply this diff to handle escaped quotes:

-$pattern3 = '/\btr\((["\'])((?:(?!\1).)*)\1\)/s';
+$pattern3 = '/\btr\((["\'])((?:[^\\\\]|\\\\.)*)?\1\)/s';

52-52: Add error handling for scandir failure.

scandir() returns false on error, which would cause a fatal error in the foreach loop. Add error handling to gracefully skip unreadable directories.

Apply this diff:

 function scanDirectory($dir, $extensions, $jsExtensions, $phpExtensions, $pattern1, $pattern2, $pattern3, &$translationsJS, &$translationsPHP, &$fileCountJS, &$fileCountPHP) {
-    $items = scandir($dir);
+    $items = @scandir($dir);
+    if ($items === false) {
+        echo "Warning: Could not read directory: $dir\n";
+        return;
+    }
     
     foreach ($items as $item) {
🧹 Nitpick comments (2)
emhttp/plugins/extract_translations.php (2)

77-77: Remove unused parameter $phpExtensions.

The parameter $phpExtensions is declared but never used in the function body. The code determines file type by checking if the extension is in $jsExtensions, making $phpExtensions unnecessary.

Apply this diff:

-function processFile($filePath, $ext, $jsExtensions, $phpExtensions, $pattern1, $pattern2, $pattern3, &$translationsJS, &$translationsPHP, &$fileCountJS, &$fileCountPHP) {
+function processFile($filePath, $ext, $jsExtensions, $pattern1, $pattern2, $pattern3, &$translationsJS, &$translationsPHP, &$fileCountJS, &$fileCountPHP) {

Also update the call sites at lines 68 and 63 to remove the $phpExtensions argument.

Based on static analysis hints.


179-255: Consider consolidating duplicate pattern processing functions.

Functions processPattern2 and processPattern3 are nearly identical (differing only in comments). This duplication increases maintenance burden.

Consider refactoring into a single function:

function processQuotedPattern($filePath, $content, $pattern, &$translations, $patternName) {
    $matches = [];
    $count = preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE);
    $foundCount = 0;
    
    if ($count > 0) {
        // Group 2 contains the actual text (group 1 is the quote type)
        foreach ($matches[2] as $match) {
            $text = trim($match[0]);
            $offset = $match[1];
            
            // Calculate line number from offset
            $beforeMatch = substr($content, 0, $offset);
            $lineNumber = substr_count($beforeMatch, "\n") + 1;
            
            // Text is already without quotes (captured between quotes)
            // Just unescape any escaped quotes
            $text = stripslashes($text);
            
            // Skip empty strings
            if (empty($text)) {
                continue;
            }
            
            // Store the translation with its location and line number
            if (!isset($translations[$text])) {
                $translations[$text] = [];
            }
            $translations[$text][] = $filePath . ':' . $lineNumber;
            $foundCount++;
        }
    }
    
    return $foundCount;
}

Then call it for both patterns 2 and 3.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4312b66 and 5d3b4ec.

📒 Files selected for processing (2)
  • emhttp/plugins/extract_translations.php (1 hunks)
  • emhttp/plugins/translations_js.po (1 hunks)
🧰 Additional context used
🪛 PHPMD (2.15.0)
emhttp/plugins/extract_translations.php

77-77: Avoid unused parameters such as '$phpExtensions'. (undefined)

(UnusedFormalParameter)


154-154: Avoid unused local variables such as '$temp'. (undefined)

(UnusedLocalVariable)

🔇 Additional comments (1)
emhttp/plugins/extract_translations.php (1)

283-283: Backslash escaping correctly implemented.

The addcslashes() call now includes backslash in the escape list ("\\\"\n\r\t"), which properly escapes backslashes as \\ in the .po file format. This addresses the concern from previous reviews.

However, note that the absolute paths issue flagged in translations_js.po originates here—consider making paths relative to $baseDir before writing location references.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
emhttp/plugins/extract_translations.php (1)

52-72: Add error handling for scandir failure.

The scandir() call on Line 52 can return false on error (permission denied, directory doesn't exist, etc.), which would cause a fatal error in the subsequent foreach loop.

Apply this diff to add error handling:

 function scanDirectory($dir, $extensions, $jsExtensions, $phpExtensions, $pattern1, $pattern2, $pattern3, &$translationsJS, &$translationsPHP, &$fileCountJS, &$fileCountPHP) {
-    $items = scandir($dir);
+    $items = @scandir($dir);
+    if ($items === false) {
+        echo "Warning: Could not read directory: $dir\n";
+        return;
+    }
     
     foreach ($items as $item) {
🧹 Nitpick comments (1)
emhttp/plugins/extract_translations.php (1)

154-154: Remove unused match variable.

The $temp variable in the preg_match_all() call is never used. You can simplify this by removing the third parameter since you only need the match count.

Apply this diff:

-            $specialChars = preg_match_all('/[\/\^\$\*\+\?\{\}\[\]\\\|]/', $text, $temp);
+            $specialChars = preg_match_all('/[\/\^\$\*\+\?\{\}\[\]\\\|]/', $text);

Based on static analysis hints.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 19b3b24 and 4bdcdc9.

📒 Files selected for processing (10)
  • emhttp/plugins/dynamix.docker.manager/scripts/dockerupdate (1 hunks)
  • emhttp/plugins/dynamix.plugin.manager/scripts/languagecheck (1 hunks)
  • emhttp/plugins/dynamix.plugin.manager/scripts/plugincheck (1 hunks)
  • emhttp/plugins/dynamix/ArrayOperation.page (6 hunks)
  • emhttp/plugins/dynamix/DashStats.page (5 hunks)
  • emhttp/plugins/dynamix/include/DashboardApps.php (2 hunks)
  • emhttp/plugins/dynamix/nchan/device_list (1 hunks)
  • emhttp/plugins/dynamix/nchan/update_2 (1 hunks)
  • emhttp/plugins/extract_translations.php (1 hunks)
  • emhttp/plugins/translations_js.po (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • emhttp/plugins/translations_js.po
  • emhttp/plugins/dynamix/DashStats.page
🧰 Additional context used
🪛 PHPMD (2.15.0)
emhttp/plugins/extract_translations.php

77-77: Avoid unused parameters such as '$phpExtensions'. (undefined)

(UnusedFormalParameter)


154-154: Avoid unused local variables such as '$temp'. (undefined)

(UnusedLocalVariable)

🔇 Additional comments (8)
emhttp/plugins/dynamix/nchan/update_2 (1)

396-396: LGTM! Positional placeholders improve translation flexibility.

The change from sequential %s to positional %1s, %2s placeholders allows translators to reorder arguments based on target language grammar requirements, which is a best practice for internationalization.

emhttp/plugins/dynamix.plugin.manager/scripts/languagecheck (1)

49-49: LGTM! Consistent translation improvement.

The positional placeholder update is consistent with the broader i18n improvements across the codebase.

emhttp/plugins/dynamix.plugin.manager/scripts/plugincheck (1)

52-52: LGTM! Consistent with project-wide i18n improvements.

emhttp/plugins/dynamix/include/DashboardApps.php (1)

36-36: LGTM! Positional placeholders enhance translation flexibility.

Both the PTP and NTP synchronization messages now use positional placeholders (%1s, %2s, %3s), allowing translators to reorder the server count/type, offset value, and unit based on target language requirements.

Also applies to: 52-52

emhttp/plugins/dynamix.docker.manager/scripts/dockerupdate (1)

61-61: LGTM! Consistent notification format improvement.

emhttp/plugins/dynamix/nchan/device_list (1)

81-81: LGTM! Proper translation string extraction.

The change from inline variable interpolation to sprintf with placeholder ensures the complete sentence is available for translation while maintaining dynamic content.

emhttp/plugins/dynamix/ArrayOperation.page (1)

88-89: LGTM! Translation function properly wraps return values.

The resync() function now returns translated strings directly, which is cleaner and ensures consistent translation handling.

emhttp/plugins/extract_translations.php (1)

283-283: Backslash escaping looks correct.

The addcslashes() call properly includes backslash (\\) in the escape list, which addresses the previous review concern about .po format requirements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (5)
emhttp/plugins/extract_translations.php (5)

151-172: Remove unused $temp and skip empty strings for Pattern 1.

Avoid unused var and prevent empty msgids.

-            $text = trim($match[0]);
+            $text = trim($match[0]);
@@
-            // Remove surrounding quotes if present
+            // Remove surrounding quotes if present
             if ((substr($text, 0, 1) === '"' && substr($text, -1) === '"') ||
                 (substr($text, 0, 1) === "'" && substr($text, -1) === "'")) {
                 $text = substr($text, 1, -1);
             }
+            // Skip empty strings
+            if ($text === '') {
+                continue;
+            }
@@
-            $specialChars = preg_match_all('/[\/\^\$\*\+\?\{\}\[\]\\\|]/', $text, $temp);
+            $specialChars = preg_match_all('/[\/\^\$\*\+\?\{\}\[\]\\\|]/', $text);

As per static analysis hints.


26-28: Pattern 1 still doesn’t handle nested parentheses.

Your regex allows escaped parens but not balanced nesting (e.g., (Text (inner) more)). Use PCRE subpattern recursion to properly match balanced parentheses.

Apply:

-// Pattern 1: Match _(text)_ - handles escaped characters and nested content
-$pattern1 = '/_\(((?:[^)]|\\.)*?)\)_/s';
+// Pattern 1: Match _(text)_ with balanced parentheses and escapes
+$pattern1 = '/_\(((?:[^()\\\\]|\\\\.|(?1))*)\)_/s';

29-31: Pattern 2 breaks on escaped quotes.

It stops at the first quote, even if escaped (e.g., _("a " quote")). Allow backslash-escaped characters.

-$pattern2 = '/_\((["\'])((?:(?!\1).)*)\1\)/s';
+$pattern2 = '/_\((["\'])((?:[^\\\\]|\\\\.)*)?\1\)/s';

32-34: Pattern 3 has the same escaped-quote issue as Pattern 2.

Make it escape-aware.

-$pattern3 = '/\btr\((["\'])((?:(?!\1).)*)\1\)/s';
+$pattern3 = '/\btr\((["\'])((?:[^\\\\]|\\\\.)*)?\1\)/s';

52-73: Guard against scandir() failure to avoid warnings/fatals.

scandir() can return false; the foreach then emits warnings. Add a check and continue.

-    $items = scandir($dir);
+    $items = @scandir($dir);
+    if ($items === false) {
+        echo "Warning: Could not read directory: $dir\n";
+        return;
+    }
🧹 Nitpick comments (4)
emhttp/plugins/extract_translations.php (4)

51-53: Remove unused $phpExtensions parameter (and its call sites).

It isn’t used inside processFile, and PHPMD flags it. Simplify signatures and invocations.

-function scanDirectory($dir, $extensions, $jsExtensions, $phpExtensions, $pattern1, $pattern2, $pattern3, &$translationsJS, &$translationsPHP, &$fileCountJS, &$fileCountPHP) {
+function scanDirectory($dir, $extensions, $jsExtensions, $pattern1, $pattern2, $pattern3, &$translationsJS, &$translationsPHP, &$fileCountJS, &$fileCountPHP) {
@@
-            scanDirectory($path, $extensions, $jsExtensions, $phpExtensions, $pattern1, $pattern2, $pattern3, $translationsJS, $translationsPHP, $fileCountJS, $fileCountPHP);
+            scanDirectory($path, $extensions, $jsExtensions, $pattern1, $pattern2, $pattern3, $translationsJS, $translationsPHP, $fileCountJS, $fileCountPHP);
@@
-function processFile($filePath, $ext, $jsExtensions, $phpExtensions, $pattern1, $pattern2, $pattern3, &$translationsJS, &$translationsPHP, &$fileCountJS, &$fileCountPHP) {
+function processFile($filePath, $ext, $jsExtensions, $pattern1, $pattern2, $pattern3, &$translationsJS, &$translationsPHP, &$fileCountJS, &$fileCountPHP) {
@@
-                processFile($path, $ext, $jsExtensions, $phpExtensions, $pattern1, $pattern2, $pattern3, $translationsJS, $translationsPHP, $fileCountJS, $fileCountPHP);
+                processFile($path, $ext, $jsExtensions, $pattern1, $pattern2, $pattern3, $translationsJS, $translationsPHP, $fileCountJS, $fileCountPHP);
@@
-scanDirectory($baseDir, $fileExtensions, $jsExtensions, $phpExtensions, $pattern1, $pattern2, $pattern3, $translationsJS, $translationsPHP, $fileCountJS, $fileCountPHP);
+scanDirectory($baseDir, $fileExtensions, $jsExtensions, $pattern1, $pattern2, $pattern3, $translationsJS, $translationsPHP, $fileCountJS, $fileCountPHP);

As per static analysis hints.

Also applies to: 63-69, 77-79, 319-319


296-304: Sort location references for deterministic .po diffs.

Stable ordering reduces churn between runs.

-        foreach (array_unique($locations) as $location) {
+        $locations = array_values(array_unique($locations));
+        sort($locations, SORT_STRING);
+        foreach ($locations as $location) {
             $content .= "#: " . $location . "\n";
         }

282-289: Optional: enrich .po header (Language, Plural-Forms).

Helps translators and tools; harmless defaults can be added.

     $content .= "msgid \"\"\n";
     $content .= "msgstr \"\"\n";
     $content .= "\"Content-Type: text/plain; charset=UTF-8\\n\"\n";
     $content .= "\"Content-Transfer-Encoding: 8bit\\n\"\n\n";
+    // Optional fields:
+    // $content .= "\"Language: \\n\"\n";
+    // $content .= "\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\n";

61-64: Consider excluding heavy/irrelevant dirs and symlinks.

Skip .git, vendor, node_modules, cache, and symlinked dirs to avoid slow scans or loops.

-        if (is_dir($path)) {
+        if (is_dir($path)) {
+            $base = basename($path);
+            if (is_link($path) || in_array($base, ['.git','vendor','node_modules','cache','tmp','logs'], true)) {
+                continue;
+            }
             // Recursively scan subdirectories
             scanDirectory( /* ... */ );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4bdcdc9 and 923137c.

📒 Files selected for processing (2)
  • emhttp/plugins/extract_translations.php (1 hunks)
  • emhttp/plugins/translations_js.po (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • emhttp/plugins/translations_js.po
🧰 Additional context used
🪛 PHPMD (2.15.0)
emhttp/plugins/extract_translations.php

77-77: Avoid unused parameters such as '$phpExtensions'. (undefined)

(UnusedFormalParameter)


167-167: Avoid unused local variables such as '$temp'. (undefined)

(UnusedLocalVariable)

🔇 Additional comments (1)
emhttp/plugins/extract_translations.php (1)

300-303: Nice fix: backslashes now escaped for .po.

addcslashes includes "\"; .po strings like test\path will serialize as test\path. LGTM.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
emhttp/plugins/dynamix.docker.manager/scripts/dockerupdate (1)

62-62: Consider using positional placeholder for consistency.

Line 62 still uses an unnamed placeholder %s, while line 61 (directly above) uses positional placeholders. For consistency with the broader translation refactoring in this PR, consider updating this to use %1$s.

Apply this diff:

-        $description = str_replace("'","'",sprintf(_("A new version of %s is available"),$name));
+        $description = str_replace("'","'",sprintf(_("A new version of %1$s is available"),$name));
emhttp/plugins/dynamix.plugin.manager/scripts/plugincheck (1)

53-53: Consider using positional placeholder for consistency.

While not critical for single-argument sprintf calls, using %1$s instead of %s would maintain consistency with the positional placeholder pattern introduced on line 52.

Apply this diff if you'd like to maintain consistency:

-    $description = apos(sprintf(_("A new version of %s is available"),$name));
+    $description = apos(sprintf(_("A new version of %1$s is available"),$name));
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 923137c and 1b12c39.

📒 Files selected for processing (8)
  • emhttp/plugins/dynamix.docker.manager/scripts/dockerupdate (1 hunks)
  • emhttp/plugins/dynamix.plugin.manager/scripts/languagecheck (1 hunks)
  • emhttp/plugins/dynamix.plugin.manager/scripts/plugincheck (1 hunks)
  • emhttp/plugins/dynamix/ArrayOperation.page (6 hunks)
  • emhttp/plugins/dynamix/DashStats.page (5 hunks)
  • emhttp/plugins/dynamix/include/DashboardApps.php (2 hunks)
  • emhttp/plugins/dynamix/nchan/update_2 (1 hunks)
  • emhttp/plugins/translations_js.po (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • emhttp/plugins/translations_js.po
🚧 Files skipped from review as they are similar to previous changes (4)
  • emhttp/plugins/dynamix.plugin.manager/scripts/languagecheck
  • emhttp/plugins/dynamix/include/DashboardApps.php
  • emhttp/plugins/dynamix/nchan/update_2
  • emhttp/plugins/dynamix/ArrayOperation.page
🔇 Additional comments (3)
emhttp/plugins/dynamix.docker.manager/scripts/dockerupdate (1)

61-61: LGTM! Positional placeholders improve i18n flexibility.

The migration from unnamed placeholders to positional placeholders (%1$s, %2$s) correctly maintains the argument order and enables translators to reorder parameters based on grammatical requirements in different languages.

emhttp/plugins/dynamix.plugin.manager/scripts/plugincheck (1)

52-52: LGTM! Positional placeholders improve translation flexibility.

The change from sequential %s to positional %1$s and %2$s is correct and allows translators to reorder arguments based on the target language's grammatical structure.

emhttp/plugins/dynamix/DashStats.page (1)

488-488: LGTM! Translation refactoring follows i18n best practices.

The changes correctly refactor translation strings to use sprintf with placeholders instead of direct variable interpolation. The use of positional placeholders (%1$s, %2$s, %3$s) is appropriate for internationalization, as it allows translators to reorder arguments based on the grammar rules of different languages.

Also applies to: 902-902, 981-981, 1105-1105, 1150-1150

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
emhttp/plugins/dynamix/PHPsettings.page (1)

33-33: Grammatical improvement looks good; consider addressing comma splice.

The correction from "exists" to "exist" properly fixes the subject-verb agreement with the plural subject "errors."

However, the sentence contains a comma splice (two independent clauses joined only by a comma). Consider one of these alternatives for improved clarity:

-The LOG file can quickly grow large when multiple errors exist, it is advised to use it only during development and use the default setting under normal running conditions.
+The LOG file can quickly grow large when multiple errors exist; it is advised to use it only during development and use the default setting under normal running conditions.

Or split into two sentences:

-The LOG file can quickly grow large when multiple errors exist, it is advised to use it only during development and use the default setting under normal running conditions.
+The LOG file can quickly grow large when multiple errors exist. It is advised to use it only during development and use the default setting under normal running conditions.
emhttp/plugins/dynamix.vm.manager/VMTemplates.page (1)

76-76: Consider moving punctuation inside translation markers.

The pattern "_(Error occurred)_?" places the question mark outside the translation marker. For better internationalization, consider moving punctuation inside: "_(Error occurred?)_". This allows translators to adjust punctuation according to their language conventions.

Note: This pattern exists elsewhere in the file (lines 67, 73), so addressing it would require broader changes beyond this PR.

Also applies to: 80-80, 93-93, 98-98

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7dd858 and eb5aa36.

📒 Files selected for processing (5)
  • emhttp/plugins/dynamix.vm.manager/VMTemplates.page (2 hunks)
  • emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php (3 hunks)
  • emhttp/plugins/dynamix/PHPsettings.page (1 hunks)
  • emhttp/plugins/dynamix/include/Markdown.php (1 hunks)
  • emhttp/plugins/dynamix/include/MarkdownExtra.php (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • emhttp/plugins/dynamix/include/Markdown.php
  • emhttp/plugins/dynamix/include/MarkdownExtra.php
🚧 Files skipped from review as they are similar to previous changes (1)
  • emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php
🔇 Additional comments (1)
emhttp/plugins/dynamix.vm.manager/VMTemplates.page (1)

76-76: LGTM! Spelling corrections are accurate.

The spelling fix from "Error occured" to "Error occurred" improves the quality of user-facing error messages. The changes are applied consistently across all error dialog instances.

Also applies to: 80-80, 93-93, 98-98

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
emhttp/plugins/dynamix/ArrayOperation.page (1)

681-684: Make placeholders positional to match identical messages.

Line 683 still uses %s/%s while lines 652 and 678 use %1$s/%2$s. Align for consistency and translator control.

Apply this diff:

-    <td><?=sprintf(_('Current operation %s on **%s**'),_('started'),_(my_time(_var($var,'sbUpdated',0)).day_count(_var($var,'sbSynced',0)),0))?></td></tr>
+    <td><?=sprintf(_('Current operation %1$s on **%2$s**'),_('started'),_(my_time(_var($var,'sbUpdated',0)).day_count(_var($var,'sbSynced',0)),0))?></td></tr>

Find any remaining occurrences:

#!/bin/bash
rg -nP "Current operation %s on \*\*%s\*\*"
🧹 Nitpick comments (1)
emhttp/plugins/dynamix/ArrayOperation.page (1)

92-94: Use proper pluralization instead of appending “s”.

Manual “error(s)” breaks i18n (plural rules differ). Prefer plural APIs or distinct keys.

If ngettext is available:

-function print_error($error) {
-  return sprintf(_('Finding **%s** error'.($error==1?'':'s')),$error?:'0');
-}
+function print_error($error) {
+  $n = (int)($error ?: 0);
+  return sprintf(ngettext(_('Finding **%s** error'), _('Finding **%s** errors'), $n), $n);
+}

If not, choose keys conditionally:

function print_error($error) {
  $n = (int)($error ?: 0);
  $fmt = ($n === 1) ? _('Finding **%s** error') : _('Finding **%s** errors');
  return sprintf($fmt, $n);
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb5aa36 and 747c98b.

📒 Files selected for processing (8)
  • emhttp/plugins/dynamix.docker.manager/include/CreateDocker.php (1 hunks)
  • emhttp/plugins/dynamix.vm.manager/VMTemplates.page (2 hunks)
  • emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php (4 hunks)
  • emhttp/plugins/dynamix/ArrayOperation.page (9 hunks)
  • emhttp/plugins/dynamix/DashStats.page (10 hunks)
  • emhttp/plugins/dynamix/DeviceInfo.page (3 hunks)
  • emhttp/plugins/dynamix/NewPerms.page (1 hunks)
  • emhttp/plugins/dynamix/SysDrivers.page (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • emhttp/plugins/dynamix/DeviceInfo.page
  • emhttp/plugins/dynamix/SysDrivers.page
🚧 Files skipped from review as they are similar to previous changes (3)
  • emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php
  • emhttp/plugins/dynamix.vm.manager/VMTemplates.page
  • emhttp/plugins/dynamix/DashStats.page
🔇 Additional comments (6)
emhttp/plugins/dynamix/NewPerms.page (1)

51-51: Localization improvement: question mark now part of translatable string.

Moving the question mark inside the translation marker provides better context for translators and allows them to adjust punctuation according to target language conventions.

emhttp/plugins/dynamix/ArrayOperation.page (5)

88-91: LGTM: localized resync strings.

Using _('Parity-Sync') / _('Data-Rebuild') is correct and extractor-friendly.


569-573: LGTM: placeholder-based formatting.

sprintf(_("will start **%s**"), $resync) improves translatability.


616-624: LGTM: consistent placeholder usage for progress/pause/resume/stop.

Clear, translator-friendly strings.


650-653: LGTM: positional placeholders for reordering.

%1$s/%2$s is the right choice for i18n.


676-679: LGTM: placeholder consistency maintained.

Matches pattern used elsewhere.

Comment on lines 821 to 824
function rmTemplate(tmpl) {
var name = tmpl.split(/[\/]+/).pop();
swal({title:"_(Are you sure)_?",text:"_(Remove template)_: "+name,type:"warning",html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(){$("#rmTemplate").val(tmpl);$("#formTemplate1").submit();});
swal({title:"_(Are you sure?)_",text:"_(Remove template)_: "+name,type:"warning",html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(){$("#rmTemplate").val(tmpl);$("#formTemplate1").submit();});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Escape template name before injecting into SweetAlert HTML.

This swal uses html:true and concatenates the unescaped name into text. If tmpl/name ever contains </& etc., this can render as HTML. Encode it first.

Apply this diff:

 function rmTemplate(tmpl) {
   var name = tmpl.split(/[\/]+/).pop();
-  swal({title:"_(Are you sure?)_",text:"_(Remove template)_: "+name,type:"warning",html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(){$("#rmTemplate").val(tmpl);$("#formTemplate1").submit();});
+  var safeName = $('<div/>').text(name).html(); // encode for HTML
+  swal({title:"_(Are you sure?)_",text:"_(Remove template)_: "+safeName,type:"warning",html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(){$("#rmTemplate").val(tmpl);$("#formTemplate1").submit();});

Optionally, drop html:true here if no formatting is needed.

Scan for other swal calls with html:true concatenating variables:


🏁 Script executed:

#!/bin/bash
rg -nP "swal\([^)]*html\s*:\s*true" -C2

Length of output: 4008


🏁 Script executed:

#!/bin/bash
# Verify the exact location of rmTemplate in CreateDocker.php
rg -nP "function\s+rmTemplate" emhttp/plugins/dynamix.docker.manager/

Length of output: 78


🏁 Script executed:

#!/bin/bash
# Search for CreateDocker.php anywhere in the repo
fd -t f "CreateDocker.php" 2>/dev/null || echo "File not found"

Length of output: 62


🏁 Script executed:

#!/bin/bash
# Extract lines 821-824 from CreateDocker.php
sed -n '821,824p' emhttp/plugins/dynamix.docker.manager/include/CreateDocker.php

Length of output: 318


🏁 Script executed:

#!/bin/bash
# Check if CreateDocker.php includes or references addDocker.js
grep -n "addDocker\.js" emhttp/plugins/dynamix.docker.manager/include/CreateDocker.php

Length of output: 0


Escape template name before injecting into SweetAlert HTML in both locations.

The rmTemplate function concatenates an unescaped name variable into the swal text field while html:true is enabled. This creates an XSS vulnerability if tmpl/name contains HTML characters like < or &.

Apply this fix to both:

  1. emhttp/plugins/dynamix.docker.manager/include/CreateDocker.php (lines 821-824):
 function rmTemplate(tmpl) {
   var name = tmpl.split(/[\/]+/).pop();
-  swal({title:"_(Are you sure?)_",text:"_(Remove template)_: "+name,type:"warning",html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(){$("#rmTemplate").val(tmpl);$("#formTemplate1").submit();});
+  var safeName = $('<div/>').text(name).html(); // encode for HTML
+  swal({title:"_(Are you sure?)_",text:"_(Remove template)_: "+safeName,type:"warning",html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(){$("#rmTemplate").val(tmpl);$("#formTemplate1").submit();});
 }
  1. emhttp/plugins/dynamix.docker.manager/javascript/addDocker.js (line 17):
 function rmTemplate(tmpl) {
 	var name = tmpl.split(/[\/]+/).pop();
-	swal({title:"Are you sure?",text:"Remove template: "+name,type:"warning",html:true,showCancelButton:true},function(){$("#rmTemplate").val(tmpl);$("#formTemplate").submit();});
+	var safeName = $('<div/>').text(name).html();
+	swal({title:"Are you sure?",text:"Remove template: "+safeName,type:"warning",html:true,showCancelButton:true},function(){$("#rmTemplate").val(tmpl);$("#formTemplate").submit();});
 }
🤖 Prompt for AI Agents
emhttp/plugins/dynamix.docker.manager/include/CreateDocker.php (lines 821-824)
and emhttp/plugins/dynamix.docker.manager/javascript/addDocker.js (line 17): the
code injects the raw template name into swal text while html:true is enabled,
creating an XSS risk; update both locations to HTML-escape the name before
concatenation (replace &, <, >, " and ' with their HTML entities) or remove
html:true and pass the name via swal's text-only path so user input is not
interpreted as HTML, then use the escaped value in the swal call and submit
logic.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
emhttp/plugins/dynamix/ArrayOperation.page (1)

684-684: Inconsistent placeholder style remains unaddressed.

Line 684 still uses sequential placeholders (%s, %s) for the "Current operation" message, while lines 653 and 679 use positional placeholders (%1$s, %2$s) for the same message pattern. For consistency and maintainability, all three lines should use the same placeholder style.

Apply this diff to make Line 684 consistent:

-    <td><?=sprintf(_('Current operation %s on **%s**'),_('started'),_(my_time(_var($var,'sbUpdated',0)).day_count(_var($var,'sbSynced',0)),0))?></td></tr>
+    <td><?=sprintf(_('Current operation %1$s on **%2$s**'),_('started'),_(my_time(_var($var,'sbUpdated',0)).day_count(_var($var,'sbSynced',0)),0))?></td></tr>
emhttp/plugins/extract_translations.php (2)

176-176: Remove unused variable (previously reported as fixed).

The $temp variable in the preg_match_all call is unused—only the match count is needed. This was reportedly addressed in a previous commit but is still present in the code.

Apply this diff:

-            $specialChars = preg_match_all('/[\/\^\$\*\+\?\{\}\[\]\\\|]/', $text, $temp);
+            $specialChars = preg_match_all('/[\/\^\$\*\+\?\{\}\[\]\\\|]/', $text);

57-78: Add error handling for directory scanning failures.

If scandir() fails (e.g., due to permission issues), it returns false, which will cause a fatal error in the foreach loop on Line 60. Add error handling to gracefully skip unreadable directories.

Apply this diff:

 function scanDirectory($dir, $extensions, $jsExtensions, $phpExtensions, $pattern1, $pattern2, $pattern3, $pattern4, &$translationsJS, &$translationsPHP, &$pluralsJS, &$pluralsPHP, &$fileCountJS, &$fileCountPHP) {
-    $items = scandir($dir);
+    $items = @scandir($dir);
+    if ($items === false) {
+        echo "Warning: Could not read directory: $dir\n";
+        return;
+    }
     
     foreach ($items as $item) {
🧹 Nitpick comments (2)
emhttp/plugins/extract_translations.php (2)

28-37: Consider more robust regex patterns for edge cases.

The current patterns have limitations with nested parentheses and escaped quotes (as noted in previous reviews):

  • Pattern 1 (Line 28): May fail on strings with nested unescaped parentheses like _(outer (inner))_
  • Patterns 2-4 (Lines 31, 34, 37): Don't handle escaped quotes like _("text with \" escaped")

These edge cases may be rare in practice, but consider using more robust patterns if translation strings become more complex. For example, Pattern 2-4 could use ((?:[^\\\\]|\\\\.)*)?\1 to handle escaped characters.


83-136: Consider removing unused parameter.

The $phpExtensions parameter is never used within this function (it's only used in scanDirectory). Consider removing it from the function signature to reduce noise.

Apply this diff:

-function processFile($filePath, $ext, $jsExtensions, $phpExtensions, $pattern1, $pattern2, $pattern3, $pattern4, &$translationsJS, &$translationsPHP, &$pluralsJS, &$pluralsPHP, &$fileCountJS, &$fileCountPHP) {
+function processFile($filePath, $ext, $jsExtensions, $pattern1, $pattern2, $pattern3, $pattern4, &$translationsJS, &$translationsPHP, &$pluralsJS, &$pluralsPHP, &$fileCountJS, &$fileCountPHP) {

And update the call site on Line 74:

-                processFile($path, $ext, $jsExtensions, $phpExtensions, $pattern1, $pattern2, $pattern3, $pattern4, $translationsJS, $translationsPHP, $pluralsJS, $pluralsPHP, $fileCountJS, $fileCountPHP);
+                processFile($path, $ext, $jsExtensions, $pattern1, $pattern2, $pattern3, $pattern4, $translationsJS, $translationsPHP, $pluralsJS, $pluralsPHP, $fileCountJS, $fileCountPHP);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 26a56a4 and 8d08075.

📒 Files selected for processing (24)
  • emhttp/plugins/dynamix.docker.manager/include/CreateDocker.php (1 hunks)
  • emhttp/plugins/dynamix.docker.manager/include/DockerContainers.php (1 hunks)
  • emhttp/plugins/dynamix.docker.manager/scripts/dockerupdate (1 hunks)
  • emhttp/plugins/dynamix.plugin.manager/scripts/languagecheck (1 hunks)
  • emhttp/plugins/dynamix.plugin.manager/scripts/plugincheck (1 hunks)
  • emhttp/plugins/dynamix.vm.manager/VMTemplates.page (2 hunks)
  • emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php (1 hunks)
  • emhttp/plugins/dynamix.vm.manager/scripts/VMAjaxCall.php (1 hunks)
  • emhttp/plugins/dynamix.vm.manager/scripts/VMClone.php (1 hunks)
  • emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php (4 hunks)
  • emhttp/plugins/dynamix/ArrayOperation.page (9 hunks)
  • emhttp/plugins/dynamix/DashStats.page (10 hunks)
  • emhttp/plugins/dynamix/DeviceInfo.page (3 hunks)
  • emhttp/plugins/dynamix/NewPerms.page (1 hunks)
  • emhttp/plugins/dynamix/PHPsettings.page (1 hunks)
  • emhttp/plugins/dynamix/SysDrivers.page (1 hunks)
  • emhttp/plugins/dynamix/include/CPUset.php (1 hunks)
  • emhttp/plugins/dynamix/include/DashboardApps.php (2 hunks)
  • emhttp/plugins/dynamix/include/Markdown.php (1 hunks)
  • emhttp/plugins/dynamix/include/MarkdownExtra.php (1 hunks)
  • emhttp/plugins/dynamix/nchan/device_list (1 hunks)
  • emhttp/plugins/dynamix/nchan/update_2 (1 hunks)
  • emhttp/plugins/extract_translations.php (1 hunks)
  • emhttp/plugins/translations_js.po (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • emhttp/plugins/dynamix.docker.manager/include/CreateDocker.php
  • emhttp/plugins/dynamix/include/MarkdownExtra.php
  • emhttp/plugins/dynamix/include/Markdown.php
🚧 Files skipped from review as they are similar to previous changes (14)
  • emhttp/plugins/dynamix.docker.manager/include/DockerContainers.php
  • emhttp/plugins/dynamix/DeviceInfo.page
  • emhttp/plugins/dynamix.vm.manager/scripts/VMClone.php
  • emhttp/plugins/dynamix/include/DashboardApps.php
  • emhttp/plugins/dynamix.plugin.manager/scripts/plugincheck
  • emhttp/plugins/dynamix/NewPerms.page
  • emhttp/plugins/dynamix.vm.manager/scripts/VMAjaxCall.php
  • emhttp/plugins/dynamix/PHPsettings.page
  • emhttp/plugins/dynamix/nchan/device_list
  • emhttp/plugins/dynamix/nchan/update_2
  • emhttp/plugins/dynamix.plugin.manager/scripts/languagecheck
  • emhttp/plugins/dynamix/DashStats.page
  • emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php
  • emhttp/plugins/translations_js.po
🧰 Additional context used
🧬 Code graph analysis (1)
emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php (2)
emhttp/plugins/dynamix.vm.manager/scripts/VMAjaxCall.php (1)
  • write (26-30)
emhttp/plugins/dynamix.vm.manager/scripts/VMClone.php (1)
  • write (26-30)
🪛 PHPMD (2.15.0)
emhttp/plugins/extract_translations.php

83-83: Avoid unused parameters such as '$phpExtensions'. (undefined)

(UnusedFormalParameter)


176-176: Avoid unused local variables such as '$temp'. (undefined)

(UnusedLocalVariable)

🔇 Additional comments (14)
emhttp/plugins/dynamix.vm.manager/include/libvirt_helpers.php (1)

1842-1842: LGTM! Translation formatting improvement.

The change from string concatenation to sprintf with a placeholder is a proper i18n best practice. This allows translators to adjust word order and properly localize the error message while maintaining the dynamic error value.

emhttp/plugins/dynamix.vm.manager/VMTemplates.page (3)

67-67: LGTM: Punctuation moved inside translation wrapper.

Moving the question mark inside the translation wrapper is correct for localization best practices, allowing translators to adjust punctuation based on target language conventions.


73-73: LGTM: Consistent punctuation cleanup.

Removing the external question marks from the "File exists" dialog titles improves translation consistency and provides cleaner strings for localization.

Also applies to: 90-90


76-76: LGTM: Spelling error corrected.

The spelling fix from "occured" to "occurred" is correct, and the punctuation cleanup on lines 76 and 80 is consistent with the localization improvements throughout the file.

Also applies to: 80-80, 98-98

emhttp/plugins/dynamix/ArrayOperation.page (4)

88-90: LGTM! Good translation practice.

The function now correctly returns translated strings, aligning with the PR's localization objectives.


92-95: LGTM! Proper plural-aware translation.

Using ngettext correctly handles singular/plural forms across different languages, which is essential for proper internationalization.


572-572: LGTM! Correct sprintf usage for dynamic translations.

These lines properly use sprintf to interpolate dynamic content into translated strings, following best practices for localization.

Also applies to: 619-619, 624-624, 718-718


653-653: LGTM! Consistent positional placeholders.

These lines correctly use explicit positional placeholders (%1$s, %2$s), which is portable across PHP versions and aligns with the past review recommendations.

Also applies to: 679-679

emhttp/plugins/dynamix.docker.manager/scripts/dockerupdate (1)

61-61: LGTM! Good localization practice.

Using explicit positional placeholders (%1$s, %2$s) instead of implicit ordering improves translation flexibility, allowing translators to reorder arguments based on language-specific grammar rules.

emhttp/plugins/dynamix/SysDrivers.page (1)

82-82: LGTM! Correct punctuation placement for translations.

Moving the question mark inside the translation string is proper localization practice, as different languages may have different punctuation rules and spacing conventions around question marks.

emhttp/plugins/dynamix/include/CPUset.php (1)

88-89: LGTM! Fixes double-translation issue.

Applying the translation function at variable assignment (Line 88) and then outputting the translated value directly (Line 89) is correct. This prevents the previous issue where translation might have been applied twice, which would break the translation lookup.

emhttp/plugins/extract_translations.php (3)

202-328: LGTM! Pattern processing functions are well-structured.

The four pattern processing functions follow a consistent structure with proper:

  • Offset-based line number calculation
  • Appropriate use of stripslashes to handle escaped quotes
  • Empty string filtering
  • Location tracking with file:line format

333-405: LGTM! PO file generation is correct.

The function properly:

  • Generates valid gettext PO headers with appropriate metadata
  • Escapes backslashes and special characters correctly (Line 365, 387-388)
  • Handles both regular translations and plural forms
  • Sorts entries alphabetically for consistency
  • Includes file location references for each translation

407-484: LGTM! Main execution flow is well-organized.

The script provides:

  • Clear progress reporting during scanning
  • Separate statistics for JavaScript and PHP/HTML files
  • Conditional file generation (only when translations exist)
  • Detailed output with sample entries for verification
  • Proper error reporting if file writes fail

if (p) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'vm-template-save',name:name,template:template,replace:"yes"},function($return){
if ($return.success == false) swal({title:"_(Error occured)_?",text:"Action error " + name.split(".")[0] + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
if ($return.success == false) swal({title:"_(Error occurred)_?",text:"Action error " + name.split(".")[0] + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix inconsistent punctuation in error dialog title.

This line still has an external question mark after the translation wrapper, while all other "Error occurred" instances (lines 76, 80, 98) have been updated to remove it. This inconsistency should be corrected for uniform localization.

Apply this diff to fix the inconsistency:

-					if ($return.success == false) swal({title:"_(Error occurred)_?",text:"Action error " + name.split(".")[0] + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
+					if ($return.success == false) swal({title:"_(Error occurred)_",text:"Action error " + name.split(".")[0] + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ($return.success == false) swal({title:"_(Error occurred)_?",text:"Action error " + name.split(".")[0] + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
if ($return.success == false) swal({title:"_(Error occurred)_",text:"Action error " + name.split(".")[0] + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
🤖 Prompt for AI Agents
In emhttp/plugins/dynamix.vm.manager/VMTemplates.page around line 93, the swal
error dialog title uses an inconsistent external question mark after the
translation wrapper ("_(Error occurred)_?"); remove the trailing question mark
so the title matches other instances ("_(Error occurred)_"), i.e., update the
title string to use only the translation wrapper without any added punctuation.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
emhttp/plugins/extract_translations.php (3)

176-176: Remove unused variable $temp.

The $temp variable receives the matches array from preg_match_all() but is never used. Only the return value (match count) is assigned to $specialChars.

Apply this diff:

-            $specialChars = preg_match_all('/[\/\^\$\*\+\?\{\}\[\]\\\|]/', $text, $temp);
+            $specialChars = preg_match_all('/[\/\^\$\*\+\?\{\}\[\]\\\|]/', $text);

Note: This was marked as addressed in a previous review but the code still contains the unused variable.

Based on static analysis hints.


31-34: Patterns fail on escaped quotes within strings.

Both Pattern 2 and Pattern 3 use (?:(?!\1).)* which stops at any occurrence of the closing quote character, even if it's escaped with a backslash. For example, _("text with \" escaped") will only match "text with \" without the closing quote.

Apply this diff to handle escaped quotes:

 // Pattern 2: Match _('text') or _("text") - function call pattern
-$pattern2 = '/_\((["\'])((?:(?!\1).)*)\1\)/s';
+$pattern2 = '/_\((["\'])((?:[^\\\\]|\\\\.)*?)\1\)/s';
 
 // Pattern 3: Match tr('text') or tr("text") - function call pattern
-$pattern3 = '/\btr\((["\'])((?:(?!\1).)*)\1\)/s';
+$pattern3 = '/\btr\((["\'])((?:[^\\\\]|\\\\.)*?)\1\)/s';

This pattern matches either non-backslash characters or a backslash followed by any character (escaped sequences).


57-78: Add error handling for scandir failure.

scandir() returns false on error (e.g., permission denied), which would cause a fatal error in the foreach loop on line 60. This would crash the entire extraction process instead of gracefully skipping unreadable directories.

Apply this diff:

 function scanDirectory($dir, $extensions, $jsExtensions, $phpExtensions, $pattern1, $pattern2, $pattern3, $pattern4, &$translationsJS, &$translationsPHP, &$pluralsJS, &$pluralsPHP, &$fileCountJS, &$fileCountPHP) {
-    $items = scandir($dir);
+    $items = @scandir($dir);
+    if ($items === false) {
+        echo "Warning: Could not read directory: $dir\n";
+        return;
+    }
     
     foreach ($items as $item) {
🧹 Nitpick comments (1)
emhttp/plugins/extract_translations.php (1)

83-136: Remove unused parameter $phpExtensions.

The $phpExtensions parameter is never used in this function. The function only checks if the extension is in $jsExtensions (line 104) to determine the file type; everything else is implicitly treated as PHP.

Apply this diff to remove the unused parameter:

-function processFile($filePath, $ext, $jsExtensions, $phpExtensions, $pattern1, $pattern2, $pattern3, $pattern4, &$translationsJS, &$translationsPHP, &$pluralsJS, &$pluralsPHP, &$fileCountJS, &$fileCountPHP) {
+function processFile($filePath, $ext, $jsExtensions, $pattern1, $pattern2, $pattern3, $pattern4, &$translationsJS, &$translationsPHP, &$pluralsJS, &$pluralsPHP, &$fileCountJS, &$fileCountPHP) {

Then update the call on line 74:

-                processFile($path, $ext, $jsExtensions, $phpExtensions, $pattern1, $pattern2, $pattern3, $pattern4, $translationsJS, $translationsPHP, $pluralsJS, $pluralsPHP, $fileCountJS, $fileCountPHP);
+                processFile($path, $ext, $jsExtensions, $pattern1, $pattern2, $pattern3, $pattern4, $translationsJS, $translationsPHP, $pluralsJS, $pluralsPHP, $fileCountJS, $fileCountPHP);

Based on static analysis hints.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d08075 and 5fbb1f3.

📒 Files selected for processing (3)
  • emhttp/plugins/dynamix/include/Translations.php (2 hunks)
  • emhttp/plugins/extract_translations.php (1 hunks)
  • emhttp/plugins/translations_js.po (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • emhttp/plugins/translations_js.po
🧰 Additional context used
🪛 PHPMD (2.15.0)
emhttp/plugins/extract_translations.php

83-83: Avoid unused parameters such as '$phpExtensions'. (undefined)

(UnusedFormalParameter)


176-176: Avoid unused local variables such as '$temp'. (undefined)

(UnusedLocalVariable)

🔇 Additional comments (1)
emhttp/plugins/dynamix/include/Translations.php (1)

18-21: LGTM: Session lock released early.

Adding session_write_close() immediately after session_start() is a good practice that prevents session file locking from blocking concurrent requests. Since this code only reads session data (the locale), releasing the lock early improves concurrency.

@ljm42 ljm42 added the 7.3 label Nov 5, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 15b430d and 7581e8a.

📒 Files selected for processing (4)
  • emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php (4 hunks)
  • emhttp/plugins/dynamix/ArrayOperation.page (9 hunks)
  • emhttp/plugins/dynamix/DashStats.page (10 hunks)
  • emhttp/plugins/dynamix/DeviceInfo.page (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • emhttp/plugins/dynamix/DeviceInfo.page
  • emhttp/plugins/dynamix.vm.manager/templates/Custom.form.php
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: SimonFair
Repo: unraid/webgui PR: 1992
File: emhttp/plugins/dynamix/include/CPUset.php:0-0
Timestamp: 2025-02-01T15:12:16.914Z
Learning: In the unRAID WebGUI, button text should be wrapped in _() for translation support and htmlspecialchars() for security, with a preference for using ternary operators for simple conditional text assignments.
Learnt from: SimonFair
Repo: unraid/webgui PR: 1992
File: emhttp/plugins/dynamix/include/CPUset.php:0-0
Timestamp: 2025-02-01T15:12:16.914Z
Learning: In the unRAID WebGUI, when both translation and HTML escaping are needed, always apply translation (_()) first, then escape the translated text with htmlspecialchars() to ensure proper handling of translation placeholders.
Learnt from: zackspear
Repo: unraid/webgui PR: 2185
File: emhttp/plugins/dynamix/Shares.page:5-15
Timestamp: 2025-05-08T18:20:16.531Z
Learning: In the Unraid WebGUI codebase, translation strings can use Markdown syntax (like `**text**` for bold) which will be properly parsed and rendered in the UI, rather than requiring HTML tags.
Learnt from: zackspear
Repo: unraid/webgui PR: 2185
File: emhttp/plugins/dynamix.vm.manager/VMs.page:23-23
Timestamp: 2025-05-08T18:20:08.009Z
Learning: In the Unraid webGUI, text within translation strings (_() function) is parsed as Markdown, so Markdown syntax like `**bold**` is appropriate and will be properly rendered.
Learnt from: zackspear
Repo: unraid/webgui PR: 2185
File: emhttp/plugins/dynamix.docker.manager/Docker.page:23-23
Timestamp: 2025-05-08T18:20:24.450Z
Learning: In the Unraid WebGUI codebase, messages within the `_()` translation function support markdown syntax (e.g., `**text**` for bold) which will be properly parsed and rendered when displayed.
Learnt from: Squidly271
Repo: unraid/webgui PR: 2333
File: emhttp/plugins/dynamix/include/publish.php:28-36
Timestamp: 2025-08-13T03:13:53.283Z
Learning: In the unraid/webgui codebase, PR #2333 by Squidly271 supersedes earlier work that already addressed all legacy publish() calls using the old overloaded syntax. There are no remaining legacy calls that need backward compatibility handling in the publish() function.
Learnt from: Squidly271
Repo: unraid/webgui PR: 0
File: :0-0
Timestamp: 2025-05-31T05:10:13.120Z
Learning: Squidly271 prefers not to receive CodeRabbit reviews or feedback while PRs are in draft status. Only provide reviews after PRs are moved out of draft.
Learnt from: Squidly271
Repo: unraid/webgui PR: 0
File: :0-0
Timestamp: 2025-09-03T07:28:01.902Z
Learning: Squidly271's preference for not reviewing drafts is specifically because they find reviews annoying during active development phases with multiple commits, but they do want reviews once the PR is ready and more stable.
📚 Learning: 2025-02-01T15:12:16.914Z
Learnt from: SimonFair
Repo: unraid/webgui PR: 1992
File: emhttp/plugins/dynamix/include/CPUset.php:0-0
Timestamp: 2025-02-01T15:12:16.914Z
Learning: In the unRAID WebGUI, button text should be wrapped in _() for translation support and htmlspecialchars() for security, with a preference for using ternary operators for simple conditional text assignments.

Applied to files:

  • emhttp/plugins/dynamix/DashStats.page
  • emhttp/plugins/dynamix/ArrayOperation.page

$sbUpdate = _var($var,'mdResyncDt') ? $sbSynced : _var($var,'sbUpdated');?>
<tr><td></td><td><input type="button" value="_(History)_" onclick="parityHistory()"></td>
<td><?=sprintf(_('Current operation %s on **%s**'),(_var($var,'mdResyncDt')?_('started'):_('paused')),_(my_time($sbUpdate).day_count($sbUpdate),0))?></td></tr>
<td><?=(_('Current operation %1$s on **%2$s**'),(_var($var,'mdResyncDt')?_('started'):_('paused')),_(my_time($sbUpdate).day_count($sbUpdate),0))?></td></tr>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Missing sprintf call causes placeholder substitution failure.

Line 685 is missing the sprintf call before the translation function, which means the placeholders %1$s and %2$s will not be substituted with the actual values. This will result in the raw translation string with placeholders being displayed to users.

Apply this diff to fix the issue:

-      <td><?=(_('Current operation %1$s on **%2$s**'),(_var($var,'mdResyncDt')?_('started'):_('paused')),_(my_time($sbUpdate).day_count($sbUpdate),0))?></td></tr>
+      <td><?=sprintf(_('Current operation %1$s on **%2$s**'),(_var($var,'mdResyncDt')?_('started'):_('paused')),_(my_time($sbUpdate).day_count($sbUpdate),0))?></td></tr>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<td><?=(_('Current operation %1$s on **%2$s**'),(_var($var,'mdResyncDt')?_('started'):_('paused')),_(my_time($sbUpdate).day_count($sbUpdate),0))?></td></tr>
<td><?=sprintf(_('Current operation %1$s on **%2$s**'),(_var($var,'mdResyncDt')?_('started'):_('paused')),_(my_time($sbUpdate).day_count($sbUpdate),0))?></td></tr>
🤖 Prompt for AI Agents
emhttp/plugins/dynamix/ArrayOperation.page around line 685: the translation call
is passing placeholders but not using sprintf, so %1$s and %2$s are not being
substituted; wrap the translated string in sprintf and pass the two computed
values (the started/paused status and the formatted date string) as arguments to
sprintf so the placeholders are replaced correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants