diff --git a/includes/install.core.inc b/includes/install.core.inc
index 9364bcc..c80331c 100644
--- a/includes/install.core.inc
+++ b/includes/install.core.inc
@@ -1381,11 +1381,21 @@ function install_import_locales(&$install_state) {
if (!isset($predefined[$install_locale])) {
// Drupal does not know about this language, so we prefill its values with
// our best guess. The user will be able to edit afterwards.
- locale_add_language($install_locale, $install_locale, $install_locale, LANGUAGE_LTR, '', '', TRUE, TRUE);
+ $language = (object) array(
+ 'language' => $install_locale,
+ 'name' => $install_locale,
+ 'native' => $install_locale,
+ 'default' => TRUE,
+ );
+ locale_language_save($language);
}
else {
// A known predefined language, details will be filled in properly.
- locale_add_language($install_locale, NULL, NULL, NULL, '', '', TRUE, TRUE);
+ $language = (object) array(
+ 'language' => $install_locale,
+ 'default' => TRUE,
+ );
+ locale_language_save($language);
}
// Collect files to import for this language.
diff --git a/includes/locale.inc b/includes/locale.inc
index f667d30..36c5be4 100644
--- a/includes/locale.inc
+++ b/includes/locale.inc
@@ -423,85 +423,70 @@ function locale_string_is_safe($string) {
}
/**
- * @defgroup locale-api-add Language addition API
- * @{
- * Add a language.
+ * API function to add or update a language.
*
- * The language addition API is used to create languages and store them.
+ * @param $language
+ * Language object with properties corresponding to 'languages' table columns.
*/
-
-/**
- * API function to add a language.
- *
- * @param $langcode
- * Language code.
- * @param $name
- * English name of the language
- * @param $native
- * Native name of the language
- * @param $direction
- * LANGUAGE_LTR or LANGUAGE_RTL
- * @param $domain
- * Optional custom domain name with protocol, without
- * trailing slash (eg. http://de.example.com).
- * @param $prefix
- * Optional path prefix for the language. Defaults to the
- * language code if omitted.
- * @param $enabled
- * Optionally TRUE to enable the language when created or FALSE to disable.
- * @param $default
- * Optionally set this language to be the default.
- */
-function locale_add_language($langcode, $name = NULL, $native = NULL, $direction = LANGUAGE_LTR, $domain = '', $prefix = '', $enabled = TRUE, $default = FALSE) {
- // Default prefix on language code.
- if (empty($prefix)) {
- $prefix = $langcode;
+function locale_language_save($language) {
+ $language->is_new = !(bool) db_query_range('SELECT 1 FROM {languages} WHERE language = :language', 0, 1, array(':language' => $language->language))->fetchField();
+ // Default prefix on language code if not provided otherwise.
+ if (!isset($language->prefix)) {
+ $language->prefix = $language->language;
}
// If name was not set, we add a predefined language.
- if (!isset($name)) {
+ if (!isset($language->name)) {
include_once DRUPAL_ROOT . '/includes/iso.inc';
$predefined = _locale_get_predefined_list();
- $name = $predefined[$langcode][0];
- $native = isset($predefined[$langcode][1]) ? $predefined[$langcode][1] : $predefined[$langcode][0];
- $direction = isset($predefined[$langcode][2]) ? $predefined[$langcode][2] : LANGUAGE_LTR;
+ $language->name = $predefined[$language->language][0];
+ $language->native = isset($predefined[$language->language][1]) ? $predefined[$language->language][1] : $predefined[$language->language][0];
+ $language->direction = isset($predefined[$language->language][2]) ? $predefined[$language->language][2] : LANGUAGE_LTR;
}
- db_insert('languages')
- ->fields(array(
- 'language' => $langcode,
- 'name' => $name,
- 'native' => $native,
- 'direction' => $direction,
- 'domain' => $domain,
- 'prefix' => $prefix,
- 'enabled' => $enabled,
- ))
- ->execute();
+ // Set to enabled for the default language and unless specified otherwise.
+ if (!empty($language->default) || !isset($language->enabled)) {
+ $language->enabled = TRUE;
+ }
+ // Let other modules modify $language before saved.
+ module_invoke_all('locale_language_presave', $language);
- // Only set it as default if enabled.
- if ($enabled && $default) {
- variable_set('language_default', (object) array('language' => $langcode, 'name' => $name, 'native' => $native, 'direction' => $direction, 'enabled' => (int) $enabled, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => $prefix, 'weight' => 0, 'javascript' => ''));
+ // Save the record and inform others about the change.
+ if ($language->is_new) {
+ drupal_write_record('languages', $language);
+ module_invoke_all('locale_language_insert', $language);
+ watchdog('locale', 'The %language (%langcode) language has been created.', array('%language' => $language->name, '%langcode' => $language->language));
}
+ else {
+ drupal_write_record('languages', $language, array('language'));
+ module_invoke_all('locale_language_update', $language);
+ watchdog('locale', 'The %language (%langcode) language has been updated.', array('%language' => $language->name, '%langcode' => $language->language));
+ }
+
+ if (!empty($language->default)) {
+ // Set the new version of this language as default in a variable.
+ $default_language = language_default();
+ variable_set('language_default', $language);
- if ($enabled) {
- // Increment enabled language count if we are adding an enabled language.
- variable_set('language_count', variable_get('language_count', 1) + 1);
+ // If this is different to the previous default language, save that one
+ // as non-default too.
+ if ($language->language != $default_language->language) {
+ $default_language->default = FALSE;
+ locale_language_save($default_language);
+ }
}
- // Kill the static cache in language_list().
- drupal_static_reset('language_list');
+ // Update language count based on enabled language count.
+ variable_set('language_count', db_query('SELECT COUNT(language) FROM {languages} WHERE enabled = 1')->fetchField());
- // Force JavaScript translation file creation for the newly added language.
- _locale_invalidate_js($langcode);
+ // Kill the static cache in language_list().
+ drupal_static_reset('language_list');
- watchdog('locale', 'The %language language (%code) has been created.', array('%language' => $name, '%code' => $langcode));
+ // Force JavaScript translation file re-creation for the modified language.
+ _locale_invalidate_js($language->language);
- module_invoke_all('multilingual_settings_changed');
+ return $language;
}
-/**
- * @} End of "locale-api-add"
- */
/**
* Parses a JavaScript file, extracts strings wrapped in Drupal.t() and
diff --git a/modules/field/tests/field.test b/modules/field/tests/field.test
index 669fc37..f2b3550 100644
--- a/modules/field/tests/field.test
+++ b/modules/field/tests/field.test
@@ -2644,7 +2644,12 @@ class FieldTranslationsTestCase extends FieldTestCase {
require_once DRUPAL_ROOT . '/includes/locale.inc';
for ($i = 0; $i < 3; ++$i) {
- locale_add_language('l' . $i, $this->randomString(), $this->randomString());
+ $language = (object) array(
+ 'language' => 'l' . $i,
+ 'name' => $this->randomString(),
+ 'native' => $this->randomString(),
+ );
+ locale_language_save($language);
}
}
diff --git a/modules/locale/locale.admin.inc b/modules/locale/locale.admin.inc
index 79ec3a1..6370326 100644
--- a/modules/locale/locale.admin.inc
+++ b/modules/locale/locale.admin.inc
@@ -350,14 +350,24 @@ function locale_languages_predefined_form_submit($form, &$form_state) {
$langcode = $form_state['values']['langcode'];
if (isset($form_state['values']['name'])) {
// Custom language form.
- locale_add_language($langcode, $form_state['values']['name'], $form_state['values']['native'], $form_state['values']['direction'], $form_state['values']['domain'], $form_state['values']['prefix']);
- drupal_set_message(t('The language %language has been created and can now be used. More information is available on the help screen.', array('%language' => t($form_state['values']['name']), '@locale-help' => url('admin/help/locale'))));
+ $language = (object) array(
+ 'language' => $langcode,
+ 'name' => $form_state['values']['name'],
+ 'native' => $form_state['values']['native'],
+ 'direction' => $form_state['values']['direction'],
+ 'domain' => $form_state['values']['domain'],
+ 'prefix' => $form_state['values']['prefix'],
+ );
+ locale_language_save($language);
}
else {
// Predefined language selection.
include_once DRUPAL_ROOT . '/includes/iso.inc';
$predefined = _locale_get_predefined_list();
- locale_add_language($langcode);
+ $language = (object) array(
+ 'language' => $langcode,
+ );
+ locale_language_save($language);
drupal_set_message(t('The language %language has been created and can now be used. More information is available on the help screen.', array('%language' => t($predefined[$langcode][0]), '@locale-help' => url('admin/help/locale'))));
}
diff --git a/modules/locale/locale.api.php b/modules/locale/locale.api.php
index e327401..8111dc0 100644
--- a/modules/locale/locale.api.php
+++ b/modules/locale/locale.api.php
@@ -162,6 +162,11 @@ function hook_language_negotiation_info_alter(array &$language_providers) {
* elsewhere. The typical implementation would trigger some kind of rebuilding,
* this way system components could properly react to the change of the enabled
* languages number.
+ *
+ * @todo
+ * To be deprecated by hook_locale_language_presave(),
+ * hook_locale_language_insert() and hook_locale_language_update() once all
+ * the language admin workflow uses locale_language_save().
*/
function hook_multilingual_settings_changed() {
field_info_cache_clear();
@@ -179,5 +184,38 @@ function hook_language_fallback_candidates_alter(array &$fallback_candidates) {
}
/**
+ * React to a language about to be added or updated in the system.
+ *
+ * @param $language
+ * A language object.
+ */
+function hook_locale_language_presave($language) {
+ if ($language->default) {
+ // React to a new default language.
+ example_new_default_language($language);
+ }
+}
+
+/**
+ * React to a language that was just added to the system.
+ *
+ * @param $language
+ * A language object.
+ */
+function hook_locale_language_insert($language) {
+ example_refresh_permissions();
+}
+
+/**
+ * React to a language that was just updated in the system.
+ *
+ * @param $language
+ * A language object.
+ */
+function hook_locale_language_update($language) {
+ example_refresh_permissions();
+}
+
+/**
* @} End of "addtogroup hooks".
*/
diff --git a/modules/locale/locale.test b/modules/locale/locale.test
index 6dad7e0..e94b4af 100644
--- a/modules/locale/locale.test
+++ b/modules/locale/locale.test
@@ -1098,12 +1098,18 @@ class LocaleUninstallFunctionalTest extends DrupalWebTestCase {
// Add a new language and optionally set it as default.
require_once DRUPAL_ROOT . '/includes/locale.inc';
- locale_add_language('fr', 'French', 'Français', LANGUAGE_LTR, '', '', TRUE, $this->language == 'fr');
+
+ $language = (object) array(
+ 'language' => 'fr',
+ 'name' => 'French',
+ 'native' => 'Français',
+ 'default' => $this->language == 'fr',
+ );
+ locale_language_save($language);
// Check the UI language.
drupal_language_initialize();
- global $language;
- $this->assertEqual($language->language, $this->language, t('Current language: %lang', array('%lang' => $language->language)));
+ $this->assertEqual($GLOBALS['language']->language, $this->language, t('Current language: %lang', array('%lang' => $GLOBALS['language']->language)));
// Enable multilingual workflow option for articles.
variable_set('language_content_type_article', 1);
@@ -1148,7 +1154,7 @@ class LocaleUninstallFunctionalTest extends DrupalWebTestCase {
// Check the init language logic.
drupal_language_initialize();
- $this->assertEqual($language->language, 'en', t('Language after uninstall: %lang', array('%lang' => $language->language)));
+ $this->assertEqual($GLOBALS['language']->language, 'en', t('Language after uninstall: %lang', array('%lang' => $GLOBALS['language']->language)));
// Check JavaScript files deletion.
$this->assertTrue($result = !file_exists($js_file), t('JavaScript file deleted: %file', array('%file' => $result ? $js_file : t('found'))));
@@ -1271,19 +1277,19 @@ class LocaleLanguageSwitchingFunctionalTest extends DrupalWebTestCase {
);
foreach ($language_switcher->ul->li as $link) {
$classes = explode(" ", (string) $link['class']);
- list($language) = array_intersect($classes, array('en', 'fr'));
+ list($langcode) = array_intersect($classes, array('en', 'fr'));
if (in_array('active', $classes)) {
- $links['active'][] = $language;
+ $links['active'][] = $langcode;
}
else {
- $links['inactive'][] = $language;
+ $links['inactive'][] = $langcode;
}
$anchor_classes = explode(" ", (string) $link->a['class']);
if (in_array('active', $anchor_classes)) {
- $anchors['active'][] = $language;
+ $anchors['active'][] = $langcode;
}
else {
- $anchors['inactive'][] = $language;
+ $anchors['inactive'][] = $langcode;
}
}
$this->assertIdentical($links, array('active' => array('en'), 'inactive' => array('fr')), t('Only the current language list item is marked as active on the language switcher block.'));
@@ -1796,13 +1802,13 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
function testUILanguageNegotiation() {
// A few languages to switch to.
// This one is unknown, should get the default lang version.
- $language_unknown = 'blah-blah';
+ $langcode_unknown = 'blah-blah';
// For testing browser lang preference.
- $language_browser_fallback = 'vi';
+ $langcode_browser_fallback = 'vi';
// For testing path prefix.
- $language = 'zh-hans';
+ $langcode = 'zh-hans';
// For setting browser language preference to 'vi'.
- $http_header_browser_fallback = array("Accept-Language: $language_browser_fallback;q=1");
+ $http_header_browser_fallback = array("Accept-Language: $langcode_browser_fallback;q=1");
// For setting browser language preference to some unknown.
$http_header_blah = array("Accept-Language: blah;q=1");
@@ -1811,8 +1817,14 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
// Setup the site languages by installing two languages.
require_once DRUPAL_ROOT . '/includes/locale.inc';
- locale_add_language($language_browser_fallback);
- locale_add_language($language);
+ $language = (object) array(
+ 'language' => $langcode_browser_fallback,
+ );
+ locale_language_save($language);
+ $language = (object) array(
+ 'language' => $langcode,
+ );
+ locale_language_save($language);
// We will look for this string in the admin/config screen to see if the
// corresponding translated string is shown.
@@ -1831,16 +1843,16 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
variable_del('language_default');
// Translate the string.
- $language_browser_fallback_string = "In $language_browser_fallback In $language_browser_fallback In $language_browser_fallback";
- $language_string = "In $language In $language In $language";
+ $language_browser_fallback_string = "In $langcode_browser_fallback In $langcode_browser_fallback In $langcode_browser_fallback";
+ $language_string = "In $langcode In $langcode In $langcode";
// Do a translate search of our target string.
$edit = array( 'string' => $default_string);
$this->drupalPost('admin/config/regional/translate/translate', $edit, t('Filter'));
// Should find the string and now click edit to post translated string.
$this->clickLink('edit');
$edit = array(
- "translations[$language_browser_fallback]" => $language_browser_fallback_string,
- "translations[$language]" => $language_string,
+ "translations[$langcode_browser_fallback]" => $language_browser_fallback_string,
+ "translations[$langcode]" => $language_string,
);
$this->drupalPost(NULL, $edit, t('Save translations'));
@@ -1859,7 +1871,7 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
// Language prefix.
array(
'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_DEFAULT),
- 'path' => "$language/admin/config",
+ 'path' => "$langcode/admin/config",
'expect' => $language_string,
'http_header' => $http_header_browser_fallback,
'message' => 'URL (PATH) > DEFAULT: with language prefix, UI language is switched based on path prefix',
@@ -1875,7 +1887,7 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
// Prefix, switch to the language.
array(
'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LOCALE_LANGUAGE_NEGOTIATION_BROWSER),
- 'path' => "$language/admin/config",
+ 'path' => "$langcode/admin/config",
'expect' => $language_string,
'http_header' => $http_header_browser_fallback,
'message' => 'URL (PATH) > BROWSER: with langage prefix, UI language is based on path prefix',
@@ -1896,13 +1908,13 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
// Unknown language prefix should return 404.
variable_set('language_negotiation_' . LANGUAGE_TYPE_INTERFACE, locale_language_negotiation_info());
- $this->drupalGet("$language_unknown/admin/config", array(), $http_header_browser_fallback);
+ $this->drupalGet("$langcode_unknown/admin/config", array(), $http_header_browser_fallback);
$this->assertResponse(404, "Unknown language path prefix should return 404");
// Setup for domain negotiation, first configure the language to have domain
// URL.
$edit = array('prefix' => '', 'domain' => "http://$language_domain");
- $this->drupalPost("admin/config/regional/language/edit/$language", $edit, t('Save language'));
+ $this->drupalPost("admin/config/regional/language/edit/$langcode", $edit, t('Save language'));
// Set the site to use domain language negotiation.
$tests = array(
@@ -1953,8 +1965,11 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
*/
function testUrlLanguageFallback() {
// Add the Italian language.
- $language_browser_fallback = 'it';
- locale_add_language($language_browser_fallback);
+ $langcode_browser_fallback = 'it';
+ $language = (object) array(
+ 'language' => $langcode_browser_fallback,
+ );
+ locale_language_save($language);
$languages = language_list();
// Enable the path prefix for the default language: this way any unprefixed
@@ -1978,14 +1993,14 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
// Access the front page without specifying any valid URL language prefix
// and having as browser language preference a non-default language.
- $http_header = array("Accept-Language: $language_browser_fallback;q=1");
+ $http_header = array("Accept-Language: $langcode_browser_fallback;q=1");
$this->drupalGet('', array(), $http_header);
// Check that the language switcher active link matches the given browser
// language.
- $args = array(':url' => base_path() . (!empty($GLOBALS['conf']['clean_url']) ? $language_browser_fallback : "?q=$language_browser_fallback"));
+ $args = array(':url' => base_path() . (!empty($GLOBALS['conf']['clean_url']) ? $langcode_browser_fallback : "?q=$langcode_browser_fallback"));
$fields = $this->xpath('//div[@id="block-locale-language"]//a[@class="language-link active" and @href=:url]', $args);
- $this->assertTrue($fields[0] == $languages[$language_browser_fallback]->native, t('The browser language is the URL active language'));
+ $this->assertTrue($fields[0] == $languages[$langcode_browser_fallback]->native, t('The browser language is the URL active language'));
// Check that URLs are rewritten using the given browser language.
$fields = $this->xpath('//div[@id="site-name"]//a[@rel="home" and @href=:url]//span', $args);
@@ -2095,7 +2110,12 @@ class LocaleMultilingualFieldsFunctionalTest extends DrupalWebTestCase {
// Add a new language.
require_once DRUPAL_ROOT . '/includes/locale.inc';
- locale_add_language('it', 'Italian', 'Italiano', LANGUAGE_LTR, '', '', TRUE, FALSE);
+ $language = (object) array(
+ 'language' => 'it',
+ 'name' => 'Italian',
+ 'native' => 'Italiano',
+ );
+ locale_language_save($language);
// Enable URL language detection and selection.
$edit = array('language[enabled][locale-url]' => '1');
@@ -2253,13 +2273,13 @@ class LocaleCommentLanguageFunctionalTest extends DrupalWebTestCase {
// language and interface language do not influence comment language, as
// only content language has to.
foreach (language_list() as $node_langcode => $node_language) {
- $language_none = LANGUAGE_NONE;
+ $langcode_none = LANGUAGE_NONE;
// Create "Article" content.
$title = $this->randomName();
$edit = array(
"title" => $title,
- "body[$language_none][0][value]" => $this->randomName(),
+ "body[$langcode_none][0][value]" => $this->randomName(),
"language" => $node_langcode,
);
$this->drupalPost("node/add/article", $edit, t('Save'));
@@ -2268,7 +2288,7 @@ class LocaleCommentLanguageFunctionalTest extends DrupalWebTestCase {
foreach (language_list() as $langcode => $language) {
// Post a comment with content language $langcode.
$prefix = empty($language->prefix) ? '' : $language->prefix . '/';
- $edit = array("comment_body[$language_none][0][value]" => $this->randomName());
+ $edit = array("comment_body[$langcode_none][0][value]" => $this->randomName());
$this->drupalPost("{$prefix}node/{$node->nid}", $edit, t('Save'));
// Check that comment language matches the current content language.
diff --git a/modules/system/system.test b/modules/system/system.test
index 846653b..fb22660 100644
--- a/modules/system/system.test
+++ b/modules/system/system.test
@@ -1215,7 +1215,11 @@ class DateTimeFunctionalTest extends DrupalWebTestCase {
$this->assertFalse($format, 'Unlocalized date format resides not in localized table.');
// Enable German language
- locale_add_language('de', NULL, NULL, LANGUAGE_LTR, '', '', TRUE, 'en');
+ $language = (object) array(
+ 'language' => 'de',
+ 'default' => TRUE,
+ );
+ locale_language_save($language);
$date_format = array(
'type' => 'short',