CPT Archive with core Category

You want display posts of a given type ({post_type}) from category ({term})
and structure of link should looks like this:

{post_type} / category / {term}

To avoid collisions with links to pages and “blog” posts, expression can not
start with ([^/]+), but should contain slug of post type entered explicitly.
This means a separate rule for each custom post type.

$regex = '^news/category/(.+?)/?$';

For the above expression, the $redirect should contain category_name and post_type parameters:

$redirect="index.php?category_name=$matches[1]&post_type=news";

And in case of a custom taxonomy (^news/custom_tax_slug/(.+?)/?$):

$redirect="index.php?CUSTOM_TAX_SLUG=$matches[1]&taxonomy=CUSTOM_TAX_SLUG&post_type=news";

To handle pagination you need another rule, which is an extended version of the above one.

$regex = '^news/category/(.+?)/page/?([0-9]{1,})/?$';
$redirect="index.php?category_name=$matches[1]&post_type=news&paged=$matches[2]";

All together:

function category_cpt_rewrites()
{
    add_rewrite_rule( '^news/category/(.+?)/page/?([0-9]{1,})/?$',
        'index.php?category_name=$matches[1]&post_type=news&paged=$matches[2]',
        'top'
    );
    add_rewrite_rule( '^news/category/(.+?)/?$',
        'index.php?category_name=$matches[1]&post_type=news',
        'top'
    );
}

Or:

function category_cpt_rewrites()
{
    $post_types = [ 'news' ];
    foreach ( $post_types as $cpt )
    {
        add_rewrite_rule( '^'. $cpt .'/category/(.+?)/page/?([0-9]{1,})/?$',
            'index.php?category_name=$matches[1]&post_type=". $cpt ."&paged=$matches[2]',
            'top'
        );
        add_rewrite_rule( '^'. $cpt .'/category/(.+?)/?$',
            'index.php?category_name=$matches[1]&post_type=" . $cpt,
            "top'
        );
    }
}