This is an automated email from the ASF dual-hosted git repository.
adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new 52be53778d FINERACT-2326: Locale Validator shall accept all valid
language codes
52be53778d is described below
commit 52be53778db4a3b65acd1f76dce52fb28987aa72
Author: Adam Saghy <[email protected]>
AuthorDate: Mon Jul 28 17:24:14 2025 +0200
FINERACT-2326: Locale Validator shall accept all valid language codes
---
.../validation/BusinessDateValidationTest.java | 2 +-
.../validation/constraints/LocaleValidator.java | 18 ++++++++++-
.../constraints/LocaleValidationTest.java | 35 +++++++++++++---------
3 files changed, 39 insertions(+), 16 deletions(-)
diff --git
a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/validation/BusinessDateValidationTest.java
b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/validation/BusinessDateValidationTest.java
index c165f41009..761911c03a 100644
---
a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/validation/BusinessDateValidationTest.java
+++
b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/businessdate/validation/BusinessDateValidationTest.java
@@ -55,7 +55,7 @@ class BusinessDateValidationTest {
@Test
void invalidLocale() {
var request =
BusinessDateUpdateRequest.builder().dateFormat("dd-MM-yyyy").type(BusinessDateType.BUSINESS_DATE).date("12-05-2025")
- .locale("EN").build();
+ .locale("INVALID").build();
var errors = validator.validateObject(request);
diff --git
a/fineract-validation/src/main/java/org/apache/fineract/validation/constraints/LocaleValidator.java
b/fineract-validation/src/main/java/org/apache/fineract/validation/constraints/LocaleValidator.java
index fb410aec4c..664559524a 100644
---
a/fineract-validation/src/main/java/org/apache/fineract/validation/constraints/LocaleValidator.java
+++
b/fineract-validation/src/main/java/org/apache/fineract/validation/constraints/LocaleValidator.java
@@ -27,6 +27,22 @@ class LocaleValidator implements ConstraintValidator<Locale,
String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
- return StringUtils.isNotBlank(value) &&
Arrays.asList(java.util.Locale.getISOLanguages()).contains(value);
+ if (StringUtils.isBlank(value)) {
+ return false; // empty string is invalid
+ }
+
+ // Normalize input to use BCP 47 format (e.g., "en-US")
+ String languageTag = value.replace('_', '-');
+
+ java.util.Locale inputLocale =
java.util.Locale.forLanguageTag(languageTag);
+
+ // If language is empty, it's not a valid locale
+ if (inputLocale.getLanguage().isEmpty()) {
+ return false;
+ }
+
+ // Check if it matches any available locale
+ return Arrays.stream(java.util.Locale.getAvailableLocales())
+ .anyMatch(available ->
available.toLanguageTag().equalsIgnoreCase(inputLocale.toLanguageTag()));
}
}
diff --git
a/fineract-validation/src/test/java/org/apache/fineract/validation/constraints/LocaleValidationTest.java
b/fineract-validation/src/test/java/org/apache/fineract/validation/constraints/LocaleValidationTest.java
index 0de66519f9..192f599450 100644
---
a/fineract-validation/src/test/java/org/apache/fineract/validation/constraints/LocaleValidationTest.java
+++
b/fineract-validation/src/test/java/org/apache/fineract/validation/constraints/LocaleValidationTest.java
@@ -27,6 +27,8 @@ import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.validation.config.ValidationConfig;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
@@ -51,24 +53,29 @@ class LocaleValidationTest {
assertThat(errors.getFieldErrors()).anyMatch(e ->
e.getField().equals("locale"));
}
- @Test
- void invalidFormat() {
- var request = LocaleModel.builder().locale("EN").build();
-
+ @ParameterizedTest
+ @ValueSource(strings = { "invalid-locale", // invalid format
+ "xx-YY", // non-existent locale
+ "random text", // random text
+ "123", // numbers
+ "en-US-extra" // extra segment
+ })
+ void invalidFormats(String locale) {
+ var request = LocaleModel.builder().locale(locale).build();
var errors = validator.validateObject(request);
-
- assertThat(errors.getFieldErrorCount()).isEqualTo(1);
-
- assertThat(errors.getFieldErrors()).anyMatch(e ->
e.getField().equals("locale"));
+ assertThat(errors.getFieldErrorCount()).as("Expected locale '%s' to be
invalid but it was valid", locale).isGreaterThan(0);
}
- @Test
- void valid() {
- var request = LocaleModel.builder().locale("en").build();
-
+ @ParameterizedTest
+ @ValueSource(strings = { "en", // language only
+ "EN", // uppercase language only
+ "en-US", // language with country (hyphen)
+ "en_US", // language with country (underscore)
+ })
+ void validLocales(String locale) {
+ var request = LocaleModel.builder().locale(locale).build();
var errors = validator.validateObject(request);
-
- assertThat(errors.getFieldErrorCount()).isEqualTo(0);
+ assertThat(errors.getFieldErrorCount()).as("Expected locale '%s' to be
valid but it was invalid", locale).isZero();
}
@Builder