[gen] Allow to use class Group as parameter of Search.group (soon, we will be able to get groups of groups of groups... of searches, to produce a tree of searches); refactored i18n-related code.
This commit is contained in:
parent
fcb1d36da0
commit
0dd870c042
|
@ -5,7 +5,6 @@ import re, time, copy, sys, types, os, os.path, mimetypes, string, StringIO, \
|
|||
from appy import Object
|
||||
from appy.gen.layout import Table
|
||||
from appy.gen.layout import defaultFieldLayouts
|
||||
from appy.gen.po import PoMessage
|
||||
from appy.gen.mail import sendNotification
|
||||
from appy.gen.indexer import defaultIndexes, XhtmlTextExtractor
|
||||
from appy.gen.utils import GroupDescr, Keywords, getClassName, SomeObjects
|
||||
|
@ -191,7 +190,6 @@ class Group:
|
|||
# (b) groupData is of the form <groupName>_<numberOfColumns>.
|
||||
groupElems = groupData.rsplit('_', 1)
|
||||
if len(groupElems) == 1:
|
||||
# We have case (a)
|
||||
res = Group(groupElems[0])
|
||||
else:
|
||||
try:
|
||||
|
@ -208,36 +206,29 @@ class Group:
|
|||
if self.master: return (self.master, self.masterValue)
|
||||
if self.group: return self.group.getMasterData()
|
||||
|
||||
def generateLabels(self, messages, classDescr, walkedGroups):
|
||||
def generateLabels(self, messages, classDescr, walkedGroups,
|
||||
forSearch=False):
|
||||
'''This method allows to generate all the needed i18n labels related to
|
||||
this group. p_messages is the list of i18n p_messages that we are
|
||||
currently building; p_classDescr is the descriptor of the class where
|
||||
this group is defined.'''
|
||||
this group. p_messages is the list of i18n p_messages (a PoMessages
|
||||
instance) that we are currently building; p_classDescr is the
|
||||
descriptor of the class where this group is defined. If p_forSearch
|
||||
is True, this group is used for grouping searches, and not fields.'''
|
||||
# A part of the group label depends on p_forSearch.
|
||||
if forSearch: gp = 'searchgroup'
|
||||
else: gp = 'group'
|
||||
if self.hasLabel:
|
||||
msgId = '%s_group_%s' % (classDescr.name, self.name)
|
||||
poMsg = PoMessage(msgId, '', self.name, niceDefault=True)
|
||||
if poMsg not in messages:
|
||||
messages.append(poMsg)
|
||||
classDescr.labelsToPropagate.append(poMsg)
|
||||
msgId = '%s_%s_%s' % (classDescr.name, gp, self.name)
|
||||
messages.append(msgId, self.name)
|
||||
if self.hasDescr:
|
||||
msgId = '%s_group_%s_descr' % (classDescr.name, self.name)
|
||||
poMsg = PoMessage(msgId, '', ' ')
|
||||
if poMsg not in messages:
|
||||
messages.append(poMsg)
|
||||
classDescr.labelsToPropagate.append(poMsg)
|
||||
msgId = '%s_%s_%s_descr' % (classDescr.name, gp, self.name)
|
||||
messages.append(msgId, ' ', nice=False)
|
||||
if self.hasHelp:
|
||||
msgId = '%s_group_%s_help' % (classDescr.name, self.name)
|
||||
poMsg = PoMessage(msgId, '', ' ')
|
||||
if poMsg not in messages:
|
||||
messages.append(poMsg)
|
||||
classDescr.labelsToPropagate.append(poMsg)
|
||||
msgId = '%s_%s_%s_help' % (classDescr.name, gp, self.name)
|
||||
messages.append(msgId, ' ', nice=False)
|
||||
if self.hasHeaders:
|
||||
for i in range(self.nbOfHeaders):
|
||||
msgId = '%s_group_%s_col%d' % (classDescr.name, self.name, i+1)
|
||||
poMsg = PoMessage(msgId, '', ' ')
|
||||
if poMsg not in messages:
|
||||
messages.append(poMsg)
|
||||
classDescr.labelsToPropagate.append(poMsg)
|
||||
msgId = '%s_%s_%s_col%d' % (classDescr.name, gp, self.name, i+1)
|
||||
messages.append(msgId, ' ', nice=False)
|
||||
walkedGroups.add(self)
|
||||
if self.group and (self.group not in walkedGroups) and \
|
||||
not self.group.label:
|
||||
|
@ -302,7 +293,8 @@ class Search:
|
|||
def __init__(self, name, group=None, sortBy='', sortOrder='asc', limit=None,
|
||||
default=False, **fields):
|
||||
self.name = name
|
||||
self.group = group # Searches may be visually grouped in the portlet
|
||||
# Searches may be visually grouped in the portlet.
|
||||
self.group = Group.get(group)
|
||||
self.sortBy = sortBy
|
||||
self.sortOrder = sortOrder
|
||||
self.limit = limit
|
||||
|
@ -905,24 +897,6 @@ class Type:
|
|||
p_self type definition on p_obj.'''
|
||||
setattr(obj, self.name, value)
|
||||
|
||||
def clone(self, forTool=True):
|
||||
'''Returns a clone of myself. If p_forTool is True, the clone will be
|
||||
adapted to its life into the tool.'''
|
||||
res = copy.copy(self)
|
||||
res.group = copy.copy(self.group)
|
||||
res.page = copy.copy(self.page)
|
||||
if not forTool: return res
|
||||
res.show = True
|
||||
# Set default layouts for all Tool fields
|
||||
res.layouts = res.formatLayouts(None)
|
||||
res.specificReadPermission = False
|
||||
res.specificWritePermission = False
|
||||
res.multiplicity = (0, self.multiplicity[1])
|
||||
if callable(res.validator):
|
||||
# We will not be able to call this function from the tool.
|
||||
res.validator = None
|
||||
return res
|
||||
|
||||
def callMethod(self, obj, method, raiseOnError=True):
|
||||
'''This method is used to call a p_method on p_obj. p_method is part of
|
||||
this type definition (ie a default method, the method of a Computed
|
||||
|
@ -2025,18 +1999,6 @@ class Ref(Type):
|
|||
if objects:
|
||||
self.linkObject(obj, objects)
|
||||
|
||||
def clone(self, forTool=True):
|
||||
'''Produces a clone of myself.'''
|
||||
res = Type.clone(self, forTool)
|
||||
res.back = copy.copy(self.back)
|
||||
if not forTool: return res
|
||||
res.link = True
|
||||
res.add = False
|
||||
res.back.attribute += 'DefaultValue'
|
||||
res.back.show = False
|
||||
res.select = None # Not callable from tool.
|
||||
return res
|
||||
|
||||
def mayAdd(self, obj):
|
||||
'''May the user create a new referred object from p_obj via this Ref?'''
|
||||
# We can't (yet) do that on back references.
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# ------------------------------------------------------------------------------
|
||||
import types, copy
|
||||
import appy.gen as gen
|
||||
from po import PoMessage
|
||||
import po
|
||||
from model import ModelClass, toolFieldPrefixes
|
||||
from utils import produceNiceMessage, getClassName
|
||||
TABS = 4 # Number of blanks in a Python indentation.
|
||||
|
@ -29,20 +29,6 @@ class ClassDescriptor(Descriptor):
|
|||
def __init__(self, klass, orderedAttributes, generator):
|
||||
Descriptor.__init__(self, klass, orderedAttributes, generator)
|
||||
self.methods = '' # Needed method definitions will be generated here
|
||||
# We remember here encountered pages and groups defined in the Appy
|
||||
# type. Indeed, after having parsed all application classes, we will
|
||||
# need to generate i18n labels for every child class of the class
|
||||
# that declared pages and groups.
|
||||
self.labelsToPropagate = [] #~[PoMessage]~ Some labels (like page,
|
||||
# group or action names) need to be propagated in children classes
|
||||
# (because they contain the class name). But at this time we don't know
|
||||
# yet every sub-class. So we store those labels here; the Generator
|
||||
# will propagate them later.
|
||||
self.toolFieldsToPropagate = [] # For this class, some fields have
|
||||
# been defined on the Tool class. Those fields need to be defined
|
||||
# for child classes of this class as well, but at this time we don't
|
||||
# know yet every sub-class. So we store field definitions here; the
|
||||
# Generator will propagate them later.
|
||||
self.name = getClassName(self.klass, generator.applicationName)
|
||||
self.predefined = False
|
||||
self.customized = False
|
||||
|
@ -280,6 +266,10 @@ class FieldDescriptor:
|
|||
self.fieldType = None
|
||||
self.widgetType = None
|
||||
|
||||
def i18n(self, id, default, nice=True):
|
||||
'''Shorthand for adding a new message into self.generator.labels.'''
|
||||
self.generator.labels.append(id, default, nice=nice)
|
||||
|
||||
def __repr__(self):
|
||||
return '<Field %s, %s>' % (self.fieldName, self.classDescr)
|
||||
|
||||
|
@ -291,7 +281,7 @@ class FieldDescriptor:
|
|||
fullPrefix = prefix + 'For'
|
||||
if fieldName.startswith(fullPrefix):
|
||||
messageId = 'MSG_%s' % prefix
|
||||
res = getattr(PoMessage, messageId)
|
||||
res = getattr(po, messageId)
|
||||
if res.find('%s') != -1:
|
||||
# I must complete the message with the field name.
|
||||
res = res % fieldName.split('_')[-1]
|
||||
|
@ -302,18 +292,15 @@ class FieldDescriptor:
|
|||
'''Gets the default label, description or help (depending on p_msgType)
|
||||
for i18n message p_msgId.'''
|
||||
default = ' '
|
||||
produceNice = False
|
||||
niceDefault = False
|
||||
if isLabel:
|
||||
produceNice = True
|
||||
niceDefault = True
|
||||
default = self.fieldName
|
||||
# Some attributes need a specific predefined message
|
||||
if isinstance(self.classDescr, ToolClassDescriptor):
|
||||
default = self.getToolFieldMessage(self.fieldName)
|
||||
if default != self.fieldName: produceNice = False
|
||||
msg = PoMessage(msgId, '', default)
|
||||
if produceNice:
|
||||
msg.produceNiceDefault()
|
||||
return msg
|
||||
if default != self.fieldName: niceDefault = False
|
||||
return msgId, default, niceDefault
|
||||
|
||||
def walkString(self):
|
||||
'''How to generate an Appy String?'''
|
||||
|
@ -322,18 +309,15 @@ class FieldDescriptor:
|
|||
# Generate i18n messages for every possible value if the list
|
||||
# of values is fixed.
|
||||
for value in self.appyType.validator:
|
||||
msgLabel = '%s_%s_list_%s' % (self.classDescr.name,
|
||||
self.fieldName, value)
|
||||
poMsg = PoMessage(msgLabel, '', value)
|
||||
poMsg.produceNiceDefault()
|
||||
self.generator.labels.append(poMsg)
|
||||
label = '%s_%s_list_%s' % (self.classDescr.name,
|
||||
self.fieldName, value)
|
||||
self.i18n(label, value)
|
||||
|
||||
def walkAction(self):
|
||||
'''Generates the i18n-related label.'''
|
||||
if self.appyType.confirm:
|
||||
label = '%s_%s_confirm' % (self.classDescr.name, self.fieldName)
|
||||
msg = PoMessage(label, '', PoMessage.CONFIRM)
|
||||
self.generator.labels.append(msg)
|
||||
self.i18n(label, po.CONFIRM, nice=False)
|
||||
|
||||
def walkRef(self):
|
||||
'''How to generate a Ref?'''
|
||||
|
@ -343,23 +327,18 @@ class FieldDescriptor:
|
|||
back = self.appyType.back
|
||||
refClassName = getClassName(self.appyType.klass, self.applicationName)
|
||||
if back.hasLabel:
|
||||
backLabel = "%s_%s" % (refClassName, self.appyType.back.attribute)
|
||||
poMsg = PoMessage(backLabel, '', self.appyType.back.attribute)
|
||||
poMsg.produceNiceDefault()
|
||||
self.generator.labels.append(poMsg)
|
||||
backName = self.appyType.back.attribute
|
||||
self.i18n('%s_%s' % (refClassName, backName), backName)
|
||||
# Add the label for the confirm message if relevant
|
||||
if self.appyType.addConfirm:
|
||||
label = '%s_%s_addConfirm' % (self.classDescr.name, self.fieldName)
|
||||
msg = PoMessage(label, '', PoMessage.CONFIRM)
|
||||
self.generator.labels.append(msg)
|
||||
self.i18n(label, po.CONFIRM, nice=False)
|
||||
|
||||
def walkPod(self):
|
||||
# Add i18n-specific messages
|
||||
if self.appyType.askAction:
|
||||
label = '%s_%s_askaction' % (self.classDescr.name, self.fieldName)
|
||||
msg = PoMessage(label, '', PoMessage.POD_ASKACTION)
|
||||
self.generator.labels.append(msg)
|
||||
self.classDescr.labelsToPropagate.append(msg)
|
||||
self.i18n(label, po.POD_ASKACTION, nice=False)
|
||||
# Add the POD-related fields on the Tool
|
||||
self.generator.tool.addPodRelatedFields(self)
|
||||
|
||||
|
@ -367,9 +346,7 @@ class FieldDescriptor:
|
|||
# Add i18n-specific messages
|
||||
for name, field in self.appyType.fields:
|
||||
label = '%s_%s_%s' % (self.classDescr.name, self.fieldName, name)
|
||||
msg = PoMessage(label, '', name)
|
||||
msg.produceNiceDefault()
|
||||
self.generator.labels.append(msg)
|
||||
self.i18n(label, name)
|
||||
|
||||
def walkCalendar(self):
|
||||
# Add i18n-specific messages
|
||||
|
@ -377,9 +354,7 @@ class FieldDescriptor:
|
|||
if not isinstance(eTypes, list) and not isinstance(eTypes, tuple):return
|
||||
for et in self.appyType.eventTypes:
|
||||
label = '%s_%s_event_%s' % (self.classDescr.name,self.fieldName,et)
|
||||
msg = PoMessage(label, '', et)
|
||||
msg.produceNiceDefault()
|
||||
self.generator.labels.append(msg)
|
||||
self.i18n(label, et)
|
||||
|
||||
def walkAppyType(self):
|
||||
'''Walks into the Appy type definition and gathers data about the
|
||||
|
@ -389,39 +364,33 @@ class FieldDescriptor:
|
|||
if self.appyType.indexed and (self.fieldName != 'title'):
|
||||
self.classDescr.addIndexMethod(self)
|
||||
# i18n labels
|
||||
messages = self.generator.labels
|
||||
if not self.appyType.label:
|
||||
# Create labels for generating them in i18n files, only if required.
|
||||
i18nPrefix = "%s_%s" % (self.classDescr.name, self.fieldName)
|
||||
i18nPrefix = '%s_%s' % (self.classDescr.name, self.fieldName)
|
||||
if self.appyType.hasLabel:
|
||||
messages.append(self.produceMessage(i18nPrefix))
|
||||
self.i18n(*self.produceMessage(i18nPrefix))
|
||||
if self.appyType.hasDescr:
|
||||
descrId = i18nPrefix + '_descr'
|
||||
messages.append(self.produceMessage(descrId,isLabel=False))
|
||||
self.i18n(*self.produceMessage(descrId,isLabel=False))
|
||||
if self.appyType.hasHelp:
|
||||
helpId = i18nPrefix + '_help'
|
||||
messages.append(self.produceMessage(helpId, isLabel=False))
|
||||
self.i18n(*self.produceMessage(helpId, isLabel=False))
|
||||
# Create i18n messages linked to pages and phases, only if there is more
|
||||
# than one page/phase for the class.
|
||||
ppMsgs = []
|
||||
if len(self.classDescr.getPhases()) > 1:
|
||||
# Create the message for the name of the phase
|
||||
phaseName = self.appyType.page.phase
|
||||
msgId = '%s_phase_%s' % (self.classDescr.name, phaseName)
|
||||
ppMsgs.append(PoMessage(msgId, '', produceNiceMessage(phaseName)))
|
||||
self.i18n(msgId, phaseName)
|
||||
if len(self.classDescr.getPages()) > 1:
|
||||
# Create the message for the name of the page
|
||||
pageName = self.appyType.page.name
|
||||
msgId = '%s_page_%s' % (self.classDescr.name, pageName)
|
||||
ppMsgs.append(PoMessage(msgId, '', produceNiceMessage(pageName)))
|
||||
for poMsg in ppMsgs:
|
||||
if poMsg not in messages:
|
||||
messages.append(poMsg)
|
||||
self.classDescr.labelsToPropagate.append(poMsg)
|
||||
self.i18n(msgId, pageName)
|
||||
# Create i18n messages linked to groups
|
||||
group = self.appyType.group
|
||||
if group and not group.label:
|
||||
group.generateLabels(messages, self.classDescr, set())
|
||||
group.generateLabels(self.generator.labels, self.classDescr, set())
|
||||
# Manage things which are specific to String types
|
||||
if self.appyType.type == 'String': self.walkString()
|
||||
# Manage things which are specific to Actions
|
||||
|
|
288
gen/generator.py
288
gen/generator.py
|
@ -3,9 +3,9 @@ import os, os.path, re, sys, parser, symbol, token, types
|
|||
import appy.pod, appy.pod.renderer
|
||||
from appy.shared.utils import FolderDeleter
|
||||
import appy.gen as gen
|
||||
from po import PoMessage, PoFile, PoParser
|
||||
import po
|
||||
from descriptors import *
|
||||
from utils import produceNiceMessage, getClassName
|
||||
from utils import getClassName
|
||||
from model import ModelClass, User, Group, Tool, Translation, Page
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -365,9 +365,13 @@ class ZopeGenerator(Generator):
|
|||
self.translation = TranslationClassDescriptor(Translation, self)
|
||||
self.page = PageClassDescriptor(Page, self)
|
||||
# i18n labels to generate
|
||||
self.labels = [] # i18n labels
|
||||
self.labels = po.PoMessages()
|
||||
self.referers = {}
|
||||
|
||||
def i18n(self, id, default, nice=True):
|
||||
'''Shorthand for adding a new message into self.labels.'''
|
||||
self.labels.append(id, default, nice=nice)
|
||||
|
||||
versionRex = re.compile('(.*?\s+build)\s+(\d+)')
|
||||
def initialize(self):
|
||||
# Determine version number
|
||||
|
@ -387,175 +391,20 @@ class ZopeGenerator(Generator):
|
|||
for fileName in os.listdir(i18nFolder):
|
||||
name, ext = os.path.splitext(fileName)
|
||||
if ext in self.poExtensions:
|
||||
poParser = PoParser(os.path.join(i18nFolder, fileName))
|
||||
poParser = po.PoParser(os.path.join(i18nFolder, fileName))
|
||||
self.i18nFiles[fileName] = poParser.parse()
|
||||
|
||||
def finalize(self):
|
||||
# Some useful aliases
|
||||
msg = PoMessage
|
||||
app = self.applicationName
|
||||
# Some global i18n messages
|
||||
poMsg = msg(app, '', app); poMsg.produceNiceDefault()
|
||||
self.labels += [poMsg,
|
||||
msg('app_name', '', msg.APP_NAME),
|
||||
msg('workflow_state', '', msg.WORKFLOW_STATE),
|
||||
msg('appy_title', '', msg.APPY_TITLE),
|
||||
msg('data_change', '', msg.DATA_CHANGE),
|
||||
msg('modified_field', '', msg.MODIFIED_FIELD),
|
||||
msg('previous_value', '', msg.PREVIOUS_VALUE),
|
||||
msg('phase', '', msg.PHASE),
|
||||
msg('workflow_comment', '', msg.WORKFLOW_COMMENT),
|
||||
msg('choose_a_value', '', msg.CHOOSE_A_VALUE),
|
||||
msg('choose_a_doc', '', msg.CHOOSE_A_DOC),
|
||||
msg('min_ref_violated', '', msg.MIN_REF_VIOLATED),
|
||||
msg('max_ref_violated', '', msg.MAX_REF_VIOLATED),
|
||||
msg('no_ref', '', msg.REF_NO),
|
||||
msg('add_ref', '', msg.REF_ADD),
|
||||
msg('action_ok', '', msg.ACTION_OK),
|
||||
msg('action_ko', '', msg.ACTION_KO),
|
||||
msg('move_up', '', msg.REF_MOVE_UP),
|
||||
msg('move_down', '', msg.REF_MOVE_DOWN),
|
||||
msg('query_create', '', msg.QUERY_CREATE),
|
||||
msg('query_import', '', msg.QUERY_IMPORT),
|
||||
msg('query_no_result', '', msg.QUERY_NO_RESULT),
|
||||
msg('query_consult_all', '', msg.QUERY_CONSULT_ALL),
|
||||
msg('import_title', '', msg.IMPORT_TITLE),
|
||||
msg('import_show_hide', '', msg.IMPORT_SHOW_HIDE),
|
||||
msg('import_already', '', msg.IMPORT_ALREADY),
|
||||
msg('import_many', '', msg.IMPORT_MANY),
|
||||
msg('import_done', '', msg.IMPORT_DONE),
|
||||
msg('search_title', '', msg.SEARCH_TITLE),
|
||||
msg('search_button', '', msg.SEARCH_BUTTON),
|
||||
msg('search_objects', '', msg.SEARCH_OBJECTS),
|
||||
msg('search_results', '', msg.SEARCH_RESULTS),
|
||||
msg('search_results_descr', '', ' '),
|
||||
msg('search_new', '', msg.SEARCH_NEW),
|
||||
msg('search_from', '', msg.SEARCH_FROM),
|
||||
msg('search_to', '', msg.SEARCH_TO),
|
||||
msg('search_or', '', msg.SEARCH_OR),
|
||||
msg('search_and', '', msg.SEARCH_AND),
|
||||
msg('ref_invalid_index', '', msg.REF_INVALID_INDEX),
|
||||
msg('bad_long', '', msg.BAD_LONG),
|
||||
msg('bad_float', '', msg.BAD_FLOAT),
|
||||
msg('bad_date', '', msg.BAD_DATE),
|
||||
msg('bad_email', '', msg.BAD_EMAIL),
|
||||
msg('bad_url', '', msg.BAD_URL),
|
||||
msg('bad_alphanumeric', '', msg.BAD_ALPHANUMERIC),
|
||||
msg('bad_select_value', '', msg.BAD_SELECT_VALUE),
|
||||
msg('select_delesect', '', msg.SELECT_DESELECT),
|
||||
msg('no_elem_selected', '', msg.NO_SELECTION),
|
||||
msg('object_edit', '', msg.EDIT),
|
||||
msg('object_delete', '', msg.DELETE),
|
||||
msg('object_unlink', '', msg.UNLINK),
|
||||
msg('delete_confirm', '', msg.DELETE_CONFIRM),
|
||||
msg('unlink_confirm', '', msg.UNLINK_CONFIRM),
|
||||
msg('delete_done', '', msg.DELETE_DONE),
|
||||
msg('unlink_done', '', msg.UNLINK_DONE),
|
||||
msg('goto_first', '', msg.GOTO_FIRST),
|
||||
msg('goto_previous', '', msg.GOTO_PREVIOUS),
|
||||
msg('goto_next', '', msg.GOTO_NEXT),
|
||||
msg('goto_last', '', msg.GOTO_LAST),
|
||||
msg('goto_source', '', msg.GOTO_SOURCE),
|
||||
msg('whatever', '', msg.WHATEVER),
|
||||
msg('yes', '', msg.YES),
|
||||
msg('no', '', msg.NO),
|
||||
msg('field_required', '', msg.FIELD_REQUIRED),
|
||||
msg('field_invalid', '', msg.FIELD_INVALID),
|
||||
msg('file_required', '', msg.FILE_REQUIRED),
|
||||
msg('image_required', '', msg.IMAGE_REQUIRED),
|
||||
msg('odt', '', msg.FORMAT_ODT),
|
||||
msg('pdf', '', msg.FORMAT_PDF),
|
||||
msg('doc', '', msg.FORMAT_DOC),
|
||||
msg('rtf', '', msg.FORMAT_RTF),
|
||||
msg('front_page_text', '', msg.FRONT_PAGE_TEXT),
|
||||
msg('captcha_text', '', msg.CAPTCHA_TEXT),
|
||||
msg('bad_captcha', '', msg.BAD_CAPTCHA),
|
||||
msg('app_login', '', msg.LOGIN),
|
||||
msg('app_connect', '', msg.CONNECT),
|
||||
msg('app_logout', '', msg.LOGOUT),
|
||||
msg('app_password', '', msg.PASSWORD),
|
||||
msg('app_home', '', msg.HOME),
|
||||
msg('login_reserved', '', msg.LOGIN_RESERVED),
|
||||
msg('login_in_use', '', msg.LOGIN_IN_USE),
|
||||
msg('login_ko', '', msg.LOGIN_KO),
|
||||
msg('login_ok', '', msg.LOGIN_OK),
|
||||
msg('password_too_short', '', msg.PASSWORD_TOO_SHORT),
|
||||
msg('passwords_mismatch', '', msg.PASSWORDS_MISMATCH),
|
||||
msg('object_save', '', msg.SAVE),
|
||||
msg('object_saved', '', msg.SAVED),
|
||||
msg('validation_error', '', msg.ERROR),
|
||||
msg('object_cancel', '', msg.CANCEL),
|
||||
msg('object_canceled', '', msg.CANCELED),
|
||||
msg('enable_cookies', '', msg.ENABLE_COOKIES),
|
||||
msg('page_previous', '', msg.PAGE_PREVIOUS),
|
||||
msg('page_next', '', msg.PAGE_NEXT),
|
||||
msg('forgot_password', '', msg.FORGOT_PASSWORD),
|
||||
msg('ask_password_reinit', '', msg.ASK_PASSWORD_REINIT),
|
||||
msg('wrong_password_reinit','', msg.WRONG_PASSWORD_REINIT),
|
||||
msg('reinit_mail_sent', '', msg.REINIT_MAIL_SENT),
|
||||
msg('reinit_password', '', msg.REINIT_PASSWORD),
|
||||
msg('reinit_password_body', '', msg.REINIT_PASSWORD_BODY),
|
||||
msg('new_password', '', msg.NEW_PASSWORD),
|
||||
msg('new_password_body', '', msg.NEW_PASSWORD_BODY),
|
||||
msg('new_password_sent', '', msg.NEW_PASSWORD_SENT),
|
||||
msg('last_user_access', '', msg.LAST_USER_ACCESS),
|
||||
msg('object_history', '', msg.OBJECT_HISTORY),
|
||||
msg('object_created_by', '', msg.OBJECT_CREATED_BY),
|
||||
msg('object_created_on', '', msg.OBJECT_CREATED_ON),
|
||||
msg('object_modified_on', '', msg.OBJECT_MODIFIED_ON),
|
||||
msg('object_action', '', msg.OBJECT_ACTION),
|
||||
msg('object_author', '', msg.OBJECT_AUTHOR),
|
||||
msg('action_date', '', msg.ACTION_DATE),
|
||||
msg('action_comment', '', msg.ACTION_COMMENT),
|
||||
msg('day_Mon_short', '', msg.DAY_MON_SHORT),
|
||||
msg('day_Tue_short', '', msg.DAY_TUE_SHORT),
|
||||
msg('day_Wed_short', '', msg.DAY_WED_SHORT),
|
||||
msg('day_Thu_short', '', msg.DAY_THU_SHORT),
|
||||
msg('day_Fri_short', '', msg.DAY_FRI_SHORT),
|
||||
msg('day_Sat_short', '', msg.DAY_SAT_SHORT),
|
||||
msg('day_Sun_short', '', msg.DAY_SUN_SHORT),
|
||||
msg('day_Mon', '', msg.DAY_MON),
|
||||
msg('day_Tue', '', msg.DAY_TUE),
|
||||
msg('day_Wed', '', msg.DAY_WED),
|
||||
msg('day_Thu', '', msg.DAY_THU),
|
||||
msg('day_Fri', '', msg.DAY_FRI),
|
||||
msg('day_Sat', '', msg.DAY_SAT),
|
||||
msg('day_Sun', '', msg.DAY_SUN),
|
||||
msg('ampm_am', '', msg.AMPM_AM),
|
||||
msg('ampm_pm', '', msg.AMPM_PM),
|
||||
msg('month_Jan_short', '', msg.MONTH_JAN_SHORT),
|
||||
msg('month_Feb_short', '', msg.MONTH_FEB_SHORT),
|
||||
msg('month_Mar_short', '', msg.MONTH_MAR_SHORT),
|
||||
msg('month_Apr_short', '', msg.MONTH_APR_SHORT),
|
||||
msg('month_May_short', '', msg.MONTH_MAY_SHORT),
|
||||
msg('month_Jun_short', '', msg.MONTH_JUN_SHORT),
|
||||
msg('month_Jul_short', '', msg.MONTH_JUL_SHORT),
|
||||
msg('month_Aug_short', '', msg.MONTH_AUG_SHORT),
|
||||
msg('month_Sep_short', '', msg.MONTH_SEP_SHORT),
|
||||
msg('month_Oct_short', '', msg.MONTH_OCT_SHORT),
|
||||
msg('month_Nov_short', '', msg.MONTH_NOV_SHORT),
|
||||
msg('month_Dec_short', '', msg.MONTH_DEC_SHORT),
|
||||
msg('month_Jan', '', msg.MONTH_JAN),
|
||||
msg('month_Feb', '', msg.MONTH_FEB),
|
||||
msg('month_Mar', '', msg.MONTH_MAR),
|
||||
msg('month_Apr', '', msg.MONTH_APR),
|
||||
msg('month_May', '', msg.MONTH_MAY),
|
||||
msg('month_Jun', '', msg.MONTH_JUN),
|
||||
msg('month_Jul', '', msg.MONTH_JUL),
|
||||
msg('month_Aug', '', msg.MONTH_AUG),
|
||||
msg('month_Sep', '', msg.MONTH_SEP),
|
||||
msg('month_Oct', '', msg.MONTH_OCT),
|
||||
msg('month_Nov', '', msg.MONTH_NOV),
|
||||
msg('month_Dec', '', msg.MONTH_DEC),
|
||||
msg('today', '', msg.TODAY),
|
||||
msg('which_event', '', msg.WHICH_EVENT),
|
||||
msg('event_span', '', msg.EVENT_SPAN),
|
||||
msg('del_next_events', '', msg.DEL_NEXT_EVENTS),
|
||||
]
|
||||
# Create a label for every role added by this application
|
||||
# Add a label for the application name
|
||||
self.i18n(self.applicationName, self.applicationName)
|
||||
# Add default Appy i18n messages
|
||||
for id, default in po.appyLabels:
|
||||
self.i18n(id, default, nice=False)
|
||||
# Add a label for every role added by this application (we ensure role
|
||||
# 'Manager' was added even if not mentioned anywhere).
|
||||
self.i18n('role_Manager', 'Manager')
|
||||
for role in self.getAllUsedRoles():
|
||||
self.labels.append(msg('role_%s' % role.name,'', role.name,
|
||||
niceDefault=True))
|
||||
self.i18n('role_%s' % role.name, role.name)
|
||||
# Create basic files (config.py, etc)
|
||||
self.generateTool()
|
||||
self.generateInit()
|
||||
|
@ -570,23 +419,16 @@ class ZopeGenerator(Generator):
|
|||
f = open(initFile, 'w')
|
||||
f.write('')
|
||||
f.close()
|
||||
# Decline i18n labels into versions for child classes
|
||||
for classDescr in self.classes:
|
||||
for poMsg in classDescr.labelsToPropagate:
|
||||
for childDescr in classDescr.getChildren():
|
||||
childMsg = poMsg.clone(classDescr.name, childDescr.name)
|
||||
if childMsg not in self.labels:
|
||||
self.labels.append(childMsg)
|
||||
# Generate i18n pot file
|
||||
potFileName = '%s.pot' % self.applicationName
|
||||
if self.i18nFiles.has_key(potFileName):
|
||||
potFile = self.i18nFiles[potFileName]
|
||||
else:
|
||||
fullName = os.path.join(self.application, 'tr', potFileName)
|
||||
potFile = PoFile(fullName)
|
||||
potFile = po.PoFile(fullName)
|
||||
self.i18nFiles[potFileName] = potFile
|
||||
# We update the POT file with our list of automatically managed labels.
|
||||
removedLabels = potFile.update(self.labels, self.options.i18nClean,
|
||||
removedLabels = potFile.update(self.labels.get(),self.options.i18nClean,
|
||||
not self.options.i18nSort)
|
||||
if removedLabels:
|
||||
print 'Warning: %d messages were removed from translation ' \
|
||||
|
@ -599,7 +441,7 @@ class ZopeGenerator(Generator):
|
|||
nbOfPages = int(len(potFile.messages)/self.config.translationsPerPage)+1
|
||||
for i in range(nbOfPages):
|
||||
msgId = '%s_page_%d' % (self.translation.name, i+2)
|
||||
pageLabels.append(msg(msgId, '', 'Page %d' % (i+2)))
|
||||
pageLabels.append(po.PoMessage(msgId, '', 'Page %d' % (i+2)))
|
||||
potFile.update(pageLabels, keepExistingOrder=False)
|
||||
potFile.generate()
|
||||
# Generate i18n po files
|
||||
|
@ -611,7 +453,7 @@ class ZopeGenerator(Generator):
|
|||
poFile = self.i18nFiles[poFileName]
|
||||
else:
|
||||
fullName = os.path.join(self.application, 'tr', poFileName)
|
||||
poFile = PoFile(fullName)
|
||||
poFile = po.PoFile(fullName)
|
||||
self.i18nFiles[poFileName] = poFile
|
||||
poFile.update(potFile.messages, self.options.i18nClean,
|
||||
not self.options.i18nSort)
|
||||
|
@ -874,11 +716,8 @@ class ZopeGenerator(Generator):
|
|||
|
||||
def generateTool(self):
|
||||
'''Generates the tool that corresponds to this application.'''
|
||||
Msg = PoMessage
|
||||
# Create Tool-related i18n-related messages
|
||||
msg = Msg(self.tool.name, '', Msg.CONFIG % self.applicationName)
|
||||
self.labels.append(msg)
|
||||
|
||||
self.i18n(self.tool.name, po.CONFIG % self.applicationName, nice=False)
|
||||
# Tune the Ref field between Tool->User and Group->User
|
||||
Tool.users.klass = User
|
||||
if self.user.customized:
|
||||
|
@ -889,8 +728,8 @@ class ZopeGenerator(Generator):
|
|||
for klass in (self.user, self.group, self.translation, self.page):
|
||||
klassType = klass.name[len(self.applicationName):]
|
||||
klass.generateSchema()
|
||||
self.labels += [ Msg(klass.name, '', klassType),
|
||||
Msg('%s_plural' % klass.name,'', klass.name+'s')]
|
||||
self.i18n(klass.name, klassType, nice=False)
|
||||
self.i18n('%s_plural' % klass.name, klass.name+'s', nice=False)
|
||||
repls = self.repls.copy()
|
||||
if klass.isFolder():
|
||||
parents = 'BaseMixin, Folder'
|
||||
|
@ -904,21 +743,16 @@ class ZopeGenerator(Generator):
|
|||
self.copyFile('Class.pyt', repls, destName='%s.py' % klass.name)
|
||||
|
||||
# Before generating the Tool class, finalize it with query result
|
||||
# columns, with fields to propagate, workflow-related fields.
|
||||
# columns, search-related and import-related fields.
|
||||
for classDescr in self.getClasses(include='allButTool'):
|
||||
for fieldName, fieldType in classDescr.toolFieldsToPropagate:
|
||||
for childDescr in classDescr.getChildren():
|
||||
childFieldName = fieldName % childDescr.name
|
||||
fieldType.group = childDescr.klass.__name__
|
||||
self.tool.addField(childFieldName, fieldType)
|
||||
if classDescr.isRoot():
|
||||
# We must be able to configure query results from the tool.
|
||||
self.tool.addQueryResultColumns(classDescr)
|
||||
# Add the search-related fields.
|
||||
self.tool.addSearchRelatedFields(classDescr)
|
||||
importMean = classDescr.getCreateMean('Import')
|
||||
if importMean:
|
||||
self.tool.addImportRelatedFields(classDescr)
|
||||
if not classDescr.isRoot(): continue
|
||||
# We must be able to configure query results from the tool.
|
||||
self.tool.addQueryResultColumns(classDescr)
|
||||
# Add the search-related fields.
|
||||
self.tool.addSearchRelatedFields(classDescr)
|
||||
importMean = classDescr.getCreateMean('Import')
|
||||
if importMean:
|
||||
self.tool.addImportRelatedFields(classDescr)
|
||||
self.tool.generateSchema()
|
||||
|
||||
# Generate the Tool class
|
||||
|
@ -939,37 +773,27 @@ class ZopeGenerator(Generator):
|
|||
baseClass = isFolder and 'Folder' or 'SimpleItem'
|
||||
icon = isFolder and 'folder.gif' or 'object.gif'
|
||||
parents = 'BaseMixin, %s' % baseClass
|
||||
classDoc = classDescr.klass.__doc__ or 'Appy class.'
|
||||
classDoc = k.__doc__ or 'Appy class.'
|
||||
repls = self.repls.copy()
|
||||
classDescr.generateSchema()
|
||||
repls.update({
|
||||
'parents': parents, 'className': classDescr.klass.__name__,
|
||||
'parents': parents, 'className': k.__name__,
|
||||
'genClassName': classDescr.name, 'baseMixin':'BaseMixin',
|
||||
'classDoc': classDoc, 'applicationName': self.applicationName,
|
||||
'methods': classDescr.methods, 'icon':icon})
|
||||
fileName = '%s.py' % classDescr.name
|
||||
# Create i18n labels (class name and plural form)
|
||||
poMsg = PoMessage(classDescr.name, '', classDescr.klass.__name__)
|
||||
poMsg.produceNiceDefault()
|
||||
self.labels.append(poMsg)
|
||||
poMsgPl = PoMessage('%s_plural' % classDescr.name, '',
|
||||
classDescr.klass.__name__+'s')
|
||||
poMsgPl.produceNiceDefault()
|
||||
self.labels.append(poMsgPl)
|
||||
self.i18n(classDescr.name, k.__name__)
|
||||
self.i18n('%s_plural' % classDescr.name, k.__name__+'s')
|
||||
# Create i18n labels for searches
|
||||
for search in classDescr.getSearches(classDescr.klass):
|
||||
searchLabel = '%s_search_%s' % (classDescr.name, search.name)
|
||||
labels = [searchLabel, '%s_descr' % searchLabel]
|
||||
if search.group:
|
||||
grpLabel = '%s_searchgroup_%s' % (classDescr.name, search.group)
|
||||
labels += [grpLabel, '%s_descr' % grpLabel]
|
||||
for label in labels:
|
||||
default = ' '
|
||||
if label == searchLabel: default = search.name
|
||||
poMsg = PoMessage(label, '', default)
|
||||
poMsg.produceNiceDefault()
|
||||
if poMsg not in self.labels:
|
||||
self.labels.append(poMsg)
|
||||
for search in classDescr.getSearches(k):
|
||||
label = '%s_search_%s' % (classDescr.name, search.name)
|
||||
self.i18n(label, search.name)
|
||||
self.i18n('%s_descr' % label, ' ', nice=False)
|
||||
# Generate labels for groups of searches
|
||||
if search.group and not search.group.label:
|
||||
search.group.generateLabels(self.labels, classDescr, set(),
|
||||
forSearch=True)
|
||||
# Generate the resulting Zope class.
|
||||
self.copyFile('Class.pyt', repls, destName=fileName)
|
||||
|
||||
|
@ -983,31 +807,23 @@ class ZopeGenerator(Generator):
|
|||
# Add i18n messages for states
|
||||
for name in dir(wfDescr.klass):
|
||||
if not isinstance(getattr(wfDescr.klass, name), gen.State): continue
|
||||
poMsg = PoMessage('%s_%s' % (wfName, name), '', name)
|
||||
poMsg.produceNiceDefault()
|
||||
self.labels.append(poMsg)
|
||||
self.i18n('%s_%s' % (wfName, name), name)
|
||||
# Add i18n messages for transitions
|
||||
for name in dir(wfDescr.klass):
|
||||
transition = getattr(wfDescr.klass, name)
|
||||
if not isinstance(transition, gen.Transition): continue
|
||||
if transition.show:
|
||||
poMsg = PoMessage('%s_%s' % (wfName, name), '', name)
|
||||
poMsg.produceNiceDefault()
|
||||
self.labels.append(poMsg)
|
||||
self.i18n('%s_%s' % (wfName, name), name)
|
||||
if transition.show and transition.confirm:
|
||||
# We need to generate a label for the message that will be shown
|
||||
# in the confirm popup.
|
||||
label = '%s_%s_confirm' % (wfName, name)
|
||||
poMsg = PoMessage(label, '', PoMessage.CONFIRM)
|
||||
self.labels.append(poMsg)
|
||||
self.i18n('%s_%s_confirm'%(wfName, name),po.CONFIRM, nice=False)
|
||||
if transition.notify:
|
||||
# Appy will send a mail when this transition is triggered.
|
||||
# So we need 2 i18n labels: one for the mail subject and one for
|
||||
# the mail body.
|
||||
subjectLabel = '%s_%s_mail_subject' % (wfName, name)
|
||||
poMsg = PoMessage(subjectLabel, '', PoMessage.EMAIL_SUBJECT)
|
||||
self.labels.append(poMsg)
|
||||
bodyLabel = '%s_%s_mail_body' % (wfName, name)
|
||||
poMsg = PoMessage(bodyLabel, '', PoMessage.EMAIL_BODY)
|
||||
self.labels.append(poMsg)
|
||||
self.i18n('%s_%s_mail_subject' % (wfName, name),
|
||||
po.EMAIL_SUBJECT, nice=False)
|
||||
self.i18n('%s_%s_mail_body' % (wfName, name),
|
||||
po.EMAIL_BODY, nice=False)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -127,7 +127,7 @@ class Table(LayoutElement):
|
|||
# We need to create a Table instance from another Table instance,
|
||||
# given in p_other. In this case, we ignore previous params.
|
||||
if derivedType != None:
|
||||
# We will not simply clone p_other. If p_derivedType is:
|
||||
# We will not simply mimic p_other. If p_derivedType is:
|
||||
# - "view", p_derivedFrom is an "edit" layout, and we must
|
||||
# create the corresponding "view" layout;
|
||||
# - "cell", p_derivedFrom is a "view" layout, and we must
|
||||
|
|
|
@ -675,7 +675,7 @@ class ToolMixin(BaseMixin):
|
|||
def getSearches(self, contentType):
|
||||
'''Returns an object with 2 attributes:
|
||||
* "searches" stores the searches that are defined for p_contentType;
|
||||
* "default" stores the search defined as default one.
|
||||
* "default" stores the search defined as the default one.
|
||||
Every item representing a search is a dict containing info about a
|
||||
search or about a group of searches.
|
||||
'''
|
||||
|
@ -685,25 +685,27 @@ class ToolMixin(BaseMixin):
|
|||
visitedGroups = {} # Names of already visited search groups
|
||||
for search in ClassDescriptor.getSearches(appyClass):
|
||||
# Determine first group label, we will need it.
|
||||
groupLabel = ''
|
||||
groupLabel = None
|
||||
groupName = None
|
||||
if search.group:
|
||||
groupLabel = '%s_searchgroup_%s' % (contentType, search.group)
|
||||
groupName = search.group.name
|
||||
groupLabel = '%s_searchgroup_%s' % (contentType, groupName)
|
||||
# Add an item representing the search group if relevant
|
||||
if search.group and (search.group not in visitedGroups):
|
||||
group = {'name': search.group, 'isGroup': True,
|
||||
if groupName and (groupName not in visitedGroups):
|
||||
group = {'name': groupName, 'isGroup': True,
|
||||
'labelId': groupLabel, 'searches': [],
|
||||
'label': self.translate(groupLabel),
|
||||
'descr': self.translate('%s_descr' % groupLabel),
|
||||
}
|
||||
searches.append(group)
|
||||
visitedGroups[search.group] = group
|
||||
visitedGroups[groupName] = group
|
||||
# Add the search itself
|
||||
searchLabel = '%s_search_%s' % (contentType, search.name)
|
||||
dSearch = {'name': search.name, 'isGroup': False,
|
||||
'label': self.translate(searchLabel),
|
||||
'descr': self.translate('%s_descr' % searchLabel)}
|
||||
if search.group:
|
||||
visitedGroups[search.group]['searches'].append(dSearch)
|
||||
if groupName:
|
||||
visitedGroups[groupName]['searches'].append(dSearch)
|
||||
else:
|
||||
searches.append(dSearch)
|
||||
if search.default:
|
||||
|
|
415
gen/po.py
415
gen/po.py
|
@ -21,197 +21,204 @@ msgstr ""
|
|||
fallbacks = {'en': 'en-us en-ca',
|
||||
'fr': 'fr-be fr-ca fr-lu fr-mc fr-ch fr-fr'}
|
||||
|
||||
# Standard Appy labels with their default value in english ---------------------
|
||||
appyLabels = [
|
||||
('app_name', 'Appy'),
|
||||
('workflow_state', 'state'),
|
||||
('workflow_comment', 'Optional comment'),
|
||||
('appy_title', 'Title'),
|
||||
('data_change', 'Data change'),
|
||||
('modified_field', 'Modified field'),
|
||||
('previous_value', 'Previous value'),
|
||||
('phase', 'phase'),
|
||||
('choose_a_value', ' - '),
|
||||
('choose_a_doc', '[ Documents ]'),
|
||||
('min_ref_violated', 'You must choose more elements here.'),
|
||||
('max_ref_violated', 'Too much elements are selected here.'),
|
||||
('no_ref', 'No object.'),
|
||||
('add_ref', 'Add a new one'),
|
||||
('action_ok', 'The action has been successfully executed.'),
|
||||
('action_ko', 'A problem occurred while executing the action.'),
|
||||
('move_up', 'Move up'),
|
||||
('move_down', 'Move down'),
|
||||
('query_create', 'create'),
|
||||
('query_import', 'import'),
|
||||
('query_no_result', 'Nothing to see for the moment.'),
|
||||
('query_consult_all', 'consult all'),
|
||||
('import_title', 'Importing data into your application'),
|
||||
('import_show_hide', 'Show / hide alreadly imported elements.'),
|
||||
('import_already', 'Already imported.'),
|
||||
('import_many', 'Import selected elements'),
|
||||
('import_done', 'Import terminated successfully.'),
|
||||
('search_title', 'Advanced search'),
|
||||
('search_button', 'Search'),
|
||||
('search_objects', 'Search objects of this type.'),
|
||||
('search_results', 'Search results'),
|
||||
('search_results_descr', ' '),
|
||||
('search_new', 'New search'),
|
||||
('search_from', 'From'),
|
||||
('search_to', 'to'),
|
||||
('search_or', 'or'),
|
||||
('search_and', 'and'),
|
||||
('ref_invalid_index', 'No move occurred: please specify a valid number.'),
|
||||
('bad_long', 'An integer value is expected; do not enter any space.'),
|
||||
('bad_float', 'A floating-point number is expected; use the dot as decimal ' \
|
||||
'separator, not a comma; do not enter any space.'),
|
||||
('bad_date', 'Please specify a valid date.'),
|
||||
('bad_email', 'Please enter a valid email.'),
|
||||
('bad_url', 'Please enter a valid URL.'),
|
||||
('bad_alphanumeric', 'Please enter a valid alphanumeric value.'),
|
||||
('bad_select_value', 'The value is not among possible values for this field.'),
|
||||
('select_delesect', '(Un)select all'),
|
||||
('no_elem_selected', 'You must select at least one element.'),
|
||||
('object_edit', 'Edit'),
|
||||
('object_delete', 'Delete'),
|
||||
('object_unlink', 'Unlink'),
|
||||
('delete_confirm', 'Are you sure you want to delete this element?'),
|
||||
('unlink_confirm', 'Are you sure you want to unlink this element?'),
|
||||
('delete_done', 'The element has been deleted.'),
|
||||
('unlink_done', 'The element has been unlinked.'),
|
||||
('goto_first', 'Go to top'),
|
||||
('goto_previous', 'Go to previous'),
|
||||
('goto_next', 'Go to next'),
|
||||
('goto_last', 'Go to end'),
|
||||
('goto_source', 'Go back'),
|
||||
('whatever', 'Whatever'),
|
||||
('yes', 'Yes'),
|
||||
('no', 'No'),
|
||||
('field_required', 'Please fill this field.'),
|
||||
('field_invalid', 'Please fill or correct this.'),
|
||||
('file_required', 'Please select a file.'),
|
||||
('image_required', 'The uploaded file must be an image.'),
|
||||
('odt', 'ODT'),
|
||||
('pdf', 'PDF'),
|
||||
('doc', 'DOC'),
|
||||
('rtf', 'RTF'),
|
||||
('front_page_text', 'Welcome to this Appy-powered site.'),
|
||||
('captcha_text', 'Please type "${text}" (without the double quotes) in the ' \
|
||||
'field besides, but without the character at position ' \
|
||||
'${number}.'),
|
||||
('bad_captcha', 'The code was not correct. Please try again.'),
|
||||
('app_login', 'Login'),
|
||||
('app_connect', 'Log in'),
|
||||
('app_logout', 'Logout'),
|
||||
('app_password', 'Password'),
|
||||
('app_home', 'Home'),
|
||||
('login_reserved', 'This login is reserved.'),
|
||||
('login_in_use', 'This login is already in use.'),
|
||||
('login_ko', 'Welcome! You are now logged in.'),
|
||||
('login_ok', 'Login failed.'),
|
||||
('password_too_short', 'Passwords must contain at least ${nb} characters.'),
|
||||
('passwords_mismatch', 'Passwords do not match.'),
|
||||
('object_save', 'Save'),
|
||||
('object_saved', 'Changes saved.'),
|
||||
('validation_error', 'Please correct the indicated errors.'),
|
||||
('object_cancel', 'Cancel'),
|
||||
('object_canceled', 'Changes canceled.'),
|
||||
('enable_cookies', 'You must enable cookies before you can log in.'),
|
||||
('page_previous', 'Previous page'),
|
||||
('page_next', 'Next page'),
|
||||
('forgot_password', 'Forgot password?'),
|
||||
('ask_password_reinit', 'Ask new password'),
|
||||
('wrong_password_reinit', 'Something went wrong. First possibility: you ' \
|
||||
'have already clicked on the link (maybe have you double-clicked?) ' \
|
||||
'and your password has already been re-initialized. Please check ' \
|
||||
'that you haven\'t received your new password in another email. ' \
|
||||
'Second possibility: the link that you received in your mailer was ' \
|
||||
'splitted on several lines. In this case, please re-type the link in ' \
|
||||
'one single line and retry. Third possibility: you have waited too ' \
|
||||
'long and your request has expired, or a technical error occurred. ' \
|
||||
'In this case, please try again to ask a new password from the start.'),
|
||||
('reinit_mail_sent', 'A mail has been sent to you. Please follow the ' \
|
||||
'instructions from this email.'),
|
||||
('reinit_password', 'Password re-initialisation'),
|
||||
('reinit_password_body', 'Hello,<br/><br/>A password re-initialisation ' \
|
||||
'has been requested, tied to this email address, for the website ' \
|
||||
'${siteUrl}. If you are not at the origin of this request, please ' \
|
||||
'ignore this email. Else, click on the link below to receive a new ' \
|
||||
'password.<br/><br/>${url}'),
|
||||
('new_password', 'Your new password'),
|
||||
('new_password_body', 'Hello,<br/><br/>The new password you have ' \
|
||||
'requested for website ${siteUrl} is ${password}<br/>' \
|
||||
'<br/>Best regards.'),
|
||||
('new_password_sent', 'Your new password has been sent to you by email.'),
|
||||
('last_user_access', 'Last access'),
|
||||
('object_history', 'History'),
|
||||
('object_created_by', 'By'),
|
||||
('object_created_on', 'On'),
|
||||
('object_modified_on', 'Last updated on'),
|
||||
('object_action', 'Action'),
|
||||
('object_author', 'Author'),
|
||||
('action_date', 'Date'),
|
||||
('action_comment', 'Comment'),
|
||||
('day_Mon_short', 'Mon'),
|
||||
('day_Tue_short', 'Tue'),
|
||||
('day_Wed_short', 'Wed'),
|
||||
('day_Thu_short', 'Thu'),
|
||||
('day_Fri_short', 'Fri'),
|
||||
('day_Sat_short', 'Sat'),
|
||||
('day_Sun_short', 'Sun'),
|
||||
('day_Mon', 'Monday'),
|
||||
('day_Tue', 'Tuesday'),
|
||||
('day_Wed', 'Wednesday'),
|
||||
('day_Thu', 'Thursday'),
|
||||
('day_Fri', 'Friday'),
|
||||
('day_Sat', 'Saturday'),
|
||||
('day_Sun', 'Sunday'),
|
||||
('ampm_am', 'AM'),
|
||||
('ampm_pm', 'PM'),
|
||||
('month_Jan_short', 'Jan'),
|
||||
('month_Feb_short', 'Feb'),
|
||||
('month_Mar_short', 'Mar'),
|
||||
('month_Apr_short', 'Apr'),
|
||||
('month_May_short', 'May'),
|
||||
('month_Jun_short', 'Jun'),
|
||||
('month_Jul_short', 'Jul'),
|
||||
('month_Aug_short', 'Aug'),
|
||||
('month_Sep_short', 'Sep'),
|
||||
('month_Oct_short', 'Oct'),
|
||||
('month_Nov_short', 'Nov'),
|
||||
('month_Dec_short', 'Dec'),
|
||||
('month_Jan', 'January'),
|
||||
('month_Feb', 'February'),
|
||||
('month_Mar', 'March'),
|
||||
('month_Apr', 'April'),
|
||||
('month_May', 'May'),
|
||||
('month_Jun', 'June'),
|
||||
('month_Jul', 'July'),
|
||||
('month_Aug', 'Augustus'),
|
||||
('month_Sep', 'September'),
|
||||
('month_Oct', 'October'),
|
||||
('month_Nov', 'November'),
|
||||
('month_Dec', 'December'),
|
||||
('today', 'Today'),
|
||||
('which_event', 'Which event type would you like to create?'),
|
||||
('event_span', 'Extend the event on the following number of days (leave ' \
|
||||
'blank to create an event on the current day only):'),
|
||||
('del_next_events', 'Also delete successive events of the same type.'),
|
||||
]
|
||||
|
||||
# Some default values for labels whose ids are not fixed (so they can't be
|
||||
# included in the previous variable).
|
||||
CONFIG = "Configuration panel for product '%s'"
|
||||
# The following messages (starting with MSG_) correspond to tool
|
||||
# attributes added for every gen-class (warning: the message IDs correspond
|
||||
# to MSG_<attributePrefix>).
|
||||
MSG_podTemplate = "POD template for field '%s'"
|
||||
MSG_formats = "Output format(s) for field '%s'"
|
||||
MSG_resultColumns = "Columns to display while showing query results"
|
||||
MSG_enableAdvancedSearch = "Enable advanced search"
|
||||
MSG_numberOfSearchColumns = "Number of search columns"
|
||||
MSG_searchFields = "Search fields"
|
||||
POD_ASKACTION = 'Trigger related action'
|
||||
EMAIL_SUBJECT = '${siteTitle} - Action \\"${transitionName}\\" has been ' \
|
||||
'performed on element entitled \\"${objectTitle}\\".'
|
||||
EMAIL_BODY = 'You can consult this element at ${objectUrl}.'
|
||||
CONFIRM = 'Are you sure ?'
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class PoMessage:
|
||||
'''Represents a i18n message (po format).'''
|
||||
CONFIG = "Configuration panel for product '%s'"
|
||||
APP_NAME = "Appy"
|
||||
# The following messages (starting with MSG_) correspond to tool
|
||||
# attributes added for every gen-class (warning: the message IDs correspond
|
||||
# to MSG_<attributePrefix>).
|
||||
MSG_podTemplate = "POD template for field '%s'"
|
||||
MSG_formats = "Output format(s) for field '%s'"
|
||||
MSG_resultColumns = "Columns to display while showing query results"
|
||||
MSG_enableAdvancedSearch = "Enable advanced search"
|
||||
MSG_numberOfSearchColumns = "Number of search columns"
|
||||
MSG_searchFields = "Search fields"
|
||||
POD_ASKACTION = 'Trigger related action'
|
||||
REF_NO = 'No object.'
|
||||
REF_ADD = 'Add a new one'
|
||||
REF_MOVE_UP = 'Move up'
|
||||
REF_MOVE_DOWN = 'Move down'
|
||||
REF_INVALID_INDEX = 'No move occurred: please specify a valid number.'
|
||||
QUERY_CREATE = 'create'
|
||||
QUERY_IMPORT = 'import'
|
||||
QUERY_CONSULT_ALL = 'consult all'
|
||||
QUERY_NO_RESULT = 'Nothing to see for the moment.'
|
||||
IMPORT_TITLE = 'Importing data into your application'
|
||||
IMPORT_SHOW_HIDE = 'Show / hide alreadly imported elements.'
|
||||
IMPORT_ALREADY = 'Already imported.'
|
||||
IMPORT_MANY = 'Import selected elements'
|
||||
IMPORT_DONE = 'Import terminated successfully.'
|
||||
SEARCH_TITLE = 'Advanced search'
|
||||
SEARCH_BUTTON = 'Search'
|
||||
SEARCH_OBJECTS = 'Search objects of this type.'
|
||||
SEARCH_RESULTS = 'Search results'
|
||||
SEARCH_NEW = 'New search'
|
||||
SEARCH_FROM = 'From'
|
||||
SEARCH_TO = 'to'
|
||||
SEARCH_OR = 'or'
|
||||
SEARCH_AND = 'and'
|
||||
WORKFLOW_COMMENT = 'Optional comment'
|
||||
WORKFLOW_STATE = 'state'
|
||||
APPY_TITLE = 'Title'
|
||||
DATA_CHANGE = 'Data change'
|
||||
MODIFIED_FIELD = 'Modified field'
|
||||
PREVIOUS_VALUE = 'Previous value'
|
||||
PHASE = 'phase'
|
||||
CHOOSE_A_VALUE = ' - '
|
||||
CHOOSE_A_DOC = '[ Documents ]'
|
||||
MIN_REF_VIOLATED = 'You must choose more elements here.'
|
||||
MAX_REF_VIOLATED = 'Too much elements are selected here.'
|
||||
BAD_LONG = 'An integer value is expected; do not enter any space.'
|
||||
BAD_FLOAT = 'A floating-point number is expected; use the dot as decimal ' \
|
||||
'separator, not a comma; do not enter any space.'
|
||||
BAD_DATE = 'Please specify a valid date.'
|
||||
BAD_EMAIL = 'Please enter a valid email.'
|
||||
BAD_URL = 'Please enter a valid URL.'
|
||||
BAD_ALPHANUMERIC = 'Please enter a valid alphanumeric value.'
|
||||
BAD_SELECT_VALUE = 'The value is not among possible values for this field.'
|
||||
ACTION_OK = 'The action has been successfully executed.'
|
||||
ACTION_KO = 'A problem occurred while executing the action.'
|
||||
FRONT_PAGE_TEXT = 'Welcome to this Appy-powered site.'
|
||||
EMAIL_SUBJECT = '${siteTitle} - Action \\"${transitionName}\\" has been ' \
|
||||
'performed on element entitled \\"${objectTitle}\\".'
|
||||
EMAIL_BODY = 'You can consult this element at ${objectUrl}.'
|
||||
SELECT_DESELECT = '(Un)select all'
|
||||
NO_SELECTION = 'You must select at least one element.'
|
||||
EDIT = 'Edit'
|
||||
DELETE = 'Delete'
|
||||
UNLINK = 'Unlink'
|
||||
DELETE_CONFIRM = 'Are you sure you want to delete this element?'
|
||||
UNLINK_CONFIRM = 'Are you sure you want to unlink this element?'
|
||||
DELETE_DONE = 'The element has been deleted.'
|
||||
UNLINK_DONE = 'The element has been unlinked.'
|
||||
GOTO_FIRST = 'Go to top'
|
||||
GOTO_PREVIOUS = 'Go to previous'
|
||||
GOTO_NEXT = 'Go to next'
|
||||
GOTO_LAST = 'Go to end'
|
||||
GOTO_SOURCE = 'Go back'
|
||||
WHATEVER = 'Whatever'
|
||||
CONFIRM = 'Are you sure ?'
|
||||
YES = 'Yes'
|
||||
NO = 'No'
|
||||
FIELD_REQUIRED = 'Please fill this field.'
|
||||
FILE_REQUIRED = 'Please select a file.'
|
||||
FIELD_INVALID = 'Please fill or correct this.'
|
||||
IMAGE_REQUIRED = 'The uploaded file must be an image.'
|
||||
FORMAT_ODT = 'ODT'
|
||||
FORMAT_PDF = 'PDF'
|
||||
FORMAT_DOC = 'DOC'
|
||||
FORMAT_RTF = 'RTF'
|
||||
CAPTCHA_TEXT = 'Please type "${text}" (without the double quotes) in the ' \
|
||||
'field besides, but without the character at position ' \
|
||||
'${number}.'
|
||||
BAD_CAPTCHA = 'The code was not correct. Please try again.'
|
||||
LOGIN = 'Login'
|
||||
CONNECT = 'Log in'
|
||||
PASSWORD = 'Password'
|
||||
LOGOUT = 'Logout'
|
||||
HOME = 'Home'
|
||||
LOGIN_RESERVED = 'This login is reserved.'
|
||||
LOGIN_IN_USE = 'This login is already in use.'
|
||||
LOGIN_OK = 'Welcome! You are now logged in.'
|
||||
LOGIN_KO = 'Login failed.'
|
||||
PASSWORD_TOO_SHORT = 'Passwords must contain at least ${nb} characters.'
|
||||
PASSWORDS_MISMATCH = 'Passwords do not match.'
|
||||
SAVE = 'Save'
|
||||
SAVED = 'Changes saved.'
|
||||
ERROR = 'Please correct the indicated errors.'
|
||||
CANCEL = 'Cancel'
|
||||
CANCELED = 'Changes canceled.'
|
||||
ENABLE_COOKIES = 'You must enable cookies before you can log in.'
|
||||
PAGE_PREVIOUS = 'Previous page'
|
||||
PAGE_NEXT = 'Next page'
|
||||
FORGOT_PASSWORD = 'Forgot password?'
|
||||
ASK_PASSWORD_REINIT = 'Ask new password'
|
||||
WRONG_PASSWORD_REINIT = 'Something went wrong. First possibility: you ' \
|
||||
'have already clicked on the link (maybe have you double-clicked?) ' \
|
||||
'and your password has already been re-initialized. Please check ' \
|
||||
'that you haven\'t received your new password in another email. ' \
|
||||
'Second possibility: the link that you received in your mailer was ' \
|
||||
'splitted on several lines. In this case, please re-type the link in ' \
|
||||
'one single line and retry. Third possibility: you have waited too ' \
|
||||
'long and your request has expired, or a technical error occurred. ' \
|
||||
'In this case, please try again to ask a new password from the start.'
|
||||
REINIT_MAIL_SENT = 'A mail has been sent to you. Please follow the ' \
|
||||
'instructions from this email.'
|
||||
REINIT_PASSWORD = 'Password re-initialisation'
|
||||
REINIT_PASSWORD_BODY = 'Hello,<br/><br/>A password re-initialisation ' \
|
||||
'has been requested, tied to this email address, for the website ' \
|
||||
'${siteUrl}. If you are not at the origin of this request, please ' \
|
||||
'ignore this email. Else, click on the link below to receive a new ' \
|
||||
'password.<br/><br/>${url}'
|
||||
NEW_PASSWORD = 'Your new password'
|
||||
NEW_PASSWORD_BODY = 'Hello,<br/><br/>The new password you have ' \
|
||||
'requested for website ${siteUrl} is ${password}<br/>' \
|
||||
'<br/>Best regards.'
|
||||
NEW_PASSWORD_SENT = 'Your new password has been sent to you by email.'
|
||||
LAST_USER_ACCESS = 'Last access'
|
||||
OBJECT_HISTORY = 'History'
|
||||
OBJECT_CREATED_BY = 'By'
|
||||
OBJECT_CREATED_ON = 'On'
|
||||
OBJECT_MODIFIED_ON = 'Last updated on'
|
||||
OBJECT_ACTION = 'Action'
|
||||
OBJECT_AUTHOR = 'Author'
|
||||
ACTION_DATE = 'Date'
|
||||
ACTION_COMMENT = 'Comment'
|
||||
DAY_MON_SHORT = 'Mon'
|
||||
DAY_TUE_SHORT = 'Tue'
|
||||
DAY_WED_SHORT = 'Wed'
|
||||
DAY_THU_SHORT = 'Thu'
|
||||
DAY_FRI_SHORT = 'Fri'
|
||||
DAY_SAT_SHORT = 'Sat'
|
||||
DAY_SUN_SHORT = 'Sun'
|
||||
DAY_MON = 'Monday'
|
||||
DAY_TUE = 'Tuesday'
|
||||
DAY_WED = 'Wednesday'
|
||||
DAY_THU = 'Thursday'
|
||||
DAY_FRI = 'Friday'
|
||||
DAY_SAT = 'Saturday'
|
||||
DAY_SUN = 'Sunday'
|
||||
AMPM_AM = 'AM'
|
||||
AMPM_PM = 'PM'
|
||||
MONTH_JAN_SHORT = 'Jan'
|
||||
MONTH_FEB_SHORT = 'Feb'
|
||||
MONTH_MAR_SHORT = 'Mar'
|
||||
MONTH_APR_SHORT = 'Apr'
|
||||
MONTH_MAY_SHORT = 'May'
|
||||
MONTH_JUN_SHORT = 'Jun'
|
||||
MONTH_JUL_SHORT = 'Jul'
|
||||
MONTH_AUG_SHORT = 'Aug'
|
||||
MONTH_SEP_SHORT = 'Sep'
|
||||
MONTH_OCT_SHORT = 'Oct'
|
||||
MONTH_NOV_SHORT = 'Nov'
|
||||
MONTH_DEC_SHORT = 'Dec'
|
||||
MONTH_JAN = 'January'
|
||||
MONTH_FEB = 'February'
|
||||
MONTH_MAR = 'March'
|
||||
MONTH_APR = 'April'
|
||||
MONTH_MAY = 'May'
|
||||
MONTH_JUN = 'June'
|
||||
MONTH_JUL = 'July'
|
||||
MONTH_AUG = 'Augustus'
|
||||
MONTH_SEP = 'September'
|
||||
MONTH_OCT = 'October'
|
||||
MONTH_NOV = 'November'
|
||||
MONTH_DEC = 'December'
|
||||
TODAY = 'Today'
|
||||
WHICH_EVENT = 'Which event type would you like to create?'
|
||||
EVENT_SPAN = 'Extend the event on the following number of days (leave ' \
|
||||
'blank to create an event on the current day only):'
|
||||
DEL_NEXT_EVENTS = 'Also delete successive events of the same type.'
|
||||
|
||||
def __init__(self, id, msg, default, fuzzy=False, comments=[],
|
||||
niceDefault=False):
|
||||
self.id = id
|
||||
|
@ -290,22 +297,30 @@ class PoMessage:
|
|||
return '<i18n msg id="%s", msg="%s", default="%s">' % \
|
||||
(self.id, self.msg, self.default)
|
||||
|
||||
def __cmp__(self, other):
|
||||
return cmp(self.id, other.id)
|
||||
|
||||
def clone(self, oldPrefix, newPrefix):
|
||||
'''This returns a cloned version of myself. The clone has another id
|
||||
that includes p_newPrefix.'''
|
||||
if self.id.startswith(oldPrefix):
|
||||
newId = newPrefix + self.id[len(oldPrefix):]
|
||||
else:
|
||||
newId = '%s_%s' % (newPrefix, self.id.split('_', 1)[1])
|
||||
return PoMessage(newId, self.msg, self.default, comments=self.comments)
|
||||
|
||||
def getMessage(self):
|
||||
'''Returns self.msg, but with some replacements.'''
|
||||
return self.msg.replace('<br/>', '\n').replace('\\"', '"')
|
||||
|
||||
class PoMessages:
|
||||
'''A list of po messages under construction.'''
|
||||
def __init__(self):
|
||||
# The list of messages
|
||||
self.messages = []
|
||||
# A dict of message ids, useful for efficiently checking if an id is
|
||||
# already in the list or not.
|
||||
self.ids = {}
|
||||
|
||||
def append(self, id, default, nice=True):
|
||||
'''Creates a new PoMessage and adds it to self.messages. If p_nice is
|
||||
True, it produces a nice default value for the message.'''
|
||||
# Avoir creating duplicate ids
|
||||
if id in self.ids: return
|
||||
message = PoMessage(id, '', default, niceDefault=nice)
|
||||
self.messages.append(message)
|
||||
self.ids[id] = True
|
||||
|
||||
def get(self): return self.messages
|
||||
|
||||
class PoHeader:
|
||||
def __init__(self, name, value):
|
||||
self.name = name
|
||||
|
|
Loading…
Reference in a new issue