Issue #2050097 by Gábor Hojtsy, Wim Leers: Map CKEditor languages to Drupal languages.

8.0.x
webchick 2013-08-29 07:57:41 -07:00
parent 7e5119cd45
commit d4b4d27cd9
5 changed files with 127 additions and 6 deletions

View File

@ -2,3 +2,10 @@ services:
plugin.manager.ckeditor.plugin:
class: Drupal\ckeditor\CKEditorPluginManager
arguments: ['@container.namespaces', '@cache.cache', '@language_manager', '@module_handler']
cache.ckeditor.languages:
class: Drupal\Core\Cache\CacheBackendInterface
tags:
- { name: cache.bin }
factory_method: get
factory_service: cache_factory
arguments: [ckeditor]

View File

@ -7,8 +7,11 @@
namespace Drupal\ckeditor\Plugin\Editor;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\ckeditor\CKEditorPluginManager;
use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageManager;
use Drupal\editor\Plugin\EditorBase;
use Drupal\editor\Annotation\Editor;
use Drupal\Core\Annotation\Translation;
@ -27,6 +30,20 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
*/
class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
/**
* The module handler to invoke hooks on.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManager
*/
protected $languageManager;
/**
* The CKEditor plugin manager.
*
@ -45,17 +62,30 @@ class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
* The plugin implementation definition.
* @param \Drupal\ckeditor\CKEditorPluginManager $ckeditor_plugin_manager
* The CKEditor plugin manager.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler to invoke hooks on.
* @param \Drupal\Core\Language\LanguageManager $language_manager
* The language manager.
*/
public function __construct(array $configuration, $plugin_id, array $plugin_definition, CKEditorPluginManager $ckeditor_plugin_manager) {
public function __construct(array $configuration, $plugin_id, array $plugin_definition, CKEditorPluginManager $ckeditor_plugin_manager, ModuleHandlerInterface $module_handler, LanguageManager $language_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->ckeditorPluginManager = $ckeditor_plugin_manager;
$this->moduleHandler = $module_handler;
$this->languageManager = $language_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container->get('plugin.manager.ckeditor.plugin'));
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('plugin.manager.ckeditor.plugin'),
$container->get('module_handler'),
$container->get('language_manager')
);
}
/**
@ -191,8 +221,6 @@ class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
* {@inheritdoc}
*/
public function getJSSettings(EditorEntity $editor) {
$language_interface = language(Language::TYPE_INTERFACE);
$settings = array();
// Get the settings for all enabled plugins, even the internal ones.
@ -202,13 +230,23 @@ class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
$settings += $plugin->getConfig($editor);
}
// Fall back on English if no matching language code was found.
$display_langcode = 'en';
// Map the interface language code to a CKEditor translation.
$ckeditor_langcodes = $this->getLangcodes();
$language_interface = $this->languageManager->getLanguage(Language::TYPE_INTERFACE);
if (isset($ckeditor_langcodes[$language_interface->id])) {
$display_langcode = $ckeditor_langcodes[$language_interface->id];
}
// Next, set the most fundamental CKEditor settings.
$external_plugin_files = $this->ckeditorPluginManager->getEnabledPluginFiles($editor);
$settings += array(
'toolbar' => $this->buildToolbarJSSetting($editor),
'contentsCss' => $this->buildContentsCssJSSetting($editor),
'extraPlugins' => implode(',', array_keys($external_plugin_files)),
'language' => $language_interface->id,
'language' => $display_langcode,
// Configure CKEditor to not load styles.js. The StylesCombo plugin will
// set stylesSet according to the user's settings, if the "Styles" button
// is enabled. We cannot get rid of this until CKEditor will stop loading
@ -227,6 +265,52 @@ class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
return $settings;
}
/**
* Returns a list of language codes supported by CKEditor.
*
* @return array
* An associative array keyed by language codes.
*/
public function getLangcodes() {
// Cache the file system based language list calculation because this would
// be expensive to calculate all the time. The cache is cleared on core
// upgrades which is the only situation the CKEditor file listing should
// change.
$langcode_cache = cache('ckeditor.languages')->get('langcodes');
if (!empty($langcode_cache)) {
$langcodes = $langcode_cache->data;
}
if (empty($langcodes)) {
$langcodes = array();
// Collect languages included with CKEditor based on file listing.
$ckeditor_languages = glob(DRUPAL_ROOT . '/core/assets/vendor/ckeditor/lang/*.js');
foreach ($ckeditor_languages as $language_filename) {
$langcode = basename($language_filename, '.js');
$langcodes[$langcode] = $langcode;
}
cache('ckeditor.languages')->set('langcodes', $langcodes);
}
// Get language mapping if available to map to Drupal language codes.
// This is configurable in the user interface and not expensive to get, so
// we don't include it in the cached language list.
$language_mappings = $this->moduleHandler->moduleExists('language') ? language_get_browser_drupal_langcode_mappings() : array();
foreach ($langcodes as $langcode) {
// If this language code is available in a Drupal mapping, use that to
// compute a possibility for matching from the Drupal langcode to the
// CKEditor langcode.
// e.g. CKEditor uses the langcode 'no' for Norwegian, Drupal uses 'nb'.
// This would then remove the 'no' => 'no' mapping and replace it with
// 'nb' => 'no'. Now Drupal knows which CKEditor translation to load.
if (isset($language_mappings[$langcode]) && !isset($langcodes[$language_mappings[$langcode]])) {
$langcodes[$language_mappings[$langcode]] = $langcode;
unset($langcodes[$langcode]);
}
}
return $langcodes;
}
/**
* {@inheritdoc}
*/

View File

@ -309,6 +309,27 @@ class CKEditorTest extends DrupalUnitTestBase {
$this->assertIdentical($expected, $stylescombo_plugin->getConfig($editor), '"StylesCombo" plugin configuration built correctly for customized toolbar.');
}
/**
* Tests language list availability in CKEditor.
*/
function testLanguages() {
// Get CKEditor supported language codes and spot-check.
$this->enableModules(array('language'));
config_install_default_config('module', 'language');
$langcodes = $this->ckeditor->getLangcodes();
// Language codes transformed with browser mappings.
$this->assertTrue($langcodes['pt-pt'] == 'pt', '"pt" properly resolved');
$this->assertTrue($langcodes['zh-hans'] == 'zh-cn', '"zh-hans" properly resolved');
// Language code both in Drupal and CKEditor.
$this->assertTrue($langcodes['gl'] == 'gl', '"gl" properly resolved');
// Language codes only in CKEditor.
$this->assertTrue($langcodes['en-au'] == 'en-au', '"en-au" properly resolved');
$this->assertTrue($langcodes['sr-latn'] == 'sr-latn', '"sr-latn" properly resolved');
}
protected function getDefaultInternalConfig() {
return array(
'customConfig' => '',

View File

@ -1,5 +1,8 @@
# Browsers use different language codes to refer to the same languages,
# these defaults handles the most common cases.
no: 'nb' # Norwegian
pt: 'pt-pt' # Portuguese
zh: 'zh-hans' # Default Chinese to simplified script
zh-tw: 'zh-hant' # Taiwan Chinese in traditional script
zh-hk: 'zh-hant' # Hong Kong Chinese in traditional script
zh-mo: 'zh-hant' # Macao Chinese in traditional script

View File

@ -111,7 +111,13 @@ function language_from_browser($languages) {
// so we multiply the qvalue by 1000 to avoid floating point comparisons.
$langcode = strtolower($match[1]);
$qvalue = isset($match[2]) ? (float) $match[2] : 1;
$browser_langcodes[$langcode] = (int) ($qvalue * 1000);
// Take the highest qvalue for this langcode. Although the request
// supposedly contains unique langcodes, our mapping possibly resolves
// to the same langcode for different qvalues. Keep the highest.
$browser_langcodes[$langcode] = max(
(int) ($qvalue * 1000),
(isset($browser_langcodes[$langcode]) ? $browser_langcodes[$langcode] : 0)
);
}
}