diff --git a/.github/workflows/linux-check.yaml b/.github/workflows/linux-check.yaml index a73561480f..aa9e977fba 100644 --- a/.github/workflows/linux-check.yaml +++ b/.github/workflows/linux-check.yaml @@ -169,5 +169,14 @@ jobs: # world_feed_integration_tests - https://github.com/organicmaps/organicmaps/issues/215 CTEST_EXCLUDE_REGEX: "drape_tests|generator_integration_tests|opening_hours_integration_tests|opening_hours_supported_features_tests|routing_benchmarks|routing_integration_tests|routing_quality_tests|search_quality_tests|storage_integration_tests|shaders_tests|world_feed_integration_tests" run: | + sudo locale-gen en_US + sudo locale-gen en_US.UTF-8 + sudo locale-gen es_ES + sudo locale-gen es_ES.UTF-8 + sudo locale-gen fr_FR + sudo locale-gen fr_FR.UTF-8 + sudo locale-gen ru_RU + sudo locale-gen ru_RU.UTF-8 + sudo update-locale ln -s ../data data ctest -LE "fixture" -E "$CTEST_EXCLUDE_REGEX" --output-on-failure diff --git a/android/app/src/main/cpp/app/organicmaps/Framework.cpp b/android/app/src/main/cpp/app/organicmaps/Framework.cpp index 9b8a1c1bbe..6cbca67c2b 100644 --- a/android/app/src/main/cpp/app/organicmaps/Framework.cpp +++ b/android/app/src/main/cpp/app/organicmaps/Framework.cpp @@ -39,6 +39,7 @@ #include "platform/country_file.hpp" #include "platform/local_country_file.hpp" #include "platform/local_country_file_utils.hpp" +#include "platform/locale.hpp" #include "platform/location.hpp" #include "platform/localization.hpp" #include "platform/measurement_utils.hpp" @@ -1294,6 +1295,10 @@ Java_app_organicmaps_Framework_nativeGenerateRouteAltitudeChartBits(JNIEnv * env totalDescent = measurement_utils::MetersToFeet(totalDescent); } + jni::TScopedLocalRef const totalAscentString(env, jni::ToJavaString(env, ToStringPrecision(totalAscent, 0))); + + jni::TScopedLocalRef const totalDescentString(env, jni::ToJavaString(env, ToStringPrecision(totalDescent, 0))); + // Passing route limits. // Do not use jni::GetGlobalClassRef, because this class is used only to init static fieldId vars. static jclass const routeAltitudeLimitsClass = env->GetObjectClass(routeAltitudeLimits); @@ -1307,6 +1312,14 @@ Java_app_organicmaps_Framework_nativeGenerateRouteAltitudeChartBits(JNIEnv * env ASSERT(totalDescentField, ()); env->SetIntField(routeAltitudeLimits, totalDescentField, static_cast(totalDescent)); + static jfieldID const totalAscentStringField = env->GetFieldID(routeAltitudeLimitsClass, "totalAscentString", "Ljava/lang/String;"); + ASSERT(totalAscentStringField, ()); + env->SetObjectField(routeAltitudeLimits, totalAscentStringField, totalAscentString.get()); + + static jfieldID const totalDescentStringField = env->GetFieldID(routeAltitudeLimitsClass, "totalDescentString", "Ljava/lang/String;"); + ASSERT(totalDescentStringField, ()); + env->SetObjectField(routeAltitudeLimits, totalDescentStringField, totalDescentString.get()); + static jfieldID const isMetricUnitsField = env->GetFieldID(routeAltitudeLimitsClass, "isMetricUnits", "Z"); ASSERT(isMetricUnitsField, ()); env->SetBooleanField(routeAltitudeLimits, isMetricUnitsField, units == Units::Metric); diff --git a/android/app/src/main/cpp/app/organicmaps/platform/Language.cpp b/android/app/src/main/cpp/app/organicmaps/platform/Language.cpp index ebcd6adbdd..1cbe2d9540 100644 --- a/android/app/src/main/cpp/app/organicmaps/platform/Language.cpp +++ b/android/app/src/main/cpp/app/organicmaps/platform/Language.cpp @@ -50,8 +50,18 @@ Locale GetCurrentLocale() "()Ljava/lang/String;"); jni::ScopedLocalRef currencyCode(env, env->CallStaticObjectMethod(g_utilsClazz, getCurrencyCodeId)); + static jmethodID const getDecimalSeparatorId = jni::GetStaticMethodID(env, g_utilsClazz, "getDecimalSeparator", + "()Ljava/lang/String;"); + jni::ScopedLocalRef decimalSeparatorChar(env, env->CallStaticObjectMethod(g_utilsClazz, getDecimalSeparatorId)); + + static jmethodID const getGroupingSeparatorId = jni::GetStaticMethodID(env, g_utilsClazz, "getGroupingSeparator", + "()Ljava/lang/String;"); + jni::ScopedLocalRef groupingSeparatorChar(env, env->CallStaticObjectMethod(g_utilsClazz, getGroupingSeparatorId)); + return {jni::ToNativeString(env, static_cast(languageCode.get())), jni::ToNativeString(env, static_cast(countryCode.get())), - currencyCode.get() ? jni::ToNativeString(env, static_cast(currencyCode.get())) : ""}; + currencyCode.get() ? jni::ToNativeString(env, static_cast(currencyCode.get())) : "", + jni::ToNativeString(env, static_cast(decimalSeparatorChar.get())), + jni::ToNativeString(env, static_cast(groupingSeparatorChar.get()))}; } } // namespace platform diff --git a/android/app/src/main/java/app/organicmaps/Framework.java b/android/app/src/main/java/app/organicmaps/Framework.java index 7cbb8d1ab5..97d1d9f545 100644 --- a/android/app/src/main/java/app/organicmaps/Framework.java +++ b/android/app/src/main/java/app/organicmaps/Framework.java @@ -120,6 +120,8 @@ public class Framework { public int totalAscent; public int totalDescent; + public String totalAscentString; + public String totalDescentString; public boolean isMetricUnits; } diff --git a/android/app/src/main/java/app/organicmaps/routing/RoutingBottomMenuController.java b/android/app/src/main/java/app/organicmaps/routing/RoutingBottomMenuController.java index 17968e678b..0c0ac00dc4 100644 --- a/android/app/src/main/java/app/organicmaps/routing/RoutingBottomMenuController.java +++ b/android/app/src/main/java/app/organicmaps/routing/RoutingBottomMenuController.java @@ -343,9 +343,8 @@ final class RoutingBottomMenuController implements View.OnClickListener mAltitudeChart.setImageBitmap(bm); UiUtils.show(mAltitudeChart); final String unit = limits.isMetricUnits ? mAltitudeDifference.getResources().getString(R.string.m) : mAltitudeDifference.getResources().getString(R.string.ft); - mAltitudeDifference.setText(String.format(Locale.getDefault(), "↗ %d %s ↘ %d %s", - limits.totalAscent, unit, - limits.totalDescent, unit)); + mAltitudeDifference.setText("↗ " + limits.totalAscentString + " " + unit + + " ↘ " + limits.totalDescentString + " " + unit); UiUtils.show(mAltitudeDifference); } } diff --git a/android/app/src/main/java/app/organicmaps/util/Utils.java b/android/app/src/main/java/app/organicmaps/util/Utils.java index 013557881e..d3dd73e1e0 100644 --- a/android/app/src/main/java/app/organicmaps/util/Utils.java +++ b/android/app/src/main/java/app/organicmaps/util/Utils.java @@ -53,6 +53,7 @@ import java.io.IOException; import java.io.Serializable; import java.lang.ref.WeakReference; import java.text.DateFormat; +import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.Arrays; @@ -495,6 +496,18 @@ public class Utils return Locale.getDefault().getLanguage(); } + @NonNull + public static String getDecimalSeparator() + { + return String.valueOf(DecimalFormatSymbols.getInstance().getDecimalSeparator()); + } + + @NonNull + public static String getGroupingSeparator() + { + return String.valueOf(DecimalFormatSymbols.getInstance().getGroupingSeparator()); + } + @Nullable public static Currency getCurrencyForLocale(@NonNull Locale locale) { diff --git a/iphone/CoreApi/CoreApi/Common/MWMGeoUtil.mm b/iphone/CoreApi/CoreApi/Common/MWMGeoUtil.mm index 30bf92cd7c..92d75df42f 100644 --- a/iphone/CoreApi/CoreApi/Common/MWMGeoUtil.mm +++ b/iphone/CoreApi/CoreApi/Common/MWMGeoUtil.mm @@ -3,6 +3,7 @@ #include "geometry/mercator.hpp" #include "geometry/angles.hpp" +#include "platform/locale.hpp" #include "platform/localization.hpp" #include "platform/settings.hpp" #include "platform/measurement_utils.hpp" @@ -31,10 +32,8 @@ */ - (NSString*) valueAsString { - if (self.value > 9.999) - return [NSString stringWithFormat:@"%.0f", self.value]; - else - return [NSString stringWithFormat:@"%.1f", self.value]; + auto const outString = measurement_utils::ToStringPrecision(self.value, self.value >= 10.0 ? 0 : 1); + return [NSString stringWithUTF8String:outString.c_str()]; } - (instancetype)initAsSpeed:(double) mps { diff --git a/platform/distance.cpp b/platform/distance.cpp index 9f808c80b0..36cd589f37 100644 --- a/platform/distance.cpp +++ b/platform/distance.cpp @@ -1,5 +1,6 @@ #include "distance.hpp" +#include "platform/locale.hpp" #include "platform/localization.hpp" #include "platform/measurement_utils.hpp" @@ -138,9 +139,7 @@ std::string Distance::GetDistanceString() const if (m_distance < 10.0 && IsHighUnits()) precision = 1; - std::ostringstream os; - os << std::fixed << std::setprecision(precision) << m_distance; - return os.str(); + return ToStringPrecision(m_distance, precision); } std::string Distance::GetUnitsString() const @@ -194,7 +193,7 @@ std::string Distance::ToString() const if (!IsValid()) return ""; - return GetDistanceString() + " " + GetUnitsString(); + return GetDistanceString() + kNarrowNonBreakingSpace + GetUnitsString(); } std::string DebugPrint(Distance::Units units) diff --git a/platform/locale.hpp b/platform/locale.hpp index 07255f2a9f..987838b998 100644 --- a/platform/locale.hpp +++ b/platform/locale.hpp @@ -4,13 +4,19 @@ namespace platform { +std::string const kNonBreakingSpace = "\u00A0"; +std::string const kNarrowNonBreakingSpace = "\u202F"; + struct Locale { public: std::string m_language; std::string m_country; std::string m_currency; + std::string m_decimalSeparator; + std::string m_groupingSeparator; }; Locale GetCurrentLocale(); +bool GetLocale(std::string localeName, Locale& result); } // namespace platform diff --git a/platform/locale.mm b/platform/locale.mm index 9302007efb..0e2854e30f 100644 --- a/platform/locale.mm +++ b/platform/locale.mm @@ -4,11 +4,28 @@ namespace platform { -Locale GetCurrentLocale() +Locale NSLocale2Locale(NSLocale *locale) { - NSLocale * locale = [NSLocale currentLocale]; return {locale.languageCode ? [locale.languageCode UTF8String] : "", locale.countryCode ? [locale.countryCode UTF8String] : "", - locale.currencyCode ? [locale.currencyCode UTF8String] : ""}; + locale.currencyCode ? [locale.currencyCode UTF8String] : "", + locale.decimalSeparator ? [locale.decimalSeparator UTF8String] : ".", + locale.groupingSeparator ? [locale.groupingSeparator UTF8String] : kNarrowNonBreakingSpace}; +} + +Locale GetCurrentLocale() +{ + return NSLocale2Locale([NSLocale currentLocale]); +} + +bool GetLocale(std::string localeName, Locale& result) +{ + NSLocale *loc = [[NSLocale alloc] initWithLocaleIdentifier: @(localeName.c_str())]; + + if (!loc) + return false; + + result = NSLocale2Locale(loc); + return true; } } // namespace platform diff --git a/platform/locale_linux.cpp b/platform/locale_linux.cpp index df68fe9e2d..bdc65383b1 100644 --- a/platform/locale_linux.cpp +++ b/platform/locale_linux.cpp @@ -1,10 +1,41 @@ #include "platform/locale.hpp" +#include + namespace platform { +Locale StdLocale2Locale(std::locale loc) +{ + return {"", + "", + std::use_facet>(loc).curr_symbol(), + std::string(1, std::use_facet>(loc).decimal_point()), + std::string(1, std::use_facet>(loc).thousands_sep())}; +} + Locale GetCurrentLocale() { - return {"", "", ""}; + // Environment's default locale. + std::locale loc; + + return StdLocale2Locale(loc); } + +bool GetLocale(std::string const localeName, Locale& result) +{ + try + { + std::locale loc(localeName); + + result = StdLocale2Locale(loc); + + return true; + } + catch(...) + { + return false; + } +} + } // namespace platform diff --git a/platform/measurement_utils.cpp b/platform/measurement_utils.cpp index 01cc03f36d..91774d6ba6 100644 --- a/platform/measurement_utils.cpp +++ b/platform/measurement_utils.cpp @@ -1,5 +1,5 @@ +#include "platform/locale.hpp" #include "platform/measurement_utils.hpp" - #include "platform/settings.hpp" #include "geometry/mercator.hpp" @@ -18,19 +18,45 @@ namespace measurement_utils { +using namespace platform; using namespace settings; using namespace std; using namespace strings; -namespace -{ string ToStringPrecision(double d, int pr) +{ + // We assume that the app will be restarted if a user changes device's locale. + static Locale const loc = GetCurrentLocale(); + + return ToStringPrecisionLocale(loc, d, pr); +} + +string ToStringPrecisionLocale(Locale loc, double d, int pr) { stringstream ss; ss << setprecision(pr) << fixed << d; - return ss.str(); + string out = ss.str(); + + // std::locale does not work on Android NDK, so decimal and grouping (thousands) separator + // shall be customized manually here. + + if (pr) + { + // Value with decimals. Set locale decimal separator. + if (loc.m_decimalSeparator != ".") + out.replace(out.size() - pr - 1, 1, loc.m_decimalSeparator); + } + else + { + // Value with no decimals. Check if it's equal or bigger than 10000 to + // insert the grouping (thousands) separator characters. + if (out.size() > 4 && !loc.m_groupingSeparator.empty()) + for (int pos = out.size() - 3; pos > 0; pos -= 3) + out.insert(pos, loc.m_groupingSeparator); + } + + return out; } -} // namespace std::string DebugPrint(Units units) { diff --git a/platform/measurement_utils.hpp b/platform/measurement_utils.hpp index a20e538db6..4d29d790d7 100644 --- a/platform/measurement_utils.hpp +++ b/platform/measurement_utils.hpp @@ -1,6 +1,7 @@ #pragma once #include "geometry/point2d.hpp" +#include "platform/locale.hpp" #include @@ -62,4 +63,6 @@ bool OSMDistanceToMeters(std::string const & osmRawValue, double & outMeters); std::string OSMDistanceToMetersString(std::string const & osmRawValue, bool supportZeroAndNegativeValues = true, int digitsAfterComma = 2); +std::string ToStringPrecision(double d, int pr); +std::string ToStringPrecisionLocale(platform::Locale loc, double d, int pr); } // namespace measurement_utils diff --git a/platform/platform_tests/distance_tests.cpp b/platform/platform_tests/distance_tests.cpp index 9a8f129178..ae26fdc8e6 100644 --- a/platform/platform_tests/distance_tests.cpp +++ b/platform/platform_tests/distance_tests.cpp @@ -6,6 +6,11 @@ namespace platform { +std::string MakeDistanceStr(std::string const & value, std::string const & unit) +{ + return value + kNarrowNonBreakingSpace + unit; +} + struct ScopedSettings { /// Saves/restores previous units and sets new units for a scope. @@ -45,7 +50,7 @@ UNIT_TEST(Distance_CreateFormatted) TEST_EQUAL(d.GetUnits(), Distance::Units::Meters, ()); TEST_ALMOST_EQUAL_ULPS(d.GetDistance(), 100.0, ()); TEST_EQUAL(d.GetDistanceString(), "100", ()); - TEST_EQUAL(d.ToString(), "100 m", ()); + TEST_EQUAL(d.ToString(), MakeDistanceStr("100", "m"), ()); } { ScopedSettings guard(measurement_utils::Units::Imperial); @@ -54,7 +59,7 @@ UNIT_TEST(Distance_CreateFormatted) TEST_EQUAL(d.GetUnits(), Distance::Units::Feet, ()); TEST_ALMOST_EQUAL_ULPS(d.GetDistance(), 330.0, ()); TEST_EQUAL(d.GetDistanceString(), "330", ()); - TEST_EQUAL(d.ToString(), "330 ft", ()); + TEST_EQUAL(d.ToString(), MakeDistanceStr("330", "ft"), ()); } } @@ -67,7 +72,25 @@ UNIT_TEST(Distance_CreateAltitudeFormatted) TEST_EQUAL(d.GetUnits(), Distance::Units::Meters, ()); TEST_ALMOST_EQUAL_ULPS(d.GetDistance(), 5.0, ()); TEST_EQUAL(d.GetDistanceString(), "5", ()); - TEST_EQUAL(d.ToString(), "5 m", ()); + TEST_EQUAL(d.ToString(), MakeDistanceStr("5", "m"), ()); + } + { + ScopedSettings guard(measurement_utils::Units::Metric); + + Distance d = Distance::CreateAltitudeFormatted(8849); + TEST_EQUAL(d.GetUnits(), Distance::Units::Meters, ()); + TEST_ALMOST_EQUAL_ULPS(d.GetDistance(), 8849.0, ()); + TEST_EQUAL(d.GetDistanceString(), "8849", ()); + TEST_EQUAL(d.ToString(), MakeDistanceStr("8849", "m"), ()); + } + { + ScopedSettings guard(measurement_utils::Units::Metric); + + Distance d = Distance::CreateAltitudeFormatted(12345); + TEST_EQUAL(d.GetUnits(), Distance::Units::Meters, ()); + TEST_ALMOST_EQUAL_ULPS(d.GetDistance(), 12345.0, ()); + TEST_EQUAL(d.GetDistanceString(), "12,345", ()); + TEST_EQUAL(d.ToString(), MakeDistanceStr("12,345", "m"), ()); } { ScopedSettings guard(measurement_utils::Units::Imperial); @@ -75,8 +98,8 @@ UNIT_TEST(Distance_CreateAltitudeFormatted) Distance d = Distance::CreateAltitudeFormatted(10000); TEST_EQUAL(d.GetUnits(), Distance::Units::Feet, ()); TEST_ALMOST_EQUAL_ULPS(d.GetDistance(), 32808.0, ()); - TEST_EQUAL(d.GetDistanceString(), "32808", ()); - TEST_EQUAL(d.ToString(), "32808 ft", ()); + TEST_EQUAL(d.GetDistanceString(), "32,808", ()); + TEST_EQUAL(d.ToString(), MakeDistanceStr("32,808", "ft"), ()); } } @@ -173,7 +196,7 @@ UNIT_TEST(Distance_ToPlatformUnitsFormatted) TEST_EQUAL(newDistance.GetUnits(), Distance::Units::Meters, (d.ToString())); TEST_ALMOST_EQUAL_ULPS(newDistance.GetDistance(), 3.0, (d.ToString())); TEST_EQUAL(newDistance.GetDistanceString(), "3", (d.ToString())); - TEST_EQUAL(newDistance.ToString(), "3 m", (d.ToString())); + TEST_EQUAL(newDistance.ToString(), MakeDistanceStr("3", "m"), (d.ToString())); d = Distance{11, Distance::Units::Kilometers}; newDistance = d.ToPlatformUnitsFormatted(); @@ -181,7 +204,7 @@ UNIT_TEST(Distance_ToPlatformUnitsFormatted) TEST_EQUAL(newDistance.GetUnits(), Distance::Units::Kilometers, (d.ToString())); TEST_ALMOST_EQUAL_ULPS(newDistance.GetDistance(), 11.0, (d.ToString())); TEST_EQUAL(newDistance.GetDistanceString(), "11", (d.ToString())); - TEST_EQUAL(newDistance.ToString(), "11 km", (d.ToString())); + TEST_EQUAL(newDistance.ToString(), MakeDistanceStr("11", "km"), (d.ToString())); } { @@ -193,7 +216,7 @@ UNIT_TEST(Distance_ToPlatformUnitsFormatted) TEST_EQUAL(newDistance.GetUnits(), Distance::Units::Feet, (d.ToString())); TEST_ALMOST_EQUAL_ULPS(newDistance.GetDistance(), 11.0, (d.ToString())); TEST_EQUAL(newDistance.GetDistanceString(), "11", (d.ToString())); - TEST_EQUAL(newDistance.ToString(), "11 ft", (d.ToString())); + TEST_EQUAL(newDistance.ToString(), MakeDistanceStr("11", "ft"), (d.ToString())); d = Distance{11, Distance::Units::Kilometers}; newDistance = d.ToPlatformUnitsFormatted(); @@ -201,7 +224,7 @@ UNIT_TEST(Distance_ToPlatformUnitsFormatted) TEST_EQUAL(newDistance.GetUnits(), Distance::Units::Miles, (d.ToString())); TEST_ALMOST_EQUAL_ULPS(newDistance.GetDistance(), 6.8, (d.ToString())); TEST_EQUAL(newDistance.GetDistanceString(), "6.8", (d.ToString())); - TEST_EQUAL(newDistance.ToString(), "6.8 mi", (d.ToString())); + TEST_EQUAL(newDistance.ToString(), MakeDistanceStr("6.8", "mi"), (d.ToString())); } } @@ -238,97 +261,99 @@ UNIT_TEST(Distance_FormattedDistance) // clang-format off TestData testData[] = { // From Meters to Meters - {Distance(0, Units::Meters), 0, Units::Meters, "0", "0 m"}, - {Distance(0.3, Units::Meters), 0, Units::Meters, "0", "0 m"}, - {Distance(0.9, Units::Meters), 1, Units::Meters, "1", "1 m"}, - {Distance(1, Units::Meters), 1, Units::Meters, "1", "1 m"}, - {Distance(1.234, Units::Meters), 1, Units::Meters, "1", "1 m"}, - {Distance(9.99, Units::Meters), 10, Units::Meters, "10", "10 m"}, - {Distance(10.01, Units::Meters), 10, Units::Meters, "10", "10 m"}, - {Distance(10.4, Units::Meters), 10, Units::Meters, "10", "10 m"}, - {Distance(10.5, Units::Meters), 11, Units::Meters, "11", "11 m"}, - {Distance(10.51, Units::Meters), 11, Units::Meters, "11", "11 m"}, - {Distance(64.2, Units::Meters), 64, Units::Meters, "64", "64 m"}, - {Distance(99, Units::Meters), 99, Units::Meters, "99", "99 m"}, - {Distance(100, Units::Meters), 100, Units::Meters, "100", "100 m"}, - {Distance(101, Units::Meters), 100, Units::Meters, "100", "100 m"}, - {Distance(109, Units::Meters), 110, Units::Meters, "110", "110 m"}, - {Distance(991, Units::Meters), 990, Units::Meters, "990", "990 m"}, + {Distance(0, Units::Meters), 0, Units::Meters, "0", MakeDistanceStr("0", "m")}, + {Distance(0.3, Units::Meters), 0, Units::Meters, "0", MakeDistanceStr("0", "m")}, + {Distance(0.9, Units::Meters), 1, Units::Meters, "1", MakeDistanceStr("1", "m")}, + {Distance(1, Units::Meters), 1, Units::Meters, "1", MakeDistanceStr("1", "m")}, + {Distance(1.234, Units::Meters), 1, Units::Meters, "1", MakeDistanceStr("1", "m")}, + {Distance(9.99, Units::Meters), 10, Units::Meters, "10", MakeDistanceStr("10", "m")}, + {Distance(10.01, Units::Meters), 10, Units::Meters, "10", MakeDistanceStr("10", "m")}, + {Distance(10.4, Units::Meters), 10, Units::Meters, "10", MakeDistanceStr("10", "m")}, + {Distance(10.5, Units::Meters), 11, Units::Meters, "11", MakeDistanceStr("11", "m")}, + {Distance(10.51, Units::Meters), 11, Units::Meters, "11", MakeDistanceStr("11", "m")}, + {Distance(64.2, Units::Meters), 64, Units::Meters, "64", MakeDistanceStr("64", "m")}, + {Distance(99, Units::Meters), 99, Units::Meters, "99", MakeDistanceStr("99", "m")}, + {Distance(100, Units::Meters), 100, Units::Meters, "100", MakeDistanceStr("100", "m")}, + {Distance(101, Units::Meters), 100, Units::Meters, "100", MakeDistanceStr("100", "m")}, + {Distance(109, Units::Meters), 110, Units::Meters, "110", MakeDistanceStr("110", "m")}, + {Distance(991, Units::Meters), 990, Units::Meters, "990", MakeDistanceStr("990", "m")}, // From Kilometers to Kilometers - {Distance(0, Units::Kilometers), 0, Units::Meters, "0", "0 m"}, - {Distance(0.3, Units::Kilometers), 300, Units::Meters, "300", "300 m"}, - {Distance(1.234, Units::Kilometers), 1.2, Units::Kilometers, "1.2", "1.2 km"}, - {Distance(10, Units::Kilometers), 10, Units::Kilometers, "10", "10 km"}, - {Distance(11, Units::Kilometers), 11, Units::Kilometers, "11", "11 km"}, - {Distance(54, Units::Kilometers), 54, Units::Kilometers, "54", "54 km"}, - {Distance(99.99, Units::Kilometers), 100, Units::Kilometers, "100", "100 km"}, - {Distance(100.01, Units::Kilometers), 100, Units::Kilometers, "100", "100 km"}, - {Distance(115, Units::Kilometers), 115, Units::Kilometers, "115", "115 km"}, - {Distance(999, Units::Kilometers), 999, Units::Kilometers, "999", "999 km"}, - {Distance(1000, Units::Kilometers), 1000, Units::Kilometers, "1000", "1000 km"}, - {Distance(1049.99, Units::Kilometers), 1050, Units::Kilometers, "1050", "1050 km"}, - {Distance(1050, Units::Kilometers), 1050, Units::Kilometers, "1050", "1050 km"}, - {Distance(1050.01, Units::Kilometers), 1050, Units::Kilometers, "1050", "1050 km"}, - {Distance(1234, Units::Kilometers), 1234, Units::Kilometers, "1234", "1234 km"}, + {Distance(0, Units::Kilometers), 0, Units::Meters, "0", MakeDistanceStr("0", "m")}, + {Distance(0.3, Units::Kilometers), 300, Units::Meters, "300", MakeDistanceStr("300", "m")}, + {Distance(1.234, Units::Kilometers), 1.2, Units::Kilometers, "1.2", MakeDistanceStr("1.2", "km")}, + {Distance(10, Units::Kilometers), 10, Units::Kilometers, "10", MakeDistanceStr("10", "km")}, + {Distance(11, Units::Kilometers), 11, Units::Kilometers, "11", MakeDistanceStr("11", "km")}, + {Distance(54, Units::Kilometers), 54, Units::Kilometers, "54", MakeDistanceStr("54", "km")}, + {Distance(99.99, Units::Kilometers), 100, Units::Kilometers, "100", MakeDistanceStr("100", "km")}, + {Distance(100.01, Units::Kilometers), 100, Units::Kilometers, "100", MakeDistanceStr("100", "km")}, + {Distance(115, Units::Kilometers), 115, Units::Kilometers, "115", MakeDistanceStr("115", "km")}, + {Distance(999, Units::Kilometers), 999, Units::Kilometers, "999", MakeDistanceStr("999", "km")}, + {Distance(1000, Units::Kilometers), 1000, Units::Kilometers, "1000", MakeDistanceStr("1000", "km")}, + {Distance(1049.99, Units::Kilometers), 1050, Units::Kilometers, "1050", MakeDistanceStr("1050", "km")}, + {Distance(1050, Units::Kilometers), 1050, Units::Kilometers, "1050", MakeDistanceStr("1050", "km")}, + {Distance(1050.01, Units::Kilometers), 1050, Units::Kilometers, "1050", MakeDistanceStr("1050", "km")}, + {Distance(1234, Units::Kilometers), 1234, Units::Kilometers, "1234", MakeDistanceStr("1234", "km")}, + {Distance(12345, Units::Kilometers), 12345, Units::Kilometers, "12,345", MakeDistanceStr("12,345", "km")}, // From Feet to Feet - {Distance(0, Units::Feet), 0, Units::Feet, "0", "0 ft"}, - {Distance(1, Units::Feet), 1, Units::Feet, "1", "1 ft"}, - {Distance(9.99, Units::Feet), 10, Units::Feet, "10", "10 ft"}, - {Distance(10.01, Units::Feet), 10, Units::Feet, "10", "10 ft"}, - {Distance(95, Units::Feet), 95, Units::Feet, "95", "95 ft"}, - {Distance(125, Units::Feet), 130, Units::Feet, "130", "130 ft"}, - {Distance(991, Units::Feet), 990, Units::Feet, "990", "990 ft"}, + {Distance(0, Units::Feet), 0, Units::Feet, "0", MakeDistanceStr("0", "ft")}, + {Distance(1, Units::Feet), 1, Units::Feet, "1", MakeDistanceStr("1", "ft")}, + {Distance(9.99, Units::Feet), 10, Units::Feet, "10", MakeDistanceStr("10", "ft")}, + {Distance(10.01, Units::Feet), 10, Units::Feet, "10", MakeDistanceStr("10", "ft")}, + {Distance(95, Units::Feet), 95, Units::Feet, "95", MakeDistanceStr("95", "ft")}, + {Distance(125, Units::Feet), 130, Units::Feet, "130", MakeDistanceStr("130", "ft")}, + {Distance(991, Units::Feet), 990, Units::Feet, "990", MakeDistanceStr("990", "ft")}, // From Miles to Miles - {Distance(0, Units::Miles), 0, Units::Feet, "0", "0 ft"}, - {Distance(0.1, Units::Miles), 530, Units::Feet, "530", "530 ft"}, - {Distance(1, Units::Miles), 1.0, Units::Miles, "1.0", "1.0 mi"}, - {Distance(1.234, Units::Miles), 1.2, Units::Miles, "1.2", "1.2 mi"}, - {Distance(9.99, Units::Miles), 10, Units::Miles, "10", "10 mi"}, - {Distance(10.01, Units::Miles), 10, Units::Miles, "10", "10 mi"}, - {Distance(11, Units::Miles), 11, Units::Miles, "11", "11 mi"}, - {Distance(54, Units::Miles), 54, Units::Miles, "54", "54 mi"}, - {Distance(145, Units::Miles), 145, Units::Miles, "145", "145 mi"}, - {Distance(999, Units::Miles), 999, Units::Miles, "999", "999 mi"}, - {Distance(1149.99, Units::Miles), 1150, Units::Miles, "1150", "1150 mi"}, - {Distance(1150, Units::Miles), 1150, Units::Miles, "1150", "1150 mi"}, - {Distance(1150.01, Units::Miles), 1150, Units::Miles, "1150", "1150 mi"}, + {Distance(0, Units::Miles), 0, Units::Feet, "0", MakeDistanceStr("0", "ft")}, + {Distance(0.1, Units::Miles), 530, Units::Feet, "530", MakeDistanceStr("530", "ft")}, + {Distance(1, Units::Miles), 1.0, Units::Miles, "1.0", MakeDistanceStr("1.0", "mi")}, + {Distance(1.234, Units::Miles), 1.2, Units::Miles, "1.2", MakeDistanceStr("1.2", "mi")}, + {Distance(9.99, Units::Miles), 10, Units::Miles, "10", MakeDistanceStr("10", "mi")}, + {Distance(10.01, Units::Miles), 10, Units::Miles, "10", MakeDistanceStr("10", "mi")}, + {Distance(11, Units::Miles), 11, Units::Miles, "11", MakeDistanceStr("11", "mi")}, + {Distance(54, Units::Miles), 54, Units::Miles, "54", MakeDistanceStr("54", "mi")}, + {Distance(145, Units::Miles), 145, Units::Miles, "145", MakeDistanceStr("145", "mi")}, + {Distance(999, Units::Miles), 999, Units::Miles, "999", MakeDistanceStr("999", "mi")}, + {Distance(1149.99, Units::Miles), 1150, Units::Miles, "1150", MakeDistanceStr("1150", "mi")}, + {Distance(1150, Units::Miles), 1150, Units::Miles, "1150", MakeDistanceStr("1150", "mi")}, + {Distance(1150.01, Units::Miles), 1150, Units::Miles, "1150", MakeDistanceStr("1150", "mi")}, + {Distance(12345.0, Units::Miles), 12345, Units::Miles, "12,345", MakeDistanceStr("12,345", "mi")}, // From Meters to Kilometers - {Distance(999, Units::Meters), 1.0, Units::Kilometers, "1.0", "1.0 km"}, - {Distance(1000, Units::Meters), 1.0, Units::Kilometers, "1.0", "1.0 km"}, - {Distance(1001, Units::Meters), 1.0, Units::Kilometers, "1.0", "1.0 km"}, - {Distance(1100, Units::Meters), 1.1, Units::Kilometers, "1.1", "1.1 km"}, - {Distance(1140, Units::Meters), 1.1, Units::Kilometers, "1.1", "1.1 km"}, - {Distance(1151, Units::Meters), 1.2, Units::Kilometers, "1.2", "1.2 km"}, - {Distance(1500, Units::Meters), 1.5, Units::Kilometers, "1.5", "1.5 km"}, - {Distance(1549.9, Units::Meters), 1.5, Units::Kilometers, "1.5", "1.5 km"}, - {Distance(1550, Units::Meters), 1.6, Units::Kilometers, "1.6", "1.6 km"}, - {Distance(1551, Units::Meters), 1.6, Units::Kilometers, "1.6", "1.6 km"}, - {Distance(9949, Units::Meters), 9.9, Units::Kilometers, "9.9", "9.9 km"}, - {Distance(9992, Units::Meters), 10, Units::Kilometers, "10", "10 km"}, - {Distance(10000, Units::Meters), 10, Units::Kilometers, "10", "10 km"}, - {Distance(10499.9, Units::Meters), 10, Units::Kilometers, "10", "10 km"}, - {Distance(10501, Units::Meters), 11, Units::Kilometers, "11", "11 km"}, - {Distance(101'001, Units::Meters), 101, Units::Kilometers, "101", "101 km"}, - {Distance(101'999, Units::Meters), 102, Units::Kilometers, "102", "102 km"}, - {Distance(287'386, Units::Meters), 287, Units::Kilometers, "287", "287 km"}, + {Distance(999, Units::Meters), 1.0, Units::Kilometers, "1.0", MakeDistanceStr("1.0", "km")}, + {Distance(1000, Units::Meters), 1.0, Units::Kilometers, "1.0", MakeDistanceStr("1.0", "km")}, + {Distance(1001, Units::Meters), 1.0, Units::Kilometers, "1.0", MakeDistanceStr("1.0", "km")}, + {Distance(1100, Units::Meters), 1.1, Units::Kilometers, "1.1", MakeDistanceStr("1.1", "km")}, + {Distance(1140, Units::Meters), 1.1, Units::Kilometers, "1.1", MakeDistanceStr("1.1", "km")}, + {Distance(1151, Units::Meters), 1.2, Units::Kilometers, "1.2", MakeDistanceStr("1.2", "km")}, + {Distance(1500, Units::Meters), 1.5, Units::Kilometers, "1.5", MakeDistanceStr("1.5", "km")}, + {Distance(1549.9, Units::Meters), 1.5, Units::Kilometers, "1.5", MakeDistanceStr("1.5", "km")}, + {Distance(1550, Units::Meters), 1.6, Units::Kilometers, "1.6", MakeDistanceStr("1.6", "km")}, + {Distance(1551, Units::Meters), 1.6, Units::Kilometers, "1.6", MakeDistanceStr("1.6", "km")}, + {Distance(9949, Units::Meters), 9.9, Units::Kilometers, "9.9", MakeDistanceStr("9.9", "km")}, + {Distance(9992, Units::Meters), 10, Units::Kilometers, "10", MakeDistanceStr("10", "km")}, + {Distance(10000, Units::Meters), 10, Units::Kilometers, "10", MakeDistanceStr("10", "km")}, + {Distance(10499.9, Units::Meters), 10, Units::Kilometers, "10", MakeDistanceStr("10", "km")}, + {Distance(10501, Units::Meters), 11, Units::Kilometers, "11", MakeDistanceStr("11", "km")}, + {Distance(101'001, Units::Meters), 101, Units::Kilometers, "101", MakeDistanceStr("101", "km")}, + {Distance(101'999, Units::Meters), 102, Units::Kilometers, "102", MakeDistanceStr("102", "km")}, + {Distance(287'386, Units::Meters), 287, Units::Kilometers, "287", MakeDistanceStr("287", "km")}, // From Feet to Miles - {Distance(999, Units::Feet), 0.2, Units::Miles, "0.2", "0.2 mi"}, - {Distance(1000, Units::Feet), 0.2, Units::Miles, "0.2", "0.2 mi"}, - {Distance(1150, Units::Feet), 0.2, Units::Miles, "0.2", "0.2 mi"}, - {Distance(5280, Units::Feet), 1.0, Units::Miles, "1.0", "1.0 mi"}, - {Distance(7920, Units::Feet), 1.5, Units::Miles, "1.5", "1.5 mi"}, - {Distance(10560, Units::Feet), 2.0, Units::Miles, "2.0", "2.0 mi"}, - {Distance(100'000, Units::Feet), 19, Units::Miles, "19", "19 mi"}, - {Distance(285'120, Units::Feet), 54, Units::Miles, "54", "54 mi"}, - {Distance(633'547, Units::Feet), 120, Units::Miles, "120", "120 mi"}, - {Distance(633'600, Units::Feet), 120, Units::Miles, "120", "120 mi"}, - {Distance(633'653, Units::Feet), 120, Units::Miles, "120", "120 mi"}, - {Distance(999'999, Units::Feet), 189, Units::Miles, "189", "189 mi"}, + {Distance(999, Units::Feet), 0.2, Units::Miles, "0.2", MakeDistanceStr("0.2", "mi")}, + {Distance(1000, Units::Feet), 0.2, Units::Miles, "0.2", MakeDistanceStr("0.2", "mi")}, + {Distance(1150, Units::Feet), 0.2, Units::Miles, "0.2", MakeDistanceStr("0.2", "mi")}, + {Distance(5280, Units::Feet), 1.0, Units::Miles, "1.0", MakeDistanceStr("1.0", "mi")}, + {Distance(7920, Units::Feet), 1.5, Units::Miles, "1.5", MakeDistanceStr("1.5", "mi")}, + {Distance(10560, Units::Feet), 2.0, Units::Miles, "2.0", MakeDistanceStr("2.0", "mi")}, + {Distance(100'000, Units::Feet), 19, Units::Miles, "19", MakeDistanceStr("19", "mi")}, + {Distance(285'120, Units::Feet), 54, Units::Miles, "54", MakeDistanceStr("54", "mi")}, + {Distance(633'547, Units::Feet), 120, Units::Miles, "120", MakeDistanceStr("120", "mi")}, + {Distance(633'600, Units::Feet), 120, Units::Miles, "120", MakeDistanceStr("120", "mi")}, + {Distance(633'653, Units::Feet), 120, Units::Miles, "120", MakeDistanceStr("120", "mi")}, + {Distance(999'999, Units::Feet), 189, Units::Miles, "189", MakeDistanceStr("189", "mi")}, }; // clang-format on diff --git a/platform/platform_tests/measurement_tests.cpp b/platform/platform_tests/measurement_tests.cpp index 42a409dbf4..78146785fa 100644 --- a/platform/platform_tests/measurement_tests.cpp +++ b/platform/platform_tests/measurement_tests.cpp @@ -4,10 +4,22 @@ #include "base/math.hpp" +#include #include using namespace measurement_utils; +using namespace platform; +std::string AddGroupingSeparators(std::string const & valueString, std::string const & groupingSeparator) +{ + std::string out(valueString); + + if (out.size() > 4 && !groupingSeparator.empty()) + for (int pos = out.size() - 3; pos > 0; pos -= 3) + out.insert(pos, groupingSeparator); + + return out; +} UNIT_TEST(LatLonToDMS_Origin) { @@ -132,3 +144,42 @@ UNIT_TEST(UnitsConversion) TEST(base::AlmostEqualAbs(KmphToMps(3.6), 1.0, kEps), ()); TEST(base::AlmostEqualAbs(MpsToKmph(1.0), 3.6, kEps), ()); } + +UNIT_TEST(ToStringPrecisionLocale) +{ + double d1 = 9.8; + int pr1 = 1; + + double d2 = 12345.0; + int pr2 = 0; + std::string d2String("12345"); + + struct TestData + { + std::string localeName; + std::string d1String; + }; + + TestData testData[] = { + // Locale name , Decimal + { "en_US.UTF-8", "9.8"}, + { "es_ES.UTF-8", "9,8"}, + { "fr_FR.UTF-8", "9,8"}, + { "ru_RU.UTF-8", "9,8"} + }; + + for (TestData const & data : testData) + { + Locale loc; + + if (!GetLocale(data.localeName, loc)) + { + std::cout << "Locale '" << data.localeName << "' not found!! Skipping test..." << std::endl; + continue; + } + + TEST_EQUAL(measurement_utils::ToStringPrecisionLocale(loc, d1, pr1), data.d1String, ()); + TEST_EQUAL(measurement_utils::ToStringPrecisionLocale(loc, d2, pr2), + AddGroupingSeparators(d2String, loc.m_groupingSeparator), ()); + } +}