Localization
DWallet features multi-language support built using slang - a type-safe i18n solution for Flutter.
Supported Languages
Section titled “Supported Languages”The app currently supports multiple languages with the infrastructure to easily add more.
How Localization Works
Section titled “How Localization Works”DWallet uses the slang package for type-safe translations:
- Define translations in JSON/YAML files
- Run code generation
- Use generated classes for type-safe access
Directory Structure
Section titled “Directory Structure”lib/├── i18n/ # Translation files│ ├── strings.i18n.json # Base language (English)│ ├── strings_es.i18n.json # Spanish│ └── strings_fr.i18n.json # French└── main.dartAdding a New Language
Section titled “Adding a New Language”1. Create Translation File
Section titled “1. Create Translation File”Create a new file in lib/i18n/:
{ "appName": "DWallet", "welcome": "ようこそ", "settings": { "title": "設定", "appearance": "外観", "language": "言語" }, "wallet": { "title": "ウォレット", "balance": "残高", "addNew": "新規追加" }}2. Run Code Generation
Section titled “2. Run Code Generation”flutter pub run slangThis generates type-safe translation classes.
3. Register the Locale
Section titled “3. Register the Locale”In main.dart, ensure the locale is supported:
ShadApp.router( localizationsDelegates: const [ ...AppLocaleUtils.localizationsDelegates, ], supportedLocales: AppLocaleUtils.supportedLocales, // ...)Using Translations
Section titled “Using Translations”In Widgets
Section titled “In Widgets”import '../../i18n/strings.g.dart';
class HomeView extends StatelessWidget { @override Widget build(BuildContext context) { final t = Translations.of(context);
return Scaffold( appBar: AppBar( title: Text(t.wallet.title), ), body: Text(t.welcome), ); }}With Riverpod
Section titled “With Riverpod”final localeProvider = StateProvider<AppLocale>((ref) { return AppLocale.en; // Default});
// Change localeref.read(localeProvider.notifier).state = AppLocale.ja;Translation Keys Structure
Section titled “Translation Keys Structure”Organize translations hierarchically:
{ "common": { "save": "Save", "cancel": "Cancel", "delete": "Delete", "confirm": "Are you sure?" }, "home": { "title": "Home", "totalBalance": "Total Balance", "income": "Income", "expense": "Expense" }, "transaction": { "add": "Add Transaction", "income": "Income", "expense": "Expense", "category": "Category", "amount": "Amount", "note": "Note" }, "wallet": { "title": "Wallets", "add": "Add Wallet", "types": { "cash": "Cash", "bank": "Bank", "mobileMoney": "Mobile Money", "other": "Other" } }, "settings": { "title": "Settings", "appearance": "Appearance", "security": "Security", "language": "Language" }}Pluralization
Section titled “Pluralization”Slang supports pluralization:
{ "transactionCount": { "zero": "No transactions", "one": "One transaction", "other": "$count transactions" }}t.transactionCount(count: 5); // "5 transactions"Date/Number Formatting
Section titled “Date/Number Formatting”Use intl package alongside slang:
import 'package:intl/intl.dart';
final dateFormat = DateFormat.yMMMd().format(transaction.date);final currencyFormat = NumberFormat.currency( symbol: currency.symbol,).format(amount);Best Practices
Section titled “Best Practices”- Use semantic keys:
saveButtonnotbutton1 - Group by feature:
home.title,wallet.add - Keep base language complete: Always translate all keys
- Use placeholders:
"Hello {name}"not string concatenation - Context-aware: Provide context for translators in comments
Changing Default Locale
Section titled “Changing Default Locale”In main.dart:
void main() { WidgetsFlutterBinding.ensureInitialized();
// Set default locale LocaleSettings.useDeviceLocale();
runApp(ProviderScope(child: DWalletApp()));}