Framework/Flutter

[Flutter]다국어 구현

  • -
반응형

 

최근 공유 가계부 앱을 런칭했습니다.

이제 기능을 하나씩 확장 중인데요. 오늘은 다국어 기능을 추가했던 과정을 기록해봤습니다.

서비스 중인 앱은 Flutter로 개발되었고, flutter_localizations와 intl 패키지를 이용해 다국어 기능을 구현했습니다.

 

테스트 환경
  • Flutter 3.27.1

intl 패키지는 Flutter에서 다국어(i18n)와 숫자, 날짜, 통화 형식 등의 로컬라이제이션을 지원하는 공식 패키지입니다.
flutter_localizations와 함께 사용하면 앱의 언어를 디바이스 설정에 따라 자동 변경할 수도 있고, 앱 내 설정을 통해 수동으로 변경할 수도 있습니다.

 

1. 패키지 다운로드

flutter pub add flutter_localizations --sdk=flutter
flutter pub add intl

 

 

2. 다국어 파일 추가

  • 한국어: lib/l10n/app_ko.arb
  • 영어: lib/l10n/app_en.arb

 

추가된 파일은 다음과 같습니다.

app_ko.arb

{
  "@@locale": "ko",
  "titleIncome": "수입",
  "titleExpense": "지출"
}

 

app_en.arb

{
  "@@locale": "en",
  "titleIncome": "Income",
  "titleExpense": "Expense"
}

 

3. 번역 코드 자동 생성

Flutter에서는 .arb 파일을 기반으로 번역 코드를 자동 생성합니다.

아래 명령어를 실행합니다.

flutter gen-l10n

 

만약 아래와 같은 오류가 발생한다면 pubspec.yaml으로 이동해 flutter 옵션에 generate: true 옵션을 추가합니다.

Attempted to generate localizations code without having the flutter: generate flag turned on.
Check pubspec.yaml and ensure that flutter: generate: true has been added and rebuild the project. Otherwise, the localizations source
code will not be importable.

 

 

4. MaterialApp 설정

MaterialApp의 localizationsDelegatessupportedLocales을 설정하여 다국어 지원을 추가합니다.

AppLocalizations를 찾지 못하는 경우 아래 파일을 import 합니다.

import 'package:flutter_gen/gen_l10n/app_localizations.dart';
MaterialApp.router(
    routerConfig: ref.watch(routerProvider),
    /....
    ..../
    debugShowCheckedModeBanner: false,
    // locale: localeProvider.locale,
    
    // 아래 추가
    locale: Locale('en'),
    localizationsDelegates: [
      AppLocalizations.delegate,
      GlobalMaterialLocalizations.delegate,
      GlobalWidgetsLocalizations.delegate,
      GlobalCupertinoLocalizations.delegate,
    ],
    supportedLocales: const [
      Locale('en'),
      Locale('ko'),
    ],
),

 

locale에 추가된 en에 의해 화면에 출력되는 문구는 영어로 보이게 됩니다.

 

5. 번역 적용

다국어가 적용된 코드의 일부를 가져왔습니다.

상단 선언된 l10n을 통해 다국어 번역 내용을 사용할 수 있게 됩니다. 2.에서 추가한 파일에 의해 titleIncome, titleExpense 키로 내용을 조회하게 됩니다.

 @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 번역 조회
    final l10n = AppLocalizations.of(context)!;
    final summaryStatus = ref.watch(summaryProvider(month));

    return Container(
      padding: const EdgeInsets.symmetric(
        horizontal: 20,
        vertical: 10,
      ),
      margin: const EdgeInsets.symmetric(horizontal: 15),
      decoration: const BoxDecoration(
          color: widgetMainColor,
          borderRadius: BorderRadius.all(
            Radius.circular(borderRadiusRate),
          )),
      child: summaryStatus.when(data: (data) {
        /..../
        return Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            Column(
              children: [
                // 번역 부분 1
                MyTextbox(
                  textField: l10n.titleIncome,
                  fontSize: fontXsmallSize,
                  fontColor: widgetWhiteColor,
                ),
                const SizedBox(height: 4),
                MyTextbox(
                  textField: '+${FormatUtil.formatCurrency(income)}원',
                  fontSize: fontSmallSize,
                  fontColor: widgetWhiteColor,
                  fontWeight: FontWeight.w800,
                ),
              ],
            ),
            Column(
              children: [
              // 번역 부분 2
                MyTextbox(
                  textField: l10n.titleExpense,
                  fontSize: fontXsmallSize,
                  fontColor: widgetWhiteColor,
                ),
                const SizedBox(height: 4),
                MyTextbox(
                  textField: '${FormatUtil.formatCurrency(expense)}원',
                  fontSize: fontSmallSize,
                  fontColor: widgetWhiteColor,
                  fontWeight: FontWeight.w800,
                ),
              ],
            ),
            Column(
              children: [
                MyTextbox(
                  textField: '합계',
                  fontSize: fontXsmallSize,
                  fontColor: widgetWhiteColor,
                ),
                const SizedBox(height: 4),
                MyTextbox(
                  textField:
                      '${total > 0 ? '+' : ''}${FormatUtil.formatCurrency(total)}원',
                  fontSize: fontSmallSize,
                  fontColor: widgetWhiteColor,
                  fontWeight: FontWeight.w800,
                ),
              ],
            ),
          ],
        );

 

6. 결과

 

 

참고문서
 

Internationalizing Flutter apps

How to internationalize your Flutter app.

docs.flutter.dev

 

반응형
Contents

포스팅 주소를 복사했습니다.

이 글이 도움이 되었다면 공감 부탁드립니다.