diff --git a/changelogs/unreleased-ee/4134-approve-additionally.yml b/changelogs/unreleased-ee/4134-approve-additionally.yml
new file mode 100644
index 0000000000000000000000000000000000000000..1af99d39d0a4f97e4ecd788df4f8c7d4225dcbc9
--- /dev/null
+++ b/changelogs/unreleased-ee/4134-approve-additionally.yml
@@ -0,0 +1,5 @@
+---
+title: Approve merge requests additionally
+merge_request: 4134
+author:
+type: changed
diff --git a/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_body.js b/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_body.js
index 6899e62e0ae70bafac534be1c643b83630f26147..42bd0d615eecae826279dafaedf8ca71335615d9 100644
--- a/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_body.js
+++ b/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_body.js
@@ -1,3 +1,4 @@
+import { n__, s__, sprintf } from '~/locale';
 import Flash from '~/flash';
 import MRWidgetAuthor from '~/vue_merge_request_widget/components/mr_widget_author.vue';
 import eventHub from '~/vue_merge_request_widget/event_hub';
@@ -44,8 +45,24 @@ export default {
   },
   computed: {
     approvalsRequiredStringified() {
-      const baseString = `${this.approvalsLeft} more approval`;
-      return this.approvalsLeft === 1 ? baseString : `${baseString}s`;
+      let approvedString = s__('MergeRequest|Approved');
+      if (this.approvalsLeft >= 1) {
+        approvedString = sprintf(n__('mrWidget|Requires 1 more approval by',
+          'MergeRequest|Requires %d more approvals by', this.approvalsLeft));
+      }
+      return approvedString;
+    },
+    approveButtonText() {
+      let approveButtonText = s__('mrWidget|Approve');
+      if (this.approvalsLeft <= 0) {
+        approveButtonText = s__('mrWidget|Approve additionally');
+      }
+      return approveButtonText;
+    },
+    approveButtonClass() {
+      return {
+        'btn-inverted': this.showApproveButton && this.approvalsLeft <= 0,
+      };
     },
     showApproveButton() {
       return this.userCanApprove && !this.userHasApproved;
@@ -65,7 +82,7 @@ export default {
         })
         .catch(() => {
           this.approving = false;
-          new Flash('An error occured while submitting your approval.'); // eslint-disable-line
+          Flash(s__('mrWidget|An error occured while submitting your approval.'));
         });
     },
   },
@@ -75,18 +92,18 @@ export default {
         <button
           :disabled="approving"
           @click="approveMergeRequest"
-          class="btn btn-primary btn-sm approve-btn">
+          class="btn btn-primary btn-sm approve-btn"
+          :class="approveButtonClass">
           <i
             v-if="approving"
             class="fa fa-spinner fa-spin"
             aria-hidden="true" />
-          Approve
+          {{approveButtonText}}
         </button>
       </span>
       <span class="approvals-required-text bold">
-        Requires {{approvalsRequiredStringified}}
+        {{approvalsRequiredStringified}}
         <span v-if="showSuggestedApprovers">
-          <span class="dash">&mdash;</span>
           <mr-widget-author
             v-for="approver in suggestedApprovers"
             :key="approver.username"
diff --git a/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_footer.js b/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_footer.js
index bfb9da7aff511f94d9f8a5130269bce7b1d62395..c8f13f2352d49fed803c8def9a040175b651372b 100644
--- a/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_footer.js
+++ b/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_footer.js
@@ -1,5 +1,6 @@
 import Flash from '~/flash';
 import LinkToMemberAvatar from '~/vue_shared/components/link_to_member_avatar';
+import { s__ } from '~/locale';
 import eventHub from '~/vue_merge_request_widget/event_hub';
 
 export default {
@@ -47,6 +48,12 @@ export default {
       const isMerged = this.mr.state === 'merged';
       return this.userHasApproved && !this.userCanApprove && !isMerged;
     },
+    approvedByText() {
+      return s__('mrWidget|Approved by');
+    },
+    removeApprovalText() {
+      return s__('mrWidget|Remove your approval');
+    },
   },
   methods: {
     unapproveMergeRequest() {
@@ -59,7 +66,7 @@ export default {
         })
         .catch(() => {
           this.unapproving = false;
-          Flash('An error occured while removing your approval.');
+          Flash(s__('mrWidget|An error occured while removing your approval.'));
         });
     },
   },
@@ -68,14 +75,14 @@ export default {
       v-if="approvedBy.length"
       class="approved-by-users approvals-footer clearfix mr-info-list">
       <div class="approvers-prefix">
-        <p>Approved by</p>
+        <p>{{approvedByText}}</p>
         <div class="approvers-list">
           <link-to-member-avatar
             v-for="(approver, index) in approvedBy"
             :key="index"
             :avatar-size="20"
             :avatar-url="approver.user.avatar_url"
-            extra-link-class="approver-avatar"
+            extra-link-class="approver-avatar js-approver-list-member"
             :display-name="approver.user.name"
             :profile-url="approver.user.web_url"
             :show-tooltip="true"
@@ -99,7 +106,7 @@ export default {
             class="fa fa-spinner fa-spin"
             aria-hidden="true">
           </i>
-          Remove your approval
+          {{removeApprovalText}}
         </button>
       </div>
     </div>
diff --git a/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/mr_widget_approvals.js b/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/mr_widget_approvals.js
index ca2e08db8feca72104faef83638ba0f529bf767d..9db2aec34c4faea2e5ab0e88aea4ab31002f5718 100644
--- a/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/mr_widget_approvals.js
+++ b/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/mr_widget_approvals.js
@@ -1,5 +1,6 @@
 import Flash from '~/flash';
 import statusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
+import { s__ } from '~/locale';
 import ApprovalsBody from './approvals_body';
 import ApprovalsFooter from './approvals_footer';
 
@@ -34,7 +35,7 @@ export default {
     },
   },
   created() {
-    const flashErrorMessage = 'An error occured while retrieving approval data for this merge request.';
+    const flashErrorMessage = s__('mrWidget|An error occured while retrieving approval data for this merge request.');
 
     this.service.fetchApprovals()
       .then((data) => {
diff --git a/ee/app/models/concerns/approvable.rb b/ee/app/models/concerns/approvable.rb
index 0cefa4277bf2759afb805792cd19927db80e9e84..00928f43392a092d329aa400c854608d0dce5046 100644
--- a/ee/app/models/concerns/approvable.rb
+++ b/ee/app/models/concerns/approvable.rb
@@ -134,11 +134,14 @@ def has_approved?(user)
     approved_by_users.include?(user)
   end
 
-  # Once there are fewer approvers left in the list than approvals required, allow other
-  # project members to approve the MR.
+  # Once there are fewer approvers left in the list than approvals required or
+  # there are no more approvals required
+  # allow other project members to approve the MR.
   #
   def any_approver_allowed?
-    approvals_left > approvers_left.count
+    remaining_approvals = approvals_left
+
+    remaining_approvals.zero? || remaining_approvals > approvers_left.count
   end
 
   def approved_by_users
diff --git a/spec/features/projects/merge_requests/user_approves_merge_request_spec.rb b/spec/features/projects/merge_requests/user_approves_merge_request_spec.rb
index d7f452f7bdf8a588e773793fb1e98ee07ff90620..32f08e04c1ec21a963aeec976610d2d1bbf488df 100644
--- a/spec/features/projects/merge_requests/user_approves_merge_request_spec.rb
+++ b/spec/features/projects/merge_requests/user_approves_merge_request_spec.rb
@@ -5,6 +5,7 @@
   let(:project) { create(:project, :repository, approvals_before_merge: 1) }
   let(:user) { create(:user) }
   let(:user2) { create(:user) }
+  let(:user3) { create(:user) }
 
   before do
     project.add_developer(user)
@@ -27,6 +28,34 @@
     end
   end
 
+  context 'when a merge request is approved additionally' do
+    before do
+      project.add_developer(user2)
+      project.add_developer(user3)
+      visit(merge_request_path(merge_request))
+    end
+
+    it 'shows multiple approvers beyond the needed count' do
+      click_button('Approve')
+      wait_for_requests
+
+      sign_out(user)
+
+      sign_in_visit_merge_request(user2)
+      sign_in_visit_merge_request(user3)
+
+      expect(all('.js-approver-list-member').count).to eq(3)
+    end
+
+    def sign_in_visit_merge_request(user)
+      sign_in(user)
+      visit(merge_request_path(merge_request))
+      click_button('Approve')
+      wait_for_requests
+      sign_out(user)
+    end
+  end
+
   context 'when user cannot approve' do
     before do
       merge_request.approvers.create(user_id: user2.id)
diff --git a/spec/javascripts/approvals/approvals_body_spec.js b/spec/javascripts/approvals/approvals_body_spec.js
index ca93179994779936f219e413993d0e7ca582b143..4ffb656facd5154deddb06651cda36ab4bdd2b4a 100644
--- a/spec/javascripts/approvals/approvals_body_spec.js
+++ b/spec/javascripts/approvals/approvals_body_spec.js
@@ -56,7 +56,7 @@ import ApprovalsBody from 'ee/vue_merge_request_widget/components/approvals/appr
     describe('Computed properties', function () {
       describe('approvalsRequiredStringified', function () {
         it('should display the correct string for 1 possible approver', function () {
-          const correctText = '1 more approval';
+          const correctText = 'Requires 1 more approval by';
           expect(this.approvalsBody.approvalsRequiredStringified).toBe(correctText);
         });
 
@@ -65,11 +65,20 @@ import ApprovalsBody from 'ee/vue_merge_request_widget/components/approvals/appr
           this.approvalsBody.suggestedApprovers.push({ name: 'Approver 2' });
 
           Vue.nextTick(() => {
-            const correctText = '2 more approvals';
+            const correctText = 'Requires 2 more approvals by';
             expect(this.approvalsBody.approvalsRequiredStringified).toBe(correctText);
             done();
           });
         });
+
+        it('shows the "Approved" text message when there is enough approvals in place', function (done) {
+          this.approvalsBody.approvalsLeft = 0;
+
+          Vue.nextTick(() => {
+            expect(this.approvalsBody.approvalsRequiredStringified).toBe('Approved');
+            done();
+          });
+        });
       });
 
       describe('showApproveButton', function () {
@@ -91,6 +100,30 @@ import ApprovalsBody from 'ee/vue_merge_request_widget/components/approvals/appr
           });
         });
       });
+
+      describe('approveButtonText', function () {
+        it('The approve button should have the "Approve" text', function (done) {
+          this.approvalsBody.approvalsLeft = 1;
+          this.approvalsBody.userHasApproved = false;
+          this.approvalsBody.userCanApprove = true;
+
+          Vue.nextTick(() => {
+            expect(this.approvalsBody.approveButtonText).toBe('Approve');
+            done();
+          });
+        });
+
+        it('The approve button should have the "Approve additionally" text', function (done) {
+          this.approvalsBody.approvalsLeft = 0;
+          this.approvalsBody.userHasApproved = false;
+          this.approvalsBody.userCanApprove = true;
+
+          Vue.nextTick(() => {
+            expect(this.approvalsBody.approveButtonText).toBe('Approve additionally');
+            done();
+          });
+        });
+      });
     });
   });
 })(window.gl || (window.gl = {}));
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 22f08ad6193b89c9ff0c68c4279c5a164a80c2c8..1dc583479ce3a5e21e3a5aef7243ad0d7e9b56f1 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -1885,6 +1885,7 @@ def set_compare(merge_request)
     context 'on a project with several members' do
       let(:approver_2) { create(:user) }
       let(:developer) { create(:user) }
+      let(:other_developer) { create(:user) }
       let(:reporter) { create(:user) }
       let(:stranger) { create(:user) }
 
@@ -1893,6 +1894,7 @@ def set_compare(merge_request)
         project.add_developer(approver)
         project.add_developer(approver_2)
         project.add_developer(developer)
+        project.add_developer(other_developer)
         project.add_reporter(reporter)
       end
 
@@ -2041,6 +2043,36 @@ def set_compare(merge_request)
             expect(merge_request.can_approve?(stranger)).to be_falsey
             expect(merge_request.can_approve?(nil)).to be_falsey
           end
+
+          context 'when only 1 approval approved' do
+            it 'only allows the approvers to approve the MR' do
+              create(:approval, user: approver, merge_request: merge_request)
+
+              expect(merge_request.can_approve?(developer)).to be_truthy
+              expect(merge_request.can_approve?(approver)).to be_falsey
+              expect(merge_request.can_approve?(approver_2)).to be_truthy
+
+              expect(merge_request.can_approve?(author)).to be_falsey
+              expect(merge_request.can_approve?(reporter)).to be_falsey
+              expect(merge_request.can_approve?(other_developer)).to be_falsey
+              expect(merge_request.can_approve?(stranger)).to be_falsey
+              expect(merge_request.can_approve?(nil)).to be_falsey
+            end
+          end
+
+          context 'when all approvals received' do
+            it 'allows anyone with write access except for author to approve the MR' do
+              create(:approval, user: approver, merge_request: merge_request)
+              create(:approval, user: approver_2, merge_request: merge_request)
+              create(:approval, user: developer, merge_request: merge_request)
+
+              expect(merge_request.can_approve?(author)).to be_falsey
+              expect(merge_request.can_approve?(reporter)).to be_falsey
+              expect(merge_request.can_approve?(other_developer)).to be_truthy
+              expect(merge_request.can_approve?(stranger)).to be_falsey
+              expect(merge_request.can_approve?(nil)).to be_falsey
+            end
+          end
         end
       end
     end