diff --git a/product/ERP5Type/Utils.py b/product/ERP5Type/Utils.py
index 10eedcc1ea05c9cc8920a603322e2c9da1d30764..c40edf6b188d3722a2780e795fd367f0f1bf542a 100644
--- a/product/ERP5Type/Utils.py
+++ b/product/ERP5Type/Utils.py
@@ -3367,3 +3367,22 @@ def reencodeUrlEscapes(url):
       url += [_reencodeUrlEscapes_map[c] for c in part]
   except StopIteration:
     return ''.join(url)
+
+from zope.tales.engine import Engine
+from zope.tales.tales import CompilerError
+
+def isValidTALESExpression(value):
+  """return if given value is valid TALES Expression.
+  This validator only validates Syntax of TALES Expression,
+  it does not tell that Expression is callable on given context
+
+  - value: string we try to compile
+
+  return tuple: (boolean result, error_message or None)
+  """
+  try:
+    Engine.compile(value)
+  except CompilerError, message:
+    return False, message
+  else:
+    return True, None
diff --git a/product/ERP5Type/__init__.py b/product/ERP5Type/__init__.py
index 32d27bab9764cabbd246485682458aadd958804e..b32c2517882047a14b2f6d9465b2e921143ade03 100644
--- a/product/ERP5Type/__init__.py
+++ b/product/ERP5Type/__init__.py
@@ -165,7 +165,8 @@ ModuleSecurityInfo('Products.ERP5Type.Utils').declarePublic(
     'sortValueList', 'convertToUpperCase', 'UpperCase',
     'convertToMixedCase', 'cartesianProduct', 'sleep', 'getCommonTimeZoneList',
     'int2letter', 'getMessageIdWithContext', 'getTranslationStringWithContext',
-    'Email_parseAddressHeader', 'guessEncodingFromText')
+    'Email_parseAddressHeader', 'guessEncodingFromText',
+    'isValidTALESExpression')
 
 allow_module('Products.ERP5Type.Message')
 ModuleSecurityInfo('Products.ERP5Type.Message').declarePublic('translateString')