localize a11y tab labels in the TabBar (#13714)

* localize a11y tab labels in the TabBar

* break import cycle

* test boilerplate

* fix German translation

* more test boilerplate fixes
diff --git a/packages/flutter/lib/src/material/material_localizations.dart b/packages/flutter/lib/src/material/material_localizations.dart
index 3053cea..846aa5e 100644
--- a/packages/flutter/lib/src/material/material_localizations.dart
+++ b/packages/flutter/lib/src/material/material_localizations.dart
@@ -85,6 +85,14 @@
   /// Title for the [PaginatedDataTable]'s "rows per page" footer.
   String get rowsPerPageTitle;
 
+  /// The accessibility label used on a tab in a [TabBar].
+  ///
+  /// This message describes the index of the selected tab and how many tabs
+  /// there are, e.g. 'Tab 1 of 2' in United States English.
+  ///
+  /// `tabIndex` and `tabCount` must be greater than or equal to one.
+  String tabLabel({int tabIndex, int tabCount});
+
   /// Title for the [PaginatedDataTable]'s selected row count header.
   String selectedRowCountTitle(int selectedRowCount);
 
@@ -518,6 +526,13 @@
   String get rowsPerPageTitle => 'Rows per page:';
 
   @override
+  String tabLabel({int tabIndex, int tabCount}) {
+    assert(tabIndex >= 1);
+    assert(tabCount >= 1);
+    return 'Tab $tabIndex of $tabCount';
+  }
+
+  @override
   String selectedRowCountTitle(int selectedRowCount) {
     switch (selectedRowCount) {
       case 0:
diff --git a/packages/flutter/lib/src/material/tabs.dart b/packages/flutter/lib/src/material/tabs.dart
index 5e13e10..7f2ba71 100644
--- a/packages/flutter/lib/src/material/tabs.dart
+++ b/packages/flutter/lib/src/material/tabs.dart
@@ -16,6 +16,7 @@
 import 'debug.dart';
 import 'ink_well.dart';
 import 'material.dart';
+import 'material_localizations.dart';
 import 'tab_controller.dart';
 import 'theme.dart';
 
@@ -747,6 +748,7 @@
 
   @override
   Widget build(BuildContext context) {
+    final MaterialLocalizations localizations = MaterialLocalizations.of(context);
     if (_controller.length == 0) {
       return new Container(
         height: _kTabHeight + widget.indicatorWeight,
@@ -811,8 +813,7 @@
               wrappedTabs[index],
               new Semantics(
                 selected: index == _currentIndex,
-                // TODO(goderbauer): I10N-ify
-                label: 'Tab ${index + 1} of $tabCount',
+                label: localizations.tabLabel(tabIndex: index + 1, tabCount: tabCount),
               ),
             ]
           ),
diff --git a/packages/flutter/test/material/app_bar_test.dart b/packages/flutter/test/material/app_bar_test.dart
index eeb35e9..e4b0875 100644
--- a/packages/flutter/test/material/app_bar_test.dart
+++ b/packages/flutter/test/material/app_bar_test.dart
@@ -8,33 +8,40 @@
 import 'package:flutter_test/flutter_test.dart';
 
 Widget buildSliverAppBarApp({ bool floating, bool pinned, double expandedHeight, bool snap: false }) {
-  return new Directionality(
-    textDirection: TextDirection.ltr,
-    child: new MediaQuery(
-      data: const MediaQueryData(),
-      child: new Scaffold(
-        body: new DefaultTabController(
-          length: 3,
-          child: new CustomScrollView(
-            primary: true,
-            slivers: <Widget>[
-              new SliverAppBar(
-                title: const Text('AppBar Title'),
-                floating: floating,
-                pinned: pinned,
-                expandedHeight: expandedHeight,
-                snap: snap,
-                bottom: new TabBar(
-                  tabs: <String>['A','B','C'].map((String t) => new Tab(text: 'TAB $t')).toList(),
+  return new Localizations(
+    locale: const Locale('en', 'US'),
+    delegates: <LocalizationsDelegate<dynamic>>[
+      DefaultMaterialLocalizations.delegate,
+      DefaultWidgetsLocalizations.delegate,
+    ],
+    child: new Directionality(
+      textDirection: TextDirection.ltr,
+      child: new MediaQuery(
+        data: const MediaQueryData(),
+        child: new Scaffold(
+          body: new DefaultTabController(
+            length: 3,
+            child: new CustomScrollView(
+              primary: true,
+              slivers: <Widget>[
+                new SliverAppBar(
+                  title: const Text('AppBar Title'),
+                  floating: floating,
+                  pinned: pinned,
+                  expandedHeight: expandedHeight,
+                  snap: snap,
+                  bottom: new TabBar(
+                    tabs: <String>['A','B','C'].map((String t) => new Tab(text: 'TAB $t')).toList(),
+                  ),
                 ),
-              ),
-              new SliverToBoxAdapter(
-                child: new Container(
-                  height: 1200.0,
-                  color: Colors.orange[400],
+                new SliverToBoxAdapter(
+                  child: new Container(
+                    height: 1200.0,
+                    color: Colors.orange[400],
+                  ),
                 ),
-              ),
-            ],
+              ],
+            ),
           ),
         ),
       ),
diff --git a/packages/flutter/test/material/tabs_test.dart b/packages/flutter/test/material/tabs_test.dart
index cd8a3f1..bbcb215 100644
--- a/packages/flutter/test/material/tabs_test.dart
+++ b/packages/flutter/test/material/tabs_test.dart
@@ -15,10 +15,17 @@
 import '../widgets/semantics_tester.dart';
 
 Widget boilerplate({ Widget child, TextDirection textDirection: TextDirection.ltr }) {
-  return new Directionality(
-    textDirection: textDirection,
-    child: new Material(
-      child: child,
+  return new Localizations(
+    locale: const Locale('en', 'US'),
+    delegates: <LocalizationsDelegate<dynamic>>[
+      DefaultMaterialLocalizations.delegate,
+      DefaultWidgetsLocalizations.delegate,
+    ],
+    child: new Directionality(
+      textDirection: textDirection,
+      child: new Material(
+        child: child,
+      ),
     ),
   );
 }
diff --git a/packages/flutter/test/widgets/nested_scroll_view_test.dart b/packages/flutter/test/widgets/nested_scroll_view_test.dart
index ec4fe92..553e98d 100644
--- a/packages/flutter/test/widgets/nested_scroll_view_test.dart
+++ b/packages/flutter/test/widgets/nested_scroll_view_test.dart
@@ -21,75 +21,82 @@
 }
 
 Widget buildTest({ ScrollController controller, String title:'TTTTTTTT' }) {
-  return new Directionality(
-    textDirection: TextDirection.ltr,
-    child: new MediaQuery(
-      data: const MediaQueryData(),
-      child: new Scaffold(
-        body: new DefaultTabController(
-          length: 4,
-          child: new NestedScrollView(
-            controller: controller,
-            headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
-              return <Widget>[
-                new SliverAppBar(
-                  title: new Text(title),
-                  pinned: true,
-                  expandedHeight: 200.0,
-                  forceElevated: innerBoxIsScrolled,
-                  bottom: const TabBar(
-                    tabs: const <Tab>[
-                      const Tab(text: 'AA'),
-                      const Tab(text: 'BB'),
-                      const Tab(text: 'CC'),
-                      const Tab(text: 'DD'),
+  return new Localizations(
+    locale: const Locale('en', 'US'),
+    delegates: <LocalizationsDelegate<dynamic>>[
+      DefaultMaterialLocalizations.delegate,
+      DefaultWidgetsLocalizations.delegate,
+    ],
+    child: new Directionality(
+      textDirection: TextDirection.ltr,
+      child: new MediaQuery(
+        data: const MediaQueryData(),
+        child: new Scaffold(
+          body: new DefaultTabController(
+            length: 4,
+            child: new NestedScrollView(
+              controller: controller,
+              headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
+                return <Widget>[
+                  new SliverAppBar(
+                    title: new Text(title),
+                    pinned: true,
+                    expandedHeight: 200.0,
+                    forceElevated: innerBoxIsScrolled,
+                    bottom: const TabBar(
+                      tabs: const <Tab>[
+                        const Tab(text: 'AA'),
+                        const Tab(text: 'BB'),
+                        const Tab(text: 'CC'),
+                        const Tab(text: 'DD'),
+                      ],
+                    ),
+                  ),
+                ];
+              },
+              body: new TabBarView(
+                children: <Widget>[
+                  new ListView(
+                    children: <Widget>[
+                      new Container(
+                        height: 300.0,
+                        child: const Text('aaa1'),
+                      ),
+                      new Container(
+                        height: 200.0,
+                        child: const Text('aaa2'),
+                      ),
+                      new Container(
+                        height: 100.0,
+                        child: const Text('aaa3'),
+                      ),
+                      new Container(
+                        height: 50.0,
+                        child: const Text('aaa4'),
+                      ),
                     ],
                   ),
-                ),
-              ];
-            },
-            body: new TabBarView(
-              children: <Widget>[
-                new ListView(
-                  children: <Widget>[
-                    new Container(
-                      height: 300.0,
-                      child: const Text('aaa1'),
-                    ),
-                    new Container(
-                      height: 200.0,
-                      child: const Text('aaa2'),
-                    ),
-                    new Container(
-                      height: 100.0,
-                      child: const Text('aaa3'),
-                    ),
-                    new Container(
-                      height: 50.0,
-                      child: const Text('aaa4'),
-                    ),
-                  ],
-                ),
-                new ListView(
-                  children: <Widget>[
-                    new Container(
-                      height: 100.0,
-                      child: const Text('bbb1'),
-                    ),
-                  ],
-                ),
-                new Container(
-                  child: const Center(child: const Text('ccc1')),
-                ),
-                new ListView(
-                  children: <Widget>[
-                    new Container(
-                      height: 10000.0,
-                      child: const Text('ddd1'),
-                    ),
-                  ],
-                ),
-              ],
+                  new ListView(
+                    children: <Widget>[
+                      new Container(
+                        height: 100.0,
+                        child: const Text('bbb1'),
+                      ),
+                    ],
+                  ),
+                  new Container(
+                    child: const Center(child: const Text('ccc1')),
+                  ),
+                  new ListView(
+                    children: <Widget>[
+                      new Container(
+                        height: 10000.0,
+                        child: const Text('ddd1'),
+                      ),
+                    ],
+                  ),
+                ],
+              ),
             ),
           ),
         ),
diff --git a/packages/flutter_localizations/lib/src/l10n/localizations.dart b/packages/flutter_localizations/lib/src/l10n/localizations.dart
index e720c6d..6720d49 100644
--- a/packages/flutter_localizations/lib/src/l10n/localizations.dart
+++ b/packages/flutter_localizations/lib/src/l10n/localizations.dart
@@ -39,6 +39,7 @@
   String get pageRowsInfoTitle => parent?.pageRowsInfoTitle;
   String get pageRowsInfoTitleApproximate => parent?.pageRowsInfoTitleApproximate;
   String get rowsPerPageTitle => parent?.rowsPerPageTitle;
+  String get tabLabel => parent?.tabLabel;
   String get selectedRowCountTitleOther => parent?.selectedRowCountTitleOther;
   String get cancelButtonLabel => parent?.cancelButtonLabel;
   String get closeButtonLabel => parent?.closeButtonLabel;
@@ -83,6 +84,7 @@
   @override String get pageRowsInfoTitle => r'من $firstRow إلى $lastRow من إجمالي $rowCount';
   @override String get pageRowsInfoTitleApproximate => r'من $firstRow إلى $lastRow من إجمالي $rowCount تقريبًا';
   @override String get rowsPerPageTitle => r'عدد الصفوف في الصفحة:';
+  @override String get tabLabel => r'من $tabIndex من إجمالي $tabCount';
   @override String get selectedRowCountTitleOther => r'تم اختيار $selectedRowCount عنصر';
   @override String get cancelButtonLabel => r'إلغاء';
   @override String get closeButtonLabel => r'إغلاق';
@@ -122,6 +124,7 @@
   @override String get pageRowsInfoTitle => r'$firstRow–$lastRow von $rowCount';
   @override String get pageRowsInfoTitleApproximate => r'$firstRow–$lastRow von etwa $rowCount';
   @override String get rowsPerPageTitle => r'Zeilen pro Seite:';
+  @override String get tabLabel => r'Tabulator $tabIndex von $tabCount';
   @override String get selectedRowCountTitleZero => r'Keine Objekte ausgewählt';
   @override String get selectedRowCountTitleOne => r'1 Element ausgewählt';
   @override String get selectedRowCountTitleOther => r'$selectedRowCount Elemente ausgewählt';
@@ -163,6 +166,7 @@
   @override String get pageRowsInfoTitle => r'$firstRow–$lastRow of $rowCount';
   @override String get pageRowsInfoTitleApproximate => r'$firstRow–$lastRow of about $rowCount';
   @override String get rowsPerPageTitle => r'Rows per page:';
+  @override String get tabLabel => r'Tab $tabIndex of $tabCount';
   @override String get selectedRowCountTitleZero => r'No items selected';
   @override String get selectedRowCountTitleOne => r'1 item selected';
   @override String get selectedRowCountTitleOther => r'$selectedRowCount items selected';
@@ -204,6 +208,7 @@
   @override String get pageRowsInfoTitle => r'$firstRow‑$lastRow de $rowCount';
   @override String get pageRowsInfoTitleApproximate => r'$firstRow‑$lastRow de aproximadamente $rowCount';
   @override String get rowsPerPageTitle => r'Filas por página:';
+  @override String get tabLabel => r'$tabIndex de $tabCount';
   @override String get selectedRowCountTitleZero => r'No se han seleccionado elementos';
   @override String get selectedRowCountTitleOne => r'1 elemento seleccionado';
   @override String get selectedRowCountTitleOther => r'$selectedRowCount elementos seleccionados';
@@ -246,6 +251,7 @@
   @override String get pageRowsInfoTitle => r'$firstRow–$lastRow از $rowCount';
   @override String get pageRowsInfoTitleApproximate => r'$firstRow–$lastRow از حدود $rowCount';
   @override String get rowsPerPageTitle => r'ردیف در هر صفحه:';
+  @override String get tabLabel => r'$tabIndex از $tabCount';
   @override String get selectedRowCountTitleOther => r'$selectedRowCount مورد انتخاب شدند';
   @override String get cancelButtonLabel => r'لغو';
   @override String get closeButtonLabel => r'بستن';
@@ -285,6 +291,7 @@
   @override String get pageRowsInfoTitle => r'$firstRow – $lastRow sur $rowCount';
   @override String get pageRowsInfoTitleApproximate => r'$firstRow – $lastRow sur environ $rowCount';
   @override String get rowsPerPageTitle => r'Lignes par page :';
+  @override String get tabLabel => r'$tabIndex sur $tabCount';
   @override String get selectedRowCountTitleZero => r'Aucun élément sélectionné';
   @override String get selectedRowCountTitleOne => r'1 élément sélectionné';
   @override String get selectedRowCountTitleOther => r'$selectedRowCount éléments sélectionnés';
@@ -366,6 +373,7 @@
   @override String get pageRowsInfoTitle => r'$lastRow–$firstRow מתוך $rowCount';
   @override String get pageRowsInfoTitleApproximate => r'$lastRow–$firstRow מתוך כ-$rowCount';
   @override String get rowsPerPageTitle => r'שורות בכל דף:';
+  @override String get tabLabel => r'$tabIndex מתוך $tabCount';
   @override String get selectedRowCountTitleOther => r'$selectedRowCount פריטים נבחרו';
   @override String get cancelButtonLabel => r'ביטול';
   @override String get closeButtonLabel => r'סגירה';
@@ -406,6 +414,7 @@
   @override String get pageRowsInfoTitle => r'$firstRow-$lastRow di $rowCount';
   @override String get pageRowsInfoTitleApproximate => r'$firstRow-$lastRow di circa $rowCount';
   @override String get rowsPerPageTitle => r'Righe per pagina:';
+  @override String get tabLabel => r'$tabIndex di $tabCount';
   @override String get selectedRowCountTitleOther => r'$selectedRowCount elementi selezionati';
   @override String get cancelButtonLabel => r'ANNULLA';
   @override String get closeButtonLabel => r'CHIUDI';
@@ -446,6 +455,7 @@
   @override String get pageRowsInfoTitle => r'$firstRow - $lastRow 行(合計 $rowCount 行)';
   @override String get pageRowsInfoTitleApproximate => r'$firstRow – $lastRow 行(合計約 $rowCount 行)';
   @override String get rowsPerPageTitle => r'ページあたりの行数:';
+  @override String get tabLabel => r'$tabIndex 行(合計 $tabCount 行)';
   @override String get selectedRowCountTitleOther => r'$selectedRowCount 件のアイテムを選択中';
   @override String get cancelButtonLabel => r'キャンセル';
   @override String get closeButtonLabel => r'閉じる';
@@ -485,6 +495,7 @@
   @override String get pageRowsInfoTitle => r'$rowCount행 중 $firstRow~$lastRow행';
   @override String get pageRowsInfoTitleApproximate => r'약 $rowCount행 중 $firstRow~$lastRow행';
   @override String get rowsPerPageTitle => r'페이지당 행 수:';
+  @override String get tabLabel => r'$tabCount행 중 $tabIndex행';
   @override String get selectedRowCountTitleOne => r'항목 1개 선택됨';
   @override String get selectedRowCountTitleOther => r'항목 $selectedRowCount개 선택됨';
   @override String get cancelButtonLabel => r'취소';
@@ -525,6 +536,7 @@
   @override String get pageRowsInfoTitle => r'$firstRow-$lastRow van $rowCount';
   @override String get pageRowsInfoTitleApproximate => r'$firstRow-$lastRow van ongeveer $rowCount';
   @override String get rowsPerPageTitle => r'Rijen per pagina:';
+  @override String get tabLabel => r'$tabIndex van $tabCount';
   @override String get selectedRowCountTitleOne => r'1 item geselecteerd';
   @override String get selectedRowCountTitleOther => r'$selectedRowCount items geselecteerd';
   @override String get cancelButtonLabel => r'ANNULEREN';
@@ -567,6 +579,7 @@
   @override String get pageRowsInfoTitle => r'$firstRow–$lastRow z $rowCount';
   @override String get pageRowsInfoTitleApproximate => r'$firstRow–$lastRow z około $rowCount';
   @override String get rowsPerPageTitle => r'Wiersze na stronie:';
+  @override String get tabLabel => r'$tabIndex z $tabCount';
   @override String get selectedRowCountTitleOne => r'1 wybrany element';
   @override String get selectedRowCountTitleOther => r'$selectedRowCount wybranego elementu';
   @override String get cancelButtonLabel => r'ANULUJ';
@@ -607,6 +620,7 @@
   @override String get pageRowsInfoTitle => r'$firstRow–$lastRow د $rowCount';
   @override String get pageRowsInfoTitleApproximate => r'$firstRow–$lastRow څخه $rowCount د';
   @override String get rowsPerPageTitle => r'د هرې پاڼې پاڼې:';
+  @override String get tabLabel => r'$tabIndex د $tabCount';
   @override String get selectedRowCountTitleOther => r'$selectedRowCount توکي غوره شوي';
   @override String get cancelButtonLabel => r'لغوه کول';
   @override String get closeButtonLabel => r'تړل';
@@ -647,6 +661,7 @@
   @override String get pageRowsInfoTitle => r'$firstRow – $lastRow de $rowCount';
   @override String get pageRowsInfoTitleApproximate => r'$firstRow – $lastRow de aproximadamente $rowCount';
   @override String get rowsPerPageTitle => r'Linhas por página:';
+  @override String get tabLabel => r'$tabIndex de $tabCount';
   @override String get selectedRowCountTitleOther => r'$selectedRowCount itens selecionados';
   @override String get cancelButtonLabel => r'CANCELAR';
   @override String get closeButtonLabel => r'FECHAR';
@@ -684,6 +699,7 @@
   @override String get pageRowsInfoTitle => r'$firstRow–$lastRow из $rowCount';
   @override String get pageRowsInfoTitleApproximate => r'$firstRow–$lastRow из примерно $rowCount';
   @override String get rowsPerPageTitle => r'Строк на странице:';
+  @override String get tabLabel => r'Закладка $tabIndex из $tabCount';
   @override String get aboutListTileTitle => r'$applicationName: сведения';
   @override String get licensesPageTitle => r'Лицензии';
   @override String get selectedRowCountTitleZero => r'Строки не выбраны';
@@ -727,6 +743,7 @@
   @override String get pageRowsInfoTitle => r'$firstRow-$lastRow จาก $rowCount';
   @override String get pageRowsInfoTitleApproximate => r'$firstRow–$lastRow จากประมาณ $rowCount';
   @override String get rowsPerPageTitle => r'แถวต่อหน้า:';
+  @override String get tabLabel => r'$tabIndex จาก $tabCount';
   @override String get selectedRowCountTitleOne => r'เลือกแล้ว 1 รายการ';
   @override String get selectedRowCountTitleOther => r'เลือกแล้ว $selectedRowCount รายการ';
   @override String get cancelButtonLabel => r'ยกเลิก';
@@ -767,6 +784,7 @@
   @override String get pageRowsInfoTitle => r'$firstRow-$lastRow / $rowCount';
   @override String get pageRowsInfoTitleApproximate => r'$firstRow-$lastRow / $rowCount';
   @override String get rowsPerPageTitle => r'Sayfa başına satır sayısı:';
+  @override String get tabLabel => r'$tabIndex, $tabCount içinde';
   @override String get selectedRowCountTitleOne => r'1 öğe seçildi';
   @override String get selectedRowCountTitleOther => r'$selectedRowCount öğe seçildi';
   @override String get cancelButtonLabel => r'İPTAL';
@@ -808,6 +826,7 @@
   @override String get pageRowsInfoTitle => r'$firstRow–$lastRow از $rowCount';
   @override String get pageRowsInfoTitleApproximate => r'$firstRow–$lastRow $rowCount میں سے تقریباً';
   @override String get rowsPerPageTitle => r'قطاریں فی صفحہ:';
+  @override String get tabLabel => r'$tabIndex از $tabCount';
   @override String get selectedRowCountTitleOther => r'$selectedRowCount آئٹمز منتخب کیے گئے';
   @override String get cancelButtonLabel => r'منسوخ کریں';
   @override String get closeButtonLabel => r'بند کریں';
@@ -844,6 +863,7 @@
   @override String get pageRowsInfoTitle => r'第 $firstRow-$lastRow 行(共 $rowCount 行)';
   @override String get pageRowsInfoTitleApproximate => r'第 $firstRow-$lastRow 行(共约 $rowCount 行)';
   @override String get rowsPerPageTitle => r'每页行数:';
+  @override String get tabLabel => r'第 $tabIndex 行(共 $tabCount 行)';
   @override String get selectedRowCountTitleOther => r'已选择 $selectedRowCount 项内容';
   @override String get cancelButtonLabel => r'取消';
   @override String get continueButtonLabel => r'继续';
diff --git a/packages/flutter_localizations/lib/src/l10n/material_ar.arb b/packages/flutter_localizations/lib/src/l10n/material_ar.arb
index c86ee7f..fc3d01a 100644
--- a/packages/flutter_localizations/lib/src/l10n/material_ar.arb
+++ b/packages/flutter_localizations/lib/src/l10n/material_ar.arb
@@ -20,6 +20,7 @@
   "pageRowsInfoTitle": "من $firstRow إلى $lastRow من إجمالي $rowCount",
   "pageRowsInfoTitleApproximate": "من $firstRow إلى $lastRow من إجمالي $rowCount تقريبًا",
   "rowsPerPageTitle": "عدد الصفوف في الصفحة:",
+  "tabLabel": "من $tabIndex من إجمالي $tabCount",
   "selectedRowCountTitleOther": "تم اختيار $selectedRowCount عنصر",
   "cancelButtonLabel": "إلغاء",
   "closeButtonLabel": "إغلاق",
diff --git a/packages/flutter_localizations/lib/src/l10n/material_de.arb b/packages/flutter_localizations/lib/src/l10n/material_de.arb
index cc0916c..47de0d4 100644
--- a/packages/flutter_localizations/lib/src/l10n/material_de.arb
+++ b/packages/flutter_localizations/lib/src/l10n/material_de.arb
@@ -15,6 +15,7 @@
   "pageRowsInfoTitle": "$firstRow–$lastRow von $rowCount",
   "pageRowsInfoTitleApproximate": "$firstRow–$lastRow von etwa $rowCount",
   "rowsPerPageTitle": "Zeilen pro Seite:",
+  "tabLabel": "Tabulator $tabIndex von $tabCount",
   "selectedRowCountTitleZero": "Keine Objekte ausgewählt",
   "selectedRowCountTitleOne": "1 Element ausgewählt",
   "selectedRowCountTitleOther": "$selectedRowCount Elemente ausgewählt",
diff --git a/packages/flutter_localizations/lib/src/l10n/material_en.arb b/packages/flutter_localizations/lib/src/l10n/material_en.arb
index 41e3e43..86eaf36 100644
--- a/packages/flutter_localizations/lib/src/l10n/material_en.arb
+++ b/packages/flutter_localizations/lib/src/l10n/material_en.arb
@@ -82,6 +82,12 @@
     "description": "The caption for the drop-down button on a paginated data table's footer that allows the user to control the number of rows of data per page of the table."
   },
 
+  "tabLabel": "Tab $tabIndex of $tabCount",
+  "@tabLabel": {
+    "description": "The accessibility label used on a tab. This message describes the index of the selected tab and how many tabs there are, e.g. 'Tab 1 of 2'. All values are greater than or equal to one.",
+    "parameters": "tabIndex, tabCount"
+  },
+
   "selectedRowCountTitleZero": "No items selected",
   "selectedRowCountTitleOne": "1 item selected",
   "selectedRowCountTitleOther": "$selectedRowCount items selected",
diff --git a/packages/flutter_localizations/lib/src/l10n/material_es.arb b/packages/flutter_localizations/lib/src/l10n/material_es.arb
index 2b9c8ab..9449711 100644
--- a/packages/flutter_localizations/lib/src/l10n/material_es.arb
+++ b/packages/flutter_localizations/lib/src/l10n/material_es.arb
@@ -15,6 +15,7 @@
   "pageRowsInfoTitle": "$firstRow‑$lastRow de $rowCount",
   "pageRowsInfoTitleApproximate": "$firstRow‑$lastRow de aproximadamente $rowCount",
   "rowsPerPageTitle": "Filas por página:",
+  "tabLabel": "$tabIndex de $tabCount",
   "selectedRowCountTitleZero": "No se han seleccionado elementos",
   "selectedRowCountTitleOne": "1 elemento seleccionado",
   "selectedRowCountTitleOther": "$selectedRowCount elementos seleccionados",
diff --git a/packages/flutter_localizations/lib/src/l10n/material_fa.arb b/packages/flutter_localizations/lib/src/l10n/material_fa.arb
index 84db5e5..af7bd1a 100644
--- a/packages/flutter_localizations/lib/src/l10n/material_fa.arb
+++ b/packages/flutter_localizations/lib/src/l10n/material_fa.arb
@@ -16,6 +16,7 @@
   "pageRowsInfoTitle": "$firstRow–$lastRow از $rowCount",
   "pageRowsInfoTitleApproximate": "$firstRow–$lastRow از حدود $rowCount",
   "rowsPerPageTitle": "ردیف در هر صفحه:",
+  "tabLabel": "$tabIndex از $tabCount",
   "selectedRowCountTitleOther": "$selectedRowCount مورد انتخاب شدند",
   "cancelButtonLabel": "لغو",
   "closeButtonLabel": "بستن",
diff --git a/packages/flutter_localizations/lib/src/l10n/material_fr.arb b/packages/flutter_localizations/lib/src/l10n/material_fr.arb
index 62203cb..6f93a36 100644
--- a/packages/flutter_localizations/lib/src/l10n/material_fr.arb
+++ b/packages/flutter_localizations/lib/src/l10n/material_fr.arb
@@ -15,6 +15,7 @@
   "pageRowsInfoTitle": "$firstRow – $lastRow sur $rowCount",
   "pageRowsInfoTitleApproximate": "$firstRow – $lastRow sur environ $rowCount",
   "rowsPerPageTitle": "Lignes par page :",
+  "tabLabel": "$tabIndex sur $tabCount",
   "selectedRowCountTitleZero": "Aucun élément sélectionné",
   "selectedRowCountTitleOne": "1 élément sélectionné",
   "selectedRowCountTitleOther": "$selectedRowCount éléments sélectionnés",
diff --git a/packages/flutter_localizations/lib/src/l10n/material_he.arb b/packages/flutter_localizations/lib/src/l10n/material_he.arb
index 014e0f5..4f65d73 100644
--- a/packages/flutter_localizations/lib/src/l10n/material_he.arb
+++ b/packages/flutter_localizations/lib/src/l10n/material_he.arb
@@ -18,6 +18,7 @@
   "pageRowsInfoTitle": "$lastRow–$firstRow מתוך $rowCount",
   "pageRowsInfoTitleApproximate": "$lastRow–$firstRow מתוך כ-$rowCount",
   "rowsPerPageTitle": "שורות בכל דף:",
+  "tabLabel": "$tabIndex מתוך $tabCount",
   "selectedRowCountTitleOther": "$selectedRowCount פריטים נבחרו",
   "cancelButtonLabel": "ביטול",
   "closeButtonLabel": "סגירה",
diff --git a/packages/flutter_localizations/lib/src/l10n/material_it.arb b/packages/flutter_localizations/lib/src/l10n/material_it.arb
index 7792e95..cff52dc 100644
--- a/packages/flutter_localizations/lib/src/l10n/material_it.arb
+++ b/packages/flutter_localizations/lib/src/l10n/material_it.arb
@@ -16,6 +16,7 @@
   "pageRowsInfoTitle": "$firstRow-$lastRow di $rowCount",
   "pageRowsInfoTitleApproximate": "$firstRow-$lastRow di circa $rowCount",
   "rowsPerPageTitle": "Righe per pagina:",
+  "tabLabel": "$tabIndex di $tabCount",
   "selectedRowCountTitleOther": "$selectedRowCount elementi selezionati",
   "cancelButtonLabel": "ANNULLA",
   "closeButtonLabel": "CHIUDI",
diff --git a/packages/flutter_localizations/lib/src/l10n/material_ja.arb b/packages/flutter_localizations/lib/src/l10n/material_ja.arb
index 3af4024..4816995 100644
--- a/packages/flutter_localizations/lib/src/l10n/material_ja.arb
+++ b/packages/flutter_localizations/lib/src/l10n/material_ja.arb
@@ -16,6 +16,7 @@
   "pageRowsInfoTitle": "$firstRow - $lastRow 行(合計 $rowCount 行)",
   "pageRowsInfoTitleApproximate": "$firstRow – $lastRow 行(合計約 $rowCount 行)",
   "rowsPerPageTitle": "ページあたりの行数:",
+  "tabLabel": "$tabIndex 行(合計 $tabCount 行)",
   "selectedRowCountTitleOther": "$selectedRowCount 件のアイテムを選択中",
   "cancelButtonLabel": "キャンセル",
   "closeButtonLabel": "閉じる",
diff --git a/packages/flutter_localizations/lib/src/l10n/material_ko.arb b/packages/flutter_localizations/lib/src/l10n/material_ko.arb
index 3671acc..e61c77f 100644
--- a/packages/flutter_localizations/lib/src/l10n/material_ko.arb
+++ b/packages/flutter_localizations/lib/src/l10n/material_ko.arb
@@ -15,6 +15,7 @@
   "pageRowsInfoTitle": "$rowCount행 중 $firstRow~$lastRow행",
   "pageRowsInfoTitleApproximate": "약 $rowCount행 중 $firstRow~$lastRow행",
   "rowsPerPageTitle": "페이지당 행 수:",
+  "tabLabel": "$tabCount행 중 $tabIndex행",
   "selectedRowCountTitleOne": "항목 1개 선택됨",
   "selectedRowCountTitleOther": "항목 $selectedRowCount개 선택됨",
   "cancelButtonLabel": "취소",
diff --git a/packages/flutter_localizations/lib/src/l10n/material_nl.arb b/packages/flutter_localizations/lib/src/l10n/material_nl.arb
index 5622fec..8028fc0 100644
--- a/packages/flutter_localizations/lib/src/l10n/material_nl.arb
+++ b/packages/flutter_localizations/lib/src/l10n/material_nl.arb
@@ -15,6 +15,7 @@
   "pageRowsInfoTitle": "$firstRow-$lastRow van $rowCount",
   "pageRowsInfoTitleApproximate": "$firstRow-$lastRow van ongeveer $rowCount",
   "rowsPerPageTitle": "Rijen per pagina:",
+  "tabLabel": "$tabIndex van $tabCount",
   "selectedRowCountTitleOne": "1 item geselecteerd",
   "selectedRowCountTitleOther": "$selectedRowCount items geselecteerd",
   "cancelButtonLabel": "ANNULEREN",
diff --git a/packages/flutter_localizations/lib/src/l10n/material_pl.arb b/packages/flutter_localizations/lib/src/l10n/material_pl.arb
index c4dd4b6..bf25f64 100644
--- a/packages/flutter_localizations/lib/src/l10n/material_pl.arb
+++ b/packages/flutter_localizations/lib/src/l10n/material_pl.arb
@@ -17,6 +17,7 @@
   "pageRowsInfoTitle": "$firstRow–$lastRow z $rowCount",
   "pageRowsInfoTitleApproximate": "$firstRow–$lastRow z około $rowCount",
   "rowsPerPageTitle": "Wiersze na stronie:",
+  "tabLabel": "$tabIndex z $tabCount",
   "selectedRowCountTitleOne": "1 wybrany element",
   "selectedRowCountTitleOther": "$selectedRowCount wybranego elementu",
   "cancelButtonLabel": "ANULUJ",
diff --git a/packages/flutter_localizations/lib/src/l10n/material_ps.arb b/packages/flutter_localizations/lib/src/l10n/material_ps.arb
index 9677310..030f7be 100644
--- a/packages/flutter_localizations/lib/src/l10n/material_ps.arb
+++ b/packages/flutter_localizations/lib/src/l10n/material_ps.arb
@@ -17,6 +17,7 @@
   "pageRowsInfoTitle": "$firstRow–$lastRow د $rowCount",
   "pageRowsInfoTitleApproximate": "$firstRow–$lastRow څخه $rowCount د",
   "rowsPerPageTitle": "د هرې پاڼې پاڼې:",
+  "tabLabel": "$tabIndex د $tabCount",
   "selectedRowCountTitleOther": "$selectedRowCount توکي غوره شوي",
   "cancelButtonLabel": "لغوه کول",
   "closeButtonLabel": "تړل",
diff --git a/packages/flutter_localizations/lib/src/l10n/material_pt.arb b/packages/flutter_localizations/lib/src/l10n/material_pt.arb
index 0d1ce12..0aee40c 100644
--- a/packages/flutter_localizations/lib/src/l10n/material_pt.arb
+++ b/packages/flutter_localizations/lib/src/l10n/material_pt.arb
@@ -20,6 +20,7 @@
   "pageRowsInfoTitle": "$firstRow – $lastRow de $rowCount",
   "pageRowsInfoTitleApproximate": "$firstRow – $lastRow de aproximadamente $rowCount",
   "rowsPerPageTitle": "Linhas por página:",
+  "tabLabel": "$tabIndex de $tabCount",
   "selectedRowCountTitleOther": "$selectedRowCount itens selecionados",
   "cancelButtonLabel": "CANCELAR",
   "closeButtonLabel": "FECHAR",
diff --git a/packages/flutter_localizations/lib/src/l10n/material_ru.arb b/packages/flutter_localizations/lib/src/l10n/material_ru.arb
index d91cc4a..31d0db3 100644
--- a/packages/flutter_localizations/lib/src/l10n/material_ru.arb
+++ b/packages/flutter_localizations/lib/src/l10n/material_ru.arb
@@ -15,6 +15,7 @@
   "pageRowsInfoTitle": "$firstRow–$lastRow из $rowCount",
   "pageRowsInfoTitleApproximate": "$firstRow–$lastRow из примерно $rowCount",
   "rowsPerPageTitle": "Строк на странице:",
+  "tabLabel": "Закладка $tabIndex из $tabCount",
   "aboutListTileTitle": "$applicationName: сведения",
   "licensesPageTitle": "Лицензии",
   "selectedRowCountTitleZero": "Строки не выбраны",
diff --git a/packages/flutter_localizations/lib/src/l10n/material_th.arb b/packages/flutter_localizations/lib/src/l10n/material_th.arb
index 3711759..e915fac 100644
--- a/packages/flutter_localizations/lib/src/l10n/material_th.arb
+++ b/packages/flutter_localizations/lib/src/l10n/material_th.arb
@@ -15,6 +15,7 @@
   "pageRowsInfoTitle": "$firstRow-$lastRow จาก $rowCount",
   "pageRowsInfoTitleApproximate": "$firstRow–$lastRow จากประมาณ $rowCount",
   "rowsPerPageTitle": "แถวต่อหน้า:",
+  "tabLabel": "$tabIndex จาก $tabCount",
   "selectedRowCountTitleOne": "เลือกแล้ว 1 รายการ",
   "selectedRowCountTitleOther": "เลือกแล้ว $selectedRowCount รายการ",
   "cancelButtonLabel": "ยกเลิก",
diff --git a/packages/flutter_localizations/lib/src/l10n/material_tr.arb b/packages/flutter_localizations/lib/src/l10n/material_tr.arb
index 5632770..6752332 100644
--- a/packages/flutter_localizations/lib/src/l10n/material_tr.arb
+++ b/packages/flutter_localizations/lib/src/l10n/material_tr.arb
@@ -15,6 +15,7 @@
   "pageRowsInfoTitle": "$firstRow-$lastRow / $rowCount",
   "pageRowsInfoTitleApproximate": "$firstRow-$lastRow / $rowCount",
   "rowsPerPageTitle": "Sayfa başına satır sayısı:",
+  "tabLabel": "$tabIndex, $tabCount içinde",
   "selectedRowCountTitleOne": "1 öğe seçildi",
   "selectedRowCountTitleOther": "$selectedRowCount öğe seçildi",
   "cancelButtonLabel": "İPTAL",
diff --git a/packages/flutter_localizations/lib/src/l10n/material_ur.arb b/packages/flutter_localizations/lib/src/l10n/material_ur.arb
index 0875ad3..3127c6c 100644
--- a/packages/flutter_localizations/lib/src/l10n/material_ur.arb
+++ b/packages/flutter_localizations/lib/src/l10n/material_ur.arb
@@ -16,6 +16,7 @@
   "pageRowsInfoTitle": "$firstRow–$lastRow از $rowCount",
   "pageRowsInfoTitleApproximate": "$firstRow–$lastRow $rowCount میں سے تقریباً",
   "rowsPerPageTitle": "قطاریں فی صفحہ:",
+  "tabLabel": "$tabIndex از $tabCount",
   "selectedRowCountTitleOther": "$selectedRowCount آئٹمز منتخب کیے گئے",
   "cancelButtonLabel": "منسوخ کریں",
   "closeButtonLabel": "بند کریں",
diff --git a/packages/flutter_localizations/lib/src/l10n/material_zh.arb b/packages/flutter_localizations/lib/src/l10n/material_zh.arb
index 19f2715..51d4e80 100644
--- a/packages/flutter_localizations/lib/src/l10n/material_zh.arb
+++ b/packages/flutter_localizations/lib/src/l10n/material_zh.arb
@@ -12,6 +12,7 @@
   "pageRowsInfoTitle": "第 $firstRow-$lastRow 行(共 $rowCount 行)",
   "pageRowsInfoTitleApproximate": "第 $firstRow-$lastRow 行(共约 $rowCount 行)",
   "rowsPerPageTitle": "每页行数:",
+  "tabLabel": "第 $tabIndex 行(共 $tabCount 行)",
   "selectedRowCountTitleOther": "已选择 $selectedRowCount 项内容",
   "cancelButtonLabel": "取消",
   "continueButtonLabel": "继续",
diff --git a/packages/flutter_localizations/lib/src/material_localizations.dart b/packages/flutter_localizations/lib/src/material_localizations.dart
index 095422b..d9f8eff 100644
--- a/packages/flutter_localizations/lib/src/material_localizations.dart
+++ b/packages/flutter_localizations/lib/src/material_localizations.dart
@@ -273,6 +273,16 @@
   String get rowsPerPageTitle => _translationBundle.rowsPerPageTitle;
 
   @override
+  String tabLabel({int tabIndex, int tabCount}) {
+    assert(tabIndex >= 1);
+    assert(tabCount >= 1);
+    final String template = _translationBundle.tabLabel;
+    return template
+      .replaceFirst(r'$tabIndex', formatDecimal(tabIndex))
+      .replaceFirst(r'tabCount', formatDecimal(tabCount));
+  }
+
+  @override
   String selectedRowCountTitle(int selectedRowCount) {
     // TODO(hmuller): the rules for mapping from an integer value to
     // "one" or "two" etc. are locale specific and an additional "few" category
diff --git a/packages/flutter_localizations/test/translations_test.dart b/packages/flutter_localizations/test/translations_test.dart
index c634a39..827a51a 100644
--- a/packages/flutter_localizations/test/translations_test.dart
+++ b/packages/flutter_localizations/test/translations_test.dart
@@ -61,19 +61,25 @@
       expect(localizations.selectedRowCountTitle(1), isNotNull);
       expect(localizations.selectedRowCountTitle(2), isNotNull);
       expect(localizations.selectedRowCountTitle(100), isNotNull);
-      expect(localizations.selectedRowCountTitle(0).contains(r'$selectedRowCount'), isFalse);
-      expect(localizations.selectedRowCountTitle(1).contains(r'$selectedRowCount'), isFalse);
-      expect(localizations.selectedRowCountTitle(2).contains(r'$selectedRowCount'), isFalse);
-      expect(localizations.selectedRowCountTitle(100).contains(r'$selectedRowCount'), isFalse);
+      expect(localizations.selectedRowCountTitle(0), isNot(contains(r'$selectedRowCount')));
+      expect(localizations.selectedRowCountTitle(1), isNot(contains(r'$selectedRowCount')));
+      expect(localizations.selectedRowCountTitle(2), isNot(contains(r'$selectedRowCount')));
+      expect(localizations.selectedRowCountTitle(100), isNot(contains(r'$selectedRowCount')));
 
       expect(localizations.pageRowsInfoTitle(1, 10, 100, true), isNotNull);
       expect(localizations.pageRowsInfoTitle(1, 10, 100, false), isNotNull);
-      expect(localizations.pageRowsInfoTitle(1, 10, 100, true).contains(r'$firstRow'), isFalse);
-      expect(localizations.pageRowsInfoTitle(1, 10, 100, true).contains(r'$lastRow'), isFalse);
-      expect(localizations.pageRowsInfoTitle(1, 10, 100, true).contains(r'$rowCount'), isFalse);
-      expect(localizations.pageRowsInfoTitle(1, 10, 100, false).contains(r'$firstRow'), isFalse);
-      expect(localizations.pageRowsInfoTitle(1, 10, 100, false).contains(r'$lastRow'), isFalse);
-      expect(localizations.pageRowsInfoTitle(1, 10, 100, false).contains(r'$rowCount'), isFalse);
+      expect(localizations.pageRowsInfoTitle(1, 10, 100, true), isNot(contains(r'$firstRow')));
+      expect(localizations.pageRowsInfoTitle(1, 10, 100, true), isNot(contains(r'$lastRow')));
+      expect(localizations.pageRowsInfoTitle(1, 10, 100, true), isNot(contains(r'$rowCount')));
+      expect(localizations.pageRowsInfoTitle(1, 10, 100, false), isNot(contains(r'$firstRow')));
+      expect(localizations.pageRowsInfoTitle(1, 10, 100, false), isNot(contains(r'$lastRow')));
+      expect(localizations.pageRowsInfoTitle(1, 10, 100, false), isNot(contains(r'$rowCount')));
+
+      expect(localizations.tabLabel(tabIndex: 2, tabCount: 5), isNotNull);
+      expect(localizations.tabLabel(tabIndex: 2, tabCount: 5), isNot(contains('tabIndex')));
+      expect(localizations.tabLabel(tabIndex: 2, tabCount: 5), isNot(contains('tabCount')));
+      expect(() => localizations.tabLabel(tabIndex: 0, tabCount: 5), throwsAssertionError);
+      expect(() => localizations.tabLabel(tabIndex: 2, tabCount: 0), throwsAssertionError);
     });
   }