From 8bfdb2abb5609e2fe30226e2915017cd9a949b00 Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Tue, 20 May 2025 17:42:42 +0100 Subject: [PATCH 1/7] Update home.html (#3560) # Description of Changes Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details. --- src/main/resources/templates/home.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html index 0bb9a2c06..26597ecd7 100644 --- a/src/main/resources/templates/home.html +++ b/src/main/resources/templates/home.html @@ -138,7 +138,7 @@

🔍Help us refine Stirling PDF for real-world enterprise use

If you're interested, you can book time with our team directly.

Looking forward to digging into your use cases and making Stirling PDF even better!

- Book meeting + Book meeting

Not a business and/or interested in a meeting?

@@ -232,4 +232,4 @@ - \ No newline at end of file + From b65624cf57a5a3a1a1edf822623566a7f54669b1 Mon Sep 17 00:00:00 2001 From: Ludy Date: Wed, 21 May 2025 16:41:11 +0200 Subject: [PATCH 2/7] Enforce `Locale.US` for Consistent Decimal Formatting in Byte-Size Output (#3562) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description of Changes Please provide a summary of the changes, including: - **What was changed** - Added `import java.util.Locale;` - Updated the `String.format` call in `humanReadableByteCount` to use `Locale.US` - **Why the change was made** By default, `String.format` uses the JVM’s default locale, which in some environments (e.g., Germany) formats decimals with a comma. Tests expected a dot (`.`) as the decimal separator (e.g., `"1.0 KB"`), so we force `Locale.US` to ensure consistent output across all locales. --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details. --- .../controller/web/UploadLimitService.java | 3 +- .../web/UploadLimitServiceTest.java | 79 +++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 src/test/java/stirling/software/SPDF/controller/web/UploadLimitServiceTest.java diff --git a/src/main/java/stirling/software/SPDF/controller/web/UploadLimitService.java b/src/main/java/stirling/software/SPDF/controller/web/UploadLimitService.java index 200df6d07..9a074b6e4 100644 --- a/src/main/java/stirling/software/SPDF/controller/web/UploadLimitService.java +++ b/src/main/java/stirling/software/SPDF/controller/web/UploadLimitService.java @@ -1,5 +1,6 @@ package stirling.software.SPDF.controller.web; +import java.util.Locale; import java.util.regex.Pattern; import org.springframework.beans.factory.annotation.Autowired; @@ -52,6 +53,6 @@ public class UploadLimitService { if (bytes < 1024) return bytes + " B"; int exp = (int) (Math.log(bytes) / Math.log(1024)); String pre = "KMGTPE".charAt(exp - 1) + "B"; - return String.format("%.1f %s", bytes / Math.pow(1024, exp), pre); + return String.format(Locale.US, "%.1f %s", bytes / Math.pow(1024, exp), pre); } } diff --git a/src/test/java/stirling/software/SPDF/controller/web/UploadLimitServiceTest.java b/src/test/java/stirling/software/SPDF/controller/web/UploadLimitServiceTest.java new file mode 100644 index 000000000..18bcaad59 --- /dev/null +++ b/src/test/java/stirling/software/SPDF/controller/web/UploadLimitServiceTest.java @@ -0,0 +1,79 @@ +package stirling.software.SPDF.controller.web; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import stirling.software.SPDF.model.ApplicationProperties; + +class UploadLimitServiceTest { + + private UploadLimitService uploadLimitService; + private ApplicationProperties applicationProperties; + private ApplicationProperties.System systemProps; + + @BeforeEach + void setUp() { + applicationProperties = mock(ApplicationProperties.class); + systemProps = mock(ApplicationProperties.System.class); + when(applicationProperties.getSystem()).thenReturn(systemProps); + + uploadLimitService = new UploadLimitService(); + // inject mock + try { + var field = UploadLimitService.class.getDeclaredField("applicationProperties"); + field.setAccessible(true); + field.set(uploadLimitService, applicationProperties); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @ParameterizedTest(name = "getUploadLimit case #{index}: input={0}, expected={1}") + @MethodSource("uploadLimitParams") + void shouldComputeUploadLimitCorrectly(String input, long expected) { + when(systemProps.getFileUploadLimit()).thenReturn(input); + + long result = uploadLimitService.getUploadLimit(); + assertEquals(expected, result); + } + + static Stream uploadLimitParams() { + return Stream.of( + // empty or null input yields 0 + Arguments.of(null, 0L), + Arguments.of("", 0L), + // invalid formats + Arguments.of("1234MB", 0L), + Arguments.of("5TB", 0L), + // valid formats + Arguments.of("10KB", 10 * 1024L), + Arguments.of("2MB", 2 * 1024 * 1024L), + Arguments.of("1GB", 1L * 1024 * 1024 * 1024), + Arguments.of("5mb", 5 * 1024 * 1024L), + Arguments.of("0MB", 0L)); + } + + @ParameterizedTest(name = "getReadableUploadLimit case #{index}: rawValue={0}, expected={1}") + @MethodSource("readableLimitParams") + void shouldReturnReadableFormat(String rawValue, String expected) { + when(systemProps.getFileUploadLimit()).thenReturn(rawValue); + String result = uploadLimitService.getReadableUploadLimit(); + assertEquals(expected, result); + } + + static Stream readableLimitParams() { + return Stream.of( + Arguments.of(null, "0 B"), + Arguments.of("", "0 B"), + Arguments.of("1KB", "1.0 KB"), + Arguments.of("2MB", "2.0 MB")); + } +} From cc938e175174709644fe5344ec4fab014d4a25c1 Mon Sep 17 00:00:00 2001 From: daenur Date: Wed, 21 May 2025 17:41:51 +0300 Subject: [PATCH 3/7] Ukrainian translation (#3567) Update messages_uk_UA.properties # Description of Changes Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details. --- src/main/resources/messages_uk_UA.properties | 110 +++++++++---------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/src/main/resources/messages_uk_UA.properties b/src/main/resources/messages_uk_UA.properties index 61b51c533..797e2c2c4 100644 --- a/src/main/resources/messages_uk_UA.properties +++ b/src/main/resources/messages_uk_UA.properties @@ -10,9 +10,9 @@ multiPdfPrompt=Оберіть PDFи (2+) multiPdfDropPrompt=Оберіть (або перетягніть) всі необхідні PDFи imgPrompt=Оберіть зображення(я) genericSubmit=Надіслати -uploadLimit=Maximum file size: -uploadLimitExceededSingular=is too large. Maximum allowed size is -uploadLimitExceededPlural=are too large. Maximum allowed size is +uploadLimit=Максимальний розмір файлу: +uploadLimitExceededSingular=занадто великий. Максимально дозволений розмір - +uploadLimitExceededPlural=занадто великі. Максимально дозволений розмір - processTimeWarning=Увага: Цей процес може тривати до хвилини в залежності від розміру файлу. pageOrderPrompt=Порядок сторінок (введіть список номерів сторінок через кому): pageSelectionPrompt=Користувацький вибір сторінки (введіть список номерів сторінок через кому 1,5,6 або функції типу 2n+1) : @@ -86,14 +86,14 @@ loading=Завантаження... addToDoc=Додати до документу reset=Скинути apply=Застосувати -noFileSelected=No file selected. Please upload one. +noFileSelected=Файл не вибрано. Будь ласка, завантажте один. legal.privacy=Політика конфіденційності legal.terms=Правила та умови legal.accessibility=Доступність legal.cookie=Політика використання файлів cookie legal.impressum=Вихідні дані -legal.showCookieBanner=Cookie Preferences +legal.showCookieBanner=Налаштування файлів cookie ############### # Pipeline # @@ -237,7 +237,7 @@ adminUserSettings.activeUsers=Активні користувачі: adminUserSettings.disabledUsers=Заблоковані користувачі: adminUserSettings.totalUsers=Всього користувачів: adminUserSettings.lastRequest=Останній запит -adminUserSettings.usage=View Usage +adminUserSettings.usage=Переглянути використання endpointStatistics.title=Статистика кінцевих точок endpointStatistics.header=Статистика кінцевих точок @@ -364,9 +364,9 @@ home.compressPdfs.title=Стиснути home.compressPdfs.desc=Стискайте PDF-файли, щоб зменшити їх розмір. compressPdfs.tags=стиск,маленький,крихітний -home.unlockPDFForms.title=Unlock PDF Forms -home.unlockPDFForms.desc=Remove read-only property of form fields in a PDF document. -unlockPDFForms.tags=remove,delete,form,field,readonly +home.unlockPDFForms.title=Розблокувати PDF форми +home.unlockPDFForms.desc=Видалити властивість "тільки для читання" з полів форми у PDF-документі. +unlockPDFForms.tags=видалити,розблокувати,форма,поле,тільки для читання home.changeMetadata.title=Змінити метадані home.changeMetadata.desc=Змінити/видалити/додати метадані з документа PDF @@ -609,7 +609,7 @@ login.userIsDisabled=Користувач деактивовано, вхід з login.alreadyLoggedIn=Ви вже увійшли до login.alreadyLoggedIn2=пристроїв (а). Будь ласка, вийдіть із цих пристроїв і спробуйте знову. login.toManySessions=У вас дуже багато активних сесій -login.logoutMessage=You have been logged out. +login.logoutMessage=Ви вийшли з системи. #auto-redact autoRedact.title=Автоматичне редагування @@ -742,10 +742,10 @@ sanitizePDF.title=Дезінфекція PDF sanitizePDF.header=Дезінфекція PDF файлу sanitizePDF.selectText.1=Видалити JavaScript sanitizePDF.selectText.2=Видалити вбудовані файли -sanitizePDF.selectText.3=Remove XMP metadata +sanitizePDF.selectText.3=Видалити XMP метадані sanitizePDF.selectText.4=Видалити посилання sanitizePDF.selectText.5=Видалити шрифти -sanitizePDF.selectText.6=Remove Document Info Metadata +sanitizePDF.selectText.6=Видалити метадані інформації про документ sanitizePDF.submit=Дезінфекція @@ -1071,7 +1071,7 @@ rotate.submit=Повернути split.title=Розділити PDF split.header=Розділити PDF split.desc.1=Числа, які ви вибрали, це номери сторінок, на яких ви хочете зробити розділ. -split.desc.2=Таким чином, вибір 1,3,7-8 розділить 10-сторінковий документ на 6 окремих PDF-файлів з: +split.desc.2=Таким чином, вибір 1,3,7-8 розділіть 10-сторінковий документ на 6 окремих PDF-файлів з: split.desc.3=Документ #1: Сторінка 1 split.desc.4=Документ #2: Сторінки 2 і 3 split.desc.5=Документ #3: Сторінки 4, 5 і 6 @@ -1372,68 +1372,68 @@ fileChooser.extractPDF=Видобування... #release notes releases.footer=Релізи -releases.title=Примечания к релизу -releases.header=Примечания к релизу -releases.current.version=Текущий релиз -releases.note=Примітка до релізу доступна тільки на англійській мові +releases.title=Примітки до релізу +releases.header=Примітки до релізу +releases.current.version=Поточний реліз +releases.note=Примітки до релізу доступні лише англійською мовою #Validate Signature validateSignature.title=Перевірка підписів PDF validateSignature.header=Перевірка цифрових підписів validateSignature.selectPDF=Виберіть підписаний PDF-файл validateSignature.submit=Перевірити підписи -validateSignature.results=Результаты проверки +validateSignature.results=Результати перевірки validateSignature.status=Статус validateSignature.signer=Підписант validateSignature.date=Дата validateSignature.reason=Причина -validateSignature.location=Местоположение -validateSignature.noSignatures=В цьому документі не знайдено цифрових підписів -validateSignature.status.valid=Дійна -validateSignature.status.invalid=Недійсна -validateSignature.chain.invalid=Перевірка цепочки сертифікатів не удалась - неможливо перевірити особистість підписанта +validateSignature.location=Місцезнаходження +validateSignature.noSignatures=У цьому документі не знайдено цифрових підписів +validateSignature.status.valid=Дійсний +validateSignature.status.invalid=Недійсний +validateSignature.chain.invalid=Перевірка ланцюга сертифікатів не вдалася - неможливо перевірити особу підписанта validateSignature.trust.invalid=Сертифікат відсутній у довіреному сховищі - джерело не може бути перевірено -validateSignature.cert.expired=Срок дії сертифіката істеку -validateSignature.cert.revoked=Сертифікат був отозван -validateSignature.signature.info=Інформація про підписи -validateSignature.signature=Подпись -validateSignature.signature.mathValid=Подпись математически корректна, НО: -validateSignature.selectCustomCert=Користувачський файл сертифіката X.509 (Необов'язково) -validateSignature.cert.info=Сведения про сертифікати -validateSignature.cert.issuer=Издатель -validateSignature.cert.subject=суб'єкт -validateSignature.cert.serialNumber=Серийний номер +validateSignature.cert.expired=Термін дії сертифіката закінчився +validateSignature.cert.revoked=Сертифікат було відкликано +validateSignature.signature.info=Інформація про підпис +validateSignature.signature=Підпис +validateSignature.signature.mathValid=Підпис математично коректний, АЛЕ: +validateSignature.selectCustomCert=Користувацький файл сертифіката X.509 (Необов'язково) +validateSignature.cert.info=Інформація про сертифікат +validateSignature.cert.issuer=Видавець +validateSignature.cert.subject=Суб'єкт +validateSignature.cert.serialNumber=Серійний номер validateSignature.cert.validFrom=Дійсний з validateSignature.cert.validUntil=Дійсний до validateSignature.cert.algorithm=Алгоритм validateSignature.cert.keySize=Розмір ключа validateSignature.cert.version=Версія validateSignature.cert.keyUsage=Використання ключа -validateSignature.cert.selfSigned=Самоподписанный +validateSignature.cert.selfSigned=Самопідписаний validateSignature.cert.bits=біт #################### # Cookie banner # #################### -cookieBanner.popUp.title=How we use Cookies -cookieBanner.popUp.description.1=We use cookies and other technologies to make Stirling PDF work better for you—helping us improve our tools and keep building features you'll love. -cookieBanner.popUp.description.2=If you’d rather not, clicking 'No Thanks' will only enable the essential cookies needed to keep things running smoothly. -cookieBanner.popUp.acceptAllBtn=Okay -cookieBanner.popUp.acceptNecessaryBtn=No Thanks -cookieBanner.popUp.showPreferencesBtn=Manage preferences -cookieBanner.preferencesModal.title=Consent Preferences Center -cookieBanner.preferencesModal.acceptAllBtn=Accept all -cookieBanner.preferencesModal.acceptNecessaryBtn=Reject all -cookieBanner.preferencesModal.savePreferencesBtn=Save preferences -cookieBanner.preferencesModal.closeIconLabel=Close modal -cookieBanner.preferencesModal.serviceCounterLabel=Service|Services -cookieBanner.preferencesModal.subtitle=Cookie Usage -cookieBanner.preferencesModal.description.1=Stirling PDF uses cookies and similar technologies to enhance your experience and understand how our tools are used. This helps us improve performance, develop the features you care about, and provide ongoing support to our users. -cookieBanner.preferencesModal.description.2=Stirling PDF cannot—and will never—track or access the content of the documents you use. -cookieBanner.preferencesModal.description.3=Your privacy and trust are at the core of what we do. -cookieBanner.preferencesModal.necessary.title.1=Strictly Necessary Cookies -cookieBanner.preferencesModal.necessary.title.2=Always Enabled -cookieBanner.preferencesModal.necessary.description=These cookies are essential for the website to function properly. They enable core features like setting your privacy preferences, logging in, and filling out forms—which is why they can’t be turned off. -cookieBanner.preferencesModal.analytics.title=Analytics -cookieBanner.preferencesModal.analytics.description=These cookies help us understand how our tools are being used, so we can focus on building the features our community values most. Rest assured—Stirling PDF cannot and will never track the content of the documents you work with. +cookieBanner.popUp.title=Як ми використовуємо файли cookie +cookieBanner.popUp.description.1=Ми використовуємо файли cookie та інші технології, щоб Stirling PDF працював краще для вас — допомагаючи нам покращувати наші інструменти та створювати функції, які вам сподобаються. +cookieBanner.popUp.description.2=Якщо ви не хочете, натискання «Ні, дякую» увімкне лише необхідні файли cookie, потрібні для безперебійної роботи. +cookieBanner.popUp.acceptAllBtn=Добре +cookieBanner.popUp.acceptNecessaryBtn=Ні, дякую +cookieBanner.popUp.showPreferencesBtn=Керувати налаштуваннями +cookieBanner.preferencesModal.title=Центр налаштувань згоди +cookieBanner.preferencesModal.acceptAllBtn=Прийняти всі +cookieBanner.preferencesModal.acceptNecessaryBtn=Відхилити всі +cookieBanner.preferencesModal.savePreferencesBtn=Зберегти налаштування +cookieBanner.preferencesModal.closeIconLabel=Закрити модальне вікно +cookieBanner.preferencesModal.serviceCounterLabel=Сервіс|Сервіси +cookieBanner.preferencesModal.subtitle=Використання файлів cookie +cookieBanner.preferencesModal.description.1=Stirling PDF використовує файли cookie та подібні технології, щоб покращити ваш досвід і зрозуміти, як використовуються наші інструменти. Це допомагає нам покращувати продуктивність, розробляти функції, які вас цікавлять, і надавати постійну підтримку нашим користувачам. +cookieBanner.preferencesModal.description.2=Stirling PDF не може — і ніколи не буде — відстежувати або отримувати доступ до вмісту документів, які ви використовуєте. +cookieBanner.preferencesModal.description.3=Ваша конфіденційність і довіра є основою того, що ми робимо. +cookieBanner.preferencesModal.necessary.title.1=Суворо необхідні файли cookie +cookieBanner.preferencesModal.necessary.title.2=Завжди увімкнені +cookieBanner.preferencesModal.necessary.description=Ці файли cookie є необхідними для правильного функціонування вебсайту. Вони забезпечують основні функції, такі як налаштування ваших уподобань конфіденційності, вхід у систему та заповнення форм — тому їх не можна вимкнути. +cookieBanner.preferencesModal.analytics.title=Аналітика +cookieBanner.preferencesModal.analytics.description=Ці файли cookie допомагають нам зрозуміти, як використовуються наші інструменти, щоб ми могли зосередитися на створенні функцій, які найбільше цінує наша спільнота. Будьте впевнені — Stirling PDF не може і ніколи не буде відстежувати вміст документів, з якими ви працюєте. From 35304a1491bb6a615282c8ebc0328d9920228db3 Mon Sep 17 00:00:00 2001 From: Ludy Date: Wed, 21 May 2025 16:42:08 +0200 Subject: [PATCH 4/7] Enhance email error handling and expand test coverage (#3561) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description of Changes Please provide a summary of the changes, including: - **What was changed** - **EmailController**: Added a `catch (MailSendException)` block to handle invalid-address errors, log the exception, and return a 500 response with the raw error message. - **EmailServiceTest**: Added unit tests for attachment-related error cases (missing filename, null filename, missing file, null file) and invalid “to” address (null or empty), expecting `MessagingException` or `MailSendException`. - **MailConfigTest**: New test class verifying `MailConfig.java` correctly initializes `JavaMailSenderImpl` with host, port, username, password, default encoding, and SMTP properties. - **EmailControllerTest**: Refactored into a parameterized test (`shouldHandleEmailRequests`) covering four scenarios: success, generic messaging error, missing `to` parameter, and invalid address formatting. - **Why the change was made** - To ensure invalid email addresses and missing attachments are handled gracefully at the controller layer, providing clearer feedback to API clients. - To improve overall test coverage and guard against regressions in email functionality. - To enforce correct mail configuration via automated tests. --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details. --- .../config/security/mail/EmailService.java | 15 ++- .../SPDF/controller/api/EmailController.java | 6 + .../security/mail/EmailServiceTest.java | 109 +++++++++++++++++ .../config/security/mail/MailConfigTest.java | 54 +++++++++ .../controller/api/EmailControllerTest.java | 111 ++++++++++-------- 5 files changed, 243 insertions(+), 52 deletions(-) create mode 100644 src/test/java/stirling/software/SPDF/config/security/mail/MailConfigTest.java diff --git a/src/main/java/stirling/software/SPDF/config/security/mail/EmailService.java b/src/main/java/stirling/software/SPDF/config/security/mail/EmailService.java index 8939fbab6..507e51599 100644 --- a/src/main/java/stirling/software/SPDF/config/security/mail/EmailService.java +++ b/src/main/java/stirling/software/SPDF/config/security/mail/EmailService.java @@ -37,8 +37,21 @@ public class EmailService { */ @Async public void sendEmailWithAttachment(Email email) throws MessagingException { - ApplicationProperties.Mail mailProperties = applicationProperties.getMail(); MultipartFile file = email.getFileInput(); + // 1) Validate recipient email address + if (email.getTo() == null || email.getTo().trim().isEmpty()) { + throw new MessagingException("Invalid Addresses"); + } + + // 2) Validate attachment + if (file == null + || file.isEmpty() + || file.getOriginalFilename() == null + || file.getOriginalFilename().isEmpty()) { + throw new MessagingException("An attachment is required to send the email."); + } + + ApplicationProperties.Mail mailProperties = applicationProperties.getMail(); // Creates a MimeMessage to represent the email MimeMessage message = mailSender.createMimeMessage(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/EmailController.java b/src/main/java/stirling/software/SPDF/controller/api/EmailController.java index 6f7dd3867..dc1c9dff4 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/EmailController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/EmailController.java @@ -3,6 +3,7 @@ package stirling.software.SPDF.controller.api; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.mail.MailSendException; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -53,6 +54,11 @@ public class EmailController { // Calls the service to send the email with attachment emailService.sendEmailWithAttachment(email); return ResponseEntity.ok("Email sent successfully"); + } catch (MailSendException ex) { + // handles your "Invalid Addresses" case + String errorMsg = ex.getMessage(); + log.error("MailSendException: {}", errorMsg, ex); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMsg); } catch (MessagingException e) { // Catches any messaging exception (e.g., invalid email address, SMTP server issues) String errorMsg = "Failed to send email: " + e.getMessage(); diff --git a/src/test/java/stirling/software/SPDF/config/security/mail/EmailServiceTest.java b/src/test/java/stirling/software/SPDF/config/security/mail/EmailServiceTest.java index 777b2b658..1781eb1bb 100644 --- a/src/test/java/stirling/software/SPDF/config/security/mail/EmailServiceTest.java +++ b/src/test/java/stirling/software/SPDF/config/security/mail/EmailServiceTest.java @@ -1,5 +1,7 @@ package stirling.software.SPDF.config.security.mail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.*; import org.junit.jupiter.api.Test; @@ -57,4 +59,111 @@ public class EmailServiceTest { // Verify that the email was sent using mailSender verify(mailSender).send(mimeMessage); } + + @Test + void testSendEmailWithAttachmentThrowsExceptionForMissingFilename() throws MessagingException { + Email email = new Email(); + email.setTo("test@example.com"); + email.setSubject("Test Email"); + email.setBody("This is a test email."); + email.setFileInput(fileInput); + + when(fileInput.isEmpty()).thenReturn(false); + when(fileInput.getOriginalFilename()).thenReturn(""); + + try { + emailService.sendEmailWithAttachment(email); + fail("Expected MessagingException to be thrown"); + } catch (MessagingException e) { + assertEquals("An attachment is required to send the email.", e.getMessage()); + } + } + + @Test + void testSendEmailWithAttachmentThrowsExceptionForMissingFilenameNull() + throws MessagingException { + Email email = new Email(); + email.setTo("test@example.com"); + email.setSubject("Test Email"); + email.setBody("This is a test email."); + email.setFileInput(fileInput); + + when(fileInput.isEmpty()).thenReturn(false); + when(fileInput.getOriginalFilename()).thenReturn(null); + + try { + emailService.sendEmailWithAttachment(email); + fail("Expected MessagingException to be thrown"); + } catch (MessagingException e) { + assertEquals("An attachment is required to send the email.", e.getMessage()); + } + } + + @Test + void testSendEmailWithAttachmentThrowsExceptionForMissingFile() throws MessagingException { + Email email = new Email(); + email.setTo("test@example.com"); + email.setSubject("Test Email"); + email.setBody("This is a test email."); + email.setFileInput(fileInput); + + when(fileInput.isEmpty()).thenReturn(true); + + try { + emailService.sendEmailWithAttachment(email); + fail("Expected MessagingException to be thrown"); + } catch (MessagingException e) { + assertEquals("An attachment is required to send the email.", e.getMessage()); + } + } + + @Test + void testSendEmailWithAttachmentThrowsExceptionForMissingFileNull() throws MessagingException { + Email email = new Email(); + email.setTo("test@example.com"); + email.setSubject("Test Email"); + email.setBody("This is a test email."); + email.setFileInput(null); // Missing file + + try { + emailService.sendEmailWithAttachment(email); + fail("Expected MessagingException to be thrown"); + } catch (MessagingException e) { + assertEquals("An attachment is required to send the email.", e.getMessage()); + } + } + + @Test + void testSendEmailWithAttachmentThrowsExceptionForInvalidAddressNull() + throws MessagingException { + Email email = new Email(); + email.setTo(null); // Invalid address + email.setSubject("Test Email"); + email.setBody("This is a test email."); + email.setFileInput(fileInput); + + try { + emailService.sendEmailWithAttachment(email); + fail("Expected MailSendException to be thrown"); + } catch (MessagingException e) { + assertEquals("Invalid Addresses", e.getMessage()); + } + } + + @Test + void testSendEmailWithAttachmentThrowsExceptionForInvalidAddressEmpty() + throws MessagingException { + Email email = new Email(); + email.setTo(""); // Invalid address + email.setSubject("Test Email"); + email.setBody("This is a test email."); + email.setFileInput(fileInput); + + try { + emailService.sendEmailWithAttachment(email); + fail("Expected MailSendException to be thrown"); + } catch (MessagingException e) { + assertEquals("Invalid Addresses", e.getMessage()); + } + } } diff --git a/src/test/java/stirling/software/SPDF/config/security/mail/MailConfigTest.java b/src/test/java/stirling/software/SPDF/config/security/mail/MailConfigTest.java new file mode 100644 index 000000000..2e47f14e3 --- /dev/null +++ b/src/test/java/stirling/software/SPDF/config/security/mail/MailConfigTest.java @@ -0,0 +1,54 @@ +package stirling.software.SPDF.config.security.mail; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Properties; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.JavaMailSenderImpl; + +import stirling.software.SPDF.model.ApplicationProperties; + +class MailConfigTest { + + private ApplicationProperties.Mail mailProps; + + @BeforeEach + void initMailProperties() { + mailProps = mock(ApplicationProperties.Mail.class); + when(mailProps.getHost()).thenReturn("smtp.example.com"); + when(mailProps.getPort()).thenReturn(587); + when(mailProps.getUsername()).thenReturn("user@example.com"); + when(mailProps.getPassword()).thenReturn("password"); + } + + @Test + void shouldConfigureJavaMailSenderWithCorrectProperties() { + ApplicationProperties appProps = mock(ApplicationProperties.class); + when(appProps.getMail()).thenReturn(mailProps); + + MailConfig config = new MailConfig(appProps); + JavaMailSender sender = config.javaMailSender(); + + assertInstanceOf(JavaMailSenderImpl.class, sender); + JavaMailSenderImpl impl = (JavaMailSenderImpl) sender; + + Properties props = impl.getJavaMailProperties(); + + assertAll( + "SMTP configuration", + () -> assertEquals("smtp.example.com", impl.getHost()), + () -> assertEquals(587, impl.getPort()), + () -> assertEquals("user@example.com", impl.getUsername()), + () -> assertEquals("password", impl.getPassword()), + () -> assertEquals("UTF-8", impl.getDefaultEncoding()), + () -> assertEquals("true", props.getProperty("mail.smtp.auth")), + () -> assertEquals("true", props.getProperty("mail.smtp.starttls.enable"))); + } +} diff --git a/src/test/java/stirling/software/SPDF/controller/api/EmailControllerTest.java b/src/test/java/stirling/software/SPDF/controller/api/EmailControllerTest.java index d33f3c947..dfd68e069 100644 --- a/src/test/java/stirling/software/SPDF/controller/api/EmailControllerTest.java +++ b/src/test/java/stirling/software/SPDF/controller/api/EmailControllerTest.java @@ -1,18 +1,25 @@ package stirling.software.SPDF.controller.api; -import static org.mockito.Mockito.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.mail.MailSendException; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.multipart.MultipartFile; import jakarta.mail.MessagingException; @@ -20,7 +27,7 @@ import stirling.software.SPDF.config.security.mail.EmailService; import stirling.software.SPDF.model.api.Email; @ExtendWith(MockitoExtension.class) -public class EmailControllerTest { +class EmailControllerTest { private MockMvc mockMvc; @@ -28,59 +35,61 @@ public class EmailControllerTest { @InjectMocks private EmailController emailController; - @Mock private MultipartFile fileInput; - @BeforeEach void setUp() { - // Set up the MockMvc instance for testing mockMvc = MockMvcBuilders.standaloneSetup(emailController).build(); } - @Test - void testSendEmailWithAttachmentSuccess() throws Exception { - // Create a mock Email object - Email email = new Email(); - email.setTo("test@example.com"); - email.setSubject("Test Email"); - email.setBody("This is a test email."); - email.setFileInput(fileInput); + @ParameterizedTest(name = "Case {index}: exception={0}, includeTo={1}") + @MethodSource("emailParams") + void shouldHandleEmailRequests( + Exception serviceException, + boolean includeTo, + int expectedStatus, + String expectedContent) + throws Exception { + if (serviceException == null) { + doNothing().when(emailService).sendEmailWithAttachment(any(Email.class)); + } else { + doThrow(serviceException).when(emailService).sendEmailWithAttachment(any(Email.class)); + } - // Mock the service to not throw any exception - doNothing().when(emailService).sendEmailWithAttachment(any(Email.class)); + var request = + multipart("/api/v1/general/send-email") + .file("fileInput", "dummy-content".getBytes()) + .param("subject", "Test Email") + .param("body", "This is a test email."); - // Perform the request and verify the response - mockMvc.perform( - multipart("/api/v1/general/send-email") - .file("fileInput", "dummy-content".getBytes()) - .param("to", email.getTo()) - .param("subject", email.getSubject()) - .param("body", email.getBody())) - .andExpect(status().isOk()) - .andExpect(content().string("Email sent successfully")); + if (includeTo) { + request = request.param("to", "test@example.com"); + } + + mockMvc.perform(request) + .andExpect(status().is(expectedStatus)) + .andExpect(content().string(expectedContent)); } - @Test - void testSendEmailWithAttachmentFailure() throws Exception { - // Create a mock Email object - Email email = new Email(); - email.setTo("test@example.com"); - email.setSubject("Test Email"); - email.setBody("This is a test email."); - email.setFileInput(fileInput); - - // Mock the service to throw a MessagingException - doThrow(new MessagingException("Failed to send email")) - .when(emailService) - .sendEmailWithAttachment(any(Email.class)); - - // Perform the request and verify the response - mockMvc.perform( - multipart("/api/v1/general/send-email") - .file("fileInput", "dummy-content".getBytes()) - .param("to", email.getTo()) - .param("subject", email.getSubject()) - .param("body", email.getBody())) - .andExpect(status().isInternalServerError()) - .andExpect(content().string("Failed to send email: Failed to send email")); + static Stream emailParams() { + return Stream.of( + // success case + Arguments.of(null, true, 200, "Email sent successfully"), + // generic messaging error + Arguments.of( + new MessagingException("Failed to send email"), + true, + 500, + "Failed to send email: Failed to send email"), + // missing 'to' results in MailSendException + Arguments.of( + new MailSendException("Invalid Addresses"), + false, + 500, + "Invalid Addresses"), + // invalid email address formatting + Arguments.of( + new MessagingException("Invalid Addresses"), + true, + 500, + "Failed to send email: Invalid Addresses")); } } From adcfe629f2ee02db56bc692a401e6028cf026578 Mon Sep 17 00:00:00 2001 From: daenur Date: Thu, 22 May 2025 12:44:14 +0300 Subject: [PATCH 5/7] Russian translation (#3572) Update messages_ru_RU.properties # Description of Changes Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details. --- src/main/resources/messages_ru_RU.properties | 136 +++++++++---------- 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/src/main/resources/messages_ru_RU.properties b/src/main/resources/messages_ru_RU.properties index 24743d85f..f337ff6af 100644 --- a/src/main/resources/messages_ru_RU.properties +++ b/src/main/resources/messages_ru_RU.properties @@ -10,9 +10,9 @@ multiPdfPrompt=Выберите PDF-файлы (2+) multiPdfDropPrompt=Выберите (или перетащите) все необходимые PDF-файлы imgPrompt=Выберите изображение(я) genericSubmit=Отправить -uploadLimit=Maximum file size: -uploadLimitExceededSingular=is too large. Maximum allowed size is -uploadLimitExceededPlural=are too large. Maximum allowed size is +uploadLimit=Максимальный размер файла: +uploadLimitExceededSingular=слишком велик. Максимально допустимый размер - +uploadLimitExceededPlural=слишком велики. Максимально допустимый размер - processTimeWarning=Внимание: Данный процесс может занять до минуты в зависимости от размера файла pageOrderPrompt=Пользовательский порядок страниц (Введите список номеров страниц через запятую или функции типа 2n+1): pageSelectionPrompt=Выбор страниц (Введите список номеров страниц через запятую 1,5,6 или функции типа 2n+1): @@ -86,14 +86,14 @@ loading=Загрузка... addToDoc=Добавить в документ reset=Сбросить apply=Применить -noFileSelected=No file selected. Please upload one. +noFileSelected=Файл не выбран. Пожалуйста, загрузите его. legal.privacy=Политика конфиденциальности legal.terms=Условия использования legal.accessibility=Доступность legal.cookie=Политика использования файлов cookie legal.impressum=Выходные данные -legal.showCookieBanner=Cookie Preferences +legal.showCookieBanner=Настройки файлов cookie ############### # Pipeline # @@ -237,7 +237,7 @@ adminUserSettings.activeUsers=Активные пользователи: adminUserSettings.disabledUsers=Отключенные пользователи: adminUserSettings.totalUsers=Всего пользователей: adminUserSettings.lastRequest=Последний запрос -adminUserSettings.usage=View Usage +adminUserSettings.usage=Просмотр использования endpointStatistics.title=Статистика конечных точек endpointStatistics.header=Статистика конечных точек @@ -292,18 +292,18 @@ home.desc=Ваше локальное решение для всех потре home.searchBar=Поиск функций... -home.viewPdf.title=View/Edit PDF +home.viewPdf.title=Просмотр/Редактирование PDF home.viewPdf.desc=Просмотр, аннотирование, добавление текста или изображений viewPdf.tags=просмотр,чтение,аннотации,текст,изображение -home.setFavorites=Set Favourites -home.hideFavorites=Hide Favourites -home.showFavorites=Show Favourites -home.legacyHomepage=Old homepage -home.newHomePage=Try our new homepage! -home.alphabetical=Alphabetical -home.globalPopularity=Global Popularity -home.sortBy=Sort by: +home.setFavorites=Добавить в избранное +home.hideFavorites=Скрыть избранное +home.showFavorites=Показать избранное +home.legacyHomepage=Старая главная страница +home.newHomePage=Попробуйте нашу новую главную страницу! +home.alphabetical=По алфавиту +home.globalPopularity=Глобальная популярность +home.sortBy=Сортировать по: home.multiTool.title=Мультиинструмент PDF home.multiTool.desc=Объединение, поворот, переупорядочивание и удаление страниц @@ -364,9 +364,9 @@ home.compressPdfs.title=Сжать home.compressPdfs.desc=Сжимайте PDF-файлы для уменьшения их размера. compressPdfs.tags=сжатие,маленький,крошечный -home.unlockPDFForms.title=Unlock PDF Forms -home.unlockPDFForms.desc=Remove read-only property of form fields in a PDF document. -unlockPDFForms.tags=remove,delete,form,field,readonly +home.unlockPDFForms.title=Разблокировать формы PDF +home.unlockPDFForms.desc=Удалить свойство только для чтения из полей формы в PDF-документе. +unlockPDFForms.tags=удалить,удаление,форма,поле,только для чтения home.changeMetadata.title=Изменить метаданные home.changeMetadata.desc=Изменить/удалить/добавить метаданные из PDF-документа @@ -494,9 +494,9 @@ home.MarkdownToPDF.title=Markdown в PDF home.MarkdownToPDF.desc=Преобразует любой файл Markdown в PDF MarkdownToPDF.tags=разметка,веб-контент,преобразование,конвертация -home.PDFToMarkdown.title=PDF to Markdown -home.PDFToMarkdown.desc=Converts any PDF to Markdown -PDFToMarkdown.tags=markup,web-content,transformation,convert,md +home.PDFToMarkdown.title=PDF в Markdown +home.PDFToMarkdown.desc=Конвертирует любой PDF в Markdown +PDFToMarkdown.tags=разметка,веб-контент,преобразование,конвертировать,md home.getPdfInfo.title=Получить ВСЮ информацию о PDF home.getPdfInfo.desc=Собирает всю возможную информацию о PDF @@ -609,7 +609,7 @@ login.userIsDisabled=Пользователь деактивирован, вхо login.alreadyLoggedIn=Вы уже вошли в login.alreadyLoggedIn2=устройств(а). Пожалуйста, выйдите из этих устройств и попробуйте снова. login.toManySessions=У вас слишком много активных сессий -login.logoutMessage=You have been logged out. +login.logoutMessage=Вы вышли из системы. #auto-redact autoRedact.title=Автоматическое редактирование @@ -648,7 +648,7 @@ redact.showAttatchments=Показать вложения redact.showLayers=Показать слои (двойной щелчок для сброса всех слоев к состоянию по умолчанию) redact.colourPicker=Выбор цвета redact.findCurrentOutlineItem=Найти текущий элемент структуры -redact.applyChanges=Apply Changes +redact.applyChanges=Применить изменения #showJS showJS.title=Показать Javascript @@ -686,9 +686,9 @@ MarkdownToPDF.credit=Использует WeasyPrint #pdf-to-markdown -PDFToMarkdown.title=PDF To Markdown -PDFToMarkdown.header=PDF To Markdown -PDFToMarkdown.submit=Convert +PDFToMarkdown.title=PDF в Markdown +PDFToMarkdown.header=PDF в Markdown +PDFToMarkdown.submit=Конвертировать #url-to-pdf @@ -742,10 +742,10 @@ sanitizePDF.title=Очистить PDF sanitizePDF.header=Очистить PDF-файл sanitizePDF.selectText.1=Удалить JavaScript-действия sanitizePDF.selectText.2=Удалить встроенные файлы -sanitizePDF.selectText.3=Remove XMP metadata +sanitizePDF.selectText.3=Удалить метаданные XMP sanitizePDF.selectText.4=Удалить ссылки sanitizePDF.selectText.5=Удалить шрифты -sanitizePDF.selectText.6=Remove Document Info Metadata +sanitizePDF.selectText.6=Удалить метаданные информации о документе sanitizePDF.submit=Очистить PDF @@ -894,8 +894,8 @@ sign.last=Последняя страница sign.next=Следующая страница sign.previous=Предыдущая страница sign.maintainRatio=Переключить сохранение пропорций -sign.undo=Undo -sign.redo=Redo +sign.undo=Отменить +sign.redo=Повторить #repair repair.title=Восстановление @@ -966,8 +966,8 @@ compress.title=Сжать compress.header=Сжать PDF compress.credit=Этот сервис использует qpdf для сжатия/оптимизации PDF. compress.grayscale.label=Применить шкалу серого для сжатия -compress.selectText.1=Compression Settings -compress.selectText.1.1=1-3 PDF compression,
4-6 lite image compression,
7-9 intense image compression Will dramatically reduce image quality +compress.selectText.1=Настройки сжатия +compress.selectText.1.1=1-3 Сжатие PDF,
4-6 легкое сжатие изображений,
7-9 интенсивное сжатие изображений Существенно снижает качество изображений compress.selectText.2=Уровень оптимизации: compress.selectText.4=Автоматический режим - автоматически настраивает качество для получения точного размера PDF compress.selectText.5=Ожидаемый размер PDF (например, 25MB, 10.8MB, 25KB) @@ -1006,7 +1006,7 @@ pdfOrganiser.mode.7=Удалить первую pdfOrganiser.mode.8=Удалить последнюю pdfOrganiser.mode.9=Удалить первую и последнюю pdfOrganiser.mode.10=Объединение четных-нечетных -pdfOrganiser.mode.11=Duplicate all pages +pdfOrganiser.mode.11=Дублировать все страницы pdfOrganiser.placeholder=(например, 1,3,2 или 4-8,2,10-12 или 2n-1) @@ -1049,7 +1049,7 @@ decrypt.success=Файл успешно расшифрован. multiTool-advert.message=Эта функция также доступна на нашей странице мультиинструмента. Попробуйте её для улучшенного постраничного интерфейса и дополнительных возможностей! #view pdf -viewPdf.title=View/Edit PDF +viewPdf.title=Просмотр/Редактирование PDF viewPdf.header=Просмотр PDF #pageRemover @@ -1191,15 +1191,15 @@ changeMetadata.keywords=Ключевые слова: changeMetadata.modDate=Дата изменения (yyyy/MM/dd HH:mm:ss): changeMetadata.producer=Производитель: changeMetadata.subject=Тема: -changeMetadata.trapped=Trapped: +changeMetadata.trapped=Захвачено: changeMetadata.selectText.4=Другие метаданные: changeMetadata.selectText.5=Добавить пользовательскую запись метаданных changeMetadata.submit=Изменить #unlockPDFForms -unlockPDFForms.title=Remove Read-Only from Form Fields -unlockPDFForms.header=Unlock PDF Forms -unlockPDFForms.submit=Remove +unlockPDFForms.title=Удалить только для чтения из полей формы +unlockPDFForms.header=Разблокировать формы PDF +unlockPDFForms.submit=Удалить #pdfToPDFA pdfToPDFA.title=PDF в PDF/A @@ -1319,15 +1319,15 @@ survey.please=Пожалуйста, примите участие в нашем survey.disabled=(Всплывающее окно опроса будет отключено в следующих обновлениях, но будет доступно в нижней части страницы) survey.button=Пройти опрос survey.dontShowAgain=Больше не показывать -survey.meeting.1=If you're using Stirling PDF at work, we'd love to speak to you. We're offering technical support sessions in exchange for a 15 minute user discovery session. -survey.meeting.2=This is a chance to: -survey.meeting.3=Get help with deployment, integrations, or troubleshooting -survey.meeting.4=Provide direct feedback on performance, edge cases, and feature gaps -survey.meeting.5=Help us refine Stirling PDF for real-world enterprise use -survey.meeting.6=If you're interested, you can book time with our team directly. (English speaking only) -survey.meeting.7=Looking forward to digging into your use cases and making Stirling PDF even better! -survey.meeting.notInterested=Not a business and/or interested in a meeting? -survey.meeting.button=Book meeting +survey.meeting.1=Если вы используете Stirling PDF на работе, мы будем рады поговорить с вами. Мы предлагаем сеансы технической поддержки в обмен на 15-минутную сессию по изучению пользователей. +survey.meeting.2=Это возможность: +survey.meeting.3=Получить помощь с развертыванием, интеграцией или устранением неполадок +survey.meeting.4=Предоставить прямую обратную связь о производительности, крайних случаях и пробелах в функциях +survey.meeting.5=Помочь нам улучшить Stirling PDF для реального использования в корпоративной среде +survey.meeting.6=Если вы заинтересованы, вы можете записаться на встречу с нашей командой напрямую. (Только на английском языке) +survey.meeting.7=С нетерпением ждем возможности изучить ваши случаи использования и сделать Stirling PDF еще лучше! +survey.meeting.notInterested=Не являетесь бизнесом и/или не заинтересованы во встрече? +survey.meeting.button=Записаться на встречу #error error.sorry=Извините за неполадки! @@ -1415,25 +1415,25 @@ validateSignature.cert.bits=бит #################### # Cookie banner # #################### -cookieBanner.popUp.title=How we use Cookies -cookieBanner.popUp.description.1=We use cookies and other technologies to make Stirling PDF work better for you—helping us improve our tools and keep building features you'll love. -cookieBanner.popUp.description.2=If you’d rather not, clicking 'No Thanks' will only enable the essential cookies needed to keep things running smoothly. -cookieBanner.popUp.acceptAllBtn=Okay -cookieBanner.popUp.acceptNecessaryBtn=No Thanks -cookieBanner.popUp.showPreferencesBtn=Manage preferences -cookieBanner.preferencesModal.title=Consent Preferences Center -cookieBanner.preferencesModal.acceptAllBtn=Accept all -cookieBanner.preferencesModal.acceptNecessaryBtn=Reject all -cookieBanner.preferencesModal.savePreferencesBtn=Save preferences -cookieBanner.preferencesModal.closeIconLabel=Close modal -cookieBanner.preferencesModal.serviceCounterLabel=Service|Services -cookieBanner.preferencesModal.subtitle=Cookie Usage -cookieBanner.preferencesModal.description.1=Stirling PDF uses cookies and similar technologies to enhance your experience and understand how our tools are used. This helps us improve performance, develop the features you care about, and provide ongoing support to our users. -cookieBanner.preferencesModal.description.2=Stirling PDF cannot—and will never—track or access the content of the documents you use. -cookieBanner.preferencesModal.description.3=Your privacy and trust are at the core of what we do. -cookieBanner.preferencesModal.necessary.title.1=Strictly Necessary Cookies -cookieBanner.preferencesModal.necessary.title.2=Always Enabled -cookieBanner.preferencesModal.necessary.description=These cookies are essential for the website to function properly. They enable core features like setting your privacy preferences, logging in, and filling out forms—which is why they can’t be turned off. -cookieBanner.preferencesModal.analytics.title=Analytics -cookieBanner.preferencesModal.analytics.description=These cookies help us understand how our tools are being used, so we can focus on building the features our community values most. Rest assured—Stirling PDF cannot and will never track the content of the documents you work with. +cookieBanner.popUp.title=Как мы используем файлы cookie +cookieBanner.popUp.description.1=Мы используем файлы cookie и другие технологии, чтобы Stirling PDF работал лучше для вас — помогая нам улучшать наши инструменты и добавлять функции, которые вам понравятся. +cookieBanner.popUp.description.2=Если вы не хотите, нажав «Нет, спасибо», вы включите только основные файлы cookie, необходимые для бесперебойной работы. +cookieBanner.popUp.acceptAllBtn=Хорошо +cookieBanner.popUp.acceptNecessaryBtn=Нет, спасибо +cookieBanner.popUp.showPreferencesBtn=Управление предпочтениями +cookieBanner.preferencesModal.title=Центр управления предпочтениями +cookieBanner.preferencesModal.acceptAllBtn=Принять все +cookieBanner.preferencesModal.acceptNecessaryBtn=Отклонить все +cookieBanner.preferencesModal.savePreferencesBtn=Сохранить предпочтения +cookieBanner.preferencesModal.closeIconLabel=Закрыть окно +cookieBanner.preferencesModal.serviceCounterLabel=Сервис|Сервисы +cookieBanner.preferencesModal.subtitle=Использование файлов cookie +cookieBanner.preferencesModal.description.1=Stirling PDF использует файлы cookie и аналогичные технологии, чтобы улучшить ваш опыт и понять, как используются наши инструменты. Это помогает нам улучшать производительность, разрабатывать функции, которые важны для нашего сообщества, и предоставлять постоянную поддержку нашим пользователям. +cookieBanner.preferencesModal.description.2=Stirling PDF не может — и никогда не будет — отслеживать или получать доступ к содержимому документов, которые вы используете. +cookieBanner.preferencesModal.description.3=Ваша конфиденциальность и доверие — в основе того, что мы делаем. +cookieBanner.preferencesModal.necessary.title.1=Строго необходимые файлы cookie +cookieBanner.preferencesModal.necessary.title.2=Всегда включены +cookieBanner.preferencesModal.necessary.description=Эти файлы cookie необходимы для правильной работы веб-сайта. Они включают основные функции, такие как установка ваших предпочтений конфиденциальности, вход в систему и заполнение форм — поэтому их нельзя отключить. +cookieBanner.preferencesModal.analytics.title=Аналитика +cookieBanner.preferencesModal.analytics.description=Эти файлы cookie помогают нам понять, как используются наши инструменты, чтобы мы могли сосредоточиться на создании функций, которые ценит наше сообщество. Будьте уверены — Stirling PDF не может и никогда не будет отслеживать содержимое документов, с которыми вы работаете. From 75c325d15a459691b310ea021e135114b67a63bb Mon Sep 17 00:00:00 2001 From: Ludy Date: Fri, 23 May 2025 11:50:54 +0200 Subject: [PATCH 6/7] Update messages_de_DE.properties (#3575) # Description of Changes Please provide a summary of the changes, including: --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/main/resources/messages_de_DE.properties | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/resources/messages_de_DE.properties b/src/main/resources/messages_de_DE.properties index 363fd8e45..600944bd6 100644 --- a/src/main/resources/messages_de_DE.properties +++ b/src/main/resources/messages_de_DE.properties @@ -364,9 +364,9 @@ home.compressPdfs.title=Komprimieren home.compressPdfs.desc=PDF komprimieren um die Dateigröße zu reduzieren compressPdfs.tags=komprimieren,verkleinern,minimieren -home.unlockPDFForms.title=Unlock PDF Forms -home.unlockPDFForms.desc=Remove read-only property of form fields in a PDF document. -unlockPDFForms.tags=remove,delete,form,field,readonly +home.unlockPDFForms.title=Schreibgeschützte PDF-Formfelder entfernen +home.unlockPDFForms.desc=Entfernen Sie die schreibgeschützte Eigenschaft von Formularfeldern in einem PDF-Dokument. +unlockPDFForms.tags=entfernen,löschen,form,feld,schreibgeschützt home.changeMetadata.title=Metadaten ändern home.changeMetadata.desc=Ändern/Entfernen/Hinzufügen von Metadaten aus einem PDF-Dokument @@ -1197,9 +1197,9 @@ changeMetadata.selectText.5=Benutzerdefinierten Metadateneintrag hinzufügen changeMetadata.submit=Ändern #unlockPDFForms -unlockPDFForms.title=Remove Read-Only from Form Fields -unlockPDFForms.header=Unlock PDF Forms -unlockPDFForms.submit=Remove +unlockPDFForms.title=Entfernen Sie schreibgeschützte Formfelder +unlockPDFForms.header=Schreibgeschützte PDF-Formfelder entfernen +unlockPDFForms.submit=Entfernen #pdfToPDFA pdfToPDFA.title=PDF zu PDF/A From f2f11496a24bed44a453c99bd8ad980c417087af Mon Sep 17 00:00:00 2001 From: Ludy Date: Fri, 23 May 2025 23:22:05 +0200 Subject: [PATCH 7/7] Fix Chinese localization split page numbering (#3574) # Description of Changes Please provide a summary of the changes, including: - **What was changed** Updated the values of `split.desc.6`, `split.desc.7`, and `split.desc.8` in `src/main/resources/messages_zh_CN.properties` to correct the page numbers: - **Why the change was made** The previous numbering was inconsistent and would have led to incorrect split outputs in the Chinese UI. This ensures that users splitting a document see the correct page ranges. - **Translation Method** The correction of these translation strings was generated and verified using AI assistance. Closes #3529 --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details. --- src/main/resources/messages_zh_CN.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/messages_zh_CN.properties b/src/main/resources/messages_zh_CN.properties index e04c77fbd..e3dd78dcf 100644 --- a/src/main/resources/messages_zh_CN.properties +++ b/src/main/resources/messages_zh_CN.properties @@ -1075,9 +1075,9 @@ split.desc.2=如选择1,3,7-9将把一个 10 页的文件分割成6个独立的P split.desc.3=文档 #1:第 1 页 split.desc.4=文档 #2:第 2 页和第 3 页 split.desc.5=文档 #3:第 4 页、第 5 页、第 6 页和第 7 页 -split.desc.6=文档 #4:第 7 页 -split.desc.7=文档 #5:第 8 页 -split.desc.8=文档 #6:第 9 页和第 10 页 +split.desc.6=文档 #4:第 8 页 +split.desc.7=文档 #5:第 9 页 +split.desc.8=文档 #6:第 10 页 split.splitPages=输入要分割的页面: split.submit=拆分