diff --git a/product/ERP5Type/Accessor/Translation.py b/product/ERP5Type/Accessor/Translation.py index d943d97c69b6abea5f7906817b50a86dde30a00c..37c4b7fe0838947087253140a52510ebe8f05c36 100644 --- a/product/ERP5Type/Accessor/Translation.py +++ b/product/ERP5Type/Accessor/Translation.py @@ -26,41 +26,67 @@ # ############################################################################## -from Base import func_code, ATTRIBUTE_PREFIX, evaluateTales, Getter as BaseGetter from zLOG import LOG from Products.ERP5Type.PsycoWrapper import psyco from Acquisition import aq_base from Products.CMFCore.utils import getToolByName +from Products.ERP5Type.Accessor.Base import func_code, ATTRIBUTE_PREFIX, evaluateTales, Getter as BaseGetter +from Products.ERP5Type.Accessor import Accessor, AcquiredProperty +from Products.ERP5Type.Accessor.TypeDefinition import type_definition + + +TRANSLATION_DOMAIN_CONTENT_TRANSLATION = 'content_translation' + + class TranslatedPropertyGetter(BaseGetter): """ Get the translated property """ - _need__name__=1 - # This can be called from the Web func_code = func_code() - func_code.co_varnames = ('self', ) + func_code.co_varnames = ('self',) func_code.co_argcount = 1 func_defaults = () - def __init__(self, id, key, warning=0): + def __init__(self, id, key, property_id, property_type, language, default=None, warning=0): self._id = id self.__name__ = id - self._key = key - self._original_key = key.replace('translated_', '') + self._property_id = property_id + self._null = type_definition[property_type]['null'] + self._language = language + self._default = default self._warning = warning def __call__(self, instance, *args, **kw): if self._warning: LOG("ERP5Type Deprecated Getter Id:",0, self._id) - domain = instance.getProperty('%s_translation_domain' % - self._original_key) - value = instance.getProperty(self._original_key) - if domain == '' or (value in ('', None)): - return value - localizer = getToolByName(instance, 'Localizer') - return localizer[domain].gettext(unicode(value, 'utf8')).encode('utf8') + domain = instance.getProperty('%s_translation_domain' % self._property_id) + + if domain==TRANSLATION_DOMAIN_CONTENT_TRANSLATION: + if len(args) > 0: + default = args[0] + else: + default = self._default + + if self._language is None: + language = kw.get('language') or getToolByName(instance, 'Localizer').get_selected_language() + else: + language = self._language + try: + return instance.getPropertyTranslation(self._property_id, language) + except KeyError: + return default + else: + value = instance.getProperty(self._property_id) + if domain == '' or (value in ('', None)): + return value + localizer = getToolByName(instance, 'Localizer') + message_catalog = getattr(localizer, domain, None) + if message_catalog is not None: + return message_catalog.gettext(unicode(value, 'utf8'), lang=self._language).encode('utf8') + else: + return value psyco.bind(__call__) @@ -80,9 +106,7 @@ class PropertyTranslationDomainGetter(BaseGetter): def __init__(self, id, key, property_type, default=None, storage_id=None): self._id = id self.__name__ = id - self._key = key self._original_key = key.replace('_translation_domain', '') - self._property_type = property_type self._default = default if storage_id is None: storage_id = "%s%s" % (ATTRIBUTE_PREFIX, key) @@ -126,3 +150,68 @@ class PropertyTranslationDomainGetter(BaseGetter): psyco.bind(__call__) +class TranslationPropertySetter(Accessor.Accessor): + """ + Set a translation into language-property pair dict. + """ + _need__name__=1 + + # Generic Definition of Method Object + # This is required to call the method form the Web + # More information at http://www.zope.org/Members/htrd/howto/FunctionTemplate + func_code = func_code() + func_code.co_varnames = ('self', 'value') + func_code.co_argcount = 2 + func_defaults = () + + def __init__(self, id, key, property_id, property_type, language): + self._id = id + self.__name__ = id + self._property_id = property_id + self._language = language + self._cast = type_definition[property_type]['cast'] + self._null = type_definition[property_type]['null'] + + def __call__(self, instance, *args, **kw): + value = args[0] + modified_object_list = [] + + domain = instance.getProperty('%s_translation_domain' % self._property_id) + if domain==TRANSLATION_DOMAIN_CONTENT_TRANSLATION: + if value in self._null: + instance.deletePropertyTranslation(self._property_id, self._language) + else: + original_property_value = instance.getProperty(self._property_id) + instance.setPropertyTranslation(self._property_id, self._language, original_property_value, self._cast(args[0])) + modified_object_list.append(instance) + else: + pass + #raise RuntimeError, 'The property %s.%s is not writable.' % (instance.portal_type, self._property_id) + return modified_object_list + + +class AcquiredPropertyGetter(AcquiredProperty.Getter): + + def __call__(self, instance, *args, **kw): + if len(args) > 0: + default = args[0] + else: + default = None + value = instance._getDefaultAcquiredProperty(self._key, None, self._null, + base_category=self._acquisition_base_category, + portal_type=self._acquisition_portal_type, + accessor_id=self._acquisition_accessor_id, + copy_value=self._acquisition_copy_value, + mask_value=self._acquisition_mask_value, + sync_value=self._acquisition_sync_value, + storage_id=self._storage_id, + alt_accessor_id=self._alt_accessor_id, + acquisition_object_id=self._acquisition_object_id, + is_list_type=self._is_list_type, + is_tales_type=self._is_tales_type, + checked_permission=kw.get('checked_permission', None) + ) + if value is not None: + return value.getProperty(self._acquired_property, default, **kw) + else: + return default diff --git a/product/ERP5Type/Base.py b/product/ERP5Type/Base.py index f83d9c4716333101c520b8ace16d5b345cff05d9..ad597d8de47ec8133150cd7e4a9e585a23b9bc12 100644 --- a/product/ERP5Type/Base.py +++ b/product/ERP5Type/Base.py @@ -64,6 +64,7 @@ from Products.ERP5Type.Utils import createExpressionContext from Products.ERP5Type.Accessor.Accessor import Accessor from Products.ERP5Type.Accessor.TypeDefinition import list_types from Products.ERP5Type.Accessor import Base as BaseAccessor +from Products.ERP5Type.mixin.property_translatable import PropertyTranslatableBuiltInDictMixIn from Products.ERP5Type.XMLExportImport import Base_asXML from Products.ERP5Type.Cache import CachingMethod, clearCache, getReadOnlyTransactionCache from Accessor import WorkflowState @@ -711,7 +712,9 @@ class Base( CopyContainer, PortalContent, ActiveObject, OFS.History.Historical, - ERP5PropertyManager ): + ERP5PropertyManager, + PropertyTranslatableBuiltInDictMixIn + ): """ This is the base class for all ERP5 Zope objects. It defines object attributes which are necessary to implement diff --git a/product/ERP5Type/TranslationProviderBase.py b/product/ERP5Type/TranslationProviderBase.py index 7a59e5b2f1ca605d34e5dbedd35dfd577388d00e..5f6b49fbb360ac45f53a85cbbe295e5108944392 100644 --- a/product/ERP5Type/TranslationProviderBase.py +++ b/product/ERP5Type/TranslationProviderBase.py @@ -24,6 +24,9 @@ from Acquisition import aq_base, Implicit import Products +from Products.ERP5Type.Accessor import Translation +from Products.CMFCore.utils import getToolByName + from zLOG import LOG _MARKER = {} @@ -86,6 +89,23 @@ class TranslationProviderBase(object): return dict((k, v.__of__(self)) for k, v in self._property_domain_dict.iteritems()) + security.declarePublic('getContentTranslationDomainPropertyNameList') + def getContentTranslationDomainPropertyNameList(self): + result = [] + for property_name, translation_information in self.getPropertyTranslationDomainDict().items(): + if translation_information.getDomainName()==Translation.TRANSLATION_DOMAIN_CONTENT_TRANSLATION: + result.append(property_name) + return result + + security.declarePublic('getTranslationDomainNameList') + def getTranslationDomainNameList(self): + return (['']+ + [object_.id + for object_ in getToolByName(self, 'Localizer').objectValues() + if object_.meta_type=='MessageCatalog']+ + [Translation.TRANSLATION_DOMAIN_CONTENT_TRANSLATION] + ) + # # ZMI methods # @@ -105,13 +125,13 @@ class TranslationProviderBase(object): t['domain_name'] = prop.getDomainName() translation_list.append(t) - # get list of Localizer catalog, add 'empty' one for no traduction - catalog = self.getPortalObject().Localizer.objectIds() + [''] - + # get a list of message catalogs and add empty one for no traduction and + # add another for content translation. + translation_domain_list = self.getTranslationDomainNameList() return self._translation_form( self , REQUEST , translations = translation_list - , possible_domain_names=catalog + , possible_domain_names=translation_domain_list , management_view='Translation' , manage_tabs_message=manage_tabs_message ) diff --git a/product/ERP5Type/Utils.py b/product/ERP5Type/Utils.py index c381ebedbff65dd84c975c6afaf7e42a9913908b..645276055a7b97285f934800664da6d711df24e3 100644 --- a/product/ERP5Type/Utils.py +++ b/product/ERP5Type/Utils.py @@ -211,6 +211,19 @@ def convertToUpperCase(key): UpperCase = convertToUpperCase +def convertToLowerCase(key): + tmp = [] + assert(key[0].isupper()) + for i in key: + if i.isupper(): + tmp.append('_') + tmp.append(i.lower()) + else: + tmp.append(i) + return ''.join(tmp) +LowerCase = convertToLowerCase + + def convertToMixedCase(key): """ This function turns an attribute name into @@ -919,6 +932,7 @@ def importLocalDocument(class_id, document_path = None): if not m.has_key(name): m[name] = [] m[name].append(method) m[name+'__roles__']=pr + return document_class, constructors def initializeLocalRegistry(directory_name, import_local_method, path_arg_name='path'): @@ -1317,7 +1331,8 @@ def setDefaultProperties(property_holder, object=None, portal=None): '%s_range_%s' % (prop['id'], value), prop=range_prop, read_permission=read_permission, - write_permission=write_permission) + write_permission=write_permission, + portal=portal) # Create translation accesor, if translatable is set if prop.get('translatable', 0): @@ -1325,8 +1340,15 @@ def setDefaultProperties(property_holder, object=None, portal=None): createTranslationAccessors( property_holder, 'translated_%s' % (prop['id']), + prop, read_permission=read_permission, write_permission=write_permission) + createTranslationLanguageAccessors( + property_holder, + prop, + read_permission=read_permission, + write_permission=write_permission, + portal=portal) # make accessor to translation_domain # first create default one as a normal property txn_prop = {} @@ -1340,7 +1362,8 @@ def setDefaultProperties(property_holder, object=None, portal=None): '%s_%s' %(prop['id'], txn_prop['id']), prop=txn_prop, read_permission=read_permission, - write_permission=write_permission) + write_permission=write_permission, + portal=portal) # then overload accesors getPropertyTranslationDomain if prop.has_key('translation_domain'): default = prop['translation_domain'] @@ -1349,6 +1372,7 @@ def setDefaultProperties(property_holder, object=None, portal=None): createTranslationAccessors( property_holder, '%s_translation_domain' % (prop['id']), + prop, read_permission=read_permission, write_permission=write_permission, default=default) @@ -1357,7 +1381,8 @@ def setDefaultProperties(property_holder, object=None, portal=None): prop['id'], prop=prop, read_permission=read_permission, - write_permission=write_permission) + write_permission=write_permission, + portal=portal) else: raise TypeError, '"%s" is invalid type for propertysheet' % \ prop['type'] @@ -1380,7 +1405,8 @@ def setDefaultProperties(property_holder, object=None, portal=None): prop['id'], prop=prop, read_permission=Permissions.AccessContentsInformation, - write_permission=Permissions.ModifyPortalContent) + write_permission=Permissions.ModifyPortalContent, + portal=portal) # Get read and write permission if portal is not None: @@ -1508,7 +1534,8 @@ from Accessor import Base, List, Acquired, Content,\ def createDefaultAccessors(property_holder, id, prop = None, read_permission=Permissions.AccessContentsInformation, - write_permission=Permissions.ModifyPortalContent): + write_permission=Permissions.ModifyPortalContent, + portal=None): """ This function creates accessor and setter for a class and a property @@ -1519,6 +1546,12 @@ def createDefaultAccessors(property_holder, id, prop = None, prop -- the property definition of the property """ + ###################################################### + # Create Translation Acquired Accessors. + if prop.get('translation_acquired_property_id'): + createTranslationAcquiredPropertyAccessors(property_holder, prop, + portal=portal) + ###################################################### # Create Getters if prop.has_key('acquisition_base_category'): @@ -2565,29 +2598,198 @@ def createRelatedValueAccessors(property_holder, id, read_permission=Permissions if accessor_name[0] != '_': BaseClass.security.declareProtected(read_permission, accessor_name) -def createTranslationAccessors(property_holder, id, +def createTranslationAcquiredPropertyAccessors( + property_holder, + property, + read_permission=Permissions.AccessContentsInformation, + write_permission=Permissions.ModifyPortalContent, + portal=None): + """Generate translation acquired property accessor to Base class""" + property = property.copy() + translation_acquired_property_id_list = [] + accessor_dict_list = [] + + # Language Dependent Getter/Setter + for language in portal.Localizer.get_languages(): + language_key = language.replace('-', '_') + for acquired_property_id in property['acquired_property_id']: + key = '%s_translated_%s' % (language_key, acquired_property_id) + capitalised_composed_id = UpperCase("%s_%s" % (property['id'], key)) + accessor_args = ( + property['type'], + property['portal_type'], + key, + property['acquisition_base_category'], + property['acquisition_portal_type'], + property['acquisition_accessor_id'], + property.get('acquisition_copy_value',0), + property.get('acquisition_mask_value',0), + property.get('acquisition_sync_value',0), + property.get('storage_id'), + property.get('alt_accessor_id'), + property.get('acquisition_object_id'), + (property['type'] in list_types or property.get('multivalued', 0)), + (property['type'] == 'tales'), + ) + + accessor_dict_list.append({'name':'get' + capitalised_composed_id, + 'key': key, + 'class':Translation.AcquiredPropertyGetter, + 'argument':accessor_args, + 'permission':read_permission}) + accessor_dict_list.append({'name':'_baseGet' + capitalised_composed_id, + 'key': key, + 'class':Translation.AcquiredPropertyGetter, + 'argument':accessor_args, + 'permission':read_permission}) + accessor_dict_list.append({'name': 'getDefault' + capitalised_composed_id, + 'key': key, + 'class': Translation.AcquiredPropertyGetter, + 'argument': accessor_args, + 'permission': read_permission}) + accessor_dict_list.append({'name': 'set' + capitalised_composed_id, + 'key': '_set' + capitalised_composed_id, + 'class': Alias.Reindex, + 'argument': (), + 'permission': write_permission}) + accessor_dict_list.append({'name': '_set' + capitalised_composed_id, + 'key': key, + 'class': AcquiredProperty.DefaultSetter, + 'argument': accessor_args, + 'permission': write_permission}) + accessor_dict_list.append({'name': 'setDefault' + capitalised_composed_id, + 'key': '_set' + capitalised_composed_id, + 'class': Alias.Reindex, + 'argument': (), + 'permission': write_permission}) + + # Language Independent Getter + for acquired_property_id in property['acquired_property_id']: + if acquired_property_id in property.get('translation_acquired_property_id',()): + key = 'translated_%s' % acquired_property_id + capitalised_composed_id = UpperCase('%s_%s' % (property['id'], key)) + accessor_args = ( + property['type'], + property['portal_type'], + key, + property['acquisition_base_category'], + property['acquisition_portal_type'], + property['acquisition_accessor_id'], + property.get('acquisition_copy_value',0), + property.get('acquisition_mask_value',0), + property.get('acquisition_sync_value',0), + property.get('storage_id'), + property.get('alt_accessor_id'), + property.get('acquisition_object_id'), + (property['type'] in list_types or property.get('multivalued', 0)), + (property['type'] == 'tales'), + ) + + accessor_dict_list.append({'name': 'get' + capitalised_composed_id, + 'key': key, + 'class': Translation.AcquiredPropertyGetter, + 'argument': accessor_args, + 'permission': read_permission}) + accessor_dict_list.append({'name': '_baseGet' + capitalised_composed_id, + 'key': key, + 'class': Translation.AcquiredPropertyGetter, + 'argument': accessor_args, + 'permission': read_permission}) + accessor_dict_list.append({'name': 'getDefault' + capitalised_composed_id, + 'key': key, + 'class': Translation.AcquiredPropertyGetter, + 'argument': accessor_args, + 'permission': read_permission}) + + for accessor_dict in accessor_dict_list: + accessor_name = accessor_dict['name'] + if getattr(property_holder, accessor_name, None) is None: + property_holder.registerAccessor(accessor_name, # id + accessor_dict['key'], + accessor_dict['class'], + accessor_dict['argument']) + property_holder.declareProtected(accessor_dict['permission'], + accessor_name) + +def createTranslationAccessors(property_holder, id, property, read_permission=Permissions.AccessContentsInformation, write_permission=Permissions.ModifyPortalContent, default=''): """ Generate the translation accessor for a class and a property """ + capitalised_id = UpperCase(id) if 'translated' in id: - accessor_name = 'get' + UpperCase(id) + accessor_name = 'get' + capitalised_id + private_accessor_name = '_baseGet' + capitalised_id if not hasattr(property_holder, accessor_name): - property_holder.registerAccessor(accessor_name, id, Translation.TranslatedPropertyGetter, ()) + property_holder.registerAccessor(accessor_name, + id, + Translation.TranslatedPropertyGetter, + (property['id'], property['type'], None, default)) property_holder.declareProtected(read_permission, accessor_name) - accessor_name = '_baseGet' + UpperCase(id) - if not hasattr(property_holder, accessor_name): - property_holder.registerAccessor(accessor_name, id, Translation.TranslatedPropertyGetter, ()) + if not hasattr(property_holder, private_accessor_name): + property_holder.registerAccessor(private_accessor_name, + id, + Translation.TranslatedPropertyGetter, + (property['id'], property['type'], None, default)) if 'translation_domain' in id: # Getter - accessor_name = 'get' + UpperCase(id) - property_holder.registerAccessor(accessor_name, id, - Translation.PropertyTranslationDomainGetter, ('string', default,)) + accessor_name = 'get' + capitalised_id + property_holder.registerAccessor(accessor_name, + id, + Translation.PropertyTranslationDomainGetter, + ('string', default,)) property_holder.declareProtected(read_permission, accessor_name) +def createTranslationLanguageAccessors(property_holder, property, + read_permission=Permissions.AccessContentsInformation, + write_permission=Permissions.ModifyPortalContent, default='', + portal=None): + """ + Generate translation language accessors + """ + accessor_dict_list = [] + + for language in portal.Localizer.get_languages(): + language_key = language.replace('-', '_') + composed_id = '%s_translated_%s' % (language_key, property['id']) + capitalised_compose_id = UpperCase(composed_id) + + getter_accessor_args = (property['id'], property['type'], language, default) + accessor_dict_list.append({'name': 'get' + capitalised_compose_id, + 'class': Translation.TranslatedPropertyGetter, + 'argument': getter_accessor_args, + 'permission': read_permission}) + accessor_dict_list.append({'name': '_baseGet' + capitalised_compose_id, + 'class': Translation.TranslatedPropertyGetter, + 'argument': getter_accessor_args, + 'permission': read_permission}) + + setter_accessor_args = (property['id'], property['type'], language) + accessor_dict_list.append({'name':'set' + capitalised_compose_id, + 'key': '_set' + capitalised_compose_id, + 'class': Alias.Reindex, + 'argument': (), + 'permission': write_permission}) + setter_accessor_args = (property['id'], property['type'], language) + accessor_dict_list.append({'name': '_set' + capitalised_compose_id, + 'class': Translation.TranslationPropertySetter, + 'argument': setter_accessor_args, + 'permission': write_permission}) + + for accessor_dict in accessor_dict_list: + accessor_name = accessor_dict['name'] + if getattr(property_holder, accessor_name, None) is None: + property_holder.registerAccessor(accessor_name, + accessor_dict.get('key', None), + accessor_dict['class'], + accessor_dict['argument']) + property_holder.declareProtected(accessor_dict['permission'], + accessor_name) + + ##################################################### # More Useful methods which require Base ##################################################### diff --git a/product/ERP5Type/interfaces/property_translatable.py b/product/ERP5Type/interfaces/property_translatable.py new file mode 100644 index 0000000000000000000000000000000000000000..ff3bd6777876fd5fd96927bec226cc2eca08c9fc --- /dev/null +++ b/product/ERP5Type/interfaces/property_translatable.py @@ -0,0 +1,47 @@ +############################################################################## +# +# Copyright (c) 2009 Nexedi KK, Nexedi SA and Contributors. All Rights Reserved. +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## +from zope.interface import Interface + + +class IPropertyTranslatable(Interface): + """ + """ + + def getPropertyTranslation(property_id, language): + """Retrieve translation text.""" + + def setPropertyTranslation(property_id, language, original_text, translation): + """Store translation text.""" + + def deletePropertyTranslation(property_id, language): + """Delete translation text.""" + + def getPropertyTranslationOriginalText(property_id, language): + """Retrieve original text which is used for translation.""" + + def isPropertyTranslated(property_id, language): + """Return True if property is translated, else return False""" diff --git a/product/ERP5Type/mixin/__init__.py b/product/ERP5Type/mixin/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/product/ERP5Type/mixin/property_translatable.py b/product/ERP5Type/mixin/property_translatable.py new file mode 100644 index 0000000000000000000000000000000000000000..4fcffcc7fd74b1d1cceda6873f47e9aac0c1df1e --- /dev/null +++ b/product/ERP5Type/mixin/property_translatable.py @@ -0,0 +1,86 @@ +############################################################################## +# +# Copyright (c) 2009 Nexedi KK, Nexedi SA and Contributors. All Rights Reserved. +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +import zope.interface +from Products.ERP5Type.interfaces.property_translatable import IPropertyTranslatable +from AccessControl import ClassSecurityInfo +from Products.ERP5Type import Permissions +from Globals import InitializeClass + + +INTERNAL_TRANSLATION_DICT_NAME = '__translation_dict' + +class PropertyTranslatableBuiltInDictMixIn: + """An implementation of IPropertyTranslatable with built-in dict.""" + + zope.interface.implements(IPropertyTranslatable) + + security = ClassSecurityInfo() + + def _getTranslationDict(self): + try: + return getattr(self, INTERNAL_TRANSLATION_DICT_NAME) + except AttributeError: + dict_ = {} + setattr(self, INTERNAL_TRANSLATION_DICT_NAME, dict_) + self._p_changed = True + return dict_ + + security.declareProtected(Permissions.AccessContentsInformation, + 'getPropertyTranslation') + def getPropertyTranslation(self, property_id, language): + return self._getTranslationDict()[(property_id, language)][1] + + security.declareProtected(Permissions.ModifyPortalContent, + 'setPropertyTranslation') + def setPropertyTranslation(self, property_id, language, original_text, translation): + self._getTranslationDict()[(property_id, language)] = (original_text, translation) + self._p_changed = True + + security.declareProtected(Permissions.ModifyPortalContent, + 'deletePropertyTranslation') + def deletePropertyTranslation(self, property_id, language): + try: + del self._getTranslationDict()[(property_id, language)] + except KeyError: + pass + + security.declareProtected(Permissions.AccessContentsInformation, + 'getPropertyTranslationOriginalText') + def getPropertyTranslationOriginalText(self, property_id, language): + return self._getTranslationDict()[(property_id, language)][0] + + security.declareProtected(Permissions.AccessContentsInformation, + 'isPropertyTranslated') + def isPropertyTranslated(self, property_id, language): + try: + self._getTranslationDict()[(property_id, language)] + return True + except KeyError: + return False + +InitializeClass(PropertyTranslatableBuiltInDictMixIn) diff --git a/product/ERP5Type/patches/Localizer.py b/product/ERP5Type/patches/Localizer.py index 8f30d3c075059fc609196e691841ba595b9eec6c..9f6ec381bf7613674c5675afba7a6dab1d314ff1 100644 --- a/product/ERP5Type/patches/Localizer.py +++ b/product/ERP5Type/patches/Localizer.py @@ -126,3 +126,128 @@ def cleanup_and_export(self, x, REQUEST=None, RESPONSE=None): return original_manage_export(self, x, REQUEST=REQUEST, RESPONSE=RESPONSE) MessageCatalog.manage_export = cleanup_and_export + +# Add a feature which allows users to be able to add a new language. +# +# Patch to LanguageManager.py +# +def get_languages_mapping(self): + """ + Returns a list of dictionary, one for each objects language. The + dictionary contains the language code, its name and a boolean + value that tells wether the language is the default one or not. + """ + return [ {'code': x, + 'name': self.get_language_name(x), + 'default': x == self._default_language} + for x in self._languages ] + +def get_language_name(self, id=None): + """ + Returns the name of the given language code. + + XXX Kept here for backwards compatibility only + """ + if id is None: + id = self.get_default_language() + language_name = LanguageManager.i18n.get_language_name(id) + if language_name=='???': + return self.get_user_defined_language_name(id) or language_name + else: + return language_name + +# New method +def get_user_defined_language_name(self, id=None): + """ + Returns the name of the given user defined language code. + """ + for language_dict in self.get_user_defined_languages(): + if language_dict['code']==id: + return language_dict['name'] + +def get_all_languages(self): + """ + Returns all ISO languages, used by 'manage_languages'. + """ + return LanguageManager.i18n.get_languages() + self.get_user_defined_languages() + +# New method +def get_user_defined_languages(self): + user_define_language_dict_list = [] + localizer = getattr(self, 'Localizer', None) + if localizer is not None: + for value in getattr(self, 'user_defined_languages', ()): + splitted_value = value.split(' ', 1) + if len(splitted_value)==2: + user_define_language_dict_list.append( + {'name':splitted_value[0].strip(), + 'code':splitted_value[1].strip(),}) + return user_define_language_dict_list + +# New method +def _add_user_defined_language(self, language_name, language_code): + self.user_defined_languages = ( + getattr(self, 'user_defined_languages', ())+ + ('%s %s' % (language_name, language_code),) + ) + self._p_changed = True + +# New method +def _del_user_defined_language(self, language_code): + user_defined_languages = [] + for language_dict in self.get_user_defined_languages(): + if language_dict['code']!=language_code: + user_defined_languages.append('%s %s' % + (language_dict['name'], + language_dict['code'])) + self.user_defined_languages = tuple(user_defined_languages) + self._p_changed = True + +from Products.Localizer import LanguageManager +LanguageManager.LanguageManager.get_languages_mapping = get_languages_mapping +LanguageManager.LanguageManager.get_language_name = get_language_name +LanguageManager.LanguageManager.get_all_languages = get_all_languages +LanguageManager.LanguageManager.get_user_defined_language_name = get_user_defined_language_name +LanguageManager.LanguageManager.get_user_defined_languages = get_user_defined_languages +LanguageManager.LanguageManager._add_user_defined_language = _add_user_defined_language +LanguageManager.LanguageManager._del_user_defined_language = _del_user_defined_language +LanguageManager.InitializeClass(LanguageManager.LanguageManager) + +# +# Patch to Localizer.py +# +_properties = ({'id': 'title', 'type': 'string'}, + {'id': 'accept_methods', 'type': 'tokens'}, + {'id': 'user_defined_languages', 'type': 'lines'},) + +user_defined_languages = () + +def get_languages_map(self): + """ + Return a list of dictionaries, each dictionary has the language + id, its title and a boolean value to indicate wether it's the + user preferred language, for example: + [{'id': 'en', 'title': 'English', 'selected': 1}] + Used in changeLanguageForm. + """ + # For now only LPM instances are considered to be containers of + # multilingual data. + try: + ob = self.getLocalPropertyManager() + except AttributeError: + ob = self + + ob_language = ob.get_selected_language() + ob_languages = ob.get_available_languages() + + langs = [] + for x in ob_languages: + langs.append({'id': x, 'title': self.get_language_name(x), + 'selected': x == ob_language}) + return langs + +from Products.Localizer import Localizer +Localizer.Localizer._properties = _properties +Localizer.Localizer.user_defined_languages = user_defined_languages +Localizer.Localizer.get_languages_map = get_languages_map +Localizer.InitializeClass(Localizer.Localizer)