diff --git a/django/template/loader.py b/django/template/loader.py
index 4185017..98ea96c 100644
--- a/django/template/loader.py
+++ b/django/template/loader.py
@@ -3,10 +3,9 @@
 # This uses the TEMPLATE_LOADERS setting, which is a list of loaders to use.
 # Each loader is expected to have this interface:
 #
-#    callable(name, dirs=[])
+#    callable(name)
 #
 # name is the template name.
-# dirs is an optional list of directories to search instead of TEMPLATE_DIRS.
 #
 # The loader should return a tuple of (template_source, path). The path returned
 # might be shown to the user for debugging purposes, so it should identify where
@@ -38,12 +37,12 @@ class BaseLoader(object):
     def __init__(self, *args, **kwargs):
         pass
 
-    def __call__(self, template_name, template_dirs=None):
-        return self.load_template(template_name, template_dirs)
+    def __call__(self, template_name):
+        return self.load_template(template_name)
 
-    def load_template(self, template_name, template_dirs=None):
-        source, display_name = self.load_template_source(template_name, template_dirs)
-        origin = make_origin(display_name, self.load_template_source, template_name, template_dirs)
+    def load_template(self, template_name):
+        source, display_name = self.load_template_source(template_name)
+        origin = make_origin(display_name, self.load_template_source, template_name)
         try:
             template = get_template_from_string(source, origin, template_name)
             return template, None
@@ -54,7 +53,7 @@ class BaseLoader(object):
             # not exist.
             return source, display_name
 
-    def load_template_source(self, template_name, template_dirs=None):
+    def load_template_source(self, template_name):
         """
         Returns a tuple containing the source and origin for the given template
         name.
@@ -71,16 +70,16 @@ class BaseLoader(object):
         pass
 
 class LoaderOrigin(Origin):
-    def __init__(self, display_name, loader, name, dirs):
+    def __init__(self, display_name, loader, name):
         super(LoaderOrigin, self).__init__(display_name)
-        self.loader, self.loadname, self.dirs = loader, name, dirs
+        self.loader, self.loadname = loader, name
 
     def reload(self):
-        return self.loader(self.loadname, self.dirs)[0]
+        return self.loader(self.loadname)[0]
 
-def make_origin(display_name, loader, name, dirs):
+def make_origin(display_name, loader, name):
     if settings.TEMPLATE_DEBUG and display_name:
-        return LoaderOrigin(display_name, loader, name, dirs)
+        return LoaderOrigin(display_name, loader, name)
     else:
         return None
 
@@ -117,7 +116,7 @@ def find_template_loader(loader):
     else:
         raise ImproperlyConfigured('Loader does not define a "load_template" callable template source loader')
 
-def find_template(name, dirs=None):
+def find_template(name):
     # Calculate template_source_loaders the first time the function is executed
     # because putting this logic in the module-level namespace may cause
     # circular import errors. See Django ticket #1292.
@@ -131,8 +130,8 @@ def find_template(name, dirs=None):
         template_source_loaders = tuple(loaders)
     for loader in template_source_loaders:
         try:
-            source, display_name = loader(name, dirs)
-            return (source, make_origin(display_name, loader, name, dirs))
+            source, display_name = loader(name)
+            return (source, make_origin(display_name, loader, name))
         except TemplateDoesNotExist:
             pass
     raise TemplateDoesNotExist(name)
diff --git a/django/template/loaders/app_directories.py b/django/template/loaders/app_directories.py
index b0560b4..2db1a47 100644
--- a/django/template/loaders/app_directories.py
+++ b/django/template/loaders/app_directories.py
@@ -3,62 +3,27 @@ Wrapper for loading templates from "templates" directories in INSTALLED_APPS
 packages.
 """
 
-import os
-import sys
+import pkgutil
 
 from django.conf import settings
-from django.core.exceptions import ImproperlyConfigured
 from django.template.base import TemplateDoesNotExist
 from django.template.loader import BaseLoader
 from django.utils._os import safe_join
-from django.utils.importlib import import_module
 
-# At compile time, cache the directories to search.
-fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
-app_template_dirs = []
-for app in settings.INSTALLED_APPS:
-    try:
-        mod = import_module(app)
-    except ImportError, e:
-        raise ImproperlyConfigured('ImportError %s: %s' % (app, e.args[0]))
-    template_dir = os.path.join(os.path.dirname(mod.__file__), 'templates')
-    if os.path.isdir(template_dir):
-        app_template_dirs.append(template_dir.decode(fs_encoding))
-
-# It won't change, so convert it to a tuple to save memory.
-app_template_dirs = tuple(app_template_dirs)
 
 class Loader(BaseLoader):
     is_usable = True
 
-    def get_template_sources(self, template_name, template_dirs=None):
-        """
-        Returns the absolute paths to "template_name", when appended to each
-        directory in "template_dirs". Any paths that don't lie inside one of the
-        template dirs are excluded from the result set, for security reasons.
-        """
-        if not template_dirs:
-            template_dirs = app_template_dirs
-        for template_dir in template_dirs:
+    def load_template_source(self, template_name):
+        for app in settings.INSTALLED_APPS:
+            resource = safe_join('/templates', template_name)[1:]
             try:
-                yield safe_join(template_dir, template_name)
-            except UnicodeDecodeError:
-                # The template dir name was a bytestring that wasn't valid UTF-8.
-                raise
-            except ValueError:
-                # The joined path was located outside of template_dir.
+                data = pkgutil.get_data(app, resource)
+                if data is not None:
+                    return data, 'app:' + app + ':'+resource
+            except Exception:
                 pass
 
-    def load_template_source(self, template_name, template_dirs=None):
-        for filepath in self.get_template_sources(template_name, template_dirs):
-            try:
-                file = open(filepath)
-                try:
-                    return (file.read().decode(settings.FILE_CHARSET), filepath)
-                finally:
-                    file.close()
-            except IOError:
-                pass
         raise TemplateDoesNotExist(template_name)
 
 _loader = Loader()
diff --git a/django/template/loaders/cached.py b/django/template/loaders/cached.py
index 4c25acd..eeb1e6e 100644
--- a/django/template/loaders/cached.py
+++ b/django/template/loaders/cached.py
@@ -27,23 +27,18 @@ class Loader(BaseLoader):
             self._cached_loaders = cached_loaders
         return self._cached_loaders
 
-    def find_template(self, name, dirs=None):
+    def find_template(self, name):
         for loader in self.loaders:
             try:
-                template, display_name = loader(name, dirs)
-                return (template, make_origin(display_name, loader, name, dirs))
+                template, display_name = loader(name)
+                return (template, make_origin(display_name, loader, name))
             except TemplateDoesNotExist:
                 pass
         raise TemplateDoesNotExist(name)
 
-    def load_template(self, template_name, template_dirs=None):
-        key = template_name
-        if template_dirs:
-            # If template directories were specified, use a hash to differentiate
-            key = '-'.join([template_name, hashlib.sha1('|'.join(template_dirs)).hexdigest()])
-
-        if key not in self.template_cache:
-            template, origin = self.find_template(template_name, template_dirs)
+    def load_template(self, template_name):
+        if template_name not in self.template_cache:
+            template, origin = self.find_template(template_name)
             if not hasattr(template, 'render'):
                 try:
                     template = get_template_from_string(template, origin, template_name)
@@ -53,8 +48,8 @@ class Loader(BaseLoader):
                     # we were asked to load. This allows for correct identification (later)
                     # of the actual template that does not exist.
                     return template, origin
-            self.template_cache[key] = template
-        return self.template_cache[key], None
+            self.template_cache[template_name] = template
+        return self.template_cache[template_name], None
 
     def reset(self):
         "Empty the template cache."
diff --git a/django/template/loaders/eggs.py b/django/template/loaders/eggs.py
deleted file mode 100644
index 42f87a4..0000000
--- a/django/template/loaders/eggs.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Wrapper for loading templates from eggs via pkg_resources.resource_string.
-
-try:
-    from pkg_resources import resource_string
-except ImportError:
-    resource_string = None
-
-from django.template.base import TemplateDoesNotExist
-from django.template.loader import BaseLoader
-from django.conf import settings
-
-class Loader(BaseLoader):
-    is_usable = resource_string is not None
-
-    def load_template_source(self, template_name, template_dirs=None):
-        """
-        Loads templates from Python eggs via pkg_resource.resource_string.
-
-        For every installed app, it tries to get the resource (app, template_name).
-        """
-        if resource_string is not None:
-            pkg_name = 'templates/' + template_name
-            for app in settings.INSTALLED_APPS:
-                try:
-                    return (resource_string(app, pkg_name).decode(settings.FILE_CHARSET), 'egg:%s:%s' % (app, pkg_name))
-                except:
-                    pass
-        raise TemplateDoesNotExist(template_name)
-
-_loader = Loader()
diff --git a/django/template/loaders/filesystem.py b/django/template/loaders/filesystem.py
index 51025ac..b926c6b 100644
--- a/django/template/loaders/filesystem.py
+++ b/django/template/loaders/filesystem.py
@@ -10,15 +10,16 @@ from django.utils._os import safe_join
 class Loader(BaseLoader):
     is_usable = True
 
-    def get_template_sources(self, template_name, template_dirs=None):
+    def __init__(self, template_dirs=None):
+        self.template_dirs = template_dirs or settings.TEMPLATE_DIRS
+
+    def get_template_sources(self, template_name):
         """
         Returns the absolute paths to "template_name", when appended to each
         directory in "template_dirs". Any paths that don't lie inside one of the
         template dirs are excluded from the result set, for security reasons.
         """
-        if not template_dirs:
-            template_dirs = settings.TEMPLATE_DIRS
-        for template_dir in template_dirs:
+        for template_dir in self.template_dirs:
             try:
                 yield safe_join(template_dir, template_name)
             except UnicodeDecodeError:
@@ -30,9 +31,9 @@ class Loader(BaseLoader):
                 # fatal).
                 pass
 
-    def load_template_source(self, template_name, template_dirs=None):
+    def load_template_source(self, template_name):
         tried = []
-        for filepath in self.get_template_sources(template_name, template_dirs):
+        for filepath in self.get_template_sources(template_name):
             try:
                 file = open(filepath)
                 try:
diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py
index 76c9bfc..158357c 100644
--- a/django/utils/translation/trans_real.py
+++ b/django/utils/translation/trans_real.py
@@ -6,6 +6,8 @@ import re
 import sys
 import gettext as gettext_module
 from threading import local
+import pkgutil
+import copy
 
 try:
     from cStringIO import StringIO
@@ -121,13 +123,30 @@ def translation(language):
 
         loc = to_locale(lang)
 
-        def _translation(path):
-            try:
-                t = gettext_module.translation('django', path, [loc], DjangoTranslation)
-                t.set_language(lang)
-                return t
-            except IOError:
-                return None
+        def _translation(path_or_appname, is_appname=False):
+            if is_appname:
+                nelangs = gettext_module._expand_lang(loc)
+                for nelang in nelangs:
+                    locpath = os.path.join('locale', nelang, 'LC_MESSAGES', 'django.mo')
+                    try:
+                        data = pkgutil.get_data(path_or_appname, locpath)
+                    except:
+                        continue
+
+                    if data is not None:
+                        t = DjangoTranslation(StringIO(data))
+                        t = copy.copy(t)
+                        break
+                else:
+                    return None
+            else:
+                try:
+                    t = gettext_module.translation('django', path_or_appname, [loc], DjangoTranslation)
+                except IOError:
+                    return None
+
+            t.set_language(lang)
+            return t
 
         res = _translation(globalpath)
 
@@ -140,8 +159,8 @@ def translation(language):
             res._info = res._info.copy()
             res._catalog = res._catalog.copy()
 
-        def _merge(path):
-            t = _translation(path)
+        def _merge(path_or_appname, is_appname=False):
+            t = _translation(path_or_appname, is_appname)
             if t is not None:
                 if res is None:
                     return t
@@ -150,11 +169,7 @@ def translation(language):
             return res
 
         for appname in reversed(settings.INSTALLED_APPS):
-            app = import_module(appname)
-            apppath = os.path.join(os.path.dirname(app.__file__), 'locale')
-
-            if os.path.isdir(apppath):
-                res = _merge(apppath)
+            res = _merge(appname, is_appname=True)
 
         for localepath in reversed(settings.LOCALE_PATHS):
             if os.path.isdir(localepath):
diff --git a/docs/ref/templates/api.txt b/docs/ref/templates/api.txt
index 03fef1d..8154d2c 100644
--- a/docs/ref/templates/api.txt
+++ b/docs/ref/templates/api.txt
@@ -655,9 +655,9 @@ class. Here are the template loaders that come with Django:
     This loader is enabled by default.
 
 ``django.template.loaders.app_directories.Loader``
-    Loads templates from Django apps on the filesystem. For each app in
-    :setting:`INSTALLED_APPS`, the loader looks for a ``templates``
-    subdirectory. If the directory exists, Django looks for templates in there.
+    Loads templates from Django apps. For each app in :setting:`INSTALLED_APPS`,
+    the loader looks for a ``templates`` subdirectory. If the directory exists, 
+    Django looks for templates in there.
 
     This means you can store templates with your individual apps. This also
     makes it easy to distribute Django apps with default templates.
@@ -672,18 +672,8 @@ class. Here are the template loaders that come with Django:
     * ``/path/to/myproject/polls/templates/foo.html``
     * ``/path/to/myproject/music/templates/foo.html``
 
-    Note that the loader performs an optimization when it is first imported: It
-    caches a list of which :setting:`INSTALLED_APPS` packages have a
-    ``templates`` subdirectory.
-
     This loader is enabled by default.
 
-``django.template.loaders.eggs.Loader``
-    Just like ``app_directories`` above, but it loads templates from Python
-    eggs rather than from the filesystem.
-
-    This loader is disabled by default.
-
 ``django.template.loaders.cached.Loader``
     By default, the templating system will read and compile your templates every
     time they need to be rendered. While the Django templating system is quite
@@ -838,8 +828,8 @@ of the ``load_template_source()`` method implemented there::
     class Loader(app_directories.Loader):
         is_usable = True
 
-        def load_template(self, template_name, template_dirs=None):
-            source, origin = self.load_template_source(template_name, template_dirs)
+        def load_template(self, template_name):
+            source, origin = self.load_template_source(template_name)
             template = Template(source)
             return template, origin
 
diff --git a/tests/regressiontests/i18n/eggs/localeegg.egg b/tests/regressiontests/i18n/eggs/localeegg.egg
new file mode 100644
index 0000000000000000000000000000000000000000..37b082e54f2cbbf5f7b7e88e61e34a15b22a9d6e
GIT binary patch
diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py
index faed4e5..4287573 100644
--- a/tests/regressiontests/i18n/tests.py
+++ b/tests/regressiontests/i18n/tests.py
@@ -4,6 +4,7 @@ from __future__ import absolute_import
 import datetime
 import decimal
 import os
+import sys
 import pickle
 from threading import local
 
@@ -868,6 +869,33 @@ class LocalePathsResolutionOrderI18NTests(ResolutionOrderI18NTests):
         with self.settings(INSTALLED_APPS=extended_apps):
             self.assertUgettext('Time', 'LOCALE_PATHS')
 
+class EggResolutionOrderI18NTests(ResolutionOrderI18NTests):
+
+    def setUp(self):
+        self.old_path = sys.path[:]
+        self.egg_dir = '%s/eggs' % os.path.dirname(__file__)
+        egg_name = '%s/localeegg.egg' % self.egg_dir
+        sys.path.insert(0, egg_name)
+        super(EggResolutionOrderI18NTests, self).setUp()
+
+    def tearDown(self):
+        super(EggResolutionOrderI18NTests, self).tearDown()
+        sys.path = self.old_path
+
+class EggAppResolutionOrderI18NTests(EggResolutionOrderI18NTests):
+
+    def setUp(self):
+        self.old_installed_apps = settings.INSTALLED_APPS
+        settings.INSTALLED_APPS = ['resolution'] + list(settings.INSTALLED_APPS)
+        super(EggAppResolutionOrderI18NTests, self).setUp()
+
+    def tearDown(self):
+        settings.INSTALLED_APPS = self.old_installed_apps
+        super(EggAppResolutionOrderI18NTests, self).tearDown()
+
+    def test_app_translation(self):
+        self.assertUgettext('Date/time', 'APP')
+
 class DjangoFallbackResolutionOrderI18NTests(ResolutionOrderI18NTests):
 
     def test_django_fallback(self):
diff --git a/tests/regressiontests/templates/eggs/templatesegg.egg b/tests/regressiontests/templates/eggs/templatesegg.egg
new file mode 100644
index 0000000000000000000000000000000000000000..9a8f315f883b1d895f1d5926ca7ad3134c23287f
GIT binary patch
diff --git a/tests/regressiontests/templates/loaders.py b/tests/regressiontests/templates/loaders.py
index 5c11916..287bfdd 100644
--- a/tests/regressiontests/templates/loaders.py
+++ b/tests/regressiontests/templates/loaders.py
@@ -1,7 +1,5 @@
 """
 Test cases for the template loaders
-
-Note: This test requires setuptools!
 """
 
 from django.conf import settings
@@ -10,65 +8,27 @@ if __name__ == '__main__':
     settings.configure()
 
 import sys
-import pkg_resources
 import imp
 import StringIO
 import os.path
 
 from django.template import TemplateDoesNotExist, Context
-from django.template.loaders.eggs import Loader as EggLoader
+from django.template.loaders.app_directories import Loader as EggLoader
 from django.template import loader
 from django.utils import unittest
 
 
-# Mock classes and objects for pkg_resources functions.
-class MockProvider(pkg_resources.NullProvider):
-    def __init__(self, module):
-        pkg_resources.NullProvider.__init__(self, module)
-        self.module = module
-
-    def _has(self, path):
-        return path in self.module._resources
-
-    def _isdir(self,path):
-        return False
-
-    def get_resource_stream(self, manager, resource_name):
-        return self.module._resources[resource_name]
-
-    def _get(self, path):
-        return self.module._resources[path].read()
-
-class MockLoader(object):
-    pass
-
-def create_egg(name, resources):
-    """
-    Creates a mock egg with a list of resources.
-
-    name: The name of the module.
-    resources: A dictionary of resources. Keys are the names and values the data.
-    """
-    egg = imp.new_module(name)
-    egg.__loader__ = MockLoader()
-    egg._resources = resources
-    sys.modules[name] = egg
-
-
 class EggLoaderTest(unittest.TestCase):
     def setUp(self):
-        pkg_resources._provider_factories[MockLoader] = MockProvider
-
-        self.empty_egg = create_egg("egg_empty", {})
-        self.egg_1 = create_egg("egg_1", {
-            os.path.normcase('templates/y.html') : StringIO.StringIO("y"),
-            os.path.normcase('templates/x.txt') : StringIO.StringIO("x"),
-        })
-        self._old_installed_apps = settings.INSTALLED_APPS
-        settings.INSTALLED_APPS = []
+        self.old_path = sys.path[:]
+        self.old_apps = settings.INSTALLED_APPS
+        self.egg_dir = '%s/eggs' % os.path.dirname(__file__)
+        egg_name = '%s/templatesegg.egg' % self.egg_dir
+        sys.path.append(egg_name)
 
     def tearDown(self):
-        settings.INSTALLED_APPS = self._old_installed_apps
+        settings.INSTALLED_APPS = self.old_apps
+        sys.path = self.old_path
 
     def test_empty(self):
         "Loading any template on an empty egg should fail"
@@ -88,7 +48,7 @@ class EggLoaderTest(unittest.TestCase):
         egg_loader = EggLoader()
         contents, template_name = egg_loader.load_template_source("y.html")
         self.assertEqual(contents, "y")
-        self.assertEqual(template_name, "egg:egg_1:templates/y.html")
+        self.assertEqual(template_name, u"app:egg_1:templates/y.html")
 
     def test_not_installed(self):
         "Loading an existent template from an egg not included in INSTALLED_APPS should fail"
diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
index e166f7e..d6d6991 100644
--- a/tests/regressiontests/templates/tests.py
+++ b/tests/regressiontests/templates/tests.py
@@ -39,14 +39,8 @@ from .nodelist import NodelistTest, ErrorIndexTest
 from .smartif import SmartIfTests
 from .response import (TemplateResponseTest, BaseTemplateResponseTest,
     CacheMiddlewareTest, SimpleTemplateResponseTest, CustomURLConfTest)
+from .loaders import RenderToStringTest, EggLoaderTest
 
-try:
-    from .loaders import RenderToStringTest, EggLoaderTest
-except ImportError, e:
-    if "pkg_resources" in e.message:
-        pass # If setuptools isn't installed, that's fine. Just move on.
-    else:
-        raise
 
 from . import filters
 
@@ -167,20 +161,16 @@ class Templates(unittest.TestCase):
         restore_warnings_state(self._warnings_state)
 
     def test_loaders_security(self):
-        ad_loader = app_directories.Loader()
-        fs_loader = filesystem.Loader()
         def test_template_sources(path, template_dirs, expected_sources):
             if isinstance(expected_sources, list):
                 # Fix expected sources so they are abspathed
                 expected_sources = [os.path.abspath(s) for s in expected_sources]
-            # Test the two loaders (app_directores and filesystem).
-            func1 = lambda p, t: list(ad_loader.get_template_sources(p, t))
-            func2 = lambda p, t: list(fs_loader.get_template_sources(p, t))
-            for func in (func1, func2):
-                if isinstance(expected_sources, list):
-                    self.assertEqual(func(path, template_dirs), expected_sources)
-                else:
-                    self.assertRaises(expected_sources, func, path, template_dirs)
+            # Test filesystem loader.
+            func = lambda p, t: list(filesystem.Loader(t).get_template_sources(p))
+            if isinstance(expected_sources, list):
+                self.assertEqual(func(path, template_dirs), expected_sources)
+            else:
+                self.assertRaises(expected_sources, func, path, template_dirs)
 
         template_dirs = ['/dir1', '/dir2']
         test_template_sources('index.html', template_dirs,
