View source
<?php
define('L10N_CLIENT_STRINGS', 100);
function l10n_client_menu() {
$items = array();
$items['l10n_client/save'] = array(
'title' => 'Save string',
'page callback' => 'l10n_client_save_string',
'access callback' => 'l10n_client_access',
'type' => MENU_CALLBACK,
);
$items['locale'] = array(
'title' => 'Translate strings',
'page callback' => 'l10n_client_translate_page',
'access callback' => 'l10n_client_access',
);
$items['locale/untranslated'] = array(
'title' => 'Untranslated',
'page arguments' => array(
'untranslated',
),
'access callback' => 'l10n_client_access',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['locale/translated'] = array(
'title' => 'Translated',
'page arguments' => array(
'translated',
),
'access callback' => 'l10n_client_access',
'type' => MENU_LOCAL_TASK,
'weight' => 10,
);
$items['admin/build/translate/import/file'] = array(
'title' => 'Import file',
'page callback' => 'locale_inc_callback',
'page arguments' => array(
'drupal_get_form',
'locale_translate_import_form',
),
'access arguments' => array(
'translate interface',
),
'weight' => -5,
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/build/translate/import/package'] = array(
'title' => 'Reimport packages',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'l10n_client_import_package_form',
),
'access arguments' => array(
'translate interface',
),
'weight' => 5,
'type' => MENU_LOCAL_TASK,
);
$items['admin/settings/language/configure/language'] = array(
'title' => 'Language negotiation',
'page callback' => 'locale_inc_callback',
'page arguments' => array(
'drupal_get_form',
'locale_languages_configure_form',
),
'access arguments' => array(
'administer languages',
),
'weight' => -10,
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/settings/language/configure/l10n_client'] = array(
'title' => 'Localization sharing',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'l10n_client_settings_form',
),
'access arguments' => array(
'administer languages',
),
'weight' => 5,
'type' => MENU_LOCAL_TASK,
);
return $items;
}
function l10n_client_perm() {
return array(
'use on-page translation',
'submit translations to localization server',
);
}
function l10n_client_init() {
global $conf, $language;
if (l10n_client_access()) {
$conf['locale_cache_strings'] = 0;
locale(NULL, NULL, TRUE);
drupal_add_css(drupal_get_path('module', 'l10n_client') . '/l10n_client.css', 'module');
drupal_add_js(drupal_get_path('module', 'l10n_client') . '/jquery.hotkeys.js', 'module');
drupal_add_js(drupal_get_path('module', 'l10n_client') . '/jquery.cookie.js', 'module');
drupal_add_js(drupal_get_path('module', 'l10n_client') . '/l10n_client.js', 'module');
drupal_add_js('misc/textarea.js', 'module');
}
}
function l10n_client_access($account = NULL) {
if (!isset($account)) {
global $user;
$account = $user;
}
return user_access('use on-page translation', $account) && empty($account->l10n_client_disabled);
}
function l10n_client_translate_page($display_translated = FALSE, $textgroup = 'default', $allow_translation = TRUE) {
global $language;
$header = $table = array();
$output = '';
$sql = "SELECT s.source, t.translation, t.language FROM {locales_source} s ";
if ($display_translated) {
$header = array(
t('Source string'),
t('Translation'),
);
$sql .= "INNER JOIN {locales_target} t ON s.lid = t.lid WHERE t.language = '%s' AND t.translation != '' ";
}
else {
$header = array(
t('Source string'),
);
$sql .= "LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = '%s' WHERE (t.translation IS NULL OR t.translation = '') ";
}
if (!empty($textgroup)) {
$sql .= "AND s.textgroup ='" . db_escape_string($textgroup) . "' ";
}
$sql .= 'ORDER BY s.source';
$allow_translation = $textgroup == 'default' && $language->language == 'en' ? FALSE : $allow_translation;
$result = pager_query($sql, L10N_CLIENT_STRINGS, 0, NULL, $language->language);
while ($data = db_fetch_object($result)) {
if ($display_translated) {
$table[] = array(
check_plain($data->source),
check_plain($data->translation),
);
if ($allow_translation) {
l10_client_add_string_to_page($data->source, $data->translation, $textgroup);
}
}
else {
$table[] = array(
check_plain($data->source),
);
if ($allow_translation) {
l10_client_add_string_to_page($data->source, TRUE, $textgroup);
}
}
}
if (!empty($table)) {
$output .= $pager = theme('pager', NULL, L10N_CLIENT_STRINGS);
$output .= theme('table', $header, $table);
$output .= $pager;
}
else {
$output .= t('No strings found to translate.');
}
return $output;
}
function l10n_client_footer() {
global $conf, $language;
if (l10n_client_access() && ($page_strings = _l10n_client_page_strings())) {
$l10n_strings = array();
foreach ($page_strings as $textgroup => $group_strings) {
foreach ($group_strings as $string => $translation) {
$l10n_strings[] = array(
$string,
$translation,
$textgroup,
);
}
}
array_multisort($l10n_strings);
$string_list = _l10n_client_string_list($l10n_strings);
$l10n_form = drupal_get_form('l10n_client_form', $l10n_strings);
$l10n_search = drupal_get_form('l10n_client_search_form');
$l10n_dom = _l10n_client_dom_strings($l10n_strings);
$string_label = '<h2>' . t('Page Text') . '</h2>';
$source_label = '<h2>' . t('Source') . '</h2>';
$translation_label = '<h2>' . t('Translation to %language', array(
'%language' => $language->native,
)) . '</h2>';
$toggle_label = t('Translate Text');
$output = "\n <div id='l10n-client' class='hidden'>\n <div class='labels'>\n <span class='toggle'>{$toggle_label}</span>\n <div class='label strings'>{$string_label}</div>\n <div class='label source'>{$source_label}</div>\n <div class='label translation'>{$translation_label}</div>\n </div>\n <div id='l10n-client-string-select'>\n {$string_list}\n {$l10n_search}\n </div>\n <div id='l10n-client-string-editor'>\n <div class='source'>\n <div class='source-text'></div>\n </div>\n <div class='translation'>\n {$l10n_form}\n </div>\n </div>\n </div>\n {$l10n_dom}\n ";
return $output;
}
}
function l10_client_add_string_to_page($source = NULL, $translation = NULL, $textgroup = 'default') {
static $strings = array();
if (isset($source)) {
$strings[$textgroup][$source] = $translation;
}
else {
return $strings;
}
}
function _l10n_client_page_strings() {
global $language;
$strings = l10_client_add_string_to_page();
if (arg(0) != 'locale' && is_array($locale = locale()) && isset($locale[$language->language])) {
$strings += array(
'default' => array(),
);
$strings['default'] = array_merge($strings['default'], $locale[$language->language]);
$result = db_query("SELECT s.source, t.translation, s.textgroup FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = '%s' WHERE s.location = '%s'", $language->language, request_uri());
while ($data = db_fetch_object($result)) {
if (!array_key_exists($data->source, $strings[$data->textgroup])) {
$strings[$data->textgroup][$data->source] = empty($data->translation) ? TRUE : $data->translation;
}
}
}
return $strings;
}
function _l10n_client_dom_strings($strings) {
$output = '';
foreach ($strings as $values) {
$source = $values[0] === TRUE ? '' : htmlspecialchars($values[0], ENT_NOQUOTES, 'UTF-8');
$target = $values[1] === TRUE ? '' : htmlspecialchars($values[1], ENT_NOQUOTES, 'UTF-8');
$textgroup = $values[2];
$output .= "<div><span class='source'>{$source}</span><span class='target'>{$target}</span><span class='textgroup'>{$textgroup}</span></div>";
}
return "<div id='l10n-client-data'>{$output}</div>";
}
function _l10n_client_string_list($strings) {
foreach ($strings as $values) {
if ($values[1] === TRUE) {
$str_class = 'untranslated';
}
else {
$str_class = 'translated';
}
$original = $values[1] === TRUE ? $values[0] : $values[1];
$string = strip_tags($original);
if (empty($string)) {
$string = htmlspecialchars(truncate_utf8($original, 78, TRUE, TRUE), ENT_NOQUOTES, 'UTF-8');
}
else {
$string = truncate_utf8($string, 78, TRUE, TRUE);
}
$select_list[] = "<li class='{$str_class}'>{$string}</li>";
}
$output = implode("\n", $select_list);
return "<ul class='string-list'>{$output}</ul>";
}
function l10n_client_form($form_id, $strings) {
global $language;
$form = array();
$form['#action'] = url('l10n_client/save');
$form['target'] = array(
'#type' => 'textarea',
'#resizable' => false,
'#rows' => 6,
'#attributes' => array(
'class' => 'translation-target',
),
);
$form['save'] = array(
'#value' => t('Save translation'),
'#type' => 'submit',
);
$form['textgroup'] = array(
'#type' => 'hidden',
'#value' => 'default',
);
$form['copy'] = array(
'#type' => 'button',
'#id' => 'l10n-client-edit-copy',
'#attributes' => array(
'class' => 'form-submit edit-copy',
),
'#value' => t('Copy source'),
);
$form['clear'] = array(
'#type' => 'button',
'#id' => 'l10n-client-edit-clear',
'#attributes' => array(
'class' => 'form-submit edit-clear',
),
'#value' => t('Clear'),
);
return $form;
}
function l10n_client_search_form() {
global $language;
$form = array();
$form['search'] = array(
'#type' => 'textfield',
'#attributes' => array(
'class' => 'string-search',
),
);
$form['clear-button'] = array(
'#type' => 'button',
'#id' => 'l10n-client-search-filter-clear',
'#attributes' => array(
'class' => 'form-submit',
),
'#value' => t('X'),
);
return $form;
}
function l10n_client_save_string() {
global $user, $language;
if (l10n_client_access()) {
if (isset($_POST['source']) && isset($_POST['target']) && isset($_POST['textgroup']) && !empty($_POST['form_token']) && drupal_valid_token($_POST['form_token'], 'l10n_client_form')) {
include_once 'includes/locale.inc';
$report = array(
'skips' => 0,
'additions' => 0,
'updates' => 0,
'deletes' => 0,
);
_locale_import_one_string_db($report, $language->language, $_POST['source'], $_POST['target'], $_POST['textgroup'], NULL, LOCALE_IMPORT_OVERWRITE);
cache_clear_all('locale:', 'cache', TRUE);
_locale_invalidate_js($language->language);
if (variable_get('l10n_client_use_server', FALSE) && user_access('submit translations to localization server') && !empty($user->l10n_client_key)) {
l10n_client_submit_translation($language->language, $_POST['source'], $_POST['target'], $user->l10n_client_key, l10n_client_user_token($user));
}
}
}
}
function l10n_client_import_package_form(&$form_state) {
$names = locale_language_list('name', TRUE);
unset($names['en']);
if (!count($names)) {
drupal_set_message(t('No languages set up to reimport packages into.'), 'warning');
return array();
}
$form = array();
$form['reimport'] = array(
'#type' => 'fieldset',
'#title' => t('Reimport translation packages'),
);
$form['reimport']['langcodes'] = array(
'#type' => 'checkboxes',
'#title' => t('Language packages'),
'#description' => t('Choose language packages to reimport translations from. All files of the packages should be already uncompressed to the Drupal directories. All translation files will be imported for enabled modules and themes and will be imported to the built-in interface textgroup.'),
'#options' => $names,
'#required' => TRUE,
);
$form['reimport']['textgroups'] = array(
'#type' => 'checkboxes',
'#title' => t('Clean up textgroups in database before reimport'),
'#description' => t('If checked, all translations for the given language and selected textgroups will be deleted from the database first, and you will loose all your customized translations and those not available in the files being imported. Use with extreme caution.'),
'#default_value' => array(),
'#options' => module_invoke_all('locale', 'groups'),
);
$form['reimport']['submit'] = array(
'#type' => 'submit',
'#value' => t('Reimport packages'),
);
return $form;
}
function l10n_client_import_package_form_submit($form, &$form_state) {
if (!empty($form_state['values']['textgroups'])) {
$langcodes = array_keys(array_filter($form_state['values']['langcodes']));
$textgroups = array_keys(array_filter($form_state['values']['textgroups']));
if (!empty($textgroups)) {
db_query("DELETE FROM {locales_target} WHERE language IN (" . db_placeholders($langcodes, 'varchar') . ") AND lid IN (SELECT lid FROM {locales_source} WHERE textgroup IN (" . db_placeholders($textgroups, 'varchar') . "))", array_merge($langcodes, $textgroups));
db_query("DELETE FROM {locales_source} WHERE lid NOT IN (SELECT lid FROM {locales_target})");
}
}
include_once 'includes/locale.inc';
foreach (array_filter($form_state['values']['langcodes']) as $langcode) {
if ($batch = locale_batch_by_language($langcode, '_locale_batch_language_finished')) {
batch_set($batch);
}
}
$form_state['redirect'] = 'admin/build/translate';
}
function l10n_client_settings_form() {
$form = array();
$form['l10n_client_use_server'] = array(
'#title' => t('Enable sharing translations with server'),
'#type' => 'checkbox',
'#default_value' => variable_get('l10n_client_use_server', FALSE),
);
$form['l10n_client_server'] = array(
'#title' => t('Address of localization server to use'),
'#type' => 'textfield',
'#description' => t('Each translation submission will also be submitted to the server, if the personal key is set up for the submitter in their user profile. We suggest you set this to <a href="@localize">http://localize.drupal.org</a> to share with the greater Drupal community.', array(
'@localize' => 'http://localize.drupal.org',
)),
'#default_value' => variable_get('l10n_client_server', ''),
);
return system_settings_form($form);
}
function l10n_client_settings_form_validate($form, &$form_state) {
if ($form_state['values']['l10n_client_use_server']) {
if (!empty($form_state['values']['l10n_client_server'])) {
$response = xmlrpc($form_state['values']['l10n_client_server'] . '/xmlrpc.php', 'l10n.server.test', '2.0');
if ($response && !empty($response['name']) && !empty($response['version'])) {
if (empty($response['supported']) || !$response['supported']) {
form_set_error('l10n_client_server', t('The given server could not handle the v2.0 remote submission API.'));
}
else {
drupal_set_message(t('Verified that the specified server can handle remote string submissions. Supported languages: %languages.', array(
'%languages' => $response['languages'],
)));
}
}
else {
form_set_error('l10n_client_server', t('Invalid localization server address specified. Make sure you specified the right server address.'));
}
}
else {
form_set_error('l10n_client_server', t('You should provide a server address, such as http://localize.drupal.org'));
}
}
}
function l10n_client_user($type, &$edit, &$account, $category = NULL) {
if ($type == 'form' && $category == 'account') {
$items = $form = array();
if (variable_get('l10n_client_use_server', FALSE) && user_access('submit translations to localization server', $account)) {
$server_link = variable_get('l10n_client_server', '') . '?q=translate/remote/userkey/' . l10n_client_user_token($account);
$items['l10n_client_key'] = array(
'#type' => 'textfield',
'#title' => t('Your Localization Server API key'),
'#default_value' => !empty($account->l10n_client_key) ? $account->l10n_client_key : '',
'#description' => t('This is a unique key that will allow you to send translations to the remote server. To get your API key go to !server-link.', array(
'!server-link' => l($server_link, $server_link),
)),
);
}
if (user_access('use on-page translation', $account)) {
$items['l10n_client_disabled'] = array(
'#type' => 'checkbox',
'#title' => t('Hide on-page translation from you'),
'#default_value' => !empty($account->l10n_client_disabled),
);
}
if (!empty($items)) {
$form = array(
'l10n_client' => array(
'#type' => 'fieldset',
'#title' => t('Localization client'),
'#weight' => 1,
'items' => $items,
),
);
}
return $form;
}
elseif ($type == 'submit' && $category == 'account' && isset($edit['l10n_client_key'])) {
$edit['l10n_client_key'] = trim($edit['l10n_client_key']);
}
}
function l10n_client_user_token($account = NULL) {
global $user;
$account = isset($account) ? $account : $user;
return md5('l10n_client' . $account->uid . drupal_get_private_key());
}
function l10n_client_submit_translation($langcode, $source, $translation, $user_key, $user_token) {
$server_uid = current(split(':', $user_key));
$signature = md5($user_key . $langcode . $source . $translation . $user_token);
$response = xmlrpc(variable_get('l10n_client_server', '') . '/xmlrpc.php', 'l10n.submit.translation', $langcode, $source, $translation, (int) $server_uid, $user_token, $signature);
if (!empty($response) && isset($response['status'])) {
if ($response['status']) {
watchdog('l10n_client', 'Translation sent and accepted by remote server.');
}
else {
watchdog('l10n_client', 'Translation rejected by remote server. Reason: %reason', array(
'%reason' => $response['reason'],
), WATCHDOG_WARNING);
}
return $response['status'];
}
else {
watchdog('l10n_client', 'The connection with the remote server failed with the following error: %error_code: %error_message.', array(
'%error_code' => xmlrpc_errno(),
'%error_message' => xmlrpc_error_msg(),
), WATCHDOG_ERROR);
return FALSE;
}
}