diff --git a/django/core/management/commands/makemessages.py b/django/core/management/commands/makemessages.py
index 95c1da0..fa4ae19 100644
--- a/django/core/management/commands/makemessages.py
+++ b/django/core/management/commands/makemessages.py
@@ -44,11 +44,20 @@ def _popen(cmd):
     p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE, close_fds=os.name != 'nt', universal_newlines=True)
     return p.communicate()
 
-def walk(root, topdown=True, onerror=None, followlinks=False):
+def walk(root, topdown=True, onerror=None, followlinks=False, ignore_patterns=[], verbosity=0, stdout=None):
     """
     A version of os.walk that can follow symlinks for Python < 2.6
     """
+    norm_patterns = map(lambda p:p.endswith('/*') and p[:-2] or p, ignore_patterns)
     for dirpath, dirnames, filenames in os.walk(root, topdown, onerror):
+        remove_dirs = []
+        for dirname in dirnames:
+            if is_ignored(os.path.normpath(os.path.join(dirpath, dirname)), norm_patterns):
+                remove_dirs.append(dirname)
+        for dirname in remove_dirs:
+            dirnames.remove(dirname)
+            if verbosity > 1:
+                stdout.write('ignoring directory %s\n' % dirname)
         yield (dirpath, dirnames, filenames)
         if followlinks:
             for d in dirnames:
@@ -66,23 +75,24 @@ def is_ignored(path, ignore_patterns):
             return True
     return False
 
-def find_files(root, ignore_patterns, verbosity, symlinks=False):
+def find_files(root, ignore_patterns, verbosity, stdout, symlinks=False):
     """
     Helper function to get all files in the given root.
     """
     all_files = []
-    for (dirpath, dirnames, filenames) in walk(".", followlinks=symlinks):
+    for (dirpath, dirnames, filenames) in walk(".", followlinks=symlinks,
+            ignore_patterns=ignore_patterns, verbosity=verbosity, stdout=stdout):
         for f in filenames:
             norm_filepath = os.path.normpath(os.path.join(dirpath, f))
             if is_ignored(norm_filepath, ignore_patterns):
                 if verbosity > 1:
-                    sys.stdout.write('ignoring file %s in %s\n' % (f, dirpath))
+                    stdout.write('ignoring file %s in %s\n' % (f, dirpath))
             else:
                 all_files.extend([(dirpath, f)])
     all_files.sort()
     return all_files
 
-def copy_plural_forms(msgs, locale, domain, verbosity):
+def copy_plural_forms(msgs, locale, domain, verbosity, stdout):
     """
     Copies plural forms header contents from a Django catalog of locale to
     the msgs string, inserting it at the right place. msgs should be the
@@ -100,7 +110,7 @@ def copy_plural_forms(msgs, locale, domain, verbosity):
             m = plural_forms_re.search(open(django_po, 'rU').read())
             if m:
                 if verbosity > 1:
-                    sys.stderr.write("copying plural forms: %s\n" % m.group('value'))
+                    stdout.write("copying plural forms: %s\n" % m.group('value'))
                 lines = []
                 seen = False
                 for line in msgs.split('\n'):
@@ -133,7 +143,7 @@ def write_pot_file(potfile, msgs, file, work_file, is_templatized):
         f.close()
 
 def process_file(file, dirpath, potfile, domain, verbosity, extensions, wrap,
-        location):
+        location, stdout):
     """
     Extract translatable literals from :param file: for :param domain:
     creating or updating the :param potfile: POT file.
@@ -144,7 +154,7 @@ def process_file(file, dirpath, potfile, domain, verbosity, extensions, wrap,
     from django.utils.translation import templatize
 
     if verbosity > 1:
-        sys.stdout.write('processing file %s in %s\n' % (file, dirpath))
+        stdout.write('processing file %s in %s\n' % (file, dirpath))
     _, file_ext = os.path.splitext(file)
     if domain == 'djangojs' and file_ext in extensions:
         is_templatized = True
@@ -206,7 +216,7 @@ def process_file(file, dirpath, potfile, domain, verbosity, extensions, wrap,
     if is_templatized:
         os.unlink(work_file)
 
-def write_po_file(pofile, potfile, domain, locale, verbosity,
+def write_po_file(pofile, potfile, domain, locale, verbosity, stdout,
         copy_pforms, wrap, location, no_obsolete):
     """
     Creates of updates the :param pofile: PO file for :param domain: and :param
@@ -232,7 +242,7 @@ def write_po_file(pofile, potfile, domain, locale, verbosity,
             raise CommandError(
                 "errors happened while running msgmerge\n%s" % errors)
     elif copy_pforms:
-        msgs = copy_plural_forms(msgs, locale, domain, verbosity)
+        msgs = copy_plural_forms(msgs, locale, domain, verbosity, stdout)
     msgs = msgs.replace(
         "#. #-#-#-#-#  %s.pot (PACKAGE VERSION)  #-#-#-#-#\n" % domain, "")
     f = open(pofile, 'wb')
@@ -250,7 +260,7 @@ def write_po_file(pofile, potfile, domain, locale, verbosity,
 
 def make_messages(locale=None, domain='django', verbosity=1, all=False,
         extensions=None, symlinks=False, ignore_patterns=None, no_wrap=False,
-        no_location=False, no_obsolete=False):
+        no_location=False, no_obsolete=False, stdout=sys.stdout):
     """
     Uses the ``locale/`` directory from the Django SVN tree or an
     application/project to process all files with translatable literals for
@@ -312,7 +322,7 @@ def make_messages(locale=None, domain='django', verbosity=1, all=False,
 
     for locale in locales:
         if verbosity > 0:
-            print "processing language", locale
+            stdout.write("processing language %s" % locale)
         basedir = os.path.join(localedir, locale, 'LC_MESSAGES')
         if not os.path.isdir(basedir):
             os.makedirs(basedir)
@@ -324,12 +334,12 @@ def make_messages(locale=None, domain='django', verbosity=1, all=False,
             os.unlink(potfile)
 
         for dirpath, file in find_files(".", ignore_patterns, verbosity,
-                symlinks=symlinks):
+                stdout, symlinks=symlinks):
             process_file(file, dirpath, potfile, domain, verbosity, extensions,
-                    wrap, location)
+                    wrap, location, stdout)
 
         if os.path.exists(potfile):
-            write_po_file(pofile, potfile, domain, locale, verbosity,
+            write_po_file(pofile, potfile, domain, locale, verbosity, stdout,
                     not invoked_for_django, wrap, location, no_obsolete)
 
 
@@ -386,7 +396,7 @@ class Command(NoArgsCommand):
             extensions = handle_extensions(extensions or ['html', 'txt'])
 
         if verbosity > 1:
-            sys.stdout.write('examining files with the extensions: %s\n'
+            self.stdout.write('examining files with the extensions: %s\n'
                              % get_text_list(list(extensions), 'and'))
 
-        make_messages(locale, domain, verbosity, process_all, extensions, symlinks, ignore_patterns, no_wrap, no_location, no_obsolete)
+        make_messages(locale, domain, verbosity, process_all, extensions, symlinks, ignore_patterns, no_wrap, no_location, no_obsolete, self.stdout)
diff --git a/tests/regressiontests/i18n/commands/extraction.py b/tests/regressiontests/i18n/commands/extraction.py
index b1fc002..4eb7abc 100644
--- a/tests/regressiontests/i18n/commands/extraction.py
+++ b/tests/regressiontests/i18n/commands/extraction.py
@@ -4,6 +4,7 @@ from __future__ import with_statement
 import os
 import re
 import shutil
+from StringIO import StringIO
 
 from django.core import management
 from django.test import TestCase
@@ -177,7 +178,11 @@ class IgnoredExtractorTests(ExtractorTests):
     def test_ignore_option(self):
         os.chdir(self.test_dir)
         pattern1 = os.path.join('ignore_dir', '*')
-        management.call_command('makemessages', locale=LOCALE, verbosity=0, ignore_patterns=[pattern1])
+        stdout = StringIO()
+        management.call_command('makemessages', locale=LOCALE, verbosity=2,
+            ignore_patterns=[pattern1], stdout=stdout)
+        data = stdout.getvalue()
+        self.assertTrue("ignoring directory ignore_dir" in data)
         self.assertTrue(os.path.exists(self.PO_FILE))
         with open(self.PO_FILE, 'r') as fp:
             po_contents = fp.read()
