Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Phrase Connector, AI Translator, and Integrity Check and more #1014

Merged
merged 105 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
67102ba
Improve HTML integrity check to catch tag ordering issues
aurambaj Jun 4, 2024
fea0e2c
Add an integrity check for Markdown links
aurambaj Jun 5, 2024
84f3cc2
Add option to remove comments from iOS .strings files in SimpleFileEd…
aurambaj Jun 5, 2024
26acb6c
Add bn-BD locale
aurambaj May 6, 2024
19a36f7
Add RepositoryTmTranslateCommand to translate repository by source match
ja-openai Aug 1, 2024
d644419
Add Dockerfile for Java 21 and document K8s integration
aurambaj Jun 5, 2024
13f0fe5
Look for SESSION and JSESSIONID as session cookie
aurambaj Jun 5, 2024
5c9968a
Android Locale to support old tag for Indonesian (in)
aurambaj Jun 5, 2024
897d44e
Fix bug in Project Request UI
aurambaj Jun 5, 2024
7f2da0b
Fix performance issue when getting the repository
aurambaj Jun 5, 2024
4641a39
Fix typo
aurambaj Jun 5, 2024
880386f
Add an option in the JSON filter to remove suffix in the text unit name
aurambaj Jun 5, 2024
51ac6ad
Package JSON updates
aurambaj Jun 5, 2024
d4afa02
Update idea.md doc for symlinks
aurambaj Jun 5, 2024
b9a8efe
Improve the ImportLocalizedAssetCommand
aurambaj Jun 5, 2024
46f6897
Make AndroidStringDocumentReader and AndroidStringDocumentWriter thro…
aurambaj Jun 7, 2024
b732b1a
Add readString() with UncheckedIOException
aurambaj Jun 7, 2024
dac3f6c
createTempDirectory with UncheckedIOException
aurambaj Jun 10, 2024
daba9db
AndroidStringDocumentMapper add optional id in text unit name
aurambaj Jun 10, 2024
5161ed0
Update Authentication documentation
aurambaj Jun 12, 2024
577fde0
Add option locale mapping type in import command
aurambaj Jun 12, 2024
1d76ac5
Fix typo in comment - move to top of branch
aurambaj Jun 12, 2024
909db03
Fix output ThirdPartySyncCommand
aurambaj Jun 14, 2024
5c5b129
AndroidStringDocumentMapper supports more Android escaping
aurambaj Jun 13, 2024
c5ff310
Improve removing comments from .strings
aurambaj Jun 13, 2024
75249ef
Add dependency for error handling in OAuth2
aurambaj Jun 18, 2024
541cc01
Add simple Integrity check for Python FStrings
aurambaj Jun 24, 2024
ba98856
Register Python fprint integrity checks
aurambaj Jun 24, 2024
1f73d80
Add a specific type for FormatJS JSON
aurambaj Jun 24, 2024
b10c232
Initial version of the Phrase connector
aurambaj Jun 10, 2024
8310366
Refactoring the pullcommand locale mapping - not backward compatible.
aurambaj Jun 12, 2024
9d91606
Add support for Plural String import
aurambaj Jun 14, 2024
145c056
Phrase Connector support N Mojito repositories to 1 project mapping
aurambaj Jun 19, 2024
ac73c8b
Fix issues related to tag names having some character converted
aurambaj Jun 22, 2024
1a43216
Make sure to not import the source comment when import translations f…
aurambaj Jun 22, 2024
867d882
Make Phrase TMS Pull Parallel
ja-openai Jun 27, 2024
c172bdb
Non-backward compatible - Fixes batch import logic to save target com…
ja-openai Jul 4, 2024
5b608e4
Ignore PhraseClientTest
ja-openai Aug 5, 2024
5ccca9e
Improve remove untranslated string in AndroidFilter
ja-openai Aug 9, 2024
f426dc0
HtmlTagIntegrityChecker check for duplicate non balanced tag
ja-openai Aug 22, 2024
3ad81ff
Improve error message
ja-openai Aug 23, 2024
f0021d4
Fix concurrency issue by isolating putBase in a separate transaction
ja-openai Aug 23, 2024
c13d1f7
Support Plural Strings with the Same Name but Different Content Acros…
ja-openai Aug 23, 2024
9391eca
Doc to re-indent list of commits
ja-openai Aug 23, 2024
fc4387b
Add test for translatable=false in Android plurals string
ja-openai Aug 23, 2024
92b7a0d
Add integrity checker for emails
ja-openai Aug 23, 2024
e61b5c9
Support unmatched double curly braces in CompositeFormatIntegrityChecker
ja-openai Aug 27, 2024
180fb4d
Add URLIntegrityChecker for standalone URLs
ja-openai Aug 27, 2024
f1d48ac
Introduce generic OptionsParser
ja-openai Aug 27, 2024
1128733
Add option to override translation status during import
ja-openai Aug 27, 2024
de2ad61
Add option to remove existing third party mapping when synchronizing
ja-openai Aug 28, 2024
3c6af3e
Make integrity check messages more specific
ja-openai Aug 28, 2024
b1e1495
Add "standalone" attribute to output document based on input
ja-openai Aug 29, 2024
ae5a05d
Add support for PKCS#1 and PKCS#8 private keys in GithubClient
ja-openai Aug 31, 2024
4142480
Add post-processing to AndroidFilter to remove non-translatable elements
ja-openai Aug 31, 2024
05ea790
Add MessageFormatIntegrityCheckerTest test for quote with curly braces
ja-openai Aug 31, 2024
a249f60
Support "dash" in HtmlTagIntegrityChecker
ja-openai Sep 3, 2024
5f165d7
Add command to create pull request in Github
ja-openai Sep 5, 2024
4c275f8
Add test for the AndroidStringDocumentWriter XML escaping
ja-openai Sep 11, 2024
89933e2
Update install documentation for java/mysql
ja-openai Sep 12, 2024
a690350
Support user creation for Oicd authentication
ja-openai Sep 13, 2024
20d7c98
Improve HtmlTagIntegrityChecker and reporting
ja-openai Sep 13, 2024
e47b90e
Improve URLIntegrityChecker
ja-openai Sep 16, 2024
a755c88
Improve CompositeFormatIntegrityChecker for imbalanced curly braces
ja-openai Sep 16, 2024
0c2a437
Bump github-api to 1.325
ja-openai Sep 16, 2024
efbafb6
Add option to not escape in AndroidStringDocumentWriter
ja-openai Sep 17, 2024
33a0c2e
Test escaping with Phrase
ja-openai Sep 17, 2024
e43cb0e
Add Github "enable auto merge" PR option
ja-openai Sep 17, 2024
85981dd
Re-implement Phrase String file upload since SDK does not accept form…
ja-openai Sep 20, 2024
b030aa5
Add an option to GithubCreatePRCommand to add labels
ja-openai Sep 23, 2024
1fa4ac5
MacStringsFilter remove untranslated test
ja-openai Sep 25, 2024
da916f4
Refactor filter post processing
ja-openai Sep 25, 2024
c7dab17
Add post-processing to MacStringsFilter
ja-openai Sep 25, 2024
711d83a
Implement native Phrase locale download
ja-openai Sep 25, 2024
5c36e7e
Support format options using native http call in ThirdPartyTMSPhrase
ja-openai Sep 26, 2024
0516441
Refactor AndroidString classes to be able to apply proper and consist…
ja-openai Sep 27, 2024
cc793dd
Add error log when the Android XML post processing fails
ja-openai Sep 30, 2024
17d4730
Add an adhoc check in the HtmlTagIntegrityChecker
ja-openai Oct 1, 2024
cd2d853
Refactor integrity checks in TextUnitBatchImporterService
ja-openai Oct 1, 2024
314f202
Add constant for using native client in ThirdPartyTMSPhrase
ja-openai Oct 1, 2024
6316016
Fix third party mapping when delete current mapping option is used
ja-openai Oct 2, 2024
a53f4ff
Improve integrity checker for batch import
ja-openai Oct 2, 2024
66ed7b5
Add an option to choose the batch import integrity check type in the …
ja-openai Oct 2, 2024
baab8f9
Add an option to re-apply integrity status unless the target are tagg…
ja-openai Oct 2, 2024
2d3a90c
Add measurement to PhraseClient wait for upload to finish
ja-openai Oct 28, 2024
a95e7bf
Add Json format output in OpenAIClient
ja-openai Oct 28, 2024
4bb6edf
Add measurement to ThirdPartyTMSPhrase
ja-openai Oct 28, 2024
6d7b9d7
Add AI Review webservice
ja-openai Oct 28, 2024
ec85151
Add a workbench modal to review translation using AI
ja-openai Oct 28, 2024
c64e652
Add option to GithubCreatePRCommand for closing PRs with a specific p…
ja-openai Oct 5, 2024
5951e45
Document Quartz multi scheduler configuration
ja-openai Oct 23, 2024
210d9cc
Add upload/download file and create batch in OpenAIClient
ja-openai Oct 24, 2024
2cd6ce3
AI Translate Web Service
ja-openai Oct 24, 2024
660ae66
Add command line to translate repository using the AiTranslateService
ja-openai Oct 25, 2024
ec6c168
Fix doc/properties for logging
ja-openai Oct 28, 2024
043b4f3
Add script to build web and its dependencies
ja-openai Nov 12, 2024
23b8feb
Add an option to skip removing unused keys and tags
ja-openai Oct 29, 2024
1c768c6
Remove Plural Entries Missing the 'Other' Form
ja-openai Oct 29, 2024
5cdacf6
Implement first version of no-batch ai translate api
ja-openai Oct 29, 2024
a1b8fcb
Handle concurrent run of PhraseTMS sync
ja-openai Nov 21, 2024
0ce43e3
Add an option to AiTranslate only a specific list of text units
ja-openai Nov 22, 2024
a6f7143
Ad hoc relaxation of the integrity checker for plural strings
ja-openai Nov 27, 2024
7954f3d
Remove System.out.println
ja-openai Dec 6, 2024
5571379
Ai translate only uesd strings
ja-openai Dec 13, 2024
8bdb008
Ai translate only used for batch
ja-openai Dec 13, 2024
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package com.box.l10n.mojito.cli.command;

import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.box.l10n.mojito.cli.console.ConsoleWriter;
import com.box.l10n.mojito.github.GithubClient;
import com.box.l10n.mojito.github.GithubClients;
import com.box.l10n.mojito.github.GithubException;
import java.io.IOException;
import java.util.List;
import org.fusesource.jansi.Ansi;
import org.kohsuke.github.GHIssueState;
import org.kohsuke.github.GHPullRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("prototype")
@Parameters(
commandNames = {"github-pr-create"},
commandDescription = "Create a Github PR")
public class GithubCreatePRCommand extends Command {

@Autowired GithubClients githubClients;

@Qualifier("ansiCodeEnabledFalse")
@Autowired
ConsoleWriter consoleWriter;

@Parameter(
names = {"--owner", "-o"},
required = true,
arity = 1,
description = "The Github repository owner")
String owner;

@Parameter(
names = {"--repository", "-r"},
required = true,
arity = 1,
description = "The Github repository name")
String repository;

@Parameter(
names = {"--title"},
required = true,
arity = 1,
description = "The PR title")
String title;

@Parameter(
names = {"--head"},
required = true,
arity = 1,
description = "The PR head")
String head;

@Parameter(
names = {"--base"},
required = true,
arity = 1,
description = "The PR base")
String base;

@Parameter(
names = {"--body"},
required = false,
arity = 1,
description = "The PR body")
String body;

@Parameter(
names = {"--reviewers"},
required = false,
variableArity = true,
description = "The PR reviewers")
List<String> reviewers;

@Parameter(
names = {"--enable-auto-merge"},
required = false,
arity = 1,
description = "Enable auto-merge with the specified method")
EnableAutoMergeType enableAutoMerge = EnableAutoMergeType.NONE;

@Parameter(
names = {"--labels"},
required = false,
variableArity = true,
description = "The PR labels")
List<String> labels;

@Parameter(
names = {"--also-close-prefixed"},
description =
"Closes all PRs that start with the specified prefix. Use this to manage PRs that are part of a batch or related by a common feature.",
required = false)
String alsoClosePrefixed = null;

enum EnableAutoMergeType {
SQUASH,
MERGE,
NONE
}

@Override
public boolean shouldShowInCommandList() {
return false;
}

@Override
protected void execute() throws CommandException {
try {

GithubClient githubClient = githubClients.getClient(owner);

if (alsoClosePrefixed != null) {
closePRPrefixedWith(githubClient, alsoClosePrefixed);
}

GHPullRequest pr = githubClient.createPR(repository, title, head, base, body, reviewers);

consoleWriter.a("PR created: ").fg(Ansi.Color.CYAN).a(pr.getHtmlUrl().toString()).println();
if (!EnableAutoMergeType.NONE.equals(enableAutoMerge)) {
githubClient.enableAutoMerge(pr, GithubClient.AutoMergeType.SQUASH);
}

githubClient.addLabelsToPR(pr, labels);

} catch (GithubException e) {
throw new CommandException(e);
}
}

void closePRPrefixedWith(GithubClient githubClient, String alsoClosePrefixed) {
githubClient.listPR(repository, GHIssueState.OPEN).stream()
.filter(pr -> pr.getTitle().startsWith(alsoClosePrefixed))
.forEach(
pullRequest -> {
try {
consoleWriter
.a("Closing: ")
.fg(Ansi.Color.CYAN)
.a(pullRequest.getHtmlUrl().toString())
.println();
pullRequest.close();
} catch (IOException e) {
throw new CommandException(e);
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
import com.beust.jcommander.Parameters;
import com.box.l10n.mojito.cli.console.ConsoleWriter;
import com.box.l10n.mojito.github.GithubClients;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
Expand Down Expand Up @@ -50,7 +47,7 @@ protected void execute() throws CommandException {
consoleWriter
.a(githubClients.getClient(owner).getGithubAppInstallationToken(repository).getToken())
.print();
} catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) {
} catch (Exception e) {
throw new CommandException(e);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,16 @@
import com.box.l10n.mojito.rest.client.LocaleClient;
import com.box.l10n.mojito.rest.client.RepositoryClient;
import com.box.l10n.mojito.rest.client.exception.AssetNotFoundException;
import com.box.l10n.mojito.rest.client.exception.PollableTaskException;
import com.box.l10n.mojito.rest.entity.Asset;
import com.box.l10n.mojito.rest.entity.ImportLocalizedAssetBody;
import com.box.l10n.mojito.rest.entity.ImportLocalizedAssetBody.StatusForEqualTarget;
import com.box.l10n.mojito.rest.entity.Locale;
import com.box.l10n.mojito.rest.entity.Repository;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.fusesource.jansi.Ansi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -72,6 +71,22 @@ public class ImportLocalizedAssetCommand extends Command {
description = Param.REPOSITORY_LOCALES_MAPPING_DESCRIPTION)
String localeMappingParam;

@Parameter(
names = {"-lmt", "--locale-mapping-type"},
arity = 1,
required = false,
description =
"Specifies how to handle locale mappings when used with --locale-mapping. "
+ "MAP_ONLY processes only the locales explicitly specified in the mapping. "
+ "WITH_REPOSITORY generates a basic mapping from the repository's locales and "
+ "supplements it with the provided mapping, potentially overriding existing entries.")
LocaleMappingType localeMappingTypeParam = LocaleMappingType.WITH_REPOSITORY;

enum LocaleMappingType {
MAP_ONLY,
WITH_REPOSITORY
}

@Parameter(
names = {Param.FILE_TYPE_LONG, Param.FILE_TYPE_SHORT},
variableArity = true,
Expand Down Expand Up @@ -124,6 +139,12 @@ public class ImportLocalizedAssetCommand extends Command {
converter = ImportLocalizedAssetBodyStatusForEqualTargetConverter.class)
StatusForEqualTarget statusForEqualTarget = StatusForEqualTarget.APPROVED;

@Parameter(
names = {"--continue-on-error"},
arity = 0,
description = "Continue import on errors")
boolean continueOnError = false;

@Autowired AssetClient assetClient;

@Autowired LocaleClient localeClient;
Expand Down Expand Up @@ -152,6 +173,7 @@ public void execute() throws CommandException {
.println(2);

repository = commandHelper.findRepositoryByName(repositoryParam);

commandDirectories = new CommandDirectories(sourceDirectoryParam, targetDirectoryParam);
inverseLocaleMapping = localeMappingHelper.getInverseLocaleMapping(localeMappingParam);

Expand All @@ -163,15 +185,37 @@ public void execute() throws CommandException {
sourcePathFilterRegex,
directoriesIncludePatterns,
directoriesExcludePatterns)) {
for (Locale locale : getLocalesForImport()) {
doImportFileMatch(sourceFileMatch, locale);
}

List<ImportLocalizedAssetBody> list =
getLocalesForImport().stream()
.map(locale -> doImportFileMatch(sourceFileMatch, locale))
.filter(Objects::nonNull)
.toList();

list.forEach(
importLocalizedAssetForContent -> {
try {
commandHelper.waitForPollableTask(
importLocalizedAssetForContent.getPollableTask().getId());
} catch (CommandException e) {
if (continueOnError) {
consoleWriter
.a(" Error while importing: ")
.fg(Ansi.Color.RED)
.a(sourceFileMatch.getPath().toString())
.println();
} else {
throw e;
}
}
});
}

consoleWriter.fg(Ansi.Color.GREEN).newLine().a("Finished").println(2);
}

protected void doImportFileMatch(FileMatch fileMatch, Locale locale) throws CommandException {
protected ImportLocalizedAssetBody doImportFileMatch(FileMatch fileMatch, Locale locale)
throws CommandException {
try {
logger.info("Importing for locale: {}", locale.getBcp47Tag());
Path targetPath = getTargetPath(fileMatch, locale);
Expand All @@ -185,56 +229,64 @@ protected void doImportFileMatch(FileMatch fileMatch, Locale locale) throws Comm
Asset assetByPathAndRepositoryId =
assetClient.getAssetByPathAndRepositoryId(fileMatch.getSourcePath(), repository.getId());

ImportLocalizedAssetBody importLocalizedAssetForContent =
assetClient.importLocalizedAssetForContent(
assetByPathAndRepositoryId.getId(),
locale.getId(),
commandHelper.getFileContent(targetPath),
statusForEqualTarget,
fileMatch.getFileType().getFilterConfigIdOverride(),
commandHelper.getFilterOptionsOrDefaults(
fileMatch.getFileType(), filterOptionsParam));

String fileContent;
try {
commandHelper.waitForPollableTask(importLocalizedAssetForContent.getPollableTask().getId());
} catch (PollableTaskException e) {
throw new CommandException(e.getMessage(), e.getCause());
fileContent = commandHelper.getFileContent(targetPath);
} catch (Exception e) {
if (continueOnError) {
if (!commandHelper.getFileContent(fileMatch.getPath()).isBlank()) {
consoleWriter
.a(" Missing file, skipping: ")
.fg(Ansi.Color.YELLOW)
.a(targetPath.toString())
.println();
}
return null;
} else {
throw e;
}
}
return assetClient.importLocalizedAssetForContent(
assetByPathAndRepositoryId.getId(),
locale.getId(),
fileContent,
statusForEqualTarget,
fileMatch.getFileType().getFilterConfigIdOverride(),
commandHelper.getFilterOptionsOrDefaults(fileMatch.getFileType(), filterOptionsParam));
} catch (AssetNotFoundException ex) {
throw new CommandException(
"No asset for file [" + fileMatch.getPath() + "] into repo [" + repositoryParam + "]",
ex);
if (continueOnError) {
consoleWriter
.a(" Asset not found: ")
.fg(Ansi.Color.YELLOW)
.a(fileMatch.getPath().toString())
.println();
} else {
throw new CommandException(
"No asset for file [" + fileMatch.getPath() + "] into repo [" + repositoryParam + "]",
ex);
}
}
return null;
}

public Collection<Locale> getLocalesForImport() {
Collection<Locale> sortedRepositoryLocales =
commandHelper.getSortedRepositoryLocales(repository).values();
filterLocalesWithMapping(sortedRepositoryLocales);
return sortedRepositoryLocales;
}

private void filterLocalesWithMapping(Collection<Locale> locales) {

if (inverseLocaleMapping != null) {
Iterator<Locale> iterator = locales.iterator();
while (iterator.hasNext()) {
Locale l = iterator.next();
if (!inverseLocaleMapping.containsKey(l.getBcp47Tag())) {
iterator.remove();
}
}
if (LocaleMappingType.MAP_ONLY.equals(localeMappingTypeParam) && inverseLocaleMapping != null) {
sortedRepositoryLocales =
sortedRepositoryLocales.stream()
.filter(l -> inverseLocaleMapping.containsKey(l.getBcp47Tag()))
.toList();
}
return sortedRepositoryLocales;
}

private Path getTargetPath(FileMatch fileMatch, Locale locale) throws CommandException {

String targetLocale;
String targetLocale = locale.getBcp47Tag();

if (inverseLocaleMapping != null) {
if (inverseLocaleMapping != null && inverseLocaleMapping.containsKey(locale.getBcp47Tag())) {
targetLocale = inverseLocaleMapping.get(locale.getBcp47Tag());
} else {
targetLocale = locale.getBcp47Tag();
}

logger.info("processing locale for import: {}", targetLocale);
Expand Down
Loading
Loading