Skip to content

Commit 9152e3a

Browse files
author
Stepan Kamenik
committed
feat(FgForrest#3): add Deepl automation integration
1 parent 6654bb8 commit 9152e3a

File tree

10 files changed

+188
-39
lines changed

10 files changed

+188
-39
lines changed

pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@
7070
</properties>
7171

7272
<dependencies>
73+
<dependency>
74+
<groupId>com.deepl.api</groupId>
75+
<artifactId>deepl-java</artifactId>
76+
<version>1.4.0</version>
77+
</dependency>
7378
<dependency>
7479
<groupId>org.springframework.boot</groupId>
7580
<artifactId>spring-boot-starter</artifactId>

src/main/java/one/edee/babylon/MainService.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ public MainService(Exporter exporter,
2929
this.importProcessor = importProcessor;
3030
}
3131

32-
public void startTranslation(Action action, TranslationConfiguration configuration, String spreadsheetId, boolean combineSheets) throws IOException, GeneralSecurityException, InterruptedException {
32+
public void startTranslation(Action action, TranslationConfiguration configuration, String spreadsheetId, boolean combineSheets, String deeplApiKey) throws IOException, GeneralSecurityException, InterruptedException {
3333
long stTime = System.currentTimeMillis();
3434
switch (action) {
3535
case EXPORT:
3636
log.info("Babylon starting...");
37-
exporter.walkPathsAndWriteSheets(configuration.getPath(), configuration.getMutations(), spreadsheetId, configuration.getSnapshotPath(), configuration.getLockedCellEditors(), combineSheets);
37+
exporter.walkPathsAndWriteSheets(configuration.getPath(), configuration.getMutations(), spreadsheetId, configuration.getSnapshotPath(), configuration.getLockedCellEditors(), combineSheets, deeplApiKey);
3838
break;
3939
case IMPORT:
4040
importProcessor.doImport(spreadsheetId);

src/main/java/one/edee/babylon/SpringBootConsoleApplication.java

+16-5
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public void run(String... args) {
4444
try {
4545
log.info("Loading config file: '" + arguments.getConfigFileName() + "'");
4646
TranslationConfiguration configuration = configurationReader.readAndCheckConfiguration(arguments.getConfigFileName());
47-
mainService.startTranslation(arguments.getAction(), configuration, arguments.getGoogleSheetId(), arguments.isCombineSheets());
47+
mainService.startTranslation(arguments.getAction(), configuration, arguments.getGoogleSheetId(), arguments.isCombineSheets(), arguments.getDeeplApiKey());
4848
} catch (Exception e) {
4949
log.error("BABYLON ERROR: ", e);
5050
System.exit(-1);
@@ -57,7 +57,7 @@ public void run(String... args) {
5757
* @return
5858
*/
5959
public static Arguments parseArguments(String... args) {
60-
if (!(args.length == 3 || args.length == 4)) {
60+
if (!(args.length == 3 || args.length == 4 || args.length == 5)) {
6161
log.error("Invalid input arguments, required: ");
6262
printRequiredArguments();
6363
System.exit(-1);
@@ -74,8 +74,12 @@ public static Arguments parseArguments(String... args) {
7474
}
7575
arguments.setConfigFileName(args[1]);
7676
arguments.setGoogleSheetId(args[2]);
77-
if (args.length == 4){
77+
if (args.length > 3){
7878
arguments.setCombineSheets(Boolean.parseBoolean(args[3]));
79+
if (args.length > 4){
80+
arguments.setDeeplApiKey(args[4]);
81+
82+
}
7983
}
8084
return arguments;
8185
}
@@ -85,6 +89,7 @@ private static void printRequiredArguments() {
8589
log.info("2 - path to translator-config.json file");
8690
log.info("3 - ID of the google sheet (e.g. 1xhnBAOpy8-9KWhl8NP0ZIy6mhlgXKnKcLJwKcIeyjPc)");
8791
log.info("4 - arg to specify combineSheets mode");
92+
log.info("5 - arg to specify deepl api key");
8893
}
8994

9095
/**
@@ -108,11 +113,17 @@ public static class Arguments {
108113
* Id of the target google spreadsheet.
109114
*/
110115
private String googleSheetId;
116+
111117
/**
112-
* Allows to write only to one sheet with name all.
113-
* This mode is useful to correct duplicates etc.
118+
* Allows writing only to one sheet with name all.
119+
* This mode is useful to correct duplicates, etc.
114120
*/
115121
private boolean combineSheets = false;
122+
123+
/**
124+
* Deepl api key.
125+
*/
126+
private String deeplApiKey;
116127
}
117128

118129
}

src/main/java/one/edee/babylon/export/Exporter.java

+70-13
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
package one.edee.babylon.export;
22

3+
import com.deepl.api.TextResult;
4+
import com.deepl.api.Translator;
5+
import lombok.extern.apachecommons.CommonsLog;
36
import one.edee.babylon.db.SnapshotUtils;
47
import one.edee.babylon.export.dto.ExportResult;
58
import one.edee.babylon.export.dto.TranslationSheet;
9+
import one.edee.babylon.sheets.SheetsException;
610
import one.edee.babylon.sheets.gsheets.model.ASheet;
711
import one.edee.babylon.snapshot.TranslationSnapshotWriteContract;
812
import one.edee.babylon.util.AntPathResourceLoader;
9-
import one.edee.babylon.sheets.SheetsException;
1013
import one.edee.babylon.util.PathUtils;
11-
import lombok.extern.apachecommons.CommonsLog;
14+
import org.springframework.util.StringUtils;
1215

1316
import java.io.File;
1417
import java.io.IOException;
@@ -52,25 +55,28 @@ public void walkPathsAndWriteSheets(List<String> patternPaths,
5255
List<String> translationLangs,
5356
String spreadsheetId,
5457
Path snapshotPath,
55-
boolean combineSheets ) {
56-
walkPathsAndWriteSheets(patternPaths, translationLangs, spreadsheetId, snapshotPath, Collections.emptyList(), combineSheets);
58+
boolean combineSheets,
59+
String deeplApiKey) {
60+
walkPathsAndWriteSheets(patternPaths, translationLangs, spreadsheetId, snapshotPath, Collections.emptyList(), combineSheets, deeplApiKey);
5761
}
5862

5963
/**
6064
* Walks message file paths, gathering messages and translations, producing translation sheets in given GSheet spreadsheet.
6165
*
62-
* @param patternPaths paths of message files to export
63-
* @param translationLangs languages to translate messages to
64-
* @param spreadsheetId id of GSheets spreadsheet, must be empty
65-
* @param snapshotPath path to the translation snapshot file
66+
* @param patternPaths paths of message files to export
67+
* @param translationLangs languages to translate messages to
68+
* @param spreadsheetId id of GSheets spreadsheet, must be empty
69+
* @param snapshotPath path to the translation snapshot file
6670
* @param lockedCellEditors list of Google account emails, these account will have the permission to edit locked cells
71+
* @param deeplApiKey
6772
*/
6873
public void walkPathsAndWriteSheets(List<String> patternPaths,
6974
List<String> translationLangs,
7075
String spreadsheetId,
7176
Path snapshotPath,
7277
List<String> lockedCellEditors,
73-
boolean combineSheets) {
78+
boolean combineSheets,
79+
String deeplApiKey) {
7480
warnDuplicatePaths(patternPaths);
7581

7682
List<ASheet> prevSheets = listAllSheets(spreadsheetId);
@@ -102,7 +108,56 @@ public void walkPathsAndWriteSheets(List<String> patternPaths,
102108
original.add(new TranslationSheet(COMBINING_SHEET_NAME,combine));
103109
}
104110

105-
uploadTranslations(result, spreadsheetId, lockedCellEditors);
111+
Map<String, List<String>> changed = new HashMap<>();
112+
113+
if (deeplApiKey != null) {
114+
try {
115+
Translator translator = new Translator(deeplApiKey);
116+
for (TranslationSheet sheet : result.getSheets()) {
117+
log.info("Translating sheet " + sheet.getSheetName());
118+
119+
List<List<String>> rows = sheet.getRows();
120+
List<String> header = rows.get(0);
121+
122+
123+
for (int i = 1; i < rows.size(); i++) {
124+
Map<Integer, String> toChange = new HashMap<>();
125+
126+
List<String> cells = rows.get(i);
127+
String original = cells.get(1);
128+
for (int l = 2; l < cells.size(); l++) {
129+
if (StringUtils.isEmpty(cells.get(l))) {
130+
131+
String lang = header.get(l);
132+
133+
if (lang.equals("en")) {
134+
lang = "en-GB";
135+
}
136+
137+
if (StringUtils.hasText(original)) {
138+
TextResult translatedText = translator.translateText(original, null, lang);
139+
toChange.put(l, translatedText.getText());
140+
141+
changed
142+
.computeIfAbsent(sheet.getSheetName(), key -> new LinkedList<>())
143+
.add(i + "_" + l);
144+
}
145+
}
146+
}
147+
148+
for (Entry<Integer, String> entry : toChange.entrySet()) {
149+
cells.remove((int) entry.getKey());
150+
cells.add(entry.getKey(), entry.getValue());
151+
}
152+
153+
}
154+
}
155+
} catch (Exception e) {
156+
log.error(e.getMessage(), e);
157+
}
158+
}
159+
160+
uploadTranslations(result, spreadsheetId, lockedCellEditors, changed);
106161

107162
updateSnapshotAndWriteToDisk(this.snapshot, result, snapshotPath);
108163

@@ -162,13 +217,13 @@ private List<ASheet> listAllSheets(String spreadsheetId) {
162217
}
163218
}
164219

165-
private void uploadTranslations(ExportResult exportResult, String spreadsheetId, List<String> lockedCellEditors) {
220+
private void uploadTranslations(ExportResult exportResult, String spreadsheetId, List<String> lockedCellEditors, Map<String, List<String>> changed) {
166221
exportResult.getSheets().stream()
167222
.filter(sheet -> !sheet.getDataRows().isEmpty())
168223
.forEach(sheet -> {
169224
try {
170225
log.info("Writing " + sheet.getDataRows().size() + " rows into sheet '" + sheet.getSheetName() + "'.");
171-
gsc.createSheet(spreadsheetId, sheet.getSheetName(), sheet.getRows(), lockedCellEditors);
226+
gsc.createSheet(spreadsheetId, sheet.getSheetName(), sheet.getRows(), lockedCellEditors, changed);
172227
} catch (SheetsException e) {
173228
String errMsg = "Error when uploading data to spreadsheet '" + spreadsheetId + "'";
174229
throw new RuntimeException(errMsg, e);
@@ -227,10 +282,12 @@ public interface SheetContract {
227282
* @param sheetTitle name to use for the new sheet
228283
* @param sheetRows rows with data cells to fill the sheet with
229284
* @param lockedCellEditors list of email accounts that will be able to edit locked cells
285+
* @param changed
230286
* @throws SheetsException when unable to upload sheets
231287
*/
232-
void createSheet(String spreadsheetId, String sheetTitle, List<List<String>> sheetRows, List<String> lockedCellEditors) throws SheetsException;
288+
void createSheet(String spreadsheetId, String sheetTitle, List<List<String>> sheetRows, List<String> lockedCellEditors, Map<String, List<String>> changed) throws SheetsException;
233289

234290
}
235291

292+
236293
}

src/main/java/one/edee/babylon/maven/BabylonExpImpBaseMojo.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public abstract class BabylonExpImpBaseMojo extends AbstractMojo {
1313

1414
public static final String CONFIG_FILE_PARAM = "config.file";
1515
public static final String GOOGLE_SHEET_ID_PARAM = "google.sheet.id";
16+
public static final String COMBINE_SHEET_PARAM = "combine.sheets";
17+
public static final String DEEPL_API_KEY_PARAM = "deepl.api.key";
1618

1719
/**
1820
* File name and relative path to the Json configuration file.
@@ -26,16 +28,30 @@ public abstract class BabylonExpImpBaseMojo extends AbstractMojo {
2628
@Parameter(property = GOOGLE_SHEET_ID_PARAM, required = true)
2729
private String googleSheetId;
2830

31+
/**
32+
* Combine sheets param.
33+
*/
34+
@Parameter(property = COMBINE_SHEET_PARAM)
35+
private Boolean combineSheets;
36+
37+
/**
38+
* Deepl api key.
39+
*/
40+
@Parameter(property = DEEPL_API_KEY_PARAM)
41+
private String deeplApiKey;
42+
2943
@Override
3044
public void execute() {
3145
SpringBootConsoleApplication.main(getArguments());
3246
}
3347

3448
private String[] getArguments() {
35-
String[] arg = new String[3];
49+
String[] arg = new String[5];
3650
arg[0] = getAction().name();
3751
arg[1] = this.configFileName;
3852
arg[2] = this.googleSheetId;
53+
arg[3] = String.valueOf(this.combineSheets);
54+
arg[4] = this.deeplApiKey;
3955
return arg;
4056
}
4157

src/main/java/one/edee/babylon/sheets/gsheets/GSheetApiRequestFactory.java

+51
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
import com.google.api.services.sheets.v4.model.*;
44

5+
import java.util.LinkedList;
56
import java.util.List;
7+
import java.util.Map;
8+
9+
import static java.util.Collections.singletonList;
610

711
/**
812
* Helps create requests for the Google Sheets API client library.
@@ -152,4 +156,51 @@ private DeleteSheetRequest deleteSheetRequest(Integer sheetId) {
152156
.setSheetId(sheetId);
153157
}
154158

159+
public List<Request> changeCellColor(Integer sheetId, String sheetTitle, Map<String, List<String>> changed) {
160+
List<String> changes = changed.get(sheetTitle);
161+
List<Request> reqs = new LinkedList<>();
162+
if (changes != null && !changes.isEmpty()) {
163+
for (String change : changes) {
164+
String[] s = change.split("_");
165+
int row = Integer.parseInt(s[0]);
166+
int column = Integer.parseInt(s[1]);
167+
168+
reqs.add(
169+
new Request()
170+
.setUpdateCells(
171+
new UpdateCellsRequest()
172+
.setRange(
173+
new GridRange()
174+
.setSheetId(sheetId)
175+
.setStartColumnIndex(column)
176+
.setEndColumnIndex(column + 1)
177+
.setStartRowIndex(row)
178+
.setEndRowIndex(row + 1)
179+
)
180+
.setRows(
181+
singletonList(
182+
new RowData()
183+
.setValues(
184+
singletonList(
185+
new CellData()
186+
.setUserEnteredFormat(
187+
new CellFormat()
188+
.setBackgroundColor(
189+
new Color()
190+
.setRed(1f)
191+
.setGreen(0.8f)
192+
.setBlue(0.61f)
193+
)
194+
)
195+
)
196+
)
197+
)
198+
)
199+
.setFields("userEnteredFormat.backgroundColor")
200+
)
201+
);
202+
}
203+
}
204+
return reqs;
205+
}
155206
}

src/main/java/one/edee/babylon/sheets/gsheets/LightGSheetService.java

+14-12
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@
88

99
import java.io.IOException;
1010
import java.security.GeneralSecurityException;
11-
import java.util.Arrays;
12-
import java.util.Collection;
13-
import java.util.Collections;
14-
import java.util.List;
11+
import java.util.*;
1512
import java.util.stream.Collectors;
1613

1714
/**
@@ -136,19 +133,24 @@ private List<? extends List<? extends Object>> convertNullsToEmptyString(List<?
136133
* Updates style of an existing sheet.
137134
* Sets wrapping strategy, resizes columns, protects firs two columns, hides first column.
138135
*
139-
* @param spreadsheetId id of spreadsheet to find sheet to update style
140-
* @param sheetId id of sheet to update
136+
* @param spreadsheetId id of spreadsheet to find sheet to update style
137+
* @param sheetTitle
138+
* @param sheetId id of sheet to update
141139
* @param lockedCellEditors list of account emails to receive edit permissions on locked cells
140+
* @param changed
142141
* @throws GeneralSecurityException
143142
* @throws IOException
144143
*/
145-
public void updateSheetStyle(String spreadsheetId, Integer sheetId, List<String> lockedCellEditors) throws GeneralSecurityException, IOException {
146-
Request setWrappingStrategy = gSheetsRequestFactory.setWrapWrappingStrategyForAllCells(sheetId);
147-
Request resizeColumns = gSheetsRequestFactory.resizeAllColumns(sheetId, COLUMN_WIDTH);
148-
Request protectColumns = gSheetsRequestFactory.protectCellsInFirstTwoColumns(sheetId, lockedCellEditors);
149-
Request hideColumn = gSheetsRequestFactory.hideFirstColumn(sheetId);
144+
public void updateSheetStyle(String spreadsheetId, String sheetTitle, Integer sheetId, List<String> lockedCellEditors, Map<String, List<String>> changed) throws GeneralSecurityException, IOException {
145+
List<Request> requests = new LinkedList<>();
146+
requests.add(gSheetsRequestFactory.setWrapWrappingStrategyForAllCells(sheetId));
147+
requests.add(gSheetsRequestFactory.resizeAllColumns(sheetId, COLUMN_WIDTH));
148+
requests.add(gSheetsRequestFactory.protectCellsInFirstTwoColumns(sheetId, lockedCellEditors));
149+
requests.add(gSheetsRequestFactory.hideFirstColumn(sheetId));
150+
requests.addAll(gSheetsRequestFactory.changeCellColor(sheetId, sheetTitle, changed));
151+
152+
executeRequests(spreadsheetId, requests.toArray(new Request[0]));
150153

151-
executeRequests(spreadsheetId, setWrappingStrategy, resizeColumns, protectColumns, hideColumn);
152154
}
153155

154156
public void deleteSheets(String spreadsheetId, Collection<Integer> sheetIds) throws GeneralSecurityException, IOException {

0 commit comments

Comments
 (0)