Updated the "name(for:)" method for the ContentViewModel view model in the Sample app target to append the region flag emoji to the locale name.

This commit is contained in:
2026-07-07 00:46:16 +02:00
parent f463c60b35
commit d27e5fb38f
2 changed files with 99 additions and 7 deletions
@@ -163,19 +163,29 @@ extension ContentView {
}
}
/// Returns the localized name of a locale for the locale picker.
/// Returns the localized language name of a locale, followed by the flag emoji of its region, for the locale picker.
///
/// - Parameter locale: The locale to name.
/// - Returns: The name of the locale in the user's current locale, or its identifier when no name is available.
/// - Returns: The localized language name in the user's current locale followed by the region's flag emoji when the
/// locale has one, or the locale's identifier when the language has no localized name.
func name(
for locale: Locale
) -> String {
Locale
.current
.localizedString(
forIdentifier: locale.identifier
)?.localizedCapitalized
let name = locale
.language
.languageCode
.flatMap { Locale.current.localizedString(forLanguageCode: $0.identifier) }?
.localizedCapitalized
?? locale.identifier
guard
let region = locale.region,
let flag = flag(for: region)
else {
return name
}
return "\(name) \(flag)"
}
/// Handles the transcription of a processed recording, presenting it in the modal sheet.
@@ -202,6 +212,35 @@ private extension ContentView.Model {
// MARK: Methods
/// Returns the flag emoji of the given region, built from its two-letter code, or `nil` when the region has no such code.
///
/// - Parameter region: The region to build a flag emoji for.
/// - Returns: The region's flag emoji, or `nil` when its identifier is not a two-letter code a numeric UN M49 region, for example.
func flag(
for region: Locale.Region
) -> String? {
let code = region.identifier
guard
code.count == 2,
code.allSatisfy(\.isLetter)
else {
return nil
}
var flag = ""
for scalar in code.uppercased().unicodeScalars {
guard let indicator = Unicode.Scalar(Constant.Flag.base + scalar.value) else {
return nil
}
flag.unicodeScalars.append(indicator)
}
return flag
}
/// Starts listening to the events emitted by the preinstalling service, unless already listening: every started, cancelled, and
/// failed download is posted to the ``ContentView/Model/notifier``, and a cancellation or failure clears the preinstalled locale
/// only when it is still the affected one, so a newer preinstallation is never forgotten letting a re-pick retry.
@@ -254,6 +293,12 @@ private extension ContentView.Model {
/// The constant values used across the model.
private enum Constant {
/// The flag constants.
enum Flag {
/// The distance from an ASCII uppercase letter to its Regional Indicator Symbol, used to build a flag emoji from a region code.
static let base: UInt32 = 0x1F1E6 - 0x41
}
/// The symbol constants.
enum Symbol {
/// The system symbol of a cancelled download.
@@ -226,6 +226,36 @@ struct ContentViewModelTests {
}
// MARK: Name
@MainActor
@Suite("Name")
struct Name {
@Test
func `name pairs the localized language name with the region flag`() {
let model = Model(preinstaller: PreinstallingMock())
#expect(model.name(for: .english) == "\(languageName("en")) 🇺🇸")
#expect(model.name(for: .dutch) == "\(languageName("nl")) 🇳🇱")
}
@Test
func `name omits the flag for a locale without a region`() {
let model = Model(preinstaller: PreinstallingMock())
#expect(!model.name(for: Locale(identifier: "en")).containsFlag)
}
@Test
func `name omits the flag for a numeric region`() {
let model = Model(preinstaller: PreinstallingMock())
#expect(!model.name(for: Locale(identifier: "es-419")).containsFlag)
}
}
}
// MARK: - Constants
@@ -238,3 +268,20 @@ private extension Locale {
/// Another supported locale to preinstall in the tests.
static let english = Locale(identifier: "en-US")
}
// MARK: - Helpers
/// The capitalized, localized language name for a language code, mirroring how the model builds the language part of a locale's name.
///
/// - Parameter code: The language code to name.
/// - Returns: The localized language name in the current locale, capitalized.
private func languageName(_ code: String) -> String {
(Locale.current.localizedString(forLanguageCode: code) ?? code).localizedCapitalized
}
private extension String {
/// Whether the string contains a Regional Indicator Symbol scalar part of a flag emoji.
var containsFlag: Bool {
unicodeScalars.contains { $0.value >= 0x1F1E6 && $0.value <= 0x1F1FF }
}
}