Плагин Flutter Intl, используемый с динамическими строками
Я использую плагин Flutter Intl от Localizely для локализации своего приложения. Я сгенерировал файлы arb для нужных мне языков и начал вводить переводы.
Например:
{
"documentsSection": "All the documents",
"favouritesSection": "Favourites",
"newsSection": "News",
"settingsSection": "Settings"
}
Каждый раз, когда я хочу локализовать текст, который использую:
S.of(context).favouritesSection;
И работает отлично.
Однако, когда у меня есть такой список:
List<Strings> sectionTitles = ["documentsSection","favouritesSection","newsSection","settingsSection"]
И я нахожусь внутри цикла itemBuilder, подобного этому:
itemBuilder: (context, index) {
String sectionName = sectionTitles[index];
return Text(
S.of(context).sectionName,
),
},
Очевидно, что это не работает, поскольку "sectionName" не является ключом в файле произвольной настройки. Но я думаю, что код выражает то, чего я хочу достичь. Может кто мне поможет. Заранее спасибо.
3 ответа
Другой способ решить эту проблему - использовать сообщения типа Select ICU format.
Обратите внимание, это решение может вводить некоторые ограничения, но, с другой стороны, оно более элегантно.
Объявление строкового ключа:
"sectionTitles": "{section, select, documentsSection {Documents section} favouritesSection {Favourites section} newsSection {News section} settingsSection {Settings section} other {Section}}",
"@sectionTitles": {
"placeholders": {
"section": {}
}
}
Использование строкового ключа:
itemBuilder: (context, index) {
String sectionName = sectionTitles[index];
return Text(
S.of(context).sectionTitles(sectionName),
),
},
Я думаю, есть два способа добиться желаемого.
Первый - это создание функции, которая отображает ваши sectionTitles
в ваши строки intl, примерно так:
String getSectionTitle(BuildContext context, String title) {
if (title == "documentsSection") {
return S.of(context).documentsSection;
} else if (title == "favouritesSection") {
return S.of(context).favouritesSection;
} else if (title == "newsSection") {
return S.of(context).newsSection;
} else if (title == "settingsSection") {
return S.of(context).settingsSection;
}
}
И используя вот так:
...
itemBuilder: (context, index) {
return Text(
getSectionTitle(context, sectionTitles[index]),
);
},
...
Второй - создание массива с вашими строками intl:
List<String> sectionTitles = [
S.of(context).documentsSection,
S.of(context).favouritesSection,
S.of(context).newsSection,
S.of(context).settingsSection,
];
Но вам нужно будет создать это внутри вашей функции сборки, потому что вам нужен контекст:
@override
Widget build(BuildContext context) {
List<String> sectionTitles = [
S.of(context).documentsSection,
S.of(context).favouritesSection,
S.of(context).newsSection,
S.of(context).settingsSection,
];
return ...
itemBuilder: (context, index) {
return Text(
sectionTitles[index],
);
},
...
}
Другой способ добиться этого без использования контекста из вашей функции сборки - использовать didChangeDependencies
метод доступен на StatefulWidgets
, нравится:
List<String> sectionTitles;
@override
void didChangeDependencies() {
super.didChangeDependencies();
sectionTitles ??= [
S.of(context).documentsSection,
S.of(context).favouritesSection,
S.of(context).newsSection,
S.of(context).settingsSection,
];
}
@override
Widget build(BuildContext context) {
return ...
itemBuilder: (context, index) {
return Text(
sectionTitles[index],
);
},
...
}
Обратите внимание, что в этом случае вы не можете использовать initState
потому что он не предоставит контекст с уже доступными строками intl, поэтому мы используем didChangeDependencies
.
Если вам интересно, что ??=
делает, он просто проверяет, есть ли у переменной (в данном случае sectionTitles
) имеет значение null, и если это так, он присвоит ему значения. Мы используем его здесь, чтобы не переопределятьsectionTitles
каждый раз.