Strings Dictionary mit dynamischen Einheiten

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Strings Dictionary mit dynamischen Einheiten

    Hallo zusammen,

    möchte im Strings Dictionary die zu verwendende Einheit (z.B. Stunden, Minuten, Sekunden) dynamisch beim Aufruf mitgeben:


    Quellcode

    1. Text("unit \(self.unit) \(Int(_value))", tableName: "SingularAndPlural")
    self.unit enthält die Einheit als String.

    Ich hab schon alles mögliche versucht, komme aber nicht drauf, wie ich es richtig anlegen muss.
    Bildschirmfoto 2022-07-27 um 08.26.13.png
    Hat jemand einen Tipp?

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von Heruhaundo ()

  • Ich will die Einheit dynamisch befüllen.
    Also anstatt …

    Quellcode

    1. Text("seconds \(Int(_value))", tableName: "SingularAndPlural")
    2. Text("minutes \(Int(_value))", tableName: "SingularAndPlural")
    … die Einheit ebenfalls in einer Variablen übergeben. Das im Endeeffekt der Aufruf so aussieht:


    Quellcode

    1. Text("\(self.unit) \(Int(_value))", tableName: "SingularAndPlural")
    Ich scheitere aber daran die Strings Dictionary richtig zu erstellen, da ich dann als Ergebnis immer „seconds 10“ anstatt korrekt übersetzt „10 Sekunden“ bekomme.
  • Oops, kannte ich noch nicht und die Beschreibung von Apple ist mal wieder nicht wirklich verständlich. ?(

    Vielleicht hilft Dir dies hier: levelup.gitconnected.com/step-…urals-in-ios-57f9deaade3e weiter. Fand ich zumindest viel verständlicher als die Doku von Apple.

    Apple scheint seit ein paar Jahren leider einen Hang zu Dokumentationen zu haben die nicht wirklich verständlich sind. :(

    Hast Du mal versucht die Localization über ein NSLocalizedString() aufzulösen? Funktioniert dies?
  • An sich die Aufrufe funktionieren korrekt.

    Text("seconds \(Int(_value))", tableName: "SingularAndPlural") liefert wie erwartet "1 Sekunde" und
    Text("minutes \(Int(_value))", tableName: "SingularAndPlural") liefert wie erwartet "1 Minute".

    Nun möchte ich dies aber generisch nutzen, so dass ich "seconds" oder "minutes" ebenfalls in einer Variablen übergeben kann.
    Also so Text("\(self.unit) \(Int(_value))", tableName: "SingularAndPlural"), dann erfolgt aber nicht die gewünschte Auflösung, sonder er gibt mit den aus den beiden Variablen zusammengesetzten String zurück.
  • Heruhaundo schrieb:

    Ist die ganz normale Geschichte für Singular und Plural: developer.apple.com/documentat…ings-that-contain-plurals
    Cool, wieder was gelernt. Vielleicht hätte ich mir damit in der Vergangenheit das Leben einfacher machen können.

    So wie ich das Thema verstehe, funktioniert Dein obiger Ansatz mit zwei Variablen in der Ausgabe aber nicht - zumindest nicht so, wie Du möchtest, mit zwei Variablen zur Auflösung.

    Ich habe einmal folgenden Code genommen, um den Übersetzungs-String einmal mit variabler und einmal mit konstanter Einheit abzufragen (sorry, ich bin noch nicht bei SwiftUI angekommen):

    Quellcode

    1. long long value = 1;
    2. NSLog(@"%@", [NSString stringWithFormat:NSLocalizedString(@"%@ %lld", @""), @"second", value]);
    3. NSLog(@"%@", [NSString stringWithFormat:NSLocalizedString(@"second %lld", @""), value]);

    Das Ergebnis in der Konsole sieht wie folgt aus:

    Quellcode

    1. 2022-07-28 15:35:28.498193+0200 Plurals-Test[35155:1611613] second 1
    2. 2022-07-28 15:35:28.498299+0200 Plurals-Test[35155:1611613] Eine Sekunde

    Ich glaube, die entscheidende Zeile in der Apple Doku ist diese

    Apple schrieb:

    If the formatted string contains multiple variables, enter a separate subdictionary for each variable.
    Dass heisst nach meinem Verständnis, Du kannst zwar mehrere Variablen verwenden, diese werden aber unabhängig ausgewertet und brauchen entsprechende Einträge in der .stringsdict-Datei, für Sekunden und Minuten z. B. sowas:

    Quellcode

    1. <plist version="1.0">
    2. <dict>
    3. <key>second %lld</key>
    4. <dict>
    5. <key>NSStringLocalizedFormatKey</key>
    6. <string>%#@second@</string>
    7. <key>second</key>
    8. <dict>
    9. <key>NSStringFormatSpecTypeKey</key>
    10. <string>NSStringPluralRuleType</string>
    11. <key>NSStringFormatValueTypeKey</key>
    12. <string>lld</string>
    13. <key>zero</key>
    14. <string>sofort</string>
    15. <key>one</key>
    16. <string>Eine Sekunde</string>
    17. <key>other</key>
    18. <string>%d Sekunden</string>
    19. </dict>
    20. </dict>
    21. <key>minute %lld</key>
    22. <dict>
    23. <key>NSStringLocalizedFormatKey</key>
    24. <string>%#@minute@</string>
    25. <key>minute</key>
    26. <dict>
    27. <key>NSStringFormatSpecTypeKey</key>
    28. <string>NSStringPluralRuleType</string>
    29. <key>NSStringFormatValueTypeKey</key>
    30. <string>lld</string>
    31. <key>zero</key>
    32. <string>sofort</string>
    33. <key>one</key>
    34. <string>Eine Minute</string>
    35. <key>other</key>
    36. <string>%d Minuten</string>
    37. </dict>
    38. </dict>
    39. </dict>
    40. </plist>
    Alles anzeigen

    Eine Auswertung einer Variablen abhängig vom Wert einer zweiten scheint mir nicht möglich ... ich lerne aber gerne weiter dazu :)

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von MyMattes ()

  • MCDan schrieb:

    Hm ok, dann sollte es doch so funktionieren:
    Für einfach Lokalisierungen ja, aber @Heruhaundo möchte ja die "Magie", dass bei unterschiedlicher Anzahl auch unterschiedliche Übersetzungen angewendet werden, also "Jetzt" bei 0, "1 Sekunde" bei 1 und "<x> Sekunden" sonst. Deshalb muss NSLocalizedString auch eine numerische Variable beinhalten, um den richtigen Ausdruck zuzuordnen.

    Quark, String() macht auch eine Lokalisierung, es muss nur vorher der Formatierungsstring inkl. Platzhalter für die jeweilige Einheit gebaut werden.

    Wenn dabei "Sekunden" auch noch variabel sein soll und z. B. auch Minuten abbilden, kommt das .stringsdict-Konstrukt m. E. an seine Grenzen.

    Doch, das geht (mit einem Zwischenschritt), siehe unten.

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von MyMattes () aus folgendem Grund: Über Swift-Features von String() gestolpert, sorry.

  • So geht's ... erst den Formatierungs-String mit der unit-Variablen erstellen und dann das Ergebnis in die Lokalisierung werfen. Hier zur Verdeutlichung im Rahmen einer Schleife:

    Quellcode

    1. long long value = 1;
    2. NSArray <NSString *> *units = @[@"second", @"minute"];
    3. for (NSString *unit in units)
    4. {
    5. NSString *formatString = [NSString stringWithFormat:@"%@ %%lld", unit];
    6. NSLog(@"%@", [NSString stringWithFormat:NSLocalizedString(formatString, @""), value]);
    7. }
    Ausgabe bei Verwendung der o. g. .stringsdict-Datei:

    Quellcode

    1. 2022-07-28 16:21:18.797923+0200 Plurals-Test[35868:1639262] Eine Sekunde
    2. 2022-07-28 16:21:18.797973+0200 Plurals-Test[35868:1639262] Eine Minute
    Mattes

    Edit: @MCDan, entschuldige: Ich glaube, Dein Ansatz war der gleiche, ich habe als Swift-Ignorant nur nicht erkannt, dass "String" bei Dir auch eine Lokalisierung zur Folge hat. Letztlich muss vorher nur der Formatierungsstring (inkl. Platzhalter für die Substituierung) erstellt werden.
    Diese Seite bleibt aus technischen Gründen unbedruckt.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von MyMattes () aus folgendem Grund: MCDan's Antwort trifft eigentlich schon den Punkt...

  • Hm, ich sehe jetzt den Unterschied nicht.

    Die o.a. Zeilen stammen aus dem Beispiel auf levelup.gitconnected.com/step-…urals-in-ios-57f9deaade3e

    Über NSLocalizedString("\(self.unit)", comment: "") wird erst der passende Format String für "seconds", "minutes" etc. ermittelt. Dieser sieht dann z.B. so "%#@seconds@" aus.

    Über String(localized, Int(_value)) wird der Format String dann entsprechend des .stringsdict aufgelöst, also z.B. für String("%#@seconds@", 0) -> "sofort", String("%#@seconds@", 1) -> "1 Sekunde", String("%#@seconds@", 2) -> "2 Sekunden".

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von MCDan ()

  • Wäre es nicht ohnehin etwas eleganter, das ganze über einen DateComponentFormatter zu lösen? Dann bist du viel flexibler (weniger fehleranfällig) und bekommst die gesamte Localization für alle Sprachen umsonst. Folgende Implementierung z.B.:

    Quellcode

    1. let duration = TimeInterval(17) // 60, 220
    2. let formatter = DateComponentsFormatter()
    3. formatter.unitsStyle = .full
    4. let formattedDuration = formatter.string(from: duration)!
    5. print(formattedDuration) // "17 seconds", "1 minute", "3 minutes, 40 seconds"
    Mittels allowedUnits könntest du auch festlegen, falls z.B. auch Zeitintervalle über mehrere Minuten nur in Sekunden angegeben werden sollen (z.B. "220 seconds" anstatt "3 minutes, 40 seconds").
  • Osxer schrieb:

    Wäre es nicht ohnehin etwas eleganter, das ganze über einen DateComponentFormatter zu lösen? Dann bist du viel flexibler (weniger fehleranfällig) und bekommst die gesamte Localization für alle Sprachen umsonst. Folgende Implementierung z.B.:

    Quellcode

    1. let duration = TimeInterval(17) // 60, 220
    2. let formatter = DateComponentsFormatter()
    3. formatter.unitsStyle = .full
    4. let formattedDuration = formatter.string(from: duration)!
    5. print(formattedDuration) // "17 seconds", "1 minute", "3 minutes, 40 seconds"
    Mittels allowedUnits könntest du auch festlegen, falls z.B. auch Zeitintervalle über mehrere Minuten nur in Sekunden angegeben werden sollen (z.B. "220 seconds" anstatt "3 minutes, 40 seconds").
    Für das Beispiel ja, aber ich habe viel mehr Einheiten. z.B. Historieneinträge, Fehlversuche, Ziffer, Buchstaben, usw.
  • Ich habs ausprobiert, funktioniert leider nicht. Ich poste mal die komplette Struct:

    Quellcode

    1. struct PropertyLimitedNumberPicker : View {
    2. var iconSystemName : String
    3. var localizedStringKey : String
    4. var property : Binding<LimitedNumber>
    5. var units : [String]
    6. var body : some View {
    7. Picker(
    8. selection: self.property,
    9. label: HStack(spacing: .zero) {
    10. Components.propertyIcon(self.iconSystemName)
    11. Components.propertyLabel(LocalizedStringKey(String("\(self.localizedStringKey).label")))
    12. }
    13. ) {
    14. if let _increments = self.property.increments.wrappedValue {
    15. ForEach((self.property.lowerBound.wrappedValue ... self.property.upperBound.wrappedValue), id: \.self) { _value in
    16. ForEach((0 ... _increments.count - 1).reversed(), id: \.self) { _index in
    17. if(_value.isMultiple(of: _increments[_index]) && (_index == _increments.count - 1 ? true : _value < _increments[_index + 1])) {
    18. Text("\(self.units[_index]) \(Int(_value / (_index == 0 ? 1 : _increments[_index])))", tableName: "SingularAndPlural")
    19. .tag(LimitedNumber(_value: _value,
    20. _lowerBound: self.property.lowerBound.wrappedValue,
    21. _upperBound: self.property.upperBound.wrappedValue,
    22. _allowedZero: self.property.isZeroAllowed.wrappedValue,
    23. _allowedInfinity: self.property.isInfinityAllowed.wrappedValue,
    24. _increments: _increments)
    25. )
    26. .navigationTitle(LocalizedStringKey(String("\(self.localizedStringKey).title")))
    27. }
    28. }
    29. }
    30. } else {
    31. ForEach((self.property.lowerBound.wrappedValue ... self.property.upperBound.wrappedValue), id: \.self) { _value in
    32. Text("\(self.units[0]) \(Int(_value))", tableName: "SingularAndPlural")
    33. .tag(LimitedNumber(_value: _value,
    34. _lowerBound: self.property.lowerBound.wrappedValue,
    35. _upperBound: self.property.upperBound.wrappedValue,
    36. _allowedZero: self.property.isZeroAllowed.wrappedValue,
    37. _allowedInfinity: self.property.isInfinityAllowed.wrappedValue,
    38. _increments: self.property.increments.wrappedValue)
    39. )
    40. .navigationTitle(LocalizedStringKey(String("\(self.localizedStringKey).title")))
    41. }
    42. }
    43. }
    44. .pickerStyle(DefaultPickerStyle())
    45. }
    46. init(iconSystemName : String, localizedStringKey : String, property : Binding<LimitedNumber>, units : [String]) {
    47. self.iconSystemName = iconSystemName
    48. self.localizedStringKey = localizedStringKey
    49. self.property = property
    50. self.units = units
    51. }
    52. }
    Alles anzeigen
    Das Problem ist wohl auch, das ich das ganze in einem Text verwende und der braucht das Ganze als LocalizedStringKey.
    Ich steh total auf dem Schlauch.