From df4e46a8d30e89568ffa3a97e2a24cee74291610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Pomyka=C5=82a?= Date: Fri, 24 Jul 2026 19:28:04 +0200 Subject: [PATCH 1/5] Improve toString method for LanguageTransform --- .../cli/client/dto/proxy/LanguageTransform.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/io/simplelocalize/cli/client/dto/proxy/LanguageTransform.java b/src/main/java/io/simplelocalize/cli/client/dto/proxy/LanguageTransform.java index e63ecbc..264a0f8 100644 --- a/src/main/java/io/simplelocalize/cli/client/dto/proxy/LanguageTransform.java +++ b/src/main/java/io/simplelocalize/cli/client/dto/proxy/LanguageTransform.java @@ -4,6 +4,8 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.util.StringJoiner; + /** * Single language mapping entry (configurable only via YAML configuration file, under mappings.lang). * Maps a SimpleLocalize language key ({@code languageKey}) to the language name used in the @@ -17,4 +19,13 @@ public class LanguageTransform { private String languageKey; private String placeholder; + + @Override + public String toString() + { + return new StringJoiner(", ", "(", ")") + .add("languageKey='" + languageKey + "'") + .add("placeholder='" + placeholder + "'") + .toString(); + } } From 34dee79ba4eefcc42b547727e6f90006037bea75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Pomyka=C5=82a?= Date: Fri, 24 Jul 2026 21:29:54 +0200 Subject: [PATCH 2/5] Polish command description --- pom.xml | 2 +- .../java/io/simplelocalize/cli/SimplelocalizeCliCommand.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index be043fd..0c09d82 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 io.simplelocalize simplelocalize-cli - 2.11.1 + 2.11.2 jar simplelocalize-cli Official SimpleLocalize Command Line Interface diff --git a/src/main/java/io/simplelocalize/cli/SimplelocalizeCliCommand.java b/src/main/java/io/simplelocalize/cli/SimplelocalizeCliCommand.java index e310f3b..e4dee75 100644 --- a/src/main/java/io/simplelocalize/cli/SimplelocalizeCliCommand.java +++ b/src/main/java/io/simplelocalize/cli/SimplelocalizeCliCommand.java @@ -462,7 +462,7 @@ public void publish( @Command( name = "purge", - description = "Purge translations from Translation Hosting. Use 'simplelocalize purge --help' to learn more about the parameters.") + description = "Purge translations from the project. Use 'simplelocalize purge --help' to learn more about the parameters.") public void purge( @Option(names = {"--apiKey"}, description = "Project API Key") String apiKey, @Option(names = {"--baseUrl"}, description = "(Optional) Set custom server URL") String baseUrl, From 8360f6544b50347441f6421e54d743563951174d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Pomyka=C5=82a?= Date: Fri, 24 Jul 2026 21:30:07 +0200 Subject: [PATCH 3/5] Bump version --- src/main/java/io/simplelocalize/cli/Version.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/simplelocalize/cli/Version.java b/src/main/java/io/simplelocalize/cli/Version.java index dcf3642..1526dc0 100644 --- a/src/main/java/io/simplelocalize/cli/Version.java +++ b/src/main/java/io/simplelocalize/cli/Version.java @@ -3,7 +3,7 @@ public class Version { - public static final String NUMBER = "2.11.1"; + public static final String NUMBER = "2.11.2"; private Version() { From 9fc056b6bf8a4158078063804d82a1c280e443cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Pomyka=C5=82a?= Date: Tue, 22 Sep 2026 16:07:00 +0200 Subject: [PATCH 4/5] Add support for fetching multi-language file formats from the API --- .github/workflows/test-executables.yml | 4 +- README.md | 4 +- pom.xml | 3 +- .../cli/client/SimpleLocalizeClient.java | 34 ++++- .../cli/client/SimpleLocalizeUriFactory.java | 5 + .../cli/command/UploadCommand.java | 62 +++++++-- .../cli/client/SimpleLocalizeClientTest.java | 59 ++++++++ .../cli/command/UploadCommandTest.java | 127 ++++++++++++++++++ 8 files changed, 280 insertions(+), 18 deletions(-) diff --git a/.github/workflows/test-executables.yml b/.github/workflows/test-executables.yml index ed069a0..9b54567 100644 --- a/.github/workflows/test-executables.yml +++ b/.github/workflows/test-executables.yml @@ -18,7 +18,7 @@ jobs: fail-fast: false matrix: os: [ macos-26, macos-26-intel, ubuntu-24.04-arm, ubuntu-latest ] - version: [ '2.10', '2.9', '2.8' ] + version: [ '2.12', '2.11', '2.10' ] steps: - uses: actions/checkout@v3 - name: Test on ${{ matrix.os }} @@ -32,7 +32,7 @@ jobs: fail-fast: false matrix: os: [ windows-latest ] - version: [ '2.10', '2.9', '2.8' ] + version: [ '2.12', '2.11', '2.10' ] steps: - uses: actions/checkout@v3 - name: Test on ${{ matrix.os }} diff --git a/README.md b/README.md index 86c6b60..d89ad4b 100644 --- a/README.md +++ b/README.md @@ -22,10 +22,10 @@ The installation process is automated by command-line scripts. Both scripts for ```bash # macOS / Linux / Windows (WSL) -curl -s https://get.simplelocalize.io/2.11/install | bash +curl -s https://get.simplelocalize.io/2.12/install | bash # Windows (PowerShell) -. { iwr -useb https://get.simplelocalize.io/2.11/install-windows } | iex; +. { iwr -useb https://get.simplelocalize.io/2.12/install-windows } | iex; # npm - https://github.com/simplelocalize/simplelocalize-cli-npm npm install @simplelocalize/cli diff --git a/pom.xml b/pom.xml index 0c09d82..92ccb0a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 io.simplelocalize simplelocalize-cli - 2.11.2 + 2.12.0 jar simplelocalize-cli Official SimpleLocalize Command Line Interface @@ -254,6 +254,7 @@ ${graalvm.quickBuild} --no-fallback + -march=compatibility --enable-http --enable-https -H:IncludeResources=logback.xml diff --git a/src/main/java/io/simplelocalize/cli/client/SimpleLocalizeClient.java b/src/main/java/io/simplelocalize/cli/client/SimpleLocalizeClient.java index 8187cda..5f630d0 100644 --- a/src/main/java/io/simplelocalize/cli/client/SimpleLocalizeClient.java +++ b/src/main/java/io/simplelocalize/cli/client/SimpleLocalizeClient.java @@ -97,6 +97,24 @@ private String prettyPrintJson(DocumentContext json) throws JsonProcessingExcept return mapper.writeValueAsString(data); } + /** + * Returns file formats that carry translations for more than one language, as declared by the API. + * The CLI keeps its own copy of that list as a fallback, see UploadCommand. + */ + public List fetchMultiLanguageFileFormats() throws IOException, InterruptedException + { + URI uri = uriFactory.buildFileFormatsUri(); + HttpRequest httpRequest = httpRequestFactory.createGetRequest(uri).build(); + HttpResponse httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString()); + if (httpResponse.statusCode() != 200) + { + // Caller falls back to its built-in list and warns, so this must not log an error on its own + throw new ApiRequestException(readErrorMessage(httpResponse)); + } + DocumentContext json = JsonPath.parse(httpResponse.body()); + return json.read("$.data.fileFormats[?(@.multiLanguageSupport == true)].value"); + } + public List exportFiles(ExportRequest exportRequest) throws IOException, InterruptedException { URI downloadUri = uriFactory.buildDownloadUri(exportRequest); @@ -244,16 +262,22 @@ private void throwOnError(HttpResponse httpResponse) { if (httpResponse.statusCode() != 200) { - String message = tryReadErrorMessage(httpResponse); - if (StringUtils.isBlank(message)) - { - message = "Unknown error, HTTP Status: " + httpResponse.statusCode(); - } + String message = readErrorMessage(httpResponse); log.error("Request failed: {}", message); throw new ApiRequestException(message); } } + private String readErrorMessage(HttpResponse httpResponse) + { + String message = tryReadErrorMessage(httpResponse); + if (StringUtils.isBlank(message)) + { + message = "Unknown error, HTTP Status: " + httpResponse.statusCode(); + } + return message; + } + private String tryReadErrorMessage(HttpResponse httpResponse) { String stringBody = safeCastHttpBodyToString(httpResponse.body()); diff --git a/src/main/java/io/simplelocalize/cli/client/SimpleLocalizeUriFactory.java b/src/main/java/io/simplelocalize/cli/client/SimpleLocalizeUriFactory.java index 3c5e2c5..609359d 100644 --- a/src/main/java/io/simplelocalize/cli/client/SimpleLocalizeUriFactory.java +++ b/src/main/java/io/simplelocalize/cli/client/SimpleLocalizeUriFactory.java @@ -147,6 +147,11 @@ public URI buildStacktraceUri() return URI.create(baseUrl + "/cli/v1/stacktrace"); } + URI buildFileFormatsUri() + { + return URI.create(baseUrl + "/api/v1/file-formats"); + } + URI buildGetRunningAutoTranslationJobsUri() { return URI.create(baseUrl + "/api/v2/jobs?status=RUNNING&type=AUTO_TRANSLATION"); diff --git a/src/main/java/io/simplelocalize/cli/command/UploadCommand.java b/src/main/java/io/simplelocalize/cli/command/UploadCommand.java index c01a2f4..b878f22 100644 --- a/src/main/java/io/simplelocalize/cli/command/UploadCommand.java +++ b/src/main/java/io/simplelocalize/cli/command/UploadCommand.java @@ -18,10 +18,16 @@ import java.io.IOException; import java.nio.file.Path; import java.util.List; +import java.util.Locale; public class UploadCommand implements CliCommand { private static final Logger log = LoggerFactory.getLogger(UploadCommand.class); + /** + * Used only when the API cannot be reached. The API is the source of truth, see + * FileFormat#hasMultipleLanguages and FileFormatServiceTest#shouldReturnExactListOfMultiLanguageFormats. + */ + private static final List FALLBACK_MULTI_LANGUAGE_FILE_FORMATS = List.of("multi-language-json", "project-json", "excel", "csv-translations", "tsv", "localizable-xcstrings"); private final FileListReader fileListReader = new FileListReader(); private final SimpleLocalizeClient client; private final Configuration configuration; @@ -215,8 +221,7 @@ private void validateConfiguration(Configuration configuration) throw new ConfigurationException("You cannot use {ns} placeholder in uploadPath and namespace parameter at the same time"); } - final boolean isMultiLanguage = isMultiLanguage(configuration); - if (isMultiLanguage && hasLanguagePlaceholder) + if (hasLanguagePlaceholder && isMultiLanguage(configuration)) { throw new ConfigurationException("You cannot use language key parameter with multi-language file formats"); } @@ -224,23 +229,64 @@ private void validateConfiguration(Configuration configuration) private boolean isMultiLanguage(Configuration configuration) { - final List multiLanguageFileFormats = List.of("multi-language-json", "excel", "csv-translations", "tsv", "localizable-xcstrings"); - for (String uploadFormat : multiLanguageFileFormats) + final List uploadOptions = configuration.getUploadOptions(); + for (String uploadOption : uploadOptions) { - if (uploadFormat.equalsIgnoreCase(configuration.getUploadFormat())) + if (uploadOption.equalsIgnoreCase("MULTI_LANGUAGE")) { return true; } } - final List uploadOptions = configuration.getUploadOptions(); - for (String uploadOption : uploadOptions) + final String uploadFormat = normalizeFileFormat(configuration.getUploadFormat()); + for (String multiLanguageFileFormat : getMultiLanguageFileFormats()) { - if (uploadOption.equalsIgnoreCase("MULTI_LANGUAGE")) + if (normalizeFileFormat(multiLanguageFileFormat).equals(uploadFormat)) { return true; } } return false; } + + /** + * API versions released before the file-formats fix report enum names ('MULTI_LANGUAGE_JSON') + * instead of format values ('multi-language-json'), so both spellings have to match the + * configured upload format. Without it such a response would silently match nothing. + */ + private String normalizeFileFormat(String fileFormat) + { + if (fileFormat == null) + { + return ""; + } + return fileFormat.toLowerCase(Locale.ROOT).replaceAll("[^a-z0-9]", ""); + } + + private List getMultiLanguageFileFormats() + { + try + { + final List fileFormats = client.fetchMultiLanguageFileFormats(); + if (fileFormats != null && !fileFormats.isEmpty()) + { + return fileFormats; + } + log.warn("Could not load multi-language file formats from the API (empty response), using the list built into this CLI version"); + } catch (InterruptedException e) + { + Thread.currentThread().interrupt(); + logFileFormatsFallback(e); + } catch (Exception e) + { + logFileFormatsFallback(e); + } + return FALLBACK_MULTI_LANGUAGE_FILE_FORMATS; + } + + private void logFileFormatsFallback(Exception e) + { + final String reason = StringUtils.isNotEmpty(e.getMessage()) ? e.getMessage() : e.getClass().getSimpleName(); + log.warn("Could not load multi-language file formats from the API ({}), using the list built into this CLI version", reason); + } } diff --git a/src/test/java/io/simplelocalize/cli/client/SimpleLocalizeClientTest.java b/src/test/java/io/simplelocalize/cli/client/SimpleLocalizeClientTest.java index e261c08..17a7fcb 100644 --- a/src/test/java/io/simplelocalize/cli/client/SimpleLocalizeClientTest.java +++ b/src/test/java/io/simplelocalize/cli/client/SimpleLocalizeClientTest.java @@ -388,4 +388,63 @@ void shouldGiveUpDownloadAfterMaxRetryAttempts() assertThat(Path.of(downloadPath)).doesNotExist(); } + @Test + void shouldFetchMultiLanguageFileFormats() throws Exception + { + //given + SimpleLocalizeClient client = new SimpleLocalizeClient(MOCK_SERVER_BASE_URL, "81707741b64e68427e1a2c20e75095b1"); + // Response shape of GET /api/v1/file-formats, wrapped by the API's global response wrapper + String responseBody = """ + { + "msg": "OK", + "status": 200, + "data": { + "fileFormats": [ + {"value": "java-properties", "multiLanguageSupport": false}, + {"value": "csv", "multiLanguageSupport": false, "deprecated": true}, + {"value": "excel", "multiLanguageSupport": true}, + {"value": "multi-language-json", "multiLanguageSupport": true}, + {"value": "ini", "multiLanguageSupport": false, "beta": true} + ] + } + } + """; + mockServer.when(request() + .withMethod("GET") + .withPath("/api/v1/file-formats"), + Times.exactly(1)) + .respond( + response() + .withStatusCode(200) + .withContentType(MediaType.APPLICATION_JSON) + .withBody(responseBody) + ); + + //when + List result = client.fetchMultiLanguageFileFormats(); + + //then + assertThat(result).containsExactly("excel", "multi-language-json"); + } + + @Test + void shouldThrowWhenFetchingFileFormatsFails() + { + //given + SimpleLocalizeClient client = new SimpleLocalizeClient(MOCK_SERVER_BASE_URL, "81707741b64e68427e1a2c20e75095b1"); + mockServer.when(request() + .withMethod("GET") + .withPath("/api/v1/file-formats"), + Times.exactly(1)) + .respond( + response() + .withStatusCode(500) + .withContentType(MediaType.APPLICATION_JSON) + .withBody("{\"msg\": \"Internal server error\"}") + ); + + //when & then + Assertions.assertThatThrownBy(client::fetchMultiLanguageFileFormats) + .isInstanceOf(ApiRequestException.class); + } } diff --git a/src/test/java/io/simplelocalize/cli/command/UploadCommandTest.java b/src/test/java/io/simplelocalize/cli/command/UploadCommandTest.java index 1cd5a55..87b88fb 100644 --- a/src/test/java/io/simplelocalize/cli/command/UploadCommandTest.java +++ b/src/test/java/io/simplelocalize/cli/command/UploadCommandTest.java @@ -5,6 +5,7 @@ import io.simplelocalize.cli.client.dto.proxy.Configuration; import io.simplelocalize.cli.client.dto.proxy.LanguageTransform; import io.simplelocalize.cli.client.dto.proxy.Mappings; +import io.simplelocalize.cli.exception.ConfigurationException; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -15,6 +16,7 @@ import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; +import java.io.IOException; import java.nio.file.Path; import java.util.Collections; import java.util.List; @@ -262,4 +264,129 @@ public void shouldUseLanguageMappingReverseValueForSingleMappedFile() throws Exc .orElseThrow(); Assertions.assertThat(enUsRequest.path().toString()).contains("values-en"); } + @Test + public void shouldUseMultiLanguageFileFormatsReturnedByApi() throws Exception + { + //given + // 'json' is not multi-language in the CLI's built-in list, so rejecting it proves the API list was used + Mockito.when(client.fetchMultiLanguageFileFormats()).thenReturn(List.of("json")); + + Configuration configuration = Configuration.defaultConfiguration(); + configuration.setApiKey("my-api-key"); + configuration.setUploadPath("./junit/download-test/values-{lang}/strings.xml"); + configuration.setUploadFormat("json"); + + //when + UploadCommand uploadCommand = new UploadCommand(client, configuration); + + //then + Assertions.assertThatThrownBy(uploadCommand::invoke) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining("multi-language file formats"); + Mockito.verify(client, times(0)).uploadFile(any()); + } + + @Test + public void shouldAcceptFormatThatApiDoesNotReportAsMultiLanguage() throws Exception + { + //given + // 'excel' is multi-language in the built-in list, but the API is the source of truth + Mockito.when(client.fetchMultiLanguageFileFormats()).thenReturn(List.of("tsv")); + + Configuration configuration = Configuration.defaultConfiguration(); + configuration.setApiKey("my-api-key"); + configuration.setUploadPath("./junit/download-test/values-{lang}/strings.xml"); + configuration.setUploadFormat("excel"); + + //when + UploadCommand uploadCommand = new UploadCommand(client, configuration); + uploadCommand.invoke(); + + //then + Mockito.verify(client, times(12)).uploadFile(any()); + } + + @Test + public void shouldFallBackToBuiltInFormatsWhenApiRequestFails() throws Exception + { + //given + Mockito.when(client.fetchMultiLanguageFileFormats()).thenThrow(new IOException("connection refused")); + + Configuration configuration = Configuration.defaultConfiguration(); + configuration.setApiKey("my-api-key"); + configuration.setUploadPath("./junit/download-test/values-{lang}/strings.xml"); + configuration.setUploadFormat("excel"); + + //when + UploadCommand uploadCommand = new UploadCommand(client, configuration); + + //then + Assertions.assertThatThrownBy(uploadCommand::invoke) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining("multi-language file formats"); + Mockito.verify(client, times(0)).uploadFile(any()); + } + + @Test + public void shouldFallBackToBuiltInFormatsWhenApiReturnsNothing() throws Exception + { + //given + Mockito.when(client.fetchMultiLanguageFileFormats()).thenReturn(List.of()); + + Configuration configuration = Configuration.defaultConfiguration(); + configuration.setApiKey("my-api-key"); + configuration.setUploadPath("./junit/download-test/values-{lang}/strings.xml"); + configuration.setUploadFormat("project-json"); + + //when + UploadCommand uploadCommand = new UploadCommand(client, configuration); + + //then + Assertions.assertThatThrownBy(uploadCommand::invoke) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining("multi-language file formats"); + Mockito.verify(client, times(0)).uploadFile(any()); + } + + @Test + public void shouldTreatMultiLanguageUploadOptionAsMultiLanguageWithoutCallingApi() throws Exception + { + //given + Configuration configuration = Configuration.defaultConfiguration(); + configuration.setApiKey("my-api-key"); + configuration.setUploadPath("./junit/download-test/values-{lang}/strings.xml"); + configuration.setUploadFormat("android"); + configuration.setUploadOptions(List.of("MULTI_LANGUAGE")); + + //when + UploadCommand uploadCommand = new UploadCommand(client, configuration); + + //then + Assertions.assertThatThrownBy(uploadCommand::invoke) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining("multi-language file formats"); + Mockito.verify(client, times(0)).fetchMultiLanguageFileFormats(); + } + + @Test + public void shouldUnderstandEnumNamesReturnedByOlderApiVersions() throws Exception + { + //given + // API versions before the file-formats fix report enum names instead of format values + Mockito.when(client.fetchMultiLanguageFileFormats()).thenReturn(List.of("MULTI_LANGUAGE_JSON", "LOCALIZABLE_XC_STRINGS", "EXCEL")); + + Configuration configuration = Configuration.defaultConfiguration(); + configuration.setApiKey("my-api-key"); + configuration.setUploadPath("./junit/download-test/values-{lang}/strings.xml"); + configuration.setUploadFormat("localizable-xcstrings"); + + //when + UploadCommand uploadCommand = new UploadCommand(client, configuration); + + //then + Assertions.assertThatThrownBy(uploadCommand::invoke) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining("multi-language file formats"); + Mockito.verify(client, times(0)).uploadFile(any()); + } } From b9d6d1e3c51b17c8818ac55968eda9628d170e60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Pomyka=C5=82a?= Date: Tue, 22 Sep 2026 16:22:18 +0200 Subject: [PATCH 5/5] Polishing --- .github/workflows/build-release.yml | 20 +++++++++---------- .github/workflows/run-tests.yml | 6 +++--- .github/workflows/test-executables.yml | 8 ++++---- README.md | 6 +++--- .../java/io/simplelocalize/cli/Version.java | 2 +- .../cli/client/SimpleLocalizeClient.java | 5 ----- .../cli/command/UploadCommand.java | 9 --------- 7 files changed, 21 insertions(+), 35 deletions(-) diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 20eed5a..a046492 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -20,9 +20,9 @@ jobs: outputs: version: ${{ steps.get-version.outputs.version }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 name: "Checkout" - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v6 name: "Install Java" with: java-version: '21' @@ -39,13 +39,13 @@ jobs: - name: 'Smoke test JAR' run: ./scripts/smoke-test.sh java -jar target/simplelocalize-cli-${{env.VERSION}}.jar - name: 'Upload artifact' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: 'simplelocalize-cli-${{env.VERSION}}.jar' path: 'target/simplelocalize-cli-${{env.VERSION}}.jar' - name: 'Upload JAR to Release' if: github.event_name == 'release' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v3 with: files: 'target/simplelocalize-cli-${{env.VERSION}}.jar' env: @@ -79,7 +79,7 @@ jobs: downloadFilename: 'simplelocalize-cli-windows.exe' runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 name: "Checkout" - name: "Print architecture" run: uname -m @@ -104,13 +104,13 @@ jobs: codesign --force --deep --sign - ./target/${{ matrix.downloadFilename }} codesign --verify --verbose ./target/${{ matrix.downloadFilename }} - name: "Upload artifact" - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: simplelocalize-cli-${{ matrix.label }} path: ./target/${{ matrix.downloadFilename }} - name: 'Upload Executable to Release' if: github.event_name == 'release' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v3 with: files: './target/${{ matrix.downloadFilename }}' env: @@ -122,7 +122,7 @@ jobs: runs-on: ubuntu-latest steps: - name: "Download all artifacts" - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: path: dist merge-multiple: true @@ -131,13 +131,13 @@ jobs: cd dist zip -j "../simplelocalize-cli-${{ needs.build-jar.outputs.version }}.zip" * - name: "Upload bundle artifact" - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: 'simplelocalize-cli-${{ needs.build-jar.outputs.version }}' path: 'simplelocalize-cli-${{ needs.build-jar.outputs.version }}.zip' - name: 'Upload bundle to Release' if: github.event_name == 'release' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v3 with: files: 'simplelocalize-cli-${{ needs.build-jar.outputs.version }}.zip' env: diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 0f8b062..6d9745e 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -16,17 +16,17 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v6 name: "Install Java" with: java-version: '21' distribution: 'corretto' cache: 'maven' - name: Cache SonarCloud packages - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar diff --git a/.github/workflows/test-executables.yml b/.github/workflows/test-executables.yml index 9b54567..87e8a37 100644 --- a/.github/workflows/test-executables.yml +++ b/.github/workflows/test-executables.yml @@ -18,9 +18,9 @@ jobs: fail-fast: false matrix: os: [ macos-26, macos-26-intel, ubuntu-24.04-arm, ubuntu-latest ] - version: [ '2.12', '2.11', '2.10' ] + version: [ '2.11', '2.10' ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v7 - name: Test on ${{ matrix.os }} run: | curl --fail -s https://get.simplelocalize.io/${{ matrix.version }}/install | bash @@ -32,9 +32,9 @@ jobs: fail-fast: false matrix: os: [ windows-latest ] - version: [ '2.12', '2.11', '2.10' ] + version: [ '2.11', '2.10' ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v7 - name: Test on ${{ matrix.os }} run: | . { iwr -useb https://get.simplelocalize.io/${{ matrix.version }}/install-windows } | iex; diff --git a/README.md b/README.md index d89ad4b..d7266a4 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ and it's a great tool for CI/CD pipelines and localization automation. - [Synchronize files](https://simplelocalize.io/auto-translation/) - keep in sync all your translation files - [Auto-translate](https://simplelocalize.io/auto-translation/) - auto-translate texts in seconds - [Host translations](https://simplelocalize.io/translation-hosting/) - manage your hosted translations -- [GitHub Actions support](https://github.com/simplelocalize/github-action-cli/) - seemles integration with GitHub Actions +- [GitHub Actions support](https://github.com/simplelocalize/github-action-cli/) - seamless integration with GitHub Actions ## Installation @@ -22,10 +22,10 @@ The installation process is automated by command-line scripts. Both scripts for ```bash # macOS / Linux / Windows (WSL) -curl -s https://get.simplelocalize.io/2.12/install | bash +curl -s https://get.simplelocalize.io/2.11/install | bash # Windows (PowerShell) -. { iwr -useb https://get.simplelocalize.io/2.12/install-windows } | iex; +. { iwr -useb https://get.simplelocalize.io/2.11/install-windows } | iex; # npm - https://github.com/simplelocalize/simplelocalize-cli-npm npm install @simplelocalize/cli diff --git a/src/main/java/io/simplelocalize/cli/Version.java b/src/main/java/io/simplelocalize/cli/Version.java index 1526dc0..98e6eb0 100644 --- a/src/main/java/io/simplelocalize/cli/Version.java +++ b/src/main/java/io/simplelocalize/cli/Version.java @@ -3,7 +3,7 @@ public class Version { - public static final String NUMBER = "2.11.2"; + public static final String NUMBER = "2.12.0"; private Version() { diff --git a/src/main/java/io/simplelocalize/cli/client/SimpleLocalizeClient.java b/src/main/java/io/simplelocalize/cli/client/SimpleLocalizeClient.java index 5f630d0..7e84638 100644 --- a/src/main/java/io/simplelocalize/cli/client/SimpleLocalizeClient.java +++ b/src/main/java/io/simplelocalize/cli/client/SimpleLocalizeClient.java @@ -97,10 +97,6 @@ private String prettyPrintJson(DocumentContext json) throws JsonProcessingExcept return mapper.writeValueAsString(data); } - /** - * Returns file formats that carry translations for more than one language, as declared by the API. - * The CLI keeps its own copy of that list as a fallback, see UploadCommand. - */ public List fetchMultiLanguageFileFormats() throws IOException, InterruptedException { URI uri = uriFactory.buildFileFormatsUri(); @@ -108,7 +104,6 @@ public List fetchMultiLanguageFileFormats() throws IOException, Interrup HttpResponse httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString()); if (httpResponse.statusCode() != 200) { - // Caller falls back to its built-in list and warns, so this must not log an error on its own throw new ApiRequestException(readErrorMessage(httpResponse)); } DocumentContext json = JsonPath.parse(httpResponse.body()); diff --git a/src/main/java/io/simplelocalize/cli/command/UploadCommand.java b/src/main/java/io/simplelocalize/cli/command/UploadCommand.java index b878f22..b7af926 100644 --- a/src/main/java/io/simplelocalize/cli/command/UploadCommand.java +++ b/src/main/java/io/simplelocalize/cli/command/UploadCommand.java @@ -23,10 +23,6 @@ public class UploadCommand implements CliCommand { private static final Logger log = LoggerFactory.getLogger(UploadCommand.class); - /** - * Used only when the API cannot be reached. The API is the source of truth, see - * FileFormat#hasMultipleLanguages and FileFormatServiceTest#shouldReturnExactListOfMultiLanguageFormats. - */ private static final List FALLBACK_MULTI_LANGUAGE_FILE_FORMATS = List.of("multi-language-json", "project-json", "excel", "csv-translations", "tsv", "localizable-xcstrings"); private final FileListReader fileListReader = new FileListReader(); private final SimpleLocalizeClient client; @@ -249,11 +245,6 @@ private boolean isMultiLanguage(Configuration configuration) return false; } - /** - * API versions released before the file-formats fix report enum names ('MULTI_LANGUAGE_JSON') - * instead of format values ('multi-language-json'), so both spellings have to match the - * configured upload format. Without it such a response would silently match nothing. - */ private String normalizeFileFormat(String fileFormat) { if (fileFormat == null)