From 503a974bc037521a9f34f0d25f465535f36e7fc6 Mon Sep 17 00:00:00 2001 From: Kateryna Oblakevych Date: Wed, 6 Mar 2024 11:40:48 +0200 Subject: [PATCH] feat: plural strings (#731) Co-authored-by: Andrii Bodnar --- .../cli/client/CrowdinProjectClient.java | 14 ++++ .../com/crowdin/cli/client/ProjectClient.java | 4 ++ .../com/crowdin/cli/commands/Actions.java | 3 +- .../cli/commands/actions/CliActions.java | 5 +- .../cli/commands/actions/StringAddAction.java | 63 +++++++++-------- .../commands/actions/StringListAction.java | 15 +++- .../functionality/RequestBuilder.java | 41 ++++++++++- .../commands/picocli/StringAddSubcommand.java | 18 ++++- .../resources/messages/messages.properties | 6 ++ .../cli/commands/actions/CliActionsTest.java | 2 +- .../commands/actions/StringAddActionTest.java | 70 +++++++++++++++++-- .../commands/picocli/PicocliTestUtils.java | 2 +- .../picocli/StringAddSubcommandTest.java | 4 +- versions.properties | 2 +- website/mantemplates/crowdin-string-add.adoc | 10 +++ 15 files changed, 211 insertions(+), 48 deletions(-) diff --git a/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java b/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java index 3d6e7a809..f718e92cf 100644 --- a/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java +++ b/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java @@ -326,6 +326,13 @@ public SourceString addSourceString(AddSourceStringRequest request) { .getData()); } + @Override + public SourceString addSourcePluralString(AddSourcePluralStringRequest request) { + return executeRequest(() -> this.client.getSourceStringsApi() + .addSourcePluralString(this.projectId, request) + .getData()); + } + @Override public SourceString addSourceStringStringsBased(AddSourceStringStringsBasedRequest request) { return executeRequest(() -> this.client.getSourceStringsApi() @@ -333,6 +340,13 @@ public SourceString addSourceStringStringsBased(AddSourceStringStringsBasedReque .getData()); } + @Override + public SourceString addSourcePluralStringStringsBased(AddSourcePluralStringStringsBasedRequest request) { + return executeRequest(() -> this.client.getSourceStringsApi() + .addSourcePluralStringStringsBased(this.projectId, request) + .getData()); + } + @Override public List listSourceString(Long fileId, Long branchId, String labelIds, String filter, String croql) { return executeRequestFullList((limit, offset) -> this.client.getSourceStringsApi() diff --git a/src/main/java/com/crowdin/cli/client/ProjectClient.java b/src/main/java/com/crowdin/cli/client/ProjectClient.java index f1deb5f17..9a0699456 100644 --- a/src/main/java/com/crowdin/cli/client/ProjectClient.java +++ b/src/main/java/com/crowdin/cli/client/ProjectClient.java @@ -80,8 +80,12 @@ default CrowdinProjectFull downloadFullProject() { SourceString addSourceString(AddSourceStringRequest request); + SourceString addSourcePluralString(AddSourcePluralStringRequest request); + SourceString addSourceStringStringsBased(AddSourceStringStringsBasedRequest request); + SourceString addSourcePluralStringStringsBased(AddSourcePluralStringStringsBasedRequest request); + List listSourceString(Long fileId, Long branchId, String labelIds, String filter, String croql); void deleteSourceString(Long id); diff --git a/src/main/java/com/crowdin/cli/commands/Actions.java b/src/main/java/com/crowdin/cli/commands/Actions.java index b116a42c2..067096106 100644 --- a/src/main/java/com/crowdin/cli/commands/Actions.java +++ b/src/main/java/com/crowdin/cli/commands/Actions.java @@ -48,7 +48,8 @@ NewAction status( boolean noProgress, String branchName, String languageId, String file, String directory, boolean isVerbose, boolean showTranslated, boolean showApproved, boolean failIfIncomplete); NewAction stringAdd( - boolean noProgress, String text, String identifier, Integer maxLength, String context, List files, List labelNames, String branch, Boolean hidden); + boolean noProgress, String text, String identifier, Integer maxLength, String context, List files, List labelNames, String branch, Boolean hidden, + String one, String two, String few, String many, String zero); NewAction stringComment(boolean plainView, boolean noProgress, String text, String stringId, String language, String type, String issueType); diff --git a/src/main/java/com/crowdin/cli/commands/actions/CliActions.java b/src/main/java/com/crowdin/cli/commands/actions/CliActions.java index 6a0b6c66f..3c0c8e4bc 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/CliActions.java +++ b/src/main/java/com/crowdin/cli/commands/actions/CliActions.java @@ -80,9 +80,10 @@ public NewAction status( @Override public NewAction stringAdd( - boolean noProgress, String text, String identifier, Integer maxLength, String context, List files, List labelNames, String branch, Boolean hidden + boolean noProgress, String text, String identifier, Integer maxLength, String context, List files, List labelNames, String branch, Boolean hidden, + String one, String two, String few, String many, String zero ) { - return new StringAddAction(noProgress, text, identifier, maxLength, context, files, labelNames, branch, hidden); + return new StringAddAction(noProgress, text, identifier, maxLength, context, files, labelNames, branch, hidden, one, two, few, many, zero); } @Override public NewAction stringComment(boolean plainView, diff --git a/src/main/java/com/crowdin/cli/commands/actions/StringAddAction.java b/src/main/java/com/crowdin/cli/commands/actions/StringAddAction.java index 11c890fbe..750f8d462 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/StringAddAction.java +++ b/src/main/java/com/crowdin/cli/commands/actions/StringAddAction.java @@ -13,8 +13,11 @@ import com.crowdin.client.projectsgroups.model.Type; import com.crowdin.client.sourcefiles.model.Branch; import com.crowdin.client.sourcefiles.model.FileInfo; +import com.crowdin.client.sourcestrings.model.AddSourcePluralStringRequest; +import com.crowdin.client.sourcestrings.model.AddSourcePluralStringStringsBasedRequest; import com.crowdin.client.sourcestrings.model.AddSourceStringRequest; import com.crowdin.client.sourcestrings.model.AddSourceStringStringsBasedRequest; +import lombok.Data; import java.util.List; import java.util.Map; @@ -25,6 +28,7 @@ import static com.crowdin.cli.utils.console.ExecutionStatus.OK; import static com.crowdin.cli.utils.console.ExecutionStatus.WARNING; +@Data class StringAddAction implements NewAction { private final boolean noProgress; @@ -36,45 +40,40 @@ class StringAddAction implements NewAction { private final List labelNames; private final String branchName; private final Boolean hidden; - - public StringAddAction( - boolean noProgress, String text, String identifier, Integer maxLength, String context, List files, List labelNames, String branchName, Boolean hidden - ) { - this.noProgress = noProgress; - this.text = text; - this.identifier = identifier; - this.maxLength = maxLength; - this.context = context; - this.files = files; - this.labelNames = labelNames; - this.branchName = branchName; - this.hidden = hidden; - } + private final String one; + private final String two; + private final String few; + private final String many; + private final String zero; @Override public void act(Outputter out, ProjectProperties pb, ProjectClient client) { CrowdinProjectFull project = ConsoleSpinner.execute(out, "message.spinner.fetching_project_info", "error.collect_project_info", this.noProgress, false, client::downloadFullProject); boolean isStringsBasedProject = Objects.equals(project.getType(), Type.STRINGS_BASED); + boolean isPluralString = one != null || two != null || few != null || many != null || zero != null; List labelIds = (labelNames != null && !labelNames.isEmpty()) ? this.prepareLabelIds(client) : null; - - if (files == null || files.isEmpty()) { - if (isStringsBasedProject) { - Branch branch = BranchUtils.getOrCreateBranch(out, branchName, client, project, false); - if (Objects.isNull(branch)) { - throw new RuntimeException(RESOURCE_BUNDLE.getString("error.branch_required_string_project")); - } + if (isStringsBasedProject) { + if (files != null && !files.isEmpty()) { + throw new RuntimeException(RESOURCE_BUNDLE.getString("message.no_file_string_project")); + } + Branch branch = BranchUtils.getOrCreateBranch(out, branchName, client, project, false); + if (Objects.isNull(branch)) { + throw new RuntimeException(RESOURCE_BUNDLE.getString("error.branch_required_string_project")); + } + if (isPluralString) { + AddSourcePluralStringStringsBasedRequest request = RequestBuilder.addPluralStringStringsBased( + this.text, this.identifier, this.maxLength, this.context, branch.getId(), this.hidden, labelIds, one, two, few, many, zero); + client.addSourcePluralStringStringsBased(request); + } else { AddSourceStringStringsBasedRequest request = RequestBuilder.addStringStringsBased(this.text, this.identifier, this.maxLength, this.context, branch.getId(), this.hidden, labelIds); client.addSourceStringStringsBased(request); - } else { - AddSourceStringRequest request = RequestBuilder.addString(this.text, this.identifier, this.maxLength, this.context, null, this.hidden, labelIds); - client.addSourceString(request); } out.println(OK.withIcon(RESOURCE_BUNDLE.getString("message.source_string_uploaded"))); } else { - if (isStringsBasedProject) { - throw new RuntimeException(RESOURCE_BUNDLE.getString("message.no_file_string_project")); + if (files == null || files.isEmpty()) { + throw new RuntimeException(RESOURCE_BUNDLE.getString("error.file_required")); } Map paths = ProjectFilesUtils.buildFilePaths(project.getDirectories(), project.getBranches(), project.getFileInfos()); boolean containsError = false; @@ -90,9 +89,15 @@ public void act(Outputter out, ProjectProperties pb, ProjectClient client) { } Long fileId = paths.get(file).getId(); - AddSourceStringRequest request = - RequestBuilder.addString(this.text, this.identifier, this.maxLength, this.context, fileId, this.hidden, labelIds); - client.addSourceString(request); + if (isPluralString) { + AddSourcePluralStringRequest request = RequestBuilder.addPluralString( + this.text, this.identifier, this.maxLength, this.context, fileId, this.hidden, labelIds, one, two, few, many, zero); + client.addSourcePluralString(request); + } else { + AddSourceStringRequest request = + RequestBuilder.addString(this.text, this.identifier, this.maxLength, this.context, fileId, this.hidden, labelIds); + client.addSourceString(request); + } out.println(OK.withIcon(String.format(RESOURCE_BUNDLE.getString("message.source_string_for_file_uploaded"), file))); } if (containsError) { diff --git a/src/main/java/com/crowdin/cli/commands/actions/StringListAction.java b/src/main/java/com/crowdin/cli/commands/actions/StringListAction.java index 3ab9bbf8d..0cb408770 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/StringListAction.java +++ b/src/main/java/com/crowdin/cli/commands/actions/StringListAction.java @@ -17,6 +17,7 @@ import java.util.List; import java.util.Map; +import java.util.HashMap; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @@ -94,7 +95,19 @@ public void act(Outputter out, ProjectProperties pb, ProjectClient client) { String labelsString = (ss.getLabelIds() != null) ? ss.getLabelIds().stream().map(labelsMap::get).map(s -> String.format("[@|cyan %s|@]", s)).collect(Collectors.joining(" ")) : ""; - out.println(String.format(RESOURCE_BUNDLE.getString("message.source_string_list_text"), ss.getId(), ss.getText(), labelsString)); + StringBuilder text = new StringBuilder(); + if (ss.getText() instanceof HashMap) { + HashMap map = (HashMap) ss.getText(); + for (Map.Entry entry : map.entrySet()) { + text.append(entry.getKey()).append(": ").append(entry.getValue()).append(" | "); + } + if (text.length() > 0) { + text.delete(text.length() - 3, text.length()); + } + } else { + text.append((String) ss.getText()); + } + out.println(String.format(RESOURCE_BUNDLE.getString("message.source_string_list_text"), ss.getId(), text, labelsString)); if (isVerbose) { if (ss.getIdentifier() != null) { out.println(String.format("\t- @|bold identifier|@: '%s'", ss.getIdentifier())); diff --git a/src/main/java/com/crowdin/cli/commands/functionality/RequestBuilder.java b/src/main/java/com/crowdin/cli/commands/functionality/RequestBuilder.java index bc323cdc2..42d2f538e 100644 --- a/src/main/java/com/crowdin/cli/commands/functionality/RequestBuilder.java +++ b/src/main/java/com/crowdin/cli/commands/functionality/RequestBuilder.java @@ -11,8 +11,7 @@ import com.crowdin.client.glossaries.model.ImportGlossaryRequest; import com.crowdin.client.labels.model.AddLabelRequest; import com.crowdin.client.sourcefiles.model.AddBranchRequest; -import com.crowdin.client.sourcestrings.model.AddSourceStringRequest; -import com.crowdin.client.sourcestrings.model.AddSourceStringStringsBasedRequest; +import com.crowdin.client.sourcestrings.model.*; import com.crowdin.client.stringcomments.model.AddStringCommentRequest; import com.crowdin.client.stringcomments.model.Type; import com.crowdin.client.tasks.model.CreateTaskRequest; @@ -42,6 +41,20 @@ public static AddSourceStringRequest addString(String text, String identifier, I return request; } + public static AddSourcePluralStringRequest addPluralString(String text, String identifier, Integer maxLength, String context, Long fileId, Boolean hidden, List labelIds, + String one, String two, String few, String many, String zero) { + AddSourcePluralStringRequest request = new AddSourcePluralStringRequest(); + PluralText pluralText = buildPluralText(text, one, two, few, many, zero); + request.setText(pluralText); + request.setIdentifier(identifier); + request.setMaxLength(maxLength); + request.setContext(context); + request.setFileId(fileId); + request.setIsHidden(hidden); + request.setLabelIds(labelIds); + return request; + } + public static AddSourceStringStringsBasedRequest addStringStringsBased(String text, String identifier, Integer maxLength, String context, Long branchId, Boolean hidden, List labelIds) { AddSourceStringStringsBasedRequest request = new AddSourceStringStringsBasedRequest(); request.setText(text); @@ -54,6 +67,20 @@ public static AddSourceStringStringsBasedRequest addStringStringsBased(String te return request; } + public static AddSourcePluralStringStringsBasedRequest addPluralStringStringsBased(String text, String identifier, Integer maxLength, String context, Long branchId, Boolean hidden, List labelIds, + String one, String two, String few, String many, String zero) { + AddSourcePluralStringStringsBasedRequest request = new AddSourcePluralStringStringsBasedRequest(); + PluralText pluralText = buildPluralText(text, one, two, few, many, zero); + request.setText(pluralText); + request.setIdentifier(identifier); + request.setMaxLength(maxLength); + request.setContext(context); + request.setBranchId(branchId); + request.setIsHidden(hidden); + request.setLabelIds(labelIds); + return request; + } + public static AddStringCommentRequest addComment(String text, String type, String language, String issueType, String stringId) { AddStringCommentRequest request = new AddStringCommentRequest(); @@ -326,4 +353,14 @@ public static AddBranchRequest addBranch(String name, String title, String expor return request; } + private static PluralText buildPluralText(String text, String one, String two, String few, String many, String zero) { + PluralText pluralText = new PluralText(); + Optional.ofNullable(text).ifPresent(pluralText::setOther); + Optional.ofNullable(one).ifPresent(pluralText::setOne); + Optional.ofNullable(two).ifPresent(pluralText::setTwo); + Optional.ofNullable(few).ifPresent(pluralText::setFew); + Optional.ofNullable(many).ifPresent(pluralText::setMany); + Optional.ofNullable(zero).ifPresent(pluralText::setZero); + return pluralText; + } } diff --git a/src/main/java/com/crowdin/cli/commands/picocli/StringAddSubcommand.java b/src/main/java/com/crowdin/cli/commands/picocli/StringAddSubcommand.java index 9c25c170d..1bdc7da79 100644 --- a/src/main/java/com/crowdin/cli/commands/picocli/StringAddSubcommand.java +++ b/src/main/java/com/crowdin/cli/commands/picocli/StringAddSubcommand.java @@ -41,6 +41,21 @@ class StringAddSubcommand extends ActCommandProject { @CommandLine.Option(names = {"--hidden"}, order = -2) protected Boolean isHidden; + @CommandLine.Option(names = {"--one"}, descriptionKey = "crowdin.string.add.one", paramLabel = "...", order = -2) + protected String one; + + @CommandLine.Option(names = {"--two"}, descriptionKey = "crowdin.string.add.two", paramLabel = "...", order = -2) + protected String two; + + @CommandLine.Option(names = {"--few"}, descriptionKey = "crowdin.string.add.few", paramLabel = "...", order = -2) + protected String few; + + @CommandLine.Option(names = {"--many"}, descriptionKey = "crowdin.string.add.many", paramLabel = "...", order = -2) + protected String many; + + @CommandLine.Option(names = {"--zero"}, descriptionKey = "crowdin.string.add.zero", paramLabel = "...", order = -2) + protected String zero; + @Override protected List checkOptions() { List errors = new ArrayList<>(); @@ -58,6 +73,7 @@ protected List checkOptions() { @Override protected NewAction getAction(Actions actions) { - return actions.stringAdd(noProgress, text, identifier, maxLength, context, files, labelNames, branch, isHidden); + return actions.stringAdd(noProgress, text, identifier, maxLength, context, files, labelNames, branch, isHidden, + one, two, few, many, zero); } } diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index 06c9636ef..ea87b1738 100755 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -179,6 +179,11 @@ crowdin.string.add.max-length=Set a max. length of the translated text for the n crowdin.string.add.context=Add a context for the new source string crowdin.string.add.file=Specify a file the new source string should be added to (multiple files could be specified) crowdin.string.add.hidden=Choose whether or not the added strings should be hidden in your Crowdin project +crowdin.string.add.one=Plural form one (singular) +crowdin.string.add.two=Plural form two (dual) +crowdin.string.add.few=Plural form few (paucal) +crowdin.string.add.many=Plural form many +crowdin.string.add.zero=Plural form zero # CROWDIN STRING COMMENT COMMAND crowdin.string.comment.usage.description=Add a comment to the given string @@ -465,6 +470,7 @@ error.while_checking_base_path=Failed to check the base path. Try to run the app error.skip_untranslated_both_strings_and_files=You cannot skip strings and files at the same time. Please use one of these parameters instead. error.file_not_exists=Project doesn't contain the '%s' file error.file_id_not_exists=Project doesn't contain file with id '%s' +error.file_required=File is required error.dir_not_exists=Project doesn't contain the '%s' directory error.label_not_exists=Project doesn't contain the '%s' label error.branch_not_exists=Project doesn't contain the '%s' branch diff --git a/src/test/java/com/crowdin/cli/commands/actions/CliActionsTest.java b/src/test/java/com/crowdin/cli/commands/actions/CliActionsTest.java index d2c76aac5..1c622e7ef 100644 --- a/src/test/java/com/crowdin/cli/commands/actions/CliActionsTest.java +++ b/src/test/java/com/crowdin/cli/commands/actions/CliActionsTest.java @@ -49,7 +49,7 @@ public void testStatus() { @Test public void testStringAdd() { - assertNotNull(actions.stringAdd(false, null, null, null, null, null, null, null, null)); + assertNotNull(actions.stringAdd(false, null, null, null, null, null, null, null, null, null, null, null, null, null)); } @Test diff --git a/src/test/java/com/crowdin/cli/commands/actions/StringAddActionTest.java b/src/test/java/com/crowdin/cli/commands/actions/StringAddActionTest.java index 9e701368d..ed8d19327 100644 --- a/src/test/java/com/crowdin/cli/commands/actions/StringAddActionTest.java +++ b/src/test/java/com/crowdin/cli/commands/actions/StringAddActionTest.java @@ -13,6 +13,8 @@ import com.crowdin.cli.utils.Utils; import com.crowdin.client.labels.model.Label; import com.crowdin.client.projectsgroups.model.Type; +import com.crowdin.client.sourcestrings.model.AddSourcePluralStringRequest; +import com.crowdin.client.sourcestrings.model.AddSourcePluralStringStringsBasedRequest; import com.crowdin.client.sourcestrings.model.AddSourceStringRequest; import com.crowdin.client.sourcestrings.model.AddSourceStringStringsBasedRequest; import org.junit.jupiter.api.Test; @@ -76,7 +78,7 @@ public void testStringAdd( .thenReturn(build); action = - new StringAddAction(true, text, identifier, maxLength, context, Arrays.asList(stringFiles), labelNames, null, hidden); + new StringAddAction(true, text, identifier, maxLength, context, Arrays.asList(stringFiles), labelNames, null, hidden, null, null, null, null, null); action.act(Outputter.getDefault(), pb, client); verify(client).downloadFullProject(); @@ -92,8 +94,7 @@ public static Stream testStringAdd() { put("first.csv", 801L); }}; return Stream.of( - arguments("first text", "1.1", 42, "It's first text", null, null, false, headers, new String[] {"first.csv"}), - arguments("first text", "1.1", 42, "It's first text", null, null, false, new HashMap(), new String[0]) + arguments("first text", "1.1", 42, "It's first text", null, null, false, headers, new String[] {"first.csv"}) ); } @@ -142,7 +143,7 @@ public void testStringAdd_throwsNotFound() { .thenReturn(build); action = - new StringAddAction(true, text, identifier, maxLength, context, Arrays.asList(stringFiles), labelNames, null, hidden); + new StringAddAction(true, text, identifier, maxLength, context, Arrays.asList(stringFiles), labelNames, null, hidden, null, null, null, null, null); assertThrows(RuntimeException.class, () -> action.act(Outputter.getDefault(), pb, client)); verify(client).downloadFullProject(); @@ -164,7 +165,7 @@ public void testGetProjectThrows() throws ResponseException { when(client.downloadFullProject()) .thenThrow(new RuntimeException("Whoops")); - action = new StringAddAction(false, null, null, null, null, null, null, null, null); + action = new StringAddAction(false, null, null, null, null, null, null, null, null, null, null, null, null, null); assertThrows(RuntimeException.class, () -> action.act(Outputter.getDefault(), pb, client)); verify(client).downloadFullProject(); @@ -224,7 +225,7 @@ public void testStringAdd_UseLabels() { action = - new StringAddAction(true, text, identifier, maxLength, context, Arrays.asList(stringFiles), new ArrayList<>(labels.values()), null, hidden); + new StringAddAction(true, text, identifier, maxLength, context, Arrays.asList(stringFiles), new ArrayList<>(labels.values()), null, hidden, null, null, null, null, null); action.act(Outputter.getDefault(), pb, client); verify(client).downloadFullProject(); @@ -243,6 +244,34 @@ private Label createLabel(Long id, String title) { return label; } + @Test + public void testStringAdd_PluralString() { + NewPropertiesWithFilesUtilBuilder pbBuilder = NewPropertiesWithFilesUtilBuilder + .minimalBuiltPropertiesBean("*", Utils.PATH_SEPARATOR + "%original_file_name%-CR-%locale%") + .setBasePath(Utils.PATH_SEPARATOR); + PropertiesWithFiles pb = pbBuilder.build(); + AddSourcePluralStringRequest request = RequestBuilder.addPluralString( + "strings", "1.1", 42, "It's first text", 101L, false, null, "string", null, null, null, null); + + ProjectBuilder projectBuilder = ProjectBuilder.emptyProject(Long.parseLong(pb.getProjectId())); + projectBuilder.addFile("file.csv", "csv", 101L, null, null); + + ProjectClient client = mock(ProjectClient.class); + CrowdinProjectFull build = projectBuilder.build(); + build.setType(Type.FILES_BASED); + when(client.downloadFullProject()) + .thenReturn(build); + + action = + new StringAddAction(true, "strings", "1.1", 42, "It's first text", Arrays.asList("file.csv"), null, null, false, "string", null, null, null, null); + action.act(Outputter.getDefault(), pb, client); + + verify(client).downloadFullProject(); + verify(client).addSourcePluralString(request); + + verifyNoMoreInteractions(client); + } + @Test public void testStringAdd_StringsBasedProject() { NewPropertiesWithFilesUtilBuilder pbBuilder = NewPropertiesWithFilesUtilBuilder @@ -261,7 +290,7 @@ public void testStringAdd_StringsBasedProject() { .thenReturn(build); action = - new StringAddAction(true, "first text", "1.1", 42, "It's first text", null, null, "main", false); + new StringAddAction(true, "first text", "1.1", 42, "It's first text", null, null, "main", false, null, null, null, null, null); action.act(Outputter.getDefault(), pb, client); verify(client).downloadFullProject(); @@ -269,4 +298,31 @@ public void testStringAdd_StringsBasedProject() { verifyNoMoreInteractions(client); } + + @Test + public void testStringAdd_PluralStringStringsBasedProject() { + NewPropertiesWithFilesUtilBuilder pbBuilder = NewPropertiesWithFilesUtilBuilder + .minimalBuiltPropertiesBean("*", Utils.PATH_SEPARATOR + "%original_file_name%-CR-%locale%") + .setBasePath(Utils.PATH_SEPARATOR); + PropertiesWithFiles pb = pbBuilder.build(); + AddSourcePluralStringStringsBasedRequest request = RequestBuilder.addPluralStringStringsBased( + "strings", "1.1", 42, "It's first text", 1L, false, null, "string", null, null, null, null); + + ProjectBuilder projectBuilder = ProjectBuilder.emptyProject(Long.parseLong(pb.getProjectId())).addBranches(1L, "main"); + + ProjectClient client = mock(ProjectClient.class); + CrowdinProjectFull build = projectBuilder.build(); + build.setType(Type.STRINGS_BASED); + when(client.downloadFullProject()) + .thenReturn(build); + + action = + new StringAddAction(true, "strings", "1.1", 42, "It's first text", null, null, "main", false, "string", null, null, null, null); + action.act(Outputter.getDefault(), pb, client); + + verify(client).downloadFullProject(); + verify(client).addSourcePluralStringStringsBased(request); + + verifyNoMoreInteractions(client); + } } diff --git a/src/test/java/com/crowdin/cli/commands/picocli/PicocliTestUtils.java b/src/test/java/com/crowdin/cli/commands/picocli/PicocliTestUtils.java index 71dc4a773..d3462ac51 100644 --- a/src/test/java/com/crowdin/cli/commands/picocli/PicocliTestUtils.java +++ b/src/test/java/com/crowdin/cli/commands/picocli/PicocliTestUtils.java @@ -72,7 +72,7 @@ void mockActions() { .thenReturn(actionMock); when(actionsMock.status(anyBoolean(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean())) .thenReturn(actionMock); - when(actionsMock.stringAdd(anyBoolean(), any(), any(), any(), any(), any(), any(), any(), any())) + when(actionsMock.stringAdd(anyBoolean(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any())) .thenReturn(actionMock); when(actionsMock.stringComment(anyBoolean(), anyBoolean(), any(), any(), any(), any(), any())) .thenReturn(actionMock); diff --git a/src/test/java/com/crowdin/cli/commands/picocli/StringAddSubcommandTest.java b/src/test/java/com/crowdin/cli/commands/picocli/StringAddSubcommandTest.java index 2d639955d..041b181d3 100644 --- a/src/test/java/com/crowdin/cli/commands/picocli/StringAddSubcommandTest.java +++ b/src/test/java/com/crowdin/cli/commands/picocli/StringAddSubcommandTest.java @@ -12,7 +12,7 @@ public class StringAddSubcommandTest extends PicocliTestUtils { public void testStringAdd() { this.execute(CommandNames.STRING, CommandNames.STRING_ADD, "\"Text\"", "--debug"); verify(actionsMock) - .stringAdd(anyBoolean(), any(), any(), any(), any(), any(), any(), any(), any()); + .stringAdd(anyBoolean(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); this.check(true); } @@ -25,7 +25,7 @@ public void testStringAddInvalidOptions() { public void testStringAdd2() { this.execute(CommandNames.STRING, CommandNames.STRING_ADD, "\"Text\"", "--file", "path/to/file.txt"); verify(actionsMock) - .stringAdd(anyBoolean(), any(), any(), any(), any(), any(), any(), any(), any()); + .stringAdd(anyBoolean(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); this.check(true); } } diff --git a/versions.properties b/versions.properties index 89c02c2f3..ec0b3de44 100644 --- a/versions.properties +++ b/versions.properties @@ -43,7 +43,7 @@ version.commons-io..commons-io=2.14.0 version.commons-cli..commons-cli=1.5.0 -version.com.github.crowdin..crowdin-api-client-java=1.14.0 +version.com.github.crowdin..crowdin-api-client-java=1.15.0 plugin.org.asciidoctor.jvm.convert=3.3.2 diff --git a/website/mantemplates/crowdin-string-add.adoc b/website/mantemplates/crowdin-string-add.adoc index eaf75bc7f..7fddeaf1b 100644 --- a/website/mantemplates/crowdin-string-add.adoc +++ b/website/mantemplates/crowdin-string-add.adoc @@ -34,3 +34,13 @@ Add a new string with the `app.hello` key to the `strings.xml` file, with labels ---- crowdin string add "Hello world" --file strings.xml --identifier "app.hello" --label app --label home ---- + +Add a new plural string: + +---- +crowdin string add Cats --file strings.xml --identifier "ui.cats" --one "cat" +---- + +In this example, the `` argument is used as the "other" plural form. + +If your source language contains more plural forms, you can also specify them using the `--two', `--few', `--many' and `--zero' options.