diff --git a/lms/djangoapps/instructor/enrollment.py b/lms/djangoapps/instructor/enrollment.py
index ca35291..da7874e 100644
--- a/lms/djangoapps/instructor/enrollment.py
+++ b/lms/djangoapps/instructor/enrollment.py
@@ -9,13 +9,16 @@ from django.contrib.auth.models import User
 from django.conf import settings
 from django.core.urlresolvers import reverse
 from django.core.mail import send_mail
+from django.utils.translation import override as override_language
 
 from student.models import CourseEnrollment, CourseEnrollmentAllowed
 from courseware.models import StudentModule
 from edxmako.shortcuts import render_to_string
+from lang_pref import LANGUAGE_KEY
 
 from submissions import api as sub_api  # installed from the edx-submissions repository
 from student.models import anonymous_id_for_user
+from openedx.core.djangoapps.user_api.models import UserPreference
 
 from microsite_configuration import microsite
 
@@ -71,7 +74,15 @@ class EmailEnrollmentState(object):
         }
 
 
-def enroll_email(course_id, student_email, auto_enroll=False, email_students=False, email_params=None):
+def get_user_email_language(user):
+    """
+    Return the language most appropriate for writing emails to user. Returns
+    None if the preference has not been set, or if the user does not exist.
+    """
+    return UserPreference.get_preference(user, LANGUAGE_KEY)
+
+
+def enroll_email(course_id, student_email, auto_enroll=False, email_students=False, email_params=None, language=None):
     """
     Enroll a student by email.
 
@@ -81,6 +92,7 @@ def enroll_email(course_id, student_email, auto_enroll=False, email_students=Fal
         enrolled in the course automatically.
     `email_students` determines if student should be notified of action by email.
     `email_params` parameters used while parsing email templates (a `dict`).
+    `language` is the language used to render the email.
 
     returns two EmailEnrollmentState's
         representing state before and after the action.
@@ -99,7 +111,7 @@ def enroll_email(course_id, student_email, auto_enroll=False, email_students=Fal
             email_params['message'] = 'enrolled_enroll'
             email_params['email_address'] = student_email
             email_params['full_name'] = previous_state.full_name
-            send_mail_to_student(student_email, email_params)
+            send_mail_to_student(student_email, email_params, language=language)
     else:
         cea, _ = CourseEnrollmentAllowed.objects.get_or_create(course_id=course_id, email=student_email)
         cea.auto_enroll = auto_enroll
@@ -107,20 +119,21 @@ def enroll_email(course_id, student_email, auto_enroll=False, email_students=Fal
         if email_students:
             email_params['message'] = 'allowed_enroll'
             email_params['email_address'] = student_email
-            send_mail_to_student(student_email, email_params)
+            send_mail_to_student(student_email, email_params, language=language)
 
     after_state = EmailEnrollmentState(course_id, student_email)
 
     return previous_state, after_state
 
 
-def unenroll_email(course_id, student_email, email_students=False, email_params=None):
+def unenroll_email(course_id, student_email, email_students=False, email_params=None, language=None):
     """
     Unenroll a student by email.
 
     `student_email` is student's emails e.g. "foo@bar.com"
     `email_students` determines if student should be notified of action by email.
     `email_params` parameters used while parsing email templates (a `dict`).
+    `language` is the language used to render the email.
 
     returns two EmailEnrollmentState's
         representing state before and after the action.
@@ -133,7 +146,7 @@ def unenroll_email(course_id, student_email, email_students=False, email_params=
             email_params['message'] = 'enrolled_unenroll'
             email_params['email_address'] = student_email
             email_params['full_name'] = previous_state.full_name
-            send_mail_to_student(student_email, email_params)
+            send_mail_to_student(student_email, email_params, language=language)
 
     if previous_state.allowed:
         CourseEnrollmentAllowed.objects.get(course_id=course_id, email=student_email).delete()
@@ -141,7 +154,7 @@ def unenroll_email(course_id, student_email, email_students=False, email_params=
             email_params['message'] = 'allowed_unenroll'
             email_params['email_address'] = student_email
             # Since no User object exists for this student there is no "full_name" available.
-            send_mail_to_student(student_email, email_params)
+            send_mail_to_student(student_email, email_params, language=language)
 
     after_state = EmailEnrollmentState(course_id, student_email)
 
@@ -169,7 +182,7 @@ def send_beta_role_email(action, user, email_params):
     else:
         raise ValueError("Unexpected action received '{}' - expected 'add' or 'remove'".format(action))
 
-    send_mail_to_student(user.email, email_params)
+    send_mail_to_student(user.email, email_params, language=get_user_email_language(user))
 
 
 def reset_student_attempts(course_id, student, module_state_key, delete_module=False):
@@ -279,7 +292,7 @@ def get_email_params(course, auto_enroll, secure=True):
     return email_params
 
 
-def send_mail_to_student(student, param_dict):
+def send_mail_to_student(student, param_dict, language=None):
     """
     Construct the email using templates and then send it.
     `student` is the student's email address (a `str`),
@@ -297,6 +310,10 @@ def send_mail_to_student(student, param_dict):
         `is_shib_course`: (a `boolean`)
     ]
 
+    `language` is the language used to render the email. If None the language
+    of the currently-logged in user (that is, the user sending the email) will
+    be used.
+
     Returns a boolean indicating whether the email was sent successfully.
     """
 
@@ -349,8 +366,9 @@ def send_mail_to_student(student, param_dict):
 
     subject_template, message_template = email_template_dict.get(message_type, (None, None))
     if subject_template is not None and message_template is not None:
-        subject = render_to_string(subject_template, param_dict)
-        message = render_to_string(message_template, param_dict)
+        subject, message = render_message_to_string(
+            subject_template, message_template, param_dict, language=language
+        )
 
     if subject and message:
         # Remove leading and trailing whitespace from body
@@ -366,6 +384,28 @@ def send_mail_to_student(student, param_dict):
         send_mail(subject, message, from_address, [student], fail_silently=False)
 
 
+def render_message_to_string(subject_template, message_template, param_dict, language=None):
+    """
+    Render a mail subject and message templates using the parameters from
+    param_dict and the given language. If language is None, the platform
+    default language is used.
+
+    Returns two strings that correspond to the rendered, translated email
+    subject and message.
+    """
+    with override_language(language):
+        return get_subject_and_message(subject_template, message_template, param_dict)
+
+
+def get_subject_and_message(subject_template, message_template, param_dict):
+    """
+    Return the rendered subject and message with the appropriate parameters.
+    """
+    subject = render_to_string(subject_template, param_dict)
+    message = render_to_string(message_template, param_dict)
+    return subject, message
+
+
 def uses_shib(course):
     """
     Used to return whether course has Shibboleth as the enrollment domain
diff --git a/lms/djangoapps/instructor/tests/test_api_email_localization.py b/lms/djangoapps/instructor/tests/test_api_email_localization.py
new file mode 100644
index 0000000..3c695cb
--- /dev/null
+++ b/lms/djangoapps/instructor/tests/test_api_email_localization.py
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+"""
+Unit tests for the localization of emails sent by instructor.api methods.
+"""
+
+from django.core import mail
+from django.core.urlresolvers import reverse
+from django.test import TestCase
+
+from courseware.tests.factories import InstructorFactory
+from lang_pref import LANGUAGE_KEY
+from student.models import CourseEnrollment
+from student.tests.factories import UserFactory
+from openedx.core.djangoapps.user_api.models import UserPreference
+from xmodule.modulestore.tests.factories import CourseFactory
+
+
+class TestInstructorAPIEnrollmentEmailLocalization(TestCase):
+    """
+    Test whether the enroll, unenroll and beta role emails are sent in the
+    proper language, i.e: the student's language.
+    """
+
+    def setUp(self):
+        # Platform language is English, instructor's language is Chinese,
+        # student's language is French, so the emails should all be sent in
+        # French.
+        self.course = CourseFactory.create()
+        self.instructor = InstructorFactory(course_key=self.course.id)
+        UserPreference.set_preference(self.instructor, LANGUAGE_KEY, 'zh-cn')
+        self.client.login(username=self.instructor.username, password='test')
+
+        self.student = UserFactory.create()
+        UserPreference.set_preference(self.student, LANGUAGE_KEY, 'fr')
+
+    def update_enrollement(self, action, student_email):
+        """
+        Update the current student enrollment status.
+        """
+        url = reverse('students_update_enrollment', kwargs={'course_id': self.course.id.to_deprecated_string()})
+        args = {'identifiers': student_email, 'email_students': 'true', 'action': action}
+        response = self.client.post(url, args)
+        return response
+
+    def check_outbox_is_french(self):
+        """
+        Check that the email outbox contains exactly one message for which both
+        the message subject and body contain a certain French string.
+        """
+        return self.check_outbox(u"Vous avez été")
+
+    def check_outbox(self, expected_message):
+        """
+        Check that the email outbox contains exactly one message for which both
+        the message subject and body contain a certain string.
+        """
+        self.assertEqual(1, len(mail.outbox))
+        self.assertIn(expected_message, mail.outbox[0].subject)
+        self.assertIn(expected_message, mail.outbox[0].body)
+
+    def test_enroll(self):
+        self.update_enrollement("enroll", self.student.email)
+
+        self.check_outbox_is_french()
+
+    def test_unenroll(self):
+        CourseEnrollment.enroll(
+            self.student,
+            self.course.id
+        )
+        self.update_enrollement("unenroll", self.student.email)
+
+        self.check_outbox_is_french()
+
+    def test_set_beta_role(self):
+        url = reverse('bulk_beta_modify_access', kwargs={'course_id': self.course.id.to_deprecated_string()})
+        self.client.post(url, {'identifiers': self.student.email, 'action': 'add', 'email_students': 'true'})
+
+        self.check_outbox_is_french()
+
+    def test_enroll_unsubscribed_student(self):
+        # Student is unknown, so the platform language should be used
+        self.update_enrollement("enroll", "newuser@hotmail.com")
+        self.check_outbox("You have been")
diff --git a/lms/djangoapps/instructor/tests/test_enrollment.py b/lms/djangoapps/instructor/tests/test_enrollment.py
index e120162..ffb8041 100644
--- a/lms/djangoapps/instructor/tests/test_enrollment.py
+++ b/lms/djangoapps/instructor/tests/test_enrollment.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 """
 Unit tests for instructor.enrollment methods.
 """
@@ -9,6 +10,8 @@ from courseware.models import StudentModule
 from django.conf import settings
 from django.test import TestCase
 from django.test.utils import override_settings
+from django.utils.translation import get_language
+from django.utils.translation import override as override_language
 from student.tests.factories import UserFactory
 from xmodule.modulestore.tests.factories import CourseFactory
 from xmodule.modulestore.tests.django_utils import TEST_DATA_MOCK_MODULESTORE
@@ -20,7 +23,8 @@ from instructor.enrollment import (
     get_email_params,
     reset_student_attempts,
     send_beta_role_email,
-    unenroll_email
+    unenroll_email,
+    render_message_to_string,
 )
 from opaque_keys.edx.locations import SlashSeparatedCourseKey
 
@@ -472,3 +476,50 @@ class TestGetEmailParams(ModuleStoreTestCase):
         self.assertEqual(result['course_about_url'], None)
         self.assertEqual(result['registration_url'], self.registration_url)
         self.assertEqual(result['course_url'], self.course_url)
+
+
+class TestRenderMessageToString(TestCase):
+    """
+    Test that email templates can be rendered in a language chosen manually.
+    """
+
+    def setUp(self):
+        self.subject_template = 'emails/enroll_email_allowedsubject.txt'
+        self.message_template = 'emails/enroll_email_allowedmessage.txt'
+        self.course = CourseFactory.create()
+
+    def get_email_params(self):
+        """
+        Returns a dictionary of parameters used to render an email.
+        """
+        email_params = get_email_params(self.course, True)
+        email_params["email_address"] = "user@example.com"
+        email_params["full_name"] = "Jean Reno"
+
+        return email_params
+
+    def get_subject_and_message(self, language):
+        """
+        Returns the subject and message rendered in the specified language.
+        """
+        return render_message_to_string(
+            self.subject_template,
+            self.message_template,
+            self.get_email_params(),
+            language=language
+        )
+
+    def test_subject_and_message_translation(self):
+        subject, message = self.get_subject_and_message('fr')
+        language_after_rendering = get_language()
+
+        you_have_been_invited_in_french = u"Vous avez été invité"
+        self.assertIn(you_have_been_invited_in_french, subject)
+        self.assertIn(you_have_been_invited_in_french, message)
+        self.assertEqual(settings.LANGUAGE_CODE, language_after_rendering)
+
+    def test_platform_language_is_used_for_logged_in_user(self):
+        with override_language('zh_CN'):    # simulate a user login
+            subject, message = self.get_subject_and_message(None)
+            self.assertIn("You have been", subject)
+            self.assertIn("You have been", message)
diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py
index 7738cfc..e50d45d 100644
--- a/lms/djangoapps/instructor/views/api.py
+++ b/lms/djangoapps/instructor/views/api.py
@@ -57,11 +57,12 @@ from instructor_task.api_helper import AlreadyRunningError
 from instructor_task.models import ReportStore
 import instructor.enrollment as enrollment
 from instructor.enrollment import (
+    get_user_email_language,
     enroll_email,
     send_mail_to_student,
     get_email_params,
     send_beta_role_email,
-    unenroll_email
+    unenroll_email,
 )
 from instructor.access import list_with_level, allow_access, revoke_access, update_forum_role
 from instructor.offline_gradecalc import student_grades
@@ -497,12 +498,14 @@ def students_update_enrollment(request, course_id):
         # First try to get a user object from the identifer
         user = None
         email = None
+        language = None
         try:
             user = get_student_from_identifier(identifier)
         except User.DoesNotExist:
             email = identifier
         else:
             email = user.email
+            language = get_user_email_language(user)
 
         try:
             # Use django.core.validators.validate_email to check email address
@@ -511,9 +514,13 @@ def students_update_enrollment(request, course_id):
             validate_email(email)  # Raises ValidationError if invalid
 
             if action == 'enroll':
-                before, after = enroll_email(course_id, email, auto_enroll, email_students, email_params)
+                before, after = enroll_email(
+                    course_id, email, auto_enroll, email_students, email_params, language=language
+                )
             elif action == 'unenroll':
-                before, after = unenroll_email(course_id, email, email_students, email_params)
+                before, after = unenroll_email(
+                    course_id, email, email_students, email_params, language=language
+                )
             else:
                 return HttpResponseBadRequest(strip_tags(
                     "Unrecognized action '{}'".format(action)