Index: gp-templates/style.css
===================================================================
--- gp-templates/style.css	(revision 2088)
+++ gp-templates/style.css	(working copy)
@@ -1024,6 +1024,26 @@
 	content: "\f142";
 }
 
+#translator-stats {
+	display: none;
+}
+
+#translator-stats li.filter span {
+	cursor: pointer;
+	border-bottom: 1px dotted #dfdfdf;
+}
+
+#translator-stats li.filter span:hover {
+	border-bottom-color: #d54e21;
+}
+
+.stats-table #translators-table thead th.locale {
+	width: auto;
+	border-top: 0;
+	border-left: 0;
+	float: none;
+}
+
 .sort-bar {
 	float: right;
 	margin: -30px 0 5px 5px;
@@ -1031,4 +1051,4 @@
 	padding: 5px;
 	border: 1px solid #dfdfdf;
 	border-top: none;
-}
\ No newline at end of file
+}
Index: gp-templates/translators-project.php
===================================================================
--- gp-templates/translators-project.php	(nonexistent)
+++ gp-templates/translators-project.php	(working copy)
@@ -0,0 +1,168 @@
+<?php
+$edit_link = gp_link_project_edit_get( $project, __( '(edit)' ) );
+
+gp_title( sprintf( __( 'Translators &lt; %s &lt; GlotPress' ), esc_html( $project->name ) ) );
+gp_breadcrumb_project( $project );
+
+wp_enqueue_script( 'common' );
+wp_enqueue_script( 'tablesorter' );
+
+$translators_recent = $translators_total = $translators_minimum10 = $translators_minimum100 = 0;
+foreach ( $project_translators as $locale_slug => $translators ) {
+	foreach ( $translators as $translator_id => $translations ) {
+		$translators_total += 1;
+
+		if ( $translations->recent > 0 ) {
+			$translators_recent += 1;
+		}
+		if ( $translations->count >= 10 ) {
+			$translators_minimum10 += 1;
+		}
+		if ( $translations->count >= 100 ) {
+			$translators_minimum100 += 1;
+		}
+	}
+}
+
+gp_tmpl_header();
+?>
+
+<div class="project-header">
+	<p class="project-description"><?php echo apply_filters( 'project_description', $project->description, $project ); ?></p>
+	<div class="project-description">
+		<a href="" id="translator-stats-toggle"><?php _e( 'Show translator stats &raquo;' ); ?></a>
+
+		<ul id="translator-stats">
+			<li><?php printf( _n( '%d people contributed translations to this project.', '%d people contributed translations to this project', $translators_total ), $translators_total ); ?></li>
+
+			<?php if ( $translators_recent > 0 ): ?>
+				<li class="filter" data-filter="recent" data-filter-name="<?php echo esc_attr( __( 'only people who translated in the last year' ) ); ?>"><span><?php printf( _n( '%d person contributed translations in the last year.', '%d people contributed translations in the last year.', $translators_recent ), $translators_recent ); ?></span></li>
+			<?php endif; ?>
+
+			<?php if ( $translators_minimum10 > 0 ): ?>
+				<li class="filter" data-filter="minimum10" data-filter-name="<?php echo esc_attr( __( 'only people who contributed at least 10 translations' ) ); ?>"><span><?php printf( _n( '%d person contributed at least 10 translations.', '%d people contributed at least 10 translations.', $translators_minimum10 ), $translators_minimum10 ); ?></span></li>
+			<?php endif; ?>
+
+			<?php if ( $translators_minimum10 > 0 ): ?>
+				<li class="filter" data-filter="minimum100" data-filter-name="<?php echo esc_attr( __( 'only people who contributed at least 100 translations' ) ); ?>"><span><?php printf( _n( '%d person contributed at least 100 translations.', '%d people contributed at least 100 translations.', $translators_minimum100 ), $translators_minimum100 ); ?></span></li>
+			<?php endif; ?>
+
+		</ul>
+	</div>
+
+
+	<div class="project-box">
+		<div class="project-box-header">
+			<div class="project-icon">
+				<?php echo $project->icon; ?>
+			</div>
+
+			<ul class="project-meta">
+				<li class="project-name"><?php echo $project->name; ?> <?php echo $edit_link; ?></li>
+			</ul>
+		</div>
+	</div>
+</div>
+
+<div class="stats-table">
+	<table id="translators-table" class="table">
+		<thead>
+			<tr>
+				<th class="locale"><?php echo _( 'Locale' ); ?> <span class="filter-name"></span></th>
+				<th class="username"><?php echo _( 'Username' ); ?></th>
+				<th class="translations"><?php echo _( 'Translations' ); ?></th>
+				<th class="translations"><?php echo _( 'Recent' ); ?></th>
+			</tr>
+		</thead>
+		<tbody>
+			<?php
+			foreach ( $project_translators as $locale_slug => $translators ) :
+				$gp_locale = GP_Locales::by_slug( $locale_slug );
+
+				$set_slug  = 'default';
+
+				// Variants (de/formal for example) don't have GP_Locales in this context
+				if ( ! $gp_locale && ( list( $base_locale_slug, $set_slug ) = explode( '/', $locale_slug ) ) ) :
+					$gp_locale = clone GP_Locales::by_slug( $base_locale_slug );
+
+					// Just append it for now..
+					$gp_locale->wp_locale .= '/' . $set_slug;
+					$gp_locale->english_name .=  ' (' . ucfirst( $set_slug ) . ')';
+				endif;
+
+				if ( ! $gp_locale || ! $gp_locale->wp_locale ) :
+					continue;
+				endif;
+
+				foreach ( $translators as $translator_id => $translations ):
+					$user = GP::$user->get( $translator_id );
+					?>
+					<tr>
+						<th title="<?php echo esc_attr( $gp_locale->wp_locale ); ?>">
+							<a href="<?php echo gp_url( gp_url_join( 'locale', $gp_locale->slug, $set_slug, $project->path ) ); ?>">
+								<?php echo esc_html( $gp_locale->english_name ); ?>
+							</a>
+						</th>
+						<td><?php echo $user->user_login; ?></td>
+						<td><?php echo $translations->count; ?></td>
+						<td><?php echo $translations->recent; ?></td>
+					</tr>
+				<?php endforeach; ?>
+			<?php endforeach; ?>
+		</tbody>
+	</table>
+</div>
+
+<script type="text/javascript">
+jQuery( function( $ ) {
+	$( '#translators-table' ).tablesorter( {
+		textExtraction: function( node ) {
+			var cellValue = $( node ).text(),
+				sortValue = $( node ).data( 'sortValue' );
+
+			return ( undefined !== sortValue ) ? sortValue : cellValue;
+		}
+	});
+
+	$( '#translator-stats-toggle' ).on( 'click', function() {
+		$( '#translator-stats' ).toggle();
+		return false;
+	});
+
+	$( '#translator-stats li.filter' ).on( 'click', function() {
+		var $this = $( this ), filter = $this.data( 'filter' );
+
+		if ( $this.hasClass( 'active' ) ) {
+			$( '#translators-table thead th.locale span.filter-name' ).text( '' );
+			$( '#translators-table tbody tr').each( function() {
+				this.style.display = 'table-row';
+			});
+			$this.removeClass( 'active' );
+			return;
+		}
+
+		$this.addClass( 'active' );
+		$( '#translators-table thead th.locale span.filter-name' ).text( '(' + $this.data( 'filter-name' ) + ')' );
+
+		if ( filter === 'recent' ) {
+			$( '#translators-table tbody tr').each( function() {
+				this.style.display = parseInt( $( this ).find( 'td' ).eq( 1 ).text(), 10 ) > 0 ?
+					'table-row' : 'none';
+			});
+		} else if ( filter === 'minimum10' ) {
+			$( '#translators-table tbody tr').each( function() {
+				this.style.display = parseInt( $( this ).find( 'td' ).eq( 2 ).text(), 10 ) >= 10 ?
+					'table-row' : 'none';
+			});
+		} else if ( filter === 'minimum100' ) {
+			$( '#translators-table tbody tr').each( function() {
+				this.style.display = parseInt( $( this ).find( 'td' ).eq( 2 ).text(), 10 ) >= 100 ?
+					'table-row' : 'none';
+			});
+		}
+		return false;
+	})
+});
+</script>
+
+<?php gp_tmpl_footer();

Property changes on: gp-templates/translators-project.php
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: gp-plugins/wporg-routes/routes/wp-plugins.php
===================================================================
--- gp-plugins/wporg-routes/routes/wp-plugins.php	(revision 2089)
+++ gp-plugins/wporg-routes/routes/wp-plugins.php	(working copy)
@@ -88,4 +88,99 @@
 
 		$this->tmpl( 'projects-wp-plugins', get_defined_vars() );
 	}
+
+	/**
+	 * [get_total_project_translators description]
+	 * @param  [type] $project_id [description]
+	 * @return [type]             [description]
+	 */
+
+	private function get_total_project_translators( $project_id ) {
+		global $gpdb;
+
+		// query all subprojects
+		$projects = $gpdb->get_results( $gpdb->prepare( "
+			SELECT id
+			FROM {$gpdb->prefix}projects
+			WHERE parent_project_id = %d
+		", $project_id ) );
+
+		// also include the main project in the count
+		$projects[] = (object) array(
+			'id' => $project_id,
+		);
+
+		$project_translators = array();
+		foreach ( $projects as $project ) {
+
+			foreach( $this->get_project_translators( $project->id ) as $row ) {
+				$locale_key = $row->locale;
+				if ( 'default' != $row->locale_slug ) {
+					$locale_key = $row->locale . '/' . $row->locale_slug;
+				}
+
+				if ( ! isset( $project_translators[ $locale_key ] ) ) {
+					$project_translators[ $locale_key ] = array();
+				}
+
+				if ( ! isset( $project_translators[ $locale_key ][ $row->user_id ] ) ) {
+					$project_translators[ $locale_key ][ $row->user_id ] = (object) array(
+						'count' => 0,
+						'recent' => 0,
+					);
+				}
+
+				// sum up translations per user over all (sub-)projects
+				$project_translators[ $locale_key ][ $row->user_id ]->count += $row->translation_count;
+				$project_translators[ $locale_key ][ $row->user_id ]->recent += $row->recent_translation_count;
+			}
+		}
+
+		return $project_translators;
+	}
+
+	/**
+	 * Get the translators of a specific project
+	 *
+	 * @param  integer $project_id           project id
+	 * @return object                        translators of the project
+	 */
+	private function get_project_translators( $project_id ) {
+		global $gpdb;
+
+		$sql = $gpdb->prepare( '
+			SELECT ts.`locale`, ts.`slug` AS `locale_slug`, t.`user_id`, COUNT(*) AS `translation_count`,
+				COUNT( CASE WHEN t.date_modified > %s THEN t.date_modified END ) AS `recent_translation_count`
+			FROM `' . $gpdb->translations .'` t, `'. $gpdb->translation_sets . '` ts
+			WHERE t.`translation_set_id` = ts.`id`
+			    AND t.`user_id` IS NOT NULL AND t.`user_id` != 0
+			    AND ts.`project_id` = %d
+			GROUP BY ts.`locale`, ts.`slug`, t.`user_id`
+		', date( 'Y-m-d', time() - 365 * 86400 ), $project_id );
+
+		return $gpdb->get_results( $sql );
+	}
+
+	function get_plugin_project_translators( $project_slug ) {
+		global $gpdb;
+
+		$project_path = 'wp-plugins/' . $project_slug;
+		$project = GP::$project->by_path( $project_path );
+		if ( ! $project ) {
+			return $this->die_with_404();
+		}
+
+		$project_translators = $this->get_total_project_translators( $project->id );
+
+		// sort by locales for display
+		ksort( $project_translators );
+
+		if ( function_exists( 'wporg_get_plugin_icon' ) ) {
+			$project->icon = wporg_get_plugin_icon( $project->slug, 64 );
+		} else {
+			$project->icon = '<div class="default-icon"><span class="dashicons dashicons-admin-plugins"></span></div>';
+		}
+
+		$this->tmpl( 'translators-project', get_defined_vars() );
+	}
 }
Index: gp-plugins/wporg-routes/wporg-routes.php
===================================================================
--- gp-plugins/wporg-routes/wporg-routes.php	(revision 2089)
+++ gp-plugins/wporg-routes/wporg-routes.php	(working copy)
@@ -43,8 +43,9 @@
 		GP::$router->prepend( "/locale/$locale/$path/$path", array( 'GP_WPorg_Route_Locale', 'get_locale_projects' ) );
 		GP::$router->prepend( "/locale/$locale/$path/$path/$path", array( 'GP_WPorg_Route_Locale', 'get_locale_project' ) );
 
-		$project = '([^/]*)/?';
-		GP::$router->prepend( "/projects/wp-plugins/$project", array( 'GP_WPorg_Route_WP_Plugins', 'get_plugin_projects' ) );
+		$project = '([^/]*)';
+		GP::$router->prepend( "/projects/wp-plugins/$project/?", array( 'GP_WPorg_Route_WP_Plugins', 'get_plugin_projects' ) );
+		GP::$router->prepend( "/projects/wp-plugins/$project/translators/?", array( 'GP_WPorg_Route_WP_Plugins', 'get_plugin_project_translators' ) );
 	}
 }
 
