[svn] fixed bug reported by stefan ebner and implemented cache_keys to fix problems with multiple laoders caching in the same folder

--HG--
branch : trunk
diff --git a/CHANGES b/CHANGES
index 8dcac27..0a7a011 100644
--- a/CHANGES
+++ b/CHANGES
@@ -83,6 +83,12 @@
 
 - fixed a corner case when defining a block inside of a condition
 
+- the cached loader mixin is now able to cache multiple templates from
+  different loaders in the same cache folder.
+
+- Translatable strings returned by ``_()`` will leave their string formatting
+  signs untouched. Thanks to Stefan Ebner for reporting.
+
 
 Version 1.0
 -----------
diff --git a/THANKS b/THANKS
index e9d865c..2bbb74e 100644
--- a/THANKS
+++ b/THANKS
@@ -4,5 +4,7 @@
 All the people listed here helped improving Jinja a lot, provided
 patches, helped working out solutions etc. Thanks to all of you!
 
+- Ronny Pfannschmidt
 - Axel Böhm
 - Alexey Melchakov
+- Stefan Ebner
diff --git a/docs/src/loaders.txt b/docs/src/loaders.txt
index 20c69e2..29da849 100644
--- a/docs/src/loaders.txt
+++ b/docs/src/loaders.txt
@@ -61,11 +61,14 @@
         
         def __init__(self, path):
             self.path = path
-            CachedLoaderMixin.__init__(
+            CachedLoaderMixin.__init__(self,
                 True,       # use memory caching
                 40,         # for up to 40 templates
                 '/tmp',     # additionally save the compiled templates in /tmp
-                True        # and reload cached templates automatically if changed
+                True,       # and reload cached templates automatically if changed
+                'foo'       # optional salt used to keep templates with the same
+                            # name in the same cache folder, but from different
+                            # loaders. New in Jinja 1.1 and can be omitted.
             )
 
         def get_source(self, environment, name, parent):
@@ -88,4 +91,4 @@
 exist the option `auto_reload` won't have an effect. Also note that the
 `check_source_changed` method must not raise an exception if the template
 does not exist but return ``-1``. The return value ``-1`` is considered
-"always reload" whereas ``0`` means "do not reload".
\ No newline at end of file
+"always reload" whereas ``0`` means "do not reload".
diff --git a/jinja/datastructure.py b/jinja/datastructure.py
index 86b74c7..5aa6b2c 100644
--- a/jinja/datastructure.py
+++ b/jinja/datastructure.py
@@ -276,8 +276,15 @@
             translator = self.environment.get_translator(self)
             def translate(s, p=None, n=None, r=None):
                 if p is None:
-                    return translator.gettext(s) % (r or {})
-                return translator.ngettext(s, p, r[n]) % (r or {})
+                    s = translator.gettext(s)
+                else:
+                    s = translator.ngettext(s, p, r[n])
+                # apply replacement substitution only if replacements
+                # are given. This is the case for {% trans %}...{% endtras %}
+                # but for the "_()" syntax and a trans tag without a body.
+                if r is not None:
+                    s %= r
+                return s
             self._translate_func = translate
         return self._translate_func
     translate_func = property(translate_func, doc=translate_func.__doc__)
diff --git a/jinja/loaders.py b/jinja/loaders.py
index 7bd63a8..6836b32 100644
--- a/jinja/loaders.py
+++ b/jinja/loaders.py
@@ -38,12 +38,13 @@
                      if p and p[0] != '.']))
 
 
-def get_cachename(cachepath, name):
+def get_cachename(cachepath, name, salt=None):
     """
     Return the filename for a cached file.
     """
     return path.join(cachepath, 'jinja_%s.cache' %
-                     sha.new('jinja(%s)tmpl' % name).hexdigest())
+                     sha.new('jinja(%s|%s)tmpl' %
+                             (name, salt or '')).hexdigest())
 
 
 class LoaderWrapper(object):
@@ -130,7 +131,8 @@
     Mixin this class to implement simple memory and disk caching.
     """
 
-    def __init__(self, use_memcache, cache_size, cache_folder, auto_reload):
+    def __init__(self, use_memcache, cache_size, cache_folder, auto_reload,
+                 cache_salt=None):
         if use_memcache:
             self.__memcache = CacheDict(cache_size)
         else:
@@ -140,6 +142,7 @@
             self.__auto_reload = False
         else:
             self.__auto_reload = auto_reload
+        self.__salt = cache_salt
         self.__times = {}
         self.__lock = Lock()
 
@@ -186,7 +189,7 @@
             # mem cache disabled or not cached by now
             # try to load if from the disk cache
             if tmpl is None and self.__cache_folder is not None:
-                cache_fn = get_cachename(self.__cache_folder, name)
+                cache_fn = get_cachename(self.__cache_folder, name, self.__salt)
                 if last_change is not None:
                     try:
                         cache_time = path.getmtime(cache_fn)
@@ -261,14 +264,20 @@
     ``auto_reload``     Set this to `False` for a slightly better
                         performance. In that case Jinja won't check for
                         template changes on the filesystem.
+    ``cache_salt``      Optional unique number to not confuse the
+                        caching system when caching more than one
+                        template loader in the same folder. Defaults
+                        to the searchpath. *New in Jinja 1.1*
     =================== =================================================
     """
 
     def __init__(self, searchpath, use_memcache=False, memcache_size=40,
-                 cache_folder=None, auto_reload=True):
+                 cache_folder=None, auto_reload=True, cache_salt=None):
+        if cache_salt is None:
+            cache_salt = searchpath
         self.searchpath = searchpath
         CachedLoaderMixin.__init__(self, use_memcache, memcache_size,
-                                   cache_folder, auto_reload)
+                                   cache_folder, auto_reload, cache_salt)
 
     def get_source(self, environment, name, parent):
         filename = get_template_filename(self.searchpath, name)
@@ -321,22 +330,30 @@
                         template changes on the filesystem. If the
                         templates are inside of an egg file this won't
                         have an effect.
+    ``cache_salt``      Optional unique number to not confuse the
+                        caching system when caching more than one
+                        template loader in the same folder. Defaults
+                        to ``package_name + '/' + package_path``.
+                        *New in Jinja 1.1*
     =================== =================================================
     """
 
     def __init__(self, package_name, package_path, use_memcache=False,
-                 memcache_size=40, cache_folder=None, auto_reload=True):
+                 memcache_size=40, cache_folder=None, auto_reload=True,
+                 cache_salt=None):
         if resource_filename is None:
             raise ImportError('setuptools not found')
         self.package_name = package_name
         self.package_path = package_path
+        if cache_salt is None:
+            cache_salt = package_name + '/' + package_path
         # if we have an loader we probably retrieved it from an egg
         # file. In that case don't use the auto_reload!
         if auto_reload and getattr(__import__(package_name, '', '', ['']),
                                    '__loader__', None) is not None:
             auto_reload = False
         CachedLoaderMixin.__init__(self, use_memcache, memcache_size,
-                                   cache_folder, auto_reload)
+                                   cache_folder, auto_reload, cache_salt)
 
     def get_source(self, environment, name, parent):
         name = '/'.join([self.package_path] + [p for p in name.split('/')
@@ -401,11 +418,15 @@
     ``auto_reload``     Set this to `False` for a slightly better
                         performance. In that case of `getmtime_func`
                         not being provided this won't have an effect.
+    ``cache_salt``      Optional unique number to not confuse the
+                        caching system when caching more than one
+                        template loader in the same folder.
     =================== =================================================
     """
 
     def __init__(self, loader_func, getmtime_func=None, use_memcache=False,
-                 memcache_size=40, cache_folder=None, auto_reload=True):
+                 memcache_size=40, cache_folder=None, auto_reload=True,
+                 cache_salt=None):
         # when changing the signature also check the jinja.plugin function
         # loader instantiation.
         self.loader_func = loader_func
@@ -413,7 +434,7 @@
         if auto_reload and getmtime_func is None:
             auto_reload = False
         CachedLoaderMixin.__init__(self, use_memcache, memcache_size,
-                                   cache_folder, auto_reload)
+                                   cache_folder, auto_reload, cache_salt)
 
     def get_source(self, environment, name, parent):
         rv = self.loader_func(name)
diff --git a/tests/test_i18n.py b/tests/test_i18n.py
index ddd5841..040f7ba 100644
--- a/tests/test_i18n.py
+++ b/tests/test_i18n.py
@@ -14,7 +14,8 @@
     'child.html': '{% extends "master.html" %}{% block body %}'
                   '{% trans "watch out" %}{% endblock %}',
     'plural.html': '{% trans user_count %}One user online{% pluralize %}'
-                   '{{ user_count }} users online{% endtrans %}'
+                   '{{ user_count }} users online{% endtrans %}',
+    'stringformat.html': '{{ _("User: %d")|format(user_count) }}'
 }
 
 
@@ -23,7 +24,8 @@
         'missing':                      'fehlend',
         'watch out':                    'pass auf',
         'One user online':              'Ein Benutzer online',
-        '%(user_count)s users online':  '%(user_count)s Benutzer online'
+        '%(user_count)s users online':  '%(user_count)s Benutzer online',
+        'User: %d':                     'Benutzer: %d'
     }
 }
 
@@ -77,3 +79,8 @@
     tmpl = i18n_env.get_template('plural.html')
     assert tmpl.render(LANGUAGE='de', user_count=1) == 'Ein Benutzer online'
     assert tmpl.render(LANGUAGE='de', user_count=2) == '2 Benutzer online'
+
+
+def test_trans_stringformatting():
+    tmpl = i18n_env.get_template('stringformat.html')
+    assert tmpl.render(LANGUAGE='de', user_count=5) == 'Benutzer: 5'