diff --git a/admin/tool/langimport/classes/locale.php b/admin/tool/langimport/classes/locale.php new file mode 100644 index 00000000000..69c43b69cf0 --- /dev/null +++ b/admin/tool/langimport/classes/locale.php @@ -0,0 +1,84 @@ +. + +/** + * Helper class for the language import tool. + * + * @package tool_langimport + * @copyright 2018 Université Rennes 2 {@link https://www.univ-rennes2.fr} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_langimport; + +use coding_exception; + +defined('MOODLE_INTERNAL') || die; + +/** + * Helper class for the language import tool. + * + * @copyright 2018 Université Rennes 2 {@link https://www.univ-rennes2.fr} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class locale { + /** + * Checks availability of locale on current operating system. + * + * @param string $langpackcode E.g.: en, es, fr, de. + * @return bool TRUE if the locale is available on OS. + * @throws coding_exception when $langpackcode parameter is a non-empty string. + */ + public function check_locale_availability(string $langpackcode) : bool { + global $CFG; + + if (empty($langpackcode)) { + throw new coding_exception('Invalid language pack code in \\'.__METHOD__.'() call, only non-empty string is allowed'); + } + + // Fetch the correct locale based on ostype. + if ($CFG->ostype === 'WINDOWS') { + $stringtofetch = 'localewin'; + } else { + $stringtofetch = 'locale'; + } + + // Store current locale. + $currentlocale = $this->set_locale(LC_ALL, 0); + + $locale = get_string_manager()->get_string($stringtofetch, 'langconfig', $a = null, $langpackcode); + + // Try to set new locale. + $return = $this->set_locale(LC_ALL, $locale); + + // Restore current locale. + $this->set_locale(LC_ALL, $currentlocale); + + // If $return is not equal to false, it means that setlocale() succeed to change locale. + return $return !== false; + } + + /** + * Wrap for the native PHP function setlocale(). + * + * @param int $category Specifying the category of the functions affected by the locale setting. + * @param string $locale E.g.: en_AU.utf8, en_GB.utf8, es_ES.utf8, fr_FR.utf8, de_DE.utf8. + * @return string|false Returns the new current locale, or FALSE on error. + */ + protected function set_locale(int $category = LC_ALL, string $locale = '0') { + return setlocale($category, $locale); + } +} diff --git a/admin/tool/langimport/index.php b/admin/tool/langimport/index.php index 92a09dad241..1180abc09a8 100644 --- a/admin/tool/langimport/index.php +++ b/admin/tool/langimport/index.php @@ -109,9 +109,16 @@ echo $OUTPUT->header(); echo $OUTPUT->heading(get_string('langimport', 'tool_langimport')); $installedlangs = get_string_manager()->get_list_of_translations(true); +$locale = new \tool_langimport\locale(); +$missinglocales = ''; $missingparents = array(); -foreach ($installedlangs as $installedlang => $unused) { +foreach ($installedlangs as $installedlang => $langpackname) { + // Check locale availability. + if (!$locale->check_locale_availability($installedlang)) { + $missinglocales .= '
  • '.$langpackname.'
  • '; + } + $parent = get_parent_language($installedlang); if (empty($parent)) { continue; @@ -121,6 +128,14 @@ foreach ($installedlangs as $installedlang => $unused) { } } +if (!empty($missinglocales)) { + // There is at least one missing locale. + $a = new stdClass(); + $a->globallocale = moodle_getlocale(); + $a->missinglocales = $missinglocales; + $controller->errors[] = get_string('langunsupported', 'tool_langimport', $a); +} + if ($availablelangs = $controller->availablelangs) { $remote = true; } else { diff --git a/admin/tool/langimport/lang/en/tool_langimport.php b/admin/tool/langimport/lang/en/tool_langimport.php index faeb02cd5d6..27739eda343 100644 --- a/admin/tool/langimport/lang/en/tool_langimport.php +++ b/admin/tool/langimport/lang/en/tool_langimport.php @@ -37,6 +37,7 @@ $string['langpackupdateskipped'] = 'Update of \'{$a}\' language pack skipped'; $string['langpackuptodate'] = 'Language pack \'{$a}\' is up-to-date'; $string['langpackupdated'] = 'Language pack \'{$a}\' was successfully updated'; $string['langpackupdatedevent'] = 'Language pack updated'; +$string['langunsupported'] = '

    Your server does not seem to fully support the following languages:

    Instead, the global locale ({$a->globallocale}) will be used to format certain strings such as dates or numbers.

    '; $string['langupdatecomplete'] = 'Language pack update completed'; $string['missingcfglangotherroot'] = 'Missing configuration value $CFG->langotherroot'; $string['missinglangparent'] = 'Missing parent language {$a->parent} of {$a->lang}.'; diff --git a/admin/tool/langimport/tests/locale_test.php b/admin/tool/langimport/tests/locale_test.php new file mode 100644 index 00000000000..4d1ffef94aa --- /dev/null +++ b/admin/tool/langimport/tests/locale_test.php @@ -0,0 +1,71 @@ +. + +/** + * Tests for \tool_langimport\locale class. + * + * @package tool_langimport + * @copyright 2018 Université Rennes 2 {@link https://www.univ-rennes2.fr} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Tests for \tool_langimport\locale class. + * + * @copyright 2018 Université Rennes 2 {@link https://www.univ-rennes2.fr} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class locale_testcase extends \advanced_testcase { + /** + * Test that \tool_langimport\locale::check_locale_availability() works as expected. + * + * @return void + */ + public function test_check_locale_availability() { + // Create a mock of set_locale() method to simulate : + // - first setlocale() call which backup current locale + // - second setlocale() call which try to set new 'es' locale + // - third setlocale() call which restore locale. + $mock = $this->getMockBuilder(\tool_langimport\locale::class) + ->setMethods(['set_locale']) + ->getMock(); + $mock->method('set_locale')->will($this->onConsecutiveCalls('en', 'es', 'en')); + + // Test what happen when locale is available on system. + $result = $mock->check_locale_availability('en'); + $this->assertTrue($result); + + // Create a mock of set_locale() method to simulate : + // - first setlocale() call which backup current locale + // - second setlocale() call which fail to set new locale + // - third setlocale() call which restore locale. + $mock = $this->getMockBuilder(\tool_langimport\locale::class) + ->setMethods(['set_locale']) + ->getMock(); + $mock->method('set_locale')->will($this->onConsecutiveCalls('en', false, 'en')); + + // Test what happen when locale is not available on system. + $result = $mock->check_locale_availability('en'); + $this->assertFalse($result); + + // Test an invalid parameter. + $locale = new \tool_langimport\locale(); + $this->expectException(coding_exception::class); + $locale->check_locale_availability(''); + } +} diff --git a/lib/moodlelib.php b/lib/moodlelib.php index 5ebe7afedec..3c44836fb0b 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -8010,6 +8010,28 @@ function moodle_major_version($fromdisk = false) { // MISCELLANEOUS. +/** + * Gets the system locale + * + * @return string Retuns the current locale. + */ +function moodle_getlocale() { + global $CFG; + + // Fetch the correct locale based on ostype. + if ($CFG->ostype == 'WINDOWS') { + $stringtofetch = 'localewin'; + } else { + $stringtofetch = 'locale'; + } + + if (!empty($CFG->locale)) { // Override locale for all language packs. + return $CFG->locale; + } + + return get_string($stringtofetch, 'langconfig'); +} + /** * Sets the system locale * @@ -8023,20 +8045,11 @@ function moodle_setlocale($locale='') { $oldlocale = $currentlocale; - // Fetch the correct locale based on ostype. - if ($CFG->ostype == 'WINDOWS') { - $stringtofetch = 'localewin'; - } else { - $stringtofetch = 'locale'; - } - // The priority is the same as in get_string() - parameter, config, course, session, user, global language. if (!empty($locale)) { $currentlocale = $locale; - } else if (!empty($CFG->locale)) { // Override locale for all language packs. - $currentlocale = $CFG->locale; } else { - $currentlocale = get_string($stringtofetch, 'langconfig'); + $currentlocale = moodle_getlocale(); } // Do nothing if locale already set up.