diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..dd8459ee --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.g.dart linguist-generated \ No newline at end of file diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..f69101ad --- /dev/null +++ b/codecov.yml @@ -0,0 +1,2 @@ +ignore: + - '**.g.dart' \ No newline at end of file diff --git a/packages/fleather/.gitignore b/packages/fleather/.gitignore index 11bbb513..b00ae1f3 100644 --- a/packages/fleather/.gitignore +++ b/packages/fleather/.gitignore @@ -18,3 +18,5 @@ doc/api/ build/ example/feather + +untranslated.txt \ No newline at end of file diff --git a/packages/fleather/analysis_options.yaml b/packages/fleather/analysis_options.yaml index 4c5af234..1e6f852f 100644 --- a/packages/fleather/analysis_options.yaml +++ b/packages/fleather/analysis_options.yaml @@ -1,5 +1,9 @@ include: package:flutter_lints/flutter.yaml +analyzer: + exclude: + - '**.g.dart' + linter: rules: always_declare_return_types: true diff --git a/packages/fleather/example/macos/Podfile.lock b/packages/fleather/example/macos/Podfile.lock index 96677979..99f1ba3b 100644 --- a/packages/fleather/example/macos/Podfile.lock +++ b/packages/fleather/example/macos/Podfile.lock @@ -19,9 +19,9 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos SPEC CHECKSUMS: - file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9 + file_selector_macos: 54fdab7caa3ac3fc43c9fac4d7d8d231277f8cf2 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95 + url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399 PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 diff --git a/packages/fleather/l10n.yaml b/packages/fleather/l10n.yaml new file mode 100644 index 00000000..65ca2532 --- /dev/null +++ b/packages/fleather/l10n.yaml @@ -0,0 +1,12 @@ +format: true +use-escaping: true +synthetic-package: false + +arb-dir: lib/l10n/translations +template-arb-file: en.arb + +output-dir: lib/l10n +output-class: FleatherLocalizations +output-localization-file: fleather_localizations.g.dart + +untranslated-messages-file: untranslated.txt \ No newline at end of file diff --git a/packages/fleather/lib/fleather.dart b/packages/fleather/lib/fleather.dart index 5236ad6a..6477aa41 100644 --- a/packages/fleather/lib/fleather.dart +++ b/packages/fleather/lib/fleather.dart @@ -5,7 +5,10 @@ library fleather; export 'package:parchment/parchment.dart'; +export 'l10n/l10n.dart' hide BuildContextLocalizationsExtension; export 'src/rendering/editor.dart'; +export 'src/services/clipboard_manager.dart'; +export 'src/widgets/autoformats.dart'; export 'src/widgets/controller.dart'; export 'src/widgets/cursor.dart'; export 'src/widgets/editor.dart'; @@ -14,5 +17,3 @@ export 'src/widgets/field.dart'; export 'src/widgets/link.dart' show LinkActionPickerDelegate, LinkMenuAction; export 'src/widgets/text_line.dart'; export 'src/widgets/theme.dart'; -export 'src/widgets/autoformats.dart'; -export 'src/services/clipboard_manager.dart'; diff --git a/packages/fleather/lib/l10n/fleather_localizations.g.dart b/packages/fleather/lib/l10n/fleather_localizations.g.dart new file mode 100644 index 00000000..3cfbd9fb --- /dev/null +++ b/packages/fleather/lib/l10n/fleather_localizations.g.dart @@ -0,0 +1,217 @@ +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 'fleather_localizations_en.g.dart'; +import 'fleather_localizations_fr.g.dart'; + +/// Callers can lookup localized strings with an instance of FleatherLocalizations +/// returned by `FleatherLocalizations.of(context)`. +/// +/// Applications need to include `FleatherLocalizations.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'l10n/fleather_localizations.g.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: FleatherLocalizations.localizationsDelegates, +/// supportedLocales: FleatherLocalizations.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 FleatherLocalizations.supportedLocales +/// property. +abstract class FleatherLocalizations { + FleatherLocalizations(String locale) + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static FleatherLocalizations? of(BuildContext context) { + return Localizations.of( + context, FleatherLocalizations); + } + + static const LocalizationsDelegate delegate = + _FleatherLocalizationsDelegate(); + + /// 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> localizationsDelegates = + >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; + + /// A list of this localizations delegate's supported locales. + static const List supportedLocales = [ + Locale('en'), + Locale('fr') + ]; + + /// Automatically assign a foreground color to the text + /// + /// In en, this message translates to: + /// **'Automatic'** + String get foregroundColorAutomatic; + + /// Assign no background color to the text + /// + /// In en, this message translates to: + /// **'No color'** + String get backgroundColorNoColor; + + /// A normal heading text style + /// + /// In en, this message translates to: + /// **'Normal'** + String get headingNormal; + + /// A level 1 heading text style + /// + /// In en, this message translates to: + /// **'Heading 1'** + String get headingLevel1; + + /// A level 2 heading text style + /// + /// In en, this message translates to: + /// **'Heading 2'** + String get headingLevel2; + + /// A level 3 heading text style + /// + /// In en, this message translates to: + /// **'Heading 3'** + String get headingLevel3; + + /// A level 4 heading text style + /// + /// In en, this message translates to: + /// **'Heading 4'** + String get headingLevel4; + + /// A level 5 heading text style + /// + /// In en, this message translates to: + /// **'Heading 5'** + String get headingLevel5; + + /// A level 6 heading text style + /// + /// In en, this message translates to: + /// **'Heading 6'** + String get headingLevel6; + + /// Label for the input decoration of the link text field in the add link dialog + /// + /// In en, this message translates to: + /// **'Paste a link'** + String get addLinkDialogPasteLink; + + /// Label for the confirmation button in the link dialog + /// + /// In en, this message translates to: + /// **'Apply'** + String get addLinkDialogApply; + + /// Open the link + /// + /// In en, this message translates to: + /// **'Open'** + String get linkDialogOpen; + + /// Copy the link + /// + /// In en, this message translates to: + /// **'Copy'** + String get linkDialogCopy; + + /// Remove the link + /// + /// In en, this message translates to: + /// **'Remove'** + String get linkDialogRemove; +} + +class _FleatherLocalizationsDelegate + extends LocalizationsDelegate { + const _FleatherLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture( + lookupFleatherLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => + ['en', 'fr'].contains(locale.languageCode); + + @override + bool shouldReload(_FleatherLocalizationsDelegate old) => false; +} + +FleatherLocalizations lookupFleatherLocalizations(Locale locale) { + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'en': + return FleatherLocalizationsEn(); + case 'fr': + return FleatherLocalizationsFr(); + } + + throw FlutterError( + 'FleatherLocalizations.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.'); +} diff --git a/packages/fleather/lib/l10n/fleather_localizations_en.g.dart b/packages/fleather/lib/l10n/fleather_localizations_en.g.dart new file mode 100644 index 00000000..28938095 --- /dev/null +++ b/packages/fleather/lib/l10n/fleather_localizations_en.g.dart @@ -0,0 +1,48 @@ +import 'fleather_localizations.g.dart'; + +/// The translations for English (`en`). +class FleatherLocalizationsEn extends FleatherLocalizations { + FleatherLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get foregroundColorAutomatic => 'Automatic'; + + @override + String get backgroundColorNoColor => 'No color'; + + @override + String get headingNormal => 'Normal'; + + @override + String get headingLevel1 => 'Heading 1'; + + @override + String get headingLevel2 => 'Heading 2'; + + @override + String get headingLevel3 => 'Heading 3'; + + @override + String get headingLevel4 => 'Heading 4'; + + @override + String get headingLevel5 => 'Heading 5'; + + @override + String get headingLevel6 => 'Heading 6'; + + @override + String get addLinkDialogPasteLink => 'Paste a link'; + + @override + String get addLinkDialogApply => 'Apply'; + + @override + String get linkDialogOpen => 'Open'; + + @override + String get linkDialogCopy => 'Copy'; + + @override + String get linkDialogRemove => 'Remove'; +} diff --git a/packages/fleather/lib/l10n/fleather_localizations_fr.g.dart b/packages/fleather/lib/l10n/fleather_localizations_fr.g.dart new file mode 100644 index 00000000..6d21f012 --- /dev/null +++ b/packages/fleather/lib/l10n/fleather_localizations_fr.g.dart @@ -0,0 +1,48 @@ +import 'fleather_localizations.g.dart'; + +/// The translations for French (`fr`). +class FleatherLocalizationsFr extends FleatherLocalizations { + FleatherLocalizationsFr([String locale = 'fr']) : super(locale); + + @override + String get foregroundColorAutomatic => 'Automatique'; + + @override + String get backgroundColorNoColor => 'Aucune couleur'; + + @override + String get headingNormal => 'Normal'; + + @override + String get headingLevel1 => 'Titre 1'; + + @override + String get headingLevel2 => 'Titre 2'; + + @override + String get headingLevel3 => 'Titre 3'; + + @override + String get headingLevel4 => 'Titre 4'; + + @override + String get headingLevel5 => 'Titre 5'; + + @override + String get headingLevel6 => 'Titre 6'; + + @override + String get addLinkDialogPasteLink => 'Coller un lien'; + + @override + String get addLinkDialogApply => 'Appliquer'; + + @override + String get linkDialogOpen => 'Ouvrir'; + + @override + String get linkDialogCopy => 'Copier'; + + @override + String get linkDialogRemove => 'Retirer'; +} diff --git a/packages/fleather/lib/l10n/l10n.dart b/packages/fleather/lib/l10n/l10n.dart new file mode 100644 index 00000000..edbf6b26 --- /dev/null +++ b/packages/fleather/lib/l10n/l10n.dart @@ -0,0 +1,4 @@ +library l10n; + +export 'fleather_localizations.g.dart'; +export 'utils.dart'; diff --git a/packages/fleather/lib/l10n/translations/en.arb b/packages/fleather/lib/l10n/translations/en.arb new file mode 100644 index 00000000..5f9af47d --- /dev/null +++ b/packages/fleather/lib/l10n/translations/en.arb @@ -0,0 +1,59 @@ +{ + "@@locale": "en", + "foregroundColorAutomatic": "Automatic", + "@foregroundColorAutomatic": { + "description": "Automatically assign a foreground color to the text" + }, + "backgroundColorNoColor": "No color", + "@backgroundColorNoColor": { + "description": "Assign no background color to the text" + }, + "headingNormal": "Normal", + "@headingNormal": { + "description": "A normal heading text style" + }, + "headingLevel1": "Heading 1", + "@headingLevel1": { + "description": "A level 1 heading text style" + }, + "headingLevel2": "Heading 2", + "@headingLevel2": { + "description": "A level 2 heading text style" + }, + "headingLevel3": "Heading 3", + "@headingLevel3": { + "description": "A level 3 heading text style" + }, + "headingLevel4": "Heading 4", + "@headingLevel4": { + "description": "A level 4 heading text style" + }, + "headingLevel5": "Heading 5", + "@headingLevel5": { + "description": "A level 5 heading text style" + }, + "headingLevel6": "Heading 6", + "@headingLevel6": { + "description": "A level 6 heading text style" + }, + "addLinkDialogPasteLink": "Paste a link", + "@addLinkDialogPasteLink": { + "description": "Label for the input decoration of the link text field in the add link dialog" + }, + "addLinkDialogApply": "Apply", + "@addLinkDialogApply": { + "description": "Label for the confirmation button in the link dialog" + }, + "linkDialogOpen": "Open", + "@linkDialogOpen": { + "description": "Open the link" + }, + "linkDialogCopy": "Copy", + "@linkDialogCopy": { + "description": "Copy the link" + }, + "linkDialogRemove": "Remove", + "@linkDialogRemove": { + "description": "Remove the link" + } +} \ No newline at end of file diff --git a/packages/fleather/lib/l10n/translations/fr.arb b/packages/fleather/lib/l10n/translations/fr.arb new file mode 100644 index 00000000..c2a8222b --- /dev/null +++ b/packages/fleather/lib/l10n/translations/fr.arb @@ -0,0 +1,17 @@ +{ + "@@locale": "fr", + "foregroundColorAutomatic": "Automatique", + "backgroundColorNoColor": "Aucune couleur", + "headingNormal": "Normal", + "headingLevel1": "Titre 1", + "headingLevel2": "Titre 2", + "headingLevel3": "Titre 3", + "headingLevel4": "Titre 4", + "headingLevel5": "Titre 5", + "headingLevel6": "Titre 6", + "addLinkDialogPasteLink": "Coller un lien", + "addLinkDialogApply": "Appliquer", + "linkDialogOpen": "Ouvrir", + "linkDialogCopy": "Copier", + "linkDialogRemove": "Retirer" +} \ No newline at end of file diff --git a/packages/fleather/lib/l10n/utils.dart b/packages/fleather/lib/l10n/utils.dart new file mode 100644 index 00000000..8b23e126 --- /dev/null +++ b/packages/fleather/lib/l10n/utils.dart @@ -0,0 +1,9 @@ +import 'package:fleather/l10n/fleather_localizations_en.g.dart'; +import 'package:flutter/widgets.dart'; + +import 'fleather_localizations.g.dart'; + +extension BuildContextLocalizationsExtension on BuildContext { + FleatherLocalizations get l => + FleatherLocalizations.of(this) ?? FleatherLocalizationsEn(); +} diff --git a/packages/fleather/lib/src/widgets/editor_toolbar.dart b/packages/fleather/lib/src/widgets/editor_toolbar.dart index 27eb3a1d..e9852a5b 100644 --- a/packages/fleather/lib/src/widgets/editor_toolbar.dart +++ b/packages/fleather/lib/src/widgets/editor_toolbar.dart @@ -1,12 +1,12 @@ import 'dart:async'; - -import 'package:fleather/src/widgets/editor.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:parchment/parchment.dart'; import 'controller.dart'; +import 'editor.dart'; import 'theme.dart'; +import '../../l10n/l10n.dart'; const double kToolbarHeight = 56.0; @@ -203,14 +203,16 @@ class _LinkDialogState extends State<_LinkDialog> { Widget build(BuildContext context) { return AlertDialog( content: TextField( - decoration: const InputDecoration(labelText: 'Paste a link'), + decoration: InputDecoration( + labelText: context.l.addLinkDialogPasteLink, + ), autofocus: true, onChanged: _linkChanged, ), actions: [ TextButton( onPressed: _link.isNotEmpty ? _applyLink : null, - child: const Text('Apply'), + child: Text(context.l.addLinkDialogApply), ), ], ); @@ -568,15 +570,19 @@ class SelectHeadingButton extends StatefulWidget { State createState() => _SelectHeadingButtonState(); } -final _headingToText = { - ParchmentAttribute.heading.unset: 'Normal', - ParchmentAttribute.heading.level1: 'Heading 1', - ParchmentAttribute.heading.level2: 'Heading 2', - ParchmentAttribute.heading.level3: 'Heading 3', - ParchmentAttribute.heading.level4: 'Heading 4', - ParchmentAttribute.heading.level5: 'Heading 5', - ParchmentAttribute.heading.level6: 'Heading 6', -}; +Map, String> _headingToText(BuildContext context) { + final localizations = context.l; + + return { + ParchmentAttribute.heading.unset: localizations.headingNormal, + ParchmentAttribute.heading.level1: localizations.headingLevel1, + ParchmentAttribute.heading.level2: localizations.headingLevel2, + ParchmentAttribute.heading.level3: localizations.headingLevel3, + ParchmentAttribute.heading.level4: localizations.headingLevel4, + ParchmentAttribute.heading.level5: localizations.headingLevel5, + ParchmentAttribute.heading.level6: localizations.headingLevel6, + }; +} class _SelectHeadingButtonState extends State { static double buttonHeight = 32; @@ -636,7 +642,7 @@ class _SelectHeadingButtonState extends State { toolbar.requestKeyboard(); } }, - child: Text(_headingToText[current] ?? ''), + child: Text(_headingToText(context)[current] ?? ''), ), ); } @@ -672,7 +678,8 @@ class _HeadingList extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, - children: _headingToText.entries + children: _headingToText(context) + .entries .map((entry) => _listItem(theme, entry.key, entry.value)) .toList(), ), @@ -815,8 +822,12 @@ class FleatherToolbar extends StatefulWidget implements PreferredSizeWidget { /// If provided, toolbar requests focus and keyboard on toolbar buttons press. final GlobalKey? editorKey; - const FleatherToolbar( - {super.key, this.editorKey, this.padding, required this.children}); + const FleatherToolbar({ + super.key, + this.editorKey, + this.padding, + required this.children, + }); factory FleatherToolbar.basic({ Key? key, @@ -882,260 +893,265 @@ class FleatherToolbar extends StatefulWidget implements PreferredSizeWidget { } return FleatherToolbar( - key: key, - editorKey: editorKey, - padding: padding, - children: [ - ...leading, - Visibility( - visible: !hideBoldButton, - child: ToggleStyleButton( - attribute: ParchmentAttribute.bold, - icon: Icons.format_bold, - controller: controller, - ), + key: key, + editorKey: editorKey, + padding: padding, + children: [ + ...leading, + + Visibility( + visible: !hideBoldButton, + child: ToggleStyleButton( + attribute: ParchmentAttribute.bold, + icon: Icons.format_bold, + controller: controller, ), - const SizedBox(width: 1), - Visibility( - visible: !hideItalicButton, - child: ToggleStyleButton( - attribute: ParchmentAttribute.italic, - icon: Icons.format_italic, - controller: controller, - ), + ), + const SizedBox(width: 1), + Visibility( + visible: !hideItalicButton, + child: ToggleStyleButton( + attribute: ParchmentAttribute.italic, + icon: Icons.format_italic, + controller: controller, ), - const SizedBox(width: 1), - Visibility( - visible: !hideUnderLineButton, - child: ToggleStyleButton( - attribute: ParchmentAttribute.underline, - icon: Icons.format_underline, - controller: controller, - ), + ), + const SizedBox(width: 1), + Visibility( + visible: !hideUnderLineButton, + child: ToggleStyleButton( + attribute: ParchmentAttribute.underline, + icon: Icons.format_underline, + controller: controller, ), - const SizedBox(width: 1), - Visibility( - visible: !hideStrikeThrough, - child: ToggleStyleButton( - attribute: ParchmentAttribute.strikethrough, - icon: Icons.format_strikethrough, - controller: controller, - ), + ), + const SizedBox(width: 1), + Visibility( + visible: !hideStrikeThrough, + child: ToggleStyleButton( + attribute: ParchmentAttribute.strikethrough, + icon: Icons.format_strikethrough, + controller: controller, ), - const SizedBox(width: 1), - Visibility( + ), + const SizedBox(width: 1), + Builder(builder: (context) { + return Visibility( visible: !hideForegroundColor, child: ColorButton( controller: controller, attributeKey: ParchmentAttribute.foregroundColor, - nullColorLabel: 'Automatic', + nullColorLabel: context.l.foregroundColorAutomatic, builder: textColorBuilder, ), - ), - Visibility( + ); + }), + Builder(builder: (context) { + return Visibility( visible: !hideBackgroundColor, child: ColorButton( controller: controller, attributeKey: ParchmentAttribute.backgroundColor, - nullColorLabel: 'No color', + nullColorLabel: context.l.backgroundColorNoColor, builder: backgroundColorBuilder, ), + ); + }), + const SizedBox(width: 1), + Visibility( + visible: !hideInlineCode, + child: ToggleStyleButton( + attribute: ParchmentAttribute.inlineCode, + icon: Icons.code, + controller: controller, ), - const SizedBox(width: 1), - Visibility( - visible: !hideInlineCode, + ), + Visibility( + visible: !hideBoldButton && + !hideItalicButton && + !hideUnderLineButton && + !hideStrikeThrough && + !hideInlineCode, + child: VerticalDivider( + indent: 16, endIndent: 16, color: Colors.grey.shade400)), + + /// ################################################################ + + Visibility( + visible: !hideDirection, child: ToggleStyleButton( - attribute: ParchmentAttribute.inlineCode, - icon: Icons.code, + attribute: ParchmentAttribute.rtl, + icon: Icons.format_textdirection_r_to_l, controller: controller, - ), + )), + Visibility( + visible: !hideDirection, + child: VerticalDivider( + indent: 16, endIndent: 16, color: Colors.grey.shade400)), + + /// ################################################################ + + Visibility( + visible: !hideAlignment, + child: ToggleStyleButton( + attribute: ParchmentAttribute.left, + icon: Icons.format_align_left, + controller: controller, ), - Visibility( - visible: !hideBoldButton && - !hideItalicButton && - !hideUnderLineButton && - !hideStrikeThrough && - !hideInlineCode, - child: VerticalDivider( - indent: 16, endIndent: 16, color: Colors.grey.shade400)), - - /// ################################################################ - - Visibility( - visible: !hideDirection, - child: ToggleStyleButton( - attribute: ParchmentAttribute.rtl, - icon: Icons.format_textdirection_r_to_l, - controller: controller, - )), - Visibility( - visible: !hideDirection, - child: VerticalDivider( - indent: 16, endIndent: 16, color: Colors.grey.shade400)), - - /// ################################################################ - - Visibility( - visible: !hideAlignment, - child: ToggleStyleButton( - attribute: ParchmentAttribute.left, - icon: Icons.format_align_left, - controller: controller, - ), + ), + const SizedBox(width: 1), + Visibility( + visible: !hideAlignment, + child: ToggleStyleButton( + attribute: ParchmentAttribute.center, + icon: Icons.format_align_center, + controller: controller, ), - const SizedBox(width: 1), - Visibility( - visible: !hideAlignment, - child: ToggleStyleButton( - attribute: ParchmentAttribute.center, - icon: Icons.format_align_center, - controller: controller, - ), + ), + const SizedBox(width: 1), + Visibility( + visible: !hideAlignment, + child: ToggleStyleButton( + attribute: ParchmentAttribute.right, + icon: Icons.format_align_right, + controller: controller, ), - const SizedBox(width: 1), - Visibility( - visible: !hideAlignment, - child: ToggleStyleButton( - attribute: ParchmentAttribute.right, - icon: Icons.format_align_right, - controller: controller, - ), + ), + const SizedBox(width: 1), + Visibility( + visible: !hideAlignment, + child: ToggleStyleButton( + attribute: ParchmentAttribute.justify, + icon: Icons.format_align_justify, + controller: controller, ), - const SizedBox(width: 1), - Visibility( + ), + Visibility( visible: !hideAlignment, - child: ToggleStyleButton( - attribute: ParchmentAttribute.justify, - icon: Icons.format_align_justify, - controller: controller, - ), - ), - Visibility( - visible: !hideAlignment, - child: VerticalDivider( - indent: 16, endIndent: 16, color: Colors.grey.shade400)), + child: VerticalDivider( + indent: 16, endIndent: 16, color: Colors.grey.shade400)), - /// ################################################################ + /// ################################################################ - Visibility( - visible: !hideIndentation, - child: IndentationButton( - increase: false, - controller: controller, - ), + Visibility( + visible: !hideIndentation, + child: IndentationButton( + increase: false, + controller: controller, ), - Visibility( - visible: !hideIndentation, - child: IndentationButton( - controller: controller, - ), + ), + Visibility( + visible: !hideIndentation, + child: IndentationButton( + controller: controller, ), - Visibility( - visible: !hideIndentation, - child: VerticalDivider( - indent: 16, endIndent: 16, color: Colors.grey.shade400)), - - /// ################################################################ - - Visibility( - visible: !hideHeadingStyle, - child: SelectHeadingButton(controller: controller)), - Visibility( - visible: !hideHeadingStyle, - child: VerticalDivider( - indent: 16, endIndent: 16, color: Colors.grey.shade400)), - - /// ################################################################ - Visibility( - visible: !hideListNumbers, - child: ToggleStyleButton( - attribute: ParchmentAttribute.block.numberList, - controller: controller, - icon: Icons.format_list_numbered, - ), + ), + Visibility( + visible: !hideIndentation, + child: VerticalDivider( + indent: 16, endIndent: 16, color: Colors.grey.shade400)), + + /// ################################################################ + + Visibility( + visible: !hideHeadingStyle, + child: SelectHeadingButton(controller: controller)), + Visibility( + visible: !hideHeadingStyle, + child: VerticalDivider( + indent: 16, endIndent: 16, color: Colors.grey.shade400)), + + /// ################################################################ + Visibility( + visible: !hideListNumbers, + child: ToggleStyleButton( + attribute: ParchmentAttribute.block.numberList, + controller: controller, + icon: Icons.format_list_numbered, ), + ), - Visibility( - visible: !hideListBullets, - child: ToggleStyleButton( - attribute: ParchmentAttribute.block.bulletList, - controller: controller, - icon: Icons.format_list_bulleted, - ), + Visibility( + visible: !hideListBullets, + child: ToggleStyleButton( + attribute: ParchmentAttribute.block.bulletList, + controller: controller, + icon: Icons.format_list_bulleted, ), - Visibility( - visible: !hideListChecks, - child: ToggleStyleButton( - attribute: ParchmentAttribute.block.checkList, - controller: controller, - icon: Icons.checklist, - ), + ), + Visibility( + visible: !hideListChecks, + child: ToggleStyleButton( + attribute: ParchmentAttribute.block.checkList, + controller: controller, + icon: Icons.checklist, ), - Visibility( - visible: !hideCodeBlock, - child: ToggleStyleButton( - attribute: ParchmentAttribute.block.code, - controller: controller, - icon: Icons.code, - ), + ), + Visibility( + visible: !hideCodeBlock, + child: ToggleStyleButton( + attribute: ParchmentAttribute.block.code, + controller: controller, + icon: Icons.code, ), - Visibility( - visible: !hideListNumbers && - !hideListBullets && - !hideListChecks && - !hideCodeBlock, - child: VerticalDivider( - indent: 16, endIndent: 16, color: Colors.grey.shade400)), - - /// ################################################################ - - Visibility( - visible: !hideQuote, - child: ToggleStyleButton( - attribute: ParchmentAttribute.block.quote, - controller: controller, - icon: Icons.format_quote, - ), + ), + Visibility( + visible: !hideListNumbers && + !hideListBullets && + !hideListChecks && + !hideCodeBlock, + child: VerticalDivider( + indent: 16, endIndent: 16, color: Colors.grey.shade400)), + + /// ################################################################ + + Visibility( + visible: !hideQuote, + child: ToggleStyleButton( + attribute: ParchmentAttribute.block.quote, + controller: controller, + icon: Icons.format_quote, ), - Visibility( - visible: !hideQuote, - child: VerticalDivider( - indent: 16, endIndent: 16, color: Colors.grey.shade400)), - - /// ################################################################ - - Visibility( - visible: !hideLink, - child: LinkStyleButton(controller: controller)), - Visibility( - visible: !hideHorizontalRule, - child: InsertEmbedButton( - controller: controller, - icon: Icons.horizontal_rule, - ), + ), + Visibility( + visible: !hideQuote, + child: VerticalDivider( + indent: 16, endIndent: 16, color: Colors.grey.shade400)), + + /// ################################################################ + + Visibility( + visible: !hideLink, child: LinkStyleButton(controller: controller)), + Visibility( + visible: !hideHorizontalRule, + child: InsertEmbedButton( + controller: controller, + icon: Icons.horizontal_rule, ), - Visibility( - visible: !hideHorizontalRule || !hideLink, - child: VerticalDivider( - indent: 16, endIndent: 16, color: Colors.grey.shade400)), + ), + Visibility( + visible: !hideHorizontalRule || !hideLink, + child: VerticalDivider( + indent: 16, endIndent: 16, color: Colors.grey.shade400)), - /// ################################################################ + /// ################################################################ - Visibility( - visible: !hideUndoRedo, - child: UndoRedoButton.undo( - controller: controller, - ), + Visibility( + visible: !hideUndoRedo, + child: UndoRedoButton.undo( + controller: controller, ), - Visibility( - visible: !hideUndoRedo, - child: UndoRedoButton.redo( - controller: controller, - ), + ), + Visibility( + visible: !hideUndoRedo, + child: UndoRedoButton.redo( + controller: controller, ), + ), - ...trailing, - ]); + ...trailing, + ], + ); } static _FleatherToolbarState _of(BuildContext context) => diff --git a/packages/fleather/lib/src/widgets/link.dart b/packages/fleather/lib/src/widgets/link.dart index 113dd1f4..2fefca26 100644 --- a/packages/fleather/lib/src/widgets/link.dart +++ b/packages/fleather/lib/src/widgets/link.dart @@ -3,6 +3,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:parchment/parchment.dart'; +import '../../l10n/l10n.dart'; + /// List of possible actions returned from [LinkActionPickerDelegate]. enum LinkMenuAction { /// Launch the link @@ -39,25 +41,27 @@ Future defaultLinkActionPickerDelegate( } Future _showCupertinoLinkMenu( - BuildContext context, String link) async { + BuildContext context, + String link, +) async { final result = await showCupertinoModalPopup( context: context, - builder: (ctx) { + builder: (context) { return CupertinoActionSheet( title: Text(link), actions: [ _CupertinoAction( - title: 'Open', + title: context.l.linkDialogOpen, icon: Icons.language_sharp, onPressed: () => Navigator.of(context).pop(LinkMenuAction.launch), ), _CupertinoAction( - title: 'Copy', + title: context.l.linkDialogCopy, icon: Icons.copy_sharp, onPressed: () => Navigator.of(context).pop(LinkMenuAction.copy), ), _CupertinoAction( - title: 'Remove', + title: context.l.linkDialogRemove, icon: Icons.link_off_sharp, onPressed: () => Navigator.of(context).pop(LinkMenuAction.remove), ), @@ -108,7 +112,9 @@ class _CupertinoAction extends StatelessWidget { } Future _showMaterialMenu( - BuildContext context, String link) async { + BuildContext context, + String link, +) async { final result = await showModalBottomSheet( clipBehavior: Clip.hardEdge, context: context, @@ -118,17 +124,17 @@ Future _showMaterialMenu( mainAxisSize: MainAxisSize.min, children: [ _MaterialAction( - title: 'Open', + title: context.l.linkDialogOpen, icon: Icons.language_sharp, onPressed: () => Navigator.of(context).pop(LinkMenuAction.launch), ), _MaterialAction( - title: 'Copy', + title: context.l.linkDialogCopy, icon: Icons.copy_sharp, onPressed: () => Navigator.of(context).pop(LinkMenuAction.copy), ), _MaterialAction( - title: 'Remove', + title: context.l.linkDialogRemove, icon: Icons.link_off_sharp, onPressed: () => Navigator.of(context).pop(LinkMenuAction.remove), ), diff --git a/packages/fleather/pubspec.yaml b/packages/fleather/pubspec.yaml index 4a2c907f..65d8e3d8 100644 --- a/packages/fleather/pubspec.yaml +++ b/packages/fleather/pubspec.yaml @@ -22,6 +22,8 @@ environment: dependencies: flutter: sdk: flutter + flutter_localizations: + sdk: flutter collection: ^1.18.0 parchment_delta: ^1.0.0 parchment: ^1.15.0