Flutter

Flutter에서 번역 기능 구현하기

GODOLs 2025. 1. 24. 14:11

Flutter에서 번역 기능 구현하기

1. 필요한 패키지 설치

먼저 pubspec.yaml에 필요한 패키지들을 추가합니다:

dependencies:

  translator: ^1.0.3  # Google Translate API 사용

  language\_detector: ^1.0.1  # 언어 감지

  freezed\_annotation: 2.4.4  # 상태 관리를 위한 freezed

dev\_dependencies:

  build\_runner: ^2.4.12

  freezed: 2.4.4

2. TranslatorService 구현

번역 기능을 담당할 서비스 클래스를 만듭니다:

import 'package:language\_detector/language\_detector.dart';

import 'package:translator/translator.dart';

import 'package:riverpod\_annotation/riverpod\_annotation.dart';

@Riverpod(keepAlive: true)

class TranslatorService extends \_$TranslatorService {

  @override

  String? build() => null;

  Future<String\> translate(String text) async {

    final translator = GoogleTranslator();

    final isSameLanguage = await detectSameLanguage(text);

    // 같은 언어면 번역하지 않음

    if (isSameLanguage) {

      return text;

    }

    // 사용자가 선택한 언어로 번역

    final translation = await translator.translate(text, to: 'ko');

    return translation.text;

  }

  Future<bool\> detectSameLanguage(String text) async {

    final languageCode = await LanguageDetector.getLanguageCode(content: text);

    return languageCode == 'ko'; // 한국어인 경우

  }

}

3. 상태 관리를 위한 Freezed 모델

게시글의 상태를 관리하기 위한 모델을 정의합니다:

@freezed

class Post with \_$Post {

  const factory Post({

    required String title,

    required String titleTranslated,

    required String content,

    required String contentTranslated,

    required bool isSameLanguage,

    required bool isTranslated,

  }) = \_Post;

}

4. 번역 기능 구현

class PostController extends \_$PostController {

  Future<void\> changePostLanguage(String postId) async {

    final post = state.value!.posts.firstWhere((post) => post.id == postId);

    // 이미 같은 언어면 번역하지 않음

    if (post.isSameLanguage) return;

    // 번역된 내용이 있으면 캐시된 내용 사용

    if (post.isTranslated) {

      final translatedPost = post.copyWith(

        titleTranslated: post.titleTranslated,

        contentTranslated: post.contentTranslated,

        isTranslated: true,

      );

      \_updatePost(postId, translatedPost);

      return;

    }

    // 새로운 번역 수행

    final translatedTitle = await \_translatorService.translate(post.title);

    final translatedContent = await \_translatorService.translate(post.content);

    final translatedPost = post.copyWith(

      titleTranslated: translatedTitle,

      contentTranslated: translatedContent,

      isTranslated: true,

    );

    \_updatePost(postId, translatedPost);

  }

  // 원본으로 복원

  Future<void\> restorePostLanguage(String postId) async {

    final post = state.value!.posts.firstWhere((post) => post.id == postId);

    final restoredPost = post.copyWith(

      titleTranslated: post.title,

      contentTranslated: post.content,

      isTranslated: false,

    );

    \_updatePost(postId, restoredPost);

  }

}

5. UI 구현

class TranslatePostCard extends ConsumerWidget {

  final Post post;

  const TranslatePostCard({

    Key? key,

    required this.post,

  }) : super(key: key);

  @override

  Widget build(BuildContext context, WidgetRef ref) {

    return Card(

      margin: const EdgeInsets.all(16.0),

      child: Padding(

        padding: const EdgeInsets.all(16.0),

        child: Column(

          crossAxisAlignment: CrossAxisAlignment.start,

          children: \[

            // 제목

            Text(

              post.isTranslated ? post.titleTranslated : post.title,

              style: const TextStyle(

                fontSize: 18,

                fontWeight: FontWeight.bold,

              ),

            ),

            const SizedBox(height: 8),

            // 본문

            Text(

              post.isTranslated ? post.contentTranslated : post.content,

              style: const TextStyle(fontSize: 16),

            ),

            const SizedBox(height: 16),

            // 번역 버튼

            if (!post.isSameLanguage) // 같은 언어가 아닐 때만 번역 버튼 표시

              Row(

                mainAxisAlignment: MainAxisAlignment.end,

                children: \[

                  TranslateButton(

                    isTranslated: post.isTranslated,

                    onPressed: () {

                      if (post.isTranslated) {

                        ref.read(postControllerProvider.notifier)

                           .restorePostLanguage(post.id);

                      } else {

                        ref.read(postControllerProvider.notifier)

                           .changePostLanguage(post.id);

                      }

                    },

                  ),

                \],

              ),

          \],

        ),

      ),

    );

  }

}

// 번역 버튼 위젯
class TranslateButton extends StatelessWidget {

  final bool isTranslated;

  final VoidCallback onPressed;

  const TranslateButton({

    Key? key,

    required this.isTranslated,

    required this.onPressed,

  }) : super(key: key);

  @override

  Widget build(BuildContext context) {

    return TextButton.icon(

      onPressed: onPressed,

      icon: Icon(

        isTranslated ? Icons.language : Icons.translate,

        size: 20,

        color: Colors.blue,

      ),

      label: Text(

        isTranslated ? '원본 보기' : '번역하기',

        style: const TextStyle(

          color: Colors.blue,

          fontSize: 14,

        ),

      ),

    );

  }

}

6. 원본 유지의 장점

  • 성능 최적화:
  • 한번 번역된 내용을 캐시로 저장해두면 반복적인 API 호출을 줄일 수 있습니다.
  • 사용자가 원본과 번역본을 전환할 때 즉각적인 응답이 가능합니다.
  • 사용자 경험:
  • 원본과 번역본을 빠르게 전환할 수 있어 더 나은 UX를 제공합니다.
  • 번역의 정확도를 사용자가 직접 확인할 수 있습니다.
  1. 데이터 무결성:
  • 원본 데이터가 보존되어 있어 번역 오류가 발생하더라도 복구가 가능합니다.
  • 중요한 정보가 번역 과정에서 손실되는 것을 방지합니다.
  • 비용 절감:
  • 불필요한 번역 API 호출을 줄여 비용을 절감할 수 있습니다.
  • 캐시된 번역 결과를 재사용하여 서버 부하를 줄일 수 있습니다.

마무리

이렇게 구현된 번역 기능은 사용자들에게 다음과 같은 이점을 제공합니다:

  • 직관적인 UI로 쉽게 번역 기능을 사용할 수 있습니다.
  • 원본과 번역본을 자유롭게 전환할 수 있습니다.
  • 빠른 응답 속도로 좋은 사용자 경험을 제공합니다.
  • 효율적인 리소스 관리로 안정적인 서비스가 가능합니다.

Freezed를 활용한 상태 관리와 원본 데이터 보존 전략으로 안정적이고 효율적인 번역 기능을 구현할 수 있습니다.

반응형