Localize UI Capitalization
PublicPut the final casing of user-visible text in the localization resource instead of calling toUpperCase() on it, because Dart's casing is wrong for some languages.
Localize UI Capitalization
Put the final casing of user-visible text in the localization resource instead of calling toUpperCase() on it, because Dart's casing is wrong for some languages.
These runtime transformations do not reliably produce the intended UI text:
Text(context.l10n.finish.toUpperCase())
// Turkish "bitir" becomes "BITIR", but the correct uppercase is "BİTİR".
Text(context.l10n.street.toUpperCase())
// German "Straße" becomes "STRASSE": one character expands to two.
Dart's String.toUpperCase() uses a language-independent Unicode mapping and explicitly only works correctly for some languages. Do not apply it to localized, user-visible text in buttons, tabs, headings, labels, tooltips, or other UI elements. Put the final presentation casing in the localization resource.
If the UI needs both normal and uppercase variants, create two localized keys:
{
"save": "Save",
"save_uppercase": "SAVE"
}
Text(context.l10n.save)
Text(context.l10n.saveUppercase)
Each locale can then translate both variants correctly or choose a different convention.
The instinct is reasonable
Other UI technologies expose presentation-layer casing. CSS, for example, provides text-transform: uppercase and applies language-specific rules when the content language is known. Reaching for an equivalent in Flutter is understandable, but Dart's toUpperCase() is not that equivalent, and localization-owned casing gives translators control.
Exceptions
- Dynamic user or server data cannot be moved into a localization file. If casing it is an explicit product requirement, use a locale-aware implementation with the locale of the text and test supported languages with special casing.
- Case operations for non-display data such as protocol constants, identifiers, enum parsing, or explicitly invariant normalization are outside this rule.
- Generated localization code is outside this rule. Change its source localization resource and regenerate it.
References
Normative and framework documentation:
- Dart
String.toUpperCase() - Unicode Special Casing
- ICU case mappings
- Flutter internationalization
- Flutter
DefaultTextStyle - Flutter
TextCapitalization - CSS Text
text-transform
Further reading on the Turkish I problem:
- The Turkish İ Problem and Why You Should Care
- The infamous Turkish locale bug
- Problematic Case Conversion in Turkish, by the IGDA Localization SIG