Skip to content
Log in

Manage singular and plural numeric translations

On this page

Overview

A string that reports a number needs more than one wording. “1 match” and “5 matches” cannot come from the same translation value, and choosing between them in the template hard-codes English grammar into your theme, where no translator can reach it.

The t filter does the choosing instead. Give one key several values, one per plural form, then pass the number as count when you call the key. The filter returns the form that matches the number.

This topic is for theme developers writing Liquid templates, and for anyone maintaining Locale Translation records for a translated store.

:::note StoreConnect has no pluralize filter. Pluralization is a feature of the t filter, described below. A template that pipes a number into pluralize produces nothing useful. :::

Syntax

Pluralization has two halves: how you name the keys, and how you call them.

Naming the keys

Add the plural form to the end of the key, after a dot:

```text

.
``` Each form is a separate **Locale Translation** record, and the records that share a parent key make up one group. This group ships with StoreConnect: | Key | Value | |------|-------| | `locations.search.results.count.zero` | `No matches` | | `locations.search.results.count.one` | `1 match` | | `locations.search.results.count.other` | `%{count} matches` | ### Calling the key Call the parent key, never an individual form, and pass the number as `count`: ```liquid {{ "locations.search.results.count" | t: count: found_locations.size }} ``` The filter reads `count`, selects the form that matches it, and returns that value. Three results give you `3 matches`, one result gives you `1 match`, and none gives you `No matches`. Both argument separators work, so `| t: count: n` and `| t, count: n` are equivalent. You will see both in the base theme. ## Plural forms A group can hold any of the six forms below. The storefront checks them in the order listed and returns the first one that both matches the count and exists in the group, so the order you create the records in makes no difference. | Form | Selected when | Example key | |------|-------|-------| | `zero` | The count is `0` | `locations.search.results.count.zero` | | `one` | The count is `1` | `locations.search.results.count.one` | | `#N` | The count is exactly `N` | `locations.search.results.count.#2` | | `N_M` | The count falls anywhere in the inclusive range `N` to `M` | `locations.search.results.count.2_10` | | `infinity` | The count is unlimited | `locations.search.results.count.infinity` | | `other` | Nothing more specific matched | `locations.search.results.count.other` | `one` and `other` are the conventional plural forms. `zero`, `#N`, `N_M`, and `infinity` are StoreConnect additions, so a group copied from another platform will not use them. Every form is optional, but a group holding nothing but `other` is the safest starting point, because it returns a value for any count. :::warning Always include an `other` form. A count that matches no form in the group fails outright rather than falling back to the nearest match, so a group holding only `one` breaks as soon as the count reaches `2`. ::: :::warning Exact-count forms need the `#`. A key ending in digits alone, such as `locations.search.results.count.2`, is read as a list position rather than a form name, and the whole group breaks. Write `#2` instead. Range forms such as `2_10` are safe, because the underscore stops the segment being read as a number. ::: ## Interpolating the count Write `%{count}` in a value and the number that selected the form is substituted into the text, as in the `other` form above. Use `%{count}` only where the number should be visible. A `one` form usually reads better with the number written out, as in `1 match` rather than `%{count} match`, and a `zero` form rarely needs it at all. ## Worked examples These groups all ship with StoreConnect and are useful patterns to copy. An exact count that replaces a phrase, because English has a single word for two weeks: | Key | Value | |------|-------| | `products.pricing.timespan_week.count.one` | `week` | | `products.pricing.timespan_week.count.#2` | `fortnight` | | `products.pricing.timespan_week.count.other` | `%{count} weeks` | An unlimited quantity, where a number would be wrong. Only two forms are needed: | Key | Value | |------|-------| | `accounts.product_approvals.shared.quantity.count.infinity` | `Unlimited` | | `accounts.product_approvals.shared.quantity.count.other` | `%{count}` | Exact counts used as position labels rather than quantities. This group names each line of an address form, and falls back to a generic label past the fourth: | Key | Value | |------|-------| | `accounts.shared.address_form.address_lines.count.#1` | `Street address` | | `accounts.shared.address_form.address_lines.count.#2` | `Suite` | | `accounts.shared.address_form.address_lines.count.#3` | `Apartment` | | `accounts.shared.address_form.address_lines.count.#4` | `Building Number` | | `accounts.shared.address_form.address_lines.count.other` | `line %{count}` | The template passes the loop position as the count: ```liquid {%- assign field = form.fields["billing_address_lines"] %} {%- for line in field.value %} {%- endfor %} ``` :::tip Name the parent key `count`, as every group above does. The call site then reads `"…orders.count" | t: count: n`, which makes it obvious at a glance that the key is pluralized. ::: ## Add a pluralized key 1. Open the **Theme Locale** record for the locale you are translating. 2. Go to the **Locale Translations** related list. 3. Click **New**. 4. In **Key**, enter the full key including the plural form, such as `locations.search.results.count.one`. 5. In **Value**, enter the wording for that form, using `%{count}` where the number should appear. 6. Click **Save**. 7. Repeat for each form in the group, making sure one of them is `other`. Themes imported from a zip file supply the same records as rows in `translations/.default.csv`, one row per form: ```csv Key,Value locations.search.results.count.zero,No matches locations.search.results.count.one,1 match locations.search.results.count.other,%{count} matches ``` ## Avoid these patterns Both patterns below work in English and break in every other language. Replace them with a pluralized key. Grammar written into the template, where no translator can correct it: ```liquid {{ n }} item{% if n != 1 %}s{% endif %} ``` Separate keys selected by a condition, which is what makes a translation set grow faster than it needs to: ```liquid {% if n == 1 %}{{ "cart.one_item" | t }}{% else %}{{ "cart.n_items" | t: count: n }}{% endif %} ```

Was this article helpful?

Was this article helpful?