+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+import Pending from 'core/pending';
+import {debounce} from 'core/utils';
+
+const SELECTORS = {
+ AVAILABLE_LANG_SELECT: 'select',
+ AVAILABLE_LANG_SEARCH: '[data-action="search"]',
+};
+
+const DEBOUNCE_TIMER = 250;
+
+/**
+ * Initialize module
+ *
+ * @param {Element} form
+ */
+const init = (form) => {
+ const availableLangsElement = form.querySelector(SELECTORS.AVAILABLE_LANG_SELECT);
+
+ const availableLangsFilter = (event) => {
+ const pendingPromise = new Pending('tool_langimport/search:filter');
+
+ // Remove existing options.
+ availableLangsElement.querySelectorAll('option').forEach((option) => {
+ option.remove();
+ });
+
+ // Filter for matching languages.
+ const searchTerm = event.target.value.toLowerCase();
+ const availableLanguages = JSON.parse(availableLangsElement.dataset.availableLanguages);
+ const filteredLanguages = Object.keys(availableLanguages).reduce((matches, langcode) => {
+ if (availableLanguages[langcode].toLowerCase().includes(searchTerm)) {
+ matches[langcode] = availableLanguages[langcode];
+ }
+ return matches;
+ }, []);
+
+ // Re-create filtered options.
+ Object.entries(filteredLanguages).forEach(([langcode, langname]) => {
+ const option = document.createElement('option');
+ option.value = langcode;
+ option.innerText = langname;
+ availableLangsElement.append(option);
+ });
+
+ pendingPromise.resolve();
+ };
+
+ // Cache initial available language options.
+ const availableLanguages = {};
+ availableLangsElement.querySelectorAll('option').forEach((option) => {
+ availableLanguages[option.value] = option.text;
+ });
+ availableLangsElement.dataset.availableLanguages = JSON.stringify(availableLanguages);
+
+ // Register event listeners on the search element.
+ const availableLangsSearch = form.querySelector(SELECTORS.AVAILABLE_LANG_SEARCH);
+ availableLangsSearch.addEventListener('keydown', (event) => {
+ if (event.key === 'Enter') {
+ event.preventDefault();
+ }
+ });
+
+ // Debounce the event listener to allow the user to finish typing.
+ availableLangsSearch.addEventListener('keyup', (event) => {
+ const pendingPromise = new Pending('tool_langimport/search:keyup');
+
+ debounce(availableLangsFilter, DEBOUNCE_TIMER)(event);
+ setTimeout(() => {
+ pendingPromise.resolve();
+ }, DEBOUNCE_TIMER);
+ });
+};
+
+export default {
+ init: init,
+};
diff --git a/admin/tool/langimport/lang/en/tool_langimport.php b/admin/tool/langimport/lang/en/tool_langimport.php
index 27739eda343ba..7fddab115f69f 100644
--- a/admin/tool/langimport/lang/en/tool_langimport.php
+++ b/admin/tool/langimport/lang/en/tool_langimport.php
@@ -45,6 +45,7 @@
$string['nolangupdateneeded'] = 'All your language packs are up to date, no update is needed';
$string['pluginname'] = 'Language packs';
$string['purgestringcaches'] = 'Purge string caches';
+$string['search'] = 'Search available language packs';
$string['selectlangs'] = 'Select languages to uninstall';
$string['uninstall'] = 'Uninstall selected language pack(s)';
$string['uninstallconfirm'] = 'You are about to completely uninstall these language packs: {$a} . Are you sure?';
diff --git a/admin/tool/langimport/styles.css b/admin/tool/langimport/styles.css
index f23c99bc3d01e..e5a827167005b 100644
--- a/admin/tool/langimport/styles.css
+++ b/admin/tool/langimport/styles.css
@@ -3,3 +3,8 @@
float: none;
width: 100%;
}
+
+#page-admin-tool-langimport-index #menuuninstalllang,
+#page-admin-tool-langimport-index #menupack {
+ height: 300px;
+}
diff --git a/admin/tool/langimport/templates/langimport.mustache b/admin/tool/langimport/templates/langimport.mustache
index 71a7515084b87..32f423965760c 100644
--- a/admin/tool/langimport/templates/langimport.mustache
+++ b/admin/tool/langimport/templates/langimport.mustache
@@ -105,6 +105,13 @@
{{/toinstalloptions}}
+
+ {{< core/search_input_auto }}
+ {{$label}}
+ {{#str}} search, tool_langimport {{/str}}
+ {{/label}}
+ {{/ core/search_input_auto }}
+
@@ -115,3 +122,8 @@
{{/caninstall}}
+{{#js}}
+ require(['tool_langimport/search'], function(search) {
+ search.init(document.querySelector('#installform'));
+ });
+{{/js}}
diff --git a/admin/tool/langimport/tests/behat/manage_langpacks.feature b/admin/tool/langimport/tests/behat/manage_langpacks.feature
index 63fcc5377faf7..6af3c4c8d3248 100644
--- a/admin/tool/langimport/tests/behat/manage_langpacks.feature
+++ b/admin/tool/langimport/tests/behat/manage_langpacks.feature
@@ -20,6 +20,16 @@ Feature: Manage language packs
And I should see "The language pack 'en_ar' was installed."
And I log out
+ @javascript
+ Scenario: Search for available language pack
+ Given I log in as "admin"
+ And I navigate to "Language > Language packs" in site administration
+ When I set the field "Search available language packs" to "pirate"
+ Then the "Available language packs" select box should not contain "es"
+ And I set the field "Available language packs" to "en_ar"
+ And I press "Install selected language pack(s)"
+ And I should see "Language pack 'en_ar' was successfully installed"
+
Scenario: Update language pack
Given outdated langpack 'en_ar' is installed
And I log in as "admin"