Skip to content

Batch translate all files & Custom Open-AI API support - #85

Open
JargeZ wants to merge 2 commits into
soberlemur:masterfrom
JargeZ:features
Open

Batch translate all files & Custom Open-AI API support#85
JargeZ wants to merge 2 commits into
soberlemur:masterfrom
JargeZ:features

Conversation

@JargeZ

@JargeZ JargeZ commented Dec 7, 2025

Copy link
Copy Markdown

Hello my mates!

I proudly vibe-coded something 🤠 I can't verify myself from a Java perspective.
BUT
it works great and has really helped me use the local LMStudio to make translations for my micro-project.
That you can, btw, check out here bbq-me.org it's a pretty fun tool that helps me find places to hang out with friends on the weekends 🌭

I want to leave this in the pull request here to not abandon the work that I believe can help many other people who have found this program very useful, but I immediately got tired of clicking on each file manually and waiting to move on to the next one. And again after adding each new line for translation...

So

@JargeZ JargeZ changed the title Batch ranslate all files & Custom Open-AI API support Batch translate all files & Custom Open-AI API support Dec 7, 2025
@torakiki
torakiki requested a review from Copilot January 3, 2026 16:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds batch translation functionality for all project files and support for custom OpenAI-compatible API endpoints (enabling use of local models like LMStudio and qwen3-30b).

Key Changes:

  • Adds a "Batch AI translation (All Files)" button that sequentially processes all translation files in a project
  • Introduces custom OpenAI base URL configuration and custom model name support for OpenAI-compatible providers
  • Enables editable model selection in UI to support arbitrary model names

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
autopo-i18n/po/autopo.pot Adds translation strings for the new "Batch AI translation (All Files)" feature
TranslationsTableToolbar.java Implements TranslateAllFilesButton class with sequential file processing logic
OpenAiModelDescriptor.java Adds HTTP client configuration, base URL support, and custom model name handling
OpenAISettings.java Updates UI to support editable model names and custom base URL field
OpenAIPersistentProperty.java Adds BASE_URL persistent property
module-info.java Adds required module dependencies for HTTP client functionality
pom.xml Adds langchain4j-http-client-jdk dependency

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +275 to +281
if (n) {
ofNullable(subscription[0]).ifPresent(Subscription::unsubscribe);
// Move to next file after translation completes
processNextFile(iterator, aiModelDescriptor, description);
}
});
}

Copilot AI Jan 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The subscription array pattern creates a potential memory leak. If the translation never completes (e.g., due to an error or cancellation), the subscription will never be unsubscribed and the listener will remain active indefinitely. Consider handling error cases or adding a cleanup mechanism to ensure subscriptions are always released.

Suggested change
if (n) {
ofNullable(subscription[0]).ifPresent(Subscription::unsubscribe);
// Move to next file after translation completes
processNextFile(iterator, aiModelDescriptor, description);
}
});
}
// Always unsubscribe to avoid leaking the listener, regardless of outcome
ofNullable(subscription[0]).ifPresent(Subscription::unsubscribe);
// Move to next file after translation completes or in case of failure/cancellation
processNextFile(iterator, aiModelDescriptor, description);
});
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +112 to +116
JdkHttpClientBuilder customHttpClientBuilder = JdkHttpClient.builder()
.httpClientBuilder(HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1) // Specify HTTP/1.1 for certain local models like vLLM
.connectTimeout(Duration.ofSeconds(10))) // Set connection timeout
.readTimeout(Duration.ofSeconds(60)); // Set read timeout

Copilot AI Jan 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code duplicates HTTP client configuration logic between translationModel() and validationModel() methods. Consider extracting this configuration into a private helper method to improve maintainability and ensure consistency.

Copilot uses AI. Check for mistakes.
Comment on lines +126 to +131
var baseUrl = repo.getString(OpenAIPersistentProperty.BASE_URL.key(), (String) null);
if (isNotBlank(baseUrl)) {
builder.baseUrl(baseUrl);
} else {
builder.baseUrl(DEFAULT_OPENAI_BASE_URL);
}

Copilot AI Jan 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The base URL configuration logic is duplicated between translationModel() and validationModel() methods. Consider extracting this logic into a private helper method to improve maintainability and reduce duplication.

Copilot uses AI. Check for mistakes.
Comment on lines +133 to +139
var modelName = repo.getString(OpenAIPersistentProperty.MODEL_NAME.key(), GPT_4.name());
try {
builder.modelName(OpenAiChatModelName.valueOf(modelName));
} catch (IllegalArgumentException e) {
// Custom model name not in enum, use as string
builder.modelName(modelName);
}

Copilot AI Jan 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The model name configuration logic with exception handling is duplicated between translationModel() and validationModel() methods. Consider extracting this logic into a private helper method to improve maintainability and ensure consistent error handling.

Copilot uses AI. Check for mistakes.
Comment on lines +106 to +112
add(new Label(i18n().tr("Base URL:")), 0, 4);
var urlField = new TextField();
urlField.setPromptText("https://api.openai.com/v1/");
ofNullable(repo.getString(OpenAIPersistentProperty.BASE_URL.key(), (String) null)).ifPresent(urlField::setText);
urlField.textProperty().subscribe((o, n) -> repo.saveString(OpenAIPersistentProperty.BASE_URL.key(), n));
setFillWidth(urlField, true);
add(urlField, 1, 4, 2, 1);

Copilot AI Jan 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Base URL field lacks input validation. Users could enter malformed URLs or non-HTTPS endpoints which could lead to runtime errors or security issues. Consider adding URL format validation and potentially restricting to HTTPS for security purposes.

Copilot uses AI. Check for mistakes.
Comment on lines +91 to +96
try {
builder.modelName(OpenAiChatModelName.valueOf(modelName));
} catch (IllegalArgumentException e) {
// Custom model name not in enum, use as string
builder.modelName(modelName);
}

Copilot AI Jan 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IllegalArgumentException catch block silently falls back to using the model name as a string without logging or notifying the user. This could make it difficult to debug issues when a user enters an invalid model name. Consider logging the exception or providing user feedback when falling back to custom model names.

Copilot uses AI. Check for mistakes.
Comment on lines +106 to 123
add(new Label(i18n().tr("Base URL:")), 0, 4);
var urlField = new TextField();
urlField.setPromptText("https://api.openai.com/v1/");
ofNullable(repo.getString(OpenAIPersistentProperty.BASE_URL.key(), (String) null)).ifPresent(urlField::setText);
urlField.textProperty().subscribe((o, n) -> repo.saveString(OpenAIPersistentProperty.BASE_URL.key(), n));
setFillWidth(urlField, true);
add(urlField, 1, 4, 2, 1);

Button clearButton = new Button(i18n().tr("Clear"));
clearButton.setTooltip(new Tooltip(i18n().tr("Clear OpenAI settings")));
clearButton.setGraphic(FontIcon.of(FluentUiFilledAL.ERASER_24));
clearButton.setOnAction(e -> {
repo.clean();
apiField.setText("");
modelCombo.getSelectionModel().clearSelection();
urlField.setText("");
});
add(clearButton, 0, 3);

Copilot AI Jan 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Base URL field is added at row 4, but the Clear button is at row 3, causing the UI layout to be inconsistent with a gap between the Temperature field (row 2) and the Clear button (row 3). The Base URL should be at row 3, and the Clear button should be moved to row 4 to maintain a logical top-to-bottom flow without gaps.

Copilot uses AI. Check for mistakes.
Comment on lines +235 to +245
final Subscription[] subscription = new Subscription[1];
subscription[0] = poFile.status().subscribe(status -> {
if (status == LoadingStatus.LOADED) {
ofNullable(subscription[0]).ifPresent(Subscription::unsubscribe);
translateCurrentFile(aiModelDescriptor, description, iterator);
} else if (status == LoadingStatus.ERROR) {
ofNullable(subscription[0]).ifPresent(Subscription::unsubscribe);
// Skip this file and continue with next
processNextFile(iterator, aiModelDescriptor, description);
}
});

Copilot AI Jan 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The subscription array pattern used here creates a potential memory leak. The subscription is stored in an array to allow self-reference, but if the status never becomes LOADED or ERROR, the subscription will never be unsubscribed and the listener will remain active indefinitely. Consider adding a timeout or cleanup mechanism to ensure the subscription is always eventually unsubscribed.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a menu entry to translate untranslated

2 participants