Added number formatting to all pluralized counts

closes https://github.com/TryGhost/Ghost/issues/12110

- adds `{{gh-pluralize}}` helper that wraps the `{{pluralize}}` helper from `ember-inflector` but formats the number using our `{{format-number}}` helper
- updates all uses of `{{pluralize}}` to `{{gh-pluralize}}`
This commit is contained in:
Kevin Ansfield 2020-08-10 11:16:03 +01:00
parent e246b2829d
commit fd91b593a5
11 changed files with 46 additions and 22 deletions

View File

@ -4,9 +4,9 @@
{{else if (or @post.isPublished @post.pastScheduledTime)}} {{else if (or @post.isPublished @post.pastScheduledTime)}}
Published Published
{{#if (or (eq @post.email.status "submitting") (eq @post.email.status "submitting"))}} {{#if (or (eq @post.email.status "submitting") (eq @post.email.status "submitting"))}}
and sending to {{pluralize @post.email.emailCount "member"}} and sending to {{gh-pluralize @post.email.emailCount "member"}}
{{else if (eq @post.email.status "submitted")}} {{else if (eq @post.email.status "submitted")}}
and sent to {{pluralize @post.email.emailCount "member"}} and sent to {{gh-pluralize @post.email.emailCount "member"}}
{{/if}} {{/if}}
{{else if @post.isScheduled}} {{else if @post.isScheduled}}
<time datetime="{{@post.publishedAtUTC}}" class="ml1 green f8" data-test-schedule-countdown> <time datetime="{{@post.publishedAtUTC}}" class="ml1 green f8" data-test-schedule-countdown>

View File

@ -14,7 +14,7 @@
</div> </div>
<div class="flex flex-column justify-center"> <div class="flex flex-column justify-center">
<p class="ma0 pa0 midgrey">Post was sent by email to</p> <p class="ma0 pa0 midgrey">Post was sent by email to</p>
<p class="ma0 pa0 f5 lh-solid">{{pluralize this.post.email.emailCount "member"}}</p> <p class="ma0 pa0 f5 lh-solid">{{gh-pluralize this.post.email.emailCount "member"}}</p>
</div> </div>
</div> </div>
<div class="pa5 pt3 pb3 f7 bb b--whitegrey"> <div class="pa5 pt3 pb3 f7 bb b--whitegrey">
@ -45,7 +45,7 @@
</div> </div>
<div class="flex flex-column justify-center"> <div class="flex flex-column justify-center">
<p class="ma0 pa0 midgrey">Post failed to send to</p> <p class="ma0 pa0 midgrey">Post failed to send to</p>
<p class="ma0 pa0 f5 lh-solid">{{pluralize this.post.email.emailCount "member"}}</p> <p class="ma0 pa0 f5 lh-solid">{{gh-pluralize this.post.email.emailCount "member"}}</p>
</div> </div>
</div> </div>
<div class="pa5 pt3 pb3 f7 bb b--whitegrey"> <div class="pa5 pt3 pb3 f7 bb b--whitegrey">

View File

@ -1,6 +1,6 @@
import ModalComponent from 'ghost-admin/components/modal-base'; import ModalComponent from 'ghost-admin/components/modal-base';
import {computed} from '@ember/object'; import {computed} from '@ember/object';
import {pluralize} from 'ember-inflector'; import {ghPluralize} from 'ghost-admin/helpers/gh-pluralize';
import {inject as service} from '@ember/service'; import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency'; import {task} from 'ember-concurrency';
@ -22,7 +22,7 @@ export default ModalComponent.extend({
return 'all members'; return 'all members';
} }
return pluralize(this.get('model.memberCount'), 'member'); return ghPluralize(this.get('model.memberCount'), 'member');
}), }),
confirmAndCheckError: task(function* () { confirmAndCheckError: task(function* () {

View File

@ -7,7 +7,7 @@
<div class="modal-body" data-test-state="delete-unconfirmed"> <div class="modal-body" data-test-state="delete-unconfirmed">
<p> <p>
You're about to delete You're about to delete
<strong data-test-text="delete-count">{{pluralize this.model.memberCount "member"}}</strong>. <strong data-test-text="delete-count">{{gh-pluralize this.model.memberCount "member"}}</strong>.
This is permanent! We warned you, k? This is permanent! We warned you, k?
</p> </p>
</div> </div>
@ -26,7 +26,7 @@
<div class="flex items-center"> <div class="flex items-center">
{{svg-jar "check-circle" class="w4 h4 stroke-green mr2"}} {{svg-jar "check-circle" class="w4 h4 stroke-green mr2"}}
<p class="ma0 pa0"> <p class="ma0 pa0">
<span class="fw6" data-test-text="deleted-count">{{pluralize this.response.deleted.count "member"}}</span> <span class="fw6" data-test-text="deleted-count">{{gh-pluralize this.response.deleted.count "member"}}</span>
deleted deleted
</p> </p>
</div> </div>
@ -35,7 +35,7 @@
{{svg-jar "warning" class="w4 h4 fill-red mr2 nudge-top--3"}} {{svg-jar "warning" class="w4 h4 fill-red mr2 nudge-top--3"}}
<div> <div>
<p class="ma0 pa0"> <p class="ma0 pa0">
<span class="fw5" data-test-text="invalid-count">{{pluralize this.response.invalid.count "member"}}</span> <span class="fw5" data-test-text="invalid-count">{{gh-pluralize this.response.invalid.count "member"}}</span>
skipped skipped
</p> </p>
{{#each this.response.invalid.errors as |error|}} {{#each this.response.invalid.errors as |error|}}

View File

@ -6,7 +6,7 @@
<div class="modal-body"> <div class="modal-body">
{{#if this.user.count.posts}} {{#if this.user.count.posts}}
<p> <p>
<strong>{{this.user.name}}</strong> and their <strong data-test-text="user-post-count">{{pluralize this.user.count.posts 'post'}}</strong> will be permanently deleted. If you dont want to lose these posts, you should assign them to a different author. <strong>{{this.user.name}}</strong> and their <strong data-test-text="user-post-count">{{gh-pluralize this.user.count.posts 'post'}}</strong> will be permanently deleted. If you dont want to lose these posts, you should assign them to a different author.
</p> </p>
<p> <p>
A backup will be automatically downloaded to your device before deletion. A backup will be automatically downloaded to your device before deletion.

View File

@ -100,11 +100,11 @@
<div class="flex items-start"> <div class="flex items-start">
<div class="w-50 gh-member-import-result-summary"> <div class="w-50 gh-member-import-result-summary">
<h2>{{format-number this.importResponse.imported.count}}</h2> <h2>{{format-number this.importResponse.imported.count}}</h2>
<p>{{pluralize this.importResponse.imported.count "Member" without-count=true}} added</p> <p>{{gh-pluralize this.importResponse.imported.count "Member" without-count=true}} added</p>
</div> </div>
<div class="bl b--whitegrey w-50 gh-member-import-result-summary"> <div class="bl b--whitegrey w-50 gh-member-import-result-summary">
<h2>{{format-number this.importResponse.invalid.count}}</h2> <h2>{{format-number this.importResponse.invalid.count}}</h2>
<p>{{pluralize this.importResponse.invalid.count "Error" without-count=true}}</p> <p>{{gh-pluralize this.importResponse.invalid.count "Error" without-count=true}}</p>
</div> </div>
</div> </div>
</div> </div>
@ -114,7 +114,7 @@
<div class="flex items-start"> <div class="flex items-start">
{{svg-jar "warning" class="w5 h5 fill-red nudge-top--3 mr3"}} {{svg-jar "warning" class="w5 h5 fill-red nudge-top--3 mr3"}}
<div class="flex-grow w-100"> <div class="flex-grow w-100">
<p class="ma0 pa0">{{format-number this.importResponse.invalid.count}} {{pluralize this.importResponse.invalid.count "member" without-count=true}} were skipped due to the following errors:</p> <p class="ma0 pa0">{{format-number this.importResponse.invalid.count}} {{gh-pluralize this.importResponse.invalid.count "member" without-count=true}} were skipped due to the following errors:</p>
<ul class="ma0 pa0 mt4 list bt b--whitegrey"> <ul class="ma0 pa0 mt4 list bt b--whitegrey">
{{#each this.importResponse.invalid.errors as |error|}} {{#each this.importResponse.invalid.errors as |error|}}
<li class="gh-members-import-errormessage">{{error.message}} <span class="fw4">({{format-number error.count}})</span></li> <li class="gh-members-import-errormessage">{{error.message}} <span class="fw4">({{format-number error.count}})</span></li>
@ -169,7 +169,7 @@
<span>Start over</span> <span>Start over</span>
</button> </button>
<button class="gh-btn gh-btn-green" {{action "upload"}} disabled={{this.importDisabled}}> <button class="gh-btn gh-btn-green" {{action "upload"}} disabled={{this.importDisabled}}>
<span>Import{{#if this.fileData.length}} {{format-number this.fileData.length}} {{pluralize this.fileData.length 'member' without-count=true}}{{/if}}</span> <span>Import{{#if this.fileData.length}} {{format-number this.fileData.length}} {{gh-pluralize this.fileData.length 'member' without-count=true}}{{/if}}</span>
</button> </button>
{{/if}} {{/if}}
{{else}} {{else}}

View File

@ -3,8 +3,7 @@ import ghostPaths from 'ghost-admin/utils/ghost-paths';
import moment from 'moment'; import moment from 'moment';
import {A} from '@ember/array'; import {A} from '@ember/array';
import {action} from '@ember/object'; import {action} from '@ember/object';
import {formatNumber} from 'ghost-admin/helpers/format-number'; import {ghPluralize} from 'ghost-admin/helpers/pluralize';
import {pluralize} from 'ember-inflector';
import {inject as service} from '@ember/service'; import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency-decorators'; import {task} from 'ember-concurrency-decorators';
import {timeout} from 'ember-concurrency'; import {timeout} from 'ember-concurrency';
@ -67,7 +66,7 @@ export default class MembersController extends Controller {
return 'Search result'; return 'Search result';
} }
let count = `${formatNumber(members.length)} ${pluralize(members.length, 'member', {withoutCount: true})}`; let count = ghPluralize(members.length, 'member');
if (selectedLabel && selectedLabel.slug) { if (selectedLabel && selectedLabel.slug) {
if (members.length > 1) { if (members.length > 1) {

View File

@ -0,0 +1,26 @@
import {formatNumber} from './format-number';
import {helper} from '@ember/component/helper';
import {isBlank} from '@ember/utils';
import {pluralize} from 'ember-inflector';
export function ghPluralize(number, word, {'without-count': withoutCount} = {}) {
let output = [];
if (!isBlank(number) && withoutCount !== true) {
output.push(formatNumber(number));
}
// default {{pluralize}} allows for {{pluralize "word"}} with no number
if (isBlank(number)) {
output.push(pluralize(word, {withoutCount: true}));
} else {
output.push(pluralize(number, word, {withoutCount: true}));
}
return output.join(' ');
}
// like {{pluralize}} but formats the number according to current locale
export default helper(function ([number, word], {'without-count': withoutCount} = {}) {
return ghPluralize(number, word, {'without-count': withoutCount});
});

View File

@ -1,9 +1,8 @@
import MemberImportError from 'ghost-admin/errors/member-import-error'; import MemberImportError from 'ghost-admin/errors/member-import-error';
import Service, {inject as service} from '@ember/service'; import Service, {inject as service} from '@ember/service';
import validator from 'validator'; import validator from 'validator';
import {formatNumber} from 'ghost-admin/helpers/format-number'; import {ghPluralize} from 'ghost-admin/helpers/gh-pluralize';
import {isEmpty} from '@ember/utils'; import {isEmpty} from '@ember/utils';
import {pluralize} from 'ember-inflector';
export default Service.extend({ export default Service.extend({
ajax: service(), ajax: service(),
@ -34,7 +33,7 @@ export default Service.extend({
if (!this.membersUtils.isStripeEnabled) { if (!this.membersUtils.isStripeEnabled) {
validationErrors.push(new MemberImportError({ validationErrors.push(new MemberImportError({
message: `Missing Stripe connection`, message: `Missing Stripe connection`,
context: `${formatNumber(totalCount)} ${pluralize(totalCount, 'Stripe customer', {withoutCount: true})} won't be imported. You need to <a href="#/settings/labs">connect to Stripe</a> to import Stripe customers.`, context: `${ghPluralize(totalCount, 'Stripe customer')} won't be imported. You need to <a href="#/settings/labs">connect to Stripe</a> to import Stripe customers.`,
type: 'warning' type: 'warning'
})); }));
} else { } else {

View File

@ -85,7 +85,7 @@
<div class="absolute flex items-center br3 bg-white {{if editor.headerClass "right-4 bottom-4" "right-6 bottom-6"}}"> <div class="absolute flex items-center br3 bg-white {{if editor.headerClass "right-4 bottom-4" "right-6 bottom-6"}}">
<div class="midgrey-l2 {{if editor.headerClass "f-supersmall pl2 pr2" "f8 pl4 pr3"}} fw4"> <div class="midgrey-l2 {{if editor.headerClass "f-supersmall pl2 pr2" "f8 pl4 pr3"}} fw4">
{{pluralize this.wordCount.wordCount "word"}} {{gh-pluralize this.wordCount.wordCount "word"}}
</div> </div>
<a href="https://ghost.org/faq/using-the-editor/" class="flex {{if editor.headerClass "pa2" "pa3"}}" target="_blank">{{svg-jar "help" class="w4 h4 stroke-midgrey-l2"}}</a> <a href="https://ghost.org/faq/using-the-editor/" class="flex {{if editor.headerClass "pa2" "pa3"}}" target="_blank">{{svg-jar "help" class="w4 h4 stroke-midgrey-l2"}}</a>
</div> </div>

View File

@ -78,7 +78,7 @@
</button> </button>
{{else}} {{else}}
<h3>Uploading {{pluralize upload.files.length "image"}}...</h3> <h3>Uploading {{gh-pluralize upload.files.length "image"}}...</h3>
{{upload.progressBar}} {{upload.progressBar}}
{{/if}} {{/if}}
</div> </div>