Getting Page Content Using Shortcode

First, I will add here 3 resources for learning most of the things you need about shortcodes:

Official WP Docs

A good article

Another article that I wrote

The idea is that you don’t echo the content in your shortcode, but you return it so that WordPress can only print it where it is needed.

For your specific shortcode, the code should look look like this:

add_shortcode('week_menu','week_menu');
function week_menu() {

    $week = date("W");

    $year = date("Y");

    $page_id = 3947;

    $page_object = get_post($page_id);
    
    $html="";

    $html += '<div class="weekmanu-wrapper"><div class="weekmenu-content"><h2 class="h2wm">Menu for '.$year.', week '.$week.'.</h2></div>';

    $html += $page_object->post_content;

    $html += '</div>';

    return $html;
}

Another way you could write this using the ob_ functions . Doing it this way, you no longer have to add all the content in a variable, but you can echo it and the result will be stored in the output buffer for being retrieved in the return statement.

add_shortcode('week_menu','week_menu');
function week_menu() {

    $date_string = date("Y, \w\e\e\k W");

    $page_id = 3947;

    $page_object = get_post($page_id);
    
    // Here we turn on output buffering.
    // Anything that is being output ( echoed ) after this line will be 
    // caught by the buffer.
    // https://www.php.net/manual/en/function.ob-start.php
    ob_start();
    ?>
    <div class="weekmanu-wrapper">
      <div class="weekmenu-content">
        <h2 class="h2wm">Menu for <?php echo $date_string ?>.</h2>
      </div>

      <?php echo $page_object->post_content; ?>
    </div>
    <?php

    // Here we get the contents of the output buffer and clean (erase) the buffer contents.
    // After this line, echoing starts working normally again. 
    // https://www.php.net/manual/en/function.ob-get-clean.php
    return ob_get_clean();
}