diff --git a/core/PluginsManager.php b/core/PluginsManager.php
index 51a22ba8dfd4a34bfb2e50138a4fdd0d243f0d12..32c469c744cfafc474159e56c5c1b037fe4df602 100644
--- a/core/PluginsManager.php
+++ b/core/PluginsManager.php
@@ -617,7 +617,7 @@ class PluginsManager
 
         $pluginName = $plugin->getPluginName();
 
-        $path = self::getPluginsDirectory() . $pluginName . '/lang/%s.php';
+        $path = self::getPluginsDirectory() . $pluginName . '/lang/%s.json';
 
         $defaultLangPath = sprintf($path, $langCode);
         $defaultEnglishLangPath = sprintf($path, 'en');
@@ -625,13 +625,19 @@ class PluginsManager
         $translations = array();
 
         if (file_exists($defaultLangPath)) {
-            require $defaultLangPath;
+            $data = file_get_contents($defaultLangPath);
+            $translations = json_decode($data, true);
         } elseif (file_exists($defaultEnglishLangPath)) {
-            require $defaultEnglishLangPath;
+            $data = file_get_contents($defaultEnglishLangPath);
+            $translations = json_decode($data, true);
         } else {
             return false;
         }
-        Translate::getInstance()->mergeTranslationArray($translations);
+
+        if(isset($translations[$pluginName])) {
+            // only merge translations of plugin - prevents overwritten strings
+            Translate::getInstance()->mergeTranslationArray(array($pluginName => $translations[$pluginName]));
+        }
         return true;
     }
 
diff --git a/core/Translate.php b/core/Translate.php
index 6ff5b3d6d897c2021733b263f19dfa08a1afee02..045774b855119563db209f63dd7d86340d74d898 100644
--- a/core/Translate.php
+++ b/core/Translate.php
@@ -73,12 +73,12 @@ class Translate
 
     private function loadCoreTranslationFile($language)
     {
-        $translations = array();
-        $path = PIWIK_INCLUDE_PATH . '/lang/' . $language . '.php';
+        $path = PIWIK_INCLUDE_PATH . '/lang/' . $language . '.json';
         if (!Common::isValidFilename($language) || !is_readable($path)) {
             throw new Exception(Piwik_TranslateException('General_ExceptionLanguageFileNotFound', array($language)));
         }
-        require $path;
+	    $data = file_get_contents($path);
+        $translations = json_decode($data, true);
         $this->mergeTranslationArray($translations);
         $this->setLocale();
         $this->loadedLanguage = $language;
@@ -90,7 +90,7 @@ class Translate
             $GLOBALS['Piwik_translations'] = array();
         }
         // we could check that no string overlap here
-        $GLOBALS['Piwik_translations'] = array_merge($GLOBALS['Piwik_translations'], array_filter($translation, 'strlen'));
+        $GLOBALS['Piwik_translations'] = array_merge_recursive($GLOBALS['Piwik_translations'], $translation);
     }
 
     /**
@@ -140,22 +140,25 @@ class Translate
 
         $js = 'var translations = {';
 
-        $moduleRegex = '#^(';
-        foreach ($moduleList as $module) {
-            $moduleRegex .= $module . '|';
-        }
-        $moduleRegex = substr($moduleRegex, 0, -1);
-        $moduleRegex .= ')_.*_js$#i';
+        $moduleRegex = '#^.*_js$#i';
 
-        // Hack: common translations used in JS but not only, force as them to be defined in JS
+        // Hack: common translations used in JS but not only, force them to be defined in JS
         $translations = $GLOBALS['Piwik_translations'];
         $toSetInJs = array('General_Save', 'General_OrCancel');
         foreach ($toSetInJs as $toSetId) {
-            $translations[$toSetId . '_js'] = $translations[$toSetId];
+            list($plugin, $key) = explode("_", $toSetId, 2);
+            $translations[$plugin][$key . '_js'] = $translations[$plugin][$key];
         }
-        foreach ($translations as $key => $value) {
-            if (preg_match($moduleRegex, $key)) {
-                $js .= '"' . $key . '": "' . str_replace('"', '\"', $value) . '",';
+        foreach ($translations as $module => $keys) {
+            // Skip modules
+            if(!in_array($module, $moduleList)) {
+                continue;
+            }
+            foreach($keys as $key => $value) {
+                // Find keys ending with _js
+                if (preg_match($moduleRegex, $key)) {
+                    $js .= sprintf('"%s_%s": "%s",', $module, $key, str_replace('"', '\"', $value));
+                }
             }
         }
         $js = substr($js, 0, -1);
@@ -175,7 +178,7 @@ class Translate
      */
     private function setLocale()
     {
-        $locale = $GLOBALS['Piwik_translations']['General_Locale'];
+        $locale = $GLOBALS['Piwik_translations']['General']['Locale'];
         $locale_variant = str_replace('UTF-8', 'UTF8', $locale);
         setlocale(LC_ALL, $locale, $locale_variant);
         setlocale(LC_CTYPE, '');
diff --git a/core/functions.php b/core/functions.php
index 98c27e322ab7e1c808ea03417408496ddeb29cd8..c706a17b6a9cd5bf8dbeea4306751313dfcd0fd0 100644
--- a/core/functions.php
+++ b/core/functions.php
@@ -66,8 +66,12 @@ namespace {
         if (!is_array($args)) {
             $args = array($args);
         }
-        if (isset($GLOBALS['Piwik_translations'][$string])) {
-            $string = $GLOBALS['Piwik_translations'][$string];
+
+        if(strpos($string, "_") !== FALSE) {
+            list($plugin, $key) = explode("_", $string, 2);
+            if (isset($GLOBALS['Piwik_translations'][$plugin]) && isset($GLOBALS['Piwik_translations'][$plugin][$key])) {
+                $string = $GLOBALS['Piwik_translations'][$plugin][$key];
+            }
         }
         if (count($args) == 0) {
             return $string;