_n( string $single, string $plural, int $number, string $domain = 'default' ): string
- Since
- 2.8.0, 5.5.0
- Source
wp-includes/l10n.php:483
Chooses between a singular and plural translated string by evaluating $number against the loaded translations for $domain. It returns the raw translated string, so you still need sprintf() or a similar call to insert the actual number into the placeholder. Reach for _n_noop() instead when the number isn't known until later and you need to defer the plural decision.
Description
Used when you want to use the appropriate form of a string based on whether a number is singular or plural.
Example:
printf( _n( '%s person', '%s people', $count, 'text-domain' ), number_format_i18n( $count ) );Compatibility
- WordPress
- since 5.5.0
- PHP
- 7.4–8.6-dev
- 6.7.7
- 6.8.8
- 6.9.7
- 7.0.4
- 7.1.0
Present in every tracked release (6.7.7 to 7.1.0), and compiles on PHP 7.4 through 8.6-dev.
Parameters
$singlestring- The text to be used if the number is singular.
$pluralstring- The text to be used if the number is plural.
$numberint- The number to compare against to use either the singular or plural form.
$domainstringoptional- Text domain. Unique identifier for retrieving translated strings.
Default 'default'.Default:'default'
Return value
string- The translated singular or plural form.
Code examples
Every example is editable and runs in a real WordPress booted in your browser by WordPress Playground. Press Run, then edit the code: clicking away re-runs it. Nothing is sent anywhere until you do.
Print a plural-aware count of posts in the site
Count the baseline posts and let _n() pick the right form of the label before formatting the output.
$posts = get_posts( array( 'post_type' => 'post', 'numberposts' => -1 ) );
$count = count( $posts );
$label = sprintf(
_n( '%s post found', '%s posts found', $count, 'text-domain' ),
number_format_i18n( $count )
);
echo esc_html( $label );Report how many posts have a price set in post meta
Query for posts carrying the price meta key seeded on post 2 and pluralize the result count.
$priced_posts = get_posts( array(
'post_type' => 'post',
'meta_key' => 'price',
'numberposts' => -1,
) );
$count = count( $priced_posts );
printf(
esc_html( _n( '%s post has a price set.', '%s posts have a price set.', $count, 'text-domain' ) ),
number_format_i18n( $count )
);Common problems and fixes · 3
- Why does _n() return the plural form for a count of 0?
- Why doesn't the number show up in the string _n() returns?
- Why does my custom text domain's plural form never get filtered?
Why does _n() return the plural form for a count of 0?
Why doesn't the number show up in the string _n() returns?
Why does my custom text domain's plural form never get filtered?
Alternatives and related functions
_n_noop- When the singular and plural strings need to be registered for translation before the actual number is known, such as inside a static array evaluated later with translate_nooped_plural().
_nx- When the same singular or plural word needs different translations depending on grammatical context, requiring an extra context string alongside $single and $plural.
__- When there is only one string to translate and no singular or plural variation to choose between.
number_format_i18n- When the numeric value returned by _n() as a placeholder needs locale-aware formatting before it is inserted into the translated string.
Performance profile
How much work a call to _n() does, and what it touches: the algorithmic scaling, the Zend instruction count per call across PHP versions, the hooks it hands control to, and the core code that calls it. Measured from the compiled opcodes, not a stopwatch, so every number is identical on any machine running the same PHP version, and every function in core is ranked by cost.
- Cost class
- Trivial
- Scaling
- Constant
- Instructions
- 34
- Plugin surface
- 2 hooks
- Called by
- 48
Touches nothing outside its own arguments.
No loop in the body: the same number of instructions runs whatever you pass in.
Executed per call on PHP 8.5. The body compiles to 34.
Third-party callbacks on 'ngettext', 'ngettext_{$domain}' run inside this call, and their cost is not bounded by anything here.
48 places in core call this, so the cost is paid more often than your own code shows.
What it touches
- hookthird-party callbacks
apply_filters()called directly
Further down the call graph this can also reach query, option, cache, serialize and transient. Those are the worst case, several calls deep and usually down an error path, not what a normal call pays.
What one call costs · 1 distinct outcome
One number would be a lie: the work depends on which branch runs. These are every distinct cost _n() can have, taken from its control-flow graph on PHP 8.5.
| When | Instructions | Calls it makes |
|---|---|---|
| always | 34 | get_translations_for_domain(), ->translate_plural(), apply_filters(), apply_filters() |
Across PHP versions
| PHP | Compiled | Executed | Branches | Notes |
|---|---|---|---|---|
| 8.6-dev | 34 | 34 | 0 | |
| 8.5 | 34 | 34 | 0 | 17 fewer instructions than PHP 8.4 |
| 8.4 | 51 | 17 | 0 | |
| 8.3 | 51 | 17 | 0 | |
| 8.2 | 51 | 17 | 0 | |
| 8.1 | 51 | 17 | 0 | |
| 7.4 | 51 | 17 | 0 |
An instruction is not a fixed amount of time, so a matching count is not necessarily the same speed; what it rules out is a difference in the work itself.
Hooks and filters fired · 2
2 hooks fire while _n() runs, in this order:
- apply_filters( ngettext )filterline 498 (+15 into the body)
Filters the singular or plural form of a string.
- apply_filters( ngettext_{$domain} )filterline 513 (+30 into the body)
Filters the singular or plural form of a string for a domain.
Uses · 2
- get_translations_for_domain()Returns the Translations instance for a text domain.
- apply_filters()Calls the callback functions that have been added to a filter hook.
Used by · 48
- Twenty_Fourteen_Ephemera_Widget::widget()Outputs the HTML for this widget.
- WP_Customize_Manager::save_changeset_post()Saves the post for the loaded changeset.
- WP_Customize_Nav_Menus::customize_register()Adds the customizer settings and controls.
- WP_Customize_Nav_Menus::enqueue_scripts()Enqueues scripts and styles for Customizer pane.
- WP_Customize_Widgets::enqueue_scripts()Enqueues scripts and styles for Customizer panel and export data to JavaScript.
- WP_List_Table::ajax_response()Handles an incoming ajax request (called from admin-ajax.php)
- WP_List_Table::comments_bubble()Displays a comment count bubble.
- WP_List_Table::pagination()Displays the pagination.
- WP_MS_Themes_List_Table::get_views()Gets the list of views (statuses) for the list table.
- WP_MS_Users_List_Table::get_views()
- WP_Plugins_List_Table::get_views()
- WP_Privacy_Requests_Table::process_bulk_action()Process bulk actions.
Show all 48
- WP_Screen::render_screen_layout()Renders the option for number of columns on the page.
- WP_Site_Health::get_test_page_cache()Tests if a full page cache is available.
- WP_Site_Health::get_test_plugin_version()Tests if plugins are outdated, or unnecessary.
- WP_Site_Health::get_test_theme_version()Tests if themes are outdated, or unnecessary.
- WP_Users_List_Table::single_row()Generates HTML for a single row on the users.php admin panel.
- WP_Widget_Custom_HTML::enqueue_admin_scripts()Loads the required scripts and styles for the widget control.
- __ngettext()Retrieve the plural or single form based on the amount.
- _nc()Legacy version of _n(), which supports contexts.
- _wp_ajax_delete_comment_response()Sends back current comment total and new page links if they need to be updated.
- comments_popup_link()Displays the link to the comments for the current post ID.
- get_comments_number_text()Displays the language string for the number of comments the current post has.
- human_readable_duration()Converts a duration to human readable format.
- human_time_diff()Determines the difference between two timestamps.
- install_plugin_information()Displays plugin information in dialog box form.
- print_embed_comments_button()Prints the necessary markup for the embed comments button.
- render_block_core_comments_title()Renders the `core/comments-title` block on the server.
- render_block_core_post_comments_link()Renders the `core/post-comments-link` block on the server.
- render_block_core_post_time_to_read()Renders the `core/post-time-to-read` block on the server.
- render_block_core_query_total()Renders the `query-total` block on the server.
- rest_validate_array_value_from_schema()Validates an array value based on a schema.
- rest_validate_object_value_from_schema()Validates an object value based on a schema.
- rest_validate_string_value_from_schema()Validates a string value based on a schema.
- translate_nooped_plural()Translates and returns the singular or plural form of a string that's been registered with _n_noop() or _nx_noop().
- twentyfourteen_list_authors()Prints a list of all site contributors who published at least one post.
- validate_plugin_requirements()Validates the plugin requirements for WordPress version and PHP version.
- wp_admin_bar_comments_menu()Adds edit comments link with awaiting moderation count bubble.
- wp_admin_bar_updates_menu()Provides an update link if theme/plugin/core updates are available.
- wp_ajax_replyto_comment()Handles replying to a comment via AJAX.
- wp_dashboard_right_now()Dashboard widget that displays some basic stats about the site.
- wp_dashboard_site_health()Displays the Site Health Status widget.
- wp_default_scripts()Registers all WordPress scripts.
- wp_get_update_data()Collects counts and UI strings for available updates.
- wp_network_dashboard_right_now()
- wp_notify_moderator()Notifies the moderator of the site about a new comment that is awaiting approval.
- wp_star_rating()Outputs a HTML element with a star rating for a given rating.
- wpmu_validate_blog_signup()Processes new site registrations.
Source code
function _n( $single, $plural, $number, $domain = 'default' ) { $translations = get_translations_for_domain( $domain ); $translation = $translations->translate_plural( $single, $plural, $number ); /** * Filters the singular or plural form of a string. * * @since 2.2.0 * * @param string $translation Translated text. * @param string $single The text to be used if the number is singular. * @param string $plural The text to be used if the number is plural. * @param int $number The number to compare against to use either the singular or plural form. * @param string $domain Text domain. Unique identifier for retrieving translated strings. */ $translation = apply_filters( 'ngettext', $translation, $single, $plural, $number, $domain ); /** * Filters the singular or plural form of a string for a domain. * * The dynamic portion of the hook name, `$domain`, refers to the text domain. * * @since 5.5.0 * * @param string $translation Translated text. * @param string $single The text to be used if the number is singular. * @param string $plural The text to be used if the number is plural. * @param int $number The number to compare against to use either the singular or plural form. * @param string $domain Text domain. Unique identifier for retrieving translated strings. */ $translation = apply_filters( "ngettext_{$domain}", $translation, $single, $plural, $number, $domain ); return $translation;}Changelog
Introduced in 5.5.0. Unchanged from 6.7.7 through 7.1.0.
Signature, return type and hooks compared across 5 parsed releases.
ngettext-{$domain} filter.from the docblockAbout this page
- Parsed data
- Generated from the wordpress-develop 7.0.4 tag, from
src/wp-includes/l10n.php, and regenerated for each WordPress release so it tracks the code rather than a snapshot of it. - Corrections
- Something wrong on this page? Report it and it gets fixed in the next regeneration.