mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-26 22:29:24 +00:00
changes
This commit is contained in:
parent
558e35de62
commit
ce417469d4
@ -94,35 +94,32 @@ public class ExceptionUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a generic IOException with internationalized message.
|
* Create a generic IOException with readable English message.
|
||||||
*
|
*
|
||||||
* @param messageKey the i18n message key
|
* @param messageKey the i18n message key for frontend translation
|
||||||
* @param defaultMessage the default message if i18n is not available
|
* @param defaultMessage the English message template
|
||||||
* @param cause the original exception
|
* @param cause the original exception
|
||||||
* @param args optional arguments for the message
|
* @param args arguments for message formatting
|
||||||
* @return IOException with user-friendly message
|
* @return IOException with readable English message
|
||||||
*/
|
*/
|
||||||
public static IOException createIOException(
|
public static IOException createIOException(
|
||||||
String messageKey, String defaultMessage, Exception cause, Object... args) {
|
String messageKey, String defaultMessage, Exception cause, Object... args) {
|
||||||
String message = messageKey != null ? defaultMessage : String.format(defaultMessage, args);
|
String message = String.format(defaultMessage, args);
|
||||||
if (messageKey != null) {
|
|
||||||
return new TranslatableIOException(message, messageKey, cause, args);
|
|
||||||
}
|
|
||||||
return new IOException(message, cause);
|
return new IOException(message, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a generic RuntimeException with internationalized message.
|
* Create a generic RuntimeException with readable English message.
|
||||||
*
|
*
|
||||||
* @param messageKey the i18n message key
|
* @param messageKey the i18n message key for frontend translation
|
||||||
* @param defaultMessage the default message if i18n is not available
|
* @param defaultMessage the English message template
|
||||||
* @param cause the original exception
|
* @param cause the original exception
|
||||||
* @param args optional arguments for the message
|
* @param args arguments for message formatting
|
||||||
* @return RuntimeException with user-friendly message
|
* @return RuntimeException with readable English message
|
||||||
*/
|
*/
|
||||||
public static RuntimeException createRuntimeException(
|
public static RuntimeException createRuntimeException(
|
||||||
String messageKey, String defaultMessage, Exception cause, Object... args) {
|
String messageKey, String defaultMessage, Exception cause, Object... args) {
|
||||||
String message = messageKey != null ? defaultMessage : String.format(defaultMessage, args);
|
String message = String.format(defaultMessage, args);
|
||||||
if (messageKey != null) {
|
if (messageKey != null) {
|
||||||
return new TranslatableException(message, messageKey, args);
|
return new TranslatableException(message, messageKey, args);
|
||||||
}
|
}
|
||||||
@ -130,17 +127,16 @@ public class ExceptionUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an IllegalArgumentException with internationalized message.
|
* Create an IllegalArgumentException with readable English message.
|
||||||
*
|
*
|
||||||
* @param messageKey the i18n message key
|
* @param messageKey the i18n message key for frontend translation
|
||||||
* @param defaultMessage the default message if i18n is not available
|
* @param defaultMessage the English message template
|
||||||
* @param args optional arguments for the message
|
* @param args arguments for message formatting
|
||||||
* @return IllegalArgumentException with user-friendly message
|
* @return IllegalArgumentException with readable English message
|
||||||
*/
|
*/
|
||||||
public static IllegalArgumentException createIllegalArgumentException(
|
public static IllegalArgumentException createIllegalArgumentException(
|
||||||
String messageKey, String defaultMessage, Object... args) {
|
String messageKey, String defaultMessage, Object... args) {
|
||||||
// Only format if no translation key provided (for backwards compatibility)
|
String message = String.format(defaultMessage, args);
|
||||||
String message = messageKey != null ? defaultMessage : String.format(defaultMessage, args);
|
|
||||||
return new TranslatableException(message, messageKey, args);
|
return new TranslatableException(message, messageKey, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
package stirling.software.common.util;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* IOException that carries translation information for frontend internationalization. The
|
|
||||||
* GlobalExceptionHandler extracts this info to create structured error responses.
|
|
||||||
*/
|
|
||||||
public class TranslatableIOException extends IOException {
|
|
||||||
|
|
||||||
private final String translationKey;
|
|
||||||
private final Object[] translationArgs;
|
|
||||||
|
|
||||||
public TranslatableIOException(
|
|
||||||
String message, String translationKey, Exception cause, Object... translationArgs) {
|
|
||||||
super(message, cause);
|
|
||||||
this.translationKey = translationKey;
|
|
||||||
this.translationArgs = translationArgs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTranslationKey() {
|
|
||||||
return translationKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object[] getTranslationArgs() {
|
|
||||||
return translationArgs;
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,6 +12,7 @@ The system uses a **backend-frontend translation split**:
|
|||||||
|
|
||||||
### 1. ExceptionUtils
|
### 1. ExceptionUtils
|
||||||
Creates `TranslatableException` instances with structured translation data for frontend.
|
Creates `TranslatableException` instances with structured translation data for frontend.
|
||||||
|
Backend uses hardcoded English strings for readability.
|
||||||
|
|
||||||
### 2. GlobalExceptionHandler
|
### 2. GlobalExceptionHandler
|
||||||
Converts exceptions to structured JSON responses with translation information.
|
Converts exceptions to structured JSON responses with translation information.
|
||||||
@ -19,6 +20,9 @@ Converts exceptions to structured JSON responses with translation information.
|
|||||||
### 3. MessageFormatter.js
|
### 3. MessageFormatter.js
|
||||||
Frontend utility for translating error messages with placeholder replacement.
|
Frontend utility for translating error messages with placeholder replacement.
|
||||||
|
|
||||||
|
### 4. GlobalModelAdvice
|
||||||
|
Loads error translations using Spring's MessageSource and adds them to all templates.
|
||||||
|
|
||||||
## Usage Examples
|
## Usage Examples
|
||||||
|
|
||||||
### Basic PDF Exception Handling
|
### Basic PDF Exception Handling
|
||||||
@ -82,19 +86,19 @@ The system returns structured JSON error responses with translation support:
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Key Features:**
|
**Key Features:**
|
||||||
- `message`: English fallback for API consumers that ignore translation
|
- `message`: Readable English hardcoded in backend for API consumers
|
||||||
- `translationKey`: Frontend translation key
|
- `translationKey`: Frontend translation key
|
||||||
- `translationArgs`: Arguments for placeholder replacement
|
- `translationArgs`: Arguments for placeholder replacement
|
||||||
- API consumers can rely on `message` for backwards compatibility
|
- Backend sends English, frontend translates to user's language
|
||||||
|
|
||||||
### Frontend Translation with MessageFormatter
|
### Frontend Translation with MessageFormatter
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Translate error messages with placeholder replacement
|
// Frontend translates using Spring's loaded translation data
|
||||||
const displayMessage = window.MessageFormatter.translate(
|
const displayMessage = window.MessageFormatter.translate(
|
||||||
json.translationKey,
|
json.translationKey,
|
||||||
json.translationArgs,
|
json.translationArgs,
|
||||||
json.message // fallback to original message
|
json.message // English fallback
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -58,25 +58,6 @@ public class GlobalExceptionHandler {
|
|||||||
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
|
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExceptionHandler(stirling.software.common.util.TranslatableIOException.class)
|
|
||||||
public ResponseEntity<ErrorResponse> handleTranslatableIOException(
|
|
||||||
stirling.software.common.util.TranslatableIOException 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)
|
@ExceptionHandler(IllegalArgumentException.class)
|
||||||
public ResponseEntity<ErrorResponse> handleIllegalArgumentException(
|
public ResponseEntity<ErrorResponse> handleIllegalArgumentException(
|
||||||
IllegalArgumentException e) {
|
IllegalArgumentException e) {
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
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,61 @@
|
|||||||
|
package stirling.software.SPDF.controller.api;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.springframework.context.MessageSource;
|
||||||
|
import org.springframework.context.i18n.LocaleContextHolder;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API endpoint for on-demand error message translation.
|
||||||
|
* Provides translations for error messages when needed instead of pre-loading all translations.
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
|
public class TranslationController {
|
||||||
|
|
||||||
|
private final MessageSource messageSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get translated error message for user's locale.
|
||||||
|
*
|
||||||
|
* @param key the translation key (e.g. "error.dpiExceedsLimit")
|
||||||
|
* @param args comma-separated arguments for message formatting
|
||||||
|
* @return translated message in user's locale
|
||||||
|
*/
|
||||||
|
@GetMapping("/translate")
|
||||||
|
public ResponseEntity<String> translate(
|
||||||
|
@RequestParam String key,
|
||||||
|
@RequestParam(required = false) String args) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
Object[] messageArgs = null;
|
||||||
|
if (args != null && !args.trim().isEmpty()) {
|
||||||
|
messageArgs = Arrays.stream(args.split(","))
|
||||||
|
.map(String::trim)
|
||||||
|
.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
String translatedMessage = messageSource.getMessage(
|
||||||
|
key,
|
||||||
|
messageArgs,
|
||||||
|
LocaleContextHolder.getLocale()
|
||||||
|
);
|
||||||
|
|
||||||
|
return ResponseEntity.ok(translatedMessage);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.debug("Translation failed for key '{}': {}", key, e.getMessage());
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,79 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -345,16 +345,18 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle structured error response with translation support
|
// Handle structured error response with async translation
|
||||||
let displayMessage = json.message;
|
let displayMessage = 'Loading...'; // Brief loading state
|
||||||
|
|
||||||
// If translation info is available, use MessageFormatter to translate
|
|
||||||
if (json.translationKey && window.MessageFormatter) {
|
if (json.translationKey && window.MessageFormatter) {
|
||||||
displayMessage = window.MessageFormatter.translate(
|
// Async translation with timeout and English fallback
|
||||||
|
displayMessage = await window.MessageFormatter.translateAsync(
|
||||||
json.translationKey,
|
json.translationKey,
|
||||||
json.translationArgs,
|
json.translationArgs,
|
||||||
json.message // fallback to original message
|
json.message // English fallback
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
displayMessage = json.message; // Direct English message
|
||||||
}
|
}
|
||||||
|
|
||||||
showErrorBanner((json.error || 'Error') + ': ' + displayMessage, json.trace || '');
|
showErrorBanner((json.error || 'Error') + ': ' + displayMessage, json.trace || '');
|
||||||
|
@ -1,66 +1,51 @@
|
|||||||
/**
|
/**
|
||||||
* Utility for formatting internationalized messages with placeholder replacement.
|
* Async translation utility with timeout and English fallback.
|
||||||
* Supports the {0}, {1}, {2}... placeholder format used by Java MessageFormat.
|
* Fetches translations on-demand from API with brief loading state.
|
||||||
*/
|
*/
|
||||||
window.MessageFormatter = (function() {
|
window.MessageFormatter = (function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a message template by replacing {0}, {1}, etc. placeholders with provided arguments.
|
* Translate error message with async API call and fallback to English.
|
||||||
|
* Shows brief loading, attempts translation, falls back to English on timeout/error.
|
||||||
*
|
*
|
||||||
* @param {string} template - The message template with {0}, {1}, etc. placeholders
|
* @param {string} translationKey - The translation key
|
||||||
* @param {Array|string} args - Arguments to replace placeholders with. Can be array or individual arguments
|
* @param {Array} translationArgs - Arguments for message formatting
|
||||||
* @returns {string} The formatted message with placeholders replaced
|
* @param {string} fallbackMessage - English fallback message
|
||||||
*
|
* @param {number} timeout - Timeout in milliseconds (default: 500ms)
|
||||||
* @example
|
* @returns {Promise<string>} - Translated message or English fallback
|
||||||
* formatMessage("Hello {0}, you have {1} messages", ["John", 5])
|
|
||||||
* // Returns: "Hello John, you have 5 messages"
|
|
||||||
*
|
|
||||||
* formatMessage("Error {0}: {1}", "404", "Not Found")
|
|
||||||
* // Returns: "Error 404: Not Found"
|
|
||||||
*/
|
*/
|
||||||
function formatMessage(template, ...args) {
|
async function translateAsync(translationKey, translationArgs, fallbackMessage, timeout = 500) {
|
||||||
if (!template || typeof template !== 'string') {
|
if (!translationKey) {
|
||||||
return template || '';
|
return fallbackMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle case where first argument is an array
|
|
||||||
const argumentArray = Array.isArray(args[0]) ? args[0] : args;
|
|
||||||
|
|
||||||
// Replace {0}, {1}, {2}, etc. with corresponding arguments
|
const controller = new AbortController();
|
||||||
return template.replace(/\{(\d+)\}/g, function(match, index) {
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
||||||
const argIndex = parseInt(index, 10);
|
|
||||||
return argumentArray[argIndex] !== undefined && argumentArray[argIndex] !== null
|
try {
|
||||||
? String(argumentArray[argIndex])
|
const params = new URLSearchParams({ key: translationKey });
|
||||||
: match; // Keep original placeholder if no argument provided
|
if (translationArgs && translationArgs.length > 0) {
|
||||||
});
|
params.append('args', translationArgs.join(','));
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`${window.stirlingPDF.translationApiUrl}?${params}`, {
|
||||||
|
signal: controller.signal
|
||||||
|
});
|
||||||
|
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Translation API returned ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.text();
|
||||||
|
} catch (error) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
console.debug('Translation failed, using English fallback:', error.message);
|
||||||
|
return fallbackMessage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
return { translateAsync };
|
||||||
* Translate and format an error message using the global translation object.
|
|
||||||
* Falls back to the provided fallback message if translation not found.
|
|
||||||
*
|
|
||||||
* @param {string} translationKey - The translation key (e.g., "error.dpiExceedsLimit")
|
|
||||||
* @param {Array} translationArgs - Arguments for placeholder replacement
|
|
||||||
* @param {string} fallbackMessage - Fallback message if translation not found
|
|
||||||
* @returns {string} The translated and formatted message
|
|
||||||
*/
|
|
||||||
function translateAndFormat(translationKey, translationArgs, fallbackMessage) {
|
|
||||||
if (!window.stirlingPDF || !window.stirlingPDF.translations) {
|
|
||||||
return fallbackMessage || translationKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
const template = window.stirlingPDF.translations[translationKey];
|
|
||||||
if (!template) {
|
|
||||||
return fallbackMessage || translationKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
return formatMessage(template, translationArgs || []);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Public API
|
|
||||||
return {
|
|
||||||
format: formatMessage,
|
|
||||||
translate: translateAndFormat
|
|
||||||
};
|
|
||||||
})();
|
})();
|
@ -251,12 +251,8 @@
|
|||||||
window.stirlingPDF.uploadLimit = /*[[${@uploadLimitService.getUploadLimit()}]]*/ 0;
|
window.stirlingPDF.uploadLimit = /*[[${@uploadLimitService.getUploadLimit()}]]*/ 0;
|
||||||
window.stirlingPDF.uploadLimitExceededSingular = /*[[#{uploadLimitExceededSingular}]]*/ 'is too large. Maximum allowed size is';
|
window.stirlingPDF.uploadLimitExceededSingular = /*[[#{uploadLimitExceededSingular}]]*/ 'is too large. Maximum allowed size is';
|
||||||
window.stirlingPDF.uploadLimitExceededPlural = /*[[#{uploadLimitExceededPlural}]]*/ 'are too large. Maximum allowed size is';
|
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.';
|
// Translation API base URL for async error message translation
|
||||||
window.stirlingPDF.tryRepairMessage = /*[[#{error.tryRepair}]]*/ 'Try using the Repair PDF feature to fix corrupted files.';
|
window.stirlingPDF.translationApiUrl = '/api/translate';
|
||||||
|
|
||||||
// Translation service for dynamic error message translation
|
|
||||||
// Dynamically populated with all error.* messages for current locale
|
|
||||||
window.stirlingPDF.translations = /*[[${errorMessages}]]*/ {};
|
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user