← Назад к вопросам

Как локализовать Flutter-приложение?

2.2 Middle🔥 121 комментариев
#Flutter виджеты#Архитектура Flutter

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Локализация Flutter приложения

Локализация (i18n) — это процесс адаптации приложения к разным языкам и культурам. Это включает переводы текста, форматирование дат, валют, и другие локально-зависимые элементы.

Основные компоненты локализации

  1. i18n (Internationalization) — разработка приложения для поддержки множества языков
  2. l10n (Localization) — адаптация приложения к конкретному языку/культуре
  3. 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.dart
  • gen_l10n/app_localizations_en.dart
  • gen_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 для сохранения выбора пользователя

Локализация — это обязательная часть современного мобильного приложения, особенно если вы планируете выходить на международный рынок.