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 @@ ...@@ -26,41 +26,67 @@
# #
############################################################################## ##############################################################################
from Base import func_code, ATTRIBUTE_PREFIX, evaluateTales, Getter as BaseGetter
from zLOG import LOG from zLOG import LOG
from Products.ERP5Type.PsycoWrapper import psyco from Products.ERP5Type.PsycoWrapper import psyco
from Acquisition import aq_base from Acquisition import aq_base
from Products.CMFCore.utils import getToolByName 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): class TranslatedPropertyGetter(BaseGetter):
""" """
Get the translated property Get the translated property
""" """
_need__name__=1
# This can be called from the Web # This can be called from the Web
func_code = func_code() func_code = func_code()
func_code.co_varnames = ('self', ) func_code.co_varnames = ('self',)
func_code.co_argcount = 1 func_code.co_argcount = 1
func_defaults = () 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._id = id
self.__name__ = id self.__name__ = id
self._key = key self._property_id = property_id
self._original_key = key.replace('translated_', '') self._null = type_definition[property_type]['null']
self._language = language
self._default = default
self._warning = warning self._warning = warning
def __call__(self, instance, *args, **kw): def __call__(self, instance, *args, **kw):
if self._warning: if self._warning:
LOG("ERP5Type Deprecated Getter Id:",0, self._id) LOG("ERP5Type Deprecated Getter Id:",0, self._id)
domain = instance.getProperty('%s_translation_domain' % domain = instance.getProperty('%s_translation_domain' % self._property_id)
self._original_key)
value = instance.getProperty(self._original_key) if domain==TRANSLATION_DOMAIN_CONTENT_TRANSLATION:
if domain == '' or (value in ('', None)): if len(args) > 0:
return value default = args[0]
localizer = getToolByName(instance, 'Localizer') else:
return localizer[domain].gettext(unicode(value, 'utf8')).encode('utf8') 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__) psyco.bind(__call__)
...@@ -80,9 +106,7 @@ class PropertyTranslationDomainGetter(BaseGetter): ...@@ -80,9 +106,7 @@ class PropertyTranslationDomainGetter(BaseGetter):
def __init__(self, id, key, property_type, default=None, storage_id=None): def __init__(self, id, key, property_type, default=None, storage_id=None):
self._id = id self._id = id
self.__name__ = id self.__name__ = id
self._key = key
self._original_key = key.replace('_translation_domain', '') self._original_key = key.replace('_translation_domain', '')
self._property_type = property_type
self._default = default self._default = default
if storage_id is None: if storage_id is None:
storage_id = "%s%s" % (ATTRIBUTE_PREFIX, key) storage_id = "%s%s" % (ATTRIBUTE_PREFIX, key)
...@@ -126,3 +150,68 @@ class PropertyTranslationDomainGetter(BaseGetter): ...@@ -126,3 +150,68 @@ class PropertyTranslationDomainGetter(BaseGetter):
psyco.bind(__call__) 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 ...@@ -64,6 +64,7 @@ from Products.ERP5Type.Utils import createExpressionContext
from Products.ERP5Type.Accessor.Accessor import Accessor from Products.ERP5Type.Accessor.Accessor import Accessor
from Products.ERP5Type.Accessor.TypeDefinition import list_types from Products.ERP5Type.Accessor.TypeDefinition import list_types
from Products.ERP5Type.Accessor import Base as BaseAccessor 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.XMLExportImport import Base_asXML
from Products.ERP5Type.Cache import CachingMethod, clearCache, getReadOnlyTransactionCache from Products.ERP5Type.Cache import CachingMethod, clearCache, getReadOnlyTransactionCache
from Accessor import WorkflowState from Accessor import WorkflowState
...@@ -711,7 +712,9 @@ class Base( CopyContainer, ...@@ -711,7 +712,9 @@ class Base( CopyContainer,
PortalContent, PortalContent,
ActiveObject, ActiveObject,
OFS.History.Historical, OFS.History.Historical,
ERP5PropertyManager ): ERP5PropertyManager,
PropertyTranslatableBuiltInDictMixIn
):
""" """
This is the base class for all ERP5 Zope objects. This is the base class for all ERP5 Zope objects.
It defines object attributes which are necessary to implement It defines object attributes which are necessary to implement
......
...@@ -24,6 +24,9 @@ from Acquisition import aq_base, Implicit ...@@ -24,6 +24,9 @@ from Acquisition import aq_base, Implicit
import Products import Products
from Products.ERP5Type.Accessor import Translation
from Products.CMFCore.utils import getToolByName
from zLOG import LOG from zLOG import LOG
_MARKER = {} _MARKER = {}
...@@ -86,6 +89,23 @@ class TranslationProviderBase(object): ...@@ -86,6 +89,23 @@ class TranslationProviderBase(object):
return dict((k, v.__of__(self)) return dict((k, v.__of__(self))
for k, v in self._property_domain_dict.iteritems()) 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 # ZMI methods
# #
...@@ -105,13 +125,13 @@ class TranslationProviderBase(object): ...@@ -105,13 +125,13 @@ class TranslationProviderBase(object):
t['domain_name'] = prop.getDomainName() t['domain_name'] = prop.getDomainName()
translation_list.append(t) translation_list.append(t)
# get list of Localizer catalog, add 'empty' one for no traduction # get a list of message catalogs and add empty one for no traduction and
catalog = self.getPortalObject().Localizer.objectIds() + [''] # add another for content translation.
translation_domain_list = self.getTranslationDomainNameList()
return self._translation_form( self return self._translation_form( self
, REQUEST , REQUEST
, translations = translation_list , translations = translation_list
, possible_domain_names=catalog , possible_domain_names=translation_domain_list
, management_view='Translation' , management_view='Translation'
, manage_tabs_message=manage_tabs_message , 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): ...@@ -126,3 +126,128 @@ def cleanup_and_export(self, x, REQUEST=None, RESPONSE=None):
return original_manage_export(self, x, REQUEST=REQUEST, RESPONSE=RESPONSE) return original_manage_export(self, x, REQUEST=REQUEST, RESPONSE=RESPONSE)
MessageCatalog.manage_export = cleanup_and_export 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