diff --git a/src/plugin/jamipluginmanager.cpp b/src/plugin/jamipluginmanager.cpp
index f25c5e9bbc907982fe8987bd7df00b70177dcbea..73d3ac0e4c48cb8f718ea9545922d0cdc66d560b 100644
--- a/src/plugin/jamipluginmanager.cpp
+++ b/src/plugin/jamipluginmanager.cpp
@@ -76,7 +76,7 @@ JamiPluginManager::getPluginDetails(const std::string& rootPath)
     }
 
     std::map<std::string, std::string> details = PluginUtils::parseManifestFile(
-        PluginUtils::manifestPath(rootPath));
+        PluginUtils::manifestPath(rootPath), rootPath);
     if (!details.empty()) {
         auto itIcon = details.find("iconPath");
         itIcon->second.insert(0, rootPath + DIR_SEPARATOR_CH + "data" + DIR_SEPARATOR_CH);
@@ -231,7 +231,7 @@ JamiPluginManager::installPlugin(const std::string& jplPath, bool force)
                                         + DIR_SEPARATOR_CH + name};
             // Find if there is an existing version of this plugin
             const auto alreadyInstalledManifestMap = PluginUtils::parseManifestFile(
-                PluginUtils::manifestPath(destinationDir));
+                PluginUtils::manifestPath(destinationDir), destinationDir);
 
             if (!alreadyInstalledManifestMap.empty()) {
                 if (force) {
diff --git a/src/plugin/pluginpreferencesutils.cpp b/src/plugin/pluginpreferencesutils.cpp
index 237d0567d5887034f3b68e7beb45274a93e0bdcc..a60a9cef8b3a4c24df9aaf327888c128c7169323 100644
--- a/src/plugin/pluginpreferencesutils.cpp
+++ b/src/plugin/pluginpreferencesutils.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "pluginpreferencesutils.h"
+#include "pluginsutils.h"
 
 #include <msgpack.hpp>
 #include <sstream>
@@ -60,51 +61,6 @@ PluginPreferencesUtils::getAllowDenyListsPath()
            + "allowdeny.msgpack";
 }
 
-std::map<std::string, std::string>
-PluginPreferencesUtils::processLocaleFile(const std::string& preferenceLocaleFilePath)
-{
-    if (!fileutils::isFile(preferenceLocaleFilePath)) {
-        return {};
-    }
-    std::ifstream file(preferenceLocaleFilePath);
-    Json::Value root;
-    Json::CharReaderBuilder rbuilder;
-    rbuilder["collectComments"] = false;
-    std::string errs;
-    std::map<std::string, std::string> locales {};
-    if (file) {
-        // Read the file to a json format
-        if (Json::parseFromStream(rbuilder, file, &root, &errs)) {
-            auto keys = root.getMemberNames();
-            for (const auto& key : keys) {
-                locales[key] = root.get(key, "").asString();
-            }
-        }
-    }
-    return locales;
-}
-
-std::map<std::string, std::string>
-PluginPreferencesUtils::getLocales(const std::string& rootPath, const std::string& lang)
-{
-    auto pluginName = rootPath.substr(rootPath.find_last_of(DIR_SEPARATOR_CH) + 1);
-    auto basePath = fmt::format("{}/data/locale/{}", rootPath, pluginName + "_");
-
-    std::map<std::string, std::string> locales = {};
-
-    // Get language translations
-    if (!lang.empty()) {
-        locales = processLocaleFile(basePath + lang + ".json");
-    }
-
-    // Get default english values if no translations were found
-    if (locales.empty()) {
-        locales = processLocaleFile(basePath + "en.json");
-    }
-
-    return locales;
-}
-
 std::string
 PluginPreferencesUtils::convertArrayToString(const Json::Value& jsonArray)
 {
@@ -159,48 +115,8 @@ PluginPreferencesUtils::getPreferences(const std::string& rootPath, const std::s
     std::vector<std::map<std::string, std::string>> preferences;
     if (file) {
         // Get preferences locale
-        std::string lang;
-        if (auto envLang = std::getenv("JAMI_LANG"))
-            lang = envLang;
-        else
-            JAMI_INFO() << "Error getting JAMI_LANG env, trying to get system language";
-        // If language preference is empty, try to get from the system.
-        if (lang.empty()) {
-#ifdef WIN32
-            WCHAR localeBuffer[LOCALE_NAME_MAX_LENGTH];
-            if (GetUserDefaultLocaleName(localeBuffer, LOCALE_NAME_MAX_LENGTH) != 0) {
-                char utf8Buffer[LOCALE_NAME_MAX_LENGTH] {};
-                WideCharToMultiByte(CP_UTF8,
-                                    0,
-                                    localeBuffer,
-                                    LOCALE_NAME_MAX_LENGTH,
-                                    utf8Buffer,
-                                    LOCALE_NAME_MAX_LENGTH,
-                                    nullptr,
-                                    nullptr);
-
-                lang.append(utf8Buffer);
-                string_replace(lang, "-", "_");
-            }
-            // Even though we default to the system variable in windows, technically this
-            // part of the code should not be reached because the client-qt must define that
-            // variable and we cannot run the client and the daemon in diferent processes in Windows.
-#else
-            // The same way described in the comment just above, the android should not reach this
-            // part of the code given the client-android must define "JAMI_LANG" system variable.
-            // And even if this part is reached, it should not work since std::locale is not
-            // supported by the NDK.
-
-            // LC_COLLATE is used to grab the locale for the case when the system user has set different
-            // values for the preferred Language and Format.
-            lang = setlocale(LC_COLLATE, "");
-            // We set the environment to avoid checking from system everytime.
-            // This is the case when running daemon and client in different processes
-            // like with dbus.
-            setenv("JAMI_LANG", lang.c_str(), 1);
-#endif // WIN32
-        }
-        auto locales = getLocales(rootPath, std::string(string_remove_suffix(lang, '.')));
+        const auto& lang = PluginUtils::getLanguage();
+        auto locales = PluginUtils::getLocales(rootPath, std::string(string_remove_suffix(lang, '.')));
 
         // Read the file to a json format
         bool ok = Json::parseFromStream(rbuilder, file, &root, &errs);
diff --git a/src/plugin/pluginpreferencesutils.h b/src/plugin/pluginpreferencesutils.h
index 559a3f0c9b2d49c60096225cf2d12d78ffb386a2..7980a1d7f104ffcbf69b5a591fe0bffddb766d51 100644
--- a/src/plugin/pluginpreferencesutils.h
+++ b/src/plugin/pluginpreferencesutils.h
@@ -67,24 +67,6 @@ public:
      */
     static std::string getAllowDenyListsPath();
 
-    /**
-     * @brief Returns the available keys and translations for a given file.
-     * If the locale is not available, return empty map.
-     * @param localeFilePath
-     * @return locales map
-     */
-    static std::map<std::string, std::string> processLocaleFile(const std::string& localeFilePath);
-
-    /**
-     * @brief Returns the available keys and translations for a given plugin.
-     * If the locale is not available, return the english default.
-     * @param rootPath
-     * @param lang
-     * @return locales map
-     */
-    static std::map<std::string, std::string> getLocales(const std::string& rootPath,
-                                                         const std::string& lang);
-
     /**
      * @brief Returns a colon separated string with values from a json::Value containing an array.
      * @param jsonArray
diff --git a/src/plugin/pluginsutils.cpp b/src/plugin/pluginsutils.cpp
index b5eb85bc7711d9232a2d5b4d5669e2ac665a34d2..a40d21df44514e411be06433305de759e69cb6e3 100644
--- a/src/plugin/pluginsutils.cpp
+++ b/src/plugin/pluginsutils.cpp
@@ -142,13 +142,14 @@ checkManifestValidity(const std::vector<uint8_t>& vec)
 }
 
 std::map<std::string, std::string>
-parseManifestFile(const std::string& manifestFilePath)
+parseManifestFile(const std::string& manifestFilePath, const std::string& rootPath)
 {
     std::lock_guard<std::mutex> guard(fileutils::getFileLock(manifestFilePath));
     std::ifstream file(manifestFilePath);
     if (file) {
         try {
-            return checkManifestValidity(file);
+            const auto& traduction = parseManifestTranslation(rootPath, file);
+            return checkManifestValidity(std::vector<uint8_t>(traduction.begin(), traduction.end()));
         } catch (const std::exception& e) {
             JAMI_ERR() << e.what();
         }
@@ -156,10 +157,36 @@ parseManifestFile(const std::string& manifestFilePath)
     return {};
 }
 
+std::string
+parseManifestTranslation(const std::string& rootPath, std::ifstream& manifestFile)
+{
+    if (manifestFile) {
+        std::stringstream buffer;
+        buffer << manifestFile.rdbuf();
+        std::string manifest = buffer.str();
+        const auto& translation = getLocales(rootPath, getLanguage());
+        std::regex pattern(R"(\{\{([^}]+)\}\})");
+        std::smatch matches;
+        // replace the pattern to the correct translation
+        while (std::regex_search(manifest, matches, pattern)) {
+            if (matches.size() == 2) {
+                auto it = translation.find(matches[1].str());
+                if (it == translation.end()) {
+                    manifest = std::regex_replace(manifest, pattern, "");
+                    continue;
+                }
+                manifest = std::regex_replace(manifest, pattern, it->second, std::regex_constants::format_first_only);
+            }
+        }
+        return manifest;
+    }
+    return {};
+}
+
 bool
 checkPluginValidity(const std::string& rootPath)
 {
-    return !parseManifestFile(manifestPath(rootPath)).empty();
+    return !parseManifestFile(manifestPath(rootPath), rootPath).empty();
 }
 
 std::map<std::string, std::string>
@@ -240,5 +267,97 @@ uncompressJplFunction(std::string_view relativeFileName)
     }
     return std::make_pair(true, relativeFileName);
 }
+
+std::string
+getLanguage()
+{
+    std::string lang;
+        if (auto envLang = std::getenv("JAMI_LANG"))
+            lang = envLang;
+        else
+            JAMI_INFO() << "Error getting JAMI_LANG env, trying to get system language";
+        // If language preference is empty, try to get from the system.
+        if (lang.empty()) {
+#ifdef WIN32
+            WCHAR localeBuffer[LOCALE_NAME_MAX_LENGTH];
+            if (GetUserDefaultLocaleName(localeBuffer, LOCALE_NAME_MAX_LENGTH) != 0) {
+                char utf8Buffer[LOCALE_NAME_MAX_LENGTH] {};
+                WideCharToMultiByte(CP_UTF8,
+                                    0,
+                                    localeBuffer,
+                                    LOCALE_NAME_MAX_LENGTH,
+                                    utf8Buffer,
+                                    LOCALE_NAME_MAX_LENGTH,
+                                    nullptr,
+                                    nullptr);
+
+                lang.append(utf8Buffer);
+                string_replace(lang, "-", "_");
+            }
+            // Even though we default to the system variable in windows, technically this
+            // part of the code should not be reached because the client-qt must define that
+            // variable and we cannot run the client and the daemon in diferent processes in Windows.
+#else
+            // The same way described in the comment just above, the android should not reach this
+            // part of the code given the client-android must define "JAMI_LANG" system variable.
+            // And even if this part is reached, it should not work since std::locale is not
+            // supported by the NDK.
+
+            // LC_COLLATE is used to grab the locale for the case when the system user has set different
+            // values for the preferred Language and Format.
+            lang = setlocale(LC_COLLATE, "");
+            // We set the environment to avoid checking from system everytime.
+            // This is the case when running daemon and client in different processes
+            // like with dbus.
+            setenv("JAMI_LANG", lang.c_str(), 1);
+#endif // WIN32
+    }
+    return lang;
+}
+
+std::map<std::string, std::string>
+getLocales(const std::string& rootPath, const std::string& lang)
+{
+    auto pluginName = rootPath.substr(rootPath.find_last_of(DIR_SEPARATOR_CH) + 1);
+    auto basePath = fmt::format("{}/data/locale/{}", rootPath, pluginName + "_");
+
+    std::map<std::string, std::string> locales = {};
+
+    // Get language translations
+    if (!lang.empty()) {
+        locales = processLocaleFile(basePath + lang + ".json");
+    }
+
+    // Get default english values if no translations were found
+    if (locales.empty()) {
+        locales = processLocaleFile(basePath + "en.json");
+    }
+
+    return locales;
+}
+
+std::map<std::string, std::string>
+processLocaleFile(const std::string& preferenceLocaleFilePath)
+{
+    if (!fileutils::isFile(preferenceLocaleFilePath)) {
+        return {};
+    }
+    std::ifstream file(preferenceLocaleFilePath);
+    Json::Value root;
+    Json::CharReaderBuilder rbuilder;
+    rbuilder["collectComments"] = false;
+    std::string errs;
+    std::map<std::string, std::string> locales {};
+    if (file) {
+        // Read the file to a json format
+        if (Json::parseFromStream(rbuilder, file, &root, &errs)) {
+            auto keys = root.getMemberNames();
+            for (const auto& key : keys) {
+                locales[key] = root.get(key, "").asString();
+            }
+        }
+    }
+    return locales;
+}
 } // namespace PluginUtils
 } // namespace jami
diff --git a/src/plugin/pluginsutils.h b/src/plugin/pluginsutils.h
index adccd201bd43fa3407fee4bba252adc40f446e03..f5df57757b9e49ad06fee98b3c2d1227151ca1da 100644
--- a/src/plugin/pluginsutils.h
+++ b/src/plugin/pluginsutils.h
@@ -81,7 +81,15 @@ std::map<std::string, std::string> checkManifestValidity(const std::vector<uint8
  * @param manifestFilePath
  * @return Map with manifest contents
  */
-std::map<std::string, std::string> parseManifestFile(const std::string& manifestFilePath);
+std::map<std::string, std::string> parseManifestFile(const std::string& manifestFilePath, const std::string& rootPath);
+
+/**
+ * @brief Parses the manifest file of an installed plugin if it's valid.
+ * @param rootPath
+ * @param manifestFile
+ * @return Map with manifest contents
+ */
+std::string parseManifestTranslation(const std::string& rootPath, std::ifstream& manifestFile);
 
 /**
  * @brief Validates a plugin based on its manifest.json file.
@@ -134,5 +142,29 @@ std::vector<uint8_t> readSignatureFileFromArchive(const std::string& jplPath);
  * @return Pair <bool, string> meaning if file should be extracted and where to.
  */
 std::pair<bool, std::string_view> uncompressJplFunction(std::string_view relativeFileName);
+
+/**
+ * @brief Returns the language of the current locale.
+ * @return language
+ */
+std::string getLanguage();
+
+/**
+ * @brief Returns the available keys and translations for a given plugin.
+ * If the locale is not available, return the english default.
+ * @param rootPath
+ * @param lang
+ * @return locales map
+ */
+std::map<std::string, std::string> getLocales(const std::string& rootPath,
+                                                     const std::string& lang);
+
+/**
+ * @brief Returns the available keys and translations for a given file.
+ * If the locale is not available, return empty map.
+ * @param localeFilePath
+ * @return locales map
+ */
+std::map<std::string, std::string> processLocaleFile(const std::string& localeFilePath);
 } // namespace PluginUtils
 } // namespace jami
diff --git a/test/unitTest/plugins/TestSuite.jpl b/test/unitTest/plugins/TestSuite.jpl
index 18efdf05ed865b418e7ac3b76b23b619b0dbe697..477a6d683f9dd2bd14b3bf038a4493e887bcbc7f 100644
Binary files a/test/unitTest/plugins/TestSuite.jpl and b/test/unitTest/plugins/TestSuite.jpl differ
diff --git a/test/unitTest/plugins/plugins.cpp b/test/unitTest/plugins/plugins.cpp
index ece8f596dbb2496fc039c9d16f4d0ca5dc3065ce..8ccf47b448b53b7a1d9bca16c0a62e0e2249f69e 100644
--- a/test/unitTest/plugins/plugins.cpp
+++ b/test/unitTest/plugins/plugins.cpp
@@ -507,18 +507,20 @@ PluginsTest::testTranslations()
     Manager::instance().pluginPreferences.setPluginsEnabled(true);
     setenv("JAMI_LANG", "en", true);
     Manager::instance().getJamiPluginManager().installPlugin(jplPath_, true);
-
     auto preferences = Manager::instance().getJamiPluginManager().getPluginPreferences(installationPath_, "");
     CPPUNIT_ASSERT(!preferences.empty());
     auto preferencesValuesEN = Manager::instance().getJamiPluginManager().getPluginPreferencesValuesMap(installationPath_, "");
+    auto detailsValuesEN = Manager::instance().getJamiPluginManager().getPluginDetails(installationPath_);
 
     setenv("JAMI_LANG", "fr", true);
 
     CPPUNIT_ASSERT(Manager::instance().getJamiPluginManager().getPluginPreferencesValuesMap(installationPath_, "") != preferencesValuesEN);
+    CPPUNIT_ASSERT(Manager::instance().getJamiPluginManager().getPluginDetails(installationPath_) != detailsValuesEN);
 
     setenv("JAMI_LANG", "en", true);
 
     CPPUNIT_ASSERT(Manager::instance().getJamiPluginManager().getPluginPreferencesValuesMap(installationPath_, "") == preferencesValuesEN);
+    CPPUNIT_ASSERT(Manager::instance().getJamiPluginManager().getPluginDetails(installationPath_) == detailsValuesEN);
 
     CPPUNIT_ASSERT(!Manager::instance().getJamiPluginManager().uninstallPlugin(installationPath_));
 }