Setting Up Pluralization in Mobile Apps

TRUETECH is engaged in the development, support and maintenance of iOS, Android, PWA mobile applications. We have extensive experience and expertise in publishing mobile applications in popular markets like Google Play, App Store, Amazon, AppGallery and others.

Development and support of all types of mobile applications:

Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1All 1734 services
Setting Up Pluralization in Mobile Apps
Simple
from 4 hours to 2 days
Frequently Asked Questions

Our competencies:

Development stages

Latest works

  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    858
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    743
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1159
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    1034
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    968
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    562

Imagine: you launch an app with Russian localization, and the user sees '2 товаров' instead of '2 товара'. Sound familiar? Our team of mobile developers with five years of experience has encountered this dozens of times. Proper pluralization is not a whim—it's a requirement for store publication and the foundation of user trust. English has only two forms: one and other. Russian has four: one, few, many, and other. Ignoring this is a guaranteed bug when localizing to any Slavic language. Our solution relies on the CLDR (Common Locale Data Repository) standard, which is supported by all modern frameworks. According to our data, implementing pluralization at the start of a project reduces localization bugs by 80%, and savings on fixing these errors can reach 70% of the localization budget. Get a consultation on implementing pluralization in your project today.

What Problems Does Correct Pluralization Solve?

Grammatical errors in the UI. Incorrect endings (e.g., '5 пользователей' instead of '5 пользователей' in Russian) reduce trust in the product. Problems with store publication: strict rules require correct localization, otherwise release rejection. Extra labor costs: if you don't plan for pluralization initially, rewriting code for each new language is expensive and risky.

Why CLDR Is the Only Correct Standard

CLDR provides a universal set of rules for all languages. Using custom conditions based on the % operator leads to bugs for fractional, negative numbers, and zero. For example, CLDR for Russian returns 'other' for numbers 0, 0.5, 1.5, 2.1, not 'few'. The native solution (plurals.xml / stringsdict) is five times more reliable than custom if-logic—it eliminates bugs for non-standard numbers.

Examples of Forms for Russian According to CLDR

CLDR category Example number Word form
one 1, 21, 31 товар
few 2, 3, 4, 22 товара
many 5, 6, 7, 11 товаров
other 0, 1.5, 2.1 товара

How to Implement Pluralization on Each Platform?

Android. Use the file res/values-ru/plurals.xml:

<plurals name="items_count">
    <item quantity="one">%d товар</item>
    <item quantity="few">%d товара</item>
    <item quantity="many">%d товаров</item>
    <item quantity="other">%d товара</item>
</plurals>

Call: resources.getQuantityString(R.plurals.items_count, count, count). The first count is for form selection, the second count for substitution in %d. Common mistake: passing only one argument—then the number is not substituted into the string.

iOS. Create a Localizable.stringsdict (plist format) with the key and nested rules. The structure includes NSStringLocalizedFormatKey and CLDR categories. For Swift packages with Xcode 15+, use String Catalog—a visual editor for plural forms.

Call: String(format: NSLocalizedString("items_count", comment: ""), count)—automatically selects the correct form.

Flutter. The intl package provides the Intl.plural() method:

Intl.plural(
  count,
  zero: 'нет товаров',
  one: '$count товар',
  few: '$count товара',
  many: '$count товаров',
  other: '$count товара',
  locale: 'ru',
);

Or via ARB files with flutter gen-l10n generation—describe pluralization in app_ru.arb with ICU syntax.

React Native. Use i18next with the i18next-icu plugin or native Intl.PluralRules (available from RN 0.70+). Manually: new Intl.PluralRules('ru').select(count) returns 'one'/'few'/'many'/'other', based on which you select the form from the translations object.

const pluralRules = new Intl.PluralRules('ru');
const form = pluralRules.select(count);
const translations = {
  one: `${count} товар`,
  few: `${count} товара`,
  many: `${count} товаров`,
  other: `${count} товара`,
};
console.log(translations[form]);

Comparison of Approaches

Platform Mechanism Number of forms (Russian) Generation
Android plurals.xml 4 (one/few/many/other) Manual or via localization service
iOS stringsdict / String Catalog 4 (one/few/many/other) Manual or via Xcode
Flutter Intl.plural / ARB 4 (one/few/many/other) Automatic via gen-l10n
React Native i18next-icu / Intl.PluralRules 4 (one/few/many/other) Manual

How to Test Pluralization on Edge Cases?

Be sure to check numbers 0, 1, 2, 5, 21, 1.5, -1. For Russian, 21 belongs to the 'one' form, and 1.5 to 'other'. For each language, these values are different. Write unit tests that run through all edge numbers and check that the correct form is returned. Use the CLDR table to automatically generate test sets.

Typical Errors and Their Solutions

  • Using only one and many without considering few for Slavic languages—bugs for numbers 2, 3, 4.
  • Wrong argument order in Android—the number is not substituted if only one parameter is passed.
  • Ignoring fractional numbers (e.g., 1.5)—without CLDR the 'other' form is not applied.
  • Custom if-logic based on % operator—when adding a new language, you'll have to rewrite conditions.

Our solution completely eliminates these errors by strictly following CLDR and automated testing on edge values.

What's Included in the Work

  1. Audit of the current codebase and translations.
  2. Development of a pluralization scheme for all target languages.
  3. Implementation of native solutions (plurals.xml, stringsdict, ARB, i18next).
  4. Writing tests for edge cases (0, 1, 2, 5, 21, 1.5).
  5. Documentation and team training.

Our team has five years of experience in mobile development and has implemented 30+ projects with localization. We guarantee correct plural form behavior on all devices. The implementation cost pays off in the first months after publication due to reduced bug reports.

Timeline: from four hours (audit + replacement of hardcode) to two days (if pluralization in multiple languages and custom components). Contact us for an audit of your project—we'll propose the optimal solution. Request a consultation today to avoid localization bugs.

Mobile App Localization: i18n, RTL, Dynamic Language Switching

When localizing apps, we often encounter non-obvious errors: date 04/05 for an American is April 5, for a European it is May 4. Amount "1,000.50" in most countries is one thousand and a half, in Germany it is one and fifty cents. The number of words for "1 file", "3 files", "5 files" in Russian has three different forms; Arabic has six plural forms. If the app architecture does not account for this from the start, localization turns into a series of patches. Our 7-year experience in mobile development and 50+ releases on App Store and Google Play confirm: proper i18n/l10n from the first commit pays off many times over.

How to Set Up Basic Localization Infrastructure?

iOS: From Localizable.strings to String Catalogs

Historically, strings in iOS were stored in Localizable.strings — a simple key=value format. With Xcode 15, String Catalogs (.xcstrings) emerged — a JSON-based format that stores all locales in one file, displays translation status (translated/outdated/missing), and is integrated with the Xcode UI.

String(localized: "welcome_title") in Swift 5.7+ replaces NSLocalizedString(...). Shorter, type-safe. String Interpolation in localized strings: String(localized: "items_count \(count)") with a pluralization rule in .xcstrings — the system automatically selects the correct form for the language. Pluralization via .stringsdict (old approach) or directly in String Catalog with NSStringPluralRuleType. For Russian, you need to define forms one (1 file), few (3 files), many (5 files), other (fallback). Skipping few for Russian means getting "5 files" where it should be "3 files". String Catalogs cut setup time by half compared to .stringsdict.

Android: XML Resources and String Formats

res/values/strings.xml for base locale (en), res/values-ru/strings.xml for Russian. Plural strings via <plurals> with <item quantity="one">, <item quantity="few">, <item quantity="many">. resources.getQuantityString(R.plurals.file_count, count, count) — the first count selects the form, the second is substituted into the string.

In Compose: stringResource(R.string.key) and pluralStringResource(R.plurals.file_count, count, count). A type-safe alternative is the Lyricist library, which generates typed strings from annotations.

Android App Bundle with android:splitByLocale="true" in bundle.gradle — resources are delivered only for device languages. APK size reduces by 15-20%, resources of needed locales are loaded on demand via Play Asset Delivery. Important: on Android 8+ Configuration.locales is a list, not a single language.

Flutter: intl and Abstraction Layers

Flutter intl package is the standard. AppLocalizations.of(context).welcomeTitle is generated from ARB files (app_en.arb, app_ru.arb). flutter gen-l10n generates typed code. Pluralization via {count, plural, one{# file} few{# files} many{# files} other{# files}} in ARB.

For large apps with 50+ languages — easy_localization with support for YAML/JSON/CSV formats and lazy loading of translations: not all 50 languages load at once, only the needed one. This reduces initial load size by 30%.

Comparison Table of Approaches

Parameter iOS (String Catalogs) Android (XML) Flutter (ARB)
Storage format JSON (.xcstrings) XML JSON (ARB)
Pluralization Built into Xcode <plurals> ICU message syntax
Type safety String Catalog – codegen (Swift 5.9) R.java / ViewBinding gen-l10n
Translation status Visual in Xcode Third-party tools only Third-party tools only
RTL out of the box Auto Layout (leading/trailing) supportsRtl + start/end Directionality Widget

How to Implement RTL Support Without Rewriting UI?

Arabic, Hebrew, Persian, Urdu are RTL (Right-to-Left) languages. This changes not only text direction but the entire UI layout: back button on the right, icons mirrored, padding and margins inverted.

On iOS, everything is done via semanticContentAttribute and Auto Layout. Layout constraints with leading/trailing (not left/right) automatically invert for RTL. UIView.semanticContentAttribute = .forceRightToLeft for a specific component. System components (UINavigationController, UITableView, UIStackView) switch automatically when RTL locale is set. Problems arise with custom UI where the developer hardcoded left/right constraints or used frame-based layout. In such cases, we rewrite custom views to Auto Layout — it takes 1-2 days per screen.

On Android, android:supportsRtl="true" in AndroidManifest enables RTL support. Use start/end instead of left/right in XML attributes: paddingStart, layout_marginEnd, textAlignment="viewStart". Use LayoutInflater with android:layoutDirection="rtl" for preview. Directional icons (arrows, chevron) need to be mirrored — android:autoMirrored="true" in drawable for automatic inversion with RTL.

On Flutter, Directionality widget with TextDirection.rtl controls direction for the subtree. Use Padding(EdgeInsetsDirectional.fromSTEB(...)) instead of EdgeInsets.only(left:...). Row automatically respects TextDirection from Directionality. Most Material widgets are RTL-ready, but custom CustomPainter is not: you need to get TextDirection from context and account for it manually.

Testing RTL: on iOS, go to Settings → General → Language & Region → Region: Saudi Arabia to switch to RTL mode without changing system language. On Android, adb shell setprop debug.force.rtl 1 forces RTL for debugging. This catches up to 80% of RTL issues before release.

Why Is Dynamic Language Switching a Bottleneck?

Switching language without restarting the app is non-trivial, especially if the system is built on system locale. We identified three main approaches.

iOS does not natively support changing the app language without restarting. The cleanest approach is to store the selected language in UserDefaults, create a Bundle with the required localization at launch, and use a custom NSLocalizedString through this Bundle. Bundle.setLanguage("ru") via swizzling Bundle.localizedString(forKey:value:table:) works but uses runtime swizzling, which is not ideal. Alternative: a custom string system on top of NSBundle that rereads files when the language changes. On switch, recreate the root ViewController.

Android with API 33: LocaleManager.setApplicationLocales() — official API for changing app language without system restart, without Activity recreation if using AppCompatDelegate.setApplicationLocales(). Below API 33 — Configuration.setLocale() + recreate() for Activity. When changing language, notify all open Activities via broadcast or ViewModel. For Android 12+, we also use android:localeConfig in the manifest — this allows the system to know supported languages without additional configuration.

Flutter — the simplest of the three. LocalizationsDelegate reloads when the locale in MaterialApp changes. Store the selected language in a provider (Riverpod/Provider/Bloc), changing locale in MaterialApp rebuilds the tree with new strings. Virtually no boilerplate when using easy_localization. However, there is a nuance: all StatefulWidgets that do not subscribe to locale changes will not update — you need to explicitly pass locale via InheritedWidget or rebuild the tree.

How to Format Dates, Numbers, and Currencies?

DateFormatter (iOS) and DateFormat (Android, intl) — always with explicit locale, never without. DateFormatter().dateStyle = .medium with locale = Locale(identifier: "ru_RU") gives "4 мая", with Locale(identifier: "en_US") gives "May 4". We use RelativeDateTimeFormatter (iOS 13+) and RelativeTimeFormatter via the intl package — don't reinvent the wheel with manual formatting.

NumberFormatter / NumberFormat.currency() for currencies. Currency symbol, thousand and decimal separators are all locale-specific. Hardcoding "₽" or "." as separator is an error. Locale(identifier: "ru_RU") + NumberFormatter.numberStyle = .currency with currencyCode = "RUB" gives correct formatting automatically.

What Are Typical Localization Mistakes?

String concatenation instead of formatting: "Hello, " + name + "!" works for SVO languages, but in Japanese the name comes before the greeting. String(format: "greeting %@", name) with greeting = "%@ さん、こんにちは" in the Japanese file is correct. Fixed UI size for text: German is on average 30% longer than English. Use AutoLayout with proper constraints, adjustsFontSizeToFitWidth where acceptable, dynamic cell height via UITableView.automaticDimension. Images with embedded text require localized versions or replacement with text overlay.

Work Process: How We Localize an App Turnkey

  1. Analysis and audit (2-5 days) — code review for i18n readiness, identify hardcoded strings, assess RTL complexity, prepare screen map.
  2. Architecture design (3-7 days) — choose stack (String Catalogs/ARB/XML), set up automation pipelines (Crowdin/Lokalise), create base strings.
  3. Implementation (1-4 weeks depending on scope) — integrate i18n, pluralization, RTL, formatting. In parallel, translate content.
  4. Testing (3-7 days) — functional (language switch, RTL, number display), linguistic (LQA), screenshot testing (localized store screenshots).
  5. Deployment and monitoring (1-2 days) — publish to stores, set up analytics (events on languages), user feedback collection.

Deliverables

  • Source code with full i18n infrastructure
  • Documentation on adding a new language (playbook)
  • Translation files (ARB/XML/xcstrings) + glossary
  • Automation: CI/CD integration with translation services
  • Access to repository with full revision history
  • Training for the client's team (1-2 sessions)
  • Code warranty — 6 months, free localization bug fixes

Timeline and Cost

Stage Timeline (approx.) What's included
Adding one new language (no RTL) 2-3 days technical work + translation time String setup, testing, store screenshots
First-time localization from scratch (10-15 screens, no RTL) 2-3 weeks Architecture, translation, testing
Project with RTL support and dynamic switching 4-6 weeks Everything included + UI adaptation, custom component rewrite

Pricing is customized based on project scope. Contact us for a detailed estimate. On average, automation achieves 40% budget savings. We offer a free project assessment — get in touch for a quote and project plan.

Why Choose Us

We are certified developers (Apple WWDC Scholarship, Google Associate Android Developer). Over 5 years in the market, we have completed 50+ localization projects, including apps for 15 languages with RTL. We guarantee compliance with App Store Review Guidelines (Section 4.2/5.1) and Google Play Policies (User Data). Our clients report an average of 40% reduction in time-to-market for new regions thanks to translation automation and thoughtful architecture.

Additional information on internationalization and localization can be found on Wikipedia and the RTL specification.

Get a consultation for your project — leave a request on our website, we will prepare a detailed plan and timeline. Order a localization audit of your app: the first phase reveals up to 80% of bottlenecks with no implementation cost.