Commit cc21d8c1 authored by Yusei Tahara's avatar Yusei Tahara

Commit content translation feature.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@30269 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 36c5a052
......@@ -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
......@@ -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
......
......@@ -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
)
......
This diff is collapsed.
##############################################################################
#
# 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"""
##############################################################################
#
# 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)
......@@ -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)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment