Как локализовать Flutter-приложение?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Локализация Flutter приложения
Локализация (i18n) — это процесс адаптации приложения к разным языкам и культурам. Это включает переводы текста, форматирование дат, валют, и другие локально-зависимые элементы.
Основные компоненты локализации
- i18n (Internationalization) — разработка приложения для поддержки множества языков
- l10n (Localization) — адаптация приложения к конкретному языку/культуре
- ARB файлы (Application Resource Bundle) — стандарт Google для хранения локализованных строк
Шаг 1: Установка зависимостей
# pubspec.yaml
dev_dependencies:
flutter_localizations:
sdk: flutter
intl: ^0.19.0
intl_utils: ^2.8.0 # Опционально, для генерации кода
Шаг 2: Настройка pubspec.yaml
flutter:
generate: true # Включить генерацию локализации
uses-material-design: true
Шаг 3: Создание ARB файлов
Создать папку lib/l10n/ и файлы для каждого языка:
lib/
l10n/
app_en.arb # Английский
app_ru.arb # Русский
app_es.arb # Испанский
app_de.arb # Немецкий
app_en.arb (Английский):
{
"@@locale": "en",
"helloWorld": "Hello, World!",
"welcome": "Welcome to {appName}",
"@welcome": {
"description": "Welcome message with app name",
"placeholders": {
"appName": {
"type": "String",
"example": "MyApp"
}
}
},
"itemCount": "{count, plural, =0{No items} one{1 item} other{{count} items}}",
"@itemCount": {
"description": "Number of items with plural support",
"placeholders": {
"count": {
"type": "int",
"example": "5"
}
}
}
}
app_ru.arb (Русский):
{
"@@locale": "ru",
"helloWorld": "Привет, мир!",
"welcome": "Добро пожаловать в {appName}",
"itemCount": "{count, plural, =0{Нет товаров} one{1 товар} few{{count} товара} other{{count} товаров}}"
}
Шаг 4: Генерация кода локализации
После сохранения ARB файлов, Flutter автоматически генерирует:
gen_l10n/app_localizations.dartgen_l10n/app_localizations_en.dartgen_l10n/app_localizations_ru.dart
Шаг 5: Настройка MaterialApp
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:gen_l10n/app_localizations.dart';
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
const Locale('en'),
const Locale('ru'),
const Locale('es'),
],
locale: const Locale('en'), // По умолчанию
home: const HomePage(),
);
}
}
Шаг 6: Использование локализованных строк
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return Scaffold(
appBar: AppBar(
title: Text(l10n.helloWorld),
),
body: Column(
children: [
Text(l10n.welcome(appName: 'MyApp')),
Text(l10n.itemCount(count: 5)),
],
),
);
}
}
Практический пример: приложение с переключением языков
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// Provider для управления языком
class LanguageProvider extends ChangeNotifier {
Locale _locale = const Locale('en');
Locale get locale => _locale;
void setLocale(Locale newLocale) {
if (_locale != newLocale) {
_locale = newLocale;
notifyListeners();
}
}
}
// Main app
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => LanguageProvider(),
child: Consumer<LanguageProvider>(
builder: (context, languageProvider, _) {
return MaterialApp(
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('en'),
const Locale('ru'),
const Locale('es'),
],
locale: languageProvider.locale,
home: const HomePage(),
);
},
),
);
}
}
// Home page с переключением языков
class HomePage extends StatelessWidget {
const HomePage();
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
final languageProvider = Provider.of<LanguageProvider>(context);
return Scaffold(
appBar: AppBar(
title: Text(l10n.helloWorld),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(l10n.welcome(appName: 'MyApp')),
const SizedBox(height: 32),
// Кнопки переключения языков
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
languageProvider.setLocale(const Locale('en'));
},
child: const Text('English'),
),
const SizedBox(width: 16),
ElevatedButton(
onPressed: () {
languageProvider.setLocale(const Locale('ru'));
},
child: const Text('Русский'),
),
const SizedBox(width: 16),
ElevatedButton(
onPressed: () {
languageProvider.setLocale(const Locale('es'));
},
child: const Text('Español'),
),
],
),
],
),
),
);
}
}
Форматирование дат и валют
import 'package:intl/intl.dart';
class FormattingExample {
// Форматирование даты
String formatDate(DateTime date, Locale locale) {
return DateFormat.yMMMMd(locale.toString()).format(date);
}
// Форматирование валюты
String formatCurrency(double amount, Locale locale, String currencyCode) {
return NumberFormat.currency(
locale: locale.toString(),
symbol: currencyCode,
).format(amount);
}
// Пример использования
void example(BuildContext context) {
final locale = Locale('ru', 'RU');
final now = DateTime.now();
print(formatDate(now, locale)); // 26 марта 2026 г.
print(formatCurrency(1234.56, locale, 'RUB')); // 1 234,56 ₽
}
}
Поддержка системного языка
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: [...],
supportedLocales: [
const Locale('en'),
const Locale('ru'),
],
// Использовать язык системы если поддерживается
locale: null, // системный язык
home: const HomePage(),
);
}
}
Тестирование локализации
import 'package:flutter_test/flutter_test.dart';
import 'package:gen_l10n/app_localizations.dart';
void main() {
testWidgets('localization test', (WidgetTester tester) async {
const ruLocale = Locale('ru');
await tester.binding.window.physicalSizeTestValue = const Size(800, 600);
addTearDown(tester.binding.window.clearPhysicalSizeTestValue);
await tester.pumpWidget(
MaterialApp(
locale: ruLocale,
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
],
supportedLocales: const [Locale('en'), Locale('ru')],
home: Scaffold(
body: Text(AppLocalizations.of(context)!.helloWorld),
),
),
);
expect(find.text('Привет, мир!'), findsOneWidget);
});
}
Лучшие практики
✅ Используйте ARB файлы — стандарт Google для локализации ✅ Хранить ключи в English ARB — основной файл ✅ Использовать placeholders — для динамического контента ✅ Поддержать системный язык — по умолчанию ✅ Тестировать локализацию — включить в unit тесты ✅ Форматировать даты/валюты — с учётом локали ✅ Не хардкодить строки — всегда через l10n ✅ Организовать переводы — через специальные сервисы (Phrase, OneSky)
Расширенные инструменты
Для управления переводами:
├─ Phrase (автоматизация переводов)
├─ OneSky (коллаборативный перевод)
├─ Crowdin (краудсорсинг переводов)
├─ Lokalise (управление локализацией)
└─ POEditor (простой инструмент)
Итоговая схема
1. Создать ARB файлы (app_en.arb, app_ru.arb, etc.)
2. Flutter генерирует code (gen_l10n/app_localizations.dart)
3. Обернуть MaterialApp в localizationsDelegates
4. Получить l10n через AppLocalizations.of(context)!
5. Использовать l10n строки в UI
6. Сохранить в SharedPreferences или Provider для сохранения выбора пользователя
Локализация — это обязательная часть современного мобильного приложения, особенно если вы планируете выходить на международный рынок.