static protected function generateOutput( array $params, $subTreeLimit, $hideRootTag, $locale )
    {
        $subTreeLimit = (int) $subTreeLimit;
        $hideRootTag = (bool) $hideRootTag;
        $locale = (string) $locale;

        if ( empty( $locale ) )
             return array( 'status' => 'success', 'message' => '', 'tags' => array() );

        // @TODO Fix synonyms not showing up in autocomplete
        // when subtree limit is defined in class attribute
        if ( $subTreeLimit > 0 )
        {
            if ( $hideRootTag )
                $params['id'] = array( '<>', $subTreeLimit );

            $params['path_string'] = array( 'like', '%/' . $subTreeLimit . '/%' );
        }

        // first fetch tags that exist in selected locale
        /** @var eZTagsObject[] $tags */
        $tags = eZTagsObject::fetchList( $params, null, null, false, $locale );
        if ( !is_array( $tags ) )
            $tags = array();

        $tagsIDsToExclude = array_map(
            function ( $tag )
            {
                /** @var eZTagsObject $tag */
                return (int) $tag->attribute( 'id' );
            },
            $tags
        );

        // then fetch the rest of tags, but exclude already fetched ones
        // fetch with main translation to be consistent with eztags attribute content

        $customConds = eZTagsObject::fetchCustomCondsSQL( $params, true );
        if ( !empty( $tagsIDsToExclude ) )
            $customConds .= " AND " . eZDB::instance()->generateSQLINStatement( $tagsIDsToExclude, 'eztags.id', true, true, 'int' ) . " ";

        $tagsRest = eZPersistentObject::fetchObjectList(
            eZTagsObject::definition(), array(), $params,
            null, null, true, false,
            array(
                'DISTINCT eztags.*',
                array(
                    'operation' => 'eztags_keyword.keyword',
                    'name'      => 'keyword'
                ),
                array(
                    'operation' => 'eztags_keyword.locale',
                    'name'      => 'locale'
                )
            ),
            array( 'eztags_keyword' ),
            $customConds
        );

        if ( !is_array( $tagsRest ) )
            $tagsRest = array();

        // finally, return both set of tags as one list

        $tags = array_merge( $tags, $tagsRest );

        $returnArray = array(
            'status' => 'success',
            'message' => '',
            'tags' => array()
        );

        foreach ( $tags as $tag )
        {
            $returnArrayChild = array();
            $returnArrayChild['parent_id']   = $tag->attribute( 'parent_id' );
            $returnArrayChild['parent_name'] = $tag->hasParent( true ) ? $tag->getParent( true )->attribute( 'keyword' ) : '';
            $returnArrayChild['name']        = $tag->attribute( 'keyword' );
            $returnArrayChild['id']          = $tag->attribute( 'id' );
            $returnArrayChild['main_tag_id'] = $tag->attribute( 'main_tag_id' );
            $returnArrayChild['locale']      = $tag->attribute( 'current_language' );
            $returnArray['tags'][]           = $returnArrayChild;
        }

        return $returnArray;
    }