← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~cjwatson/launchpad/gender-neutral into lp:launchpad

 

Colin Watson has proposed merging lp:~cjwatson/launchpad/gender-neutral into lp:launchpad.

Commit message:
Use gender-neutral pronouns where appropriate.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/gender-neutral/+merge/283988

It's 2016, and we don't have to be dinosaurs.  Use gender-neutral pronouns where appropriate.

I did this by working through the output of:

  bzr grep -l '\b\(he\|him\|his\|himself\|she\|her\|herself\)\b'

... and applying singular they everywhere it made sense to do so, either in cases where the referent was indeterminate or where it was something like Sample Person or No Privileges Person.  There were many cases where real named people were being referred to, and I left those alone.

I considered using "themself" as well in a few places (see e.g. https://stancarey.wordpress.com/2012/05/31/reflecting-on-the-reflexive-pronoun-themself/), but decided to avoid ruffling too many feathers for now; I don't think any serious ambiguity is introduced where I've used "themselves".
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/gender-neutral into lp:launchpad.
=== modified file 'cronscripts/foaf-update-karma-cache.py'
--- cronscripts/foaf-update-karma-cache.py	2015-01-28 16:10:51 +0000
+++ cronscripts/foaf-update-karma-cache.py	2016-01-26 15:58:00 +0000
@@ -21,10 +21,10 @@
     def main(self):
         """Update the KarmaCache table for all valid Launchpad users.
 
-        For each Launchpad user with a preferred email address, calculate his
-        karmavalue for each category of actions we have and update his entry
-        in the KarmaCache table. If a user doesn't have an entry for that
-        category in KarmaCache a new one will be created.
+        For each Launchpad user with a preferred email address, calculate
+        their karmavalue for each category of actions we have and update
+        their entry in the KarmaCache table. If a user doesn't have an entry
+        for that category in KarmaCache a new one will be created.
 
         Entries in the KarmaTotalCache table will also be created/updated for
         each user which has entries in the KarmaCache table. Any user which

=== modified file 'database/sampledata/current-dev.sql'
--- database/sampledata/current-dev.sql	2015-11-26 15:46:38 +0000
+++ database/sampledata/current-dev.sql	2016-01-26 15:58:00 +0000
@@ -4253,14 +4253,14 @@
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (36, 7, 1, 'questiontitlechanged', 'Question title changed', 'User changed the title of a question in the Answer Tracker');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (37, 7, 3, 'questiondescriptionchanged', 'Question description changed', 'User changed the description of a question in the Answer Tracker');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (38, 7, 5, 'questionlinkedtobug', 'Question linked to a bug', 'User linked a question in the Answer Tracker to a bug.');
-INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (39, 7, 5, 'questionansweraccepted', 'Question owner accepted answer', 'User accepted one of the message as the actual answer to his question.');
+INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (39, 7, 5, 'questionansweraccepted', 'Question owner accepted answer', 'User accepted one of the messages as the actual answer to their question.');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (40, 7, 15, 'questionanswered', 'Answered question', 'User posed a message that was accepted by the question owner as answering the question.');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (41, 7, 0, 'questionrequestedinfo', 'Requested for information on a question', 'User post a message requesting for more information from a question owner in the Answer Tracker.');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (42, 7, 0, 'questiongaveinfo', 'Gave more information on a question', 'User replied to a message asking for more information on a question in the Answer Tracker.');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (43, 7, 0, 'questiongaveanswer', 'Gave answer on a question', 'User post a message containing an answer to a question in the Answer Tracker. This is distinct from having that message confirmed as solving the problem.');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (44, 7, 0, 'questionrejected', 'Rejected question', 'User rejected a question in the Answer Tracker.');
-INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (45, 7, 3, 'questionownersolved', 'Solved own question', 'User post a message explaining how he solved his own problem.');
-INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (46, 7, 0, 'questionreopened', 'Reopened question', 'User posed a message to reopen his question in the Answer Tracker.');
+INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (45, 7, 3, 'questionownersolved', 'Solved own question', 'User posted a message explaining how they solved their own problem.');
+INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (46, 7, 0, 'questionreopened', 'Reopened question', 'User posted a message to reopen their question in the Answer Tracker.');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (47, 7, 1, 'questionasked', 'Asked question', 'User asked a question in the Answer Tracker.');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (48, 7, 5, 'faqcreated', 'FAQ created', 'User create a new FAQ in Launchpad.');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (49, 7, 1, 'faqedited', 'FAQ edited', 'User updated the details of a FAQ in Launchpad.');

=== modified file 'database/sampledata/current.sql'
--- database/sampledata/current.sql	2015-11-26 15:46:38 +0000
+++ database/sampledata/current.sql	2016-01-26 15:58:00 +0000
@@ -4179,14 +4179,14 @@
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (36, 7, 1, 'questiontitlechanged', 'Question title changed', 'User changed the title of a question in the Answer Tracker');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (37, 7, 3, 'questiondescriptionchanged', 'Question description changed', 'User changed the description of a question in the Answer Tracker');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (38, 7, 5, 'questionlinkedtobug', 'Question linked to a bug', 'User linked a question in the Answer Tracker to a bug.');
-INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (39, 7, 5, 'questionansweraccepted', 'Question owner accepted answer', 'User accepted one of the message as the actual answer to his question.');
+INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (39, 7, 5, 'questionansweraccepted', 'Question owner accepted answer', 'User accepted one of the messages as the actual answer to their question.');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (40, 7, 15, 'questionanswered', 'Answered question', 'User posed a message that was accepted by the question owner as answering the question.');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (41, 7, 0, 'questionrequestedinfo', 'Requested for information on a question', 'User post a message requesting for more information from a question owner in the Answer Tracker.');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (42, 7, 0, 'questiongaveinfo', 'Gave more information on a question', 'User replied to a message asking for more information on a question in the Answer Tracker.');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (43, 7, 0, 'questiongaveanswer', 'Gave answer on a question', 'User post a message containing an answer to a question in the Answer Tracker. This is distinct from having that message confirmed as solving the problem.');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (44, 7, 0, 'questionrejected', 'Rejected question', 'User rejected a question in the Answer Tracker.');
-INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (45, 7, 3, 'questionownersolved', 'Solved own question', 'User post a message explaining how he solved his own problem.');
-INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (46, 7, 0, 'questionreopened', 'Reopened question', 'User posed a message to reopen his question in the Answer Tracker.');
+INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (45, 7, 3, 'questionownersolved', 'Solved own question', 'User posted a message explaining how they solved their own problem.');
+INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (46, 7, 0, 'questionreopened', 'Reopened question', 'User posted a message to reopen their question in the Answer Tracker.');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (47, 7, 1, 'questionasked', 'Asked question', 'User asked a question in the Answer Tracker.');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (48, 7, 5, 'faqcreated', 'FAQ created', 'User create a new FAQ in Launchpad.');
 INSERT INTO karmaaction (id, category, points, name, title, summary) VALUES (49, 7, 1, 'faqedited', 'FAQ edited', 'User updated the details of a FAQ in Launchpad.');

=== modified file 'database/schema/comments.sql'
--- database/schema/comments.sql	2015-07-21 09:04:01 +0000
+++ database/schema/comments.sql	2016-01-26 15:58:00 +0000
@@ -934,7 +934,7 @@
 COMMENT ON COLUMN TeamMembership.proposed_by IS 'The user who proposed the person as member of the team.';
 COMMENT ON COLUMN TeamMembership.proponent_comment IS 'The comment left by the proponent.';
 COMMENT ON COLUMN TeamMembership.date_proposed IS 'The date of the proposal.';
-COMMENT ON COLUMN TeamMembership.acknowledged_by IS 'The member (or someone acting on his behalf) who accepts an invitation to join a team';
+COMMENT ON COLUMN TeamMembership.acknowledged_by IS 'The member (or someone acting on their behalf) who accepts an invitation to join a team';
 COMMENT ON COLUMN TeamMembership.date_acknowledged IS 'The date of acknowledgement.';
 COMMENT ON COLUMN TeamMembership.acknowledger_comment IS 'The comment left by the person who acknowledged the membership.';
 COMMENT ON COLUMN TeamMembership.reviewed_by IS 'The team admin who reviewed (approved/declined) the membership.';
@@ -1289,7 +1289,7 @@
 COMMENT ON COLUMN PersonLocation.longitude IS 'The longitude this person has given for their default location.';
 COMMENT ON COLUMN PersonLocation.last_modified_by IS 'The person who last updated this record. We allow people to provide location and time zone information for other users, when those users have not specified their own location. This allows people to garden the location information for their teams, for example, like a wiki.';
 COMMENT ON COLUMN PersonLocation.date_last_modified IS 'The date this record was last modified.';
-COMMENT ON COLUMN PersonLocation.locked IS 'Whether or not this record can be modified by someone other than the person himself?';
+COMMENT ON COLUMN PersonLocation.locked IS 'Whether or not this record can be modified by someone other than the person themselves?';
 COMMENT ON COLUMN PersonLocation.visible IS 'Should this person''s location and time zone be visible to others?';
 
 
@@ -1818,7 +1818,7 @@
 COMMENT ON COLUMN Vote.poll IS 'The poll for which this vote applies.';
 COMMENT ON COLUMN Vote.preference IS 'Used to identify in what order the options were chosen by a given user (in case of preferential voting).';
 COMMENT ON COLUMN Vote.option IS 'The choosen option.';
-COMMENT ON COLUMN Vote.token IS 'A unique token that''s give to the user so he can change his vote later.';
+COMMENT ON COLUMN Vote.token IS 'A unique token that''s given to the user so they can change their vote later.';
 
 -- VoteCast
 COMMENT ON TABLE VoteCast IS 'Here we store who has already voted in a poll, to ensure they do not vote again, and potentially to notify people that they may still vote.';

=== modified file 'lib/lp/answers/browser/question.py'
--- lib/lp/answers/browser/question.py	2015-10-26 14:54:43 +0000
+++ lib/lp/answers/browser/question.py	2016-01-26 15:58:00 +0000
@@ -548,8 +548,8 @@
 class QuestionAddView(QuestionSupportLanguageMixin, LaunchpadFormView):
     """Multi-page add view.
 
-    The user enters first his question summary and then he is shown a list
-    of similar results before adding the question.
+    The user enters first their question summary and then they are shown a
+    list of similar results before adding the question.
     """
     label = _('Ask a question')
 
@@ -988,7 +988,7 @@
         self.context.giveAnswer(self.user, data['message'])
         # Owners frequently solve their questions, but their messages imply
         # that another user provided an answer. When a question has answers
-        # that can be confirmed, suggest to the owner that he use the
+        # that can be confirmed, suggest to the owner that they use the
         # confirmation button.
         if self.context.can_confirm_answer:
             msgid = _("Your question is solved. If a particular message "
@@ -1075,7 +1075,7 @@
     def _addNotificationAndHandlePossibleSubscription(self, message, data):
         """Post-processing work common to all workflow actions.
 
-        Adds a notification, subscribe the user if he checked the
+        Adds a notification, subscribe the user if they checked the
         'Email me...' option and redirect to the question page.
         """
         self.request.response.addNotification(message)

=== modified file 'lib/lp/answers/browser/questiontarget.py'
--- lib/lp/answers/browser/questiontarget.py	2014-11-24 01:30:25 +0000
+++ lib/lp/answers/browser/questiontarget.py	2016-01-26 15:58:00 +0000
@@ -765,10 +765,10 @@
         """Check or update the Person's preferred languages as needed.
 
         Answer contacts must tell Launchpad in which languages they provide
-        help. If the Person has not already set his preferred languages, they
-        are set to his browser languages. In the case of a team without
-        languages, only English is added to the preferred languages. When
-        languages are added, a notification is added to the response.
+        help. If the Person has not already set their preferred languages,
+        they are set to their browser languages. In the case of a team
+        without languages, only English is added to the preferred languages.
+        When languages are added, a notification is added to the response.
         """
         if len(person_or_team.languages) > 0:
             return

=== modified file 'lib/lp/answers/browser/tests/views.txt'
--- lib/lp/answers/browser/tests/views.txt	2015-12-01 05:26:11 +0000
+++ lib/lp/answers/browser/tests/views.txt	2016-01-26 15:58:00 +0000
@@ -100,7 +100,7 @@
     >>> getAvailableActionNames(workflow_harness.view)
     []
 
-When question is in the OPEN state, the owner can comment, answer his
+When question is in the OPEN state, the owner can comment, answer their
 own question or provide more information.
 
     >>> login('test@xxxxxxxxxxxxx')
@@ -108,7 +108,7 @@
     >>> getAvailableActionNames(workflow_harness.view)
     ['comment', 'giveinfo', 'selfanswer']
 
-But when another user sees the question, he can comment, provide an
+But when another user sees the question, they can comment, provide an
 answer or request more information.
 
     >>> login('no-priv@xxxxxxxxxxxxx')
@@ -147,7 +147,7 @@
     >>> getAvailableActionNames(workflow_harness.view)
     ['comment', 'giveinfo', 'selfanswer']
 
-If he replies with the requested information, the question is moved back
+If they reply with the requested information, the question is moved back
 to the OPEN state.
 
     >>> form = {
@@ -184,8 +184,8 @@
     '.../+question/2'
 
 Once the question is answered, the set of possible actions for the
-question owner changes. He can now either comment, confirm the answer,
-answer the problem himself, or reopen the request because that answer
+question owner changes. They can now either comment, confirm the answer,
+answer the problem themselves, or reopen the request because that answer
 isn't working.
 
     >>> login('test@xxxxxxxxxxxxx')
@@ -193,7 +193,7 @@
     >>> getAvailableActionNames(workflow_harness.view)
     ['comment', 'confirm', 'reopen', 'selfanswer']
 
-Let's say he confirms the previous answer, in this case, the question
+Let's say they confirm the previous answer, in this case, the question
 will move to the 'SOLVED' state. Note that the UI doesn't enable the
 user to enter a confirmation message at that stage.
 
@@ -266,7 +266,7 @@
     >>> workflow_harness.redirectionTarget()
     '.../+question/2'
 
-When the question owner answers his own question, it is moved straight
+When the question owner answers their own question, it is moved straight
 to the SOLVED state. The question owner is attributed as the answerer,
 but no answer message is assigned to the answer.
 
@@ -553,12 +553,12 @@
 is used in browser forms to create a vocabulary containing only the
 languages that are likely to interest the user.
 
-When the user has not configured his preferred languages, the vocabulary
+When the user has not configured their preferred languages, the vocabulary
 will contain languages from the HTTP request, or the most likely
 interesting languages based on GeoIP information.
 
-For example, if the user doesn't log in and his browser is configured to
-accept brazilian Portuguese, the vocabulary will contain the languages
+For example, if the user doesn't log in and their browser is configured to
+accept Brazilian Portuguese, the vocabulary will contain the languages
 spoken in South Africa (because the 127.0.0.1 IP address is mapped to
 South Africa in the tests).
 
@@ -573,7 +573,7 @@
     >>> sorted([lang.code for lang in languages])
     [u'af', u'en', u'pt_BR', u'st', u'xh', u'zu']
 
-If the user logs in but didn't configure his preferred languages, the
+If the user logs in but didn't configure their preferred languages, the
 same logic is used to find the languages:
 
     >>> login('test@xxxxxxxxxxxxx')
@@ -586,7 +586,7 @@
     >>> sorted(lang.code for lang in languages)
     [u'af', u'en', u'pt_BR', u'st', u'xh', u'zu']
 
-But if the user configured his preferred languages, only these are used:
+But if the user configured their preferred languages, only these are used:
 
     >>> login('carlos@xxxxxxxxxxxxx')
     >>> user = getUtility(ILaunchBag).user
@@ -654,7 +654,7 @@
 Like all operations involving languages in the Answer Tracker, we ignore
 all other English variants.
 
-When the user is not logged in, or didn't define his preferred
+When the user is not logged in, or didn't define their preferred
 languages, the set will be initialized from the request. That's the
 languages configured in the browser, plus other inferred from the GeoIP
 database.
@@ -940,8 +940,8 @@
 ManageAnswerContactView
 -----------------------
 
-That view is used by a user to register himself or any team he
-administrates as an answer contact for the project.
+That view is used by a user to register themselves or any team they
+administer as an answer contact for the project.
 
 Jeff Waugh is an administrator for the Ubuntu Team. Thus he can register
 himself or the Ubuntu Team as answer contact for ubuntu:
@@ -1003,7 +1003,7 @@
     You have been added as an answer contact for Ubuntu.
 
 The same view is used to remove answer contact registrations. The user
-can only remove his own registration.
+can only remove their own registration.
 
     >>> request = LaunchpadTestRequest(
     ...     method='POST', form={

=== modified file 'lib/lp/answers/doc/karma.txt'
--- lib/lp/answers/doc/karma.txt	2011-04-26 15:44:26 +0000
+++ lib/lp/answers/doc/karma.txt	2016-01-26 15:58:00 +0000
@@ -5,7 +5,7 @@
 very important that we acknowledge their work and give them some karma
 points.
 
-These karma points are assigned to a user when he performs one of the
+These karma points are assigned to a user when they perform one of the
 actions we consider to be a reasonable contribution.
 
     >>> from lp.registry.interfaces.person import IPersonSet
@@ -129,7 +129,7 @@
 Confirming that the problem is solved
 .....................................
 
-When the user confirms that his problem is solved, karma will be given
+When the user confirms that their problem is solved, karma will be given
 for accepting an answer. The person whose answer was accepted will also
 receives karma.
 
@@ -201,7 +201,7 @@
 ...................
 
 There is a special karma action to cover the case when the question
-owner comes back to provide an answer to his own problem. We no longer
+owner comes back to provide an answer to their own problem. We no longer
 award karma for the questionownersolved action. It remains among the
 answers karma actions so that we can continue to calculate karma for
 persons who were awarded it in the past.

=== modified file 'lib/lp/answers/doc/notifications.txt'
--- lib/lp/answers/doc/notifications.txt	2015-12-01 05:26:11 +0000
+++ lib/lp/answers/doc/notifications.txt	2016-01-26 15:58:00 +0000
@@ -37,7 +37,7 @@
 email address. Answer's uses real names to help the questioner and the
 answerers communicate. The Reply-To address is the question's address.
 When using the mail client's reply feature, the user should clearly see
-that he is replying to the question and not the user. [sic] Kiko and
+that they are replying to the question and not the user. [sic] Kiko and
 Danilo have a story worth telling.
 
     >>> add_notification = notifications[0]
@@ -71,7 +71,7 @@
     >>> ubuntu.addAnswerContact(ubuntu_team, ubuntu_team.teamowner)
     True
 
-And assign this question to Foo Bar, so that he will also receive
+And assign this question to Foo Bar, so that they will also receive
 notifications:
 
     >>> login('foo.bar@xxxxxxxxxxxxx')
@@ -172,7 +172,7 @@
     >>> len(notifications)
     0
 
-After receiving that notification, Foo Bar unassigned himself:
+After receiving that notification, Foo Bar unassigned themselves:
 
     >>> ubuntu_question.assignee = None
 
@@ -298,7 +298,7 @@
     this email or enter your reply at the following page:
     http://.../ubuntu/+question/...
 
-Of course, if the owner unsubscribe from the question, he won't receives
+Of course, if the owner unsubscribes from the question, they won't receive
 a notification.
 
     >>> login('test@xxxxxxxxxxxxx')

=== modified file 'lib/lp/answers/doc/person.txt'
--- lib/lp/answers/doc/person.txt	2015-07-07 12:46:21 +0000
+++ lib/lp/answers/doc/person.txt	2016-01-26 15:58:00 +0000
@@ -233,7 +233,7 @@
     ...            for language in foo_bar.getQuestionLanguages()))
     en, pt_BR
 
-...and questions for which he's the answerer...
+...and questions for which they're the answerer...
 
     >>> es_question = getUtility(IQuestionSet).get(12)
     >>> es_question.reject(foo_bar_raw, 'Reject question.')
@@ -269,8 +269,8 @@
 ====================
 
 IQuestionsPerson defines getDirectAnswerQuestionTargets that can be used to
-retrieve a list of IQuestionTargets that a person subscribed himself to as an
-answer contact.
+retrieve a list of IQuestionTargets that a person subscribed themselves to
+as an answer contact.
 
     >>> no_priv_raw = personset.getByName('no-priv')
     >>> no_priv = IQuestionsPerson(no_priv_raw)
@@ -295,7 +295,7 @@
 
 IQuestionsPerson defines getTeamAnswerQuestionTargets that retrieves a list of
 IQuestionTargets that the person is subscribed to indirectly as an answer
-contact through his team membership.
+contact through their team membership.
 
     >>> landscape_team = personset.getByName("landscape-developers")
     >>> ignored = landscape_team.addMember(no_priv_raw, foo_bar_raw)

=== modified file 'lib/lp/answers/doc/question.txt'
--- lib/lp/answers/doc/question.txt	2015-09-29 05:04:44 +0000
+++ lib/lp/answers/doc/question.txt	2016-01-26 15:58:00 +0000
@@ -340,10 +340,10 @@
 Unsupported questions
 =====================
 
-While a Person may ask questions in his language of choice, that does not mean
-that indirect subscribers (Answer Contacts) to an IQuestionTarget speak that
-language.  IQuestionTarget can return a list Questions in languages that are
-not supported.
+While a Person may ask questions in their language of choice, that does not
+mean that indirect subscribers (Answer Contacts) to an IQuestionTarget speak
+that language.  IQuestionTarget can return a list of Questions in languages
+that are not supported.
 
     >>> unsupported_questions = firefox.searchQuestions(unsupported=True)
     >>> sorted(question.title for question in unsupported_questions)

=== modified file 'lib/lp/answers/doc/questiontarget.txt'
--- lib/lp/answers/doc/questiontarget.txt	2015-09-25 02:33:15 +0000
+++ lib/lp/answers/doc/questiontarget.txt	2016-01-26 15:58:00 +0000
@@ -309,7 +309,7 @@
 ---------------------------
 
 You can search among the questions that need attention.  A question needs the
-attention of a user if he owns it and if it is in the NEEDSINFO or ANSWERED
+attention of a user if they own it and if it is in the NEEDSINFO or ANSWERED
 state.  Questions on which the user gave an answer or requested for more
 information, and that are back in the OPEN state, are also included.
 
@@ -410,7 +410,7 @@
     >>> target.addAnswerContact(name18, name18)
     False
 
-An answer contact must have at least one language among his preferred
+An answer contact must have at least one language among their preferred
 languages.
 
     >>> sample_person = getUtility(IPersonSet).getByName('name12')

=== modified file 'lib/lp/answers/doc/workflow.txt'
--- lib/lp/answers/doc/workflow.txt	2012-12-26 01:32:19 +0000
+++ lib/lp/answers/doc/workflow.txt	2016-01-26 15:58:00 +0000
@@ -79,15 +79,15 @@
 The following scenarios are now possible.
 
 
-1) Another user helps the submitter with his question
-=====================================================
+1) Another user helps the submitter with their question
+=======================================================
 
 The most common scenario is where another user comes to help the submitter and
-answers his question.  This may involve exchanging information with the
+answers their question.  This may involve exchanging information with the
 submitter to clarify the question.
 
 The requestInfo() method is used to ask the user for more information.  This
-method takes two mandatory parameters: the user asking the question and his
+method takes two mandatory parameters: the user asking the question and their
 question.  It can also takes a 'datecreated' parameter specifying the creation
 date of the question (which defaults to 'now').
 
@@ -188,7 +188,7 @@
     True
 
 At that point, the question is considered answered, but we don't have
-feedback from the user on whether it solved his problem or not.  If it
+feedback from the user on whether it solved their problem or not.  If it
 doesn't, the user can reopen the question.
 
     >>> login('no-priv@xxxxxxxxxxxxx')
@@ -228,8 +228,9 @@
     >>> print question.status.name
     ANSWERED
 
-The question owner will hopefully come back to confirm that his problem is
-solved.  He can specify which answer message helped him solved his problem.
+The question owner will hopefully come back to confirm that their problem is
+solved.  They can specify which answer message helped them solve their
+problem.
 
     >>> login('no-priv@xxxxxxxxxxxxx')
     >>> two_weeks_from_now = now + timedelta(days=14)
@@ -262,9 +263,9 @@
 =================
 
 In this scenario the user comes back to give the solution to the question
-himself.  The question owner can choose a best answer message later on.  The
-workflow permits the question owner to choose an answer before or after the
-question status is set to SOLVED.
+themselves.  The question owner can choose a best answer message later on.
+The workflow permits the question owner to choose an answer before or after
+the question status is set to SOLVED.
 
 A new question is posed.
 
@@ -280,7 +281,8 @@
     ...     "to install to. You must mess with the hardware to trick "
     ...     "the core chips to let it install. You may not want to do this.")
 
-The question has researched the problem, and has comes to a solution himself.
+The question owner has researched the problem, and has come to a solution
+themselves.
 
     >>> login('no-priv@xxxxxxxxxxxxx')
     >>> self_answer_message = question.giveAnswer(
@@ -310,7 +312,7 @@
     >>> print question.answer
     None
 
-The question owner can still specify which message helped him solved his
+The question owner can still specify which message helped them solve their
 problem.  The confirmAnswer() method is used when the question owner chooses
 another user's answer as a best answer.  The status will remain SOLVED.  The
 'answerer' will be the message owner, and the 'answer' will be the message.
@@ -464,7 +466,7 @@
 Many other scenarios are possible and some are likely more common than others.
 For example, it is likely that a user will directly answer a question without
 asking for other information first.  Sometimes, the original questioner won't
-come back to confirm that an answer solved his problem.
+come back to confirm that an answer solved their problem.
 
 Another likely scenario is where the question will expire in the NEEDSINFO
 state because the question owner doesn't reply to the request for more
@@ -577,7 +579,7 @@
 Users with launchpad.Moderator privileges, which are answer contacts,
 question target owners, and admins, can assign someone to answer a question.
 
-Sample Person is an answer contact for ubuntu, so he can set the assignee.
+Sample Person is an answer contact for ubuntu, so they can set the assignee.
 
     >>> login('test@xxxxxxxxxxxxx')
     >>> question.assignee = stub

=== modified file 'lib/lp/answers/enums.py'
--- lib/lp/answers/enums.py	2013-01-07 02:40:55 +0000
+++ lib/lp/answers/enums.py	2016-01-26 15:58:00 +0000
@@ -285,7 +285,7 @@
     SOLVED = DBItem(20, """
         Solved
 
-        The submitter confirmed that an answer solved his question.
+        The submitter confirmed that an answer solved their question.
         """)
 
     EXPIRED = DBItem(25, """

=== modified file 'lib/lp/answers/interfaces/question.py'
--- lib/lp/answers/interfaces/question.py	2015-07-21 09:04:01 +0000
+++ lib/lp/answers/interfaces/question.py	2016-01-26 15:58:00 +0000
@@ -107,7 +107,8 @@
     answer = exported(Reference(
         title=_('Answer'), required=False,
         description=_("The IQuestionMessage that contains the answer "
-            "confirmed by the owner as providing a solution to his problem."),
+            "confirmed by the owner as providing a solution to their "
+            "problem."),
         schema=IQuestionMessage),
         readonly=True, as_of="devel")
     datecreated = exported(Datetime(
@@ -310,7 +311,7 @@
 
     can_confirm_answer = Attribute(
         'Whether the question is in a state for the question owner to '
-        'confirm that an answer solved his problem.')
+        'confirm that an answer solved their problem.')
 
     def confirmAnswer(comment, answer=None, datecreated=None):
         """Confirm that a solution to the question was found.

=== modified file 'lib/lp/answers/interfaces/questionsperson.py'
--- lib/lp/answers/interfaces/questionsperson.py	2013-01-07 02:40:55 +0000
+++ lib/lp/answers/interfaces/questionsperson.py	2016-01-26 15:58:00 +0000
@@ -40,7 +40,7 @@
         """Return a list of IQuestionTargets that a person is subscribed to.
 
         This will return IQuestionTargets that the person is registered as an
-        answer contact because he subscribed himself.
+        answer contact because they subscribed themselves.
         """
 
     @operation_returns_collection_of(Interface)  # IQuestionTarget
@@ -50,7 +50,7 @@
         """Return a list of IQuestionTargets that are indirect subscriptions.
 
         This will return IQuestionTargets that the person or team is
-        registered as an answer contact because of his membership in a team.
+        registered as an answer contact because of their membership in a team.
         """
 
     @operation_parameters(

=== modified file 'lib/lp/answers/interfaces/questiontarget.py'
--- lib/lp/answers/interfaces/questiontarget.py	2013-01-07 02:40:55 +0000
+++ lib/lp/answers/interfaces/questiontarget.py	2016-01-26 15:58:00 +0000
@@ -83,7 +83,7 @@
     def getAnswerContactsForLanguage(language):
         """Return the list of Persons that provide support for a language.
 
-        An answer contact supports questions in his preferred languages.
+        An answer contact supports questions in their preferred languages.
         """
 
     def getAnswerContactRecipients(language):
@@ -105,7 +105,7 @@
         """Return a list of languages spoken by at the answer contacts.
 
         An answer contact is considered to speak a given language if that
-        language is listed as one of his preferred languages.
+        language is listed as one of their preferred languages.
         """
 
     answer_contacts = List(

=== modified file 'lib/lp/answers/notification.py'
--- lib/lp/answers/notification.py	2015-07-10 15:31:28 +0000
+++ lib/lp/answers/notification.py	2016-01-26 15:58:00 +0000
@@ -342,7 +342,7 @@
 
 
 class QuestionModifiedOwnerNotification(QuestionModifiedDefaultNotification):
-    """Notification sent to the owner when his question is modified."""
+    """Notification sent to the owner when their question is modified."""
 
     recipient_set = QuestionRecipientSet.ASKER
     # These actions will be done by the owner, so use the second person.

=== modified file 'lib/lp/answers/stories/answer-contact-report.txt'
--- lib/lp/answers/stories/answer-contact-report.txt	2015-06-27 04:10:49 +0000
+++ lib/lp/answers/stories/answer-contact-report.txt	2016-01-26 15:58:00 +0000
@@ -18,7 +18,7 @@
     No Privileges Person is not an answer contact for any project.
 
 But when the person is an answer contact, the page displays the project
-he registered for.
+they registered for.
 
     >>> anon_browser.open('http://answers.launchpad.dev/~name16')
     >>> anon_browser.getLink('Answer contact for').click()
@@ -43,8 +43,9 @@
     >>> print anon_browser.title
     Questions : gnomebaker
 
-When the user is logged in, and he is visiting this page in his profile
-he will see a link after each project to manage his registration.
+When the user is logged in, and they are visiting this page in their
+profile, they will see a link after each project to manage their
+registration.
 
     >>> browser.addHeader('Authorization', 'Basic test@xxxxxxxxxxxxx:test')
     >>> browser.open(
@@ -63,8 +64,8 @@
     >>> print browser.title
     Answer contact for...
 
-The Remove yourself/team links only appears in his profile. He cannot
-see the link for other users
+The Remove yourself/team links only appears in their profile. They cannot
+see the link for other users.
 
     >>> browser.open(
     ...     'http://answers.launchpad.dev/~name16')

=== modified file 'lib/lp/answers/stories/distribution-package-answer-contact.txt'
--- lib/lp/answers/stories/distribution-package-answer-contact.txt	2014-11-27 22:13:36 +0000
+++ lib/lp/answers/stories/distribution-package-answer-contact.txt	2016-01-26 15:58:00 +0000
@@ -32,8 +32,8 @@
     ...     anon_browser.contents,
     ...     'Answer contacts for evolution in Ubuntu')
 
-To register himself as answer contact, the user clicks on the
-'Set answer contact' link. He needs to login to access that function.
+To register themselves as answer contact, the user clicks on the
+'Set answer contact' link. They need to login to access that function.
 
     >>> anon_browser.getLink('Set answer contact').click()
     Traceback (most recent call last):
@@ -49,8 +49,8 @@
     Answer contact for evolution package...
 
 On this page, the user can choose to become an answer contact by
-clicking a checkbox. All the teams, he's a member of are also displayed
-and he can register these teams as well.
+clicking a checkbox. All the teams they're a member of are also displayed,
+and they can register these teams as well.
 
     >>> browser.getControl(
     ...     "I want to be an answer contact for evolution").selected
@@ -62,7 +62,7 @@
     False
     >>> browser.getControl("Landscape Developers").selected = True
 
-To save his choices, the user clicks on the 'Continue' button and
+To save their choices, the user clicks on the 'Continue' button and
 a message is displayed to confirm the changes:
 
     >>> browser.getControl('Continue').click()

=== modified file 'lib/lp/answers/stories/faq-add.txt'
--- lib/lp/answers/stories/faq-add.txt	2015-10-05 06:34:17 +0000
+++ lib/lp/answers/stories/faq-add.txt	2016-01-26 15:58:00 +0000
@@ -4,7 +4,7 @@
 privileges. This includes answer contacts and the project's owner.
 
 No Privileges Person is not an answer contact for Mozilla Firefox, nor
-the project owner, therefore he cannot create a new FAQ.
+the project owner, therefore they cannot create a new FAQ.
 
     >>> user_browser.open('http://answers.launchpad.dev/firefox')
     >>> user_browser.getLink('All FAQs').click()
@@ -20,9 +20,9 @@
       ...
     Unauthorized: ...
 
-Sample Person does have that ability to create a FAQ because he is the
-project owner. He is looking for a FAQ about RSS, but he does not find
-one. He chooses to create one because he has answered several questions
+Sample Person does have that ability to create a FAQ because they are the
+project owner. They are looking for a FAQ about RSS, but they do not find
+one. They choose to create one because they have answered several questions
 from various sources about the subject.
 
     >>> owner_browser = setupBrowser(auth='Basic test@xxxxxxxxxxxxx:test')
@@ -43,7 +43,7 @@
 
 The page for creating a new FAQ contains three fields that are empty.
 Sample Person adds a title, keywords, and puts the answer into the
-content field. He then submits the form using the 'Create' button.
+content field. They then submit the form using the 'Create' button.
 
     >>> owner_browser.url
     'http://answers.launchpad.dev/firefox/+createfaq'
@@ -75,7 +75,7 @@
     >>> extract_text(find_tag_by_id(content, 'faq-content'))
     u'Use the bookmark bar.'
 
-He returns to the projects list of FAQs to verify that the newest
+They return to the project's list of FAQs to verify that the newest
 FAQ is listed first.
 
     >>> owner_browser.getLink('List all FAQs').click()

=== modified file 'lib/lp/answers/stories/faq-edit.txt'
--- lib/lp/answers/stories/faq-edit.txt	2011-12-23 23:44:59 +0000
+++ lib/lp/answers/stories/faq-edit.txt	2016-01-26 15:58:00 +0000
@@ -1,7 +1,7 @@
 = Editing FAQs =
 
 It is possible to modify the title, keywords and content of an existing
-FAQ. To do this, the user goes to the FAQ that he wants to modify and
+FAQ. To do this, the user goes to the FAQ that they want to modify and
 clicks the 'Edit FAQ' action.
 
 That action is only available to project owners and answer contacts.
@@ -48,8 +48,8 @@
     >>> print browser.title
     Edit FAQ...
 
-The user can change the title, keywords and content fields. He then
-clicks 'Save' to save his changes.
+The user can change the title, keywords and content fields. They then
+click 'Save' to save their changes.
 
     >>> print browser.getControl('Title').value
     How do I install Java?
@@ -66,7 +66,7 @@
 
     >>> browser.getControl('Save').click()
 
-The user can see his changes on the page:
+The user can see their changes on the page:
 
     >>> print browser.url
     http://answers.launchpad.dev/firefox/+faq/7

=== modified file 'lib/lp/answers/stories/project-add-question.txt'
--- lib/lp/answers/stories/project-add-question.txt	2014-01-30 15:04:06 +0000
+++ lib/lp/answers/stories/project-add-question.txt	2016-01-26 15:58:00 +0000
@@ -13,8 +13,8 @@
 Ask a question about a Product in a ProjectGroup
 ------------------------------------------------
 
-The user must be logged in to ask a question. When he attempts to ask a
-question, without being logged in, he encounters an unauthorized
+The user must be logged in to ask a question. When they attempt to ask a
+question, without being logged in, they encounter an unauthorized
 exception (and the user will be prompted to login from another page).
 The logged user will see the Ask a Question page, for the Mozilla
 Project in this case.
@@ -59,7 +59,7 @@
     Xhosa (xh)
     Zulu (zu)
 
-No Privileged Person enters a short summary of his problem, and submits
+No Privileged Person enters a short summary of their problem, and submits
 the form with the 'Continue' button. In this case, a question for
 Firefox in English regarding SVG.
 
@@ -67,8 +67,8 @@
     ...     'Problem with SVG')
     >>> user_browser.getControl('Continue').click()
 
-He's shown a list of similar questions related to the product Firefox
-that he submitted:
+They're shown a list of similar questions related to the product Firefox
+that they submitted:
 
     >>> similar_questions = find_tag_by_id(
     ...     user_browser.contents, 'similar-questions')
@@ -76,15 +76,15 @@
     ...     print row.a.renderContents()
     2: Problem showing the SVG demo on W3C site
 
-No Privileged Person can still change the product for which he's asking
+No Privileged Person can still change the product for which they're asking
 the question. The user chooses Thunderbird from the 'Project' product
 list.
 
     >>> user_browser.getControl('Mozilla Thunderbird').selected = True
 
-If he empties the question summary, and submits the form he'll be
-redirected to the first page. Let's assume he does this by accident as
-he revises the summary after reading the similar questions.
+If they empty the question summary and submits the form, they'll be
+redirected to the first page. Let's assume they do this by accident as
+they revise the summary after reading the similar questions.
 
     >>> user_browser.getControl('Summary').value = ''
     >>> user_browser.getControl('Post Question').click()
@@ -96,8 +96,8 @@
     >>> print soup.first('div', 'message').renderContents()
     You must enter a summary of your problem.
 
-The product Thunderbird that he selected on the previous screen is still
-selected. No Privileged Person re-enters his question summary, and
+The product Thunderbird that they selected on the previous screen is still
+selected. No Privileged Person re-enters their question summary, and
 submits the form.
 
     >>> print user_browser.getControl('Project').displayValue
@@ -109,15 +109,15 @@
 
 The user is again shown similar questions, this time for the product
 Thunderbird. Since there are no similar questions against Thunderbird,
-an appropriate message is displayed to inform him of this:
+an appropriate message is displayed to inform them of this:
 
     >>> soup = find_main_content(user_browser.contents)
     >>> print soup.first('p').renderContents()
     There are no existing FAQs or questions similar to the summary you
     entered.
 
-The user then elaborates upon his question by entering a description of
-the problem. He submits the form using the 'Post Question' button.
+The user then elaborates upon their question by entering a description of
+the problem. They submit the form using the 'Post Question' button.
 
     >>> user_browser.getControl('Description').value = (
     ...  "I received an HTML message containing an inlined SVG\n"
@@ -125,7 +125,7 @@
     ...  "Is there a way to configure Thunderbird to display SVG properly?\n")
     >>> user_browser.getControl('Post Question').click()
 
-No Privileged Person is taken to page displaying his question. From this
+No Privileged Person is taken to page displaying their question. From this
 point on, the user's interaction with the question follows to regular
 workflow. (see 30-question-workflow.txt for the details).
 
@@ -192,9 +192,9 @@
     Added Japanese to your preferred languages.
 
 So if No Privileges Person were to visit the Ask a Question page for
-Thunderbird directly, he will see that Japanese, as well English (the
+Thunderbird directly, they will see that Japanese, as well as English (the
 default supported language), have asterisks next to them in the Language
-list. This indicates that he can ask a question in Japanese or English
+list. This indicates that they can ask a question in Japanese or English
 and expect someone to reply in the same language.
 
     >>> user_browser.open(
@@ -245,7 +245,7 @@
 English is flagged because we do not know which Product the question is
 about. Without knowing the product, we cannot flag the supported
 languages other than the default language of English. If the user were
-to submit his question in another language, he might find that the
+to submit their question in another language, they might find that the
 language is supported on the next page.
 
     >>> print user_browser.getControl('Language').displayOptions
@@ -253,26 +253,26 @@
 
     >>> user_browser.getControl('Language').value = ['en']
 
-No Privileges Person enters a short summary of his problem in English
-because Japanese is not listed as supported. He submits the form with
-the 'Continue' button without setting the product. In this case, he is
+No Privileges Person enters a short summary of their problem in English
+because Japanese is not listed as supported. They submits the form with
+the 'Continue' button without setting the product. In this case, they are
 asking a question for Firefox in English regarding SVG.
 
     >>> user_browser.getControl('Summary').value = (
     ...     'Problem displaying complex SVG')
     >>> user_browser.getControl('Continue').click()
 
-He's shown a list of similar questions related to the product Firefox.
-He can see which of his preferred languages are supported for the
+They're shown a list of similar questions related to the product Firefox.
+They can see which of their preferred languages are supported for the
 Firefox product by reviewing which languages has asterisks in the
 Languages list--only English in the example.
 
     >>> print user_browser.getControl('Language').displayOptions
     ['English (en) *', 'Japanese (ja)']
 
-No Privileges Person can still change the product for which he's asking
-the question. He relizes he should have selected Thunderbird as the
-subject of the question. He chooses Thunderbird from the 'Project'
+No Privileges Person can still change the product for which they're asking
+the question. They realize they should have selected Thunderbird as the
+subject of the question. They choose Thunderbird from the 'Project'
 product list and reviews the list of supported languages again. The
 language list does not change because the Thunderbird was not submitted
 as the product.
@@ -292,7 +292,7 @@
 ................................................................
 
 Let's try this again from the starting page, but this time, No
-Privileges Person correctly chooses Thunderbird as the subject of his
+Privileges Person correctly chooses Thunderbird as the subject of their
 question.
 
     >>> user_browser.open('http://answers.launchpad.dev/mozilla')
@@ -302,7 +302,7 @@
 
     >>> user_browser.getControl('Mozilla Thunderbird').selected = True
 
-He writes his summary in English as he sees that is the only supported
+They write their summary in English as he sees that is the only supported
 Language, and 'Continues' to the next page.
 
     >>> print user_browser.getControl('Language').displayOptions
@@ -312,9 +312,9 @@
     ...     'Problem displaying complex SVG')
     >>> user_browser.getControl('Continue').click()
 
-The product Thunderbird that he selected on the previous screen is still
-selected. He can see that this product has support for Japanese as well
-as English when he sees the asterisks next to both in the Languages
+The product Thunderbird that they selected on the previous screen is still
+selected. They can see that this product has support for Japanese as well
+as English when they see the asterisks next to both in the Languages
 list. Japanese is supported because Dafydd speaks Japanese and is an
 answer contact for Thunderbird. We see this only after a question
 summary is submitted for a product.
@@ -322,7 +322,7 @@
     >>> print user_browser.getControl('Language').displayOptions
     ['English (en) *', 'Japanese (ja) *']
 
-No Privileges Person sets the language to Japanese, changes his question
+No Privileges Person sets the language to Japanese, changes their question
 summary, writes a description, and submits the form with the 'Post
 Question' button.
 
@@ -336,7 +336,7 @@
     ...      "Something in kanji and hiragana.")
     >>> user_browser.getControl('Post Question').click()
 
-The user is taken to page displaying his question. Changing the language
+The user is taken to page displaying their question. Changing the language
 or the summary did not search for similar questions again--the question
 is created.
 

=== modified file 'lib/lp/answers/stories/question-add-in-other-languages.txt'
--- lib/lp/answers/stories/question-add-in-other-languages.txt	2012-01-15 11:06:57 +0000
+++ lib/lp/answers/stories/question-add-in-other-languages.txt	2016-01-26 15:58:00 +0000
@@ -9,8 +9,8 @@
     >>> user_browser.getControl('Language').value
     ['en']
 
-The user may choose from any of his preferred languages and there is a
-link to enable to change his preferred languages:
+The user may choose from any of their preferred languages and there is a
+link to enable to change their preferred languages:
 
     >>> user_browser.getLink('Change your preferred languages').click()
     >>> print user_browser.title
@@ -27,7 +27,7 @@
     ['English (en) *', 'Portuguese (Brazil) (pt_BR)']
 
 Although it's possible to ask questions in any of the user's preferred
-languages, we need to do some checks to warn the user in case he's using
+languages, we need to do some checks to warn the user in case they're using
 a language that is not spoken/understood by any of the context's answer
 contacts.
 
@@ -69,7 +69,7 @@
     '.../+editmylanguages'
 
 Since we've already shown the warning, we won't try to block the user
-from asking a question in the language of his choice.
+from asking a question in the language of their choice.
 
     >>> browser.getControl('Language').value
     ['pt_BR']
@@ -101,7 +101,7 @@
     Language: Portuguese (Brazil) ...
 
 It's also possible that the user chose English in the first page but
-then changed his mind on the second page.
+then changed their mind on the second page.
 
     >>> browser = setupBrowser(auth='Basic daf@xxxxxxxxxxxxx:test')
     >>> browser.open('http://launchpad.dev/ubuntu/+addquestion')
@@ -110,14 +110,14 @@
     >>> browser.getControl('Summary').value = 'some random words'
     >>> browser.getControl('Continue').click()
 
-In this case he won't be warned, because we assume all members of the
+In this case they won't be warned, because we assume all members of the
 support community can understand English.
 
     >>> len(find_tags_by_class(browser.contents, 'warning message'))
     0
 
-If now he changes his mind and decides to enter the question details in
-Welsh, we'll have to warn him.
+If now they change their mind and decides to enter the question details in
+Welsh, we'll have to warn them.
 
     >>> browser.getControl('Language').value = ['cy']
     >>> browser.getControl('Summary').value = 'Gofyn cymorth'
@@ -136,7 +136,7 @@
     contacts will be able to read it. Currently, the languages spoken
     by at least one answer contact are: English.
 
-If he changes the language to another unsupported language, we will
+If they change the language to another unsupported language, we will
 display the warning again.
 
     >>> browser.getControl('Language').value = ['ja']
@@ -154,7 +154,7 @@
     contacts will be able to read it. Currently, the languages spoken
     by at least one answer contact are: English.
 
-If even after the warning he decides to go ahead, we have to accept the
+If even after the warning they decide to go ahead, we have to accept the
 new question.
 
     >>> browser.getControl('Post Question').click()

=== modified file 'lib/lp/answers/stories/question-add.txt'
--- lib/lp/answers/stories/question-add.txt	2014-11-27 22:13:36 +0000
+++ lib/lp/answers/stories/question-add.txt	2016-01-26 15:58:00 +0000
@@ -51,11 +51,11 @@
 ----------------------------------------
 
 Asking a new question is a two-step process. In the first step, the user
-enters a summary of his problem. Using that summary, a search
+enters a summary of their problem. Using that summary, a search
 is performed to find similar questions and similar FAQs that might
 exists.
 
-That step cannot be skipped by the user, if he just clicks 'Continue',
+That step cannot be skipped by the user, if they just click 'Continue',
 an error message will be displayed.
 
     >>> user_browser.getControl('Summary').value
@@ -65,7 +65,7 @@
     There is 1 error.
     You must enter a summary of your problem.
 
-The user enters his problem summary and a list of similar FAQs and
+The user enters their problem summary and a list of similar FAQs and
 questions are displayed.
 
 XXX: Original search, disabled due to performance issues RBC 20100725. This
@@ -128,21 +128,21 @@
 Creating a New Question
 -----------------------
 
-If the shown questions don't help the user, he may post a new question
-by filling in the 'Description' field. He may also edit the
-summary he provided.
+If the shown questions don't help the user, they may post a new question
+by filling in the 'Description' field. They may also edit the
+summary they provided.
 
     >>> user_browser.getControl('Summary').value
     'java web pages'
 
-If the user doesn't provide details, he'll get an error message:
+If the user doesn't provide details, they'll get an error message:
 
     >>> user_browser.getControl('Post Question').click()
     >>> print_feedback_messages(user_browser.contents)
     There is 1 error.
     You must provide details about your problem.
 
-And if he decides to remove the title, he'll be brought back to the
+And if they decide to remove the title, they'll be brought back to the
 first step:
 
     >>> user_browser.getControl('Summary').value = ''

=== modified file 'lib/lp/answers/stories/question-answer-contact.txt'
--- lib/lp/answers/stories/question-answer-contact.txt	2011-08-15 08:21:38 +0000
+++ lib/lp/answers/stories/question-answer-contact.txt	2016-01-26 15:58:00 +0000
@@ -36,8 +36,8 @@
     ['landscape-developers', 'launchpad-users']
 
 The user can select any of these checkboxes to register as an answer
-contact himself or one of the team for which he's an administrator. In
-our case, the user decides to register himself and the Landscape
+contact themselves or one of the team for which they're an administrator. In
+our case, the user decides to register themselves and the Landscape
 Developers team.
 
     >>> browser.getControl(
@@ -47,9 +47,9 @@
 
 Answer contacts must tell Launchpad which languages they provide help
 in. As a convenience, the Answer Tracker will set the Person's preferred
-languages for him. The browser languages are used for a Person. In the
+languages for them. The browser languages are used for a Person. In the
 case of a Team, only English is added. Sample Person reads the notice
-that his preferred languages were set, and uses the link to in the
+that their preferred languages were set, and uses the link to in the
 notice to review the changes. Sample Person's browser sends the accept-
 languages header with English and Spanish.
 
@@ -64,7 +64,7 @@
     True
 
 To remove oneself from the answer contacts, the user uses the same 'Set
-answer contact' link and uncheck himself or the team he want to remove
+answer contact' link and uncheck themselves or the team they want to remove
 from the answer contact list.
 
     >>> browser.open('http://launchpad.dev/ubuntu/+questions')
@@ -110,7 +110,7 @@
 to set exactly which languages the team supports.
 
 Sample Person visits the Answers facet of Ubuntu as described in the
-main story above. He wants Landscape Developers team to be answer
+main story above. They want Landscape Developers team to be answer
 contacts for Ubuntu, but only for Spanish questions to keep the email
 traffic to a manageable volume.
 
@@ -126,7 +126,7 @@
     Landscape Developers has been added as an answer contact for Ubuntu.
 
 Sample Person navigates to the team page to set it's preferred
-languages. He must add Spanish to the team's preferred languages.
+languages. They must add Spanish to the team's preferred languages.
 
     >>> browser.open('http://launchpad.dev/~landscape-developers')
     >>> browser.title

=== modified file 'lib/lp/answers/stories/question-browse-and-search.txt'
--- lib/lp/answers/stories/question-browse-and-search.txt	2014-11-27 22:13:36 +0000
+++ lib/lp/answers/stories/question-browse-and-search.txt	2016-01-26 15:58:00 +0000
@@ -321,7 +321,7 @@
 
 === Answered Questions ===
 
-A random user has a problem with firefox in Ubuntu. He uses the
+A random user has a problem with firefox in Ubuntu. They use the
 'Answered' link on the 'Answers' facet of the distribution to look for
 similar problems. (This listing includes both 'Answered' and 'Solved'
 questions.)
@@ -340,8 +340,8 @@
     mailto: problem in webpage
     Installation of Java Runtime Environment for Mozilla
 
-This report is also searchable. He's having a problem with Evolution, so
-he enters 'Evolution' as a keyword and hits the search button.
+This report is also searchable. They're having a problem with Evolution, so
+they enter 'Evolution' as a keyword and hit the search button.
 
     >>> browser.getControl(name='field.search_text').value = 'Evolution'
     >>> browser.getControl('Search', index=0).click()
@@ -355,11 +355,11 @@
 
 === My questions ===
 
-Sample Person remembers posting a question on mozilla-firefox. He uses
+Sample Person remembers posting a question on mozilla-firefox. They use
 the 'My questions' link on the distribution source package Answers facet
-to list all the questions he ever made about that package.
+to list all the questions they ever made about that package.
 
-He needs to login to access that page:
+They need to login to access that page:
 
     >>> anon_browser.open(
     ...     'http://launchpad.dev/ubuntu/+source/mozilla-firefox/'
@@ -384,20 +384,20 @@
     mailto: problem in webpage
     Installation of Java Runtime Environment for Mozilla
 
-His problem was about integrating his email client in firefox, so he
-enters 'email client in firefox'
+Their problem was about integrating their email client in firefox, so they
+enter 'email client in firefox'
 
     >>> sample_person_browser.getControl(name='field.search_text').value = (
     ...     'email client in firefox')
 
-He also remembers that his question was answered, so he unselects the
-other statuses and hits the search button.
+They also remember that their question was answered, so they unselect the
+other statuses and hit the search button.
 
     >>> sample_person_browser.getControl('Open').selected = False
     >>> sample_person_browser.getControl('Invalid').selected = False
     >>> sample_person_browser.getControl('Search', index=0).click()
 
-The exact question he was searching for is displayed!
+The exact question they were searching for is displayed!
 
     >>> questions = find_tag_by_id(
     ...     sample_person_browser.contents, 'question-listing')
@@ -406,7 +406,7 @@
     mailto: problem in webpage
 
 If the user didn't make any questions on the product, a message
-informing him of this fact is displayed.
+informing them of this fact is displayed.
 
     >>> # gnmomebaker must enable answers to access questions.
     >>> from lp.registry.interfaces.product import IProductSet
@@ -426,13 +426,13 @@
 
 === Need attention ===
 
-A user can often forget which questions need his attention. For
+A user can often forget which questions need their attention. For
 this purpose, there is a 'Need attention' report which displays the
 questions made by the user which require a reply. It also lists
-the questions on which he provided an answer or requested for more
+the questions on which they provided an answer or requested for more
 information and that are now back in the 'Open' state.
 
-He needs to login to access that page:
+They need to login to access that page:
 
     >>> anon_browser.open('http://launchpad.dev/distros/ubuntu/+questions')
     >>> anon_browser.getLink('Need attention').click()
@@ -466,7 +466,7 @@
     ...
 
 If there is no questions needing the user's attention, a message
-informing him of this fact is displayed.
+informing them of this fact is displayed.
 
     >>> sample_person_browser.open(
     ...    'http://launchpad.dev/products/gnomebaker/+questions')
@@ -617,7 +617,7 @@
 
 === Subscribed ===
 
-Foo Bar can find all the questions to which he is subscribed by
+Foo Bar can find all the questions to which they are subscribed by
 visiting the 'Subscribed' link in the 'Answers' facet.
 
     >>> browser.getLink('Subscribed').click()
@@ -748,7 +748,7 @@
 only in a particular project. In this context a "project" means either
 a distribution, product or project group.
 
-He must enter the project's name in the text field:
+They must enter the project's name in the text field:
 
     >>> anon_browser.open('http://answers.launchpad.dev')
     >>> anon_browser.getControl('One project').selected = True

=== modified file 'lib/lp/answers/stories/question-message.txt'
--- lib/lp/answers/stories/question-message.txt	2012-12-20 13:43:48 +0000
+++ lib/lp/answers/stories/question-message.txt	2016-01-26 15:58:00 +0000
@@ -34,7 +34,7 @@
 == Email addresses are only shown to authenticated users ==
 
 Email addresses are visible to authenticated users. Sample Person is
-authenticated already, He will see 'human@xxxxxxxxxxxxx'
+authenticated already, so they will see 'human@xxxxxxxxxxxxx'.
 
     >>> print user_browser.title.decode('utf-8')
     Question #11 :  ...
@@ -47,7 +47,7 @@
     Witty signatures rock!
 
 Unauthenticated users, such as a bot will see the mock email address
-of 'person@xxxxxxxxxx'. The anonymous user is unauthenticated, he will
+of 'person@xxxxxxxxxx'. The anonymous user is unauthenticated, so they will
 see the obfuscated email address (<email address hidden>).
 
     >>> anon_browser.open('http://answers.launchpad.dev/ubuntu/+question/11')

=== modified file 'lib/lp/answers/stories/question-obfuscation.txt'
--- lib/lp/answers/stories/question-obfuscation.txt	2012-12-11 06:57:26 +0000
+++ lib/lp/answers/stories/question-obfuscation.txt	2016-01-26 15:58:00 +0000
@@ -19,8 +19,8 @@
     ...     li['title']
     u'I am not able to ... if i click on a mailto:user@xxxxxxxxxx link ...'
 
-He can also see the email address in the tooltip for the question in the
-project's questions. When he views the question, he can see the address
+They can also see the email address in the tooltip for the question in the
+project's questions. When they view the question, they can see the address
 in the question's description.
 
     >>> user_browser.getControl(name='field.search_text').value = 'mailto'
@@ -62,7 +62,7 @@
       mailto:user@xxxxxxxxxx link in a webpage in...'
 
 No Privileges Person creates a question with an email address in the
-description. He can then see the email address in the tooltip in the
+description. They can then see the email address in the tooltip in the
 'Latest questions asked' portlet for Answers front page.
 
     >>> user_browser.getLink('#9 mailto: problem in webpage').click()
@@ -110,8 +110,8 @@
     u'The clicking mailto:<email address hidden> crashes the browser.'
      ...
 
-Nor can he see it in the question listings for the project.
-He cannot see the address reading the question either.
+Nor can they see it in the question listings for the project.
+They cannot see the address reading the question either.
 
     >>> anon_browser.getControl(name='field.search_text').value = 'mailto'
     >>> anon_browser.getControl('Find Answers').click()

=== modified file 'lib/lp/answers/stories/question-reject-and-change-status.txt'
--- lib/lp/answers/stories/question-reject-and-change-status.txt	2012-12-11 05:41:50 +0000
+++ lib/lp/answers/stories/question-reject-and-change-status.txt	2016-01-26 15:58:00 +0000
@@ -4,8 +4,8 @@
 This should be done when the question isn't an actual pertinent question
 for the product. For example, if the question is a duplicate or spam.
 
-No Privileges Person isn't an answer contact or administrator, so he
-doesn't have access to that feature.
+No Privileges Person isn't an answer contact or administrator, so they
+don't have access to that feature.
 
     >>> user_browser.open('http://launchpad.dev/firefox/+question/2')
     >>> user_browser.getLink('Reject question')
@@ -13,7 +13,7 @@
       ...
     LinkNotFoundError...
 
-Even when trying to access the page directly, he will get an unauthorized
+Even when trying to access the page directly, they will get an unauthorized
 error.
 
     >>> user_browser.open(
@@ -28,7 +28,7 @@
     >>> admin_browser.getLink('Reject question').click()
     >>> admin_browser.getControl('Reject').click()
 
-He needs to enter a message explaining the rejection:
+They need to enter a message explaining the rejection:
 
     >>> for message in find_tags_by_class(admin_browser.contents, 'message'):
     ...     print message.renderContents()
@@ -36,7 +36,7 @@
     You must provide an explanation message.
 
 At this point the user might decide this is a bad idea, so there is a
-cancel link to take him back to the question:
+cancel link to take them back to the question:
 
     >>> print admin_browser.getLink('Cancel').url
     http://answers.launchpad.dev/firefox/+question/2

=== modified file 'lib/lp/answers/stories/question-search-multiple-languages.txt'
--- lib/lp/answers/stories/question-search-multiple-languages.txt	2012-02-21 22:46:28 +0000
+++ lib/lp/answers/stories/question-search-multiple-languages.txt	2016-01-26 15:58:00 +0000
@@ -7,7 +7,7 @@
 == Anonymous searching ==
 
 For example, a user who isn't logged in will only see questions
-written in English, in one of the language configured in his browser,
+written in English, in one of the language configured in their browser,
 or inferred from GeoIP information. In this example, only English
 questions will be shown when the user has not provided language
 information.
@@ -111,7 +111,7 @@
 is among the users' languages, the language controls are not displayed.
 The mozilla-firefox sourcepackage only has English questions. When the
 anonymous user makes a request from a GeoIP that has no languages
-mapped, we assume he speaks the default language of English.
+mapped, we assume they speak the default language of English.
 
     >>> anon_browser.addHeader('X_FORWARDED_FOR', '172.16.1.1')
     >>> anon_browser.open(
@@ -121,7 +121,7 @@
       ...
     LookupError: name 'field.language...
 
-But if the user configures his browser to accept Spanish and English
+But if the user configures their browser to accept Spanish and English
 then questions with those language will be displayed:
 
     >>> anon_browser.addHeader('Accept-Language', 'es, en')
@@ -155,7 +155,7 @@
 the languages determined by what their browser sends in the
 Accept-Languages request, or by their GeoIP, just as with anonymous
 users. In this example, No Privileges Person does not have preferred
-languages, nor is his browser configured with a language; we use GeoIP
+languages, nor is their browser configured with a language; we use GeoIP
 rules. As with the anonymous user, the intersection of the GeoIP
 languages and the target's question languages is 'en'.
 
@@ -174,7 +174,7 @@
       ...
     LookupError: name 'field.language...
 
-When No Privileges Person adds Spanish and English to his browser, they
+When No Privileges Person adds Spanish and English to their browser, they
 are added to the language controls.
 
     >>> user_browser.addHeader('Accept-Language', 'es, en')
@@ -232,7 +232,7 @@
 
 The user's languages are presented as controls in the question form.
 The controls are filters that allow the user to see questions in
-his languages. Daf, in this example, can see a language filter for
+their languages. Daf, in this example, can see a language filter for
 English, and can use it to locate English questions.
 
     >>> daf_browser.open('http://launchpad.dev/distros/ubuntu/+questions')
@@ -262,8 +262,8 @@
 speak, the project questions page displays links to see those
 questions. Each link is to a page that displays the questions
 for a specific language. No Privileges Person is looking for a
-project that needs help. He visits Kubuntu and does not see a need,
-but when he visits Mozilla Firefox, he is informed that there are
+project that needs help. They visit Kubuntu and does not see a need,
+but when they visit Mozilla Firefox, they are informed that there are
 questions going unanswered.
 
     >>> user_browser.open('http://answers.launchpad.dev/kubuntu')
@@ -306,9 +306,9 @@
 == My questions ignores preferred languages ==
 
 The "My questions" view ignores the user's language preferences, because
-he may change them over time, but he must always see his questions. For
+they may change them over time, but they must always see their questions. For
 example, Sample Person has asked a question in Arabic in the Ubuntu
-project. He can see the question on the second page of "My questions"...
+project. They can see the question on the second page of "My questions"...
 
     >>> sample_person_browser = setupBrowser(
     ...     auth='Basic test@xxxxxxxxxxxxx:test')
@@ -329,7 +329,7 @@
     ...     question.find('a').renderContents()
     '\xd8\xb9\xd9\x83\xd8\xb3 \xd8\xa7\xd9\x84\xd8\xaa\xd8\xba...'
 
-...even though he has not set Arabic as one of his preferred languages.
+...even though they have not set Arabic as one of their preferred languages.
 
     >>> sample_person_browser.getLink(
     ...     'Change your preferred languages').click()

=== modified file 'lib/lp/answers/stories/question-subscriptions.txt'
--- lib/lp/answers/stories/question-subscriptions.txt	2015-07-21 09:04:01 +0000
+++ lib/lp/answers/stories/question-subscriptions.txt	2016-01-26 15:58:00 +0000
@@ -8,7 +8,7 @@
 
 To subscribe, users use the 'Subscribe' link and then confirm that
 they want to subscribe by clicking on the 'Subscribe' button. The user
-sees a link to his subscribed questions.
+sees a link to their subscribed questions.
 
     >>> user_browser.open(
     ...     'http://launchpad.dev/firefox/+question/2')
@@ -21,7 +21,7 @@
 
     >>> user_browser.getControl('Subscribe').click()
 
-A message confirming that he was subscribed is displayed:
+A message confirming that they were subscribed is displayed:
 
     >>> print_feedback_messages(user_browser.contents)
     You have subscribed to this question.

=== modified file 'lib/lp/answers/stories/question-workflow.txt'
--- lib/lp/answers/stories/question-workflow.txt	2012-12-11 02:19:13 +0000
+++ lib/lp/answers/stories/question-workflow.txt	2016-01-26 15:58:00 +0000
@@ -48,10 +48,10 @@
 quite vague. In these cases, to help the user, some more information
 will be required.
 
-No Privileges Person visits the question. He see the heading 'Can you
-help with this problem?'. The problem is not clear, he needs more
+No Privileges Person visits the question. They see the heading 'Can you
+help with this problem?'. The problem is not clear, they need more
 information. To request for more information from the question owner, No
-Privileges Person enters his question in the 'Message' field and clicks
+Privileges Person enters their question in the 'Message' field and clicks
 on the 'Add Information Request' button.
 
     >>> support_browser.open(
@@ -115,11 +115,11 @@
 -------------------------------
 
 When the question is in the 'Needs information' state, it means that the
-question owner should come back and provide more information. He can do
+question owner should come back and provide more information. They can do
 so by entering the reply in the 'Message' box and clicking on the "I'm
 Providing More Information" button. Note that the question owner cannot
 see the 'Can you help with this problem?' heading because it is not
-relevant to his tasks.
+relevant to their tasks.
 
     >>> owner_browser.open(
     ...     'http://launchpad.dev/firefox/+question/2')
@@ -134,7 +134,7 @@
     >>> owner_browser.getControl("I'm Providing More Information").click()
 
 Once the owner replied with the, hopefully, requested information, the
-status is changed to Open and his answer appended to the question
+status is changed to Open and their answer appended to the question
 discussion.
 
     >>> print find_request_status(owner_browser.contents)
@@ -173,7 +173,7 @@
 Confirming an Answer
 --------------------
 
-When the owner comes back on the question page, he will now see a new
+When the owner comes back on the question page, they will now see a new
 'This Solved My Problem' button near the answer.
 
     >>> owner_browser.open(
@@ -259,7 +259,7 @@
 ---------
 
 It can happen that, altough the owner confirmed the question was solved,
-the original problem reappears. In this case, he can reopen the question
+the original problem reappears. In this case, they can reopen the question
 by entering a new message and clicking the "I Still Need an Answer"
 button.
 
@@ -307,8 +307,8 @@
 Self-Answer
 -----------
 
-The owner can also gives the solution to his own question. He simply has
-to enter his solution in the 'Message' box and click the 'Problem
+The owner can also give the solution to their own question. They simply have
+to enter their solution in the 'Message' box and click the 'Problem
 Solved' button.
 
     >>> owner_browser.getControl('Message').value = (
@@ -331,7 +331,7 @@
 
 A message is displayed to the user confirming that the question is
 solved and suggesting that the user choose an answer that helped the
-question owner to solve the his problem.
+question owner to solve their problem.
 
     >>> for message in soup.findAll('div', 'informational message'):
     ...     print extract_text(message)
@@ -444,14 +444,14 @@
 Asking a separate question
 --------------------------
 
-A user that is new to Answers is not familiar with the workflow. He may
-have a problem of his own, and has discovered an existing question. We
-want him to ask his own question instead of intruding into the workflow
+A user that is new to Answers is not familiar with the workflow. They may
+have a problem of their own, and has discovered an existing question. We
+want them to ask their own question instead of intruding into the workflow
 of existing questions.
 
 No Privileges Person (a different user from the one above) discovers the
-Firefox question. The solution does not work, but he thinks he has a
-similar problem so he asks his own question.
+Firefox question. The solution does not work, but they think they have a
+similar problem so they ask their own question.
 
     >>> user_browser.open('http://launchpad.dev/firefox/+question/2')
 

=== modified file 'lib/lp/answers/stories/this-is-a-faq.txt'
--- lib/lp/answers/stories/this-is-a-faq.txt	2015-10-05 06:34:17 +0000
+++ lib/lp/answers/stories/this-is-a-faq.txt	2016-01-26 15:58:00 +0000
@@ -29,7 +29,7 @@
 the question to an existing FAQ.
 
 For example, No Privileges Person comes across a question about SVG
-support in Firefox. That question has a well-known answer, so he clicks
+support in Firefox. That question has a well-known answer, so they click
 on 'Link to a FAQ' to answer the question:
 
     # We use backslashreplace because the page title includes smart quotes.
@@ -101,13 +101,13 @@
     >>> user_browser.getControl('10').selected = True
 
 There is a 'Message' field that will be used to answer the question.
-It is pre-filled, but he can change its value. The FAQ reference will
+It is pre-filled, but they can change its value. The FAQ reference will
 be appended to the message.
 
     >>> print user_browser.getControl('Message').value
     No Privileges Person suggests this article as an answer to your question:
 
-He can then click 'Link to FAQ' to answer the question with the selected
+They can then click 'Link to FAQ' to answer the question with the selected
 FAQ. After clicking the button, the user is redirected to the question
 page.
 
@@ -115,7 +115,7 @@
     >>> print user_browser.url
     http://answers.launchpad.dev/firefox/+question/2
 
-He sees that the question's status was changed to 'Answered':
+They see that the question's status was changed to 'Answered':
 
     >>> def print_question_status(browser):
     ...     print extract_text(
@@ -145,8 +145,8 @@
 
 The link to the FAQ can be changed by using the same 'Link to a FAQ'
 option. Continuing on the previous example, the user went on to read
-the FAQ that he just linked and found that it doesn't really answer
-the question. To correct the mistake, he uses the same 'Link to a FAQ'
+the FAQ that they just linked and found that it doesn't really answer
+the question. To correct the mistake, they use the same 'Link to a FAQ'
 action.
 
     >>> user_browser.getLink('Link to a FAQ').click()
@@ -160,13 +160,13 @@
     ( ) 8: How do I install Extensions?
     ( ) 9: How do I troubleshoot problems with extensions/themes?
 
-He changes the message and click 'Link to FAQ'.
+They change the message and click 'Link to FAQ'.
 
     >>> user_browser.getControl('Message').value = (
     ...     "Sorry, this document doesn't really answer your question.")
     >>> user_browser.getControl('Link to FAQ').click()
 
-But since, he forgot to change the link, the form is displayed again
+But since they forgot to change the link, the form is displayed again
 with an error message.
 
     >>> print user_browser.url
@@ -203,7 +203,7 @@
 answer contacts and the project's owner).
 
 Since No Privileges Person isn't an answer contact for the project nor
-the project owner, he doesn't have the possibility to create a new FAQ.
+the project owner, they don't have the possibility to create a new FAQ.
 
     >>> user_browser.getLink('Create a FAQ')
     Traceback (most recent call last):
@@ -236,8 +236,8 @@
     >>> print owner_browser.title
     Create a FAQ for Mozilla...
 
-The FAQ title and content are pre-filled with the target question. He
-edits them to be more appropriate:
+The FAQ title and content are pre-filled with the target question. They
+edit them to be more appropriate:
 
     >>> print owner_browser.getControl('Title').value
     SVG extension
@@ -250,13 +250,13 @@
     >>> owner_browser.getControl('Content').value = (
     ...     'Upgrade your browser to Firefox 2.0.')
 
-He can also enter keywords describing the FAQ:
+They can also enter keywords describing the FAQ:
 
     >>> owner_browser.getControl('Keywords').value = (
     ...     'scalable vector graphic')
 
 There is a 'Message' field that will be used to answer the question.
-It is pre-filled, but he can change its value:
+It is pre-filled, but they can change its value:
 
     >>> print owner_browser.getControl(
     ...     'Additional comment for question #2').value
@@ -358,7 +358,7 @@
 comment.
 
 No Privileges Person sees a recently solved question that relates to a
-FAQ. He decided to add it to the question to provide additional
+FAQ. They decided to add it to the question to provide additional
 information.
 
     >>> user_browser.open(
@@ -379,7 +379,7 @@
     >>> user_browser.getControl('Link to FAQ').click()
 
 The question is still solved. No Privileges Person sees the FAQ was
-added to the question, and his message was added to the question's
+added to the question, and their message was added to the question's
 discussion.
 
     >>> print user_browser.title

=== modified file 'lib/lp/answers/tests/emailinterface.txt'
--- lib/lp/answers/tests/emailinterface.txt	2015-03-13 19:05:50 +0000
+++ lib/lp/answers/tests/emailinterface.txt	2016-01-26 15:58:00 +0000
@@ -4,7 +4,7 @@
 The Answer Tracker has an email interface, although it's quite limited
 at the moment. The only thing you can do is post new messages on the
 question. This is an important feature, though, since it ensures that if
-a user decides to reply to a question notification, his email won't be
+a user decides to reply to a question notification, their email won't be
 lost, it will be added to the question.
 
 Incoming emails for the Answer Tracker are processed by the
@@ -175,10 +175,10 @@
     Give more information
 
 In these states, the other possibility would be that the message is
-really stating the owner solved his own problem. This is a less likely
-scenario, since it would mean that the owner is replying to one of his
-own message. And if that was the case, it is easy for the owner to
-correct our bad decision, since the question will stay on his
+really stating the owner solved their own problem. This is a less likely
+scenario, since it would mean that the owner is replying to one of their
+own messages. And if that was the case, it is easy for the owner to
+correct our bad decision, since the question will stay on their
 list of open questions.
 
 Answered and Expired
@@ -204,7 +204,7 @@
 be that it was confirming that the provided answer work. We minimize the
 chance of this happening by adding an explanation message in the footer
 of the notification containing the answer. The other possibility is that
-the user sent a message to explain that he solved his problem. We do
+the user sent a message to explain that they solved their problem. We do
 support this use case yet.
 
 From the Expired state:
@@ -221,7 +221,7 @@
     Reopen
 
 From the Expired state, the other possibility is the less probable
-explaining that the owner solved his problem. Again, to minimize
+explaining that the owner solved their problem. Again, to minimize
 confusion, the outoing notification contain a footer explaining what
 will happen if one reply to the message.
 
@@ -243,7 +243,7 @@
     Comment
 
 The other alternative is that the owner wanted to reopen the question.
-But it is more likely that an email after he marked the problem as
+But it is more likely that an email after they marked the problem as
 solved would come as a reply to another comment, so it is safer to
 assume it was a comment.
 

=== modified file 'lib/lp/app/browser/tests/launchpad-search-pages.txt'
--- lib/lp/app/browser/tests/launchpad-search-pages.txt	2015-01-16 12:51:28 +0000
+++ lib/lp/app/browser/tests/launchpad-search-pages.txt	2016-01-26 15:58:00 +0000
@@ -111,8 +111,8 @@
     None
 
 Private bugs are not matched if the user does not have permission to
-see them. For example, Sample Person can see a private bug that he
-created because he is the owner.
+see them. For example, Sample Person can see a private bug that they
+created because they are the owner.
 
     >>> from lp.services.webapp.interfaces import ILaunchBag
     >>> from lp.app.enums import InformationType

=== modified file 'lib/lp/app/doc/datehandling.txt'
--- lib/lp/app/doc/datehandling.txt	2011-12-24 15:18:32 +0000
+++ lib/lp/app/doc/datehandling.txt	2016-01-26 15:58:00 +0000
@@ -1,13 +1,3 @@
-Date Handling
-=============
-
-She's really hot, you're really nervous. What are you going to wear? What if
-she doesn't like the restaurant you suggested? What if you get spaghetti sauce
-all over your shirt?
-
-Handling dates can be tricky. This document is about handling dates.
-Thankfully, it's not about handling /those/ kinds of dates.
-
 Date Handling in Launchpad
 ==========================
 
@@ -22,7 +12,7 @@
     >>> from lp.testing import login
     >>> login("testing@xxxxxxxxxxxxx")
 
-Then let's grab one of his bug tasks to work with:
+Then let's grab one of their bug tasks to work with:
 
     >>> from zope.component import getUtility
     >>> from lp.bugs.interfaces.bugtask import IBugTaskSet

=== modified file 'lib/lp/app/doc/menus.txt'
--- lib/lp/app/doc/menus.txt	2015-07-08 16:05:11 +0000
+++ lib/lp/app/doc/menus.txt	2016-01-26 15:58:00 +0000
@@ -631,7 +631,7 @@
 -----------------
 
 A link is not linked (the anchor is not rendered) when its URL matches
-the request URI; the user should not navigate to a page he is already
+the request URI; the user should not navigate to a page they are already
 seeing. The matched URI comes from the view's request...
 
     >>> recipe_navigationmenu = queryAdapter(

=== modified file 'lib/lp/app/doc/tales.txt'
--- lib/lp/app/doc/tales.txt	2015-10-05 06:34:17 +0000
+++ lib/lp/app/doc/tales.txt	2016-01-26 15:58:00 +0000
@@ -1609,7 +1609,7 @@
 The team 'myteam' is a private team so only the team members
 and Launchpad admins can see the details.
 
-Foo Bar is an administrator so he can see all.
+Foo Bar is an administrator so they can see all.
 
     >>> login('foo.bar@xxxxxxxxxxxxx')
     >>> myteam = getUtility(IPersonSet).getByName('myteam')
@@ -1622,7 +1622,7 @@
     >>> test_tales("team/fmt:unique_displayname", team=myteam)
     u'My Team (myteam)'
 
-Owner is a member of myteam so he can see all.
+Owner is a member of myteam so they can see all.
 
     >>> login('owner@xxxxxxxxxxxxx')
     >>> test_tales("team/fmt:link", team=myteam)

=== modified file 'lib/lp/app/stories/basics/xx-preferred-charsets.txt'
--- lib/lp/app/stories/basics/xx-preferred-charsets.txt	2011-12-19 13:20:13 +0000
+++ lib/lp/app/stories/basics/xx-preferred-charsets.txt	2016-01-26 15:58:00 +0000
@@ -1,6 +1,6 @@
 In order to minimise problems related to page encodings, we want all
 our pages to always be encoded using utf-8, even if the client says
-that he doesn't accept it.
+that it doesn't accept it.
 
     >>> anon_browser.addHeader('Accept-Charset', 'iso8859-1')
     >>> anon_browser.open('http://launchpad.dev/')

=== modified file 'lib/lp/app/stories/basics/xx-soft-timeout.txt'
--- lib/lp/app/stories/basics/xx-soft-timeout.txt	2011-12-29 05:29:36 +0000
+++ lib/lp/app/stories/basics/xx-soft-timeout.txt	2016-01-26 15:58:00 +0000
@@ -10,8 +10,8 @@
     Location: http://.../+soft-timeout/+login
     ...
 
-Sample Person doesn't have access to the page since he isn't a
-Launchpad develper:
+Sample Person doesn't have access to the page since they aren't a
+Launchpad developer:
 
     >>> print http(r"""
     ... GET /+soft-timeout HTTP/1.1
@@ -20,7 +20,7 @@
     HTTP/1.1 403 Forbidden
     ...
 
-Foo Bar is a Launchpad developer, so he has access to the page. Since
+Foo Bar is a Launchpad developer, so they have access to the page. Since
 no timeout value is set, no soft timeout will be generated, though.
 
     >>> from lp.services.config import config

=== modified file 'lib/lp/app/stories/launchpad-root/site-search.txt'
--- lib/lp/app/stories/launchpad-root/site-search.txt	2015-07-08 16:52:34 +0000
+++ lib/lp/app/stories/launchpad-root/site-search.txt	2016-01-26 15:58:00 +0000
@@ -52,8 +52,8 @@
     >>> print extract_text(find_tag_by_id(anon_browser.contents, 'no-search'))
     Enter a term or many terms to find matching pages...
 
-When the user searches for specific item, such as a project name, he
-sees a result for that exact match in Launchpad.
+When the user searches for specific item, such as a project name, they
+see a result for that exact match in Launchpad.
 
     >>> search_for('firefox')
     >>> print anon_browser.title
@@ -99,7 +99,7 @@
     Bug #1 in Mozilla Firefox: ...Firefox does not support SVG...
     ...
 
-He can see there really are only twenty matches in the page.
+They can see there really are only twenty matches in the page.
 
     >>> first_page_results = list(
     ...     find_tags_by_class(anon_browser.contents, 'pagematch'))
@@ -185,7 +185,7 @@
     Registry Administrators
     on 2006-10-16
 
-The user enters the number 1, and he sees a bug and a question in the
+The user enters the number 1, and they see a bug and a question in the
 "Exact matches" section.
 
     >>> search_for('1')
@@ -299,7 +299,7 @@
 -----------------------
 
 Most pages have the global search form in them. Any user can enter terms
-in the page he is viewing and submit the form to see the results.
+in the page they are viewing and submit the form to see the results.
 
     >>> anon_browser.open('http://bugs.launchpad.dev/firefox')
     >>> print anon_browser.title

=== modified file 'lib/lp/app/validators/validation.py'
--- lib/lp/app/validators/validation.py	2015-09-28 17:38:45 +0000
+++ lib/lp/app/validators/validation.py	2016-01-26 15:58:00 +0000
@@ -101,8 +101,8 @@
 
     This validator is supposed to be used only when creating a new profile
     using the /people/+newperson page, as the message will say clearly to the
-    user that the profile he's trying to create already exists, so there's no
-    need to create another one.
+    user that the profile they're trying to create already exists, so there's
+    no need to create another one.
     """
     from lp.services.webapp.publisher import canonical_url
     from lp.registry.interfaces.person import IPersonSet

=== modified file 'lib/lp/app/widgets/doc/image-widget.txt'
--- lib/lp/app/widgets/doc/image-widget.txt	2012-06-02 11:53:05 +0000
+++ lib/lp/app/widgets/doc/image-widget.txt	2016-01-26 15:58:00 +0000
@@ -343,7 +343,7 @@
                        LaunchpadValidationError(u'\nThis image is not exactly
                                                   193x191\npixels in size.'))
 
-Finally, if the user specifies the 'change' action he must also provide
+Finally, if the user specifies the 'change' action they must also provide
 a file to be uploaded.
 
     >>> form = {'field.mugshot.action': 'change', 'field.mugshot.image': ''}

=== modified file 'lib/lp/archivepublisher/tests/test_generate_ppa_htaccess.py'
--- lib/lp/archivepublisher/tests/test_generate_ppa_htaccess.py	2015-09-08 09:09:28 +0000
+++ lib/lp/archivepublisher/tests/test_generate_ppa_htaccess.py	2016-01-26 15:58:00 +0000
@@ -266,7 +266,7 @@
         for person in tokens:
             self.assertNotDeactivated(tokens[person])
 
-        # Now remove someone from team1, he will lose his token but
+        # Now remove someone from team1. They will lose their token but
         # everyone else keeps theirs.
         with lp_dbuser():
             team1_person.leave(team1)
@@ -282,8 +282,8 @@
         # Ensure that a cancellation email was sent.
         self.assertEmailQueueLength(1)
 
-        # Promiscuous_person now leaves team1, but does not lose his
-        # token because he's also in team2. No other tokens are
+        # Promiscuous_person now leaves team1, but does not lose their
+        # token because they're also in team2. No other tokens are
         # affected.
         with lp_dbuser():
             promiscuous_person.leave(team1)
@@ -310,11 +310,11 @@
         for person in persons2:
             self.assertDeactivated(tokens[person])
 
-        # promiscuous_person also loses the token because he's not in
+        # promiscuous_person also loses the token because they're not in
         # either team now.
         self.assertDeactivated(tokens[promiscuous_person])
 
-        # lonely_person still has his token, he's not in any teams.
+        # lonely_person still has their token; they're not in any teams.
         self.assertNotDeactivated(tokens[lonely_person])
 
     def setupDummyTokens(self):

=== modified file 'lib/lp/archiveuploader/tests/nascentupload.txt'
--- lib/lp/archiveuploader/tests/nascentupload.txt	2015-12-30 23:34:34 +0000
+++ lib/lp/archiveuploader/tests/nascentupload.txt	2016-01-26 15:58:00 +0000
@@ -815,8 +815,8 @@
 The ACL rules also enable us to specify that a user has a
 package-specific upload right.  In the test package data, bar_1.0-1 is
 signed by "Foo Bar" who is name16 in the sample data.  As shown above,
-he currently has no upload rights at all to Ubuntu.  However, we can add
-an ArchivePermission record to permit him to upload "bar" specifically.
+they currently have no upload rights at all to Ubuntu.  However, we can add
+an ArchivePermission record to permit them to upload "bar" specifically.
 
     >>> from lp.registry.interfaces.sourcepackagename import (
     ...     ISourcePackageNameSet)

=== modified file 'lib/lp/archiveuploader/tests/test_uploadprocessor.py'
--- lib/lp/archiveuploader/tests/test_uploadprocessor.py	2015-10-05 06:34:17 +0000
+++ lib/lp/archiveuploader/tests/test_uploadprocessor.py	2016-01-26 15:58:00 +0000
@@ -1560,7 +1560,7 @@
 
         # Upload the first version and accept it to make it known in
         # Ubuntu.  The uploader has rights to upload NEW packages to
-        # components that he does not have direct rights to.
+        # components that they do not have direct rights to.
         upload_dir = self.queueUpload("bar_1.0-1")
         self.processUpload(uploadprocessor, upload_dir)
         self.publishPackage('bar', '1.0-1')
@@ -1616,7 +1616,7 @@
 
         # Upload the first version and accept it to make it known in
         # Ubuntu.  The uploader has rights to upload NEW packages to
-        # components that he does not have direct rights to.
+        # components that they do not have direct rights to.
         upload_dir = self.queueUpload("bar_1.0-1")
         self.processUpload(uploadprocessor, upload_dir)
         self.publishPackage('bar', '1.0-1')

=== modified file 'lib/lp/archiveuploader/uploadpolicy.py'
--- lib/lp/archiveuploader/uploadpolicy.py	2015-10-02 10:46:08 +0000
+++ lib/lp/archiveuploader/uploadpolicy.py	2016-01-26 15:58:00 +0000
@@ -239,7 +239,7 @@
                 "if you need more space." % (
                 new_size / MEGA, self.archive.authorized_size))
         else:
-            # No need to warn user about his PPA's size.
+            # No need to warn user about their PPA's size.
             pass
 
     def policySpecificChecks(self, upload):

=== modified file 'lib/lp/blueprints/doc/sprint.txt'
--- lib/lp/blueprints/doc/sprint.txt	2015-08-24 12:25:10 +0000
+++ lib/lp/blueprints/doc/sprint.txt	2016-01-26 15:58:00 +0000
@@ -202,7 +202,7 @@
     >>> paris.driver == sample_person
     True
 
-Obviously, we'd expect isDriver to return true for her.
+Obviously, we'd expect isDriver to return true for them.
 
     >>> paris.isDriver(sample_person)
     True

=== modified file 'lib/lp/blueprints/model/tests/test_subscription.py'
--- lib/lp/blueprints/model/tests/test_subscription.py	2012-11-21 16:46:21 +0000
+++ lib/lp/blueprints/model/tests/test_subscription.py	2016-01-26 15:58:00 +0000
@@ -61,8 +61,8 @@
         return spec, subscriber, subscribed_by, subscription
 
     def test_can_unsubscribe_self(self):
-        # The user can of course unsubscribe himself, even if someone else
-        # subscribed him.
+        # The user can of course unsubscribe themselves, even if someone
+        # else subscribed them.
         (spec, subscriber,
             subscribed_by, subscription) = self._make_subscription()
         self.assertTrue(subscription.canBeUnsubscribedByUser(subscriber))
@@ -72,14 +72,14 @@
     # subscribed_by can unsubscribe, sometimes not.
     def test_subscriber_cannot_unsubscribe_user_from_public_spec(self):
         # For public specifications, the one who subscribed the
-        # subscriber doesn't have permission to unsubscribe him.
+        # subscriber doesn't have permission to unsubscribe them.
         (spec, subscriber,
             subscribed_by, subscription) = self._make_subscription()
         self.assertFalse(subscription.canBeUnsubscribedByUser(subscribed_by))
 
     def test_subscriber_can_unsubscribe_user_from_private_spec(self):
         # For private specifications, the one who subscribed the
-        # subscriber has permission to unsubscribe him.
+        # subscriber has permission to unsubscribe them.
         (spec, subscriber,
             subscribed_by, subscription) = self._make_subscription(True)
         self.assertTrue(subscription.canBeUnsubscribedByUser(subscribed_by))
@@ -91,7 +91,7 @@
         self.assertFalse(subscription.canBeUnsubscribedByUser(None))
 
     def test_can_unsubscribe_team(self):
-        # A user can unsubscribe a team he's a member of.
+        # A user can unsubscribe a team they're a member of.
         (spec, subscriber,
             subscribed_by, subscription) = self._make_subscription()
         team = self.factory.makeTeam()
@@ -105,7 +105,7 @@
         self.assertFalse(subscription.canBeUnsubscribedByUser(non_member))
 
     def test_cannot_unsubscribe_team(self):
-        # A user cannot unsubscribe a team he's a not member of.
+        # A user cannot unsubscribe a team they're not a member of.
         (spec, subscriber,
             subscribed_by, subscription) = self._make_subscription()
         team = self.factory.makeTeam()

=== modified file 'lib/lp/blueprints/stories/sprints/xx-sprints.txt'
--- lib/lp/blueprints/stories/sprints/xx-sprints.txt	2015-01-06 12:13:28 +0000
+++ lib/lp/blueprints/stories/sprints/xx-sprints.txt	2016-01-26 15:58:00 +0000
@@ -414,7 +414,7 @@
     ['yes']
 
 Sample Person can set a specific start and end time for participation,
-and specify that he is registering Carlos.
+and specify that they are registering Carlos.
 
     >>> browser.getControl('Attendee').value = 'carlos@xxxxxxxxxxxxx'
     >>> browser.getControl('From').value = '2006-01-10 18:30:00'

=== modified file 'lib/lp/bugs/browser/bugalsoaffects.py'
--- lib/lp/bugs/browser/bugalsoaffects.py	2015-10-01 17:32:41 +0000
+++ lib/lp/bugs/browser/bugalsoaffects.py	2016-01-26 15:58:00 +0000
@@ -184,8 +184,8 @@
 
         # The user has entered a product name but we couldn't find it.
         # Tell the user to search for it using the popup widget as it'll allow
-        # the user to register a new product if the one he is looking for is
-        # not yet registered.
+        # the user to register a new product if the one they are looking for
+        # is not yet registered.
         widget_link_id = self.widgets['product'].show_widget_id
         self.setFieldError(
             'product',
@@ -271,7 +271,7 @@
                 extracted_bugtracker, extracted_bug = getUtility(
                     IBugWatchSet).extractBugTrackerAndBug(bug_url)
             except NoBugTrackerFound:
-                # Delegate to another view which will ask the user if (s)he
+                # Delegate to another view which will ask the user if they
                 # wants to create the bugtracker now.
                 if 'product' in self.target_field_names:
                     self.next_step = UpstreamBugTrackerCreationStep
@@ -379,7 +379,7 @@
             target.bug_tracking_usage != ServiceUsage.LAUNCHPAD):
             # We have no URL for the remote bug and the target does not use
             # Launchpad for bug tracking, so we warn the user this is not
-            # optimal and ask for his confirmation.
+            # optimal and ask for their confirmation.
 
             # Add a hidden field to fool LaunchpadFormView into thinking we
             # submitted the action it expected when in fact we're submiting
@@ -686,7 +686,7 @@
 class BugTrackerCreationStep(AlsoAffectsStep):
     """View for creating a bugtracker from the given URL.
 
-    This view will ask the user if he really wants to register the new bug
+    This view will ask the user if they really want to register the new bug
     tracker, perform the registration and then delegate to one of
     BugTaskCreationStep's subclasses.
     """
@@ -847,7 +847,7 @@
 
         If the URL of the remote bug given is of a bugtracker used by any
         other products registered in Launchpad, then we show these products to
-        the user and ask if he doesn't want to create the task in one of them.
+        the user and ask if they don't want to create the task in one of them.
         """
         if self.existing_products and not self.request.form.get('create_new'):
             # Present the projects using that bugtracker to the user as

=== modified file 'lib/lp/bugs/browser/bugattachment.py'
--- lib/lp/bugs/browser/bugattachment.py	2015-07-08 16:05:11 +0000
+++ lib/lp/bugs/browser/bugattachment.py	2016-01-26 15:58:00 +0000
@@ -149,7 +149,7 @@
             # real patch data, indicated by guessed_content_type ==
             # 'text/x-diff'. If there are inconsistencies, we don't
             # set the value automatically. Instead, we lead the user to
-            # another form where we ask him if he is sure about his
+            # another form where we ask them if they are sure about their
             # choice of the patch flag.
             new_type_consistent_with_guessed_type = (
                 self.attachmentTypeConsistentWithContentType(
@@ -191,8 +191,8 @@
 
     If the user sets the "patch" flag to a value that is inconsistent
     with the result of a call of guess_content_type() for this
-    attachment, we show this view to ask the user if he is sure
-    about his selection.
+    attachment, we show this view to ask the user if they are sure
+    about their selection.
     """
 
     schema = IBugAttachmentIsPatchConfirmationForm

=== modified file 'lib/lp/bugs/browser/bugmessage.py'
--- lib/lp/bugs/browser/bugmessage.py	2015-09-10 08:18:35 +0000
+++ lib/lp/bugs/browser/bugmessage.py	2016-01-26 15:58:00 +0000
@@ -108,7 +108,7 @@
             # If the patch flag is not consistent with the result of
             # the guess made in attachmentTypeConsistentWithContentType(),
             # we use the guessed type and lead the user to a page
-            # where he can override the flag value, if Launchpad's
+            # where they can override the flag value, if Launchpad's
             # guess is wrong.
             patch_flag_consistent = (
                 self.attachmentTypeConsistentWithContentType(

=== modified file 'lib/lp/bugs/browser/bugtarget.py'
--- lib/lp/bugs/browser/bugtarget.py	2015-07-08 16:05:11 +0000
+++ lib/lp/bugs/browser/bugtarget.py	2016-01-26 15:58:00 +0000
@@ -717,7 +717,7 @@
         """See IBrowserPublisher."""
         if self.extra_data_token is not None:
             # publishTraverse() has already been called once before,
-            # which means that he URL contains more path components than
+            # which means that the URL contains more path components than
             # expected.
             raise NotFound(self, name, request=request)
 
@@ -726,7 +726,7 @@
             # The URL might be mistyped, or the blob has expired.
             # XXX: Bjorn Tillenius 2006-01-15:
             #      We should handle this case better, since a user might
-            #      come to this page when finishing his account
+            #      come to this page when finishing their account
             #      registration. In that case we should inform the user
             #      that the blob has expired.
             raise NotFound(self, name, request=request)

=== modified file 'lib/lp/bugs/browser/bugtask.py'
--- lib/lp/bugs/browser/bugtask.py	2015-09-30 00:29:32 +0000
+++ lib/lp/bugs/browser/bugtask.py	2016-01-26 15:58:00 +0000
@@ -2368,7 +2368,7 @@
         owner_is_subscribed = question.isSubscribed(self.context.bug.owner)
         question.unlinkBug(self.context.bug, user=self.user)
         # The question.owner was implicitly unsubscribed when the bug
-        # was unlinked. We resubscribe the owner if he was subscribed.
+        # was unlinked. We resubscribe the owner if they were subscribed.
         if owner_is_subscribed is True:
             self.context.bug.subscribe(question.owner, self.user)
         self.request.response.addNotification(

=== modified file 'lib/lp/bugs/browser/structuralsubscription.py'
--- lib/lp/bugs/browser/structuralsubscription.py	2015-09-28 17:38:45 +0000
+++ lib/lp/bugs/browser/structuralsubscription.py	2016-01-26 15:58:00 +0000
@@ -345,8 +345,8 @@
     def subscribe(self):
         """The subscribe menu link.
 
-        If the user, or any of the teams he's a member of, already has a
-        subscription to the context, the link offer to edit the subscriptions
+        If the user, or any of the teams they're a member of, already has a
+        subscription to the context, the link offers to edit the subscriptions
         and displays the edit icon. Otherwise, the link offers to subscribe
         and displays the add icon.
         """

=== modified file 'lib/lp/bugs/browser/tests/bugs-views.txt'
--- lib/lp/bugs/browser/tests/bugs-views.txt	2012-05-11 12:53:58 +0000
+++ lib/lp/bugs/browser/tests/bugs-views.txt	2016-01-26 15:58:00 +0000
@@ -74,7 +74,7 @@
 
 Only the bugs that the user has permission to view are shown in the
 list, so if we mark bug #4 as private, No Privileges won't see it, since
-he's not subscribed to it.
+they're not subscribed to it.
 
     >>> bug_4 = getUtility(IBugSet).get(4)
     >>> bug_4.setPrivate(True, getUtility(ILaunchBag).user)

=== modified file 'lib/lp/bugs/browser/tests/bugtask-adding-views.txt'
--- lib/lp/bugs/browser/tests/bugtask-adding-views.txt	2015-10-01 17:32:41 +0000
+++ lib/lp/bugs/browser/tests/bugtask-adding-views.txt	2016-01-26 15:58:00 +0000
@@ -144,7 +144,7 @@
     None
 
 Sometimes the distribution won't have any series, though. In that
-case, we won't prompt the user to add a link, since he can't actually
+case, we won't prompt the user to add a link, since they can't actually
 add one.
 
     >>> gentoo = getUtility(IDistributionSet).getByName('gentoo')
@@ -307,7 +307,7 @@
     Mozilla Firefox
 
 If the URL is valid but there's no bugtracker registered with that URL,
-we ask the user if he wants to register the bugtracker as well.
+we ask the user if they want to register the bugtracker as well.
 
     >>> form['field.product'] = u'aptoncd'
     >>> form['field.bug_url'] = (
@@ -382,7 +382,7 @@
     'Unknown'
 
 If the same bug watch is added to another bug, the bug watch will be
-added, but a notification is shown to the user informing him that
+added, but a notification is shown to the user informing them that
 another bug links to the same bug.
 
     >>> bug_five = getUtility(IBugSet).get(5)
@@ -477,7 +477,7 @@
     None
 
 Since the user is just creating the product in Launchpad to link to an
-upstream she probably isn't interested in maintaining the product for
+upstream they probably aren't interested in maintaining the product for
 the long-term.  In recognition of that we set the maintainer to be the
 Registry Admins team while keeping the user as the registrant.
 

=== modified file 'lib/lp/bugs/browser/tests/person-bug-views.txt'
--- lib/lp/bugs/browser/tests/person-bug-views.txt	2014-11-29 00:03:36 +0000
+++ lib/lp/bugs/browser/tests/person-bug-views.txt	2016-01-26 15:58:00 +0000
@@ -333,7 +333,7 @@
 
 It is possible to search for all the bugs commented on by a specific Person
 using that Person's +commentedbugs page. Since No Privileges Person hasn't
-commented on any bugs, viewing his +commentedbugs page will return no bugs:
+commented on any bugs, viewing their +commentedbugs page will return no bugs:
 
     >>> no_priv = getUtility(IPersonSet).getByName('no-priv')
     >>> commented_bugtasks_view = create_view(no_priv, '+commentedbugs')
@@ -341,10 +341,10 @@
     >>> [bugtask.bug.id for bugtask in sorted(commented_bugs)]
     []
 
-If No Privileges Person comments on bug one, his +commentedbugs page will list
-that bug as being one of the bugs on which he has commented. The bug will be
-listed three times since there are three BugTasks for that particular bug (see
-bug 1357):
+If No Privileges Person comments on bug one, their +commentedbugs page will
+list that bug as being one of the bugs on which they have commented. The bug
+will be listed three times since there are three BugTasks for that
+particular bug (see bug 1357):
 
     >>> from lp.bugs.interfaces.bug import IBugSet
     >>> bug_one = getUtility(IBugSet).get(1)

=== modified file 'lib/lp/bugs/browser/tests/test_bugattachment_file_access.py'
--- lib/lp/bugs/browser/tests/test_bugattachment_file_access.py	2012-01-01 02:58:52 +0000
+++ lib/lp/bugs/browser/tests/test_bugattachment_file_access.py	2016-01-26 15:58:00 +0000
@@ -98,8 +98,8 @@
         self.assertIsNot(None, mo)
 
     def test_access_to_restricted_file_unauthorized(self):
-        # If a user cannot access the bug attachment itself, he can neither
-        # access the restricted Librarian file.
+        # If a user cannot access the bug attachment itself, they cannot
+        # access the restricted Librarian file either.
         lfa_with_parent = getMultiAdapter(
             (self.bugattachment.libraryfile, self.bugattachment),
             ILibraryFileAliasWithParent)

=== modified file 'lib/lp/bugs/browser/tests/test_bugsubscription_views.py'
--- lib/lp/bugs/browser/tests/test_bugsubscription_views.py	2015-04-30 10:21:14 +0000
+++ lib/lp/bugs/browser/tests/test_bugsubscription_views.py	2016-01-26 15:58:00 +0000
@@ -840,7 +840,7 @@
                 dumps([expected_result]), harness.view.subscriber_data_js)
 
     def test_data_person_subscription_user_excluded(self):
-        # With the subscriber logged in, he is not included in the results.
+        # With the subscriber logged in, they are not included in the results.
         bug = self._makeBugWithNoSubscribers()
         subscriber = self.factory.makePerson(
             name='a-person', displayname='Subscriber Name')

=== modified file 'lib/lp/bugs/browser/tests/test_bugtarget_filebug.py'
--- lib/lp/bugs/browser/tests/test_bugtarget_filebug.py	2015-01-29 14:14:01 +0000
+++ lib/lp/bugs/browser/tests/test_bugtarget_filebug.py	2016-01-26 15:58:00 +0000
@@ -243,7 +243,7 @@
 
     def test_bug_filing_view_with_dupe_search_enabled(self):
         # When a user files a bug for a product where searching for
-        # duplicate bugs is enabled, he is asked to provide a
+        # duplicate bugs is enabled, they are asked to provide a
         # summary of the bug. This summary is used to find possible
         # existing duplicates f this bug.
         product = self.factory.makeProduct()
@@ -269,7 +269,7 @@
 
     def test_bug_filing_view_with_dupe_search_disabled(self):
         # When a user files a bug for a product where searching for
-        # duplicate bugs is disabled, he can directly enter all
+        # duplicate bugs is disabled, they can directly enter all
         # details of the bug.
         product = self.factory.makeProduct()
         login_person(product.owner)

=== modified file 'lib/lp/bugs/browser/tests/test_bugtask.py'
--- lib/lp/bugs/browser/tests/test_bugtask.py	2015-10-15 14:09:50 +0000
+++ lib/lp/bugs/browser/tests/test_bugtask.py	2016-01-26 15:58:00 +0000
@@ -393,7 +393,7 @@
 
     def test_other_users_affected_count(self):
         # The number of other users affected does not change when the
-        # logged-in user marked him or herself as affected or not.
+        # logged-in user marked themselves as affected or not.
         self.failUnlessEqual(
             1, self.view.other_users_affected_count)
         self.bug.markUserAffected(self.view.user, True)

=== modified file 'lib/lp/bugs/browser/widgets/bugtask.py'
--- lib/lp/bugs/browser/widgets/bugtask.py	2015-07-08 16:05:11 +0000
+++ lib/lp/bugs/browser/widgets/bugtask.py	2016-01-26 15:58:00 +0000
@@ -263,10 +263,10 @@
         return context.userCanUnassign(user) or context.assignee is None
 
     def showPersonChooserWidget(self):
-        """Should the person chooser widget bw shown?
+        """Should the person chooser widget be shown?
 
         The person chooser is shown only if the user can assign at least
-        one other person or team in addition to himself.
+        one other person or team in addition to themselves.
         """
         user = getUtility(ILaunchBag).user
         context = self.context.context

=== modified file 'lib/lp/bugs/doc/bug.txt'
--- lib/lp/bugs/doc/bug.txt	2015-10-01 02:59:28 +0000
+++ lib/lp/bugs/doc/bug.txt	2016-01-26 15:58:00 +0000
@@ -1204,7 +1204,7 @@
 
 However, if the user was also marked as being affected by the master
 bug, then the master bug's user_affected_count does *not* increment
-just because she is also affected by the duplicate.
+just because they are also affected by the duplicate.
 
     >>> test_bug.markUserAffected(dupe_affected_user, affected=True)
     >>> test_bug.users_affected_count_with_dupes
@@ -1229,7 +1229,7 @@
     >>> test_bug.users_affected_count_with_dupes
     4
 
-If the user claims that two bugs both affect her, then if they are
+If the user claims that two bugs both affect them, then if they are
 both marked as duplicates of the master bugs, the master bug's
 user_affected_count still only increments by 1 for that user.
 
@@ -1257,9 +1257,9 @@
     paul-dianno
     sheila-shakespeare
 
-If the user marks the master bug as not affecting her, but the master
-bug still has a duplicate that she claims affects her, then that
-duplicate is also marked as not affecting her either.
+If the user marks the master bug as not affecting them, but the master
+bug still has a duplicate that they claim affects them, then that
+duplicate is also marked as not affecting them either.
 
     >>> dupe_one.users_affected_count
     1
@@ -1290,7 +1290,7 @@
     <BLANKLINE>
 
 Since the user who filed the first two dups had an entry explicitly
-saying she was affected, they now claim that she is unaffected.
+saying they were affected, they now claim that they are unaffected.
 
     >>> print '\n'.join(
     ...     sorted(user.name for user in dupe_one.users_unaffected))
@@ -1299,10 +1299,10 @@
     ...     sorted(user.name for user in dupe_two.users_unaffected))
     sheila-shakespeare
 
-But she didn't file the third dup, so there was never any explicit
-record saying she was affected by it.  Thus she also does not appear
+But they didn't file the third dup, so there was never any explicit
+record saying they were affected by it.  Thus they also do not appear
 as explicitly unaffected, even after marking the master bug as not
-affecting her.
+affecting them.
 
     >>> print '\n'.join(
     ...     sorted(user.name for user in dupe_three.users_unaffected))

=== modified file 'lib/lp/bugs/doc/bugattachments.txt'
--- lib/lp/bugs/doc/bugattachments.txt	2012-09-17 16:13:40 +0000
+++ lib/lp/bugs/doc/bugattachments.txt	2016-01-26 15:58:00 +0000
@@ -299,9 +299,9 @@
 Security
 --------
 
-If a user can view/edit the bug the attachment is attached to, he can
+If a user can view/edit the bug the attachment is attached to, they can
 also view/edit the attachment. At the moment the bug_four is public, so
-anonymous can read the attachment's attributes, but he can't set them:
+anonymous can read the attachment's attributes, but they can't set them:
 
     >>> login(ANONYMOUS)
     >>> attachment.title
@@ -331,8 +331,8 @@
     >>> bug_four.setPrivate(True, getUtility(ILaunchBag).user)
     True
 
-Foo Bar isn't explicitly subscribed to the bug, BUT he is an admin, so he can
-access and set the attachment's attributes:
+Foo Bar isn't explicitly subscribed to the bug, BUT they are an admin, so
+they can access and set the attachment's attributes:
 
     >>> attachment.title
     u'Even Better Title'
@@ -364,7 +364,7 @@
     ...
     Unauthorized: (..., 'title',...
 
-Sample Person is explicitly subscribed, so he can both access and set
+Sample Person is explicitly subscribed, so they can both access and set
 the attributes:
 
     >>> login('test@xxxxxxxxxxxxx')

=== modified file 'lib/lp/bugs/doc/bugnotification-sending.txt'
--- lib/lp/bugs/doc/bugnotification-sending.txt	2015-10-05 06:34:17 +0000
+++ lib/lp/bugs/doc/bugnotification-sending.txt	2016-01-26 15:58:00 +0000
@@ -1022,7 +1022,7 @@
     http://bugs.launchpad.dev/.../+bug/.../+subscriptions
     ----------------------------------------------------------------------
 
-And Concise Team Person does too, even though his team doesn't want them:
+And Concise Team Person does too, even though their team doesn't want them:
 
     >>> print_notification(collated_messages['conciseteam@xxxxxxxxxxx'][0])
     To: conciseteam@xxxxxxxxxxx
@@ -1124,7 +1124,7 @@
 The notifications generated by addCommentNotification() are sent only to
 structural subscribers with no filters, or with the notification level
 of COMMENTS or higher. Sample Person's subscription currently does not
-have any filters other than the initial catch-all one, so he receives these
+have any filters other than the initial catch-all one, so they receive these
 notifications.
 
     >>> print subscription_no_priv.bug_filters.count()
@@ -1177,7 +1177,7 @@
     ----------------------------------------------------------------------
 
 If Sample Person gets a filter with an explicit notification level of
-COMMENTS, he also receives these notifications.
+COMMENTS, they also receive these notifications.
 
 
     >>> flush_notifications()
@@ -1235,7 +1235,7 @@
     ...
     ----------------------------------------------------------------------
 
-If Sample Person's notification level is set to METADATA, he receives
+If Sample Person's notification level is set to METADATA, they receive
 no comment notifications.
 
     >>> flush_notifications()
@@ -1285,8 +1285,8 @@
 
 The notifications generated by addChange() are sent only to structural
 subscribers with the notification level METADATA or higher. The
-notification level of Sample Person is currently METADATA, hence he
-receives these notifications.
+notification level of Sample Person is currently METADATA, hence they
+receive these notifications.
 
     >>> bug_one.addChange(
     ...     BugTitleChange(
@@ -1342,7 +1342,7 @@
     ...
     ----------------------------------------------------------------------
 
-If Sample Person sets his notification level to LIFECYCLE, he receives
+If Sample Person sets their notification level to LIFECYCLE, they receive
 no notifications created by addChange().
 
     >>> flush_notifications()

=== modified file 'lib/lp/bugs/doc/bugnotificationrecipients.txt'
--- lib/lp/bugs/doc/bugnotificationrecipients.txt	2012-12-26 01:32:19 +0000
+++ lib/lp/bugs/doc/bugnotificationrecipients.txt	2016-01-26 15:58:00 +0000
@@ -228,8 +228,8 @@
           of testing Spanish team, which is subscribed to a
           duplicate bug report.
 
-This doesn't help the end-user too much if he's a member of this team
-indirectly (for instance, if he's a member of a team which is in turn a
+This doesn't help the end-user too much if they're a member of this team
+indirectly (for instance, if they're a member of a team which is in turn a
 member of another team); however, in that case, the user can still visit
 the team page and see the membership graph directly. This may be worth
 fixing in the future.
@@ -239,7 +239,7 @@
 
 Another important property of BugNotificationRecipients is that the
 first rationale presented to it is the one that is presented -- even if
-the recipient has multiple reasons for which he might be emailed. Here's
+the recipient has multiple reasons for which they might be emailed. Here's
 a pathological example:
 
     >>> recipients = BugNotificationRecipients()
@@ -247,7 +247,7 @@
     >>> recipients.addAssignee(test)
     >>> recipients.addDirectSubscriber(foo_bar)
 
-This guy is emailed because he's a direct subscriber, an assignee and an
+This guy is emailed because they're a direct subscriber, an assignee and an
 upstream registrant. However, if we ask the rationales instance:
 
     >>> print_rationales(recipients)

=== modified file 'lib/lp/bugs/doc/bugnotifications.txt'
--- lib/lp/bugs/doc/bugnotifications.txt	2011-08-01 05:25:59 +0000
+++ lib/lp/bugs/doc/bugnotifications.txt	2016-01-26 15:58:00 +0000
@@ -223,7 +223,7 @@
 === Editing a task ===
 
 Let's demonstrate a notification email where Sample Person marks a
-task Fixed, and assigns himself to it.
+task Fixed, and assigns themselves to it.
 
     >>> from lazr.lifecycle.snapshot import Snapshot
     >>> from lp.bugs.interfaces.bugtask import (

=== modified file 'lib/lp/bugs/doc/bugsubscription.txt'
--- lib/lp/bugs/doc/bugsubscription.txt	2014-04-29 00:44:32 +0000
+++ lib/lp/bugs/doc/bugsubscription.txt	2016-01-26 15:58:00 +0000
@@ -271,7 +271,7 @@
     Sample Person
 
 If No Privileges Person created a single filter with a notification
-level set to LIFECYCLE, he will not be included, if the parameter
+level set to LIFECYCLE, they will not be included, if the parameter
 `level` is METADATA or COMMENTS.
 
     >>> from lp.testing import person_logged_in
@@ -383,7 +383,7 @@
      ('mark@xxxxxxxxxxx', 'Subscriber'),
      ('test@xxxxxxxxxxxxx', 'Assignee')]
 
-When Sample Person is unsubscribed from linux_source_bug, he is no
+When Sample Person is unsubscribed from linux_source_bug, they are no
 longer included in the result of getBugNotificationRecipients() for
 the COMMENTS level...
 
@@ -533,7 +533,7 @@
     >>> subscription.canBeUnsubscribedByUser(None)
     False
 
-A user can unsubscribe a team he's a member of.
+A user can unsubscribe a team they're a member of.
 
     >>> team = factory.makeTeam()
     >>> member = factory.makePerson()
@@ -588,7 +588,7 @@
     ...  for subscription in new_bug.subscriptions]
     [u'Foo Bar']
 
-But because Sample Person is the distribution contact for Ubuntu, he
+But because Sample Person is the distribution contact for Ubuntu, they
 will be implicitly added to the notification recipients.
 
     >>> getSubscribers(new_bug)

=== modified file 'lib/lp/bugs/doc/bugtask-display-widgets.txt'
--- lib/lp/bugs/doc/bugtask-display-widgets.txt	2012-08-29 06:24:05 +0000
+++ lib/lp/bugs/doc/bugtask-display-widgets.txt	2016-01-26 15:58:00 +0000
@@ -10,7 +10,7 @@
 ---------------------
 
 This widget is used to display the assignee of a bug task. It displays
-the assignee's browser name, with a link to his person page, and with a
+the assignee's browser name, with a link to their person page, and with a
 person icon in front of the name.
 
     >>> from lp.bugs.browser.widgets.bugtask import AssigneeDisplayWidget

=== modified file 'lib/lp/bugs/doc/bugtask-expiration.txt'
--- lib/lp/bugs/doc/bugtask-expiration.txt	2012-05-22 12:05:51 +0000
+++ lib/lp/bugs/doc/bugtask-expiration.txt	2016-01-26 15:58:00 +0000
@@ -421,7 +421,7 @@
     >>> print sorted(bug.title for bug in visible_bugs)
     [u'recent']
 
-... unless he's subscribed to the bug.
+... unless they're subscribed to the bug.
 
     >>> private_bug.subscribe(no_priv, sample_person)
     <lp.bugs.model.bugsubscription.BugSubscription ...>

=== modified file 'lib/lp/bugs/doc/bugtask-search.txt'
--- lib/lp/bugs/doc/bugtask-search.txt	2015-10-05 06:34:17 +0000
+++ lib/lp/bugs/doc/bugtask-search.txt	2016-01-26 15:58:00 +0000
@@ -150,7 +150,7 @@
 must be specified by a bus as enumerated by HWBus, a vendor ID and a
 product ID. If we search for bugs related to the PCI device (0x10de,
 0x0455), which appears in a HWDB submission from Sample Person, bugs
-reported by him will be returned.
+reported by them will be returned.
 
     >>> from lp.hardwaredb.interfaces.hwdb import HWBus
     >>> search_params = BugTaskSearchParams(

=== modified file 'lib/lp/bugs/doc/externalbugtracker-bug-imports.txt'
--- lib/lp/bugs/doc/externalbugtracker-bug-imports.txt	2015-07-21 09:04:01 +0000
+++ lib/lp/bugs/doc/externalbugtracker-bug-imports.txt	2016-01-26 15:58:00 +0000
@@ -34,7 +34,7 @@
     ...     'http://example.com/')
 
 When importing bugs, the reporter is automatically created in Launchpad,
-if he doesn't exist.
+if they don't exist.
 
     >>> from lp.registry.interfaces.person import IPersonSet
     >>> print getUtility(IPersonSet).getByEmail('joe.bloggs@xxxxxxxxxxx')
@@ -85,8 +85,8 @@
     >>> print bug.owner.displayname
     Joe Bloggs
 
-Since he didn't have a Launchpad account before, he doesn't have a
-preferred email address, and the one that is associated with his
+Since they didn't have a Launchpad account before, they don't have a
+preferred email address, and the one that is associated with their
 account is marked as NEW, since we don't know whether it's valid.
 
     >>> from lp.services.identity.interfaces.emailaddress import (
@@ -168,7 +168,7 @@
 ----------------------------------------
 
 Even if the reporter of the bug has an account in Launchpad (and thus a
-valid email address), he still won't be subscribed to the imported bug.
+valid email address), they still won't be subscribed to the imported bug.
 
     >>> no_priv = getUtility(IPersonSet).getByName('no-priv')
     >>> no_priv.preferredemail is not None

=== modified file 'lib/lp/bugs/interfaces/bugtask.py'
--- lib/lp/bugs/interfaces/bugtask.py	2015-10-01 17:32:41 +0000
+++ lib/lp/bugs/interfaces/bugtask.py	2016-01-26 15:58:00 +0000
@@ -603,8 +603,8 @@
         :param person: The person to check to see if they are a contributor.
 
         Return a dict with the following values:
-        is_contributor: True if the user has any bugs assigned to him in the
-        context of this bug task's pillar, either directly or by team
+        is_contributor: True if the user has any bugs assigned to them in
+        the context of this bug task's pillar, either directly or by team
         participation.
         person_name: the displayname of the person
         pillar_name: the displayname of the bug task's pillar

=== modified file 'lib/lp/bugs/mail/bugnotificationbuilder.py'
--- lib/lp/bugs/mail/bugnotificationbuilder.py	2015-09-11 12:20:23 +0000
+++ lib/lp/bugs/mail/bugnotificationbuilder.py	2016-01-26 15:58:00 +0000
@@ -54,8 +54,8 @@
         return format_address(person.displayname, person.preferredemail.email)
 
     # XXX: Bjorn Tillenius 2006-04-05:
-    # The person doesn't have a preferred email set, but he
-    # added a comment (either via the email UI, or because he was
+    # The person doesn't have a preferred email set, but they
+    # added a comment (either via the email UI, or because they were
     # imported as a deaf reporter). It shouldn't be possible to use the
     # email UI if you don't have a preferred email set, but work around
     # it for now by trying hard to find the right email address to use.

=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py	2015-10-21 03:46:31 +0000
+++ lib/lp/bugs/model/bug.py	2016-01-26 15:58:00 +0000
@@ -751,7 +751,7 @@
         # Do the search as the Janitor, to ensure that this bug can be
         # found, even if it's private. We don't have access to the user
         # calling this property. If the user has access to view this
-        # property, he has permission to see the bug, so we're not
+        # property, they have permission to see the bug, so we're not
         # exposing something we shouldn't. The Janitor has access to
         # view all bugs.
         bugtasks = getUtility(IBugTaskSet).findExpirableBugTasks(
@@ -776,7 +776,7 @@
         # Do the search as the Janitor, to ensure that this bug can be
         # found, even if it's private. We don't have access to the user
         # calling this property. If the user has access to view this
-        # property, he has permission to see the bug, so we're not
+        # property, they have permission to see the bug, so we're not
         # exposing something we shouldn't. The Janitor has access to
         # view all bugs.
         bugtasks = getUtility(IBugTaskSet).findExpirableBugTasks(

=== modified file 'lib/lp/bugs/model/tests/test_bugtask.py'
--- lib/lp/bugs/model/tests/test_bugtask.py	2015-10-08 08:59:00 +0000
+++ lib/lp/bugs/model/tests/test_bugtask.py	2016-01-26 15:58:00 +0000
@@ -600,7 +600,7 @@
         self.assertEqual(sorted(bug_ids), [1, 4, 5, 6])
         apgs.revoke([(policy, mr_no_privs)])
 
-        # Privacy and Priviledged Users
+        # Privacy and Privileged Users
         # Now, we'll log in as Mark Shuttleworth, who was assigned to this bug
         # when it was marked private:
         login("mark@xxxxxxxxxxx")
@@ -611,8 +611,8 @@
             BugTaskStatus.NEW, getUtility(ILaunchBag).user)
 
         # Privacy and Team Awareness
-        # No Privileges Person can't see the private bug, because he's not a
-        # subscriber:
+        # No Privileges Person can't see the private bug, because they're
+        # not a subscriber:
         no_priv = getUtility(IPersonSet).getByEmail('no-priv@xxxxxxxxxxxxx')
         params = BugTaskSearchParams(
             status=any(BugTaskStatus.NEW, BugTaskStatus.CONFIRMED),
@@ -1040,7 +1040,7 @@
             self.series_bugtask.userCanSetAnyAssignee(
                 self.series_driver_member))
         if self.supervisor_member is not None:
-            # But he cannot assign anybody to bug tasks of the main target...
+            # But they cannot assign anybody to bug tasks of the main target...
             self.assertFalse(
                 self.target_bugtask.userCanSetAnyAssignee(
                     self.series_driver_member))

=== modified file 'lib/lp/bugs/security.py'
--- lib/lp/bugs/security.py	2012-03-02 00:19:37 +0000
+++ lib/lp/bugs/security.py	2016-01-26 15:58:00 +0000
@@ -144,7 +144,7 @@
 class ViewBugAttachment(DelegatedAuthorization):
     """Security adapter for viewing a bug attachment.
 
-    If the user is authorized to view the bug, he's allowed to view the
+    If the user is authorized to view the bug, they're allowed to view the
     attachment.
     """
     permission = 'launchpad.View'
@@ -158,7 +158,7 @@
 class EditBugAttachment(DelegatedAuthorization):
     """Security adapter for editing a bug attachment.
 
-    If the user is authorized to view the bug, he's allowed to edit the
+    If the user is authorized to view the bug, they're allowed to edit the
     attachment.
     """
     permission = 'launchpad.Edit'

=== modified file 'lib/lp/bugs/stories/bug-also-affects/xx-also-affects-new-upstream.txt'
--- lib/lp/bugs/stories/bug-also-affects/xx-also-affects-new-upstream.txt	2013-09-27 04:13:23 +0000
+++ lib/lp/bugs/stories/bug-also-affects/xx-also-affects-new-upstream.txt	2016-01-26 15:58:00 +0000
@@ -96,7 +96,7 @@
 == Error handling ==
 
 If the URL of the remote bug is not recognized by Launchpad, we'll tell the
-user and ask him to check if it's correct.
+user and ask them to check if it's correct.
 
     >>> user_browser.open(
     ...     'http://bugs.launchpad.dev/firefox/+bug/1/+affects-new-product')

=== modified file 'lib/lp/bugs/stories/bug-also-affects/xx-bug-also-affects.txt'
--- lib/lp/bugs/stories/bug-also-affects/xx-bug-also-affects.txt	2015-10-05 06:34:17 +0000
+++ lib/lp/bugs/stories/bug-also-affects/xx-bug-also-affects.txt	2016-01-26 15:58:00 +0000
@@ -353,7 +353,7 @@
     Required input is missing.
 
 If we enter a product name that doesn't exist, we inform the user about
-this and ask him to search for the product.
+this and ask them to search for the product.
 
     >>> user_browser.getControl('Project').value = 'no-such-product'
     >>> user_browser.getControl('Continue').click()
@@ -579,7 +579,7 @@
     >>> print cancel_link.url
     http://bugs.launchpad.dev/firefox/+bug/4
 
-Now the user confirms he wants us to register the bug tracker for him
+Now the user confirms they want us to register the bug tracker for them
 and we do that before creating the new bug watch.
 
     >>> user_browser.getControl('Register Bug Tracker').click()

=== modified file 'lib/lp/bugs/stories/bug-privacy/xx-bug-privacy.txt'
--- lib/lp/bugs/stories/bug-privacy/xx-bug-privacy.txt	2015-06-26 14:00:41 +0000
+++ lib/lp/bugs/stories/bug-privacy/xx-bug-privacy.txt	2016-01-26 15:58:00 +0000
@@ -6,7 +6,7 @@
     ...     "+bug/2/+secrecy")
 
 Foo Bar is not Cc'd on this bug, but is able to set the bug private
-anyway, because he is an admin.
+anyway, because they are an admin.
 
     >>> browser.getControl("Private", index=1).selected = True
     >>> browser.getControl("Change").click()
@@ -21,8 +21,8 @@
     >>> browser.getControl("Private", index=1).selected
     True
 
-Foo Bar files a security (private) bug on Ubuntu. He gets
-redirected to the bug page.
+Foo Bar files a security (private) bug on Ubuntu. They get redirected to the
+bug page.
 
     >>> browser = setupBrowser("Basic foo.bar@xxxxxxxxxxxxx:test")
     >>> browser.open("http://launchpad.dev/ubuntu/+filebug";)
@@ -47,7 +47,7 @@
     <!DOCTYPE...
     ...Security-related bugs are by default private...
 
-Foo Bar sees the private bug he filed.
+Foo Bar sees the private bug they filed.
 
     >>> browser.open("http://launchpad.dev/ubuntu/+bugs";)
     >>> print browser.contents.replace(bug_id, "BUG-ID")

=== modified file 'lib/lp/bugs/stories/bugs/bug-add-subscriber.txt'
--- lib/lp/bugs/stories/bugs/bug-add-subscriber.txt	2015-09-11 12:20:23 +0000
+++ lib/lp/bugs/stories/bugs/bug-add-subscriber.txt	2016-01-26 15:58:00 +0000
@@ -13,7 +13,7 @@
     ...
     Unauthorized: ...
 
-No Privileges wants to subscribe David Allouche to the bug because he knows
+No Privileges wants to subscribe David Allouche to the bug because they know
 that he's interested in that feature.
 
     >>> user_browser.open('http://bugs.launchpad.dev/firefox/+bug/1')
@@ -28,7 +28,7 @@
     >>> cancel_link.url
     'http://bugs.launchpad.dev/firefox/+bug/1'
 
-By looking at the 'Subscribers' portlet, he sees that David Allouche is not
+By looking at the 'Subscribers' portlet, they see that David Allouche is not
 currently subscribed to the bug:
 
     >>> from lp.bugs.tests.bug import (
@@ -45,7 +45,7 @@
     Foo Bar
     Mark Shuttleworth
 
-He subscribes David Allouche to the bug using his Launchpad username.
+They subscribe David Allouche to the bug using his Launchpad username.
 
     >>> user_browser.open('http://bugs.launchpad.dev/firefox/+bug/1/+addsubscriber')
     >>> user_browser.getControl("Person").value = 'ddaa'
@@ -53,7 +53,7 @@
     >>> user_browser.url
     'http://bugs.launchpad.dev/firefox/+bug/1'
 
-He is notified that David Allouche has been subscribed.
+They are notified that David Allouche has been subscribed.
 
     >>> for tag in find_tags_by_class(user_browser.contents,
     ...     'informational message'):
@@ -120,8 +120,8 @@
 -----------------------------
 
 Private teams can be subscribed to bugs. Any logged in user can see
-the private team in the subscribers list. Additionally if she is a member of
-the private team she can unsubscribe the team.
+the private team in the subscribers list. Additionally if they are a member
+of the private team they can unsubscribe the team.
 
     >>> # Create a private team with Foo Bar as the owner.
     >>> from zope.component import getUtility

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-affects-me-too.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-affects-me-too.txt	2012-07-07 14:00:30 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-affects-me-too.txt	2016-01-26 15:58:00 +0000
@@ -53,7 +53,7 @@
 
    >>> user_browser.getLink(url='+affectsmetoo').click()
 
-The user changes his selection to 'No' and submits the form.
+The user changes their selection to 'No' and submits the form.
 
    >>> user_browser.getControl(name='field.affects').value = ['NO']
    >>> user_browser.getControl('Change').click()

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-comments-truncated.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-comments-truncated.txt	2012-12-11 05:41:50 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-comments-truncated.txt	2016-01-26 15:58:00 +0000
@@ -116,7 +116,7 @@
     ...     "-----END PGP SIGNATURE-----\n")
     >>> user_browser.getControl('Post Comment', index=-1).click()
 
-No Privileges Person is authenticated in user_browser, so he can see
+No Privileges Person is authenticated in user_browser, so they can see
 email addresses in messages.
 
     >>> user_browser.title.decode('utf-8')
@@ -131,7 +131,7 @@
 
 Unauthenticated users, such as a bot will see the obfuscated email
 address, '<email address hidden>'. The anonymous user is
-unauthenticated, so he will see the obfuscated email address.
+unauthenticated, so they will see the obfuscated email address.
 
     >>> anon_browser.open('http://bugs.launchpad.dev/tomcat/+bug/2')
     >>> anon_browser.title.decode('utf-8')

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-create-question.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-create-question.txt	2015-10-05 06:34:17 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-create-question.txt	2016-01-26 15:58:00 +0000
@@ -21,8 +21,8 @@
     ...
     Unauthorized: ...
 
-No Privileges Person is doing triage for Ubuntu. He recognizes bug 10 is
-really a question. He chooses to make a question from it.
+No Privileges Person is doing triage for Ubuntu. They recognize bug 10 is
+really a question. They choose to make a question from it.
 
     >>> user_browser.open(
     ...     'http://bugs.launchpad.dev'
@@ -36,7 +36,7 @@
 
 The 'Convert this to a question' page explains what will happen if No
 Privileges Person chooses to make the bug into a question. There is a
-field for a comment. He decides to create the question using the
+field for a comment. They decide to create the question using the
 'Convert this bug to a question' button.
 
     >>> find_main_content(user_browser.contents).p
@@ -80,14 +80,14 @@
 
     >>> user_browser.goBack(1)
 
-He sees his comment was appended to the bug report's messages.
+They see their comment was appended to the bug report's messages.
 
     >>> print extract_text(
     ...     find_tags_by_class(str(content), 'boardCommentBody')[-1])
     This bug is a question.
 
 No Privileges Person looks at the page heading and sees that Foo Bar is
-the bug owner. He sees the link to the question in the 'Related
+the bug owner. They see the link to the question in the 'Related
 questions' portlet, and uses it to go to the question page.
 
     >>> print user_browser.url
@@ -102,7 +102,7 @@
     >>> user_browser.getLink('another test bug').click()
 
 No Privileges Person case see that the question was created from a bug.
-He uses the link to Related bug to return to the bug.
+They use the link to Related bug to return to the bug.
 
     >>> print user_browser.title
     Question #... : Questions : linux-source-2.6.15 package : Ubuntu
@@ -121,7 +121,7 @@
 
 Thunderbird does not use Launchpad to track bugs. Questions cannot be
 made from its bugs. When No Privileges Person uses the link in the Bug
-Actions menu, the page explains why he cannot make the bug into a
+Actions menu, the page explains why they cannot make the bug into a
 question.
 
     >>> user_browser.open('http://bugs.launchpad.dev/thunderbird/+bug/9')
@@ -152,8 +152,8 @@
     LookupError: ...
 
 If No Privileges Person were to create a question from a bug, then
-return to the create a question from a bug page using his back button or
-a bookmark, he sees that he cannot create the question again.
+return to the create a question from a bug page using their back button or
+a bookmark, they see that they cannot create the question again.
 
     >>> user_browser.open(
     ...     'http://bugs.launchpad.dev'
@@ -171,9 +171,9 @@
     ...
     LookupError: ...
 
-Most browsers cache pages. When No Privileges Person uses his his
-browser's back button, after creating a question, he is re-shown the
-form as it was. He resubmit the form, and is notified that a question
+Most browsers cache pages. When No Privileges Person uses their
+browser's back button, after creating a question, they are re-shown the
+form as it was. They resubmit the form, and are notified that a question
 could not be created.
 
     >>> # Jokosher must enable answers to access questions.
@@ -231,7 +231,7 @@
 
 No Privileges Person is shown the bug page again. There is a notice
 stating that a question was removed from the bug. The Related Questions
-portlet is gone too. He views the question and sees that it is still in
+portlet is gone too. They view the question and sees that it is still in
 the Open status.
 
     >>> user_browser.title
@@ -255,8 +255,8 @@
     ...     find_tag_by_id(user_browser.contents, 'question-status'))
     Status: Open
 
-No Privileges Person uses his browser's back button to view the bug
-again. The bug status is sill Invalid for Jokosher, but he can edit it.
+No Privileges Person uses their browser's back button to view the bug
+again. The bug status is sill Invalid for Jokosher, but they can edit it.
 
     >>> user_browser.goBack(count=1)
     >>> content = find_main_content(user_browser.contents)
@@ -266,7 +266,7 @@
     Affecting: Jokosher
     Filed here by: Foo Bar...
 
-He reads his comment that was appended to the bug report's messages.
+They read their comment that was appended to the bug report's messages.
 
     >>> print extract_text(
     ...     find_tags_by_class(str(content), 'boardComment')[-1])
@@ -278,7 +278,7 @@
 
 When the remove question page is visited, and there is no question, the
 form is not displayed. This can happened if the URL is hacked or the
-question was removed, and the user used his back button to return to the
+question was removed, and the user used their back button to return to the
 page. No Privileges Person sees a message that there is no question to
 remove.
 

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-nomination-table-row.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-nomination-table-row.txt	2012-10-04 22:33:23 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-nomination-table-row.txt	2016-01-26 15:58:00 +0000
@@ -6,7 +6,7 @@
 the distroseries or productseries, to approve or decline the
 nomination.
 
-no-priv cannot approve or decline his nomination, because he does not
+no-priv cannot approve or decline their nomination, because they do not
 have the launchpad.Driver permission.
 
     >>> from zope.component import getUtility
@@ -32,7 +32,7 @@
     ...     "http://launchpad.dev/distros/ubuntu/+source/mozilla-firefox/";
     ...     "+bug/1/nominations/2/+bugtasks-and-nominations-table-row")
 
-no-priv will, of course, see his nomination.
+no-priv will, of course, see their nomination.
 
     >>> user_browser.contents
     '...Nominated...for...Hoary...by...No Privileges Person...'

=== modified file 'lib/lp/bugs/stories/bugs/xx-front-page-search.txt'
--- lib/lp/bugs/stories/bugs/xx-front-page-search.txt	2014-11-29 06:41:25 +0000
+++ lib/lp/bugs/stories/bugs/xx-front-page-search.txt	2016-01-26 15:58:00 +0000
@@ -71,7 +71,7 @@
 
 == Searching one project ==
 
-If the user chooses to search only one project, he will be forwarded to
+If the user chooses to search only one project, they will be forwarded to
 the project's bug listing, and the search will be performed there. If no
 name is specified, an error message will be displayed.
 
@@ -105,7 +105,7 @@
     ...     print message.renderContents()
     There is no project named &#x27;invalid&#x27; registered in Launchpad
 
-If the user doesn't know what name to write, he can use the 'Choose'
+If the user doesn't know what name to write, they can use the 'Choose'
 link if the browser supports javascript. The test browser does not
 support javascript, so a 'Find' link pointing to /bugs is displayed.
 

=== modified file 'lib/lp/bugs/stories/bugs/xx-incomplete-bugs.txt'
--- lib/lp/bugs/stories/bugs/xx-incomplete-bugs.txt	2014-11-29 06:41:25 +0000
+++ lib/lp/bugs/stories/bugs/xx-incomplete-bugs.txt	2016-01-26 15:58:00 +0000
@@ -38,7 +38,7 @@
     >>> user_browser.getControl('Search', index=1).click()
 
 The bug No Privileges Person examined earlier does not have any new
-information, so he does not see it in the list.
+information, so they do not see it in the list.
 
     >>> print extract_text(
     ...     find_tag_by_id(user_browser.contents, 'bugs-table-listing'))
@@ -55,7 +55,7 @@
     >>> import transaction
     >>> transaction.commit()
 
-He tries again to find that bug using the advanced search form.
+They try again to find that bug using the advanced search form.
 
     >>> user_browser.open(
     ...     'http://bugs.launchpad.dev/jokosher/+bugs?advanced=1')
@@ -65,7 +65,7 @@
     >>> find_tag_by_id(user_browser.contents, 'bugs-table-listing').findChild('a')
     <a href="http://bugs.launchpad.dev/jokosher/+bug/11"; class="bugtitle">...</a>
 
-The bug is there, since he supplied new information in a comment. No
+The bug is there, since they supplied new information in a comment. No
 Privileges Person makes sure that it no longer is in the list of
 incomplete bugs without response.
 
@@ -150,7 +150,7 @@
     >>> expirable_bugs_link = user_browser.getLink('Incomplete bugs')
 
 The link is to the expirable bugs page. No Privileges Person can see
-the bug he set to Incomplete previously.
+the bug they set to Incomplete previously.
 
     >>> expirable_bugs_link.click()
     >>> print user_browser.title
@@ -195,7 +195,7 @@
     11  Make Jokosher use autoaudiosink  ...
 
 When No Privileges Person confirms the bug, the notice is removed.
-He sees that the number on expirable bugs is updated when he returns
+They see that the number on expirable bugs is updated when they return to
 Jokosher's bug page.
 
     >>> user_browser.getLink('Make Jokosher use autoaudiosink').click()
@@ -235,7 +235,7 @@
     None
 
 If No Privileges Person hacks the URL to see a listing of Debian's
-expirable bugs he reads that Debian does not use bug expiration.
+expirable bugs they read that Debian does not use bug expiration.
 
     >>> user_browser.open('http://bugs.launchpad.dev/debian/+expirable-bugs')
     >>> print extract_text(find_main_content(user_browser.contents).p)

=== modified file 'lib/lp/bugs/stories/bugtask-management/xx-bug-importance-change.txt'
--- lib/lp/bugs/stories/bugtask-management/xx-bug-importance-change.txt	2012-08-16 05:18:54 +0000
+++ lib/lp/bugs/stories/bugtask-management/xx-bug-importance-change.txt	2016-01-26 15:58:00 +0000
@@ -74,7 +74,7 @@
     True
 
 For someone else. We'll unset no_priv as the bug supervisor, and note that
-he can no longer see the widget.
+they can no longer see the widget.
 
     >>> login("foo.bar@xxxxxxxxxxxxx")
     >>> firefox.bug_supervisor = None

=== modified file 'lib/lp/bugs/stories/bugtask-management/xx-bug-privileged-statuses.txt'
--- lib/lp/bugs/stories/bugtask-management/xx-bug-privileged-statuses.txt	2011-07-26 01:38:22 +0000
+++ lib/lp/bugs/stories/bugtask-management/xx-bug-privileged-statuses.txt	2016-01-26 15:58:00 +0000
@@ -27,7 +27,7 @@
     >>> print_highlighted_bugtask(user_browser)
     mozilla-firefox (Ubuntu) ... Confirmed  Medium Unassigned ...
 
-But he cannot change the status to Won't Fix or to Triaged, and so
+But they cannot change the status to Won't Fix or to Triaged, and so
 those statuses are not shown in the UI:
 
     >>> user_browser.open(

=== modified file 'lib/lp/bugs/stories/bugtask-management/xx-bugtask-edit-forms.txt'
--- lib/lp/bugs/stories/bugtask-management/xx-bugtask-edit-forms.txt	2013-04-11 00:51:46 +0000
+++ lib/lp/bugs/stories/bugtask-management/xx-bugtask-edit-forms.txt	2016-01-26 15:58:00 +0000
@@ -8,7 +8,7 @@
 
 If a user visits a bug page with a browser that does not have
 Javascript, the links shown in the columns "Status" and "Importance"
-of the bug task table lead him to the bug task edit forms of the
+of the bug task table lead them to the bug task edit forms of the
 respective bug task.
 
     >>> admin_browser.open('http://bugs.launchpad.dev/firefox/+bug/1')

=== modified file 'lib/lp/bugs/stories/bugtask-management/xx-change-assignee.txt'
--- lib/lp/bugs/stories/bugtask-management/xx-change-assignee.txt	2015-06-27 04:10:49 +0000
+++ lib/lp/bugs/stories/bugtask-management/xx-change-assignee.txt	2016-01-26 15:58:00 +0000
@@ -141,7 +141,7 @@
     >>> user_browser.getControl("Save Changes", index=0).click()
     >>> print_errors(user_browser.contents)
 
-But if he tries to set other persons or teams, he gets an error message.
+But if they try to set other persons or teams, they get an error message.
 
     >>> user_browser.open("http://bugs.launchpad.dev/jokosher/+bug/11";)
     >>> assignee_control = user_browser.getControl(

=== modified file 'lib/lp/bugs/stories/bugtask-management/xx-change-milestone.txt'
--- lib/lp/bugs/stories/bugtask-management/xx-change-milestone.txt	2015-11-02 17:48:51 +0000
+++ lib/lp/bugs/stories/bugtask-management/xx-change-milestone.txt	2016-01-26 15:58:00 +0000
@@ -4,7 +4,7 @@
 Bugtasks associated with products, productseries, distributions and
 distroseries can be targeted to milestones.
 
-Sample Person is the owner of the Firefox product. He decides to plan
+Sample Person is the owner of the Firefox product. They decide to plan
 when bugs should be fixed by setting up some milestones.
 
     >>> owner_browser = setupBrowser('Basic test@xxxxxxxxxxxxx:test')
@@ -49,7 +49,7 @@
     >>> owner_browser.getControl('Register Milestone').click()
     >>> print_errors(owner_browser.contents)
 
-...and then he approves the nomination of bug #1 for the firefox series 1.0.
+...and then they approve the nomination of bug #1 for the firefox series 1.0.
 
     >>> owner_browser.open('http://launchpad.dev/firefox/+bug/1')
     >>> owner_browser.getControl('Approve', index=0).click()
@@ -72,15 +72,15 @@
     ['Mozilla Firefox 1.0.1']
 
 Foo Bar is a member of the Ubuntu team that owns the Ubuntu
-distribution. He decides to assign some bug to some milestones.
-First he registers a new milestone.
+distribution. They decide to assign some bug to some milestones.
+First they register a new milestone.
 
     >>> admin_browser.open('http://launchpad.dev/ubuntu/hoary/+addmilestone')
     >>> name_field = admin_browser.getControl('Name:')
     >>> name_field.value = '5.04.rc1'
     >>> admin_browser.getControl('Register Milestone').click()
 
-He targets bug #1 to milestone 5.04-rc1 for Ubuntu.
+They target bug #1 to milestone 5.04-rc1 for Ubuntu.
 
     >>> admin_browser.open('http://launchpad.dev/firefox/+bug/1')
     >>> table = find_tag_by_id(admin_browser.contents, 'affected-software')
@@ -110,14 +110,14 @@
     >>> milestone_control.displayValue
     ['Ubuntu 5.04.rc1']
 
-Bug #1 is already nominated for Hoary, so he can create another bugtask
+Bug #1 is already nominated for Hoary, so they can create another bugtask
 by clicking on the "approve" button for this nomination.
 
     >>> admin_browser.getControl('Approve').click()
 
-Now he can set the milestone for the bug in Hoary. Note that the
-milestone assigned for Ubuntu has been "carried over", so he
-removes the milestone now.
+Now they can set the milestone for the bug in Hoary. Note that the
+milestone assigned for Ubuntu has been "carried over", so they
+remove the milestone now.
 
     >>> milestone_control = admin_browser.getControl(
     ...     name='ubuntu_hoary_mozilla-firefox.milestone')

=== modified file 'lib/lp/bugs/stories/bugtask-management/xx-edit-email-address-bugtask.txt'
--- lib/lp/bugs/stories/bugtask-management/xx-edit-email-address-bugtask.txt	2012-08-16 05:18:54 +0000
+++ lib/lp/bugs/stories/bugtask-management/xx-edit-email-address-bugtask.txt	2016-01-26 15:58:00 +0000
@@ -132,8 +132,8 @@
     Status: True
     Importance: True
 
-An ordinary user can see the Status widget. He can't see the
-Importance widget because he would not normally be permitted to alter
+An ordinary user can see the Status widget. They can't see the
+Importance widget because they would not normally be permitted to alter
 the importance of a bugtask in Alsa Utils.
 
     >>> login("foo.bar@xxxxxxxxxxxxx")

=== modified file 'lib/lp/bugs/stories/bugtask-searches/xx-listing-basics.txt'
--- lib/lp/bugs/stories/bugtask-searches/xx-listing-basics.txt	2012-10-03 02:32:16 +0000
+++ lib/lp/bugs/stories/bugtask-searches/xx-listing-basics.txt	2016-01-26 15:58:00 +0000
@@ -44,7 +44,7 @@
     >>> print_bugtasks(anon_browser.contents)
     2 Blackhole Trash folder Tomcat Low New
 
-Foo Bar views the upstream Ubuntu bug tasks listing. Note that he can
+Foo Bar views the upstream Ubuntu bug tasks listing. Note that they can
 see the extra "quick search" links "my todo list" and "submitted by
 me".
 

=== modified file 'lib/lp/bugs/stories/cve/cve-linking.txt'
--- lib/lp/bugs/stories/cve/cve-linking.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/bugs/stories/cve/cve-linking.txt	2016-01-26 15:58:00 +0000
@@ -54,12 +54,12 @@
     <BLANKLINE>
     Not a valid bug number or nickname.
 
-The user can correct his error and submit the form again.
+The user can correct their error and submit the form again.
 
     >>> user_browser.getControl('Bug ID').value = 'blackhole'
     >>> user_browser.getControl('Link').click()
 
-A notification is displayed telling the user which bug he just linked:
+A notification is displayed telling the user which bug they just linked:
 
     >>> soup = find_main_content(user_browser.contents)
     >>> soup.first('div', 'informational message')
@@ -87,8 +87,8 @@
 Removing links
 --------------
 
-To remove bug links, the user would use the 'Remove Bug Link' action. He
-can select the bug reports, he wants to unlink from the CVE.
+To remove bug links, the user would use the 'Remove Bug Link' action. They
+can select the bug reports that they want to unlink from the CVE.
 
     >>> user_browser.getLink('Remove bug link').click()
     >>> user_browser.getControl('#2: Blackhole').selected = True

=== modified file 'lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.txt'
--- lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.txt	2012-12-10 13:43:47 +0000
+++ lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.txt	2016-01-26 15:58:00 +0000
@@ -172,7 +172,7 @@
     >>> user_browser.getControl('Summary', index=0).value
     'Initial bug summary'
 
-The user can of course change the summary if he wants to.
+The user can of course change the summary if they want to.
 
     >>> user_browser.getControl('Summary', index=0).value = 'Another summary'
     >>> user_browser.getControl('Continue').click()
@@ -203,7 +203,7 @@
     >>> user_browser.getControl('Tags').value
     'bar foo'
 
-The user can of course change the tags if he wants.
+The user can of course change the tags if they want.
 
     >>> user_browser.getControl('Tags').value = 'bar baz'
     >>> user_browser.getControl('Summary', index=0).value = 'Bug Summary'

=== modified file 'lib/lp/bugs/stories/structural-subscriptions/xx-bug-subscriptions.txt'
--- lib/lp/bugs/stories/structural-subscriptions/xx-bug-subscriptions.txt	2015-07-21 09:04:01 +0000
+++ lib/lp/bugs/stories/structural-subscriptions/xx-bug-subscriptions.txt	2016-01-26 15:58:00 +0000
@@ -53,7 +53,7 @@
       To all Ubuntu bugs:
         Landscape Developers
 
-Sample Person can also unsubscribe himself and the Landscape team.
+Sample Person can also unsubscribe themselves and the Landscape team.
 
     >>> subscribe_myself = browser.getControl(
     ...    'I want to receive these notifications by email')
@@ -92,7 +92,7 @@
     >>> flush_database_updates()
     >>> logout()
 
-The driver sees an extended form in the +subscribe view, which allows him
+The driver sees an extended form in the +subscribe view, which allows them
 to subscribe other people.
 
     >>> browser.open(
@@ -179,7 +179,7 @@
       To all Ubuntu bugs:
         Landscape Developers
 
-When Sample Person now visits the bug subscription page, he no longer sees
+When Sample Person now visits the bug subscription page, they no longer see
 the UI elements for the subscription/unsubscription of arbitrary persons.
 
     >>> browser.open(

=== modified file 'lib/lp/bugs/stories/upstream-bugprivacy/xx-upstream-bug-privacy.txt'
--- lib/lp/bugs/stories/upstream-bugprivacy/xx-upstream-bug-privacy.txt	2012-08-23 04:20:48 +0000
+++ lib/lp/bugs/stories/upstream-bugprivacy/xx-upstream-bug-privacy.txt	2016-01-26 15:58:00 +0000
@@ -80,16 +80,15 @@
     ...
     NotFound:...
 
-Foo Bar accesses the bug page of a private bug. He is allowed to
-view the page because he is an explicit subscriber on the bug.
+Foo Bar accesses the bug page of a private bug. They are allowed to
+view the page because they are an explicit subscriber on the bug.
 
     >>> browser = setupBrowser(auth="Basic foo.bar@xxxxxxxxxxxxx:test")
     >>> browser.open("http://launchpad.dev/firefox/+bug/%s"; % bug_id)
     >>> print browser.headers["Status"]
     200 Ok
 
-He now accesses the task page of a task on a private bug; also
-permitted.
+They now access the task page of a task on a private bug; also permitted.
 
     >>> browser = setupBrowser(auth="Basic foo.bar@xxxxxxxxxxxxx:test")
     >>> browser.open("http://launchpad.dev/firefox/+bug/%s/+editstatus"; % bug_id)

=== modified file 'lib/lp/bugs/stories/webservice/xx-bug.txt'
--- lib/lp/bugs/stories/webservice/xx-bug.txt	2015-10-06 06:48:01 +0000
+++ lib/lp/bugs/stories/webservice/xx-bug.txt	2016-01-26 15:58:00 +0000
@@ -853,9 +853,8 @@
     self_link: u'http://.../bugs/1/+subscription/cprov'
     ...
 
-An individual can only unsubscribe him or her self.  If
-the person argument is not provided, the web service
-uses the calling user.
+An individual can only unsubscribe themselves.  If the person argument is
+not provided, the web service uses the calling user.
 
     >>> print webservice.named_post(
     ...     bug_one['self_link'], 'unsubscribe')
@@ -897,8 +896,7 @@
     ...     person=webservice.getAbsoluteUrl('/~mark'))
     HTTP/1.1 401 Unauthorized...
 
-An individual can, however, unsubscribe a team to which
-he or she belongs.
+An individual can, however, unsubscribe a team to which they belong.
 
 For this example, we need a member of the ubuntu-team group,
 any member will do.
@@ -1738,7 +1736,7 @@
     >>> int(related['entries'][0]['bug_link'].split('/')[-1]) == testbug2.id
     True
 
-`testuser3` is not active, so the collection of related tasks to him is
+`testuser3` is not active, so the collection of related tasks to them is
 empty:
 
     >>> related = webservice.named_get(

=== modified file 'lib/lp/bugs/stories/xx-bugs-statistics-portlet.txt'
--- lib/lp/bugs/stories/xx-bugs-statistics-portlet.txt	2012-05-18 05:31:54 +0000
+++ lib/lp/bugs/stories/xx-bugs-statistics-portlet.txt	2016-01-26 15:58:00 +0000
@@ -394,7 +394,7 @@
     0 Bugs with patches
     1 Open CVE bug - CVE report
 
-Once the user has identified him or herself, information on assigned
+Once the user has identified themselves, information on assigned
 bugs is also shown.
 
     >>> print_bugfilters_portlet_unfilled(user_browser, path)

=== modified file 'lib/lp/bugs/tests/buglinktarget.txt'
--- lib/lp/bugs/tests/buglinktarget.txt	2015-09-29 06:06:00 +0000
+++ lib/lp/bugs/tests/buglinktarget.txt	2016-01-26 15:58:00 +0000
@@ -80,8 +80,8 @@
       ...
     Unauthorized...
 
-A user can only link to a private bug if he is subscribed to the bug or
-if he is an administrator:
+A user can only link to a private bug if they are subscribed to the bug or
+if they are an administrator:
 
     >>> login('no-priv@xxxxxxxxxxxxx')
     >>> private_bug = bugset.get(6)
@@ -152,8 +152,8 @@
     >>> unlinked_events
     []
 
-A user can only remove a link to a private bug if he is subscribed to
-the bug or if he is an administrator.
+A user can only remove a link to a private bug if they are subscribed to
+the bug or if they are an administrator.
 
     >>> target.unlinkBug(private_bug)
     Traceback (most recent call last):

=== modified file 'lib/lp/bugs/tests/bugs-emailinterface.txt'
--- lib/lp/bugs/tests/bugs-emailinterface.txt	2015-10-01 02:59:28 +0000
+++ lib/lp/bugs/tests/bugs-emailinterface.txt	2016-01-26 15:58:00 +0000
@@ -434,7 +434,7 @@
     >>> print bug_one.title
     Better summary
 
-And an error message was sent to the Sample Person, telling him what's
+And an error message was sent to the Sample Person, telling them what's
 wrong.
 
     >>> print_latest_email()
@@ -794,7 +794,7 @@
 
 Unsubscribing from a bug also unsubscribes you from its duplicates. To
 demonstrate, let's first make no_privs an indirect subscriber from bug
-#5, by subscribing him directly to a dupe of bug #5, bug #6.
+#5, by subscribing them directly to a dupe of bug #5, bug #6.
 
     >>> from lp.registry.interfaces.person import IPersonSet
 
@@ -817,7 +817,7 @@
     ...  for subscriber in bug_five.getIndirectSubscribers()])
     [u'No Privileges Person', u'Sample Person']
 
-Now, if we unsubscribe no-priv from bug #5, he will actually get
+Now, if we unsubscribe no-priv from bug #5, they will actually get
 unsubscribed from bug #6, thus no longer being indirectly subscribed to
 bug #5.
 
@@ -1242,7 +1242,7 @@
     Mozilla Firefox
     Ubuntu
 
-Because Sample Person isn't a driver of Ubuntu, he's not allowed to
+Because Sample Person isn't a driver of Ubuntu, they're not allowed to
 target a bug directly, instead a nomination was created.
 
     >>> for nomination in bug.getNominations():
@@ -1277,7 +1277,7 @@
     Ubuntu Hoary
     Ubuntu Warty
 
-If Sample Person would be the Ubuntu driver, he'll be able to target
+If Sample Person would be the Ubuntu driver, they'll be able to target
 bugs directly to series.
 
     >>> from lp.testing.dbuser import lp_dbuser
@@ -1302,7 +1302,7 @@
     Ubuntu
     Ubuntu Grumpy
 
-He can also approve existing nominations.
+They can also approve existing nominations.
 
     >>> submit_commands(bug, 'affects ubuntu/warty')
 
@@ -1772,7 +1772,7 @@
 ~~~~~~~~~~~~~~~~~~~~
 
 If none of the bug tasks can be chosen, an error message is sent to the
-user, telling him that he has to use the 'affects' command.
+user, telling them that they have to use the 'affects' command.
 
     >>> del stub.test_emails[:]
     >>> login('stuart.bishop@xxxxxxxxxxxxx')
@@ -1802,7 +1802,7 @@
 -------------------------
 
 If an error is encountered, an email is sent to the sender informing
-him about the error. Let's start with trying to submit a bug without
+them about the error. Let's start with trying to submit a bug without
 signing the mail:
 
     >>> del stub.test_emails[:]

=== modified file 'lib/lp/bugs/tests/bugtarget-questiontarget.txt'
--- lib/lp/bugs/tests/bugtarget-questiontarget.txt	2015-09-25 02:33:15 +0000
+++ lib/lp/bugs/tests/bugtarget-questiontarget.txt	2016-01-26 15:58:00 +0000
@@ -58,7 +58,7 @@
 
 Sample Person recognises that this bug is a question while reviewing the
 bugtarget's bugs, and choose to make it into a question. The UI would
-pass Sample Person as the Person changing the status. He may provide a
+pass Sample Person as the Person changing the status. They may provide a
 message about why the report is a question.
 
     >>> from lp.registry.interfaces.person import IPersonSet

=== modified file 'lib/lp/bugs/tests/test_bugchanges.py'
--- lib/lp/bugs/tests/test_bugchanges.py	2015-09-28 17:38:45 +0000
+++ lib/lp/bugs/tests/test_bugchanges.py	2016-01-26 15:58:00 +0000
@@ -212,7 +212,7 @@
         subscriber = self.factory.makePerson(displayname='Mom')
         self.bug.subscribe(self.user, subscriber)
         self.saveOldChanges()
-        # Only the user can unsubscribe him or her self.
+        # Only the user can unsubscribe themselves.
         self.bug.unsubscribe(self.user, self.user)
 
         # This checks the activity's attribute and target attributes.

=== modified file 'lib/lp/bugs/tests/test_bugtaskset.py'
--- lib/lp/bugs/tests/test_bugtaskset.py	2012-06-14 05:18:22 +0000
+++ lib/lp/bugs/tests/test_bugtaskset.py	2016-01-26 15:58:00 +0000
@@ -48,7 +48,7 @@
             'product_id=20 count=2')
 
         # A Launchpad admin will get a higher count for the product with id=20
-        # because he can see the private bug.
+        # because they can see the private bug.
         bugtask_counts = getUtility(IBugTaskSet).getOpenBugTasksPerProduct(
             foobar, products)
         res = sorted(bugtask_counts.items())

=== modified file 'lib/lp/buildmaster/stories/xx-builder-page.txt'
--- lib/lp/buildmaster/stories/xx-builder-page.txt	2015-09-11 13:56:38 +0000
+++ lib/lp/buildmaster/stories/xx-builder-page.txt	2016-01-26 15:58:00 +0000
@@ -1,7 +1,7 @@
 Builder page
 ============
 
-An anonymous user visits the +builds page. He can see a summary of the
+An anonymous user visits the +builds page. They can see a summary of the
 builder state. In the sampledata, the builder 'bob' is building
 'mozilla-firefox'.
 

=== modified file 'lib/lp/code/doc/branch-notifications.txt'
--- lib/lp/code/doc/branch-notifications.txt	2015-09-11 12:20:23 +0000
+++ lib/lp/code/doc/branch-notifications.txt	2016-01-26 15:58:00 +0000
@@ -250,7 +250,7 @@
     The size of the diff (6000 lines) is larger than your specified limit of
     5000 lines...
 
-Foo Bar is getting the email due to his membership in the Launchpad
+Foo Bar is getting the email due to their membership in the Launchpad
 developers team.  Since the email is due to a team, there is no
 unsubscribe link.
 

=== modified file 'lib/lp/code/model/tests/test_branchvisibility.py'
--- lib/lp/code/model/tests/test_branchvisibility.py	2012-10-08 07:15:07 +0000
+++ lib/lp/code/model/tests/test_branchvisibility.py	2016-01-26 15:58:00 +0000
@@ -162,7 +162,7 @@
             (test_branches[0], False),
         ], branch_info)
 
-        # An arbitrary person is not in eligible to see any of the private
+        # An arbitrary person is not eligible to see any of the private
         # branches.
         person = self.factory.makePerson()
         branch_info = [(branch, branch.private)
@@ -174,8 +174,8 @@
             (test_branches[0], False),
         ], branch_info)
 
-        # Private owner sees his new private branch and other public
-        # branches, but not other's private branches.
+        # Private owner sees their new private branch and other public
+        # branches, but not other private branches.
         branch_info = [(branch, branch.private)
                 for branch in branch_set.getRecentlyRegisteredBranches(
                     3, visible_by_user=private_owner)]

=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/model/tests/test_sourcepackagerecipe.py	2016-01-20 12:21:21 +0000
+++ lib/lp/code/model/tests/test_sourcepackagerecipe.py	2016-01-26 15:58:00 +0000
@@ -517,7 +517,7 @@
 
         The case where the user is not in the PPA owner team but is allowed to
         upload to the PPA via an explicit ArchivePermission takes a different
-        security path than if he were part of the team.
+        security path than if they were part of the team.
         """
 
         # Create a team private PPA.

=== modified file 'lib/lp/code/stories/branches/xx-branch-listings.txt'
--- lib/lp/code/stories/branches/xx-branch-listings.txt	2015-06-02 10:52:14 +0000
+++ lib/lp/code/stories/branches/xx-branch-listings.txt	2016-01-26 15:58:00 +0000
@@ -17,7 +17,7 @@
 Batching is applied to branch listings where there are many items.
 Luckily for us, many is 5 in the tests.
 
-Sample Person is used as the logged in user in order to show his private
+Sample Person is used as the logged in user in order to show their private
 branches in the listings.
 
     >>> browser = setupBrowser(auth='Basic test@xxxxxxxxxxxxx:test')

=== modified file 'lib/lp/code/stories/branches/xx-product-branches.txt'
--- lib/lp/code/stories/branches/xx-product-branches.txt	2015-06-26 12:57:00 +0000
+++ lib/lp/code/stories/branches/xx-product-branches.txt	2016-01-26 15:58:00 +0000
@@ -216,7 +216,7 @@
     Import a branch
     Configure Code
 
-The owner of the project sees the links for the activities he can
+The owner of the project sees the links for the activities they can
 perform, everything except defining branch visibility.
 
     >>> owner_browser = setupBrowser(auth='Basic test@xxxxxxxxxxxxx:test')

=== modified file 'lib/lp/code/stories/branches/xx-subscribing-branches.txt'
--- lib/lp/code/stories/branches/xx-subscribing-branches.txt	2013-09-27 04:13:23 +0000
+++ lib/lp/code/stories/branches/xx-subscribing-branches.txt	2016-01-26 15:58:00 +0000
@@ -257,10 +257,10 @@
     Mark Shuttleworth
 
 
-Private team's in public subscriptions
-======================================
+Private teams in public subscriptions
+=====================================
 
-If a private team is subscribed to a publicthe branch, it is visible
+If a private team is subscribed to a public branch, it is visible
 to everyone.
 
     >>> from lp.testing import login, logout
@@ -283,7 +283,7 @@
     >>> url = canonical_url(branch)
     >>> logout()
 
-No-priv is not a member of the private team, but he can see the team's
+No-priv is not a member of the private team, but they can see the team's
 display name in the subscriber list.
 
     >>> browser.open(url)

=== modified file 'lib/lp/coop/answersbugs/stories/question-buglink.txt'
--- lib/lp/coop/answersbugs/stories/question-buglink.txt	2013-09-27 04:13:23 +0000
+++ lib/lp/coop/answersbugs/stories/question-buglink.txt	2016-01-26 15:58:00 +0000
@@ -36,7 +36,7 @@
     >>> soup.first('div', 'message')
     <div class="message">Not a valid bug number or nickname.</div>
 
-The user is offered a link to search for bug in case he doesn't know the
+The user is offered a link to search for bug in case they don't know the
 bug number.
 
     >>> user_browser.getLink('Bug listing').click()
@@ -107,7 +107,7 @@
     >>> flush_database_updates()
     >>> logout()
 
-A regular user shouldn't be able to link to a private bug he doesn't
+A regular user shouldn't be able to link to a private bug they don't
 have access to:
 
     # We use the no-priv user here because sample person is subscribed

=== modified file 'lib/lp/hardwaredb/doc/hwdb-device-tables.txt'
--- lib/lp/hardwaredb/doc/hwdb-device-tables.txt	2012-08-07 02:31:56 +0000
+++ lib/lp/hardwaredb/doc/hwdb-device-tables.txt	2016-01-26 15:58:00 +0000
@@ -2278,7 +2278,7 @@
 
 By default, only bug reporters are looked up. Sample Person has made
 a hardware report containing the IDE disk Seagate ST3250820NS, and
-he has filed bug 1.
+they have filed bug 1.
 
     >>> from lp.bugs.interfaces.bug import IBugSet
     >>> bug_set = getUtility(IBugSet)
@@ -2291,8 +2291,8 @@
     ...     print person.displayname
     Sample Person
 
-If Foo Bar says that he is affected by this bug, he will be listed
-too, since he too owns the Seagate disk.
+If Foo Bar says that they are affected by this bug, they will be listed
+too, since they too own the Seagate disk.
 
     >>> foo_bar = getUtility(IPersonSet).getByEmail('foo.bar@xxxxxxxxxxxxx')
     >>> bug_one = bug_set.get(1)
@@ -2304,7 +2304,7 @@
     Foo Bar
     Sample Person
 
-When he says that he is not affected by the bug, he will no longer
+When they say that they are not affected by the bug, they will no longer
 be listed.
 
     >>> bug_one.markUserAffected(foo_bar, False)
@@ -2467,8 +2467,8 @@
     ...     print person.displayname
     Sample Person
 
-If Foo Bar says that he is affected by this bug, he will be listed
-too, since his machine too uses the sd driver.
+If Foo Bar says that they are affected by this bug, they will be listed
+too, since their machine too uses the sd driver.
 
     >>> bug_one.markUserAffected(foo_bar)
     >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
@@ -2477,7 +2477,7 @@
     Foo Bar
     Sample Person
 
-He is also listed, if he subscribes to bug 1.
+They are also listed if they subscribe to bug 1.
 
     >>> bug_one.markUserAffected(foo_bar, False)
     >>> bug_one.subscribe(foo_bar, subscribed_by=foo_bar)
@@ -2692,7 +2692,7 @@
     >>> bug_one.unsubscribe(foo_bar, unsubscribed_by=foo_bar)
 
 Data from private submissions is not included by default.
-The owner of a private submission can see his or her submission.
+The owner of a private submission can see their submission.
 
     >>> bug_one.subscribe(no_priv, subscribed_by=no_priv)
     <lp.bugs.model.bugsubscription.BugSubscription ...>
@@ -2731,8 +2731,8 @@
     (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'SEAGATE', u'ST3250820NS     ')
     (u'no-priv', <DBItem HWBus.IDE, (7) IDE>, u'SEAGATE', u'ST3250820NS     ')
 
-But sample_person cannot see the private entry because she
-is not the owner or an admin.
+But sample_person cannot see the private entry because they
+are not the owner or an admin.
 
     >>> for entry in submission_set.hwInfoByBugRelatedUsers(
     ...     bug_ids=[1], subscribed_to_bug=True, user=sample_person):

=== modified file 'lib/lp/hardwaredb/doc/hwdb.txt'
--- lib/lp/hardwaredb/doc/hwdb.txt	2015-04-20 09:48:57 +0000
+++ lib/lp/hardwaredb/doc/hwdb.txt	2016-01-26 15:58:00 +0000
@@ -385,7 +385,7 @@
     Beeblebrox
 
 If somebody has a Launchpad account, but submits HWDB test data using
-an email address which he has not added to his account, the submission
+an email address which they have not added to their account, the submission
 will have the `owner` field set to None.
 
     >>> submission = hw_submission_set.createSubmission(
@@ -404,7 +404,7 @@
     >>> print submission.owner
     None
 
-When he adds this email address to his list of addresses in Launchpad,
+When they add this email address to their list of addresses in Launchpad,
 the field `owner` is updated.
 
     >>> email = getUtility(IEmailAddressSet).new(

=== modified file 'lib/lp/hardwaredb/scripts/tests/hardwaretest-natty.xml'
--- lib/lp/hardwaredb/scripts/tests/hardwaretest-natty.xml	2011-12-19 13:55:28 +0000
+++ lib/lp/hardwaredb/scripts/tests/hardwaretest-natty.xml	2016-01-26 15:58:00 +0000
@@ -45,7 +45,7 @@
     <private value="False"/>
 
     <!-- contactable: If True, the owner agrees to be contacted by other
-             persons about devices which appear in his submission.
+             persons about devices which appear in their submission.
              Example of a use case: Developers can ask device owners
              to perform tests.
     -->

=== modified file 'lib/lp/hardwaredb/scripts/tests/hardwaretest-udev.xml'
--- lib/lp/hardwaredb/scripts/tests/hardwaretest-udev.xml	2011-12-19 13:55:28 +0000
+++ lib/lp/hardwaredb/scripts/tests/hardwaretest-udev.xml	2016-01-26 15:58:00 +0000
@@ -36,7 +36,7 @@
     <private value="False"/>
 
     <!-- contactable: If True, the owner agrees to be contacted by other
-             persons about devices which appear in his submission.
+             persons about devices which appear in their submission.
              Example of a use case: Developers can ask device owners
              to perform tests.
     -->

=== modified file 'lib/lp/hardwaredb/scripts/tests/hardwaretest.xml'
--- lib/lp/hardwaredb/scripts/tests/hardwaretest.xml	2011-12-19 13:55:28 +0000
+++ lib/lp/hardwaredb/scripts/tests/hardwaretest.xml	2016-01-26 15:58:00 +0000
@@ -36,7 +36,7 @@
     <private value="False"/>
 
     <!-- contactable: If True, the owner agrees to be contacted by other
-             persons about devices which appear in his submission. 
+             persons about devices which appear in their submission. 
              Example of a use case: Developers can ask device owners
              to perform tests.
     -->

=== modified file 'lib/lp/hardwaredb/stories/hwdb/xx-hwdb.txt'
--- lib/lp/hardwaredb/stories/hwdb/xx-hwdb.txt	2012-03-07 07:15:00 +0000
+++ lib/lp/hardwaredb/stories/hwdb/xx-hwdb.txt	2016-01-26 15:58:00 +0000
@@ -260,7 +260,7 @@
     >>> anon_browser.goBack(1)
 
 Submissions that are marked as private are only displayed to the
-submitter himself.
+submitter themselves.
 
     >>> browser.open('http://launchpad.dev/+hwdb/+submit')
     >>> from StringIO import StringIO
@@ -305,8 +305,8 @@
     >>> import transaction
     >>> transaction.commit()
 
-The owner of a submission sees a complete lists of his own submissions.
-He also gets an additional column in the listing, showing the "private"
+The owner of a submission sees a complete list of their own submissions.
+They also get an additional column in the listing, showing the "private"
 status of each submission.
 
     >>> browser = setupBrowser(auth='Basic test@xxxxxxxxxxxxx:test')

=== modified file 'lib/lp/registry/browser/peoplemerge.py'
--- lib/lp/registry/browser/peoplemerge.py	2015-07-21 09:04:01 +0000
+++ lib/lp/registry/browser/peoplemerge.py	2016-01-26 15:58:00 +0000
@@ -182,8 +182,8 @@
         self.setUpPeople(data)
         if not self.dupe_person_emails.is_empty():
             # We're merging a person which has one or more email addresses,
-            # so we better warn the admin doing the operation and have him
-            # check the emails that will be reassigned to ensure he's not
+            # so we better warn the admin doing the operation and have them
+            # check the emails that will be reassigned to ensure they're not
             # doing anything stupid.
             self.should_confirm_email_reassignment = True
             return
@@ -246,7 +246,7 @@
         self.setUpPeople(data)
         if not self.dupe_person.activemembers.is_empty():
             # Merging teams with active members is not possible, so we'll
-            # ask the admin if he wants to deactivate all members and then
+            # ask the admin if they want to deactivate all members and then
             # merge.
             self.should_confirm_member_deactivation = True
             return
@@ -341,7 +341,7 @@
         result_count = results.count()
         if not result_count:
             # The user came back to visit this page with nothing to
-            # merge, so we redirect him away to somewhere useful.
+            # merge, so we redirect them away to somewhere useful.
             self.request.response.redirect(canonical_url(user))
             return
         assert result_count == 1
@@ -410,7 +410,7 @@
                 for emailaddress in emails:
                     email = emailaddrset.getByEmail(emailaddress)
                     if email is None or email not in self.dupeemails:
-                        # The dupe person has changes his email addresses.
+                        # The dupe person has changed their email addresses.
                         # See bug 239838.
                         self.request.response.addNotification(
                             "An address was removed from the duplicate "
@@ -444,7 +444,7 @@
     address and then redirect the user to other page saying that everything
     went fine. Otherwise we redirect the user to another page where we list
     all email addresses owned by the dupe account and the user selects which
-    of those (s)he wants to claim.
+    of those they want to claim.
     """
 
     label = 'Merge Launchpad accounts'
@@ -466,8 +466,8 @@
         emails_count = emails.count()
         if emails_count > 1:
             # The dupe account have more than one email address. Must redirect
-            # the user to another page to ask which of those emails (s)he
-            # wants to claim.
+            # the user to another page to ask which of those emails they
+            # want to claim.
             self.next_url = '+requestmerge-multiple?dupe=%d' % dupeaccount.id
             return
 

=== modified file 'lib/lp/registry/browser/person.py'
--- lib/lp/registry/browser/person.py	2015-10-05 13:36:06 +0000
+++ lib/lp/registry/browser/person.py	2016-01-26 15:58:00 +0000
@@ -1140,7 +1140,7 @@
 
 
 class RedirectToEditLanguagesView(LaunchpadView):
-    """Redirect the logged in user to his +editlanguages page.
+    """Redirect the logged in user to their +editlanguages page.
 
     This view should always be registered with a launchpad.AnyPerson
     permission, to make sure the user is logged in. It exists so that
@@ -1914,7 +1914,7 @@
     def userIsParticipant(self):
         """Return true if the user is a participant of this team.
 
-        A person is said to be a team participant when he's a member
+        A person is said to be a team participant when they're a member
         of that team, either directly or indirectly via another team
         membership.
         """
@@ -2723,7 +2723,7 @@
     label = 'Change your personal details'
     page_title = label
 
-    # Will contain an hidden input when the user is renaming his
+    # Will contain an hidden input when the user is renaming their
     # account with full knowledge of the consequences.
     i_know_this_is_an_openid_security_issue_input = None
 
@@ -3166,7 +3166,7 @@
     def _mailing_list_subscription_type(self, mailing_list):
         """Return the context user's subscription type for the given list.
 
-        This is 'Preferred address' if the user is subscribed using her
+        This is 'Preferred address' if the user is subscribed using their
         preferred address and 'Don't subscribe' if the user is not
         subscribed at all. Otherwise it's the EmailAddress under
         which the user is subscribed to this mailing list.
@@ -3279,7 +3279,7 @@
                 else:
                     if new_value == "Preferred address":
                         # If the user is subscribed but not under any
-                        # particular address, her current preferred
+                        # particular address, their current preferred
                         # address will always be used.
                         new_value = None
                     subscription = mailing_list.getSubscription(self.context)
@@ -4064,8 +4064,8 @@
         """Initialize the state based on the context and the user.
 
         The recipients are determined by the relationship between the user
-        and the context that he is contacting: another user, himself, his
-        team, another team.
+        and the context that they are contacting: another user, themselves,
+        their team, another team.
 
         :param user: The person doing the contacting.
         :type user: an `IPerson`.

=== modified file 'lib/lp/registry/browser/poll.py'
--- lib/lp/registry/browser/poll.py	2015-07-08 16:05:11 +0000
+++ lib/lp/registry/browser/poll.py	2016-01-26 15:58:00 +0000
@@ -255,7 +255,7 @@
     """A view class to where the user can vote on a poll.
 
     If the user already voted, the current vote is displayed and the user can
-    change it. Otherwise he can register his vote.
+    change it. Otherwise they can register their vote.
     """
 
     default_template = ViewPageTemplateFile(
@@ -359,7 +359,7 @@
                 # XXX: Guilherme Salgado 2005-09-14:
                 # User tried to specify a value which we can't convert to
                 # an integer. Better thing to do would be to notify the user
-                # and ask him to fix it.
+                # and ask them to fix it.
                 preference = None
             newvotes[option] = preference
 

=== modified file 'lib/lp/registry/browser/product.py'
--- lib/lp/registry/browser/product.py	2015-12-11 04:40:07 +0000
+++ lib/lp/registry/browser/product.py	2016-01-26 15:58:00 +0000
@@ -2303,9 +2303,9 @@
     def _createDisclaimMaintainerField(self):
         """Return a Bool field for disclaiming maintainer.
 
-        If the registrant does not want to maintain the project she can select
-        this checkbox and the ownership will be transfered to the registry
-        admins team.
+        If the registrant does not want to maintain the project they can
+        select this checkbox and the ownership will be transferred to the
+        registry admins team.
         """
         return form.Fields(
             Bool(__name__='disclaim_maintainer',

=== modified file 'lib/lp/registry/browser/team.py'
--- lib/lp/registry/browser/team.py	2015-10-01 10:25:19 +0000
+++ lib/lp/registry/browser/team.py	2016-01-26 15:58:00 +0000
@@ -1276,7 +1276,7 @@
     page_title = label
 
     def __init__(self, context, request):
-        # Only the member himself or admins of the member (in case it's a
+        # Only the member themselves or admins of the member (in case it's a
         # team) can see the page in which they renew memberships that are
         # about to expire.
         if not check_permission('launchpad.Edit', context.person):
@@ -1585,7 +1585,7 @@
         if not userIsActiveTeamMember(self.person):
             enabled = False
         if self.person.teamowner == self.user:
-            # The owner cannot leave his team.
+            # The owner cannot leave their team.
             enabled = False
         target = '+leave'
         text = 'Leave the Team'
@@ -1807,8 +1807,8 @@
     def user_can_request_to_join(self):
         """Can the logged in user request to join this team?
 
-        The user can request if he's allowed to join this team and if he's
-        not yet an active member of this team.
+        The user can request if they're allowed to join this team and if
+        they're not yet an active member of this team.
         """
         if not self.join_allowed:
             return False
@@ -2092,11 +2092,11 @@
     def _afterOwnerChange(self, team, oldOwner, newOwner):
         """Add the new and the old owners as administrators of the team.
 
-        When a user creates a new team, he is added as an administrator of
+        When a user creates a new team, they are added as an administrator of
         that team. To be consistent with this, we must make the new owner an
         administrator of the team. This rule is ignored only if the new owner
-        is an inactive member of the team, as that means he's not interested
-        in being a member. The same applies to the old owner.
+        is an inactive member of the team, as that means they're not
+        interested in being a member. The same applies to the old owner.
         """
         # Both new and old owners won't be added as administrators of the team
         # only if they're inactive members. If they're either active or

=== modified file 'lib/lp/registry/browser/teamjoin.py'
--- lib/lp/registry/browser/teamjoin.py	2011-12-24 17:49:30 +0000
+++ lib/lp/registry/browser/teamjoin.py	2016-01-26 15:58:00 +0000
@@ -77,6 +77,6 @@
     def user_can_request_to_leave(self):
         """Return true if the user can request to leave this team.
 
-        A given user can leave a team only if he's an active member.
+        A given user can leave a team only if they're an active member.
         """
         return self.user_is_active_member

=== modified file 'lib/lp/registry/browser/tests/mailinglist-views.txt'
--- lib/lp/registry/browser/tests/mailinglist-views.txt	2011-12-24 17:49:30 +0000
+++ lib/lp/registry/browser/tests/mailinglist-views.txt	2016-01-26 15:58:00 +0000
@@ -42,7 +42,7 @@
     >>> check_permission('launchpad.Moderate', view)
     False
 
-Sample Person trusts No Privileges Person so she makes no-priv a team
+Sample Person trusts No Privileges Person so they make no-priv a team
 administrator.
 
     >>> from lp.registry.interfaces.teammembership import (
@@ -131,7 +131,7 @@
     >>> mailman.act()
     >>> transaction.commit()
 
-The team owner can purge his list, as well as a Launchpad administrator and
+The team owner can purge their list, as well as a Launchpad administrator and
 a mailing list expert.
 
     >>> login(ANONYMOUS)

=== modified file 'lib/lp/registry/browser/tests/milestone-views.txt'
--- lib/lp/registry/browser/tests/milestone-views.txt	2015-01-29 16:28:30 +0000
+++ lib/lp/registry/browser/tests/milestone-views.txt	2016-01-26 15:58:00 +0000
@@ -721,8 +721,8 @@
     >>> [subscription for subscription in owner.structural_subscriptions]
     []
 
-No Privileges Person cannot access this view because he is neither the
-project owner or series driver..
+No Privileges Person cannot access this view because they are neither the
+project owner or series driver.
 
     >>> milestone = firefox_1_0.newMilestone('1.0.12')
     >>> ignored = login_person(no_priv)

=== modified file 'lib/lp/registry/browser/tests/person-views.txt'
--- lib/lp/registry/browser/tests/person-views.txt	2012-11-27 16:04:54 +0000
+++ lib/lp/registry/browser/tests/person-views.txt	2016-01-26 15:58:00 +0000
@@ -61,7 +61,7 @@
     >>> view.visible_email_addresses
     [u'mark@xxxxxxxxxxx']
 
-As for Sample Person, he has chosen not to disclose his email addresses.
+As for Sample Person, they have chosen not to disclose their email addresses.
 
     >>> login(ANONYMOUS)
     >>> sample_person = person_set.getByEmail('test@xxxxxxxxxxxxx')
@@ -78,7 +78,7 @@
     []
 
 No Privileges Person cannot see them either because the state is HIDDEN.
-There is no description for the email addresses because he cannot view
+There is no description for the email addresses because they cannot view
 them.
 
     >>> login('no-priv@xxxxxxxxxxxxx')
@@ -173,7 +173,7 @@
 person speaks. The contact details portlet displays the user languages.
 
 English is the default language in Launchpad. If the user has not set
-his preferred languages, English is used.
+their preferred languages, English is used.
 
     >>> sample_person.languages
     []
@@ -183,8 +183,8 @@
     >>> print view.languages
     English
 
-This assumption is visible to the user when he views his own profile
-page, and he can set his preferred languages if he wants to make a
+This assumption is visible to the user when they view their own profile
+page, and they can set their preferred languages if they want to make a
 correction. The list of languages is alphabetized.
 
     >>> from lp.services.worlddata.interfaces.language import ILanguageSet
@@ -436,8 +436,8 @@
     >>> view.should_show_ppa_section
     False
 
-For a user with no PPAs, nobody will see the section apart from himself.
-This aspect allows him to access the 'Create a new PPA' link.
+For a user with no PPAs, nobody will see the section apart from themselves.
+This aspect allows them to access the 'Create a new PPA' link.
 
     >>> print sample_person.archive
     None
@@ -453,7 +453,7 @@
     False
 
 If the person is a member of teams with PPAs but doesn't own any
-himself, the section will still not appear for anyone but people with
+themselves, the section will still not appear for anyone but people with
 lp.edit.
 
     >>> from lp.app.interfaces.launchpad import ILaunchpadCelebrities

=== modified file 'lib/lp/registry/browser/tests/product-edit-people-view.txt'
--- lib/lp/registry/browser/tests/product-edit-people-view.txt	2012-08-13 19:34:10 +0000
+++ lib/lp/registry/browser/tests/product-edit-people-view.txt	2016-01-26 15:58:00 +0000
@@ -16,8 +16,8 @@
     >>> print sample_person.name
     name12
 
-No Privileges Person is taking over the project, but he cannot access the
-view because he is not yet an owner/maintainer or admin.
+No Privileges Person is taking over the project, but they cannot access the
+view because they are not yet an owner/maintainer or admin.
 
     >>> from lp.services.webapp.authorization import check_permission
 

=== modified file 'lib/lp/registry/browser/tests/product-views.txt'
--- lib/lp/registry/browser/tests/product-views.txt	2015-11-26 13:31:45 +0000
+++ lib/lp/registry/browser/tests/product-views.txt	2016-01-26 15:58:00 +0000
@@ -119,7 +119,7 @@
 Launchpad admins and members of the registry experts team can review a
 project's licences.
 
-The Commercial Admin user is not in the registry admins team so he
+The Commercial Admin user is not in the registry admins team so they
 cannot access the page.
 
     >>> login('commercial-member@xxxxxxxxxxxxx')
@@ -138,7 +138,7 @@
     Review project
 
 Adding the Commercial Admin to the registry experts team will give
-him access.
+them access.
 
     >>> commercial_member = getUtility(IPersonSet).getByEmail(
     ...     'commercial-member@xxxxxxxxxxxxx')
@@ -173,7 +173,7 @@
     [...This project cannot be deactivated since it is linked to
     ...source packages</a>.']
 
-The reviewer can deactivate a project if he concludes it is bogus.
+The reviewer can deactivate a project if they conclude it is bogus.
 
     >>> product = factory.makeProduct(name='tomato', title='Tomato')
     >>> product.active

=== modified file 'lib/lp/registry/browser/tests/test_distroseries.py'
--- lib/lp/registry/browser/tests/test_distroseries.py	2015-10-26 14:54:43 +0000
+++ lib/lp/registry/browser/tests/test_distroseries.py	2016-01-26 15:58:00 +0000
@@ -2012,7 +2012,7 @@
 
     def test_sync_error_no_perm_component(self):
         # A user without upload rights on the destination component
-        # will get an error when he syncs packages to this component.
+        # will get an error when they sync packages to this component.
         derived_series, parent_series, unused, diff_id = self._setUpDSD(
             'my-src-name')
         person, another_component = self.makePersonWithComponentPermission(

=== modified file 'lib/lp/registry/browser/tests/test_milestone.py'
--- lib/lp/registry/browser/tests/test_milestone.py	2015-09-29 01:38:34 +0000
+++ lib/lp/registry/browser/tests/test_milestone.py	2016-01-26 15:58:00 +0000
@@ -296,7 +296,8 @@
         self.factory.makeBug(milestone=milestone)
         self.factory.makeBug(
             milestone=milestone, information_type=InformationType.USERDATA)
-        # Remove the APG the product owner has so he can't see the private bug.
+        # Remove the APG the product owner has so they can't see the private
+        # bug.
         ap = getUtility(IAccessPolicySource).find(
             [(milestone.product, InformationType.USERDATA)]).one()
         getUtility(IAccessPolicyGrantSource).revoke(

=== modified file 'lib/lp/registry/browser/tests/test_peoplemerge.py'
--- lib/lp/registry/browser/tests/test_peoplemerge.py	2015-07-21 09:04:01 +0000
+++ lib/lp/registry/browser/tests/test_peoplemerge.py	2016-01-26 15:58:00 +0000
@@ -62,7 +62,7 @@
     def _assert_perform_merge_request(self):
         # Perform a merge request, asserting expected bahviour along the way.
         # We are redirected to a page displaying the email addresses owned by
-        # the dupe account. The user chooses which one he wants to claim.
+        # the dupe account. The user chooses which one they want to claim.
         target = self.factory.makePerson()
         login_person(target)
         browser = self.getUserBrowser(
@@ -133,7 +133,7 @@
 
     def test_validation_email_complete(self):
         # Test that the merge completes successfully when the user proves that
-        # he's the owner of the second email address of the dupe account.
+        # they're the owner of the second email address of the dupe account.
         browser, emails = self._assert_validation_email_confirm()
         ignore, ignore2, raw_msg2 = emails.pop()
         token_url = get_token_url_from_email(raw_msg2)

=== modified file 'lib/lp/registry/configure.zcml'
--- lib/lp/registry/configure.zcml	2015-10-13 13:22:08 +0000
+++ lib/lp/registry/configure.zcml	2016-01-26 15:58:00 +0000
@@ -1049,9 +1049,9 @@
 
         <!-- Adding SSH keys is the only possibly harmful thing that can be done
         without the user's password, so we use a special permission here to make
-        sure only the user (and not launchpad admins) will be able to edit her own
-        ssh keys. Another option would be to require the user's password on this
-        page. GuilhermeSalgado 25/08/2005 -->
+        sure only the user (and not launchpad admins) will be able to edit
+        their own ssh keys. Another option would be to require the user's
+        password on this page. GuilhermeSalgado 25/08/2005 -->
 
 
         <!-- Team Views -->

=== modified file 'lib/lp/registry/doc/distribution-mirror.txt'
--- lib/lp/registry/doc/distribution-mirror.txt	2015-09-28 17:38:45 +0000
+++ lib/lp/registry/doc/distribution-mirror.txt	2016-01-26 15:58:00 +0000
@@ -186,7 +186,7 @@
 == Finding the best mirror for a given user ==
 
 In order to be able to guess what would be the best mirror for a given
-user based on his IP address, we provide simple API to find official
+user based on their IP address, we provide simple API to find official
 mirrors of a given content type located on a given country (or any other
 country in its continent if the country doesn't have any).
 

=== modified file 'lib/lp/registry/doc/distroseries.txt'
--- lib/lp/registry/doc/distroseries.txt	2015-10-13 13:22:08 +0000
+++ lib/lp/registry/doc/distroseries.txt	2016-01-26 15:58:00 +0000
@@ -658,8 +658,8 @@
 
 Users with launchpad.Driver permission may create DistroSeries. In the
 case of a distribution that doesn't use Soyuz officially, a user who is
-a driver can create the series and he is automatically assigned to the
-series' driver role so that he can edit it.
+a driver can create the series and they are automatically assigned to the
+series' driver role so that they can edit it.
 
     >>> youbuntu = factory.makeDistribution(name='youbuntu')
     >>> yo_driver = factory.makePerson(name='yo-driver')

=== modified file 'lib/lp/registry/doc/person-account.txt'
--- lib/lp/registry/doc/person-account.txt	2015-01-07 00:35:53 +0000
+++ lib/lp/registry/doc/person-account.txt	2016-01-26 15:58:00 +0000
@@ -58,13 +58,13 @@
 Deactivating user accounts
 --------------------------
 
-Any user can deactivate his own account, in case they don't want it
+Any user can deactivate their own account, in case they don't want it
 anymore or they don't want to be shown as Launchpad users.
 
-As seen below, Foo Bar has a bunch of stuff assigned/owned to/by him in
-Launchpad which we'll want to be reassigned/unassigned if his account is
+As seen below, Foo Bar has a bunch of stuff assigned/owned to/by them in
+Launchpad which we'll want to be reassigned/unassigned if their account is
 deactivated.  Unfortunately, Foo Bar has no specifications assigned to
-him, so we'll assign one just to prove that deactivating his account
+them, so we'll assign one just to prove that deactivating their account
 will cause this spec to be reassigned.
 
 
@@ -123,7 +123,7 @@
     >>> comment = ("I'm a person who doesn't want to be listed "
     ...            "as a Launchpad user.")
 
-The deactivate method is restricted to the user himself --not
+The deactivate method is restricted to the user themselves --not
 even launchpad admins can use it.
 
     >>> login('mark@xxxxxxxxxxx')

=== modified file 'lib/lp/registry/doc/person-karma.txt'
--- lib/lp/registry/doc/person-karma.txt	2015-06-26 14:00:41 +0000
+++ lib/lp/registry/doc/person-karma.txt	2016-01-26 15:58:00 +0000
@@ -2,7 +2,7 @@
 People karma
 ============
 
-In Launchpad, everytime a user performs an action, we give him some karma
+In Launchpad, everytime a user performs an action, we give them some karma
 points. These karma points are stored in the KarmaAction table and the
 assignment to a user is made in the Karma table. The method used to calculate
 a users karma is time-dependent, because we want to give more karma points for

=== modified file 'lib/lp/registry/doc/person-merge.txt'
--- lib/lp/registry/doc/person-merge.txt	2015-10-01 10:25:19 +0000
+++ lib/lp/registry/doc/person-merge.txt	2016-01-26 15:58:00 +0000
@@ -214,7 +214,7 @@
     >>> results.get_one()[0] is None
     True
 
-An email is sent to the user informing him that he should review his
+An email is sent to the user informing them that they should review their
 email and mailing list subscription settings.
 
     >>> from lp.registry.interfaces.personnotification import (

=== modified file 'lib/lp/registry/doc/person.txt'
--- lib/lp/registry/doc/person.txt	2015-01-07 00:35:41 +0000
+++ lib/lp/registry/doc/person.txt	2016-01-26 15:58:00 +0000
@@ -106,8 +106,8 @@
     >>> p.hide_email_addresses
     True
 
-Since this person has chosen to hide his email addresses they won't be
-visible to other users which are not admins.
+Since this person has chosen to hide their email addresses they won't be
+visible to other users who are not admins.
 
     >>> from lp.services.webapp.authorization import check_permission
     >>> login('randomuser@xxxxxxxxxxxxxx')
@@ -497,7 +497,7 @@
     [u'ddaa']
 
     # As said previously, no notifications are sent when we add the
-    # team owner as a member of his team.
+    # team owner as a member of their team.
 
     >>> transaction.commit()
     >>> stub.test_emails
@@ -551,7 +551,7 @@
     [u'Guilherme Salgado', u'Sample Person']
 
 TeamMemberships with a PROPOSED or INVITED status represent a
-person/team which has proposed himself as a member or which has been
+person/team which has proposed themselves as a member or which has been
 invited to join the team.
 
     >>> [member.displayname for member in landscape_devs.proposedmembers]
@@ -947,8 +947,8 @@
  3. getLatestUploadedPPAPackages
 
 The 1st will return the latest SourcePackageReleases related to a person
-in which he is listed as the Maintainer. The second will return the
-latest SourcePackageReleases a person uploaded (and where he isn't the
+in which they are listed as the Maintainer. The second will return the
+latest SourcePackageReleases a person uploaded (and where they aren't the
 maintainer).
 
 Both, 1st and 2nd methods, only consider sources upload to primary

=== modified file 'lib/lp/registry/doc/pillar.txt'
--- lib/lp/registry/doc/pillar.txt	2015-06-26 14:00:41 +0000
+++ lib/lp/registry/doc/pillar.txt	2016-01-26 15:58:00 +0000
@@ -140,7 +140,7 @@
 Setting the aliases of a pillar is an operation that requires launchpad.Admin
 rights on the pillar.
 
-Sample Person has edit rights on firefox, but he'd need admin rights
+Sample Person has edit rights on firefox, but they'd need admin rights
 to be able to set its aliases.
 
     >>> login('test@xxxxxxxxxxxxx')

=== modified file 'lib/lp/registry/doc/private-team-roles.txt'
--- lib/lp/registry/doc/private-team-roles.txt	2015-05-14 13:57:51 +0000
+++ lib/lp/registry/doc/private-team-roles.txt	2016-01-26 15:58:00 +0000
@@ -38,7 +38,7 @@
 
     >>> bug.unsubscribe(team_owner, priv_team)
 
-Only the person can unsubscribe him or her self.
+Only the person can unsubscribe themselves.
 
     >>> priv_subscription = bug.subscribe(team_owner, priv_team)
     >>> bug.unsubscribe(team_owner, team_owner)

=== modified file 'lib/lp/registry/doc/productseries.txt'
--- lib/lp/registry/doc/productseries.txt	2015-01-29 16:28:30 +0000
+++ lib/lp/registry/doc/productseries.txt	2016-01-26 15:58:00 +0000
@@ -86,8 +86,8 @@
     >>> print emacs_series.releasefileglob
     ftp://gnu.org/emacs*.gz
 
-When a driver creates a series, he is also the driver of the new series
-to make him the release manager.
+When a driver creates a series, they are also the driver of the new series
+to make them the release manager.
 
     >>> firefox.driver = series_driver
     >>> ignored = login_person(series_driver)

=== modified file 'lib/lp/registry/doc/teammembership-email-notification.txt'
--- lib/lp/registry/doc/teammembership-email-notification.txt	2015-09-11 12:20:23 +0000
+++ lib/lp/registry/doc/teammembership-email-notification.txt	2016-01-26 15:58:00 +0000
@@ -603,10 +603,10 @@
 ------------------------------
 
 When we get close to the expiration date of a given membership, an
-expiration warning is sent to the member, so that he can contact the
-team's administrators (or renew it himself when he has necessary rights)
-in case he wants to retain that membership. This is done by the flag-
-expired-memberships cronscript, which uses
+expiration warning is sent to the member, so that they can contact the
+team's administrators (or renew it themselves if they have the necessary
+permissions) in case they want to retain that membership. This is done by
+the flag-expired-memberships cronscript, which uses
 ITeamMembership.sendExpirationWarningEmail to do its job.
 
     >>> import pytz
@@ -614,9 +614,9 @@
     >>> utc_now = datetime.now(pytz.timezone('UTC'))
 
 In the case of the beta-testers team, the email is sent only to the
-team's owner, which doesn't have the necessary rights to renew the
-membership of his team, so he's instructed to contact one of the ubuntu-
-team's admins.
+team's owner, who doesn't have the necessary rights to renew the
+membership of their team, so they're instructed to contact one of the
+ubuntu-team's admins.
 
     >>> beta_testers = personset.getByName('launchpad-beta-testers')
     >>> beta_testers_on_ubuntu_team = membershipset.getByPersonAndTeam(
@@ -662,7 +662,7 @@
     ----------------------------------------
 
 If the team's renewal policy is ONDEMAND, though, the member is invited
-to renew his own membership.
+to renew their own membership.
 
     >>> ubuntu_team.renewal_policy = TeamMembershipRenewalPolicy.ONDEMAND
     >>> ubuntu_team.defaultrenewalperiod = 365
@@ -732,9 +732,9 @@
     ----------------------------------------
 
 If the team's renewal policy is NONE but the member has the necessary
-rights to change the expiration date of his own membership (i.e. by
-being the team's owner), the notification he gets will contain a link to
-his memberhip page, where he can extend it.
+rights to change the expiration date of their own membership (i.e. by
+being the team's owner), the notification they get will contain a link to
+their membership page, where they can extend it.
 
     >>> landscape.renewal_policy = TeamMembershipRenewalPolicy.NONE
     >>> landscape.teamowner.preferredemail.email
@@ -780,7 +780,7 @@
 
 Another possible renewal policy for teams is ONDEMAND, which means that
 team members are invited to renew their membership once it gets close to
-their expiration date. When a member renew his own membership, a
+their expiration date. When a member renews their own membership, a
 notification is sent to all team admins.
 
     >>> karl = personset.getByName('karl')
@@ -834,8 +834,8 @@
 Some special cases
 ------------------
 
-When creating a new team, the owner has his membership's status changed
-from approved to admin, but he won't get a notification of that.
+When creating a new team, the owner has their membership's status changed
+from approved to admin, but they won't get a notification of that.
 
     >>> team = personset.newTeam(mark, 'testteam', 'Test')
     >>> run_mail_jobs()
@@ -896,8 +896,8 @@
     <BLANKLINE>
     ----------------------------------------
 
-If a team admin changes his own membership, the notification sent will
-clearly say that the change was performed by the user himself, and it
+If a team admin changes their own membership, the notification sent will
+clearly say that the change was performed by the user themselves, and it
 will only be sent to the team administrators.
 
     >>> jdub = getUtility(IPersonSet).getByName('jdub')

=== modified file 'lib/lp/registry/doc/teammembership.txt'
--- lib/lp/registry/doc/teammembership.txt	2013-06-05 09:40:19 +0000
+++ lib/lp/registry/doc/teammembership.txt	2016-01-26 15:58:00 +0000
@@ -63,8 +63,8 @@
 Adding new members
 ------------------
 
-One way of adding new members to a team is by having the user himself join the
-team he wants.
+One way of adding new members to a team is by having the user themselves
+join the team they want.
 
     >>> salgado = personset.getByName('salgado')
     >>> ignored = login_person(salgado)
@@ -153,7 +153,7 @@
     # Log in as the team owner.
     >>> ignored = login_person(t3.teamowner)
 
-If the member was added (i.e. he wasn't already a member of the team),
+If the member was added (i.e. they weren't already a member of the team),
 addMember returns a tuple with True plus the new membership status.
 
     >>> t3.addMember(
@@ -227,7 +227,7 @@
     >>> [m.displayname for m in t1.allmembers]
     [u'James Blackwell', u'No Privileges Person', u't2']
 
-A team admin can also decline an invitation made to his team.
+A team admin can also decline an invitation made to their team.
 
     >>> t2.addMember(t3, reviewer=mark)
     (True, <DBItem TeamMembershipStatus.INVITED...)
@@ -305,8 +305,8 @@
 
 After adding all this mess, this is what we have:
 
-(This table doesn't include the team owner (Foo Bar), but since he's the
-owner he's also a direct member of all teams)
+(This table doesn't include the team owner (Foo Bar), but since they're the
+owner they're also a direct member of all teams)
 =============================================================
 ||  Team      ||  Direct Members   ||  Indirect Members    ||
 =============================================================
@@ -376,8 +376,8 @@
 
 This is what we have now, after removing t2 from t5 and Salgado from t3.
 
-(This table doesn't include the team owner (Foo Bar), but since he's the
-owner he's also a direct member of all teams)
+(This table doesn't include the team owner (Foo Bar), but since they're the
+owner they're also a direct member of all teams)
 =============================================================
 ||  Team      ||  Members          ||  Indirect Members    ||
 =============================================================
@@ -408,8 +408,8 @@
 
 
 It's important to note that even if the owner leaves the team, which
-removes his membership, he will still be the team's owner and retain his
-rights over it. This ensures we'll never have teams which can't be
+removes their membership, they will still be the team's owner and retain
+their rights over it. This ensures we'll never have teams which can't be
 managed. This does not imply that the owner will be a member of the team.
 
     >>> ignored = login_person(t5.teamowner)
@@ -447,8 +447,8 @@
 the expiry date of their own memberships, the setExpirationDate() method does
 an extra check to ensure that doesn't happen.
 
-    # Foo Bar is a launchpad admin, but even so he can't change a membership's
-    # status/expiry-date by hand.
+    # Foo Bar is a launchpad admin, but even so they can't change a
+    # membership's status/expiry-date by hand.
     >>> ignored = login_person(foobar)
     >>> membership = foobar.team_memberships[0]
     >>> membership.status = None
@@ -461,8 +461,8 @@
     ...
     ForbiddenAttribute: ...
 
-Foo Bar asked to join Warty Security Team on 2006-01-26 and he's been doing
-good work, so we'll approve his membership.
+Foo Bar asked to join Warty Security Team on 2006-01-26 and they've been doing
+good work, so we'll approve their membership.
 
     >>> warty_team = getUtility(IPersonSet).getByName('name20')
     >>> membership = membershipset.getByPersonAndTeam(foobar, warty_team)
@@ -473,7 +473,7 @@
     >>> print membership.datejoined
     None
 
-When we approve his membership, the datejoined will contain the date that it
+When we approve their membership, the datejoined will contain the date that it
 was approved. It returns True to indicate that the status was changed.
 
     >>> membership.setStatus(TeamMembershipStatus.APPROVED, foobar)
@@ -520,11 +520,11 @@
     True
 
 When changing the expiry date we need to provide a date in the future and,
-as mentioned above, the change can't be done by a team admin to his own
+as mentioned above, the change can't be done by a team admin to their own
 membership.
 
 We're still logged in as Foo Bar, which is a launchpad admin and thus
-can change any membership's expiry date (even his own), as long as
+can change any membership's expiry date (even their own), as long as
 the new expiry date is not in the past.
 
     >>> foobar == foobar_on_buildd.team.teamowner
@@ -644,7 +644,7 @@
 changing its dateexpires (which can be done only by admins of the
 membership's team) or by using IPerson.renewTeamMembership, which is
 accessible only to the membership's member a few days before it expires.
-Also, for a member to renew his own membership, it's necessary that the
+Also, for a member to renew their own membership, it's necessary that the
 team's renewal policy is set to ONDEMAND and that the membership is
 still active.
 
@@ -658,7 +658,7 @@
     >>> print karl_on_mirroradmins.dateexpires
     None
 
-The member himself can't change the expiration date of his membership.
+The member themselves can't change the expiration date of their membership.
 
     >>> ignored = login_person(karl)
     >>> karl_on_mirroradmins.setExpirationDate(tomorrow, karl)
@@ -675,7 +675,7 @@
     True
 
 If the team's renewal policy is ONDEMAND, the membership can be renewed
-by the member himself. (That is only true because this membership is
+by the member themselves. (That is only true because this membership is
 active and set to expire tomorrow).
 
     >>> print karl_on_mirroradmins.team.renewal_policy.name
@@ -685,7 +685,7 @@
     >>> ondemand = TeamMembershipRenewalPolicy.ONDEMAND
     >>> karl_on_mirroradmins.team.renewal_policy = ondemand
 
-    # When a user renews his own membership, we use the team's default
+    # When a user renews their own membership, we use the team's default
     # renewal period, so we must specify that for the mirror admins
     # team.
     >>> mirror_admins.defaultrenewalperiod = 365
@@ -761,7 +761,7 @@
 ---------------------------
 
 Another convenient method is getDirectAdministrators(), which returns the
-admin members plus the owner in case he is not one of the admin members.
+admin members plus the owner in case they are not one of the admin members.
 
     >>> [admin.unique_displayname for admin in t3.adminmembers]
     [u'Jeff Waugh (jdub)']

=== modified file 'lib/lp/registry/doc/vocabularies.txt'
--- lib/lp/registry/doc/vocabularies.txt	2015-11-26 15:46:38 +0000
+++ lib/lp/registry/doc/vocabularies.txt	2016-01-26 15:58:00 +0000
@@ -1016,7 +1016,7 @@
      (u'Warty Security Team', u'Mark Shuttleworth'),
      (u'testing Spanish team', u'Carlos Perell\xf3 Mar\xedn')]
 
-A user who is a member of a private team will see that team in his
+A user who is a member of a private team will see that team in their
 search.
 
     >>> login('no-priv@xxxxxxxxxxxxx')

=== modified file 'lib/lp/registry/interfaces/person.py'
--- lib/lp/registry/interfaces/person.py	2015-10-01 10:25:19 +0000
+++ lib/lp/registry/interfaces/person.py	2016-01-26 15:58:00 +0000
@@ -368,7 +368,7 @@
         Created by a user to represent a person which does not use Launchpad.
 
         A user wanted to reference a person which is not a Launchpad user, so
-        he created this "placeholder" profile.
+        they created this "placeholder" profile.
         """)
 
     OWNER_CREATED_UBUNTU_SHOP = DBItem(12, """
@@ -423,7 +423,7 @@
         return IPerson
 
     def _getByName(self, name):
-        """Return a Person by looking up his name."""
+        """Return a Person by looking up their name."""
         return getUtility(IPersonSet).getByName(name, ignore_merged=False)
 
     def _validate(self, input):
@@ -1175,7 +1175,7 @@
     @export_read_operation()
     @operation_for_version("devel")
     def getOwnedProjects(match_name=None, transitive=False, user=None):
-        """Projects owned by this person or teams to which she belongs.
+        """Projects owned by this person or teams to which they belong.
 
         :param match_name: string optional project name to screen the results.
         """
@@ -1232,7 +1232,7 @@
         """Is this person is a member of `team`?
 
         Returns `True` when you ask if an `IPerson` (or an `ITeam`,
-        since it inherits from `IPerson`) is a member of himself
+        since it inherits from `IPerson`) is a member of themselves
         (i.e. `person1.inTeam(person1)`).
 
         :param team: Either an object providing `IPerson`, the string name of
@@ -1266,7 +1266,7 @@
 
     def getLatestUploadedButNotMaintainedPackages():
         """Return `SourcePackageRelease`s created by this person but
-        not maintained by him.
+        not maintained by them.
 
         This method will only include the latest source package release
         for each source package name, distribution series combination.
@@ -1289,7 +1289,7 @@
 
     def hasUploadedButNotMaintainedPackages():
         """Are there `SourcePackageRelease`s created by this person but
-        not maintained by him.
+        not maintained by them.
         """
 
     def hasUploadedPPAPackages():
@@ -1375,7 +1375,7 @@
     def isBugContributor(user):
         """Is the person a contributer to bugs in Launchpad?
 
-        Return True if the user has any bugs assigned to him, either
+        Return True if the user has any bugs assigned to them, either
         directly or by team participation.
 
         :user: The user doing the search. Private bugs that this
@@ -1386,7 +1386,7 @@
     def isBugContributorInTarget(user, target):
         """Is the person a contributor to bugs in `target`?
 
-        Return True if the user has any bugs assigned to him in the
+        Return True if the user has any bugs assigned to them in the
         context of a specific target, either directly or by team
         participation.
 
@@ -1421,7 +1421,7 @@
                 taken if the list is None, or in an unusable state.
 
         :param requester: The person requesting the list subscription,
-                if not the user himself.  The default assumes the user
+                if not the user themselves.  The default assumes the user
                 themself is making the request.
 
         :return: True if the user was subscribed, false if they weren't.
@@ -1610,7 +1610,7 @@
 
         :param requester: The person who requested the membership on
             behalf of a team or None when a person requests the
-            membership for himself.
+            membership for themselves.
 
         :param may_subscribe_to_list: If True, also try subscribing to
             the team mailing list.
@@ -2063,7 +2063,8 @@
 
         :param name: The name to be checked.
         :param user: The `IPerson` that wants to use the name. If the user
-            is an admin for the nameblacklist expression, he can use the name.
+            is an admin for the nameblacklist expression, they can use the
+            name.
         """
 
     def createPersonAndEmail(

=== modified file 'lib/lp/registry/interfaces/poll.py'
--- lib/lp/registry/interfaces/poll.py	2015-10-26 14:54:43 +0000
+++ lib/lp/registry/interfaces/poll.py	2016-01-26 15:58:00 +0000
@@ -442,8 +442,8 @@
 class IVote(Interface):
     """Here we store the vote itself, linked to a special token.
 
-    This token is given to the user when he votes, so he can change his vote
-    later.
+    This token is given to the user when they vote, so they can change their
+    vote later.
     """
 
     id = Int(

=== modified file 'lib/lp/registry/interfaces/teammembership.py'
--- lib/lp/registry/interfaces/teammembership.py	2015-03-09 11:37:50 +0000
+++ lib/lp/registry/interfaces/teammembership.py	2016-01-26 15:58:00 +0000
@@ -46,8 +46,8 @@
 from lp import _
 
 # One week before a membership expires we send a notification to the member,
-# either inviting him to renew his own membership or asking him to get a team
-# admin to do so, depending on the team's renewal policy.
+# either inviting them to renew their own membership or asking them to get a
+# team admin to do so, depending on the team's renewal policy.
 DAYS_BEFORE_EXPIRATION_WARNING_IS_SENT = 7
 
 
@@ -136,7 +136,7 @@
     reviewed_by = Attribute(
         _("The team admin who approved/rejected the member."))
     acknowledged_by = Attribute(
-        _('The person (usually the member or someone acting on his behalf) '
+        _('The person (usually the member or someone acting on their behalf) '
           'that acknowledged (accepted/declined) a membership invitation.'))
     last_changed_by = exported(
         Reference(title=_('Last person who change this'),
@@ -190,8 +190,8 @@
 
         A membership's expiration date can be changed by the team owner, by a
         Launchpad admin or by a team admin. In the latter case, though, the
-        expiration date can only be changed if the admin is not changing his
-        own membership.
+        expiration date can only be changed if the admin is not changing
+        their own membership.
         """
 
     @call_with(user=REQUEST_USER)
@@ -206,7 +206,7 @@
         """
 
     def canBeRenewedByMember():
-        """Can this membership be renewed by the member himself?
+        """Can this membership be renewed by the member themselves?
 
         A membership can be renewed if the team's renewal policy is ONDEMAND,
         the membership itself is active (status = [ADMIN|APPROVED]) and it's
@@ -216,7 +216,7 @@
 
     def sendSelfRenewalNotification():
         """Send an email to the team admins notifying that this membership
-        has been renewed by the member himself.
+        has been renewed by the member themselves.
 
         This method must not be called if the team's renewal policy is not
         ONDEMAND.

=== modified file 'lib/lp/registry/javascript/sharing/tests/test_granteepicker.js'
--- lib/lp/registry/javascript/sharing/tests/test_granteepicker.js	2013-03-20 03:41:40 +0000
+++ lib/lp/registry/javascript/sharing/tests/test_granteepicker.js	2016-01-26 15:58:00 +0000
@@ -207,7 +207,7 @@
                     }
                 });
             });
-            // Back button should not he shown
+            // Back button should not be shown
             var back_button = cb.one('button.prev');
             Y.Assert.isNull(back_button);
             // When submit is clicked, the correct person uri is used.

=== modified file 'lib/lp/registry/model/mailinglist.py'
--- lib/lp/registry/model/mailinglist.py	2015-10-01 10:25:19 +0000
+++ lib/lp/registry/model/mailinglist.py	2016-01-26 15:58:00 +0000
@@ -496,7 +496,7 @@
             # administrator of the team we're creating the mailing list for.
             # So you can't just do "registrant in
             # team.getDirectAdministrators()".  It's okay to use .inTeam() for
-            # all cases because a person is always a member of himself.
+            # all cases because a person is always a member of themselves.
             for admin in team.getDirectAdministrators():
                 if registrant.inTeam(admin):
                     break

=== modified file 'lib/lp/registry/model/person.py'
--- lib/lp/registry/model/person.py	2015-11-26 13:31:45 +0000
+++ lib/lp/registry/model/person.py	2016-01-26 15:58:00 +0000
@@ -1690,7 +1690,7 @@
         """
         tm = TeamMembership.selectOneBy(person=self, team=team)
         assert tm.canBeRenewedByMember(), (
-            "This membership can't be renewed by the member himself.")
+            "This membership can't be renewed by the member themselves.")
 
         assert (team.defaultrenewalperiod is not None
                 and team.defaultrenewalperiod > 0), (
@@ -2807,7 +2807,8 @@
 
         Active 'ppa_only' flag is usually associated with active
         'uploader_only' because there shouldn't be any sense of maintainership
-        for packages uploaded to PPAs by someone else than the user himself.
+        for packages uploaded to PPAs by someone else than the user
+        themselves.
         """
         clauses = []
         if uploader_only:

=== modified file 'lib/lp/registry/model/product.py'
--- lib/lp/registry/model/product.py	2016-01-14 17:24:14 +0000
+++ lib/lp/registry/model/product.py	2016-01-26 15:58:00 +0000
@@ -1501,7 +1501,7 @@
             summary=summary, branch=branch, releasefileglob=releasefileglob)
         if owner.inTeam(self.driver) and not owner.inTeam(self.owner):
             # The user is a product driver, and should be the driver of this
-            # series to make him the release manager.
+            # series to make them the release manager.
             series.driver = owner
         return series
 

=== modified file 'lib/lp/registry/model/teammembership.py'
--- lib/lp/registry/model/teammembership.py	2015-09-02 02:46:18 +0000
+++ lib/lp/registry/model/teammembership.py	2016-01-26 15:58:00 +0000
@@ -265,7 +265,7 @@
         # case with our script to expire team memberships.
         flush_database_updates()
 
-        # When a member proposes himself, a more detailed notification is
+        # When a member proposes themselves, a more detailed notification is
         # sent to the team admins by a subscriber of JoinTeamEvent; that's
         # why we don't send anything here.
         if ((self.person != self.last_changed_by or self.status != proposed)

=== modified file 'lib/lp/registry/stories/distribution/xx-distribution-launchpad-usage.txt'
--- lib/lp/registry/stories/distribution/xx-distribution-launchpad-usage.txt	2015-05-19 01:52:39 +0000
+++ lib/lp/registry/stories/distribution/xx-distribution-launchpad-usage.txt	2016-01-26 15:58:00 +0000
@@ -124,8 +124,8 @@
 
     >>> content = find_main_content(admin_browser.contents)
 
-Foo Bar chooses to switch the bug tracker again, but this time he
-does not change the expiration check box, and he does the whole
+Foo Bar chooses to switch the bug tracker again, but this time they
+do not change the expiration check box, and they do the whole
 operation before the page complete loading.
 
     >>> admin_browser.getLink('Change details').click()

=== modified file 'lib/lp/registry/stories/gpg-coc/xx-gpg-coc.txt'
--- lib/lp/registry/stories/gpg-coc/xx-gpg-coc.txt	2015-10-05 06:34:17 +0000
+++ lib/lp/registry/stories/gpg-coc/xx-gpg-coc.txt	2016-01-26 15:58:00 +0000
@@ -152,8 +152,8 @@
 == Claim a sign-only GPG key ==
 
 Here, Sample Person wants to claim a GPG key that can only sign
-content. He can't verify his key by decrypting content on demand, but
-he can verify it by signing content. Launchpad sends him an email
+content. They can't verify their key by decrypting content on demand, but
+they can verify it by signing content. Launchpad sends them an email
 token. The email step ensures that an attacker who knows Sample
 Person's Launchpad password can't associate arbitrary GPG keys with
 their Launchpad account.
@@ -168,7 +168,7 @@
     confirm the key 1024D/17B05A8F is yours, follow
     the link inside.
 
-Sample Person checks his email.
+Sample Person checks their email.
 
     >>> from_addr, to_addrs, raw_msg = stub.test_emails.pop()
     >>> msg = email.message_from_string(raw_msg)
@@ -183,7 +183,7 @@
     False
 
 The email does contain some information about the key, and a token URL
-Sample Person should visit to verify his ownership of the key.
+Sample Person should visit to verify their ownership of the key.
 
     >>> print body
     <BLANKLINE>
@@ -217,8 +217,8 @@
     ...     2005,04,01, 12,00,00, tzinfo=pytz.timezone('UTC'))
     >>> logintoken.sync()
 
-Back to Sample User. He visits the token URL and is asked to sign some
-text to prove he owns the key.
+Back to Sample User. They visit the token URL and is asked to sign some
+text to prove they own the key.
 
     >>> browser.open(token_url)
     >>> browser.title
@@ -232,7 +232,7 @@
     Please register 447DBF38C4F9C4ED752246B77D88913717B05A8F to the
     Launchpad user name12.  2005-04-01 12:00:00 UTC
 
-If he refuses to sign the text, he gets an error message.
+If they refuse to sign the text, they get an error message.
 
     >>> browser.getControl('Continue').click()
     >>> browser.title
@@ -241,7 +241,7 @@
     There is 1 error.
     Required input is missing.
 
-If he signs a different text, he gets an error message.
+If they sign a different text, they get an error message.
 
     >>> login(ANONYMOUS)
     >>> key = import_secret_test_key('sign.only@xxxxxxxxxxxxxxxxx')
@@ -256,7 +256,7 @@
     There is 1 error.
     The signed content does not match the message found in the email.
 
-If he signs the text with a different key, he gets an error
+If they sign the text with a different key, they get an error
 message. The following text was signed with the key DFD20543:
 
     >>> signed_content = """
@@ -280,7 +280,7 @@
     The key used to sign the content (A419AE861E88BC9E04B9C26FBA2B9389DFD20543)
     is not the key you were registering
 
-If he signs the text correctly, he is redirected to his home page.
+If they sign the text correctly, they are redirected to their home page.
 
     >>> login(ANONYMOUS)
     >>> good = gpghandler.signContent(
@@ -302,8 +302,8 @@
     >>> consumed_token.date_consumed is not None
     True
 
-Now Sample Person's sign-only key is associated with his account. He
-verifies this:
+Now Sample Person's sign-only key is associated with their account. They
+verify this:
 
     >>> browser.open("http://launchpad.dev/~name12/+editpgpkeys";)
 
@@ -311,7 +311,7 @@
     >>> browser.getControl(name='DEACTIVATE_GPGKEY').displayOptions
     [...'1024D/17B05A8F (sign only)']
 
-On a mad whim he decides to de-activate the key he just imported.
+On a mad whim they decide to de-activate the key they just imported.
 
     >>> browser.getControl(name="DEACTIVATE_GPGKEY").displayValue = [
     ...     '1024D/17B05A8F (sign only)']
@@ -320,7 +320,7 @@
     >>> print_feedback_messages(browser.contents)
     Deactivated key(s): 1024D/17B05A8F
 
-Coming to his senses, he asks for a re-validation of the key.
+Coming to their senses, they ask for a re-validation of the key.
 
     >>> browser.getControl(name="REACTIVATE_GPGKEY").value = ['3']
     >>> browser.getControl('Reactivate Key').click()
@@ -329,7 +329,7 @@
     A message has been sent to test@xxxxxxxxxxxxx with instructions
     to reactivate these key(s): 1024D/17B05A8F
 
-He opens the page and checks that the key is displayed as pending
+They open the page and checks that the key is displayed as pending
 revalidation.
 
     >>> browser.reload()
@@ -419,7 +419,7 @@
     >>> print browser.url
     http://launchpad.dev/~name12/+codesofconduct
 
-And now Sample Person's Codes of Conduct page shows that he's signed it.
+And now Sample Person's Codes of Conduct page shows that they've signed it.
 
     >>> browser.open('http://launchpad.dev/~name12/+codesofconduct')
     >>> print extract_text(find_main_content(browser.contents))
@@ -434,7 +434,7 @@
     ...
 
 
-Now Sample Person will deactivate his key...
+Now Sample Person will deactivate their key...
 
     >>> browser = setupBrowserFreshLogin(name12)
     >>> browser.open('http://launchpad.dev/~name12/+editpgpkeys')
@@ -447,7 +447,7 @@
     ...1024D/DFD20543...
 
 
-... but he forgot to select the checkbox of the key he wants to remove.
+... but they forgot to select the checkbox of the key they want to remove.
 
     >>> browser.getControl('Deactivate Key').click()
     >>> for tag in find_main_content(browser.contents)('p', 'error message'):
@@ -455,7 +455,7 @@
     No key(s) selected for deactivation.
 
 
-Now he selects the checkbox and deactivates it.
+Now they select the checkbox and deactivate it.
 
     >>> browser.getControl('1024D/DFD20543').selected = True
     >>> browser.getControl('Deactivate Key').click()
@@ -477,7 +477,7 @@
     ...1024D/DFD20543...
 
 
-Now he'll request his key to be reactivated.
+Now they'll request their key to be reactivated.
 
     >>> browser.getControl('Reactivate Key').click()
     >>> soup = find_main_content(browser.contents)

=== modified file 'lib/lp/registry/stories/gpg-coc/xx-ubuntu-codeofconduct-signer.txt'
--- lib/lp/registry/stories/gpg-coc/xx-ubuntu-codeofconduct-signer.txt	2009-11-22 15:43:16 +0000
+++ lib/lp/registry/stories/gpg-coc/xx-ubuntu-codeofconduct-signer.txt	2016-01-26 15:58:00 +0000
@@ -28,8 +28,8 @@
     ...
     LinkNotFoundError
 
-No-priv hasn't signed the Ubuntu Code of Conduct yet.  His homepage has a link
-to the Ubuntu Code of Conduct forms.
+No-priv hasn't signed the Ubuntu Code of Conduct yet.  Their homepage has a
+link to the Ubuntu Code of Conduct forms.
 
     >>> browser.addHeader('Authorization', 'Basic no-priv@xxxxxxxxxxxxx:test')
     >>> browser.open('http://launchpad.dev/~no-priv')

=== modified file 'lib/lp/registry/stories/location/personlocation-edit.txt'
--- lib/lp/registry/stories/location/personlocation-edit.txt	2012-02-21 12:43:54 +0000
+++ lib/lp/registry/stories/location/personlocation-edit.txt	2016-01-26 15:58:00 +0000
@@ -17,7 +17,7 @@
     ...
     Unauthorized:...
 
-A user can set his own time zone:
+A user can set their own time zone:
 
     >>> self_browser = setupBrowser(auth="Basic zzz@xxxxxxx:test")
     >>> self_browser.open('http://launchpad.dev/~zzz/+editlocation')
@@ -30,8 +30,8 @@
     u'Europe/Madrid'
     >>> logout()
 
-And when he comes back to change it later, he'll see it there as the selected
-value.
+And when they come back to change it later, they'll see it there as the
+selected value.
 
     >>> self_browser.open('http://launchpad.dev/~zzz/+editlocation')
     >>> print self_browser.getControl(name='field.time_zone').value

=== modified file 'lib/lp/registry/stories/mailinglists/lifecycle.txt'
--- lib/lp/registry/stories/mailinglists/lifecycle.txt	2013-09-27 04:13:23 +0000
+++ lib/lp/registry/stories/mailinglists/lifecycle.txt	2016-01-26 15:58:00 +0000
@@ -18,7 +18,7 @@
     >>> print backslashreplace(browser.title)
     Mailing list configuration...
 
-He thinks for a second whether his mailing list is for Ubuntu or not.
+They think for a second whether their mailing list is for Ubuntu or not.
 
     >>> print extract_text(find_tag_by_id(browser.contents, 'ubuntu-notice'))
     Ubuntu does not use Launchpad to host its mailing lists. Create them
@@ -290,7 +290,7 @@
     ...     print mailing_list.status.name
     ...     logout()
 
-The team owner can see that he can purge or reactivate mailing list.
+The team owner can see that they can purge or reactivate mailing list.
 
     >>> user_browser.open('http://launchpad.dev/~aardvarks/+mailinglist')
     >>> user_browser.getControl('Create new Mailing List').click()
@@ -320,7 +320,7 @@
 
 Mailing list experts can also purge mailing lists.  Sample Person is
 trustworthy enough to become a mailing list expert, but not a Launchpad
-administrator.  He's given mailing list expert authority so that he can
+administrator.  They're given mailing list expert authority so that they can
 purge mailing lists.
 
     >>> login('foo.bar@xxxxxxxxxxxxx')

=== modified file 'lib/lp/registry/stories/mailinglists/subscriptions.txt'
--- lib/lp/registry/stories/mailinglists/subscriptions.txt	2015-02-27 00:57:04 +0000
+++ lib/lp/registry/stories/mailinglists/subscriptions.txt	2016-01-26 15:58:00 +0000
@@ -4,8 +4,8 @@
 
 Teams can have mailing lists associated with them, and a member of a
 team with a usable mailing list can subscribe to that list. A person
-can subscribe to a mailing list using any of his email addresses, or a
-dynamic subscription which uses whichever email address is his
+can subscribe to a mailing list using any of their email addresses, or a
+dynamic subscription which uses whichever email address is their
 preferred address at a given time.
 
 Non-team members can request a subscription to a mailing list at the

=== modified file 'lib/lp/registry/stories/milestone/xx-milestone-add-and-edit.txt'
--- lib/lp/registry/stories/milestone/xx-milestone-add-and-edit.txt	2014-02-19 02:11:16 +0000
+++ lib/lp/registry/stories/milestone/xx-milestone-add-and-edit.txt	2016-01-26 15:58:00 +0000
@@ -106,7 +106,7 @@
     ...
 
 A user with launchpad.Edit rights for a release can see the delete link and
-access the delete page. Sample Person is the driver so he has those rights.
+access the delete page. Sample Person is the driver so they have those rights.
 
     >>> driver_browser.getLink('0.9.2').click()
     >>> print driver_browser.title

=== modified file 'lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt'
--- lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt	2015-10-01 17:32:41 +0000
+++ lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt	2016-01-26 15:58:00 +0000
@@ -20,7 +20,7 @@
     Source Package      Bugs    Translations
     pmount          No bugs     64 strings ...
 
-He looks at the pmount source package page in Hoary and reads that the
+They look at the pmount source package page in Hoary and read that the
 upstream project is not set.
 
     >>> user_browser.getLink('pmount').click()
@@ -34,7 +34,7 @@
     Register the upstream project
 
 No Privileges Person knows that the pmount package comes from the thunderbird
-project. He sets the upstream packaging link and sees that it is set.
+project. They set the upstream packaging link and see that it is set.
 
     >>> user_browser.getControl(
     ...     'Choose another upstream project').selected = True
@@ -47,7 +47,7 @@
     ...     user_browser.contents, 'upstreams'))
     The Mozilla Project...Mozilla Thunderbird...trunk...
 
-He sees the "Show upstream links" link and takes a look at the project's
+They see the "Show upstream links" link and take a look at the project's
 packaging in distributions.
 
     >>> user_browser.getLink('Show upstream links').click()
@@ -92,7 +92,7 @@
     Step 2 (of 2): Check for duplicate projects
 
 When No Privileges Person selects "Choose another upstream project" and
-then finds out that the project doesn't exist, he uses the
+then finds out that the project doesn't exist, they use the
 "Link to Upstream Project" button to register the project.
 
     >>> user_browser.open(
@@ -115,7 +115,7 @@
     ...     find_tag_by_id(user_browser.contents, 'step-title'))
     Step 2 (of 2): Check for duplicate projects
 
-After No Privileges Person select the licences, he user is redirected back
+After No Privileges Person selects the licences, the user is redirected back
 to the source package page and an informational message will be displayed.
 
     >>> user_browser.getControl(name='field.licenses').value = ['BSD']

=== modified file 'lib/lp/registry/stories/person/xx-admin-person-review.txt'
--- lib/lp/registry/stories/person/xx-admin-person-review.txt	2015-01-06 10:47:37 +0000
+++ lib/lp/registry/stories/person/xx-admin-person-review.txt	2016-01-26 15:58:00 +0000
@@ -63,7 +63,7 @@
     >>> for tr in content.find(id='summary').findAll('tr'):
     ...     print extract_text(tr)
     Created: 2005-06-06
-    Creation reason: Created by the owner himself, coming from Launchpad.
+    Creation reason: Created by the owner themselves, coming from Launchpad.
     OpenID identifiers: salgado_oid
     Email addresses: guilherme.salgado@xxxxxxxxxxxxx
     Guessed email addresses: salgado@xxxxxxxxxx

=== modified file 'lib/lp/registry/stories/person/xx-deactivate-account.txt'
--- lib/lp/registry/stories/person/xx-deactivate-account.txt	2014-11-27 07:48:25 +0000
+++ lib/lp/registry/stories/person/xx-deactivate-account.txt	2016-01-26 15:58:00 +0000
@@ -52,8 +52,8 @@
     >>> job.run()
     >>> logout()
 
-And now the Launchpad page for Sample Person person will clearly say he
-does not use Launchpad.
+And now the Launchpad page for Sample Person person will clearly say they
+do not use Launchpad.
 
     >>> browser.open('http://launchpad.dev/~name12-deactivatedaccount')
     >>> print extract_text(
@@ -80,7 +80,7 @@
     None
 
 The action of deactivating an account is something that can only be done by
-the user himself --not even Launchpad admins can do that on behalf of other
+the user themselves --not even Launchpad admins can do that on behalf of other
 people.
 
     >>> admin_browser.open('http://launchpad.dev/~cprov/+deactivate-account')

=== modified file 'lib/lp/registry/stories/person/xx-person-claim-merge.txt'
--- lib/lp/registry/stories/person/xx-person-claim-merge.txt	2010-04-15 22:42:41 +0000
+++ lib/lp/registry/stories/person/xx-person-claim-merge.txt	2016-01-26 15:58:00 +0000
@@ -24,7 +24,7 @@
     >>> user_browser.open('http://launchpad.dev/~matsubara')
     >>> user_browser.getLink("Are you Diogo Matsubara?").click()
 
-Clicking on that link brought him to a page where he can request to
+Clicking on that link brought them to a page where they can request to
 merge the accounts, and the account name is already populated in the
 field.
 

=== modified file 'lib/lp/registry/stories/person/xx-person-edit-jabber-ids.txt'
--- lib/lp/registry/stories/person/xx-person-edit-jabber-ids.txt	2010-08-10 14:03:29 +0000
+++ lib/lp/registry/stories/person/xx-person-edit-jabber-ids.txt	2016-01-26 15:58:00 +0000
@@ -8,7 +8,7 @@
 Adding and editing an ID
 ------------------------
 
-To register a Jabber ID with his account, the user visits his
+To register a Jabber ID with their account, the user visits their
 profile page and uses the 'Update Jabber IDs' link.
 
     >>> user_browser.open('http://launchpad.dev/~no-priv')
@@ -36,7 +36,7 @@
     The Jabber ID jeff@xxxxxxxxxx is already registered by Jeff Waugh.
 
 However, if the user enters a Jabber ID which isn't already registered,
-it will be associated with his account.
+it will be associated with their account.
 
     >>> user_browser.getControl(name='field.jabberid').value = (
     ...     'no-priv@xxxxxxxxxx')

=== modified file 'lib/lp/registry/stories/person/xx-person-edit.txt'
--- lib/lp/registry/stories/person/xx-person-edit.txt	2015-07-21 09:04:01 +0000
+++ lib/lp/registry/stories/person/xx-person-edit.txt	2016-01-26 15:58:00 +0000
@@ -1,7 +1,7 @@
 Changing personal details
 =========================
 
-A user wants to check if his personal information is up to date in
+A user wants to check if their personal information is up to date in
 Launchpad.
 
     >>> from lp.testing.sampledata import ADMIN_EMAIL
@@ -20,15 +20,15 @@
     ...     'Hide my email addresses from other Launchpad users').selected
     False
 
-He can see a link to an FAQ that explains launchpad accounts and passwords.
+They can see a link to an FAQ that explains launchpad accounts and passwords.
 Note that this is a link to an FAQ in production databases.
 
     >>> print browser.getLink(
     ...     'Learn about your Launchpad account and password').url
     http://launchpad.dev/launchpad/+faq/51
 
-He noticed that his information is out of date and will update it.
-(If he leaves leading or trailing whitespace in the displayname it'll
+They noticed that their information is out of date and will update it.
+(If they leave leading or trailing whitespace in the displayname it'll
 be stripped)
 
     >>> browser.getControl('Name', index=1).value = 'rayjay'
@@ -60,7 +60,7 @@
     >>> 'ray@xxxxxxxxxxx' in user_browser.contents
     False
 
-He will decide to make all bug notifications that are sent to him
+They will decide to make all bug notifications that are sent to them
 not include descriptions.
 
     >>> bug_description_control = browser.getControl(
@@ -69,8 +69,8 @@
     True
     >>> bug_description_control.click()
 
-He will also decide to not receive bug notifications generated by his own
-actions.
+They will also decide to not receive bug notifications generated by their
+own actions.
 
     >>> self_generated_control = browser.getControl(
     ...     "Send me bug notifications for changes I make")
@@ -78,7 +78,7 @@
     False
     >>> self_generated_control.click()
 
-He will enable expanded mail notification footers.
+They will enable expanded mail notification footers.
 
     >>> expanded_footer_control = browser.getControl(
     ...     "Include filtering information in email footers")

=== modified file 'lib/lp/registry/stories/person/xx-person-editgpgkeys-invalid-key.txt'
--- lib/lp/registry/stories/person/xx-person-editgpgkeys-invalid-key.txt	2015-07-21 09:04:01 +0000
+++ lib/lp/registry/stories/person/xx-person-editgpgkeys-invalid-key.txt	2016-01-26 15:58:00 +0000
@@ -111,7 +111,7 @@
     and try again.
 
 The login tokens are only consumed if they're successfully processed.
-Otherwise they're kept around so the user can try again after fixing his
+Otherwise they're kept around so the user can try again after fixing their
 key.
 
     >>> login(ANONYMOUS)

=== modified file 'lib/lp/registry/stories/person/xx-person-home.txt'
--- lib/lp/registry/stories/person/xx-person-home.txt	2015-07-21 09:04:01 +0000
+++ lib/lp/registry/stories/person/xx-person-home.txt	2016-01-26 15:58:00 +0000
@@ -35,8 +35,8 @@
     ...     find_tag_by_id(sample_browser.contents, 'email-addresses'))
     Email: mark@xxxxxxxxxxx
 
-As for Sample Person, he has chosen not to disclose his email addresses.
-unprivileged users like No Privileges Person cannot see his addresses:
+As for Sample Person, they have chosen not to disclose their email addresses.
+Unprivileged users like No Privileges Person cannot see their addresses:
 
     >>> user_browser.open('http://launchpad.dev/~name12')
     >>> print extract_text(
@@ -58,8 +58,7 @@
 Open ID link
 ------------
 
-When a person visits his or her own page, they'll see their OpenID login
-URL.
+When a person visits their own page, they'll see their OpenID login URL.
 
     >>> user_browser.open('http://launchpad.dev/~no-priv')
     >>> print extract_text(
@@ -131,7 +130,7 @@
     Languages:
     Catalan, English, Spanish
 
-When viewing his own page, No Privileges Person sees his languages and
+When viewing their own page, No Privileges Person sees their languages and
 can edit them.
 
     >>> user_browser.open('http://launchpad.dev/~no-priv')
@@ -184,7 +183,7 @@
 
 A person's home page also displays a table with the contributions made
 by that person. This table includes 5 projects in which this person is
-most active and also the areas in which (s)he worked on each project.
+most active and also the areas in which they worked on each project.
 
     >>> anon_browser.open('http://launchpad.dev/~name16')
     >>> table = find_tag_by_id(anon_browser.contents, 'contributions')

=== modified file 'lib/lp/registry/stories/person/xx-person-subscriptions.txt'
--- lib/lp/registry/stories/person/xx-person-subscriptions.txt	2014-02-19 02:11:16 +0000
+++ lib/lp/registry/stories/person/xx-person-subscriptions.txt	2016-01-26 15:58:00 +0000
@@ -54,8 +54,8 @@
 permission to remove the subscription, for bugs to which the person or team is
 subscribed.
 
-Webster can see and manage his direct bug subscriptions using the page too.
-He chooses to view the bug about Scofflaw.
+Webster can see and manage their direct bug subscriptions using the page too.
+They choose to view the bug about Scofflaw.
 
     >>> login('foo.bar@xxxxxxxxxxxxx')
     >>> subscriber_browser = setupBrowser(
@@ -71,8 +71,8 @@
     >>> "unsubscribe me from this bug" in page_text
     True
 
-After viewing the +subscribe page Webster decides that he still wants to be
-subscribed to the bug.  He clicks cancel which takes him back to his
+After viewing the +subscribe page Webster decides that they still want to be
+subscribed to the bug.  They click cancel which takes them back to their
 +subscription page.
 
     >>> cancel_link = subscriber_browser.getLink('Cancel')
@@ -80,7 +80,7 @@
     >>> print subscriber_browser.title
     Subscriptions : Bugs : Webster
 
-He chooses to unsubscribe from the bug about Affluenza.
+They choose to unsubscribe from the bug about Affluenza.
 
     >>> subscriber_browser.getLink(url='/affluenza/+bug/%s/+subscribe'
     ...     % bugB.id).click()
@@ -90,8 +90,8 @@
     >>> print subscriber_browser.title
     Subscriptions : Bugs : Webster
 
-Webster can see that the bug about Affluenza is no longer listed in his direct
-bug subscriptions.
+Webster can see that the bug about Affluenza is no longer listed in their
+direct bug subscriptions.
 
     >>> subscriber_browser.open(
     ...     'http://bugs.launchpad.dev/~webster/+subscriptions')
@@ -103,8 +103,8 @@
     Word needs more popularity
     Scofflaw
 
-Webster adds a bug subscription for a team, team America, of which he is a
-member.
+Webster adds a bug subscription for a team, team America, of which they are
+a member.
 
     >>> login('foo.bar@xxxxxxxxxxxxx')
     >>> team = factory.makeTeam(name='america')
@@ -114,7 +114,7 @@
     >>> subscriptionC = bugC.subscribe(team, subscriber)
     >>> logout()
 
-Webster chooses to review the subscriptions for his team America.
+Webster chooses to review the subscriptions for their team America.
 
     >>> subscriber_browser.open(
     ...     'http://bugs.launchpad.dev/~america/+subscriptions')

=== modified file 'lib/lp/registry/stories/person/xx-user-to-user.txt'
--- lib/lp/registry/stories/person/xx-user-to-user.txt	2011-09-20 01:33:04 +0000
+++ lib/lp/registry/stories/person/xx-user-to-user.txt	2016-01-26 15:58:00 +0000
@@ -40,7 +40,8 @@
     Subject: Hi Salgado
     ...
 
-No Priv starts to send another message to Salgado, but then changes her mind.
+No Priv starts to send another message to Salgado, but then changes their
+mind.
 
     >>> user_browser.getLink('Contact this user').click()
     >>> user_browser.getControl('Subject').value = 'Hi Salgado'
@@ -60,13 +61,13 @@
     >>> browser.getControl('Subject').value = 'Hi Salgado'
     >>> browser.getControl('Message').value = 'Hello again'
 
-By default, Sample can use her preferred email address.
+By default, Sample can use their preferred email address.
 
     >>> print browser.getControl('From').value
     ['test@xxxxxxxxxxxxx']
 
-But she doesn't have to use her preferred address, she can use one of her
-alternative addresses.
+But they don't have to use their preferred address; they can use one of
+their alternative addresses.
 
     >>> del stub.test_emails[:]
     >>> browser.getControl('From').value = ['testing@xxxxxxxxxxxxx']

=== modified file 'lib/lp/registry/stories/pillar/xx-pillar-deactivation.txt'
--- lib/lp/registry/stories/pillar/xx-pillar-deactivation.txt	2010-10-18 22:24:59 +0000
+++ lib/lp/registry/stories/pillar/xx-pillar-deactivation.txt	2016-01-26 15:58:00 +0000
@@ -61,8 +61,8 @@
     ...
     NotFound:...
 
-But an administrator can still see them if he traverses directly, and
-he'll see an informative message. He can then reactivate them..
+But an administrator can still see them if they traverse directly, and
+they'll see an informative message. They can then reactivate them..
 
     >>> admin_browser.open('http://launchpad.dev/firefox')
     >>> print find_tag_by_id(admin_browser.contents, 'project-inactive').renderContents()

=== modified file 'lib/lp/registry/stories/product/xx-launchpad-project-search.txt'
--- lib/lp/registry/stories/product/xx-launchpad-project-search.txt	2012-07-06 22:26:00 +0000
+++ lib/lp/registry/stories/product/xx-launchpad-project-search.txt	2016-01-26 15:58:00 +0000
@@ -18,7 +18,7 @@
 
 Launchpad shows up to config.launchpad.default_batch_size results; if there
 are more results than that, the user sees an informational message that asks
-the user to refine his search.
+the user to refine their search.
 
     >>> from lp.services.config import config
     >>> config.launchpad.default_batch_size
@@ -65,7 +65,7 @@
     >>> anon_browser.url
     'http://launchpad.dev/tomcat'
 
-If the user searches without any search terms, the page asks him to enter
+If the user searches without any search terms, the page asks them to enter
 some terms and do another search.
 
     >>> anon_browser.open('http://launchpad.dev/projects')

=== modified file 'lib/lp/registry/stories/product/xx-product-add.txt'
--- lib/lp/registry/stories/product/xx-product-add.txt	2015-06-26 12:57:00 +0000
+++ lib/lp/registry/stories/product/xx-product-add.txt	2016-01-26 15:58:00 +0000
@@ -1,8 +1,8 @@
 Adding projects
 ===============
 
-Sample Person decides to register her new project, which she can do either
-from the Launchpad home page or the projects home page.
+Sample Person decides to register their new project, which they can do
+either from the Launchpad home page or the projects home page.
 
     >>> user_browser.open('http://launchpad.dev')
     >>> user_browser.getLink('Register a project').click()
@@ -36,14 +36,14 @@
 Project basics
 --------------
 
-Sample Person doesn't want Ubuntu CDs, she wants to register her new project.
-This is done in two steps.  First, Sample Person provides some basic
-information about her project, such as its name, final URL component, title,
-and summary.
+Sample Person doesn't want Ubuntu CDs, they want to register their new
+project.  This is done in two steps.  First, Sample Person provides some
+basic information about their project, such as its name, final URL
+component, title, and summary.
 
     >>> user_browser.getControl('Name').value = 'Aardvark Center'
 
-She makes some mistakes though, entering illegal characters for the URL, and
+They make some mistakes though, entering illegal characters for the URL, and
 forgetting to enter a title or summary.
 
     >>> user_browser.getControl('URL').value = 'aard vark'
@@ -61,8 +61,8 @@
     Required input is missing.
     A short paragraph to introduce the project's work.
 
-Realizing her mistake, Sample Person fills out the project's basic information
-correctly this time.
+Realizing their mistake, Sample Person fills out the project's basic
+information correctly this time.
 
     >>> user_browser.getControl('URL').value = 'aardvark'
     >>> user_browser.getControl('Summary').value = (
@@ -89,7 +89,7 @@
     aardvark
 
 Sample Person is given the opportunity though to change the summary.
-She can also add a longer description.
+They can also add a longer description.
 
     >>> user_browser.getControl('Summary').value = (
     ...     'Control pesky aardvarkian fnords')
@@ -128,8 +128,8 @@
 ---------------------------
 
 Sample Person wants to create a project in Launchpad for a project
-that exists elsewhere as an upstream.  She wants it to exist in
-Launchpad so she can file a bug, for instance, but she is not
+that exists elsewhere as an upstream.  They want it to exist in
+Launchpad so they can file a bug, for instance, but they are not
 interested in being the project maintainer for the long run.
 
     >>> user_browser.open('http://launchpad.dev')
@@ -162,7 +162,7 @@
 Search results
 --------------
 
-Sample Person has another project she wants to register.  It is similar to
+Sample Person has another project they want to register.  It is similar to
 Firefox.
 
     >>> user_browser.open('http://launchpad.dev')
@@ -179,7 +179,7 @@
 A search is performed using the terms in the URL, title, and summary.  The
 Firefox project is discovered.
 
-Instead of registering her new project, Sample Person decides to participate
+Instead of registering their new project, Sample Person decides to participate
 in the Mozilla Project.
 
     >>> user_browser.getLink('The Mozilla Project').click()

=== modified file 'lib/lp/registry/stories/productrelease/xx-productrelease-basics.txt'
--- lib/lp/registry/stories/productrelease/xx-productrelease-basics.txt	2013-09-27 04:13:23 +0000
+++ lib/lp/registry/stories/productrelease/xx-productrelease-basics.txt	2016-01-26 15:58:00 +0000
@@ -160,7 +160,7 @@
     >>> from lp.services.database.sqlbase import flush_database_updates
     >>> flush_database_updates()
 
-... he can edit existing releases as well, even if they are owned by
+... they can edit existing releases as well, even if they are owned by
 others:
 
     >>> browser = setupBrowser(auth='Basic no-priv@xxxxxxxxxxxxx:test')

=== modified file 'lib/lp/registry/stories/productseries/xx-productseries-delete.txt'
--- lib/lp/registry/stories/productseries/xx-productseries-delete.txt	2013-09-27 04:13:23 +0000
+++ lib/lp/registry/stories/productseries/xx-productseries-delete.txt	2016-01-26 15:58:00 +0000
@@ -3,7 +3,7 @@
 
 A project series can be made by accident and the project owner or driver
 may choose to delete it. Sample Person is the owner of the Firefox
-project and he wants to delete the trunk series because development
+project and they want to delete the trunk series because development
 is really happening in the 1.0 series.
 
     >>> owner_browser = setupBrowser(auth='Basic test@xxxxxxxxxxxxx:test')
@@ -16,7 +16,7 @@
     Delete Mozilla Firefox trunk series...
 
 The trunk series is the focus of development. It cannot be deleted.
-The owner learns that he must make another series the focus of development
+The owner learns that they must make another series the focus of development
 first. There is no delete button on the page.
 
     >>> print extract_text(
@@ -32,7 +32,7 @@
     LookupError: ...
 
 Sample person chooses to cancel the action and make the 1.0 series the focus
-of development. He then removes the linked package which he believes is
+of development. They then remove the linked package which they believe is
 bogus.
 
     >>> owner_browser.getLink('Cancel').click()
@@ -51,9 +51,9 @@
     >>> link.click()
     >>> owner_browser.getControl('Unlink').click()
 
-Then he returns to delete trunk. He is informed that deletion is permanent,
-and that the milestones, releases, and files will also be deleted. The
-milestones and releases are linked.
+Then they return to delete trunk. They are informed that deletion is
+permanent, and that the milestones, releases, and files will also be
+deleted. The milestones and releases are linked.
 
     >>> owner_browser.getLink('trunk series').click()
     >>> owner_browser.getLink('Delete series').click()
@@ -81,7 +81,7 @@
     ... find_tag_by_id(contents, 'bugtasks-and-blueprints'))
     Support E4X in EcmaScript
 
-The owner deletes the series and the project page is displayed. He reads
+The owner deletes the series and the project page is displayed. They read
 that the series was deleted, and can not see a link to it anymore.
 
     >>> owner_browser.getControl('Delete this Series').click()
@@ -97,7 +97,7 @@
     LinkNotFoundError
 
 A series with translations can never be deleted. The project owner or
-release manager sees the explaination when he tries to delete the series.
+release manager sees the explanation when they try to delete the series.
 
     >>> owner_browser.open('http://launchpad.dev/evolution/trunk')
     >>> owner_browser.getLink('Delete series').click()

=== modified file 'lib/lp/registry/stories/productseries/xx-productseries-driver.txt'
--- lib/lp/registry/stories/productseries/xx-productseries-driver.txt	2009-09-18 15:24:30 +0000
+++ lib/lp/registry/stories/productseries/xx-productseries-driver.txt	2016-01-26 15:58:00 +0000
@@ -1,8 +1,8 @@
 Appointing a Product Series Driver (release manager)
 ====================================================
 
-Sample Person is the driver of Firefox 1.0 series because he is also
-the project owner. He can delegate the responsibility of release manager
+Sample Person is the driver of Firefox 1.0 series because they are also
+the project owner. They can delegate the responsibility of release manager
 by appointing someone else as the driver.
 
     >>> browser.addHeader('Authorization', 'Basic test@xxxxxxxxxxxxx:test')

=== modified file 'lib/lp/registry/stories/productseries/xx-productseries-index.txt'
--- lib/lp/registry/stories/productseries/xx-productseries-index.txt	2012-12-11 05:41:50 +0000
+++ lib/lp/registry/stories/productseries/xx-productseries-index.txt	2016-01-26 15:58:00 +0000
@@ -99,8 +99,8 @@
     >>> print extract_text(find_tag_by_id(content, 'branch-details'))
     No revision control details recorded for Mozilla Firefox trunk series.
 
-The driver sees that he can link a branch to this series, and there is
-an explanation where he can push the branch.
+The driver sees that they can link a branch to this series, and there is
+an explanation where they can push the branch.
 
     >>> print extract_text(find_tag_by_id(driver_content, 'branch-details'))
     You haven't yet told Launchpad where your source code is ...

=== modified file 'lib/lp/registry/stories/productseries/xx-productseries-set-branch.txt'
--- lib/lp/registry/stories/productseries/xx-productseries-set-branch.txt	2013-09-27 04:13:23 +0000
+++ lib/lp/registry/stories/productseries/xx-productseries-set-branch.txt	2016-01-26 15:58:00 +0000
@@ -94,7 +94,7 @@
     >>> print browser.url
     http://launchpad.dev/firefox/trunk
 
-The branch owner can be the logged in user or one of her teams.
+The branch owner can be the logged in user or one of their teams.
 
     >>> browser.open('http://launchpad.dev/firefox/trunk/+setbranch')
     >>> browser.getControl('Import a branch hosted somewhere else').click()

=== modified file 'lib/lp/registry/stories/project/xx-project-driver.txt'
--- lib/lp/registry/stories/project/xx-project-driver.txt	2011-12-13 23:06:54 +0000
+++ lib/lp/registry/stories/project/xx-project-driver.txt	2016-01-26 15:58:00 +0000
@@ -12,7 +12,7 @@
     >>> browser.getControl('Change').click()
 
 After changing the driver, the user is redirected to the overview page where a
-message inform him of the driver change.
+message informs them of the driver change.
 
     >>> browser.url
     'http://launchpad.dev/gnome'

=== modified file 'lib/lp/registry/stories/project/xx-reassign-project.txt'
--- lib/lp/registry/stories/project/xx-reassign-project.txt	2013-04-11 01:27:33 +0000
+++ lib/lp/registry/stories/project/xx-reassign-project.txt	2016-01-26 15:58:00 +0000
@@ -1,7 +1,7 @@
   Change the owner of the mozilla project.
 
 
-  Logged in as no-priv@xxxxxxxxxxxxx we can't do that, because he's not the
+  Logged in as no-priv@xxxxxxxxxxxxx we can't do that, because they're not the
   owner of the project nor a member of admins.
 
   >>> print http(r"""

=== modified file 'lib/lp/registry/stories/team-polls/vote-poll.txt'
--- lib/lp/registry/stories/team-polls/vote-poll.txt	2013-09-27 04:13:23 +0000
+++ lib/lp/registry/stories/team-polls/vote-poll.txt	2016-01-26 15:58:00 +0000
@@ -1,8 +1,8 @@
 = Voting on polls =
 
 Foo Bar (a member of the ubuntu-team) wants to vote on the 'never-closes'
-poll, which is a poll with secret votes, which means he'll get a token that he
-must use to see/change his vote afterwards.
+poll, which is a poll with secret votes, which means they'll get a token
+that they must use to see/change their vote afterwards.
 
     >>> browser = setupBrowser(auth='Basic foo.bar@xxxxxxxxxxxxx:test')
     >>> browser.open('http://launchpad.dev/~ubuntu-team/+polls')

=== modified file 'lib/lp/registry/stories/team-polls/xx-poll-condorcet-voting.txt'
--- lib/lp/registry/stories/team-polls/xx-poll-condorcet-voting.txt	2013-04-11 01:27:33 +0000
+++ lib/lp/registry/stories/team-polls/xx-poll-condorcet-voting.txt	2016-01-26 15:58:00 +0000
@@ -29,7 +29,7 @@
 
 
   If a non-member (Sample Person) guesses the voting URL and tries to vote,
-  he won't be allowed.
+  they won't be allowed.
 
   >>> print http(r"""
   ... GET /~ubuntu-team/+poll/never-closes2/+vote HTTP/1.1

=== modified file 'lib/lp/registry/stories/team/xx-team-claim.txt'
--- lib/lp/registry/stories/team/xx-team-claim.txt	2013-09-27 04:13:23 +0000
+++ lib/lp/registry/stories/team/xx-team-claim.txt	2016-01-26 15:58:00 +0000
@@ -70,7 +70,7 @@
     >>> user_browser.title
     'Claim Launchpad team'
 
-Here the user sees a form where he can enter a few more details about the
+Here the user sees a form where they can enter a few more details about the
 team and finish the claiming process, making the person who started the
 claim process the team's owner.
 

=== modified file 'lib/lp/registry/stories/team/xx-team-home.txt'
--- lib/lp/registry/stories/team/xx-team-home.txt	2014-02-26 13:34:52 +0000
+++ lib/lp/registry/stories/team/xx-team-home.txt	2016-01-26 15:58:00 +0000
@@ -110,8 +110,8 @@
     hidden
 
 In the above case there's no user logged in, so it doesn't actually show
-what's the user's involvement with the team. If the user logs in, he'll see
-that, though.
+what's the user's involvement with the team. If the user logs in, they'll
+see that, though.
 
     >>> print extract_text(
     ...     find_tag_by_id(browser.contents, 'your-involvement'))
@@ -216,7 +216,7 @@
     Join...
     You are not a member of this team...
 
-He can see the contact address, and the link explains the email
+They can see the contact address, and the link explains the email
 will actually go to the team's administrators.
 
     >>> print extract_text(find_tag_by_id(

=== modified file 'lib/lp/registry/stories/team/xx-team-membership.txt'
--- lib/lp/registry/stories/team/xx-team-membership.txt	2015-02-26 03:00:35 +0000
+++ lib/lp/registry/stories/team/xx-team-membership.txt	2016-01-26 15:58:00 +0000
@@ -140,7 +140,7 @@
     'Approved'
     >>> logout()
 
-Sample person had his membership declined to the guadamen group. Test
+Sample person had their membership declined to the guadamen group. Test
 that the page works and that foo.bar@canonical can access it.
 
     >>> browser.open('http://launchpad.dev/~guadamen/+member/name12/')

=== modified file 'lib/lp/registry/stories/teammembership/xx-member-renewed-membership.txt'
--- lib/lp/registry/stories/teammembership/xx-member-renewed-membership.txt	2014-11-27 07:48:25 +0000
+++ lib/lp/registry/stories/teammembership/xx-member-renewed-membership.txt	2016-01-26 15:58:00 +0000
@@ -1,7 +1,7 @@
 = Renewing your own memberships =
 
 Depending on a team's renewal policy, a team member may be allowed to
-renew his own membership. That is allowed for teams which have ONDEMAND
+renew their own membership. That is allowed for teams which have ONDEMAND
 as the renewal policy and for active memberships which are about to
 expire. Also note that this can only be done by the members themselves or
 by an administrator of a member team.

=== modified file 'lib/lp/registry/stories/teammembership/xx-team-leave.txt'
--- lib/lp/registry/stories/teammembership/xx-team-leave.txt	2014-02-26 13:34:52 +0000
+++ lib/lp/registry/stories/teammembership/xx-team-leave.txt	2016-01-26 15:58:00 +0000
@@ -1,7 +1,7 @@
 == Leaving the team ==
 
-Foo Bar decided to leave the 'ubuntu-team'. He visits the home page
-and chooses the "Leave" link. He confirm that wants to leave the team
+Foo Bar decided to leave the 'ubuntu-team'. They visit the home page
+and chooses the "Leave" link. They confirm that they want to leave the team
 by choosing the 'Leave' button.
 
     >>> admin_browser.open('http://launchpad.dev/~ubuntu-team')
@@ -16,7 +16,7 @@
     <p>Are you sure you want to leave this team?</p>
     >>> admin_browser.getControl('Leave').click()
 
-Foo Bar is redirect to the team page after he leaves.
+Foo Bar is redirected to the team page after they leave.
 
     >>> admin_browser.title
     'Ubuntu Team in Launchpad'

=== modified file 'lib/lp/registry/stories/teammembership/xx-teammembership.txt'
--- lib/lp/registry/stories/teammembership/xx-teammembership.txt	2012-08-15 22:01:39 +0000
+++ lib/lp/registry/stories/teammembership/xx-teammembership.txt	2016-01-26 15:58:00 +0000
@@ -25,7 +25,7 @@
     >>> browser.title
     'your own team in Launchpad'
 
-The owner of a team is always added as an administrator of his team.
+The owner of a team is always added as an administrator of their team.
 
     >>> from lp.registry.model.person import Person
     >>> [a.name for a in Person.byName('myemail').adminmembers]
@@ -106,14 +106,14 @@
     approve your membership before you actually become a member.
     ...
 
-If the user changes his mind because this is a moderated team, he can hit
-the 'Cancel' button, going back to the team's page...
+If the user changes their mind because this is a moderated team, they can
+hit the 'Cancel' button, going back to the team's page...
 
     >>> browser.getLink('Cancel').click()
     >>> browser.url
     'http://launchpad.dev/~myemail'
 
-...and then do everything again, if he really wants to join.
+...and then do everything again, if they really want to join.
 
     >>> browser.getLink('Join the team').click()
     >>> browser.getControl(name='field.actions.join').click()
@@ -144,7 +144,7 @@
     ...
     LinkNotFoundError
 
-If the user manually craft the URL to the +join page, he'll only see a
+If the user manually crafts the URL to the +join page, they'll only see a
 message explaining that this is a restricted team.
 
     >>> browser.open('http://launchpad.dev/~myemail/+join')
@@ -157,7 +157,7 @@
     Only a team administrator can add new members.
 
 But we provide a 'Back' button to take the user back to the team's
-home page, since he can't join it.
+home page, since they can't join it.
 
     >>> browser.getLink('Back').click()
     >>> browser.url

=== modified file 'lib/lp/registry/stories/vouchers/xx-voucher-redemption.txt'
--- lib/lp/registry/stories/vouchers/xx-voucher-redemption.txt	2013-09-27 04:13:23 +0000
+++ lib/lp/registry/stories/vouchers/xx-voucher-redemption.txt	2016-01-26 15:58:00 +0000
@@ -27,7 +27,7 @@
     Purchase and Redeem Commercial Subscription Vouchers
     ...Select the project you wish to subscribe...
 
-A user can access her voucher page but not someone else's.  Here
+A user can access their voucher page but not someone else's.  Here
 no-priv tries to access '+vouchers' on another user and is not
 allowed.
 
@@ -37,7 +37,7 @@
     ...
     Unauthorized: ...
 
-Unless, of course, that user is an administrator.  Then he can access
+Unless, of course, that user is an administrator.  Then they can access
 the '+vouchers' page on any user.
 
     >>> browser = setupBrowser(auth='Basic foo.bar@xxxxxxxxxxxxx:test')
@@ -88,8 +88,8 @@
     There is 1 error.
     Invalid value
 
-If, however, a person is a member of the Launchpad Commercial team he
-can apply a voucher to any project, not just those he administers.
+If, however, a person is a member of the Launchpad Commercial team they
+can apply a voucher to any project, not just those they administer.
 This ability is provided so that commercial admins in Launchpad can
 help projects with voucher issues, which is especially useful when
 setting up Canonical-internal projects.
@@ -98,7 +98,7 @@
     ...     auth='Basic commercial-member@xxxxxxxxxxxxx:test')
 
 Show that the commercial member does not own or drive any projects by
-viewing his +related-projects page.
+viewing their +related-projects page.
 
     >>> login('foo.bar@xxxxxxxxxxxxx')
     >>> from zope.component import getUtility
@@ -119,8 +119,8 @@
     ...
     Commercial Member doesn't own or drive any projects.
 
-He can still redeem a voucher against a commercial project even though
-he is not responsible for it.
+They can still redeem a voucher against a commercial project even though
+they are not responsible for it.
 
     >>> browser.open('http://launchpad.dev/~commercial-member/+vouchers')
     >>> browser.getControl(name='field.project').value='mega-money-maker'

=== modified file 'lib/lp/registry/stories/webservice/xx-person.txt'
--- lib/lp/registry/stories/webservice/xx-person.txt	2014-07-24 09:37:03 +0000
+++ lib/lp/registry/stories/webservice/xx-person.txt	2016-01-26 15:58:00 +0000
@@ -786,8 +786,8 @@
     >>> webservice.get("/~ubuntu-team/+member/name20").jsonBody()['status']
     u'Invitation declined'
 
-The retractTeamMembership method allows a team admin to remove his team from
-another team.
+The retractTeamMembership method allows a team admin to remove their team
+from another team.
 
     >>> print webservice.named_post(
     ...     landscape_developers['self_link'], 'retractTeamMembership',

=== modified file 'lib/lp/registry/templates/person-review.pt'
--- lib/lp/registry/templates/person-review.pt	2015-01-07 00:35:53 +0000
+++ lib/lp/registry/templates/person-review.pt	2016-01-26 15:58:00 +0000
@@ -21,7 +21,7 @@
         <div metal:fill-slot="extra_info">
           <tal:review-person tal:condition="view/is_viewing_person">
             <p>
-              Changing a user's name will change his OpenID identifier and
+              Changing a user's name will change their OpenID identifier and
               may cause problems with relying parties. PPA and mailing lists
               will be broken too.
             </p>

=== modified file 'lib/lp/registry/templates/team-mailinglist-moderate.pt'
--- lib/lp/registry/templates/team-mailinglist-moderate.pt	2012-02-10 10:02:34 +0000
+++ lib/lp/registry/templates/team-mailinglist-moderate.pt	2016-01-26 15:58:00 +0000
@@ -26,7 +26,7 @@
 
         <ul>
           <li><strong>Approve</strong> - Accept the message, post it to the
-          mailing list, and credit the original author for his or her on-topic
+          mailing list, and credit the original author for their on-topic
           posting.</li>
           <li><strong>Decline</strong> - Reject the message, sending a bounce
           message back to the original author and giving no credit for the

=== modified file 'lib/lp/registry/tests/test_karmacache_updater.py'
--- lib/lp/registry/tests/test_karmacache_updater.py	2013-05-02 00:40:14 +0000
+++ lib/lp/registry/tests/test_karmacache_updater.py	2016-01-26 15:58:00 +0000
@@ -53,9 +53,9 @@
     # it's run.
     def test_karmacache_entries(self):
         # Sample Person has some KarmaCache entries, but it's a long time
-        # since we last updated this cache, and now the karma he earned a long
-        # ago is not worth anything, so the karmacache-updater script will
-        # delete the cache entries for Sample Person.
+        # since we last updated this cache, and now the karma they earned a
+        # long ago is not worth anything, so the karmacache-updater script
+        # will delete the cache entries for Sample Person.
         sample_person = self.personset.getByName('name12')
         cache_entries = self._getCacheEntriesByPerson(sample_person)
         self.failUnless(not cache_entries.is_empty())
@@ -63,7 +63,7 @@
             self.failIf(cache.karmavalue <= 0)
 
         # As we can see, Foo Bar already has some karmacache entries. We'll
-        # now add some fresh Karma entries for him and later we'll check that
+        # now add some fresh Karma entries for them and later we'll check that
         # the cache-updater script simply updated the existing cache entries
         # instead of creating new ones.
         foobar = self.personset.getByName('name16')
@@ -75,9 +75,9 @@
         firefox = getUtility(IProductSet)['firefox']
         foobar.assignKarma('bugcreated', firefox)
 
-        # In the case of No Priv, he has no KarmaCache entries, so if we add
-        # some fresh Karma entries to him, our cache-updater script will have
-        # to create new KarmaCache entries for him.
+        # In the case of No Priv, they have no KarmaCache entries, so if we
+        # add some fresh Karma entries to them, our cache-updater script
+        # will have to create new KarmaCache entries for them.
         nopriv = self.personset.getByName('no-priv')
         self.failUnless(self._getCacheEntriesByPerson(nopriv).count() == 0)
         nopriv.assignKarma('bugcreated', firefox)
@@ -94,7 +94,7 @@
         self.failUnless(
             self._getCacheEntriesByPerson(sample_person).count() == 0)
 
-        # Check that Foo Bar had his KarmaCache entries updated.
+        # Check that Foo Bar had their KarmaCache entries updated.
         entries_count = self._getCacheEntriesByPerson(foobar).count()
         # The cache entries that would have their karmavalue updated to 0 are
         # instead deleted from the DB; that's why the new count can be smaller

=== modified file 'lib/lp/registry/tests/test_person.py'
--- lib/lp/registry/tests/test_person.py	2015-10-01 17:32:41 +0000
+++ lib/lp/registry/tests/test_person.py	2016-01-26 15:58:00 +0000
@@ -810,7 +810,7 @@
         self.assertEquals([expected_error], user.canDeactivate())
 
     def test_deactivate_copes_with_names_already_in_use(self):
-        """When a user deactivates his account, its name is changed.
+        """When a user deactivates their account, their name is changed.
 
         We do that so that other users can use that name, which the original
         user doesn't seem to want anymore.
@@ -826,8 +826,9 @@
         # Now that name12 is free Foo Bar can use it.
         foo_bar = Person.byName('name16')
         foo_bar.name = 'name12'
-        # If Foo Bar deactivates his account, though, we'll have to use a name
-        # other than name12-deactivatedaccount because that is already in use.
+        # If Foo Bar deactivates their account, though, we'll have to use a
+        # name other than name12-deactivatedaccount because that is already
+        # in use.
         login(foo_bar.preferredemail.email)
         foo_bar.deactivate(comment="blah!")
         self.failUnlessEqual(foo_bar.name, 'name12-deactivatedaccount1')

=== modified file 'lib/lp/registry/tests/test_private_team_visibility.py'
--- lib/lp/registry/tests/test_private_team_visibility.py	2012-09-18 19:41:02 +0000
+++ lib/lp/registry/tests/test_private_team_visibility.py	2016-01-26 15:58:00 +0000
@@ -152,8 +152,8 @@
         self.assertFalse(pub_team in self.priv_team.activemembers)
         self.assertFalse(pub_owner in self.priv_team.activemembers)
 
-        # The public team's owner can now see the priv-team's bits since his
-        # team has been invited to join.
+        # The public team's owner can now see the priv-team's bits since
+        # their team has been invited to join.
         login_person(pub_owner)
         self.assertEqual('priv-team', self.priv_team.name)
 

=== modified file 'lib/lp/registry/tests/test_sourcepackage.py'
--- lib/lp/registry/tests/test_sourcepackage.py	2013-05-29 09:05:54 +0000
+++ lib/lp/registry/tests/test_sourcepackage.py	2016-01-26 15:58:00 +0000
@@ -342,7 +342,7 @@
                 sp.setPackaging(series, owner)
 
     def test_setPackagingReturnSharingDetailPermissions__ordinary_user(self):
-        """An ordinary user can create a packaging link but he cannot
+        """An ordinary user can create a packaging link but they cannot
         set the series' branch or translation syncronisation settings,
         or the translation usage settings of the product.
         """
@@ -404,7 +404,7 @@
             owner=self.factory.makePerson())
 
     def test_getSharingDetailPermissions__product_owner(self):
-        """A product owner can create a packaging link, and he can set the
+        """A product owner can create a packaging link, and they can set the
         series' branch and the translation syncronisation settings, and the
         translation usage settings of the product.
         """

=== modified file 'lib/lp/registry/tests/test_team.py'
--- lib/lp/registry/tests/test_team.py	2015-09-22 15:04:52 +0000
+++ lib/lp/registry/tests/test_team.py	2016-01-26 15:58:00 +0000
@@ -349,8 +349,8 @@
 
     def test_closed_team_with_closed_super_team_cannot_become_open(self):
         # The team cannot compromise the membership of the super team
-        # by becoming open. The user must remove his team from the super team
-        # first.
+        # by becoming open. The user must remove their team from the super
+        # team first.
         self.setUpTeams()
         self.other_team.addMember(self.team, self.team.teamowner)
         self.assertFalse(

=== modified file 'lib/lp/registry/tests/test_teammembership.py'
--- lib/lp/registry/tests/test_teammembership.py	2015-09-14 14:12:21 +0000
+++ lib/lp/registry/tests/test_teammembership.py	2016-01-26 15:58:00 +0000
@@ -1256,7 +1256,7 @@
         """The script reports spurious participants of people.
 
         Teams can have multiple participants, but only the person should be a
-        paricipant of him/herself.
+        paricipant of themselves.
         """
         cursor().execute(self.script_create_inconsistent_participation)
         transaction.commit()

=== modified file 'lib/lp/registry/vocabularies.py'
--- lib/lp/registry/vocabularies.py	2015-10-13 13:22:08 +0000
+++ lib/lp/registry/vocabularies.py	2016-01-26 15:58:00 +0000
@@ -509,10 +509,11 @@
         BasePersonVocabulary, SQLObjectVocabularyBase):
     """The set of valid, viewable Persons/Teams in Launchpad.
 
-    A Person is considered valid if she has a preferred email address, and
+    A Person is considered valid if they have a preferred email address, and
     Person.merged is None. Teams have no restrictions at all, which means that
     all teams the user has the permission to view are considered valid.  A
-    user can view private teams in which she is a member and any public team.
+    user can view private teams in which they are a member and any public
+    team.
 
     This vocabulary is registered as ValidPersonOrTeam, ValidAssignee,
     ValidMaintainer and ValidOwner, because they have exactly the same
@@ -1072,7 +1073,7 @@
 
 class AllUserTeamsParticipationPlusSelfVocabulary(
     UserTeamsParticipationPlusSelfVocabulary):
-    """All public and private teams participates in and himself.
+    """All public and private teams participated in and themselves.
 
     This redefines UserTeamsParticipationVocabulary to include private teams
     and it includes the logged in user from

=== modified file 'lib/lp/scripts/utilities/js/tests/test_combo.py'
--- lib/lp/scripts/utilities/js/tests/test_combo.py	2015-12-01 12:27:09 +0000
+++ lib/lp/scripts/utilities/js/tests/test_combo.py	2016-01-26 15:58:00 +0000
@@ -467,7 +467,7 @@
             expected)
 
     def test_no_parent_hack(self):
-        """If someone tries to hack going up the root, he'll get a miss."""
+        """If someone tries to hack going up the root, they'll get a miss."""
         test_dir = self.makeDir()
         self.makeSampleFile(
             test_dir,
@@ -503,7 +503,7 @@
             expected)
 
     def test_no_absolute_path_hack(self):
-        """If someone tries to fetch an absolute file, he'll get nothing."""
+        """If someone tries to fetch an absolute file, they'll get nothing."""
         test_dir = self.makeDir()
 
         hack = "/etc/passwd"

=== modified file 'lib/lp/security.py'
--- lib/lp/security.py	2016-01-06 12:24:47 +0000
+++ lib/lp/security.py	2016-01-26 15:58:00 +0000
@@ -885,7 +885,7 @@
     usedfor = IPerson
 
     def checkAuthenticated(self, user):
-        """A user can edit the Person who is herself.
+        """A user can edit the Person who is themselves.
 
         The admin team can also edit any Person.
         """
@@ -920,7 +920,7 @@
     usedfor = IPerson
 
     def checkAuthenticated(self, user):
-        """A user can edit the Person who is herself."""
+        """A user can edit the Person who is themselves."""
         return self.obj.id == user.person.id
 
 
@@ -2885,8 +2885,8 @@
     def checkAuthenticated(self, user):
         """Can the user see the details of this email address?
 
-        If the email address' owner doesn't want his email addresses to be
-        hidden, anyone can see them.  Otherwise only the owner himself or
+        If the email address' owner doesn't want their email addresses to be
+        hidden, anyone can see them.  Otherwise only the owner themselves or
         admins can see them.
         """
         # Always allow users to see their own email addresses.

=== modified file 'lib/lp/services/database/doc/textsearching.txt'
--- lib/lp/services/database/doc/textsearching.txt	2012-07-27 14:39:14 +0000
+++ lib/lp/services/database/doc/textsearching.txt	2016-01-26 15:58:00 +0000
@@ -636,7 +636,7 @@
 want more fuzzy searches.
 
 For example, the KDE bug tracker has a guided bug submission form where
-the user first enters the summary of his problem. A list of similar
+the user first enters the summary of their problem. A list of similar
 bug reports is then displayed. The key here is 'similar', we want bug
 reports that have some words in common with the summary and we want the
 ones that are the most similar listed first. We don't necessarily want

=== modified file 'lib/lp/services/geoip/doc/geoip.txt'
--- lib/lp/services/geoip/doc/geoip.txt	2011-12-24 17:49:30 +0000
+++ lib/lp/services/geoip/doc/geoip.txt	2016-01-26 15:58:00 +0000
@@ -1,7 +1,7 @@
 GeoIP
 =====
 
-GeoIP allows us to guess the location of a user based on his IP address.
+GeoIP allows us to guess the location of a user based on their IP address.
 Our IGeoIP utility provides a couple methods to get location information
 from a given IP address.
 

=== modified file 'lib/lp/services/gpg/interfaces.py'
--- lib/lp/services/gpg/interfaces.py	2012-12-26 01:12:37 +0000
+++ lib/lp/services/gpg/interfaces.py	2016-01-26 15:58:00 +0000
@@ -286,7 +286,7 @@
             signing the content.
         :param password: optional password to the key identified by
             key_fingerprint, the default value is '',
-        :param mode: optional he type of GPG signature to produce, the
+        :param mode: optional type of GPG signature to produce, the
             default mode is gpgme.SIG_MODE_CLEAR (clearsigned signatures)
 
         :return: The ASCII-armored signature for the content.

=== modified file 'lib/lp/services/identity/interfaces/account.py'
--- lib/lp/services/identity/interfaces/account.py	2015-03-10 11:38:21 +0000
+++ lib/lp/services/identity/interfaces/account.py	2016-01-26 15:58:00 +0000
@@ -144,21 +144,21 @@
         """)
 
     OWNER_CREATED_LAUNCHPAD = DBItem(8, """
-        Created by the owner himself, coming from Launchpad.
+        Created by the owner themselves, coming from Launchpad.
 
         Somebody was navigating through Launchpad and at some point decided to
         create an account.
         """)
 
     OWNER_CREATED_SHIPIT = DBItem(9, """
-        Created by the owner himself, coming from Shipit.
+        Created by the owner themselves, coming from Shipit.
 
         Somebody went to one of the shipit sites to request Ubuntu CDs and was
         directed to Launchpad to create an account.
         """)
 
     OWNER_CREATED_UBUNTU_WIKI = DBItem(10, """
-        Created by the owner himself, coming from the Ubuntu wiki.
+        Created by the owner themselves, coming from the Ubuntu wiki.
 
         Somebody went to the Ubuntu wiki and was directed to Launchpad to
         create an account.
@@ -168,18 +168,18 @@
         Created by a user to represent a person which does not use Launchpad.
 
         A user wanted to reference a person which is not a Launchpad user, so
-        he created this "placeholder" profile.
+        they created this "placeholder" profile.
         """)
 
     OWNER_CREATED_UBUNTU_SHOP = DBItem(12, """
-        Created by the owner himself, coming from the Ubuntu Shop.
+        Created by the owner themselves, coming from the Ubuntu Shop.
 
         Somebody went to the Ubuntu Shop and was directed to Launchpad to
         create an account.
         """)
 
     OWNER_CREATED_UNKNOWN_TRUSTROOT = DBItem(13, """
-        Created by the owner himself, coming from unknown OpenID consumer.
+        Created by the owner themselves, coming from unknown OpenID consumer.
 
         Somebody went to an OpenID consumer we don't know about and was
         directed to Launchpad to create an account.

=== modified file 'lib/lp/services/librarian/tests/test_libraryfilealias_with_parent.py'
--- lib/lp/services/librarian/tests/test_libraryfilealias_with_parent.py	2012-09-18 19:41:02 +0000
+++ lib/lp/services/librarian/tests/test_libraryfilealias_with_parent.py	2016-01-26 15:58:00 +0000
@@ -52,7 +52,7 @@
         login_person(other_person)
         self.assertRaises(
             Unauthorized, setattr, self.bug_attachment, 'title', 'whatever')
-        # ...he also can't change the LibraryFileAlias for this bug.
+        # ...they also can't change the LibraryFileAlias for this bug.
         self.assertRaises(
             Unauthorized, setattr, self.lfa_with_parent, 'restricted', True)
 

=== modified file 'lib/lp/services/mail/doc/notification-recipient-set.txt'
--- lib/lp/services/mail/doc/notification-recipient-set.txt	2012-12-26 01:32:19 +0000
+++ lib/lp/services/mail/doc/notification-recipient-set.txt	2016-01-26 15:58:00 +0000
@@ -177,8 +177,8 @@
     >>> [person.displayname for person in recipients]
     [u'Ubuntu Gnome Team']
 
-So Sample Person is not in the recipients list, even if his email will
-be notified for he's a member of Warty Security Team, itself a member of
+So Sample Person is not in the recipients list, even if their email will
+be notified for they're a member of Warty Security Team, itself a member of
 Ubuntu Gnome Team:
 
     >>> warty_security_team = person_set.getByName('name20')
@@ -200,7 +200,7 @@
     >>> 'test@xxxxxxxxxxxxx' in recipients
     True
 
-His email will have the same rationale than the team:
+Their email will have the same rationale than the team:
 
     >>> recipients.getReason(ubuntu_gnome_team)
     ('Notified because a member of the team', 'Team')

=== modified file 'lib/lp/services/mail/interfaces.py'
--- lib/lp/services/mail/interfaces.py	2015-03-13 19:05:50 +0000
+++ lib/lp/services/mail/interfaces.py	2016-01-26 15:58:00 +0000
@@ -174,7 +174,7 @@
 
         When the added person is a team without an email address, all its
         members emails will be added. If the person is already in the
-        recipients list, the reson for contacting him is not changed.
+        recipients list, the reason for contacting them is not changed.
 
         :param person: The `IPerson` or a sequence of `IPerson`
             that will be notified.

=== modified file 'lib/lp/services/mail/mailwrapper.py'
--- lib/lp/services/mail/mailwrapper.py	2009-11-05 19:59:52 +0000
+++ lib/lp/services/mail/mailwrapper.py	2016-01-26 15:58:00 +0000
@@ -79,7 +79,7 @@
                     wrapped_lines += self._text_wrapper.wrap(line)
             else:
                 # If the user has gone through the trouble of wrapping
-                # the lines, we shouldn't re-wrap them for him.
+                # the lines, we shouldn't re-wrap them for them.
                 wrapped_lines += (
                     [indentation + lines[0]] +
                     [self.indent + line for line in lines[1:]])

=== modified file 'lib/lp/services/mail/notification.py'
--- lib/lp/services/mail/notification.py	2015-03-13 19:05:50 +0000
+++ lib/lp/services/mail/notification.py	2016-01-26 15:58:00 +0000
@@ -32,7 +32,7 @@
                                     max_return_size=MAX_RETURN_MESSAGE_SIZE):
     """Send a mail about an error occurring while using the email interface.
 
-    Tells the user that an error was encountered while processing his
+    Tells the user that an error was encountered while processing their
     request and attaches the original email which caused the error to
     happen.  The original message will be truncated to
     max_return_size bytes.

=== modified file 'lib/lp/services/oauth/doc/oauth-pages.txt'
--- lib/lp/services/oauth/doc/oauth-pages.txt	2014-06-13 14:22:09 +0000
+++ lib/lp/services/oauth/doc/oauth-pages.txt	2016-01-26 15:58:00 +0000
@@ -5,7 +5,7 @@
 == +authorize-token ==
 
 This is the page where a user reviews (approving or declining) a
-consumer's request to access Launchpad on his behalf.
+consumer's request to access Launchpad on their behalf.
 
     # Define some things we're going to use throughout this test.
     >>> from zope.component import getMultiAdapter

=== modified file 'lib/lp/services/oauth/interfaces.py'
--- lib/lp/services/oauth/interfaces.py	2015-01-29 11:58:57 +0000
+++ lib/lp/services/oauth/interfaces.py	2016-01-26 15:58:00 +0000
@@ -215,7 +215,7 @@
     date_reviewed = Datetime(
         title=_('Date reviewed'), required=True, readonly=True,
         description=_('The date in which the user authorized (or not) the '
-                      'consumer to access his protected resources on '
+                      'consumer to access their protected resources on '
                       'Launchpad.'))
 
     is_reviewed = Bool(

=== modified file 'lib/lp/services/oauth/stories/authorize-token.txt'
--- lib/lp/services/oauth/stories/authorize-token.txt	2014-06-13 14:22:09 +0000
+++ lib/lp/services/oauth/stories/authorize-token.txt	2016-01-26 15:58:00 +0000
@@ -3,7 +3,7 @@
 
 Once the consumer gets a request token, it must send the user to
 Launchpad's +authorize-token page in order for the user to authenticate
-and authorize or not the consumer to act on his behalf.
+and authorize or not the consumer to act on their behalf.
 
     >>> def request_token_for(consumer):
     ...     """Helper method to create a request token."""
@@ -179,7 +179,7 @@
     [('READ_PRIVATE', 'Read Anything'),
      ('UNAUTHORIZED', 'No Access')]
 
-Once the user authorizes the application to access Launchpad on his
+Once the user authorizes the application to access Launchpad on their
 behalf, we issue a redirect to the given oauth_callback (if it was
 specified by the application).
 

=== modified file 'lib/lp/services/salesforce/doc/voucher.txt'
--- lib/lp/services/salesforce/doc/voucher.txt	2015-10-01 17:32:41 +0000
+++ lib/lp/services/salesforce/doc/voucher.txt	2016-01-26 15:58:00 +0000
@@ -156,7 +156,7 @@
     0
 
 Similarly the call to get all of a user's vouchers returns the empty
-list when she has none.
+list when they have none.
 
     >>> vouchers = voucher_proxy.getAllVouchers(foobar)
     >>> len(vouchers)

=== modified file 'lib/lp/services/verification/browser/logintoken.py'
--- lib/lp/services/verification/browser/logintoken.py	2015-10-01 10:25:19 +0000
+++ lib/lp/services/verification/browser/logintoken.py	2016-01-26 15:58:00 +0000
@@ -524,8 +524,8 @@
         - If the duplicate user has no other email addresses, does the merge.
 
         """
-        # The user proved that he has access to this email address of the
-        # dupe account, so we can assign it to him.
+        # The user proved that they have access to this email address of the
+        # dupe account, so we can assign it to them.
         requester = self.context.requester
         emailset = getUtility(IEmailAddressSet)
         email = removeSecurityProxy(emailset.getByEmail(self.context.email))

=== modified file 'lib/lp/services/verification/browser/tests/logintoken-views.txt'
--- lib/lp/services/verification/browser/tests/logintoken-views.txt	2011-12-24 17:49:30 +0000
+++ lib/lp/services/verification/browser/tests/logintoken-views.txt	2016-01-26 15:58:00 +0000
@@ -1,9 +1,9 @@
 = LoginToken pages =
 
 Users interact with login tokens for operations that require the user to prove
-he has access to a resource that is external to Launchpad. For example,
+they have access to a resource that is external to Launchpad. For example,
 claiming an email address or a OpenPGP key require the user to use the login
-token sent to him in an email.
+token sent to them in an email.
 
 
 == Validating GPG keys ==
@@ -13,7 +13,7 @@
 The last step in that workflow is the +validategpg page, which can
 be accessed by non-logged-in users. That page will attempt to access
 all email addresses of the requester but that may fail when the user
-is not logged in and the requester has chosen to hide his email
+is not logged in and the requester has chosen to hide their email
 addresses, so here we'll make sure that even non-logged-in users can
 perform the last step of the workflow for adding a GPG key.
 

=== modified file 'lib/lp/services/verification/doc/logintoken.txt'
--- lib/lp/services/verification/doc/logintoken.txt	2012-04-17 11:59:00 +0000
+++ lib/lp/services/verification/doc/logintoken.txt	2016-01-26 15:58:00 +0000
@@ -44,7 +44,7 @@
     >>> stub.test_emails = []
 
 The email does not have a precedence header because the user implicitly
-requested it to complete his task.
+requested it to complete their task.
 
     >>> import email
 
@@ -52,7 +52,7 @@
     >>> print msg['Precedence']
     None
 
-As the process is not yet finished, foobar will see this as one of his
+As the process is not yet finished, foobar will see this as one of their
 unconfirmed email addresses.
 
     >>> flush_database_updates()
@@ -70,7 +70,7 @@
     >>> foobar.unvalidatedemails
     [u'foo.bar2@xxxxxxxxxxxxx']
 
-Once foobar finished the process, confirming his new email address, we
+Once foobar finished the process, confirming their new email address, we
 mark the token as consumed.
 
     >>> token.date_consumed is not None

=== modified file 'lib/lp/services/verification/interfaces/authtoken.py'
--- lib/lp/services/verification/interfaces/authtoken.py	2013-01-07 02:40:55 +0000
+++ lib/lp/services/verification/interfaces/authtoken.py	2016-01-26 15:58:00 +0000
@@ -76,14 +76,14 @@
     VALIDATEGPG = DBItem(6, """
         Validate GPG key
 
-        A user has submited a new GPG key to his account and it need to
+        A user has submitted a new GPG key to their account and it needs to
         be validated.
         """)
 
     VALIDATESIGNONLYGPG = DBItem(7, """
         Validate a sign-only GPG key
 
-        A user has submitted a new sign-only GPG key to his account and it
+        A user has submitted a new sign-only GPG key to their account and it
         needs to be validated.
         """)
 
@@ -164,7 +164,7 @@
 
     redirection_url = Text(
         title=_('The URL to where we should redirect the user after '
-                'processing his request'),
+                'processing their request'),
         required=False,
         )
 

=== modified file 'lib/lp/services/verification/tests/test_logintoken.py'
--- lib/lp/services/verification/tests/test_logintoken.py	2012-01-01 02:58:52 +0000
+++ lib/lp/services/verification/tests/test_logintoken.py	2016-01-26 15:58:00 +0000
@@ -26,7 +26,7 @@
     layer = DatabaseFunctionalLayer
 
     def test_sendMergeRequestEmail(self):
-        # sendMergeRequestEmail() sends an email to the user informing him/her
+        # sendMergeRequestEmail() sends an email to the user informing them
         # of the request.
 
         user1 = self.factory.makePerson(name="requester")

=== modified file 'lib/lp/services/webapp/interfaces.py'
--- lib/lp/services/webapp/interfaces.py	2015-10-11 20:06:23 +0000
+++ lib/lp/services/webapp/interfaces.py	2016-01-26 15:58:00 +0000
@@ -496,12 +496,12 @@
 class IPlacelessAuthUtility(IAuthentication):
     """This is a marker interface for a utility that supplies the interface
     of the authentication service placelessly, with the addition of
-    a method to allow the acquisition of a principal using his
+    a method to allow the acquisition of a principal using their
     login name.
     """
 
     def getPrincipalByLogin(login):
-        """Return a principal based on his login name."""
+        """Return a principal based on their login name."""
 
 
 class IPlacelessLoginSource(IPrincipalSource):
@@ -511,7 +511,7 @@
     """
 
     def getPrincipalByLogin(login):
-        """Return a principal based on his login name."""
+        """Return a principal based on their login name."""
 
     def getPrincipals(name):
         """Not implemented.

=== modified file 'lib/lp/services/webapp/tests/test_preferredcharsets.txt'
--- lib/lp/services/webapp/tests/test_preferredcharsets.txt	2011-12-24 17:49:30 +0000
+++ lib/lp/services/webapp/tests/test_preferredcharsets.txt	2016-01-26 15:58:00 +0000
@@ -13,7 +13,7 @@
     >>> user_preferred.getPreferredCharsets()
     ['utf-8']
 
-Even if the user specifies that he doesn't want utf-8:
+Even if the user specifies that they don't want utf-8:
 
     >>> no_utf8_request = TestRequest(
     ...     environ={'HTTP_ACCEPT_CHARSET': 'iso8859-1'})

=== modified file 'lib/lp/services/webservice/doc/launchpadlib.txt'
--- lib/lp/services/webservice/doc/launchpadlib.txt	2011-12-28 17:03:06 +0000
+++ lib/lp/services/webservice/doc/launchpadlib.txt	2016-01-26 15:58:00 +0000
@@ -17,7 +17,7 @@
     ...
     HTTPError: HTTP Error 404: Not Found
 
-...and when he doesn't, create him.
+...and when they don't, create them.
 
     >>> browser.open('%s/people/+newteam' % root_url)
     >>> browser.getControl(name='field.name').value = 'stimpy'

=== modified file 'lib/lp/soyuz/browser/archive.py'
--- lib/lp/soyuz/browser/archive.py	2015-09-30 22:46:59 +0000
+++ lib/lp/soyuz/browser/archive.py	2016-01-26 15:58:00 +0000
@@ -1901,7 +1901,7 @@
 
         Reorder the fields in a way the make more sense to users and also
         present a checkbox for acknowledging the PPA-ToS if the user is
-        creating his first PPA.
+        creating their first PPA.
         """
         LaunchpadFormView.setUpFields(self)
 

=== modified file 'lib/lp/soyuz/browser/tests/archive-views.txt'
--- lib/lp/soyuz/browser/tests/archive-views.txt	2015-11-26 13:31:45 +0000
+++ lib/lp/soyuz/browser/tests/archive-views.txt	2016-01-26 15:58:00 +0000
@@ -708,8 +708,8 @@
     0
 
 When there is no dependencies the form focus is set to the
-'dependency_candidate' input field. Where the user can directly type
-the owner of the PPA he wants to mark as dependency.
+'dependency_candidate' input field, where the user can directly type
+the owner of the PPA they want to mark as dependency.
 
     >>> print view.focusedElementScript()
     <!--
@@ -1114,8 +1114,8 @@
     >>> view.can_copy_to_context_ppa
     True
 
-Lets exercise the properties. 'No Privileges Person' user has his own
-PPA, thus he can copy to it, but not to Celso's PPA.
+Lets exercise the properties. 'No Privileges Person' user has their own
+PPA, thus they can copy to it, but not to Celso's PPA.
 
     >>> login('no-priv@xxxxxxxxxxxxx')
     >>> view = create_initialized_view(
@@ -1138,7 +1138,7 @@
     >>> cprov.archive.newComponentUploader(no_priv, "main")
     <ArchivePermission ...>
 
-He becomes able to copy to the context PPA.
+They become able to copy to the context PPA.
 
     >>> login('no-priv@xxxxxxxxxxxxx')
     >>> view = create_initialized_view(
@@ -1174,7 +1174,7 @@
     >>> login('foo.bar@xxxxxxxxxxxxx')
     >>> no_priv.archive.enable()
 
-'Foo Bar' user has no PPA, so he cannot perform copies at all.
+'Foo Bar' user has no PPA, so they cannot perform copies at all.
 
     >>> view = create_initialized_view(
     ...     cprov.archive, name="+copy-packages")

=== modified file 'lib/lp/soyuz/browser/tests/test_build_views.py'
--- lib/lp/soyuz/browser/tests/test_build_views.py	2015-02-17 07:39:47 +0000
+++ lib/lp/soyuz/browser/tests/test_build_views.py	2016-01-26 15:58:00 +0000
@@ -150,9 +150,9 @@
         with person_logged_in(self.admin):
             self.assertTrue(build.can_be_retried)
         nopriv = getUtility(IPersonSet).getByName("no-priv")
-        # Mr no privileges can't retry
+        # A person with no privileges can't retry
         self.assertBuildViewRetryIsExpected(build, nopriv, False)
-        # But he can as a member of launchpad-buildd-admins
+        # But they can as a member of launchpad-buildd-admins
         buildd_admins = getUtility(IPersonSet).getByName(
             "launchpad-buildd-admins")
         with person_logged_in(self.admin):

=== modified file 'lib/lp/soyuz/doc/archive.txt'
--- lib/lp/soyuz/doc/archive.txt	2015-11-26 15:46:38 +0000
+++ lib/lp/soyuz/doc/archive.txt	2016-01-26 15:58:00 +0000
@@ -1190,7 +1190,7 @@
     >>> for ppa in archive_set.getPPAsForUser(indirect_uploader):
     ...     print ppa.displayname
 
-But if we make him part of the uploader_team he'll gain access:
+But if we make them part of the uploader_team they'll gain access:
 
     >>> ignored = uploader_team.addMember(
     ...     indirect_uploader, indirect_uploader)

=== modified file 'lib/lp/soyuz/doc/gina-multiple-arch.txt'
--- lib/lp/soyuz/doc/gina-multiple-arch.txt	2015-04-20 09:48:57 +0000
+++ lib/lp/soyuz/doc/gina-multiple-arch.txt	2016-01-26 15:58:00 +0000
@@ -171,7 +171,7 @@
 
 The bdftopcf package is in a bit of a fix. Its binary package is present
 in universe, but no source package is listed for it, and the actual
-package files are in main! Gina to the rescue: she finds them in the
+package files are in main! Gina to the rescue: it finds them in the
 right place, updates the component, and creates it with a semi-bogus
 DSC.
 

=== modified file 'lib/lp/soyuz/doc/gina.txt'
--- lib/lp/soyuz/doc/gina.txt	2015-04-21 10:03:15 +0000
+++ lib/lp/soyuz/doc/gina.txt	2016-01-26 15:58:00 +0000
@@ -121,7 +121,7 @@
     >>> path = os.path.join(os.getcwd(), relative_path)
     >>> os.symlink(path, '/tmp/gina_test_archive')
 
-And give her a spin:
+And give it a spin:
 
     >>> gina_proc = [sys.executable, 'scripts/gina.py', '-q',
     ...              'hoary', 'breezy']

=== modified file 'lib/lp/soyuz/doc/package-diff.txt'
--- lib/lp/soyuz/doc/package-diff.txt	2015-11-20 17:57:46 +0000
+++ lib/lp/soyuz/doc/package-diff.txt	2016-01-26 15:58:00 +0000
@@ -471,9 +471,9 @@
 version to the Foo Bar's PPA.
 
 Note that this is a legitimate use-case, let's say Foo Bar user
-suspects 'staging' will be affected by his new toolchain, already
-hosted in the PPA. Since he cannot copy the primary archive sources,
-he simply re-upload the source as it is in ubuntu to his PPA and check
+suspects 'staging' will be affected by their new toolchain, already
+hosted in the PPA. Since they cannot copy the primary archive sources,
+they simply re-upload the source as it is in ubuntu to their PPA and check
 if it builds correctly.
 
     >>> packager = FakePackager(

=== modified file 'lib/lp/soyuz/doc/soyuz-set-of-uploads.txt'
--- lib/lp/soyuz/doc/soyuz-set-of-uploads.txt	2015-10-01 10:25:19 +0000
+++ lib/lp/soyuz/doc/soyuz-set-of-uploads.txt	2016-01-26 15:58:00 +0000
@@ -56,7 +56,7 @@
 
 Before asking the system to process the upload, we must prepare the
 database and services to receive it. Since we're using
-'sample.person@xxxxxxxxxxxxx' as our Changed-By address and his
+'sample.person@xxxxxxxxxxxxx' as our Changed-By address and their
 key has signed all the relevant uploads in the suite of uploads we're
 using, this essentially boils down to ensuring that test keyserver and the
 librarian are running and making sure that the key is attached to the

=== modified file 'lib/lp/soyuz/interfaces/archive.py'
--- lib/lp/soyuz/interfaces/archive.py	2016-01-06 16:00:01 +0000
+++ lib/lp/soyuz/interfaces/archive.py	2016-01-26 15:58:00 +0000
@@ -980,7 +980,7 @@
         """The `ArchivePermission` records for the person's package sets.
 
         :param person: An `IPerson` for whom you want to find out which
-            package sets he has access to.
+            package sets they have access to.
 
         :return: `ArchivePermission` records for all the package sets that
             'person' is allowed to upload to.
@@ -1028,7 +1028,7 @@
         :param sourcepackagename: the source package name; can be
             either a string or a `ISourcePackageName`.
         :param person: An `IPerson` for whom you want to find out which
-            package sets he has access to.
+            package sets they have access to.
 
         :raises NoSuchSourcePackageName: if a source package with the
             given name could not be found.
@@ -1082,7 +1082,7 @@
         Return True if there exists a permission that combines
             * this archive
             * a package set that includes the given source package name
-            * the given person or a team he is a member of
+            * the given person or a team they are a member of
 
         If the source package name is included by *any* package set with
         an explicit permission then only such explicit permissions will
@@ -1091,7 +1091,7 @@
         :param sourcepackagename: the source package name; can be
             either a string or a `ISourcePackageName`.
         :param person: An `IPerson` for whom you want to find out which
-            package sets he has access to.
+            package sets they have access to.
         :param distroseries: The `IDistroSeries` for which to check
             permissions. If none is supplied then `currentseries` in
             the archive's distribution is assumed.

=== modified file 'lib/lp/soyuz/interfaces/archivepermission.py'
--- lib/lp/soyuz/interfaces/archivepermission.py	2014-07-04 13:03:43 +0000
+++ lib/lp/soyuz/interfaces/archivepermission.py	2016-01-26 15:58:00 +0000
@@ -201,7 +201,7 @@
 
         :param archive: The context `IArchive` for the permission check.
         :param person: An `IPerson` for whom you want to find out which
-            components he has access to.
+            components they have access to.
 
         :return: `ArchivePermission` records for all the components that
             'person' is allowed to upload to.
@@ -212,7 +212,7 @@
 
         :param archive: The context `IArchive` for the permission check.
         :param person: An `IPerson` for whom you want to find out which
-            packages he has access to.
+            packages they have access to.
 
         :return: `ArchivePermission` records for all the packages that
             'person' is allowed to upload to.
@@ -235,7 +235,7 @@
 
         :param archive: The archive the permission applies to.
         :param person: An `IPerson` for whom you want to find out which
-            package sets he has access to.
+            package sets they have access to.
 
         :return: `ArchivePermission` records for all the package sets that
             'person' is allowed to upload to.
@@ -254,7 +254,7 @@
         :param sourcepackagename: the source package name; can be
             either a string or a `ISourcePackageName`.
         :param person: An `IPerson` for whom you want to find out which
-            package sets he has access to.
+            package sets they have access to.
 
         :raises SourceNotFound: if a source package with the given
             name could not be found.
@@ -291,7 +291,7 @@
         Return True if there exists a permission that combines
             * the given `archive`
             * a package set that includes the given source package name
-            * the given person or a team he is a member of
+            * the given person or a team they are a member of
 
         If the source package name is included by *any* package set with
         an explicit permission then only such explicit permissions will
@@ -301,7 +301,7 @@
         :param sourcepackagename: the source package name; can be
             either a string or a `ISourcePackageName`.
         :param person: An `IPerson` for whom you want to find out which
-            package sets he has access to.
+            package sets they have access to.
         :param distroseries: The `IDistroSeries` for which to check
             permissions.
 
@@ -342,7 +342,7 @@
 
         :param archive: The context `IArchive` for the permission check.
         :param person: An `IPerson` for whom you want to find out which
-            pockets he has access to.
+            pockets they have access to.
 
         :return: `ArchivePermission` records for all the pockets that
             'person' is allowed to upload to.
@@ -365,7 +365,7 @@
         :param archive: The context `IArchive` for the permission check, or
             an iterable of `IArchive`s.
         :param person: An `IPerson` for whom you want to find out which
-            components he has access to.
+            components they have access to.
 
         :return: `ArchivePermission` records for all the components that
             'person' is allowed to administer the queue for.
@@ -388,7 +388,7 @@
         :param archive: The context `IArchive` for the permission check, or
             an iterable of `IArchive`s.
         :param person: An `IPerson` for whom you want to find out which
-            pockets he has access to.
+            pockets they have access to.
 
         :return: `ArchivePermission` records for all the pockets that
             'person' is allowed to administer the queue for.

=== modified file 'lib/lp/soyuz/model/queue.py'
--- lib/lp/soyuz/model/queue.py	2015-09-03 15:14:07 +0000
+++ lib/lp/soyuz/model/queue.py	2016-01-26 15:58:00 +0000
@@ -469,7 +469,7 @@
         sourcepackagename = self.sources[
             0].sourcepackagerelease.sourcepackagename
 
-        # The package creator always gets his karma.
+        # The package creator always gets their karma.
         changed_by.assignKarma(
             main_karma_action, distribution=distribution,
             sourcepackagename=sourcepackagename)
@@ -477,7 +477,7 @@
         if self.archive.is_ppa:
             return
 
-        # If a sponsor was involved, give him some too.
+        # If a sponsor was involved, give them some too.
         if uploader is not None and changed_by != uploader:
             uploader.assignKarma(
                 'sponsoruploadaccepted', distribution=distribution,

=== modified file 'lib/lp/soyuz/scripts/packagecopier.py'
--- lib/lp/soyuz/scripts/packagecopier.py	2015-10-05 18:48:27 +0000
+++ lib/lp/soyuz/scripts/packagecopier.py	2016-01-26 15:58:00 +0000
@@ -162,7 +162,7 @@
         # Bulk-load the data we'll need from each source publication.
         load_related(SourcePackageRelease, sources, ["sourcepackagereleaseID"])
 
-    # If there is a requester, check that he has upload permission into
+    # If there is a requester, check that they have upload permission into
     # the destination (archive, component, pocket). This check is done
     # here rather than in the security adapter because it requires more
     # info than is available in the security adapter.

=== modified file 'lib/lp/soyuz/stories/ppa/xx-copy-packages.txt'
--- lib/lp/soyuz/stories/ppa/xx-copy-packages.txt	2015-05-22 10:14:21 +0000
+++ lib/lp/soyuz/stories/ppa/xx-copy-packages.txt	2016-01-26 15:58:00 +0000
@@ -9,7 +9,7 @@
 
  * One or more source packages from the current PPA;
 
- * A destination PPA amongst the ones he has access to, including the
+ * A destination PPA amongst the ones they have access to, including the
    current one;
 
  * A destination series amongst all ubuntu series;

=== modified file 'lib/lp/soyuz/stories/ppa/xx-delete-packages.txt'
--- lib/lp/soyuz/stories/ppa/xx-delete-packages.txt	2014-11-27 22:13:36 +0000
+++ lib/lp/soyuz/stories/ppa/xx-delete-packages.txt	2016-01-26 15:58:00 +0000
@@ -426,7 +426,7 @@
     >>> print_ppa_packages(user_browser.contents)
     No matching package for ''.
 
-When he selects 'Superseded' the SUPERSEDED source shows up again.
+When they select 'Superseded' the SUPERSEDED source shows up again.
 
     >>> user_browser.getControl(
     ...     name='field.status_filter').value = ['superseded']
@@ -455,7 +455,7 @@
 
 After the deletion, any user accessing No-privileges' PPA page can see
 a row representing 'foo' and it is marked as 'superseded'. In its
-corresponding expandable area, he can see that the 'Built packages'
+corresponding expandable area, they can see that the 'Built packages'
 section is omitted, however the source and binary files can be
 downloaded from librarian.
 

=== modified file 'lib/lp/soyuz/stories/ppa/xx-edit-dependencies.txt'
--- lib/lp/soyuz/stories/ppa/xx-edit-dependencies.txt	2014-08-08 17:27:07 +0000
+++ lib/lp/soyuz/stories/ppa/xx-edit-dependencies.txt	2016-01-26 15:58:00 +0000
@@ -325,7 +325,7 @@
 The user can modify this aspect by selecting a different option and
 clicking on 'Save'.
 
-He will see a notification containing a summary of what was changed.
+They will see a notification containing a summary of what was changed.
 
     >>> admin_browser.getControl(
     ...     "Proposed (default dependencies and proposed updates"
@@ -373,7 +373,7 @@
 
 In order to make the PPA use the default dependencies again the user
 can simply select this pre-defined option and the form will restore
-the default dependencies for him.
+the default dependencies for them.
 
     >>> admin_browser.getControl(
     ...     "Default (security dependencies and recommended updates"

=== modified file 'lib/lp/soyuz/stories/ppa/xx-ppa-files.txt'
--- lib/lp/soyuz/stories/ppa/xx-ppa-files.txt	2015-10-05 06:34:17 +0000
+++ lib/lp/soyuz/stories/ppa/xx-ppa-files.txt	2016-01-26 15:58:00 +0000
@@ -139,7 +139,7 @@
     ...             print '%s: NOT OK (%s != %s)' % (
     ...                 libraryfile.filename, found_url, expected_url)
 
-No Privileges user can access the files related with his PPA and its
+No Privileges user can access the files related with their PPA and its
 builds.
 
     >>> no_priv_browser = setupBrowser(

=== modified file 'lib/lp/soyuz/stories/ppa/xx-ppa-navigation.txt'
--- lib/lp/soyuz/stories/ppa/xx-ppa-navigation.txt	2014-11-27 07:48:25 +0000
+++ lib/lp/soyuz/stories/ppa/xx-ppa-navigation.txt	2016-01-26 15:58:00 +0000
@@ -135,7 +135,7 @@
 
     >>> anon_browser.open('http://launchpad.dev/~cprov/+archive/ubuntu/ppa?batch=1')
 
-Since he is on the first page, the 'First' and 'Previous' links are
+Since they are on the first page, the 'First' and 'Previous' links are
 inactive:
 
     >>> 'Previous' in anon_browser.contents
@@ -151,12 +151,12 @@
       ..
     LinkNotFoundError...
 
-The user does not see the package he is looking for and proceeds to the
+The user does not see the package they are looking for and proceeds to the
 next page.
 
     >>> anon_browser.getLink('Next').click()
 
-The package of interest is not on this page either so he goes to the
+The package of interest is not on this page either so they go to the
 next page.
 
     >>> anon_browser.getLink('Next').click()

=== modified file 'lib/lp/soyuz/stories/ppa/xx-ppa-workflow.txt'
--- lib/lp/soyuz/stories/ppa/xx-ppa-workflow.txt	2015-11-26 15:46:38 +0000
+++ lib/lp/soyuz/stories/ppa/xx-ppa-workflow.txt	2016-01-26 15:58:00 +0000
@@ -24,8 +24,8 @@
     >>> print anon_browser.title
     PPA for Celso Providelo : Celso Providelo
 
-On the other hand, Sample Person hasn't activated his PPA yet, so he
-can quickly activate one via the link in his PPA section.
+On the other hand, Sample Person hasn't activated their PPA yet, so they
+can quickly activate one via the link in their PPA section.
 
     >>> sample_browser = setupBrowser(auth="Basic test@xxxxxxxxxxxxx:test")
     >>> sample_browser.open("http://launchpad.dev/~name12";)
@@ -113,7 +113,7 @@
     >>> sample_browser.getControl("Save").click()
 
 After confirming the changes Sample Person is sent to the PPA page
-where he can see the updated information.
+where they can see the updated information.
 
     >>> print sample_browser.title
     Sample testing PPA : Sample Person
@@ -136,7 +136,7 @@
     None
 
 On the other hand, the PPA 'displayname' field is required. Sample
-user can't have an emtpy displayname on his PPA.
+user can't have an empty displayname on their PPA.
 
     >>> sample_browser.getLink("Change details").click()
     >>> sample_browser.getControl(name="field.displayname").value = ('')
@@ -397,7 +397,7 @@
     ...     ).value = "deb http://my.spethial.repo.com/ %(series)s main"
     >>> admin_browser.getControl("Save").click()
 
-Once confirmed the administrator is sent to the PPA page where he can
+Once confirmed the administrator is sent to the PPA page where they can
 see some of the updated information.
 
     >>> print admin_browser.title
@@ -542,7 +542,7 @@
 the usual PPA activation form where the checkbox for acknowledging the
 PPA-ToS is no longer present and a list of 'Existing PPAs' is presented.
 Launchpad requires a user to acknowledge the PPA-ToS only once for
-all his PPAs.
+all their PPAs.
 
     >>> cprov_browser.getLink("Create a new PPA").click()
 
@@ -775,17 +775,17 @@
     Are you sure ?
     ...
 
-If the user changes his mind, he can click on the cancel link to go back
+If the user changes their mind, they can click on the cancel link to go back
 a page:
 
     >>> print no_priv_browser.getLink("Cancel").url
     http://launchpad.dev/~no-priv/+archive/ubuntu/ppa
 
-Otherwise, he has a button to press to confirm the deletion.
+Otherwise, they have a button to press to confirm the deletion.
 
     >>> no_priv_browser.getControl("Permanently delete PPA").click()
 
-This action will redirect the user back to his profile page, which will
+This action will redirect the user back to their profile page, which will
 contain a notification message that the deletion is in progress.
 
     >>> print no_priv_browser.url

=== modified file 'lib/lp/soyuz/stories/ppa/xx-private-ppa-subscription-stories.txt'
--- lib/lp/soyuz/stories/ppa/xx-private-ppa-subscription-stories.txt	2014-07-24 09:37:03 +0000
+++ lib/lp/soyuz/stories/ppa/xx-private-ppa-subscription-stories.txt	2016-01-26 15:58:00 +0000
@@ -116,7 +116,7 @@
     from PPA named p3a for Celso Providelo. Members of Launchpad Developers
     will be notified of the access via email.
 
-== Story 2: An owner edits a subscription for his private archive ==
+== Story 2: An owner edits a subscription for their private archive ==
 
  * As a software developer who has released some private software
  * I want to edit subscriptions to my private PPA

=== modified file 'lib/lp/soyuz/stories/ppa/xx-private-ppas.txt'
--- lib/lp/soyuz/stories/ppa/xx-private-ppas.txt	2014-07-24 09:37:03 +0000
+++ lib/lp/soyuz/stories/ppa/xx-private-ppas.txt	2016-01-26 15:58:00 +0000
@@ -41,7 +41,7 @@
     PPA for Mark Shuttleworth...
     PPA for No Privileges Person...
 
-The owner of the archive will see his own archive in the listing:
+The owner of the archive will see their own archive in the listing:
 
     >>> cprov_browser = setupBrowser(
     ...     auth="Basic celso.providelo@xxxxxxxxxxxxx:test")
@@ -69,7 +69,7 @@
     >>> admin_browser.getControl(name="field.private").value = True
     >>> admin_browser.getControl("Save").click()
 
-A member of a private team PPA will see his team's PPA in the listing.
+A member of a private team PPA will see their team's PPA in the listing.
 "name12" is a member of landscape-developers, so is permitted to see
 the landscape-developers PPA:
 
@@ -125,7 +125,7 @@
     PPA named p3a for Celso Providelo : Celso Providelo
 
 When a non-privileged user browses to a profile page for a person or
-team that has a private PPA for which he is not authorised to see, the
+team that has a private PPA for which they are not authorised to see, the
 link to the PPA page is not present.
 
     >>> browser.open("http://launchpad.dev/~landscape-developers";)

=== modified file 'lib/lp/soyuz/stories/ppa/xx-ubuntu-ppas.txt'
--- lib/lp/soyuz/stories/ppa/xx-ubuntu-ppas.txt	2015-09-11 01:59:06 +0000
+++ lib/lp/soyuz/stories/ppa/xx-ubuntu-ppas.txt	2016-01-26 15:58:00 +0000
@@ -637,7 +637,7 @@
     >>> transaction.commit()
 
 Publications created, now when any user access the 'Ubuntu PPAs' page,
-he will be able to see 4 PPAs where we've added publications listed in
+they will be able to see 4 PPAs where we've added publications listed in
 the 'Most active' section.
 
     >>> anon_browser.open("http://launchpad.dev/ubuntu/+ppas";)

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-builds-pages.txt'
--- lib/lp/soyuz/stories/soyuz/xx-builds-pages.txt	2014-11-27 22:13:36 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-builds-pages.txt	2016-01-26 15:58:00 +0000
@@ -396,7 +396,7 @@
     >>> find_tag_by_id(anon_browser.contents, 'empty-result') is None
     True
 
-When he updates the page, the message changes to say that 'No matching
+When they update the page, the message changes to say that 'No matching
 builds' be found. This message clearly differentiate first page loads
 from searches, helping the anonymous user to figure out exactly what
 was done before.
@@ -443,7 +443,7 @@
     >>> find_tag_by_id(anon_browser.contents, 'empty-result') is None
     True
 
-When he updates the page, the message for empty results changes to 'No
+When they update the page, the message for empty results changes to 'No
 matching builds'.
 
     >>> anon_browser.getControl(name="build_state").value = ['all']

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-person-packages.txt'
--- lib/lp/soyuz/stories/soyuz/xx-person-packages.txt	2014-11-27 22:13:36 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-person-packages.txt	2016-01-26 15:58:00 +0000
@@ -256,7 +256,7 @@
     >>> print_ppa_rows(anon_browser)
     source1 PPA for Celso Providelo - Ubuntu Hoary 666 ...ago None
 
-However no-priv himself and any Launchpad Administrator can still see
+However no-priv themselves and any Launchpad Administrator can still see
 both packages:
 
     >>> nopriv_browser = setupBrowser(auth="Basic no-priv@xxxxxxxxxxxxx:test")

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-private-builds.txt'
--- lib/lp/soyuz/stories/soyuz/xx-private-builds.txt	2015-03-24 11:30:04 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-private-builds.txt	2016-01-26 15:58:00 +0000
@@ -44,7 +44,7 @@
     >>> private_build_id = private_build.id
 
 We'll also make name12 a member of the launchpad-buildd-admins team, so we
-can use him to see builds from a buildd-admin perspective.
+can use them to see builds from a buildd-admin perspective.
 
     >>> name12 = getUtility(IPersonSet).getByName("name12")
     >>> buildd_admins = getUtility(

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-queue-pages-motu.txt'
--- lib/lp/soyuz/stories/soyuz/xx-queue-pages-motu.txt	2013-09-27 04:13:23 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-queue-pages-motu.txt	2016-01-26 15:58:00 +0000
@@ -23,7 +23,7 @@
 
 In our sample data, there is a user 'no-team-memberships' who has
 rights to administer the queue in the universe and multiverse
-components only.  We'll set up a browser for him:
+components only.  We'll set up a browser for them:
 
     >>> motu_browser = setupBrowser(
     ...     auth="Basic no-team-memberships@xxxxxxxx:test")
@@ -97,11 +97,11 @@
     >>> logout()
 
 So now our user will be able to manipulate the alsa-utils source and
-the pmount binary.  However, he is still constrained with any component
-override that is applied; this must still be one of his permitted
+the pmount binary.  However, they are still constrained with any component
+override that is applied; this must still be one of their permitted
 components.
 
-If he tries to override back to main, it will fail:
+If they try to override back to main, it will fail:
 
     >>> motu_browser.getControl(name="QUEUE_ID").value = ['4']
     >>> motu_browser.getControl(
@@ -137,9 +137,9 @@
     >>> print_feedback_messages(motu_browser.contents)
     OK: alsa-utils
 
-In some cases the user might select more than one item at once, but he
-only has permission to change a subset of those items.  In this case,
-the items he has permission to change will be processed, but the others
+In some cases the user might select more than one item at once, but they
+only have permission to change a subset of those items.  In this case,
+the items they have permission to change will be processed, but the others
 will be left alone.
 
     >>> motu_browser.getControl(name="QUEUE_ID").value = ['1', '3']

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-queue-pages.txt'
--- lib/lp/soyuz/stories/soyuz/xx-queue-pages.txt	2015-09-03 15:14:07 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-queue-pages.txt	2016-01-26 15:58:00 +0000
@@ -479,7 +479,7 @@
 
     >>> upload_manager_browser.getControl(name="Accept").click()
 
-He sees the informational message that confirms the details of what was
+They see the informational message that confirms the details of what was
 overridden:
 
     >>> print_feedback_messages(upload_manager_browser.contents)

=== modified file 'lib/lp/soyuz/stories/webservice/xx-archive-commercial.txt'
--- lib/lp/soyuz/stories/webservice/xx-archive-commercial.txt	2014-07-24 09:37:03 +0000
+++ lib/lp/soyuz/stories/webservice/xx-archive-commercial.txt	2016-01-26 15:58:00 +0000
@@ -51,7 +51,7 @@
     This person does not have a valid subscription for the target archive
 
 In order to allow access for the test user, the agent has to subscribe
-him first.
+them first.
 
     >>> response = agent_webservice.named_post(cp3a['self_link'],
     ...   'newSubscription', subscriber=joe['self_link'])

=== modified file 'lib/lp/translations/browser/pofile.py'
--- lib/lp/translations/browser/pofile.py	2015-02-20 00:30:40 +0000
+++ lib/lp/translations/browser/pofile.py	2016-01-26 15:58:00 +0000
@@ -634,8 +634,8 @@
             id = int(match.group(1))
             potmsgset = self.context.potemplate.getPOTMsgSetByID(id)
             if potmsgset is None:
-                # This should only happen if someone tries to POST his own
-                # form instead of ours, and he uses a POTMsgSet id that
+                # This should only happen if someone tries to POST their own
+                # form instead of ours, and they use a POTMsgSet id that
                 # does not exist for this POTemplate.
                 raise UnexpectedFormData(
                     "Got translation for POTMsgID %d which is not in the "

=== modified file 'lib/lp/translations/browser/tests/pofile-views.txt'
--- lib/lp/translations/browser/tests/pofile-views.txt	2015-10-05 08:36:52 +0000
+++ lib/lp/translations/browser/tests/pofile-views.txt	2016-01-26 15:58:00 +0000
@@ -236,7 +236,7 @@
     >>> pofile_view.request.response.getHeader('Location')
     'http://127.0.0.1?memo=10&start=10'
 
-The messsage's sequence is the position of that message in latest imported
+The message's sequence is the position of that message in latest imported
 template. We are going to test now what happens when we submit a potmsgset
 that has a sequence == 0. It means that that msgset is disabled and we don't
 serve such messages in our translation form, but we could get it in some
@@ -245,7 +245,8 @@
  - A user gets a translation form for the template X.
  - A new template X is imported into the system that removes some messages
    from the previous import.
- - Previous user, submits the translation form he got for the old template X.
+ - Previous user, submits the translation form they got for the old template
+   X.
 
 The problem here is that some of the messages on that form are disabled so
 their sequence is 0.

=== modified file 'lib/lp/translations/doc/pofile.txt'
--- lib/lp/translations/doc/pofile.txt	2012-12-26 01:32:19 +0000
+++ lib/lp/translations/doc/pofile.txt	2016-01-26 15:58:00 +0000
@@ -625,7 +625,7 @@
     --
 
     # We can see that there is another translator that doesn't appear in
-    # previous list because the template he translated is not current.
+    # previous list because the template they translated is not current.
 
     >>> non_current_pofile = POFile.get(31)
     >>> non_current_pofile.potemplate.iscurrent

=== modified file 'lib/lp/translations/doc/rosetta-karma.txt'
--- lib/lp/translations/doc/rosetta-karma.txt	2014-02-19 04:01:46 +0000
+++ lib/lp/translations/doc/rosetta-karma.txt	2016-01-26 15:58:00 +0000
@@ -220,7 +220,7 @@
     >>> transaction.commit()
 
 Tell the PO file to import from the file data it has.  The user has rights
-to edit translations directly, so his suggestion is approved directly.
+to edit translations directly, so their suggestion is approved directly.
 No karma is awarded for this action.
 
     >>> potemplate = POTemplate.get(1)
@@ -292,15 +292,15 @@
     >>> fuzzy = False
     >>> by_maintainer = False
 
-And we can see as he won't get any karma activity from that, otherwise it'd be
-printed after the call to set current translation.
+And we can see as they won't get any karma activity from that, otherwise
+it'd be printed after the call to set current translation.
 
     >>> translationmessage = factory.makeCurrentTranslationMessage(
     ...     pofile, potmsgset, no_priv, translations=new_translations,
     ...     current_other=by_maintainer)
     >>> flush_database_caches()
 
-But now, he will provide a new suggestion.
+But now, they will provide a new suggestion.
 
     >>> new_translations = {0: u'somethingelse'}
 
@@ -320,9 +320,9 @@
 
     >>> kurem = personset.getByEmail('kurem@xxxxxxxxx')
 
-Now, he will approve a suggestion.  This will give him karma for reviewing the
-suggestion and will also give karma to the user who made the suggestion for it
-being approved.
+Now, they will approve a suggestion.  This will give them karma for
+reviewing the suggestion and will also give karma to the user who made the
+suggestion for it being approved.
 
     >>> potemplate = POTemplate.get(1)
     >>> pofile = potemplate.getPOFileByLang('es')
@@ -335,8 +335,8 @@
     Karma added: action=translationreview, product=evolution
     >>> transaction.commit()
 
-Finally, this reviewer, is going to add a new translation directly. He should
-get karma for his translation, but not for a review.
+Finally, this reviewer, is going to add a new translation directly. They
+should get karma for their translation, but not for a review.
 
     >>> kurem = personset.getByEmail('kurem@xxxxxxxxx')
     >>> potemplate = POTemplate.get(1)
@@ -357,8 +357,8 @@
 because they are giving more information to our users about the usage of
 that template.
 
-We are going to use Sample Person for this test as he's the owner of the
-product from where the IPOTemplate is and he has rights to change the
+We are going to use Sample Person for this test as they're the owner of the
+product from where the IPOTemplate is and they have rights to change the
 description.
 
     >>> sample_person = personset.getByEmail('test@xxxxxxxxxxxxx')

=== modified file 'lib/lp/translations/doc/translationbranchapprover.txt'
--- lib/lp/translations/doc/translationbranchapprover.txt	2010-03-17 18:44:32 +0000
+++ lib/lp/translations/doc/translationbranchapprover.txt	2016-01-26 15:58:00 +0000
@@ -84,8 +84,8 @@
     >>> print repr(entry.status)
     <DBItem RosettaImportStatus.APPROVED, (1) Approved>
 
-Now the project owner wants to use two translation domains in his project and
-thus creates another template file in the branch. The branch upload job
+Now the project owner wants to use two translation domains in their project
+and thus creates another template file in the branch. The branch upload job
 detects this new file on its next run and places it into the import queue
 (but not the first one which is left unchanged).
 
@@ -113,10 +113,10 @@
     <DBItem RosettaImportStatus.APPROVED, (1) Approved>
 
 
-Next the owner of the branch realizes that he needs to put his translation
+Next the owner of the branch realizes that they need to put their translation
 template files in proper subdirectories for multiple templates to work
-correctly. Also, he starts using a tool that calls the template 
-"messages.pot" consistently. So he moves and renames the files. The branch
+correctly. Also, they start using a tool that calls the template 
+"messages.pot" consistently. So they move and rename the files. The branch
 upload job detects two changed files and places them in the upload queue.
 
     >>> foo_entry = queue.addOrUpdateEntry("po/foo/messages.pot",

=== modified file 'lib/lp/translations/doc/translationgroup.txt'
--- lib/lp/translations/doc/translationgroup.txt	2011-12-24 15:18:32 +0000
+++ lib/lp/translations/doc/translationgroup.txt	2016-01-26 15:58:00 +0000
@@ -70,7 +70,7 @@
     >>> pofile.canEditTranslations(no_priv)
     False
 
-Let's add him to the group.
+Let's add them to the group.
 
     >>> welsh = language_set['cy']
 

=== modified file 'lib/lp/translations/interfaces/translationrelicensingagreement.py'
--- lib/lp/translations/interfaces/translationrelicensingagreement.py	2012-05-24 20:25:54 +0000
+++ lib/lp/translations/interfaces/translationrelicensingagreement.py	2016-01-26 15:58:00 +0000
@@ -43,7 +43,7 @@
         readonly=False, default=True, required=True)
 
     date_decided = Datetime(
-        title=_("The date person made her decision"),
+        title=_("The date person made their decision"),
         readonly=True, required=True)
 
 

=== modified file 'lib/lp/translations/interfaces/translator.py'
--- lib/lp/translations/interfaces/translator.py	2013-01-07 02:40:55 +0000
+++ lib/lp/translations/interfaces/translator.py	2016-01-26 15:58:00 +0000
@@ -25,7 +25,7 @@
 
 
 class IEditTranslator(Interface):
-    """Set of translator attributes that the translator can edit himself as
+    """Set of translator attributes that the translator can edit themselves as
     well as being editable by the translation group owner.
 
     Translators can edit the data in their `ITranslator` entry.

=== modified file 'lib/lp/translations/model/pofile.py'
--- lib/lp/translations/model/pofile.py	2015-09-28 17:38:45 +0000
+++ lib/lp/translations/model/pofile.py	2016-01-26 15:58:00 +0000
@@ -1676,7 +1676,7 @@
             email = self._pofile.lasttranslator.safe_email_or_blank
             if not email:
                 # We are supposed to have always a valid email address, but
-                # with removed accounts or people not wanting to show his
+                # with removed accounts or people not wanting to show their
                 # email that's not true anymore so we just set it to 'Unknown'
                 # to note we don't know it.
                 email = 'Unknown'

=== modified file 'lib/lp/translations/model/translationsperson.py'
--- lib/lp/translations/model/translationsperson.py	2015-10-14 16:23:18 +0000
+++ lib/lp/translations/model/translationsperson.py	2016-01-26 15:58:00 +0000
@@ -106,7 +106,7 @@
     def _translations_relicensing_agreement(self):
         """Return whether translator agrees to relicense their translations.
 
-        If she has made no explicit decision yet, return None.
+        If they have made no explicit decision yet, return None.
         """
         relicensing_agreement = TranslationRelicensingAgreement.selectOneBy(
             person=self.person)
@@ -121,7 +121,7 @@
     def set_translations_relicensing_agreement(self, value):
         """Set a translations relicensing decision by translator.
 
-        If she has already made a decision, overrides it with the new one.
+        If they have already made a decision, overrides it with the new one.
         """
         relicensing_agreement = TranslationRelicensingAgreement.selectOneBy(
             person=self.person)

=== modified file 'lib/lp/translations/stories/importqueue/xx-translation-import-queue-edit-autofilling.txt'
--- lib/lp/translations/stories/importqueue/xx-translation-import-queue-edit-autofilling.txt	2015-10-05 08:36:52 +0000
+++ lib/lp/translations/stories/importqueue/xx-translation-import-queue-edit-autofilling.txt	2016-01-26 15:58:00 +0000
@@ -65,7 +65,7 @@
   'alsa-utils'
 
 Let's move to the .po file. The language is guessed from the file name
-and the user sees a warning so he checks that it's ok.
+and the user sees a warning so they check that it's ok.
 
   >>> browser.open('http://translations.launchpad.dev/+imports/%d' % po_qid)
   >>> browser.getControl(name='field.file_type').value

=== modified file 'lib/lp/translations/stories/importqueue/xx-translation-import-queue.txt'
--- lib/lp/translations/stories/importqueue/xx-translation-import-queue.txt	2015-10-05 08:36:52 +0000
+++ lib/lp/translations/stories/importqueue/xx-translation-import-queue.txt	2016-01-26 15:58:00 +0000
@@ -250,7 +250,7 @@
   ...Foo Bar...
   ...Template "evolution-2.2-test" in Evolution trunk...
 
-Foo Bar Person is a launchpad admin and he's allowed to remove an entry.
+Foo Bar Person is a launchpad admin and they're allowed to remove an entry.
 
   >>> admin_browser.open('http://translations.launchpad.dev/+imports')
   >>> admin_browser.getControl(name='field.status_2').value = ['DELETED']

=== modified file 'lib/lp/translations/stories/productseries/xx-productseries-translations-settings.txt'
--- lib/lp/translations/stories/productseries/xx-productseries-translations-settings.txt	2013-09-27 04:13:23 +0000
+++ lib/lp/translations/stories/productseries/xx-productseries-translations-settings.txt	2016-01-26 15:58:00 +0000
@@ -84,14 +84,14 @@
     ...         ['IMPORT_TEMPLATES'])
     >>> browser.getControl('Save settings').click()
 
-The user is automatically redirected to the page he came from.
+The user is automatically redirected to the page they came from.
 
     >>> print browser.url
     http://translations.launchpad.dev/evolution/trunk/
     >>> print_feedback_messages(browser.contents)
     The settings have been updated.
 
-If he looks at the synchonization settings page again, he sees that
+If they look at the synchonization settings page again, they see that
 the changes have been saved.
 
     >>> browser.getLink('Set up branch synchronization').click()

=== modified file 'lib/lp/translations/stories/standalone/xx-licensing.txt'
--- lib/lp/translations/stories/standalone/xx-licensing.txt	2014-11-24 09:16:35 +0000
+++ lib/lp/translations/stories/standalone/xx-licensing.txt	2016-01-26 15:58:00 +0000
@@ -92,7 +92,7 @@
 Permissions
 -----------
 
-If another logged-in user comes across Karl's translations page, he sees
+If another logged-in user comes across Karl's translations page, they see
 no link to change Karl's licensing choice.
 
     >>> user_browser.open('http://translations.launchpad.dev/~karl/')

=== modified file 'lib/lp/translations/stories/standalone/xx-person-activity.txt'
--- lib/lp/translations/stories/standalone/xx-person-activity.txt	2012-02-22 21:24:26 +0000
+++ lib/lp/translations/stories/standalone/xx-person-activity.txt	2016-01-26 15:58:00 +0000
@@ -64,7 +64,7 @@
     >>> person_url = canonical_url(ab, rootsite='translations')
     >>> logout()
 
-When a+b goes to see the translations she has done, she sees a correctly
+When a+b goes to see the translations they have done, they see a correctly
 encoded link to a filtered PO file page.
 
     >>> user_browser.open(person_url + '/+activity')

=== modified file 'lib/lp/translations/stories/standalone/xx-person-editlanguages.txt'
--- lib/lp/translations/stories/standalone/xx-person-editlanguages.txt	2012-12-12 07:30:38 +0000
+++ lib/lp/translations/stories/standalone/xx-person-editlanguages.txt	2016-01-26 15:58:00 +0000
@@ -144,8 +144,8 @@
 
 Team admins may set their team's preferred languages to select the
 which languages they support in the Answer Tracker. Sample Person
-is the admin for Landscape Developers. He decides he wants the team
-to support Spanish questions for Ubuntu, so he sets the team's
+is the admin for Landscape Developers. They decide they want the team
+to support Spanish questions for Ubuntu, so they set the team's
 preferred languages.
 
     >>> browser = setupBrowser(auth='Basic test@xxxxxxxxxxxxx:test')
@@ -168,8 +168,8 @@
 
 Admins can change a user's language settings if needed. Foo Bar, a
 Launchpad Admin has a question from No Privileges Person claiming that
-he cannot add Esperanto to his languages. Foo Bar visits No Privileges
-Person's page to do it himself.
+they cannot add Esperanto to their languages. Foo Bar visits No Privileges
+Person's page to do it themselves.
 
     >>> admin_browser.open('http://launchpad.dev/~no-priv')
     >>> admin_browser.title

=== modified file 'lib/lp/translations/stories/standalone/xx-pofile-translate-alternative-language.txt'
--- lib/lp/translations/stories/standalone/xx-pofile-translate-alternative-language.txt	2013-09-27 04:13:23 +0000
+++ lib/lp/translations/stories/standalone/xx-pofile-translate-alternative-language.txt	2016-01-26 15:58:00 +0000
@@ -152,7 +152,7 @@
 ----------------------
 
 The translate page also allows the user to filter the translatable strings to
-show only the strings he is interested in.
+show only the strings they are interested in.
 
     >>> browser.getControl(name='show', index=1).displayOptions
     ['all items', 'untranslated items', 'translated items',
@@ -209,8 +209,8 @@
     ...     name='field.alternative_language', index=0).displayValue
     ['German (de)']
 
-The samer user chooses to start a new en_GB translation of alsa-utils. The
-form is displayed for him to edit, but the 'Make suggestions from' control
+The same user chooses to start a new en_GB translation of alsa-utils. The
+form is displayed for them to edit, but the 'Make suggestions from' control
 is unset.
 
     >>> user_browser.open(
@@ -228,8 +228,8 @@
 There is only one alternative language at a time
 ------------------------------------------------
 
-If a user specifies more than one alternative language in the URL, he
-gets an UnexpectedFormData exception:
+If a user specifies more than one alternative language in the URL, they
+get an UnexpectedFormData exception:
 
     >>> browser.open(
     ...     'http://translations.launchpad.dev/ubuntu/hoary/+source/'

=== modified file 'lib/lp/translations/stories/standalone/xx-pofile-translate-message-filtering.txt'
--- lib/lp/translations/stories/standalone/xx-pofile-translate-message-filtering.txt	2015-10-05 08:36:52 +0000
+++ lib/lp/translations/stories/standalone/xx-pofile-translate-message-filtering.txt	2016-01-26 15:58:00 +0000
@@ -51,7 +51,7 @@
     Spanish (es) : Template ...evolution-2.2... :
     Hoary (5.04) : Translations : evolution package : Ubuntu
 
-He can see that there are 22 messages.
+They can see that there are 22 messages.
 
     >>> contents = find_main_content(user_browser.contents)
     >>> print_batch_header(contents)
@@ -69,8 +69,8 @@
 ............
 
 No Privileges Person chooses to see the untranslated messages in the
-evolution-2.2 sourcepackage. He sets the view filter to 'Untranslated'
-to filter the messages. He sees 15 messages are not translated.
+evolution-2.2 sourcepackage. They set the view filter to 'Untranslated'
+to filter the messages. They see 15 messages are not translated.
 
     >>> user_browser.getControl(name='show', index=1).value = ['untranslated']
     >>> user_browser.getControl('Change').click()
@@ -147,7 +147,7 @@
     >>> print_batch_header(contents)
     10 ... 19  of 21 results
 
-When No Privileges Person returns to the previous page, he can see the
+When No Privileges Person returns to the previous page, they can see the
 first 10 untranslated messages. The message is translated is not
 displayed.
 
@@ -162,7 +162,7 @@
 
 Projects can restrict translation to privileged users. The messages that
 No Privileges Person adds to upstream Evolution are then taken as
-suggestions, not translations. His changes do not change the total
+suggestions, not translations. Their changes do not change the total
 number of untranslated messages; they do not affect the batch
 navigation.
 
@@ -208,14 +208,14 @@
     >>> user_browser.getControl('Save & Continue').click()
 
 No Privileges Person can see that the number of untranslated messages
-has not changed, and that he is seeing messages 11 though 20.
+has not changed, and that they are seeing messages 11 though 20.
 
     >>> contents = find_main_content(user_browser.contents)
     >>> print_batch_header(contents)
     11 ... 20  of 22 results
 
-He returns to the previous page to check that his suggest of 'fnord' was
-accepted.
+They return to the previous page to check that their suggestion of 'fnord'
+was accepted.
 
     >>> user_browser.getLink('Previous').click()
     >>> contents = find_main_content(user_browser.contents)
@@ -326,8 +326,8 @@
 
 When the filter changes, the batch is reset to the start of the set of
 messages, while preserving the batch size. No Privileges Person can see
-the batch header when he switches the filter to show 'untranslated'
-message; he is seeing the first batch.
+the batch header when they switch the filter to show 'untranslated'
+message; they are seeing the first batch.
 
     >>> user_browser.open(
     ...     'http://translations.launchpad.dev/ubuntu/hoary/'
@@ -348,7 +348,7 @@
 -------------------------
 
 When there are errors in translations that No Privileges Person submits,
-he sees a general error message at the top of the page, plus individual
+they see a general error message at the top of the page, plus individual
 error messages for the individual problematic translations.
 
 The error page shows the same messages that the user submitted for: it
@@ -422,7 +422,7 @@
     ...     name='msgset_130_zh_CN_translation_0_new').value = 'Chinese!'
     >>> user_browser.getControl(name='submit_translations').click()
 
-When he returns to the first page of messages, he is still shown Spanish
+When they return to the first page of messages, they are still shown Spanish
 suggestions.
 
     >>> user_browser.getLink("Previous").click()

=== modified file 'lib/lp/translations/stories/standalone/xx-pofile-translate.txt'
--- lib/lp/translations/stories/standalone/xx-pofile-translate.txt	2013-09-27 04:13:23 +0000
+++ lib/lp/translations/stories/standalone/xx-pofile-translate.txt	2016-01-26 15:58:00 +0000
@@ -107,8 +107,8 @@
 suggestions from'. English cannot be either of these.
 
 If someone were to attempt an English translation, to create an 'en'
-PO message set, he will get an error. The user would generally have
-to hack the URL, but he may have old bookmarks, or have followed old
+PO message set, they will get an error. The user would generally have
+to hack the URL, but they may have old bookmarks, or have followed old
 links from off-site; Launchpad did make links for English translations
 in the past.
 

=== modified file 'lib/lp/translations/stories/translationgroups/xx-link-to-documentation.txt'
--- lib/lp/translations/stories/translationgroups/xx-link-to-documentation.txt	2012-06-29 08:40:05 +0000
+++ lib/lp/translations/stories/translationgroups/xx-link-to-documentation.txt	2016-01-26 15:58:00 +0000
@@ -61,7 +61,7 @@
     >>> print carlos_browser.url
     http://translations.launchpad.dev/+groups/testing-translation-team
 
-Sample Person can not administer her ITranslator record but she can edit
+Sample Person can not administer their ITranslator record but they can edit
 the documentation url through the edit view.
 
     >>> test_browser = setupBrowser(auth='Basic test@xxxxxxxxxxxxx:test')

=== modified file 'lib/lp/translations/stories/translationgroups/xx-translationgroups.txt'
--- lib/lp/translations/stories/translationgroups/xx-translationgroups.txt	2015-10-05 08:36:52 +0000
+++ lib/lp/translations/stories/translationgroups/xx-translationgroups.txt	2016-01-26 15:58:00 +0000
@@ -149,7 +149,7 @@
     ...     print t.renderContents()
     Sample Person
 
-That means that Sample Person is allowed to administer "his" group.
+That means that Sample Person is allowed to administer "their" group.
 
     >>> browser.addHeader('Authorization', 'Basic test@xxxxxxxxxxxxx:test')
     >>> browser.open(
@@ -741,7 +741,7 @@
 translation group, and they are the translation group for Ubuntu, which
 uses the Closed translation mode. This means that No Privileges Person
 should be able to translate any strings in Ubuntu to Welsh. In other
-languages, he will not be able to add or change translations.
+languages, they will not be able to add or change translations.
 
 Let's see if No Privileges Person can see the translated strings in
 Southern Sotho. We expect them to see a readonly form:
@@ -819,7 +819,7 @@
 we are going to set as having Restricted translations. This means that
 No Privileges Person should be able to translate any strings in Ubuntu
 to Welsh. In other languages, No Privileges Person should be warned that
-he is not a designated translator.
+they are not a designated translator.
 
     >>> browser.addHeader('Authorization', 'Basic no-priv@xxxxxxxxxxxxx:test')
 

=== modified file 'lib/lp/translations/stories/translations/xx-translations.txt'
--- lib/lp/translations/stories/translations/xx-translations.txt	2011-12-22 05:09:10 +0000
+++ lib/lp/translations/stories/translations/xx-translations.txt	2016-01-26 15:58:00 +0000
@@ -298,8 +298,8 @@
 ==========================
 
 On template overview page, when user is not logged in (and there is no
-sufficient GeoIP data), or user has not set his preferred languages,
-legend should be hidden from him since he's looking only at the list
+sufficient GeoIP data), or user has not set their preferred languages,
+legend should be hidden from them since he's looking only at the list
 of templates.
 
 We are pretending to be coming from 255.255.255.255 so no GeoIP data
@@ -314,7 +314,7 @@
     >>> find_tag_by_id(anon_browser.contents, 'legend') is None
     False
 
-When looking at a specific template with at least one translation, he
+When looking at a specific template with at least one translation, they
 will again see the legend.
 
     >>> anon_browser.open(


Follow ups