Налаштування форматування дат та чисел під локаль у мобільній програмі
Програма показує «1,234.56» користувачу з Німеччини, у якого це виглядає як «одна тисяча двісті тридцять чотири цілих п'ятьдесят шість». Або дату «04/05/2024» — квітня п'яте або травня четверте? Залежить від локалі користувача, та це не просто косметика: у фінансових та медичних програмах неправильне форматування — баг з реальними наслідками.
Де конкретно ломається
Android. String.format("%.2f", price) — використовує Locale.getDefault() системи, але лише якщо не передати локаль явно. На деяких версіях MIUI Locale.getDefault() повертає Locale.US незалежно від налаштувань користувача. Правильно: String.format(Locale.getDefault(Locale.Category.FORMAT), "%.2f", price). Або NumberFormat.getInstance(locale).format(price).
SimpleDateFormat — не потокобезпечний та застарілий. DateTimeFormatter (java.time, API 26+) або ThreeTenABP для старих версій. Паттерн "dd/MM/yyyy" — hardcod, не враховує локаль. DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(locale) — правильний шлях.
iOS. DateFormatter без явно заданого locale бере Locale.current — здається, правильно. Але якщо DateFormatter створюється у фоновому потоці та кешується, він може захопити locale до того, як користувач сменив мову у програмі. NumberFormatter аналогічно. Правило: formatter.locale = Locale(identifier: userSelectedLocale) — завжди явно.
Locale(identifier: "ru_RU") vs Locale(identifier: "ru") — різниця є: з регіоном застосовуються регіональні налаштування чисел та дат (у Росії розділювач цілої/дробної частини — кома, у Беларусі — теж, але формати телефонів інші).
Flutter. Пакет intl: DateFormat.yMd(locale).format(date), NumberFormat.currency(locale: locale, symbol: symbol).format(amount). Важливо: intl не включає locale-дані автоматично — потрібна initializeDateFormatting(locale) перед першим використанням, інакше MissingLocaleDataException у рантаймі.
Валюта — окрема історія
NumberFormat.getCurrencyInstance(locale).format(amount) — форматує суму з символом валюти за правилами локалі. Але символ та його позиція залежать від locale: у en_US це $1,234.56, у de_DE — 1.234,56 €, у ru_RU — 1 234,56 ₽. Для мультивалютної програми символ передавайте явно через NumberFormat.getCurrencyInstance(locale).apply { currency = Currency.getInstance("EUR") }.
Окрема біль — тисячні розділювачі. У hi_IN (хінді) групування не трійками, а за схемою 2-2-3 (лакхи): 1,23,456. NumberFormat це підтримує, користувацька реалізація через replace — ні.
Що робимо
Аудит всіх місць форматування у коді: grep -rn "SimpleDateFormat\|String.format\|\.toString()". Замінюємо на locale-aware форматтери. Додаємо утилітний шар LocaleFormatter / AppFormatter — централізоване місце, де locale підставляється з поточного контексту програми. Пишемо unit-тести з кількома locale-фіксачами (de_DE, ar_SA, hi_IN).
Часові рамки: 1 день для програми без користувацьких компонентів відображення. Стоимость розраховується індивідуально.







