๊ฐ์
ํ๋ฌํฐ์์ ํ์งํ(Localization)๋ฅผ ์ ์ฉํ๊ธฐ ์ํ ํจํค์ง๋ ๋ค์ํ๋ค.
๊ฐ์ฅ ๋ํ์ ์ผ๋ก ์ฌ์ฉ๋๋ ํจํค์ง์ธ easy_localization๊ณผ ํ๋ฌํฐ ๊ธฐ๋ณธ ํจํค์ง์ธ flutter_localizations๊ฐ ์๋ค (flutter_localization ์๋)
๊ทธ๋ฌ๋ ์ค ์ค์๊ฐ ๋ฐ์ํ ์ฑ์ ๊ฐ๋ฐํ๋ค๊ฐ localization์ ์ ์ฉํ์ ๋ ๋งค๋ฒ ์ฑ์ ์ฌ์์ํ๋ค๋ ์ ์ด ๋๋ฅผ ๊ดด๋กญ๊ฒ ํ๋ค.
๋ํ ์ธ๋ถ ํจํค์ง๋ฅผ ๋์๋ฐ์์ localization์ ์ ์ฉํ๋ ค๋ ์ฑ์ด ๋ฌด๊ฑฐ์์ง๋ ๋๋๋ ๋๋ ๊ฒ ์ข์ง ์์๋ค.
์ด์ฐธ์ ์๋กญ๊ฒ ๊ฐ๋ฐ์ ํ๋ ๊ฒธ ๋๋ ์ธ๋ถํจํค์ง์ ๋์ ์์ด ์ง์ localization์ ์ ์ฉํด ๋ณด์๊ณ ๊ฒฐ์ฌ์ ํ๊ณ , ๊น๋ํ๊ฒ ๋๋ง์ ๋ฐฉ์์ผ๋ก localization์ ์ ์ฉํ๊ฒ ๋์๋ค.
๊ตฌ์ฑ
- Flutter 3.10.x
- Android Studio
- Riverpod (๋ค๋ฅธ ์ํ๊ด๋ฆฌ ํจํค์ง๋ก๋ ๊ฐ๋ฅ)
- Android (Web, IOS, PC ๋ชจ๋ ๊ฐ๋ฅ
์งํ
ํ๋ก์ ํธ๋ฅผ ์์ฑํ๊ณ ๊ฐ์ฅ ๋จผ์ ํ ์ผ์ riverpod ์์กด์ฑ์ ์ถ๊ฐํ๋๋ก ํ๋ค.
flutter pub add riverpod
๋ง์ฝ ๋ค๋ฅธ ์ํ๊ด๋ฆฌํด์ ์ฌ์ฉํ๋ค๋ฉด ๋ฐ๊ฟ๋ ๋๋ค.
์ด์ ํ๋ก์ ํธ์ ๊ธฐ๋ณธ ๊ตฌ์กฐ์ ๋ํด ์ค๋ช ์ ํ๋๋ก ํ๊ฒ ๋ค.
- app_localizations.dart: ์ฌ์ ์ ์๋ ํ์งํ ๊ด๋ จ ์์ ๋ฐ ์ ์ ๋ณ์๋ฅผ ๊ฐ์ง๊ณ ์๋ค.
- widget_ref_extension.dart: RiverPod์์ ์ํ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํ ์ฃผ์ ํด๋์ค์ธ WidgetRef๋ฅผ ํ์ฅํ์ฌ Localization๊ธฐ๋ฅ์ ์ถ๊ฐํ์ฌ ์ฌ์ฉํ๊ธฐ ์ํจ. ๋ง์ฝ Riverpod์ ์ํ ChangeNotification์ ์ฌ์ฉํ ํ์๊ฐ ์๊ฑฐ๋ WidgetRef ์์ฒด๋ฅผ ์ฌ์ฉํ์ง ์๋๋ค๋ฉด, current_language_state๋ฅผ ์ฑ๊ธํค ํจํด์ผ๋ก ๋ณ๊ฒฝํ ํ BuildContext์ extension๋ก ๋ง๋ค์ด ์ฌ์ฉํด๋ ๋๋ค. (๋์ ์์ ฏ์ ์ง์ ์๋ก๊ณ ์ณ์ผ ํด๋น ํ๋ฉด์์ ์ธ์ด๊ฐ ๋ณ๊ฒฝ๋จ)
- current_language_state.dart: ํ์ฌ ์ธ์ด์ ๋ํ ์ํ๋ฅผ ์ ๊ณตํด์ค๋ค.
- home_screen.dart: UI ๊ตฌ์ฑ
์ด์ ์ฝ๋๋ฅผ ์ฐจ๊ทผ์ฐจ๊ทผ ์ดํด๋ณด๋ฉฐ ์ดํดํ๋๋ก ํ๊ฒ ๋ค.
๋จผ์ app_localization.dart๋ถํฐ ์์ํ๊ฒ ๋ค.
<app_localization.dart>
//์ธ๋ถ์์ ์ํ๋ฅผ ๋ค๋ฃจ๊ธฐ ์ํ ์ธ์ด์ฝ๋
enum LanguageCode{
ko,
en
}
class AppLocalizations{
//์ ์ ๋ณ์๋ก ์ธ์ด๋ค์ ๊ฐ์ ธ์ state๋ก ์ ๊ณตํ ์ค๋น
static final KoreanLocalization _koreanLocalization = KoreanLocalization();
static final EnglishLocalization _englishLocalization = EnglishLocalization();
static Localization get(LanguageCode code){
if(code == LanguageCode.ko) {
return _koreanLocalization;
}else if(code== LanguageCode.en){
return _englishLocalization;
}else{
return _koreanLocalization;
}
}
}
//๊ฐ ์ธ์ด๋ณ ๊ณตํต์ผ๋ก ์ ์ธํด์ผํ ๋ณ์๋ค์ Localization์ด๋ผ๋ ์ถ์ํด๋์ค์ ์ ์ธ
abstract class Localization{
//์ถ์ํด๋์ค์ ๋ณ์๋ค์ ์ ์ธ๋ง ๊ฐ๋ฅํ๊ณ ํ ๋น๋๋ฉด ์๋จ.
String get start_page_hello;
String get start_page_change_to_kor;
String get start_page_change_to_eng;
}
//์ถ์ํด๋์ค์ ๊ตฌํ
class KoreanLocalization implements Localization{
//์ถ์ํด๋์ค์ ๋ณ์ ์ค๋ฒ๋ผ์ด๋(์ฌํ ๋น)
@override
String get start_page_hello => '์๋
ํ์ธ์';
@override
String get start_page_change_lang => '์ธ์ด๋ณ๊ฒฝ';
@override
String get start_page_change_to_kor => 'ํ๊ตญ์ด๋ก';
@override
String get start_page_change_to_eng => '์์ด๋ก';
}
class EnglishLocalization implements Localization{
@override
String get start_page_hello => 'Hello';
@override
String get start_page_change_lang => 'Change Language';
@override
String get start_page_change_to_kor => 'to Korean';
@override
String get start_page_change_to_eng => 'to English';
}
ํด๋น ์์ค์ฝ๋ ํ์ผ์์๋ Localization์์ ์ฌ์ฉํ ์ธ์ด์ฝ๋๋ค, ์ ์ ํจ์, ์ธ์ด๋ณ ์ ์ ๊ฐ์ฒด๋ค์ ์ ๊ณตํ๊ณ ์๋ค.
์ฃผ๋ก AppLocalizations ํด๋์ค์์ ์ธ์ด์ ๋ํ ์ ์ ๋ณ์๋ฅผ ์ ์ธ ๋ฐ ์ ๊ณต์ ํด์ฃผ๋๋ฐ ํด๋น ํด๋์ค์ get๋ฉ์๋๋ extension์์ ํธ์ถํด ์๋ง์ ์ธ์ด๋ฅผ ์ ์ฉํ ์ ์๊ฒ ๋์์ฃผ๋๋ก ํ๋ค.
<current_language_state.dart>
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../constants/app_localizations.dart';
final currentLanguageProvider =
StateNotifierProvider<CurrentLanguageNotifier, LanguageCode>((ref) {
return CurrentLanguageNotifier();
});
class CurrentLanguageNotifier extends StateNotifier<LanguageCode> {
CurrentLanguageNotifier() : super(LanguageCode.ko);
LanguageCode get currentLanguage => state;
void changeLanguage(LanguageCode targetLanguage) {
state = targetLanguage;
}
}
์ฌ๊ธฐ์๋ ํ์ฌ ์ธ์ด์ ๋ํ ์ํ๋ฅผ ์ ๊ณตํด์ฃผ๊ณ ์๋ค.
์์ ์๊ธฐํ๋ฏ, ๊ตณ์ด Riverpodํจํด์ ์ฌ์ฉํ๋ ๊ฒ ์๋๋ผ Bloc, Singleton ๋ฑ ๋ค์ํ ํจํด์ผ๋ก ๋์ฒดํ ์ ์๋ค. ๋์ UI ์ ๋ฐ์ดํธ์ ๊ดํ ๋ถ๋ถ์ ๊ฐ ํจํด์ ๋ง๊ฒ ์์ ์ ํด์ผ ํ๋ค. (์ฑ๊ธํคํจํด์ ์ฌ์ฉํ ๊ฒฝ์ฐ ์ด๋ฏธ ์ด๋ ค์๋ ํ์ด์ง๋ค์ ์ฑ UI๋ฅผ ์ง์ ์ ๋ฐ์ดํธํด์ค์ผ ํ๋ค)
์์ ์ฝ๋์์๋ LanguageCode๋ผ๋ enumํ์์ ๋ณ์๋ฅผ currentLanguage๋ก ์ ์ธํ์ผ๋ฉฐ changeLanguage๊ฐ ํธ์ถ๋์ด ์์ ๋ณ์์ธ state์ ๊ฐ์ด ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ ์๋์ผ๋ก NotifyListners()๊ฐ ํธ์ถ๋ ๊ฒ์ด๋ค.
<widget_ref_extension.dart>
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../constants/app_localizations.dart';
import '../states/current_language_state.dart';
//RiverPod์ ํต์ฌ ํด๋์ค์ธ WidgetRef ํด๋์ค๋ฅผ ํ์ฅ
extension WidgetRefExtension on WidgetRef{
Localization get localizations => AppLocalizations.get(watch(currentLanguageProvider));
}
์ํ, ์ ์ ๋ณ์๋ค ๋ฑ ๋ชจ๋ ๊ตฌ์ฑ์์๋ค์ ๊ฐ์ถ์๋ค๋ฉด ์ด์ ์ฌ์ฉ์ ํ๊ธฐ ์ํ ์ค๋น๋ฅผ ํด์ผ ํ๋ค.
riverpodํจํด์ผ๋ก ์ง์ UI ๋ด์์ ์ธ์ด ๋ณ๊ฒฝ์ ๋ํ ๋ฆฌ์ค๋๋ค์ ๋ฑ๋กํด ์ค๋ ๋์ง๋ง, ๋ชจ๋ Widget์ ๋ฆฌ์ค๋๋ฅผ ๋ฑ๋กํ๋ ๊ฑด ์ธ๋ฐ์์ด ๊ธ์๋ฅผ ๋ญ๋นํ๋ ์ ์ด๋ค.
์กฐ๊ธ์ด๋ผ๋ ๋ ์ ์ ์ฝ๋๋ก ๊ฐ์ ๊ธฐ๋ฅ์ ์ํํ ์ ์๋ค๋ฉด ์ต๋ํ ๊ทธ๋ ๊ฒ ํ๋ ๊ฒ ์ข๋ค.
๊ทธ๋ ๊ธฐ์ ๋๋ flutter ์ฑ์ ๊ฐ๋ฐํ ๋ extension์ ์์ฃผ ํ์ฉํ๋ ํธ์ด๊ณ , ์ด๋ฒ์๋ ์ญ์ WidgetRef ํด๋์ค ๋ํ Consumer Widget์ผ๋ก ์ ์ธ๋ ๋ชจ๋ ๊ณณ์์ ์ฌ์ฉ์ ํ๊ธฐ ๋๋ฌธ์ WidgetRef๋ง์ ์ํ extension์ ๋ง๋ค์ด ์คฌ๋ค.
์ฌ๊ธฐ๊น์ง๊ฐ Localization๋ก์ง๊ณผ ๊ด๋ จ๋ ์ฝ๋์ ๋์ด๋ค.
๋จ์ ๊ฑด UI ๊ตฌ์ฑ๊ณผ main.dart์ ์์ ์ด๋ค.
<home_screen.dart>
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_self_localization/constants/app_localizations.dart';
import 'package:flutter_self_localization/extensions/widget_ref_extension.dart';
import '../states/current_language_state.dart';
class HomeScreen extends ConsumerWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
title: const Text('MyLocalization'),
),
body: SizedBox(
width: double.infinity,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(ref.localizations.start_page_hello),
const SizedBox(height: 50,),
ElevatedButton(
onPressed: () => _onPressedToKorean(ref),
child: Text(ref.localizations.start_page_change_to_kor)),
const SizedBox(height: 10,),
ElevatedButton(
onPressed: () => _onPressedToEnglish(ref),
child: Text(ref.localizations.start_page_change_to_eng)),
],
),
),
);
}
void _onPressedToKorean(WidgetRef ref) {
final currentLanguageNotifier = ref.read(currentLanguageProvider.notifier);
currentLanguageNotifier.changeLanguage(LanguageCode.ko);
}
void _onPressedToEnglish(WidgetRef ref) {
final currentLanguageNotifier = ref.read(currentLanguageProvider.notifier);
currentLanguageNotifier.changeLanguage(LanguageCode.en);
}
}
UI๊ตฌ์ฑ์ ์ด๋ ๊ฒ ํ๊ณ ConsumerWidget์ ํตํด ์ธ์ด๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์๋์ผ๋ก UI๋ฅผ ์ ๋ฐ์ดํธํ๋๋ก ๊ตฌ์ฑํด ์คฌ๋ค.
<main.dart>
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_self_localization/ui/home_screen.dart';
void main() {
runApp(
//RiverPod์ ํ๋ก๋ฐ์ด๋ ์ฌ์ฉ์ ์ํด ๋ํ
const ProviderScope(child: MyApp())
);
}
class MyApp extends ConsumerWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context, WidgetRef ref) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: false,
),
home: const HomeScreen(),
);
}
}
main.dart์์๋ ๊ฐ๋จํ ProviderScope๋ง ๋ํ ํ๋๋ก ํ๋ค.
์ด ์ํ๋ก ์ฑ์ ์คํํด ๋ณด์.
์ฒ์ ์ ์ฉ๋ ์ธ์ด๊ฐ ํ๊ตญ์ด๋ก ๋์ด์์ด์ ์ง๊ธ์ ํ๊ธ๋ก ๋ ์๋ ์ํ๋ค.
์์ด๋ก ๋ฒํผ์ ํด๋ฆญํ์ ๋ UI๊ฐ ์ฆ์ ์ ๋ฐ์ดํธ ๋๋ ๋ชจ์ต์ ๋ณผ ์ ์๋ค.
๊ฒฐ๋ก
๋ง์ฝ ์ฑ์ ๊ท๋ชจ๊ฐ ์ปค์ง๊ฒ ๋ ๊ฒฝ์ฐ app_localization ์์์ ๋ชจ๋ ๊ฒ์ ์ ๊ณตํ๊ธฐ์ ์ฝ๋์ ์์ด ๋ง์์ง๊ฒ ๋ปํ๋ค.
์ด ๊ธ์ ์์ ๋ ์ต๋ํ ์ดํด๋ฅผ ๋๊ธฐ ์ํด ํ ํ์ผ ์์ ๋ชจ๋ ๊ฒ์ ๋ด์์ผ๋ฉฐ, ์ค์ ๋ก ์ฌ์ฉ์ ํ ๋๋ ์ญํ ๋ณ๋ก ์ฝ๋๋ค์ ์ ๋๋์ด์ผ ๋๋ค.
'๐ Flutter' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Flutter] Stack์ Widgets๋ฅผ ์ํ์ผ๋ก ๋ฐฐ์นํ๊ธฐ (Circle Layout) (0) | 2024.10.08 |
---|---|
[Flutter] MediaQuery.of(context) ์ฌ์ฉ์ ๋ฌธ์ ์ (0) | 2024.10.07 |
[Flutter] Factory๋ฅผ ํตํ Singleton ํจํด (0) | 2024.10.05 |
[Flutter Web] ๋ํ์ ์ธ ๋ ๋๋ฌ HTML๊ณผ CanvasKit ์ฐจ์ด์ ๋ํ ๊ณ ์ฐฐ (3) | 2024.10.04 |
[Flutter] ์์ดํจ๋์์ ํ๋ฌํฐ ์ฝ๋ฉํ๊ธฐ with Code-Server (0) | 2024.04.22 |