{"id":8768,"date":"2019-10-30T14:15:41","date_gmt":"2019-10-30T12:15:41","guid":{"rendered":"https:\/\/phrase.com\/blog\/?p=8036"},"modified":"2024-03-06T20:37:27","modified_gmt":"2024-03-06T19:37:27","slug":"guide-to-the-icu-message-format","status":"publish","type":"post","link":"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/","title":{"rendered":"A Practical Guide to the ICU Message Format"},"content":{"rendered":"\n<div id=\"acf\/text-block_9c63fa074b9d55a2b027b4788d208424\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>The <a href=\"https:\/\/unicode-org.github.io\/icu\/\">International Components for Unicode (ICU)<\/a> is a dependable open-source library suite in the world of software internationalization (i18n) and localization (l10n). Born from the innovative minds at Taligent, IBM, and Sun Microsystems and initially designed for Java and C\/C++, ICU has since expanded its reach to encompass all major programming environments. Its standout feature is a comprehensive toolkit for Unicode text processing, complete with language-specific functions and a wealth of locale data. Plus, ICU shines when it comes to formatting plurals and selectors in translation messages. And it\u2019s no slouch when it comes to parsing and formatting dates, times, and numbers, making it a gem in global software development.<\/p>\n<p>Indeed, ICU has many features, and they can at times seem overwhelming. So this guide focuses on practical aspects of ICU (the portion you\u2019re likely to use day-to-day). We\u2019ll work through pragmatic examples in JavaScript, but you don\u2019t need to be a JS expert to follow along. And we\u2019ll provide links to ICU implementations in other programming languages.<\/p>\n<p>\ud83d\uddd2\ufe0f Internationalization (i18n) and localization (l10n) allow us to make our apps available in different languages and to different regions, often for more profit. If you\u2019re new to i18n and l10n, check out our guide to <a href=\"https:\/\/phrase.com\/blog\/posts\/i18n-a-simple-definition\/\">internationalization<\/a>.<\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_69_1 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Overview<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#how-does-icu-work-with-i18n-libraries\" title=\"How does ICU work with i18n libraries?\">How does ICU work with i18n libraries?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#a-note-on-locales\" title=\"A note on locales\">A note on locales<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#icu-message-format\" title=\"ICU Message Format\">ICU Message Format<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#the-demo\" title=\"The demo\">The demo<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#icu-enabled-libraries\" title=\"ICU-enabled libraries\">ICU-enabled libraries<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#cc\" title=\"C\/C++\">C\/C++<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#java\" title=\"Java\">Java<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#javascript\" title=\"JavaScript\">JavaScript<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#dartflutter\" title=\"Dart\/Flutter\">Dart\/Flutter<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#php\" title=\"PHP\">PHP<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#net\" title=\".NET\">.NET<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#rust\" title=\"Rust\">Rust<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#interpolation\" title=\"Interpolation\">Interpolation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-14\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#plurals\" title=\"Plurals\">Plurals<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-15\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#what-is-the-cldr\" title=\"What is the CLDR?\">What is the CLDR?<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-16\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#ordinal-plurals\" title=\"Ordinal plurals\">Ordinal plurals<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-17\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#select\" title=\"Select\">Select<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-18\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#number-formatting\" title=\"Number formatting\">Number formatting<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-19\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#date-formatting\" title=\"Date formatting\">Date formatting<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-20\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#a-bridge-across-language-barriers\" title=\"A bridge across language barriers\">A bridge across language barriers<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"how-does-icu-work-with-i18n-libraries\"><\/span>How does ICU work with i18n libraries?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Let\u2019s hit the ground running with our first example. Say we\u2019re localizing an app: We often want to decouple our strings out of the UI and put them into message files.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"plaintext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\"># Part of our project folder structure\n.\n\u251c\u2500\u2500 src\n\u2502   \u251c\u2500\u2500 feature1\n\u2502   \u2514\u2500\u2500 feature2\n\u2514\u2500\u2500 translations\n    \u251c\u2500\u2500 en-US.json\n    \u251c\u2500\u2500 ar-EG.json\n    \u2514\u2500\u2500 fr-FR.json\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">plaintext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_e5b5687666eef24f4aa7f00f83495870\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ translations\/en-US.json<\/span>\n{\n  <span class=\"hljs-attr\">\"hello\"<\/span>: <span class=\"hljs-string\">\"Hello, world!\"<\/span>\n}\n\n<span class=\"hljs-comment\">\/\/ translations\/ar-EG.json<\/span>\n{\n  <span class=\"hljs-attr\">\"hello\"<\/span>: <span class=\"hljs-string\">\"\u0645\u0631\u062d\u0628\u0627 \u0628\u0627\u0644\u0639\u0627\u0644\u0645!\"<\/span>\n}\n\n<span class=\"hljs-comment\">\/\/ translations\/fr-FR.json<\/span>\n{\n  <span class=\"hljs-attr\">\"hello\"<\/span>: <span class=\"hljs-string\">\"Bonjour, monde!\"<\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_e5b5687666eef24f4aa7f00f83495870\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ In our UI code<\/span>\n\n<span class=\"hljs-comment\">\/\/ We assume t() is provided by our i18n library,<\/span>\n<span class=\"hljs-comment\">\/\/ and resolves a translation message for the<\/span>\n<span class=\"hljs-comment\">\/\/ active locale given its key.<\/span>\nt(<span class=\"hljs-string\">\"hello\"<\/span>);\n\n<span class=\"hljs-comment\">\/\/ en-US =&gt; \"Hello, world!\"<\/span>\n<span class=\"hljs-comment\">\/\/ fr-FR =&gt; \"Bonjour, monde!\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_bd139eefd2fa0faab11dd8eaf66f25fc\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Our i18n library then manages an <strong>active locale<\/strong> and displays our app translated in that locale using the translations from the appropriate file.<\/p>\n<figure id=\"attachment_76659\" aria-describedby=\"caption-attachment-76659\" style=\"width: 600px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-76659\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/10\/locale-switching.gif\" alt=\"Our message displayed in the translation of the active locale.\" width=\"600\" height=\"129\" \/><figcaption id=\"caption-attachment-76659\" class=\"wp-caption-text\">Our message displayed in the translation of the active locale.<\/figcaption><\/figure>\n<p>Again, this locale management and switching is usually handled by an i18n library like <a href=\"https:\/\/phrase.com\/blog\/posts\/localizing-react-apps-with-i18next\/\">i18next<\/a> or <a href=\"https:\/\/phrase.com\/blog\/posts\/react-i18n-format-js\/\">react-intl<\/a> (JavaScript), the first-party <a href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/\">Flutter intl<\/a> library, or the i18n capabilities of <a href=\"https:\/\/phrase.com\/blog\/posts\/how-to-localize-spring-applications-like-a-pro\/\">Java Spring<\/a>. ICU itself handles the <strong>formatting<\/strong> of messages. We\u2019ve seen plain text messages so far, but we\u2019ll explore ICU\u2019s powerful capabilities more so you can see what we mean.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"a-note-on-locales\"><\/span>A note on locales<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>A locale defines a language, a region, and sometimes more. Locales typically use <a href=\"https:\/\/en.wikipedia.org\/wiki\/IETF_language_tag\">IETF BCP 47 language tags<\/a>, like <code>en<\/code> for English, <code>fr<\/code> for French, and <code>es<\/code> for Spanish. Adding a region with the ISO Alpha-2 code (e.g., <code>BH<\/code> for Bahrain, <code>CN<\/code> for China, <code>US<\/code> for the United States) is recommended for accurate date and number localization. So a complete locale might look like <code>en-US<\/code> for American English or <code>zh-CN<\/code> for Chinese as used in China.<\/p>\n<p>\ud83d\udd17 Explore more language tags on <a href=\"https:\/\/en.wikipedia.org\/wiki\/List_of_ISO_639-1_codes\">Wikipedia<\/a> and find country codes through the ISO&#8217;s <a href=\"https:\/\/www.iso.org\/obp\/ui\/#search\">search tool<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"icu-message-format\"><\/span>ICU Message Format<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>For simple translation messages like the ones above you might opt for a basic i18n library or even create your own, with no need for ICU. However, ICU&#8217;s strengths become evident in the complexity it can handle <strong>within<\/strong> translation messages. To showcase this, let&#8217;s dive into plurals. English has straightforward plural rules (e.g., &#8220;one tree&#8221; vs. &#8220;many trees&#8221;), but other languages may have no plural forms or extremely complex plural rules. ICU is equipped to handle all these variations.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ translations\/en-US.js<\/span>\n{\n  <span class=\"hljs-comment\">\/\/ English only has two plural forms,<\/span>\n  <span class=\"hljs-comment\">\/\/ one and other.<\/span>\n  <span class=\"hljs-string\">\"user_messages\"<\/span>: <span class=\"hljs-string\">`\n    {count, plural,\n      one {You have # message.}\n      other {You have # messages.}\n    }\n  `<\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_e5b5687666eef24f4aa7f00f83495870\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ translations\/ar-EG.js<\/span>\n{\n  <span class=\"hljs-comment\">\/\/ The same message in Arabic needs to<\/span>\n  <span class=\"hljs-comment\">\/\/ cover six plural forms.<\/span>\n  <span class=\"hljs-string\">\"user_messages\"<\/span>: <span class=\"hljs-string\">`\n    {count, plural,\n       zero {\u0644\u064a\u0633 \u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0626\u0644}\n       one {\u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0644\u0629 \u0648\u0627\u062d\u062f\u0629}\n       two {\u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0644\u062a\u064a\u0646}\n       few {\u0644\u062f\u064a\u0643 # \u0631\u0633\u0627\u0626\u0644}\n       many {\u0644\u062f\u064a\u0643 # \u0631\u0633\u0627\u0644\u0629}\n       other {\u0644\u062f\u064a\u0643 # \u0631\u0633\u0627\u0644\u0629}\n   }\n`<\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_e5b5687666eef24f4aa7f00f83495870\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ In our UI code<\/span>\nt(<span class=\"hljs-string\">\"user_messages\"<\/span>, { <span class=\"hljs-attr\">count<\/span>: <span class=\"hljs-number\">9<\/span> });\n\n<span class=\"hljs-comment\">\/\/ en-US =&gt; \"You have 9 messages.\"<\/span>\n<span class=\"hljs-comment\">\/\/ ar-EG =&gt; \"\u0644\u062f\u064a\u0643 \u0669 \u0631\u0633\u0627\u0626\u0644\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_fb5790442ba7da10cd09450499aaa4d1\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-76665\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/10\/en-plurals.gif\" alt=\"The English plural message rendered for different count values.\" width=\"600\" height=\"230\" \/><br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-76671\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/10\/ar-plurals.gif\" alt=\"The Arabic plural message rendered for different count values.\" width=\"600\" height=\"284\" \/><br \/>\nThis module of ICU is known as the ICU <strong>Message Format<\/strong>, and it manages plurals, interpolation, and other complex logic within translation messages. We\u2019ll cover plurals in more detail, as well as the other capabilities of the ICU Message Format syntax, a bit later in this guide. Our goal here is to give you a taste of the power of ICU.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"the-demo\"><\/span>The demo<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>The screen captures above are from an interactive ICU demo that we\u2019ve built as a companion to this article. It\u2019s a React app using the <a href=\"https:\/\/formatjs.io\/docs\/react-intl\/\">React Intl<\/a> i18n library, which has excellent ICU support. You don\u2019t need to know React or React Intl to <a href=\"https:\/\/stackblitz.com\/~\/github.com\/PhraseApp-Blog\/icu-2023\">play with the demo<\/a>, however.<\/p>\n<p>\ud83d\udd17\u00a0Play with ICU features in our <a href=\"https:\/\/stackblitz.com\/~\/github.com\/PhraseApp-Blog\/icu-2023\">interactive demo on StackBlitz<\/a>.<\/p>\n<p>\ud83d\udd17\u00a0You can <a href=\"https:\/\/github.com\/PhraseApp-Blog\/icu-2023\">grab the code of the demo from GitHub<\/a> and run it on your machine instead. Just make sure you have a recent version of <a href=\"https:\/\/nodejs.org\/en\">Node.js<\/a> installed.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"icu-enabled-libraries\"><\/span>ICU-enabled libraries<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>It\u2019s important to clarify at this point that while ICU is canonically a suite of libraries in Java and C++, it\u2019s also become an unofficial standard. So odds are ICU support will either be built into your i18n library, or there will be ICU plugins\/packages for your i18n library or programming language.<\/p>\n<p>For example, the demo we built for this article uses the React Intl library, which sits on top of Format.JS, a JavaScript i18n solution that adheres well to the ICU \u201cstandard\u201d.<\/p>\n<p>So before we continue our round-up of ICU features, let\u2019s go over some of the ICU implementations in a variety of programming environments.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"cc\"><\/span>C\/C++<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><a href=\"https:\/\/unicode-org.github.io\/icu-docs\/apidoc\/released\/icu4c\/\">ICU4C<\/a> \u2014 an official spec and implementation; this and the Java library are where ICU started. Of course, ICU4C is a complete implementation of ICU.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"java\"><\/span>Java<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><a href=\"https:\/\/unicode-org.github.io\/icu-docs\/apidoc\/released\/icu4j\/\">ICU4J<\/a> \u2014 the other official spec and implementation; this and the C\/C++ library are the ICU originals. ICU4J is a complete implementation of ICU.<\/p>\n<p><strong>Java Guides<\/strong><\/p>\n<p>\ud83d\udd17\u00a0<a href=\"https:\/\/phrase.com\/blog\/posts\/java-i18n-guide\/\">The Java i18n Guide You\u2019ve Been Waiting For<\/a> covers Java i18n with the ICU.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"javascript\"><\/span>JavaScript<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>JavaScript doesn&#8217;t have an official, first-party i18n message format. But this is JavaScript we&#8217;re talking about, so of course you have your pick of third-party libraries to use for both browsers and Node.<\/p>\n<ul>\n<li><a href=\"https:\/\/formatjs.io\/\">Format.JS<\/a> \u2014\u00a0built on the ICU message syntax standard, among others, Format.JS is a popular collection of JavaScript i18n libraries.<\/li>\n<li><a href=\"https:\/\/formatjs.io\/docs\/react-intl\">React Intl<\/a> \u2014\u00a0another part of the Format.JS suite, React Intl provides React components and hooks for easy ICU integration with React apps. (The <a href=\"https:\/\/stackblitz.com\/~\/github.com\/PhraseApp-Blog\/icu-2023\">ICU demo playground<\/a> we built for this article was made with React Intl).<\/li>\n<li><a href=\"https:\/\/formatjs.io\/docs\/vue-intl\">Vue Intl<\/a> \u2014 you guessed it: yet another part of Format.JS, Vue Intl eases the usage of ICU in Vue apps.<\/li>\n<li><a href=\"https:\/\/next-intl-docs.vercel.app\/\">Next Intl<\/a> \u2014 while <strong>not<\/strong> a part of Format.JS, next-intl does use the ICU Message Format and easily integrates into Next.js apps (both Page and App Router).<\/li>\n<li><a href=\"https:\/\/angular.io\/guide\/i18n-overview\">Angular<\/a> \u2014 Google&#8217;s popular front-end framework uses ICU in its first-party i18n solution.<\/li>\n<li><a href=\"https:\/\/github.com\/i18next\/i18next-icu\">i18next ICU module<\/a> \u2014 an official ICU extension for the massively popular i18next library.<\/li>\n<\/ul>\n<p><strong>JavaScript Guides<\/strong><\/p>\n<p>\ud83d\udd17\u00a0<a href=\"https:\/\/phrase.com\/blog\/posts\/react-i18n-format-js\/\">A Guide to Localizing React Apps with react-intl\/FormatJS<\/a><\/p>\n<p>\ud83d\udd17\u00a0<a href=\"https:\/\/phrase.com\/blog\/posts\/next-js-app-router-localization-next-intl\/\">A Deep Dive into Next.js App Router Localization with next-intl<\/a><\/p>\n<p>\ud83d\udd17\u00a0<a href=\"https:\/\/phrase.com\/blog\/posts\/angular-localization-i18n\/\">The Ultimate Guide to Angular Localization<\/a><\/p>\n<h3><span class=\"ez-toc-section\" id=\"dartflutter\"><\/span>Dart\/Flutter<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<ul>\n<li><a href=\"https:\/\/pub.dev\/packages\/intl\">intl<\/a> \u2014 the first-party Dart i18n package implements ICU message formatting.<br \/>\n<a href=\"https:\/\/pub.dev\/packages\/flutter_i18n\">Flutter i18n<\/a> \u2014 based on Dart&#8217;s intl, Flutter&#8217;s first-party i18n library also uses ICU message formats.<\/li>\n<\/ul>\n<p><strong>Flutter Guides<\/strong><\/p>\n<p>\ud83d\udd17\u00a0<a href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/\">The Ultimate Guide to Flutter Localization<\/a><\/p>\n<h3><span class=\"ez-toc-section\" id=\"php\"><\/span>PHP<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><a href=\"https:\/\/symfony.com\/doc\/current\/reference\/formats\/message_format.html\">Symfony<\/a> \u2014 the popular web framework is has first-party support for ICU messages.<\/p>\n<p><strong>PHP Guides<\/strong><\/p>\n<p>\ud83d\udd17\u00a0<a href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/\">Symfony Internationalization: A Step-by-Step Guide<\/a><\/p>\n<h3><span class=\"ez-toc-section\" id=\"net\"><\/span>.NET<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/core\/extensions\/globalization-icu\">.NET globalization and ICU<\/a> \u2014\u00a0Microsoft\u2019s .NET environment has first-party ICU support.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"rust\"><\/span>Rust<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><a href=\"https:\/\/docs.rs\/icu\/latest\/icu\/\">ICU4X<\/a> \u2014\u00a0a robust implementation of ICU for Rust.<\/p>\n<p>This just a small sample of ICU-enabled i18n libraries and packages. So if you didn\u2019t find something that works for your environment in the above list make sure to search around more. There are many ICU implementations out there!<\/p>\n<h2><span class=\"ez-toc-section\" id=\"interpolation\"><\/span>Interpolation<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Let\u2019s get back to how we use ICU. First, we\u2019ll take a look at how the ICU message syntax allows us to inject runtime values into our translation messages, otherwise known as interpolation.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ translations\/en-US.json<\/span>\n{\n  <span class=\"hljs-attr\">\"hello_user\"<\/span>: <span class=\"hljs-string\">\"Hello, {name}!\"<\/span>\n}\n\n<span class=\"hljs-comment\">\/\/ translations\/ar-EG.json<\/span>\n{\n  <span class=\"hljs-attr\">\"hello_user\"<\/span>: <span class=\"hljs-string\">\"\u0645\u0631\u062d\u0628\u0627 {name}!\"<\/span>\n}\n\n<span class=\"hljs-comment\">\/\/ translations\/fr-FR.json<\/span>\n{\n  <span class=\"hljs-attr\">\"hello_user\"<\/span>: <span class=\"hljs-string\">\"Bonjour, {name}!\"<\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_e5b5687666eef24f4aa7f00f83495870\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ In our UI code.<\/span>\n<span class=\"hljs-comment\">\/\/ (i18n libraries often provide a<\/span>\n<span class=\"hljs-comment\">\/\/ key\/value for injecting values<\/span>\n<span class=\"hljs-comment\">\/\/ at runtime).<\/span>\nt(<span class=\"hljs-string\">\"hello_user\"<\/span>, { <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">\"Adam\"<\/span> });\n\n<span class=\"hljs-comment\">\/\/ en-US =&gt; \"Hello, Adam!\"<\/span>\n<span class=\"hljs-comment\">\/\/ ar-EG =&gt; \"\u0645\u0631\u062d\u0628\u0627 Adam!\"<\/span>\n<span class=\"hljs-comment\">\/\/ fr-FR =&gt; \"Bonjour, Adam!\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_c1159a2406bf2689b88e2f46f639d0d0\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>We can have as many <code>{variable}<\/code>s in our messages as we want.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-76677\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/10\/interpolation.gif\" alt=\"Adding runtime variables to a translation message and providing values for them.\" width=\"600\" height=\"251\" \/><\/p>\n<h2><span class=\"ez-toc-section\" id=\"plurals\"><\/span>Plurals<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We brushed over plurals a bit earlier so let\u2019s cover them in a bit more detail. As we mentioned before, plurals are more complex than the relatively simple kind found in English: \u201cone street\u201d (the <code>one<\/code> form) and \u201cthree streets\u201d (the <code>other<\/code> form). Each language has a different number of plural forms, and the rules around which form to use can get complicated.<\/p>\n<p>\ud83d\udd17\u00a0The <a href=\"https:\/\/www.unicode.org\/cldr\/charts\/42\/supplemental\/language_plural_rules.html\">CLDR Language Plural Rules<\/a> listing is the canonical source for languages\u2019 plural forms. (We\u2019ll cover the CLDR in the following section).<\/p>\n<p>The ICU Message Format has full support for these plural rules. Let\u2019s look at an English example first. The general syntax here is <code>{counterVariable, plural, ...pluralForms}<\/code>.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">{\n  <span class=\"hljs-string\">\"pokemon_count\"<\/span>:\n    <span class=\"hljs-string\">`{count, plural,\n      one {You have your first Pok\u00e9mon!}\n      other {You have collected # Pok\u00e9mon!}\n    }`<\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_029fc911d4ca01c9550bde07f525dc12\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\ud83d\uddd2\ufe0f\u00a0Generally speaking, the <code>other<\/code> form is always required.<\/p>\n<p>In our code, we would use this message like this:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">t(<span class=\"hljs-string\">\"pokemon_count\"<\/span>, { <span class=\"hljs-attr\">count<\/span>: <span class=\"hljs-number\">1<\/span> });\n\n<span class=\"hljs-comment\">\/\/ =&gt; \"You have your first Pok\u00e9mon!\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_c4eb1c6a9c5e8f67d387062bcd117dae\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-76683\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/10\/en-plurals-1.gif\" alt=\"The one and other forms of English plurals rendering for different counts.\" width=\"600\" height=\"230\" \/><\/p>\n<p>Note that the special <code>#<\/code> character is swapped in for the value of <code>count<\/code>.<\/p>\n<p>But what if we wanted a special case for zero Pok\u00e9mon? No problem. The ICU plural syntax allows overriding any arbitrary value with the special form specifier, <code>=n<\/code>, where <code>n<\/code> is the integer case we\u2019re overriding.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"Diff\" data-shcb-language-slug=\"diff\"><span><code class=\"hljs language-diff\">\/\/ Our translation message\n{\n  \"pokemon_count\":\n    `{count, plural,\n<span class=\"hljs-addition\">+     =0 {Go talk to Professor Oak!}<\/span>\n      one {You have your first Pok\u00e9mon!}\n      other {You have collected # Pok\u00e9mon!}\n    }`\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Diff<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">diff<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_e5b5687666eef24f4aa7f00f83495870\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ Our view code<\/span>\nt(<span class=\"hljs-string\">\"pokemon_count\"<\/span>, { <span class=\"hljs-attr\">count<\/span>: <span class=\"hljs-number\">0<\/span> });\n\n<span class=\"hljs-comment\">\/\/ =&gt; \"Go talk to Professor Oak!\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_e59395d7b88ea28c50c8a56cc9fab89c\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>We could of course use any integer in the <code>=n<\/code> specifier and have as many <code>=n<\/code> specifiers as we want in a message.<\/p>\n<p>As for the sometimes complicated rules around languages\u2019 plural forms, ICU handles those too. Here\u2019s the Arabic example from earlier:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-76689\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/10\/ar-plurals-1.gif\" alt=\"The Arabic plural message rendered for different count values.\" width=\"600\" height=\"284\" \/><\/p>\n<p>Arabic has six plural forms and its <code>few<\/code>, <code>many<\/code>, and <code>other<\/code> forms are not straightforward to resolve. For example, a <code>count<\/code> of <code>103<\/code> resolves to <code>few<\/code>, while <code>102<\/code> resolves to <code>other<\/code>. Arabic translators understand these nuances, and they can use the plural forms afforded by the ICU Message Format to ensure that Arabic readers get the best possible plural translation.<\/p>\n<p>\ud83d\udd17\u00a0Feel free to <a href=\"https:\/\/stackblitz.com\/~\/github.com\/PhraseApp-Blog\/icu-2023\">play with the plural messages on our StackBlitz demo<\/a>. (Alternatively, <a href=\"https:\/\/github.com\/PhraseApp-Blog\/icu-2023\">grab the demo code from GitHub<\/a> and run it on your machine).<\/p>\n<p>\ud83d\uddd2\ufe0f We cover plurals in more detail in our <a href=\"https:\/\/phrase.com\/blog\/posts\/pluralization\/\">Guide to Localizing Plurals<\/a>.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"what-is-the-cldr\"><\/span>What is the CLDR?<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Let\u2019s take a brief sidebar and go over the <a href=\"https:\/\/cldr.unicode.org\/\">CLDR (Common Locale Data Repository)<\/a>. The CLDR is the official Unicode collection of localization data. For a given locale, the CLDR can give you the locale\u2019s script (letters), preferred calendar, number system, date formats, pluralization rules, and more. The CLDR is used by the main ICU project and other libraries that implement the ICU \u201cstandard\u201d.<\/p>\n<p>\ud83d\uddd2\ufe0f\u00a0CLDR data is commonly embedded in i18n libraries, and we often use it without much thought. However, CLDR data must sometimes be manually imported for specific app locales. Always check your i18n library&#8217;s documentation to determine if this step is necessary.<\/p>\n<p>\ud83d\udd17 Check out the <a href=\"http:\/\/cldr.unicode.org\/\">official CLDR documentation<\/a> for more info.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"ordinal-plurals\"><\/span>Ordinal plurals<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Alright, back to the ICU Message Format, and what we can do with it. The plurals we covered above are technically called <strong>cardinal<\/strong> plurals. Another kind is <strong>ordinal<\/strong> plurals: These represent number ranks (e.g., first, second, third), and languages like English have special ordinal forms (e.g., 1st, 2nd, 3rd).<\/p>\n<p>Just like cardinals, languages can vary in the number of ordinals they represent, if any. For example, English has four ordinal forms (<code>one<\/code>, <code>two<\/code>, <code>few<\/code>, and <code>other<\/code>). Many languages have 2-3 ordinal forms; some have one or just the <code>other<\/code> form.<\/p>\n<p>\ud83d\udd17\u00a0The <a href=\"https:\/\/www.unicode.org\/cldr\/charts\/42\/supplemental\/language_plural_rules.html\">CLDR Language Plural Rules<\/a> listing covers both languages\u2019 cardinal and ordinal plural forms.<\/p>\n<p>\ud83d\uddd2\ufe0f We cover ordinal plurals in more detail in our <a href=\"https:\/\/phrase.com\/blog\/posts\/pluralization\/\">Guide to Localizing Plurals<\/a>.<\/p>\n<p>As you\u2019ve probably guessed, the ICU Message Format has excellent support for ordinal plurals. We designate an ordinal in an ICU message using the <code>selectordinal<\/code> keyword.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ English message<\/span>\n{\n  <span class=\"hljs-string\">\"trees_planted\"<\/span>:\n  <span class=\"hljs-string\">`{count, selectordinal,\n     one {We planted our #st tree.}\n     two {We planted our #nd tree.}\n     few {We planted our #rd tree.}\n     other {We planted our #th tree.}\n  }`<\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_3041c6ab08391bb19928a8f025c44e22\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Just like cardinal plurals, the <code>#<\/code> character is replaced with the given <code>count<\/code> at render time.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ In our view code<\/span>\nt(<span class=\"hljs-string\">\"trees_planted\"<\/span>, { <span class=\"hljs-attr\">count<\/span>: <span class=\"hljs-number\">3<\/span> });\n\n<span class=\"hljs-comment\">\/\/ =&gt; \"We planted our 3rd tree.\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_bb8795da9041c4a4dcee0d3d6bf71a32\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-76695\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/10\/en-ordinals.gif\" alt=\"\" width=\"600\" height=\"254\" \/><\/p>\n<h2><span class=\"ez-toc-section\" id=\"select\"><\/span>Select<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>A general conditional expression in ICU messages is <code>select<\/code>: It functions like <code>switch<\/code> or <code>match<\/code> in programming languages.<\/p>\n<p>Just like <code>plural<\/code> and <code>selectordinal<\/code>, for <code>select<\/code> we provide a runtime variable and a few branches in the message. What\u2019s different here is that <strong>we<\/strong> determine what the branches are and what they mean.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ English message<\/span>\n{\n  <span class=\"hljs-string\">\"fruit_picking\"<\/span>:\n    <span class=\"hljs-string\">`{fruit, select,\n       apple {Let's pick apples!}\n       cherry {Let's pick cherries!}\n       other {Let's pick fruits!}\n     }`<\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_e5b5687666eef24f4aa7f00f83495870\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ In our view code<\/span>\n\nt(<span class=\"hljs-string\">\"fruit_picking\"<\/span>, { <span class=\"hljs-attr\">fruit<\/span>: <span class=\"hljs-string\">\"cherry\"<\/span> });\n\n<span class=\"hljs-comment\">\/\/ =&gt; \"Let's pick cherries!\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_5b28fc91a594cae6045a29baa355be7e\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>The <code>select<\/code> keyword is used to designate the message as a conditional. The <code>other<\/code> branch is a fallback (like the <code>default<\/code> case in a <code>switch<\/code> statement). Again, the keywords to resolve the branches are entirely up to us here. In the above message, we went with fruits (<code>apple | cherry<\/code>). Here\u2019s a more typical example:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-76701\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/10\/select-gender.png\" alt=\"Using select to choose the proper gender pronoun.\" width=\"1572\" height=\"636\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/10\/select-gender.png 1572w, https:\/\/phrase.com\/wp-content\/uploads\/2019\/10\/select-gender-300x121.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2019\/10\/select-gender-1024x414.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2019\/10\/select-gender-768x311.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2019\/10\/select-gender-1536x621.png 1536w\" sizes=\"(max-width: 1572px) 100vw, 1572px\" \/><\/p>\n<p>\ud83d\uddd2\ufe0f\u00a0Note that in the above example, the <code>select<\/code> expression is part of a bigger message. This can be done with <code>plural<\/code> and <code>selectordinal<\/code> expressions as well.<\/p>\n<p>\ud83d\udd17\u00a0Feel free to <a href=\"https:\/\/stackblitz.com\/~\/github.com\/PhraseApp-Blog\/icu-2023\">play with the previous examples in our StackBlitz demo<\/a>. (Alternatively, <a href=\"https:\/\/github.com\/PhraseApp-Blog\/icu-2023\">grab the demo code from GitHub<\/a> and run it on your machine).<\/p>\n<h2><span class=\"ez-toc-section\" id=\"number-formatting\"><\/span>Number formatting<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Let\u2019s switch gears and look at numbers. We\u2019ll start with numbers formatted outside of messages. For this, we\u2019ll need to look at an official ICU implementation like <a href=\"https:\/\/unicode-org.github.io\/icu\/userguide\/icu4j\/\">ICU4J (Java)<\/a>. This is because our demo app, built with React Intl, formats our numbers with JavaScript\u2019s <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Intl\/NumberFormat\">Intl.NumberFormat<\/a>, which doesn\u2019t conform to the ICU (although it offers similar capabilities).<\/p>\n<p>Alright, let\u2019s look at a Java example.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">import<\/span> java.util.Locale;\n<span class=\"hljs-keyword\">import<\/span> com.ibm.icu.number.NumberFormatter;\n<span class=\"hljs-keyword\">import<\/span> com.ibm.icu.number.Notation;\n<span class=\"hljs-keyword\">import<\/span> com.ibm.icu.util.Currency;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Main<\/span> <\/span>{\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">main<\/span><span class=\"hljs-params\">(String&#91;] args)<\/span> <\/span>{\n    Locale&#91;] locales = {\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"zh\"<\/span>, <span class=\"hljs-string\">\"CN\"<\/span>), <span class=\"hljs-comment\">\/\/ Mandarin Chinese (China)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"es\"<\/span>, <span class=\"hljs-string\">\"ES\"<\/span>), <span class=\"hljs-comment\">\/\/ Spanish (Spain)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"en\"<\/span>, <span class=\"hljs-string\">\"US\"<\/span>), <span class=\"hljs-comment\">\/\/ English (United States)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"hi\"<\/span>, <span class=\"hljs-string\">\"IN\"<\/span>), <span class=\"hljs-comment\">\/\/ Hindi (India)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"bn\"<\/span>, <span class=\"hljs-string\">\"BD\"<\/span>), <span class=\"hljs-comment\">\/\/ Bengali (Bangladesh)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"pt\"<\/span>, <span class=\"hljs-string\">\"BR\"<\/span>), <span class=\"hljs-comment\">\/\/ Portuguese (Brazil)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"ru\"<\/span>, <span class=\"hljs-string\">\"RU\"<\/span>), <span class=\"hljs-comment\">\/\/ Russian (Russia)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"ja\"<\/span>, <span class=\"hljs-string\">\"JP\"<\/span>), <span class=\"hljs-comment\">\/\/ Japanese (Japan)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"mr\"<\/span>, <span class=\"hljs-string\">\"IN\"<\/span>), <span class=\"hljs-comment\">\/\/ Marathi (India)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"fr\"<\/span>, <span class=\"hljs-string\">\"FR\"<\/span>), <span class=\"hljs-comment\">\/\/ French (France)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"ar\"<\/span>, <span class=\"hljs-string\">\"SA\"<\/span>), <span class=\"hljs-comment\">\/\/ Arabic (Saudi Arabia)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"id\"<\/span>, <span class=\"hljs-string\">\"ID\"<\/span>), <span class=\"hljs-comment\">\/\/ Indonesian (Indonesia)<\/span>\n    };\n\n    <span class=\"hljs-comment\">\/\/ NumberFormat<\/span>\n    System.out.println(<span class=\"hljs-string\">\"Format a number\"<\/span>);\n    System.out.println(<span class=\"hljs-string\">\"---------------\"<\/span>);\n\n    <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">var<\/span> locale : locales) {\n      <span class=\"hljs-keyword\">var<\/span> formattedNumber = NumberFormatter.with()\n          .notation(Notation.compactShort())\n          .unit(Currency.getInstance(<span class=\"hljs-string\">\"EUR\"<\/span>))\n          .locale(locale)\n          .format(<span class=\"hljs-number\">1234<\/span>)\n          .toString();\n\n      <span class=\"hljs-keyword\">var<\/span> output = String.format(<span class=\"hljs-string\">\"%s: %s\"<\/span>, locale, formattedNumber);\n\n      System.out.println(output);\n    }\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_c07d9a1b0a551d490486140317d49fb9\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\ud83d\uddd2\ufe0f\u00a0Make sure to <a href=\"https:\/\/unicode-org.github.io\/icu\/userguide\/icu4j\/#how-to-download-icu4j\">install ICU4J<\/a> if you want to run the code above yourself.<\/p>\n<p>Here\u2019s the output:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"plaintext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">Format a number\n---------------\nzh_CN: \u20ac1200\nes_ES: 1,2 mil \u20ac\nen_US: \u20ac1.2K\nhi_IN: \u20ac1.2 \u0939\u091c\u093c\u093e\u0930\nbn_BD: \u09e7.\u09e8 \u09b9\u09be\u20ac\npt_BR: \u20ac 1,2 mil\nru_RU: 1,2 \u0442\u044b\u0441. \u20ac\nja_JP: \u20ac1200\nmr_IN: \u20ac\u0967.\u0968 \u0939\nfr_FR: 1,2 k \u20ac\nar_SA: \u0661\u066b\u0662 \u0623\u0644\u0641 \u20ac\nid_ID: \u20ac1,2 rb\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">plaintext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_19cf5f7813ee75c98035ffc42a488a69\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Note how were able to use <code>Notation.compactShort()<\/code> to show an abbreviated version of the number (1.2K). Of course, we\u2019re also showing the number as a currency (Euro) value using <code>.unit(Currency.getInstance(\"EUR\"))<\/code>.<\/p>\n<p>ICU is extremely powerful and flexible when it comes to number formatting. We can achieve the above formatting much more succinctly using <a href=\"https:\/\/unicode-org.github.io\/icu\/userguide\/format_parse\/numbers\/skeletons.html\">number skeletons<\/a>, which are compact tokens that define a format.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">import<\/span> java.util.Locale;\n<span class=\"hljs-keyword\">import<\/span> com.ibm.icu.number.NumberFormatter;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Main<\/span> <\/span>{\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">main<\/span><span class=\"hljs-params\">(String&#91;] args)<\/span> <\/span>{\n    Locale&#91;] locales = {\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"zh\"<\/span>, <span class=\"hljs-string\">\"CN\"<\/span>),\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"es\"<\/span>, <span class=\"hljs-string\">\"ES\"<\/span>),\n        <span class=\"hljs-comment\">\/\/ ...<\/span>\n    };\n\n    <span class=\"hljs-comment\">\/\/...<\/span>\n\n    <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">var<\/span> locale : locales) {\n      <span class=\"hljs-keyword\">var<\/span> formatter = NumberFormatter\n        <span class=\"hljs-comment\">\/\/ Use a number skeleton.<\/span>\n        <span class=\"hljs-comment\">\/\/ \"K\" is equivalent to `Notation.compactShort()`<\/span>\n        .forSkeleton(<span class=\"hljs-string\">\"currency\/EUR K\"<\/span>)\n        .locale(locale);\n\n      <span class=\"hljs-keyword\">var<\/span> output = formatter.format(<span class=\"hljs-number\">1234<\/span>).toString();\n\n      System.out.println(output);\n    }\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_b37eee8b2f6771017a8c0da910a918b2\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>This gives us the same output we got before.<\/p>\n<p>We can use number skeletons inside of ICU messages as well. We just need to use the special syntax, <code>{variable, number, ::skeleton}}<\/code>. We can use these skeletons with React Intl (and many other libraries that support the ICU Message Format).<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ English message<\/span>\n<span class=\"hljs-comment\">\/\/ We use a percent skeleton here.<\/span>\n{\n  <span class=\"hljs-string\">\"completion\"<\/span>: <span class=\"hljs-string\">\"{completion, number, ::percent}\"<\/span>\n}\n\n<span class=\"hljs-comment\">\/\/ We can use the above message for all our locale<\/span>\n<span class=\"hljs-comment\">\/\/ translation files, which we're assuming here. We<\/span>\n<span class=\"hljs-comment\">\/\/ can alternatively use a different date format<\/span>\n<span class=\"hljs-comment\">\/\/ for each locale.<\/span>\n\n<span class=\"hljs-comment\">\/\/ In our view code:<\/span>\nt(<span class=\"hljs-string\">\"completion\"<\/span>, { <span class=\"hljs-attr\">completion<\/span>: <span class=\"hljs-number\">0.85<\/span> });\n\n<span class=\"hljs-comment\">\/\/ en-US =&gt; \"85%\"<\/span>\n<span class=\"hljs-comment\">\/\/ bn-BD =&gt; \"\u09ee\u09eb%\"<\/span>\n<span class=\"hljs-comment\">\/\/ ar-SA =&gt; \"\u0668\u0665\u066a\u061c\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_85c6225455402a3f6ccc41e7f1443c7c\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Here\u2019s another example from our <a href=\"https:\/\/stackblitz.com\/~\/github.com\/PhraseApp-Blog\/icu-2023\">demo playground<\/a>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-76707\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2019\/10\/numbers-in-message.gif\" alt=\"Currency and compact short skeletons used to render a number in various locales.\" width=\"600\" height=\"224\" \/><\/p>\n<p>\ud83d\uddd2\ufe0f\u00a0Keep in mind that our demo playground utilizes React Intl, which supports ICU number skeletons in translation messages, but leverages JavaScript\u2019s <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Intl\/NumberFormat\">Intl.NumberFormat<\/a> under the hood to format them. While many of ICU\u2019s number skeletons are likely to be compatible with React Intl, be aware that any unsupported ones might be due to limitations in Intl.NumberFormat.<\/p>\n<p>\ud83d\uddd2\ufe0f Dive deeper into number localization with our <a href=\"https:\/\/phrase.com\/blog\/posts\/number-localization\/\">Concise Guide to Number Localization<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"date-formatting\"><\/span>Date formatting<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>ICU\u2019s flexibility and power extend fully to date formatting. Let\u2019s again start by formatting dates outside of messages. We\u2019ll return to the official Java ICU implementation, <a href=\"https:\/\/unicode-org.github.io\/icu\/userguide\/icu4j\/\">ICU4J (Java)<\/a>. (Our demo app, built with React Intl, uses JavaScript\u2019s <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Intl\/DateTimeFormat\">Intl.DateTimeFormat<\/a>, which, like Intl.NumberFormat offers capabilities akin to ICU but does not fully conform to it).<\/p>\n<p>Here&#8217;s an example in Java:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">import<\/span> java.util.Date;\n<span class=\"hljs-keyword\">import<\/span> java.util.Locale;\n<span class=\"hljs-keyword\">import<\/span> com.ibm.icu.text.SimpleDateFormat;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Main<\/span> <\/span>{\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">main<\/span><span class=\"hljs-params\">(String&#91;] args)<\/span> <\/span>{\n    Locale&#91;] locales = {\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"zh\"<\/span>, <span class=\"hljs-string\">\"CN\"<\/span>), <span class=\"hljs-comment\">\/\/ Mandarin Chinese (China)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"es\"<\/span>, <span class=\"hljs-string\">\"ES\"<\/span>), <span class=\"hljs-comment\">\/\/ Spanish (Spain)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"en\"<\/span>, <span class=\"hljs-string\">\"US\"<\/span>), <span class=\"hljs-comment\">\/\/ English (United States)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"hi\"<\/span>, <span class=\"hljs-string\">\"IN\"<\/span>), <span class=\"hljs-comment\">\/\/ Hindi (India)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"bn\"<\/span>, <span class=\"hljs-string\">\"BD\"<\/span>), <span class=\"hljs-comment\">\/\/ Bengali (Bangladesh)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"pt\"<\/span>, <span class=\"hljs-string\">\"BR\"<\/span>), <span class=\"hljs-comment\">\/\/ Portuguese (Brazil)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"ru\"<\/span>, <span class=\"hljs-string\">\"RU\"<\/span>), <span class=\"hljs-comment\">\/\/ Russian (Russia)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"ja\"<\/span>, <span class=\"hljs-string\">\"JP\"<\/span>), <span class=\"hljs-comment\">\/\/ Japanese (Japan)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"mr\"<\/span>, <span class=\"hljs-string\">\"IN\"<\/span>), <span class=\"hljs-comment\">\/\/ Marathi (India)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"fr\"<\/span>, <span class=\"hljs-string\">\"FR\"<\/span>), <span class=\"hljs-comment\">\/\/ French (France)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"ar\"<\/span>, <span class=\"hljs-string\">\"SA\"<\/span>), <span class=\"hljs-comment\">\/\/ Arabic (Saudi Arabia)<\/span>\n        <span class=\"hljs-keyword\">new<\/span> Locale(<span class=\"hljs-string\">\"id\"<\/span>, <span class=\"hljs-string\">\"ID\"<\/span>), <span class=\"hljs-comment\">\/\/ Indonesian (Indonesia)<\/span>\n    };\n\n    <span class=\"hljs-comment\">\/\/ SimpleDateFormat<\/span>\n    System.out.println(<span class=\"hljs-string\">\"Format a date\"<\/span>);\n    System.out.println(<span class=\"hljs-string\">\"-------------\"<\/span>);\n\n    Date currentDate = <span class=\"hljs-keyword\">new<\/span> Date();\n\n    <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">var<\/span> locale : locales) {\n      <span class=\"hljs-comment\">\/\/ Use a date skeleton to define the<\/span>\n      <span class=\"hljs-comment\">\/\/ format.<\/span>\n      <span class=\"hljs-keyword\">var<\/span> dateFormat = <span class=\"hljs-keyword\">new<\/span> SimpleDateFormat(\n          <span class=\"hljs-string\">\"yyyy.MMMM.dd GGG hh:mm aaa\"<\/span>, locale);\n\n      <span class=\"hljs-keyword\">var<\/span> formattedDate = dateFormat.format(currentDate);\n\n      <span class=\"hljs-keyword\">var<\/span> output = String.format(<span class=\"hljs-string\">\"%s: %s\"<\/span>, locale, formattedDate);\n\n      System.out.println(output);\n    }\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_9ad3c9912dbb00f71113bd2d57026a27\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\ud83d\uddd2\ufe0f Make sure to <a href=\"https:\/\/unicode-org.github.io\/icu\/userguide\/icu4j\/#how-to-download-icu4j\">install ICU4J<\/a> if you want to run the above code.<\/p>\n<p>This will output the current date in different formats based on the locale:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"plaintext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">Format a date\n-------------\nzh_CN: 2023.\u5341\u4e8c\u6708.22 \u516c\u5143 12:00 \u4e0b\u5348\nes_ES: 2023.diciembre.22 d. C. 12:00 p. m.\nen_US: 2023.December.22 AD 12:00 PM\nhi_IN: 2023.\u0926\u093f\u0938\u0902\u092c\u0930.22 \u0908\u0938\u094d\u0935\u0940 12:00 pm\nbn_BD: \u09e8\u09e6\u09e8\u09e9.\u09a1\u09bf\u09b8\u09c7\u09ae\u09cd\u09ac\u09b0.\u09e8\u09e8 \u0996\u09c3\u09b7\u09cd\u099f\u09be\u09ac\u09cd\u09a6 \u09e7\u09e8:\u09e6\u09e6 PM\npt_BR: 2023.dezembro.22 d.C. 12:00 PM\nru_RU: 2023.\u0434\u0435\u043a\u0430\u0431\u0440\u044f.22 \u043d. \u044d. 12:00 PM\nja_JP: 2023.12\u6708.22 \u897f\u66a6 12:00 \u5348\u5f8c\nmr_IN: \u0968\u0966\u0968\u0969.\u0921\u093f\u0938\u0947\u0902\u092c\u0930.\u0968\u0968 \u0907. \u0938. \u0967\u0968:\u0966\u0966 PM\nfr_FR: 2023.d\u00e9cembre.22 ap. J.-C. 12:00 PM\nar_SA: \u0661\u0664\u0664\u0665.\u062c\u0645\u0627\u062f\u0649 \u0627\u0644\u0622\u062e\u0631\u0629.\u0660\u0669 \u0647\u0640 \u0661\u0662:\u0660\u0660 \u0645\nid_ID: 2023.Desember.22 M 12:00 PM\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">plaintext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_6c89726b243e4a32162a66aab05056ee\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Note that we used an <a href=\"https:\/\/unicode-org.github.io\/icu\/userguide\/format_parse\/datetime\/#date-field-symbol-table\">ICU date skeleton<\/a> to define the date format above. There\u2019s a wide variety of formatting options available here, and they can be used in ICU messages as well. As you may have guessed, we need a special syntax in our messages to format dates: <code>{variable, date, ::skeleton}<\/code>.<\/p>\n<p>Let\u2019s see an example in JavaScript:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ Translation message<\/span>\n{\n  <span class=\"hljs-string\">\"eventDate\"<\/span>: <span class=\"hljs-string\">\"{date, date, ::yyyyMMMd}\"<\/span>\n}\n\n<span class=\"hljs-comment\">\/\/ Again, we assume that we're using the same<\/span>\n<span class=\"hljs-comment\">\/\/ message for each locale (but we could use<\/span>\n<span class=\"hljs-comment\">\/\/ a different date format for each locale if<\/span>\n<span class=\"hljs-comment\">\/\/ we wanted to).<\/span>\n\n<span class=\"hljs-comment\">\/\/ In our view code:<\/span>\nt(<span class=\"hljs-string\">\"eventDate\"<\/span>, { <span class=\"hljs-attr\">date<\/span>: <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Date<\/span>(<span class=\"hljs-string\">\"2024-03-12\"<\/span>) });\n\n<span class=\"hljs-comment\">\/\/ en-US =&gt; \"Mar 12, 2024\"<\/span>\n<span class=\"hljs-comment\">\/\/ es-ES =&gt; \"12 mar 2024\"<\/span>\n<span class=\"hljs-comment\">\/\/ zh-CN =&gt; \"2024\u5e743\u670812\u65e5\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_0ba8d4d003b6b409e98080eb1424a8b6\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\ud83d\udd17 In our <a href=\"https:\/\/stackblitz.com\/~\/github.com\/PhraseApp-Blog\/icu-2023\">demo playground<\/a>, you can see different date formats rendered for various locales.<\/p>\n<p>\ud83d\uddd2\ufe0f\u00a0Much like with number formatting, our demo playground only supports a <a href=\"https:\/\/formatjs.io\/docs\/core-concepts\/icu-syntax#supported-datetime-skeleton\">subset of ICU datetime skeletons<\/a>. This is because our demo is built with React Intl, which uses JavaScript\u2019s <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Intl\/DateTimeFormat\">Intl.DateTimeFormat<\/a> for its actual formatting. So some ICU datetime skeletons may not be supported due to Intl.DateTimeFormat&#8217;s limitations.<\/p>\n<p>\ud83d\udd17\u00a0We\u2019re just giving you a taste of what the ICU can offer regarding dates. The libraries can also format <a href=\"https:\/\/unicode-org.github.io\/icu\/userguide\/format_parse\/datetime\/#producing-relative-date-formats-for-a-locale\">relative datetimes<\/a>, work with <a href=\"https:\/\/unicode-org.github.io\/icu\/userguide\/format_parse\/datetime\/#time-zone-display-names\">time zones<\/a>, <a href=\"https:\/\/unicode-org.github.io\/icu\/userguide\/format_parse\/datetime\/#parsing-dates\">parse dates<\/a>, and much more.<\/p>\n<p>\ud83d\udd17\u00a0Feel free to <a href=\"https:\/\/stackblitz.com\/~\/github.com\/PhraseApp-Blog\/icu-2023\">play with the previous examples in our StackBlitz demo<\/a>. (Alternatively, <a href=\"https:\/\/github.com\/PhraseApp-Blog\/icu-2023\">grab the demo code from GitHub<\/a> and run it on your machine).<\/p>\n<h2><span class=\"ez-toc-section\" id=\"a-bridge-across-language-barriers\"><\/span><strong>A bridge across language barriers<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>That brings us to the end of this ICU guide. In our connected world, the role of tools like ICU in i18n and l10n is crucial. With its proficiency in handling plurals, selectors, and complex formats, ICU can be an incredible tool for developers aiming to present software effectively across different languages and regions. And we\u2019ve just scratched the surface of what ICU can do. We hope that this guide has been helpful to you as a start to your work with ICU as you develop software for everyone.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>The ICU message format syntax is used by a number of i18n libraries and can often become a source of confusion itself. Let&#8217;s clear some facts up about it!<\/p>\n","protected":false},"author":41,"featured_media":2612,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_stopmodifiedupdate":false,"_modified_date":"","_searchwp_excluded":"","footnotes":""},"categories":[40],"class_list":["post-8768","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-software-localization"],"acf":[],"_links":{"self":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts\/8768"}],"collection":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/comments?post=8768"}],"version-history":[{"count":7,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts\/8768\/revisions"}],"predecessor-version":[{"id":76713,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts\/8768\/revisions\/76713"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/media\/2612"}],"wp:attachment":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/media?parent=8768"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/categories?post=8768"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}