diff --git a/src/lib/stepper/public-api.ts b/src/lib/stepper/public-api.ts
index 3fa16f569ac..30208512423 100644
--- a/src/lib/stepper/public-api.ts
+++ b/src/lib/stepper/public-api.ts
@@ -11,4 +11,4 @@ export * from './step-label';
export * from './stepper';
export * from './stepper-button';
export * from './step-header';
-
+export * from './stepper-intl';
diff --git a/src/lib/stepper/step-header.html b/src/lib/stepper/step-header.html
index 580a382e4b2..e0115b4a0f5 100644
--- a/src/lib/stepper/step-header.html
+++ b/src/lib/stepper/step-header.html
@@ -15,6 +15,6 @@
{{label}}
-
Optional
+
{{_intl.optionalLabel}}
diff --git a/src/lib/stepper/step-header.ts b/src/lib/stepper/step-header.ts
index 90fdadcefb8..42f11b755ee 100644
--- a/src/lib/stepper/step-header.ts
+++ b/src/lib/stepper/step-header.ts
@@ -8,8 +8,18 @@
import {FocusMonitor} from '@angular/cdk/a11y';
import {coerceBooleanProperty, coerceNumberProperty} from '@angular/cdk/coercion';
-import {Component, Input, ViewEncapsulation, ElementRef, OnDestroy, Renderer2} from '@angular/core';
+import {
+ Component,
+ Input,
+ ViewEncapsulation,
+ ChangeDetectorRef,
+ OnDestroy,
+ ElementRef,
+ Renderer2,
+} from '@angular/core';
import {MatStepLabel} from './step-label';
+import {MatStepperIntl} from './stepper-intl';
+import {Subscription} from 'rxjs/Subscription';
@Component({
@@ -25,6 +35,8 @@ import {MatStepLabel} from './step-label';
preserveWhitespaces: false,
})
export class MatStepHeader implements OnDestroy {
+ private _intlSubscription: Subscription;
+
/** Icon for the given step. */
@Input() icon: string;
@@ -64,13 +76,17 @@ export class MatStepHeader implements OnDestroy {
private _optional: boolean;
constructor(
+ public _intl: MatStepperIntl,
private _focusMonitor: FocusMonitor,
private _element: ElementRef,
- renderer: Renderer2) {
+ renderer: Renderer2,
+ changeDetectorRef: ChangeDetectorRef) {
_focusMonitor.monitor(_element.nativeElement, renderer, true);
+ this._intlSubscription = _intl.changes.subscribe(() => changeDetectorRef.markForCheck());
}
ngOnDestroy() {
+ this._intlSubscription.unsubscribe();
this._focusMonitor.stopMonitoring(this._element.nativeElement);
}
diff --git a/src/lib/stepper/stepper-intl.ts b/src/lib/stepper/stepper-intl.ts
new file mode 100644
index 00000000000..2975cdb8006
--- /dev/null
+++ b/src/lib/stepper/stepper-intl.ts
@@ -0,0 +1,24 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {Injectable} from '@angular/core';
+import {Subject} from 'rxjs/Subject';
+
+
+/** Stepper data that is required for internationalization. */
+@Injectable()
+export class MatStepperIntl {
+ /**
+ * Stream that emits whenever the labels here are changed. Use this to notify
+ * components if the labels have changed after initialization.
+ */
+ changes: Subject = new Subject();
+
+ /** Label that is rendered below optional steps. */
+ optionalLabel = 'Optional';
+}
diff --git a/src/lib/stepper/stepper-module.ts b/src/lib/stepper/stepper-module.ts
index 02f8b4f6586..970e93b4509 100644
--- a/src/lib/stepper/stepper-module.ts
+++ b/src/lib/stepper/stepper-module.ts
@@ -18,6 +18,7 @@ import {MatStepHeader} from './step-header';
import {MatStepLabel} from './step-label';
import {MatHorizontalStepper, MatStep, MatStepper, MatVerticalStepper} from './stepper';
import {MatStepperNext, MatStepperPrevious} from './stepper-button';
+import {MatStepperIntl} from './stepper-intl';
@NgModule({
@@ -44,5 +45,6 @@ import {MatStepperNext, MatStepperPrevious} from './stepper-button';
],
declarations: [MatHorizontalStepper, MatVerticalStepper, MatStep, MatStepLabel, MatStepper,
MatStepperNext, MatStepperPrevious, MatStepHeader],
+ providers: [MatStepperIntl],
})
export class MatStepperModule {}
diff --git a/src/lib/stepper/stepper.md b/src/lib/stepper/stepper.md
index e41d89b68c3..808a25b769c 100644
--- a/src/lib/stepper/stepper.md
+++ b/src/lib/stepper/stepper.md
@@ -7,8 +7,8 @@ that drives a stepped workflow. Material stepper extends the CDK stepper and has
styling.
### Stepper variants
-There are two stepper components: `mat-horizontal-stepper` and `mat-vertical-stepper`. They
-can be used the same way. The only difference is the orientation of stepper.
+There are two stepper components: `mat-horizontal-stepper` and `mat-vertical-stepper`. They
+can be used the same way. The only difference is the orientation of stepper.
`mat-horizontal-stepper` selector can be used to create a horizontal stepper, and
`mat-vertical-stepper` can be used to create a vertical stepper. `mat-step` components need to be
placed inside either one of the two stepper components.
@@ -26,7 +26,7 @@ If a step's label is only text, then the `label` attribute can be used.
```
-For more complex labels, add a template with the `matStepLabel` directive inside the
+For more complex labels, add a template with the `matStepLabel` directive inside the
`mat-step`.
```html
@@ -49,14 +49,14 @@ There are two button directives to support navigation between different steps:
-
+
```
### Linear stepper
The `linear` attribute can be set on `mat-horizontal-stepper` and `mat-vertical-stepper` to create
a linear stepper that requires the user to complete previous steps before proceeding
to following steps. For each `mat-step`, the `stepControl` attribute can be set to the top level
-`AbstractControl` that is used to check the validity of the step.
+`AbstractControl` that is used to check the validity of the step.
There are two possible approaches. One is using a single form for stepper, and the other is
using a different form for each step.
@@ -64,7 +64,7 @@ using a different form for each step.
#### Using a single form
When using a single form for the stepper, `matStepperPrevious` and `matStepperNext` have to be
set to `type="button"` in order to prevent submission of the form before all steps
-are completed.
+are completed.
```html
```
@@ -106,11 +106,11 @@ are completed.
#### Optional step
If completion of a step in linear stepper is not required, then the `optional` attribute can be set
-on `mat-step`.
+on `mat-step`.
#### Editable step
By default, steps are editable, which means users can return to previously completed steps and
-edit their responses. `editable="true"` can be set on `mat-step` to change the default.
+edit their responses. `editable="true"` can be set on `mat-step` to change the default.
#### Completed step
By default, the `completed` attribute of a step returns `true` if the step is valid (in case of
@@ -124,6 +124,20 @@ this default `completed` behavior by setting the `completed` attribute as needed
- TAB: Focuses the next tabbable element
- TAB+SHIFT: Focuses the previous tabbable element
+### Localizing labels
+Labels used by the stepper are provided through `MatStepperIntl`. Localization of these messages
+can be done by providing a subclass with translated values in your application root module.
+
+```ts
+@NgModule({
+ imports: [MatStepperModule],
+ providers: [
+ {provide: MatStepperIntl, useClass: MyIntl},
+ ],
+})
+export class MyApp {}
+```
+
### Accessibility
The stepper is treated as a tabbed view for accessibility purposes, so it is given
`role="tablist"` by default. The header of step that can be clicked to select the step
@@ -131,4 +145,4 @@ is given `role="tab"`, and the content that can be expanded upon selection is gi
`role="tabpanel"`. `aria-selected` attribute of step header and `aria-expanded` attribute of
step content is automatically set based on step selection change.
-The stepper and each step should be given a meaningful label via `aria-label` or `aria-labelledby`.
\ No newline at end of file
+The stepper and each step should be given a meaningful label via `aria-label` or `aria-labelledby`.
diff --git a/src/lib/stepper/stepper.spec.ts b/src/lib/stepper/stepper.spec.ts
index 8f07d66f4ef..7413e001946 100644
--- a/src/lib/stepper/stepper.spec.ts
+++ b/src/lib/stepper/stepper.spec.ts
@@ -2,13 +2,14 @@ import {Directionality} from '@angular/cdk/bidi';
import {ENTER, LEFT_ARROW, RIGHT_ARROW, SPACE} from '@angular/cdk/keycodes';
import {dispatchKeyboardEvent} from '@angular/cdk/testing';
import {Component, DebugElement} from '@angular/core';
-import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {async, ComponentFixture, TestBed, inject} from '@angular/core/testing';
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
import {By} from '@angular/platform-browser';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {MatStepperModule} from './index';
import {MatHorizontalStepper, MatStep, MatStepper, MatVerticalStepper} from './stepper';
import {MatStepperNext, MatStepperPrevious} from './stepper-button';
+import {MatStepperIntl} from './stepper-intl';
const VALID_REGEX = /valid/;
@@ -95,6 +96,21 @@ describe('MatHorizontalStepper', () => {
it('should set done icon if step is not editable and is completed', () => {
assertCorrectStepIcon(fixture, false, 'done');
});
+
+ it('should re-render when the i18n labels change',
+ inject([MatStepperIntl], (intl: MatStepperIntl) => {
+ const header = fixture.debugElement.queryAll(By.css('mat-step-header'))[2].nativeElement;
+ const optionalLabel = header.querySelector('.mat-step-optional');
+
+ expect(optionalLabel).toBeTruthy();
+ expect(optionalLabel.textContent).toBe('Optional');
+
+ intl.optionalLabel = 'Valgfri';
+ intl.changes.next();
+ fixture.detectChanges();
+
+ expect(optionalLabel.textContent).toBe('Valgfri');
+ }));
});
describe('RTL', () => {
@@ -686,7 +702,7 @@ function assertCorrectStepIcon(fixture: ComponentFixture,
-
+
Content 3