Mercurial > hg > cmmr2012-drupal-site
comparison core/modules/locale/locale.module @ 5:12f9dff5fda9 tip
Update to Drupal core 8.7.1
author | Chris Cannam |
---|---|
date | Thu, 09 May 2019 15:34:47 +0100 |
parents | a9cd425dd02b |
children |
comparison
equal
deleted
inserted
replaced
4:a9cd425dd02b | 5:12f9dff5fda9 |
---|---|
8 * displayed in different languages, and nodes can have languages assigned. The | 8 * displayed in different languages, and nodes can have languages assigned. The |
9 * setup of languages and translations is completely web based. Gettext portable | 9 * setup of languages and translations is completely web based. Gettext portable |
10 * object files are supported. | 10 * object files are supported. |
11 */ | 11 */ |
12 | 12 |
13 use Drupal\Component\Gettext\PoItem; | |
13 use Drupal\Component\Serialization\Json; | 14 use Drupal\Component\Serialization\Json; |
14 use Drupal\Component\Utility\Html; | 15 use Drupal\Component\Utility\Html; |
15 use Drupal\Component\Utility\UrlHelper; | 16 use Drupal\Component\Utility\UrlHelper; |
16 use Drupal\Component\Utility\Xss; | 17 use Drupal\Component\Utility\Xss; |
18 use Drupal\Core\File\Exception\FileException; | |
19 use Drupal\Core\File\FileSystemInterface; | |
17 use Drupal\Core\Url; | 20 use Drupal\Core\Url; |
18 use Drupal\Core\Asset\AttachedAssetsInterface; | 21 use Drupal\Core\Asset\AttachedAssetsInterface; |
19 use Drupal\Core\Form\FormStateInterface; | 22 use Drupal\Core\Form\FormStateInterface; |
20 use Drupal\Core\Routing\RouteMatchInterface; | 23 use Drupal\Core\Routing\RouteMatchInterface; |
21 use Drupal\Core\Language\LanguageInterface; | 24 use Drupal\Core\Language\LanguageInterface; |
144 function locale_help($route_name, RouteMatchInterface $route_match) { | 147 function locale_help($route_name, RouteMatchInterface $route_match) { |
145 switch ($route_name) { | 148 switch ($route_name) { |
146 case 'help.page.locale': | 149 case 'help.page.locale': |
147 $output = ''; | 150 $output = ''; |
148 $output .= '<h3>' . t('About') . '</h3>'; | 151 $output .= '<h3>' . t('About') . '</h3>'; |
149 $output .= '<p>' . t('The Interface Translation module allows you to translate interface text (<em>strings</em>) into different languages, and to switch between them for the display of interface text. It uses the functionality provided by the <a href=":language">Language module</a>. For more information, see the <a href=":doc-url">online documentation for the Interface Translation module</a>.', [':doc-url' => 'https://www.drupal.org/documentation/modules/locale/', ':language' => \Drupal::url('help.page', ['name' => 'language'])]) . '</p>'; | 152 $output .= '<p>' . t('The Interface Translation module allows you to translate interface text (<em>strings</em>) into different languages, and to switch between them for the display of interface text. It uses the functionality provided by the <a href=":language">Language module</a>. For more information, see the <a href=":doc-url">online documentation for the Interface Translation module</a>.', [':doc-url' => 'https://www.drupal.org/documentation/modules/locale/', ':language' => Url::fromRoute('help.page', ['name' => 'language'])->toString()]) . '</p>'; |
150 $output .= '<h3>' . t('Uses') . '</h3>'; | 153 $output .= '<h3>' . t('Uses') . '</h3>'; |
151 $output .= '<dl>'; | 154 $output .= '<dl>'; |
152 $output .= '<dt>' . t('Importing translation files') . '</dt>'; | 155 $output .= '<dt>' . t('Importing translation files') . '</dt>'; |
153 $output .= '<dd>' . t('Translation files with translated interface text are imported automatically when languages are added on the <a href=":languages">Languages</a> page, or when modules or themes are enabled. On the <a href=":locale-settings">Interface translation settings</a> page, the <em>Translation source</em> can be restricted to local files only, or to include the <a href=":server">Drupal translation server</a>. Although modules and themes may not be fully translated in all languages, new translations become available frequently. You can specify whether and how often to check for translation file updates and whether to overwrite existing translations on the <a href=":locale-settings">Interface translation settings</a> page. You can also manually import a translation file on the <a href=":import">Interface translation import</a> page.', [':import' => \Drupal::url('locale.translate_import'), ':locale-settings' => \Drupal::url('locale.settings'), ':languages' => \Drupal::url('entity.configurable_language.collection'), ':server' => 'https://localize.drupal.org']) . '</dd>'; | 156 $output .= '<dd>' . t('Translation files with translated interface text are imported automatically when languages are added on the <a href=":languages">Languages</a> page, or when modules or themes are enabled. On the <a href=":locale-settings">Interface translation settings</a> page, the <em>Translation source</em> can be restricted to local files only, or to include the <a href=":server">Drupal translation server</a>. Although modules and themes may not be fully translated in all languages, new translations become available frequently. You can specify whether and how often to check for translation file updates and whether to overwrite existing translations on the <a href=":locale-settings">Interface translation settings</a> page. You can also manually import a translation file on the <a href=":import">Interface translation import</a> page.', [':import' => Url::fromRoute('locale.translate_import')->toString(), ':locale-settings' => Url::fromRoute('locale.settings')->toString(), ':languages' => Url::fromRoute('entity.configurable_language.collection')->toString(), ':server' => 'https://localize.drupal.org']) . '</dd>'; |
154 $output .= '<dt>' . t('Checking the translation status') . '</dt>'; | 157 $output .= '<dt>' . t('Checking the translation status') . '</dt>'; |
155 $output .= '<dd>' . t('You can check how much of the interface on your site is translated into which language on the <a href=":languages">Languages</a> page. On the <a href=":translation-updates">Available translation updates</a> page, you can check whether interface translation updates are available on the <a href=":server">Drupal translation server</a>.', [':languages' => \Drupal::url('entity.configurable_language.collection'), ':translation-updates' => \Drupal::url('locale.translate_status'), ':server' => 'https://localize.drupal.org']) . '<dd>'; | 158 $output .= '<dd>' . t('You can check how much of the interface on your site is translated into which language on the <a href=":languages">Languages</a> page. On the <a href=":translation-updates">Available translation updates</a> page, you can check whether interface translation updates are available on the <a href=":server">Drupal translation server</a>.', [':languages' => Url::fromRoute('entity.configurable_language.collection')->toString(), ':translation-updates' => Url::fromRoute('locale.translate_status')->toString(), ':server' => 'https://localize.drupal.org']) . '<dd>'; |
156 $output .= '<dt>' . t('Translating individual strings') . '</dt>'; | 159 $output .= '<dt>' . t('Translating individual strings') . '</dt>'; |
157 $output .= '<dd>' . t('You can translate individual strings directly on the <a href=":translate">User interface translation</a> page, or download the currently-used translation file for a specific language on the <a href=":export">Interface translation export</a> page. Once you have edited the translation file, you can then import it again on the <a href=":import">Interface translation import</a> page.', [':translate' => \Drupal::url('locale.translate_page'), ':export' => \Drupal::url('locale.translate_export'), ':import' => \Drupal::url('locale.translate_import')]) . '</dd>'; | 160 $output .= '<dd>' . t('You can translate individual strings directly on the <a href=":translate">User interface translation</a> page, or download the currently-used translation file for a specific language on the <a href=":export">Interface translation export</a> page. Once you have edited the translation file, you can then import it again on the <a href=":import">Interface translation import</a> page.', [':translate' => Url::fromRoute('locale.translate_page')->toString(), ':export' => Url::fromRoute('locale.translate_export')->toString(), ':import' => Url::fromRoute('locale.translate_import')->toString()]) . '</dd>'; |
158 $output .= '<dt>' . t('Overriding default English strings') . '</dt>'; | 161 $output .= '<dt>' . t('Overriding default English strings') . '</dt>'; |
159 $output .= '<dd>' . t('If translation is enabled for English, you can <em>override</em> the default English interface text strings in your site with other English text strings on the <a href=":translate">User interface translation</a> page. Translation is off by default for English, but you can turn it on by visiting the <em>Edit language</em> page for <em>English</em> from the <a href=":languages">Languages</a> page.', [':translate' => \Drupal::url('locale.translate_page'), ':languages' => \Drupal::url('entity.configurable_language.collection')]) . '</dd>'; | 162 $output .= '<dd>' . t('If translation is enabled for English, you can <em>override</em> the default English interface text strings in your site with other English text strings on the <a href=":translate">User interface translation</a> page. Translation is off by default for English, but you can turn it on by visiting the <em>Edit language</em> page for <em>English</em> from the <a href=":languages">Languages</a> page.', [':translate' => Url::fromRoute('locale.translate_page')->toString(), ':languages' => Url::fromRoute('entity.configurable_language.collection')->toString()]) . '</dd>'; |
160 $output .= '</dl>'; | 163 $output .= '</dl>'; |
161 return $output; | 164 return $output; |
162 | 165 |
163 case 'entity.configurable_language.collection': | 166 case 'entity.configurable_language.collection': |
164 return '<p>' . t('Interface translations are automatically imported when a language is added, or when new modules or themes are enabled. The report <a href=":update">Available translation updates</a> shows the status. Interface text can be customized in the <a href=":translate">user interface translation</a> page.', [':update' => \Drupal::url('locale.translate_status'), ':translate' => \Drupal::url('locale.translate_page')]) . '</p>'; | 167 return '<p>' . t('Interface translations are automatically imported when a language is added, or when new modules or themes are enabled. The report <a href=":update">Available translation updates</a> shows the status. Interface text can be customized in the <a href=":translate">user interface translation</a> page.', [':update' => Url::fromRoute('locale.translate_status')->toString(), ':translate' => Url::fromRoute('locale.translate_page')->toString()]) . '</p>'; |
165 | 168 |
166 case 'locale.translate_page': | 169 case 'locale.translate_page': |
167 $output = '<p>' . t('This page allows a translator to search for specific translated and untranslated strings, and is used when creating or editing translations. (Note: Because translation tasks involve many strings, it may be more convenient to <a title="User interface translation export" href=":export">export</a> strings for offline editing in a desktop Gettext translation editor.) Searches may be limited to strings in a specific language.', [':export' => \Drupal::url('locale.translate_export')]) . '</p>'; | 170 $output = '<p>' . t('This page allows a translator to search for specific translated and untranslated strings, and is used when creating or editing translations. (Note: Because translation tasks involve many strings, it may be more convenient to <a title="User interface translation export" href=":export">export</a> strings for offline editing in a desktop Gettext translation editor.) Searches may be limited to strings in a specific language.', [':export' => Url::fromRoute('locale.translate_export')->toString()]) . '</p>'; |
168 return $output; | 171 return $output; |
169 | 172 |
170 case 'locale.translate_import': | 173 case 'locale.translate_import': |
171 $output = '<p>' . t('Translation files are automatically downloaded and imported when <a title="Languages" href=":language">languages</a> are added, or when modules or themes are enabled.', [':language' => \Drupal::url('entity.configurable_language.collection')]) . '</p>'; | 174 $output = '<p>' . t('Translation files are automatically downloaded and imported when <a title="Languages" href=":language">languages</a> are added, or when modules or themes are enabled.', [':language' => Url::fromRoute('entity.configurable_language.collection')->toString()]) . '</p>'; |
172 $output .= '<p>' . t('This page allows translators to manually import translated strings contained in a Gettext Portable Object (.po) file. Manual import may be used for customized translations or for the translation of custom modules and themes. To customize translations you can download a translation file from the <a href=":url">Drupal translation server</a> or <a title="User interface translation export" href=":export">export</a> translations from the site, customize the translations using a Gettext translation editor, and import the result using this page.', [':url' => 'https://localize.drupal.org', ':export' => \Drupal::url('locale.translate_export')]) . '</p>'; | 175 $output .= '<p>' . t('This page allows translators to manually import translated strings contained in a Gettext Portable Object (.po) file. Manual import may be used for customized translations or for the translation of custom modules and themes. To customize translations you can download a translation file from the <a href=":url">Drupal translation server</a> or <a title="User interface translation export" href=":export">export</a> translations from the site, customize the translations using a Gettext translation editor, and import the result using this page.', [':url' => 'https://localize.drupal.org', ':export' => Url::fromRoute('locale.translate_export')->toString()]) . '</p>'; |
173 $output .= '<p>' . t('Note that importing large .po files may take several minutes.') . '</p>'; | 176 $output .= '<p>' . t('Note that importing large .po files may take several minutes.') . '</p>'; |
174 return $output; | 177 return $output; |
175 | 178 |
176 case 'locale.translate_export': | 179 case 'locale.translate_export': |
177 return '<p>' . t('This page exports the translated strings used by your site. An export file may be in Gettext Portable Object (<em>.po</em>) form, which includes both the original string and the translation (used to share translations with others), or in Gettext Portable Object Template (<em>.pot</em>) form, which includes the original strings only (used to create new translations with a Gettext translation editor).') . '</p>'; | 180 return '<p>' . t('This page exports the translated strings used by your site. An export file may be in Gettext Portable Object (<em>.po</em>) form, which includes both the original string and the translation (used to share translations with others), or in Gettext Portable Object Template (<em>.pot</em>) form, which includes the original strings only (used to create new translations with a Gettext translation editor).') . '</p>'; |
834 * | 837 * |
835 * @return int | 838 * @return int |
836 * FALSE on failure. Otherwise SAVED_NEW or SAVED_UPDATED. | 839 * FALSE on failure. Otherwise SAVED_NEW or SAVED_UPDATED. |
837 */ | 840 */ |
838 function locale_translation_update_file_history($file) { | 841 function locale_translation_update_file_history($file) { |
839 $status = db_merge('locale_file') | 842 $status = \Drupal::database()->merge('locale_file') |
840 ->key([ | 843 ->key([ |
841 'project' => $file->project, | 844 'project' => $file->project, |
842 'langcode' => $file->langcode, | 845 'langcode' => $file->langcode, |
843 ]) | 846 ]) |
844 ->fields([ | 847 ->fields([ |
861 * language code(s) are specified the conditions will be ANDed. | 864 * language code(s) are specified the conditions will be ANDed. |
862 * @param array $langcodes | 865 * @param array $langcodes |
863 * Language code(s) to be deleted from the file history. | 866 * Language code(s) to be deleted from the file history. |
864 */ | 867 */ |
865 function locale_translation_file_history_delete($projects = [], $langcodes = []) { | 868 function locale_translation_file_history_delete($projects = [], $langcodes = []) { |
866 $query = db_delete('locale_file'); | 869 $query = \Drupal::database()->delete('locale_file'); |
867 if (!empty($projects)) { | 870 if (!empty($projects)) { |
868 $query->condition('project', $projects, 'IN'); | 871 $query->condition('project', $projects, 'IN'); |
869 } | 872 } |
870 if (!empty($langcodes)) { | 873 if (!empty($langcodes)) { |
871 $query->condition('langcode', $langcodes, 'IN'); | 874 $query->condition('langcode', $langcodes, 'IN'); |
1148 \s*.+?\s*,\s* # match count argument | 1151 \s*.+?\s*,\s* # match count argument |
1149 (' . LOCALE_JS_STRING . ')\s*,\s* # match singular string argument | 1152 (' . LOCALE_JS_STRING . ')\s*,\s* # match singular string argument |
1150 ( # capture plural string argument | 1153 ( # capture plural string argument |
1151 (?: # non-capturing group to repeat string pieces | 1154 (?: # non-capturing group to repeat string pieces |
1152 (?: | 1155 (?: |
1153 \' # match start of single-quoted string | 1156 \'(?:\\\\\'|[^\'])*\' # match single-quoted string with any character except unescaped single-quote |
1154 (?:\\\\\'|[^\'])* # match any character except unescaped single-quote | |
1155 @count # match "@count" | |
1156 (?:\\\\\'|[^\'])* # match any character except unescaped single-quote | |
1157 \' # match end of single-quoted string | |
1158 | | 1157 | |
1159 " # match start of double-quoted string | 1158 "(?:\\\\"|[^"])*" # match double-quoted string with any character except unescaped double-quote |
1160 (?:\\\\"|[^"])* # match any character except unescaped double-quote | |
1161 @count # match "@count" | |
1162 (?:\\\\"|[^"])* # match any character except unescaped double-quote | |
1163 " # match end of double-quoted string | |
1164 ) | 1159 ) |
1165 (?:\s*\+\s*)? # match "+" with possible whitespace, for str concat | 1160 (?:\s*\+\s*)? # match "+" with possible whitespace, for str concat |
1166 )+ # match multiple because we supports concatenating strs | 1161 )+ # match multiple because we supports concatenating strs |
1167 )\s* # end capturing of plural string argument | 1162 )\s* # end capturing of plural string argument |
1168 (?:,\s*' . LOCALE_JS_OBJECT . '\s* # optionally capture string args | 1163 (?:,\s*' . LOCALE_JS_OBJECT . '\s* # optionally capture string args |
1182 } | 1177 } |
1183 | 1178 |
1184 // Add string from Drupal.formatPlural(). | 1179 // Add string from Drupal.formatPlural(). |
1185 foreach ($plural_matches[1] as $key => $string) { | 1180 foreach ($plural_matches[1] as $key => $string) { |
1186 $matches[] = [ | 1181 $matches[] = [ |
1187 'source' => _locale_strip_quotes($string) . LOCALE_PLURAL_DELIMITER . _locale_strip_quotes($plural_matches[2][$key]), | 1182 'source' => _locale_strip_quotes($string) . PoItem::DELIMITER . _locale_strip_quotes($plural_matches[2][$key]), |
1188 'context' => _locale_strip_quotes($plural_matches[3][$key]), | 1183 'context' => _locale_strip_quotes($plural_matches[3][$key]), |
1189 ]; | 1184 ]; |
1190 } | 1185 } |
1191 | 1186 |
1192 // Loop through all matches and process them. | 1187 // Loop through all matches and process them. |
1295 | 1290 |
1296 // Delete old file, if we have no translations anymore, or a different file to | 1291 // Delete old file, if we have no translations anymore, or a different file to |
1297 // be saved. | 1292 // be saved. |
1298 $locale_javascripts = \Drupal::state()->get('locale.translation.javascript') ?: []; | 1293 $locale_javascripts = \Drupal::state()->get('locale.translation.javascript') ?: []; |
1299 $changed_hash = !isset($locale_javascripts[$language->getId()]) || ($locale_javascripts[$language->getId()] != $data_hash); | 1294 $changed_hash = !isset($locale_javascripts[$language->getId()]) || ($locale_javascripts[$language->getId()] != $data_hash); |
1295 | |
1296 /** @var \Drupal\Core\File\FileSystemInterface $file_system */ | |
1297 $file_system = \Drupal::service('file_system'); | |
1298 | |
1300 if (!empty($locale_javascripts[$language->getId()]) && (!$data || $changed_hash)) { | 1299 if (!empty($locale_javascripts[$language->getId()]) && (!$data || $changed_hash)) { |
1301 file_unmanaged_delete($dir . '/' . $language->getId() . '_' . $locale_javascripts[$language->getId()] . '.js'); | 1300 try { |
1301 $file_system->delete($dir . '/' . $language->getId() . '_' . $locale_javascripts[$language->getId()] . '.js'); | |
1302 } | |
1303 catch (FileException $e) { | |
1304 // Ignore. | |
1305 } | |
1302 $locale_javascripts[$language->getId()] = ''; | 1306 $locale_javascripts[$language->getId()] = ''; |
1303 $status = 'deleted'; | 1307 $status = 'deleted'; |
1304 } | 1308 } |
1305 | 1309 |
1306 // Only create a new file if the content has changed or the original file got | 1310 // Only create a new file if the content has changed or the original file got |
1307 // lost. | 1311 // lost. |
1308 $dest = $dir . '/' . $language->getId() . '_' . $data_hash . '.js'; | 1312 $dest = $dir . '/' . $language->getId() . '_' . $data_hash . '.js'; |
1309 if ($data && ($changed_hash || !file_exists($dest))) { | 1313 if ($data && ($changed_hash || !file_exists($dest))) { |
1310 // Ensure that the directory exists and is writable, if possible. | 1314 // Ensure that the directory exists and is writable, if possible. |
1311 file_prepare_directory($dir, FILE_CREATE_DIRECTORY); | 1315 $file_system->prepareDirectory($dir, FileSystemInterface::CREATE_DIRECTORY); |
1312 | 1316 |
1313 // Save the file. | 1317 // Save the file. |
1314 if (file_unmanaged_save_data($data, $dest)) { | 1318 try { |
1315 $locale_javascripts[$language->getId()] = $data_hash; | 1319 if ($file_system->saveData($data, $dest)) { |
1316 // If we deleted a previous version of the file and we replace it with a | 1320 $locale_javascripts[$language->getId()] = $data_hash; |
1317 // new one we have an update. | 1321 // If we deleted a previous version of the file and we replace it with a |
1318 if ($status == 'deleted') { | 1322 // new one we have an update. |
1319 $status = 'updated'; | 1323 if ($status == 'deleted') { |
1324 $status = 'updated'; | |
1325 } | |
1326 // If the file did not exist previously and the data has changed we have | |
1327 // a fresh creation. | |
1328 elseif ($changed_hash) { | |
1329 $status = 'created'; | |
1330 } | |
1331 // If the data hash is unchanged the translation was lost and has to be | |
1332 // rebuilt. | |
1333 else { | |
1334 $status = 'rebuilt'; | |
1335 } | |
1320 } | 1336 } |
1321 // If the file did not exist previously and the data has changed we have | 1337 else { |
1322 // a fresh creation. | 1338 $locale_javascripts[$language->getId()] = ''; |
1323 elseif ($changed_hash) { | 1339 $status = 'error'; |
1324 $status = 'created'; | |
1325 } | 1340 } |
1326 // If the data hash is unchanged the translation was lost and has to be | 1341 } |
1327 // rebuilt. | 1342 catch (FileException $e) { |
1328 else { | 1343 // Do nothing. |
1329 $status = 'rebuilt'; | |
1330 } | |
1331 } | |
1332 else { | |
1333 $locale_javascripts[$language->getId()] = ''; | |
1334 $status = 'error'; | |
1335 } | 1344 } |
1336 } | 1345 } |
1337 | 1346 |
1338 // Save the new JavaScript hash (or an empty value if the file just got | 1347 // Save the new JavaScript hash (or an empty value if the file just got |
1339 // deleted). Act only if some operation was executed that changed the hash | 1348 // deleted). Act only if some operation was executed that changed the hash |