diff --git a/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/expected/target/fr.ts b/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/expected/target/fr.ts index 0582848988..9fb8d4f22f 100644 --- a/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/expected/target/fr.ts +++ b/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/expected/target/fr.ts @@ -1,5 +1,6 @@ namespace Translations { - "100_character_description_": "Description de 100 caractères :", + "100_character_description_": `Description de +100 caractères : \`command\``, // File lock dialog duration "15_min_duration": "15 min", // File lock dialog duration diff --git a/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/expected/target/ja.ts b/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/expected/target/ja.ts index 25c4863929..119a5637f2 100644 --- a/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/expected/target/ja.ts +++ b/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/expected/target/ja.ts @@ -1,5 +1,6 @@ namespace Translations { - "100_character_description_": "Description de 100 caractères :", + "100_character_description_": `100文字 +の説明: \`command\``, // File lock dialog duration "15_min_duration": "15分", // File lock dialog duration diff --git a/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/expected/target_modified/fr.ts b/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/expected/target_modified/fr.ts index d7d0b22024..3ff9607c47 100644 --- a/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/expected/target_modified/fr.ts +++ b/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/expected/target_modified/fr.ts @@ -1,5 +1,6 @@ namespace Translations { - "100_character_description_": "Description de 100 caractères :", + "100_character_description_": `Description de +100 caractères : \`command\``, // File lock dialog duration "15_min_duration": "15 min", // File lock dialog duration diff --git a/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/expected/target_modified/ja.ts b/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/expected/target_modified/ja.ts index c7f78ae7d4..2785990370 100644 --- a/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/expected/target_modified/ja.ts +++ b/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/expected/target_modified/ja.ts @@ -1,5 +1,6 @@ namespace Translations { - "100_character_description_": "Description de 100 caractères :", + "100_character_description_": `100文字 +の説明: \`command\``, // File lock dialog duration "15_min_duration": "15分", // File lock dialog duration diff --git a/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/input/source/en.ts b/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/input/source/en.ts index 049ed2c5a7..825a95521f 100644 --- a/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/input/source/en.ts +++ b/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/input/source/en.ts @@ -1,5 +1,6 @@ namespace Translations { - "100_character_description_": "100 character description:", + "100_character_description_": `100 character +description: \`command\``, // File lock dialog duration "15_min_duration": "15 min", // File lock dialog duration diff --git a/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/input/source_modified/en.ts b/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/input/source_modified/en.ts index 8a386da5cb..567acc23e5 100644 --- a/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/input/source_modified/en.ts +++ b/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/input/source_modified/en.ts @@ -1,5 +1,6 @@ namespace Translations { - "100_character_description_": "100 character description:", + "100_character_description_": `100 character +description: \`command\``, // File lock dialog duration "15_min_duration": "15 min", // File lock dialog duration diff --git a/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/input/translations/source-xliff_fr-FR.xliff b/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/input/translations/source-xliff_fr-FR.xliff index bc04285bb2..725c423aa6 100644 --- a/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/input/translations/source-xliff_fr-FR.xliff +++ b/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/input/translations/source-xliff_fr-FR.xliff @@ -1,10 +1,12 @@ - + - 100 character description: - Description de 100 caractères : + 100 character +description: `command` + Description de +100 caractères : `command` 15 min diff --git a/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/input/translations/source-xliff_ja-JP.xliff b/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/input/translations/source-xliff_ja-JP.xliff index 249e38b745..81e0894d62 100644 --- a/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/input/translations/source-xliff_ja-JP.xliff +++ b/cli/src/test/resources/com/box/l10n/mojito/cli/command/PullCommandTest/pullTS/input/translations/source-xliff_ja-JP.xliff @@ -1,10 +1,12 @@ - + - 100 character description: - Description de 100 caractères : + 100 character +description: `command` + 100文字 +の説明: `command` 15 min diff --git a/webapp/src/main/java/com/box/l10n/mojito/okapi/filters/JSEncoder.java b/webapp/src/main/java/com/box/l10n/mojito/okapi/filters/JSEncoder.java new file mode 100644 index 0000000000..25a92742d6 --- /dev/null +++ b/webapp/src/main/java/com/box/l10n/mojito/okapi/filters/JSEncoder.java @@ -0,0 +1,35 @@ +package com.box.l10n.mojito.okapi.filters; + +import net.sf.okapi.common.encoder.EncoderContext; + +/** + * + * @author jyi + */ +public class JSEncoder extends SimpleEncoder { + + @Override + public String encode(char value, EncoderContext context) { + String res; + + switch (value) { + case '\n': + res = "\\n"; + break; + case '\r': + res = "\\r"; + break; + case '"': + res = "\\\""; + break; + case '`': + res = "\\`"; + break; + default: + res = String.valueOf(value); + break; + } + + return res; + } +} diff --git a/webapp/src/main/java/com/box/l10n/mojito/okapi/filters/JSFilter.java b/webapp/src/main/java/com/box/l10n/mojito/okapi/filters/JSFilter.java index 438c50de3f..0e25e19b42 100644 --- a/webapp/src/main/java/com/box/l10n/mojito/okapi/filters/JSFilter.java +++ b/webapp/src/main/java/com/box/l10n/mojito/okapi/filters/JSFilter.java @@ -1,21 +1,79 @@ package com.box.l10n.mojito.okapi.filters; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; +import net.sf.okapi.common.BOMNewlineEncodingDetector; +import net.sf.okapi.common.Event; +import net.sf.okapi.common.EventType; +import net.sf.okapi.common.IParameters; +import net.sf.okapi.common.MimeTypeMapper; +import net.sf.okapi.common.Util; +import net.sf.okapi.common.encoder.EncoderManager; +import net.sf.okapi.common.exceptions.OkapiIOException; +import net.sf.okapi.common.exceptions.OkapiUnsupportedEncodingException; +import net.sf.okapi.common.filters.AbstractFilter; import net.sf.okapi.common.filters.FilterConfiguration; +import net.sf.okapi.common.filterwriter.GenericFilterWriter; +import net.sf.okapi.common.filterwriter.IFilterWriter; +import net.sf.okapi.common.resource.Ending; +import net.sf.okapi.common.resource.ITextUnit; +import net.sf.okapi.common.resource.Property; +import net.sf.okapi.common.resource.RawDocument; +import net.sf.okapi.common.resource.StartDocument; +import net.sf.okapi.common.resource.TextUnit; +import net.sf.okapi.common.skeleton.GenericSkeleton; +import net.sf.okapi.common.skeleton.ISkeletonWriter; +import net.sf.okapi.filters.properties.Parameters; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Configurable; /** * @author jyi */ -public class JSFilter extends RegexEscapeDoubleQuoteFilter { +@Configurable +public class JSFilter extends AbstractFilter { public static final String FILTER_CONFIG_ID = "okf_regex@mojito"; + private static final int STATE_START = 0; + private static final int STATE_KEY = 1; + private static final int STATE_SEP = 2; + private static final int STATE_VALUE = 3; + + private Parameters params; + private BufferedReader reader; + private RawDocument input; + private String docName; + private String encoding; + private boolean hasUTF8BOM; + private String lineBreak; + + EncoderManager encoderManager; + private IFilterWriter filterWriter; + private LinkedList queue; + private int parseState = 0; + + private GenericSkeleton skel; + private String textLine; + + @Autowired + UnescapeUtils unescapeUtils; @Override public String getName() { return FILTER_CONFIG_ID; } + @Override + public String getMimeType() { + return MimeTypeMapper.JAVASCRIPT_MIME_TYPE; + } + @Override public List getConfigurations() { List list = new ArrayList(); @@ -24,8 +82,301 @@ public List getConfigurations() { getClass().getName(), "Text (JS Strings)", "Configuration for JS .js/.ts files.", - "js_mojito.fprm")); + null)); return list; } + public JSFilter() { + params = new Parameters();; + } + + @Override + protected boolean isUtf8Encoding() { + return StandardCharsets.UTF_8.name().equals(encoding); + } + + @Override + protected boolean isUtf8Bom() { + return hasUTF8BOM; + } + + @Override + public IParameters getParameters() { + return params; + } + + @Override + public void setParameters(IParameters params) { + this.params = (Parameters) params; + } + + @Override + public EncoderManager getEncoderManager() { + if (encoderManager == null) { + encoderManager = new EncoderManager(); + encoderManager.setMapping(getMimeType(), "com.box.l10n.mojito.okapi.filters.JSEncoder"); + } + return encoderManager; + } + + @Override + public ISkeletonWriter createSkeletonWriter() { + return new JSSkeletonWriter(); + } + + @Override + public IFilterWriter createFilterWriter() { + if (filterWriter != null) { + return filterWriter; + } + return new GenericFilterWriter(createSkeletonWriter(), getEncoderManager()); + } + + @Override + public void close() { + if (input != null) { + input.close(); + } + try { + if (reader != null) { + reader.close(); + reader = null; + } + parseState = 0; + } catch (IOException e) { + throw new OkapiIOException(e); + } + } + + @Override + public void open(RawDocument input) { + + // initialize + super.open(input, true); + this.input = input; + parseState = 1; + queue = new LinkedList<>(); + + // detect encoding + BOMNewlineEncodingDetector detector = new BOMNewlineEncodingDetector( + input.getStream(), input.getEncoding()); + detector.detectAndRemoveBom(); + input.setEncoding(detector.getEncoding()); + encoding = input.getEncoding(); + + // open the input reader from the provided reader + try { + reader = new BufferedReader(new InputStreamReader( + detector.getInputStream(), encoding)); + } catch (UnsupportedEncodingException e) { + throw new OkapiUnsupportedEncodingException(String.format( + "The encoding '%s' is not supported.", encoding), e); + } + hasUTF8BOM = detector.hasUtf8Bom(); + lineBreak = detector.getNewlineType().toString(); + if (input.getInputURI() != null) { + docName = input.getInputURI().getPath(); + } + + // start event + StartDocument startDoc = new StartDocument("sd"); + startDoc.setName(docName); + startDoc.setEncoding(encoding, hasUTF8BOM); + startDoc.setLocale(input.getSourceLocale()); + startDoc.setFilterParameters(params); + startDoc.setFilterWriter(createFilterWriter()); + startDoc.setLineBreak(lineBreak); + startDoc.setType(getMimeType()); + startDoc.setMimeType(getMimeType()); + queue.add(new Event(EventType.START_DOCUMENT, startDoc)); + } + + @Override + public boolean hasNext() { + return (parseState > 0); + } + + @Override + public Event next() { + + // cancel if requested + if (super.isCanceled()) { + parseState = 0; + queue.clear(); + queue.add(new Event(EventType.CANCELED)); + } + + // return event in the queue + if (queue.size() > 0) { + return queue.poll(); + } + + // parse the input + ITextUnit textUnit = readTextUnit(); + if (textUnit != null) { + return new Event(EventType.TEXT_UNIT, textUnit); + } + + // end event + Ending ending = new Ending("ed"); + ending.setSkeleton(skel); + parseState = 0; + return new Event(EventType.END_DOCUMENT, ending); + } + + private TextUnit readTextUnit() { + try { + TextUnit textUnit; + skel = new GenericSkeleton(); + String comment = null; + StringBuilder data = new StringBuilder(); + boolean emptyLine = false; + + while (true) { + textLine = reader.readLine(); + + if (textLine == null) { + if (emptyLine) { + skel.append(data.toString()); + } else { + skel.append(data.substring(0, data.lastIndexOf(lineBreak))); + } + return null; + } + + String tmp = Util.trimStart(textLine, "\t\r\n \f"); + + // empty line + if (tmp.length() == 0) { + data.append(textLine).append(lineBreak); + emptyLine = true; + continue; + } else { + emptyLine = false; + } + + // comments + boolean isComment = tmp.startsWith("//"); + if (isComment) { + comment = tmp.substring(2).trim(); + data.append(textLine).append(lineBreak); + continue; + } + + // key/value + boolean isKeyValue = tmp.startsWith("\""); + if (isKeyValue) { + skel.append(data.toString()); + textUnit = processKeyValueLine(); + if (textUnit != null && comment != null) { + textUnit.setProperty(new Property(Property.NOTE, comment, true)); + } + break; + } else { + data.append(textLine).append(lineBreak); + continue; + } + + } + + return textUnit; + + } catch (IOException e) { + throw new OkapiIOException(e); + } + } + + private TextUnit processKeyValueLine() throws IOException { + StringBuilder buffer = new StringBuilder(); + String key = null; + String value = null; + int state = STATE_START; + int pos = 0; + boolean backquoted = false; + boolean done = false; + + while (!done) { + // handle empty line + if (!textLine.isEmpty()) { + char c = textLine.charAt(pos); + switch (state) { + case STATE_START: + buffer.append(c); + if (c == '"') { + skel.append(buffer.toString()); + buffer = new StringBuilder(); + state++; + } + break; + + case STATE_KEY: + if (c == '"' && textLine.charAt(pos - 1) != '\\') { + skel.append(buffer.toString()); + key = buffer.toString(); + buffer = new StringBuilder(); + state++; + } + buffer.append(c); + break; + + case STATE_SEP: + buffer.append(c); + if (c == '"' || c == '`') { + skel.append(buffer.toString()); + buffer = new StringBuilder(); + state++; + if (c == '`') { + backquoted = true; + } + } + break; + + case STATE_VALUE: + if ((backquoted && c == '`' && textLine.charAt(pos - 1) != '\\') || (!backquoted && c == '"' && textLine.charAt(pos - 1) != '\\')) { + value = unescapeUtils.unescape(buffer.toString()); + if (backquoted) { + value = unescapeUtils.replaceEscapedBackquotes(value); + } + buffer = new StringBuilder(); + state++; + } + buffer.append(c); + break; + + default: + buffer.append(c); + } + pos++; + } + + // end of line reached + if (pos == textLine.length()) { + //check for multi-line value + if (state == STATE_VALUE && backquoted) { + buffer.append(lineBreak); + textLine = reader.readLine(); + pos = 0; + } else { + done = true; + } + } + } + + TextUnit textUnit = null; + if (key != null) { + textUnit = new TextUnit(key, value); + textUnit.setName(key); + textUnit.setPreserveWhitespaces(true); + skel.addContentPlaceholder(textUnit, null); + skel.append(buffer.toString()); + skel.append(lineBreak); + textUnit.setSkeleton(skel); + if (backquoted) { + textUnit.setProperty(new Property("template", "true", true)); + } + } + + return textUnit; + } + } diff --git a/webapp/src/main/java/com/box/l10n/mojito/okapi/filters/JSSkeletonWriter.java b/webapp/src/main/java/com/box/l10n/mojito/okapi/filters/JSSkeletonWriter.java new file mode 100644 index 0000000000..9d48cc7d50 --- /dev/null +++ b/webapp/src/main/java/com/box/l10n/mojito/okapi/filters/JSSkeletonWriter.java @@ -0,0 +1,33 @@ +package com.box.l10n.mojito.okapi.filters; + +import net.sf.okapi.common.resource.ITextUnit; +import net.sf.okapi.common.resource.Property; +import net.sf.okapi.common.skeleton.GenericSkeletonWriter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Configurable; + +/** + * + * @author jyi + */ +@Configurable +public class JSSkeletonWriter extends GenericSkeletonWriter { + + @Autowired + UnescapeUtils unescapeUtils; + + @Override + public String processTextUnit(ITextUnit resource) { + String string = super.processTextUnit(resource); + Property template = resource.getProperty("template"); + if (template == null) { + // unescape backquote when the string is double-quoted + string = unescapeUtils.replaceEscapedBackquotes(string); + + } else { + // unescape newline when the string is template string + string = unescapeUtils.replaceEscapedLineFeed(string); + } + return string; + } +} diff --git a/webapp/src/main/java/com/box/l10n/mojito/okapi/filters/UnescapeUtils.java b/webapp/src/main/java/com/box/l10n/mojito/okapi/filters/UnescapeUtils.java index d0f2875115..117e19fd97 100644 --- a/webapp/src/main/java/com/box/l10n/mojito/okapi/filters/UnescapeUtils.java +++ b/webapp/src/main/java/com/box/l10n/mojito/okapi/filters/UnescapeUtils.java @@ -20,6 +20,7 @@ public class UnescapeUtils { private static final Pattern ESCAPED_CARIAGE_RETURN = Pattern.compile("\\\\r"); private static final Pattern ESCAPED_LINE_FEED = Pattern.compile("\\\\n"); private static final Pattern ESCAPED_QUOTES = Pattern.compile("\\\\(\"|')"); + private static final Pattern ESCAPED_BACKQUOTES = Pattern.compile("\\\\(`)"); private static final Pattern ESCAPED_CHARACTERS = Pattern.compile("\\\\(.)?"); private static final Pattern SPACES = Pattern.compile("\\s+"); private static final Pattern LINE_FEED = Pattern.compile("\n"); @@ -55,6 +56,16 @@ String replaceEscapedQuotes(String text) { return ESCAPED_QUOTES.matcher(text).replaceAll("$1"); } + /** + * Replaces \` with ` + * + * @param text + * @return + */ + String replaceEscapedBackquotes(String text) { + return ESCAPED_BACKQUOTES.matcher(text).replaceAll("$1"); + } + /** * Replace other escape character with the character itself. *

diff --git a/webapp/src/main/resources/com/box/l10n/mojito/okapi/filters/js_mojito.fprm b/webapp/src/main/resources/com/box/l10n/mojito/okapi/filters/js_mojito.fprm deleted file mode 100644 index d71f0b7bb8..0000000000 --- a/webapp/src/main/resources/com/box/l10n/mojito/okapi/filters/js_mojito.fprm +++ /dev/null @@ -1,44 +0,0 @@ -#v1 -useLd.b=true -localizeOutside.b=true -startString=" -endString=" -extractOuterStrings.b=false -useBSlashEscape.b=true -useDoubleCharEscape.b=false -oneLevelGroups.b=false -regexOptions.i=40 -mimeType=text/plain -ruleCount.i=2 -rule0.ruleName=LineComments+String -rule0.ruleType.i=0 -rule0.expr=^\s*//\s*(.*?)\n\s*\"(.*?)\":(.*?)$ -rule0.groupSource.i=3 -rule0.groupTarget.i=-1 -rule0.groupName.i=2 -rule0.groupNote.i=1 -rule0.preserveWS.b=true -rule0.useCodeFinder.b=true -rule0.sample=// Comment1$0a$"cancel": "Cancel",$0a$ -rule0.codeFinderRules.count.i=3 -rule0.codeFinderRules.rule0=%(([-0+#]?)[-0+#]?)((\d\$)?)(([\d\*]*)(\.[\d\*]*)?)[dioxXucsfeEgGpn] -rule0.codeFinderRules.rule1=(\\r\\n)|\\a|\\b|\\f|\\n|\\r|\\t|\\v -rule0.codeFinderRules.rule2=\<(/?)\w+[^>]*?> -rule0.codeFinderRules.sample= -rule0.codeFinderRules.useAllRulesWhenTesting.b=false -rule1.ruleName=String -rule1.ruleType.i=0 -rule1.expr=^\s*\"(.*?)\":(.*?)$ -rule1.groupSource.i=2 -rule1.groupTarget.i=-1 -rule1.groupName.i=1 -rule1.groupNote.i=-1 -rule1.preserveWS.b=true -rule1.useCodeFinder.b=true -rule1.sample="cancel": "Cancel",$0a$ -rule1.codeFinderRules.count.i=3 -rule1.codeFinderRules.rule0=%(([-0+#]?)[-0+#]?)((\d\$)?)(([\d\*]*)(\.[\d\*]*)?)[dioxXucsfeEgGpn] -rule1.codeFinderRules.rule1=(\\r\\n)|\\a|\\b|\\f|\\n|\\r|\\t|\\v -rule1.codeFinderRules.rule2=\<(/?)\w+[^>]*?> -rule1.codeFinderRules.sample= -rule1.codeFinderRules.useAllRulesWhenTesting.b=false \ No newline at end of file diff --git a/webapp/src/test/java/com/box/l10n/mojito/service/tm/TMServiceTest.java b/webapp/src/test/java/com/box/l10n/mojito/service/tm/TMServiceTest.java index b7c796c029..62fafe31aa 100644 --- a/webapp/src/test/java/com/box/l10n/mojito/service/tm/TMServiceTest.java +++ b/webapp/src/test/java/com/box/l10n/mojito/service/tm/TMServiceTest.java @@ -2761,13 +2761,22 @@ public void testLocalizeTSFile() throws Exception { + " // login comment\n" + " \"loginText\": \"Log In\",\n" + " // signup comment\n" - + " \"signupText\": \"Sign up\",\n" + + " \"signupText\": \"Sign up with `backquote`\",\n" + " \"quotedText\": \"Hello \\\"%s\\\"\",\n" - + " \"noComment\": \"String with no comment\"\n" + + " \"noComment\": \"String with no comment\\nand newline\",\n" + + " // template literals\n" + + " \"templateText1\": `one line`,\n" + + " \"templateText2\": `one line no comment`,\n" + + " \"templateText3\": `one line \\`/special\\` character`,\n" + + " // template multiline literals\n" + + " \"templateMultilineText1\": `first line\nsecond line`,\n" + + " // template multiline literals with escaped backquote\n" + + " \"templateMultilineText2\": `special character\ncheck \\`/command\\` out`,\n\n" + " };\n" + "}\n" + "\n" + "export default Translations;"; + asset = assetService.createAssetWithContent(repo.getId(), "translations.ts", assetContent); asset = assetRepository.findOne(asset.getId()); assetId = asset.getId(); @@ -2785,14 +2794,16 @@ public void testLocalizeTSFile() throws Exception { textUnitSearcherParameters.setRepositoryIds(repo.getId()); textUnitSearcherParameters.setStatusFilter(StatusFilter.FOR_TRANSLATION); List textUnitDTOs = textUnitSearcher.search(textUnitSearcherParameters); - assertEquals(4, textUnitDTOs.size()); for (TextUnitDTO textUnitDTO : textUnitDTOs) { - if ("quotedText".equals(textUnitDTO.getName())) { - assertEquals("Hello \"%s\"", textUnitDTO.getSource()); - } logger.debug("{}\n{}=[{}]", textUnitDTO.getComment(), textUnitDTO.getName(), textUnitDTO.getSource()); } + assertEquals(9, textUnitDTOs.size()); + assertEquals("Sign up with `backquote`", textUnitDTOs.get(1).getSource()); + assertEquals("Hello \"%s\"", textUnitDTOs.get(2).getSource()); + assertEquals("String with no comment\nand newline", textUnitDTOs.get(3).getSource()); + assertEquals("special character\ncheck `/command` out", textUnitDTOs.get(8).getSource()); + String localizedAsset = tmService.generateLocalized(asset, assetContent, repoLocale, "en-GB", null, null, Status.ALL, InheritanceMode.USE_PARENT); logger.debug("localized=\n{}", localizedAsset); assertEquals(assetContent, localizedAsset);