diff --git a/CHANGELOG.md b/CHANGELOG.md
index bcbdcf85fd..640d9c3299 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- [Core] Upload Cucumber Reports with Gzip encoding ([#3115](https://github.com/cucumber/cucumber-jvm/pull/3115))
+- [Java] Any custom method declared as `@DefaultParameterTransformer` can now have a `Locale` argument to optionally consider the locale declaration of the current Feature ([cucumber/cucumber-expressions#376](https://github.com/cucumber/cucumber-expressions/issues/376) Stefan Gasterstädt)
## [7.32.0] - 2025-11-21
### Changed
diff --git a/cucumber-java/src/main/java/io/cucumber/java/DefaultParameterTransformer.java b/cucumber-java/src/main/java/io/cucumber/java/DefaultParameterTransformer.java
index 1986fe5fa9..c0e5507f38 100644
--- a/cucumber-java/src/main/java/io/cucumber/java/DefaultParameterTransformer.java
+++ b/cucumber-java/src/main/java/io/cucumber/java/DefaultParameterTransformer.java
@@ -14,8 +14,11 @@
*
* - {@code String, Type -> Object}
* - {@code Object, Type -> Object}
+ * - {@code String, Type, Locale -> Object}
+ * - {@code Object, Type, Locale -> Object}
*
*
+ * @see io.cucumber.cucumberexpressions.LocaleParameterByTypeTransformer
* @see io.cucumber.cucumberexpressions.ParameterByTypeTransformer
* @see io.cucumber.cucumberexpressions.ParameterType
*/
diff --git a/cucumber-java/src/main/java/io/cucumber/java/JavaDefaultParameterTransformerDefinition.java b/cucumber-java/src/main/java/io/cucumber/java/JavaDefaultParameterTransformerDefinition.java
index 873e03562d..f1b1faa11c 100644
--- a/cucumber-java/src/main/java/io/cucumber/java/JavaDefaultParameterTransformerDefinition.java
+++ b/cucumber-java/src/main/java/io/cucumber/java/JavaDefaultParameterTransformerDefinition.java
@@ -2,10 +2,11 @@
import io.cucumber.core.backend.DefaultParameterTransformerDefinition;
import io.cucumber.core.backend.Lookup;
-import io.cucumber.cucumberexpressions.ParameterByTypeTransformer;
+import io.cucumber.cucumberexpressions.LocaleParameterByTypeTransformer;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
+import java.util.Locale;
import static io.cucumber.java.InvalidMethodSignatureException.builder;
@@ -13,12 +14,22 @@ class JavaDefaultParameterTransformerDefinition extends AbstractGlueDefinition
implements DefaultParameterTransformerDefinition {
private final Lookup lookup;
- private final ParameterByTypeTransformer transformer;
+ private final LocaleParameterByTypeTransformer transformer;
JavaDefaultParameterTransformerDefinition(Method method, Lookup lookup) {
super(requireValidMethod(method), lookup);
this.lookup = lookup;
- this.transformer = this::execute;
+ this.transformer = new LocaleParameterByTypeTransformer() {
+ @Override
+ public Object transform(String fromValue, Type toValueType) throws Throwable {
+ return this.transform(fromValue, toValueType, null);
+ }
+
+ @Override
+ public Object transform(String fromValue, Type toValueType, Locale locale) throws Throwable {
+ return JavaDefaultParameterTransformerDefinition.this.execute(fromValue, toValueType, locale);
+ }
+ };
}
private static Method requireValidMethod(Method method) {
@@ -28,7 +39,7 @@ private static Method requireValidMethod(Method method) {
}
Class>[] parameterTypes = method.getParameterTypes();
- if (parameterTypes.length != 2) {
+ if ((parameterTypes.length != 2) && (parameterTypes.length != 3)) {
throw createInvalidSignatureException(method);
}
@@ -40,11 +51,20 @@ private static Method requireValidMethod(Method method) {
throw createInvalidSignatureException(method);
}
+ if ((parameterTypes.length == 3) && !Locale.class.equals(parameterTypes[2])) {
+ throw createInvalidSignatureException(method);
+ }
return method;
}
- private Object execute(String fromValue, Type toValueType) {
- return Invoker.invoke(this, lookup.getInstance(method.getDeclaringClass()), method, fromValue, toValueType);
+ private Object execute(String fromValue, Type toValueType, Locale locale) {
+ Object[] args;
+ if (method.getParameterTypes().length == 3) {
+ args = new Object[] { fromValue, toValueType, locale };
+ } else {
+ args = new Object[] { fromValue, toValueType };
+ }
+ return invokeMethod(args);
}
private static InvalidMethodSignatureException createInvalidSignatureException(Method method) {
@@ -52,11 +72,13 @@ private static InvalidMethodSignatureException createInvalidSignatureException(M
.addAnnotation(DefaultParameterTransformer.class)
.addSignature("public Object defaultDataTableEntry(String fromValue, Type toValueType)")
.addSignature("public Object defaultDataTableEntry(Object fromValue, Type toValueType)")
+ .addSignature("public Object defaultDataTableEntry(String fromValue, Type toValueType, Locale locale)")
+ .addSignature("public Object defaultDataTableEntry(Object fromValue, Type toValueType, Locale locale)")
.build();
}
@Override
- public ParameterByTypeTransformer parameterByTypeTransformer() {
+ public LocaleParameterByTypeTransformer parameterByTypeTransformer() {
return transformer;
}
diff --git a/cucumber-java/src/test/java/io/cucumber/java/JavaDefaultParameterTransformerDefinitionTest.java b/cucumber-java/src/test/java/io/cucumber/java/JavaDefaultParameterTransformerDefinitionTest.java
index fe7b184eec..13b06b6c5b 100644
--- a/cucumber-java/src/test/java/io/cucumber/java/JavaDefaultParameterTransformerDefinitionTest.java
+++ b/cucumber-java/src/test/java/io/cucumber/java/JavaDefaultParameterTransformerDefinitionTest.java
@@ -5,6 +5,7 @@
import java.lang.reflect.Method;
import java.lang.reflect.Type;
+import java.util.Locale;
import java.util.Map;
import static org.hamcrest.CoreMatchers.startsWith;
@@ -33,10 +34,36 @@ void can_transform_string_to_type() throws Throwable {
assertThat(transformed, is("transform_string_to_type"));
}
+ @Test
+ void can_transform_string_to_type_ignoring_locale() throws Throwable {
+ Method method = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("transform_string_to_type",
+ String.class, Type.class);
+ JavaDefaultParameterTransformerDefinition definition = new JavaDefaultParameterTransformerDefinition(method,
+ lookup);
+ Object transformed = definition.parameterByTypeTransformer().transform("something", String.class,
+ Locale.ENGLISH);
+ assertThat(transformed, is("transform_string_to_type"));
+ }
+
public Object transform_string_to_type(String fromValue, Type toValueType) {
return "transform_string_to_type";
}
+ @Test
+ void can_transform_string_to_type_using_locale() throws Throwable {
+ Method method = JavaDefaultParameterTransformerDefinitionTest.class.getMethod(
+ "transform_string_to_type_with_locale", String.class, Type.class, Locale.class);
+ JavaDefaultParameterTransformerDefinition definition = new JavaDefaultParameterTransformerDefinition(method,
+ lookup);
+ Object transformed = definition.parameterByTypeTransformer().transform("something", String.class,
+ Locale.ENGLISH);
+ assertThat(transformed, is("transform_string_to_type_with_locale_en"));
+ }
+
+ public Object transform_string_to_type_with_locale(String fromValue, Type toValueType, Locale locale) {
+ return "transform_string_to_type_with_locale_" + locale.getLanguage();
+ }
+
@Test
void can_transform_object_to_type() throws Throwable {
Method method = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("transform_object_to_type",
@@ -47,10 +74,36 @@ void can_transform_object_to_type() throws Throwable {
assertThat(transformed, is("transform_object_to_type"));
}
+ @Test
+ void can_transform_object_to_type_ignoring_locale() throws Throwable {
+ Method method = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("transform_object_to_type",
+ Object.class, Type.class);
+ JavaDefaultParameterTransformerDefinition definition = new JavaDefaultParameterTransformerDefinition(method,
+ lookup);
+ String transformed = (String) definition.parameterByTypeTransformer().transform("something", String.class,
+ Locale.ENGLISH);
+ assertThat(transformed, is("transform_object_to_type"));
+ }
+
public Object transform_object_to_type(Object fromValue, Type toValueType) {
return "transform_object_to_type";
}
+ @Test
+ void can_transform_object_to_type_using_locale() throws Throwable {
+ Method method = JavaDefaultParameterTransformerDefinitionTest.class.getMethod(
+ "transform_object_to_type_with_locale", Object.class, Type.class, Locale.class);
+ JavaDefaultParameterTransformerDefinition definition = new JavaDefaultParameterTransformerDefinition(method,
+ lookup);
+ String transformed = (String) definition.parameterByTypeTransformer().transform("something", String.class,
+ Locale.ENGLISH);
+ assertThat(transformed, is("transform_object_to_type_with_locale_en"));
+ }
+
+ public Object transform_object_to_type_with_locale(Object fromValue, Type toValueType, Locale locale) {
+ return "transform_object_to_type_with_locale_" + locale.getLanguage();
+ }
+
@Test
void must_have_non_void_return() throws Throwable {
Method method = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("transforms_string_to_void",
@@ -61,6 +114,8 @@ void must_have_non_void_return() throws Throwable {
"A @DefaultParameterTransformer annotated method must have one of these signatures:\n" +
" * public Object defaultDataTableEntry(String fromValue, Type toValueType)\n" +
" * public Object defaultDataTableEntry(Object fromValue, Type toValueType)\n" +
+ " * public Object defaultDataTableEntry(String fromValue, Type toValueType, Locale locale)\n" +
+ " * public Object defaultDataTableEntry(Object fromValue, Type toValueType, Locale locale)\n" +
"at io.cucumber.java.JavaDefaultParameterTransformerDefinitionTest.transforms_string_to_void(java.lang.String,java.lang.reflect.Type)"));
}
@@ -68,28 +123,32 @@ public void transforms_string_to_void(String fromValue, Type toValueType) {
}
@Test
- void must_have_two_arguments() throws Throwable {
+ void must_have_two_or_three_arguments() throws Throwable {
Method oneArg = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("one_argument", String.class);
assertThrows(InvalidMethodSignatureException.class,
() -> new JavaDefaultParameterTransformerDefinition(oneArg, lookup));
- Method threeArg = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("three_arguments", String.class,
- Type.class, Object.class);
+ Method fourArg = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("four_arguments", String.class,
+ Type.class, Locale.class, Object.class);
assertThrows(InvalidMethodSignatureException.class,
- () -> new JavaDefaultParameterTransformerDefinition(threeArg, lookup));
+ () -> new JavaDefaultParameterTransformerDefinition(fourArg, lookup));
}
public Object one_argument(String fromValue) {
- return "one_arguments";
+ return "one_argument";
}
- public Object three_arguments(String fromValue, Type toValueType, Object extra) {
- return "three_arguments";
+ public Object four_arguments(String fromValue, Type toValueType, Locale locale, Object extra) {
+ return "four_arguments";
}
@Test
void must_have_string_or_object_as_from_value() throws Throwable {
- Method threeArg = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("map_as_from_value", Map.class,
+ Method twoArg = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("map_as_from_value", Map.class,
Type.class);
+ assertThrows(InvalidMethodSignatureException.class,
+ () -> new JavaDefaultParameterTransformerDefinition(twoArg, lookup));
+ Method threeArg = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("map_as_from_value_with_locale",
+ Map.class, Type.class, Locale.class);
assertThrows(InvalidMethodSignatureException.class,
() -> new JavaDefaultParameterTransformerDefinition(threeArg, lookup));
}
@@ -98,10 +157,18 @@ public Object map_as_from_value(Map fromValue, Type toValueType)
return "map_as_from_value";
}
+ public Object map_as_from_value_with_locale(Map fromValue, Type toValueType, Locale locale) {
+ return "map_as_from_value_with_locale";
+ }
+
@Test
void must_have_type_as_to_value_type() throws Throwable {
- Method threeArg = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("object_as_to_value_type",
+ Method twoArg = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("object_as_to_value_type",
String.class, Object.class);
+ assertThrows(InvalidMethodSignatureException.class,
+ () -> new JavaDefaultParameterTransformerDefinition(twoArg, lookup));
+ Method threeArg = JavaDefaultParameterTransformerDefinitionTest.class.getMethod(
+ "object_as_to_value_type_with_locale", String.class, Object.class, Locale.class);
assertThrows(InvalidMethodSignatureException.class,
() -> new JavaDefaultParameterTransformerDefinition(threeArg, lookup));
}
@@ -110,4 +177,8 @@ public Object object_as_to_value_type(String fromValue, Object toValueType) {
return "object_as_to_value_type";
}
+ public Object object_as_to_value_type_with_locale(String fromValue, Object toValueType, Locale locale) {
+ return "object_as_to_value_type_with_locale";
+ }
+
}