This is an automated email from the ASF dual-hosted git repository.

michaelo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-invoker-plugin.git


The following commit(s) were added to refs/heads/master by this push:
     new a154e5b  [MINVOKER-344] Move reporting rendering logic into a 
ReportRenderer class
a154e5b is described below

commit a154e5b13dd7d900829d1d951efff50c41f25738
Author: Michael Osipov <[email protected]>
AuthorDate: Sun Jun 11 16:07:27 2023 +0200

    [MINVOKER-344] Move reporting rendering logic into a ReportRenderer class
    
    This closes #193
---
 .../maven/plugins/invoker/InvokerReport.java       | 254 ++-------------------
 .../plugins/invoker/InvokerReportRenderer.java     | 210 +++++++++++++++++
 src/main/resources/invoker-report.properties       |  37 ++-
 src/main/resources/invoker-report_de.properties    |  33 ++-
 src/main/resources/invoker-report_fr.properties    |  35 ++-
 5 files changed, 281 insertions(+), 288 deletions(-)

diff --git a/src/main/java/org/apache/maven/plugins/invoker/InvokerReport.java 
b/src/main/java/org/apache/maven/plugins/invoker/InvokerReport.java
index 40cc33b..339705a 100644
--- a/src/main/java/org/apache/maven/plugins/invoker/InvokerReport.java
+++ b/src/main/java/org/apache/maven/plugins/invoker/InvokerReport.java
@@ -20,15 +20,10 @@ package org.apache.maven.plugins.invoker;
 
 import java.io.File;
 import java.io.IOException;
-import java.text.DecimalFormat;
-import java.text.DecimalFormatSymbols;
-import java.text.MessageFormat;
-import java.text.NumberFormat;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 
-import org.apache.maven.doxia.sink.Sink;
 import org.apache.maven.plugins.annotations.Component;
 import org.apache.maven.plugins.annotations.Mojo;
 import org.apache.maven.plugins.annotations.Parameter;
@@ -52,12 +47,6 @@ import 
org.codehaus.plexus.util.xml.pull.XmlPullParserException;
 @Mojo(name = "report", threadSafe = true)
 public class InvokerReport extends AbstractMavenReport {
 
-    /**
-     * Internationalization component.
-     */
-    @Component
-    protected I18N i18n;
-
     /**
      * Base directory where all build reports have been written to.
      */
@@ -65,58 +54,14 @@ public class InvokerReport extends AbstractMavenReport {
     private File reportsDirectory;
 
     /**
-     * The number format used to print percent values in the report locale.
-     */
-    private NumberFormat percentFormat;
-
-    /**
-     * The number format used to print time values in the report locale.
-     */
-    private NumberFormat secondsFormat;
-
-    /**
-     * The format used to print build name and description.
+     * Internationalization component
      */
-    private MessageFormat nameAndDescriptionFormat;
+    @Component
+    protected I18N i18n;
 
     protected void executeReport(Locale locale) throws MavenReportException {
-        DecimalFormatSymbols symbols = new DecimalFormatSymbols(locale);
-        percentFormat = new DecimalFormat(getText(locale, 
"report.invoker.format.percent"), symbols);
-        secondsFormat = new DecimalFormat(getText(locale, 
"report.invoker.format.seconds"), symbols);
-        nameAndDescriptionFormat = new MessageFormat(getText(locale, 
"report.invoker.format.name_with_description"));
-
-        Sink sink = getSink();
-
-        sink.head();
-
-        sink.title();
-        sink.text(getText(locale, "report.invoker.result.title"));
-        sink.title_();
-
-        sink.head_();
-
-        sink.body();
-
-        sink.section1();
-        sink.sectionTitle1();
-        sink.text(getText(locale, "report.invoker.result.title"));
-        sink.sectionTitle1_();
-        sink.paragraph();
-        sink.text(getText(locale, "report.invoker.result.description"));
-        sink.paragraph_();
-        sink.section1_();
-
-        // ----------------------------------
-        // build buildJob beans
-        // ----------------------------------
-        File[] reportFiles = ReportUtils.getReportFiles(reportsDirectory);
-        if (reportFiles.length <= 0) {
-            getLog().info("no invoker report files found, skip report 
generation");
-            return;
-        }
-
+        File[] reportFiles = getReportFiles();
         BuildJobXpp3Reader buildJobReader = new BuildJobXpp3Reader();
-
         List<BuildJob> buildJobs = new ArrayList<>(reportFiles.length);
         for (File reportFile : reportFiles) {
             try (XmlStreamReader xmlReader = 
ReaderFactory.newXmlReader(reportFile)) {
@@ -127,193 +72,38 @@ public class InvokerReport extends AbstractMavenReport {
                 throw new MavenReportException("Failed to read report file: " 
+ reportFile, e);
             }
         }
-
-        // ----------------------------------
-        // summary
-        // ----------------------------------
-
-        constructSummarySection(buildJobs, locale);
-
-        // ----------------------------------
-        // per file/it detail
-        // ----------------------------------
-
-        sink.section2();
-        sink.sectionTitle2();
-
-        sink.text(getText(locale, "report.invoker.detail.title"));
-
-        sink.sectionTitle2_();
-
-        sink.section2_();
-
-        // detail tests table header
-        sink.table();
-        sink.tableRows(null, false);
-
-        sink.tableRow();
-        // -------------------------------------------
-        // name | Result | time | message
-        // -------------------------------------------
-        sinkTableHeader(sink, getText(locale, "report.invoker.detail.name"));
-        sinkTableHeader(sink, getText(locale, "report.invoker.detail.result"));
-        sinkTableHeader(sink, getText(locale, "report.invoker.detail.time"));
-        sinkTableHeader(sink, getText(locale, 
"report.invoker.detail.message"));
-
-        sink.tableRow_();
-
-        for (BuildJob buildJob : buildJobs) {
-            renderBuildJob(buildJob);
-        }
-
-        sink.tableRows_();
-        sink.table_();
-
-        sink.body_();
-
-        sink.flush();
-        sink.close();
+        InvokerReportRenderer r = new InvokerReportRenderer(getSink(), i18n, 
locale, getLog(), buildJobs);
+        r.render();
     }
 
-    private void constructSummarySection(List<? extends BuildJob> buildJobs, 
Locale locale) {
-        Sink sink = getSink();
-
-        sink.section2();
-        sink.sectionTitle2();
-
-        sink.text(getText(locale, "report.invoker.summary.title"));
-
-        sink.sectionTitle2_();
-        sink.section2_();
-
-        // 
------------------------------------------------------------------------
-        // Building a table with
-        // it number | succes nb | failed nb | Success rate | total time | avg 
time
-        // 
------------------------------------------------------------------------
-
-        sink.table();
-        sink.tableRows(null, false);
-
-        sink.tableRow();
-
-        sinkTableHeader(sink, getText(locale, 
"report.invoker.summary.number"));
-        sinkTableHeader(sink, getText(locale, 
"report.invoker.summary.success"));
-        sinkTableHeader(sink, getText(locale, 
"report.invoker.summary.failed"));
-        sinkTableHeader(sink, getText(locale, 
"report.invoker.summary.skipped"));
-        sinkTableHeader(sink, getText(locale, 
"report.invoker.summary.success.rate"));
-        sinkTableHeader(sink, getText(locale, 
"report.invoker.summary.time.total"));
-        sinkTableHeader(sink, getText(locale, 
"report.invoker.summary.time.avg"));
-
-        int number = buildJobs.size();
-        int success = 0;
-        int failed = 0;
-        int skipped = 0;
-        double totalTime = 0;
-
-        for (BuildJob buildJob : buildJobs) {
-            if (BuildJob.Result.SUCCESS.equals(buildJob.getResult())) {
-                success++;
-            } else if (BuildJob.Result.SKIPPED.equals(buildJob.getResult())) {
-                skipped++;
-            } else {
-                failed++;
-            }
-            totalTime += buildJob.getTime();
-        }
-
-        sink.tableRow_();
-        sink.tableRow();
-
-        sinkCell(sink, Integer.toString(number));
-        sinkCell(sink, Integer.toString(success));
-        sinkCell(sink, Integer.toString(failed));
-        sinkCell(sink, Integer.toString(skipped));
-
-        if (success + failed > 0) {
-            sinkCell(sink, percentFormat.format((double) success / (success + 
failed)));
-        } else {
-            sinkCell(sink, "");
-        }
-
-        sinkCell(sink, secondsFormat.format(totalTime));
-
-        sinkCell(sink, secondsFormat.format(totalTime / number));
-
-        sink.tableRow_();
-
-        sink.tableRows_();
-        sink.table_();
-    }
-
-    private void renderBuildJob(BuildJob buildJob) {
-        Sink sink = getSink();
-        sink.tableRow();
-        sinkCell(sink, getBuildJobReportName(buildJob));
-        // FIXME image
-        sinkCell(sink, buildJob.getResult());
-        sinkCell(sink, secondsFormat.format(buildJob.getTime()));
-        sinkCell(sink, buildJob.getFailureMessage());
-        sink.tableRow_();
-    }
-
-    private String getBuildJobReportName(BuildJob buildJob) {
-        String buildJobName = buildJob.getName();
-        String buildJobDescription = buildJob.getDescription();
-        boolean emptyJobName = buildJobName == null || buildJobName.isEmpty();
-        boolean emptyJobDescription = buildJobDescription == null || 
buildJobDescription.isEmpty();
-        boolean isReportJobNameComplete = !emptyJobName && 
!emptyJobDescription;
-        if (isReportJobNameComplete) {
-            return getFormattedName(buildJobName, buildJobDescription);
-        } else {
-            String buildJobProject = buildJob.getProject();
-            if (!emptyJobName) {
-                getLog().warn(incompleteNameWarning("description", 
buildJobProject));
-            } else if (!emptyJobDescription) {
-                getLog().warn(incompleteNameWarning("name", buildJobProject));
-            }
-            return buildJobProject;
-        }
-    }
-
-    private static String incompleteNameWarning(String missing, String pom) {
-        return String.format(
-                "Incomplete job name-description: %s is missing. " + "POM (%s) 
will be used in place of job name.",
-                missing, pom);
+    /**
+     * @param locale The locale
+     * @param key The key to search for
+     * @return The text appropriate for the locale.
+     */
+    private String getI18nString(Locale locale, String key) {
+        return i18n.getString("invoker-report", locale, "report.invoker." + 
key);
     }
 
-    private String getFormattedName(String name, String description) {
-        return nameAndDescriptionFormat.format(new Object[] {name, 
description});
+    /** {@inheritDoc} */
+    public String getName(Locale locale) {
+        return getI18nString(locale, "name");
     }
 
+    /** {@inheritDoc} */
     public String getDescription(Locale locale) {
-        return getText(locale, "report.invoker.result.description");
-    }
-
-    public String getName(Locale locale) {
-        return getText(locale, "report.invoker.result.name");
+        return getI18nString(locale, "description");
     }
 
     public String getOutputName() {
         return "invoker-report";
     }
 
-    public boolean canGenerateReport() {
-        return ReportUtils.getReportFiles(reportsDirectory).length > 0;
+    private File[] getReportFiles() {
+        return ReportUtils.getReportFiles(reportsDirectory);
     }
 
-    private String getText(Locale locale, String key) {
-        return i18n.getString("invoker-report", locale, key);
-    }
-
-    private void sinkTableHeader(Sink sink, String header) {
-        sink.tableHeaderCell();
-        sink.text(header);
-        sink.tableHeaderCell_();
-    }
-
-    private void sinkCell(Sink sink, String text) {
-        sink.tableCell();
-        sink.text(text);
-        sink.tableCell_();
+    public boolean canGenerateReport() {
+        return getReportFiles().length > 0;
     }
 }
diff --git 
a/src/main/java/org/apache/maven/plugins/invoker/InvokerReportRenderer.java 
b/src/main/java/org/apache/maven/plugins/invoker/InvokerReportRenderer.java
new file mode 100644
index 0000000..eae9739
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugins/invoker/InvokerReportRenderer.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.plugins.invoker;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.MessageFormat;
+import java.text.NumberFormat;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.plugins.invoker.model.BuildJob;
+import org.apache.maven.reporting.AbstractMavenReportRenderer;
+import org.codehaus.plexus.i18n.I18N;
+
+public class InvokerReportRenderer extends AbstractMavenReportRenderer {
+    private final I18N i18n;
+    private final Locale locale;
+    private final Log log;
+    private final List<BuildJob> buildJobs;
+
+    /**
+     * The number format used to print percent values in the report locale.
+     */
+    private NumberFormat percentFormat;
+
+    /**
+     * The number format used to print time values in the report locale.
+     */
+    private NumberFormat secondsFormat;
+
+    /**
+     * The format used to print build name and description.
+     */
+    private MessageFormat nameAndDescriptionFormat;
+
+    public InvokerReportRenderer(Sink sink, I18N i18n, Locale locale, Log log, 
List<BuildJob> buildJobs) {
+        super(sink);
+        this.i18n = i18n;
+        this.locale = locale;
+        this.log = log;
+        this.buildJobs = buildJobs;
+    }
+
+    @Override
+    public String getTitle() {
+        return getI18nString("title");
+    }
+
+    /**
+     * @param key The key to translate.
+     * @return the translated key.
+     */
+    private String getI18nString(String key) {
+        return i18n.getString("invoker-report", locale, "report.invoker." + 
key);
+    }
+
+    /**
+     * @param key The key to translate.
+     * @param args The args to pass to translated string.
+     * @return the translated key.
+     */
+    private String formatI18nString(String key, Object... args) {
+        return i18n.format("invoker-report", locale, "report.invoker." + key, 
args);
+    }
+
+    @Override
+    protected void renderBody() {
+        DecimalFormatSymbols symbols = new DecimalFormatSymbols(locale);
+        percentFormat = new DecimalFormat(getI18nString("format.percent"), 
symbols);
+        secondsFormat = new DecimalFormat(getI18nString("format.seconds"), 
symbols);
+        nameAndDescriptionFormat = new 
MessageFormat(getI18nString("format.name_with_description"));
+
+        startSection(getTitle());
+        paragraph(getI18nString("description"));
+
+        renderSectionSummary();
+
+        renderSectionDetails();
+
+        endSection();
+    }
+
+    private void renderSectionSummary() {
+        startSection(getI18nString("summary.title"));
+
+        startTable();
+
+        tableHeader(new String[] {
+            getI18nString("summary.builds"),
+            getI18nString("summary.success"),
+            getI18nString("summary.failures"),
+            getI18nString("summary.skipped"),
+            getI18nString("summary.successrate"),
+            getI18nString("summary.time")
+        });
+
+        int totalBuilds = buildJobs.size();
+        int totalSuccess = 0;
+        int totalFailures = 0;
+        int totalSkipped = 0;
+        float totalTime = 0.0f;
+
+        for (BuildJob buildJob : buildJobs) {
+            switch (buildJob.getResult()) {
+                case BuildJob.Result.SUCCESS:
+                    totalSuccess++;
+                    break;
+                case BuildJob.Result.SKIPPED:
+                    totalSkipped++;
+                    break;
+                default:
+                    totalFailures++;
+            }
+            totalTime += buildJob.getTime();
+        }
+
+        tableRow(new String[] {
+            Integer.toString(totalBuilds),
+            Integer.toString(totalSuccess),
+            Integer.toString(totalFailures),
+            Integer.toString(totalSkipped),
+            (totalSuccess + totalFailures > 0)
+                    ? percentFormat.format(totalSuccess / (float) 
(totalSuccess + totalFailures))
+                    : "",
+            secondsFormat.format(totalTime)
+        });
+
+        endTable();
+
+        endSection();
+    }
+
+    private void renderSectionDetails() {
+        startSection(getI18nString("detail.title"));
+
+        startTable();
+
+        tableHeader(new String[] {
+            getI18nString("detail.name"),
+            getI18nString("detail.result"),
+            getI18nString("detail.time"),
+            getI18nString("detail.message")
+        });
+
+        for (BuildJob buildJob : buildJobs) {
+            renderBuildJob(buildJob);
+        }
+
+        endTable();
+
+        endSection();
+    }
+
+    private void renderBuildJob(BuildJob buildJob) {
+        tableRow(new String[] {
+            getBuildJobReportName(buildJob),
+            // FIXME image
+            buildJob.getResult(),
+            secondsFormat.format(buildJob.getTime()),
+            buildJob.getFailureMessage()
+        });
+    }
+
+    private String getBuildJobReportName(BuildJob buildJob) {
+        String buildJobName = buildJob.getName();
+        String buildJobDescription = buildJob.getDescription();
+        boolean emptyJobName = buildJobName == null || buildJobName.isEmpty();
+        boolean emptyJobDescription = buildJobDescription == null || 
buildJobDescription.isEmpty();
+        boolean isReportJobNameComplete = !emptyJobName && 
!emptyJobDescription;
+        if (isReportJobNameComplete) {
+            return getFormattedName(buildJobName, buildJobDescription);
+        } else {
+            String buildJobProject = buildJob.getProject();
+            if (!emptyJobName) {
+                log.warn(incompleteNameWarning("description", 
buildJobProject));
+            } else if (!emptyJobDescription) {
+                log.warn(incompleteNameWarning("name", buildJobProject));
+            }
+            return buildJobProject;
+        }
+    }
+
+    private static String incompleteNameWarning(String missing, String pom) {
+        return "Incomplete job name-description: " + missing + " is missing. 
POM (" + pom
+                + ") will be used in place of job name!";
+    }
+
+    private String getFormattedName(String name, String description) {
+        return nameAndDescriptionFormat.format(new Object[] {name, 
description});
+    }
+}
diff --git a/src/main/resources/invoker-report.properties 
b/src/main/resources/invoker-report.properties
index 5fe569d..e746c3e 100644
--- a/src/main/resources/invoker-report.properties
+++ b/src/main/resources/invoker-report.properties
@@ -15,22 +15,21 @@
 # specific language governing permissions and limitations
 # under the License.
 
-report.invoker.result.description   = The results of the Maven invocations.
-report.invoker.result.name          = Invoker Build Results
-report.invoker.result.title         = Invoker Report
-report.invoker.summary.title        = Summary
-report.invoker.summary.number       = Builds
-report.invoker.summary.success      = Success
-report.invoker.summary.failed       = Failures
-report.invoker.summary.skipped      = Skipped
-report.invoker.summary.success.rate = Success Rate
-report.invoker.summary.time.total   = Total Time
-report.invoker.summary.time.avg     = Avg Time
-report.invoker.detail.title         = Build Details
-report.invoker.detail.name          = Name
-report.invoker.detail.result        = Result
-report.invoker.detail.time          = Time
-report.invoker.detail.message       = Message
-report.invoker.format.percent       = 0.0%
-report.invoker.format.seconds       = 0.0\u00A0s
-report.invoker.format.name_with_description = {0}: {1}
+report.invoker.name=Invoker
+report.invoker.description=Report on the build results of the Maven 
invocations.
+report.invoker.title=Invoker Report
+report.invoker.summary.title=Summary
+report.invoker.summary.builds=Builds
+report.invoker.summary.success=Success
+report.invoker.summary.failures=Failures
+report.invoker.summary.skipped=Skipped
+report.invoker.summary.successrate=Success Rate
+report.invoker.summary.time=Time
+report.invoker.detail.title=Build Details
+report.invoker.detail.name=Name
+report.invoker.detail.result=Result
+report.invoker.detail.time=Time
+report.invoker.detail.message=Message
+report.invoker.format.percent=0.0%
+report.invoker.format.seconds=0.0\u00A0s
+report.invoker.format.name_with_description={0}: {1}
diff --git a/src/main/resources/invoker-report_de.properties 
b/src/main/resources/invoker-report_de.properties
index d1016e3..41e030b 100644
--- a/src/main/resources/invoker-report_de.properties
+++ b/src/main/resources/invoker-report_de.properties
@@ -15,21 +15,18 @@
 # specific language governing permissions and limitations
 # under the License.
 
-report.invoker.result.description   = Die Ergebnisse der 
Maven-Ausf\u00FChrungen.
-report.invoker.result.name          = Invoker-Build-Ergebnisse
-report.invoker.result.title         = Invoker-Bericht
-report.invoker.summary.title        = Zusammenfassungen
-report.invoker.summary.number       = Builds
-report.invoker.summary.success      = Erfolge
-report.invoker.summary.failed       = Fehlschl\u00E4ge
-report.invoker.summary.skipped      = Ausgelassen
-report.invoker.summary.success.rate = Erfolgsrate
-report.invoker.summary.time.total   = Gesamtzeit
-report.invoker.summary.time.avg     = Durchschnittszeit
-report.invoker.detail.title         = Build-Details
-report.invoker.detail.name          = Name
-report.invoker.detail.result        = Ergebnis
-report.invoker.detail.time          = Zeit
-report.invoker.detail.message       = Meldung
-report.invoker.format.percent       = 0.0\u00A0%
-report.invoker.format.seconds       = 0.0\u00A0s
+report.invoker.description=Bericht \u00FCber die Build-Ergebnisse der 
Maven-Ausf\u00FChrungen.
+report.invoker.title=Invoker-Bericht
+report.invoker.summary.title=Zusammenfassung
+report.invoker.summary.builds=Builds
+report.invoker.summary.success=Erfolge
+report.invoker.summary.failures=Fehlschl\u00E4ge
+report.invoker.summary.skipped=Ausgelassen
+report.invoker.summary.successrate=Erfolgsrate
+report.invoker.summary.time=Zeit
+report.invoker.detail.title=Build-Details
+report.invoker.detail.name=Name
+report.invoker.detail.result=Ergebnis
+report.invoker.detail.time=Zeit
+report.invoker.detail.message=Meldung
+report.invoker.format.percent=0.0\u00A0%
diff --git a/src/main/resources/invoker-report_fr.properties 
b/src/main/resources/invoker-report_fr.properties
index 1d3393a..7da545a 100644
--- a/src/main/resources/invoker-report_fr.properties
+++ b/src/main/resources/invoker-report_fr.properties
@@ -15,22 +15,19 @@
 # specific language governing permissions and limitations
 # under the License.
 
-report.invoker.result.description   = R�sultat des invocations de Maven
-report.invoker.result.name          = Invoker R�sultat de builds
-report.invoker.result.title         = Invoker Rapport
-report.invoker.summary.title        = Sommaire
-report.invoker.summary.number       = Builds
-report.invoker.summary.success      = R�ussis
-report.invoker.summary.failed       = Echecs
-report.invoker.summary.skipped      = Ignor�s
-report.invoker.summary.success.rate = Taux de r�ussite
-report.invoker.summary.time.total   = Dur�e totale
-report.invoker.summary.time.avg     = Dur�e moyenne
-report.invoker.detail.title         = D�tails de Build 
-report.invoker.detail.name          = Nom
-report.invoker.detail.result        = R�sultat
-report.invoker.detail.time          = Dur�e
-report.invoker.detail.message       = Message
-report.invoker.format.percent       = 0.0%
-report.invoker.format.seconds       = 0.0\u00A0s
-report.invoker.format.name_with_description = {0} : {1}
+report.invoker.description=R�sultat des invocations de Maven
+report.invoker.title=Invoker Rapport
+report.invoker.summary.title=Sommaire
+report.invoker.summary.builds=Builds
+report.invoker.summary.success=R�ussis
+report.invoker.summary.failures=Echecs
+report.invoker.summary.skipped=Ignor�s
+report.invoker.summary.successrate=Taux de r�ussite
+report.invoker.summary.time=Dur�e
+report.invoker.detail.title=D�tails de Build
+report.invoker.detail.name=Nom
+report.invoker.detail.result=R�sultat
+report.invoker.detail.time=Dur�e
+report.invoker.detail.message=Message
+report.invoker.format.percent=0.0\u00A0%
+report.invoker.format.name_with_description={0} : {1}

Reply via email to