Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch '1.3' into scaffold-design

  • Loading branch information...
commit ee8ebce8373eafd01bd9bbfc010f0b18efd748a0 2 parents 6ba16f3 + 84840c7
@markstory markstory authored
Showing with 2,407 additions and 1,516 deletions.
  1. +3 −80 app/webroot/test.php
  2. +11 −4 cake/console/libs/tasks/fixture.php
  3. +6 −5 cake/console/libs/tasks/model.php
  4. +38 −6 cake/console/libs/tasks/test.php
  5. +2 −2 cake/console/libs/tasks/view.php
  6. +128 −114 cake/console/libs/testsuite.php
  7. +8 −1 cake/console/templates/default/classes/model.ctp
  8. +16 −13 cake/libs/configure.php
  9. +1 −1  cake/libs/controller/components/auth.php
  10. +3 −3 cake/libs/controller/components/request_handler.php
  11. +54 −29 cake/libs/inflector.php
  12. +148 −139 cake/libs/l10n.php
  13. +6 −1 cake/libs/model/connection_manager.php
  14. +12 −0 cake/libs/model/datasources/dbo/dbo_postgres.php
  15. +12 −6 cake/libs/model/datasources/dbo_source.php
  16. +27 −5 cake/libs/model/model.php
  17. +21 −0 cake/libs/router.php
  18. +42 −11 cake/libs/sanitize.php
  19. +73 −11 cake/libs/validation.php
  20. +9 −44 cake/libs/view/helpers/cache.php
  21. +6 −8 cake/libs/view/helpers/form.php
  22. +1 −0  cake/libs/view/helpers/jquery_engine.php
  23. +2 −3 cake/libs/view/helpers/prototype_engine.php
  24. +13 −2 cake/tests/cases/console/libs/tasks/model.test.php
  25. +7 −6 cake/tests/cases/dispatcher.test.php
  26. +5 −5 cake/tests/cases/libs/code_coverage_manager.test.php
  27. +12 −1 cake/tests/cases/libs/configure.test.php
  28. +25 −0 cake/tests/cases/libs/controller/components/auth.test.php
  29. +4 −4 cake/tests/cases/libs/controller/components/request_handler.test.php
  30. +24 −0 cake/tests/cases/libs/inflector.test.php
  31. +151 −157 cake/tests/cases/libs/l10n.test.php
  32. +19 −1 cake/tests/cases/libs/model/datasources/dbo_source.test.php
  33. +23 −0 cake/tests/cases/libs/model/model_read.test.php
  34. +52 −0 cake/tests/cases/libs/router.test.php
  35. +37 −15 cake/tests/cases/libs/sanitize.test.php
  36. +6 −7 cake/tests/cases/libs/test_manager.test.php
  37. +64 −7 cake/tests/cases/libs/validation.test.php
  38. +77 −13 cake/tests/cases/libs/view/helpers/cache.test.php
  39. +16 −5 cake/tests/cases/libs/view/helpers/form.test.php
  40. +3 −3 cake/tests/cases/libs/view/helpers/jquery_engine.test.php
  41. +11 −0 cake/tests/cases/libs/view/helpers/prototype_engine.test.php
  42. +1 −0  cake/tests/cases/libs/view/view.test.php
  43. +249 −0 cake/tests/lib/cake_test_suite_dispatcher.php
  44. +0 −60 cake/tests/lib/cake_text_reporter.php
  45. +0 −101 cake/tests/lib/cli_reporter.php
  46. +107 −90 cake/tests/lib/code_coverage_manager.php
  47. +226 −0 cake/tests/lib/reporter/cake_base_reporter.php
  48. +179 −0 cake/tests/lib/reporter/cake_cli_reporter.php
  49. +163 −56 cake/tests/lib/{cake_reporter.php → reporter/cake_html_reporter.php}
  50. +199 −0 cake/tests/lib/reporter/cake_text_reporter.php
  51. +7 −1 cake/tests/lib/{ → templates}/footer.php
  52. +2 −2 cake/tests/lib/{ → templates}/header.php
  53. +2 −2 cake/tests/lib/{content.php → templates/menu.php}
  54. +4 −2 cake/tests/lib/{ → templates}/simpletest.php
  55. +8 −5 cake/tests/lib/{ → templates}/xdebug.php
  56. +82 −485 cake/tests/lib/test_manager.php
View
83 app/webroot/test.php
@@ -18,7 +18,6 @@
* @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
*/
set_time_limit(0);
-ini_set('memory_limit','128M');
ini_set('display_errors', 1);
/**
* Use the DS to separate the directories in other defines
@@ -85,89 +84,13 @@
define('TEST_CAKE_CORE_INCLUDE_PATH', CAKE_CORE_INCLUDE_PATH);
}
-require_once CAKE_TESTS_LIB . 'test_manager.php';
-
if (Configure::read('debug') < 1) {
die(__('Debug setting does not allow access to this url.', true));
}
-if (!isset($_SERVER['SERVER_NAME'])) {
- $_SERVER['SERVER_NAME'] = '';
-}
-if (empty( $_GET['output'])) {
- $_GET['output'] = 'html';
-}
-/**
- *
- * Used to determine output to display
- */
-define('CAKE_TEST_OUTPUT_HTML', 1);
-define('CAKE_TEST_OUTPUT_TEXT', 2);
-
-if (isset($_GET['output']) && $_GET['output'] == 'html') {
- define('CAKE_TEST_OUTPUT', CAKE_TEST_OUTPUT_HTML);
-} else {
- Debugger::output('txt');
- define('CAKE_TEST_OUTPUT', CAKE_TEST_OUTPUT_TEXT);
-}
-
-if (!App::import('Vendor', 'simpletest' . DS . 'reporter')) {
- CakePHPTestHeader();
- include CAKE_TESTS_LIB . 'simpletest.php';
- CakePHPTestSuiteFooter();
- exit();
-}
-
-$analyzeCodeCoverage = false;
-if (isset($_GET['code_coverage'])) {
- $analyzeCodeCoverage = true;
- require_once CAKE_TESTS_LIB . 'code_coverage_manager.php';
- if (!extension_loaded('xdebug')) {
- CakePHPTestHeader();
- include CAKE_TESTS_LIB . 'xdebug.php';
- CakePHPTestSuiteFooter();
- exit();
- }
-}
-
-CakePHPTestHeader();
-CakePHPTestSuiteHeader();
-define('RUN_TEST_LINK', $_SERVER['PHP_SELF']);
-
-if (isset($_GET['group'])) {
- if ('all' == $_GET['group']) {
- TestManager::runAllTests(CakeTestsGetReporter());
- } else {
- if ($analyzeCodeCoverage) {
- CodeCoverageManager::start($_GET['group'], CakeTestsGetReporter());
- }
- TestManager::runGroupTest(ucfirst($_GET['group']), CakeTestsGetReporter());
- if ($analyzeCodeCoverage) {
- CodeCoverageManager::report();
- }
- }
+require_once CAKE_TESTS_LIB . 'cake_test_suite_dispatcher.php';
- CakePHPTestRunMore();
- CakePHPTestAnalyzeCodeCoverage();
-} elseif (isset($_GET['case'])) {
- if ($analyzeCodeCoverage) {
- CodeCoverageManager::start($_GET['case'], CakeTestsGetReporter());
- }
-
- TestManager::runTestCase($_GET['case'], CakeTestsGetReporter());
-
- if ($analyzeCodeCoverage) {
- CodeCoverageManager::report();
- }
+$Dispatcher = new CakeTestSuiteDispatcher();
+$Dispatcher->dispatch();
- CakePHPTestRunMore();
- CakePHPTestAnalyzeCodeCoverage();
-} elseif (isset($_GET['show']) && $_GET['show'] == 'cases') {
- CakePHPTestCaseList();
-} else {
- CakePHPTestGroupTestList();
-}
-CakePHPTestSuiteFooter();
-$output = ob_get_clean();
-echo $output;
?>
View
15 cake/console/libs/tasks/fixture.php
@@ -54,6 +54,7 @@ class FixtureTask extends Shell {
* The db connection being used for baking
*
* @var string
+ * @access public
*/
var $connection = null;
@@ -61,6 +62,7 @@ class FixtureTask extends Shell {
* Schema instance
*
* @var object
+ * @access protected
*/
var $_Schema = null;
@@ -140,6 +142,7 @@ function __interactive() {
*
* @param string $modelName Name of model you are dealing with.
* @return array Array of import options.
+ * @access public
*/
function importOptions($modelName) {
$options = array();
@@ -167,8 +170,8 @@ function importOptions($modelName) {
* @param string $model Name of model to bake.
* @param string $useTable Name of table to use.
* @param array $importOptions Options for var $import
- * @return string Baked fixture
- * @access private
+ * @return string Baked fixture content
+ * @access public
*/
function bake($model, $useTable = false, $importOptions = array()) {
if (!class_exists('CakeSchema')) {
@@ -228,8 +231,8 @@ function bake($model, $useTable = false, $importOptions = array()) {
*
* @param string $model name of the model being generated
* @param string $fixture Contents of the fixture file.
+ * @return string Content saved into fixture file.
* @access public
- * @return void
*/
function generateFixtureFile($model, $otherVars) {
$defaults = array('table' => null, 'schema' => null, 'records' => null, 'import' => null, 'fields' => null);
@@ -255,6 +258,7 @@ function generateFixtureFile($model, $otherVars) {
*
* @param array $table Table schema array
* @return string fields definitions
+ * @access protected
*/
function _generateSchema($tableInfo) {
$schema = $this->_Schema->generateTable('f', $tableInfo);
@@ -266,6 +270,7 @@ function _generateSchema($tableInfo) {
*
* @param array $table Table schema array
* @return array Array of records to use in the fixture.
+ * @access protected
*/
function _generateRecords($tableInfo, $recordCount = 1) {
$records = array();
@@ -337,6 +342,7 @@ function _generateRecords($tableInfo, $recordCount = 1) {
*
* @param array $records Array of records to be converted to string
* @return string A string value of the $records array.
+ * @access protected
*/
function _makeRecordString($records) {
$out = "array(\n";
@@ -360,6 +366,7 @@ function _makeRecordString($records) {
* @param string $modelName name of the model to take records from.
* @param string $useTable Name of table to use.
* @return array Array of records.
+ * @access protected
*/
function _getRecordsFromTable($modelName, $useTable = null) {
if ($this->interactive) {
@@ -378,7 +385,7 @@ function _getRecordsFromTable($modelName, $useTable = null) {
'recursive' => -1
));
$db =& ConnectionManager::getDataSource($modelObject->useDbConfig);
- $schema = $modelObject->schema();
+ $schema = $modelObject->schema(true);
$out = array();
foreach ($records as $record) {
$row = array();
View
11 cake/console/libs/tasks/model.php
@@ -186,7 +186,7 @@ function __interactive() {
if (in_array($useTable, $this->__tables)) {
$tempModel = new Model(array('name' => $currentModelName, 'table' => $useTable, 'ds' => $this->connection));
- $fields = $tempModel->schema();
+ $fields = $tempModel->schema(true);
if (!array_key_exists('id', $fields)) {
$primaryKey = $this->findPrimaryKey($fields);
}
@@ -447,7 +447,7 @@ function doAssociations(&$model) {
$this->out(__('One moment while the associations are detected.', true));
}
- $fields = $model->schema();
+ $fields = $model->schema(true);
if (empty($fields)) {
return false;
}
@@ -487,7 +487,7 @@ function doAssociations(&$model) {
* @return array $associations with belongsTo added in.
*/
function findBelongsTo(&$model, $associations) {
- $fields = $model->schema();
+ $fields = $model->schema(true);
foreach ($fields as $fieldName => $field) {
$offset = strpos($fieldName, '_id');
if ($fieldName != $model->primaryKey && $fieldName != 'parent_id' && $offset !== false) {
@@ -562,7 +562,7 @@ function findHasAndBelongsToMany(&$model, $associations) {
$foreignKey = $this->_modelKey($model->name);
foreach ($this->__tables as $otherTable) {
$tempOtherModel = $this->_getModelObject($this->_modelName($otherTable), $otherTable);
- $modelFieldsTemp = $tempOtherModel->schema();
+ $modelFieldsTemp = $tempOtherModel->schema(true);
$offset = strpos($otherTable, $model->table . '_');
$otherOffset = strpos($otherTable, '_' . $model->table);
@@ -695,7 +695,7 @@ function _generatePossibleKeys() {
$possible = array();
foreach ($this->__tables as $otherTable) {
$tempOtherModel = & new Model(array('table' => $otherTable, 'ds' => $this->connection));
- $modelFieldsTemp = $tempOtherModel->schema();
+ $modelFieldsTemp = $tempOtherModel->schema(true);
foreach ($modelFieldsTemp as $fieldName => $field) {
if ($field['type'] == 'integer' || $field['type'] == 'string') {
$possible[$otherTable][] = $fieldName;
@@ -821,6 +821,7 @@ function getAllTables($useDbConfig = null) {
$tables = array();
$db =& ConnectionManager::getDataSource($useDbConfig);
+ $db->cacheSources = false;
$usePrefix = empty($db->config['prefix']) ? '' : $db->config['prefix'];
if ($usePrefix) {
foreach ($db->listSources() as $table) {
View
44 cake/console/libs/tasks/test.php
@@ -46,6 +46,7 @@ class TestTask extends Shell {
* Tasks used.
*
* @var array
+ * @access public
*/
var $tasks = array('Template');
@@ -53,6 +54,7 @@ class TestTask extends Shell {
* class types that methods can be generated for
*
* @var array
+ * @access public
*/
var $classTypes = array('Model', 'Controller', 'Component', 'Behavior', 'Helper');
@@ -60,6 +62,7 @@ class TestTask extends Shell {
* Internal list of fixtures that have been added so far.
*
* @var string
+ * @access protected
*/
var $_fixtures = array();
@@ -164,6 +167,7 @@ function bake($type, $className) {
* Interact with the user and get their chosen type. Can exit the script.
*
* @return string Users chosen type.
+ * @access public
*/
function getObjectType() {
$this->hr();
@@ -188,6 +192,7 @@ function getObjectType() {
*
* @param string $objectType Type of object to list classes for i.e. Model, Controller.
* @return string Class name the user chose.
+ * @access public
*/
function getClassName($objectType) {
$options = App::objects(strtolower($objectType));
@@ -207,8 +212,11 @@ function getClassName($objectType) {
/**
* Checks whether the chosen type can find its own fixtures.
* Currently only model, and controller are supported
- *
+ *
+ * @param string $type The Type of object you are generating tests for eg. controller
+ * @param string $className the Classname of the class the test is being generated for.
* @return boolean
+ * @access public
*/
function typeCanDetectFixtures($type) {
$type = strtolower($type);
@@ -218,7 +226,10 @@ function typeCanDetectFixtures($type) {
/**
* Check if a class with the given type is loaded or can be loaded.
*
+ * @param string $type The Type of object you are generating tests for eg. controller
+ * @param string $className the Classname of the class the test is being generated for.
* @return boolean
+ * @access public
*/
function isLoadableClass($type, $class) {
return App::import($type, $class);
@@ -228,7 +239,10 @@ function isLoadableClass($type, $class) {
* Construct an instance of the class to be tested.
* So that fixtures can be detected
*
- * @return object
+ * @param string $type The Type of object you are generating tests for eg. controller
+ * @param string $class the Classname of the class the test is being generated for.
+ * @return object And instance of the class that is going to be tested.
+ * @access public
*/
function &buildTestSubject($type, $class) {
ClassRegistry::flush();
@@ -245,7 +259,10 @@ function &buildTestSubject($type, $class) {
/**
* Gets the real class name from the cake short form.
*
+ * @param string $type The Type of object you are generating tests for eg. controller
+ * @param string $class the Classname of the class the test is being generated for.
* @return string Real classname
+ * @access public
*/
function getRealClassName($type, $class) {
if (strtolower($type) == 'model') {
@@ -260,6 +277,7 @@ function getRealClassName($type, $class) {
*
* @param string $className Name of class to look at.
* @return array Array of method names.
+ * @access public
*/
function getTestableMethods($className) {
$classMethods = get_class_methods($className);
@@ -278,8 +296,9 @@ function getTestableMethods($className) {
* Generate the list of fixtures that will be required to run this test based on
* loaded models.
*
- * @param object The object you want to generate fixtures for.
+ * @param object $subject The object you want to generate fixtures for.
* @return array Array of fixtures to be included in the test.
+ * @access public
*/
function generateFixtureList(&$subject) {
$this->_fixtures = array();
@@ -295,6 +314,7 @@ function generateFixtureList(&$subject) {
* Process a model recursively and pull out all the
* model names converting them to fixture names.
*
+ * @param Model $subject A Model class to scan for associations and pull fixtures off of.
* @return void
* @access protected
*/
@@ -319,6 +339,7 @@ function _processModel(&$subject) {
* Process all the models attached to a controller
* and generate a fixture list.
*
+ * @param Controller $subject A controller to pull model names off of.
* @return void
* @access protected
*/
@@ -337,6 +358,7 @@ function _processController(&$subject) {
* Add classname to the fixture list.
* Sets the app. or plugin.plugin_name. prefix.
*
+ * @param string $name Name of the Model class that a fixture might be required for.
* @return void
* @access protected
*/
@@ -354,7 +376,8 @@ function _addFixture($name) {
/**
* Interact with the user to get additional fixtures they want to use.
*
- * @return void
+ * @return array Array of fixtures the user wants to add.
+ * @access public
*/
function getUserFixtures() {
$proceed = $this->in(__('Bake could not detect fixtures, would you like to add some?', true), array('y','n'), 'n');
@@ -372,7 +395,9 @@ function getUserFixtures() {
* Is a mock class required for this type of test?
* Controllers require a mock class.
*
+ * @param string $type The type of object tests are being generated for eg. controller.
* @return boolean
+ * @access public
*/
function hasMockClass($type) {
$type = strtolower($type);
@@ -382,7 +407,10 @@ function hasMockClass($type) {
/**
* Generate a constructor code snippet for the type and classname
*
+ * @param string $type The Type of object you are generating tests for eg. controller
+ * @param string $className the Classname of the class the test is being generated for.
* @return string Constructor snippet for the thing you are building.
+ * @access public
*/
function generateConstructor($type, $fullClassName) {
$type = strtolower($type);
@@ -397,10 +425,13 @@ function generateConstructor($type, $fullClassName) {
}
/**
- * make the filename for the test case. resolve the suffixes for controllers
+ * Make the filename for the test case. resolve the suffixes for controllers
* and get the plugin path if needed.
*
- * @return string filename the test should be created on
+ * @param string $type The Type of object you are generating tests for eg. controller
+ * @param string $className the Classname of the class the test is being generated for.
+ * @return string filename the test should be created on.
+ * @access public
*/
function testCaseFileName($type, $className) {
$path = $this->path;
@@ -418,6 +449,7 @@ function testCaseFileName($type, $className) {
* Show help file.
*
* @return void
+ * @access public
*/
function help() {
$this->hr();
View
4 cake/console/libs/tasks/view.php
@@ -283,7 +283,7 @@ function __loadController() {
$displayField = $modelObj->displayField;
$singularVar = Inflector::variable($modelClass);
$singularHumanName = $this->_singularHumanName($modelClass);
- $schema = $modelObj->schema();
+ $schema = $modelObj->schema(true);
$fields = array_keys($schema);
$associations = $this->__associations($modelObj);
} else {
@@ -469,7 +469,7 @@ function __associations(&$model) {
$associations[$type][$assocKey]['displayField'] = $model->{$assocKey}->displayField;
$associations[$type][$assocKey]['foreignKey'] = $assocData['foreignKey'];
$associations[$type][$assocKey]['controller'] = Inflector::pluralize(Inflector::underscore($assocData['className']));
- $associations[$type][$assocKey]['fields'] = array_keys($model->{$assocKey}->schema());
+ $associations[$type][$assocKey]['fields'] = array_keys($model->{$assocKey}->schema(true));
}
}
return $associations;
View
242 cake/console/libs/testsuite.php
@@ -70,14 +70,6 @@ class TestSuiteShell extends Shell {
var $doCoverage = false;
/**
- * The headline for the test output
- *
- * @var string
- * @access public
- */
- var $headline = 'CakePHP Test Shell';
-
-/**
* Initialization method installs Simpletest and loads all plugins
*
* @return void
@@ -94,61 +86,87 @@ function initialize() {
$this->__installSimpleTest();
require_once CAKE . 'tests' . DS . 'lib' . DS . 'test_manager.php';
- require_once CAKE . 'tests' . DS . 'lib' . DS . 'cli_reporter.php';
+ require_once CAKE . 'tests' . DS . 'lib' . DS . 'reporter' . DS . 'cake_cli_reporter.php';
$plugins = App::objects('plugin');
foreach ($plugins as $p) {
$this->plugins[] = Inflector::underscore($p);
}
+ $this->parseArgs();
+ $this->getManager();
}
/**
- * Main entry point to this shell
+ * Parse the arguments given into the Shell object properties.
*
* @return void
* @access public
*/
- function main() {
- $this->out($this->headline);
- $this->hr();
+ function parseArgs() {
+ if (empty($this->args)) {
+ return;
+ }
+ $this->category = $this->args[0];
- if (count($this->args) > 0) {
- $this->category = $this->args[0];
+ if (!in_array($this->category, array('app', 'core'))) {
+ $this->isPluginTest = true;
+ }
- if (!in_array($this->category, array('app', 'core'))) {
- $this->isPluginTest = true;
- }
+ if (isset($this->args[1])) {
+ $this->type = $this->args[1];
+ }
- if (isset($this->args[1])) {
- $this->type = $this->args[1];
+ if (isset($this->args[2])) {
+ if ($this->args[2] == 'cov') {
+ $this->doCoverage = true;
+ } else {
+ $this->file = Inflector::underscore($this->args[2]);
}
+ }
- if (isset($this->args[2])) {
- if ($this->args[2] == 'cov') {
- $this->doCoverage = true;
- } else {
- $this->file = Inflector::underscore($this->args[2]);
- }
- }
+ if (isset($this->args[3]) && $this->args[3] == 'cov') {
+ $this->doCoverage = true;
+ }
+ }
- if (isset($this->args[3]) && $this->args[3] == 'cov') {
- $this->doCoverage = true;
- }
- } else {
- $this->err('Sorry, you did not pass any arguments!');
+/**
+ * Gets a manager instance, and set the app/plugin properties.
+ *
+ * @return void
+ */
+ function getManager() {
+ $this->Manager = new TestManager();
+ $this->Manager->appTest = ($this->category === 'app');
+ if ($this->isPluginTest) {
+ $this->Manager->pluginTest = $this->category;
+ }
+ }
+
+/**
+ * Main entry point to this shell
+ *
+ * @return void
+ * @access public
+ */
+ function main() {
+ $this->out(__('CakePHP Test Shell', true));
+ $this->hr();
+
+ if (count($this->args) == 0) {
+ $this->error(__('Sorry, you did not pass any arguments!', true));
}
if ($this->__canRun()) {
- $this->out('Running '.$this->category.' '.$this->type.' '.$this->file);
+ $message = sprintf(__('Running %s %s %s', true), $this->category, $this->type, $this->file);
+ $this->out($message);
$exitCode = 0;
if (!$this->__run()) {
$exitCode = 1;
}
- exit($exitCode);
+ $this->_stop($exitCode);
} else {
- $this->err('Sorry, the tests could not be found.');
- exit(1);
+ $this->error(__('Sorry, the tests could not be found.', true));
}
}
@@ -201,50 +219,37 @@ function __canRun() {
$isPlugin = in_array(Inflector::underscore($this->category), $this->plugins);
if ($isNeitherAppNorCore && !$isPlugin) {
- $this->err($this->category.' is an invalid test category (either "app", "core" or name of a plugin)');
+ $message = sprintf(
+ __('%s is an invalid test category (either "app", "core" or name of a plugin)', true),
+ $this->category
+ );
+ $this->error($message);
return false;
}
$folder = $this->__findFolderByCategory($this->category);
if (!file_exists($folder)) {
- $this->err($folder . ' not found');
+ $this->err(sprintf(__('%s not found', true), $folder));
return false;
}
if (!in_array($this->type, array('all', 'group', 'case'))) {
- $this->err($this->type.' is invalid. Should be case, group or all');
+ $this->err(sprintf(__('%s is invalid. Should be case, group or all', true), $this->type));
return false;
}
- switch ($this->type) {
- case 'all':
- return true;
- break;
- case 'group':
- if (file_exists($folder.DS.'groups'.DS.$this->file.'.group.php')) {
- return true;
- }
- break;
- case 'case':
- if ($this->category == 'app' && file_exists($folder.DS.'cases'.DS.$this->file.'.test.php')) {
- return true;
- }
- $coreCaseExists = file_exists($folder.DS.'cases'.DS.$this->file.'.test.php');
- $coreLibCaseExists = file_exists($folder.DS.'cases'.DS.'libs'.DS.$this->file.'.test.php');
- if ($this->category == 'core' && ($coreCaseExists || $coreLibCaseExists)) {
- return true;
- }
-
- if ($isPlugin && file_exists($folder.DS.'cases'.DS.$this->file.'.test.php')) {
- return true;
- }
- break;
+ $fileName = $this->__getFileName($folder, $this->isPluginTest);
+ if ($fileName === true || file_exists($folder . $fileName)) {
+ return true;
}
- $this->err($this->category.' '.$this->type.' '.$this->file.' is an invalid test identifier');
+ $message = sprintf(
+ __('%s %s %s is an invalid test identifier', true),
+ $this->category, $this->type, $this->file
+ );
+ $this->err($message);
return false;
}
-
/**
* Executes the tests depending on our settings
*
@@ -252,69 +257,82 @@ function __canRun() {
* @access private
*/
function __run() {
- $reporter = new CLIReporter();
- $this->__setGetVars();
+ $Reporter = new CakeCliReporter('utf-8', array(
+ 'app' => $this->Manager->appTest,
+ 'plugin' => $this->Manager->pluginTest,
+ 'group' => ($this->type === 'group'),
+ 'codeCoverage' => $this->doCoverage
+ ));
if ($this->type == 'all') {
- return TestManager::runAllTests($reporter);
+ return $this->Manager->runAllTests($Reporter);
}
if ($this->doCoverage) {
if (!extension_loaded('xdebug')) {
- $this->out('You must install Xdebug to use the CakePHP(tm) Code Coverage Analyzation. Download it from http://www.xdebug.org/docs/install');
- exit(0);
+ $this->out(__('You must install Xdebug to use the CakePHP(tm) Code Coverage Analyzation. Download it from http://www.xdebug.org/docs/install', true));
+ $this->_stop(0);
}
}
if ($this->type == 'group') {
$ucFirstGroup = ucfirst($this->file);
-
- $path = CORE_TEST_GROUPS;
- if ($this->category == 'app') {
- $path = APP_TEST_GROUPS;
- } elseif ($this->isPluginTest) {
- $path = APP.'plugins'.DS.$this->category.DS.'tests'.DS.'groups';
- }
-
if ($this->doCoverage) {
require_once CAKE . 'tests' . DS . 'lib' . DS . 'code_coverage_manager.php';
- CodeCoverageManager::start($ucFirstGroup, $reporter);
- }
- $result = TestManager::runGroupTest($ucFirstGroup, $reporter);
- if ($this->doCoverage) {
- CodeCoverageManager::report();
+ CodeCoverageManager::init($ucFirstGroup, $Reporter);
+ CodeCoverageManager::start();
}
+ $result = $this->Manager->runGroupTest($ucFirstGroup, $Reporter);
return $result;
}
- if ($this->category === 'core') {
- $coreCaseExists = file_exists(CORE_TEST_CASES.DS.$this->file.'.test.php');
- if ($coreCaseExists) {
- $case = $this->file . '.test.php';
- } else {
- $case = 'libs' . DS . $this->file . '.test.php';
- }
- } elseif ($this->category === 'app') {
- $case = $this->file.'.test.php';
- } elseif ($this->isPluginTest) {
- $case = $this->file.'.test.php';
- }
+
+ $folder = $folder = $this->__findFolderByCategory($this->category);
+ $case = $this->__getFileName($folder, $this->isPluginTest);
if ($this->doCoverage) {
require_once CAKE . 'tests' . DS . 'lib' . DS . 'code_coverage_manager.php';
- CodeCoverageManager::start($case, $reporter);
+ CodeCoverageManager::init($case, $Reporter);
+ CodeCoverageManager::start();
}
+ $result = $this->Manager->runTestCase($case, $Reporter);
+ return $result;
+ }
- $result = TestManager::runTestCase($case, $reporter);
- if ($this->doCoverage) {
- CodeCoverageManager::report();
- }
+/**
+ * Gets the concrete filename for the inputted test name and category/type
+ *
+ * @param string $folder Folder name to look for files in.
+ * @param boolean $isPlugin If the test case is a plugin.
+ * @return mixed Either string filename or boolean false on failure. Or true if the type is 'all'
+ * @access private
+ */
+ function __getFileName($folder, $isPlugin) {
+ $ext = $this->Manager->getExtension($this->type);
+ switch ($this->type) {
+ case 'all':
+ return true;
+ case 'group':
+ return $this->file . $ext;
+ case 'case':
+ if ($this->category == 'app' || $isPlugin) {
+ return $this->file . $ext;
+ }
+ $coreCase = $this->file . $ext;
+ $coreLibCase = 'libs' . DS . $this->file . $ext;
- return $result;
+ if ($this->category == 'core' && file_exists($folder . DS . $coreCase)) {
+ return $coreCase;
+ } elseif ($this->category == 'core' && file_exists($folder . DS . $coreLibCase)) {
+ return $coreLibCase;
+ }
+ }
+ return false;
}
/**
- * Finds the correct folder to look for tests for based on the input category
+ * Finds the correct folder to look for tests for based on the input category and type.
*
+ * @param string $category The category of the test. Either 'app', 'core' or a plugin name.
* @return string the folder path
* @access private
*/
@@ -322,20 +340,16 @@ function __findFolderByCategory($category) {
$folder = '';
$paths = array(
'core' => CAKE,
- 'app' => APP
+ 'app' => APP
);
+ $typeDir = $this->type === 'group' ? 'groups' : 'cases';
if (array_key_exists($category, $paths)) {
- $folder = $paths[$category] . 'tests';
+ $folder = $paths[$category] . 'tests' . DS . $typeDir . DS;
} else {
- $scoredCategory = Inflector::underscore($category);
- $folder = APP . 'plugins' . DS . $scoredCategory . DS;
- $pluginPaths = App::path('plugins');
- foreach ($pluginPaths as $path) {
- if (file_exists($path . $scoredCategory . DS . 'tests')) {
- $folder = $path . $scoredCategory . DS . 'tests';
- break;
- }
+ $pluginPath = App::pluginPath($category);
+ if (is_dir($pluginPath . 'tests')) {
+ $folder = $pluginPath . 'tests' . DS . $typeDir . DS;
}
}
return $folder;
@@ -350,8 +364,8 @@ function __findFolderByCategory($category) {
function __setGetVars() {
if (in_array($this->category, $this->plugins)) {
$_GET['plugin'] = $this->category;
- } elseif (in_array(Inflector::Humanize($this->category), $this->plugins)) {
- $_GET['plugin'] = Inflector::Humanize($this->category);
+ } elseif (in_array(Inflector::humanize($this->category), $this->plugins)) {
+ $_GET['plugin'] = Inflector::humanize($this->category);
} elseif ($this->category == 'app') {
$_GET['app'] = true;
}
@@ -368,7 +382,7 @@ function __setGetVars() {
*/
function __installSimpleTest() {
if (!App::import('Vendor', 'simpletest' . DS . 'reporter')) {
- $this->err('Sorry, Simpletest could not be found. Download it from http://simpletest.org and install it to your vendors directory.');
+ $this->err(__('Sorry, Simpletest could not be found. Download it from http://simpletest.org and install it to your vendors directory.', true));
exit;
}
}
View
9 cake/console/templates/default/classes/model.ctp
@@ -42,7 +42,14 @@ if (!empty($validate)):
foreach ($validate as $field => $validations):
echo "\t\t'$field' => array(\n";
foreach ($validations as $key => $validator):
- echo "\t\t\t'$key' => array('rule' => array('$validator')),\n";
+ echo "\t\t\t'$key' => array(\n";
+ echo "\t\t\t\t'rule' => array('$validator'),\n";
+ echo "\t\t\t\t//'message' => 'Your custom message here',\n";
+ echo "\t\t\t\t//'allowEmpty' => false,\n";
+ echo "\t\t\t\t//'required' => false,\n";
+ echo "\t\t\t\t//'last' => false, // Stop validation after this rule\n";
+ echo "\t\t\t\t//'on' => 'create', // Limit validation to 'create' or 'update' operations\n";
+ echo "\t\t\t),\n";
endforeach;
echo "\t\t),\n";
endforeach;
View
29 cake/libs/configure.php
@@ -1,8 +1,6 @@
<?php
/**
- * Short description for file.
- *
- * Long description for filec
+ * App and Configure classes
*
* PHP versions 4 and 5
*
@@ -217,7 +215,7 @@ function delete($var = null) {
* Loads a file from app/config/configure_file.php.
* Config file variables should be formated like:
* `$config['name'] = 'value';`
- * These will be used to create dynamic Configure vars. load() is also used to
+ * These will be used to create dynamic Configure vars. load() is also used to
* load stored config files created with Configure::store()
*
* - To load config files from app/config use `Configure::load('configure_file');`.
@@ -236,7 +234,7 @@ function load($fileName) {
$pluginPath = App::pluginPath($plugin);
}
$pos = strpos($fileName, '..');
-
+
if ($pos === false) {
if ($pluginPath && file_exists($pluginPath . 'config' . DS . $fileName . '.php')) {
include($pluginPath . 'config' . DS . $fileName . '.php');
@@ -450,7 +448,7 @@ function __loadBootstrap($boot) {
}
/**
- * Class and file loader.
+ * Class/file loader and path management.
*
* @link http://book.cakephp.org/view/499/The-App-Class
* @since CakePHP(tm) v 1.2.0.6001
@@ -634,8 +632,9 @@ class App extends Object {
/**
* Used to read information stored path
*
- * Usage
- * App::path('models'); will return all paths for models
+ * Usage:
+ *
+ * `App::path('models'); will return all paths for models`
*
* @param string $type type of path
* @return string array
@@ -731,7 +730,7 @@ function pluginPath($plugin) {
* Passing $type only returns the values for a given value of $key.
*
* @param string $type valid values are: 'model', 'behavior', 'controller', 'component',
- * 'view', 'helper', 'datasource', 'libs', and 'cake'
+ * 'view', 'helper', 'datasource', 'libs', and 'cake'
* @return array numeric keyed array of core lib paths
* @access public
*/
@@ -783,9 +782,11 @@ function core($type = null) {
/**
* Returns an index of objects of the given type, with the physical path to each object.
*
- * @param string $type Type of object, i.e. 'model', 'controller', 'helper', or 'plugin'
- * @param mixed $path Optional
- * @return Configure instance
+ * @param string $type Type of object, i.e. 'model', 'controller', 'helper', or 'plugin'
+ * @param mixed $path Optional Scan only the path given. If null, paths for the chosen
+ * type will be used.
+ * @param boolean $cache Set to false to rescan objects of the chosen type. Defaults to true.
+ * @return mixed Either false on incorrect / miss. Or an array of found objects.
* @access public
*/
function objects($type, $path = null, $cache = true) {
@@ -822,7 +823,7 @@ function objects($type, $path = null, $cache = true) {
$items = array();
foreach ((array)$path as $dir) {
- if ($type === 'file' || $type === 'class' || strpos($dir, $type) !== false) {
+ if ($dir != APP) {
$items = $_this->__list($dir, $types[$type]['suffix'], $extension);
$objects = array_merge($items, array_diff($objects, $items));
}
@@ -1074,6 +1075,7 @@ function __load($file) {
* @param string $name unique name for this map
* @param string $type type object being mapped
* @param string $plugin camelized if object is from a plugin, the name of the plugin
+ * @return void
* @access private
*/
function __map($file, $name, $type, $plugin) {
@@ -1254,6 +1256,7 @@ function __remove($name, $type, $plugin) {
* @param string $path Path to scan for files
* @param string $suffix if false, return only directories. if string, match and return files
* @return array List of directories or files in directory
+ * @access private
*/
function __list($path, $suffix = false, $extension = false) {
if (!class_exists('Folder')) {
View
2  cake/libs/controller/components/auth.php
@@ -337,7 +337,7 @@ function startup(&$controller) {
if ($loginAction == $url) {
$model =& $this->getModel();
if (empty($controller->data) || !isset($controller->data[$model->alias])) {
- if (!$this->Session->check('Auth.redirect') && env('HTTP_REFERER')) {
+ if (!$this->Session->check('Auth.redirect') && !$this->loginRedirect && env('HTTP_REFERER')) {
$this->Session->write('Auth.redirect', $controller->referer(null, true));
}
return false;
View
6 cake/libs/controller/components/request_handler.php
@@ -219,10 +219,10 @@ function startup(&$controller) {
}
$xml = new Xml(trim(file_get_contents('php://input')));
- if (is_object($xml->child('data')) && count($xml->children) == 1) {
- $controller->data = $xml->child('data');
+ if (count($xml->children) == 1 && is_object($dataNode = $xml->child('data'))) {
+ $controller->data = $dataNode->toArray();
} else {
- $controller->data = $xml;
+ $controller->data = $xml->toArray();
}
}
}
View
83 cake/libs/inflector.php
@@ -189,12 +189,12 @@ class Inflector {
var $_singularized = array();
/**
- * Cached Underscored Inflections
+ * Cached Underscore Inflections
*
* @var array
* @access protected
*/
- var $_underscored = array();
+ var $_underscore = array();
/**
* Cached Camelize Inflections
@@ -213,7 +213,7 @@ class Inflector {
var $_classify = array();
/**
- * Tablized cached inflections
+ * Tablize cached inflections
*
* @var array
* @access protected
@@ -244,6 +244,31 @@ function &getInstance() {
}
/**
+ * Cache inflected values, and return if already available
+ *
+ * @param string $type Inflection type
+ * @param string $key Original value
+ * @param string $value Inflected value
+ * @return string Inflected value, from cache
+ * @access protected
+ */
+ function _cache($type, $key, $value = false) {
+ $_this =& Inflector::getInstance();
+
+ $key = '_' . $key;
+ $type = '_' . $type;
+ if ($value !== false) {
+ $_this->{$type}[$key] = $value;
+ return $value;
+ }
+
+ if (!isset($_this->{$type}[$key])) {
+ return false;
+ }
+ return $_this->{$type}[$key];
+ }
+
+/**
* Adds custom inflection $rules, of either 'plural' or 'singular' $type.
*
* @param string $type The type of inflection, either 'singular' or 'plural'
@@ -379,11 +404,11 @@ function singularize($word) {
*/
function camelize($lowerCaseAndUnderscoredWord) {
$_this =& Inflector::getInstance();
-
- if (!isset($_this->_camelize[$lowerCaseAndUnderscoredWord])) {
- $_this->_camelize[$lowerCaseAndUnderscoredWord] = str_replace(" ", "", ucwords(str_replace("_", " ", $lowerCaseAndUnderscoredWord)));
+ if (!($result = $_this->_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord))) {
+ $result = str_replace(' ', '', Inflector::humanize($lowerCaseAndUnderscoredWord));
+ $_this->_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord, $result);
}
- return $_this->_camelize[$lowerCaseAndUnderscoredWord];
+ return $result;
}
/**
@@ -397,11 +422,11 @@ function camelize($lowerCaseAndUnderscoredWord) {
*/
function underscore($camelCasedWord) {
$_this =& Inflector::getInstance();
-
- if (!isset($_this->_underscored[$camelCasedWord])) {
- $_this->_underscored[$camelCasedWord] = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $camelCasedWord));
+ if (!($result = $_this->_cache(__FUNCTION__, $camelCasedWord))) {
+ $result = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $camelCasedWord));
+ $_this->_cache(__FUNCTION__, $camelCasedWord, $result);
}
- return $_this->_underscored[$camelCasedWord];
+ return $result;
}
/**
@@ -416,11 +441,11 @@ function underscore($camelCasedWord) {
*/
function humanize($lowerCaseAndUnderscoredWord) {
$_this =& Inflector::getInstance();
-
- if (!isset($_this->_humanize[$lowerCaseAndUnderscoredWord])) {
- $_this->_humanize[$lowerCaseAndUnderscoredWord] = ucwords(str_replace("_", " ", $lowerCaseAndUnderscoredWord));
+ if (!($result = $_this->_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord))) {
+ $result = ucwords(str_replace('_', ' ', $lowerCaseAndUnderscoredWord));
+ $_this->_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord, $result);
}
- return $_this->_humanize[$lowerCaseAndUnderscoredWord];
+ return $result;
}
/**
@@ -434,11 +459,11 @@ function humanize($lowerCaseAndUnderscoredWord) {
*/
function tableize($className) {
$_this =& Inflector::getInstance();
-
- if (!isset($_this->_tableize[$className])) {
- $_this->_tableize[$className] = Inflector::pluralize(Inflector::underscore($className));
+ if (!($result = $_this->_cache(__FUNCTION__, $className))) {
+ $result = Inflector::pluralize(Inflector::underscore($className));
+ $_this->_cache(__FUNCTION__, $className, $result);
}
- return $_this->_tableize[$className];
+ return $result;
}
/**
@@ -452,11 +477,11 @@ function tableize($className) {
*/
function classify($tableName) {
$_this =& Inflector::getInstance();
-
- if (!isset($_this->_classify[$tableName])) {
- $_this->_classify[$tableName] = Inflector::camelize(Inflector::singularize($tableName));
+ if (!($result = $_this->_cache(__FUNCTION__, $tableName))) {
+ $result = Inflector::camelize(Inflector::singularize($tableName));
+ $_this->_cache(__FUNCTION__, $tableName, $result);
}
- return $_this->_classify[$tableName];
+ return $result;
}
/**
@@ -470,13 +495,13 @@ function classify($tableName) {
*/
function variable($string) {
$_this =& Inflector::getInstance();
-
- if (!isset($_this->_variable[$string])) {
- $string = Inflector::camelize(Inflector::underscore($string));
- $replace = strtolower(substr($string, 0, 1));
- $_this->_variable[$string] = preg_replace('/\\w/', $replace, $string, 1);
+ if (!($result = $_this->_cache(__FUNCTION__, $string))) {
+ $string2 = Inflector::camelize(Inflector::underscore($string));
+ $replace = strtolower(substr($string2, 0, 1));
+ $result = preg_replace('/\\w/', $replace, $string2, 1);
+ $_this->_cache(__FUNCTION__, $string, $result);
}
- return $_this->_variable[$string];
+ return $result;
}
/**
View
287 cake/libs/l10n.php
@@ -77,6 +77,14 @@ class L10n extends Object {
var $charset = 'utf-8';
/**
+ * Text direction for current locale
+ *
+ * @var string
+ * @access public
+ */
+ var $direction = 'ltr';
+
+/**
* Set to true if a locale is found
*
* @var string
@@ -176,145 +184,145 @@ class L10n extends Object {
* @var array
* @access private
*/
- var $__l10nCatalog = array('af' => array('language' => 'Afrikaans', 'locale' => 'afr', 'localeFallback' => 'afr', 'charset' => 'utf-8'),
- 'ar' => array('language' => 'Arabic', 'locale' => 'ara', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
- 'ar-ae' => array('language' => 'Arabic (U.A.E.)', 'locale' => 'ar_ae', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
- 'ar-bh' => array('language' => 'Arabic (Bahrain)', 'locale' => 'ar_bh', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
- 'ar-dz' => array('language' => 'Arabic (Algeria)', 'locale' => 'ar_dz', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
- 'ar-eg' => array('language' => 'Arabic (Egypt)', 'locale' => 'ar_eg', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
- 'ar-iq' => array('language' => 'Arabic (Iraq)', 'locale' => 'ar_iq', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
- 'ar-jo' => array('language' => 'Arabic (Jordan)', 'locale' => 'ar_jo', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
- 'ar-kw' => array('language' => 'Arabic (Kuwait)', 'locale' => 'ar_kw', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
- 'ar-lb' => array('language' => 'Arabic (Lebanon)', 'locale' => 'ar_lb', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
- 'ar-ly' => array('language' => 'Arabic (Libya)', 'locale' => 'ar_ly', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
- 'ar-ma' => array('language' => 'Arabic (Morocco)', 'locale' => 'ar_ma', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
- 'ar-om' => array('language' => 'Arabic (Oman)', 'locale' => 'ar_om', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
- 'ar-qa' => array('language' => 'Arabic (Qatar)', 'locale' => 'ar_qa', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
- 'ar-sa' => array('language' => 'Arabic (Saudi Arabia)', 'locale' => 'ar_sa', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
- 'ar-sy' => array('language' => 'Arabic (Syria)', 'locale' => 'ar_sy', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
- 'ar-tn' => array('language' => 'Arabic (Tunisia)', 'locale' => 'ar_tn', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
- 'ar-ye' => array('language' => 'Arabic (Yemen)', 'locale' => 'ar_ye', 'localeFallback' => 'ara', 'charset' => 'utf-8'),
- 'be' => array('language' => 'Byelorussian', 'locale' => 'bel', 'localeFallback' => 'bel', 'charset' => 'utf-8'),
- 'bg' => array('language' => 'Bulgarian', 'locale' => 'bul', 'localeFallback' => 'bul', 'charset' => 'utf-8'),
- 'bs' => array('language' => 'Bosnian', 'locale' => 'bos', 'localeFallback' => 'bos', 'charset' => 'utf-8'),
- 'ca' => array('language' => 'Catalan', 'locale' => 'cat', 'localeFallback' => 'cat', 'charset' => 'utf-8'),
- 'cs' => array('language' => 'Czech', 'locale' => 'cze', 'localeFallback' => 'cze', 'charset' => 'utf-8'),
- 'da' => array('language' => 'Danish', 'locale' => 'dan', 'localeFallback' => 'dan', 'charset' => 'utf-8'),
- 'de' => array('language' => 'German (Standard)', 'locale' => 'deu', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
- 'de-at' => array('language' => 'German (Austria)', 'locale' => 'de_at', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
- 'de-ch' => array('language' => 'German (Swiss)', 'locale' => 'de_ch', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
- 'de-de' => array('language' => 'German (Germany)', 'locale' => 'de_de', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
- 'de-li' => array('language' => 'German (Liechtenstein)', 'locale' => 'de_li', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
- 'de-lu' => array('language' => 'German (Luxembourg)', 'locale' => 'de_lu', 'localeFallback' => 'deu', 'charset' => 'utf-8'),
- 'e' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8'),
- 'el' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8'),
- 'en' => array('language' => 'English', 'locale' => 'eng', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
- 'en-au' => array('language' => 'English (Australian)', 'locale' => 'en_au', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
- 'en-bz' => array('language' => 'English (Belize)', 'locale' => 'en_bz', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
- 'en-ca' => array('language' => 'English (Canadian)', 'locale' => 'en_ca', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
- 'en-gb' => array('language' => 'English (British)', 'locale' => 'en_gb', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
- 'en-ie' => array('language' => 'English (Ireland)', 'locale' => 'en_ie', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
- 'en-jm' => array('language' => 'English (Jamaica)', 'locale' => 'en_jm', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
- 'en-nz' => array('language' => 'English (New Zealand)', 'locale' => 'en_nz', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
- 'en-tt' => array('language' => 'English (Trinidad)', 'locale' => 'en_tt', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
- 'en-us' => array('language' => 'English (United States)', 'locale' => 'en_us', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
- 'en-za' => array('language' => 'English (South Africa)', 'locale' => 'en_za', 'localeFallback' => 'eng', 'charset' => 'utf-8'),
- 'es' => array('language' => 'Spanish (Spain - Traditional)', 'locale' => 'spa', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'es-ar' => array('language' => 'Spanish (Argentina)', 'locale' => 'es_ar', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'es-bo' => array('language' => 'Spanish (Bolivia)', 'locale' => 'es_bo', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'es-cl' => array('language' => 'Spanish (Chile)', 'locale' => 'es_cl', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'es-co' => array('language' => 'Spanish (Colombia)', 'locale' => 'es_co', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'es-cr' => array('language' => 'Spanish (Costa Rica)', 'locale' => 'es_cr', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'es-do' => array('language' => 'Spanish (Dominican Republic)', 'locale' => 'es_do', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'es-ec' => array('language' => 'Spanish (Ecuador)', 'locale' => 'es_ec', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'es-es' => array('language' => 'Spanish (Spain)', 'locale' => 'es_es', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'es-gt' => array('language' => 'Spanish (Guatemala)', 'locale' => 'es_gt', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'es-hn' => array('language' => 'Spanish (Honduras)', 'locale' => 'es_hn', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'es-mx' => array('language' => 'Spanish (Mexican)', 'locale' => 'es_mx', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'es-ni' => array('language' => 'Spanish (Nicaragua)', 'locale' => 'es_ni', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'es-pa' => array('language' => 'Spanish (Panama)', 'locale' => 'es_pa', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'es-pe' => array('language' => 'Spanish (Peru)', 'locale' => 'es_pe', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'es-pr' => array('language' => 'Spanish (Puerto Rico)', 'locale' => 'es_pr', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'es-py' => array('language' => 'Spanish (Paraguay)', 'locale' => 'es_py', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'es-sv' => array('language' => 'Spanish (El Salvador)', 'locale' => 'es_sv', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'es-uy' => array('language' => 'Spanish (Uruguay)', 'locale' => 'es_uy', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'es-ve' => array('language' => 'Spanish (Venezuela)', 'locale' => 'es_ve', 'localeFallback' => 'spa', 'charset' => 'utf-8'),
- 'et' => array('language' => 'Estonian', 'locale' => 'est', 'localeFallback' => 'est', 'charset' => 'utf-8'),
- 'eu' => array('language' => 'Basque', 'locale' => 'baq', 'localeFallback' => 'baq', 'charset' => 'utf-8'),
- 'fa' => array('language' => 'Farsi', 'locale' => 'per', 'localeFallback' => 'per', 'charset' => 'utf-8'),
- 'fi' => array('language' => 'Finnish', 'locale' => 'fin', 'localeFallback' => 'fin', 'charset' => 'utf-8'),
- 'fo' => array('language' => 'Faeroese', 'locale' => 'fao', 'localeFallback' => 'fao', 'charset' => 'utf-8'),
- 'fr' => array('language' => 'French (Standard)', 'locale' => 'fre', 'localeFallback' => 'fre', 'charset' => 'utf-8'),
- 'fr-be' => array('language' => 'French (Belgium)', 'locale' => 'fr_be', 'localeFallback' => 'fre', 'charset' => 'utf-8'),
- 'fr-ca' => array('language' => 'French (Canadian)', 'locale' => 'fr_ca', 'localeFallback' => 'fre', 'charset' => 'utf-8'),
- 'fr-ch' => array('language' => 'French (Swiss)', 'locale' => 'fr_ch', 'localeFallback' => 'fre', 'charset' => 'utf-8'),
- 'fr-fr' => array('language' => 'French (France)', 'locale' => 'fr_fr', 'localeFallback' => 'fre', 'charset' => 'utf-8'),
- 'fr-lu' => array('language' => 'French (Luxembourg)', 'locale' => 'fr_lu', 'localeFallback' => 'fre', 'charset' => 'utf-8'),
- 'ga' => array('language' => 'Irish', 'locale' => 'gle', 'localeFallback' => 'gle', 'charset' => 'utf-8'),
- 'gd' => array('language' => 'Gaelic (Scots)', 'locale' => 'gla', 'localeFallback' => 'gla', 'charset' => 'utf-8'),
- 'gd-ie' => array('language' => 'Gaelic (Irish)', 'locale' => 'gd_ie', 'localeFallback' => 'gla', 'charset' => 'utf-8'),
- 'gl' => array('language' => 'Galician', 'locale' => 'glg', 'localeFallback' => 'glg', 'charset' => 'utf-8'),
- 'he' => array('language' => 'Hebrew', 'locale' => 'heb', 'localeFallback' => 'heb', 'charset' => 'utf-8'),
- 'hi' => array('language' => 'Hindi', 'locale' => 'hin', 'localeFallback' => 'hin', 'charset' => 'utf-8'),
- 'hr' => array('language' => 'Croatian', 'locale' => 'hrv', 'localeFallback' => 'hrv', 'charset' => 'utf-8'),
- 'hu' => array('language' => 'Hungarian', 'locale' => 'hun', 'localeFallback' => 'hun', 'charset' => 'utf-8'),
- 'hy' => array('language' => 'Armenian - Armenia', 'locale' => 'hye', 'localeFallback' => 'hye', 'charset' => 'utf-8'),
- 'id' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8'),
- 'in' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8'),
- 'is' => array('language' => 'Icelandic', 'locale' => 'ice', 'localeFallback' => 'ice', 'charset' => 'utf-8'),
- 'it' => array('language' => 'Italian', 'locale' => 'ita', 'localeFallback' => 'ita', 'charset' => 'utf-8'),
- 'it-ch' => array('language' => 'Italian (Swiss) ', 'locale' => 'it_ch', 'localeFallback' => 'ita', 'charset' => 'utf-8'),
- 'ja' => array('language' => 'Japanese', 'locale' => 'jpn', 'localeFallback' => 'jpn', 'charset' => 'utf-8'),
- 'ko' => array('language' => 'Korean', 'locale' => 'kor', 'localeFallback' => 'kor', 'charset' => 'kr'),
- 'ko-kp' => array('language' => 'Korea (North)', 'locale' => 'ko_kp', 'localeFallback' => 'kor', 'charset' => 'kr'),
- 'ko-kr' => array('language' => 'Korea (South)', 'locale' => 'ko_kr', 'localeFallback' => 'kor', 'charset' => 'kr'),
- 'koi8-r' => array('language' => 'Russian', 'locale' => 'koi8_r', 'localeFallback' => 'rus', 'charset' => 'koi8-r'),
- 'lt' => array('language' => 'Lithuanian', 'locale' => 'lit', 'localeFallback' => 'lit', 'charset' => 'utf-8'),
- 'lv' => array('language' => 'Latvian', 'locale' => 'lav', 'localeFallback' => 'lav', 'charset' => 'utf-8'),
- 'mk' => array('language' => 'FYRO Macedonian', 'locale' => 'mk', 'localeFallback' => 'mac', 'charset' => 'utf-8'),
- 'mk-mk' => array('language' => 'Macedonian', 'locale' => 'mk_mk', 'localeFallback' => 'mac', 'charset' => 'utf-8'),
- 'ms' => array('language' => 'Malaysian', 'locale' => 'may', 'localeFallback' => 'may', 'charset' => 'utf-8'),
- 'mt' => array('language' => 'Maltese', 'locale' => 'mlt', 'localeFallback' => 'mlt', 'charset' => 'utf-8'),
- 'n' => array('language' => 'Dutch (Standard)', 'locale' => 'dut', 'localeFallback' => 'dut', 'charset' => 'utf-8'),
- 'nb' => array('language' => 'Norwegian Bokmal', 'locale' => 'nob', 'localeFallback' => 'nor', 'charset' => 'utf-8'),
- 'nl' => array('language' => 'Dutch (Standard)', 'locale' => 'dut', 'localeFallback' => 'dut', 'charset' => 'utf-8'),
- 'nl-be' => array('language' => 'Dutch (Belgium)', 'locale' => 'nl_be', 'localeFallback' => 'dut', 'charset' => 'utf-8'),
- 'nn' => array('language' => 'Norwegian Nynorsk', 'locale' => 'nno', 'localeFallback' => 'nor', 'charset' => 'utf-8'),
- 'no' => array('language' => 'Norwegian', 'locale' => 'nor', 'localeFallback' => 'nor', 'charset' => 'utf-8'),
- 'p' => array('language' => 'Polish', 'locale' => 'pol', 'localeFallback' => 'pol', 'charset' => 'utf-8'),
- 'pl' => array('language' => 'Polish', 'locale' => 'pol', 'localeFallback' => 'pol', 'charset' => 'utf-8'),
- 'pt' => array('language' => 'Portuguese (Portugal)', 'locale' => 'por', 'localeFallback' => 'por', 'charset' => 'utf-8'),
- 'pt-br' => array('language' => 'Portuguese (Brazil)', 'locale' => 'pt_br', 'localeFallback' => 'por', 'charset' => 'utf-8'),
- 'rm' => array('language' => 'Rhaeto-Romanic', 'locale' => 'roh', 'localeFallback' => 'roh', 'charset' => 'utf-8'),
- 'ro' => array('language' => 'Romanian', 'locale' => 'rum', 'localeFallback' => 'rum', 'charset' => 'utf-8'),
- 'ro-mo' => array('language' => 'Romanian (Moldavia)', 'locale' => 'ro_mo', 'localeFallback' => 'rum', 'charset' => 'utf-8'),
- 'ru' => array('language' => 'Russian', 'locale' => 'rus', 'localeFallback' => 'rus', 'charset' => 'utf-8'),
- 'ru-mo' => array('language' => 'Russian (Moldavia)', 'locale' => 'ru_mo', 'localeFallback' => 'rus', 'charset' => 'utf-8'),
- 'sb' => array('language' => 'Sorbian', 'locale' => 'wen', 'localeFallback' => 'wen', 'charset' => 'utf-8'),
- 'sk' => array('language' => 'Slovak', 'locale' => 'slo', 'localeFallback' => 'slo', 'charset' => 'utf-8'),
- 'sl' => array('language' => 'Slovenian', 'locale' => 'slv', 'localeFallback' => 'slv', 'charset' => 'utf-8'),
- 'sq' => array('language' => 'Albanian', 'locale' => 'alb', 'localeFallback' => 'alb', 'charset' => 'utf-8'),
- 'sr' => array('language' => 'Serbian', 'locale' => 'scc', 'localeFallback' => 'scc', 'charset' => 'utf-8'),
- 'sv' => array('language' => 'Swedish', 'locale' => 'swe', 'localeFallback' => 'swe', 'charset' => 'utf-8'),
- 'sv-fi' => array('language' => 'Swedish (Findland)', 'locale' => 'sv_fi', 'localeFallback' => 'swe', 'charset' => 'utf-8'),
- 'sx' => array('language' => 'Sutu', 'locale' => 'sx', 'localeFallback' => 'sx', 'charset' => 'utf-8'),
- 'sz' => array('language' => 'Sami (Lappish)', 'locale' => 'smi', 'localeFallback' => 'smi', 'charset' => 'utf-8'),
- 'th' => array('language' => 'Thai', 'locale' => 'tha', 'localeFallback' => 'tha', 'charset' => 'utf-8'),
- 'tn' => array('language' => 'Tswana', 'locale' => 'tsn', 'localeFallback' => 'tsn', 'charset' => 'utf-8'),
- 'tr' => array('language' => 'Turkish', 'locale' => 'tur', 'localeFallback' => 'tur', 'charset' => 'utf-8'),
- 'ts' => array('language' => 'Tsonga', 'locale' => 'tso', 'localeFallback' => 'tso', 'charset' => 'utf-8'),
- 'uk' => array('language' => 'Ukrainian', 'locale' => 'ukr', 'localeFallback' => 'ukr', 'charset' => 'utf-8'),
- 'ur' => array('language' => 'Urdu', 'locale' => 'urd', 'localeFallback' => 'urd', 'charset' => 'utf-8'),
- 've' => array('language' => 'Venda', 'locale' => 'ven', 'localeFallback' => 'ven', 'charset' => 'utf-8'),
- 'vi' => array('language' => 'Vietnamese', 'locale' => 'vie', 'localeFallback' => 'vie', 'charset' => 'utf-8'),
- 'xh' => array('language' => 'Xhosa', 'locale' => 'xho', 'localeFallback' => 'xho', 'charset' => 'utf-8'),
- 'yi' => array('language' => 'Yiddish', 'locale' => 'yid', 'localeFallback' => 'yid', 'charset' => 'utf-8'),
- 'zh' => array('language' => 'Chinese', 'locale' => 'chi', 'localeFallback' => 'chi', 'charset' => 'utf-8'),
- 'zh-cn' => array('language' => 'Chinese (PRC)', 'locale' => 'zh_cn', 'localeFallback' => 'chi', 'charset' => 'GB2312'),
- 'zh-hk' => array('language' => 'Chinese (Hong Kong)', 'locale' => 'zh_hk', 'localeFallback' => 'chi', 'charset' => 'utf-8'),
- 'zh-sg' => array('language' => 'Chinese (Singapore)', 'locale' => 'zh_sg', 'localeFallback' => 'chi', 'charset' => 'utf-8'),
- 'zh-tw' => array('language' => 'Chinese (Taiwan)', 'locale' => 'zh_tw', 'localeFallback' => 'chi', 'charset' => 'utf-8'),
- 'zu' => array('language' => 'Zulu', 'locale' => 'zul', 'localeFallback' => 'zul', 'charset' => 'utf-8'));
+ var $__l10nCatalog = array('af' => array('language' => 'Afrikaans', 'locale' => 'afr', 'localeFallback' => 'afr', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'ar' => array('language' => 'Arabic', 'locale' => 'ara', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+ 'ar-ae' => array('language' => 'Arabic (U.A.E.)', 'locale' => 'ar_ae', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+ 'ar-bh' => array('language' => 'Arabic (Bahrain)', 'locale' => 'ar_bh', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+ 'ar-dz' => array('language' => 'Arabic (Algeria)', 'locale' => 'ar_dz', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+ 'ar-eg' => array('language' => 'Arabic (Egypt)', 'locale' => 'ar_eg', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+ 'ar-iq' => array('language' => 'Arabic (Iraq)', 'locale' => 'ar_iq', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+ 'ar-jo' => array('language' => 'Arabic (Jordan)', 'locale' => 'ar_jo', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+ 'ar-kw' => array('language' => 'Arabic (Kuwait)', 'locale' => 'ar_kw', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+ 'ar-lb' => array('language' => 'Arabic (Lebanon)', 'locale' => 'ar_lb', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+ 'ar-ly' => array('language' => 'Arabic (Libya)', 'locale' => 'ar_ly', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+ 'ar-ma' => array('language' => 'Arabic (Morocco)', 'locale' => 'ar_ma', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+ 'ar-om' => array('language' => 'Arabic (Oman)', 'locale' => 'ar_om', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+ 'ar-qa' => array('language' => 'Arabic (Qatar)', 'locale' => 'ar_qa', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+ 'ar-sa' => array('language' => 'Arabic (Saudi Arabia)', 'locale' => 'ar_sa', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+ 'ar-sy' => array('language' => 'Arabic (Syria)', 'locale' => 'ar_sy', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+ 'ar-tn' => array('language' => 'Arabic (Tunisia)', 'locale' => 'ar_tn', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+ 'ar-ye' => array('language' => 'Arabic (Yemen)', 'locale' => 'ar_ye', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+ 'be' => array('language' => 'Byelorussian', 'locale' => 'bel', 'localeFallback' => 'bel', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'bg' => array('language' => 'Bulgarian', 'locale' => 'bul', 'localeFallback' => 'bul', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'bs' => array('language' => 'Bosnian', 'locale' => 'bos', 'localeFallback' => 'bos', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'ca' => array('language' => 'Catalan', 'locale' => 'cat', 'localeFallback' => 'cat', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'cs' => array('language' => 'Czech', 'locale' => 'cze', 'localeFallback' => 'cze', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'da' => array('language' => 'Danish', 'locale' => 'dan', 'localeFallback' => 'dan', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'de' => array('language' => 'German (Standard)', 'locale' => 'deu', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'de-at' => array('language' => 'German (Austria)', 'locale' => 'de_at', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'de-ch' => array('language' => 'German (Swiss)', 'locale' => 'de_ch', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'de-de' => array('language' => 'German (Germany)', 'locale' => 'de_de', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'de-li' => array('language' => 'German (Liechtenstein)', 'locale' => 'de_li', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'de-lu' => array('language' => 'German (Luxembourg)', 'locale' => 'de_lu', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'e' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'el' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'en' => array('language' => 'English', 'locale' => 'eng', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'en-au' => array('language' => 'English (Australian)', 'locale' => 'en_au', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'en-bz' => array('language' => 'English (Belize)', 'locale' => 'en_bz', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'en-ca' => array('language' => 'English (Canadian)', 'locale' => 'en_ca', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'en-gb' => array('language' => 'English (British)', 'locale' => 'en_gb', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'en-ie' => array('language' => 'English (Ireland)', 'locale' => 'en_ie', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'en-jm' => array('language' => 'English (Jamaica)', 'locale' => 'en_jm', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'en-nz' => array('language' => 'English (New Zealand)', 'locale' => 'en_nz', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'en-tt' => array('language' => 'English (Trinidad)', 'locale' => 'en_tt', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'en-us' => array('language' => 'English (United States)', 'locale' => 'en_us', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'en-za' => array('language' => 'English (South Africa)', 'locale' => 'en_za', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es' => array('language' => 'Spanish (Spain - Traditional)', 'locale' => 'spa', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es-ar' => array('language' => 'Spanish (Argentina)', 'locale' => 'es_ar', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es-bo' => array('language' => 'Spanish (Bolivia)', 'locale' => 'es_bo', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es-cl' => array('language' => 'Spanish (Chile)', 'locale' => 'es_cl', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es-co' => array('language' => 'Spanish (Colombia)', 'locale' => 'es_co', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es-cr' => array('language' => 'Spanish (Costa Rica)', 'locale' => 'es_cr', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es-do' => array('language' => 'Spanish (Dominican Republic)', 'locale' => 'es_do', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es-ec' => array('language' => 'Spanish (Ecuador)', 'locale' => 'es_ec', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es-es' => array('language' => 'Spanish (Spain)', 'locale' => 'es_es', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es-gt' => array('language' => 'Spanish (Guatemala)', 'locale' => 'es_gt', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es-hn' => array('language' => 'Spanish (Honduras)', 'locale' => 'es_hn', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es-mx' => array('language' => 'Spanish (Mexican)', 'locale' => 'es_mx', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es-ni' => array('language' => 'Spanish (Nicaragua)', 'locale' => 'es_ni', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es-pa' => array('language' => 'Spanish (Panama)', 'locale' => 'es_pa', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es-pe' => array('language' => 'Spanish (Peru)', 'locale' => 'es_pe', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es-pr' => array('language' => 'Spanish (Puerto Rico)', 'locale' => 'es_pr', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es-py' => array('language' => 'Spanish (Paraguay)', 'locale' => 'es_py', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es-sv' => array('language' => 'Spanish (El Salvador)', 'locale' => 'es_sv', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es-uy' => array('language' => 'Spanish (Uruguay)', 'locale' => 'es_uy', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'es-ve' => array('language' => 'Spanish (Venezuela)', 'locale' => 'es_ve', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'et' => array('language' => 'Estonian', 'locale' => 'est', 'localeFallback' => 'est', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'eu' => array('language' => 'Basque', 'locale' => 'baq', 'localeFallback' => 'baq', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'fa' => array('language' => 'Farsi', 'locale' => 'per', 'localeFallback' => 'per', 'charset' => 'utf-8', 'direction' => 'rtl'),
+ 'fi' => array('language' => 'Finnish', 'locale' => 'fin', 'localeFallback' => 'fin', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'fo' => array('language' => 'Faeroese', 'locale' => 'fao', 'localeFallback' => 'fao', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'fr' => array('language' => 'French (Standard)', 'locale' => 'fre', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'fr-be' => array('language' => 'French (Belgium)', 'locale' => 'fr_be', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'fr-ca' => array('language' => 'French (Canadian)', 'locale' => 'fr_ca', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'fr-ch' => array('language' => 'French (Swiss)', 'locale' => 'fr_ch', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'fr-fr' => array('language' => 'French (France)', 'locale' => 'fr_fr', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'fr-lu' => array('language' => 'French (Luxembourg)', 'locale' => 'fr_lu', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'ga' => array('language' => 'Irish', 'locale' => 'gle', 'localeFallback' => 'gle', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'gd' => array('language' => 'Gaelic (Scots)', 'locale' => 'gla', 'localeFallback' => 'gla', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'gd-ie' => array('language' => 'Gaelic (Irish)', 'locale' => 'gd_ie', 'localeFallback' => 'gla', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'gl' => array('language' => 'Galician', 'locale' => 'glg', 'localeFallback' => 'glg', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'he' => array('language' => 'Hebrew', 'locale' => 'heb', 'localeFallback' => 'heb', 'charset' => 'utf-8', 'direction' => 'rtl'),
+ 'hi' => array('language' => 'Hindi', 'locale' => 'hin', 'localeFallback' => 'hin', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'hr' => array('language' => 'Croatian', 'locale' => 'hrv', 'localeFallback' => 'hrv', 'charset' => 'utf-8', 'direction' => 'ltr'),
+ 'hu' => array('language' => 'Hungarian', 'locale' => 'hun', 'localeFallback' => 'hun', 'charset' => 'utf-8', 'direction' => 'ltr'),