Flutter에서 다국어 지원을 위한 커스텀 번역 기능 구현하기

길위에 히피·2024년 6월 21일
0

Flutter

목록 보기
27/40

Flutter 애플리케이션에서 다국어 지원을 구현하는 것은 글로벌 사용자를 위한 중요한 기능입니다. 이 블로그에서는 Flutter의 GetX 라이브러리를 사용하여 커스텀 번역 기능을 어떻게 구현하는지에 대해 자세히 설명합니다.

기본 구조
이 코드는 Flutter 애플리케이션에서 다국어 지원을 위한 커스텀 번역 기능을 구현하기 위한 것입니다. GetX 라이브러리를 사용하여 언어 설정, 번역 문자열 관리, 번역 기능을 확장합니다.

주요 클래스와 기능
_IntlHost 클래스

class _IntlHost {
  Locale? locale; // 현재 언어 설정
  Locale? fallbackLocale; // 기본 언어 설정
  Map<String, Map<String, String>> translations = {}; // 번역 데이터 저장
}
locale: 현재 사용 중인 언어와 지역을 나타냅니다.
fallbackLocale: 기본 언어로 설정된 Locale입니다. 만약 현재 언어의 번역이 없다면 기본 언어를 사용합니다.
translations: 각 언어와 키에 대한 번역 데이터를 저장하는 맵입니다.
FirstWhereExt 확장 함수
dart
코드 복사
extension FirstWhereExt<T> on List<T> {
  T? firstWhereOrNull(bool Function(T element) test) {
    for (var element in this) {
      if (test(element)) return element;
    }
    return null;
  }
}

firstWhereOrNull: 주어진 조건에 맞는 첫 번째 요소를 찾고, 조건에 맞는 요소가 없다면 null을 반환하는 함수입니다. 이 함수는 컬렉션에서 조건에 맞는 요소를 찾을 때 유용합니다.
LocalesIntl 확장 함수

dart

extension LocalesIntl on GetInterface {
  static final _intlHost = _IntlHost();

  Locale? get locale => _intlHost.locale;
  Locale? get fallbackLocale => _intlHost.fallbackLocale;

  set locale(Locale? newLocale) => _intlHost.locale = newLocale;
  set fallbackLocale(Locale? newLocale) => _intlHost.fallbackLocale = newLocale;

  Map<String, Map<String, String>> get translations => _intlHost.translations;

  void addTranslations(Map<String, Map<String, String>> tr) {
    translations.addAll(tr);
  }

  void clearTranslations() {
    translations.clear();
  }

  void appendTranslations(Map<String, Map<String, String>> tr) {
    tr.forEach((key, map) {
      if (translations.containsKey(key)) {
        translations[key]!.addAll(map);
      } else {
        translations[key] = map;
      }
    });
  }
}

locale 및 fallbackLocale: 현재 언어와 기본 언어를 가져오고 설정할 수 있는 속성입니다.
translations: 현재 저장된 모든 번역 데이터를 가져옵니다.
addTranslations: 새로운 번역 데이터를 추가합니다.
clearTranslations: 모든 번역 데이터를 삭제합니다.
appendTranslations: 기존 번역 데이터에 새 데이터를 추가합니다.
Trans 확장 함수
dart
코드 복사

extension Trans on String {
  bool get _fullLocaleAndKey {
    return Get.translations.containsKey(
            "${Get.locale!.languageCode}_${Get.locale!.countryCode}") &&
        Get.translations[
                "${Get.locale!.languageCode}_${Get.locale!.countryCode}"]!
            .containsKey(this);
  }

  Map<String, String>? get _getSimilarLanguageTranslation {
    final translationsWithNoCountry = Get.translations
        .map((key, value) => MapEntry(key.split("_").first, value));
    final containsKey = translationsWithNoCountry
        .containsKey(Get.locale!.languageCode.split("_").first);

    if (!containsKey) {
      return null;
    }

    return translationsWithNoCountry[Get.locale!.languageCode.split("_").first];
  }

  String get tr {
    if (Get.locale?.languageCode == null) return this;

    if (_fullLocaleAndKey) {
      return Get.translations[
          "${Get.locale!.languageCode}_${Get.locale!.countryCode}"]![this]!;
    }
    final similarTranslation = _getSimilarLanguageTranslation;
    if (similarTranslation != null && similarTranslation.containsKey(this)) {
      return similarTranslation[this]!;
    } else if (Get.fallbackLocale != null) {
      final fallback = Get.fallbackLocale!;
      final key = "${fallback.languageCode}_${fallback.countryCode}";

      if (Get.translations.containsKey(key) &&
          Get.translations[key]!.containsKey(this)) {
        return Get.translations[key]![this]!;
      }
      if (Get.translations.containsKey(fallback.languageCode) &&
          Get.translations[fallback.languageCode]!.containsKey(this)) {
        return Get.translations[fallback.languageCode]![this]!;
      }
      return this;
    } else {
      return this;
    }
  }

  String trArgs([List<String> args = const []]) {
    var key = tr;
    if (args.isNotEmpty) {
      for (final arg in args) {
        key = key.replaceFirst(RegExp(r'%s'), arg.toString());
      }
    }
    return key;
  }

  String trPlural([String? pluralKey, int? i, List<String> args = const []]) {
    return i == 1 ? trArgs(args) : pluralKey!.trArgs(args);
  }

  String trParams([Map<String, String> params = const {}]) {
    var trans = tr;
    if (params.isNotEmpty) {
      params.forEach((key, value) {
        trans = trans.replaceAll('@$key', value);
      });
    }
    return trans;
  }

  String trPluralParams(
      [String? pluralKey, int? i, Map<String, String> params = const {}]) {
    return i == 1 ? trParams(params) : pluralKey!.trParams(params);
  }
}

tr: 현재 설정된 언어에 해당하는 번역된 문자열을 반환합니다. 번역된 문자열이 없으면 기본 문자열을 반환합니다.
trArgs: 번역된 문자열에 인수를 삽입합니다. '%s'를 인수로 대체합니다.
trPlural: 단수형과 복수형을 처리하여 번역된 문자열을 반환합니다.
trParams: 번역된 문자열에 매개변수 값을 삽입합니다.
trPluralParams: 복수형 매개변수 값을 삽입하여 번역된 문자열을 반환합니다.
코드 분석
1. 번역 데이터 추가와 관리
addTranslations와 appendTranslations 메서드를 사용하여 번역 데이터를 추가하고 관리할 수 있습니다. 이 메서드를 통해 새로운 언어 데이터를 동적으로 추가하거나 기존 데이터를 업데이트할 수 있습니다.

  1. 번역 문자열 검색
    String 확장 함수에서 tr 메서드를 사용하여 현재 언어 설정에 맞는 번역된 문자열을 가져올 수 있습니다. 특정 언어 설정이 없을 경우, 기본 언어 설정을 사용하여 번역된 문자열을 가져옵니다.

  2. 다양한 번역 옵션 제공
    trArgs와 trParams 같은 메서드를 사용하여 번역된 문자열에 동적 인수와 매개변수를 삽입할 수 있습니다. 이를 통해 더 유연한 번역 문자열을 처리할 수 있습니다.

결론
이 블로그에서는 Flutter 애플리케이션에서 다국어 지원을 위한 커스텀 번역 기능을 구현하는 방법을 알아보았습니다. GetX 라이브러리를 사용하여 동적으로 번역 데이터를 추가하고, 다양한 언어 설정에 따라 번역된 문자열을 관리하는 방법을 배웠습니다. 이 코드를 응용하여 다양한 언어 지원이 필요한 애플리케이션에 적용할 수 있습니다.

추가적으로 번역 데이터 파일을 JSON이나 YAML 형식으로 외부에서 관리하고, 이를 애플리케이션에 동적으로 로드하는 방법을 구현하면 더 효과적으로 다국어 지원을 할 수 있습니다.

profile
마음맘은 히피인 일꾼러

0개의 댓글