0

I'm on Joomla! 3.6.2 with Purity III template (JA). I try to use the glossary layout with Cyrillic alphabet but the php doesn't check the language and puts the articles in alphabet order only in English language.

Could somebody suggest a proper php coding for this?

Thanks!

Christina

Original glossary.php file located in templates/purity_iii/html/com_content/category:

<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_content
 *
 * @copyright   Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

defined('_JEXEC') or die;

require_once T3_TEMPLATE_PATH . '/helper.php';

// get featured items
$app      = JFactory::getApplication();
$params   = $this->params;
$catid = $app->input->getInt('id');

$items = JATemplateHelper::getArticles($params, $catid, 0);
$groups = array();
$other = 'others';
for ($group = 'a'; $group < 'z'; $group++) {
    $groups[$group] = array();
}
$groups[$other] = array();

foreach ($items as $item) {
    $title = clean_special_chars ($item->title);
    $firstChar = strtolower($title[0]);

    if ($firstChar < 'a' || $firstChar > 'z') {
        $groups[$other][] = $item;
    } else {
        $groups[$firstChar][] = $item;
    }
}

/* clean special char to put it into alphabet list */
function clean_special_chars ($s, $d=false) {
    if($d) $s = utf8_decode( $s );

    $chars = array(
        '_' => '/`|´|\^|~|¨|ª|º|©|®/',
        'a' => '/à|á|ả|ạ|ã|â|ầ|ấ|ẩ|ậ|ẫ|ă|ằ|ắ|ẳ|ặ|ẵ|ä|å|æ/',
        'd' => '/đ/',
        'e' => '/è|é|ẻ|ẹ|ẽ|ê|ề|ế|ể|ệ|ễ|ë/',
        'i' => '/ì|í|ỉ|ị|ĩ|î|ï/',
        'o' => '/ò|ó|ỏ|ọ|õ|ô|ồ|ố|ổ|ộ|ỗ|ö|ø/',
        'u' => '/ù|ú|û|ũ|ü|ů|ủ|ụ|ư|ứ|ừ|ữ|ử|ự/',
        'A' => '/À|Á|Ả|Ạ|Ã|Â|Ầ|Ấ|Ẩ|Ậ|Ẫ|Ă|Ằ|Ắ|Ẳ|Ặ|Ẵ|Ä|Å|Æ/',
        'D' => '/Đ/',
        'E' => '/È|É|Ẻ|Ẹ|Ẽ|Ê|Ề|Ế|Ể|Ệ|Ễ|Ê|Ë/',
        'I' => '/Ì|Í|Ỉ|Ị|Ĩ|Î|Ï/',
        'O' => '/Ò|Ó|Ỏ|Ọ|Õ|Ô|Ồ|Ố|Ổ|Ộ|Ỗ|Ö|Ø/',
        'U' => '/Ù|Ú|Û|Ũ|Ü|Ů|Ủ|Ụ|Ư|Ứ|Ừ|Ữ|Ử|Ự/',
        'c' => '/ć|ĉ|ç/',
        'C' => '/Ć|Ĉ|Ç/',
        'n' => '/ñ/',
        'N' => '/Ñ/',
        'y' => '/ý|ỳ|ỷ|ỵ|ỹ|ŷ|ÿ/',
        'Y' => '/Ý|Ỳ|Ỷ|Ỵ|Ỹ|Ŷ|Ÿ/'
    );

    return preg_replace( $chars, array_keys( $chars ), $s );
}
?>


<?php if ($this->params->get('show_page_heading', 1)) : ?>
    <div class="page-header clearfix">
        <h1 class="page-title"><?php echo $this->escape($this->params->get('page_heading')); ?> </h1>
    </div>
<?php endif; ?>

<?php if ($this->params->get('show_category_title', 1) or $this->params->get('page_subheading')) : ?>
    <div class="page-subheader clearfix">
        <h2 class="page-subtitle"><?php echo $this->escape($this->params->get('page_subheading')); ?>
            <?php if ($this->params->get('show_category_title')) : ?>
                <?php echo $this->category->title;?>
            <?php endif; ?>
        </h2>
    </div>
<?php endif; ?>

<?php if ($this->params->get('show_description', 1) || $this->params->def('show_description_image', 1)) : ?>
    <div class="category-desc clearfix">
        <?php if ($this->params->get('show_description_image') && $this->category->getParams()->get('image')) : ?>
            <img src="<?php echo $this->category->getParams()->get('image'); ?>"/>
        <?php endif; ?>
        <?php if ($this->params->get('show_description') && $this->category->description) : ?>
            <?php echo JHtml::_('content.prepare', $this->category->description, '', 'com_content.category'); ?>
        <?php endif; ?>
    </div>
<?php endif; ?>


<?php if ($params->get ('show_navigation', 1)) : ?>
    <div class="glossary-nav">
        <nav class="container">
            <ul class="nav nav-pills">
            <?php foreach ($groups as $group => $group_items): ?>
                <?php if (count ($group_items) || $params->get ('show_empty_group', 0)) : ?>
                <li><a href="#<?php echo $group ?>"><?php echo $group ?></a></li>
                <?php endif ?>
            <?php endforeach ?>
            </ul>
        </nav>
    </div>

    <script type="text/javascript">
        !function($) {
            $('.glossary-nav nav').affix({
                offset:{
                    top: function(){
                        return $('#t3-content').offset().top - $('#t3-mainnav').height();
                    }
                }
            });
        } (jQuery);
    </script>
<?php endif ?>

<div class="glossary-items">
<?php foreach ($groups as $group=>$group_items) : ?>
    <?php if (count ($group_items) || $params->get ('show_empty_group', 0)) {
        $this->group = $group;
        $this->group_items = $group_items;
        echo $this->loadTemplate ('group');
    } ?>
<?php endforeach ?>
</div>

<?php if ($params->get('show_detail_in_popup', 1)): ?>
<!-- Modal -->
<div class="modal fade" id="glossary-detail" tabindex="-1" role="dialog" aria-labelledby="glossary-detail-label" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                <h4 class="modal-title" id="glossary-detail-label">Modal title</h4>
            </div>
            <div class="modal-body">

            </div>
        </div><!-- /.modal-content -->
    </div><!-- /.modal-dialog -->
</div><!-- /.modal -->

<script type="text/javascript">
    !function ($) {
        // click to open popup
        var $a = $('.glossary-group-items ul li a');
        $a.click (function() {
            var url = this.href,
                label = this.innerHTML;
            // ajax to request content
            url += (/\?/.test(url) ? '&' : '?') + 'tmpl=component';
            $.ajax (url).done(function(data){
                $('#glossary-detail .modal-body').html($(data).find ('section.article-content'));
                $('#glossary-detail-label').html(label );
                $('#glossary-detail').modal ('show');
                console.log ($('.modal-content').height());
            });

            return false;
        })
    } (jQuery);
</script>
<?php endif ?>
  • What is the location of the file contents in your question? – Michael Yaeger Sep 5 '16 at 13:14
  • Also, do the Cyrillic characters display properly on your site, they just aren't sorted properly? – Michael Yaeger Sep 5 '16 at 13:17
  • @MichaelYaeger, thank you for your time! The Cyrillic characters display properly on the site, just not sorted properly. It's like it confuses the page language and takes it for a English instead of Bulgarian. Thereby it puts all articles in a group of "others" instead in an alphabet order. – Christina Sep 6 '16 at 6:44
  • The location of the file I posted is: templates/purity_iii/html/com_content/category File is glossary.php – Christina Sep 6 '16 at 6:55
1

The following lines of code are what determine your glossary headings:

$items = JATemplateHelper::getArticles($params, $catid, 0);
$groups = array();
$other = 'others';
for ($group = 'a'; $group < 'z'; $group++) {
    $groups[$group] = array();
}
$groups[$other] = array();

foreach ($items as $item) {
    $title = clean_special_chars ($item->title);
    $firstChar = strtolower($title[0]);

    if ($firstChar < 'a' || $firstChar > 'z') {
        $groups[$other][] = $item;
    } else {
        $groups[$firstChar][] = $item;
    }
}

The headings are stored in an array of letters a-z. All others are in "others." What you would need to do is add the characters in the Cyrillic alphabet to that array so those items don't get lumped into "others."


Step 1

After:

for ($group = 'a'; $group < 'z'; $group++) {  //LATIN SMALL LETTERS
    $groups[$group] = array();
}

Add:

for ($group = 'а'; $group < 'џ'; $group++) {  //CYRILLIC SMALL LETTERS
    $groups[$group] = array();
}

So, now in your array, you have 'a'-'z', 'а'-'џ', & 'others' stored as groups.


Step 2

Change:

$firstChar = strtolower($title[0]);

To:

$firstChar = mb_convert_case($title[0], MB_CASE_LOWER, "UTF-8");

This is because strtolower does not support Cyrillic characters. However, mb_convert_case is a slower PHP function, so there may be a delay in load times (you can help that by using the Joomla! cache setting).


Step 3

Change:

if ($firstChar < 'a' || $firstChar > 'z') {
    $groups[$other][] = $item;
} else {
    $groups[$firstChar][] = $item;
}

To:

if (($firstChar >= 'a' && $firstChar <= 'z' ) || ($firstChar >= 'а' && $firstChar <= 'џ' )) {
    $groups[$firstChar][] = $item;
} else {
    $groups[$other][] = $item;
}

This will make sure the items beginning with either Latin or Cyrillic characters are grouped properly.


Optional: I don't like the heading "others" displaying in the glossary as all the other headings are single characters. My preference would be to change $other = 'others'; to $other = '*'; or some other special character for better visual consistency.


Note 1: If you have items that begin with other special characters in the Cyrillic alphabet besides а-џ and you want them to be indexed accordingly (as opposed to being added to the '*' group, the code will get a bit more complex. I'm in favor of writing the simplest code instead of the most globally inclusive code. However, if you have these other cases, the code will need to be expanded.


Note 2: You will want to keep a local backup of the glossary.php file as it could possibly be overwritten if you update the template.


Reference: Unicode/UTF-8-character table - starting from code position 0400 (Cyrillic)

  • Thank you for your effort! I followed the steps and got this message: Fatal error: Maximum execution time of 30 seconds exceeded in /public_html/templates/purity_iii/html/com_content/category/glossary.php on line 27 On that line there is: for ($group = 'а'; $group < 'џ'; $group++) { //CYRILLIC SMALL LETTERS – Christina Sep 6 '16 at 15:18
  • For testing, try adding echo $group; in that loop and see what is output on the page. – Michael Yaeger Sep 6 '16 at 16:28
  • It looks like the page loaded this time, but didn't correctly group the item. Also, the echo $group; line inside the Cyrillic loop seems to be outputting a Latin 'z', which definitely isn't right. Are you sure you added the echo to the correct loop? If so, try changing all occurrences of the variable in the Cyrillic loop from $group to $Cgroup and see if that works. – Michael Yaeger Sep 7 '16 at 0:59
  • I'm sorry, I made a mistake, thought that I should replace for ($group = 'а'; $group < 'џ'; $group++) { //CYRILLIC SMALL LETTERS with echo $group; – Christina Sep 7 '16 at 6:40
0

here is a working solution! You are welcome !

header('Content-type: text/html; charset=utf-8');
mb_internal_encoding("UTF-8");
defined('_JEXEC') or die;

require_once T3_TEMPLATE_PATH . '/helper.php';

// get featured items
$app      = JFactory::getApplication();
$params   = $this->params;
$catid = $app->input->getInt('id');

$items = JATemplateHelper::getArticles($params, $catid, 0);
$groups = array();
$other = '*';
$letters = 'абвгдеёжзийклмнопрстуфхцчшщыэюяabcdefghijklmnopqrstuvwxyz';


for ($i = 0; $i < mb_strlen($letters, "UTF-8") ; $i++) {
$group = mb_substr($letters,$i, 1,"UTF-8");

  $groups[$group] = array();
}
$groups[$other] = array();

foreach ($items as $item) {
    $title = clean_special_chars ($item->title);

     $firstChar = mb_convert_case(mb_substr($title,0, 1,"UTF-8") , MB_CASE_LOWER, "UTF-8");

     if (array_key_exists($firstChar, $groups)){  
    $groups[$firstChar][] = $item;  
 } else {  
    $groups[$other][] = $item;  
 }

}
  • This site is not merely a collection of working snippets. This community is most helpful to researchers when working solutions are fully explained. Please edit your answer to explain how your answer works and why it is an advisable technique. Please also edit your snippet to implement proper code tabbing for clarity. – mickmackusa Jul 6 at 0:40
  • Your $group initializing loop could use some refinement. I recommend just 2 function calls versus your 114 mb_ function calls. You should be saving mb_strlen($letters, "UTF-8") as a variable and NOT recounting $letters after every iteration. Here is a demo that I have written to crystallize my advice: 3v4l.org/6Rcuq – mickmackusa Jul 6 at 1:09
0

Your task seems to desire a maximum of 27 characters in the glossary-nav container. For this reason, I feel the simplest way to way to generate this array of data is with:

$groups = array_fill_keys(array_merge(range('a','z'), ['other']), []);

Upon closer inspection of your snippet, I discovered that your for loop was not including the z letter -- a rather unfortunate and sneaky bug in that technique.


Next, you don't need to translate the entire title, nor do you need to translate lowercase AND uppercase characters. It will lead to less code bloat if you extract the first (potentialy multibyte) character, convert it to lowercase, then attempt to translate it to Latin before assigning the whole item object to its appropriate group. I will offer a basic translation array, but you may with to extend it with characters that you expect to encounter and declare the appropriate translation.

$translate = [
    'à' => 'a', 'á' => 'a', 'ả' => 'a', 'ạ' => 'a', 'ã' => 'a', 'â' => 'a', 'ầ' => 'a', 'ấ' => 'a', 'ẩ' => 'a', 'ậ' => 'a',     'ẫ' => 'a', 'ă' => 'a', 'ằ' => 'a', 'ắ' => 'a', 'ẳ' => 'a', 'ặ' => 'a', 'ẵ' => 'a', 'ä' => 'a', 'å' => 'a', 'æ' => 'a',
    'đ' => 'd',
    'è' => 'e', 'é' => 'e', 'ẻ' => 'e', 'ẹ' => 'e', 'ẽ' => 'e', 'ê' => 'e', 'ề' => 'e', 'ế' => 'e', 'ể' => 'e', 'ệ' => 'e', 'ễ' => 'e', 'ë' => 'e',
    'ì' => 'i', 'í' => 'i', 'ỉ' => 'i', 'ị' => 'i', 'ĩ' => 'i', 'î' => 'i', 'ï' => 'i',
    'ò' => 'o', 'ó' => 'o', 'ỏ' => 'o', 'ọ' => 'o', 'õ' => 'o', 'ô' => 'o', 'ồ' => 'o', 'ố' => 'o', 'ổ' => 'o', 'ộ' => 'o', 'ỗ' => 'o', 'ö' => 'o', 'ø' => 'o',
    'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ũ' => 'u', 'ü' => 'u', 'ů' => 'u', 'ủ' => 'u', 'ụ' => 'u', 'ư' => 'u', 'ứ' => 'u', 'ừ' => 'u', 'ữ' => 'u', 'ử' => 'u', 'ự' => 'u',
    'ć' => 'c', 'ĉ' => 'c', 'ç' => 'c', 'ç' => 'c',
    'ñ' => 'n',
    'ý' => 'y', 'ỳ' => 'y', 'ỷ' => 'y', 'ỵ' => 'y', 'ỹ' => 'y', 'ŷ' => 'y', 'ÿ' => 'y'
];

foreach ($items as $item) {
    $first = strtr(mb_strtolower(mb_substr($item->title, 0, 1, "UTF-8"), "UTF-8"), $translate);
    if (isset($groups[$first])) {
        $groups[$first] = $item;
    } else {
        $groups['other'] = $item;
    }
}

Here's a demonstration of my above suggestions: Demo

I have worked with multi-byte strings before, here are some of my related posts on Stackoverflow:

In the end, if you are still having translation issues, this may be an encoding issue that should be solved by UTF-8 All The Way Through.

As an aside, if you want to check if a group "is empty or not" (and you know that the element isset()) you can simply check with if ($group_items) { instead of if(count($group_items)) {. This will spare the iterated count() function calls and deliver the same evaluation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.