Index: src/lxml/xslt.pxd
===================================================================
--- src/lxml/xslt.pxd	(revision 61518)
+++ src/lxml/xslt.pxd	(working copy)
@@ -133,6 +133,11 @@
                                       xsltTransformContext* ctxt) nogil
     cdef xmlDoc* xsltGetProfileInformation(xsltTransformContext* ctxt) nogil
 
+cdef extern from "libxslt/variables.h":
+    cdef int xsltQuoteUserParams(
+        xsltTransformContext* ctxt,
+        char** params)
+
 cdef extern from "libxslt/extra.h":
     cdef char* XSLT_LIBXSLT_NAMESPACE
     cdef char* XSLT_XALAN_NAMESPACE
Index: src/lxml/xslt.pxi
===================================================================
--- src/lxml/xslt.pxi	(revision 61518)
+++ src/lxml/xslt.pxi	(working copy)
@@ -441,6 +441,19 @@
         about the XSLT.  The result of the XSLT will have a property
         xslt_profile that holds an XML tree with profiling data.
         """
+        return self.transform(_input, profile_run, kw)
+
+    def transform(self, _input, profile_run=False, kw=None, strparams=None):
+        """transform(self, _input, profile_run=False, kw=None, strparams=None)
+        
+        Execute the XSL transformation.
+
+        The routine works like __call__, except you can use strparams.
+
+        The difference between kw and strparams is strparams do not evaluate
+        a value, i.e. you can pass parameters with both single and double 
+        quotes.
+        """
         cdef _XSLTContext context
         cdef _XSLTResolverContext resolver_context
         cdef _Document input_doc
@@ -452,6 +465,7 @@
         cdef xmlDoc* c_result = NULL
         cdef xmlDoc* c_doc
         cdef tree.xmlDict* c_dict
+        cdef char** cstrparams
 
         input_doc = _documentOrRaise(_input)
         root_node = _rootNodeOrRaise(_input)
@@ -470,6 +484,9 @@
         if profile_run:
             transform_ctxt.profile = 1
 
+        cstrparams = pydict_to_charlist(strparams)
+        xslt.xsltQuoteUserParams(transform_ctxt, cstrparams)
+
         try:
             self._error_log.connect()
             context = self._context._copy()
@@ -494,6 +511,8 @@
         finally:
             if context is not None:
                 context.free_context()
+            if cstrparams is not NULL:
+                python.PyMem_Free(cstrparams)
             _destroyFakeDoc(input_doc._c_doc, c_doc)
             self._error_log.disconnect()
 
@@ -552,7 +571,6 @@
                                 xslt.xsltTransformContext* transform_ctxt):
         cdef xmlDoc* c_result
         cdef char** params
-        cdef Py_ssize_t i, parameter_count
         cdef list keep_ref
 
         xslt.xsltSetTransformErrorFunc(transform_ctxt, <void*>self._error_log,
@@ -561,31 +579,7 @@
         if self._access_control is not None:
             self._access_control._register_in_context(transform_ctxt)
 
-        parameter_count = python.PyDict_Size(parameters)
-        if parameter_count > 0:
-            # allocate space for parameters
-            # * 2 as we want an entry for both key and value,
-            # and + 1 as array is NULL terminated
-            params = <char**>python.PyMem_Malloc(
-                sizeof(char*) * (parameter_count * 2 + 1))
-            try:
-                i = 0
-                keep_ref = []
-                for key, value in parameters.items():
-                    k = _utf8(key)
-                    keep_ref.append(k)
-                    v = _utf8(value)
-                    keep_ref.append(v)
-                    params[i] = _cstr(k)
-                    i += 1
-                    params[i] = _cstr(v)
-                    i += 1
-            except:
-                python.PyMem_Free(params)
-                raise
-            params[i] = NULL
-        else:
-            params = NULL
+        params = pydict_to_charlist(parameters)
 
         with nogil:
             c_result = xslt.xsltApplyStylesheetUser(
Index: src/lxml/apihelpers.pxi
===================================================================
--- src/lxml/apihelpers.pxi	(revision 61518)
+++ src/lxml/apihelpers.pxi	(working copy)
@@ -1396,3 +1396,41 @@
         return os_path_abspath(filename)
     # can't determine filename
     return None
+
+cdef char** pydict_to_charlist(kw):
+    u"""Make char** from a python dict. 
+
+    Return NULL if kw is empty string or kw is None.
+    """
+    cdef char** params
+    cdef Py_ssize_t i, kw_count
+
+    if not kw:
+        return NULL
+
+    kw_count = python.PyDict_Size(kw)
+    if kw_count > 0:
+        # allocate space for parameters
+        # * 2 as we want an entry for both key and value,
+        # and + 1 as array is NULL terminated
+        params = <char**>python.PyMem_Malloc(
+            sizeof(char*) * (kw_count * 2 + 1))
+        try:
+            i = 0
+            keep_ref = []
+            for key, value in kw.items():
+                k = _utf8(key)
+                keep_ref.append(k)
+                v = _utf8(value)
+                keep_ref.append(v)
+                params[i] = _cstr(k)
+                i += 1
+                params[i] = _cstr(v)
+                i += 1
+        except:
+            python.PyMem_Free(params)
+            raise
+        params[i] = NULL
+    else:
+        params = NULL
+    return params
Index: src/lxml/tests/test_xslt.py
===================================================================
--- src/lxml/tests/test_xslt.py	(revision 61518)
+++ src/lxml/tests/test_xslt.py	(working copy)
@@ -329,6 +329,24 @@
 ''',
                           str(res))
 
+    def test_xslt_string_parameters(self):
+        tree = self.parse('<a><b>B</b><c>C</c></a>')
+        style = self.parse('''\
+<xsl:stylesheet version="1.0"
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+  <xsl:template match="/">
+    <foo><xsl:value-of select="$bar" /></foo>
+  </xsl:template>
+</xsl:stylesheet>''')
+
+        st = etree.XSLT(style)
+        res = st.transform(tree, strparams=dict(bar='''it's me, "Bar"'''))
+        self.assertEquals('''\
+<?xml version="1.0"?>
+<foo>it's me, "Bar"</foo>
+''',
+                          str(res))
+
     def test_xslt_parameter_invalid(self):
         tree = self.parse('<a><b>B</b><c>C</c></a>')
         style = self.parse('''\
