Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Multiple Language support added #1071

Open
wants to merge 2 commits into
base: flutter_app
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions assets/lang/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"title":"BadgeMusic",
"speed":"Speed",
"animation":"Animation",
"effects":"Effects",
"save":"Save",
"reset":"Reset",
"transfer":"Transfer",
"anim_left":"Left",
"anim_right":"Right",
"anim_up":"Up",
"anim_down":"Down",
"anim_fixed":"Fixed",
"anim_snowflake":"Snowflake",
"anim_picture":"Picture",
"anim_anim":"Animation",
"anim_laser":"Laser",
"effect_invert":"Invert",
"effect_effect":"Effect",
"effect_marquee":"Marquee",
"create_badge":"Create Badge",
"draw_badge":"Draw Badge",
"saved_badges":"Saved Badges",
"saved_cliparts":"Saved Cliparts",
"settings":"Settings",
"about":"About Us",
"buy_bages":"Buy Badges",
"share":"Share",
"rate":"Rate Us",
"feedback":"Feedback/Bug Reports",
"privacy_policy":"Privacy Policy",
"draw":"Draw",
"erase":"Erase",
"no_cliparts":"No saved clipart!",
"no_clip_desc":"Looks like there are no saved cliparts yet.",
"language":"Language",
"select_badge":"Select Badge"
}
38 changes: 38 additions & 0 deletions assets/lang/zh.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"title": "徽章音乐",
"speed": "速度",
"animation": "动画",
"effects": "效果",
"save": "保存",
"reset": "重置",
"transfer": "传输",
"anim_left": "左",
"anim_right": "右",
"anim_up": "上",
"anim_down": "下",
"anim_fixed": "固定",
"anim_snowflake": "雪花",
"anim_picture": "图片",
"anim_anim": "动画",
"anim_laser": "激光",
"effect_invert": "反转",
"effect_effect": "效果",
"effect_marquee": "跑马灯",
"create_badge": "创建徽章",
"draw_badge": "绘制徽章",
"saved_badges": "已保存的徽章",
"saved_cliparts": "已保存的剪贴画",
"settings": "设置",
"about": "关于我们",
"buy_bages": "购买徽章",
"share": "分享",
"rate": "评价我们",
"feedback": "反馈/错误报告",
"privacy_policy": "隐私政策",
"draw": "绘制",
"erase": "擦除",
"no_cliparts": "没有保存的剪贴画!",
"no_clip_desc": "看起来还没有保存的剪贴画。",
"language": "语言",
"select_badge": "选择徽章"
}
19 changes: 18 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import 'package:badgemagic/providers/app_localisation.dart';
import 'package:badgemagic/providers/getitlocator.dart';
import 'package:badgemagic/providers/imageprovider.dart';
import 'package:badgemagic/providers/locale_provider.dart';
import 'package:badgemagic/view/about_us_screen.dart';
import 'package:badgemagic/view/draw_badge_screen.dart';
import 'package:badgemagic/view/homescreen.dart';
import 'package:badgemagic/view/save_badge_screen.dart';
import 'package:badgemagic/view/saved_clipart.dart';
import 'package:badgemagic/view/settings_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:provider/provider.dart';

Expand All @@ -17,20 +20,34 @@ void main() {
providers: [
ChangeNotifierProvider<InlineImageProvider>(
create: (context) => getIt<InlineImageProvider>()),
ChangeNotifierProvider(create: (_) => LocaleProvider()),
],
child: const MyApp(),
));
}

class MyApp extends StatelessWidget {
class MyApp extends StatefulWidget {
const MyApp({super.key});

@override
State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: const Size(360, 690),
builder: (context, child) {
return MaterialApp(
supportedLocales: const [Locale('en'), Locale('zh')],
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
locale: Provider.of<LocaleProvider>(context).locale,
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorSchemeSeed: Colors.white,
Expand Down
87 changes: 87 additions & 0 deletions lib/providers/app_localisation.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class AppLocalizations {
final Locale? locale;

AppLocalizations({
this.locale,
});

// A static method to obtain an instance of AppLocalizations in the context
static AppLocalizations? of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations);
}

// A LocalizationsDelegate for this localization class
static const LocalizationsDelegate<AppLocalizations> delegate =
_AppLocalizationsDelegate();

// A map to store the localized strings
late Map<String, String> _localizedStrings;

// Method to load localized strings from JSON files
Future loadJsonLanguage() async {
String jsonString =
await rootBundle.loadString("assets/lang/${locale!.languageCode}.json");

// Parse the JSON string into a map
Map<String, dynamic> jsonMap = json.decode(jsonString);

// Convert the map values to strings and store them in the _localizedStrings map
_localizedStrings = jsonMap.map((key, value) {
return MapEntry(key, value.toString());
});
}

// Method to translate a key into a localized string
String translate(String key) => _localizedStrings[key] ?? "";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Consider adding debug logging for missing translation keys

In debug mode, log a warning when a translation key is missing to help catch issues during development.

Suggested change
String translate(String key) => _localizedStrings[key] ?? "";
String translate(String key) {
final value = _localizedStrings[key];
if (value == null) {
assert(() {
print('Missing translation key: $key');
return true;
}());
}
return value ?? '';
}


// Method to translate a key with dynamic arguments
String translateWithArgs(String key, Map<String, dynamic> args) {
String translation = _localizedStrings[key] ?? "";

// Replace placeholders in the translation string with actual values from args
args.forEach((key, value) {
translation = translation.replaceAll("{$key}", value.toString());
});

return translation;
}
}

class _AppLocalizationsDelegate
extends LocalizationsDelegate<AppLocalizations> {
const _AppLocalizationsDelegate();

@override
bool isSupported(Locale locale) {
// Define the supported locales (e.g., 'en' for English, 'ar' for Arabic)
return ['en', 'zh'].contains(locale.languageCode);
}

@override
Future<AppLocalizations> load(Locale locale) async {
// Create an instance of AppLocalizations and load the appropriate JSON file
AppLocalizations localizations = AppLocalizations(locale: locale);
await localizations.loadJsonLanguage();
return localizations;
}

@override
bool shouldReload(covariant LocalizationsDelegate<AppLocalizations> old) =>
false;
}

extension TranslateX on String {
String tr(BuildContext context) {
return AppLocalizations.of(context)!.translate(this);
}
}

extension TranslateWithArg on String {
String trWithArg(BuildContext context, Map<String, dynamic> args) {
return AppLocalizations.of(context)!.translateWithArgs(this, args);
}
}
15 changes: 15 additions & 0 deletions lib/providers/locale_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// lib/providers/locale_provider.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:intl/intl.dart';

class LocaleProvider with ChangeNotifier {
Locale _locale = Locale('en', 'US');

Locale get locale => _locale;

void changeLocale(Locale newLocale) {
_locale = newLocale;
notifyListeners();
}
}
15 changes: 8 additions & 7 deletions lib/view/homescreen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:badgemagic/badge_effect/invert_led_effect.dart';
import 'package:badgemagic/badge_effect/marquee_effect.dart';
import 'package:badgemagic/constants.dart';
import 'package:badgemagic/providers/animation_badge_provider.dart';
import 'package:badgemagic/providers/app_localisation.dart';
import 'package:badgemagic/providers/badge_message_provider.dart';
import 'package:badgemagic/providers/imageprovider.dart';
import 'package:badgemagic/providers/speed_dial_provider.dart';
Expand Down Expand Up @@ -123,7 +124,7 @@ class _HomeScreenState extends State<HomeScreen>
child: DefaultTabController(
length: 3,
child: CommonScaffold(
title: 'BadgeMagic',
title: "title".tr(context),
body: SafeArea(
child: Stack(
children: [
Expand Down Expand Up @@ -192,10 +193,10 @@ class _HomeScreenState extends State<HomeScreen>
return null;
},
),
tabs: const [
Tab(text: 'Speed'),
Tab(text: 'Animation'),
Tab(text: 'Effects'),
tabs: [
Tab(text: "speed".tr(context)),
Tab(text: 'animation'.tr(context)),
Tab(text: 'effects'.tr(context)),
],
),
SizedBox(
Expand Down Expand Up @@ -247,7 +248,7 @@ class _HomeScreenState extends State<HomeScreen>
BorderRadius.circular(2.r),
color: Colors.grey.shade400,
),
child: const Text('Save'),
child: Text('save'.tr(context)),
),
),
],
Expand Down Expand Up @@ -285,7 +286,7 @@ class _HomeScreenState extends State<HomeScreen>
BorderRadius.circular(2.r),
color: Colors.grey.shade400,
),
child: const Text('Transfer'),
child: Text('transfer'.tr(context)),
),
),
],
Expand Down
19 changes: 15 additions & 4 deletions lib/view/settings_screen.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import 'package:badgemagic/main.dart';
import 'package:badgemagic/providers/app_localisation.dart';
import 'package:badgemagic/providers/locale_provider.dart';
import 'package:badgemagic/view/widgets/common_scaffold_widget.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class SettingsScreen extends StatefulWidget {
const SettingsScreen({super.key});
Expand All @@ -13,6 +17,10 @@ class SettingsScreenState extends State<SettingsScreen> {
String selectedBadge = 'LSLED';

final List<String> languages = ['ENGLISH', 'CHINESE'];
Map<String, String> languageMap = {
'ENGLISH': 'en',
'CHINESE': 'zh',
};
final List<String> badges = ['LSLED', 'VBLAB'];

@override
Expand All @@ -23,8 +31,8 @@ class SettingsScreenState extends State<SettingsScreen> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Language',
Text(
'language'.tr(context),
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Expand All @@ -42,6 +50,9 @@ class SettingsScreenState extends State<SettingsScreen> {
onChanged: (String? newValue) {
setState(() {
selectedLanguage = newValue!;
// print(languageMap[selectedLanguage]);
Provider.of<LocaleProvider>(context, listen: false)
.changeLocale(Locale(languageMap[newValue!]!));
});
},
items:
Expand All @@ -56,8 +67,8 @@ class SettingsScreenState extends State<SettingsScreen> {
),
),
const SizedBox(height: 20),
const Text(
'Select Badge',
Text(
'select_badge'.tr(context),
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Expand Down
Loading
Loading