How to properly localize AppIntent dialogs for Siri?

Hi!

I have defined the following app intent. It returns a result with a dialog to confirm that the intent has been executed.

Naturally, that dialog needs to be localized properly. But the String interpolation with the provided format doesn't do that.

  • I specified wide for the width parameter and expect spelled-out unit names. However, in the textual output, Siri always uses the abbreviated unit (e.g. "min" or "s"), in all languages I tested. In the audio output, Siri says "minutes" in English where the textual representation is "min". In German, Siri says "min", so it basically reads the textual representation aloud and that's not quite understandable to the user.

struct StartTimerIntent: AppIntent {
    static let title: LocalizedStringResource = "Start New Timer"
    static var description = IntentDescription("Starts a timer with a custom duration.")
    
    @Parameter(title: "Duration", description: "The duration of the timer.") var duration: Measurement<UnitDuration>

    func perform() async throws -> some IntentResult & ProvidesDialog {
        // [code to execute intent goes here]
        return .result(
            dialog: .init(
                full: "\(duration, format: .measurement(width: .wide, usage: .asProvided)) timer started.",
                systemImageName: "timer"
            )
        )
    }
}

As this SwiftUI-style formatter doesn't seem to work with localization, I tried a different approach with a MeasurementFormatter:

extension Measurement where UnitType == UnitDuration {
    func localized() -> String {
        let formatter = MeasurementFormatter()
        formatter.locale = .autoupdatingCurrent
        formatter.unitOptions = .providedUnit
        formatter.unitStyle = .long
        return formatter.string(from: self)
    }
}

Usage with String interpolation:

"\(duration.localized()) timer started."

This works great as long as these two languages are set to the same language on the user's device:

  1. [UI language] Settings → General → Language & Region → Preferred Language
  2. [Siri langauge] Settings → Apple Intelligence & Siri → Language

However, when they differ, even this method doesn't yield correct results.

  • For example, I have my general (UI) language set to English, but my Siri language set to German. Then Siri replies in German, but the unit is formatted in English and Siri speaks it in English, so the result is a messed up sentence that's half German, half English.

What is the proper way to localize parameters in dialogs for Siri?

How can I make sure that parameters are localized to match Siri's language?

"(duration.localized()) timer started." This works great as long as these two languages are set to the same language on the user's device:

  1. [UI language] Settings → General → Language & Region → Preferred Language
  2. [Siri langauge] Settings → Apple Intelligence & Siri → Language

However, when they differ, even this method doesn't yield correct results.

This behavior makes sense to me. Assuming that:

  • Your device has the UI language set to English, the region set to United States, and the Siri language set to German.
  • duration is set to 10 seconds.

I believe this is how it works:

  1. Siri creates an instance of your app intent (StartTimerIntent), and runs its perform method in your app's process.

  2. The perform method creates an IntentDialog, which triggers localized(). There, .autoupdatingCurrent returns the current Preferred locale (en_US), and so localized() returns: "10 seconds timer started.”.

  3. The string is used as a key to create the localized string resource (LocalizedStringResource) for the dialog message.

  4. Siri receives the localized string resource, sets the locale appropriately, and then looks up the localized string, which matches the following entry in your app's string catalog: "%@ timer started.” [a].

  5. The Siri language is German, and so the system returns the German version of the string, and replaces %@ with "10 second".

As the result, you get a German version of the string, with the unit being "seconds".

[a]: When building your code, Xcode should extract the localizable string key from your code ("(duration.localized()) timer started."), and add it to your app's string catalog as:

%@ timer started.

I specified wide for the width parameter and expect spelled-out unit names. However, in the textual output, Siri always uses the abbreviated unit (e.g. "min" or "s"), in all languages I tested.

I can't explain why the system behaves like this. Ideally, when handling a LocalizedStringResource object, the system should pass duration + formatStyle to Siri so Siri can use the appropriate locale to look up the localized string and replace the placeholder with the localized duration string. Based on your description, however, that doesn't seem to be the case.

Given that, would you mind to file a feedback report and share your report ID here? I'd use your report to check with my colleague if I miss something.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

How to properly localize AppIntent dialogs for Siri?
 
 
Q