AGA в Swift не работает в inline if-выражениях
Узнайте, почему автоматическое согласование грамматики (AGA) в Swift не работает в inline if-выражениях, и найдите правильные решения для условного согласования в iOS-разработке.
Почему автоматическое согласование грамматики (AGA) в Swift не работает в строковых выражениях с условным оператором if?
У меня возникла проблема: AGA работает корректно в обычном тексте, но не работает, когда используется внутри inline‑условного оператора.
Обычное поведение AGA
Text("I have ^[\(dogCount) dogs](inflect: true).")
При dogCount = 2 выводится «I have 2 dogs.»
При dogCount = 1 выводится «I have 1 dog.»
Проблема с inline‑условным оператором
Text(dogCount == 0 ? "No Dogs?!" : "I have ^[\(dogCount) dogs](inflect: true).")
Условный оператор работает правильно, но AGA внутри ветки else не склоняется.
Как правильно реализовать условное согласование грамматики в Swift, когда используется inline‑условный оператор?
Swift‑евая автоматическая согласованность грамматики (AGA) не работает в встроенных if‑выражениях, потому что параметр inflect: true требует специфической обработки строки, которая выполняется во время локализации, а это не происходит, когда строка является частью условного выражения. Синтаксис AGA ^[variable] (inflect: true) должен обрабатываться в контексте локализованного AttributedString, но встроенные условия обходят этот механизм локализации.
Содержание
- Понимание ограничения AGA
- Почему встроенные
if‑выражения ломают AGA - Решения для условной согласованности грамматики
- Лучшие практики реализации AGA
- Продвинутый контроль морфологии
- Вывод
Понимание ограничения AGA
Автоматическая согласованность грамматики в Swift опирается на параметр inflect: true внутри локализованных строк. Как описано в документации Apple по InflectionRule, эта функция работает через AttributedString, которые проходят морфологический анализ во время процесса локализации.
Синтаксис AGA ^[variable] (inflect: true) специально предназначен для локализованного контента, позволяя системе анализировать грамматический контекст и применять соответствующие правила склонения. Однако, когда вы помещаете этот синтаксис в встроенное условие, строка не проходит через обычный пайплайн локализации, который обеспечивает функциональность AGA.
Ключевой вывод: AGA требует обработки
AttributedStringс параметромlocalized:для корректной работы. Встроенные условия полностью обходят этот механизм.
Почему встроенные if‑выражения ломают AGA
Когда вы используете встроенные if‑выражения, например тернарный оператор, в вашем примере:
Text(dogCount == 0 ? "No Dogs?!" : "I have ^[\(dogCount) dogs](inflect: true).")
Проблема возникает из‑за того, что:
- Время обработки строки: Синтаксис AGA должен обрабатываться во время локализации, но встроенные условия создают строку во время выполнения, обходя этот этап.
- Требование к
AttributedString: Согласно форумам Apple, AGA требуетAttributedStringс корректным морфологическим анализом, чего нет в условных выражениях. - Контекст локализации: Параметр
inflect: trueожидает находиться внутри локализованной строки, но встроенные условия нарушают этот контекст.
Как отмечено в обсуждении на Stack Overflow, это известное ограничение, когда AGA просто не функционирует, когда встроена в условную логику.
Решения для условной согласованности грамматики
Решение 1: Использовать отдельные AttributedString
Самый надёжный подход — создать отдельные AttributedString для каждого условия и использовать правильную локализацию:
Text(
dogCount == 0
? AttributedString(localized: "No Dogs?!")
: createInflectedAttributedString(count: dogCount)
)
private func createInflectedAttributedString(count: Int) -> AttributedString {
let string = AttributedString(localized: "I have ^[\(count) dogs](inflect: true).")
return string
}
Решение 2: Ручной контроль морфологии
Для более сложных сценариев можно использовать Morphology и InflectionRule напрямую:
private func createConditionalAttributedString(count: Int) -> AttributedString {
var morphology = Morphology()
let number: Morphology.GrammaticalNumber = count == 0
? .zero
: count == 1
? .singular
: .plural
morphology.number = number
var string: AttributedString
if count == 0 {
string = AttributedString(localized: "No Dogs?!")
} else {
string = AttributedString(localized: "I have \(count) dogs.")
}
string.inflect = InflectionRule(morphology: morphology)
return string.inflected()
}
Решение 3: Условное построение строки
Простой подход — построить строку отдельно:
private func createDogCountMessage(count: Int) -> AttributedString {
if count == 0 {
return AttributedString(localized: "No Dogs?!")
}
return AttributedString(
localized: "I have ^[\(count) dogs](inflect: true)."
)
}
// Использование
Text(createDogCountMessage(count: dogCount))
Лучшие практики реализации AGA
1. Избегайте встроенных AGA в условиях
Никогда не встраивайте синтаксис AGA в условные выражения. Вместо этого разделяйте логику:
// ❌ Не делайте так
Text(dogCount == 0 ? "No Dogs?!" : "I have ^[\(dogCount) dogs](inflect: true).")
// ✅ Делайте так
Text(getDogCountMessage(dogCount))
2. Используйте AttributedString последовательно
Как отмечено в форуме Hacking with Swift, всегда работайте с AttributedString, когда используете функции AGA:
private func getInflectedString(_ format: String, _ arguments: CVarArg...) -> AttributedString {
return AttributedString(localized: String(format: format, arguments))
}
3. Обрабатывайте крайние случаи явно
AGA работает лучше всего с обычными формами множественного числа, но вам может понадобиться специальная обработка:
private func getPersonCountMessage(count: Int) -> AttributedString {
switch count {
case 0:
return AttributedString(localized: "No people found.")
case 1:
return AttributedString(localized: "I have ^[\(count) person](inflect: true).")
default:
return AttributedString(localized: "I have ^[\(count) people](inflect: true).")
}
}
Продвинутый контроль морфологии
Для сложных сценариев, требующих тонкого контроля над согласованием грамматики, можно работать напрямую с Morphology:
private func createAdvancedAttributedString(count: Int, item: String) -> AttributedString {
var morphology = Morphology()
morphology.number = count == 1 ? .singular : .plural
var string: AttributedString
if count == 0 {
string = AttributedString(localized: "No \(item)s available.")
} else {
string = AttributedString(localized: "You have ^[\(count) \(item)](inflect: true).")
}
string.inflect = InflectionRule(morphology: morphology)
return string.inflected()
}
Этот подход даёт больше контроля над грамматическими правилами и обрабатывает случаи, когда синтаксис AGA может не работать как ожидается.
Вывод
Автоматическая согласованность грамматики в Swift не работает в встроенных if‑выражениях, потому что параметр inflect: true требует специфической локализации и обработки AttributedString, которую встроенные условия не обеспечивают. Чтобы корректно реализовать условную согласованность грамматики:
- Разделяйте условную логику от синтаксиса AGA, создавая отдельные функции.
- Последовательно используйте
AttributedStringс правильной локализацией. - При необходимости применяйте
MorphologyиInflectionRuleдля сложных грамматических требований. - Явно обрабатывайте крайние случаи, когда стандартное множественное число не подходит.
Ключевой момент — понять, что AGA является функцией локализации во времени, требующей надлежащего контекста строки, который встроенные условия не могут предоставить. Перестраивая код так, чтобы он работал с объектами AttributedString и корректной локализацией, вы сможете достичь того же условного поведения, сохраняя преимущества автоматической согласованности грамматики.
Источники
- Automatic Grammar Agreement with an In‑line If Statement – Stack Overflow
- InflectionRule | Apple Developer Documentation
- Apple Developer Forums – Automatic Grammar Agreement
- Taking advantage of the iOS 15 grammar agreement feature for English strings – Stack Overflow
- Automatic Grammar Agreement Makes It Easy to Handle Plurals in Your iOS App – Osas Blogs