feat: Add Japanese localization support
Summary
This PR adds Japanese localization support to the Quitter app, enabling Japanese users to use the app in their native language.
Changes Made
- Added
flutter_localizationsdependency to support multiple languages - Created l10n configuration (
l10n.yaml) - Added 247 localized strings in ARB format:
lib/l10n/app_en.arb(English)lib/l10n/app_ja.arb(Japanese)
- Updated
MaterialAppwith localization delegates and supported locales - Replaced all hardcoded UI strings with
AppLocalizationscalls across 20 files - Updated l10n output directory to
lib/l10n/generated/for Flutter 3.38+ compatibility (synthetic-package was deprecated) - The app automatically displays in Japanese when the device language is set to Japanese
Files Changed
- Configuration:
pubspec.yaml,l10n.yaml - Localization files:
lib/l10n/app_en.arb,lib/l10n/app_ja.arb - Generated files:
lib/l10n/generated/ - Main app:
lib/main.dart - UI pages (20 files): All user-facing pages updated to use localized strings
Screenshots
Home Screen (Japanese)
Addiction Detail Page (Japanese)
Journal Page (Japanese)
Settings Page (Japanese)
Testing
- ✅
flutter analyzepasses with no issues - ✅ App builds successfully (Flutter 3.38.3)
- ✅ All UI strings display correctly in Japanese
- ✅ English localization works as before
- ✅ Tested on Android device
Notes
- The app automatically detects the device language and displays the appropriate localization
- All existing functionality remains unchanged
- Future language support can be easily added by creating new ARB files
- Milestone descriptions remain in English (localization would require architectural changes)
Great work King thank you for your contribution. How would we need to change the milestones to allow them to be localized as well?
Thanks for the kind words!
I investigated the milestone localization, and it turned out to be simpler than I initially expected. In my original PR description, I mentioned "architectural changes would be required" - but after actually implementing it, the existing l10n system handles it well.
What I did:
Changed the milestone list from a const field to a method that takes AppLocalizations:
// Before: const list with hardcoded strings
final List<QuitMilestone> miles = const [
QuitMilestone(day: 1, title: "Sleep Quality Begins to Improve", ...),
];
// After: dynamic list using l10n
List<QuitMilestone> _getMilestones(AppLocalizations l10n) {
return [
QuitMilestone(day: 1, title: l10n.alcoholMilestone1Title, ...),
];
}
I've implemented this for the Alcohol page as a proof of concept and verified it works on a real device.
The reference and link fields remain in English since they're source citations (URLs and publication names).
I can extend this to all addiction types if you'd like. Let me know how you'd like to proceed!
Thanks for the kind words!
I investigated the milestone localization, and it turned out to be simpler than I initially expected. In my original PR description, I mentioned "architectural changes would be required" - but after actually implementing it, the existing l10n system handles it well.
What I did: Changed the milestone list from a
constfield to a method that takesAppLocalizations:// Before: const list with hardcoded strings final List<QuitMilestone> miles = const [ QuitMilestone(day: 1, title: "Sleep Quality Begins to Improve", ...), ]; // After: dynamic list using l10n List<QuitMilestone> _getMilestones(AppLocalizations l10n) { return [ QuitMilestone(day: 1, title: l10n.alcoholMilestone1Title, ...), ]; }I've implemented this for the Alcohol page as a proof of concept and verified it works on a real device.
The
referenceandlinkfields remain in English since they're source citations (URLs and publication names).I can extend this to all addiction types if you'd like. Let me know how you'd like to proceed!
Yes please. Thanks for the hard work i'll review and accept this soon
I'm still testing on a real device and will push the milestone localization for all addiction types tonight (JST).
By the way, I found a critical bug while testing (this exists in the current production version too):
PIN Lock Issue When setting a PIN using a Japanese keyboard, users can input full-width numbers (e.g., "1234"). However, the PIN entry screen only allows half-width numbers ("1234") via the numeric keypad. This causes a permanent lockout since the PINs never match.
I'll investigate the root cause tonight and share my findings. However, since this involves user data security, I'd prefer to leave the actual fix to you.
Done! Milestone localization for all 9 addiction types:
- Alcohol, Smoking, Vaping, Marijuana, Opioid, Social Media, Nicotine Pouches, Pornography, and Custom
- 81 milestones total (9 per type) × 2 languages = 162 entries
- References/links kept in English (source citations)
All tested on real device.
Regarding the PIN bug - I'll share my investigation findings separately.
Note: Test setup requires update
After adding localization to the milestone pages, the existing tests will fail because they don't set up the localization delegates.
Cause: Tests create MaterialApp without localizationsDelegates, so AppLocalizations.of(context) returns null.
Fix: Add localization setup to test widgets:
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:quitter/l10n/generated/app_localizations.dart';
Widget createTestWidget() {
return MaterialApp(
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: AppLocalizations.supportedLocales,
home: SettingsPage(),
);
}
This affects:
test/settings_page_test.darttest/home_page_test.darttest/journal_page_test.dart
I can submit a fix for this if you'd like, or leave it to you.
Thank you i'll work through fixing the unit tests right now. The PIN thing doesn't sound like a big issue security wise, i'll probably reject non-ascii characters 😎
Going to just add 1 thing - manually picking language in the settings. Will be useful for testing, not sure anyone who picks japanese / other languages in android settings will want each app to specify the language, however might as well
Thanks for merging!