Index: /extensions/plugin_lang_analysis/admin.php
===================================================================
--- /extensions/plugin_lang_analysis/admin.php	(revision 23421)
+++ /extensions/plugin_lang_analysis/admin.php	(revision 23421)
@@ -0,0 +1,170 @@
+<?php
+defined('PLA_PATH') or die('Hacking attempt!');
+ 
+global $template, $page;
+
+$page['active_menu'] = get_active_menu('updates');
+
+include_once(PLA_PATH . 'include/functions.inc.php');
+
+
+/* PLUGINS LIST */
+if (!isset($_GET['plugin_id']))
+{
+  $query = '
+SELECT *
+  FROM '.PLUGINS_TABLE.'
+  ORDER BY LOWER(id)
+;';
+
+  $plugins = hash_from_query($query, 'id');
+
+  $template->assign(array(
+    'PLA_STEP' => 'select',
+    'PLA_PLUGINS' => $plugins,
+    'F_ACTION' => PLA_ADMIN,
+    ));
+}
+
+/* FILES LIST */
+else if (!isset($_GET['analyze']))
+{
+  $files = list_plugin_files($_GET['plugin_id']);
+  
+  if (file_exists(PLA_PATH.'_data/'.$_GET['plugin_id'].'.php'))
+  {
+    list($filename, $saved_files) = @include(PLA_PATH.'_data/'.$_GET['plugin_id'].'.php');
+  }
+  else
+  {
+    $saved_files = array();
+    $filename = 'plugin.lang.php';
+  }
+  
+  foreach ($files as &$file)
+  {
+    if (isset($saved_files[$file]))
+    {
+      $file = $saved_files[$file];
+    }
+    else
+    {
+      $file = array(
+        'path' => $file,
+        'is_admin' => strpos($file, '/admin') === 0,
+        );
+    }
+  }
+  unset($file);
+  
+  $template->assign(array(
+    'PLA_STEP' => 'config',
+    'PLA_PLUGIN' => $_GET['plugin_id'],
+    'PLA_FILES' => $files,
+    'PLA_FILENAME' => $filename,
+    'F_ACTION' => PLA_ADMIN.'&amp;plugin_id='.$_GET['plugin_id'].'&amp;analyze',
+    'U_BACK' => PLA_ADMIN,
+    ));
+}
+else
+{
+  // save
+  if (isset($_POST['files']))
+  {
+    $filename = $_POST['filename'];
+    
+    $files = array();
+    foreach ($_POST['files'] as $file => $is_admin)
+    {
+      $files[$file] = array(
+        'path' => $file,
+        'is_admin' => $is_admin=='true',
+        );
+    }
+    
+    $content = "<?php\nreturn ";
+    $content.= var_export(array($filename, $files), true);
+    $content.= ";\n?>";
+    
+    @mkdir(PLA_PATH.'_data/', true, 0755);
+    file_put_contents(PLA_PATH.'_data/'.$_GET['plugin_id'].'.php', $content);
+  }
+  else
+  {
+    list($filename, $files) = include(PLA_PATH.'_data/'.$_GET['plugin_id'].'.php');
+  }
+  
+  
+  $strings = array();
+  $counts = array('ok'=>0,'missing'=>0,'useless'=>0);
+  
+  // get strings list
+  foreach ($files as $file => $file_data)
+  {
+    $file_strings = analyze_file($_GET['plugin_id'].$file);
+    
+    foreach ($file_strings as $string => $lines)
+    {
+      if (empty($strings[ $string ]))
+        $strings[ $string ]['is_admin'] = $file_data['is_admin'];
+      else
+        $strings[ $string ]['is_admin'] = $strings[ $string ]['is_admin'] && $file_data['is_admin'];
+      
+      $strings[ $string ]['files'][ $file ] = $lines;
+    }
+  }
+  
+  // load language files
+  $lang_common = load_language_file(PHPWG_ROOT_PATH.'language/en_UK/common.lang.php');
+  $lang_admin = load_language_file(PHPWG_ROOT_PATH.'language/en_UK/admin.lang.php');
+  $lang_plugin = load_language_file(PHPWG_PLUGINS_PATH.$_GET['plugin_id'].'/language/en_UK/'.$filename);
+  
+  // analyze
+  foreach ($strings as $string => &$string_data)
+  {
+    $string_data['in_common'] = array_key_exists($string, $lang_common);
+    $string_data['in_admin'] = array_key_exists($string, $lang_admin);
+    $string_data['in_plugin'] = array_key_exists($string, $lang_plugin);
+    
+    if ($string_data['in_plugin'] && ($string_data['in_common'] || ($string_data['is_admin'] && $string_data['in_admin'])))
+    {
+      $string_data['stat'] = 'useless';
+      $counts['useless']++;
+    }
+    else if (!$string_data['in_plugin'] && !$string_data['in_common'] && (!$string_data['is_admin'] || !$string_data['in_admin']))
+    {
+      $string_data['stat'] = 'missing';
+      $counts['missing']++;
+    }
+    else
+    {
+      $string_data['stat'] = 'ok';
+      $counts['ok']++;
+    }
+  }
+  unset($string_data);
+  
+  uksort($strings, 'strnatcasecmp');
+  $counts['total'] = array_sum($counts);
+  
+  $template->assign(array(
+    'PLA_STEP' => 'analysis',
+    'PLA_PLUGIN' => $_GET['plugin_id'],
+    'PLA_FILES' => $files,
+    'PLA_STRINGS' => $strings,
+    'PLA_COUNTS' => $counts,
+    'U_BACK' => PLA_ADMIN.'&amp;plugin_id='.$_GET['plugin_id'],
+    ));
+}
+
+// template vars
+$template->assign(array(
+  'PLA_PATH'=> PLA_PATH, 
+  'PLA_ABS_PATH'=> realpath(PLA_PATH), 
+  'PLA_ADMIN' => PLA_ADMIN,
+  ));
+
+$template->set_filename('pla_content', realpath(PLA_PATH.'template/main.tpl'));
+$template->assign_var_from_handle('ADMIN_CONTENT', 'pla_content');
+
+?>
Index: /extensions/plugin_lang_analysis/include/functions.inc.php
===================================================================
--- /extensions/plugin_lang_analysis/include/functions.inc.php	(revision 23421)
+++ /extensions/plugin_lang_analysis/include/functions.inc.php	(revision 23421)
@@ -0,0 +1,124 @@
+<?php
+defined('PLA_PATH') or die('Hacking attempt!');
+
+/**
+ * list files of a plugin
+ */
+function list_plugin_files($id, $path=null)
+{
+  if (empty($path))
+  {
+    $path = '/';
+  }
+  
+  if ($path == '/language/')
+  {
+    return array();
+  }
+  
+  if (strlen($path)-strrpos($path, '_data/') == 6)
+  {
+    return array();
+  }
+  
+  if (($handle = opendir(PHPWG_PLUGINS_PATH.$id.$path)) === false)
+  {
+    return array();
+  }
+  
+  $data = array();
+  
+  while ($entry = readdir($handle))
+  {
+    if ($entry=='.' || $entry=='..' || $entry=='.svn' || $entry=='index.php') continue;
+    
+    if (is_dir(PHPWG_PLUGINS_PATH.$id.$path.$entry))
+    {
+      $data = array_merge($data, list_plugin_files($id, $path.$entry.'/'));
+    }
+    else
+    {
+      $ext = strtolower(get_extension($entry));
+      if (in_array($ext, array('php', 'tpl')))
+      {
+        $data[] = $path.$entry;
+      }
+    }
+  }
+  
+  closedir($handle);
+  
+  return $data;
+}
+
+/**
+ * list translated strings of a file
+ */
+function analyze_file($path)
+{
+  $lines = file(PHPWG_PLUGINS_PATH.$path, FILE_IGNORE_NEW_LINES);
+  
+  $strings = array();
+  
+  foreach ($lines as $i => $line)
+  {
+    // l10n
+    if (preg_match_all('#l10n\((?:[ ]*)(?:["\']{1})(.*?)(?:["\']{1})(?:[ ]*)\)#', $line, $matches))
+    {
+      for ($j=0; $j<count($matches[1]); ++$j)
+      {
+        $strings[ stripslashes($matches[1][$j]) ][] = $i+1;
+      }
+    }
+    // translate
+    if (preg_match_all('#\{(?:["\']{1})(.*?)(?:["\']{1})\|\@translate#', $line, $matches))
+    {
+      for ($j=0; $j<count($matches[1]); ++$j)
+      {
+        $strings[ stripslashes($matches[1][$j]) ][] = $i+1;
+      }
+    }
+    // l10n_dec on one line
+    if (preg_match_all('#l10n_dec\((?:[ ]*)(?:["\']{1})(.*?)(?:["\']{1})(?:[ ]*),(?:[ ]*)(?:["\']{1})(.*?)(?:["\']{1})#', $line, $matches))
+    {
+      for ($j=0; $j<count($matches[1]); ++$j)
+      {
+        $strings[ stripslashes($matches[1][$j]) ][] = $i+1;
+        $strings[ stripslashes($matches[2][$j]) ][] = $i+1;
+      }
+    }
+    // l10n_dec on two or three lines
+    else if (strpos($line, 'l10n_dec')!==false)
+    {
+      $three_lines = $lines[$i];
+      if (isset($lines[$i+1]))
+      {
+        $three_lines.= ' '.$lines[$i+1];
+        if (isset($lines[$i+2])) $three_lines.= ' '.$lines[$i+2];
+      }
+      
+      if (preg_match_all('#l10n_dec\((?:[ ]*)(?:["\']{1})(.*?)(?:["\']{1})(?:[ ]*),(?:[ ]*)(?:["\']{1})(.*?)(?:["\']{1})#', $three_lines, $matches))
+      {
+        for ($j=0; $j<count($matches[1]); ++$j)
+        {
+          $strings[ stripslashes($matches[1][$j]) ][] = $i+1;
+          $strings[ stripslashes($matches[2][$j]) ][] = $i+1;
+        }
+      }
+    }
+  }
+  
+  return $strings;
+}
+
+/**
+ * load a language file
+ */
+function load_language_file($path)
+{
+  @include($path);
+  if (!isset($lang)) return array();
+  return $lang;
+}
+
+?>
Index: /extensions/plugin_lang_analysis/include/index.php
===================================================================
--- /extensions/plugin_lang_analysis/include/index.php	(revision 23421)
+++ /extensions/plugin_lang_analysis/include/index.php	(revision 23421)
@@ -0,0 +1,7 @@
+<?php
+$url = '../';
+header( 'Request-URI: '.$url );
+header( 'Content-Location: '.$url );
+header( 'Location: '.$url );
+exit();
+?>
Index: /extensions/plugin_lang_analysis/index.php
===================================================================
--- /extensions/plugin_lang_analysis/index.php	(revision 23421)
+++ /extensions/plugin_lang_analysis/index.php	(revision 23421)
@@ -0,0 +1,7 @@
+<?php
+$url = '../';
+header( 'Request-URI: '.$url );
+header( 'Content-Location: '.$url );
+header( 'Location: '.$url );
+exit();
+?>
Index: /extensions/plugin_lang_analysis/main.inc.php
===================================================================
--- /extensions/plugin_lang_analysis/main.inc.php	(revision 23421)
+++ /extensions/plugin_lang_analysis/main.inc.php	(revision 23421)
@@ -0,0 +1,38 @@
+<?php 
+/*
+Plugin Name: Plugin Language Analysis
+Version: auto
+Description: Add a tool to analyse translation strings of plugins
+Plugin URI: auto
+Author: Mistic
+Author URI: http://www.strangeplanet.fr
+*/
+
+defined('PHPWG_ROOT_PATH') or die('Hacking attempt!');
+
+if (!defined('IN_ADMIN'))
+{
+  return;
+}
+
+
+defined('PLA_ID') or define('PLA_ID', basename(dirname(__FILE__)));
+define('PLA_PATH' , PHPWG_PLUGINS_PATH . PLA_ID . '/');
+define('PLA_ADMIN', get_root_url() . 'admin.php?page=plugin-' . PLA_ID);
+
+add_event_handler('loc_begin_admin', 'pla_begin_admin');
+
+function pla_begin_admin()
+{
+  global $template;
+  $template->set_prefilter('admin', 'pla_add_menu_item');
+}
+
+function pla_add_menu_item($content, &$smarty)
+{
+  $search = '<li><a class="icon-arrows-cw" href="{$U_UPDATES}">{\'Updates\'|@translate}</a></li>';
+  $add = '<li><a class="icon-language" href="'.PLA_ADMIN.'">Plugin Language Analysis</a></li>';
+  return str_replace($search, $search."\n".$add, $content);
+}
+
+?>
Index: /extensions/plugin_lang_analysis/template/fontello/config.json
===================================================================
--- /extensions/plugin_lang_analysis/template/fontello/config.json	(revision 23421)
+++ /extensions/plugin_lang_analysis/template/fontello/config.json	(revision 23421)
@@ -0,0 +1,31 @@
+{
+  "name": "fontello_pla",
+  "css_prefix_text": "iconpla-",
+  "css_use_suffix": false,
+  "glyphs": [
+    {
+      "uid": "1400d5103edd2fa6d2d61688fee79a5a",
+      "css": "ok-squared",
+      "code": 59394,
+      "src": "fontawesome"
+    },
+    {
+      "uid": "eeadb020bb75d089b25d8424aabe19e0",
+      "css": "minus-circled",
+      "code": 59392,
+      "src": "fontawesome"
+    },
+    {
+      "uid": "c76b7947c957c9b78b11741173c8349b",
+      "css": "attention",
+      "code": 59396,
+      "src": "fontawesome"
+    },
+    {
+      "uid": "b035c28eba2b35c6ffe92aee8b0df507",
+      "css": "attention-circled",
+      "code": 59397,
+      "src": "fontawesome"
+    }
+  ]
+}
Index: /extensions/plugin_lang_analysis/template/fontello/css/fontello_pla.css
===================================================================
--- /extensions/plugin_lang_analysis/template/fontello/css/fontello_pla.css	(revision 23421)
+++ /extensions/plugin_lang_analysis/template/fontello/css/fontello_pla.css	(revision 23421)
@@ -0,0 +1,56 @@
+@font-face {
+  font-family: 'fontello_pla';
+  src: url('../font/fontello_pla.eot?40386331');
+  src: url('../font/fontello_pla.eot?40386331#iefix') format('embedded-opentype'),
+       url('../font/fontello_pla.woff?40386331') format('woff'),
+       url('../font/fontello_pla.ttf?40386331') format('truetype'),
+       url('../font/fontello_pla.svg?40386331#fontello_pla') format('svg');
+  font-weight: normal;
+  font-style: normal;
+}
+/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
+/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
+/*
+@media screen and (-webkit-min-device-pixel-ratio:0) {
+  @font-face {
+    font-family: 'fontello_pla';
+    src: url('../font/fontello_pla.svg?40386331#fontello_pla') format('svg');
+  }
+}
+*/
+ 
+ [class^="iconpla-"]:before, [class*=" iconpla-"]:before {
+  font-family: "fontello_pla";
+  font-style: normal;
+  font-weight: normal;
+  speak: none;
+ 
+  display: inline-block;
+  text-decoration: inherit;
+  width: 1em;
+  margin-right: .2em;
+  text-align: center;
+  /* opacity: .8; */
+ 
+  /* For safety - reset parent styles, that can break glyph codes*/
+  font-variant: normal;
+  text-transform: none;
+     
+  /* fix buttons height, for twitter bootstrap */
+  line-height: 1em;
+ 
+  /* Animation center compensation - margins should be symmetric */
+  /* remove if not needed */
+  margin-left: .2em;
+ 
+  /* you can be more comfortable with increased icons size */
+  /* font-size: 120%; */
+ 
+  /* Uncomment for 3D effect */
+  /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
+}
+ 
+.iconpla-attention:before { content: '\e804'; } /* '' */
+.iconpla-attention-circled:before { content: '\e805'; } /* '' */
+.iconpla-minus-circled:before { content: '\e800'; } /* '' */
+.iconpla-ok-squared:before { content: '\e802'; } /* '' */
Index: /extensions/plugin_lang_analysis/template/fontello/demo.html
===================================================================
--- /extensions/plugin_lang_analysis/template/fontello/demo.html	(revision 23421)
+++ /extensions/plugin_lang_analysis/template/fontello/demo.html	(revision 23421)
@@ -0,0 +1,268 @@
+<!DOCTYPE html>
+<html>
+  <head><!--[if lt IE 9]>
+    <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
+    <meta charset="UTF-8"><style type="text/css">/*
+ * Bootstrap v2.2.1
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+.clearfix {
+  *zoom: 1;
+}
+.clearfix:before,
+.clearfix:after {
+  display: table;
+  content: "";
+  line-height: 0;
+}
+.clearfix:after {
+  clear: both;
+}
+html {
+  font-size: 100%;
+  -webkit-text-size-adjust: 100%;
+  -ms-text-size-adjust: 100%;
+}
+a:focus {
+  outline: thin dotted #333;
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+a:hover,
+a:active {
+  outline: 0;
+}
+button,
+input,
+select,
+textarea {
+  margin: 0;
+  font-size: 100%;
+  vertical-align: middle;
+}
+button,
+input {
+  *overflow: visible;
+  line-height: normal;
+}
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+  padding: 0;
+  border: 0;
+}
+body {
+  margin: 0;
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  font-size: 14px;
+  line-height: 20px;
+  color: #333;
+  background-color: #fff;
+}
+a {
+  color: #08c;
+  text-decoration: none;
+}
+a:hover {
+  color: #005580;
+  text-decoration: underline;
+}
+.row {
+  margin-left: -20px;
+  *zoom: 1;
+}
+.row:before,
+.row:after {
+  display: table;
+  content: "";
+  line-height: 0;
+}
+.row:after {
+  clear: both;
+}
+[class*="span"] {
+  float: left;
+  min-height: 1px;
+  margin-left: 20px;
+}
+.container,
+.navbar-static-top .container,
+.navbar-fixed-top .container,
+.navbar-fixed-bottom .container {
+  width: 940px;
+}
+.span12 {
+  width: 940px;
+}
+.span11 {
+  width: 860px;
+}
+.span10 {
+  width: 780px;
+}
+.span9 {
+  width: 700px;
+}
+.span8 {
+  width: 620px;
+}
+.span7 {
+  width: 540px;
+}
+.span6 {
+  width: 460px;
+}
+.span5 {
+  width: 380px;
+}
+.span4 {
+  width: 300px;
+}
+.span3 {
+  width: 220px;
+}
+.span2 {
+  width: 140px;
+}
+.span1 {
+  width: 60px;
+}
+[class*="span"].pull-right,
+.row-fluid [class*="span"].pull-right {
+  float: right;
+}
+.container {
+  margin-right: auto;
+  margin-left: auto;
+  *zoom: 1;
+}
+.container:before,
+.container:after {
+  display: table;
+  content: "";
+  line-height: 0;
+}
+.container:after {
+  clear: both;
+}
+p {
+  margin: 0 0 10px;
+}
+.lead {
+  margin-bottom: 20px;
+  font-size: 21px;
+  font-weight: 200;
+  line-height: 30px;
+}
+small {
+  font-size: 85%;
+}
+h1 {
+  margin: 10px 0;
+  font-family: inherit;
+  font-weight: bold;
+  line-height: 20px;
+  color: inherit;
+  text-rendering: optimizelegibility;
+}
+h1 small {
+  font-weight: normal;
+  line-height: 1;
+  color: #999;
+}
+h1 {
+  line-height: 40px;
+}
+h1 {
+  font-size: 38.5px;
+}
+h1 small {
+  font-size: 24.5px;
+}
+body {
+  margin-top: 90px;
+}
+.header {
+  position: fixed;
+  top: 0;
+  left: 50%;
+  margin-left: -480px;
+  background-color: #fff;
+  border-bottom: 1px solid #ddd;
+  padding-top: 10px;
+  z-index: 10;
+}
+.footer {
+  color: #ddd;
+  font-size: 12px;
+  text-align: center;
+  margin-top: 20px;
+}
+.footer a {
+  color: #ccc;
+  text-decoration: underline;
+}
+.the-icons {
+  font-size: 14px;
+  line-height: 24px;
+}
+.switch {
+  position: absolute;
+  right: 0;
+  bottom: 10px;
+  color: #666;
+}
+.switch input {
+  margin-right: 0.3em;
+}
+.codesOn .i-name {
+  display: none;
+}
+.codesOn .i-code {
+  display: inline;
+}
+.i-code {
+  display: none;
+}
+</style>
+    <link rel="stylesheet" href="css/fontello_pla.css">
+    <link rel="stylesheet" href="css/animation.css"><!--[if IE 7]>
+    <link rel="stylesheet" href="css/fontello_pla-ie7.css"><![endif]-->
+    <script>
+      function toggleCodes(on) {
+        var obj = document.getElementById('icons');
+        
+        if (on) {
+          obj.className += ' codesOn';
+        } else {
+          obj.className = obj.className.replace(' codesOn', '');
+        }
+      }
+      
+    </script>
+  </head>
+  <body>
+    <div class="container header">
+      <h1>
+        fontello_pla
+         <small>font demo</small>
+      </h1>
+      <label class="switch">
+        <input type="checkbox" onclick="toggleCodes(this.checked)">show codes
+      </label>
+    </div>
+    <div id="icons" class="container">
+      <div class="row">
+        <div title="Code: 0xe804" class="the-icons span3"><i class="iconpla-attention"></i> <span class="i-name">iconpla-attention</span><span class="i-code">0xe804</span></div>
+        <div title="Code: 0xe805" class="the-icons span3"><i class="iconpla-attention-circled"></i> <span class="i-name">iconpla-attention-circled</span><span class="i-code">0xe805</span></div>
+        <div title="Code: 0xe800" class="the-icons span3"><i class="iconpla-minus-circled"></i> <span class="i-name">iconpla-minus-circled</span><span class="i-code">0xe800</span></div>
+        <div title="Code: 0xe802" class="the-icons span3"><i class="iconpla-ok-squared"></i> <span class="i-name">iconpla-ok-squared</span><span class="i-code">0xe802</span></div>
+      </div>
+    </div>
+    <div class="container footer">Generated by <a href="http://fontello.com">fontello.com</a></div>
+  </body>
+</html>
Index: /extensions/plugin_lang_analysis/template/fontello/font/fontello_pla.svg
===================================================================
--- /extensions/plugin_lang_analysis/template/fontello/font/fontello_pla.svg	(revision 23421)
+++ /extensions/plugin_lang_analysis/template/fontello/font/fontello_pla.svg	(revision 23421)
@@ -0,0 +1,15 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata>Copyright (C) 2012 by original authors @ fontello.com</metadata>
+<defs>
+<font id="fontello_pla" horiz-adv-x="1000" >
+<font-face font-family="fontello_pla" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
+<missing-glyph horiz-adv-x="1000" />
+<glyph glyph-name="attention" unicode="&#xe804;" d="M571 83l0 106q0 8-5 13t-13 5l-107 0q-7 0-13-5t-5-13l0-106q0-8 5-13t13-5l107 0q7 0 13 5t5 13z m-1 209l10 256q0 7-6 11-7 6-13 6l-123 0q-6 0-13-6-6-4-6-12l9-255q0-6 6-9t13-4l103 0q8 0 13 4t6 9z m-8 521l429-786q20-35-1-70-9-16-26-26t-35-9l-857 0q-19 0-35 9t-26 26q-21 35-1 70l429 786q9 17 26 27t36 10 36-10 26-27z" horiz-adv-x="1000" />
+<glyph glyph-name="attention-circled" unicode="&#xe805;" d="M429 779q117 0 215-57t156-156 57-215-57-215-156-156-215-57-215 57-156 156-57 215 57 215 156 156 215 57z m71-696l0 106q0 8-5 13t-12 5l-107 0q-7 0-13-6t-6-13l0-106q0-7 6-13t13-6l107 0q7 0 12 5t5 13z m-1 192l10 347q0 7-6 10-6 4-13 4l-123 0q-8 0-13-4-6-3-6-10l9-347q0-6 6-10t13-4l103 0q8 0 13 4t6 10z" horiz-adv-x="857.143" />
+<glyph glyph-name="minus-circled" unicode="&#xe800;" d="M679 314l0 71q0 15-11 25t-25 11l-429 0q-15 0-25-11t-11-25l0-71q0-15 11-25t25-11l429 0q15 0 25 11t11 25z m179 36q0-117-57-215t-156-156-215-57-215 57-156 156-57 215 57 215 156 156 215 57 215-57 156-156 57-215z" horiz-adv-x="857.143" />
+<glyph glyph-name="ok-squared" unicode="&#xe802;" d="M382 125l343 343q11 11 11 25t-11 25l-57 57q-11 11-25 11t-25-11l-261-261-118 118q-11 11-25 11t-25-11l-57-57q-11-11-11-25t11-25l200-200q11-11 25-11t25 11z m475 493l0-536q0-66-47-114t-114-47l-536 0q-66 0-114 47t-47 114l0 536q0 66 47 114t114 47l536 0q66 0 114-47t47-114z" horiz-adv-x="857.143" />
+</font>
+</defs>
+</svg>
Index: /extensions/plugin_lang_analysis/template/index.php
===================================================================
--- /extensions/plugin_lang_analysis/template/index.php	(revision 23421)
+++ /extensions/plugin_lang_analysis/template/index.php	(revision 23421)
@@ -0,0 +1,7 @@
+<?php
+$url = '../';
+header( 'Request-URI: '.$url );
+header( 'Content-Location: '.$url );
+header( 'Location: '.$url );
+exit();
+?>
Index: /extensions/plugin_lang_analysis/template/main.tpl
===================================================================
--- /extensions/plugin_lang_analysis/template/main.tpl	(revision 23421)
+++ /extensions/plugin_lang_analysis/template/main.tpl	(revision 23421)
@@ -0,0 +1,185 @@
+{combine_css path=$PLA_PATH|@cat:"template/style.css"}
+{combine_css path=$PLA_PATH|@cat:"template/fontello/css/fontello_pla.css"}
+
+
+<div class="titrePage">
+	<h2>Plugin Language Analysis</h2>
+</div>
+
+{if isset($U_BACK)}
+<p style="text-align:left;margin-left:20px;"><a href="{$U_BACK}" class="buttonLike">{'Back'|@translate}</a></p>
+{/if}
+
+
+{* <!-- select --> *}
+{if $PLA_STEP=='select'}
+{footer_script}{literal}
+$('form#pla').submit(function() {
+  window.location.href = location.href + '&plugin_id=' + $(this).find('select').val();
+  return false;
+});
+{/literal}{/footer_script}
+
+<form method="GET" action="{$F_ACTION}" class="properties" id="pla">
+<fieldset>
+  <legend>{'How to use?'|@translate}</legend>
+
+  <p>
+    This tool analyzes each file of the selected plugin, searching for <b>l10n</b>, <b>l10n_dec</b> and <b>translate</b> functions.<br>
+    Then it compares the matched strings to the content of the plugin language file and the common and admin core language files.<br>
+    Each file of the plugin can be configured as <b>Common</b> (public) or <b>Admin</b>.<br>
+    <br>
+    When the analysis is complete, each string can have one of these three status :
+  </p>
+  
+  <dl style="margin-left:30px;">
+    <dt class="iconpla-attention">missing</dt>
+    <dd>
+      not in the plugin language file<br>
+      <b>AND</b> not in the common language file<br>
+      <b>AND</b> (<br>
+      &nbsp;&nbsp;&nbsp;&nbsp;belonging to at least one common file<br>
+      &nbsp;&nbsp;&nbsp;&nbsp;<b>OR</b> not in the admin language file<br>
+      )
+    </dd>
+  
+    <dt class="iconpla-attention-circled">useless</dt>
+    <dd>
+      in the plugin language file<br>
+      <b>AND</b> (<br>
+      &nbsp;&nbsp;&nbsp;&nbsp;in the common language file<br>
+      &nbsp;&nbsp;&nbsp;&nbsp;<b>OR</b> (<br>
+      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;not belonging to any common file<br>
+      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>AND</b> in the admin language file<br>
+      &nbsp;&nbsp;&nbsp;&nbsp;)<br>
+      )
+    </dd>
+  
+    <dt class="iconpla-ok-squared">ok</dt>
+    <dd>
+      any other case
+    </dd>
+  </dl>
+  
+  <p>
+    You can click the <span class="icon-plus-circled"></span> icon on the left of each string to see where it is located.
+  </p>
+</fieldset>
+
+<fieldset>
+  <legend>{'Select a plugin'|@translate}</legend>
+  
+  <select name="plugin_id">
+  {foreach from=$PLA_PLUGINS item=plugin}
+    <option value="{$plugin.id}">{$plugin.id} ({$plugin.version})</option>
+  {/foreach}
+  </select>
+
+  <p class="formButtons"><input type="submit" value="{'Continue'|@translate}"></p>
+</fieldset>
+</form>
+
+{* <!-- configure --> *}
+{else if $PLA_STEP=='config'}
+{footer_script}{literal}
+$('.type-button span').click(function() {
+  $(this).siblings('span').removeClass('active');
+  $(this).addClass('active');
+  
+  if ($(this).data('type') == 'common') {
+    $(this).nextAll('input').val('false');
+  }
+  else {
+    $(this).nextAll('input').val('true');
+  }
+});
+{/literal}{/footer_script}
+
+<form method="POST" action="{$F_ACTION}" class="properties">
+<fieldset>
+  <legend>{'Select dependencies'|@translate}</legend>
+  
+  <b>{'Plugin language file name'|@translate}</b>
+  <input type="test" name="filename" value="{$PLA_FILENAME}">
+  <br><br>
+  
+  {foreach from=$PLA_FILES item=file}
+    <div class="type-button">
+      <span class="item {if not $file.is_admin}active{/if}" data-type="common">{'Common'|@translate}</span><!--
+      --><span class="item {if $file.is_admin}active{/if}" data-type="admin">{'Admin'|@translate}</span>
+      <input type="hidden" name="files[{$file.path}]" value="{if $file.is_admin}true{else}false{/if}">
+    </div>
+    {$file.path}<br>
+  {/foreach}
+  
+  <p class="formButtons"><input type="submit" value="{'Continue'|@translate}"></p>
+</fieldset>
+</form>
+
+{* <!-- analysis --> *}
+{else if $PLA_STEP=='analysis'}
+{footer_script}{literal}
+$('.strings tr td:first-child').click(function() {
+  if ($(this).hasClass('icon-plus-circled')) {
+    $(this).removeClass('icon-plus-circled').addClass('iconpla-minus-circled');
+    $(this).next().children('ul').show();
+  }
+  else {
+    $(this).removeClass('iconpla-minus-circled').addClass('icon-plus-circled');
+    $(this).next().children('ul').hide();
+  }
+});
+{/literal}{/footer_script}
+
+<fieldset>
+  <legend>{$PLA_PLUGIN}</legend>
+  
+  <table class="strings">
+  <thead>
+    <tr>
+      <th></th>
+      <th class="legend">
+        <span class="missing">{'Missing'|@translate}</span>
+        <span class="useless">{'Useless'|@translate}</span>
+      </th>
+      <th>{'Dependency'|@translate}</th>
+      <th>{'In plugin'|@translate}</th>
+      <th>{'In common'|@translate}</th>
+      <th>{'In admin'|@translate}</th>
+    </tr>
+  </thead>
+    
+  <tbody>
+  {foreach from=$PLA_STRINGS item=data key=string}
+    <tr class="string {$data.stat}">
+      <td class="icon-plus-circled"></td>
+      <td>
+        {$string|htmlspecialchars}
+        <ul>
+        {foreach from=$data.files item=lines key=file}
+          <li>{$file} <i>({', '|@implode:$lines})</i></li>
+        {/foreach}
+        </ul>
+      </td>
+      <td>{if $data.is_admin}{'Admin'|@translate}{else}{'Common'|@translate}{/if}</td>
+      <td>{if $data.in_plugin}<b>{'Yes'|@translate}</b>{else}{'No'|@translate}{/if}</td>
+      <td>{if $data.in_common}<b>{'Yes'|@translate}</b>{else}{'No'|@translate}{/if}</td>
+      <td>{if $data.in_admin}<b>{'Yes'|@translate}</b>{else}{'No'|@translate}{/if}</td>
+    </tr>
+  {/foreach}
+  </tbody>
+  
+  <tfoot>
+    <tr>
+      <th></th>
+      <th>{'%d strings : %d missing and %d useless.'|@translate|sprintf:$PLA_COUNTS.total:$PLA_COUNTS.missing:$PLA_COUNTS.useless}</th>
+      <th></th>
+      <th></th>
+      <th></th>
+      <th></th>
+    </tr>
+  </tfoot>
+  </table>
+</fieldset>
+
+{/if}
Index: /extensions/plugin_lang_analysis/template/style.css
===================================================================
--- /extensions/plugin_lang_analysis/template/style.css	(revision 23421)
+++ /extensions/plugin_lang_analysis/template/style.css	(revision 23421)
@@ -0,0 +1,115 @@
+.type-button {
+  display:inline-block;
+  white-space:no-wrap;
+}  
+  .type-button span {
+    display:inline-block;
+    padding:5px 8px;
+    margin:1px;
+    border:1px solid #d3d3d3;
+    background:#e6e6e6;
+    color:#555;
+    cursor:pointer;
+  }
+    .type-button span.active {
+      color:#212121;
+      border-color:#aaa;
+      background:#fff;
+    }
+    .type-button span:first-of-type {
+      border-radius:5px 0 0 5px;
+    }
+    .type-button span:last-of-type {
+      border-radius:0 5px 5px 0;
+    }
+    
+.strings {
+  border-collapse:collapse;
+  text-align:left !important;
+  margin:0;
+}
+  .strings th {
+    background:#999;
+    color:#eee;
+    padding:2px 5px;
+  }
+    .strings thead th:not(:first-of-type) {
+      border-left:1px solid #eee;
+    }
+    .strings thead th:first-of-type {
+      border-radius:5px 0 0 0;
+    }
+    .strings thead th:last-of-type {
+      border-radius:0 5px 0 0;
+    }
+    .strings tfoot th:first-of-type {
+      border-radius:0 0 0 5px;
+    }
+    .strings tfoot th:last-of-type {
+      border-radius:0 0 5px 0;
+    }
+  
+  .strings td {
+    background:#e7e7e7;
+    border-bottom:1px solid #ccc;
+    padding:2px 3px;
+    vertical-align:top;
+    color:#444;
+  }
+    .strings tr.missing td {
+      background:#EBD9D9;
+    }
+    .strings tr.useless td {
+      background:#DBEACC;
+    }
+    .strings tr:hover td {
+      background:#D9E2EB;
+    }
+  
+  .strings tr td:first-child {
+    cursor:pointer;
+    color:#666;
+  }
+    .strings tr td:first-child:hover:before {
+      color:#222;;
+    }
+  .strings tr td:nth-child(2) {
+    min-width:300px;
+    max-width:700px;
+  }
+  .strings tr td:nth-last-child(-n+3) {
+    text-align:center;
+  }
+  .strings tr td:nth-last-child(4) {
+    text-align:right;
+  }
+  
+  .strings ul {
+    border-top:1px solid #ccc;
+    display:none;
+    margin:3px 0 3px -3px !important;
+    padding:3px 0 0 20px !important;
+    font-family:monospace;
+    font-size:13px;
+  }
+    .strings ul li {
+      margin:0 !important;
+      line-height:1.5em !important;
+      list-style:disc;
+    }
+    
+  .strings .legend span:before {
+    content:" ";
+    display:inline-block;
+    width:10px;
+    height:10px;
+    overflow:hidden;
+    margin:0 3px 0 10px;
+  }
+    .strings .legend span.missing:before {
+      background:#EAAFAF;
+    }
+    .strings .legend span.useless:before {
+      background:#BFE599;
+    }
+  
