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 메서드를 사용하여 번역 데이터를 추가하고 관리할 수 있습니다. 이 메서드를 통해 새로운 언어 데이터를 동적으로 추가하거나 기존 데이터를 업데이트할 수 있습니다.
번역 문자열 검색
String 확장 함수에서 tr 메서드를 사용하여 현재 언어 설정에 맞는 번역된 문자열을 가져올 수 있습니다. 특정 언어 설정이 없을 경우, 기본 언어 설정을 사용하여 번역된 문자열을 가져옵니다.
다양한 번역 옵션 제공
trArgs와 trParams 같은 메서드를 사용하여 번역된 문자열에 동적 인수와 매개변수를 삽입할 수 있습니다. 이를 통해 더 유연한 번역 문자열을 처리할 수 있습니다.
결론
이 블로그에서는 Flutter 애플리케이션에서 다국어 지원을 위한 커스텀 번역 기능을 구현하는 방법을 알아보았습니다. GetX 라이브러리를 사용하여 동적으로 번역 데이터를 추가하고, 다양한 언어 설정에 따라 번역된 문자열을 관리하는 방법을 배웠습니다. 이 코드를 응용하여 다양한 언어 지원이 필요한 애플리케이션에 적용할 수 있습니다.
추가적으로 번역 데이터 파일을 JSON이나 YAML 형식으로 외부에서 관리하고, 이를 애플리케이션에 동적으로 로드하는 방법을 구현하면 더 효과적으로 다국어 지원을 할 수 있습니다.