Skip to content

Commit

Permalink
fix: psedo localization mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
katerina20 committed Jul 11, 2024
1 parent f742d84 commit 4894f01
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 14 deletions.
34 changes: 33 additions & 1 deletion src/main/java/com/crowdin/cli/client/LanguageMapping.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package com.crowdin.cli.client;

import com.crowdin.client.languages.model.Language;
import com.crowdin.client.translations.model.CharTransformation;
import lombok.NonNull;
import lombok.ToString;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;

import static com.crowdin.cli.utils.PlaceholderUtil.*;

@ToString
public class LanguageMapping {
Expand All @@ -15,7 +21,11 @@ protected LanguageMapping() {
}

public boolean containsValue(String langCode, String placeholder) {
return languageMapping.containsKey(langCode) && languageMapping.get(langCode).containsKey(placeholder);
return hasMappingForLangCode(langCode) && languageMapping.get(langCode).containsKey(placeholder);
}

public boolean hasMappingForLangCode(String langCode) {
return languageMapping.containsKey(langCode);
}

public String getValue(String langCode, String placeholder) {
Expand Down Expand Up @@ -71,6 +81,28 @@ public static LanguageMapping fromConfigFileLanguageMapping(Map<String, Map<Stri
: new LanguageMapping(reverse(fileLanguageMapping));
}

public static LanguageMapping pseudoLanguageMapping(CharTransformation charTransformation, Language language) {
String standardCode = languageCodeByCharTransformation(charTransformation);
Map<String, Map<String, String>> languageMapping = new HashMap<>();
Map<String, String> placeholderMapping = new HashMap<>();

Map<String, Supplier<String>> codeMappings = Map.of(
PLACEHOLDER_NAME_TWO_LETTERS_CODE, language::getTwoLettersCode,
PLACEHOLDER_NAME_THREE_LETTERS_CODE, language::getThreeLettersCode,
PLACEHOLDER_NAME_LOCALE, language::getLocale,
PLACEHOLDER_NAME_ANDROID_CODE, language::getAndroidCode,
PLACEHOLDER_NAME_OSX_CODE, language::getOsxCode,
PLACEHOLDER_NAME_OSX_LOCALE, language::getOsxLocale
);
codeMappings.forEach((placeholder, codeSupplier) ->
Optional.ofNullable(codeSupplier.get()).ifPresent(code ->
placeholderMapping.put(placeholder, code)
)
);
languageMapping.put(standardCode, placeholderMapping);
return new LanguageMapping(languageMapping);
}

public static LanguageMapping populate(@NonNull LanguageMapping from, @NonNull LanguageMapping to) {
Map<String, Map<String, String>> sumLangMapping = deepCopy(to.languageMapping);
for (Map.Entry<String, Map<String, String>> entry : from.languageMapping.entrySet()) {
Expand Down
43 changes: 34 additions & 9 deletions src/main/java/com/crowdin/cli/commands/actions/DownloadAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,14 @@ public void act(Outputter out, PropertiesWithFiles pb, ProjectClient client) {
new PlaceholderUtil(
project.getSupportedLanguages(), project.getProjectLanguages(true), pb.getBasePath());

List<Language> languages = languageIds == null ? null : languageIds.stream()
.map(lang -> project.findLanguageById(lang, true)
.orElseThrow(() -> new RuntimeException(
String.format(RESOURCE_BUNDLE.getString("error.language_not_exist"), lang))))
.collect(Collectors.toList());
List<Language> languages = null;
if (!pseudo) {
languages = languageIds == null ? null : languageIds.stream()
.map(lang -> project.findLanguageById(lang, true)
.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, true)
.orElseThrow(() -> new RuntimeException(
Expand All @@ -132,6 +135,14 @@ public void act(Outputter out, PropertiesWithFiles pb, ProjectClient client) {
try {
if (pseudo) {
List<Language> forLanguages = project.getSupportedLanguages();
Optional<Language> langToMap = Optional.empty();
if (languageIds != null) {
Optional<String> langMapping = languageIds.stream().findFirst();
if (langMapping.isPresent()) {
langToMap = forLanguages.stream()
.filter(l -> Objects.equals(l.getId(), langMapping.get())).findFirst();
}
}
if (!plainView) {
out.println(OK.withIcon(RESOURCE_BUNDLE.getString("message.build_archive_pseudo")));
}
Expand All @@ -150,9 +161,14 @@ public void act(Outputter out, PropertiesWithFiles pb, ProjectClient client) {
: RequestBuilder.crowdinTranslationCreateProjectPseudoBuildForm(true, null, null, null, null);
}

LanguageMapping pseudoLanguageMapping = null;
if (langToMap.isPresent() && pl != null) {
pseudoLanguageMapping = LanguageMapping.pseudoLanguageMapping(pl.getCharTransformation(), langToMap.get());
}

Pair<File, List<String>> downloadedFiles = this.download(request, client, pb.getBasePath(), keepArchive);
for (FileBean fb : pb.getFiles()) {
Map<String, String> filesWithMapping = this.getFiles(fb, pb.getBasePath(), serverLanguageMapping, forLanguages, placeholderUtil, new ArrayList<>(serverSources.keySet()), pb.getPreserveHierarchy());
Map<String, String> filesWithMapping = this.getFiles(fb, pb.getBasePath(), serverLanguageMapping, pseudoLanguageMapping, forLanguages, placeholderUtil, new ArrayList<>(serverSources.keySet()), pb.getPreserveHierarchy());
fileBeansWithDownloadedFiles.putIfAbsent(downloadedFiles.getLeft(), new ArrayList<>());
fileBeansWithDownloadedFiles.get(downloadedFiles.getLeft()).add(filesWithMapping);
}
Expand Down Expand Up @@ -212,7 +228,7 @@ public void act(Outputter out, PropertiesWithFiles pb, ProjectClient client) {
&& fb.getExportStringsThatPassedWorkflow() == downloadConfiguration.getRight().getRight()) {

Map<String, String> filesWithMapping =
this.getFiles(fb, pb.getBasePath(), serverLanguageMapping, forLanguages, placeholderUtil, new ArrayList<>(serverSources.keySet()), pb.getPreserveHierarchy());
this.getFiles(fb, pb.getBasePath(), serverLanguageMapping, null, forLanguages, placeholderUtil, new ArrayList<>(serverSources.keySet()), pb.getPreserveHierarchy());
fileBeansWithDownloadedFiles.putIfAbsent(downloadedFiles.getLeft(), new ArrayList<>());
fileBeansWithDownloadedFiles.get(downloadedFiles.getLeft()).add(filesWithMapping);
}
Expand Down Expand Up @@ -383,7 +399,7 @@ private Pair<File, List<String>> download(BuildProjectTranslationRequest request
}

private Map<String, String> getFiles(
FileBean fb, String basePath, LanguageMapping serverLanguageMapping, List<Language> forLanguages, PlaceholderUtil placeholderUtil, List<String> allServerSources, boolean preserveHierarchy
FileBean fb, String basePath, LanguageMapping serverLanguageMapping, LanguageMapping pseudoLanguageMapping, List<Language> forLanguages, PlaceholderUtil placeholderUtil, List<String> allServerSources, boolean preserveHierarchy
) {
List<String> sources =
SourcesUtils.getFiles(basePath, fb.getSource(), fb.getIgnore(), placeholderUtil)
Expand All @@ -400,6 +416,9 @@ private Map<String, String> getFiles(
}
LanguageMapping localLanguageMapping = LanguageMapping.fromConfigFileLanguageMapping(fb.getLanguagesMapping());
LanguageMapping languageMapping = LanguageMapping.populate(localLanguageMapping, serverLanguageMapping);
if (pseudoLanguageMapping != null) {
languageMapping = LanguageMapping.populate(pseudoLanguageMapping, languageMapping);
}
Map<String, String> translationReplace =
fb.getTranslationReplace() != null ? fb.getTranslationReplace() : new HashMap<>();

Expand Down Expand Up @@ -503,6 +522,7 @@ private Map<String, String> doTranslationMapping(
boolean preserveHierarchy
) {
Map<String, String> mapping = new HashMap<>();
Map<String, String> mappedCodes = new HashMap<>();

for (Language language : languages) {

Expand All @@ -527,9 +547,14 @@ private Map<String, String> doTranslationMapping(

translationFile2 = PropertiesBeanUtils.useTranslationReplace(translationFile2, translationReplace);

mapping.put(translationProject2, translationFile2);
if (!languageMapping.hasMappingForLangCode(language.getId())) {
mapping.put(translationProject2, translationFile2);
} else {
mappedCodes.put(translationProject2, translationFile2);
}
}
}
mapping.putAll(mappedCodes);
return mapping;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,21 +218,21 @@ public static TranslationMemoryExportRequest exportTranslationMemory(
return request;
}

public static CrowdinTranslationCraeteProjectPseudoBuildForm crowdinTranslationCreateProjectPseudoBuildForm(
public static CrowdinTranslationCreateProjectPseudoBuildForm crowdinTranslationCreateProjectPseudoBuildForm(
long branchId, Boolean pseudo, Integer lengthCorrection, String prefix, String suffix, CharTransformation charTransformation
) {
CrowdinTranslationCraeteProjectPseudoBuildForm request
CrowdinTranslationCreateProjectPseudoBuildForm request
= crowdinTranslationCreateProjectPseudoBuildForm(pseudo, lengthCorrection, prefix, suffix, charTransformation);

request.setBranchId(branchId);

return request;
}

public static CrowdinTranslationCraeteProjectPseudoBuildForm crowdinTranslationCreateProjectPseudoBuildForm(
public static CrowdinTranslationCreateProjectPseudoBuildForm crowdinTranslationCreateProjectPseudoBuildForm(
Boolean pseudo, Integer lengthCorrection, String prefix, String suffix, CharTransformation charTransformation
) {
CrowdinTranslationCraeteProjectPseudoBuildForm request = new CrowdinTranslationCraeteProjectPseudoBuildForm();
CrowdinTranslationCreateProjectPseudoBuildForm request = new CrowdinTranslationCreateProjectPseudoBuildForm();
request.setPseudo(pseudo);
request.setLengthTransformation(lengthCorrection);
request.setPrefix(prefix);
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/com/crowdin/cli/utils/PlaceholderUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.crowdin.cli.client.LanguageMapping;
import com.crowdin.client.languages.model.Language;
import com.crowdin.client.translations.model.CharTransformation;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;

Expand Down Expand Up @@ -67,6 +68,12 @@ public class PlaceholderUtil {
private static final String ESCAPE_ASTERISK_REPLACEMENT_FROM = ".+" + Utils.PATH_SEPARATOR_REGEX;
private static final String ESCAPE_ASTERISK_REPLACEMENT_TO = "(.+" + Utils.PATH_SEPARATOR_REGEX + ")?";

private static final String ASIAN_LANGUAGE_CODE = "zh-TW";
private static final String CYRILLIC_LANGUAGE_CODE = "uk";
private static final String EUROPEAN_LANGUAGE_CODE = "fr";
private static final String ARABIC_LANGUAGE_CODE = "ar";
private static final String DEFAULT_LANGUAGE_CODE = "en";

private List<Language> supportedLanguages;
private List<Language> projectLanguages;
private String basePath;
Expand Down Expand Up @@ -308,4 +315,16 @@ public static boolean containsLangPlaceholders(String translationsPattern) {
PLACEHOLDER_OSX_CODE,
PLACEHOLDER_OSX_LOCALE);
}

public static String languageCodeByCharTransformation(CharTransformation transformation) {
if (transformation == null) {
return DEFAULT_LANGUAGE_CODE;
}
return switch (transformation) {
case ASIAN -> ASIAN_LANGUAGE_CODE;
case CYRILLIC -> CYRILLIC_LANGUAGE_CODE;
case EUROPEAN -> EUROPEAN_LANGUAGE_CODE;
case ARABIC -> ARABIC_LANGUAGE_CODE;
};
}
}
12 changes: 12 additions & 0 deletions src/test/java/com/crowdin/cli/client/LanguageMappingTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.crowdin.cli.client;

import com.crowdin.client.languages.model.Language;
import com.crowdin.client.translations.model.CharTransformation;
import org.junit.jupiter.api.Test;

import java.util.HashMap;
Expand Down Expand Up @@ -29,6 +31,10 @@ public class LanguageMappingTest {
}}
);
}};
private static final Language ARAGONESE_LANGUAGE = new Language() {{
setId("an");
setTwoLettersCode("an");
}};

@Test
public void testLanguageMappingForServer() {
Expand Down Expand Up @@ -57,4 +63,10 @@ public void testPopulate() {
assertTrue(result.containsValue("uk", "three_letters_code"));
assertEquals(CONFIG_FILE_LANGUAGE_MAPPING.get("name").get("uk"), result.getValue("uk", "name"));
}

@Test
public void testPseudoLanguageMapping() {
LanguageMapping result = LanguageMapping.pseudoLanguageMapping(CharTransformation.ARABIC, ARAGONESE_LANGUAGE);
assertEquals("an", result.getValue("ar", "two_letters_code"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
import com.crowdin.cli.commands.functionality.FilesInterface;
import com.crowdin.cli.properties.PropertiesWithFiles;
import com.crowdin.cli.properties.NewPropertiesWithFilesUtilBuilder;
import com.crowdin.cli.properties.PseudoLocalization;
import com.crowdin.cli.properties.helper.FileHelperTest;
import com.crowdin.cli.properties.helper.TempProject;
import com.crowdin.cli.utils.Utils;
import com.crowdin.client.translations.model.CrowdinTranslationCreateProjectBuildForm;
import com.crowdin.client.translations.model.CrowdinTranslationCreateProjectPseudoBuildForm;
import com.crowdin.client.translations.model.ProjectBuild;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -21,6 +23,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 @@ -853,4 +856,60 @@ public void testProjectFittingFile_OneLanguage() throws IOException, ResponseExc
verify(files).deleteDirectory(tempDir.get());
verifyNoMoreInteractions(files);
}

@Test
public void testProjectOneFittingFile_PseudoLocalizationWithMapping() throws IOException, ResponseException {
NewPropertiesWithFilesUtilBuilder pbBuilder = NewPropertiesWithFilesUtilBuilder
.minimalBuiltPropertiesBean("*", Utils.PATH_SEPARATOR + "%original_file_name%-CR-%locale%")
.setBasePath(project.getBasePath());
PropertiesWithFiles pb = pbBuilder.build();
PseudoLocalization pseudoLocalization = new PseudoLocalization();
pb.setPseudoLocalization(pseudoLocalization);

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());
CrowdinTranslationCreateProjectPseudoBuildForm buildProjectTranslationRequest = new CrowdinTranslationCreateProjectPseudoBuildForm();
buildProjectTranslationRequest.setPseudo(true);
long buildId = 42L;
when(client.startBuildingTranslation(eq(buildProjectTranslationRequest)))
.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-en-GB"));
}};
}));

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

verify(client).downloadFullProject(null);
verify(client).startBuildingTranslation(eq(buildProjectTranslationRequest));
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-en-GB"),
new File(pb.getBasePath() + "first.po-CR-de-DE"));
verify(files).deleteFile(eq(zipArchive.get()));
verify(files).deleteDirectory(tempDir.get());
verifyNoMoreInteractions(files);
}

}
10 changes: 10 additions & 0 deletions src/test/java/com/crowdin/cli/utils/PlaceholderUtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.crowdin.cli.client.LanguageMapping;
import com.crowdin.client.languages.model.Language;
import com.crowdin.client.translations.model.CharTransformation;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
Expand Down Expand Up @@ -175,4 +176,13 @@ public void testReplaceLanguageDependentPlaceholdersLang() {
String result = placeholderUtil.replaceLanguageDependentPlaceholders(toFormat, languageMapping, language);
assertEquals(expected, result);
}

@Test
public void testLanguageCodeByCharTransformation() {
assertEquals("zh-TW", PlaceholderUtil.languageCodeByCharTransformation(CharTransformation.ASIAN));
assertEquals("uk", PlaceholderUtil.languageCodeByCharTransformation(CharTransformation.CYRILLIC));
assertEquals("fr", PlaceholderUtil.languageCodeByCharTransformation(CharTransformation.EUROPEAN));
assertEquals("ar", PlaceholderUtil.languageCodeByCharTransformation(CharTransformation.ARABIC));
assertEquals("en", PlaceholderUtil.languageCodeByCharTransformation(null));
}
}

0 comments on commit 4894f01

Please sign in to comment.