diff --git a/functional-test/pom.xml b/functional-test/pom.xml
index 98c78c18e7..9f19ef3efc 100644
--- a/functional-test/pom.xml
+++ b/functional-test/pom.xml
@@ -194,6 +194,11 @@
resteasy-jaxb-provider
+
+ org.zanata
+ zanata-adapter-po
+
+
org.zanata
@@ -204,10 +209,6 @@
org.jboss.resteasy
jaxrs-api
-
- org.jboss.resteasy
- resteasy-jaxrs
-
org.jboss.seam
jboss-seam
diff --git a/functional-test/sample-projects/plural/pl/test.po b/functional-test/sample-projects/plural/pl/test.po
index f1428bd515..5978055144 100644
--- a/functional-test/sample-projects/plural/pl/test.po
+++ b/functional-test/sample-projects/plural/pl/test.po
@@ -15,39 +15,64 @@ msgid "One file removed"
msgid_plural "%d files removed"
msgstr[0] "1 aoeuaouaou"
msgstr[1] "%d aoeuaouao"
+msgstr[2] ""
#, c-format
msgid "One other file removed"
msgid_plural "%d files removed"
msgstr[0] ""
msgstr[1] "%d aoeuaouao"
+msgstr[2] ""
#, c-format
msgid "One file removed_"
msgid_plural "%d more files removed"
msgstr[0] "1 tnhdaoeuaoue"
msgstr[1] ""
+msgstr[2] ""
#, c-format
msgid "File removed"
msgid_plural "%d files removed"
msgstr[0] "1 tnhdnthdntioei"
+msgstr[1] ""
+msgstr[2] ""
-#, c-format, fuzzy
+#, fuzzy, c-format
msgid "Yet Another File removed"
msgid_plural "%d files removed"
msgstr[0] "%d aoenuthaosneuh"
+msgstr[1] ""
+msgstr[2] ""
-#, c-format, fuzzy
+#, c-format
msgid "2 Another File removed"
msgid_plural "%d files removed"
msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+#, c-format
+msgid "msgid"
+msgstr ""
+
+#, c-format
+msgid "%d files removed"
+msgid_plural "One file removed"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
-# not legal because it has multiple msgstr but no msgid_plural:
#, c-format
-#msgid "File removed."
-#msgstr[0] ""
-#msgstr[1] ""
+msgid "aaaaaaaaaaaa"
+msgid_plural "bbbbbbbbbbb"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
-msgid "pl msgid"
-msgstr "pl msgstr"
+#, c-format
+msgid "bbbbbbbbbbb"
+msgid_plural "aaaaaaaaaaaa"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
diff --git a/functional-test/sample-projects/plural/pom.xml b/functional-test/sample-projects/plural/pom.xml
index c1284a463f..da53efa558 100644
--- a/functional-test/sample-projects/plural/pom.xml
+++ b/functional-test/sample-projects/plural/pom.xml
@@ -4,6 +4,10 @@
null
0
+
+ pot
+ .
+
@@ -11,8 +15,8 @@
zanata-maven-plugin
${zanata.client.version}
- pot
- .
+ ${zanata.srcDir}
+ ${zanata.transDir}
diff --git a/functional-test/src/main/java/org/zanata/page/webtrans/EditorPage.java b/functional-test/src/main/java/org/zanata/page/webtrans/EditorPage.java
index dbee1e5d53..c8621cb641 100644
--- a/functional-test/src/main/java/org/zanata/page/webtrans/EditorPage.java
+++ b/functional-test/src/main/java/org/zanata/page/webtrans/EditorPage.java
@@ -11,6 +11,7 @@
import org.zanata.util.WebElementUtil;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import lombok.extern.slf4j.Slf4j;
@@ -24,6 +25,7 @@ public class EditorPage extends BasePage {
private static final String SOURCE_ID_FMT =
"gwt-debug-%d-source-panel-%d-container";
private static final int SINGULAR = 0;
+ private static final int PLURAL = 1;
// first %d is row index, second %d is plural form index (i.e. 0-6)
private static final String TARGET_ID_FMT = "gwt-debug-%d-target-%d";
@@ -83,6 +85,7 @@ public List> apply(WebDriver input) {
* Get content of a text flow source at given row.
* This assumes the text flow has singular form (i.e. no plural).
* If a test requires to access plural content, this can be changed.
+ * TODO pahuang rename method name
*
* @param rowIndex
* row index
@@ -92,6 +95,13 @@ public String getTranslationSourceAtRowIndex(final int rowIndex) {
return getCodeMirrorContent(rowIndex, SOURCE_ID_FMT, SINGULAR);
}
+ public String getMessageSourceAtRowIndex(int rowIndex, int pluralIndex) {
+ Preconditions.checkArgument(
+ pluralIndex == SINGULAR || pluralIndex == PLURAL,
+ "plural index must be 0 or 1");
+ return getCodeMirrorContent(rowIndex, SOURCE_ID_FMT, pluralIndex);
+ }
+
/**
* Get content of a text flow target at given row.
* This assumes the text flow has singular form (i.e. no plural).
@@ -120,4 +130,10 @@ public String apply(WebDriver input) {
}
});
}
+
+ public String getMessageTargetAtRowIndex(int rowIndex, int pluralIndex) {
+ Preconditions.checkArgument(pluralIndex >= 0 && pluralIndex <= 6,
+ "plural index must be in range [0,6]");
+ return getCodeMirrorContent(rowIndex, TARGET_ID_FMT, pluralIndex);
+ }
}
diff --git a/functional-test/src/main/java/org/zanata/util/TestFileGenerator.java b/functional-test/src/main/java/org/zanata/util/TestFileGenerator.java
index 3f611eacbe..a5d7dca8bb 100644
--- a/functional-test/src/main/java/org/zanata/util/TestFileGenerator.java
+++ b/functional-test/src/main/java/org/zanata/util/TestFileGenerator.java
@@ -24,6 +24,18 @@
import java.io.*;
import java.nio.charset.Charset;
+import java.util.List;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlElements;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.Lists;
+import lombok.Setter;
/**
* Create and manipulate basic text files for testing.
@@ -154,4 +166,53 @@ public String getFirstFileNameInDirectory(String directory) {
+ " but none found.");
}
}
+
+ /**
+ * Generates a zanata.xml with url default to test instance.
+ *
+ * @param output where to write it
+ * @param projectSlug project slug
+ * @param versionSlug version slug
+ * @param projectType project type
+ * @param locales locales
+ */
+ public void generateZanataXml(File output, String projectSlug, String versionSlug, String projectType, List locales) {
+ ZanataXml zanataXml = new ZanataXml();
+ zanataXml.setProject(projectSlug);
+ zanataXml.setProjectVersion(versionSlug);
+ zanataXml.setProjectType(projectType);
+ zanataXml.setLocales(locales);
+ marshall(output, zanataXml, ZanataXml.class);
+ }
+
+ private static void marshall(File output, T object, Class xmlClass) {
+ try {
+ JAXBContext jaxbContext = JAXBContext.newInstance(xmlClass);
+ Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
+ jaxbMarshaller.marshal(object, output);
+ }
+ catch (JAXBException e) {
+ throw Throwables.propagate(e);
+ }
+ }
+
+ @XmlRootElement(namespace = "http://zanata.org/namespace/config/",
+ name = "config")
+ @Setter
+ private static class ZanataXml {
+ @XmlElement
+ private String url = PropertiesHolder
+ .getProperty(Constants.zanataInstance.value());
+ @XmlElement
+ private String project;
+ @XmlElement(name = "project-version")
+ private String projectVersion;
+ @XmlElement(name = "project-type")
+ private String projectType;
+ @XmlElementWrapper(name="locales")
+ @XmlElements(
+ @XmlElement(name = "locale")
+ )
+ private List locales;
+ }
}
diff --git a/functional-test/src/test/java/org/zanata/feature/clientserver/GettextPluralSupportTest.java b/functional-test/src/test/java/org/zanata/feature/clientserver/GettextPluralSupportTest.java
new file mode 100644
index 0000000000..afa960b09b
--- /dev/null
+++ b/functional-test/src/test/java/org/zanata/feature/clientserver/GettextPluralSupportTest.java
@@ -0,0 +1,149 @@
+package org.zanata.feature.clientserver;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.xml.sax.InputSource;
+import org.zanata.adapter.po.PoReader2;
+import org.zanata.common.LocaleId;
+import org.zanata.feature.BasicAcceptanceTest;
+import org.zanata.page.webtrans.EditorPage;
+import org.zanata.rest.dto.resource.TextFlow;
+import org.zanata.rest.dto.resource.TextFlowTarget;
+import org.zanata.util.SampleProjectRule;
+import org.zanata.util.ZanataRestCaller;
+import org.zanata.workflow.BasicWorkFlow;
+import org.zanata.workflow.ClientPushWorkFlow;
+import org.zanata.workflow.LoginWorkFlow;
+import com.google.common.io.Files;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * This covers TCMS case 217601 and case
+ * 217905
+ *
+ * @author Patrick Huang pahuang@redhat.com
+ */
+@Category(BasicAcceptanceTest.class)
+public class GettextPluralSupportTest {
+ @Rule
+ public SampleProjectRule sampleProjectRule = new SampleProjectRule();
+
+ private ClientPushWorkFlow client = new ClientPushWorkFlow();
+ private ZanataRestCaller restCaller;
+
+ private File tempDir = Files.createTempDir();
+
+ private String userConfigPath = ClientPushWorkFlow
+ .getUserConfigPath("admin");
+ private File projectRootPath;
+
+ @Before
+ public void setUp() throws IOException {
+ projectRootPath = client.getProjectRootPath("plural");
+ Files.copy(new File(projectRootPath, "pom.xml"), new File(tempDir,
+ "pom.xml"));
+ Files.copy(new File(projectRootPath, "zanata.xml"), new File(tempDir,
+ "zanata.xml"));
+ File potDir = new File(tempDir, "pot");
+ potDir.mkdirs();
+ File plDir = new File(tempDir, "pl");
+ plDir.mkdirs();
+ Files.copy(new File(projectRootPath + "/pot", "test.pot"), new File(
+ potDir, "test.pot"));
+ Files.copy(new File(projectRootPath + "/pl", "test.po"), new File(
+ plDir, "test.po"));
+ restCaller = new ZanataRestCaller("admin");
+ }
+
+ @Test
+ public void canPushAndPullPlural() throws IOException {
+ restCaller.createProjectAndVersion("plurals", "master", "podir");
+ List output =
+ client.callWithTimeout(tempDir,
+ "mvn -B zanata:push -Dzanata.pushType=both -Dzanata.userConfig="
+ + userConfigPath);
+
+ assertThat(client.isPushSuccessful(output), Matchers.is(true));
+
+ EditorPage editorPage = verifyPluralPushedToEditor();
+
+ File pullDir = Files.createTempDir();
+ String pullDirPath = pullDir.getAbsolutePath();
+ String command =
+ "mvn -B zanata:pull -Dzanata.pullType=both -Dzanata.srcDir="
+ + pullDirPath + " -Dzanata.transDir=" + pullDirPath
+ + " -Dzanata.userConfig=" + userConfigPath;
+ output = client.callWithTimeout(tempDir, command);
+ assertThat(client.isPushSuccessful(output), Matchers.is(true));
+
+ // source round trip
+ List originalTextFlows =
+ getTextFlows(new File(projectRootPath + "/pot/test.pot"));
+ List pulledTextFlows =
+ getTextFlows(new File(pullDir, "test.pot"));
+ assertThat(pulledTextFlows, Matchers.equalTo(originalTextFlows));
+
+ // translation round trip
+ List originalTargets =
+ getTextFlowTargets(new File(projectRootPath + "/pl/test.po"));
+ List pulledTargets =
+ getTextFlowTargets(new File(pullDir + "/pl/test.po"));
+ assertThat(pulledTargets, Matchers.equalTo(originalTargets));
+
+ // TODO translate some text in UI and then pull and compare
+ }
+
+ private static EditorPage verifyPluralPushedToEditor() {
+ // first message
+ // msgid "One file removed"
+ // msgid_plural "%d files removed"
+ // msgstr[0] "1 aoeuaouaou"
+ // msgstr[1] "%d aoeuaouao"
+
+ new LoginWorkFlow().signIn("admin", "admin");
+ EditorPage editorPage =
+ new BasicWorkFlow().goToPage(String.format(
+ BasicWorkFlow.EDITOR_TEMPLATE, "plurals", "master",
+ "pl", "test"), EditorPage.class);
+
+ assertThat(editorPage.getMessageSourceAtRowIndex(0, 0),
+ Matchers.equalTo("One file removed"));
+ assertThat(editorPage.getMessageSourceAtRowIndex(0, 1),
+ Matchers.equalTo("%d files removed"));
+ // nplural for Polish is 3
+ assertThat(editorPage.getMessageTargetAtRowIndex(0, 0),
+ Matchers.equalTo("1 aoeuaouaou"));
+ assertThat(editorPage.getMessageTargetAtRowIndex(0, 1),
+ Matchers.equalTo("%d aoeuaouao"));
+ assertThat(editorPage.getMessageTargetAtRowIndex(0, 2),
+ Matchers.equalTo(" "));
+
+ return editorPage;
+ }
+
+ private static List getTextFlows(File file)
+ throws FileNotFoundException {
+ return new PoReader2().extractTemplate(
+ new InputSource(new FileInputStream(file)), LocaleId.EN_US,
+ file.getName()).getTextFlows();
+ }
+
+ private static List getTextFlowTargets(File file)
+ throws FileNotFoundException {
+ return new PoReader2().extractTarget(
+ new InputSource(new FileInputStream(file)))
+ .getTextFlowTargets();
+ }
+}