diff --git a/src/Plugin/Scanner/Node.php b/src/Plugin/Scanner/Node.php
index 4dd0b95da7d02b0d47b345ab59a5a8051f92ef3f..b8744e9422e1892448099f7f07b136511b4c274d 100644
--- a/src/Plugin/Scanner/Node.php
+++ b/src/Plugin/Scanner/Node.php
@@ -40,52 +40,81 @@ class Node extends Entity {
       $conditionVals = parent::buildCondition($values['search'], $values['mode'], $values['wholeword'], $values['regex'], $values['preceded'], $values['followed']);
       $this->addQueryCondition($query, $conditionVals, $fieldname, $values['mode'], $values['language']);
 
-      // Disable the normal access check.
+      // Run Drupal database query.
       $query->accessCheck(FALSE);
+      $nids = $query->execute();
+
+      // Iterate over matched entities (nodes) in all saerched languages
+      // to extract information that will be rendered in the results.
+      $languages = $values['language'] === 'all' ? array_keys(AdminHelper::getAllEnabledLanguages()) : [$values['language']];
+      foreach ($nids as $nid) {
+        foreach ($languages as $language) {
+          // Results key must be unique, accounting for > 1 language.
+          $result_key = $values['language'] === 'all' ? "$nid-$language" : "$nid";
+
+          $node = CoreNode::load($nid);
+          if (!$node->hasTranslation($language)) {
+            continue;
+          }
+          $node = $node->getTranslation($language);
+          $nodeField = $node->get($fieldname);
+          $fieldType = $nodeField->getFieldDefinition()->getType();
+
+          if (in_array($fieldType, ['text_with_summary', 'text', 'text_long'])) {
+            // @todo Add support for fields with cardinality > 1.
+            $fieldValue = $nodeField->getValue()[0] ?? [];
+
+            // Find all instances of the term we're looking for.
+            preg_match_all($conditionVals['phpRegex'], $fieldValue['value'], $matches, PREG_OFFSET_CAPTURE);
+            if (empty($matches) || !isset($matches[0]) || !isset($matches[0][0])) {
+              // Match not guaranteed for nodes with > 1 language.
+              continue;
+            }
 
-      $entities = $query->execute();
-      // Iterate over matched entities (nodes) to extract information that will
-      // be rendered in the results.
-      foreach ($entities as $id) {
-        $node = CoreNode::load($id);
-        $nodeField = $node->get($fieldname);
-        $fieldType = $nodeField->getFieldDefinition()->getType();
-        if (in_array($fieldType, ['text_with_summary', 'text', 'text_long'])) {
-          $fieldValue = $nodeField->getValue()[0];
-          $title_collect[$id]['title'] = $node->getTitle();
-          // Find all instances of the term we're looking for.
-          preg_match_all($conditionVals['phpRegex'], $fieldValue['value'], $matches, PREG_OFFSET_CAPTURE);
-          $newValues = [];
-          // Build an array of strings which are displayed in the results.
-          foreach ($matches[0] as $v) {
-            // The offset of the matched term(s) in the field's text.
-            $start = $v[1];
-            if ($values['preceded'] !== '') {
-              // Bolding won't work if starting position is in the middle of a
-              // word (non-word bounded searches), therefore move the start
-              // position back as many character as there are in the 'preceded'
-              // text.
-              $start -= strlen($values['preceded']);
+            // Build an array of strings which are displayed in the results.
+            $newValues = [];
+            foreach ($matches[0] as $v) {
+              // The offset of the matched term(s) in the field's text.
+              $start = $v[1];
+              if ($values['preceded'] !== '') {
+                // Bolding won't work if starting position is in the middle of
+                // a word (non-word bounded searches), therefore move the start
+                // position back as many character as there are in the
+                // 'preceded' text.
+                $start -= strlen($values['preceded']);
+              }
+              // Extract part of the text which include the search term plus six
+              // "words" following it. After finding the string, bold the search
+              // term.
+              $replaced = preg_replace($conditionVals['phpRegex'], "<strong>$v[0]</strong>", preg_split("/\s+/", substr($fieldValue['value'], $start), 6));
+              if (count($replaced) > 1) {
+                // The final index contains the remainder of the text, which we
+                // don't care about, so we discard it.
+                array_pop($replaced);
+              }
+              $newValues[] = implode(' ', $replaced);
             }
-            // Extract part of the text which include the search term plus six
-            // "words" following it. After finding the string, bold the search
-            // term.
-            $replaced = preg_replace($conditionVals['phpRegex'], "<strong>$v[0]</strong>", preg_split("/\s+/", substr($fieldValue['value'], $start), 6));
-            if (count($replaced) > 1) {
-              // The final index contains the remainder of the text, which we
-              // don't care about, so we discard it.
-              array_pop($replaced);
+
+            $title_collect[$result_key]['title'] = $node->getTitle();
+            $title_collect[$result_key]['field'] = $newValues;
+            $title_collect[$result_key]['view_url'] = $node->toUrl()->toString();
+            $title_collect[$result_key]['edit_url'] = $node->toUrl('edit-form')->toString();
+          }
+          elseif (in_array($fieldType, ['string', 'link'])) {
+            preg_match($conditionVals['phpRegex'], $nodeField->getString(), $matches, PREG_OFFSET_CAPTURE);
+            if (empty($matches) || !isset($matches[0]) || !isset($matches[0][0])) {
+              // Match not guaranteed for nodes with > 1 language.
+              continue;
             }
-            $newValues[] = implode(' ', $replaced);
+
+            $match = $matches[0][0];
+            $replaced = preg_replace($conditionVals['phpRegex'], "<strong>$match</strong>", $nodeField->getString());
+
+            $title_collect[$result_key]['title'] = $node->getTitle();
+            $title_collect[$result_key]['field'] = [$replaced];
+            $title_collect[$result_key]['view_url'] = $node->toUrl()->toString();
+            $title_collect[$result_key]['edit_url'] = $node->toUrl('edit-form')->toString();
           }
-          $title_collect[$id]['field'] = $newValues;
-        }
-        elseif (in_array($fieldType, ['string', 'link'])) {
-          $title_collect[$id]['title'] = $node->getTitle();
-          preg_match($conditionVals['phpRegex'], $nodeField->getString(), $matches, PREG_OFFSET_CAPTURE);
-          $match = $matches[0][0];
-          $replaced = preg_replace($conditionVals['phpRegex'], "<strong>$match</strong>", $nodeField->getString());
-          $title_collect[$id]['field'] = [$replaced];
         }
       }
     }
@@ -101,9 +130,12 @@ class Node extends Entity {
    */
   public function replace(string $field, array $values, array $undo_data): array {
     $data = $undo_data;
-    [$entityType, $bundle, $fieldname] = explode(':', $field);
 
     try {
+      // $field will be string composed of entity type, bundle name, and field
+      // name delimited by ':' characters.
+      [$entityType, $bundle, $fieldname] = explode(':', $field);
+
       $query = $this->entityTypeManager->getStorage($entityType)->getQuery();
       $query->condition('type', $bundle);
       if ($values['published']) {
@@ -115,77 +147,53 @@ class Node extends Entity {
       // Disable the normal access check.
       $query->accessCheck(FALSE);
 
-      $entities = $query->execute();
+      $languages = $values['language'] === 'all' ? array_keys(AdminHelper::getAllEnabledLanguages()) : [$values['language']];
+      $nids = $query->execute();
 
-      foreach ($entities as $id) {
-        $node = CoreNode::load($id);
-        $nodeField = $node->get($fieldname);
-        $fieldType = $nodeField->getFieldDefinition()->getType();
-        if (in_array($fieldType, ['text_with_summary', 'text', 'text_long'])) {
-          if ($values['language'] === 'all') {
-            $other_languages = AdminHelper::getAllEnabledLanguages();
-            foreach ($other_languages as $langcode => $languageName) {
-              if ($node->hasTranslation($langcode)) {
-                $node = $node->getTranslation($langcode);
-                $nodeField = $node->get($fieldname);
-              }
-              $fieldValue = $nodeField->getValue()[0];
-              // Replace the search term with the replacement term.
-              $fieldValue['value'] = preg_replace($conditionVals['phpRegex'], $values['replace'], $fieldValue['value']);
-              $node->$fieldname = $fieldValue;
-            }
+      // Iterate over matched entities (nodes) to perform the replacement.
+      foreach ($nids as $nid) {
+        foreach ($languages as $language) {
+          $node = CoreNode::load($nid);
+          if ($node->hasTranslation($language)) {
+            $node = $node->getTranslation($language);
           }
-          else {
-            $requested_lang = $values['language'];
-            if ($node->hasTranslation($requested_lang)) {
-              $node = $node->getTranslation($requested_lang);
-              $nodeField = $node->get($fieldname);
-            }
-            $fieldValue = $nodeField->getValue()[0];
+          $nodeField = $node->get($fieldname);
+          $fieldType = $nodeField->getFieldDefinition()->getType();
+
+          if (in_array($fieldType, ['text_with_summary', 'text', 'text_long'])) {
+            // @todo Add support for fields with cardinality > 1.
+            $fieldValue = $nodeField->getValue()[0] ?? [];
+
             // Replace the search term with the replacement term.
             $fieldValue['value'] = preg_replace($conditionVals['phpRegex'], $values['replace'], $fieldValue['value']);
             $node->$fieldname = $fieldValue;
+
           }
-          if (!isset($data["node:$id"]['new_vid'])) {
-            $data["node:$id"]['old_vid'] = $node->vid->getString();
-            // Crete a new revision so that we can have the option of undoing it
-            // later on.
-            $node->setNewRevision();
-            $node->revision_log = $this->t('Replaced %search with %replace via Scanner Search and Replace module.', [
-              '%search' => $values['search'],
-              '%replace' => $values['replace'],
-            ]);
-            $node->setRevisionUserId($this->currentUser->id());
+          elseif ($fieldType == 'string') {
+            $fieldValue = preg_replace($conditionVals['phpRegex'], $values['replace'], $nodeField->getString());
+            $node->$fieldname = $fieldValue;
           }
-          // Save the updated node.
-          $node->save();
-          // Fetch the new revision id.
-          $data["node:$id"]['new_vid'] = $node->vid->getString();
-        }
-        elseif ($fieldType == 'string') {
-          if (!isset($data["node:$id"]['new_vid'])) {
-            if ($values['language'] === 'all') {
-              $all_languages = AdminHelper::getAllEnabledLanguages();
-              foreach ($all_languages as $langcode => $languageName) {
-                if ($node->hasTranslation($langcode)) {
-                  $node = $node->getTranslation($langcode);
-                  $nodeField = $node->get($fieldname);
+          elseif ($fieldType == 'link') {
+            $new_value = [];
+            foreach ($nodeField->getValue() as $delta => $field_value) {
+              foreach ($field_value as $field_element => $field_element_value) {
+                if (is_string($field_element_value)) {
+                  $fieldValue = preg_replace($conditionVals['phpRegex'], $values['replace'], $field_element_value);
                 }
-                $fieldValue = preg_replace($conditionVals['phpRegex'], $values['replace'], $nodeField->getString());
-                $node->$fieldname = $fieldValue;
-              }
-            }
-            else {
-              $requested_lang = $values['language'];
-              if ($node->hasTranslation($requested_lang)) {
-                // $nodeField = $nodeField->getTranslation($requested_lang);
-                $node = $node->getTranslation($requested_lang);
-                $nodeField = $node->get($fieldname);
+                else {
+                  $fieldValue = $field_element_value;
+                }
+                $new_value[$delta][$field_element] = $fieldValue;
               }
-              $fieldValue = preg_replace($conditionVals['phpRegex'], $values['replace'], $nodeField->getString());
-              $node->$fieldname = $fieldValue;
             }
-            $data["node:$id"]['old_vid'] = $node->vid->getString();
+            $node->$fieldname = $new_value;
+          }
+
+          // Create a new revision and save the node for this translation.
+          if (!isset($data["node:$nid"]['new_vid'])) {
+            $data["node:$nid"]['old_vid'] = $node->vid->getString();
+            // Crete a new revision so that we can have the option of undoing it
+            // later on.
             $node->setNewRevision();
             $node->revision_log = $this->t('Replaced %search with %replace via Scanner Search and Replace module.', [
               '%search' => $values['search'],
@@ -193,70 +201,11 @@ class Node extends Entity {
             ]);
             $node->setRevisionUserId($this->currentUser->id());
           }
+
+          // Save the updated node.
           $node->save();
-          $data["node:$id"]['new_vid'] = $node->vid->getString();
-        }
-        elseif ($fieldType == 'link') {
-          if (!isset($data["node:$id"]['new_vid'])) {
-            if ($values['language'] === 'all') {
-              $all_languages = AdminHelper::getAllEnabledLanguages();
-              foreach ($all_languages as $langcode => $languageName) {
-                if ($node->hasTranslation($langcode)) {
-                  $node = $node->getTranslation($langcode);
-                  $nodeField = $node->get($fieldname);
-                }
-                $new_value = [];
-                foreach ($nodeField->getValue() as $delta => $field_value) {
-                  foreach ($field_value as $field_element => $field_element_value) {
-                    if (is_string($field_element_value)) {
-                      $fieldValue = preg_replace($conditionVals['phpRegex'], $values['replace'], $field_element_value);
-                    }
-                    else {
-                      $fieldValue = $field_element_value;
-                    }
-                    $new_value[$delta][$field_element] = $fieldValue;
-                  }
-                }
-                $node->$fieldname = $new_value;
-              }
-              $data["node:$id"]['old_vid'] = $node->vid->getString();
-              $node->setNewRevision();
-              $node->revision_log = $this->t('Replaced %search with %replace via Scanner Search and Replace module.', [
-                '%search' => $values['search'],
-                '%replace' => $values['replace'],
-              ]);
-            }
-            else {
-              $requested_lang = $values['language'];
-              if ($node->hasTranslation($requested_lang)) {
-                // $nodeField = $nodeField->getTranslation($requested_lang);
-                $node = $node->getTranslation($requested_lang);
-                $nodeField = $node->get($fieldname);
-              }
-              $new_value = [];
-              foreach ($nodeField->getValue() as $delta => $field_value) {
-                foreach ($field_value as $field_element => $field_element_value) {
-                  if (is_string($field_element_value)) {
-                    $fieldValue = preg_replace($conditionVals['phpRegex'], $values['replace'], $field_element_value);
-                  }
-                  else {
-                    $fieldValue = $field_element_value;
-                  }
-                  $new_value[$delta][$field_element] = $fieldValue;
-                }
-              }
-              $node->$fieldname = $new_value;
-              $data["node:$id"]['old_vid'] = $node->vid->getString();
-              $node->setNewRevision();
-              $node->revision_log = $this->t('Replaced %search with %replace via Scanner Search and Replace module.', [
-                '%search' => $values['search'],
-                '%replace' => $values['replace'],
-              ]);
-            }
-          }
-          $node->setRevisionUserId($this->currentUser->id());
-          $node->save();
-          $data["node:$id"]['new_vid'] = $node->vid->getString();
+          // Fetch the new revision id.
+          $data["node:$nid"]['new_vid'] = $node->vid->getString();
         }
       }
     }
diff --git a/src/Plugin/Scanner/Paragraph.php b/src/Plugin/Scanner/Paragraph.php
index 0d29ddb436fc03672011dd2f16405ba06172f9d9..b522e71a5e7c295830b7befbb476029aba9c9932 100644
--- a/src/Plugin/Scanner/Paragraph.php
+++ b/src/Plugin/Scanner/Paragraph.php
@@ -7,6 +7,7 @@ use Drupal\Component\Plugin\Exception\PluginNotFoundException;
 use Drupal\Core\Entity\EntityStorageException;
 use Drupal\Core\Logger\LoggerChannelTrait;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\scanner\AdminHelper;
 
 /**
  * A Scanner plugin for handling Paragraph entities.
@@ -27,10 +28,10 @@ class Paragraph extends Entity {
   public function search(string $field, array $values): array {
     $title_collect = [];
     try {
-      [, $bundle, $fieldname] = explode(':', $field);
+      [$entityType, $bundle, $fieldname] = explode(':', $field);
 
-      $query = $this->entityTypeManager->getStorage('paragraph')->getQuery();
-      $query->condition('type', $bundle);
+      $query = $this->entityTypeManager->getStorage($entityType)->getQuery();
+      $query->condition('type', $bundle, '=');
       if ($values['published']) {
         $query->condition('status', 1);
       }
@@ -40,82 +41,99 @@ class Paragraph extends Entity {
       // Disable the normal access check.
       $query->accessCheck(FALSE);
 
+      $languages = $values['language'] === 'all' ? array_keys(AdminHelper::getAllEnabledLanguages()) : [$values['language']];
       $entities = $query->execute();
+      if (empty($entities)) {
+        return $title_collect;
+      }
 
-      if (!empty($entities)) {
-        // Load the paragraph(s) which match the criteria.
-        $paragraphs = $this->entityTypeManager->getStorage('paragraph')
-          ->loadMultiple($entities);
-        // Iterate over matched paragraphs to extract information that will be
-        // rendered in the results.
-        foreach ($paragraphs as $paragraph) {
-          if (!empty($paragraph)) {
-            // Load the entity the paragraph is referenced in.
-            $parentEntity = $paragraph->getParentEntity();
+      // Load the paragraph(s) which match the criteria.
+      /** @var \Drupal\paragraphs\Entity\Paragraph[] $paragraphs */
+      $paragraphs = $this->entityTypeManager->getStorage('paragraph')->loadMultiple($entities);
+
+      foreach ($paragraphs as $paragraph) {
+        foreach ($languages as $language) {
+          if ($paragraph->hasTranslation($language)) {
+            $paragraph = $paragraph->getTranslation($language);
+          }
 
-            if (!empty($parentEntity)) {
-              $parentEntityType = $parentEntity->getEntityTypeId();
-              // In the case of nested relationships we need to find the base
-              // entity.
-              if ($parentEntityType != 'node') {
-                // If child is only nested one level deep.
-                if ($parentEntity->getParentEntity()->getEntityTypeId() == 'node') {
-                  $parentEntity = $parentEntity->getParentEntity();
-                }
-                // Two or more levels of nesting.
-                else {
-                  while ($parentEntity->getParentEntity()->getEntityTypeId() != 'node') {
-                    $parentEntity = $parentEntity->getParentEntity();
-                  }
-                }
+          // Load the entity the paragraph is referenced in.
+          $parentEntity = $paragraph->getParentEntity();
+          if (empty($parentEntity)) {
+            continue;
+          }
+
+          // Find the base node this paragraph is associated with.
+          if ($parentEntity->getEntityTypeId() != 'node') {
+            // If child is only nested one level deep.
+            if ($parentEntity->getParentEntity()->getEntityTypeId() == 'node') {
+              $parentEntity = $parentEntity->getParentEntity();
+            }
+            else {
+              // Two or more levels of nesting.
+              while ($parentEntity->getParentEntity()->getEntityTypeId() != 'node') {
+                $parentEntity = $parentEntity->getParentEntity();
               }
-              $id = $parentEntity->id();
-              // Get the value of the specified field.
-              $paraField = $paragraph->get($fieldname);
-              $fieldType = $paraField->getFieldDefinition()->getType();
-              if (in_array($fieldType, [
-                'text_with_summary',
-                'text',
-                'text_long',
-              ])) {
-                // Get the value of the field.
-                $fieldValue = $paraField->getValue()[0];
-                // Get the parent entity's title.
-                $title_collect[$id]['title'] = $parentEntity->getTitle();
-                // Find all instances of the term we're looking for.
-                preg_match_all($conditionVals['phpRegex'], $fieldValue['value'], $matches, PREG_OFFSET_CAPTURE);
-                $newValues = [];
-                // Build an array of strings which are displayed in the results
-                // with the searched term bolded.
-                foreach ($matches[0] as $v) {
-                  // The offset of the matched term(s) in the field's text.
-                  $start = $v[1];
-                  if ($values['preceded'] !== '') {
-                    // Bolding won't work if starting position is in the middle
-                    // of a word (non-word bounded searches), therefore we move
-                    // the start position back as many character as there are in
-                    // the 'preceded' text.
-                    $start -= strlen($values['preceded']);
-                  }
-                  $replaced = preg_replace($conditionVals['phpRegex'], "<strong>$v[0]</strong>", preg_split("/\s+/", substr($fieldValue['value'], $start), 6));
-                  if (count($replaced) > 1) {
-                    // The final index contains the remainder of the text, which
-                    // we don't care about, so we discard it.
-                    array_pop($replaced);
-                  }
-                  $newValues[] = implode(' ', $replaced);
-                }
-                $title_collect[$id]['field'] = $newValues;
+            }
+          }
+          $id = $parentEntity->id();
+          // Get the value of the specified field.
+          $paraField = $paragraph->get($fieldname);
+          $fieldType = $paraField->getFieldDefinition()->getType();
+          if (in_array($fieldType, ['text_with_summary', 'text', 'text_long'])) {
+            // Get the value of the field.
+            // @todo Handle fields with cardinality greater than 1.
+            $fieldValue = $paraField->getValue()[0] ?? [];
+
+            // Find all instances of the term we're looking for.
+            preg_match_all($conditionVals['phpRegex'], $fieldValue['value'], $matches, PREG_OFFSET_CAPTURE);
+            if (empty($matches) || !isset($matches[0]) || !isset($matches[0][0])) {
+              // Match not guaranteed when paragraph has > 1 language.
+              continue;
+            }
+
+            $newValues = [];
+            // Build an array of strings which are displayed in the results
+            // with the searched term bolded.
+            foreach ($matches[0] as $v) {
+              // The offset of the matched term(s) in the field's text.
+              $start = $v[1];
+              if ($values['preceded'] !== '') {
+                // Bolding won't work if starting position is in the middle
+                // of a word (non-word bounded searches), therefore we move
+                // the start position back as many character as there are in
+                // the 'preceded' text.
+                $start -= strlen($values['preceded']);
               }
-              elseif (in_array($fieldType, ['string', 'link'])) {
-                $title_collect[$id]['title'] = $parentEntity->getTitle();
-                preg_match($conditionVals['phpRegex'], $paraField->getString(), $matches, PREG_OFFSET_CAPTURE);
-                $match = $matches[0][0];
-                $replaced = preg_replace($conditionVals['phpRegex'], "<strong>$match</strong>", $paraField->getString());
-                $title_collect[$id]['field'] = [$replaced];
+              $replaced = preg_replace($conditionVals['phpRegex'], "<strong>$v[0]</strong>", preg_split("/\s+/", substr($fieldValue['value'], $start), 6));
+              if (count($replaced) > 1) {
+                // The final index contains the remainder of the text, which
+                // we don't care about, so we discard it.
+                array_pop($replaced);
               }
+              $newValues[] = implode(' ', $replaced);
             }
+            $title_collect[$id]['title'] = $parentEntity->getTitle();
+            $title_collect[$id]['field'] = $newValues;
+            $title_collect[$id]['view_url'] = $parentEntity->toUrl()->toString();
+            $title_collect[$id]['edit_url'] = $parentEntity->toUrl('edit-form')->toString();
           }
+          elseif (in_array($fieldType, ['string', 'link'])) {
+            preg_match($conditionVals['phpRegex'], $paraField->getString(), $matches, PREG_OFFSET_CAPTURE);
+            if (empty($matches) || !isset($matches[0]) || !isset($matches[0][0])) {
+              // Match not guaranteed for paragraphs with > 1 language.
+              continue;
+            }
+
+            $match = $matches[0][0];
+            $replaced = preg_replace($conditionVals['phpRegex'], "<strong>$match</strong>", $paraField->getString());
+
+            $title_collect[$id]['title'] = $parentEntity->getTitle();
+            $title_collect[$id]['field'] = [$replaced];
+            $title_collect[$id]['view_url'] = $parentEntity->toUrl()->toString();
+            $title_collect[$id]['edit_url'] = $parentEntity->toUrl('edit-form')->toString();
+          }
+
         }
       }
     }
diff --git a/templates/scanner-results.html.twig b/templates/scanner-results.html.twig
index b1c7375b2c1f9a1da5f976751a0f613bf2ac9b47..06c762e041b76eede513bc70f37ef8be90d18b16 100644
--- a/templates/scanner-results.html.twig
+++ b/templates/scanner-results.html.twig
@@ -10,8 +10,8 @@
           {% for id, value in values %}
             <li>
               {{value.title}} | 
-              <a href="{{ path('entity.node.canonical', {'node':id}) }}">{{ 'view'|t }}</a> |
-              <a href="/node/{{id}}/edit">{{ 'edit'|t }}</a>
+              <a href="{{ value.view_url }}">{{ 'view'|t }}</a> |
+              <a href="{{ value.edit_url }}">{{ 'edit'|t }}</a>
             </li>
             {% if value.field|length > 1 %}
               <span>[{{ '@count matches in the field'|t({'@count': value.field|length})}}]</span>