mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-26 22:29:24 +00:00
change translation logic to front end only
This commit is contained in:
parent
c9c44723c2
commit
666fc05ec6
@ -5,8 +5,8 @@ import java.io.IOException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Utility class for handling exceptions with internationalized error messages. Provides consistent
|
||||
* error handling and user-friendly messages across the application.
|
||||
* Utility class for handling exceptions with consistent English error messages. Frontend will
|
||||
* handle translation to user's language.
|
||||
*/
|
||||
@Slf4j
|
||||
public class ExceptionUtils {
|
||||
@ -32,15 +32,12 @@ public class ExceptionUtils {
|
||||
String message;
|
||||
if (context != null && !context.isEmpty()) {
|
||||
message =
|
||||
I18nUtils.getMessage(
|
||||
"error.pdfCorruptedDuring",
|
||||
"Error {0}: PDF file appears to be corrupted or damaged. Please try using the 'Repair PDF' feature first to fix the file before proceeding with this operation.",
|
||||
String.format(
|
||||
"Error %s: PDF file appears to be corrupted or damaged. Please try using the 'Repair PDF' feature first to fix the file before proceeding with this operation.",
|
||||
context);
|
||||
} else {
|
||||
message =
|
||||
I18nUtils.getMessage(
|
||||
"error.pdfCorrupted",
|
||||
"PDF file appears to be corrupted or damaged. Please try using the 'Repair PDF' feature first to fix the file before proceeding with this operation.");
|
||||
"PDF file appears to be corrupted or damaged. Please try using the 'Repair PDF' feature first to fix the file before proceeding with this operation.";
|
||||
}
|
||||
return new IOException(message, cause);
|
||||
}
|
||||
@ -53,9 +50,7 @@ public class ExceptionUtils {
|
||||
*/
|
||||
public static IOException createMultiplePdfCorruptedException(Exception cause) {
|
||||
String message =
|
||||
I18nUtils.getMessage(
|
||||
"error.pdfCorruptedMultiple",
|
||||
"One or more PDF files appear to be corrupted or damaged. Please try using the 'Repair PDF' feature on each file first before attempting to merge them.");
|
||||
"One or more PDF files appear to be corrupted or damaged. Please try using the 'Repair PDF' feature on each file first before attempting to merge them.";
|
||||
return new IOException(message, cause);
|
||||
}
|
||||
|
||||
@ -67,10 +62,7 @@ public class ExceptionUtils {
|
||||
*/
|
||||
public static IOException createPdfEncryptionException(Exception cause) {
|
||||
String message =
|
||||
I18nUtils.getMessage(
|
||||
"error.pdfEncryption",
|
||||
"The PDF appears to have corrupted encryption data. This can happen when the PDF was created with incompatible encryption methods. Please try using the 'Repair PDF' feature first, or contact the document creator for a new copy.",
|
||||
cause.getMessage());
|
||||
"The PDF appears to have corrupted encryption data. This can happen when the PDF was created with incompatible encryption methods. Please try using the 'Repair PDF' feature first, or contact the document creator for a new copy.";
|
||||
return new IOException(message, cause);
|
||||
}
|
||||
|
||||
@ -82,9 +74,7 @@ public class ExceptionUtils {
|
||||
*/
|
||||
public static IOException createPdfPasswordException(Exception cause) {
|
||||
String message =
|
||||
I18nUtils.getMessage(
|
||||
"error.pdfPassword",
|
||||
"The PDF Document is passworded and either the password was not provided or was incorrect");
|
||||
"The PDF Document is passworded and either the password was not provided or was incorrect";
|
||||
return new IOException(message, cause);
|
||||
}
|
||||
|
||||
@ -97,11 +87,9 @@ public class ExceptionUtils {
|
||||
*/
|
||||
public static IOException createFileProcessingException(String operation, Exception cause) {
|
||||
String message =
|
||||
I18nUtils.getMessage(
|
||||
"error.fileProcessing",
|
||||
"An error occurred while processing the file during {0} operation: {1}",
|
||||
operation,
|
||||
cause.getMessage());
|
||||
String.format(
|
||||
"An error occurred while processing the file during %s operation: %s",
|
||||
operation, cause.getMessage());
|
||||
return new IOException(message, cause);
|
||||
}
|
||||
|
||||
@ -116,7 +104,7 @@ public class ExceptionUtils {
|
||||
*/
|
||||
public static IOException createIOException(
|
||||
String messageKey, String defaultMessage, Exception cause, Object... args) {
|
||||
String message = I18nUtils.getMessage(messageKey, defaultMessage, args);
|
||||
String message = String.format(defaultMessage, args);
|
||||
return new IOException(message, cause);
|
||||
}
|
||||
|
||||
@ -131,7 +119,7 @@ public class ExceptionUtils {
|
||||
*/
|
||||
public static RuntimeException createRuntimeException(
|
||||
String messageKey, String defaultMessage, Exception cause, Object... args) {
|
||||
String message = I18nUtils.getMessage(messageKey, defaultMessage, args);
|
||||
String message = String.format(defaultMessage, args);
|
||||
return new RuntimeException(message, cause);
|
||||
}
|
||||
|
||||
@ -145,8 +133,8 @@ public class ExceptionUtils {
|
||||
*/
|
||||
public static IllegalArgumentException createIllegalArgumentException(
|
||||
String messageKey, String defaultMessage, Object... args) {
|
||||
String message = I18nUtils.getMessage(messageKey, defaultMessage, args);
|
||||
return new IllegalArgumentException(message);
|
||||
String message = String.format(defaultMessage, args);
|
||||
return new TranslatableException(message, messageKey, args);
|
||||
}
|
||||
|
||||
/** Create file validation exceptions. */
|
||||
|
@ -1,117 +0,0 @@
|
||||
package stirling.software.common.util;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Utility class for internationalized (i18n) message handling. Provides centralized access to
|
||||
* Spring MessageSource for consistent error messaging.
|
||||
*/
|
||||
@Slf4j
|
||||
public class I18nUtils {
|
||||
|
||||
private static MessageSource messageSource;
|
||||
|
||||
/**
|
||||
* Get the MessageSource bean from the application context.
|
||||
*
|
||||
* @return MessageSource instance, or null if not available
|
||||
*/
|
||||
public static MessageSource getMessageSource() {
|
||||
if (messageSource == null) {
|
||||
try {
|
||||
messageSource = ApplicationContextProvider.getBean(MessageSource.class);
|
||||
} catch (Exception e) {
|
||||
log.debug("MessageSource not available in application context", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return messageSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a localized message for the given key with parameters.
|
||||
*
|
||||
* @param key the message key
|
||||
* @param args optional arguments for the message
|
||||
* @return the localized message, or the key itself if message source is not available
|
||||
*/
|
||||
public static String getMessage(String key, Object... args) {
|
||||
return getMessage(key, LocaleContextHolder.getLocale(), args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a localized message for the given key with specific locale and parameters.
|
||||
*
|
||||
* @param key the message key
|
||||
* @param locale the locale to use
|
||||
* @param args optional arguments for the message
|
||||
* @return the localized message, or the key itself if message source is not available
|
||||
*/
|
||||
public static String getMessage(String key, Locale locale, Object... args) {
|
||||
MessageSource ms = getMessageSource();
|
||||
if (ms != null) {
|
||||
try {
|
||||
return ms.getMessage(key, args, locale);
|
||||
} catch (Exception e) {
|
||||
log.debug("Failed to get message for key '{}': {}", key, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: return the key with arguments if available
|
||||
if (args != null && args.length > 0) {
|
||||
return key
|
||||
+ " ["
|
||||
+ String.join(
|
||||
", ",
|
||||
java.util.Arrays.stream(args)
|
||||
.map(Object::toString)
|
||||
.toArray(String[]::new))
|
||||
+ "]";
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a localized message with a fallback default message.
|
||||
*
|
||||
* @param key the message key
|
||||
* @param defaultMessage the default message to use if key is not found
|
||||
* @param args optional arguments for the message
|
||||
* @return the localized message or the default message
|
||||
*/
|
||||
public static String getMessage(String key, String defaultMessage, Object... args) {
|
||||
MessageSource ms = getMessageSource();
|
||||
if (ms != null) {
|
||||
try {
|
||||
return ms.getMessage(key, args, defaultMessage, LocaleContextHolder.getLocale());
|
||||
} catch (Exception e) {
|
||||
log.debug("Failed to get message for key '{}': {}", key, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Apply arguments to default message if it contains placeholders
|
||||
if (defaultMessage != null && args != null && args.length > 0) {
|
||||
try {
|
||||
return java.text.MessageFormat.format(defaultMessage, args);
|
||||
} catch (Exception e) {
|
||||
log.debug("Failed to format default message: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return defaultMessage != null ? defaultMessage : key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if MessageSource is available.
|
||||
*
|
||||
* @return true if MessageSource is available, false otherwise
|
||||
*/
|
||||
public static boolean isMessageSourceAvailable() {
|
||||
return getMessageSource() != null;
|
||||
}
|
||||
}
|
@ -2,8 +2,6 @@ package stirling.software.common.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
|
||||
/** Utility class for detecting and handling PDF-related errors. */
|
||||
public class PdfErrorUtils {
|
||||
|
||||
@ -30,81 +28,4 @@ public class PdfErrorUtils {
|
||||
|| message.contains("BadPaddingException")
|
||||
|| message.contains("Given final block not properly padded");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a user-friendly error message for corrupted PDF files using i18n.
|
||||
*
|
||||
* @param messageSource the Spring MessageSource for i18n
|
||||
* @return a user-friendly error message
|
||||
* @deprecated Use ExceptionUtils.createPdfCorruptedException() instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static String getCorruptedPdfMessage(MessageSource messageSource) {
|
||||
return I18nUtils.getMessage(
|
||||
"error.pdfCorrupted",
|
||||
"PDF file appears to be corrupted or damaged. Please try using the 'Repair PDF' feature first to fix the file before proceeding with this operation.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a user-friendly error message for corrupted PDF files with context using i18n.
|
||||
*
|
||||
* @param messageSource the Spring MessageSource for i18n
|
||||
* @param context additional context about where the error occurred (e.g., "during merge",
|
||||
* "during processing")
|
||||
* @return a user-friendly error message
|
||||
* @deprecated Use ExceptionUtils.createPdfCorruptedException(context, cause) instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static String getCorruptedPdfMessage(MessageSource messageSource, String context) {
|
||||
if (context != null && !context.isEmpty()) {
|
||||
return I18nUtils.getMessage(
|
||||
"error.pdfCorruptedDuring",
|
||||
"Error {0}: PDF file appears to be corrupted or damaged. Please try using the 'Repair PDF' feature first to fix the file before proceeding with this operation.",
|
||||
context);
|
||||
}
|
||||
return getCorruptedPdfMessage(messageSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a user-friendly error message for multiple corrupted PDF files (e.g., during merge)
|
||||
* using i18n.
|
||||
*
|
||||
* @param messageSource the Spring MessageSource for i18n
|
||||
* @return a user-friendly error message for multiple file operations
|
||||
* @deprecated Use ExceptionUtils.createMultiplePdfCorruptedException() instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static String getCorruptedPdfMessageForMultipleFiles(MessageSource messageSource) {
|
||||
return I18nUtils.getMessage(
|
||||
"error.pdfCorruptedMultiple",
|
||||
"One or more PDF files appear to be corrupted or damaged. Please try using the 'Repair PDF' feature on each file first before attempting to merge them.");
|
||||
}
|
||||
|
||||
// Fallback methods for backwards compatibility (when MessageSource is not available)
|
||||
/**
|
||||
* Creates a user-friendly error message for corrupted PDF files (fallback).
|
||||
*
|
||||
* @param context additional context about where the error occurred
|
||||
* @return a user-friendly error message
|
||||
*/
|
||||
public static String getCorruptedPdfMessage(String context) {
|
||||
String baseMessage =
|
||||
"PDF file appears to be corrupted or damaged. "
|
||||
+ "Please try using the 'Repair PDF' feature first to fix the file before proceeding with this operation.";
|
||||
|
||||
if (context != null && !context.isEmpty()) {
|
||||
return "Error " + context + ": " + baseMessage;
|
||||
}
|
||||
return baseMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a user-friendly error message for multiple corrupted PDF files (fallback).
|
||||
*
|
||||
* @return a user-friendly error message for multiple file operations
|
||||
*/
|
||||
public static String getCorruptedPdfMessageForMultipleFiles() {
|
||||
return "One or more PDF files appear to be corrupted or damaged. "
|
||||
+ "Please try using the 'Repair PDF' feature on each file first before attempting to merge them.";
|
||||
}
|
||||
}
|
||||
|
@ -141,8 +141,9 @@ public class PdfUtils {
|
||||
if (DPI > MAX_SAFE_DPI) {
|
||||
throw ExceptionUtils.createIllegalArgumentException(
|
||||
"error.dpiExceedsLimit",
|
||||
"DPI value {0} exceeds maximum safe limit of {1}. High DPI values can cause memory issues and crashes. Please use a lower DPI value.",
|
||||
DPI, MAX_SAFE_DPI);
|
||||
"DPI value %d exceeds maximum safe limit of %d. High DPI values can cause memory issues and crashes. Please use a lower DPI value.",
|
||||
DPI,
|
||||
MAX_SAFE_DPI);
|
||||
}
|
||||
|
||||
try (PDDocument document = pdfDocumentFactory.load(inputStream)) {
|
||||
@ -177,8 +178,9 @@ public class PdfUtils {
|
||||
.contains("Maximum size of image exceeded")) {
|
||||
throw ExceptionUtils.createIllegalArgumentException(
|
||||
"error.pageTooBigForDpi",
|
||||
"PDF page {0} is too large to render at {1} DPI. Please try a lower DPI value (recommended: 150 or less).",
|
||||
i + 1, DPI);
|
||||
"PDF page %d is too large to render at %d DPI. Please try a lower DPI value (recommended: 150 or less).",
|
||||
i + 1,
|
||||
DPI);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
@ -221,8 +223,9 @@ public class PdfUtils {
|
||||
.contains("Maximum size of image exceeded")) {
|
||||
throw ExceptionUtils.createIllegalArgumentException(
|
||||
"error.pageTooBigExceedsArray",
|
||||
"PDF page {0} is too large to render at {1} DPI. The resulting image would exceed Java's maximum array size. Please try a lower DPI value (recommended: 150 or less).",
|
||||
i + 1, DPI);
|
||||
"PDF page %d is too large to render at %d DPI. The resulting image would exceed Java's maximum array size. Please try a lower DPI value (recommended: 150 or less).",
|
||||
i + 1,
|
||||
DPI);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
@ -261,8 +264,9 @@ public class PdfUtils {
|
||||
.contains("Maximum size of image exceeded")) {
|
||||
throw ExceptionUtils.createIllegalArgumentException(
|
||||
"error.pageTooBigForDpi",
|
||||
"PDF page {0} is too large to render at {1} DPI. Please try a lower DPI value (recommended: 150 or less).",
|
||||
i + 1, DPI);
|
||||
"PDF page %d is too large to render at %d DPI. Please try a lower DPI value (recommended: 150 or less).",
|
||||
i + 1,
|
||||
DPI);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
@ -293,8 +297,9 @@ public class PdfUtils {
|
||||
&& e.getMessage().contains("Maximum size of image exceeded")) {
|
||||
throw ExceptionUtils.createIllegalArgumentException(
|
||||
"error.pageTooBigForDpi",
|
||||
"PDF page {0} is too large to render at {1} DPI. Please try a lower DPI value (recommended: 150 or less).",
|
||||
i + 1, DPI);
|
||||
"PDF page %d is too large to render at %d DPI. Please try a lower DPI value (recommended: 150 or less).",
|
||||
i + 1,
|
||||
DPI);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
@ -343,7 +348,7 @@ public class PdfUtils {
|
||||
&& e.getMessage().contains("Maximum size of image exceeded")) {
|
||||
throw ExceptionUtils.createIllegalArgumentException(
|
||||
"error.pageTooBigFor300Dpi",
|
||||
"PDF page {0} is too large to render at 300 DPI. The resulting image would exceed Java's maximum array size. Please use a lower DPI value for PDF-to-image conversion.",
|
||||
"PDF page %d is too large to render at 300 DPI. The resulting image would exceed Java's maximum array size. Please use a lower DPI value for PDF-to-image conversion.",
|
||||
page + 1);
|
||||
}
|
||||
throw e;
|
||||
|
@ -0,0 +1,25 @@
|
||||
package stirling.software.common.util;
|
||||
|
||||
/**
|
||||
* Exception that carries translation information for frontend internationalization. The
|
||||
* GlobalExceptionHandler extracts this info to create structured error responses.
|
||||
*/
|
||||
public class TranslatableException extends IllegalArgumentException {
|
||||
|
||||
private final String translationKey;
|
||||
private final Object[] translationArgs;
|
||||
|
||||
public TranslatableException(String message, String translationKey, Object... translationArgs) {
|
||||
super(message);
|
||||
this.translationKey = translationKey;
|
||||
this.translationArgs = translationArgs;
|
||||
}
|
||||
|
||||
public String getTranslationKey() {
|
||||
return translationKey;
|
||||
}
|
||||
|
||||
public Object[] getTranslationArgs() {
|
||||
return translationArgs;
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Global exception handler that creates structured error responses with translation information for
|
||||
* frontend internationalization support.
|
||||
*/
|
||||
@ControllerAdvice
|
||||
@Slf4j
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
public static class ErrorResponse {
|
||||
public String error;
|
||||
public String message;
|
||||
public String trace;
|
||||
public String translationKey;
|
||||
public List<String> translationArgs;
|
||||
|
||||
public ErrorResponse(
|
||||
String error,
|
||||
String message,
|
||||
String trace,
|
||||
String translationKey,
|
||||
List<String> translationArgs) {
|
||||
this.error = error;
|
||||
this.message = message;
|
||||
this.trace = trace;
|
||||
this.translationKey = translationKey;
|
||||
this.translationArgs = translationArgs;
|
||||
}
|
||||
}
|
||||
|
||||
@ExceptionHandler(stirling.software.common.util.TranslatableException.class)
|
||||
public ResponseEntity<ErrorResponse> handleTranslatableException(
|
||||
stirling.software.common.util.TranslatableException e) {
|
||||
List<String> translationArgs = null;
|
||||
if (e.getTranslationArgs() != null) {
|
||||
translationArgs = Arrays.stream(e.getTranslationArgs()).map(String::valueOf).toList();
|
||||
}
|
||||
|
||||
ErrorResponse errorResponse =
|
||||
new ErrorResponse(
|
||||
"Bad Request",
|
||||
e.getMessage(),
|
||||
getStackTrace(e),
|
||||
e.getTranslationKey(),
|
||||
translationArgs);
|
||||
|
||||
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public ResponseEntity<ErrorResponse> handleIllegalArgumentException(
|
||||
IllegalArgumentException e) {
|
||||
ErrorResponse errorResponse =
|
||||
new ErrorResponse("Bad Request", e.getMessage(), getStackTrace(e), null, null);
|
||||
|
||||
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@ExceptionHandler(RuntimeException.class)
|
||||
public ResponseEntity<ErrorResponse> handleRuntimeException(RuntimeException e) {
|
||||
ErrorResponse errorResponse =
|
||||
new ErrorResponse(
|
||||
"Internal Server Error", e.getMessage(), getStackTrace(e), null, null);
|
||||
|
||||
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<ErrorResponse> handleGenericException(Exception e) {
|
||||
ErrorResponse errorResponse =
|
||||
new ErrorResponse(
|
||||
"Internal Server Error", e.getMessage(), getStackTrace(e), null, null);
|
||||
|
||||
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
private String getStackTrace(Exception e) {
|
||||
if (e.getCause() != null) {
|
||||
return e.getCause().toString();
|
||||
}
|
||||
return e.getClass().getSimpleName() + ": " + e.getMessage();
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import stirling.software.SPDF.service.TranslationService;
|
||||
|
||||
/**
|
||||
* Global controller advice that adds common model attributes to all templates. Makes translation
|
||||
* service available for client-side error message translation.
|
||||
*/
|
||||
@ControllerAdvice
|
||||
@RequiredArgsConstructor
|
||||
public class GlobalModelAdvice {
|
||||
|
||||
private final TranslationService translationService;
|
||||
|
||||
/** Add error messages to all templates for frontend translation support. */
|
||||
@ModelAttribute
|
||||
public void addErrorMessages(Model model) {
|
||||
model.addAttribute("errorMessages", translationService.getErrorMessages());
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package stirling.software.SPDF.service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Service for providing translation data to frontend JavaScript. Dynamically loads all error.*
|
||||
* messages for client-side translation.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class TranslationService {
|
||||
|
||||
private final MessageSource messageSource;
|
||||
|
||||
/**
|
||||
* Get all error messages for the current locale to pass to frontend JavaScript. This allows
|
||||
* dynamic translation of error messages sent from backend.
|
||||
*
|
||||
* @return Map of error message keys to localized values
|
||||
*/
|
||||
public Map<String, String> getErrorMessages() {
|
||||
return getErrorMessages(LocaleContextHolder.getLocale());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all error messages for a specific locale.
|
||||
*
|
||||
* @param locale the locale to get messages for
|
||||
* @return Map of error message keys to localized values
|
||||
*/
|
||||
public Map<String, String> getErrorMessages(Locale locale) {
|
||||
Map<String, String> errorMessages = new HashMap<>();
|
||||
|
||||
try {
|
||||
// Load the base messages file to get all available keys
|
||||
ClassPathResource resource = new ClassPathResource("messages_en_GB.properties");
|
||||
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
|
||||
|
||||
// Filter for error.* keys and get their localized values
|
||||
for (Object keyObj : properties.keySet()) {
|
||||
String key = (String) keyObj;
|
||||
if (key.startsWith("error.")) {
|
||||
try {
|
||||
String localizedMessage = messageSource.getMessage(key, null, locale);
|
||||
errorMessages.put(key, localizedMessage);
|
||||
} catch (Exception e) {
|
||||
log.debug(
|
||||
"Could not resolve message for key '{}' in locale '{}': {}",
|
||||
key,
|
||||
locale,
|
||||
e.getMessage());
|
||||
// Fallback to the default message from properties
|
||||
errorMessages.put(key, (String) properties.get(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("Loaded {} error messages for locale '{}'", errorMessages.size(), locale);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to load error messages for locale '{}': {}", locale, e.getMessage());
|
||||
}
|
||||
|
||||
return errorMessages;
|
||||
}
|
||||
}
|
@ -194,7 +194,9 @@ error.invalidFormat=Invalid {0} format: {1}
|
||||
error.endpointDisabled=This endpoint has been disabled by the admin
|
||||
error.urlNotReachable=URL is not reachable, please provide a valid URL
|
||||
|
||||
# DPI and image rendering messages
|
||||
# DPI and image rendering messages - used by frontend for dynamic translation
|
||||
# Backend sends: [TRANSLATE:messageKey:arg1,arg2] English message
|
||||
# Frontend parses this and replaces with localized versions using these keys
|
||||
error.dpiExceedsLimit=DPI value {0} exceeds maximum safe limit of {1}. High DPI values can cause memory issues and crashes. Please use a lower DPI value.
|
||||
error.pageTooBigForDpi=PDF page {0} is too large to render at {1} DPI. Please try a lower DPI value (recommended: 150 or less).
|
||||
error.pageTooBigExceedsArray=PDF page {0} is too large to render at {1} DPI. The resulting image would exceed Java's maximum array size. Please try a lower DPI value (recommended: 150 or less).
|
||||
|
@ -330,7 +330,9 @@
|
||||
|
||||
async function handleJsonResponse(response) {
|
||||
const json = await response.json();
|
||||
const errorMessage = JSON.stringify(json, null, 2);
|
||||
|
||||
// Check for password-related errors first
|
||||
const errorMessage = json.message || '';
|
||||
if (
|
||||
errorMessage.toLowerCase().includes('the password is incorrect') ||
|
||||
errorMessage.toLowerCase().includes('Password is not provided') ||
|
||||
@ -340,9 +342,28 @@
|
||||
firstErrorOccurred = true;
|
||||
alert(pdfPasswordPrompt);
|
||||
}
|
||||
} else {
|
||||
showErrorBanner(json.error + ':' + json.message, json.trace);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle structured error response with translation support
|
||||
let displayMessage = json.message;
|
||||
|
||||
// If translation info is available, use it to translate the message
|
||||
if (json.translationKey && window.stirlingPDF && window.stirlingPDF.translations) {
|
||||
const translatedTemplate = window.stirlingPDF.translations[json.translationKey];
|
||||
if (translatedTemplate) {
|
||||
displayMessage = translatedTemplate;
|
||||
|
||||
// Replace placeholders with args if available
|
||||
if (json.translationArgs && Array.isArray(json.translationArgs)) {
|
||||
json.translationArgs.forEach((arg, index) => {
|
||||
displayMessage = displayMessage.replace(`{${index}}`, arg);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showErrorBanner((json.error || 'Error') + ': ' + displayMessage, json.trace || '');
|
||||
}
|
||||
|
||||
async function handleResponse(blob, filename, considerViewOptions = false, isZip = false) {
|
||||
|
@ -252,6 +252,10 @@
|
||||
window.stirlingPDF.uploadLimitExceededPlural = /*[[#{uploadLimitExceededPlural}]]*/ 'are too large. Maximum allowed size is';
|
||||
window.stirlingPDF.pdfCorruptedMessage = /*[[#{error.pdfInvalid}]]*/ 'The PDF file "{0}" appears to be corrupted or has an invalid structure. Please try using the \'Repair PDF\' feature to fix the file before proceeding.';
|
||||
window.stirlingPDF.tryRepairMessage = /*[[#{error.tryRepair}]]*/ 'Try using the Repair PDF feature to fix corrupted files.';
|
||||
|
||||
// Translation service for dynamic error message translation
|
||||
// Dynamically populated with all error.* messages for current locale
|
||||
window.stirlingPDF.translations = /*[[${errorMessages}]]*/ {};
|
||||
})();
|
||||
</script>
|
||||
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
||||
|
Loading…
x
Reference in New Issue
Block a user