diff --git a/include/ajax.config.php b/include/ajax.config.php index c263bd4a253993240b27e4557060b1fadc689ae1..a4e11bf22ab1ea5bebbab361604edbf71e569065 100644 --- a/include/ajax.config.php +++ b/include/ajax.config.php @@ -46,7 +46,7 @@ class ConfigAjaxAPI extends AjaxController { 'has_rtl' => $rtl, 'lang_flag' => strtolower($info['flag'] ?: $locale ?: $sl), 'primary_lang_flag' => strtolower($primary_info['flag'] ?: $primary_locale ?: $primary_sl), - 'primary_language' => $primary, + 'primary_language' => Internationalization::rfc1766($primary), 'secondary_languages' => $cfg->getSecondaryLanguages(), 'page_size' => $thisstaff->getPageLimit(), ); @@ -70,7 +70,7 @@ class ConfigAjaxAPI extends AjaxController { 'lang' => $lang, 'short_lang' => $sl, 'has_rtl' => $rtl, - 'primary_language' => $cfg->getPrimaryLanguage(), + 'primary_language' => Internationalization::rfc1766($cfg->getPrimaryLanguage()), 'secondary_languages' => $cfg->getSecondaryLanguages(), ); diff --git a/include/class.i18n.php b/include/class.i18n.php index 94f6b679c41587f2c3c43272f6aa5b80137d2586..27486ba9b3db761ba91752cac6436f589094a690 100644 --- a/include/class.i18n.php +++ b/include/class.i18n.php @@ -408,6 +408,15 @@ class Internationalization { return $locale; } + static function rfc1766($what) { + if (is_array($what)) + return array_map(array(get_called_class(), 'rfc1766'), $what); + + $lr = explode('_', $what); + if (isset($lr[1])) + $lr[1] = strtoupper($lr[1]); + return implode('-', $lr); + } static function getTtfFonts() { if (!class_exists('Phar')) diff --git a/include/client/header.inc.php b/include/client/header.inc.php index db6e4006c2b72d9d8a06036189b3f35b65c6634f..c49e896395633f3b8a63e8eae3b62797a417592c 100644 --- a/include/client/header.inc.php +++ b/include/client/header.inc.php @@ -8,11 +8,17 @@ $signout_url = ROOT_PATH . "logout.php?auth=".$ost->getLinkToken(); header("Content-Type: text/html; charset=UTF-8"); ?> <!DOCTYPE html> -<html <?php +<html<?php if (($lang = Internationalization::getCurrentLanguage()) && ($info = Internationalization::getLanguageInfo($lang)) && (@$info['direction'] == 'rtl')) - echo 'dir="rtl" class="rtl"'; + echo ' dir="rtl" class="rtl"'; +if ($lang) { + $langs = array_unique(array($lang, $cfg->getPrimaryLanguage())); + $langs = Internationalization::rfc1766($langs); + echo ' lang="' . $lang . '"'; + header("Content-Language: ".implode(', ', $langs)); +} ?>> <head> <meta charset="utf-8"> @@ -48,6 +54,25 @@ if (($lang = Internationalization::getCurrentLanguage()) if($ost && ($headers=$ost->getExtraHeaders())) { echo "\n\t".implode("\n\t", $headers)."\n"; } + + // Offer alternate links for search engines + // @see https://support.google.com/webmasters/answer/189077?hl=en + if (($all_langs = Internationalization::getConfiguredSystemLanguages()) + && (count($all_langs) > 1) + ) { + $langs = Internationalization::rfc1766(array_keys($all_langs)); + $qs = array(); + parse_str($_SERVER['QUERY_STRING'], $qs); + foreach ($langs as $L) { + $qs['lang'] = $L; ?> + <link rel="alternate" href="//<?php echo $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; ?>?<?php + echo http_build_query($qs); ?>" hreflang="<?php echo $L; ?>" /> +<?php + } ?> + <link rel="alternate" href="//<?php echo $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; ?>" + hreflang="x-default"; +<?php + } ?> </head> <body> diff --git a/include/staff/header.inc.php b/include/staff/header.inc.php index 0945a916441d42a0b97f7034ced1bca34036983f..0e7bf469cb0f435d257b5bf40fd48f3068482073 100644 --- a/include/staff/header.inc.php +++ b/include/staff/header.inc.php @@ -2,11 +2,14 @@ header("Content-Type: text/html; charset=UTF-8"); if (!isset($_SERVER['HTTP_X_PJAX'])) { ?> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> -<html <?php +<html<?php if (($lang = Internationalization::getCurrentLanguage()) && ($info = Internationalization::getLanguageInfo($lang)) && (@$info['direction'] == 'rtl')) - echo 'dir="rtl" class="rtl"'; + echo ' dir="rtl" class="rtl"'; +if ($lang) { + echo ' lang="' . Internationalization::rfc1766($lang) . '"'; +} ?>> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> diff --git a/include/staff/templates/content-manage.tmpl.php b/include/staff/templates/content-manage.tmpl.php index 3fa4e8fcbd8c1554649c576d84f3ba74e74e4eca..e90b8ff874719aad5e688dc446e9ebd2df506319 100644 --- a/include/staff/templates/content-manage.tmpl.php +++ b/include/staff/templates/content-manage.tmpl.php @@ -27,7 +27,8 @@ if (count($langs) > 1) { ?> class="tab_content left-tabs" style="padding:0" lang="<?php echo $cfg->getPrimaryLanguage(); ?>"> <div class="error"><?php echo $errors['name']; ?></div> <input type="text" style="width: 100%; font-size: 14pt" name="name" value="<?php - echo Format::htmlchars($info['title']); ?>" /> + echo Format::htmlchars($info['title']); ?>" spellcheck="true" + lang="<?php echo $cfg->getPrimaryLanguage(); ?>" /> <div style="margin-top: 5px"> <div class="error"><?php echo $errors['body']; ?></div> <textarea class="richtext no-bar" name="body" @@ -46,7 +47,8 @@ if (count($langs) > 1) { ?> <input type="text" style="width: 100%; font-size: 14pt" name="trans[<?php echo $tag; ?>][title]" value="<?php echo Format::htmlchars($trans['title']); ?>" - placeholder="<?php echo __('Title'); ?>" /> + placeholder="<?php echo __('Title'); ?>" spellcheck="true" + lang="<?php echo $tag; ?>" /> <div style="margin-top: 5px"> <textarea class="richtext no-bar" data-direction=<?php echo $nfo['direction']; ?> data-root-context="<?php echo $content->getType(); ?>" diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php index 35fb34acacdc3334b36e1ffd7381ad9d82b72bd3..1bd831a38ee245e6b82adca315094aaf2c874b8f 100644 --- a/include/staff/ticket-view.inc.php +++ b/include/staff/ticket-view.inc.php @@ -433,7 +433,7 @@ $tcount = $ticket->getThreadEntries($types)->count(); </ul> <?php if ($role->hasPerm(TicketModel::PERM_REPLY)) { ?> - <form id="reply" class="tab_content" action="tickets.php?id=<?php + <form id="reply" class="tab_content spellcheck" action="tickets.php?id=<?php echo $ticket->getId(); ?>" name="reply" method="post" enctype="multipart/form-data"> <?php csrf_token(); ?> <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>"> @@ -622,7 +622,7 @@ $tcount = $ticket->getThreadEntries($types)->count(); </form> <?php } ?> - <form id="note" class="hidden tab_content" action="tickets.php?id=<?php + <form id="note" class="hidden tab_content spellcheck" action="tickets.php?id=<?php echo $ticket->getId(); ?>#note" name="note" method="post" enctype="multipart/form-data"> <?php csrf_token(); ?> <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>"> @@ -705,7 +705,7 @@ $tcount = $ticket->getThreadEntries($types)->count(); </form> <?php if ($role->hasPerm(TicketModel::PERM_TRANSFER)) { ?> - <form id="transfer" class="hidden tab_content" action="tickets.php?id=<?php + <form id="transfer" class="hidden tab_content spellcheck" action="tickets.php?id=<?php echo $ticket->getId(); ?>#transfer" name="transfer" method="post" enctype="multipart/form-data"> <?php csrf_token(); ?> <input type="hidden" name="ticket_id" value="<?php echo $ticket->getId(); ?>"> @@ -766,7 +766,7 @@ $tcount = $ticket->getThreadEntries($types)->count(); } ?> <?php if ($role->hasPerm(TicketModel::PERM_ASSIGN)) { ?> - <form id="assign" class="hidden tab_content" action="tickets.php?id=<?php + <form id="assign" class="hidden tab_content spellcheck" action="tickets.php?id=<?php echo $ticket->getId(); ?>#assign" name="assign" method="post" enctype="multipart/form-data"> <?php csrf_token(); ?> <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>"> diff --git a/js/redactor-osticket.js b/js/redactor-osticket.js index 23b1ee76a4aaed7305df84ed4f974294dbe53139..19ff52a6acb6906f9fca184f7f16fa40e2fc33e5 100644 --- a/js/redactor-osticket.js +++ b/js/redactor-osticket.js @@ -268,6 +268,12 @@ $(function() { 'tabFocus': false, 'toolbarFixedBox': true, 'focusCallback': function() { this.$box.addClass('no-pjax'); }, + 'initCallback': function() { + this.$editor.attr('spellcheck', 'true'); + var lang = this.$editor.closest('[lang]').attr('lang'); + if (lang) + this.$editor.attr('lang', lang); + }, 'linkSize': 100000, 'definedLinks': 'ajax.php/config/links' }, options||{}); diff --git a/scp/js/jquery.translatable.js b/scp/js/jquery.translatable.js index 08a28ae3c7e030fdf20c79a6882a6dfd55974019..08f754a1a5e85c0d50f9759edb458b1a2e7e8a7e 100644 --- a/scp/js/jquery.translatable.js +++ b/scp/js/jquery.translatable.js @@ -60,6 +60,7 @@ .focus($.proxy(function() { this.addClass('focus'); }, this.$container)) .blur($.proxy(function() { this.removeClass('focus'); }, this.$container)); getConfig().then($.proxy(function(c) { + this.attr({'spellcheck': 'true', 'lang': c.primary_language}) $('<span class="flag"></span>') .addClass('flag-' + c.primary_lang_flag) .insertAfter(this); @@ -113,6 +114,8 @@ .text(info.name) .prepend($('<span>').addClass('flag flag-'+info.flag)) .append($('<input type="text" data-lang="'+lang+'">') + .attr('lang', lang) + .attr('spellcheck', 'true') .attr('dir', info.direction || 'ltr') .on('change keydown', $.proxy(this.showCommit, this)) .val(text) diff --git a/scp/js/scp.js b/scp/js/scp.js index 24be12c76210a71a943c9c975995ad4802673c0d..502b7f06171a02f90eb2207df7c73867653d5b01 100644 --- a/scp/js/scp.js +++ b/scp/js/scp.js @@ -563,6 +563,16 @@ $(document).keydown(function(e) { } }); + +$(document).on('focus', 'form.spellcheck textarea, form.spellcheck input[type=text]', function() { + var $this = $(this); + if ($this.attr('lang') !== undefined) + return; + var lang = $(this).closest('[lang]').attr('lang'); + if (lang) + $(this).attr({'spellcheck':'true', 'lang': lang}); +}); + $.toggleOverlay = function (show) { if (typeof(show) === 'undefined') { return $.toggleOverlay(!$('#overlay').is(':visible'));