Localized measurement units fail when an app treats translation as the whole job. Replacing mi with km does not fix the underlying problem. The app still has to choose a unit system, convert a canonical value, apply rules for the specific use, round once, and format the result. Locale can provide a default, but it should not erase a user's stated preference. Scatter those decisions across screens and the same value appears in different units or at different precision. A single policy prevents that drift and makes the behavior testable on iOS and Android.
Why translated labels still produce wrong measurements
A measurement has four separate parts: the stored quantity, its storage unit, the unit selected for presentation, and the localized display string. Combining them too early hides information. A database value such as "5 mi" cannot be converted safely without parsing presentation text. A value stored as kilometers but labeled with a translated abbreviation is worse because its number and label disagree.
Locale is an incomplete proxy for preference. Someone may use an English interface and prefer metric units. The same person may expect feet and inches for height but miles for road distance. Unicode CLDR models territory-based unit preferences by quantity, usage, region, threshold, and output unit. This is more specific than a single metric or imperial switch because conventions differ by task.
Formatting and unit selection are separate operations. Apple's MeasurementFormatter produces localized representations of measurements. Android's MeasureFormat combines locale-sensitive number formatting with localized unit names and widths. Neither API can supply a product rule that the app never defined, such as whether a fitness profile should use its saved preference before a regional default.
Mixed units expose the boundary. A height of 1.905 meters may need to appear as 6 feet 3 inches, not 6.25 feet. A developer trying to display feet and inches with MeasurementFormatter found that choosing an imperial length unit did not automatically produce the usage-specific mixed form.
Define the unit decision before formatting
Write the policy before calling a formatter. It should accept a canonical measurement and presentation context, then return the output units and precision. Its inputs should include:
- The quantity, such as length, mass, temperature, speed, volume, or pressure.
- The usage, such as road distance, person height, cooking volume, or package weight.
- The user's choice: automatic, metric, US customary, UK mixed, or a product-specific option.
- The effective app locale and its resolved region.
- Display constraints, including available space and accessibility needs.
Use a stable precedence rule. An explicit account or device preference comes first. If it is absent, use the region associated with the effective app locale. If no region can be resolved, apply a documented product default. A screen should never read the process locale while another reads the app locale.
Language should not determine the measurement system. en-US, es-US, and zh-US may share regional defaults despite using different languages. English is also used in regions with different conventions. Resolve the region independently, then let the saved preference override it. The same separation of language and region applies to localized dates, times, and numbers.
Keep storage independent from presentation
Store the original observation or a canonical base value with its dimension. A product might use meters for distance, kilograms for mass, and kelvin or another chosen base unit for temperature. Keep enough precision for later conversion. The rounded display number is not a source value.
Canonical storage prevents conversion drift. If a screen converts 10 miles to kilometers, rounds it, saves the result, and later converts it back, repeated edits can move the value. Derive each display from the same canonical quantity. Convert user input to the canonical unit once, when the edit is committed.
Guard the dimension too. A numeric field paired with a free-form unit string permits invalid combinations. A distance converter should reject a mass unit instead of returning a plausible number. Typed measurement APIs reduce this risk, but the app must still validate data received from a server or local database.
Separate automatic defaults from explicit choices
Automatic is a policy, not a unit system. Persist a value such as automatic, metric, or usCustomary, then derive presentation units from it. This lets the app respond predictably when the user changes region or app language.
Define where the preference lives. If it belongs to an account, a choice made on Android should carry to iOS. If it is device-specific, say so in the setting. During migration, distinguish a missing preference from an existing preference that happened to match the old default.
Some products need a separate choice for each quantity. A runner may prefer kilometers for distance and pounds for body weight. Do not force a global switch if independent settings match the product better. Per-quantity overrides can sit above the same canonical storage model.
Implement one measurement presentation pipeline
Put unit resolution, conversion, precision, and formatting behind one service or shared interface. A view should request a presentation value. It should not assemble a number and translated suffix.
A platform-neutral flow can look like this:
formatMeasurement(value, context):
assert value.dimension == context.quantity
preference = loadPreference(context.account, context.quantity)
region = resolveRegion(context.effectiveAppLocale)
rule = chooseUnitRule(
quantity = context.quantity,
usage = context.usage,
preference = preference,
region = region
)
convertedParts = convertCanonicalValue(value, rule.outputUnits)
roundedParts = roundForDisplay(convertedParts, rule.precision)
return localeFormatter.format(
roundedParts,
locale = context.effectiveAppLocale,
width = context.unitWidth
)
Resolve the rule before conversion, convert before rounding, and format only after the numeric result is final. For mixed output, split the canonical value into ordered components and handle carry between them. If 6 feet 11.8 inches rounds to the next inch, the output should become 7 feet rather than 6 ft 12 in.
On iOS, Foundation measurement types can handle dimensional conversion, while MeasurementFormatter or current format-style APIs render localized output. Set locale and configuration explicitly, then run tests on every supported operating-system version. On Android, use typed units or explicit conversion functions before handing the result to MeasureFormat. Its unit widths let the same value fit a compact card or an accessibility-friendly detail view.
Do not keep unit abbreviations in ordinary app translation files. Locale data controls unit grammar, spacing, plural forms, and abbreviations. Code such as number + " " + t("kilometers") can produce the wrong spacing or inflection. Product translations still own the surrounding sentence, but the complete formatted measurement should enter that sentence as one typed argument.
For example, translators may need to reorder the sentence "Your route is 5 kilometers long." Keep it as one localized message and inject the formatted measurement. Do not concatenate a translated prefix, number, and suffix.
Make rounding and thresholds part of the policy
A conversion can be mathematically correct and still be unhelpful. Showing 0.621371 miles instead of 1 kilometer adds noise. Precision should match the task rather than the conversion library's default.
Set precision by quantity, use, and magnitude. Navigation might show one decimal place for a long route and whole meters near the destination. Package dimensions may need a fixed manufacturing tolerance. Medication and safety measurements require domain review instead of a generic localization setting.
Choose units from the unrounded canonical value. Suppose the app switches from meters to kilometers at 1,000 meters. Rounding 999.6 meters first may produce 1,000 m while another screen shows 1 km. Select the output unit first, then round it for display.
Temperature conversion includes an offset, not just a scale. Speed joins distance and time units. Fuel economy may invert the relationship between distance and volume in different regions. Give each of these quantities its own policy rather than feeding arbitrary strings into a multiplication function.
Handle changes, failures, and fallbacks
Reformat visible values when the locale, region, or unit preference changes. Updating only the unit label leaves the number unconverted. In a reactive UI, expose the resolved policy as observable state and include its version in presentation-cache keys.
A missing region should not crash the screen. Use the documented product unit and preserve the canonical value. If a long localized unit name is unavailable, a platform-provided short form is a reasonable display fallback. Switching to another measurement system is not.
Server-rendered measurements need the same context as the app. A notification containing preformatted 12 mi will disagree with an app set to kilometers. Prefer sending a canonical value, dimension, and usage, then format at the presentation edge. When a server must format the final string, send an explicit locale and unit preference instead of inheriting its process locale.
Log invalid dimensions, unsupported units, and formatter failures without recording sensitive measurements. A fallback can preserve the canonical number and use a documented unit. It must never relabel an unconverted value simply to keep the screen populated.
Verify localized measurement units across platforms
Build a fixture matrix rather than testing one distance under one locale. Cover these cases:
- US English with automatic units, then with a metric override.
- British English for road distance, person height, and body weight.
- French in Canada and France, proving that language alone does not select the region.
- A locale without a region subtag, which exercises the product fallback.
- Values below, at, and above every unit-switch threshold.
- Zero, valid negative values, large values, and high-precision input.
- Mixed-unit carry, including inches that round into the next foot.
- Locale and preference changes while the screen remains open.
- Cold start, restored preferences, offline data, and server-provided values.
- VoiceOver or TalkBack output with both short and long unit styles.
Assert semantics as well as snapshots. Each test should inspect the chosen unit, converted value, precision rule, and final string. That tells you whether a failure came from policy, conversion, rounding, or rendering.
Run formatter tests with an explicit locale. CI and local development machines may have different process settings, so an inherited locale makes results depend on the environment. The fixture should provide every setting that influences output.
Cross-platform golden tests can share canonical inputs and expected unit choices while allowing documented typography differences. iOS and Android may ship different locale-data versions, which can affect spacing or abbreviations. They should still agree on the selected system, converted magnitude, and product precision.
Common mistakes to block in review
Reject display code that appends a translated suffix to an arbitrary number. Reject storage models that contain only preformatted text. Flag conversion inside view components, repeated round trips between systems, and defaults read from an unrelated device locale.
Review accessibility output separately from the compact visual label. A value such as 5' 8" may be familiar on screen and still read poorly through assistive technology. Generate an understandable long form from the same canonical value and policy instead of maintaining a second manual string.
Localization should not trigger conversion by itself. Scientific, industrial, or contractual values may have to retain the source unit. The policy should support preserving that unit as an explicit rule. Product requirements decide whether conversion happens; formatting only applies that decision.
Put the policy into one release gate
Inventory every screen, notification, export, widget, and API response that displays a measurement. Replace manual number-plus-unit assembly with the shared pipeline. Define canonical storage, preference precedence, usage rules, thresholds, precision, and fallbacks before migrating views.
Add the locale, region, override, threshold, mixed-unit, and environment fixtures to CI. The implementation is done when iOS and Android remain consistent after language changes, preference changes, cold starts, and server delivery. Start with one high-traffic measurement screen, trace its value from storage to pixels, and move that complete path behind the policy before converting the rest.
References
- Apple
MeasurementFormattersupports localized representations of units and measurements on Apple platforms. - Android
MeasureFormatdocuments locale-sensitive measurement rendering and unit-width options. - Unicode CLDR territory-based unit preferences supplies the quantity, usage, region, threshold, and mixed-unit model behind robust defaults.
- Stack Overflow: feet and inches with
MeasurementFormatterdemonstrates a practitioner failure where unit selection alone does not produce a usage-specific mixed format.
