Updates to messages (#762)

* Updates to messages

* Format

* Change carbs name

* Regenerate example

* Rev versions

* Changes as per review

* Remove unused file
diff --git a/pkgs/intl4x/CHANGELOG.md b/pkgs/intl4x/CHANGELOG.md
index 9e5e4f8..e41644a 100644
--- a/pkgs/intl4x/CHANGELOG.md
+++ b/pkgs/intl4x/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.7.1
+
+- Export plural rules.
+
 ## 0.7.0
 
 - Add conformance testing workflow.
diff --git a/pkgs/intl4x/lib/intl4x.dart b/pkgs/intl4x/lib/intl4x.dart
index 3496255..aba21c3 100644
--- a/pkgs/intl4x/lib/intl4x.dart
+++ b/pkgs/intl4x/lib/intl4x.dart
@@ -24,6 +24,7 @@
 import 'src/plural_rules/plural_rules_options.dart';
 
 export 'src/locale/locale.dart';
+export 'src/plural_rules/plural_rules.dart';
 
 typedef Icu4xKey = String;
 
diff --git a/pkgs/intl4x/pubspec.yaml b/pkgs/intl4x/pubspec.yaml
index 5a80045..2ceab4a 100644
--- a/pkgs/intl4x/pubspec.yaml
+++ b/pkgs/intl4x/pubspec.yaml
@@ -1,7 +1,7 @@
 name: intl4x
 description: >-
   A lightweight modular library for internationalization (i18n) functionality.
-version: 0.7.0
+version: 0.7.1
 repository: https://github.com/dart-lang/i18n/tree/main/pkgs/intl4x
 platforms: ## TODO: Add native platforms once ICU4X is integrated.
   web:
diff --git a/pkgs/messages/CHANGELOG.md b/pkgs/messages/CHANGELOG.md
index 822f95b..6903662 100644
--- a/pkgs/messages/CHANGELOG.md
+++ b/pkgs/messages/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.2.0
+
+- Remove `IntlObject` interface.
+- Introduce `PluralRules` to retrieve the correct message for plurals.
+
 ## 0.1.1
 
 - Update README.
diff --git a/pkgs/messages/example_json/bin/example.dart b/pkgs/messages/example_json/bin/example.dart
index 5721893..f53bce4 100644
--- a/pkgs/messages/example_json/bin/example.dart
+++ b/pkgs/messages/example_json/bin/example.dart
@@ -7,13 +7,10 @@
 import 'dart:io';
 
 import 'package:example_json/testarbctx2.g.dart';
-import 'package:messages/package_intl_object.dart';
 
 Future<void> main(List<String> arguments) async {
-  final messages = AboutPageMessages(
-    (String id) async => File(id).readAsString(),
-    const OldIntlObject(),
-  );
+  final messages =
+      AboutPageMessages((String id) async => File(id).readAsString());
   // final index = AboutPageMessagesEnum.aboutMessage;
 
   await messages.loadLocale('en');
diff --git a/pkgs/messages/example_json/lib/testarb.g.dart b/pkgs/messages/example_json/lib/testarb.g.dart
index 03b7516..f35050f 100644
--- a/pkgs/messages/example_json/lib/testarb.g.dart
+++ b/pkgs/messages/example_json/lib/testarb.g.dart
@@ -1,12 +1,10 @@
-// Generated by package:messages_builder
+// Generated by package:messages_builder.
 
+import 'package:intl4x/intl4x.dart';
 import 'package:messages/messages_json.dart';
 
 class HomePageMessages {
-  HomePageMessages(
-    this._fileLoader,
-    this.intlObject,
-  );
+  HomePageMessages(this._fileLoader);
 
   final Future<String> Function(String id) _fileLoader;
 
@@ -14,28 +12,26 @@
 
   final Map<String, MessageList> _messages = {};
 
-  static const carbs = {
+  static const _dataFiles = {
     'de': ('lib/testarb_de.json', 'hbDN1MhX'),
     'en': ('lib/testarb.json', 'dr9Md951')
   };
 
-  IntlObject intlObject;
-
   String get currentLocale => _currentLocale;
 
   MessageList get _currentMessages => _messages[currentLocale]!;
 
-  static Iterable<String> get knownLocales => carbs.keys;
+  static Iterable<String> get knownLocales => _dataFiles.keys;
 
   Future<void> loadLocale(String locale) async {
     if (!_messages.containsKey(locale)) {
-      final info = carbs[locale];
+      final info = _dataFiles[locale];
       final carb = info?.$1;
       if (carb == null) {
         throw ArgumentError('Locale $locale is not in $knownLocales');
       }
       final data = await _fileLoader(carb);
-      final messageList = MessageListJson.fromString(data, intlObject);
+      final messageList = MessageListJson.fromString(data, pluralSelector);
       if (messageList.preamble.hash != info?.$2) {
         throw ArgumentError('''
               Messages file for locale $locale has different hash "${messageList.preamble.hash}" than generated code "${info?.$2}".''');
@@ -46,11 +42,31 @@
   }
 
   void loadAllLocales() {
-    for (var locale in knownLocales) {
+    for (final locale in knownLocales) {
       loadLocale(locale);
     }
   }
 
+  Message pluralSelector(
+    num howMany, {
+    required Message other,
+    Message? few,
+    Message? many,
+    Map<int, Message>? numberCases,
+    Map<int, Message>? wordCases,
+  }) {
+    Message getCase(int i) => numberCases?[i] ?? wordCases?[i] ?? other;
+    return switch (
+        Intl(locale: Locale.parse(currentLocale)).plural().select(howMany)) {
+      PluralCategory.zero => getCase(0),
+      PluralCategory.one => getCase(1),
+      PluralCategory.two => getCase(2),
+      PluralCategory.few => few ?? other,
+      PluralCategory.many => many ?? other,
+      PluralCategory.other => other,
+    };
+  }
+
   String helloAndWelcome(
     String firstName,
     String lastName,
diff --git a/pkgs/messages/example_json/lib/testarb.json b/pkgs/messages/example_json/lib/testarb.json
index eb1082f..d46d15a 100644
--- a/pkgs/messages/example_json/lib/testarb.json
+++ b/pkgs/messages/example_json/lib/testarb.json
@@ -1 +1 @@
-[0,"en","dr9Md951",0,null,["Welcome  von !",[8,0],[13,1]],["Welcome  von !",[8,0],[13,1]],[6,"test ",[3,0,["test  new messages",[5,0]],[2,"No new messages",4,"One new message",5,"Two new Messages"]]],[6,"test ",[4,0,"Two new Messages",{"male":"No new messages","female":"One new message"}]]]
\ No newline at end of file
+[0,"en","dr9Md951",0,null,["Welcome  von !",[8,0],[13,1]],["Welcome  von !",[8,0],[13,1]],[6,"test ",[3,0,["test  new messages",[5,0]],[0,"No new messages",1,"One new message","w2","Two new Messages"]]],[6,"test ",[4,0,"Two new Messages",{"male":"No new messages","female":"One new message"}]]]
\ No newline at end of file
diff --git a/pkgs/messages/example_json/lib/testarb_de.json b/pkgs/messages/example_json/lib/testarb_de.json
index f77a4e5..8480d16 100644
--- a/pkgs/messages/example_json/lib/testarb_de.json
+++ b/pkgs/messages/example_json/lib/testarb_de.json
@@ -1 +1 @@
-[0,"de","hbDN1MhX",0,null,["Willkommen  von ",[11,0],[16,1]],["Willkommen  von  2",[11,0],[16,1]],[6,"testde ",[3,0,["test  new messages",[5,0]],[2,"No new messages",4,"One new message",5,"Two new Messages"]]],"testdse is just a simple message"]
\ No newline at end of file
+[0,"de","hbDN1MhX",0,null,["Willkommen  von ",[11,0],[16,1]],["Willkommen  von  2",[11,0],[16,1]],[6,"testde ",[3,0,["test  new messages",[5,0]],[0,"No new messages",1,"One new message","w2","Two new Messages"]]],"testdse is just a simple message"]
\ No newline at end of file
diff --git a/pkgs/messages/example_json/lib/testarbctx2.g.dart b/pkgs/messages/example_json/lib/testarbctx2.g.dart
index 0a7c6d8..55aa20f 100644
--- a/pkgs/messages/example_json/lib/testarbctx2.g.dart
+++ b/pkgs/messages/example_json/lib/testarbctx2.g.dart
@@ -1,12 +1,10 @@
-// Generated by package:messages_builder
+// Generated by package:messages_builder.
 
+import 'package:intl4x/intl4x.dart';
 import 'package:messages/messages_json.dart';
 
 class AboutPageMessages {
-  AboutPageMessages(
-    this._fileLoader,
-    this.intlObject,
-  );
+  AboutPageMessages(this._fileLoader);
 
   final Future<String> Function(String id) _fileLoader;
 
@@ -14,28 +12,26 @@
 
   final Map<String, MessageList> _messages = {};
 
-  static const carbs = {
+  static const _dataFiles = {
     'fr': ('lib/testarbctx2_fr.json', 'EyPjEJJU'),
     'en': ('lib/testarbctx2.json', 'QrwRSsOy')
   };
 
-  IntlObject intlObject;
-
   String get currentLocale => _currentLocale;
 
   MessageList get _currentMessages => _messages[currentLocale]!;
 
-  static Iterable<String> get knownLocales => carbs.keys;
+  static Iterable<String> get knownLocales => _dataFiles.keys;
 
   Future<void> loadLocale(String locale) async {
     if (!_messages.containsKey(locale)) {
-      final info = carbs[locale];
+      final info = _dataFiles[locale];
       final carb = info?.$1;
       if (carb == null) {
         throw ArgumentError('Locale $locale is not in $knownLocales');
       }
       final data = await _fileLoader(carb);
-      final messageList = MessageListJson.fromString(data, intlObject);
+      final messageList = MessageListJson.fromString(data, pluralSelector);
       if (messageList.preamble.hash != info?.$2) {
         throw ArgumentError('''
               Messages file for locale $locale has different hash "${messageList.preamble.hash}" than generated code "${info?.$2}".''');
@@ -46,11 +42,31 @@
   }
 
   void loadAllLocales() {
-    for (var locale in knownLocales) {
+    for (final locale in knownLocales) {
       loadLocale(locale);
     }
   }
 
+  Message pluralSelector(
+    num howMany, {
+    required Message other,
+    Message? few,
+    Message? many,
+    Map<int, Message>? numberCases,
+    Map<int, Message>? wordCases,
+  }) {
+    Message getCase(int i) => numberCases?[i] ?? wordCases?[i] ?? other;
+    return switch (
+        Intl(locale: Locale.parse(currentLocale)).plural().select(howMany)) {
+      PluralCategory.zero => getCase(0),
+      PluralCategory.one => getCase(1),
+      PluralCategory.two => getCase(2),
+      PluralCategory.few => few ?? other,
+      PluralCategory.many => many ?? other,
+      PluralCategory.other => other,
+    };
+  }
+
   String aboutMessage(String websitename) =>
       _currentMessages.generateStringAtIndex(0, [websitename]);
 
diff --git a/pkgs/messages/example_json/lib/testarbctx2.json b/pkgs/messages/example_json/lib/testarbctx2.json
index f59abb6..a56c79c 100644
--- a/pkgs/messages/example_json/lib/testarbctx2.json
+++ b/pkgs/messages/example_json/lib/testarbctx2.json
@@ -1 +1 @@
-[0,"en","QrwRSsOy",0,null,["About ",[6,0]],["Welcome  von  <",[8,0],[13,1]],[6,"test ",[3,0,["test  new messages",[5,0]],[2,"No new messages",4,"One new message",5,"Two new Messages"]]],[6,"test ",[4,0,"Two new Messages",{"male":"No new messages","female":"One new message"}]],"other"]
\ No newline at end of file
+[0,"en","QrwRSsOy",0,null,["About ",[6,0]],["Welcome  von  <",[8,0],[13,1]],[6,"test ",[3,0,["test  new messages",[5,0]],[0,"No new messages",1,"One new message","w2","Two new Messages"]]],[6,"test ",[4,0,"Two new Messages",{"male":"No new messages","female":"One new message"}]],"other"]
\ No newline at end of file
diff --git a/pkgs/messages/example_json/lib/testarbctx2_fr.json b/pkgs/messages/example_json/lib/testarbctx2_fr.json
index a1c89f3..61b6c08 100644
--- a/pkgs/messages/example_json/lib/testarbctx2_fr.json
+++ b/pkgs/messages/example_json/lib/testarbctx2_fr.json
@@ -1 +1 @@
-[0,"fr","EyPjEJJU",0,null,["Sur ",[4,0]],["Welcome  von  <",[8,0],[13,1]],[6,"test ",[3,0,["test  new messages",[5,0]],[2,"No new messages",4,"One new message",5,"Two new Messages"]]],[6,"test ",[4,0,"Two new Messages",{"male":"No new messages","female":"One new message"}]],"other"]
\ No newline at end of file
+[0,"fr","EyPjEJJU",0,null,["Sur ",[4,0]],["Welcome  von  <",[8,0],[13,1]],[6,"test ",[3,0,["test  new messages",[5,0]],[0,"No new messages",1,"One new message","w2","Two new Messages"]]],[6,"test ",[4,0,"Two new Messages",{"male":"No new messages","female":"One new message"}]],"other"]
\ No newline at end of file
diff --git a/pkgs/messages/example_json/pubspec.yaml b/pkgs/messages/example_json/pubspec.yaml
index 348628f..6807cce 100644
--- a/pkgs/messages/example_json/pubspec.yaml
+++ b/pkgs/messages/example_json/pubspec.yaml
@@ -6,6 +6,7 @@
   sdk: ^3.0.0
 
 dependencies:
+  intl4x: ^0.7.0
   messages:
     path: ../
 
@@ -25,3 +26,4 @@
     generateMethods: true
     generateFindById: false
     generateFindBy: integer
+    pluralSelector: intl4x
diff --git a/pkgs/messages/example_json/pubspec_overrides.yaml b/pkgs/messages/example_json/pubspec_overrides.yaml
index 4477e4d..54623dd 100644
--- a/pkgs/messages/example_json/pubspec_overrides.yaml
+++ b/pkgs/messages/example_json/pubspec_overrides.yaml
@@ -3,3 +3,5 @@
     path: ../
   messages_serializer:
     path: ../../messages_serializer
+  intl4x:
+    path: ../../intl4x
diff --git a/pkgs/messages/lib/messages.dart b/pkgs/messages/lib/messages.dart
index 9926f34..6544241 100644
--- a/pkgs/messages/lib/messages.dart
+++ b/pkgs/messages/lib/messages.dart
@@ -3,6 +3,4 @@
 // BSD-style license that can be found in the LICENSE file.
 
 export 'src/deserializer/deserializer.dart';
-export 'src/intl_object.dart' show IntlObject;
-export 'src/intl_style_lookup.dart' show Intl;
 export 'src/message_format.dart';
diff --git a/pkgs/messages/lib/package_intl_object.dart b/pkgs/messages/lib/package_intl_object.dart
deleted file mode 100644
index 42ae38e..0000000
--- a/pkgs/messages/lib/package_intl_object.dart
+++ /dev/null
@@ -1,6 +0,0 @@
-// Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-//TODO: Move OldIntlObject to package:intl
-export 'src/old_intl_object.dart' show OldIntlObject;
diff --git a/pkgs/messages/lib/src/deserializer/deserializer.dart b/pkgs/messages/lib/src/deserializer/deserializer.dart
index 7888ddc..1bc6120 100644
--- a/pkgs/messages/lib/src/deserializer/deserializer.dart
+++ b/pkgs/messages/lib/src/deserializer/deserializer.dart
@@ -2,9 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import '../intl_object.dart';
 import '../message_format.dart';
+import '../plural_selector.dart';
 
 abstract class Deserializer<T extends MessageList> {
-  T deserialize(IntlObject intl);
+  T deserialize(PluralSelector selector);
 }
diff --git a/pkgs/messages/lib/src/deserializer/deserializer_json.dart b/pkgs/messages/lib/src/deserializer/deserializer_json.dart
index a6aa509..98a56ed 100644
--- a/pkgs/messages/lib/src/deserializer/deserializer_json.dart
+++ b/pkgs/messages/lib/src/deserializer/deserializer_json.dart
@@ -4,9 +4,9 @@
 
 import 'dart:convert';
 
-import '../intl_object.dart';
 import '../message_format.dart';
 import '../message_list_json.dart';
+import '../plural_selector.dart';
 import 'deserializer.dart';
 
 class JsonDeserializer extends Deserializer<MessageListJson> {
@@ -20,7 +20,7 @@
   }
 
   @override
-  MessageListJson deserialize(IntlObject intl) {
+  MessageListJson deserialize(PluralSelector selector) {
     if (preamble.version != serializationVersion) {
       throw ArgumentError(
           '''This message has version ${preamble.version}, while the deserializer has version $serializationVersion''');
@@ -32,7 +32,7 @@
     return MessageListJson(
       preamble,
       _messages,
-      intl,
+      selector,
       mapping?.map((key, value) => MapEntry(
             int.parse(key, radix: serializationRadix),
             int.parse(value as String, radix: serializationRadix),
@@ -55,8 +55,6 @@
         return _forPlural(message, start, id);
       } else if (typeOrId == SelectMessage.type) {
         return _forSelect(message, start, id);
-      } else if (typeOrId == GenderMessage.type) {
-        return _forGender(message, start, id);
       } else if (typeOrId == CombinedMessage.type) {
         return _forCombined(message, start, id);
       } else if (typeOrId is String) {
@@ -83,51 +81,29 @@
   PluralMessage _forPlural(List<dynamic> message, int start, String? id) {
     final argIndex = message[start] as int;
     final otherMessage = getMessage(message[start + 1]);
-    Message? zeroWordMessage;
-    Message? zeroNumberMessage;
-    Message? oneWordMessage;
-    Message? oneNumberMessage;
-    Message? twoWordMessage;
-    Message? twoNumberMessage;
     Message? fewMessage;
     Message? manyMessage;
+    final numberCases = <int, Message>{};
+    final wordCases = <int, Message>{};
     final submessages = message[start + 2] as List;
     for (var i = 0; i < submessages.length - 1; i += 2) {
       final msg = getMessage(submessages[i + 1]);
-      switch (submessages[i]) {
-        case Plural.zeroWord:
-          zeroWordMessage = msg;
-          break;
-        case Plural.zeroNumber:
-          zeroNumberMessage = msg;
-          break;
-        case Plural.oneWord:
-          oneWordMessage = msg;
-          break;
-        case Plural.oneNumber:
-          oneNumberMessage = msg;
-          break;
-        case Plural.twoWord:
-          twoWordMessage = msg;
-          break;
-        case Plural.twoNumber:
-          twoNumberMessage = msg;
-          break;
-        case Plural.few:
-          fewMessage = msg;
-          break;
-        case Plural.many:
-          manyMessage = msg;
-          break;
+      final messageMarker = submessages[i];
+      if (messageMarker case PluralMarker.few) {
+        fewMessage = msg;
+      } else if (messageMarker case PluralMarker.many) {
+        manyMessage = msg;
+      } else if (messageMarker case final int digit) {
+        numberCases[digit] = msg;
+      } else if (messageMarker is String &&
+          messageMarker.startsWith(PluralMarker.wordCase)) {
+        final digit = int.parse(messageMarker.substring(1));
+        wordCases[digit] = msg;
       }
     }
     return PluralMessage(
-      zeroNumber: zeroNumberMessage,
-      zeroWord: zeroWordMessage,
-      oneNumber: oneNumberMessage,
-      oneWord: oneWordMessage,
-      twoNumber: twoNumberMessage,
-      twoWord: twoWordMessage,
+      numberCases: numberCases,
+      wordCases: wordCases,
       few: fewMessage,
       many: manyMessage,
       argIndex: argIndex,
@@ -153,30 +129,4 @@
       message.skip(start).map(getMessage).toList(),
     );
   }
-
-  GenderMessage _forGender(List<dynamic> message, int start, String? id) {
-    final argIndex = message[start] as int;
-    final otherMessage = getMessage(message[start + 1]);
-    final submessages = message[start + 2] as List;
-    Message? femaleMessage;
-    Message? maleMessage;
-    for (var i = 0; i < submessages.length - 1; i += 2) {
-      final msg = getMessage(submessages[i + 1]);
-      switch (submessages[i]) {
-        case Gender.female:
-          femaleMessage = msg;
-          break;
-        case Gender.male:
-          maleMessage = msg;
-          break;
-      }
-    }
-    return GenderMessage(
-      female: femaleMessage,
-      male: maleMessage,
-      other: otherMessage,
-      argIndex: argIndex,
-      id: id,
-    );
-  }
 }
diff --git a/pkgs/messages/lib/src/intl_object.dart b/pkgs/messages/lib/src/intl_object.dart
deleted file mode 100644
index 0638121..0000000
--- a/pkgs/messages/lib/src/intl_object.dart
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'message.dart';
-
-abstract class IntlObject {
-  const IntlObject();
-
-  Message gender(
-    GenderEnum gender,
-    Message? female,
-    Message? male,
-    Message other,
-  );
-
-  Message plural(
-    num howMany, {
-    Message? zero,
-    Message? one,
-    Message? two,
-    Message? few,
-    Message? many,
-    required Message other,
-    String? locale,
-  });
-
-  Message select(Object arg, Map<Object, Message> cases);
-}
diff --git a/pkgs/messages/lib/src/intl_style_lookup.dart b/pkgs/messages/lib/src/intl_style_lookup.dart
deleted file mode 100644
index 5a5d4a6..0000000
--- a/pkgs/messages/lib/src/intl_style_lookup.dart
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-class Intl {
-  static MessageLookup? l;
-
-  static String message(
-    String s, {
-    required List<String> args,
-    required String id,
-  }) {
-    if (l != null) return l!.getById(id, args);
-    for (var i = 0; i < args.length; i++) {
-      s = s.replaceAll('#$i', args[i]);
-    }
-    return s;
-  }
-}
-
-abstract class MessageLookup {
-  String getById(String id, List<dynamic> args);
-}
diff --git a/pkgs/messages/lib/src/message.dart b/pkgs/messages/lib/src/message.dart
index 63da1fe..54adec1 100644
--- a/pkgs/messages/lib/src/message.dart
+++ b/pkgs/messages/lib/src/message.dart
@@ -2,7 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'intl_object.dart';
+import 'plural_selector.dart';
 
 sealed class Message {
   final String? id;
@@ -11,7 +11,7 @@
 
   String generateString(
     List allArgs, {
-    required IntlObject intl,
+    required PluralSelector pluralSelector,
     String? locale,
     String Function(String p1)? cleaner,
   });
@@ -25,14 +25,14 @@
   @override
   String generateString(
     List allArgs, {
-    required IntlObject intl,
+    required PluralSelector pluralSelector,
     String Function(String p1)? cleaner,
     String? locale,
   }) =>
       messages
           .map((e) => e.generateString(
                 allArgs,
-                intl: intl,
+                pluralSelector: pluralSelector,
                 cleaner: cleaner,
                 locale: locale,
               ))
@@ -57,7 +57,7 @@
   @override
   String generateString(
     List allArgs, {
-    required IntlObject intl,
+    required PluralSelector pluralSelector,
     String Function(String p1)? cleaner,
     String? locale,
   }) {
@@ -81,73 +81,20 @@
   }
 }
 
-final class GenderMessage extends Message {
-  final Message? male;
-  final Message? female;
-  final Message other;
-  final int argIndex;
-
-  GenderMessage({
-    this.male,
-    this.female,
-    required this.other,
-    required this.argIndex,
-    String? id,
-  }) : super(id);
-
-  static const int type = 5;
-
-  @override
-  String generateString(
-    List allArgs, {
-    required IntlObject intl,
-    String Function(String p1)? cleaner,
-    String? locale,
-  }) {
-    return intl
-        .gender(
-          allArgs[argIndex] as GenderEnum,
-          female,
-          male,
-          other,
-        )
-        .generateString(
-          allArgs,
-          intl: intl,
-          cleaner: cleaner,
-          locale: locale,
-        );
-  }
-}
-
-enum GenderEnum {
-  female,
-  male,
-  other;
-}
-
 final class PluralMessage extends Message {
-  final Message? zeroWord;
-  final Message? zeroNumber;
-  final Message? oneWord;
-  final Message? oneNumber;
-  final Message? twoWord;
-  final Message? twoNumber;
+  final Map<int, Message> numberCases;
+  final Map<int, Message> wordCases;
   final Message? few;
   final Message? many;
   final Message other;
   final int argIndex;
 
   PluralMessage({
+    this.numberCases = const {},
+    this.wordCases = const {},
     this.few,
     this.many,
     required this.other,
-    this.zeroWord,
-    this.zeroNumber,
-    this.oneWord,
-    this.oneNumber,
-    this.twoWord,
-    this.twoNumber,
     required this.argIndex,
     String? id,
   }) : super(id);
@@ -157,22 +104,19 @@
   @override
   String generateString(
     List allArgs, {
-    required IntlObject intl,
+    required PluralSelector pluralSelector,
     String Function(String p1)? cleaner,
     String? locale,
   }) {
-    return intl
-        .plural(
-          allArgs[argIndex] as num,
-          few: few,
-          many: many,
-          zero: zeroNumber ?? zeroWord,
-          one: oneNumber ?? oneWord,
-          two: twoNumber ?? twoWord,
-          other: other,
-          locale: locale,
-        )
-        .generateString(allArgs, intl: intl, cleaner: cleaner, locale: locale);
+    return pluralSelector(
+      allArgs[argIndex] as num,
+      numberCases: numberCases,
+      wordCases: wordCases,
+      few: few,
+      many: many,
+      other: other,
+    ).generateString(allArgs,
+        pluralSelector: pluralSelector, cleaner: cleaner, locale: locale);
   }
 }
 
@@ -192,14 +136,13 @@
   @override
   String generateString(
     List allArgs, {
-    required IntlObject intl,
+    required PluralSelector pluralSelector,
     String Function(String p1)? cleaner,
     String? locale,
   }) {
-    final selected =
-        intl.select(allArgs[argIndex] as Object, {...cases, 'other': other});
+    final selected = cases[allArgs[argIndex]!] ?? other;
     return selected.generateString(
-      intl: intl,
+      pluralSelector: pluralSelector,
       allArgs,
       cleaner: cleaner,
       locale: locale,
diff --git a/pkgs/messages/lib/src/message_format.dart b/pkgs/messages/lib/src/message_format.dart
index c8e881e..909d7a4 100644
--- a/pkgs/messages/lib/src/message_format.dart
+++ b/pkgs/messages/lib/src/message_format.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 // ignore_for_file: non_constant_identifier_names
-import 'intl_object.dart';
+import 'plural_selector.dart';
 
 export 'message.dart';
 
@@ -30,36 +30,15 @@
 
 abstract class MessageList {
   Preamble get preamble;
-  IntlObject get intl;
+  PluralSelector get pluralSelector;
 
   String generateStringAtIndex(int index, List args);
 
   String generateStringAtId(String id, List args);
 }
 
-enum PluralEnum {
-  zeroWord,
-  zeroNumber,
-  oneWord,
-  oneNumber,
-  twoWord,
-  twoNumber,
-  few,
-  many,
-}
-
-class Plural {
-  static const int zeroWord = 1;
-  static const int zeroNumber = 2;
-  static const int oneWord = 3;
-  static const int oneNumber = 4;
-  static const int twoWord = 5;
-  static const int twoNumber = 6;
-  static const int few = 7;
-  static const int many = 8;
-}
-
-class Gender {
-  static const int female = 1;
-  static const int male = 2;
+sealed class PluralMarker {
+  static const String wordCase = 'w';
+  static const String few = 'f';
+  static const String many = 'm';
 }
diff --git a/pkgs/messages/lib/src/message_list_json.dart b/pkgs/messages/lib/src/message_list_json.dart
index 773e4a3..bb0eb1c 100644
--- a/pkgs/messages/lib/src/message_list_json.dart
+++ b/pkgs/messages/lib/src/message_list_json.dart
@@ -3,8 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'deserializer/deserializer_json.dart';
-import 'intl_object.dart';
 import 'message_format.dart';
+import 'plural_selector.dart';
 
 class JsonPreamble extends Preamble {
   final List _data;
@@ -40,12 +40,12 @@
 
 class MessageListJson extends MessageList {
   final List<Message> messages;
-  final IntlObject _intl;
+  final PluralSelector _selector;
   final JsonPreamble _preamble;
   final Map<int, int>? messageIndices;
 
   @override
-  IntlObject get intl => _intl;
+  PluralSelector get pluralSelector => _selector;
 
   @override
   Preamble get preamble => _preamble;
@@ -53,22 +53,22 @@
   MessageListJson(
     this._preamble,
     this.messages,
-    this._intl,
+    this._selector,
     this.messageIndices,
   );
 
-  factory MessageListJson.fromString(String string, IntlObject intl) =>
+  factory MessageListJson.fromString(String string, PluralSelector intl) =>
       JsonDeserializer(string).deserialize(intl);
 
   @override
   String generateStringAtId(String id, List args) => messages
       .where((element) => element.id == id)
       .first
-      .generateString(args, intl: _intl);
+      .generateString(args, pluralSelector: _selector);
 
   @override
   String generateStringAtIndex(int index, List args) =>
-      messages[getIndex(index)].generateString(args, intl: _intl);
+      messages[getIndex(index)].generateString(args, pluralSelector: _selector);
 
   int getIndex(int index) => messageIndices?[index] ?? index;
 }
diff --git a/pkgs/messages/lib/src/old_intl_object.dart b/pkgs/messages/lib/src/old_intl_object.dart
deleted file mode 100644
index a217376..0000000
--- a/pkgs/messages/lib/src/old_intl_object.dart
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'package:intl/intl.dart' as old_intl;
-
-import 'intl_object.dart';
-import 'message.dart';
-
-class OldIntlObject extends IntlObject {
-  const OldIntlObject();
-  @override
-  Message gender(
-    GenderEnum gender,
-    Message? female,
-    Message? male,
-    Message other,
-  ) =>
-      old_intl.Intl.genderLogic<Message>(
-        gender.name,
-        female: female,
-        male: male,
-        other: other,
-      );
-
-  @override
-  Message plural(
-    num howMany, {
-    Message? zero,
-    Message? one,
-    Message? two,
-    Message? few,
-    Message? many,
-    required Message other,
-    String? locale,
-  }) {
-    return old_intl.Intl.pluralLogic(
-      howMany,
-      few: few,
-      many: many,
-      zero: zero,
-      one: one,
-      two: two,
-      other: other,
-      locale: locale,
-    );
-  }
-
-  @override
-  Message select(Object arg, Map<Object, Message> cases) {
-    return old_intl.Intl.selectLogic(
-      arg,
-      cases,
-    );
-  }
-}
diff --git a/pkgs/messages/lib/src/plural_selector.dart b/pkgs/messages/lib/src/plural_selector.dart
new file mode 100644
index 0000000..5d6899b
--- /dev/null
+++ b/pkgs/messages/lib/src/plural_selector.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'message.dart';
+
+typedef PluralSelector = Message Function(
+  num howMany, {
+  Map<int, Message>? numberCases,
+  Map<int, Message>? wordCases,
+  Message? few,
+  Message? many,
+  required Message other,
+});
diff --git a/pkgs/messages/pubspec.yaml b/pkgs/messages/pubspec.yaml
index 7ccaf86..82e3b90 100644
--- a/pkgs/messages/pubspec.yaml
+++ b/pkgs/messages/pubspec.yaml
@@ -1,6 +1,6 @@
 name: messages
 description: A lightweight modular library for localization (l10n) functionality.
-version: 0.1.1
+version: 0.2.0
 repository: https://github.com/dart-lang/i18n/tree/main/pkgs/messages
 
 environment:
diff --git a/pkgs/messages/test/messagelist_json_test.dart b/pkgs/messages/test/messagelist_json_test.dart
index 2c90587..ab29145 100644
--- a/pkgs/messages/test/messagelist_json_test.dart
+++ b/pkgs/messages/test/messagelist_json_test.dart
@@ -2,10 +2,31 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'package:intl/intl.dart' as old_intl;
 import 'package:messages/messages_json.dart';
-import 'package:messages/package_intl_object.dart';
 import 'package:test/test.dart';
 
+Message intlPluralSelector(
+  num howMany, {
+  Map<int, Message>? numberCases,
+  Map<int, Message>? wordCases,
+  Message? few,
+  Message? many,
+  required Message other,
+  String? locale,
+}) {
+  return old_intl.Intl.pluralLogic(
+    howMany,
+    few: few,
+    many: many,
+    zero: numberCases?[0] ?? wordCases?[0],
+    one: numberCases?[1] ?? wordCases?[1],
+    two: numberCases?[2] ?? wordCases?[2],
+    other: other,
+    locale: locale,
+  );
+}
+
 void main() {
   test('JSON MessageList', () {
     final MessageList messageList = MessageListJson(
@@ -27,17 +48,19 @@
             'case2': StringMessage('Case 2'),
             'case3': PluralMessage(
               other: StringMessage('other nested'),
-              twoNumber: StringMessage(': ', argPositions: [
-                (stringIndex: 0, argIndex: 0),
-                (stringIndex: 2, argIndex: 1),
-              ]),
+              numberCases: {
+                2: StringMessage(': ', argPositions: [
+                  (stringIndex: 0, argIndex: 0),
+                  (stringIndex: 2, argIndex: 1),
+                ])
+              },
               argIndex: 1,
             ),
           },
           0,
         )
       ],
-      const OldIntlObject(),
+      intlPluralSelector,
       null,
     );
 
diff --git a/pkgs/messages_builder/CHANGELOG.md b/pkgs/messages_builder/CHANGELOG.md
index 9ee6d8d..e17f754 100644
--- a/pkgs/messages_builder/CHANGELOG.md
+++ b/pkgs/messages_builder/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.2.0
+
+- Remove `IntlObject` interface.
+- Introduce `PluralRules` to retrieve the correct message for plurals.
+
 ## 0.1.1
 
 - Add `header` to `GenerationOptions`.
diff --git a/pkgs/messages_builder/lib/code_generation/constructor_generation.dart b/pkgs/messages_builder/lib/code_generation/constructor_generation.dart
index 468c2d2..936ef66 100644
--- a/pkgs/messages_builder/lib/code_generation/constructor_generation.dart
+++ b/pkgs/messages_builder/lib/code_generation/constructor_generation.dart
@@ -21,9 +21,10 @@
             ..name = '_fileLoader'
             ..toThis = true,
         ),
-        Parameter((pb) => pb
-          ..name = 'intlObject'
-          ..toThis = true),
+        if (options.pluralSelector == PluralSelectorType.custom)
+          Parameter((pb) => pb
+            ..name = 'pluralSelector'
+            ..toThis = true),
       ]));
     return [nativeConstructor];
   }
diff --git a/pkgs/messages_builder/lib/code_generation/field_generation.dart b/pkgs/messages_builder/lib/code_generation/field_generation.dart
index 7c667fb..a6d28f6 100644
--- a/pkgs/messages_builder/lib/code_generation/field_generation.dart
+++ b/pkgs/messages_builder/lib/code_generation/field_generation.dart
@@ -42,29 +42,30 @@
         ..name = '_messages'
         ..assignment = const Code('{}'),
     );
-    final carbs = Field(
+    final dataFiles = Field(
       (fb) {
         final paths = localeToResourceInfo.entries
             .map((e) => "'${e.key}' : ('${e.value.path}', '${e.value.hasch}')")
             .join(',');
         fb
-          ..name = 'carbs'
+          ..name = '_dataFiles'
           ..modifier = FieldModifier.constant
           ..static = true
           ..assignment = Code('{$paths}');
       },
     );
-    final intlObject = Field(
+    final pluralSelector = Field(
       (fb) => fb
-        ..name = 'intlObject'
-        ..type = const Reference('IntlObject'),
+        ..name = 'pluralSelector'
+        ..type = const Reference(
+            '''Message Function(num howMany, {Map<int, Message>? numberCases, Map<int, Message>? wordCases, Message? few, Message? many, Message other, String? locale})'''),
     );
     final fields = [
       loadingStrategy,
       currentLocale,
       messages,
-      carbs,
-      intlObject,
+      dataFiles,
+      if (options.pluralSelector == PluralSelectorType.custom) pluralSelector,
     ];
     return fields;
   }
diff --git a/pkgs/messages_builder/lib/code_generation/import_generation.dart b/pkgs/messages_builder/lib/code_generation/import_generation.dart
index 66a4b7f..79ee4e5 100644
--- a/pkgs/messages_builder/lib/code_generation/import_generation.dart
+++ b/pkgs/messages_builder/lib/code_generation/import_generation.dart
@@ -19,6 +19,13 @@
           Directive.import('package:messages/messages_json.dart')
         ],
     };
-    return serializationImports;
+    final pluralImports = switch (options.pluralSelector) {
+      PluralSelectorType.intl => [Directive.import('package:intl/intl.dart')],
+      PluralSelectorType.intl4x => [
+          Directive.import('package:intl4x/intl4x.dart')
+        ],
+      PluralSelectorType.custom => <Directive>[],
+    };
+    return [...serializationImports, ...pluralImports];
   }
 }
diff --git a/pkgs/messages_builder/lib/code_generation/method_generation.dart b/pkgs/messages_builder/lib/code_generation/method_generation.dart
index 39f684d..c2438f7 100644
--- a/pkgs/messages_builder/lib/code_generation/method_generation.dart
+++ b/pkgs/messages_builder/lib/code_generation/method_generation.dart
@@ -63,7 +63,7 @@
         final loading = switch (options.deserialization) {
           DeserializationType.web => '''
           final data = await _fileLoader(carb);
-          final messageList = MessageListJson.fromString(data, intlObject);''',
+          final messageList = MessageListJson.fromString(data, pluralSelector);''',
         };
         mb
           ..name = 'loadLocale'
@@ -75,7 +75,7 @@
           ..modifier = MethodModifier.async
           ..body = Code('''
           if (!_messages.containsKey(locale)) {
-            final info = carbs[locale];
+            final info = _dataFiles[locale];
             final carb = info?.\$1;
             if (carb == null) {
               throw ArgumentError('Locale \$locale is not in \$knownLocales');
@@ -98,7 +98,7 @@
           ..name = 'loadAllLocales'
           ..returns = const Reference('void')
           ..body = const Code('''
-          for (var locale in knownLocales) {
+          for (final locale in knownLocales) {
              loadLocale(locale);
           }
       ''');
@@ -110,7 +110,7 @@
         ..type = MethodType.getter
         ..lambda = true
         ..static = true
-        ..body = const Code('carbs.keys')
+        ..body = const Code('_dataFiles.keys')
         ..returns = const Reference('Iterable<String>'),
     );
     final getCurrentMessages = Method(
@@ -166,6 +166,60 @@
           const Code('_currentMessages.generateStringAtIndex(val.index, args)')
       ..lambda = true
       ..returns = const Reference('String'));
+    // Message Function(num,
+    //     {Message? few,
+    //     String? locale,
+    //     Message? many,
+    //     Map<int, Message>? numberCases,
+    //     required Message other,
+    //     Map<int, Message>? wordCases}) intl;
+    Method pluralSelector() => Method(
+          (mb) => mb
+            ..name = 'pluralSelector'
+            ..returns = const Reference('Message')
+            ..requiredParameters.addAll([
+              Parameter(
+                (pb) => pb
+                  ..name = 'howMany'
+                  ..type = const Reference('num')
+                  ..named = false,
+              ),
+            ])
+            ..optionalParameters.addAll([
+              Parameter(
+                (pb) => pb
+                  ..name = 'other'
+                  ..type = const Reference('Message')
+                  ..required = true
+                  ..named = true,
+              ),
+              Parameter(
+                (pb) => pb
+                  ..name = 'few'
+                  ..type = const Reference('Message?')
+                  ..named = true,
+              ),
+              Parameter(
+                (pb) => pb
+                  ..name = 'many'
+                  ..type = const Reference('Message?')
+                  ..named = true,
+              ),
+              Parameter(
+                (pb) => pb
+                  ..name = 'numberCases'
+                  ..type = const Reference('Map<int, Message>?')
+                  ..named = true,
+              ),
+              Parameter(
+                (pb) => pb
+                  ..name = 'wordCases'
+                  ..type = const Reference('Map<int, Message>?')
+                  ..named = true,
+              ),
+            ])
+            ..body = pluralSelectorBody(),
+        );
 
     return [
       getCurrentLocale,
@@ -175,7 +229,37 @@
       getKnownLocales,
       loadLocale,
       loadAllLocales,
+      if (options.pluralSelector != PluralSelectorType.custom) pluralSelector(),
       ...messageCalls,
     ];
   }
+
+  Code pluralSelectorBody() {
+    return switch (options.pluralSelector) {
+      PluralSelectorType.intl => const Code('''
+return Intl.pluralLogic(
+    howMany,
+    few: few,
+    many: many,
+    zero: numberCases?[0] ?? wordCases?[0],
+    one: numberCases?[1] ?? wordCases?[1],
+    two: numberCases?[2] ?? wordCases?[2],
+    other: other,
+    locale: currentLocale,
+  );
+  '''),
+      PluralSelectorType.intl4x => const Code('''
+Message getCase(int i) => numberCases?[i] ?? wordCases?[i] ?? other;
+    return switch (Intl(locale: Locale.parse(currentLocale)).plural().select(howMany)) {
+      PluralCategory.zero => getCase(0),
+      PluralCategory.one => getCase(1),
+      PluralCategory.two => getCase(2),
+      PluralCategory.few => few ?? other,
+      PluralCategory.many => many ?? other,
+      PluralCategory.other => other,
+    };
+    '''),
+      PluralSelectorType.custom => throw ArgumentError(),
+    };
+  }
 }
diff --git a/pkgs/messages_builder/lib/generation_options.dart b/pkgs/messages_builder/lib/generation_options.dart
index e9e2616..6573b3f 100644
--- a/pkgs/messages_builder/lib/generation_options.dart
+++ b/pkgs/messages_builder/lib/generation_options.dart
@@ -36,8 +36,12 @@
   /// dart native code.
   final DeserializationType deserialization;
 
+  /// The header to add to all generated files, for example for licensing.
   final String header;
 
+  /// The origin of the algorithm for determining which plural case to use.
+  final PluralSelectorType pluralSelector;
+
   GenerationOptions({
     required this.serialization,
     required this.deserialization,
@@ -45,6 +49,7 @@
     required this.findById,
     required this.indexType,
     required this.header,
+    required this.pluralSelector,
   });
 
   static Future<GenerationOptions> fromPubspec(BuildStep buildStep) async {
@@ -58,16 +63,31 @@
       deserialization: DeserializationType.web,
       messageCalls: (messagesOptions?['generateMethods'] as bool?) ?? true,
       findById: (messagesOptions?['generateFindById'] as bool?) ?? false,
-      indexType: IndexType.values
-              .where((type) =>
-                  type.name == messagesOptions?['generateFindBy'] as String?)
-              .firstOrNull ??
-          IndexType.integer,
+      indexType: _indexType(messagesOptions),
       header: messagesOptions?['header'] as String? ??
           'Generated by package:messages_builder.',
+      pluralSelector: _pluralSelector(messagesOptions),
     );
     return generationOptions;
   }
+
+  static IndexType _indexType(YamlMap? messagesOptions) {
+    final generateFindString = messagesOptions?['generateFindBy'] as String?;
+    return generateFindString != null
+        ? IndexType.values
+            .where((type) => type.name == generateFindString)
+            .first
+        : IndexType.integer;
+  }
+
+  static PluralSelectorType _pluralSelector(YamlMap? messagesOptions) {
+    final pluralSelectorString = messagesOptions?['pluralSelector'] as String?;
+    return pluralSelectorString != null
+        ? PluralSelectorType.values
+            .where((type) => type.name == pluralSelectorString)
+            .first
+        : PluralSelectorType.intl;
+  }
 }
 
 enum SerializationType {
@@ -78,8 +98,27 @@
   web;
 }
 
+/// How the indexing of the messages should be implemented.
 enum IndexType {
+  /// No indexing.
   none,
+
+  /// Indexing via a collection of `static int`s.
   integer,
+
+  /// Indexing via an enum.
   enumerate;
 }
+
+/// The origin of the algorithm for determining which plural case to use.
+enum PluralSelectorType {
+  /// From `package:intl`.
+  intl,
+
+  /// From `package:intl4x`.
+
+  intl4x,
+
+  /// A user-specified algorithm.
+  custom;
+}
diff --git a/pkgs/messages_builder/lib/message_parser/plural_parser.dart b/pkgs/messages_builder/lib/message_parser/plural_parser.dart
index 92fd340..77ec654 100644
--- a/pkgs/messages_builder/lib/message_parser/plural_parser.dart
+++ b/pkgs/messages_builder/lib/message_parser/plural_parser.dart
@@ -56,12 +56,8 @@
     final numberCases = getNumberCases(parts, arguments);
     final wordCases = getWordCases(parts, arguments);
     return PluralMessage(
-      zeroNumber: numberCases[0],
-      zeroWord: wordCases[0],
-      oneNumber: numberCases[1],
-      oneWord: wordCases[1],
-      twoNumber: numberCases[2],
-      twoWord: wordCases[2],
+      numberCases: numberCases,
+      wordCases: wordCases,
       few: getNamed(parts, 'few', arguments),
       many: getNamed(parts, 'many', arguments),
       other: getOther(parts, arguments)!,
diff --git a/pkgs/messages_builder/pubspec.yaml b/pkgs/messages_builder/pubspec.yaml
index 962f923..1cf7124 100644
--- a/pkgs/messages_builder/pubspec.yaml
+++ b/pkgs/messages_builder/pubspec.yaml
@@ -1,6 +1,6 @@
 name: messages_builder
 description: Build the messages for consumption by package:messages
-version: 0.1.1
+version: 0.2.0
 repository: https://github.com/dart-lang/i18n/pkgs/messages_builder
 
 environment:
diff --git a/pkgs/messages_builder/test/web_deserializer_native_test.dart b/pkgs/messages_builder/test/web_deserializer_native_test.dart
index 0761683..e93fa82 100644
--- a/pkgs/messages_builder/test/web_deserializer_native_test.dart
+++ b/pkgs/messages_builder/test/web_deserializer_native_test.dart
@@ -5,8 +5,8 @@
 import 'dart:convert';
 
 import 'package:build/src/asset/id.dart';
+import 'package:intl/intl.dart' as old_intl;
 import 'package:messages/messages_json.dart';
-import 'package:messages/package_intl_object.dart';
 import 'package:messages_builder/arb_parser.dart';
 import 'package:messages_builder/message_with_metadata.dart';
 import 'package:messages_serializer/messages_serializer.dart';
@@ -14,6 +14,27 @@
 
 import 'testarb.arb.dart';
 
+Message intlPluralSelector(
+  num howMany, {
+  Map<int, Message>? numberCases,
+  Map<int, Message>? wordCases,
+  Message? few,
+  Message? many,
+  required Message other,
+  String? locale,
+}) {
+  return old_intl.Intl.pluralLogic(
+    howMany,
+    few: few,
+    many: many,
+    zero: numberCases?[0] ?? wordCases?[0],
+    one: numberCases?[1] ?? wordCases?[1],
+    two: numberCases?[2] ?? wordCases?[2],
+    other: other,
+    locale: locale,
+  );
+}
+
 void main() {
   final uniqueKey = AssetId('package', 'path');
   test('generateMessageFile from Object json', () {
@@ -24,7 +45,7 @@
         .serialize('', '', messageList.map((e) => e.message).toList())
         .data;
     final messages =
-        JsonDeserializer(buffer).deserialize(const OldIntlObject()).messages;
+        JsonDeserializer(buffer).deserialize(intlPluralSelector).messages;
     expect((messages[0] as StringMessage).value, message.value);
   });
 
@@ -38,7 +59,7 @@
         .serialize('', '', parsed.messages.map((e) => e.message).toList())
         .data;
     final messages =
-        JsonDeserializer(buffer).deserialize(const OldIntlObject()).messages;
+        JsonDeserializer(buffer).deserialize(intlPluralSelector).messages;
     expect((messages[0] as StringMessage).value, 'Hello World');
   });
   test('generateMessageFile from simple arb JSON with placeholder', () {
@@ -51,7 +72,7 @@
         .serialize('', '', parsed.messages.map((e) => e.message).toList())
         .data;
     final messages =
-        JsonDeserializer(buffer).deserialize(const OldIntlObject()).messages;
+        JsonDeserializer(buffer).deserialize(intlPluralSelector).messages;
     expect((messages[0] as StringMessage).value, 'Hello ');
     expect(
       (messages[0] as StringMessage).argPositions,
@@ -68,7 +89,7 @@
         .serialize('', '', parsed.messages.map((e) => e.message).toList())
         .data;
     final messages =
-        JsonDeserializer(buffer).deserialize(const OldIntlObject()).messages;
+        JsonDeserializer(buffer).deserialize(intlPluralSelector).messages;
     expect((messages[0] as StringMessage).value, '');
     expect(
       (messages[0] as StringMessage).argPositions,
@@ -87,11 +108,11 @@
         .serialize('', '', parsed.messages.map((e) => e.message).toList())
         .data;
     final messages =
-        JsonDeserializer(buffer).deserialize(const OldIntlObject()).messages;
+        JsonDeserializer(buffer).deserialize(intlPluralSelector).messages;
     expect(
         messages[2].generateString(
           ['female', 'b'],
-          intl: const OldIntlObject(),
+          pluralSelector: intlPluralSelector,
         ),
         'test One new message');
   });
diff --git a/pkgs/messages_serializer/CHANGELOG.md b/pkgs/messages_serializer/CHANGELOG.md
index a0712a7..47f0d5d 100644
--- a/pkgs/messages_serializer/CHANGELOG.md
+++ b/pkgs/messages_serializer/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.2.0
+
+- Switch plural encoding to map.
+
 ## 0.1.0
 
 - Initial version.
diff --git a/pkgs/messages_serializer/lib/src/serializer_json.dart b/pkgs/messages_serializer/lib/src/serializer_json.dart
index 2b468af..a1bb1a5 100644
--- a/pkgs/messages_serializer/lib/src/serializer_json.dart
+++ b/pkgs/messages_serializer/lib/src/serializer_json.dart
@@ -59,8 +59,6 @@
       messageIndex = encodePlural(message, isVisible);
     } else if (message is CombinedMessage) {
       messageIndex = encodeCombined(message, isVisible);
-    } else if (message is GenderMessage) {
-      messageIndex = encodeGender(message, isVisible);
     } else {
       throw ArgumentError('Unknown message type');
     }
@@ -136,36 +134,21 @@
     m.add(encodeMessage(message.other));
     final caseIndices = <Object>[];
     if (message.few != null) {
-      caseIndices.add(Plural.few);
+      caseIndices.add(PluralMarker.few);
       caseIndices.add(encodeMessage(message.few!));
     }
     if (message.many != null) {
-      caseIndices.add(Plural.many);
+      caseIndices.add(PluralMarker.many);
       caseIndices.add(encodeMessage(message.many!));
     }
-    if (message.zeroNumber != null) {
-      caseIndices.add(Plural.zeroNumber);
-      caseIndices.add(encodeMessage(message.zeroNumber!));
+    for (final MapEntry(key: caseIndex, value: messageIndex)
+        in message.numberCases.entries) {
+      caseIndices.add(caseIndex);
+      caseIndices.add(encodeMessage(messageIndex));
     }
-    if (message.zeroWord != null) {
-      caseIndices.add(Plural.zeroWord);
-      caseIndices.add(encodeMessage(message.zeroWord!));
-    }
-    if (message.oneNumber != null) {
-      caseIndices.add(Plural.oneNumber);
-      caseIndices.add(encodeMessage(message.oneNumber!));
-    }
-    if (message.oneWord != null) {
-      caseIndices.add(Plural.oneWord);
-      caseIndices.add(encodeMessage(message.oneWord!));
-    }
-    if (message.twoNumber != null) {
-      caseIndices.add(Plural.twoNumber);
-      caseIndices.add(encodeMessage(message.twoNumber!));
-    }
-    if (message.twoWord != null) {
-      caseIndices.add(Plural.twoWord);
-      caseIndices.add(encodeMessage(message.twoWord!));
+    for (final entry in message.wordCases.entries) {
+      caseIndices.add(PluralMarker.wordCase + entry.key.toString());
+      caseIndices.add(encodeMessage(entry.value));
     }
     m.add(caseIndices);
     return m;
@@ -187,34 +170,6 @@
     return m;
   }
 
-  /// Encodes a gender message as follows:
-  ///
-  /// * int | the GenderMessage type
-  /// * if we write IDs: String | the message id
-  /// * int | the argument index on which the gender switches
-  /// * int | the index of the other case message, which must be present
-  /// * List\<int\> | the cases, which are added in pairs of two:
-  ///   * int | the case index as encoded by the constants in `Gender`
-  ///   * int | the message index of the case
-  List encodeGender(GenderMessage message, bool isVisible) {
-    final m = <Object>[];
-    m.add(GenderMessage.type);
-    addId(message, m, isVisible);
-    m.add(message.argIndex);
-    m.add(encodeMessage(message.other));
-    final caseIndices = <Object>[];
-    if (message.female != null) {
-      caseIndices.add(Gender.female);
-      caseIndices.add(encodeMessage(message.female!));
-    }
-    if (message.male != null) {
-      caseIndices.add(Gender.male);
-      caseIndices.add(encodeMessage(message.male!));
-    }
-    m.add(caseIndices);
-    return m;
-  }
-
   /// Add a non-null ID iff `writeIds` is enabled
   void addId(Message message, List<dynamic> m, bool isVisible) {
     if (writeIds && message.id != null && isVisible) m.add(message.id!);
diff --git a/pkgs/messages_serializer/pubspec.yaml b/pkgs/messages_serializer/pubspec.yaml
index 0fbb33d..0653fd1 100644
--- a/pkgs/messages_serializer/pubspec.yaml
+++ b/pkgs/messages_serializer/pubspec.yaml
@@ -1,6 +1,6 @@
 name: messages_serializer
 description: Serialization of messages for package:messages.
-version: 0.1.0
+version: 0.2.0
 repository: https://github.com/dart-lang/i18n/tree/main/pkgs/messages_serializer
 
 environment:
@@ -12,4 +12,5 @@
 
 dev_dependencies:
   dart_flutter_team_lints: ^2.1.1
+  intl: ^0.18.1
   test: ^1.21.0
diff --git a/pkgs/messages_serializer/test/messages_serializer_test.dart b/pkgs/messages_serializer/test/messages_serializer_test.dart
index a53e43b..b9ebee9 100644
--- a/pkgs/messages_serializer/test/messages_serializer_test.dart
+++ b/pkgs/messages_serializer/test/messages_serializer_test.dart
@@ -4,11 +4,32 @@
 
 import 'dart:math';
 
+import 'package:intl/intl.dart' as old_intl;
 import 'package:messages/messages_json.dart';
-import 'package:messages/package_intl_object.dart';
 import 'package:messages_serializer/messages_serializer.dart';
 import 'package:test/test.dart';
 
+Message intlPluralSelector(
+  num howMany, {
+  Map<int, Message>? numberCases,
+  Map<int, Message>? wordCases,
+  Message? few,
+  Message? many,
+  required Message other,
+  String? locale,
+}) {
+  return old_intl.Intl.pluralLogic(
+    howMany,
+    few: few,
+    many: many,
+    zero: numberCases?[0] ?? wordCases?[0],
+    one: numberCases?[1] ?? wordCases?[1],
+    two: numberCases?[2] ?? wordCases?[2],
+    other: other,
+    locale: locale,
+  );
+}
+
 StringMessage stringMessage = StringMessage('Hello World', id: 'hello_world');
 
 CombinedMessage combinedMessage = CombinedMessage('combined', [
@@ -20,8 +41,8 @@
   id: 'pluralMes',
   few: StringMessage('few case'),
   many: StringMessage('many case'),
-  oneNumber: StringMessage('oneNumber case'),
-  twoWord: StringMessage('twoWord case'),
+  numberCases: {1: StringMessage('oneNumber case')},
+  wordCases: {2: StringMessage('twoWord case')},
   other: StringMessage('Other case'),
   argIndex: 0,
 );
@@ -36,14 +57,6 @@
   'selectMes',
 );
 
-GenderMessage genderMessage = GenderMessage(
-  female: StringMessage('Female'),
-  male: StringMessage('Male'),
-  argIndex: 0,
-  other: StringMessage('other'),
-  id: 'genderMes',
-);
-
 void main() {
   test('Serialize with IDs', () {
     final messages = [
@@ -51,12 +64,11 @@
       combinedMessage,
       pluralMessage,
       selectMessage,
-      genderMessage
     ];
     final serialized =
         JsonSerializer(true).serialize('hash', 'locale', messages);
     final deserialize =
-        JsonDeserializer(serialized.data).deserialize(const OldIntlObject());
+        JsonDeserializer(serialized.data).deserialize(intlPluralSelector);
     expect(
       deserialize.messages.map((e) => e.id),
       orderedEquals(messages.map((e) => e.id)),
@@ -69,15 +81,14 @@
       combinedMessage,
       pluralMessage,
       selectMessage,
-      genderMessage
     ];
     final serialized =
-        JsonSerializer(true).serialize('hash', 'locale', messages, [1, 4]);
+        JsonSerializer(true).serialize('hash', 'locale', messages, [1, 3]);
     final deserialize =
-        JsonDeserializer(serialized.data).deserialize(const OldIntlObject());
+        JsonDeserializer(serialized.data).deserialize(intlPluralSelector);
     expect(
       deserialize.messages.map((e) => e.id),
-      orderedEquals([messages[1], messages[4]].map((e) => e.id)),
+      orderedEquals([messages[1], messages[3]].map((e) => e.id)),
     );
   });
 
@@ -89,7 +100,6 @@
         combinedMessage,
         pluralMessage,
         selectMessage,
-        genderMessage
       ]
     ];
     final params = [
@@ -116,7 +126,7 @@
   final serialized = serializer.serialize(hash, locale, messages);
 
   final deserializer = deserializerBuilder(serialized.data);
-  final deserialized = deserializer.deserialize(const OldIntlObject());
+  final deserialized = deserializer.deserialize(intlPluralSelector);
 
   expect(deserialized.preamble.hash, hash);
   expect(deserialized.preamble.locale, locale);
@@ -139,12 +149,19 @@
   if (original is StringMessage) {
     expect((deserialized as StringMessage).value, original.value);
   } else if (original is PluralMessage) {
-    compareMessage((deserialized as PluralMessage).zeroWord, original.zeroWord);
-    compareMessage(deserialized.zeroNumber, original.zeroNumber);
-    compareMessage(deserialized.oneWord, original.oneWord);
-    compareMessage(deserialized.oneNumber, original.oneNumber);
-    compareMessage(deserialized.twoWord, original.twoWord);
-    compareMessage(deserialized.twoNumber, original.twoNumber);
+    final deserialized2 = deserialized as PluralMessage;
+    for (final key in original.wordCases.keys) {
+      compareMessage(
+        deserialized2.wordCases[key],
+        original.wordCases[key],
+      );
+    }
+    for (final key in original.numberCases.keys) {
+      compareMessage(
+        deserialized.numberCases[key],
+        original.numberCases[key],
+      );
+    }
     compareMessage(deserialized.few, original.few);
     compareMessage(deserialized.many, original.many);
     compareMessage(deserialized.other, original.other);
diff --git a/pkgs/messages_shrinker/CHANGELOG.md b/pkgs/messages_shrinker/CHANGELOG.md
index a0712a7..19e1dfe 100644
--- a/pkgs/messages_shrinker/CHANGELOG.md
+++ b/pkgs/messages_shrinker/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.2.0
+
+- Adapt to `PluralRules` interface to retrieve the correct message for plurals.
+
 ## 0.1.0
 
 - Initial version.
diff --git a/pkgs/messages_shrinker/lib/messages_shrinker.dart b/pkgs/messages_shrinker/lib/messages_shrinker.dart
index 5703fd7..2099b3e 100644
--- a/pkgs/messages_shrinker/lib/messages_shrinker.dart
+++ b/pkgs/messages_shrinker/lib/messages_shrinker.dart
@@ -6,7 +6,6 @@
 import 'dart:io';
 
 import 'package:messages/messages_json.dart';
-import 'package:messages/package_intl_object.dart';
 import 'package:messages_serializer/messages_serializer.dart';
 
 class MessageShrinker {
@@ -33,7 +32,12 @@
   /// message indices in [messagesToKeep].
   String shrinkJson(String buffer, List<int> messagesToKeep) {
     final sizeBefore = buffer.length;
-    final json = JsonDeserializer(buffer).deserialize(const OldIntlObject());
+    final json = JsonDeserializer(buffer).deserialize(
+      (howMany, {few, locale, many, numberCases, required other, wordCases}) {
+        throw StateError('As the deserialized MessageList is not used, but '
+            'just immediately reserialized, this selector will not be called.');
+      },
+    );
     final data = JsonSerializer(json.preamble.hasIds)
         .serialize(
           json.preamble.hash,
diff --git a/pkgs/messages_shrinker/pubspec.yaml b/pkgs/messages_shrinker/pubspec.yaml
index cfb73ad..088558e 100644
--- a/pkgs/messages_shrinker/pubspec.yaml
+++ b/pkgs/messages_shrinker/pubspec.yaml
@@ -1,6 +1,6 @@
 name: messages_shrinker
 description: A starting point for Dart libraries or applications.
-version: 0.1.0
+version: 0.2.0
 repository: https://github.com/dart-lang/i18n/pkgs/messages_shrinker
 
 environment:
@@ -17,4 +17,5 @@
 
 dev_dependencies:
   dart_flutter_team_lints: ^2.0.0
+  intl: ^0.18.1
   test: ^1.21.0
diff --git a/pkgs/messages_shrinker/test/message_shrinker_test.dart b/pkgs/messages_shrinker/test/message_shrinker_test.dart
index 276ddb5..5c8f76f 100644
--- a/pkgs/messages_shrinker/test/message_shrinker_test.dart
+++ b/pkgs/messages_shrinker/test/message_shrinker_test.dart
@@ -9,15 +9,36 @@
 import 'dart:io';
 
 import 'package:build/build.dart';
+import 'package:intl/intl.dart' as old_intl;
 import 'package:messages/messages_json.dart';
-import 'package:messages/package_intl_object.dart';
 import 'package:messages_builder/arb_parser.dart';
 import 'package:messages_serializer/messages_serializer.dart';
 import 'package:messages_shrinker/messages_shrinker.dart';
 import 'package:test/test.dart';
 
+Message intlPluralSelector(
+  num howMany, {
+  Map<int, Message>? numberCases,
+  Map<int, Message>? wordCases,
+  Message? few,
+  Message? many,
+  required Message other,
+  String? locale,
+}) {
+  return old_intl.Intl.pluralLogic(
+    howMany,
+    few: few,
+    many: many,
+    zero: numberCases?[0] ?? wordCases?[0],
+    one: numberCases?[1] ?? wordCases?[1],
+    two: numberCases?[2] ?? wordCases?[2],
+    other: other,
+    locale: locale,
+  );
+}
+
 void main() {
-  final intl = const OldIntlObject();
+  final intl = intlPluralSelector;
   late String dataFileContents;
   late String dataFile;