From db1863fd0f43416c34c10ff76848b1f973d217dd Mon Sep 17 00:00:00 2001 From: Ignacio Machuca <4760146+comunacho@users.noreply.github.com> Date: Mon, 3 Aug 2020 10:00:02 -0700 Subject: [PATCH] TextUnit <-> AndroidString mappers --- .../mojito/android/strings/AndroidPlural.java | 11 +- .../strings/AndroidStringDocument.java | 6 +- .../strings/AndroidStringDocumentMapper.java | 190 +++++++++ .../strings/AndroidStringDocumentWriter.java | 4 +- .../strings/AndroidStringsTextUnit.java | 44 --- .../mojito/service/tm/PluralNameParser.java | 18 + .../AndroidStringDocumentMapperTest.java | 362 ++++++++++++++++++ .../AndroidStringDocumentWriterTest.java | 11 + .../service/tm/PluralNameParserTest.java | 39 +- .../android/strings/test_resources_file.xml | 19 + 10 files changed, 645 insertions(+), 59 deletions(-) create mode 100644 webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidStringDocumentMapper.java delete mode 100644 webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidStringsTextUnit.java create mode 100644 webapp/src/test/java/com/box/l10n/mojito/android/strings/AndroidStringDocumentMapperTest.java create mode 100644 webapp/src/test/resources/com/box/l10n/mojito/android/strings/test_resources_file.xml diff --git a/webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidPlural.java b/webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidPlural.java index 8d49bd490b..d1a4c434b5 100644 --- a/webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidPlural.java +++ b/webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidPlural.java @@ -7,6 +7,7 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; public class AndroidPlural extends AbstractAndroidString { @@ -26,9 +27,13 @@ public Map getItems() { } public void forEachItemSorted(Consumer itemConsumer){ - items.values().stream() - .sorted(Comparator.comparingInt(item -> item.getQuantity().ordinal())) - .forEach(itemConsumer); + sortedStream().forEach(itemConsumer); + } + + public Stream sortedStream(){ + return items.values() + .stream() + .sorted(Comparator.comparingInt(item -> item.getQuantity().ordinal())); } @Override diff --git a/webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidStringDocument.java b/webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidStringDocument.java index f46f56fdd9..b23fb224f3 100644 --- a/webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidStringDocument.java +++ b/webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidStringDocument.java @@ -37,7 +37,7 @@ public void addSingular(AndroidStringElement element, Node comment) { addSingular(new AndroidSingular(element.getIdAttribute(), element.getNameAttribute(), element.getUnescapedContent(), - comment.getTextContent())); + comment != null ? comment.getTextContent() : null)); } public void addPlural(AndroidPlural plural) { @@ -51,7 +51,9 @@ public void addPlural(AndroidStringElement element, Node comment) { AndroidPlural.AndroidPluralBuilder builder = AndroidPlural.builder(); builder.setName(element.getNameAttribute()); - builder.setComment(comment.getTextContent()); + if (comment != null) { + builder.setComment(comment.getTextContent()); + } element.forEachPluralItem(builder::addItem); diff --git a/webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidStringDocumentMapper.java b/webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidStringDocumentMapper.java new file mode 100644 index 0000000000..4ebce9f7db --- /dev/null +++ b/webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidStringDocumentMapper.java @@ -0,0 +1,190 @@ +package com.box.l10n.mojito.android.strings; + +import com.box.l10n.mojito.service.tm.PluralNameParser; +import com.box.l10n.mojito.service.tm.search.TextUnitDTO; +import com.google.common.base.Strings; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.box.l10n.mojito.android.strings.AndroidPluralQuantity.OTHER; + +public class AndroidStringDocumentMapper { + + private static final String DEFAULT_ASSET_DELIMITER = "#@#"; + + private final String pluralSeparator; + private final String assetDelimiter; + private final String locale; + private final String repositoryName; + private final PluralNameParser pluralNameParser; + + public AndroidStringDocumentMapper(String pluralSeparator, + String assetDelimiter, + String locale, + String repositoryName) { + this.pluralSeparator = pluralSeparator; + this.assetDelimiter = Optional.ofNullable(Strings.emptyToNull(assetDelimiter)).orElse(DEFAULT_ASSET_DELIMITER); + this.locale = locale; + this.repositoryName = repositoryName; + this.pluralNameParser = new PluralNameParser(); + } + + public AndroidStringDocumentMapper(String pluralSeparator, String assetDelimiter) { + this(pluralSeparator, assetDelimiter, null, null); + } + + public AndroidStringDocument readFromTextUnits(List textUnits, boolean useSource) { + + AndroidStringDocument document = new AndroidStringDocument(); + Map pluralByOther = new HashMap<>(); + + for (TextUnitDTO textUnit : textUnits) { + + if (isSingularTextUnit(textUnit)) { + document.addSingular(textUnitToAndroidSingular(textUnit, useSource)); + } else { + pluralByOther.compute(textUnit.getPluralFormOther(), (key, builder) -> { + if (builder == null) { + builder = AndroidPlural.builder(); + } + + if (OTHER.name().equalsIgnoreCase(textUnit.getPluralForm())) { + String name = pluralNameParser.getPrefix(textUnit.getName(), pluralSeparator); + builder.setName(textUnit.getAssetPath() + assetDelimiter + name); + builder.setComment(textUnit.getComment()); + } + + builder.addItem(new AndroidPluralItem( + textUnit.getPluralForm(), + textUnit.getTmTextUnitId(), + useSource ? textUnit.getSource() : textUnit.getTarget())); + + return builder; + + }); + } + } + + pluralByOther.forEach((pluralFormOther, builder) -> document.addPlural(builder.build())); + + return document; + } + + public AndroidStringDocument readFromSourceTextUnits(List textUnits) { + return readFromTextUnits(textUnits, true); + } + + public AndroidStringDocument readFromTargetTextUnits(List textUnits) { + return readFromTextUnits(textUnits, false); + } + + public List mapToTextUnits(AndroidStringDocument document) { + return mapToTextUnitStream(document).collect(Collectors.toList()); + } + + public Stream mapToTextUnitStream(AndroidStringDocument document) { + return document.getStrings() + .stream() + .flatMap(this::stringToTextUnits); + } + + TextUnitDTO addTextUnitDTOAttributes(TextUnitDTO textUnit) { + + if (!Strings.isNullOrEmpty(locale)){ + textUnit.setTargetLocale(locale); + } + + if (!Strings.isNullOrEmpty(repositoryName)){ + textUnit.setRepositoryName(repositoryName); + } + + if (textUnit.getName().contains(assetDelimiter)){ + String[] nameParts = textUnit.getName().split(assetDelimiter); + + if (nameParts.length == 2) { + textUnit.setAssetPath(nameParts[0]); + textUnit.setName(nameParts[1]); + } + } + + return textUnit; + } + + Stream stringToTextUnits(AbstractAndroidString androidString) { + Stream result; + + if (androidString.isSingular()){ + result = singularToTextUnit((AndroidSingular) androidString); + } else { + result = pluralToTextUnits((AndroidPlural) androidString); + } + + return result; + } + + Stream singularToTextUnit(AndroidSingular singular) { + TextUnitDTO textUnit = new TextUnitDTO(); + + textUnit.setName(singular.getName()); + textUnit.setComment(singular.getComment()); + textUnit.setTmTextUnitId(singular.getId()); + textUnit.setTarget(unescape(singular.getContent())); + addTextUnitDTOAttributes(textUnit); + + return Stream.of(textUnit); + } + + Stream pluralToTextUnits(AndroidPlural plural) { + return plural.sortedStream() + .map(item -> { + + TextUnitDTO textUnit = new TextUnitDTO(); + String quantity = item.getQuantity().toString(); + String name = pluralNameParser.toPluralName(plural.getName(), quantity, pluralSeparator); + String pluralFormOther = pluralNameParser.toPluralName(plural.getName(), OTHER.toString(), pluralSeparator); + + textUnit.setName(name); + textUnit.setComment(plural.getComment()); + textUnit.setTmTextUnitId(item.getId()); + textUnit.setPluralForm(quantity); + textUnit.setPluralFormOther(pluralFormOther); + textUnit.setTarget(unescape(item.getContent())); + addTextUnitDTOAttributes(textUnit); + + return textUnit; + + }); + } + + boolean isSingularTextUnit(TextUnitDTO textUnit) { + return Strings.isNullOrEmpty(textUnit.getPluralForm()); + } + + AndroidSingular textUnitToAndroidSingular(TextUnitDTO textUnit, boolean useSource) { + return new AndroidSingular( + textUnit.getTmTextUnitId(), + textUnit.getAssetPath() + assetDelimiter + textUnit.getName(), + removeBadCharacters(useSource ? textUnit.getSource() : textUnit.getTarget()), + textUnit.getComment()); + } + + static String removeBadCharacters(String source) { + return Strings.nullToEmpty(source) + .replaceAll("\u001d", "") + .replaceAll("\u001c", "") + .replaceAll("\u0000", ""); + } + + static String unescape(String str) { + return Strings.nullToEmpty(str) + .replaceAll("\\\\'", "'") + .replaceAll("\\\\\"", "\"") + .replaceAll("\\\\@", "@") + .replaceAll("\\\\n", "\n"); + } +} diff --git a/webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidStringDocumentWriter.java b/webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidStringDocumentWriter.java index bfd65412c4..ea0524c757 100644 --- a/webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidStringDocumentWriter.java +++ b/webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidStringDocumentWriter.java @@ -144,9 +144,9 @@ private void setAttribute(Element element, String name, String value) { } } - private static String escapeQuotes(String str) { + static String escapeQuotes(String str) { return Strings.nullToEmpty(str) .replaceAll("\"", "\\\\\"") - .replaceAll("\n", "\\\\\n"); + .replaceAll("\n", "\\\\n"); } } diff --git a/webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidStringsTextUnit.java b/webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidStringsTextUnit.java deleted file mode 100644 index 851eeeaf8e..0000000000 --- a/webapp/src/main/java/com/box/l10n/mojito/android/strings/AndroidStringsTextUnit.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.box.l10n.mojito.android.strings; - -public class AndroidStringsTextUnit { - - private String comment; - private String name; - private String content; - private String id; - private String pluralForm; - private String pluralFormOther; - - public AndroidStringsTextUnit(String name, String content, String comment, String id, String pluralForm, String pluralFormOther) { - this.comment = comment; - this.name = name; - this.content = content; - this.id = id; - this.pluralForm = pluralForm; - this.pluralFormOther = pluralFormOther; - } - - public String getComment() { - return comment; - } - - public String getName() { - return name; - } - - public String getContent() { - return content; - } - - public String getId() { - return id; - } - - public String getPluralForm() { - return pluralForm; - } - - public String getPluralFormOther() { - return pluralFormOther; - } -} diff --git a/webapp/src/main/java/com/box/l10n/mojito/service/tm/PluralNameParser.java b/webapp/src/main/java/com/box/l10n/mojito/service/tm/PluralNameParser.java index 2d14367e58..59993abb44 100644 --- a/webapp/src/main/java/com/box/l10n/mojito/service/tm/PluralNameParser.java +++ b/webapp/src/main/java/com/box/l10n/mojito/service/tm/PluralNameParser.java @@ -2,10 +2,28 @@ import org.springframework.stereotype.Component; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + @Component public class PluralNameParser { public String getPrefix(String nameWithPluralForm) { return nameWithPluralForm.replaceAll("_(zero|one|two|few|many|other)$", ""); } + + public String getPrefix(String name, String separator) { + + Pattern pattern = Pattern.compile("(.*)" + separator + "(zero|one|two|few|many|other)"); + + return Optional.of(pattern.matcher(name)) + .filter(Matcher::matches) + .map(m -> m.group(1)) + .orElse(name); + } + + public String toPluralName(String prefix, String name, String separator){ + return String.format("%s%s%s", prefix, separator, name); + } } diff --git a/webapp/src/test/java/com/box/l10n/mojito/android/strings/AndroidStringDocumentMapperTest.java b/webapp/src/test/java/com/box/l10n/mojito/android/strings/AndroidStringDocumentMapperTest.java new file mode 100644 index 0000000000..63ed2f3eaa --- /dev/null +++ b/webapp/src/test/java/com/box/l10n/mojito/android/strings/AndroidStringDocumentMapperTest.java @@ -0,0 +1,362 @@ +package com.box.l10n.mojito.android.strings; + +import com.box.l10n.mojito.service.tm.search.TextUnitDTO; +import com.google.common.io.Resources; +import org.assertj.core.groups.Tuple; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static com.box.l10n.mojito.android.strings.AndroidPluralQuantity.FEW; +import static com.box.l10n.mojito.android.strings.AndroidPluralQuantity.MANY; +import static com.box.l10n.mojito.android.strings.AndroidPluralQuantity.ONE; +import static com.box.l10n.mojito.android.strings.AndroidPluralQuantity.OTHER; +import static com.box.l10n.mojito.android.strings.AndroidPluralQuantity.TWO; +import static com.box.l10n.mojito.android.strings.AndroidPluralQuantity.ZERO; +import static com.box.l10n.mojito.android.strings.AndroidStringDocumentMapper.removeBadCharacters; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +public class AndroidStringDocumentMapperTest { + + String assetDelimiter = "#@#"; + AndroidStringDocumentMapper mapper; + AndroidStringDocument document; + List textUnits = new ArrayList<>(); + + @Test + public void testReadFromSourceTextUnitsForEmptyList() { + mapper = new AndroidStringDocumentMapper("", assetDelimiter); + + document = mapper.readFromSourceTextUnits(textUnits); + + assertThat(document).isNotNull(); + assertThat(document.getSingulars()).isEmpty(); + assertThat(document.getPlurals()).isEmpty(); + } + + @Test + public void testReadFromSourceTextUnitsWithoutPluralForms() { + mapper = new AndroidStringDocumentMapper("_", assetDelimiter); + textUnits.add(sourceTextUnitDTO(123L, "name0", "content0", "comment0", "my/path0", null, null)); + textUnits.add(sourceTextUnitDTO(124L, "name1", "content1", "comment1", "my/path1", null, null)); + + document = mapper.readFromSourceTextUnits(textUnits); + + assertThat(document).isNotNull(); + assertThat(document.getPlurals()).isEmpty(); + assertThat(document.getSingulars()).hasSize(2); + AndroidSingular singular1 = document.getSingulars().get(0); + assertThat(singular1.getId()).isEqualTo(123L); + assertThat(singular1.getName()).isEqualTo("my/path0#@#name0"); + assertThat(singular1.getContent()).isEqualTo("content0"); + assertThat(singular1.getComment()).isEqualTo("comment0"); + AndroidSingular singular2 = document.getSingulars().get(1); + assertThat(singular2.getId()).isEqualTo(124L); + assertThat(singular2.getName()).isEqualTo("my/path1#@#name1"); + assertThat(singular2.getContent()).isEqualTo("content1"); + assertThat(singular2.getComment()).isEqualTo("comment1"); + } + + @Test + public void testReadFromSourceTextUnitsWithoutPluralFormsRemovesBadCharacters() { + mapper = new AndroidStringDocumentMapper("_", assetDelimiter); + textUnits.add(sourceTextUnitDTO(124L, "name1", "test" + '\u001D' + "content1", "comment1", "my/path1", null, null)); + + document = mapper.readFromSourceTextUnits(textUnits); + + assertThat(document).isNotNull(); + assertThat(document.getPlurals()).isEmpty(); + assertThat(document.getSingulars()).hasSize(1); + AndroidSingular singular = document.getSingulars().get(0); + assertThat(singular.getName()).isEqualTo("my/path1#@#name1"); + assertThat(singular.getContent()).isEqualTo("testcontent1"); + } + + @Test + public void testReadFromSourceTextUnitsWithPlurals() { + mapper = new AndroidStringDocumentMapper(" _", assetDelimiter); + textUnits.add(sourceTextUnitDTO(123L, "name0", "content0", "comment0", "my/path0", null, null)); + + textUnits.add(sourceTextUnitDTO(100L, "name1 _other", "content1_zero", "comment1", "my/path1", "zero", "name1_other")); + textUnits.add(sourceTextUnitDTO(101L, "name1 _other", "content1_one", "comment1", "my/path1", "one", "name1_other")); + textUnits.add(sourceTextUnitDTO(102L, "name1 _other", "content1_two", "comment1", "my/path1", "two", "name1_other")); + textUnits.add(sourceTextUnitDTO(103L, "name1 _other", "content1_few", "comment1", "my/path1", "few", "name1_other")); + textUnits.add(sourceTextUnitDTO(104L, "name1 _other", "content1_many", "comment1", "my/path1", "many", "name1_other")); + textUnits.add(sourceTextUnitDTO(105L, "name1 _other", "content1_other", "comment1", "my/path1", "other", "name1_other")); + + document = mapper.readFromSourceTextUnits(textUnits); + + assertThat(document).isNotNull(); + assertThat(document.getSingulars()).hasSize(1); + AndroidSingular singular = document.getSingulars().get(0); + assertThat(singular.getId()).isEqualTo(123L); + assertThat(singular.getName()).isEqualTo("my/path0#@#name0"); + assertThat(singular.getContent()).isEqualTo("content0"); + assertThat(singular.getComment()).isEqualTo("comment0"); + + assertThat(document.getPlurals()).hasSize(1); + AndroidPlural plural = document.getPlurals().get(0); + assertThat(plural.getName()).isEqualTo("my/path1#@#name1"); + assertThat(plural.getComment()).isEqualTo("comment1"); + assertThat(plural.getItems()).hasSize(6); + assertThat(plural.getItems().get(ZERO).getId()).isEqualTo(100L); + assertThat(plural.getItems().get(ZERO).getContent()).isEqualTo("content1_zero"); + assertThat(plural.getItems().get(ONE).getId()).isEqualTo(101L); + assertThat(plural.getItems().get(ONE).getContent()).isEqualTo("content1_one"); + assertThat(plural.getItems().get(TWO).getId()).isEqualTo(102L); + assertThat(plural.getItems().get(TWO).getContent()).isEqualTo("content1_two"); + assertThat(plural.getItems().get(FEW).getId()).isEqualTo(103L); + assertThat(plural.getItems().get(FEW).getContent()).isEqualTo("content1_few"); + assertThat(plural.getItems().get(MANY).getId()).isEqualTo(104L); + assertThat(plural.getItems().get(MANY).getContent()).isEqualTo("content1_many"); + assertThat(plural.getItems().get(OTHER).getId()).isEqualTo(105L); + assertThat(plural.getItems().get(OTHER).getContent()).isEqualTo("content1_other"); + } + + @Test + public void testReadFromTargetTextUnitsForEmptyList() { + mapper = new AndroidStringDocumentMapper("", assetDelimiter); + + document = mapper.readFromTargetTextUnits(textUnits); + + assertThat(document).isNotNull(); + assertThat(document.getSingulars()).isEmpty(); + assertThat(document.getPlurals()).isEmpty(); + } + + @Test + public void testReadFromTargetTextUnitsWithoutPlural() { + mapper = new AndroidStringDocumentMapper("_", assetDelimiter); + textUnits.add(targetTextUnitDTO(123L, "name0", "content0", "comment0", "my/path0", null, null)); + textUnits.add(targetTextUnitDTO(124L, "name1", "content1", "comment1", "my/path1", null, null)); + + document = mapper.readFromTargetTextUnits(textUnits); + + assertThat(document).isNotNull(); + assertThat(document.getPlurals()).isEmpty(); + assertThat(document.getSingulars()).hasSize(2); + AndroidSingular singular1 = document.getSingulars().get(0); + assertThat(singular1.getId()).isEqualTo(123L); + assertThat(singular1.getName()).isEqualTo("my/path0#@#name0"); + assertThat(singular1.getContent()).isEqualTo("content0"); + assertThat(singular1.getComment()).isEqualTo("comment0"); + AndroidSingular singular2 = document.getSingulars().get(1); + assertThat(singular2.getId()).isEqualTo(124L); + assertThat(singular2.getName()).isEqualTo("my/path1#@#name1"); + assertThat(singular2.getContent()).isEqualTo("content1"); + assertThat(singular2.getComment()).isEqualTo("comment1"); + } + + @Test + public void testReadFromTargetTextUnitsWithoutPluralsRemovesBadCharacters() { + mapper = new AndroidStringDocumentMapper("_", assetDelimiter); + textUnits.add(targetTextUnitDTO(124L, "name1", "test" + '\u001D' + "content1", "comment1", "my/path1", null, null)); + + document = mapper.readFromTargetTextUnits(textUnits); + + assertThat(document).isNotNull(); + assertThat(document.getPlurals()).isEmpty(); + assertThat(document.getSingulars()).hasSize(1); + AndroidSingular singular = document.getSingulars().get(0); + assertThat(singular.getName()).isEqualTo("my/path1#@#name1"); + assertThat(singular.getContent()).isEqualTo("testcontent1"); + } + + @Test + public void testReadFromTargetTextUnitsWithPlurals() { + mapper = new AndroidStringDocumentMapper(" _", assetDelimiter); + textUnits.add(targetTextUnitDTO(123L, "name0", "content0", "comment0", "my/path0", null, null)); + + textUnits.add(targetTextUnitDTO(100L, "name1 _other", "content1_zero", "comment1", "my/path1", "zero", "name1_other")); + textUnits.add(targetTextUnitDTO(101L, "name1 _other", "content1_one", "comment1", "my/path1", "one", "name1_other")); + textUnits.add(targetTextUnitDTO(102L, "name1 _other", "content1_two", "comment1", "my/path1", "two", "name1_other")); + textUnits.add(targetTextUnitDTO(103L, "name1 _other", "content1_few", "comment1", "my/path1", "few", "name1_other")); + textUnits.add(targetTextUnitDTO(104L, "name1 _other", "content1_many", "comment1", "my/path1", "many", "name1_other")); + textUnits.add(targetTextUnitDTO(105L, "name1 _other", "content1_other", "comment1", "my/path1", "other", "name1_other")); + + document = mapper.readFromTargetTextUnits(textUnits); + + assertThat(document).isNotNull(); + assertThat(document.getSingulars()).hasSize(1); + AndroidSingular singular = document.getSingulars().get(0); + assertThat(singular.getId()).isEqualTo(123L); + assertThat(singular.getName()).isEqualTo("my/path0#@#name0"); + assertThat(singular.getContent()).isEqualTo("content0"); + assertThat(singular.getComment()).isEqualTo("comment0"); + + assertThat(document.getPlurals()).hasSize(1); + AndroidPlural plural = document.getPlurals().get(0); + assertThat(plural.getName()).isEqualTo("my/path1#@#name1"); + assertThat(plural.getComment()).isEqualTo("comment1"); + assertThat(plural.getItems()).hasSize(6); + assertThat(plural.getItems().get(ZERO).getId()).isEqualTo(100L); + assertThat(plural.getItems().get(ZERO).getContent()).isEqualTo("content1_zero"); + assertThat(plural.getItems().get(ONE).getId()).isEqualTo(101L); + assertThat(plural.getItems().get(ONE).getContent()).isEqualTo("content1_one"); + assertThat(plural.getItems().get(TWO).getId()).isEqualTo(102L); + assertThat(plural.getItems().get(TWO).getContent()).isEqualTo("content1_two"); + assertThat(plural.getItems().get(FEW).getId()).isEqualTo(103L); + assertThat(plural.getItems().get(FEW).getContent()).isEqualTo("content1_few"); + assertThat(plural.getItems().get(MANY).getId()).isEqualTo(104L); + assertThat(plural.getItems().get(MANY).getContent()).isEqualTo("content1_many"); + assertThat(plural.getItems().get(OTHER).getId()).isEqualTo(105L); + assertThat(plural.getItems().get(OTHER).getContent()).isEqualTo("content1_other"); + } + + @Test + public void testMapToTextUnitsFromEmptyDocument() { + mapper = new AndroidStringDocumentMapper("", assetDelimiter); + document = new AndroidStringDocument(); + + textUnits = mapper.mapToTextUnits(document); + + assertThat(textUnits).isEmpty(); + } + + @Test + public void testMapToTextUnitsFromSingularsDocument() { + mapper = new AndroidStringDocumentMapper("", assetDelimiter); + + document = new AndroidStringDocument(); + document.addSingular(new AndroidSingular(10L, "string/path1#@#name1", "content1", "comment1")); + document.addSingular(new AndroidSingular(11L, "string/path2#@#name2", "content2", "comment2")); + textUnits = mapper.mapToTextUnits(document); + + assertThat(textUnits).isNotEmpty(); + assertThat(textUnits).hasSize(2); + assertThat(textUnits).extracting("tmTextUnitId", "name", "assetPath", "target") + .contains(Tuple.tuple(10L, "name1", "string/path1", "content1")) + .contains(Tuple.tuple(11L, "name2", "string/path2", "content2")); + } + + @Test + public void testMapToTextUnitsFromPluralsDocument() { + String locale = "pt-BR"; + String repo = "brazil"; + mapper = new AndroidStringDocumentMapper("_", assetDelimiter, locale, repo); + + document = new AndroidStringDocument(); + AndroidPlural.AndroidPluralBuilder builder1 = AndroidPlural.builder(); + builder1.setComment("comment1"); + builder1.setName("string/path1#@#name1"); + builder1.addItem(new AndroidPluralItem(10L, ONE, "one1_content")); + builder1.addItem(new AndroidPluralItem(11L, MANY, "many1_content")); + document.addPlural(builder1.build()); + + AndroidPlural.AndroidPluralBuilder builder2 = AndroidPlural.builder(); + builder2.setComment("comment2"); + builder2.setName("string/path2#@#name2"); + builder2.addItem(new AndroidPluralItem(21L, ONE, "one2_content")); + builder2.addItem(new AndroidPluralItem(22L, TWO, "two2_content")); + builder2.addItem(new AndroidPluralItem(23L, MANY, "many2_content")); + document.addPlural(builder2.build()); + + AndroidPlural.AndroidPluralBuilder builder3 = AndroidPlural.builder(); + builder3.setComment("comment3"); + builder3.setName("string/path3#@#name3"); + builder3.addItem(new AndroidPluralItem(30L, ZERO, "zero3_content")); + builder3.addItem(new AndroidPluralItem(31L, ONE, "one3_content")); + builder3.addItem(new AndroidPluralItem(32L, TWO, "two3_content")); + builder3.addItem(new AndroidPluralItem(33L, FEW, "few3_content")); + builder3.addItem(new AndroidPluralItem(34L, MANY, "many3_content")); + builder3.addItem(new AndroidPluralItem(35L, OTHER, "other3_content")); + document.addPlural(builder3.build()); + + textUnits = mapper.mapToTextUnits(document); + + assertThat(textUnits).hasSize(11); + assertThat(textUnits) + .extracting("tmTextUnitId", "name", "pluralForm", "assetPath", "target", "targetLocale", "repositoryName") + .contains(tuple(10L, "name1_one", "one", "string/path1", "one1_content", locale, repo)) + .contains(tuple(11L, "name1_many", "many", "string/path1", "many1_content", locale, repo)) + .contains(tuple(21L, "name2_one", "one", "string/path2", "one2_content", locale, repo)) + .contains(tuple(22L, "name2_two", "two", "string/path2", "two2_content", locale, repo)) + .contains(tuple(23L, "name2_many", "many", "string/path2", "many2_content", locale, repo)) + .contains(tuple(30L, "name3_zero", "zero", "string/path3", "zero3_content", locale, repo)) + .contains(tuple(31L, "name3_one", "one", "string/path3", "one3_content", locale, repo)) + .contains(tuple(32L, "name3_two", "two", "string/path3", "two3_content", locale, repo)) + .contains(tuple(33L, "name3_few", "few", "string/path3", "few3_content", locale, repo)) + .contains(tuple(34L, "name3_many", "many", "string/path3", "many3_content", locale, repo)) + .contains(tuple(35L, "name3_other", "other", "string/path3", "other3_content", locale, repo)); + } + + @Test + public void testReadingTextUnitsFromFile() throws Exception { + String path = Resources.getResource("com/box/l10n/mojito/android/strings/test_resources_file.xml").getPath(); + mapper = new AndroidStringDocumentMapper("_", assetDelimiter); + textUnits = mapper.mapToTextUnits(AndroidStringDocumentReader.fromFile(path)); + + assertThat(textUnits).isNotEmpty(); + assertThat(textUnits).extracting("assetPath").containsOnly("Mojito/src/main/res/values/strings.xml"); + + assertThat(textUnits).filteredOn(tu -> tu.getName().equalsIgnoreCase("some_string")) + .extracting("target", "comment") + .containsOnly(tuple("Dela...", "Some string")); + + assertThat(textUnits).filteredOn(tu -> tu.getName().equalsIgnoreCase("show_options")) + .extracting("target", "comment") + .containsOnly(tuple("Mer \" dela", "Options")); + + assertThat(textUnits).filteredOn(tu -> tu.getName().equalsIgnoreCase("without_comment")) + .extracting("target", "comment") + .containsOnly(tuple("ei ' kommentteja", null)); + + assertThat(textUnits).filteredOn(tu -> tu.getName().equalsIgnoreCase("line_break")) + .extracting("target", "comment") + .containsOnly(tuple("salto de \nlinea", null)); + + assertThat(textUnits).filteredOn(tu -> tu.getName().startsWith("test_plural_hindi")) + .extracting("name", "pluralForm", "target") + .contains(tuple("test_plural_hindi_one", "one", "%1$d @ बहुवचन")) + .contains(tuple("test_plural_hindi_other", "other", "%1$d @ विलक्षण")); + + assertThat(textUnits).filteredOn(tu -> tu.getName().startsWith("plural_russian")) + .extracting("name", "pluralForm", "target") + .contains(tuple("plural_russian_one", "one", "одно слово")) + .contains(tuple("plural_russian_many", "many", "больше слов")); + + } + + @Test + public void testRemoveBadCharacters() { + + assertThat(removeBadCharacters(null)).isEqualTo(""); + assertThat(removeBadCharacters("")).isEqualTo(""); + assertThat(removeBadCharacters("String")).isEqualTo("String"); + assertThat(removeBadCharacters("second" + '\u0000' + "String")).isEqualTo("secondString"); + assertThat(removeBadCharacters("third" + '\u001c' + "String")).isEqualTo("thirdString"); + assertThat(removeBadCharacters("fourth" + '\u001d' + "String")).isEqualTo("fourthString"); + assertThat(removeBadCharacters("all" + '\u0000' + "Bad" + '\u001c' + "Characters" + '\u001d' + "Removed")) + .isEqualTo("allBadCharactersRemoved"); + + } + + private TextUnitDTO sourceTextUnitDTO(Long id, String name, String content, String comment, String assetPath, String pluralForm, String pluralFormOther){ + return textUnitDTO(id, name, content, comment, assetPath, pluralForm, pluralFormOther, true); + } + + private TextUnitDTO targetTextUnitDTO(Long id, String name, String content, String comment, String assetPath, String pluralForm, String pluralFormOther){ + return textUnitDTO(id, name, content, comment, assetPath, pluralForm, pluralFormOther, false); + } + + private TextUnitDTO textUnitDTO(Long id, String name, String content, String comment, String assetPath, String pluralForm, String pluralFormOther, boolean toSource){ + + TextUnitDTO textUnit = new TextUnitDTO(); + textUnit.setTmTextUnitId(id); + textUnit.setName(name); + textUnit.setComment(comment); + textUnit.setAssetPath(assetPath); + textUnit.setPluralForm(pluralForm); + textUnit.setPluralFormOther(pluralFormOther); + + if (toSource) { + textUnit.setSource(content); + } else { + textUnit.setTarget(content); + } + + return textUnit; + } +} diff --git a/webapp/src/test/java/com/box/l10n/mojito/android/strings/AndroidStringDocumentWriterTest.java b/webapp/src/test/java/com/box/l10n/mojito/android/strings/AndroidStringDocumentWriterTest.java index e4c33361bf..a864abdfda 100644 --- a/webapp/src/test/java/com/box/l10n/mojito/android/strings/AndroidStringDocumentWriterTest.java +++ b/webapp/src/test/java/com/box/l10n/mojito/android/strings/AndroidStringDocumentWriterTest.java @@ -12,6 +12,7 @@ import java.nio.charset.StandardCharsets; import java.util.List; +import static com.box.l10n.mojito.android.strings.AndroidStringDocumentWriter.escapeQuotes; import static org.assertj.core.api.Assertions.assertThat; public class AndroidStringDocumentWriterTest { @@ -214,6 +215,16 @@ public void testWritePreservesOrdering() throws Exception { assertThat(getTempFileContent()).isEqualTo(result); } + @Test + public void testEscapeQuotes() { + assertThat(escapeQuotes(null)).isEqualTo(""); + assertThat(escapeQuotes("")).isEqualTo(""); + assertThat(escapeQuotes("String")).isEqualTo("String"); + assertThat(escapeQuotes("second\\\"String")).isEqualTo("second\\\\\"String"); + assertThat(escapeQuotes("third\nString")).isEqualTo("third\\nString"); + assertThat(escapeQuotes("fourth\ntest\\\"String\\\"")).isEqualTo("fourth\\ntest\\\\\"String\\\\\""); + } + private String getTempFileContent() throws IOException { return FileUtils.readFileToString(tmpFile, StandardCharsets.UTF_8); } diff --git a/webapp/src/test/java/com/box/l10n/mojito/service/tm/PluralNameParserTest.java b/webapp/src/test/java/com/box/l10n/mojito/service/tm/PluralNameParserTest.java index 70d265811e..c4fbfa3786 100644 --- a/webapp/src/test/java/com/box/l10n/mojito/service/tm/PluralNameParserTest.java +++ b/webapp/src/test/java/com/box/l10n/mojito/service/tm/PluralNameParserTest.java @@ -2,19 +2,42 @@ import org.junit.Test; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class PluralNameParserTest { PluralNameParser pluralNameParser = new PluralNameParser(); @Test - public void testGetPrefix() { - assertEquals("name", pluralNameParser.getPrefix("name_zero")); - assertEquals("name", pluralNameParser.getPrefix("name_one")); - assertEquals("name", pluralNameParser.getPrefix("name_two")); - assertEquals("name", pluralNameParser.getPrefix("name_few")); - assertEquals("name", pluralNameParser.getPrefix("name_many")); - assertEquals("name", pluralNameParser.getPrefix("name_other")); + public void testGetPrefixWithoutSeparator() { + assertThat(pluralNameParser.getPrefix("name_zero")).isEqualTo("name"); + assertThat(pluralNameParser.getPrefix("name_one")).isEqualTo("name"); + assertThat(pluralNameParser.getPrefix("name_two")).isEqualTo("name"); + assertThat(pluralNameParser.getPrefix("name_few")).isEqualTo("name"); + assertThat(pluralNameParser.getPrefix("name_many")).isEqualTo("name"); + assertThat(pluralNameParser.getPrefix("name_other")).isEqualTo("name"); + } + + @Test + public void testGetPrefixWithSeparator() { + assertThat(pluralNameParser.getPrefix("name_zero", "_")).isEqualTo("name"); + assertThat(pluralNameParser.getPrefix("name_one", "_")).isEqualTo("name"); + assertThat(pluralNameParser.getPrefix("name_two", "_")).isEqualTo("name"); + assertThat(pluralNameParser.getPrefix("name_few", "_")).isEqualTo("name"); + assertThat(pluralNameParser.getPrefix("name_many", "_")).isEqualTo("name"); + assertThat(pluralNameParser.getPrefix("name_other", "_")).isEqualTo("name"); + + assertThat(pluralNameParser.getPrefix("name _zero", " _")).isEqualTo("name"); + assertThat(pluralNameParser.getPrefix("name _one", " _")).isEqualTo("name"); + assertThat(pluralNameParser.getPrefix("name _two", " _")).isEqualTo("name"); + assertThat(pluralNameParser.getPrefix("name _few", " _")).isEqualTo("name"); + assertThat(pluralNameParser.getPrefix("name _many", " _")).isEqualTo("name"); + assertThat(pluralNameParser.getPrefix("name _other", " _")).isEqualTo("name"); + + assertThat(pluralNameParser.getPrefix("#@#name _zero", " _")).isEqualTo("#@#name"); + assertThat(pluralNameParser.getPrefix("$%name@ _one", " _")).isEqualTo("$%name@"); + assertThat(pluralNameParser.getPrefix("_name_ _two", " _")).isEqualTo("_name_"); + assertThat(pluralNameParser.getPrefix("name_few", " _")).isEqualTo("name_few"); + assertThat(pluralNameParser.getPrefix("name%many", "_")).isEqualTo("name%many"); } } diff --git a/webapp/src/test/resources/com/box/l10n/mojito/android/strings/test_resources_file.xml b/webapp/src/test/resources/com/box/l10n/mojito/android/strings/test_resources_file.xml new file mode 100644 index 0000000000..6f877f2698 --- /dev/null +++ b/webapp/src/test/resources/com/box/l10n/mojito/android/strings/test_resources_file.xml @@ -0,0 +1,19 @@ + + + + Dela... + + Mer \\" dela + ei \\' kommentteja + salto de \nlinea + + + %1$d \@ बहुवचन + %1$d \@ विलक्षण + + + + одно слово + больше слов + +