diff --git a/src/main/java/com/crowdin/cli/properties/FileBean.java b/src/main/java/com/crowdin/cli/properties/FileBean.java index d02886bc0..eec195e6b 100755 --- a/src/main/java/com/crowdin/cli/properties/FileBean.java +++ b/src/main/java/com/crowdin/cli/properties/FileBean.java @@ -11,34 +11,7 @@ import java.util.Map; import static com.crowdin.cli.BaseCli.RESOURCE_BUNDLE; -import static com.crowdin.cli.properties.PropertiesBuilder.CONTENT_SEGMENTATION; -import static com.crowdin.cli.properties.PropertiesBuilder.CUSTOM_SEGMENTATION; -import static com.crowdin.cli.properties.PropertiesBuilder.DEST; -import static com.crowdin.cli.properties.PropertiesBuilder.ESCAPE_QUOTES; -import static com.crowdin.cli.properties.PropertiesBuilder.ESCAPE_SPECIAL_CHARACTERS; -import static com.crowdin.cli.properties.PropertiesBuilder.EXPORT_QUOTES; -import static com.crowdin.cli.properties.PropertiesBuilder.EXCLUDED_TARGET_LANGUAGES; -import static com.crowdin.cli.properties.PropertiesBuilder.EXPORT_APPROVED_ONLY; -import static com.crowdin.cli.properties.PropertiesBuilder.FIRST_LINE_CONTAINS_HEADER; -import static com.crowdin.cli.properties.PropertiesBuilder.IGNORE; -import static com.crowdin.cli.properties.PropertiesBuilder.LABELS; -import static com.crowdin.cli.properties.PropertiesBuilder.LANGUAGES_MAPPING; -import static com.crowdin.cli.properties.PropertiesBuilder.MULTILINGUAL_SPREADSHEET; -import static com.crowdin.cli.properties.PropertiesBuilder.SCHEME; -import static com.crowdin.cli.properties.PropertiesBuilder.SKIP_UNTRANSLATED_FILES; -import static com.crowdin.cli.properties.PropertiesBuilder.SKIP_UNTRANSLATED_STRINGS; -import static com.crowdin.cli.properties.PropertiesBuilder.EXPORT_STRINGS_THAT_PASSED_WORKFLOW; -import static com.crowdin.cli.properties.PropertiesBuilder.SOURCE; -import static com.crowdin.cli.properties.PropertiesBuilder.TRANSLATABLE_ELEMENTS; -import static com.crowdin.cli.properties.PropertiesBuilder.TRANSLATE_ATTRIBUTES; -import static com.crowdin.cli.properties.PropertiesBuilder.TRANSLATE_CONTENT; -import static com.crowdin.cli.properties.PropertiesBuilder.TRANSLATION; -import static com.crowdin.cli.properties.PropertiesBuilder.TRANSLATION_REPLACE; -import static com.crowdin.cli.properties.PropertiesBuilder.TYPE; -import static com.crowdin.cli.properties.PropertiesBuilder.UPDATE_OPTION; -import static com.crowdin.cli.properties.PropertiesBuilder.IMPORT_TRANSLATIONS; -import static com.crowdin.cli.properties.PropertiesBuilder.checkForDoubleAsterisks; -import static com.crowdin.cli.properties.PropertiesBuilder.hasRelativePaths; +import static com.crowdin.cli.properties.PropertiesBuilder.*; @Data public class FileBean { @@ -48,6 +21,7 @@ public class FileBean { private String source; private String translation; private List ignore; + private Boolean multilingual; private String dest; private String type; private String updateOption; @@ -98,6 +72,7 @@ public FileBean buildFromMap(Map map) { PropertiesBuilder.setBooleanPropertyIfExists(fileBean::setTranslateAttributes, map, TRANSLATE_ATTRIBUTES); PropertiesBuilder.setBooleanPropertyIfExists(fileBean::setTranslateContent, map, TRANSLATE_CONTENT); PropertiesBuilder.setBooleanPropertyIfExists(fileBean::setContentSegmentation, map, CONTENT_SEGMENTATION); + PropertiesBuilder.setBooleanPropertyIfExists(fileBean::setMultilingual, map, MULTILINGUAL); PropertiesBuilder.setBooleanPropertyIfExists(fileBean::setMultilingualSpreadsheet, map, MULTILINGUAL_SPREADSHEET); PropertiesBuilder.setBooleanPropertyIfExists(fileBean::setSkipTranslatedOnly, map, SKIP_UNTRANSLATED_STRINGS); PropertiesBuilder.setBooleanPropertyIfExists(fileBean::setSkipUntranslatedFiles, map, SKIP_UNTRANSLATED_FILES); @@ -120,7 +95,9 @@ public void populateWithDefaultValues(FileBean bean) { //Translation if (bean.getTranslation() != null) { bean.setTranslation(Utils.normalizePath(bean.getTranslation())); - if (!PlaceholderUtil.containsLangPlaceholders(bean.getTranslation()) && bean.getScheme() != null) { + if (!PlaceholderUtil.containsLangPlaceholders(bean.getTranslation()) + && (bean.getScheme() != null + || (bean.getMultilingual() != null && bean.getMultilingual()))) { bean.setTranslation(Utils.noSepAtStart(bean.getTranslation())); } else { bean.setTranslation(Utils.sepAtStart(bean.getTranslation())); @@ -135,6 +112,10 @@ public void populateWithDefaultValues(FileBean bean) { } bean.setIgnore(ignores); } + //Multilingual + if (bean.getMultilingual() == null) { + bean.setMultilingual(Boolean.FALSE); + } //dest if (StringUtils.isNotEmpty(bean.getDest())) { bean.setDest(bean.getDest().replaceAll("[/\\\\]+", Utils.PATH_SEPARATOR_REGEX)); @@ -179,7 +160,9 @@ public List checkProperties(FileBean bean) { errors.add(RESOURCE_BUNDLE.getString("error.config.double_asterisk")); } - if (!PlaceholderUtil.containsLangPlaceholders(bean.getTranslation()) && bean.getScheme() == null) { + if (!PlaceholderUtil.containsLangPlaceholders(bean.getTranslation()) + && bean.getScheme() == null + && (bean.getMultilingual() == null || !bean.getMultilingual())) { errors.add(RESOURCE_BUNDLE.getString("error.config.translation_has_no_language_placeholders")); } diff --git a/src/main/java/com/crowdin/cli/properties/PropertiesBuilder.java b/src/main/java/com/crowdin/cli/properties/PropertiesBuilder.java index fd94ccce6..514df2fe0 100644 --- a/src/main/java/com/crowdin/cli/properties/PropertiesBuilder.java +++ b/src/main/java/com/crowdin/cli/properties/PropertiesBuilder.java @@ -52,6 +52,8 @@ public abstract class PropertiesBuilder public static final String IGNORE = "ignore"; + public static final String MULTILINGUAL = "multilingual"; + public static final String DEST = "dest"; public static final String TYPE = "type"; diff --git a/src/test/java/com/crowdin/cli/commands/functionality/PropertiesBuilderTest.java b/src/test/java/com/crowdin/cli/commands/functionality/PropertiesBuilderTest.java index 4f418ba68..2b111020e 100644 --- a/src/test/java/com/crowdin/cli/commands/functionality/PropertiesBuilderTest.java +++ b/src/test/java/com/crowdin/cli/commands/functionality/PropertiesBuilderTest.java @@ -13,6 +13,7 @@ import static com.crowdin.cli.BaseCli.RESOURCE_BUNDLE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -181,6 +182,21 @@ public void testBuildNoConfigFileAndNoToken() { } + @Test + public void testNoTranslationLangPlaceholder() { + ParamsWithFiles params = new ParamsWithFiles() {{ + setIdParam("666"); + setTokenParam("123abc456"); + setSourceParam(Utils.regexPath(Utils.normalizePath("/Localizable.xcstrings"))); + setTranslationParam("Localizable.xcstrings"); + }}; + + Exception actualException = assertThrows(RuntimeException.class, () -> + propertiesBuilders.buildPropertiesWithFiles(out, null, null, params)); + + assertTrue(actualException.getMessage().contains(RESOURCE_BUNDLE.getString("error.config.translation_has_no_language_placeholders"))); + } + @Test public void testPropertiesWithTarget() { File configFile = new File("folder/crowdinTest.yml"); diff --git a/website/docs/advanced.md b/website/docs/advanced.md index 8871dd8c1..4355592ed 100644 --- a/website/docs/advanced.md +++ b/website/docs/advanced.md @@ -123,7 +123,7 @@ Visit the [KB article](https://developer.crowdin.com/pseudolocalization/) to rea **Escape Quotes** -The `escape_qutes` option defines whether a single quote should be escaped by another single quote or backslash in exported translations. You can add the `escape_quotes` per-file option. Acceptable values are `0`, `1`, `2`, `3`. Default is `3`. +The `escape_quotes` option defines whether a single quote should be escaped by another single quote or backslash in exported translations. You can add the `escape_quotes` per-file option. Acceptable values are `0`, `1`, `2`, `3`. Default is `3`. - `0` - do not escape - `1` - escape single quote with another single quote @@ -168,24 +168,20 @@ Example of the configuration: ] ``` -#### Apple Strings Catalog +### Multilingual Files -Apple Strings Catalog is a natively supported file format by Crowdin, you can use it without any additional installation. Since this file format is **multilingual** (contains multiple languages in one file), you can omit the language placeholders in the `translation` pattern: +For multilingual file formats (containing multiple languages in one file) you can use the `multilingual` option in the configuration. This option allows you to omit the language placeholders in the `translation` pattern: ```yml title="crowdin.yml" "files": [ { "source": "Localizable.xcstrings", "translation": "Localizable.xcstrings", - "scheme": "" + "multilingual": true } ] ``` -:::caution -Note the `scheme` option in the configuration above. It's needed to indicate that this file is multilingual for correct file processing. -::: - ### Configure export options for each file group There is a way to specify export options for each file-group in the `crowdin.yml` configuration file: