diff --git src/wp-includes/class-wp-post-type.php src/wp-includes/class-wp-post-type.php
index ba37a02fb5..595120488c 100644
--- src/wp-includes/class-wp-post-type.php
+++ src/wp-includes/class-wp-post-type.php
@@ -13,6 +13,10 @@
  * @since 4.6.0
  *
  * @see register_post_type()
+ *
+ * @property string label
+ * @property object labels
+ * @property string description
  */
 final class WP_Post_Type {
 	/**
@@ -27,9 +31,9 @@ final class WP_Post_Type {
 	 * Name of the post type shown in the menu. Usually plural.
 	 *
 	 * @since 4.6.0
-	 * @var string $label
+	 * @var callable|string $_label
 	 */
-	public $label;
+	private $_label;
 
 	/**
 	 * Labels object for this post type.
@@ -40,9 +44,9 @@ final class WP_Post_Type {
 	 * @see get_post_type_labels()
 	 *
 	 * @since 4.6.0
-	 * @var object $labels
+	 * @var callable|object $_labels
 	 */
-	public $labels;
+	private $_labels;
 
 	/**
 	 * A short descriptive summary of what the post type is.
@@ -50,9 +54,9 @@ final class WP_Post_Type {
 	 * Default empty.
 	 *
 	 * @since 4.6.0
-	 * @var string $description
+	 * @var callable|string $description
 	 */
-	public $description = '';
+	private $_description = '';
 
 	/**
 	 * Whether a post type is intended for use publicly either via the admin interface or by front-end users.
@@ -498,12 +502,72 @@ final class WP_Post_Type {
 			}
 		}
 
+
+		$args['_labels']      = $args['labels'];
+		$args['_description'] = $args['description'];
+
+		if ( isset( $args['label'] ) ) {
+			$args['_label'] = $args['label'];
+		}
+
+		unset( $args['label'], $args['labels'], $args['description'] );
+
 		foreach ( $args as $property_name => $property_value ) {
 			$this->$property_name = $property_value;
 		}
+	}
+
+	/**
+	 * Magic method for accessing the post type labels.
+	 *
+	 * @since 5.0.0
+	 * @access public
+	 *
+	 * @param string $key Label to retrieve.
+	 * @return object|string Post type labels.
+	 */
+	public function __get( $key ) {
+		if ( 'label' === $key ) {
+			if ( is_callable( $this->_label ) ) {
+				return call_user_func( $this->_label );
+			}
+
+			if ( is_string( $this->_label ) ) {
+				return $this->_label;
+			}
+
+			return $this->labels->name;
+		}
+
+		if ( 'labels' === $key ) {
+			$labels = array();
+
+			if ( is_callable( $this->_labels ) ) {
+				$labels = call_user_func( $this->_labels );
+			}
+
+			if ( is_array( $this->_labels ) ) {
+				$labels = $this->_labels;
+			}
+
+			$post_type_object = (object) array(
+				'name'         => $this->name,
+				'hierarchical' => $this->hierarchical,
+				'labels'       => (object) $labels,
+			);
+
+			return get_post_type_labels( $post_type_object );
+		}
+
+		if ( 'description' === $key ) {
+			if ( is_callable( $this->_description ) ) {
+				return call_user_func( $this->_description );
+			}
+
+			return $this->_description;
+		}
 
-		$this->labels = get_post_type_labels( $this );
-		$this->label  = $this->labels->name;
+		return null;
 	}
 
 	/**
diff --git src/wp-includes/class-wp-taxonomy.php src/wp-includes/class-wp-taxonomy.php
index 92ad89b557..242e5e9571 100644
--- src/wp-includes/class-wp-taxonomy.php
+++ src/wp-includes/class-wp-taxonomy.php
@@ -11,6 +11,10 @@
  * Core class used for interacting with taxonomies.
  *
  * @since 4.7.0
+ *
+ * @property string label
+ * @property object labels
+ * @property string description
  */
 final class WP_Taxonomy {
 	/**
@@ -27,7 +31,7 @@ final class WP_Taxonomy {
 	 * @since 4.7.0
 	 * @var string
 	 */
-	public $label;
+	private $_label;
 
 	/**
 	 * An array of labels for this taxonomy.
@@ -35,7 +39,7 @@ final class WP_Taxonomy {
 	 * @since 4.7.0
 	 * @var object
 	 */
-	public $labels = array();
+	private $_labels = array();
 
 	/**
 	 * A short descriptive summary of what the taxonomy is for.
@@ -43,7 +47,7 @@ final class WP_Taxonomy {
 	 * @since 4.7.0
 	 * @var string
 	 */
-	public $description = '';
+	private $_description = '';
 
 	/**
 	 * Whether a taxonomy is intended for use publicly either via the admin interface or by front-end users.
@@ -371,12 +375,71 @@ final class WP_Taxonomy {
 			}
 		}
 
+		$args['_labels']      = $args['labels'];
+		$args['_description'] = $args['description'];
+
+		if ( isset( $args['label'] ) ) {
+			$args['_label'] = $args['label'];
+		}
+
+		unset( $args['label'], $args['labels'], $args['description'] );
+
 		foreach ( $args as $property_name => $property_value ) {
 			$this->$property_name = $property_value;
 		}
+	}
+
+	/**
+	 * Magic method for accessing the taxonomy labels.
+	 *
+	 * @since 5.0.0
+	 * @access public
+	 *
+	 * @param string $key Label to retrieve.
+	 * @return object|string Taxonomy labels.
+	 */
+	public function __get( $key ) {
+		if ( 'label' === $key ) {
+			if ( is_callable( $this->_label ) ) {
+				return call_user_func( $this->_label );
+			}
+
+			if ( is_string( $this->_label ) ) {
+				return $this->_label;
+			}
+
+			return $this->labels->name;
+		}
+
+		if ( 'labels' === $key ) {
+			$labels = array();
+
+			if ( is_callable( $this->_labels ) ) {
+				$labels = call_user_func( $this->_labels );
+			}
+
+			if ( is_array( $this->_labels ) ) {
+				$labels = $this->_labels;
+			}
+
+			$taxonomy_object = (object) array(
+				'name'         => $this->name,
+				'hierarchical' => $this->hierarchical,
+				'labels'       => (object) $labels,
+			);
+
+			return get_taxonomy_labels( $taxonomy_object );
+		}
+
+		if ( 'description' === $key ) {
+			if ( is_callable( $this->_description ) ) {
+				return call_user_func( $this->_description );
+			}
+
+			return $this->_description;
+		}
 
-		$this->labels = get_taxonomy_labels( $this );
-		$this->label  = $this->labels->name;
+		return null;
 	}
 
 	/**
diff --git src/wp-includes/post.php src/wp-includes/post.php
index 7feb284e85..f8030226c6 100644
--- src/wp-includes/post.php
+++ src/wp-includes/post.php
@@ -1173,13 +1173,13 @@ function get_post_types( $args = array(), $output = 'names', $operator = 'and' )
  * @param array|string $args {
  *     Array or string of arguments for registering a post type.
  *
- *     @type string      $label                 Name of the post type shown in the menu. Usually plural.
+ *     @type callable|string      $label        Name of the post type shown in the menu. Usually plural.
  *                                              Default is value of $labels['name'].
- *     @type array       $labels                An array of labels for this post type. If not set, post
+ *     @type callable|array       $labels       An array of labels for this post type. If not set, post
  *                                              labels are inherited for non-hierarchical types and page
  *                                              labels for hierarchical ones. See get_post_type_labels() for a full
  *                                              list of supported labels.
- *     @type string      $description           A short descriptive summary of what the post type is.
+ *     @type callable|string      $description  A short descriptive summary of what the post type is.
  *                                              Default empty.
  *     @type bool        $public                Whether a post type is intended for use publicly either via
  *                                              the admin interface or by front-end users. While the default
@@ -1257,7 +1257,7 @@ function get_post_types( $args = array(), $output = 'names', $operator = 'and' )
  *         @type bool   $feeds      Whether the feed permastruct should be built for this post type.
  *                                  Default is value of $has_archive.
  *         @type bool   $pages      Whether the permastruct should provide for pagination. Default true.
- *         @type const  $ep_mask    Endpoint mask to assign. If not specified and permalink_epmask is set,
+ *         @type string $ep_mask    Endpoint mask to assign. If not specified and permalink_epmask is set,
  *                                  inherits from $permalink_epmask. If not specified and permalink_epmask
  *                                  is not set, defaults to EP_PERMALINK.
  *     }
diff --git src/wp-includes/taxonomy.php src/wp-includes/taxonomy.php
index 7a278323af..5bb55b2f9b 100644
--- src/wp-includes/taxonomy.php
+++ src/wp-includes/taxonomy.php
@@ -342,11 +342,13 @@ function is_taxonomy_hierarchical( $taxonomy ) {
  * @param array|string $args        {
  *     Optional. Array or query string of arguments for registering a taxonomy.
  *
- *     @type array         $labels                An array of labels for this taxonomy. By default, Tag labels are
+ *     @type callable|string        $label        Name of the taxonomy shown in the menu. Usually plural.
+ *                                                Default is value of $labels['name'].
+ *     @type callable|array         $labels       An array of labels for this taxonomy. By default, Tag labels are
  *                                                used for non-hierarchical taxonomies, and Category labels are used
  *                                                for hierarchical taxonomies. See accepted values in
  *                                                get_taxonomy_labels(). Default empty array.
- *     @type string        $description           A short descriptive summary of what the taxonomy is for. Default empty.
+ *     @type callable|string        $description  A short descriptive summary of what the taxonomy is for. Default empty.
  *     @type bool          $public                Whether a taxonomy is intended for use publicly either via
  *                                                the admin interface or by front-end users. The default settings
  *                                                of `$publicly_queryable`, `$show_ui`, and `$show_in_nav_menus`
@@ -495,7 +497,7 @@ function unregister_taxonomy( $taxonomy ) {
  * @since 4.4.0 Added the `items_list_navigation` and `items_list` labels.
  * @since 4.9.0 Added the `most_used` and `back_to_items` labels.
  *
- * @param WP_Taxonomy $tax Taxonomy object.
+ * @param object|WP_Taxonomy $tax Taxonomy object.
  * @return object {
  *     Taxonomy labels object. The first default value is for non-hierarchical taxonomies
  *     (like tags) and the second one is for hierarchical taxonomies (like categories).
diff --git tests/phpunit/data/languages/de_DE.mo tests/phpunit/data/languages/de_DE.mo
index b65a14e19c..87dca3b265 100644
Binary files tests/phpunit/data/languages/de_DE.mo and tests/phpunit/data/languages/de_DE.mo differ
diff --git tests/phpunit/data/languages/de_DE.po tests/phpunit/data/languages/de_DE.po
index 4d66b76c10..b372f97fd2 100644
--- tests/phpunit/data/languages/de_DE.po
+++ tests/phpunit/data/languages/de_DE.po
@@ -2,12 +2,12 @@
 # This file is distributed under the same license as the 4.9.x package.
 msgid ""
 msgstr ""
-"PO-Revision-Date: 2018-08-13 19:19+0300\n"
+"PO-Revision-Date: 2018-09-14 16:50+0900\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Poedit 2.1.1\n"
+"X-Generator: Poedit 2.1\n"
 "Project-Id-Version: Development (4.9.x)\n"
 "Language: de_DE\n"
 "POT-Creation-Date: \n"
@@ -48,3 +48,47 @@ msgstr "Jetzt %s aktualisieren"
 #: wp-includes/user.php:3445
 msgid "[%1$s] Confirm Action: %2$s"
 msgstr "[%1$s] Aktion bestätigen: %2$s"
+
+#: wp-includes/post.php:1445
+msgctxt "post type singular name"
+msgid "Post"
+msgstr "Beitrag"
+
+#: wp-includes/post.php:1445
+msgctxt "post type singular name"
+msgid "Page"
+msgstr "Seite"
+
+#: wp-includes/post.php:1444
+msgctxt "post type general name"
+msgid "Pages"
+msgstr "Seiten"
+
+#: wp-includes/post.php:1447
+msgid "Add New Page"
+msgstr "Neue Seite erstellen"
+
+#: wp-includes/post.php:1452
+msgid "Search Posts"
+msgstr "Beiträge durchsuchen"
+
+#: wp-includes/post.php:1448
+msgid "Edit Page"
+msgstr "Seite bearbeiten"
+
+#: wp-includes/post.php:1444
+msgctxt "post type general name"
+msgid "Posts"
+msgstr "Beiträge"
+
+#: wp-includes/post.php:1455
+msgid "Parent Page:"
+msgstr "Übergeordnete Seite:"
+
+#: wp-includes/post.php:1447
+msgid "Add New Post"
+msgstr "Neuen Beitrag erstellen"
+
+#: wp-includes/post.php:1452
+msgid "Search Pages"
+msgstr "Seiten durchsuchen"
diff --git tests/phpunit/tests/post/getPostTypeLabels.php tests/phpunit/tests/post/getPostTypeLabels.php
index aff320e641..b919970568 100644
--- tests/phpunit/tests/post/getPostTypeLabels.php
+++ tests/phpunit/tests/post/getPostTypeLabels.php
@@ -96,9 +96,10 @@ class Tests_Get_Post_Type_Labels extends WP_UnitTestCase {
 
 		unregister_post_type( 'foo' );
 
-		$this->assertObjectHasAttribute( 'labels', $post_type_object );
-		$this->assertObjectHasAttribute( 'label', $post_type_object );
+		$this->assertInternalType( 'object', $post_type_object->labels );
+		$this->assertInternalType( 'string', $post_type_object->label );
 		$this->assertObjectHasAttribute( 'not_found_in_trash', $post_type_object->labels );
+		$this->assertObjectHasAttribute( 'parent_item_colon', $post_type_object->labels );
 	}
 
 	public function test_label_should_be_derived_from_labels_when_registering_a_post_type() {
@@ -131,9 +132,145 @@ class Tests_Get_Post_Type_Labels extends WP_UnitTestCase {
 	}
 
 	public function filter_post_type_labels( $labels ) {
-		unset( $labels->featured_image );
-		unset( $labels->set_featured_image );
+		unset( $labels->featured_image, $labels->set_featured_image );
 
 		return $labels;
 	}
+
+	/**
+	 * @ticket 26511
+	 */
+	public function test_post_type_menu_label() {
+		register_post_type(
+			'foo',
+			array(
+				'label' => 'Foos',
+			)
+		);
+
+		register_post_type( 'bar' );
+
+		$this->assertSame( 'Foos', get_post_type_object( 'foo' )->label );
+		$this->assertSame( 'Posts', get_post_type_object( 'bar' )->label );
+	}
+
+	/**
+	 * @ticket 26511
+	 */
+	public function test_post_type_menu_label_callback() {
+		register_post_type(
+			'foo',
+			array(
+				'label' => function () {
+					return 'Foos';
+				},
+			)
+		);
+
+		register_post_type( 'bar' );
+
+		$this->assertSame( 'Foos', get_post_type_object( 'foo' )->label );
+		$this->assertSame( 'Posts', get_post_type_object( 'bar' )->label );
+	}
+
+	/**
+	 * @ticket 26511
+	 */
+	public function test_post_type_labels_callback() {
+		register_post_type(
+			'foo',
+			array(
+				'labels' => function () {
+					return array(
+						'singular_name' => 'Bar',
+					);
+				},
+			)
+		);
+
+		$this->assertSame( 'Posts', get_post_type_object( 'foo' )->labels->name );
+		$this->assertSame( 'Bar', get_post_type_object( 'foo' )->labels->singular_name );
+	}
+
+	/**
+	 * @ticket 26511
+	 */
+	public function test_post_type_labels_should_be_translated_after_locale_switching() {
+		register_post_type( 'foo' );
+
+		$post_type_object = get_post_type_object( 'foo' );
+
+		$labels_en = $post_type_object->labels;
+
+		switch_to_locale( 'de_DE' );
+
+		$labels_de = $post_type_object->labels;
+
+		// Cleanup.
+		restore_current_locale();
+
+		$this->assertEqualSetsWithIndex(
+			array(
+				'name'                  => 'Posts',
+				'singular_name'         => 'Post',
+				'add_new'               => 'Add New',
+				'add_new_item'          => 'Add New Post',
+				'edit_item'             => 'Edit Post',
+				'new_item'              => 'New Post',
+				'view_item'             => 'View Post',
+				'view_items'            => 'View Posts',
+				'search_items'          => 'Search Posts',
+				'not_found'             => 'No posts found.',
+				'not_found_in_trash'    => 'No posts found in Trash.',
+				'parent_item_colon'     => null,
+				'all_items'             => 'All Posts',
+				'archives'              => 'Post Archives',
+				'attributes'            => 'Post Attributes',
+				'insert_into_item'      => 'Insert into post',
+				'uploaded_to_this_item' => 'Uploaded to this post',
+				'featured_image'        => 'Featured Image',
+				'set_featured_image'    => 'Set featured image',
+				'remove_featured_image' => 'Remove featured image',
+				'use_featured_image'    => 'Use as featured image',
+				'filter_items_list'     => 'Filter posts list',
+				'items_list_navigation' => 'Posts list navigation',
+				'items_list'            => 'Posts list',
+				'menu_name'             => 'Posts',
+				'name_admin_bar'        => 'foo',
+			),
+			(array) $labels_en
+		);
+
+		$this->assertEqualSetsWithIndex(
+			array(
+				'name'                  => 'Beiträge',
+				'singular_name'         => 'Beitrag',
+				'add_new'               => 'Add New',
+				'add_new_item'          => 'Neuen Beitrag erstellen',
+				'edit_item'             => 'Edit Post',
+				'new_item'              => 'New Post',
+				'view_item'             => 'View Post',
+				'view_items'            => 'View Posts',
+				'search_items'          => 'Beiträge durchsuchen',
+				'not_found'             => 'No posts found.',
+				'not_found_in_trash'    => 'No posts found in Trash.',
+				'parent_item_colon'     => null,
+				'all_items'             => 'All Posts',
+				'archives'              => 'Post Archives',
+				'attributes'            => 'Post Attributes',
+				'insert_into_item'      => 'Insert into post',
+				'uploaded_to_this_item' => 'Uploaded to this post',
+				'featured_image'        => 'Featured Image',
+				'set_featured_image'    => 'Set featured image',
+				'remove_featured_image' => 'Remove featured image',
+				'use_featured_image'    => 'Use as featured image',
+				'filter_items_list'     => 'Filter posts list',
+				'items_list_navigation' => 'Posts list navigation',
+				'items_list'            => 'Posts list',
+				'menu_name'             => 'Beiträge',
+				'name_admin_bar'        => 'foo',
+			),
+			(array) $labels_de
+		);
+	}
 }
