Flutter를 하면서 안드로이드에서 ios와 다르게 동작해서 해결하기 어려웠던 경우가 꽤 있었는데 notification이 특히 그랬다.
특히 안드로이드는 ios와 달리 background에서 별도의 isolate가 생기기 때문에 foreground의 context 등 변수를 사용할 수 없고, 설정에서 언어를 변경했을 때 앱이 재시작되는 ios와 달리 앱이 재시작되지 않기 때문에 다국어와 관련해서도 추가적으로 처리가 필요할 수 있다.
앱에서 notification 텍스트를 자체적으로 다국어 처리한다면 foreground에서는 크게 문제가 되지 않지만, background에서는 위와 같은 안드로이드의 특성에 영향을 받을 수 있다.
기존에는 다국어 처리를 위해 l10n
을 사용하고 있었는데, 항상 context를 사용해야 한다.
AppLocalizations.of(context).save
그런데 안드로이드 background에서는 context가 저장되지 않기 때문에 먼저 context를 사용하지 않는 다국어 패키지인 easy_localization
을 찾았다.
easy_localization
https://pub.dev/packages/easy_localization
이 패키지를 사용하면 context를 사용하지 않지만, 안드로이드 background에서는 다국어 정보를 찾지 못했다.
분명 비슷한 문제를 겪는 사람이 있을 거라고 생각했고, 구글링하던 중에 easy_localization 깃헙에서 다음과 같은 issue를 찾았다.
https://github.com/aissat/easy_localization/issues/210
그리고 댓글을 참고해서 background에 새로운 easy_localization는 init해주는 코드를 추가하였다.
// 자동으로 import되지 않으므로 추가해주어야 함
import 'package:easy_localization/src/easy_localization_controller.dart';
FirebaseMessaging.onBackgroundMessage(_handleBackgroundMessage);
Future<void> _handleBackgroundMessage(RemoteMessage event) async {
...
const locales = [Locale('en'), Locale('ko')];
const localizationPath = 'assets/translations';
// background에서 EasyLocalization 다시 init
await EasyLocalization.ensureInitialized();
final controller = EasyLocalizationController(
// true로 설정하면 처음 불러온 locale이 저장되고, 그 다음에는 저장된 locale을 먼저 불러옴
saveLocale: true,
supportedLocales: locales,
useOnlyLangCode: true,
fallbackLocale: locales[0],
useFallbackTranslations: true,
path: localizationPath,
onLoadError: (FlutterError e) {},
assetLoader: const RootBundleAssetLoader(),
);
await controller.loadTranslations();
Localization.load(
controller.locale,
translations: controller.translations,
fallbackTranslations: controller.fallbackTranslations,
);
...
}
이렇게 하면 background에서도 다국어 처리된 메시지를 볼 수 있다.
그런데 여기까지만 처리하면 안드로이드 설정에서 기기 언어를 변경했을 때 변경된 언어가 반영되지 않는다.
(하지만 실제 언어를 변경해서 사용하는 사용자는 많지 않을 것 같다)
특히 saveLocale을 true로 설정하면 처음 설정된 언어가 저장되어 계속 불러온다.
saveLocale을 false로 바꾸고, background에서 기기 언어를 불러오기 위해 다음과 같은 코드를 사용하였지만 background에서 기기 언어를 아에 읽지 못했다.
Platform.localeName
WidgetsBinding.instance.window.locales
// easy_localization
context.deviceLocale
기기의 언어를 알 수 있는 방법을 찾다가 devicelocale
이라는 패키지를 찾았고, 이 패키지를 사용하면 background에서도 현재 locale을 읽을 수 있었다.
devicelocale
https://pub.dev/packages/devicelocale
그래서 위의 코드에 현재 기기의 언어를 확인해서 easy_localization
으로 locale을 바꿔주는 코드를 추가하였다.
// 현재 언어 확인
final currentLocale = await Devicelocale.currentLocale;
await EasyLocalization.ensureInitialized();
final controller = EasyLocalizationController(
saveLocale: false,
supportedLocales: locales,
useOnlyLangCode: true,
fallbackLocale: locales[0],
useFallbackTranslations: true,
path: localizationPath,
onLoadError: (FlutterError e) {},
assetLoader: const RootBundleAssetLoader(),
);
// 현재 기기 언어로 locale set
if (currentLocale != null) {
await controller.setLocale(Locale(currentLocale.substring(0, 2)));
}
await controller.loadTranslations();
Localization.load(
controller.locale,
translations: controller.translations,
fallbackTranslations: controller.fallbackTranslations,
);
열심히 삽질을 해서 결국 발견한 문제들은 모두 해결하였지만 다국어 처리를 포함해서 notification message 자체를 서버에서 내려주기로 변경되어, 최종적으로 이 코드들은 사용되지 않았다.
아무래도 앱에서 notification message까지 처리하게 되면 새로운 notification을 추가할 때마다 앱을 새로 배포해야 하고, 앱 심사기간이 있기 때문에 공지 같은 단발성 notification에도 빠르게 대응하기 어려울 것이다.
그렇지만 이 문제를 해결하면서 notification과 관련해서 많은 부분을 새로 공부할 수 있었다.
단순히 문서나 다른 사람의 기록을 보는 것 보다 실제로 코드를 짜보고 특히 에러가 발생했을 때 해결하면서 많이 배우게 되는 것 같다.
또, 플러터를 사용한지 이제 반년정도 됬는데 문제를 하나씩 해결할 때마다 뿌듯하기도 하지만 한편으로는 아직 참고할만한 자료가 많지 않은 것 같다.
물론 아직 검색어를 잘 못 잡거나, 공식문서를 제대로 살펴보지 않아서 그럴 수도 있지만, 분명 나만 겪는 문제가 아닐 것 같은데???
생각보다 찾아보면 자료 찾기가 어렵다고 느낀다.
플러터 생태계가 빨리 빨리 커지길....!