Index: includes/locale.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/locale.inc,v
retrieving revision 1.205
diff -u -p -r1.205 locale.inc
--- includes/locale.inc 22 Feb 2009 17:55:29 -0000 1.205
+++ includes/locale.inc 24 Feb 2009 01:26:56 -0000
@@ -2605,6 +2605,8 @@ function locale_batch_by_language($langc
// Collect all files to import for all enabled modules and themes.
$files = array();
$components = array();
+
+ $language_with_fallbacks = _locale_get_language_with_fallbacks($langcode);
$query = db_select('system', 's');
$query->fields('s', array('name', 'filename'));
$query->condition('s.status', 1);
@@ -2613,11 +2615,22 @@ function locale_batch_by_language($langc
}
$result = $query->execute();
foreach ($result as $component) {
- // Collect all files for all components, names as $langcode.po or
- // with names ending with $langcode.po. This allows for filenames
- // like node-module.de.po to let translators use small files and
- // be able to import in smaller chunks.
- $files = array_merge($files, file_scan_directory(dirname($component->filename) . '/translations', '/(^|\.)' . $langcode . '\.po$/', array('recurse' => FALSE)));
+ // Iterate through a language and its potential fallbacks.
+ // We need to do this in order to ensure that the language takes
+ // precedence over its fallback.
+ foreach ($language_with_fallbacks as $language) {
+ // Collect all files for all components, names as $language.po or
+ // with names ending with $language.po. This allows for filenames
+ // like node-module.de.po to let translators use small files and
+ // be able to import in smaller chunks.
+ $new_files = file_scan_directory(dirname($component->filename) . '/translations', '/(^|\.)' . $language . '\.po$/', array('recurse' => FALSE));
+ // Whether for the original or a fallback language, the imports
+ // need to be assigned to the original language.
+ foreach (array_keys($new_files) as $key) {
+ $new_files[$key]->langcode = $langcode;
+ }
+ $files = array_merge($files, $new_files);
+ }
$components[] = $component->name;
}
@@ -2640,16 +2653,32 @@ function locale_batch_by_component($comp
$languages = language_list('enabled');
unset($languages[1]['en']);
if (count($languages[1])) {
- $language_list = join('|', array_keys($languages[1]));
- // Collect all files to import for all $components.
- $result = db_query("SELECT name, filename FROM {system} WHERE status = 1");
- while ($component = db_fetch_object($result)) {
- if (in_array($component->name, $components)) {
- // Collect all files for this component in all enabled languages, named
- // as $langcode.po or with names ending with $langcode.po. This allows
- // for filenames like node-module.de.po to let translators use small
- // files and be able to import in smaller chunks.
- $files = array_merge($files, file_scan_directory(dirname($component->filename) . '/translations', '/(^|\.)(' . $language_list . ')\.po$/', array('recurse' => FALSE)));
+ foreach (array_keys($languages[1]) as $langcode) {
+ $language_with_fallbacks = _locale_get_language_with_fallbacks($langcode);
+ // Collect all files to import for all $components.
+ $query = db_select('system', 's');
+ $query->fields('s', array('name', 'filename'));
+ $query->condition('s.status', 1);
+ $result = $query->execute();
+ foreach ($result as $component) {
+ if (in_array($component->name, $components)) {
+ // Iterate through a language and its potential fallbacks.
+ // We need to do this in order to ensure that the language takes
+ // precedence over its fallback.
+ foreach ($language_with_fallbacks as $language) {
+ // Collect all files for this component in all enabled languages, named
+ // as $language.po or with names ending with $language.po. This allows
+ // for filenames like node-module.de.po to let translators use small
+ // files and be able to import in smaller chunks.
+ $new_files = file_scan_directory(dirname($component->filename) . '/translations', '/(^|\.)' . $language . '\.po$/', array('recurse' => FALSE));
+ // Whether for the original or a fallback language, the imports
+ // need to be assigned to the original language.
+ foreach (array_keys($new_files) as $key) {
+ $new_files[$key]->langcode = $langcode;
+ }
+ $files = array_merge($files, $new_files);
+ }
+ }
}
}
return _locale_batch_build($files, $finished);
@@ -2675,7 +2704,7 @@ function _locale_batch_build($files, $fi
$operations = array();
foreach ($files as $file) {
// We call _locale_batch_import for every batch operation.
- $operations[] = array('_locale_batch_import', array($file->filepath));
+ $operations[] = array('_locale_batch_import', array($file->filepath, $file->langcode));
}
$batch = array(
'operations' => $operations,
@@ -2700,15 +2729,18 @@ function _locale_batch_build($files, $fi
*
* @param $filepath
* Path to a file to import.
- * @param $results
+ * @param $langcode_override
+ * A language code to be set to the imported strings. Enables the use
+ * of language import fallbacks.
+ * @param $context
* Contains a list of files imported.
*/
-function _locale_batch_import($filepath, &$context) {
+function _locale_batch_import($filepath, $langcode_override, &$context) {
// The filename is either {langcode}.po or {prefix}.{langcode}.po, so
// we can extract the language code to use for the import from the end.
if (preg_match('!(/|\.)([^\./]+)\.po$!', $filepath, $langcode)) {
$file = (object) array('filename' => basename($filepath), 'filepath' => $filepath);
- _locale_import_read_po('db-store', $file, LOCALE_IMPORT_KEEP, $langcode[2]);
+ _locale_import_read_po('db-store', $file, LOCALE_IMPORT_KEEP, $langcode_override ? $langcode_override : $langcode[2]);
$context['results'][] = $filepath;
}
}
@@ -2734,5 +2766,21 @@ function _locale_batch_language_finished
}
/**
+ * Return an array of a language and its fallbacks, if any.
+ *
+ * By default, if a language code has more than two digits, the first
+ * two digits are used as a fallback.
+ */
+function _locale_get_language_with_fallbacks($langcode) {
+ $langcodes = array($langcode);
+ if (strlen($langcode) > 2) {
+ $langcodes[] = substr($langcode, 0, 2);
+ }
+ // Allow other modules to alter the language fallbacks.
+ drupal_alter('locale_language_fallbacks', $langcodes, $langcode);
+ return $langcodes;
+}
+
+/**
* @} End of "locale-autoimport"
*/
Index: modules/locale/locale.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.test,v
retrieving revision 1.18
diff -u -p -r1.18 locale.test
--- modules/locale/locale.test 22 Feb 2009 20:55:18 -0000 1.18
+++ modules/locale/locale.test 24 Feb 2009 01:27:01 -0000
@@ -55,13 +55,13 @@ class LocaleConfigurationTest extends Dr
// Add custom language.
// Code for the language.
- $langcode = $this->randomName(6, 'si-');
+ $langcode = 'xx';
// The English name for the language.
$name = $this->randomName(16);
// The native name for the language.
$native = $this->randomName(16);
// The domain prefix.
- $prefix = strtolower(str_replace('si-', '', $langcode));
+ $prefix = $langcode;
$edit = array(
'langcode' => $langcode,
'name' => $name,
@@ -188,13 +188,13 @@ class LocaleTranslationFunctionalTest ex
// User to translate and delete string.
$translate_user = $this->drupalCreateUser(array('translate interface', 'access administration pages'));
// Code for the language.
- $langcode = $this->randomName(6, 'si-');
+ $langcode = 'xx';
// The English name for the language. This will be translated.
$name = $this->randomName(16);
// The native name for the language.
$native = $this->randomName(16);
// The domain prefix.
- $prefix = strtolower(str_replace('si-', '', $langcode));
+ $prefix = $langcode;
// This is the language indicator on the translation search screen for
// untranslated strings. Copied straight from locale.inc.
$language_indicator = "$langcode ";
@@ -314,13 +314,13 @@ class LocaleTranslationFunctionalTest ex
// User to add language and strings.
$admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages', 'translate interface'));
$this->drupalLogin($admin_user);
- $langcode = $this->randomName(6, 'si-');
+ $langcode = 'xx';
// The English name for the language. This will be translated.
$name = $this->randomName(16);
// The native name for the language.
$native = $this->randomName(16);
// The domain prefix.
- $prefix = strtolower(str_replace('si-', '', $langcode));
+ $prefix = $langcode;
// This is the language indicator on the translation search screen for
// untranslated strings. Copied straight from locale.inc.
$language_indicator = "$langcode ";
@@ -381,13 +381,13 @@ class LocaleTranslationFunctionalTest ex
$translate_user = $this->drupalCreateUser(array('translate interface', 'access administration pages'));
// Code for the language.
- $langcode = $this->randomName(6, 'si-');
+ $langcode = 'xx';
// The English name for the language. This will be translated.
$name = $this->randomName(16);
// The native name for the language.
$native = $this->randomName(16);
// The domain prefix.
- $prefix = strtolower(str_replace('si-', '', $langcode));
+ $prefix = $langcode;
// This is the language indicator on the translation search screen for
// untranslated strings. Copied straight from locale.inc.
$language_indicator = "$langcode ";
@@ -655,22 +655,14 @@ class LocaleImportFunctionalTest extends
* is enabled.
*/
function testAutomaticModuleTranslationImportLanguageEnable() {
- // Code for the language.
- $langcode = $this->randomName(6, 'si-');
+ // Code for the language - manually set to match the test translation file.
+ $langcode = 'xx';
// The English name for the language.
$name = $this->randomName(16);
// The native name for the language.
$native = $this->randomName(16);
// The domain prefix.
- $prefix = strtolower(str_replace('si-', '', $langcode));
-
- // Create a .po file.
- $translations_dir = drupal_get_path('module', 'locale_test') . '/translations/';
- if (!file_exists($translations_dir)) {
- mkdir($translations_dir);
- }
- $filename = $translations_dir . $langcode . '.po';
- file_put_contents($filename, $this->getPoFile());
+ $prefix = $langcode;
// Create a custom language.
$edit = array(
@@ -696,8 +688,53 @@ class LocaleImportFunctionalTest extends
$this->drupalPost('admin/build/translate/translate', $search, t('Filter'));
$this->assertNoText(t('No strings found for your search.'), t('String successfully imported.'));
- // Remove our temporary .po file.
- unlink($filename);
+ // Now repeat with a language including an extended code to test language
+ // fallback imports.
+
+ // Code for the language.
+ $langcode = 'xx-yy';
+ // The English name for the language.
+ $name = $this->randomName(16);
+ // The native name for the language.
+ $native = $this->randomName(16);
+ // The domain prefix.
+ $prefix = $langcode;
+
+ // Create a custom language.
+ $edit = array(
+ 'langcode' => $langcode,
+ 'name' => $name,
+ 'native' => $native,
+ 'prefix' => $prefix,
+ 'direction' => '0',
+ );
+ $this->drupalPost('admin/settings/language/add', $edit, t('Add custom language'));
+
+ // Ensure the translation files were automatically imported when language was
+ // added.
+ $this->assertText(t('2 translation files imported for the enabled modules.'), t('Language file and fallback language file automatically imported.'));
+
+ // Ensure primary language strings were successfully imported.
+ $search = array(
+ 'string' => 'vacances',
+ 'language' => $langcode,
+ 'translation' => 'translated',
+ 'group' => 'all',
+ );
+ $this->drupalPost('admin/build/translate/translate', $search, t('Filter'));
+ $this->assertNoText(t('No strings found for your search.'), t('String in primary language successfully saved.'));
+
+ // Ensure fallback language string was not imported if string existed in
+ // primary language.
+ $search['string'] = 'lundi';
+ $this->drupalPost('admin/build/translate/translate', $search, t('Filter'));
+ $this->assertText(t('No strings found for your search.'), t('String not imported in fallback language when found in primary language.'));
+
+ // Ensure fallback language string was imported if string did not exist in
+ // primary language.
+ $search['string'] = 'mardi';
+ $this->drupalPost('admin/build/translate/translate', $search, t('Filter'));
+ $this->assertNoText(t('No strings found for your search.'), t('String successfully imported in fallback language when not found in primary language.'));
}
/**
@@ -1102,13 +1139,13 @@ class LocaleUserLanguageFunctionalTest e
// Add custom language.
$this->drupalLogin($admin_user);
// Code for the language.
- $langcode = $this->randomName(6, 'si-');
+ $langcode = 'xx';
// The English name for the language.
$name = $this->randomName(16);
// The native name for the language.
$native = $this->randomName(16);
// The domain prefix.
- $prefix = strtolower(str_replace('si-', '', $langcode));
+ $prefix = 'xx';
$edit = array(
'langcode' => $langcode,
'name' => $name,
@@ -1120,13 +1157,13 @@ class LocaleUserLanguageFunctionalTest e
// Add custom language and disable it.
// Code for the language.
- $langcode_disabled = $this->randomName(6, 'si-');
+ $langcode_disabled = 'xx-yy';
// The English name for the language. This will be translated.
$name_disabled = $this->randomName(16);
// The native name for the language.
$native_disabled = $this->randomName(16);
// The domain prefix.
- $prefix_disabled = strtolower(str_replace('si-', '', $langcode_disabled));
+ $prefix_disabled = $langcode_disabled;
$edit = array(
'langcode' => $langcode_disabled,
'name' => $name_disabled,
@@ -1195,13 +1232,13 @@ class LocalePathFunctionalTest extends D
// Add custom language.
$this->drupalLogin($admin_user);
// Code for the language.
- $langcode = $this->randomName(6, 'si-');
+ $langcode = 'xx';
// The English name for the language.
$name = $this->randomName(16);
// The native name for the language.
$native = $this->randomName(16);
// The domain prefix.
- $prefix = strtolower(str_replace('si-', '', $langcode));
+ $prefix = $langcode;
$edit = array(
'langcode' => $langcode,
'name' => $name,
@@ -1281,13 +1318,13 @@ class LocaleContentFunctionalTest extend
// Add custom language.
$this->drupalLogin($admin_user);
// Code for the language.
- $langcode = $this->randomName(6, 'si-');
+ $langcode = 'xx';
// The English name for the language.
$name = $this->randomName(16);
// The native name for the language.
$native = $this->randomName(16);
// The domain prefix.
- $prefix = strtolower(str_replace('si-', '', $langcode));
+ $prefix = $langcode;
$edit = array(
'langcode' => $langcode,
'name' => $name,
@@ -1299,13 +1336,13 @@ class LocaleContentFunctionalTest extend
// Add disabled custom language.
// Code for the language.
- $langcode_disabled = $this->randomName(6, 'si-');
+ $langcode_disabled = 'xx-yy';
// The English name for the language.
$name_disabled = $this->randomName(16);
// The native name for the language.
$native_disabled = $this->randomName(16);
// The domain prefix.
- $prefix_disabled = strtolower(str_replace('si-', '', $langcode_disabled));
+ $prefix_disabled = $langcode_disabled;
$edit = array(
'langcode' => $langcode_disabled,
'name' => $name_disabled,
Index: modules/locale/tests/translations/test.xx-yy.po
===================================================================
RCS file: modules/locale/tests/translations/test.xx-yy.po
diff -N modules/locale/tests/translations/test.xx-yy.po
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ modules/locale/tests/translations/test.xx-yy.po 24 Feb 2009 01:27:01 -0000
@@ -0,0 +1,11 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Drupal 7\\n"
+"MIME-Version: 1.0\\n"
+"Content-Type: text/plain; charset=UTF-8\\n"
+"Content-Transfer-Encoding: 8bit\\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\\n"
+
+msgid "Monday"
+msgstr "vacances"
+
Index: modules/locale/tests/translations/test.xx.po
===================================================================
RCS file: modules/locale/tests/translations/test.xx.po
diff -N modules/locale/tests/translations/test.xx.po
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ modules/locale/tests/translations/test.xx.po 24 Feb 2009 01:27:01 -0000
@@ -0,0 +1,28 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Drupal 7\\n"
+"MIME-Version: 1.0\\n"
+"Content-Type: text/plain; charset=UTF-8\\n"
+"Content-Transfer-Encoding: 8bit\\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\\n"
+
+msgid "Monday"
+msgstr "lundi"
+
+msgid "Tuesday"
+msgstr "mardi"
+
+msgid "Wednesday"
+msgstr "mercredi"
+
+msgid "Thursday"
+msgstr "jeudi"
+
+msgid "Friday"
+msgstr "vendredi"
+
+msgid "Saturday"
+msgstr "samedi"
+
+msgid "Sunday"
+msgstr "dimanche"