Commits

seccanj  committed 1e46670

Release 1.3.12. Enhancements #8321 and #8322 and Ticket #8323.
o Enhancement #8321 (Track-Hacks): Add standard internationalization support (i18n)
o Enhancement #8322 (Track-Hacks): Show timestamps according to User's locale
o Fixed Ticket #8323 (Track-Hacks): Unable to expand Available plans and Test case status change history collapsable sections

  • Participants
  • Parent commits b1cbbf5

Comments (0)

Files changed (25)

 
 (Refer to the tickets on trac-hacks or SourceForge for complete descriptions.)
 
+Release 1.3.12 (2010-12-19):
+  o Enhancement #8321 (Track-Hacks): Add standard internationalization support (i18n)
+  o Enhancement #8322 (Track-Hacks): Show timestamps according to User's locale
+  o Fixed Ticket #8323 (Track-Hacks): Unable to expand Available plans and Test case status change history collapsable sections
+
 Release 1.3.11 (2010-12-02):
   o Added out of the box operation to workflow engine: set_owner and set_owner_to_self
   o Enhancement #8259 (Track-Hacks): Add navigation from a test case to its related tickets
 xcopy /y dist\*.egg ..\..\bin
 
 cd ..\..\testman4trac\trunk
+
+rem python setup.py extract_messages
+rem python setup.py extract_messages_js
+rem python setup.py update_catalog -l it_IT
+rem python setup.py update_catalog_js -l it_IT
+rem python ./setup.py compile_catalog -f -l it_IT
+rem python ./setup.py compile_catalog_js -f -l it_IT
+
 python setup.py bdist_egg
 xcopy /y dist\*.egg ..\..\bin
 
 cp dist/*.egg ../../bin
 
 cd ../../testman4trac/trunk
+
+#python setup.py extract_messages
+#python setup.py extract_messages_js
+#python ./setup.py update_catalog -l it_IT
+#python ./setup.py update_catalog_js -l it_IT
+#python ./setup.py compile_catalog -f -l it_IT
+#python ./setup.py compile_catalog_js -f -l it_IT
+
 python setup.py bdist_egg
 cp dist/*.egg ../../bin
 
 
 cd testman4trac.$VER
 
-./clean.sh
+. ./clean.sh
 find . -type d -name .svn -print -exec rm -rf {} \;
 
 cd ..

File testman4trac/trunk/messages-js.cfg

+# mapping file for extracting messages from javascript files into
+# <path>/locale/messages-js.pot (see setup.cfg)
+[javascript: **.js]
+
+[extractors]
+javascript_script = trac.util.dist:extract_javascript_script
+
+[javascript_script: **.html]

File testman4trac/trunk/setup.cfg

+[extract_messages]
+add_comments = TRANSLATOR:
+copyright_holder = Roberto Longobardi
+msgid_bugs_address = seccanj@gmail.com
+output_file = testmanager/locale/messages.pot
+# Note: specify as 'keywords' the functions for which the messages
+#       should be extracted. This should match the list of functions
+#       that you've listed in the 'domain_functions()' call above.
+keywords = _ N_ tag_
+# Other example:
+#keywords = _ ngettext:1,2 N_ tag_
+width = 72
+
+[init_catalog]
+input_file = testmanager/locale/messages.pot
+output_dir = testmanager/locale
+domain = testmanager
+
+[compile_catalog]
+directory = testmanager/locale
+domain = testmanager
+
+[update_catalog]
+input_file = testmanager/locale/messages.pot
+output_dir = testmanager/locale
+domain = testmanager
+
+
+[extract_messages_js]
+add_comments = TRANSLATOR:
+copyright_holder = Roberto Longobardi
+msgid_bugs_address = seccanj@gmail.com
+output_file = testmanager/locale/messages-js.pot
+keywords = _ N_ tag_
+mapping_file = messages-js.cfg
+
+[init_catalog_js]
+domain = testmanager-js
+input_file = testmanager/locale/messages-js.pot
+output_dir = testmanager/locale
+
+[compile_catalog_js]
+domain = testmanager-js
+directory = testmanager/locale
+
+[update_catalog_js]
+domain = testmanager-js
+input_file = testmanager/locale/messages-js.pot
+output_dir = testmanager/locale
+
+[generate_messages_js]
+domain = testmanager-js
+input_dir = testmanager/locale
+output_dir = testmanager/htdocs/js

File testman4trac/trunk/setup.py

 from setuptools import setup
 
+extra = {} 
+
+try:
+    from trac.util.dist import get_l10n_js_cmdclass 
+    cmdclass = get_l10n_js_cmdclass() 
+    if cmdclass: # OK, Babel is there
+        extra['cmdclass'] = cmdclass 
+        extractors = [ 
+            ('**.py',                'python', None), 
+            ('**/templates/**.html', 'genshi', None), 
+            ('**/templates/**.txt',  'genshi', { 
+                'template_class': 'genshi.template:TextTemplate' 
+            }), 
+        ] 
+        extra['message_extractors'] = { 
+            'testmanager': extractors, 
+        }
+except ImportError: 
+    pass
+
 setup(
     name='TestManager',
-    version='1.3.11',
+    version='1.3.12',
     packages=['testmanager'],
-    package_data={'testmanager' : ['*.txt', 'templates/*.html', 'htdocs/js/*.js', 'htdocs/css/*.css', 'htdocs/images/*.*']},
+    package_data={
+        'testmanager' : [
+            '*.txt', 
+            'templates/*.html', 
+            'htdocs/js/*.js', 
+            'htdocs/css/*.css', 
+            'htdocs/images/*.*', 
+            'locale/*.*', 
+            'locale/*/LC_MESSAGES/*.mo',
+            'htdocs/testmanager/*.js'
+        ]
+    },
     author = 'Roberto Longobardi',
     author_email='seccanj@gmail.com',
     license='BSD. See the file LICENSE.txt contained in the package.',
     entry_points = {'trac.plugins': ['testmanager = testmanager']},
     dependency_links=['http://svn.edgewall.org/repos/genshi/trunk#egg=Genshi-dev'],
     install_requires=['Genshi >= 0.5'],
+    **extra
     )

File testman4trac/trunk/testmanager/README.txt

 
 (Refer to the tickets on trac-hacks or SourceForge for complete descriptions.)
 
+Release 1.3.12 (2010-12-19):
+  o Enhancement #8321 (Track-Hacks): Add standard internationalization support (i18n)
+  o Enhancement #8322 (Track-Hacks): Show timestamps according to User's locale
+  o Fixed Ticket #8323 (Track-Hacks): Unable to expand Available plans and Test case status change history collapsable sections
+
 Release 1.3.11 (2010-12-02):
   o Added out of the box operation to workflow engine: set_owner and set_owner_to_self
   o Enhancement #8259 (Track-Hacks): Add navigation from a test case to its related tickets

File testman4trac/trunk/testmanager/api.py

 #
 
 import re
+import pkg_resources
 import sys
 import time
 import traceback
 from trac.resource import Resource, IResourceManager, render_resource_link, get_resource_url
 from trac.util import get_reporter_id
 from trac.util.datefmt import utc
-from trac.util.translation import _, N_, gettext
 from trac.web.api import IRequestHandler
 
 from tracgenericclass.model import GenericClassModelProvider
 from tracgenericclass.util import *
 
-from testmanager.labels import *
 from testmanager.model import TestCatalog, TestCase, TestCaseInPlan, TestPlan
 
+try:
+    from trac.util.translation import domain_functions
+    _, tag_, N_, add_domain = domain_functions('testmanager', ('_', 'tag_', 'N_', 'add_domain'))
+except ImportError:
+	from trac.util.translation import _, N_
+	tag_ = _
+	add_domain = lambda env_path, locale_dir: None
 
 class TestManagerSystem(Component):
     """Test Manager system for Trac."""
 
     implements(IPermissionRequestor, IRequestHandler, IResourceManager)
 
+    def __init__(self):
+        import pkg_resources
+        # bind the 'testmanager' catalog to the specified locale directory
+        locale_dir = pkg_resources.resource_filename(__name__, 'locale')
+        add_domain(self.env.path, locale_dir)
+
     def get_next_id(self, type):
         propname = _get_next_prop_name(type)
     
             db.rollback()
             raise
     
+    def get_default_tc_status(self):
+        """Returns the default test case in plan status"""
+        
+        return 'TO_BE_TESTED'
+    
+    def get_tc_statuses(self):
+        """
+        Returns the available test case in plan statuses, along with
+        their base locale captions and meaning:
+          0: successful, green
+          1: to be tested, yellow
+          2: failed, red
+        """
+        
+        result = {
+            'SUCCESSFUL': ["Successful", _("Successful"), 0],
+            'TO_BE_TESTED': ["Untested", _("Untested"), 1],
+            'FAILED': ["Failed", _("Failed"), 2]
+        }
+        
+        return result
+    
+    def get_tc_status_caption(self, req, status):
+        """
+        Returns the caption for the given test case status in the
+        request's locale (Trac 0.12+)
+        """
+        
+        return _(self.get_tc_statuses()[status])
+    
     def get_testcase_status_history_markup(self, id, planid):
         """Returns a test case status in a plan audit trail."""
 
         result = '<table class="listing"><thead>'
-        result += '<tr><th>'+LABELS['timestamp']+'</th><th>'+LABELS['author']+'</th><th>'+LABELS['status']+'</th></tr>'
+        result += '<tr><th>'+_("Timestamp")+'</th><th>'+_("Author")+'</th><th>'+_("Status")+'</th></tr>'
         result += '</thead><tbody>'
         
         db = get_db(self.env)
             result += '<tr>'
             result += '<td>'+str(from_any_timestamp(ts))+'</td>'
             result += '<td>'+author+'</td>'
-            result += '<td>'+LABELS[status]+'</td>'
+            result += '<td>'+_("Status")+'</td>'
             result += '</tr>'
 
         result += '</tbody></table>'

File testman4trac/trunk/testmanager/htdocs/js/compatibility.js

+/*- coding: utf-8
+ *
+ * Copyright (C) 2010 Roberto Longobardi - seccanj@gmail.com
+ */
+
+/******************************************************/
+/**              Compatibility layer                  */
+/******************************************************/
+
+
+function expandCollapseSection(nodeId) {
+    /* In Trac 0.11 we must handle sections explicitly */
+    $('#'+nodeId).toggleClass('collapsed');
+}

File testman4trac/trunk/testmanager/htdocs/js/it.js

+// Generated messages javascript file from compiled MO file
+babel.Translations.load({"domain":"testmanager-js","locale":"it","messages":{"Are you sure you want to delete the test plan and the state of all its contained test cases?":"Sei sicuro di voler eliminare il piano di test e lo stato di tutti i test case in esso contenuti?","Are you sure you want to duplicate the test catalog and all its contained test cases?":"Sei sicuro di voler duplicare il catalogo e tutti i test case in esso contenuti?","Length between 4 and 90 characters.":"Lunghezza da 4 a 90 caratteri.","Results: ":"Risultati: ","You must specify a name. Length between 4 and 90 characters.":"Devi indicare un nome. Lunghezza da 4 a 90 caratteri."},"plural_expr":"(n != 1)"}).install();

File testman4trac/trunk/testmanager/htdocs/js/testmanager.js

-/*- coding: utf-8
+/*- coding: utf-8
  *
  * Copyright (C) 2010 Roberto Longobardi - seccanj@gmail.com
  */
 	var catalogName = tcInput.value;
     
     if (catalogName == null || catalogName.length == 0) {
-		document.getElementById('catErrorMsgSpan').innerHTML = messages['name_help'];
+		document.getElementById('catErrorMsgSpan').innerHTML = _("You must specify a name. Length between 4 and 90 characters.");
     } else {
     	var catName = stripLessSpecialChars(catalogName);
     	
     	if (catName.length > 90 || catName.length < 4) {
-    		document.getElementById('catErrorMsgSpan').innerHTML = messages['length_error'];
+    		document.getElementById('catErrorMsgSpan').innerHTML = _("Length between 4 and 90 characters.");
     	} else { 
     		document.getElementById('catErrorMsgSpan').innerHTML = ''; 
     		var url = baseLocation+"/testcreate?type=catalog&path="+path+"&title="+catName;
 	var testCaseName = tcInput.value; 
 
     if (testCaseName == null || testCaseName.length == 0) {
-		document.getElementById('errorMsgSpan').innerHTML = messages['name_help'];
+		document.getElementById('errorMsgSpan').innerHTML = _("You must specify a name. Length between 4 and 90 characters.");
     } else {
     	var tcName = stripLessSpecialChars(testCaseName); 
     	
     	if (tcName.length > 90 || tcName.length < 4) {
-    		document.getElementById('errorMsgSpan').innerHTML = messages['length_error'];
+    		document.getElementById('errorMsgSpan').innerHTML = _("Length between 4 and 90 characters.");
     	} else { 
     		document.getElementById('errorMsgSpan').innerHTML = ''; 
     		var url = baseLocation+"/testcreate?type=testcase&path="+catName+"&title="+tcName;
 	var testPlanName = planInput.value; 
 
     if (testPlanName == null || testPlanName.length == 0) {
-		document.getElementById('errorMsgSpan2').innerHTML = messages['name_help'];
+		document.getElementById('errorMsgSpan2').innerHTML = _("You must specify a name. Length between 4 and 90 characters.");
     } else {
     	var tplanName = stripLessSpecialChars(testPlanName); 
     	
     	if (tplanName.length > 90 || tplanName.length < 4) {
-    		document.getElementById('errorMsgSpan2').innerHTML = messages['length_error'];
+    		document.getElementById('errorMsgSpan2').innerHTML = _("Length between 4 and 90 characters.");
     	} else { 
     		document.getElementById('errorMsgSpan2').innerHTML = ''; 
     		var url = baseLocation+"/testcreate?type=testplan&path="+catName+"&title="+tplanName;
 }
 
 function duplicateTestCatalog(catName){
-    if (confirm(messages['duplicate_catalog_confirm'])) {
+    if (confirm(_("Are you sure you want to duplicate the test catalog and all its contained test cases?"))) {
         var url = baseLocation+'/testcreate?type=catalog&duplicate=true&path='+catName; 
         window.location = url;
     }
 }
 
 function deleteTestPlan(url){
-    if (confirm(messages['delete_plan_confirm'])) {
+    if (confirm(_("Are you sure you want to delete the test plan and the state of all its contained test cases?"))) {
         window.location = url;
     }
 }
             }
         }
 
-        document.getElementById('searchResultsNumberId').innerHTML = labels['results']+searchResults;
+        document.getElementById('searchResultsNumberId').innerHTML = _("Results: ")+searchResults;
     }
 }
 
             }
         }
 
-        document.getElementById('searchResultsNumberId').innerHTML = labels['results']+searchResults;
+        document.getElementById('searchResultsNumberId').innerHTML = _("Results: ")+searchResults;
     }
 }
 
 /******************************************************/
 
 function expandCollapseSection(nodeId) {
-    $(nodeId).toggleClass("collapsed");
+    /* In Trac 0.12 should do nothing, because a listener is
+     * already in charge of handling sections */
 }
 
 function stripSpecialChars(str) {
-    result = str.replace(/[ ',;:àèéìòù£§<>!"%&=@#��\[\]\-\\\\^\$\.\|\?\*\+\(\)\{\}]/g, '');
+    result = str.replace(/[ ',;:àèéìòù£§<>!"%&=@#\[\]\-\\\\^\$\.\|\?\*\+\(\)\{\}]/g, '');
     return result;
 }
 
     }
 }
 
-function toggleClass(nodeId, className) {
-    var node = document.getElementById(nodeId);
-    if (node.className === "") {
-        node.className = className;
-    } else {
-        node.className = "";
-    }
-}
-
 function doAjaxCall(url) {
     if (window.XMLHttpRequest) {
         /* code for IE7+, Firefox, Chrome, Opera, Safari */
     displayNode('update_button_'+name, false);
 }
 
+function getLocale() {
+	if ( navigator ) {
+		if ( navigator.language ) {
+			return navigator.language;
+		}
+		else if ( navigator.browserLanguage ) {
+			return navigator.browserLanguage;
+		}
+		else if ( navigator.systemLanguage ) {
+			return navigator.systemLanguage;
+		}
+		else if ( navigator.userLanguage ) {
+			return navigator.userLanguage;
+		}
+	}
+}
+
+function include(filename) {
+	var head = document.getElementsByTagName('head')[0];
+	
+	script = document.createElement('script');
+	script.src = filename;
+	script.type = 'text/javascript';
+	
+	head.appendChild(script)
+}
+
+function loadMessageCatalog() {
+	var lc = getLocale();
+	include('../chrome/testmanager/js/' + lc + '.js');
+}
+
 /**
  * Adds the specified function, by name or by pointer, to the window.onload() queue.
  * 
             func(); 
         } 
     } 
-} 
+}
 
 /**
  * Do some checks as soon as the page is loaded.
 addLoadHandler(function() {
         checkFilter(true);
         checkMoveTCDisplays();
+		/* loadMessageCatalog(); */
     });

File testman4trac/trunk/testmanager/locale/it/LC_MESSAGES/testmanager-js.mo

Binary file added.

File testman4trac/trunk/testmanager/locale/it/LC_MESSAGES/testmanager-js.po

+# Italian (Italy) translations for TestManager.
+# Copyright (C) 2010 Roberto Longobardi
+# This file is distributed under the same license as the TestManager
+# project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: TestManager 1.3.12\n"
+"Report-Msgid-Bugs-To: seccanj@gmail.com\n"
+"POT-Creation-Date: 2010-12-17 16:54+0100\n"
+"PO-Revision-Date: 2010-12-17 16:55+0100\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: it_IT <LL@li.org>\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.5\n"
+
+#: testmanager/htdocs/js/testmanager.js:15
+#: testmanager/htdocs/js/testmanager.js:34
+#: testmanager/htdocs/js/testmanager.js:53
+msgid "You must specify a name. Length between 4 and 90 characters."
+msgstr "Devi indicare un nome. Lunghezza da 4 a 90 caratteri."
+
+#: testmanager/htdocs/js/testmanager.js:20
+#: testmanager/htdocs/js/testmanager.js:39
+#: testmanager/htdocs/js/testmanager.js:58
+msgid "Length between 4 and 90 characters."
+msgstr "Lunghezza da 4 a 90 caratteri."
+
+#: testmanager/htdocs/js/testmanager.js:88
+msgid ""
+"Are you sure you want to duplicate the test catalog and all its contained"
+" test cases?"
+msgstr "Sei sicuro di voler duplicare il catalogo e tutti i test case in esso contenuti?"
+
+#: testmanager/htdocs/js/testmanager.js:95
+msgid ""
+"Are you sure you want to delete the test plan and the state of all its "
+"contained test cases?"
+msgstr "Sei sicuro di voler eliminare il piano di test e lo stato di tutti i test case in esso contenuti?"
+
+#: testmanager/htdocs/js/testmanager.js:212
+#: testmanager/htdocs/js/testmanager.js:361
+msgid "Results: "
+msgstr "Risultati: "
+

File testman4trac/trunk/testmanager/locale/it/LC_MESSAGES/testmanager.mo

Binary file added.

File testman4trac/trunk/testmanager/locale/it/LC_MESSAGES/testmanager.po

+# Italian (Italy) translations for TestManager.
+# Copyright (C) 2010 ORGANIZATION
+# This file is distributed under the same license as the TestManager
+# project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: TestManager 1.3.11\n"
+"Report-Msgid-Bugs-To: seccanj@gmail.com\n"
+"POT-Creation-Date: 2010-12-14 12:35+0100\n"
+"PO-Revision-Date: 2010-12-18 21:34+0100\n"
+"Last-Translator: Roberto Longobardi <seccanj@gmail.com>\n"
+"Language-Team: Italian it_IT <trac-dev@googlegroups.com>\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.5\n"
+
+#: testmanager/api.py:102
+msgid "Successful"
+msgstr "Passato"
+
+#: testmanager/api.py:103
+msgid "Untested"
+msgstr "Da Testare"
+
+#: testmanager/api.py:104
+msgid "Failed"
+msgstr "Fallito"
+
+#: testmanager/api.py:121 testmanager/macros.py:540 testmanager/macros.py:864
+msgid "Timestamp"
+msgstr "Timestamp"
+
+#: testmanager/api.py:121 testmanager/macros.py:490 testmanager/macros.py:540
+#: testmanager/macros.py:864 testmanager/model.py:471
+msgid "Author"
+msgstr "Autore"
+
+#: testmanager/api.py:121 testmanager/api.py:134 testmanager/macros.py:490
+#: testmanager/macros.py:864 testmanager/model.py:464
+msgid "Status"
+msgstr "Stato"
+
+#: testmanager/macros.py:217
+msgid "All Catalogs"
+msgstr "Tutti i Cataloghi"
+
+#: testmanager/macros.py:306 testmanager/macros.py:327
+#: testmanager/macros.py:445 testmanager/macros.py:467
+msgid "Filter:"
+msgstr "Filtro:"
+
+#: testmanager/macros.py:306 testmanager/macros.py:327
+#: testmanager/macros.py:445 testmanager/macros.py:467
+msgid ""
+"Type the test to search for, even more than one word. You can also filter"
+" on the test case status (untested, successful, failed)."
+msgstr ""
+"Inserire il testo da cercare, anche piu\\' parole. Si puo\\' filtrare "
+"anche per stato dei test cases (da testare, passato, fallito)."
+
+#: testmanager/macros.py:307 testmanager/macros.py:446
+msgid "Expand all"
+msgstr "Espandi tutto"
+
+#: testmanager/macros.py:307 testmanager/macros.py:446
+msgid "Collapse all"
+msgstr "Comprimi tutto"
+
+#: testmanager/macros.py:332 testmanager/macros.py:472 testmanager/model.py:470
+msgid "Name"
+msgstr "Nome"
+
+#: testmanager/macros.py:341 testmanager/macros.py:481 testmanager/model.py:453
+#: testmanager/model.py:457 testmanager/model.py:461 testmanager/model.py:467
+msgid "ID"
+msgstr "ID"
+
+#: testmanager/macros.py:351
+msgid "Description"
+msgstr "Descrizione"
+
+#: testmanager/macros.py:490
+msgid "Last Change"
+msgstr "Ultima Modifica"
+
+#: testmanager/macros.py:526
+msgid "Available Test Plans"
+msgstr "Piani di Test disponibili"
+
+#: testmanager/macros.py:540
+msgid "Plan Name"
+msgstr "Nome del Piano"
+
+#: testmanager/macros.py:546
+msgid "Open Test Plan"
+msgstr "Apri il Piano di Test"
+
+#: testmanager/macros.py:551
+msgid "Delete"
+msgstr "Elimina"
+
+#: testmanager/macros.py:614 testmanager/macros.py:709
+msgid "Open"
+msgstr "Apri"
+
+#: testmanager/macros.py:655 testmanager/macros.py:657
+msgid "Edit the Test Case"
+msgstr "Modifica il Caso di Test"
+
+#: testmanager/macros.py:821
+msgid "Change the Status:"
+msgstr "Modifica lo Stato:"
+
+#: testmanager/macros.py:831
+#, fuzzy
+msgid "SUCCESSFUL"
+msgstr "Passato"
+
+#: testmanager/macros.py:839
+#, fuzzy
+msgid "TO_BE_TESTED"
+msgstr "Da Testare"
+
+#: testmanager/macros.py:847
+#, fuzzy
+msgid "FAILED"
+msgstr "Fallito"
+
+#: testmanager/macros.py:861
+msgid "Status change history"
+msgstr "Storia dei cambiamenti di stato"
+
+#: testmanager/model.py:454 testmanager/model.py:458 testmanager/model.py:463
+#: testmanager/model.py:469
+msgid "Wiki page name"
+msgstr "Nome della pagina wiki"
+
+#: testmanager/model.py:462
+msgid "Plan ID"
+msgstr "ID del Piano"
+
+#: testmanager/model.py:468
+msgid "Catalog ID"
+msgstr "ID del Catalogo"
+
+#: testmanager/model.py:472
+msgid "Created"
+msgstr "Creato"
+
+#: testmanager/web_ui.py:67
+msgid "Test Manager"
+msgstr "Test Manager"
+
+#: testmanager/wiki.py:152
+msgid ""
+"Select the catalog into which to paste the Test Case and click on 'Move "
+"the copied Test Case here'. "
+msgstr ""
+"Seleziona il catalogo dove incollare il Caso di Test copiato e clicca su "
+"'Sposta il Caso di Test copiato qui'. "
+
+#: testmanager/wiki.py:153 testmanager/wiki.py:175 testmanager/wiki.py:348
+msgid "Cancel"
+msgstr "Annulla"
+
+#: testmanager/wiki.py:155
+msgid "Test Catalogs List"
+msgstr "Lista dei Cataloghi di Test"
+
+#: testmanager/wiki.py:158
+msgid "New Catalog:"
+msgstr "Nuovo Catalogo:"
+
+#: testmanager/wiki.py:159
+msgid "Add a Catalog"
+msgstr "Aggungi un Catalogo"
+
+#: testmanager/wiki.py:174
+msgid ""
+"Select the catalog (even this one) into which to paste the Test Case and "
+"click on 'Move the copied Test Case here'. "
+msgstr ""
+"Seleziona il catalogo (anche questo stesso) dove incollare il Caso di "
+"Test copiato e clicca su 'Sposta il Caso di Test copiato qui'. "
+
+#: testmanager/wiki.py:178
+msgid "Test Catalog"
+msgstr "Catalogo di Test"
+
+#: testmanager/wiki.py:180
+msgid "New Sub-Catalog:"
+msgstr "Nuovo Sotto Catalogo:"
+
+#: testmanager/wiki.py:181
+msgid "Add a Sub-Catalog"
+msgstr "Aggiungi un Sotto Catalogo"
+
+#: testmanager/wiki.py:196
+msgid "Move the copied Test Case here"
+msgstr "Sposta il Caso di Test copiato qui"
+
+#: testmanager/wiki.py:218
+msgid "New Test Case:"
+msgstr "Nuovo Caso di Test:"
+
+#: testmanager/wiki.py:222
+msgid "Add a Test Case"
+msgstr "Aggiungi un Caso di Test"
+
+#: testmanager/wiki.py:226
+msgid "New Test Plan:"
+msgstr "Nuovo Piano di Test:"
+
+#: testmanager/wiki.py:230
+msgid "Generate a new Test Plan"
+msgstr "Genera un Piano di Test"
+
+#: testmanager/wiki.py:279 testmanager/wiki.py:449
+msgid "Back to the Catalog"
+msgstr "Torna al Catalogo"
+
+#: testmanager/wiki.py:290
+msgid "Test Plan: "
+msgstr "Piano di Test: "
+
+#: testmanager/wiki.py:347
+msgid ""
+"The Test Case has been copied. Now select the catalog into which to move "
+"the Test Case and click on 'Move the copied Test Case here'. "
+msgstr ""
+"Il Caso di Test e' stato copiato. Adesso seleziona il catalogo dove "
+"spostarlo e clicca su 'Sposta il Caso di Test copiato qui'. "
+
+#: testmanager/wiki.py:353 testmanager/wiki.py:416
+msgid "Test Case"
+msgstr "Caso di Test"
+
+#: testmanager/wiki.py:363 testmanager/wiki.py:428
+msgid "Open a Ticket on this Test Case"
+msgstr "Apri una Segnalazione su questo Caso di Test"
+
+#: testmanager/wiki.py:365 testmanager/wiki.py:430
+msgid "Show Related Tickets"
+msgstr "Mostra le Segnalazioni correlate"
+
+#: testmanager/wiki.py:367
+msgid "Move the Test Case into another catalog"
+msgstr "Sposta il Caso di Test in altro catalogo"
+
+#: testmanager/wiki.py:369
+msgid "Duplicate the Test Case"
+msgstr "Duplica il Caso di Test"
+
+#: testmanager/wiki.py:446
+msgid "Back to the Test Plan"
+msgstr "Torna al Piano di Test"
+
+#: testmanager/wiki.py:514
+msgid "Edit"
+msgstr "Modifica"
+
+#: testmanager/wiki.py:518
+msgid "Save"
+msgstr "Salva"
+
+#: testmanager/templates/testmanagerstats.html:53
+msgid "Test Management Statistics"
+msgstr "Statistiche sulla gestione dei Test"
+
+#: testmanager/templates/testmanagerstats.html:83
+msgid "Settings"
+msgstr "Impostazioni"
+
+#: testmanager/templates/testmanagerstats.html:85
+msgid "Start Date:"
+msgstr "Dal:"
+
+#: testmanager/templates/testmanagerstats.html:87
+msgid "End Date:"
+msgstr "Al:"
+
+#: testmanager/templates/testmanagerstats.html:89
+msgid "Resolution:"
+msgstr "Risoluzione:"
+
+#: testmanager/templates/testmanagerstats.html:92
+msgid "1 Day"
+msgstr "1 Giorno"
+
+#: testmanager/templates/testmanagerstats.html:93
+msgid "1 Week"
+msgstr "1 Settimana"
+
+#: testmanager/templates/testmanagerstats.html:94
+msgid "2 Weeks"
+msgstr "2 Settimane"
+
+#: testmanager/templates/testmanagerstats.html:95
+msgid "1 Month"
+msgstr "1 Mese"
+
+#: testmanager/templates/testmanagerstats.html:96
+msgid "2 Months"
+msgstr "2 Mesi"
+
+#: testmanager/templates/testmanagerstats.html:97
+msgid "3 Months"
+msgstr "3 Mesi"
+
+#: testmanager/templates/testmanagerstats.html:98
+msgid "6 Months"
+msgstr "6 Mesi"
+
+#: testmanager/templates/testmanagerstats.html:99
+msgid "1 Year"
+msgstr "1 Anno"
+
+#: testmanager/templates/testmanagerstats.html:101
+#, fuzzy
+msgid "Test Plan:"
+msgstr "Piano di Test: "
+
+#: testmanager/templates/testmanagerstats.html:103
+#, fuzzy
+msgid "All Test Plans"
+msgstr "Tutti i Piani di Test"
+
+#: testmanager/templates/testmanagerstats.html:112
+msgid "URL to bookmark:"
+msgstr "URL da salvare nei preferiti:"
+
+#: testmanager/templates/testmanagerstats.html:126
+msgid "Export this data to Excel"
+msgstr "Esporta i dati in Excel"
+
+#: testmanager/templates/testmanagerstats.html:126
+msgid "(CSV format)"
+msgstr "(formato CSV)"
+

File testman4trac/trunk/testmanager/locale/messages-js.pot

+# Translations template for TestManager.
+# Copyright (C) 2010 Roberto Longobardi
+# This file is distributed under the same license as the TestManager
+# project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: TestManager 1.3.12\n"
+"Report-Msgid-Bugs-To: seccanj@gmail.com\n"
+"POT-Creation-Date: 2010-12-17 16:54+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.5\n"
+
+#: testmanager/htdocs/js/testmanager.js:15
+#: testmanager/htdocs/js/testmanager.js:34
+#: testmanager/htdocs/js/testmanager.js:53
+msgid "You must specify a name. Length between 4 and 90 characters."
+msgstr ""
+
+#: testmanager/htdocs/js/testmanager.js:20
+#: testmanager/htdocs/js/testmanager.js:39
+#: testmanager/htdocs/js/testmanager.js:58
+msgid "Length between 4 and 90 characters."
+msgstr ""
+
+#: testmanager/htdocs/js/testmanager.js:88
+msgid ""
+"Are you sure you want to duplicate the test catalog and all its contained"
+" test cases?"
+msgstr ""
+
+#: testmanager/htdocs/js/testmanager.js:95
+msgid ""
+"Are you sure you want to delete the test plan and the state of all its "
+"contained test cases?"
+msgstr ""
+
+#: testmanager/htdocs/js/testmanager.js:212
+#: testmanager/htdocs/js/testmanager.js:361
+msgid "Results: "
+msgstr ""
+

File testman4trac/trunk/testmanager/locale/messages.pot

+# Translations template for TestManager.
+# Copyright (C) 2010 Roberto Longobardi
+# This file is distributed under the same license as the TestManager
+# project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: TestManager 1.3.12\n"
+"Report-Msgid-Bugs-To: seccanj@gmail.com\n"
+"POT-Creation-Date: 2010-12-18 21:34+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.5\n"
+
+#: testmanager/api.py:102
+msgid "Successful"
+msgstr ""
+
+#: testmanager/api.py:103
+msgid "Untested"
+msgstr ""
+
+#: testmanager/api.py:104
+msgid "Failed"
+msgstr ""
+
+#: testmanager/api.py:121 testmanager/macros.py:540
+#: testmanager/macros.py:864
+msgid "Timestamp"
+msgstr ""
+
+#: testmanager/api.py:121 testmanager/macros.py:490
+#: testmanager/macros.py:540 testmanager/macros.py:864
+#: testmanager/model.py:471
+msgid "Author"
+msgstr ""
+
+#: testmanager/api.py:121 testmanager/api.py:134 testmanager/macros.py:490
+#: testmanager/macros.py:864 testmanager/model.py:464
+msgid "Status"
+msgstr ""
+
+#: testmanager/macros.py:217
+msgid "All Catalogs"
+msgstr ""
+
+#: testmanager/macros.py:306 testmanager/macros.py:327
+#: testmanager/macros.py:445 testmanager/macros.py:467
+msgid "Filter:"
+msgstr ""
+
+#: testmanager/macros.py:306 testmanager/macros.py:327
+#: testmanager/macros.py:445 testmanager/macros.py:467
+msgid ""
+"Type the test to search for, even more than one word. You can also "
+"filter on the test case status (untested, successful, failed)."
+msgstr ""
+
+#: testmanager/macros.py:307 testmanager/macros.py:446
+msgid "Expand all"
+msgstr ""
+
+#: testmanager/macros.py:307 testmanager/macros.py:446
+msgid "Collapse all"
+msgstr ""
+
+#: testmanager/macros.py:332 testmanager/macros.py:472
+#: testmanager/model.py:470
+msgid "Name"
+msgstr ""
+
+#: testmanager/macros.py:341 testmanager/macros.py:481
+#: testmanager/model.py:453 testmanager/model.py:457
+#: testmanager/model.py:461 testmanager/model.py:467
+msgid "ID"
+msgstr ""
+
+#: testmanager/macros.py:351
+msgid "Description"
+msgstr ""
+
+#: testmanager/macros.py:490
+msgid "Last Change"
+msgstr ""
+
+#: testmanager/macros.py:526
+msgid "Available Test Plans"
+msgstr ""
+
+#: testmanager/macros.py:540
+msgid "Plan Name"
+msgstr ""
+
+#: testmanager/macros.py:546
+msgid "Open Test Plan"
+msgstr ""
+
+#: testmanager/macros.py:551
+msgid "Delete"
+msgstr ""
+
+#: testmanager/macros.py:614 testmanager/macros.py:709
+msgid "Open"
+msgstr ""
+
+#: testmanager/macros.py:655 testmanager/macros.py:657
+msgid "Edit the Test Case"
+msgstr ""
+
+#: testmanager/macros.py:821
+msgid "Change the Status:"
+msgstr ""
+
+#: testmanager/macros.py:831
+msgid "SUCCESSFUL"
+msgstr ""
+
+#: testmanager/macros.py:839
+msgid "TO_BE_TESTED"
+msgstr ""
+
+#: testmanager/macros.py:847
+msgid "FAILED"
+msgstr ""
+
+#: testmanager/macros.py:861
+msgid "Status change history"
+msgstr ""
+
+#: testmanager/model.py:454 testmanager/model.py:458
+#: testmanager/model.py:463 testmanager/model.py:469
+msgid "Wiki page name"
+msgstr ""
+
+#: testmanager/model.py:462
+msgid "Plan ID"
+msgstr ""
+
+#: testmanager/model.py:468
+msgid "Catalog ID"
+msgstr ""
+
+#: testmanager/model.py:472
+msgid "Created"
+msgstr ""
+
+#: testmanager/web_ui.py:67
+msgid "Test Manager"
+msgstr ""
+
+#: testmanager/wiki.py:152
+msgid ""
+"Select the catalog into which to paste the Test Case and click on "
+"'Move the copied Test Case here'. "
+msgstr ""
+
+#: testmanager/wiki.py:153 testmanager/wiki.py:175 testmanager/wiki.py:348
+msgid "Cancel"
+msgstr ""
+
+#: testmanager/wiki.py:155
+msgid "Test Catalogs List"
+msgstr ""
+
+#: testmanager/wiki.py:158
+msgid "New Catalog:"
+msgstr ""
+
+#: testmanager/wiki.py:159
+msgid "Add a Catalog"
+msgstr ""
+
+#: testmanager/wiki.py:174
+msgid ""
+"Select the catalog (even this one) into which to paste the Test Case "
+"and click on 'Move the copied Test Case here'. "
+msgstr ""
+
+#: testmanager/wiki.py:178
+msgid "Test Catalog"
+msgstr ""
+
+#: testmanager/wiki.py:180
+msgid "New Sub-Catalog:"
+msgstr ""
+
+#: testmanager/wiki.py:181
+msgid "Add a Sub-Catalog"
+msgstr ""
+
+#: testmanager/wiki.py:196
+msgid "Move the copied Test Case here"
+msgstr ""
+
+#: testmanager/wiki.py:218
+msgid "New Test Case:"
+msgstr ""
+
+#: testmanager/wiki.py:222
+msgid "Add a Test Case"
+msgstr ""
+
+#: testmanager/wiki.py:226
+msgid "New Test Plan:"
+msgstr ""
+
+#: testmanager/wiki.py:230
+msgid "Generate a new Test Plan"
+msgstr ""
+
+#: testmanager/wiki.py:279 testmanager/wiki.py:449
+msgid "Back to the Catalog"
+msgstr ""
+
+#: testmanager/wiki.py:290
+msgid "Test Plan: "
+msgstr ""
+
+#: testmanager/wiki.py:347
+msgid ""
+"The Test Case has been copied. Now select the catalog into which to "
+"move the Test Case and click on 'Move the copied Test Case here'. "
+msgstr ""
+
+#: testmanager/wiki.py:353 testmanager/wiki.py:416
+msgid "Test Case"
+msgstr ""
+
+#: testmanager/wiki.py:363 testmanager/wiki.py:428
+msgid "Open a Ticket on this Test Case"
+msgstr ""
+
+#: testmanager/wiki.py:365 testmanager/wiki.py:430
+msgid "Show Related Tickets"
+msgstr ""
+
+#: testmanager/wiki.py:367
+msgid "Move the Test Case into another catalog"
+msgstr ""
+
+#: testmanager/wiki.py:369
+msgid "Duplicate the Test Case"
+msgstr ""
+
+#: testmanager/wiki.py:446
+msgid "Back to the Test Plan"
+msgstr ""
+
+#: testmanager/wiki.py:514
+msgid "Edit"
+msgstr ""
+
+#: testmanager/wiki.py:518
+msgid "Save"
+msgstr ""
+
+#: testmanager/templates/testmanagerstats.html:53
+msgid "Test Management Statistics"
+msgstr ""
+
+#: testmanager/templates/testmanagerstats.html:83
+msgid "Settings"
+msgstr ""
+
+#: testmanager/templates/testmanagerstats.html:85
+msgid "Start Date:"
+msgstr ""
+
+#: testmanager/templates/testmanagerstats.html:87
+msgid "End Date:"
+msgstr ""
+
+#: testmanager/templates/testmanagerstats.html:89
+msgid "Resolution:"
+msgstr ""
+
+#: testmanager/templates/testmanagerstats.html:92
+msgid "1 Day"
+msgstr ""
+
+#: testmanager/templates/testmanagerstats.html:93
+msgid "1 Week"
+msgstr ""
+
+#: testmanager/templates/testmanagerstats.html:94
+msgid "2 Weeks"
+msgstr ""
+
+#: testmanager/templates/testmanagerstats.html:95
+msgid "1 Month"
+msgstr ""
+
+#: testmanager/templates/testmanagerstats.html:96
+msgid "2 Months"
+msgstr ""
+
+#: testmanager/templates/testmanagerstats.html:97
+msgid "3 Months"
+msgstr ""
+
+#: testmanager/templates/testmanagerstats.html:98
+msgid "6 Months"
+msgstr ""
+
+#: testmanager/templates/testmanagerstats.html:99
+msgid "1 Year"
+msgstr ""
+
+#: testmanager/templates/testmanagerstats.html:101
+msgid "Test Plan:"
+msgstr ""
+
+#: testmanager/templates/testmanagerstats.html:103
+msgid "All Test Plans"
+msgstr ""
+
+#: testmanager/templates/testmanagerstats.html:112
+msgid "URL to bookmark:"
+msgstr ""
+
+#: testmanager/templates/testmanagerstats.html:126
+msgid "Export this data to Excel"
+msgstr ""
+
+#: testmanager/templates/testmanagerstats.html:126
+msgid "(CSV format)"
+msgstr ""
+

File testman4trac/trunk/testmanager/macros.py

 
 from trac.core import *
 from trac.mimeview.api import Context
+from trac.util import format_datetime
 from trac.wiki.macros import WikiMacroBase
 from trac.wiki.api import WikiSystem, parse_args
 from trac.wiki.formatter import Formatter, format_to_html
 from tracgenericclass.model import GenericClassModelProvider
 from tracgenericclass.util import *
 
-from testmanager.labels import *
+from testmanager.api import TestManagerSystem
 from testmanager.model import TestCatalog, TestCase, TestCaseInPlan, TestPlan
 from testmanager.util import *
 
+try:
+    from testmanager.api import _, tag_, N_
+except ImportError:
+	from trac.util.translation import _, N_
+	tag_ = _
 
 # Macros
 
     tokens = path_name.split("_")
     curr_path = 'TC'
     
-    breadcrumb = [{'name': 'TC', 'title': LABELS['all_catalogs'], 'id': 'TC'}]
+    breadcrumb = [{'name': 'TC', 'title': _("All Catalogs"), 'id': 'TC'}]
 
     for i, tc in enumerate(tokens):
         curr_path += '_'+tc
     text = ''
 
     if mode == 'tree':
-        text +='<div style="padding: 0px 0px 10px 10px">'+LABELS['filter_label']+' <input id="tcFilter" title="'+LABELS['filter_help']+'" type="text" size="40" onkeyup="starthighlight(this.value)"/>&nbsp;&nbsp;<span id="searchResultsNumberId" style="font-weight: bold;"></span></div>'
-        text +='<div style="font-size: 0.8em;padding-left: 10px"><a style="margin-right: 10px" onclick="toggleAll(true)" href="javascript:void(0)">'+LABELS['expand_all']+'</a><a onclick="toggleAll(false)" href="javascript:void(0)">'+LABELS['collapse_all']+'</a></div>';
+        text +='<div style="padding: 0px 0px 10px 10px">'+_("Filter:")+' <input id="tcFilter" title="'+_("Type the test to search for, even more than one word. You can also filter on the test case status (untested, successful, failed).")+'" type="text" size="40" onkeyup="starthighlight(this.value)"/>&nbsp;&nbsp;<span id="searchResultsNumberId" style="font-weight: bold;"></span></div>'
+        text +='<div style="font-size: 0.8em;padding-left: 10px"><a style="margin-right: 10px" onclick="toggleAll(true)" href="javascript:void(0)">'+_("Expand all")+'</a><a onclick="toggleAll(false)" href="javascript:void(0)">'+_("Collapse all")+'</a></div>';
         text +='<div id="ticketContainer">'
 
-        text += _render_subtree('-1', components, ind, 0)
+        text += _render_subtree(env, '-1', components, ind, 0)
         
         text +='</div>'
         
             'testcaseinplan': [False, None]
             }
 
-        text +='<div style="padding: 0px 0px 10px 10px">'+LABELS['filter_label']+' <input id="tcFilter" title="'+LABELS['filter_help']+'" type="text" size="40" onkeyup="starthighlightTable(this.value)"/>&nbsp;&nbsp;<span id="searchResultsNumberId" style="font-weight: bold;"></span></div>'
+        text +='<div style="padding: 0px 0px 10px 10px">'+_("Filter:")+' <input id="tcFilter" title="'+_("Type the test to search for, even more than one word. You can also filter on the test case status (untested, successful, failed).")+'" type="text" size="40" onkeyup="starthighlightTable(this.value)"/>&nbsp;&nbsp;<span id="searchResultsNumberId" style="font-weight: bold;"></span></div>'
         text += '<form id="testCatalogRunBook"><fieldset id="testCatalogRunBookFields" class="expanded">'
         text += '<table id="testcaseList" class="listing"><thead><tr>';
         
         # Common columns
-        text += '<th>'+LABELS['name_header']+'</th>'
+        text += '<th>'+_("Name")+'</th>'
         
         # Custom testcatalog columns
         if tcat_has_custom:
                     text += '<th>'+f['label']+'</th>'
 
         # Base testcase columns
-        text += '<th>'+LABELS['id_header']+'</th>'
+        text += '<th>'+_("ID")+'</th>'
 
         # Custom testcase columns
         if tc_has_custom:
         
         # Test case full details
         if fulldetails:
-            text += '<th>'+LABELS['description_header']+'</th>'
+            text += '<th>'+_("Description")+'</th>'
             
         text += '</tr></thead><tbody>';
         
     return text
     
 def _build_testplan_tree(env, req, context, planid, curpage, mode='tree',sortby='name'):
+    testmanagersystem = TestManagerSystem(env)
+    default_status = testmanagersystem.get_default_tc_status()
+    
     # Determine current catalog name
     cat_name = 'TC'
     if curpage.find('_TC') >= 0:
                 else:
                     ts = tp['time']
                     author = tp['author']
-                    status = 'TO_BE_TESTED'
+                    status = default_status
                 
                 if sortby == 'name':
                     key = subpage_title
     text = ''
 
     if mode == 'tree':
-        text +='<div style="padding: 0px 0px 10px 10px">'+LABELS['filter_label']+' <input id="tcFilter" title="'+LABELS['filter_help']+'" type="text" size="40" onkeyup="starthighlight(this.value)"/>&nbsp;&nbsp;<span id="searchResultsNumberId" style="font-weight: bold;"></span></div>'
-        text +='<div style="font-size: 0.8em;padding-left: 10px"><a style="margin-right: 10px" onclick="toggleAll(true)" href="javascript:void(0)">'+LABELS['expand_all']+'</a><a onclick="toggleAll(false)" href="javascript:void(0)">'+LABELS['collapse_all']+'</a></div>';
+        text +='<div style="padding: 0px 0px 10px 10px">'+_("Filter:")+' <input id="tcFilter" title="'+_("Type the test to search for, even more than one word. You can also filter on the test case status (untested, successful, failed).")+'" type="text" size="40" onkeyup="starthighlight(this.value)"/>&nbsp;&nbsp;<span id="searchResultsNumberId" style="font-weight: bold;"></span></div>'
+        text +='<div style="font-size: 0.8em;padding-left: 10px"><a style="margin-right: 10px" onclick="toggleAll(true)" href="javascript:void(0)">'+_("Expand all")+'</a><a onclick="toggleAll(false)" href="javascript:void(0)">'+_("Collapse all")+'</a></div>';
         text +='<div id="ticketContainer">'
-        text += _render_subtree(planid, components, ind, 0)
+        text += _render_subtree(env, planid, components, ind, 0)
         text +='</div>'
 
     elif mode == 'tree_table':
             'testcaseinplan': [tcip_has_custom, tcip_fields]
             }
 
-        text +='<div style="padding: 0px 0px 10px 10px">'+LABELS['filter_label']+' <input id="tcFilter" title="'+LABELS['filter_help']+'" type="text" size="40" onkeyup="starthighlightTable(this.value)"/>&nbsp;&nbsp;<span id="searchResultsNumberId" style="font-weight: bold;"></span></div>'
+        text +='<div style="padding: 0px 0px 10px 10px">'+_("Filter:")+' <input id="tcFilter" title="'+_("Type the test to search for, even more than one word. You can also filter on the test case status (untested, successful, failed).")+'" type="text" size="40" onkeyup="starthighlightTable(this.value)"/>&nbsp;&nbsp;<span id="searchResultsNumberId" style="font-weight: bold;"></span></div>'
         text += '<form id="testPlan"><fieldset id="testPlanFields" class="expanded">'
         text += '<table id="testcaseList" class="listing"><thead><tr>';
 
         # Common columns
-        text += '<th>'+LABELS['name_header']+'</th>'
+        text += '<th>'+_("Name")+'</th>'
         
         # Custom testcatalog columns
         if custom_ctx['testcatalog'][0]:
                     text += '<th>'+f['label']+'</th>'
 
         # Base testcase columns
-        text += '<th>'+LABELS['id_header']+'</th>'
+        text += '<th>'+_("ID")+'</th>'
 
         #Custom testcase columns
         if custom_ctx['testcase'][0]:
                     text += '<th>'+f['label']+'</th>'
 
         # Base testcaseinplan columns
-        text += '<th>'+LABELS['status']+'</th><th>'+LABELS['author']+'</th><th>'+LABELS['last_change_header']+'</th>'
+        text += '<th>'+_("Status")+'</th><th>'+_("Author")+'</th><th>'+_("Last Change")+'</th>'
         
         # Custom testcaseinplan columns
         if custom_ctx['testcaseinplan'][0]:
     
     markup, num_plans = _render_testplan_list(env, catid, mode, fulldetails, show_delete_button)
 
-    text = '<form id="testPlanList"><fieldset id="testPlanListFields" class="collapsed"><legend class="foldable" style="cursor: pointer;"><a href="#no4"  onclick="expandCollapseSection(\'testPlanListFields\')">'+LABELS['test_plan_list']+' ('+str(num_plans)+')</a></legend>'
+    text = '<form id="testPlanList"><fieldset id="testPlanListFields" class="collapsed"><legend class="foldable" style="cursor: pointer;"><a href="#no4"  onclick="expandCollapseSection(\'testPlanListFields\')">'+_("Available Test Plans")+' ('+str(num_plans)+')</a></legend>'
     text += markup
     text += '</fieldset></form>'
 
     cat = TestCatalog(env, catid)
     
     result = '<table class="listing"><thead>'
-    result += '<tr><th>'+LABELS['plan_name']+'</th><th>'+LABELS['author']+'</th><th>'+LABELS['timestamp']+'</th><th></th></tr>'
+    result += '<tr><th>'+_("Plan Name")+'</th><th>'+_("Author")+'</th><th>'+_("Timestamp")+'</th><th></th></tr>'
     result += '</thead><tbody>'
     
     num_plans = 0
     for tp in sorted(cat.list_testplans(), cmp=lambda x,y: cmp(x['time'],y['time']), reverse=True):
         result += '<tr>'
-        result += '<td><a title="'+LABELS['open_testplan_title']+'" href="'+tp['page_name']+'?planid='+tp['id']+'">'+tp['name']+'</a></td>'
+        result += '<td><a title="'+_("Open Test Plan")+'" href="'+tp['page_name']+'?planid='+tp['id']+'">'+tp['name']+'</a></td>'
         result += '<td>'+tp['author']+'</td>'
-        result += '<td>'+str(tp['time'])+'</td>'
+        result += '<td>'+format_datetime(tp['time'])+'</td>'
         
         if show_delete_button:
-            result += '<td style="cursor: pointer;"><img class="iconElement" alt="'+LABELS['delete']+'" title="'+LABELS['delete']+'" src="'+delete_icon+'" onclick="deleteTestPlan(\'../testdelete?type=testplan&path='+tp['page_name']+'&mode='+mode+'&fulldetails='+str(fulldetails)+'&planid='+tp['id']+'\')"/></td>'
+            result += '<td style="cursor: pointer;"><img class="iconElement" alt="'+_("Delete")+'" title="'+_("Delete")+'" src="'+delete_icon+'" onclick="deleteTestPlan(\'../testdelete?type=testplan&path='+tp['page_name']+'&mode='+mode+'&fulldetails='+str(fulldetails)+'&planid='+tp['id']+'\')"/></td>'
         else:
             result += '<td></td>'
         
     return text
  
 # Render the subtree
-def _render_subtree(planid, component, ind, level):
+def _render_subtree(env, planid, component, ind, level):
     data = component
     text = ''
     if (level == 0):
             else:
                 plan_param = ''
                 
-            text+='<span name="'+toggable+'" style="cursor: pointer" id="b_'+index+'"><span onclick="toggle(\'b_'+index+'\')"><img class="iconElement" src="'+toggle_icon+'" /></span><span id="l_'+index+'" onmouseover="underlineLink(\'l_'+index+'\')" onmouseout="removeUnderlineLink(\'l_'+index+'\')" onclick="window.location=\''+comp['id']+plan_param+'\'" title='+LABELS['open']+'>'+comp['title']+'</span></span><span style="color: gray;">&nbsp;('+str(comp['tot'])+')</span>'
+            text+='<span name="'+toggable+'" style="cursor: pointer" id="b_'+index+'"><span onclick="toggle(\'b_'+index+'\')"><img class="iconElement" src="'+toggle_icon+'" /></span><span id="l_'+index+'" onmouseover="underlineLink(\'l_'+index+'\')" onmouseout="removeUnderlineLink(\'l_'+index+'\')" onclick="window.location=\''+comp['id']+plan_param+'\'" title='+_("Open")+'>'+comp['title']+'</span></span><span style="color: gray;">&nbsp;('+str(comp['tot'])+')</span>'
             text +='<ul id="b_'+index+'_list" style="display:none;list-style: none;">';
             ind['count']+=1
-            text+=_render_subtree(planid, subcData, ind, level+1)
+            text+=_render_subtree(env, planid, subcData, ind, level+1)
             if ('childrenT' in comp):            
                 mtData=comp['childrenT']
-                text+=_render_testcases(planid, mtData)
+                text+=_render_testcases(env, planid, mtData)
         text+='</ul>'
         text+='</li>'
     if (level == 0):
         if ('childrenT' in component):            
             cmtData=component['childrenT']
-            text+=_render_testcases(planid, cmtData)
+            text+=_render_testcases(env, planid, cmtData)
         text+='</ul>'        
     return text
 
-def _render_testcases(planid, data): 
+def _render_testcases(env, planid, data): 
+    
+    testmanagersystem = TestManagerSystem(env)
+    tc_statuses = testmanagersystem.get_tc_statuses()
+    
     text=''
     keyList = data.keys()
     sortedList = sorted(keyList)
         tick = data[x]
         status = tick['status']
         has_status = True
-        if status == 'SUCCESSFUL':
-            statusIcon='../chrome/testmanager/images/green.png'
-        elif status == 'FAILED':
-            statusIcon='../chrome/testmanager/images/red.png'
-        elif status == 'TO_BE_TESTED':
-            statusIcon='../chrome/testmanager/images/yellow.png'
+        if status is not None and len(status) > 0 and status != 'NONE':
+            stat_meaning = tc_statuses[status][2]
+            if stat_meaning == 0:
+                statusIcon='../chrome/testmanager/images/green.png'
+            elif stat_meaning == 1:
+                statusIcon='../chrome/testmanager/images/yellow.png'
+            elif stat_meaning == 2:
+                statusIcon='../chrome/testmanager/images/red.png'
         else:
             has_status = False
 
         if has_status:
-            statusLabel = LABELS[status]
-            text+="<li style='font-weight: normal;' onmouseover='showPencil(\"pencilIcon"+tick['id']+"\", true)' onmouseout='hidePencil(\"pencilIcon"+tick['id']+"\", false)'><img class='iconElement' src='"+statusIcon+"' title='"+statusLabel+"'></img><a href='"+tick['id']+"?planid="+planid+"' target='_blank'>"+tick['title']+"&nbsp;</a><span style='display: none;'>"+statusLabel+"</span><span><a class='rightIcon' style='display: none;' title='"+LABELS['edit_test_case_label']+"' href='"+tick['id']+"?action=edit&planid="+planid+"' target='_blank' id='pencilIcon"+tick['id']+"'></a></span></li>"
+            statusLabel = tc_statuses[status][1]
+            text+="<li style='font-weight: normal;' onmouseover='showPencil(\"pencilIcon"+tick['id']+"\", true)' onmouseout='hidePencil(\"pencilIcon"+tick['id']+"\", false)'><img class='iconElement' src='"+statusIcon+"' title='"+statusLabel+"'></img><a href='"+tick['id']+"?planid="+planid+"' target='_blank'>"+tick['title']+"&nbsp;</a><span style='display: none;'>"+statusLabel+"</span><span><a class='rightIcon' style='display: none;' title='"+_("Edit the Test Case")+"' href='"+tick['id']+"?action=edit&planid="+planid+"' target='_blank' id='pencilIcon"+tick['id']+"'></a></span></li>"
         else:
-            text+="<li style='font-weight: normal;' onmouseover='showPencil(\"pencilIcon"+tick['id']+"\", true)' onmouseout='hidePencil(\"pencilIcon"+tick['id']+"\", false)'><a href='"+tick['id']+"' target='_blank'>"+tick['title']+"&nbsp;</a><span><a class='rightIcon' style='display: none;' title='"+LABELS['edit_test_case_label']+"' href='"+tick['id']+"?action=edit' target='_blank' id='pencilIcon"+tick['id']+"'></a></span></li>"
+            text+="<li style='font-weight: normal;' onmouseover='showPencil(\"pencilIcon"+tick['id']+"\", true)' onmouseout='hidePencil(\"pencilIcon"+tick['id']+"\", false)'><a href='"+tick['id']+"' target='_blank'>"+tick['title']+"&nbsp;</a><span><a class='rightIcon' style='display: none;' title='"+_("Edit the Test Case")+"' href='"+tick['id']+"?action=edit' target='_blank' id='pencilIcon"+tick['id']+"'></a></span></li>"
             
     return text
         
 def _build_testcase_status(env, req, planid, curpage):
+    testmanagersystem = TestManagerSystem(env)
+    tc_statuses = testmanagersystem.get_tc_statuses()
+    
     tc_id = curpage.rpartition('_TC')[2]
     
     tcip = TestCaseInPlan(env, tc_id, planid)
     if tcip.exists:
         status = tcip['status']
     else:
-        status = 'TO_BE_TESTED'
+        status = testmanagersystem.get_default_tc_status()
     
-    display = {'SUCCESSFUL': 'none', 'TO_BE_TESTED': 'none', 'FAILED': 'none'}
-    display[status] = 'block'
+    # Hide all icons except the one relative to the current test
+    # case status
+    display = {'0': 'none', '1': 'none', '2': 'none'}
+    display[str(tc_statuses[status][2])] = 'block'
     
     text = ''
-    text += '<img style="display: '+display['TO_BE_TESTED']+';" id="tcTitleStatusIconTO_BE_TESTED" src="../chrome/testmanager/images/yellow.png" title="'+LABELS['TO_BE_TESTED']+'"></img></span>'
-    text += '<img style="display: '+display['FAILED']+';" id="tcTitleStatusIconFAILED" src="../chrome/testmanager/images/red.png" title="'+LABELS['FAILED']+'"></img></span>'
-    text += '<img style="display: '+display['SUCCESSFUL']+';" id="tcTitleStatusIconSUCCESSFUL" src="../chrome/testmanager/images/green.png" title="'+LABELS['SUCCESSFUL']+'"></img></span>'
+    # TODO: Replace 'SUCCESSFUL' and others with generic mapping
+    text += '<img style="display: '+display['0']+';" id="tcTitleStatusIconSUCCESSFUL" src="../chrome/testmanager/images/green.png" title="'+_(tc_statuses['SUCCESSFUL'][0])+'"></img></span>'
+    text += '<img style="display: '+display['1']+';" id="tcTitleStatusIconTO_BE_TESTED" src="../chrome/testmanager/images/yellow.png" title="'+_(tc_statuses['TO_BE_TESTED'][0])+'"></img></span>'
+    text += '<img style="display: '+display['2']+';" id="tcTitleStatusIconFAILED" src="../chrome/testmanager/images/red.png" title="'+_(tc_statuses['FAILED'][0])+'"></img></span>'
     
     return text
     
                 plan_param = ''
             
             # Common columns
-            text += '<tr name="testcatalog"><td style="padding-left: '+str(level*30)+'px;"><a href="'+comp['id']+'?mode=tree_table'+plan_param+'&fulldetails='+str(fulldetails)+'" title="'+LABELS['open']+'">'+comp['title']+'</a></td>'
+            text += '<tr name="testcatalog"><td style="padding-left: '+str(level*30)+'px;"><a href="'+comp['id']+'?mode=tree_table'+plan_param+'&fulldetails='+str(fulldetails)+'" title="'+_("Open")+'">'+comp['title']+'</a></td>'
 
             # Custom testcatalog columns
             if custom_ctx['testcatalog'][0]:
     return text
 
 def _render_testcases_as_table(env, context, planid, data, level=0, custom_ctx=None, fulldetails=False): 
+
+    testmanagersystem = TestManagerSystem(env)
+    tc_statuses = testmanagersystem.get_tc_statuses()
+    
     text=''
     keyList = data.keys()
     sortedList = sorted(keyList)
         tick = data[x]
         status = tick['status']
         has_status = True
-        if status == 'SUCCESSFUL':
-            statusIcon='../chrome/testmanager/images/green.png'
-        elif status == 'FAILED':
-            statusIcon='../chrome/testmanager/images/red.png'
-        elif status == 'TO_BE_TESTED':
-            statusIcon='../chrome/testmanager/images/yellow.png'
+        if status is not None and len(status) > 0 and status != 'NONE':
+            stat_meaning = tc_statuses[status][2]
+            if stat_meaning == 0:
+                statusIcon='../chrome/testmanager/images/green.png'
+            elif stat_meaning == 1:
+                statusIcon='../chrome/testmanager/images/yellow.png'
+            elif stat_meaning == 2:
+                statusIcon='../chrome/testmanager/images/red.png'
         else:
             has_status = False
 
 
         # Common columns
         if has_status:
-            statusLabel = LABELS[status]
+            statusLabel = tc_statuses[status][1]
             text += '<td style="padding-left: '+str(level*30)+'px;"><img class="iconElement" src="'+statusIcon+'" title="'+statusLabel+'"></img><a href="'+tick['id']+'?planid='+planid+'&mode=tree_table" target="_blank">'+tick['title']+'</a></td>'
         else:
             text += '<td style="padding-left: '+str(level*30)+'px;"><a href="'+tick['id']+'?mode=tree_table&fulldetails='+str(fulldetails)+'" target="_blank">'+tick['title']+'</a></td>'
 
         if has_status:
             # Base testcaseinplan columns
-            text += '<td>'+statusLabel+'</td><td>'+tick['author']+'</td><td>'+str(tick['ts'])+'</td>'
+            text += '<td>'+statusLabel+'</td><td>'+tick['author']+'</td><td>'+format_datetime(tick['ts'])+'</td>'
 
             # Custom testcaseinplan columns
             if custom_ctx['testcaseinplan'][0]:
     return text
         
 def _build_testcase_change_status(env, req, planid, curpage):
+    testmanagersystem = TestManagerSystem(env)
+    tc_statuses = testmanagersystem.get_tc_statuses()
+
     tc_id = curpage.rpartition('_TC')[2]
     
     tcip = TestCaseInPlan(env, tc_id, planid)
     if tcip.exists:
         status = tcip['status']
     else:
-        status = 'TO_BE_TESTED'
+        status = testmanagersystem.get_default_tc_status()
+
+    status_meaning = tc_statuses[status][2]
     
     text = ''
     
     text += '<script type="text/javascript">var currStatus = "'+status+'";</script>'
 
-    text += LABELS['change_status_label']
+    text += _("Change the Status:")
     
     text += '<span style="margin-left: 15px;">'
 
     border = ''
-    if status == 'SUCCESSFUL':
+    if status_meaning == 0:
         border = 'border: 2px solid black;'
 
+    # TODO: Replace "SUCCESSFUL" with generic mapping
     text += '<span id="tcStatusSUCCESSFUL" style="padding: 3px; cursor: pointer;'+border+'" onclick="changestate(\''+tc_id+'\', \''+planid+'\', \''+curpage+'\', \'SUCCESSFUL\')">'
-    text += '<img src="../chrome/testmanager/images/green.png" title="'+LABELS['SUCCESSFUL']+'"></img></span>'
+    text += '<img src="../chrome/testmanager/images/green.png" title="'+tc_statuses["SUCCESSFUL"][1]+'"></img></span>'
 
     border = ''
-    if status == 'TO_BE_TESTED':
+    if status_meaning == 1:
         border = 'border: 2px solid black;'
 
+    # TODO: Replace "TO_BE_TESTED" with generic mapping
     text += '<span id="tcStatusTO_BE_TESTED" style="padding: 3px; cursor: pointer;'+border+'" onclick="changestate(\''+tc_id+'\', \''+planid+'\', \''+curpage+'\', \'TO_BE_TESTED\')">'
-    text += '<img src="../chrome/testmanager/images/yellow.png" title="'+LABELS['TO_BE_TESTED']+'"></img></span>'
+    text += '<img src="../chrome/testmanager/images/yellow.png" title="'+tc_statuses["TO_BE_TESTED"][1]+'"></img></span>'
 
     border = ''
-    if status == 'FAILED':
+    if status_meaning == 2:
         border = 'border: 2px solid black;'
 
+    # TODO: Replace "FAILED" with generic mapping
     text += '<span id="tcStatusFAILED" style="padding: 3px; cursor: pointer;'+border+'" onclick="changestate(\''+tc_id+'\', \''+planid+'\', \''+curpage+'\', \'FAILED\')">'
-    text += '<img src="../chrome/testmanager/images/red.png" title="'+LABELS['FAILED']+'"></img></span>'
+    text += '<img src="../chrome/testmanager/images/red.png" title="'+tc_statuses["FAILED"][1]+'"></img></span>'
 
     text += '</span>'
     
     return text
     
 def _build_testcase_status_history(env,req,planid,curpage):
+    testmanagersystem = TestManagerSystem(env)
+    tc_statuses = testmanagersystem.get_tc_statuses()
+
     tc_id = curpage.rpartition('_TC')[2]
     
     tcip = TestCaseInPlan(env, tc_id, planid)
     
-    text = '<form id="testCaseHistory"><fieldset id="testCaseHistoryFields" class="collapsed"><legend class="foldable" style="cursor: pointer;"><a href="#no3"  onclick="expandCollapseSection(\'testCaseHistoryFields\')">'+LABELS['status_change_hist']+'</a></legend>'
+    text = '<form id="testCaseHistory"><fieldset id="testCaseHistoryFields" class="collapsed"><legend class="foldable" style="cursor: pointer;"><a href="#no3"  onclick="expandCollapseSection(\'testCaseHistoryFields\')">'+_("Status change history")+'</a></legend>'
     
     text += '<table class="listing"><thead>'
-    text += '<tr><th>'+LABELS['timestamp']+'</th><th>'+LABELS['author']+'</th><th>'+LABELS['status']+'</th></tr>'
+    text += '<tr><th>'+_("Timestamp")+'</th><th>'+_("Author")+'</th><th>'+_("Status")+'</th></tr>'
     text += '</thead><tbody>'
 
     for ts, author, status in tcip.list_history():
         text += '<tr>'
-        text += '<td>'+str(from_any_timestamp(ts))+'</td>'
+        text += '<td>'+format_datetime(from_any_timestamp(ts))+'</td>'
         text += '<td>'+author+'</td>'
-        text += '<td>'+LABELS[status]+'</td>'
+        text += '<td>'+tc_statuses[status][1]+'</td>'
         text += '</tr>'
         
     text += '</tbody></table>'

File testman4trac/trunk/testmanager/model.py

 from trac.resource import Resource, ResourceNotFound
 from trac.util.datefmt import utc, utcmax
 from trac.util.text import CRLF
-from trac.util.translation import _, N_, gettext
 from trac.wiki.api import WikiSystem
 from trac.wiki.model import WikiPage
 
 
 from testmanager.util import *
 
+try:
+    from testmanager.api import _, tag_, N_
+except ImportError:
+	from trac.util.translation import _, N_
+	tag_ = _
 
 class AbstractTestDescription(AbstractWikiPageWrapper):
     """

File testman4trac/trunk/testmanager/web_ui.py

 from trac.util import to_unicode
 from trac.util.compat import sorted, set, any
 from trac.util.text import CRLF
-from trac.util.translation import _
 from trac.web.chrome import ITemplateProvider, INavigationContributor, \
                             add_stylesheet, add_script, add_ctxtnav
 from trac.wiki.formatter import Formatter
 from trac.wiki.model import WikiPage
 
 from testmanager.api import TestManagerSystem
-from testmanager.labels import *
 from testmanager.model import TestCatalog, TestCase, TestCaseInPlan, TestPlan, TestManagerModelProvider
 
+try:
+    from testmanager.api import _, tag_, N_
+except ImportError:
+	from trac.util.translation import _, N_
+	tag_ = _
+
 class TestManagerTemplateProvider(Component):
     """Provides templates and static resources for the TestManager plugin."""
 
     def get_navigation_items(self, req):
         if 'TEST_VIEW' in req.perm:
             yield ('mainnav', 'testmanager',
-                tag.a(LABELS['main_tab_title'], href=req.href.wiki()+'/TC', accesskey='M'))
+                tag.a(_("Test Manager"), href=req.href.wiki()+'/TC', accesskey='M'))
 
 

File testman4trac/trunk/testmanager/wiki.py

 from tracgenericclass.model import GenericClassModelProvider
 
 from testmanager.macros import TestCaseBreadcrumbMacro, TestCaseTreeMacro, TestPlanTreeMacro, TestPlanListMacro, TestCaseStatusMacro, TestCaseChangeStatusMacro, TestCaseStatusHistoryMacro
-from testmanager.labels import *
 from testmanager.model import TestCatalog, TestCase, TestCaseInPlan, TestPlan
 
+try:
+    from testmanager.api import _, tag_, N_
+except ImportError:
+	from trac.util.translation import _, N_
+	tag_ = _
 
 class WikiTestManagerInterface(Component):
     """Implement generic template provider."""
         add_script(req, 'testmanager/js/labels.js')
         add_script(req, 'testmanager/js/testmanager.js')
 
+        if self.env.get_version() < 25:
+            add_script(req, 'testmanager/js/compatibility.js')
+        
+        try:
+            if req.locale is not None:
+                add_script(req, 'testmanager/js/%s.js' % req.locale)
+        except:
+            # Trac 0.11
+			pass
+
         tree_macro = TestCaseTreeMacro(self.env)
 
         if page_name == 'TC':
             # Root of all catalogs
             insert1 = tag.div()(
-                        tag.div(id='pasteTCHereMessage', class_='messageBox', style='display: none;')(LABELS['select_cat_to_move'],
-                            tag.a(href='javascript:void(0);', onclick='cancelTCMove()')(LABELS['cancel'])
+                        tag.div(id='pasteTCHereMessage', class_='messageBox', style='display: none;')(_("Select the catalog into which to paste the Test Case and click on 'Move the copied Test Case here'. "),
+                            tag.a(href='javascript:void(0);', onclick='cancelTCMove()')(_("Cancel"))
                             ),
-                        tag.h1(LABELS['tc_list']),
+                        tag.h1(_("Test Catalogs List")),
                         tag.br(), tag.br()
                         )
-            fieldLabel = LABELS['new_catalog']
-            buttonLabel = LABELS['add_catalog']
+            fieldLabel = _("New Catalog:")
+            buttonLabel = _("Add a Catalog")
         else:
             insert1 = tag.div()(
                         self._get_breadcrumb_markup(formatter, None, page_name, mode, fulldetails),
                                 )),
                         tag.br(), 
                         tag.div(id='pasteTCHereMessage', class_='messageBox', style='display: none;')(
-                            LABELS['select_cat_to_move2'],
-                            tag.a(href='javascript:void(0);', onclick='cancelTCMove()')(LABELS['cancel'])
+                            _("Select the catalog (even this one) into which to paste the Test Case and click on 'Move the copied Test Case here'. "),
+                            tag.a(href='javascript:void(0);', onclick='cancelTCMove()')(_("Cancel"))
                             ),
                         tag.br(),
-                        tag.h1(LABELS['tc_catalog'])
+                        tag.h1(_("Test Catalog"))
                         )
-            fieldLabel = LABELS['new_subcatalog']
-            buttonLabel = LABELS['add_subcatalog']
+            fieldLabel = _("New Sub-Catalog:")
+            buttonLabel = _("Add a Sub-Catalog")
 
         insert2 = tag.div()(