diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/PATCHES.txt b/PATCHES.txt
new file mode 100644
index 0000000..bb756d1
--- /dev/null
+++ b/PATCHES.txt
@@ -0,0 +1,7 @@
+This file was automatically generated by Composer Patches (https://github.com/cweagans/composer-patches)
+Patches applied to this directory:
+
+Add support for paragraphs in translations
+Source: https://www.drupal.org/files/issues/2024-06-26/chatgpt_plugin_paragraph_support.patch
+
+
diff --git a/chatgpt_plugin.info.yml b/chatgpt_plugin.info.yml
index 84734d2..ac4ab3d 100644
--- a/chatgpt_plugin.info.yml
+++ b/chatgpt_plugin.info.yml
@@ -4,3 +4,8 @@ description: 'Feature to generate contents from OpenAI.'
 package: Custom
 configure: chatgpt_plugin.admin_settings
 core_version_requirement: ^9 || ^10 || ^11
+
+# Information added by Drupal.org packaging script on 2024-07-24
+version: '2.1.5'
+project: 'chatgpt_plugin'
+datestamp: 1721844317
diff --git a/chatgpt_plugin.routing.yml b/chatgpt_plugin.routing.yml
index 28e9b9b..080d4f2 100644
--- a/chatgpt_plugin.routing.yml
+++ b/chatgpt_plugin.routing.yml
@@ -19,6 +19,8 @@ chatgpt_plugin.translate_content:
     _title: 'ChatGPT Translation'
   requirements:
     _permission: 'access chatgpt translation'
+  options:
+    _admin_route: TRUE
 chatgpt_plugin.chatgpt_assist_tool:
   path: '/admin/config/chatgpt_assist_tool'
   defaults:
diff --git a/chatgpt_plugin.services.yml b/chatgpt_plugin.services.yml
index 4357d30..c251f1c 100644
--- a/chatgpt_plugin.services.yml
+++ b/chatgpt_plugin.services.yml
@@ -9,3 +9,5 @@ services:
   chatgpt_plugin.dalle_api:
     class: Drupal\chatgpt_plugin\DallEApiService
     arguments: ['@http_client', '@config.factory']
+  chatgpt_plugin.translation_service:
+    class: Drupal\chatgpt_plugin\ChatGPTTranslationService
diff --git a/src/ChatGPTBatches.php b/src/ChatGPTBatches.php
new file mode 100644
index 0000000..e2a3814
--- /dev/null
+++ b/src/ChatGPTBatches.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace Drupal\chatgpt_plugin;
+
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\node\Entity\Node;
+use GuzzleHttp\Exception\GuzzleException;
+
+class ChatGPTBatches {
+
+  public static function initiateBatchProcessing(array $bundleFields, Node $node, $langName, $langCode) {
+    $batch = [
+      'title' => t('Copying content from EN to US-EN...'),
+      'operations' => [],
+      'finished' => '\Drupal\chatgpt_plugin\ChatGPTBatches::finishedCallback',
+    ];
+
+    foreach ($bundleFields as $fieldName => $bundleField) {
+      $batch['operations'][] = ['\Drupal\chatgpt_plugin\ChatGPTBatches::operationCallback', [$fieldName, $bundleField, $node, $langName, $langCode, $bundleFields]];
+    }
+
+    // Set the batch.
+    batch_set($batch);
+  }
+
+  public static function operationCallback($fieldName, $bundleField, $node, $langName, $langCode, $bundleFields, &$context) {
+    // Access the service using Drupal's service container.
+    $translationService = \Drupal::service('chatgpt_plugin.translation_service');
+
+    if (!isset($context['results']['processed'])) {
+      $context['results']['processed'] = 0;
+    }
+
+    if (!isset($context['results']['bundleFields'])) {
+      $context['results']['bundleFields'] = $bundleFields;
+    }
+
+    if (!isset($context['results']['node'])) {
+      $context['results']['node'] = $node;
+    }
+
+    if (!isset($context['results']['lang_code'])) {
+      $context['results']['lang_code'] = $langCode;
+    }
+
+    $context['results']['processed']++;
+
+    if ($bundleField['isTranslatable']) {
+      $field = $node->get($fieldName);
+
+      if (!isset($bundleField['subfields'])) {
+        $field_value = $field->value;
+        if ($field_value) {
+          $translated_text = $translationService->chatgptTranslateContent($field_value, $langName);
+          $context['results']['bundleFields'][$fieldName]['translation'] = $translated_text;
+        }
+      }
+      else {
+        $context['results']['bundleFields'] = $translationService->getTranslationsRecursively($fieldName, $bundleField, $langName, $context['results']['bundleFields']);
+      }
+    }
+
+    $context['message'] = t('Processed @count nodes.', ['@count' => $context['results']['processed']]);
+  }
+
+  public static function finishedCallback($success, array $results, array $operations) {
+    // Access the service using Drupal's service container.
+    $translationService = \Drupal::service('chatgpt_plugin.translation_service');
+    $translationService->insertTranslation($results['node'], $results['lang_code'], $results['bundleFields']);
+
+    if ($success) {
+      $message = t('Translation process completed successfully.');
+    }
+    else {
+      $message = t('Translation process finished with errors.');
+    }
+    \Drupal::messenger()->addStatus($message);
+  }
+}
diff --git a/src/ChatGPTTranslationService.php b/src/ChatGPTTranslationService.php
new file mode 100644
index 0000000..6aeb405
--- /dev/null
+++ b/src/ChatGPTTranslationService.php
@@ -0,0 +1,157 @@
+<?php
+
+namespace Drupal\chatgpt_plugin;
+
+use Drupal\Core\Entity\ContentEntityInterface;
+use GuzzleHttp\Exception\GuzzleException;
+use Drupal\paragraphs\Entity\Paragraph;
+
+class ChatGPTTranslationService {
+
+  public function chatgptTranslateContent($input_text, $lang_name) {
+    $prompt_text = "Translate this into " . $lang_name . " - " . $input_text;
+
+    try {
+      $gptApi = \Drupal::service('chatgpt_plugin.gpt_api');
+      $response = $gptApi->getGptResponse($prompt_text);
+    } catch (GuzzleException $exception) {
+      $error_msg = $exception->getMessage();
+      return $error_msg;
+    }
+
+    return $response;
+  }
+
+  public function getTranslationsRecursively($field_name, $label, $lang_name, $bundleFields): array {
+    foreach ($label['subfields'] as $subfield_key => $subfield) {
+      foreach ($subfield as $field_name_sub => $label_sub) {
+        if (!isset($label_sub['subfields'])) {
+          if (isset($label_sub['value'])) {
+            $field_value = $label_sub['value'];
+            $translated_text = $this->chatgptTranslateContent($field_value, $lang_name);
+            $bundleFields[$field_name]['subfields'][$subfield_key][$field_name_sub]['translation'] = $translated_text;
+          }
+        }
+        else {
+          $bundleFields[$field_name]['subfields'][$subfield_key] =
+            $this->getTranslationsRecursively($field_name_sub, $label_sub, $lang_name, $bundleFields[$field_name]['subfields'][$subfield_key]);
+        }
+      }
+    }
+
+    return $bundleFields;
+  }
+
+  public function insertTranslation(ContentEntityInterface $node, string $target_language, array $translations): bool {
+    $status = FALSE;
+    if (!$node->hasTranslation($target_language)) {
+      $translatedNode = $node->addTranslation($target_language);
+      $status = TRUE;
+
+      foreach ($translations as $field_name => $field_info) {
+        if ($field_name != 'moderation_state') {
+          if (!empty($field_info['subfields'])) {
+            $paragraph = $this->createParagraphsFromStructure($target_language, $field_info['subfields']);
+            $translatedNode->set($field_name, $paragraph);
+          }
+          else {
+            if ($field_info['isTranslatable']) {
+              if (isset($field_info['translation'])) {
+                $translatedNode->$field_name->value = $field_info['translation'];
+              }
+              if (isset($field_info['format'])) {
+                $translatedNode->$field_name->format = $field_info['format'];
+              }
+            }
+          }
+        }
+      }
+
+      try {
+        $translatedNode->save();
+      } catch (EntityStorageException $e) {
+        $status = FALSE;
+        \Drupal::logger('chatgpt_plugin')->error($e->getMessage());
+      }
+    }
+
+    return $status;
+  }
+
+  private function createParagraphsFromStructure(string $target_language, array $structure): array {
+    $paragraphs = [];
+    foreach ($structure as $structure_item) {
+      if (isset($structure_item[array_key_first($structure_item)]['bundle'])) {
+        $paragraph = Paragraph::create([
+          'type' => $structure_item[array_key_first($structure_item)]['bundle'],
+          'langcode' => $target_language,
+        ]);
+
+        foreach ($structure_item as $field_name => $field_info) {
+          if ($field_info['isTranslatable'] || !empty($field_info['subfields'])) {
+            if (isset($field_info['translation'])) {
+              $paragraph->$field_name->value = $field_info['translation'];
+            }
+            if (isset($field_info['format'])) {
+              $paragraph->$field_name->format = $field_info['format'];
+            }
+            if (!empty($field_info['subfields'])) {
+              $nested_paragraphs = $this->createParagraphsFromStructure($target_language, $field_info['subfields']);
+              $paragraph->set($field_name, $nested_paragraphs);
+            }
+          }
+          elseif (str_starts_with($field_name, 'field_')) {
+            $field_definition = $paragraph->getFieldDefinition($field_name);
+            $field_type = $field_definition->getType();
+            $field_value = $field_info['field_value'];
+            switch ($field_type) {
+              case 'entity_reference':
+              case 'entity_reference_revisions':
+                if (is_array($field_value)) {
+                  $paragraph->set($field_name, $field_value);
+                }
+                break;
+              case 'file':
+                if (is_array($field_value) && isset($field_value['target_id'])) {
+                  $paragraph->set($field_name, ['target_id' => $field_value['target_id']]);
+                }
+                break;
+              case 'image':
+                if (is_array($field_value) && isset($field_value['target_id'])) {
+                  $paragraph->set($field_name, ['target_id' => $field_value['target_id'], 'alt' => $field_value['alt'] ?? '']);
+                }
+                break;
+              case 'link':
+                if (is_array($field_value)) {
+                  $paragraph->set($field_name, [
+                    'uri' => $field_value['uri'],
+                    'title' => $field_value['title'] ?? '',
+                  ]);
+                }
+                break;
+              case 'boolean':
+                $paragraph->set($field_name, (bool) $field_value);
+                break;
+              default:
+                $paragraph->set($field_name, $field_value);
+                break;
+            }
+          }
+          else {
+            \Drupal::logger('chatgpt_plugin')->error(t('@fieldName is not translatable.', ['@fieldName' => $field_name]));
+          }
+
+        }
+
+        try {
+          $paragraph->save();
+          $paragraphs[] = $paragraph;
+        } catch (EntityStorageException $e) {
+          \Drupal::logger('chatgpt_plugin')->error($e->getMessage());
+        }
+      }
+    }
+    return $paragraphs;
+  }
+
+}
diff --git a/src/Controller/ChatGPTTranslateController.php b/src/Controller/ChatGPTTranslateController.php
index 72b614e..740c332 100644
--- a/src/Controller/ChatGPTTranslateController.php
+++ b/src/Controller/ChatGPTTranslateController.php
@@ -2,11 +2,15 @@
 
 namespace Drupal\chatgpt_plugin\Controller;
 
+use Drupal\chatgpt_plugin\ChatGPTBatches;
 use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Extension\ModuleHandler;
+use Drupal\node\Entity\Node;
+use Drupal\paragraphs\Entity\Paragraph;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 use GuzzleHttp\Exception\GuzzleException;
 use Drupal\Core\Entity\EntityStorageException;
-use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Entity\EntityFieldManagerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -54,6 +58,13 @@ class ChatGPTTranslateController extends ControllerBase {
    */
   protected $gptApi;
 
+  /**
+   * If the paragraph module is enabled.
+   *
+   * @var bool
+   */
+  protected $isParagraphsEnabled;
+
   /**
    * Creates an ContentTranslationPreviewController object.
    *
@@ -68,17 +79,23 @@ class ChatGPTTranslateController extends ControllerBase {
    * @param \Drupal\chatgpt_plugin\GPTApiService $gpt_api
    *   Our custom GPT API service.
    */
-  public function __construct(EntityTypeManagerInterface $entity_type_manager,
-  EntityFieldManagerInterface $entity_field_manager,
-  ClientInterface $http_client,
-  RequestStack $requestStack,
-  GPTApiService $gpt_api
+  public function __construct(
+    EntityTypeManagerInterface $entity_type_manager,
+    EntityFieldManagerInterface $entity_field_manager,
+    ClientInterface $http_client,
+    RequestStack $requestStack,
+    GPTApiService $gpt_api,
+    ModuleHandler $moduleHandler
   ) {
     $this->entityTypeManager = $entity_type_manager;
     $this->entityFieldManager = $entity_field_manager;
     $this->httpclient = $http_client;
     $this->request = $requestStack;
     $this->gptApi = $gpt_api;
+    $this->isParagraphsEnabled = FALSE;
+    if ($moduleHandler->moduleExists('paragraphs')) {
+      $this->isParagraphsEnabled = TRUE;
+    }
   }
 
   /**
@@ -91,6 +108,7 @@ class ChatGPTTranslateController extends ControllerBase {
       $container->get('http_client'),
       $container->get('request_stack'),
       $container->get('chatgpt_plugin.gpt_api'),
+      $container->get('module_handler'),
     );
   }
 
@@ -107,13 +125,34 @@ class ChatGPTTranslateController extends ControllerBase {
    * @return \Symfony\Component\HttpFoundation\RedirectResponse
    *   the function will return a RedirectResponse to the translate
    *   overview page by showing a success or error message.
+   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
+   * @throws \Drupal\Core\Entity\EntityStorageException
    */
-  public function translate($lang_code, $lang_name, $node_id) {
+  public function translate(string $lang_code, string $lang_name, int $node_id) {
     $node = $this->entityTypeManager->getStorage('node')->load($node_id);
-    $entity_type_id = 'node';
-    $bundle = $node->bundle();
+    $bundleFields = $this->getFieldsToTranslate($node);
     $bundleFields['title']['label'] = 'Title';
+    $bundleFields['title']['value'] = $node->label();
+    $bundleFields['title']['isTranslatable'] = TRUE;
+
+    ChatGPTBatches::initiateBatchProcessing($bundleFields, $node, $lang_name, $lang_code);
 
+    $refererUrl = $this->request->getCurrentRequest()->server->get('HTTP_REFERER');
+
+    return batch_process($refererUrl);
+  }
+
+  /**
+   * Get the fields that can be translated.
+   *
+   * @param $entity
+   *  The entity to translate.
+   *
+   * @return array
+   *  The fields that can be translated.
+   */
+  private function getFieldsToTranslate($entity): array {
     $allowed_values = [
       'text',
       'text_with_summary',
@@ -121,93 +160,117 @@ class ChatGPTTranslateController extends ControllerBase {
       'string',
       'string_long',
     ];
-    foreach ($this->entityFieldManager->getFieldDefinitions($entity_type_id, $bundle) as $field_name => $field_definition) {
-      if (!empty($field_definition->getTargetBundle()) && in_array($field_definition->getType(), $allowed_values)) {
-        $bundleFields[$field_name]['label'] = $field_definition->getLabel();
-      }
-    }
 
-    foreach ($bundleFields as $field_name => $label) {
-      $field = $node->get($field_name);
-      $field_value = $field->value;
-      if ($field_value) {
-        $translated_text = $this->chatgptTranslateContent($field_value, $lang_name);
-        $bundleFields[$field_name]['translation'] = $translated_text;
-      }
+    if ($this->isParagraphsEnabled) {
+      $allowed_values[] = 'entity_reference_revisions';
     }
-    $insertTranslation = $this->insertTranslation($node, $lang_code, $bundleFields);
 
-    $refererUrl = $this->request->getCurrentRequest()->server->get('HTTP_REFERER');
-    $response = new RedirectResponse($refererUrl);
-    $response->send();
-    $messenger = $this->messenger();
-
-    if ($insertTranslation) {
-      $messenger->addStatus($this->t('Content translated successfully.'));
-    }
-    else {
-      $messenger->addError($this->t('There was some issue with content translation.'));
-    }
-    return $response;
+    return $this->getFieldsToTranslateRecursive($entity, $allowed_values);
   }
 
   /**
-   * Get the trnslated content by making API call to GPT API.
-   *
-   * @param string $input_text
-   *   Input prompt for the GPT API.
-   * @param string $lang_name
-   *   The target language name.
+   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
+   *  The entity to translate.
+   * @param array $allowed_values
+   *  The allowed values.
    *
-   * @return string
-   *   The text translation received from GPT API.
+   * @return array
    */
-  public function chatgptTranslateContent($input_text, $lang_name) {
-    $prompt_text = "Translate this into " . $lang_name . " - " . $input_text;
+  private function getFieldsToTranslateRecursive(ContentEntityInterface $entity, array $allowed_values): array {
+    $bundleFields = [];
 
-    // Calling our custom GPT API service..
-    try {
-      $response = $this->gptApi->getGptResponse($prompt_text);
-    }
-    catch (GuzzleException $exception) {
-      // Error handling for ChatGPT API call.
-      $error_msg = $exception->getMessage();
-      return $error_msg;
+    foreach ($this->entityFieldManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $field_name => $field_definition) {
+      $bundleFields[$field_name] = [
+        'label' => $field_name,
+        'bundle' => $entity->bundle(),
+        'type' => $entity->getEntityTypeId(),
+      ];
+
+      if (!empty($field_definition->getTargetBundle()) && in_array($field_definition->getType(), $allowed_values)) {
+        $bundleFields[$field_name]['isTranslatable'] = TRUE;
+
+        if ($entity->get($field_name)->value) {
+          $bundleFields[$field_name]['value'] = $entity->get($field_name)->value;
+          $bundleFields[$field_name]['format'] = $entity->get($field_name)->format;
+        }
+
+        if ($this->isParagraphsEnabled && $field_definition->getType() === 'entity_reference_revisions') {
+          $referencedEntities = $entity->$field_name->referencedEntities();
+          foreach ($referencedEntities as $referencedEntity) {
+            if ($referencedEntity instanceof Paragraph) {
+              $bundleFields[$field_name]['subfields'][] = $this->getFieldsToTranslateRecursive($referencedEntity, $allowed_values);
+            }
+          }
+        }
+      }
+      else {
+        $bundleFields[$field_name]['isTranslatable'] = FALSE;
+        $bundleFields[$field_name]['field_value'] = $this->getEntityFieldValue($entity, $field_name);
+      }
     }
 
-    return $response;
+    return $bundleFields;
   }
 
   /**
-   * Adding the translation in database and linking it to the original node.
+   * Get the value of a field from an entity.
    *
-   * @param \Drupal\Core\Entity\ContentEntityInterface $node
+   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
    *   The entity object.
-   * @param string $target_language
-   *   The target language.
-   * @param array $bundleFields
-   *   An array of field name and their translation.
+   * @param string $field_name
+   *   The machine name of the field.
+   *
+   * @return mixed
+   *   The field value, or NULL if the field does not exist or is empty.
    */
-  public function insertTranslation(ContentEntityInterface $node, $target_language, array $bundleFields) {
-    if (!$node->hasTranslation($target_language)) {
-      $node_translation = $node->addTranslation($target_language);
-      $status = TRUE;
-
-      foreach ($bundleFields as $field_name => $val) {
-        if ($field_name != 'moderation_state') {
-          $node_translation->$field_name->value = $val['translation'] ?? NULL;
-          $node_translation->$field_name->format = 'full_html';
-        }
-      }
-
-      try {
-        $node_translation->save();
+  function getEntityFieldValue(ContentEntityInterface $entity, string $field_name) {
+    if ($entity->hasField($field_name) && !$entity->get($field_name)->isEmpty()) {
+      $field = $entity->get($field_name);
+      $field_type = $field->getFieldDefinition()->getType();
+      switch ($field_type) {
+        case 'string':
+        case 'string_long':
+        case 'text':
+        case 'list_integer':
+        case 'list_string':
+        case 'decimal':
+        case 'float':
+        case 'integer':
+        case 'date':
+        case 'datetime':
+        case 'text_long':
+          return $field->value;
+        case 'text_with_summary':
+          return [
+            'value' => $field->value,
+            'summary' => $field->summary,
+            'format' => $field->format,
+          ];
+        case 'entity_reference_revisions':
+        case 'entity_reference':
+          return $field->referencedEntities();
+        case 'image':
+          return [
+            'alt' => $field->alt,
+            'target_id' => $field->target_id,
+          ];
+        case 'file':
+          return [
+            'target_id' => $field->target_id,
+          ];
+        case 'link':
+          return [
+            'uri' => $field->uri,
+            'title' => $field->title,
+          ];
+        case 'boolean':
+          return (bool) $field->value;
+        default:
+          return $field->getValue();
       }
-      catch (EntityStorageException $e) {
-        $status = FALSE;
-      }
-      return $status;
     }
+
+    return NULL;
   }
 
 }
diff --git a/src/Controller/ChatGPTTranslateController.php.rej b/src/Controller/ChatGPTTranslateController.php.rej
new file mode 100644
index 0000000..fd89408
--- /dev/null
+++ b/src/Controller/ChatGPTTranslateController.php.rej
@@ -0,0 +1,119 @@
+--- src/Controller/ChatGPTTranslateController.php
++++ src/Controller/ChatGPTTranslateController.php
+@@ -391,27 +597,106 @@ class ChatGPTTranslateController extends ControllerBase {
+    *   The entity object.
+    * @param string $target_language
+    *   The target language.
+-   * @param array $bundleFields
+-   *   An array of field name and their translation.
++   * @param array $translations
++   *   The translations from GPT.
++   *
++   * @return bool
++   *   If the translation is successful to set message.
++   * @throws \Drupal\Core\Entity\EntityStorageException
+    */
+-  public function insertTranslation(ContentEntityInterface $node, $target_language, array $bundleFields) {
++  public function insertTranslation(ContentEntityInterface $node, string $target_language, array $translations): bool {
++    $status = FALSE;
+     if (!$node->hasTranslation($target_language)) {
+-      $node_translation = $node->addTranslation($target_language);
++      $translatedNode = $node->addTranslation($target_language);
+       $status = TRUE;
+ 
+-      foreach ($bundleFields as $field_name => $val) {
+-        $node_translation->$field_name->value = $val['translation'];
+-        $node_translation->$field_name->format = 'full_html';
+-      }
++      foreach ($translations as $field_name => $field_info) {
++        if (!empty($field_info['subfields'])) {
++          $paragraph = $this->createParagraphsFromStructure($target_language, $field_info['subfields']);
++          $translatedNode->set($field_name, $paragraph);
++        } else {
++          if ($field_info['isTranslatable']) {
++            if (isset($field_info['translation'])) {
++              $translatedNode->$field_name->value = $field_info['translation'];
++            }
++            if (isset($field_info['format'])) {
++              $translatedNode->$field_name->format = $field_info['format'];
++            }
++          }
++        }
+ 
++      }
+       try {
+-        $node_translation->save();
++        $translatedNode->save();
+       }
+       catch (EntityStorageException $e) {
+         $status = FALSE;
++        \Drupal::logger('chatgpt_plugin')->error($e->getMessage());
+       }
+-      return $status;
+     }
++
++    return $status;
++  }
++
++  /**
++   * Get the value of a field from an entity.
++   *
++   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
++   *   The entity object.
++   * @param string $field_name
++   *   The machine name of the field.
++   *
++   * @return mixed
++   *   The field value, or NULL if the field does not exist or is empty.
++   */
++  function getEntityFieldValue(ContentEntityInterface $entity, string $field_name) {
++    if ($entity->hasField($field_name) && !$entity->get($field_name)->isEmpty()) {
++      $field = $entity->get($field_name);
++      $field_type = $field->getFieldDefinition()->getType();
++      switch ($field_type) {
++        case 'string':
++        case 'string_long':
++        case 'text':
++        case 'list_integer':
++        case 'list_string':
++        case 'decimal':
++        case 'float':
++        case 'integer':
++        case 'date':
++        case 'datetime':
++        case 'text_long':
++          return $field->value;
++        case 'text_with_summary':
++          return [
++            'value' => $field->value,
++            'summary' => $field->summary,
++            'format' => $field->format,
++          ];
++        case 'entity_reference_revisions':
++        case 'entity_reference':
++          return $field->referencedEntities();
++        case 'image':
++          return [
++            'alt' => $field->alt,
++            'target_id' => $field->target_id,
++          ];
++        case 'file':
++          return [
++            'target_id' => $field->target_id,
++          ];
++        case 'link':
++          return [
++            'uri' => $field->uri,
++            'title' => $field->title,
++          ];
++        case 'boolean':
++          return (bool) $field->value;
++        default:
++          return $field->getValue();
++      }
++    }
++
++    return NULL;
+   }
+ 
+ }
diff --git a/src/Form/ChatGPTConfigForm.php b/src/Form/ChatGPTConfigForm.php
index 5391278..784a720 100644
--- a/src/Form/ChatGPTConfigForm.php
+++ b/src/Form/ChatGPTConfigForm.php
@@ -127,6 +127,7 @@ class ChatGPTConfigForm extends ConfigFormBase {
       '#description' => $this->t('Please provide the OpenAI API Access Token here.'),
       '#default_value' => $config->get('access_token'),
       '#required' => TRUE,
+      '#maxlength' => 164
     ];
 
     $form['chatgpt_max_token'] = [
@@ -134,7 +135,7 @@ class ChatGPTConfigForm extends ConfigFormBase {
       '#title' => $this->t('OpenAI API Max Token'),
       '#description' => $this->t('Please provide the OpenAI API max token here to limit the output words. Max token is the
                         limit <br>of tokens combining both input prompt and output text. 1 token is approx 4 chars in English.
-                        <br>You can use this <a href="https://platform.openai.com/tokenizer" target="_blank"><b>Tokenizer Tool</b></a> 
+                        <br>You can use this <a href="https://platform.openai.com/tokenizer" target="_blank"><b>Tokenizer Tool</b></a>
                         to count number of tokens for your text.'),
       '#default_value' => $config->get('chatgpt_max_token'),
       '#required' => TRUE,
