diff --git a/core/API/DocumentationGenerator.php b/core/API/DocumentationGenerator.php index bbada6c27fcb6a2d3733be0272e059d2c83df5ff..0f5f678c15b04c5de51342abeaf84ef27a0b2a25 100644 --- a/core/API/DocumentationGenerator.php +++ b/core/API/DocumentationGenerator.php @@ -165,9 +165,11 @@ class Piwik_API_DocumentationGenerator // the parameter 'format' can be set to all API methods (used in tests) // the parameter 'hideIdSubDatable' is used for integration tests only // the parameter 'serialize' sets php outputs human readable, used in integration tests and debug + // the parameter 'language' sets the language for the response (eg. country names) $aParameters['format'] = false; $aParameters['hideIdSubDatable'] = false; $aParameters['serialize'] = false; + $aParameters['language'] = false; $moduleName = Piwik_API_Proxy::getInstance()->getModuleNameFromClassName($class); $urlExample = '?module=API&method='.$moduleName.'.'.$methodName.'&'; @@ -185,7 +187,6 @@ class Piwik_API_DocumentationGenerator return false; } } - return substr($urlExample,0,-1); } diff --git a/core/Common.php b/core/Common.php index 9ef0705ccc918a1f67babb71cb285b731177fe33..51e7d1cc8f70ec2402e33245f5b023ce7ca67c81 100644 --- a/core/Common.php +++ b/core/Common.php @@ -250,7 +250,8 @@ class Piwik_Common return; } $nextRunTime = $cache['lastTrackerCronRun'] + $minimumInterval; - if($cache['lastTrackerCronRun'] === false + if( (isset($GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS']) && $GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS']) + || $cache['lastTrackerCronRun'] === false || $nextRunTime < $now ) { $cache['lastTrackerCronRun'] = $now; diff --git a/core/FrontController.php b/core/FrontController.php index 96457287167a23d1e3c468f17871d2c2374eff87..c5c26910fe0481f9dda386b6b7cebc637f85ffde 100644 --- a/core/FrontController.php +++ b/core/FrontController.php @@ -239,13 +239,12 @@ class Piwik_FrontController } Zend_Registry::get('access')->reloadAccess($authAdapter); - Piwik_Translate::getInstance()->loadUserTranslation(); - Piwik::raiseMemoryLimitIfNecessary(); + Piwik_Translate::getInstance()->reloadLanguage(); - $pluginsManager->setLanguageToLoad( Piwik_Translate::getInstance()->getLanguageToLoad() ); - $pluginsManager->loadTranslations(); + Piwik::raiseMemoryLimitIfNecessary(); $pluginsManager->postLoadPlugins(); + Piwik_PostEvent('FrontController.checkForUpdates'); } catch(Exception $e) { diff --git a/core/PluginsManager.php b/core/PluginsManager.php index 4c029c785eb709ea9e8403c21107c9e82539ce43..e4b9d65a96ded9d5999acf319d810edc525c3bd4 100644 --- a/core/PluginsManager.php +++ b/core/PluginsManager.php @@ -37,7 +37,6 @@ class Piwik_PluginsManager public $dispatcher; protected $pluginsToLoad = array(); - protected $languageToLoad = null; protected $doLoadPlugins = true; protected $loadedPlugins = array(); @@ -193,13 +192,17 @@ class Piwik_PluginsManager $this->doLoadAlwaysActivatedPlugins = false; } - public function loadTranslations() + public function loadPluginTranslations($language = false) { + if(empty($language)) + { + $language = Piwik_Translate::getInstance()->getLanguageToLoad(); + } $plugins = $this->getLoadedPlugins(); foreach($plugins as $plugin) { - $this->loadTranslation( $plugin, $this->languageToLoad ); + $this->loadTranslation( $plugin, $language ); } } @@ -327,11 +330,6 @@ class Piwik_PluginsManager return $newPlugin; } - public function setLanguageToLoad( $code ) - { - $this->languageToLoad = $code; - } - /** * @param Piwik_Plugin $plugin */ diff --git a/core/TaskScheduler.php b/core/TaskScheduler.php index f13b1a4c7cf07fc740bd6999f318e44c0d26a150..dcc585d94fb59c1cf7ef7c7acf8fd6cf1d7c7fbf 100644 --- a/core/TaskScheduler.php +++ b/core/TaskScheduler.php @@ -46,8 +46,10 @@ class Piwik_TaskScheduler $timetable = unserialize($option); } - // DEBUG Force trigger all Scheduled tasks, uncomment -// $timetable = array(); + if(isset($GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS']) && $GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS']) + { + $timetable = array(); + } // Collects tasks Piwik_PostEvent(self::GET_TASKS_EVENT, $tasks); diff --git a/core/Translate.php b/core/Translate.php index 59561f218ebb56466d020ab68da4572dd92c5ba9..ca5da07fb70a8a2635cea3746df534767781ab50 100644 --- a/core/Translate.php +++ b/core/Translate.php @@ -16,7 +16,7 @@ class Piwik_Translate { static private $instance = null; - private $englishLanguageLoaded = false; + private $loadedLanguage = false; /** * @return Piwik_Translate @@ -32,26 +32,45 @@ class Piwik_Translate public function loadEnglishTranslation() { - require PIWIK_INCLUDE_PATH . '/lang/en.php'; - $this->mergeTranslationArray($translations); - $this->setLocale(); - $this->englishLanguageLoaded = true; + $this->loadTranslation('en'); } public function unloadEnglishTranslation() { $GLOBALS['Piwik_translations'] = array(); - $this->englishLanguageLoaded = false; } - public function loadUserTranslation() + public function reloadLanguage($language = false) { - $language = $this->getLanguageToLoad(); - if($language === 'en' - && $this->englishLanguageLoaded) + if(empty($language)) + { + $language = $this->getLanguageToLoad(); + } + Piwik_Translate::getInstance()->loadCoreTranslation($language); + Piwik_PluginsManager::getInstance()->loadPluginTranslations($language); + } + + /** + * Reads the specified code translation file in memory. + * + * @param $language 2 letter language code. If not specified, will detect current user translation, or load default translation. + * @return void + */ + public function loadCoreTranslation($language = false) + { + if(empty($language)) + { + $language = $this->getLanguageToLoad(); + } + if($this->loadedLanguage == $language) { return; } + $this->loadTranslation($language); + } + + private function loadTranslation($language) + { $path = PIWIK_INCLUDE_PATH . '/lang/' . $language . '.php'; if(!is_readable($path)) { @@ -60,6 +79,7 @@ class Piwik_Translate require $path; $this->mergeTranslationArray($translations); $this->setLocale(); + $this->loadedLanguage = $language; } public function mergeTranslationArray($translation) @@ -89,7 +109,7 @@ class Piwik_Translate $language = Piwik_Common::getRequestVar('language', is_null($language) ? '' : $language, 'string'); if(empty($language)) { - $language = Zend_Registry::get('config')->General->default_language; + $language = $this->getLanguageDefault(); } if( Piwik_Common::isValidFilename($language)) { @@ -101,6 +121,10 @@ class Piwik_Translate } } + public function getLanguageDefault() + { + return Zend_Registry::get('config')->General->default_language; + } /** * Generate javascript translations array * diff --git a/core/Url.php b/core/Url.php index 625747d57af297ba2bf31b1a0c368bbcc9cf7dd0..4e12c7f8cc0085c251c6c8d8f0c4fa657ee78d62 100644 --- a/core/Url.php +++ b/core/Url.php @@ -252,7 +252,8 @@ class Piwik_Url $query = ''; foreach($parameters as $name => $value) { - if(empty($value)) + if(is_null($value) + || $value === false) { continue; } diff --git a/lang/en.php b/lang/en.php index cdb002624b9a9bc8739111b41aac83d9e03c906d..7d28cd176256e9946c6b90f4b472ccb3827f3dd1 100644 --- a/lang/en.php +++ b/lang/en.php @@ -1200,7 +1200,7 @@ $translations = array( 'PDFReports_ReportsIncludedInPDF' => 'Reports included in PDF', 'PDFReports_CreatePDFReport' => 'Create PDF Report', 'PDFReports_UpdatePDFReport' => 'Update PDF Report', - 'PDFReports_PiwikReports' => 'Piwik reports', + 'PDFReports_PiwikReports' => 'Piwik Reports', 'PDFReports_DefaultPDFContainingAllReports' => 'Default PDF containing all available reports.', 'PDFReports_EmailHello' => 'Hello,', 'PDFReports_PleaseFindAttachedFile' => 'Please find in attached file your %1$s report for %2$s.', diff --git a/plugins/API/API.php b/plugins/API/API.php index 46556e21e9a46d59cc80a73f4ca8399e03619275..6013562358abac2703d1e1d5de044d19198aa088 100644 --- a/plugins/API/API.php +++ b/plugins/API/API.php @@ -96,17 +96,20 @@ class Piwik_API_API } /* - * Loads reports metadata, then return the requested one (possibly matching parameters, if passed) + * Loads reports metadata, then return the requested one, + * matching optional API parameters. */ - public function getMetadata($idSite, $apiModule, $apiAction, $apiParameters = array()) + public function getMetadata($idSite, $apiModule, $apiAction, $apiParameters = array(), $language = false) { + Piwik_Translate::getInstance()->reloadLanguage($language); static $reportsMetadata = array(); - if(!isset($reportsMetadata[$idSite])) + $cacheKey = $idSite.$language; + if(!isset($reportsMetadata[$cacheKey])) { - $reportsMetadata[$idSite] = Piwik_API_API::getInstance()->getReportMetadata($idSite); + $reportsMetadata[$cacheKey] = $this->getReportMetadata($idSite); } - foreach($reportsMetadata[$idSite] as $report) + foreach($reportsMetadata[$cacheKey] as $report) { if($report['module'] == $apiModule && $report['action'] == $apiAction) @@ -139,7 +142,7 @@ class Piwik_API_API public function getReportMetadata($idSites = false) { $idSites = Piwik_Site::getIdSitesFromIdSitesString($idSites); - + $availableReports = array(); Piwik_PostEvent('API.getReportMetadata', $availableReports, $idSites); foreach ($availableReports as &$availableReport) { @@ -200,17 +203,17 @@ class Piwik_API_API return $availableReports; } - public function getProcessedReport($idSite, $date, $period, $apiModule, $apiAction, $apiParameters = false) + public function getProcessedReport($idSite, $date, $period, $apiModule, $apiAction, $apiParameters = false, $language = false) { if($apiParameters === false) { $apiParameters = array(); } // Is this report found in the Metadata available reports? - $reportMetadata = $this->getMetadata($idSite, $apiModule, $apiAction, $apiParameters); + $reportMetadata = $this->getMetadata($idSite, $apiModule, $apiAction, $apiParameters, $language); if(empty($reportMetadata)) { - throw new Exception("Requested report not found in the list of available reports. \n"); + throw new Exception("Requested report $apiModule.$apiAction for Website id=$idSite not found in the list of available reports. \n"); } $reportMetadata = reset($reportMetadata); @@ -221,6 +224,8 @@ class Piwik_API_API 'period' => $period, 'date' => $date, 'format' => 'original', + 'serialize' => '0', + 'language' => $language, )); $url = Piwik_Url::getQueryStringFromParameters($parameters); $request = new Piwik_API_Request($url); @@ -230,7 +235,6 @@ class Piwik_API_API } catch(Exception $e) { throw new Exception("API returned an error: ".$e->getMessage()."\n"); } - // Table with a Dimension (Keywords, Pages, Browsers, etc.) if(isset($reportMetadata['dimension'])) { @@ -309,7 +313,7 @@ class Piwik_API_API if(isset($reportMetadata['processedMetrics'])) { - $processedMetricsAdded = Piwik_API_API::getInstance()->getDefaultProcessedMetrics(); + $processedMetricsAdded = $this->getDefaultProcessedMetrics(); foreach($processedMetricsAdded as $processedMetricId => $processedMetricTranslation) { // this processed metric can be displayed for this report diff --git a/plugins/CorePluginsAdmin/Controller.php b/plugins/CorePluginsAdmin/Controller.php index 8a430ea90cff847608f85e919f7c6f847976655c..1d2d46ac0667434b54247eec8a5121da028d7ae3 100644 --- a/plugins/CorePluginsAdmin/Controller.php +++ b/plugins/CorePluginsAdmin/Controller.php @@ -31,8 +31,7 @@ class Piwik_CorePluginsAdmin_Controller extends Piwik_Controller 'alwaysActivated' => Piwik_PluginsManager::getInstance()->isPluginAlwaysActivated($pluginName), ); } - - Piwik_PluginsManager::getInstance()->loadTranslations(); + Piwik_PluginsManager::getInstance()->loadPluginTranslations(); $loadedPlugins = Piwik_PluginsManager::getInstance()->getLoadedPlugins(); foreach($loadedPlugins as $oPlugin) diff --git a/plugins/Installation/Installation.php b/plugins/Installation/Installation.php index eefa73cc152edefc46a9271328e2588431e56d67..a32654381838980a7f07dc256fbc64a353be25d2 100644 --- a/plugins/Installation/Installation.php +++ b/plugins/Installation/Installation.php @@ -61,7 +61,7 @@ class Piwik_Installation extends Piwik_Plugin $message = ''; } - Piwik_Translate::getInstance()->loadUserTranslation(); + Piwik_Translate::getInstance()->loadCoreTranslation(); Piwik_PostEvent('Installation.startInstallation', $this); diff --git a/plugins/PDFReports/API.php b/plugins/PDFReports/API.php index 403bce594aee2555f62c63b8e748c5697e110237..372442dcdeae88d9485345fb37e1d10392fb40ba 100644 --- a/plugins/PDFReports/API.php +++ b/plugins/PDFReports/API.php @@ -197,11 +197,20 @@ class Piwik_PDFReports_API * @param int $idSite * @param string $period * @param string $date + * @param string|false $language * @param int $idReport If not passed, will generate a PDF containing all reports. * @param bool $outputType + * @param string $periodUsedFullReport If idReport == 0, will generate the report for the specified period */ - public function generateReport($idReport, $date, $idSite = false, $outputType = false, $periodUsedFullReport = 'day') + public function generateReport($idReport, $date, $idSite = false, $language = false, $outputType = false, $periodUsedFullReport = 'day') { + // Load specified language + if(empty($language)) + { + $language = Piwik_Translate::getInstance()->getLanguageDefault(); + } + Piwik_Translate::getInstance()->reloadLanguage($language); + // Available reports static $reportMetadata = null; if(is_null($reportMetadata)) @@ -237,10 +246,10 @@ class Piwik_PDFReports_API } } - // PDF will display the first 30 rows, then aggeregate other rows in a summary row + // PDF will display the first 30 rows, then aggregate other rows in a summary row $filterTruncateGET = Piwik_Common::getRequestVar('filter_truncate', false); $_GET['filter_truncate'] = 30; - + $date = Piwik_Date::factory($date)->toString('Y-m-d'); $websiteName = $prettyDate = false; @@ -254,12 +263,13 @@ class Piwik_PDFReports_API { $apiParameters = $action['parameters']; } - $report = Piwik_API_API::getInstance()->getProcessedReport($idSite, $date, $period, $apiModule, $apiAction, $apiParameters); + $report = Piwik_API_API::getInstance()->getProcessedReport($idSite, $date, $period, $apiModule, $apiAction, $apiParameters, $language); $websiteName = $report['website']; $prettyDate = $report['prettyDate']; $processedReports[] = $report; } + // Restore values if($filterTruncateGET !== false) { $_GET['filter_truncate'] = $filterTruncateGET; @@ -273,7 +283,7 @@ class Piwik_PDFReports_API $pdf->setReport($report['metadata'], $report['reportData'], $report['columns'], $report['reportMetadata']); $pdf->paintReport(); } - $outputFilename = 'Analytics report - '.$prettyDate.' - '.$websiteName.".pdf"; + $outputFilename = 'PDF Report - '.$idReport.'.'.$date.'.'.$idSite.'.'.$language.'.pdf'; switch($outputType) { @@ -305,19 +315,13 @@ class Piwik_PDFReports_API { $reports = $this->getReports($idSite, $period = false, $idReport); $report = reset($reports); - list($outputFilename, $prettyDate, $websiteName) = - $this->generateReport( - $idReport, - Piwik_Date::now()->subPeriod(1, $report['period'])->toString(), - $idSite, - $outputType = Piwik_PDFReports_API::OUTPUT_PDF_SAVE_ON_DISK - ); - + // Get user emails and languages $emails = self::getEmailsFromString($report['additional_emails']); if($report['email_me'] == 1) - { - if(Piwik::getCurrentUserLogin() == $report['login']) + { + if(Piwik::getCurrentUserLogin() == $report['login'] + || Piwik::isUserIsSuperUser()) { $emails[] = Piwik::getCurrentUserEmail(); } @@ -327,6 +331,16 @@ class Piwik_PDFReports_API $emails[] = $user['email']; } } + $language = Piwik_LanguagesManager_API::getInstance()->getLanguageForUser($report['login']); + list($outputFilename, $prettyDate, $websiteName) = + $this->generateReport( + $idReport, + Piwik_Date::now()->subPeriod(1, $report['period'])->toString(), + $idSite, + $language, + $outputType = Piwik_PDFReports_API::OUTPUT_PDF_SAVE_ON_DISK + ); + $this->sendReportEmailPdfAttached($emails, $outputFilename, $prettyDate, $websiteName, $report); } @@ -335,7 +349,7 @@ class Piwik_PDFReports_API $periods = self::getPeriodToFrequency(); $message = Piwik_Translate('PDFReports_EmailHello'); $message .= "\n" . Piwik_Translate('PDFReports_PleaseFindAttachedFile', array($periods[$report['period']], $websiteName)); - $subject = "Reports " . $websiteName . " - ".$prettyDate; + $subject = Piwik_Translate('General_Report') . ' '. $websiteName . " - ".$prettyDate; if(!file_exists($outputFilename)) { @@ -351,12 +365,13 @@ class Piwik_PDFReports_API $mail->setBodyText($message); $fromEmailName = Piwik_Translate('PDFReports_PiwikReports'); $fromEmailAddress = Zend_Registry::get('config')->General->noreply_email_address; + $attachmentName = $subject; $mail->setFrom($fromEmailAddress, $fromEmailName); $mail->createAttachment( $contents, 'application/pdf', Zend_Mime::DISPOSITION_INLINE, Zend_Mime::ENCODING_BASE64, - $filename + $attachmentName.'.pdf' ); foreach ($emails as $email) @@ -366,8 +381,14 @@ class Piwik_PDFReports_API try { $mail->send(); } catch(Exception $e) { - throw new Exception("An error occured while sending the PDF Report ". - " to ". implode(', ',$mail->getRecipients()). ". Error was '". $e->getMessage()."'"); + + // If running from piwik.php with debug, we ignore the 'email not sent' error + if(!isset($GLOBALS['PIWIK_TRACKER_DEBUG']) || !$GLOBALS['PIWIK_TRACKER_DEBUG']) + { + throw new Exception("An error occured while sending '$filename' ". + " to ". implode(', ',$mail->getRecipients()). ". + Error was '". $e->getMessage()."'"); + } } $mail->clearRecipients(); } @@ -377,8 +398,11 @@ class Piwik_PDFReports_API "idreport = " . $report['idreport'] ); - // Remove PDF file - unlink($outputFilename); + // If running from piwik.php with debug, do not delete the PDF after sending the email + if(!isset($GLOBALS['PIWIK_TRACKER_DEBUG']) || !$GLOBALS['PIWIK_TRACKER_DEBUG']) + { + unlink($outputFilename); + } } private function checkAdditionalEmails($additionalEmails) diff --git a/tests/integration/Integration.php b/tests/integration/Integration.php index 9d83f208364bc4ff31b6516c09dc53bd94334a90..8fda09c6676dc619e5ed1d0479741ff9b2530387 100644 --- a/tests/integration/Integration.php +++ b/tests/integration/Integration.php @@ -212,10 +212,10 @@ abstract class Test_Integration extends Test_Database * @param $formats Array of 'format' to fetch from API * @param $periods Array of 'period' to query API * @param $setDateLastN If set to true, the 'date' parameter will be rewritten to query instead a range of dates, rather than one period only. - * + * @param $language 2 letter language code, defaults to default piwik language * @return array of API URLs query strings */ - protected function generateUrlsApi( $parametersToSet, $formats, $periods, $setDateLastN = false ) + protected function generateUrlsApi( $parametersToSet, $formats, $periods, $setDateLastN = false, $language = false ) { // Get the URLs to query against the API for all functions starting with get* $skipped = $requestUrls = array(); @@ -263,6 +263,11 @@ abstract class Test_Integration extends Test_Database $parametersToSet['date'] = $firstDate . ',' . $secondDate; } + // Set response language + if($language !== false) + { + $parametersToSet['language'] = $language; + } // Generate for each specified format foreach($formats as $format) { @@ -307,16 +312,22 @@ abstract class Test_Integration extends Test_Database * @param $formats String or array of formats to fetch from API * @param $idSite Id site * @param $dateTime Date time string of reports to request + * @param $periods String or array of strings of periods (day, week, month, year) * @param $setDateLastN When set to true, 'date' parameter passed to API request will be rewritten to query a range of dates rather than 1 date only + * @param $language 2 letter language code to request data in * * @return void */ - function callGetApiCompareOutput($testName, $formats = 'xml', $idSite = false, $dateTime = false, $periods = 'day', $setDateLastN = false) + function callGetApiCompareOutput($testName, $formats = 'xml', $idSite = false, $dateTime = false, $periods = false, $setDateLastN = false, $language = false) { $path = $this->getPathToTestDirectory(); $pathProcessed = $path . "/processed/"; $pathExpected = $path . "/expected/"; + if($periods === false) + { + $periods = 'day'; + } if(!is_writable($pathProcessed)) { $this->fail('To run the tests, you need to give write permissions to the following directory (create it if it doesn\'t exist).<code><br/>mkdir '. $pathProcessed.'<br/>chmod 777 '.$pathProcessed.'</code><br/>'); @@ -339,7 +350,7 @@ abstract class Test_Integration extends Test_Database { $periods = array($periods); } - $requestUrls = $this->generateUrlsApi($parametersToSet, $formats, $periods, $setDateLastN); + $requestUrls = $this->generateUrlsApi($parametersToSet, $formats, $periods, $setDateLastN, $language); foreach($requestUrls as $apiId => $requestUrl) { diff --git a/tests/integration/Main.test.php b/tests/integration/Main.test.php index 8abee9c2a2e9f4e0edb8ce64176c10879c87a2f1..6f090800331a0b8a4ae4c1703deee4d46e210304 100644 --- a/tests/integration/Main.test.php +++ b/tests/integration/Main.test.php @@ -55,13 +55,18 @@ class Test_Piwik_Integration_Main extends Test_Integration $this->callGetApiCompareOutput(__FUNCTION__, 'xml', $idSite, $dateTime); } + /** + * Test the Yearly metadata API response, + * with no visits, with custom response language + */ function test_apiGetReportMetadata_year() { $this->setApiNotToCall(array()); $this->setApiToCall( 'API.getProcessedReport' ); $dateTime = '2009-01-04 00:11:42'; $idSite = $this->createWebsite($dateTime); - $this->callGetApiCompareOutput(__FUNCTION__, 'xml', $idSite, $dateTime, 'year'); + $language = 'fr'; + $this->callGetApiCompareOutput(__FUNCTION__, 'xml', $idSite, $dateTime, 'year', $setDateLastN = false, $language); } /* diff --git a/tests/integration/expected/test_apiGetReportMetadata_year__API.getProcessedReport_year.xml b/tests/integration/expected/test_apiGetReportMetadata_year__API.getProcessedReport_year.xml index 9c57b325a670b2f77fcc64321c57dabc0c0c7d45..defc7a3adaf5d121f793ad7ffd76e8e7d52a23ca 100644 --- a/tests/integration/expected/test_apiGetReportMetadata_year__API.getProcessedReport_year.xml +++ b/tests/integration/expected/test_apiGetReportMetadata_year__API.getProcessedReport_year.xml @@ -3,45 +3,45 @@ <website>Piwik test</website> <prettyDate>2009</prettyDate> <metadata> - <category>Visitors</category> - <name>Country</name> + <category>Visiteurs</category> + <name>Pays</name> <module>UserCountry</module> <action>getCountry</action> - <dimension>Country</dimension> + <dimension>Pays</dimension> <metrics> - <nb_visits>Visits</nb_visits> + <nb_visits>Visites</nb_visits> <nb_actions>Actions</nb_actions> </metrics> <processedMetrics> - <nb_actions_per_visit>Actions per Visit</nb_actions_per_visit> - <avg_time_on_site>Avg. Time on Website</avg_time_on_site> - <bounce_rate>Bounce Rate</bounce_rate> - <conversion_rate>Conversion Rate</conversion_rate> + <nb_actions_per_visit>Actions par visite</nb_actions_per_visit> + <avg_time_on_site>Temps moyen sur le site</avg_time_on_site> + <bounce_rate>Taux de rebond</bounce_rate> + <conversion_rate>Taux de conversion</conversion_rate> </processedMetrics> <metricsGoal> <nb_conversions>Conversions</nb_conversions> - <conversion_rate>Conversion Rate</conversion_rate> - <revenue>Revenue</revenue> + <conversion_rate>Taux de conversion</conversion_rate> + <revenue>Revenu</revenue> </metricsGoal> <processedMetricsGoal> - <revenue_per_visit>Value per Visit</revenue_per_visit> + <revenue_per_visit>Valeur par Visite</revenue_per_visit> </processedMetricsGoal> <uniqueId>UserCountry_getCountry</uniqueId> </metadata> <columns> - <label>Country</label> - <nb_visits>Visits</nb_visits> + <label>Pays</label> + <nb_visits>Visites</nb_visits> <nb_actions>Actions</nb_actions> - <nb_actions_per_visit>Actions per Visit</nb_actions_per_visit> - <avg_time_on_site>Avg. Time on Website</avg_time_on_site> - <bounce_rate>Bounce Rate</bounce_rate> - <conversion_rate>Conversion Rate</conversion_rate> - <revenue>Revenue</revenue> + <nb_actions_per_visit>Actions par visite</nb_actions_per_visit> + <avg_time_on_site>Temps moyen sur le site</avg_time_on_site> + <bounce_rate>Taux de rebond</bounce_rate> + <conversion_rate>Taux de conversion</conversion_rate> + <revenue>Revenu</revenue> </columns> <reportData/>