Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 27 additions & 18 deletions src/main/java/com/crowdin/cli/commands/actions/DownloadAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,8 @@ public void act(Outputter out, PropertiesWithFiles pb, ProjectClient client) {
PlaceholderUtil placeholderUtil =
new PlaceholderUtil(project.getProjectLanguages(true), pb.getBasePath());

List<Language> languages = languageIds == null ? null : languageIds.stream()
.map(lang -> project.findLanguageById(lang)
.orElseThrow(() -> new RuntimeException(
String.format(RESOURCE_BUNDLE.getString("error.language_not_exist"), lang))))
.collect(Collectors.toList());
List<Language> excludeLanguages = excludeLanguageIds == null ? new ArrayList<>() : excludeLanguageIds.stream()
.map(lang -> project.findLanguageById(lang)
.orElseThrow(() -> new RuntimeException(
String.format(RESOURCE_BUNDLE.getString("error.language_not_exist"), lang))))
.collect(Collectors.toList());
List<Language> languages = languageIds == null ? null : resolveLanguagesById(languageIds, project);
List<Language> excludeLanguages = excludeLanguageIds == null ? new ArrayList<>() : resolveLanguagesById(excludeLanguageIds, project);

Optional<Branch> branch = Optional.ofNullable(project.getBranch());

Expand Down Expand Up @@ -157,21 +149,30 @@ public void act(Outputter out, PropertiesWithFiles pb, ProjectClient client) {
}
tempDirs.put(downloadedFiles.getLeft(), downloadedFiles.getRight());
} else {
List<Language> forLanguages = languages != null ? languages :
project.getProjectLanguages(true).stream()
.filter(language -> !excludeLanguages.contains(language))
.collect(Collectors.toList());

List<Language> forLanguages;
List<Language> exportLanguages = new ArrayList<>();
if (languages != null) {
forLanguages = languages;
} else {
List<Language> baseLanguages;
List<String> exportLanguageIdsFromConfig = pb.getExportLanguages();
exportLanguages = exportLanguageIdsFromConfig == null ? new ArrayList<>() : resolveLanguagesById(exportLanguageIdsFromConfig, project);
baseLanguages = exportLanguages.isEmpty() ? project.getProjectLanguages(true) : exportLanguages;
Set<String> excludeIds = excludeLanguages.stream()
.map(Language::getId)
.collect(Collectors.toSet());
forLanguages = baseLanguages.stream()
.filter(l -> !excludeIds.contains(l.getId()))
.collect(Collectors.toList());
}
if (!plainView) {
out.println((languageIds != null)
? OK.withIcon(String.format(RESOURCE_BUNDLE.getString("message.build_language_archive"), String.join(", ", languageIds)))
: OK.withIcon(RESOURCE_BUNDLE.getString("message.build_archive")));
}
CrowdinTranslationCreateProjectBuildForm templateRequest = new CrowdinTranslationCreateProjectBuildForm();

if (languages != null) {
templateRequest.setTargetLanguageIds(languages.stream().map(Language::getId).collect(Collectors.toList()));
} else if (!excludeLanguages.isEmpty()) {
if (languages != null || !exportLanguages.isEmpty() || !excludeLanguages.isEmpty()) {
templateRequest.setTargetLanguageIds(forLanguages.stream().map(Language::getId).collect(Collectors.toList()));
}

Expand Down Expand Up @@ -334,6 +335,14 @@ public void act(Outputter out, PropertiesWithFiles pb, ProjectClient client) {
}
}

private List<Language> resolveLanguagesById(List<String> languageIds, CrowdinProjectFull project) {
return languageIds.stream()
.map(lang -> project.findLanguageById(lang)
.orElseThrow(() -> new RuntimeException(
String.format(RESOURCE_BUNDLE.getString("error.language_not_exist"), lang))))
.collect(Collectors.toList());
}

/**
* Download archive, extract it and return information about that temporary directory
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ public abstract class PropertiesBuilder<T extends Properties, P extends Params>

public static final String IMPORT_TRANSLATIONS = "import_translations";

public static final String EXPORT_LANGUAGES = "export_languages";

private Outputter out;
private Map<String, Object> configFileParams;
private Map<String, Object> identityFileParams;
Expand Down
13 changes: 10 additions & 3 deletions src/main/java/com/crowdin/cli/properties/PropertiesWithFiles.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@

import static com.crowdin.cli.BaseCli.IGNORE_HIDDEN_FILES_PATTERN;
import static com.crowdin.cli.BaseCli.RESOURCE_BUNDLE;
import static com.crowdin.cli.properties.PropertiesBuilder.FILES;
import static com.crowdin.cli.properties.PropertiesBuilder.PRESERVE_HIERARCHY;
import static com.crowdin.cli.properties.PropertiesBuilder.PSEUDO_LOCALIZATION;
import static com.crowdin.cli.properties.PropertiesBuilder.*;

@EqualsAndHashCode(callSuper = true)
@Data
Expand All @@ -27,6 +25,7 @@ public class PropertiesWithFiles extends ProjectProperties {
private Boolean preserveHierarchy;
private List<FileBean> files;
private PseudoLocalization pseudoLocalization;
private List<String> exportLanguages;

static class PropertiesWithFilesConfigurator implements PropertiesConfigurator<PropertiesWithFiles> {

Expand All @@ -42,6 +41,7 @@ public void populateWithValues(PropertiesWithFiles props, Map<String, Object> ma
.map(FileBean.CONFIGURATOR::buildFromMap)
.collect(Collectors.toList()));
props.setPseudoLocalization(PseudoLocalization.CONFIGURATOR.buildFromMap(PropertiesBuilder.getMap(map, PSEUDO_LOCALIZATION)));
PropertiesBuilder.setPropertyIfExists(props::setExportLanguages, map, EXPORT_LANGUAGES, List.class);
}

@Override
Expand Down Expand Up @@ -100,6 +100,13 @@ public PropertiesBuilder.Messages checkProperties(PropertiesWithFiles props, Che
}
}
}
if (props.getExportLanguages() != null) {
List<?> raw = (List<?>) (Object) props.getExportLanguages();
boolean hasNonString = raw.stream().anyMatch(item -> !(item instanceof String));
if (hasNonString) {
messages.addError(String.format(RESOURCE_BUNDLE.getString("error.config.list_of_strings"), EXPORT_LANGUAGES));
}
}
if (props.getPseudoLocalization() != null) {
messages.addAllErrors(PseudoLocalization.CONFIGURATOR.checkProperties(props.getPseudoLocalization()));
}
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/messages/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,7 @@ error.config.enum_class_exception=Configuration file contains unexpected '%s' va
error.config.enum_wrong_value=Configuration file contains unexpected '%s' value. The expected values are: %s
error.config.pseudo_localization_length_correction_out_of_bounds=Acceptable value for 'length_correction' is from -50 to 100
error.config.languages_mapping=The mapping format is the following: crowdin_language_code: code_you_use. Check the full list of Crowdin language codes that can be used for mapping: https://developer.crowdin.com/language-codes.
error.config.list_of_strings=Property %s must be a list of strings
error.init.project_id_is_not_number='%s' is not a number! (Enter the correct value or leave the field empty)
error.init.skip_project_validation=Skipping project checking due to lack of parameters
error.init.path_not_exist=Path '%s' doesn't exist
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

import static org.junit.jupiter.api.Assertions.assertThrows;
Expand Down Expand Up @@ -909,4 +910,59 @@ public void testProjectFittingFile_MultilingualWithDest() throws IOException, Re
verify(files).deleteDirectory(tempDir.get());
verifyNoMoreInteractions(files);
}

@Test
public void testProjectFittingFile_ExportLanguagesOnly() throws IOException, ResponseException {
NewPropertiesWithFilesUtilBuilder pbBuilder = NewPropertiesWithFilesUtilBuilder
.minimalBuiltPropertiesBean("*", Utils.PATH_SEPARATOR + "%original_file_name%-CR-%locale%")
.setBasePath(project.getBasePath())
.setExportLanguages(List.of("de", "ua"));
PropertiesWithFiles pb = pbBuilder.build();

project.addFile("first.po");

ProjectClient client = mock(ProjectClient.class);
when(client.downloadFullProject(null))
.thenReturn(ProjectBuilder.emptyProject(Long.parseLong(pb.getProjectId()))
.addFile("first.po", "gettext", 101L, null, null, "/%original_file_name%-CR-%locale%").build());
CrowdinTranslationCreateProjectBuildForm expectedRequest = new CrowdinTranslationCreateProjectBuildForm();
expectedRequest.setTargetLanguageIds(List.of("de"));

long buildId = 42L;
when(client.startBuildingTranslation(eq(expectedRequest)))
.thenReturn(buildProjectBuild(buildId, Long.parseLong(pb.getProjectId()), "finished", 100));
URL urlMock = MockitoUtils.getMockUrl(getClass());
when(client.downloadBuild(eq(buildId)))
.thenReturn(urlMock);

FilesInterface files = mock(FilesInterface.class);
AtomicReference<File> zipArchive = new AtomicReference<>();
AtomicReference<File> tempDir = new AtomicReference<>();
when(files.extractZipArchive(any(), any()))
.thenAnswer((invocation -> {
zipArchive.set(invocation.getArgument(0));
tempDir.set(invocation.getArgument(1));
return new ArrayList<File>() {{
add(new File(tempDir.get().getAbsolutePath() + Utils.PATH_SEPARATOR + "first.po-CR-de-DE"));
}};
}));

NewAction<PropertiesWithFiles, ProjectClient> action =
new DownloadAction(files, false, null, List.of("ua"), false, null, false, false, false, false, false);
action.act(Outputter.getDefault(), pb, client);

verify(client).downloadFullProject(null);
verify(client).startBuildingTranslation(eq(expectedRequest));
verify(client).downloadBuild(eq(buildId));
verifyNoMoreInteractions(client);

verify(files).writeToFile(any(), any());
verify(files).extractZipArchive(any(), any());
verify(files).copyFile(
new File(tempDir.get().getAbsolutePath() + Utils.PATH_SEPARATOR + "first.po-CR-de-DE"),
new File(pb.getBasePath() + "first.po-CR-de-DE"));
verify(files).deleteFile(eq(zipArchive.get()));
verify(files).deleteDirectory(tempDir.get());
verifyNoMoreInteractions(files);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ public NewPropertiesWithFilesUtilBuilder setPreserveHierarchy(Boolean preserveHi
return this;
}

public NewPropertiesWithFilesUtilBuilder setExportLanguages(List<String> exportLanguages) {
this.pb.setExportLanguages(exportLanguages);
return this;
}

public NewPropertiesWithFilesUtilBuilder addBuiltFileBean(String source, String translation) {
return this.addBuiltFileBean(source, translation, null, null);
}
Expand Down Expand Up @@ -120,6 +125,14 @@ public String buildToString() {
if (pb.getPreserveHierarchy() != null) {
sb.append("\"preserve_hierarchy\": \"").append(pb.getPreserveHierarchy()).append("\"\n");
}
if (pb.getExportLanguages() != null && !pb.getExportLanguages().isEmpty()) {
sb.append("\"export_languages\": [");
for (int i = 0; i < pb.getExportLanguages().size(); i++) {
if (i > 0) sb.append(", ");
sb.append("\"").append(pb.getExportLanguages().get(i)).append("\"");
}
sb.append("]\n");
}
if (pb.getFiles() != null && !pb.getFiles().isEmpty()) {
sb.append("files: [\n");
for (FileBean fb : pb.getFiles()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.crowdin.cli.properties.PropertiesConfigurator.CheckType;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
Expand Down Expand Up @@ -119,4 +124,28 @@ public void testCheckProperties_sourceDoubleAsteriskPattern_noError() {

assertTrue(messages.getErrors().isEmpty(), "Expected no errors when ** pattern matches files");
}

@Test
public void testPropertiesWithFiles_parseExportLanguagesFromMap() {
Map<String, Object> map = new HashMap<>();
map.put("project_id", "1");
map.put("api_token", "token");
map.put("base_path", ".");
map.put("base_url", "https://crowdin.com");
map.put("preserve_hierarchy", false);

List<Map<String, Object>> files = new ArrayList<>();
Map<String, Object> file = new HashMap<>();
file.put("source", "*");
file.put("translation", "/%original_file_name%-CR-%locale%");
files.add(file);
map.put("files", files);

map.put("export_languages", List.of("de", "ua"));

PropertiesWithFiles props = new PropertiesWithFiles();
PropertiesWithFiles.CONFIGURATOR.populateWithValues(props, map);

assertEquals(List.of("de", "ua"), props.getExportLanguages());
}
}
Loading