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: add localization support #12

Merged
merged 1 commit into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ jobs:
uses: cashapp/activate-hermit@v1

- name: Install Dependencies
run: flutter pub get
run: make get

- name: Run Flutter Analyze
run: flutter analyze
run: make analyze

- name: Run Flutter Test
run: flutter test
run: make test
42 changes: 42 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
.PHONY: all
all: run

.PHONY: run
run:
flutter run

.PHONY: get
get:
flutter pub get

.PHONY: clean
clean:
flutter clean

.PHONY: build
build:
flutter build apk

.PHONY: test
test:
flutter test

.PHONY: analyze
analyze:
flutter analyze

.PHONY: generate
generate:
flutter gen-l10n

.PHONY: help
help:
@echo "Available commands:"
@echo " make run - Run the Flutter app"
@echo " make get - Get packages"
@echo " make clean - Clean the project"
@echo " make build - Build the app for release"
@echo " make test - Run tests"
@echo " make analyze - Analyze the project's Dart code"
@echo " make generate - Generate code"
@echo " make watch - Watch for file changes and generate code automatically"
4 changes: 4 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
include: package:flutter_lints/flutter.yaml

analyzer:
exclude:
- "lib/l10n/**"

linter:
rules:
1 change: 1 addition & 0 deletions bin/.make-4.4.pkg
1 change: 1 addition & 0 deletions bin/make
6 changes: 6 additions & 0 deletions l10n.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-class: Loc
output-localization-file: app_localizations.dart
synthetic-package: false
nullable-getter: false
23 changes: 23 additions & 0 deletions lib/features/app/app.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import 'package:flutter_starter/features/home/home_page.dart';
import 'package:flutter_starter/l10n/app_localizations.dart';

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

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const HomePage(),
localizationsDelegates: Loc.localizationsDelegates,
supportedLocales: const [
Locale('en', ''),
],
);
}
}
46 changes: 46 additions & 0 deletions lib/features/home/home_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import 'package:flutter/material.dart';
import 'package:flutter_starter/l10n/app_localizations.dart';

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

@override
State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
int _counter = 0;

void _incrementCounter() {
setState(() {
_counter++;
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(Loc.of(context).appName),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(Loc.of(context).youHavePushedTheButton),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
4 changes: 4 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"appName": "Flutter Starter App",
"youHavePushedTheButton": "You have pushed the button this many times:"
}
136 changes: 136 additions & 0 deletions lib/l10n/app_localizations.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:intl/intl.dart' as intl;

import 'app_localizations_en.dart';

/// Callers can lookup localized strings with an instance of Loc
/// returned by `Loc.of(context)`.
///
/// Applications need to include `Loc.delegate()` in their app's
/// `localizationDelegates` list, and the locales they support in the app's
/// `supportedLocales` list. For example:
///
/// ```dart
/// import 'l10n/app_localizations.dart';
///
/// return MaterialApp(
/// localizationsDelegates: Loc.localizationsDelegates,
/// supportedLocales: Loc.supportedLocales,
/// home: MyApplicationHome(),
/// );
/// ```
///
/// ## Update pubspec.yaml
///
/// Please make sure to update your pubspec.yaml to include the following
/// packages:
///
/// ```yaml
/// dependencies:
/// # Internationalization support.
/// flutter_localizations:
/// sdk: flutter
/// intl: any # Use the pinned version from flutter_localizations
///
/// # Rest of dependencies
/// ```
///
/// ## iOS Applications
///
/// iOS applications define key application metadata, including supported
/// locales, in an Info.plist file that is built into the application bundle.
/// To configure the locales supported by your app, you’ll need to edit this
/// file.
///
/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file.
/// Then, in the Project Navigator, open the Info.plist file under the Runner
/// project’s Runner folder.
///
/// Next, select the Information Property List item, select Add Item from the
/// Editor menu, then select Localizations from the pop-up menu.
///
/// Select and expand the newly-created Localizations item then, for each
/// locale your application supports, add a new item and select the locale
/// you wish to add from the pop-up menu in the Value field. This list should
/// be consistent with the languages listed in the Loc.supportedLocales
/// property.
abstract class Loc {
Loc(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString());

final String localeName;

static Loc of(BuildContext context) {
return Localizations.of<Loc>(context, Loc)!;
}

static const LocalizationsDelegate<Loc> delegate = _LocDelegate();

/// A list of this localizations delegate along with the default localizations
/// delegates.
///
/// Returns a list of localizations delegates containing this delegate along with
/// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
/// and GlobalWidgetsLocalizations.delegate.
///
/// Additional delegates can be added by appending to this list in
/// MaterialApp. This list does not have to be used at all if a custom list
/// of delegates is preferred or required.
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates = <LocalizationsDelegate<dynamic>>[
delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
];

/// A list of this localizations delegate's supported locales.
static const List<Locale> supportedLocales = <Locale>[
Locale('en')
];

/// No description provided for @appName.
///
/// In en, this message translates to:
/// **'Flutter Starter App'**
String get appName;

/// No description provided for @youHavePushedTheButton.
///
/// In en, this message translates to:
/// **'You have pushed the button this many times:'**
String get youHavePushedTheButton;
}

class _LocDelegate extends LocalizationsDelegate<Loc> {
const _LocDelegate();

@override
Future<Loc> load(Locale locale) {
return SynchronousFuture<Loc>(lookupLoc(locale));
}

@override
bool isSupported(Locale locale) => <String>['en'].contains(locale.languageCode);

@override
bool shouldReload(_LocDelegate old) => false;
}

Loc lookupLoc(Locale locale) {


// Lookup logic when only language code is specified.
switch (locale.languageCode) {
case 'en': return LocEn();
}

throw FlutterError(
'Loc.delegate failed to load unsupported locale "$locale". This is likely '
'an issue with the localizations generation tool. Please file an issue '
'on GitHub with a reproducible sample app and the gen-l10n configuration '
'that was used.'
);
}
12 changes: 12 additions & 0 deletions lib/l10n/app_localizations_en.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'app_localizations.dart';

/// The translations for English (`en`).
class LocEn extends Loc {
LocEn([String locale = 'en']) : super(locale);

@override
String get appName => 'Flutter Starter App';

@override
String get youHavePushedTheButton => 'You have pushed the button this many times:';
}
66 changes: 2 additions & 64 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,68 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_starter/features/app/app.dart';

void main() {
runApp(const MyApp());
}

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

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;

@override
State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;

void _incrementCounter() {
setState(() {
_counter++;
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
runApp(const App());
}
Loading