This is an automated email from the ASF dual-hosted git repository.
hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git
The following commit(s) were added to refs/heads/main by this push:
new 953d1c02ea Search on EN properties too when using different language,
fixes #2633 (#7375)
953d1c02ea is described below
commit 953d1c02ea76a986aac961e7b078dc0d314f2bf9
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Tue Jun 30 14:58:19 2026 +0200
Search on EN properties too when using different language, fixes #2633
(#7375)
---
.../apache/hop/core/plugins/BasePluginType.java | 184 ++++++++++++++++----
.../java/org/apache/hop/core/plugins/IPlugin.java | 21 +++
.../java/org/apache/hop/core/plugins/Plugin.java | 19 +++
.../plugins/BasePluginTypeEnglishKeywordsTest.java | 187 +++++++++++++++++++++
.../englishsearch/messages/messages.properties | 19 +++
.../messages/messages_fr_FR.properties | 19 +++
.../hopgui/file/pipeline/HopGuiPipelineGraph.java | 2 +
.../pipeline/context/HopGuiPipelineContext.java | 4 +
.../hopgui/file/workflow/HopGuiWorkflowGraph.java | 2 +
.../workflow/context/HopGuiWorkflowContext.java | 2 +
10 files changed, 423 insertions(+), 36 deletions(-)
diff --git a/core/src/main/java/org/apache/hop/core/plugins/BasePluginType.java
b/core/src/main/java/org/apache/hop/core/plugins/BasePluginType.java
index c6e7e06562..4be3a06f31 100644
--- a/core/src/main/java/org/apache/hop/core/plugins/BasePluginType.java
+++ b/core/src/main/java/org/apache/hop/core/plugins/BasePluginType.java
@@ -31,12 +31,18 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import lombok.Getter;
+import lombok.Setter;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
@@ -49,6 +55,7 @@ import org.apache.hop.core.util.Utils;
import org.apache.hop.core.xml.XmlHandler;
import org.apache.hop.i18n.BaseMessages;
import org.apache.hop.i18n.GlobalMessageUtil;
+import org.apache.hop.i18n.LanguageChoice;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.IndexView;
@@ -60,9 +67,9 @@ public abstract class BasePluginType<T extends Annotation>
implements IPluginTyp
protected final PluginRegistry registry;
- private String id;
+ @Setter private String id;
- private String name;
+ @Setter private String name;
private final LogChannel log;
@@ -70,7 +77,7 @@ public abstract class BasePluginType<T extends Annotation>
implements IPluginTyp
private final Class<T> pluginClass;
- private List<String> extraLibraryFolders;
+ @Setter @Getter private List<String> extraLibraryFolders;
public BasePluginType(Class<T> pluginClazz) {
this.log = new LogChannel("Plugin type");
@@ -187,13 +194,6 @@ public abstract class BasePluginType<T extends Annotation>
implements IPluginTyp
return id;
}
- /**
- * @param id the id to set
- */
- public void setId(String id) {
- this.id = id;
- }
-
/**
* @return the name
*/
@@ -202,13 +202,6 @@ public abstract class BasePluginType<T extends Annotation>
implements IPluginTyp
return name;
}
- /**
- * @param name the name to set
- */
- public void setName(String name) {
- this.name = name;
- }
-
protected static String getCodedTranslation(String codedString) {
if (codedString == null) {
return null;
@@ -295,6 +288,134 @@ public abstract class BasePluginType<T extends
Annotation> implements IPluginTyp
}
}
+ /**
+ * Builds the English-locale search aliases for a plugin: its name, category
and keywords resolved
+ * against the English (failover) locale, with anything that already matches
in the active locale
+ * removed. The context dialog adds these to a transform/action's searchable
keywords so a user
+ * can find it by its English term even when the UI runs in another language
(issue #2633).
+ *
+ * @return the deduplicated English aliases, or an empty array when the UI
is already English
+ */
+ protected String[] extractEnglishSearchKeywords(
+ T annotation,
+ String packageName,
+ Class<?> clazz,
+ String localizedName,
+ String localizedCategory,
+ String[] localizedKeywords) {
+
+ // When the UI is already English the localized strings are the English
strings: nothing to add.
+ //
+ Locale defaultLocale = LanguageChoice.getInstance().getDefaultLocale();
+ if (defaultLocale == null
+ || GlobalMessageUtil.FAILOVER_LOCALE
+ .getLanguage()
+ .equalsIgnoreCase(defaultLocale.getLanguage())) {
+ return new String[0];
+ }
+
+ // The terms we already match on in the active locale, used to avoid
redundant aliases.
+ //
+ Set<String> alreadyMatched = new LinkedHashSet<>();
+ addSearchTerm(alreadyMatched, localizedName);
+ addSearchTerm(alreadyMatched, localizedCategory);
+ if (localizedKeywords != null) {
+ for (String keyword : localizedKeywords) {
+ addSearchTerm(alreadyMatched, keyword);
+ }
+ }
+
+ // Resolve the same annotation values against English and keep only the
genuinely new terms.
+ //
+ Set<String> aliases = new LinkedHashSet<>();
+ collectEnglishAlias(
+ aliases,
+ alreadyMatched,
+ getEnglishTranslation(extractName(annotation), packageName, clazz));
+ collectEnglishAlias(
+ aliases,
+ alreadyMatched,
+ getEnglishTranslation(extractCategory(annotation), packageName,
clazz));
+ String[] englishKeywords =
+ getEnglishTranslations(extractKeywords(annotation), packageName,
clazz);
+ if (englishKeywords != null) {
+ for (String keyword : englishKeywords) {
+ collectEnglishAlias(aliases, alreadyMatched, keyword);
+ }
+ }
+
+ return aliases.toArray(new String[0]);
+ }
+
+ private static void addSearchTerm(Set<String> terms, String term) {
+ if (StringUtils.isNotEmpty(term)) {
+ terms.add(term.toLowerCase());
+ }
+ }
+
+ private static void collectEnglishAlias(
+ Set<String> aliases, Set<String> alreadyMatched, String englishTerm) {
+ if (StringUtils.isNotEmpty(englishTerm)
+ && !alreadyMatched.contains(englishTerm.toLowerCase())) {
+ aliases.add(englishTerm);
+ }
+ }
+
+ /** Mirrors {@link #getTranslations} but resolves every entry against the
English locale. */
+ protected static String[] getEnglishTranslations(
+ String[] strings, String packageName, Class<?> resourceClass) {
+ if (strings == null) {
+ return null;
+ }
+ String[] translations = new String[strings.length];
+ for (int i = 0; i < translations.length; i++) {
+ translations[i] = getEnglishTranslation(strings[i], packageName,
resourceClass);
+ }
+ return translations;
+ }
+
+ /**
+ * Resolves an annotation value against the English (failover) locale,
mirroring {@link
+ * #getTranslation} but without consulting the active locale. Returns {@code
null} when an
+ * i18n-coded key cannot be resolved so the raw {@code i18n:...} code never
leaks into the search
+ * keywords.
+ */
+ protected static String getEnglishTranslation(
+ String string, String packageName, Class<?> resourceClass) {
+ if (string == null) {
+ return null;
+ }
+
+ if (string.startsWith(Const.I18N_PREFIX)) {
+ String[] parts = string.split(":");
+ if (parts.length != 3) {
+ return string;
+ }
+ String i18nPackage = StringUtils.isEmpty(parts[1]) ? packageName :
parts[1];
+ return lookupEnglishString(i18nPackage, parts[2], resourceClass);
+ }
+
+ if (Utils.isEmpty(packageName)) {
+ // Translations are not supported, simply keep the original text.
+ return string;
+ }
+ String english = lookupEnglishString(packageName, string, resourceClass);
+ return english != null ? english : string;
+ }
+
+ private static String lookupEnglishString(
+ String i18nPackage, String key, Class<?> resourceClass) {
+ try {
+ ResourceBundle bundle =
+ GlobalMessageUtil.getBundle(
+ GlobalMessageUtil.FAILOVER_LOCALE, i18nPackage +
".messages.messages", resourceClass);
+ return bundle.getString(key);
+ } catch (MissingResourceException e) {
+ // No English resource for this key: leave it to the caller to fall back.
+ return null;
+ }
+ }
+
protected List<PluginClassFile> findAnnotatedClassFiles(String
annotationClassName)
throws HopPluginException {
JarCache cache = JarCache.getInstance();
@@ -322,7 +443,7 @@ public abstract class BasePluginType<T extends Annotation>
implements IPluginTyp
new PluginClassFile(className, jarFile.toURI().toURL(),
folder.toURI().toURL()));
} catch (Exception e) {
- System.out.println(
+ LogChannel.GENERAL.logError(
"Error searching annotation for " + pluginClass + " in " +
jarFile);
}
}
@@ -395,7 +516,7 @@ public abstract class BasePluginType<T extends Annotation>
implements IPluginTyp
"jar", "JAR",
},
true);
- jarFiles.stream().forEach(file -> files.add(file.getPath()));
+ jarFiles.forEach(file -> files.add(file.getPath()));
}
}
return files;
@@ -421,7 +542,7 @@ public abstract class BasePluginType<T extends Annotation>
implements IPluginTyp
"jar", "JAR",
},
true);
- jarFiles.stream().forEach(file -> files.add(file.getAbsolutePath()));
+ jarFiles.forEach(file -> files.add(file.getAbsolutePath()));
}
}
}
@@ -752,6 +873,13 @@ public abstract class BasePluginType<T extends Annotation>
implements IPluginTyp
plugin.setSupportedEngines(supportedEngines);
plugin.setExcludedEngines(excludedEngines);
+ // English-locale search aliases so the context dialog finds a
transform/action by its original
+ // English name, category or keywords even when the UI runs in another
language (issue #2633).
+ //
+ plugin.setEnglishKeywords(
+ extractEnglishSearchKeywords(
+ annotation, packageName, clazz, pluginName, category, keywords));
+
ParentFirst parentFirstAnnotation = clazz.getAnnotation(ParentFirst.class);
if (parentFirstAnnotation != null) {
registry.addParentClassLoaderPatterns(plugin,
parentFirstAnnotation.patterns());
@@ -768,22 +896,6 @@ public abstract class BasePluginType<T extends Annotation>
implements IPluginTyp
}
}
- /**
- * Gets extraLibraryFolders
- *
- * @return value of extraLibraryFolders
- */
- public List<String> getExtraLibraryFolders() {
- return extraLibraryFolders;
- }
-
- /**
- * @param extraLibraryFolders The extraLibraryFolders to set
- */
- public void setExtraLibraryFolders(List<String> extraLibraryFolders) {
- this.extraLibraryFolders = extraLibraryFolders;
- }
-
/**
* Register an extra plugin from the classpath. Useful for testing.
*
diff --git a/core/src/main/java/org/apache/hop/core/plugins/IPlugin.java
b/core/src/main/java/org/apache/hop/core/plugins/IPlugin.java
index 057b142cce..b2c0c5ee21 100644
--- a/core/src/main/java/org/apache/hop/core/plugins/IPlugin.java
+++ b/core/src/main/java/org/apache/hop/core/plugins/IPlugin.java
@@ -227,6 +227,27 @@ public interface IPlugin {
// intentional no-op default
}
+ /**
+ * English-locale aliases for this plugin's name, category and keywords.
These let the context
+ * dialog match a transform/action by its original English term even when
the UI runs in another
+ * language (issue #2633). Empty when the UI is already English (the
localized values are the
+ * English ones) or for external implementations that don't participate.
+ *
+ * <p>Default returns an empty array so existing external {@link IPlugin}
implementations stay
+ * binary-compatible.
+ */
+ default String[] getEnglishKeywords() {
+ return new String[0];
+ }
+
+ /**
+ * Setter counterpart to {@link #getEnglishKeywords()}. Default no-op so
external {@link IPlugin}
+ * implementers don't have to participate — concrete plugins ({@link
Plugin}) override this.
+ */
+ default void setEnglishKeywords(String[] englishKeywords) {
+ // intentional no-op default
+ }
+
/**
* A flag to indicate that the plugin needs libraries outside of the plugin
folder
*
diff --git a/core/src/main/java/org/apache/hop/core/plugins/Plugin.java
b/core/src/main/java/org/apache/hop/core/plugins/Plugin.java
index 54f347fc43..e9c7b3c53d 100644
--- a/core/src/main/java/org/apache/hop/core/plugins/Plugin.java
+++ b/core/src/main/java/org/apache/hop/core/plugins/Plugin.java
@@ -62,6 +62,7 @@ public class Plugin implements IPlugin, Comparable<Plugin> {
private String[] supportedEngines = new String[0];
private String[] excludedEngines = new String[0];
+ private String[] englishKeywords = new String[0];
public Plugin(
String[] ids,
@@ -607,6 +608,24 @@ public class Plugin implements IPlugin, Comparable<Plugin>
{
this.keywords = keywords;
}
+ /**
+ * Gets the English-locale search aliases (name, category and keywords).
+ *
+ * @return value of englishKeywords
+ */
+ @Override
+ public String[] getEnglishKeywords() {
+ return englishKeywords;
+ }
+
+ /**
+ * @param englishKeywords The English-locale search aliases to set
+ */
+ @Override
+ public void setEnglishKeywords(String[] englishKeywords) {
+ this.englishKeywords = englishKeywords;
+ }
+
/**
* Gets usingLibrariesOutsidePluginFolder
*
diff --git
a/engine/src/test/java/org/apache/hop/core/plugins/BasePluginTypeEnglishKeywordsTest.java
b/engine/src/test/java/org/apache/hop/core/plugins/BasePluginTypeEnglishKeywordsTest.java
new file mode 100644
index 0000000000..f2fb03801a
--- /dev/null
+++
b/engine/src/test/java/org/apache/hop/core/plugins/BasePluginTypeEnglishKeywordsTest.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.core.plugins;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import org.apache.hop.core.annotations.Transform;
+import org.apache.hop.i18n.LanguageChoice;
+import org.apache.hop.junit.rules.RestoreHopEnvironmentExtension;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+/**
+ * Unit tests for the English-locale search aliases added by {@link
BasePluginType} (issue #2633).
+ * When the UI runs in a non-English language, a transform/action must still
be findable in the
+ * context dialog by its original English name, category and keywords.
+ *
+ * <p>The tests drive the real {@link TransformPluginType} (a {@link
BasePluginType} subclass) with
+ * a synthetic {@code @Transform} fixture whose i18n keys point at the test
bundle {@code
+ * org/apache/hop/core/plugins/englishsearch/messages/messages*.properties}
(English base + {@code
+ * _fr_FR}).
+ */
+@ExtendWith(RestoreHopEnvironmentExtension.class)
+class BasePluginTypeEnglishKeywordsTest {
+
+ private static final String NAME_KEY =
+ "i18n:org.apache.hop.core.plugins.englishsearch:EnglishSearch.Name";
+ private static final String CATEGORY_KEY =
+ "i18n:org.apache.hop.core.plugins.englishsearch:EnglishSearch.Category";
+ private static final String KEYWORDS_KEY =
+ "i18n:org.apache.hop.core.plugins.englishsearch:EnglishSearch.Keywords";
+
+ private Locale originalLocale;
+
+ @BeforeEach
+ void rememberLocale() {
+ originalLocale = LanguageChoice.getInstance().getDefaultLocale();
+ }
+
+ @AfterEach
+ void restoreLocale() {
+ LanguageChoice.getInstance().setDefaultLocale(originalLocale);
+ }
+
+ @Test
+ void frenchLocaleAddsEnglishAliasesForNameCategoryAndKeywords() {
+ LanguageChoice.getInstance().setDefaultLocale(Locale.FRENCH);
+
+ // Localized terms differ from English, so all three English variants must
be added as aliases.
+ List<String> aliases =
+ Arrays.asList(extractAliases("Génération de lignes", "Entrée", new
String[] {"ligne"}));
+
+ assertTrue(aliases.contains("Generate rows"), "English name missing: " +
aliases);
+ assertTrue(aliases.contains("Input"), "English category missing: " +
aliases);
+ assertTrue(aliases.contains("row,generator"), "English keywords missing: "
+ aliases);
+ }
+
+ @Test
+ void dedupSkipsTermsAlreadyMatchedInTheActiveLocale() {
+ LanguageChoice.getInstance().setDefaultLocale(Locale.FRENCH);
+
+ // The active-locale terms already equal their English counterparts:
nothing new to add.
+ String[] aliases = extractAliases("Generate rows", "Input", new String[]
{"row,generator"});
+
+ assertEquals(0, aliases.length, "Expected no aliases, got: " +
Arrays.toString(aliases));
+ }
+
+ @Test
+ void englishLocaleAddsNoAliases() {
+ LanguageChoice.getInstance().setDefaultLocale(Locale.US);
+
+ String[] aliases = extractAliases("anything", "anything", new String[]
{"anything"});
+
+ assertEquals(0, aliases.length, "Expected no aliases, got: " +
Arrays.toString(aliases));
+ }
+
+ @Test
+ void getEnglishTranslationResolvesEnglishRegardlessOfActiveLocale() {
+ LanguageChoice.getInstance().setDefaultLocale(Locale.FRENCH);
+
+ assertEquals(
+ "Generate rows",
+ BasePluginType.getEnglishTranslation(NAME_KEY, packageName(),
EnglishSearchFixture.class));
+ assertEquals(
+ "Input",
+ BasePluginType.getEnglishTranslation(
+ CATEGORY_KEY, packageName(), EnglishSearchFixture.class));
+ }
+
+ @Test
+ void getEnglishTranslationReturnsNullForUnresolvableI18nCode() {
+ // An i18n-coded key that has no entry must not leak the raw "i18n:..."
code into the search
+ // set.
+ assertNull(
+ BasePluginType.getEnglishTranslation(
+ "i18n:org.apache.hop.core.plugins.englishsearch:No.Such.Key",
+ packageName(),
+ EnglishSearchFixture.class));
+ }
+
+ @Test
+ void getEnglishTranslationKeepsPlainStringWhenTranslationsUnsupported() {
+ assertEquals(
+ "PlainEnglish",
+ BasePluginType.getEnglishTranslation("PlainEnglish", "",
EnglishSearchFixture.class));
+ assertNull(
+ BasePluginType.getEnglishTranslation(null, packageName(),
EnglishSearchFixture.class));
+ }
+
+ @Test
+ void unresolvableI18nKeywordIsNotAddedAsAlias() {
+ LanguageChoice.getInstance().setDefaultLocale(Locale.FRENCH);
+
+ Transform annotation =
UnresolvableKeywordFixture.class.getAnnotation(Transform.class);
+ String[] aliases =
+ TransformPluginType.getInstance()
+ .extractEnglishSearchKeywords(
+ annotation,
+ packageName(),
+ UnresolvableKeywordFixture.class,
+ "Nom",
+ "Catégorie",
+ new String[] {"motclef"});
+
+ assertFalse(
+ Arrays.stream(aliases).anyMatch(a -> a.startsWith("i18n:")),
+ "Raw i18n code leaked into search aliases: " +
Arrays.toString(aliases));
+ }
+
+ // ---- helpers
--------------------------------------------------------------------------------
+
+ private static String packageName() {
+ return EnglishSearchFixture.class.getPackage().getName();
+ }
+
+ private static String[] extractAliases(
+ String localizedName, String localizedCategory, String[]
localizedKeywords) {
+ Transform annotation =
EnglishSearchFixture.class.getAnnotation(Transform.class);
+ return TransformPluginType.getInstance()
+ .extractEnglishSearchKeywords(
+ annotation,
+ packageName(),
+ EnglishSearchFixture.class,
+ localizedName,
+ localizedCategory,
+ localizedKeywords);
+ }
+
+ // ---- fixture annotations
--------------------------------------------------------------------
+
+ @Transform(
+ id = "EnglishSearchFixture",
+ name = NAME_KEY,
+ categoryDescription = CATEGORY_KEY,
+ keywords = {KEYWORDS_KEY})
+ private static final class EnglishSearchFixture {}
+
+ @Transform(
+ id = "UnresolvableKeywordFixture",
+ name = NAME_KEY,
+ categoryDescription = CATEGORY_KEY,
+ keywords =
{"i18n:org.apache.hop.core.plugins.englishsearch:No.Such.Key"})
+ private static final class UnresolvableKeywordFixture {}
+}
diff --git
a/engine/src/test/resources/org/apache/hop/core/plugins/englishsearch/messages/messages.properties
b/engine/src/test/resources/org/apache/hop/core/plugins/englishsearch/messages/messages.properties
new file mode 100644
index 0000000000..912c7c00f4
--- /dev/null
+++
b/engine/src/test/resources/org/apache/hop/core/plugins/englishsearch/messages/messages.properties
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+EnglishSearch.Name=Generate rows
+EnglishSearch.Category=Input
+EnglishSearch.Keywords=row,generator
diff --git
a/engine/src/test/resources/org/apache/hop/core/plugins/englishsearch/messages/messages_fr_FR.properties
b/engine/src/test/resources/org/apache/hop/core/plugins/englishsearch/messages/messages_fr_FR.properties
new file mode 100644
index 0000000000..097e43a61b
--- /dev/null
+++
b/engine/src/test/resources/org/apache/hop/core/plugins/englishsearch/messages/messages_fr_FR.properties
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+EnglishSearch.Name=Génération de lignes
+EnglishSearch.Category=Entrée
+# EnglishSearch.Keywords intentionally left untranslated to exercise the en_US
fallback
diff --git
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
index 42a074d570..695102723d 100644
---
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
+++
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
@@ -3319,6 +3319,8 @@ public class HopGuiPipelineGraph extends
HopGuiAbstractGraph
context.getClick()));
guiAction.getKeywords().addAll(Arrays.asList(plugin.getKeywords()));
guiAction.getKeywords().add(plugin.getCategory());
+ // Also search on the English name/category/keywords for non-English
locales (issue #2633)
+
guiAction.getKeywords().addAll(Arrays.asList(plugin.getEnglishKeywords()));
guiAction.setCategory(plugin.getCategory());
guiAction.setCategoryOrder(plugin.getCategory());
try {
diff --git
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/context/HopGuiPipelineContext.java
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/context/HopGuiPipelineContext.java
index 69187f435f..8587dc92a7 100644
---
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/context/HopGuiPipelineContext.java
+++
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/context/HopGuiPipelineContext.java
@@ -103,6 +103,10 @@ public class HopGuiPipelineContext extends
BaseGuiContextHandler implements IGui
true,
click));
createTransformAction.getKeywords().addAll(Arrays.asList(transformPlugin.getKeywords()));
+ // Also search on the English name/category/keywords for non-English
locales (issue #2633)
+ createTransformAction
+ .getKeywords()
+ .addAll(Arrays.asList(transformPlugin.getEnglishKeywords()));
createTransformAction.setCategory(transformPlugin.getCategory());
createTransformAction.setCategoryOrder(
"9999_" + transformPlugin.getCategory()); // sort alphabetically
diff --git
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/HopGuiWorkflowGraph.java
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/HopGuiWorkflowGraph.java
index 7de1bd7f97..fc87bc46f9 100644
---
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/HopGuiWorkflowGraph.java
+++
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/HopGuiWorkflowGraph.java
@@ -2920,6 +2920,8 @@ public class HopGuiWorkflowGraph extends
HopGuiAbstractGraph
context.getClick()));
guiAction.getKeywords().addAll(Arrays.asList(plugin.getKeywords()));
guiAction.getKeywords().add(plugin.getCategory());
+ // Also search on the English name/category/keywords for non-English
locales (issue #2633)
+
guiAction.getKeywords().addAll(Arrays.asList(plugin.getEnglishKeywords()));
guiAction.setCategory(plugin.getCategory());
guiAction.setCategoryOrder(plugin.getCategory());
diff --git
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/context/HopGuiWorkflowContext.java
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/context/HopGuiWorkflowContext.java
index 764a09c005..042eea05a5 100644
---
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/context/HopGuiWorkflowContext.java
+++
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/context/HopGuiWorkflowContext.java
@@ -102,6 +102,8 @@ public class HopGuiWorkflowContext extends
BaseGuiContextHandler implements IGui
controlClicked,
click));
createActionGuiAction.getKeywords().addAll(Arrays.asList(actionPlugin.getKeywords()));
+ // Also search on the English name/category/keywords for non-English
locales (issue #2633)
+
createActionGuiAction.getKeywords().addAll(Arrays.asList(actionPlugin.getEnglishKeywords()));
createActionGuiAction.setCategory(actionPlugin.getCategory());
createActionGuiAction.setCategoryOrder(
"9999_" + actionPlugin.getCategory()); // sort alphabetically