[CrOS MultiDevice] Make strings in OOBE setup translate.

The crbug.com/945632 pointed out that the MultiDevice setup OOBE UI
showed an unfortunate behavior in other languages: only one string on
the page translated [1].

It turned out there were a few different problems masking one another.
First, the start-setup-page element did not bind its translations to
I18nBehavior.locale, which is the standard mechanism for passing
location data in OOBE. Once that was fixed, it became clear that the
OOBE screen 'MultiDeviceSetupScreen' was not notifying any Multidevice
Polymer elements of locale updates so the I18nBehavior.locale bindings
were not receiving updates. Also, because the buttons and headings were
translated in ui_page_container_behavior.js, those translations had to
be bound to I18nBehavior.locale separately. I also moved those
translations to more visible locations and used the literal string IDs
instead of factoring out the string IDs and computing the translations
in the UiPageContainerBehavior in an effort to make the data flow more
transparent.

The next big issue was that the strings with placeholders were not
registered appropriately in OOBE localization. This difference didn't
become clear until the binding issue was resolved because there was no
visible difference in the behavior of the placeholder strings and the
other strings until that point. Also, because the registration
infrastructure in OOBE uses a totally different API, the placeholder
strings were translating correctly post OOBE, which added to the
confusion. See multidevice_setup_localized_strings_provider.cc for the
relevant change.

After this change, the start-setup-page translates strings correctly in
OOBE [2] (this is the only MultiDevice screen in the OOBE flow) and
post-OOBE [3] along with the password-page [4] and setup-succeeded-page
[5].

Once this change is merged into the current branch (i.e. the immediate
fire is out), we can simplify
multidevice_setup_localized_string_provider.cc by splitting up the
strings with placeholders into regular strings and concatenatng them
manually in HTML (see crbug.com/961841).

[1] https://screenshot.googleplex.com/w2tWTTZJkvj
[2] https://screenshot.googleplex.com/dGvFOTQozLH
[3] https://screenshot.googleplex.com/9GuB8bwkqS4
[4] https://screenshot.googleplex.com/QGZc9aaEdtE
[5] https://screenshot.googleplex.com/FCAWDNTTVn4

Bug: 945632
Change-Id: I141e925e70550b1e33b06aef25ed4f72a06d5988
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1591965
Commit-Queue: Jordy Greenblatt <jordynass@chromium.org>
Reviewed-by: Kyle Horimoto <khorimoto@chromium.org>
Reviewed-by: Alexander Alekseev <alemate@chromium.org>
Cr-Commit-Position: refs/heads/master@{#662029}
diff --git a/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html b/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html
index 2316e51..a583b89 100644
--- a/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html
+++ b/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html
@@ -84,18 +84,19 @@
         min-height: 0;
       }
     </style>
-    <multidevice-setup delegate="[[delegate_]]"
+    <multidevice-setup id="multideviceSetup"
+        delegate="[[delegate_]]"
         on-setup-exited="onExitRequested_"
         on-forward-button-focus-requested="onForwardButtonFocusRequested_"
-        forward-button-text="{{forwardButtonText_}}"
+        forward-button-text-id="{{forwardButtonTextId_}}"
         forward-button-disabled="{{forwardButtonDisabled_}}"
-        cancel-button-text="{{cancelButtonText_}}">
+        cancel-button-text-id="{{cancelButtonTextId_}}">
       <oobe-text-button slot="cancel-button">
-        <div>[[cancelButtonText_]]</div>
+        <div>[[i18nDynamic(locale, cancelButtonTextId_)]]</div>
       </oobe-text-button>
-      <oobe-next-button id="next-button" slot="forward-button"
+      <oobe-next-button id="nextButton" slot="forward-button"
           disabled$="[[forwardButtonDisabled_]]">
-        <div>[[forwardButtonText_]]</div>
+        <div>[[i18nDynamic(locale, forwardButtonTextId_)]]</div>
       </oobe-next-button>
     </multidevice-setup>
 
diff --git a/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.js b/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.js
index c2660bf..a8696594 100644
--- a/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.js
+++ b/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.js
@@ -60,12 +60,11 @@
       delegate_: Object,
 
       /**
-       * Text to be shown on the forward navigation button.
+       * ID of loadTimeData string to be shown on the forward navigation button.
        * @private {string|undefined}
        */
-      forwardButtonText_: {
+      forwardButtonTextId_: {
         type: String,
-        value: '',
       },
 
       /**
@@ -78,12 +77,11 @@
       },
 
       /**
-       * Text to be shown on the cancel button.
+       * ID of loadTimeData string to be shown on the cancel button.
        * @private {string|undefined}
        */
-      cancelButtonText_: {
+      cancelButtonTextId_: {
         type: String,
-        value: '',
       },
 
       /** Whether the webview overlay should be hidden. */
@@ -111,8 +109,18 @@
       this.delegate_ = new MultiDeviceSetupFirstRunDelegate();
     },
 
+    /** @override */
+    ready: function() {
+      this.updateLocalizedContent();
+    },
+
+    updateLocalizedContent: function() {
+      this.i18nUpdateLocale();
+      this.$.multideviceSetup.updateLocalizedContent();
+    },
+
     onForwardButtonFocusRequested_: function() {
-      this.$$('#next-button').focus();
+      this.$.nextButton.focus();
     },
 
     /**
diff --git a/chrome/browser/resources/chromeos/login/screen_multidevice_setup.js b/chrome/browser/resources/chromeos/login/screen_multidevice_setup.js
index 626a1bf..d0f8cce 100644
--- a/chrome/browser/resources/chromeos/login/screen_multidevice_setup.js
+++ b/chrome/browser/resources/chromeos/login/screen_multidevice_setup.js
@@ -10,5 +10,12 @@
     get defaultControl() {
       return $('multidevice-setup-impl');
     },
+
+    /**
+     * This is called after resources are updated.
+     */
+    updateLocalizedContent: function() {
+      $('multidevice-setup-impl').updateLocalizedContent();
+    },
   };
 });
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html
index 7192cf2..64e541d 100644
--- a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html
+++ b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.html
@@ -3,6 +3,7 @@
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.html">
 <link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="post_oobe_delegate.html">
 
@@ -22,22 +23,22 @@
         on-setup-exited="onExitRequested_"
         on-forward-button-focus-requested="onForwardButtonFocusRequested_"
         on-visible-page-name-changed="onVisiblePageNameChanged_"
-        forward-button-text="{{forwardButtonText_}}"
+        forward-button-text-id="{{forwardButtonTextId_}}"
         forward-button-disabled="{{forwardButtonDisabled_}}"
-        cancel-button-text="{{cancelButtonText_}}"
-        backward-button-text="{{backwardButtonText_}}">
+        cancel-button-text-id="{{cancelButtonTextId_}}"
+        backward-button-text-id="{{backwardButtonTextId_}}">
       <paper-button id="backward-button"
           slot="backward-button" class="cancel-button">
-        [[backwardButtonText_]]
+        [[i18n(backwardButtonTextId_)]]
       </paper-button>
       <paper-button id="cancel-button"
           slot="cancel-button" class="cancel-button">
-        [[cancelButtonText_]]
+        [[i18n(cancelButtonTextId_)]]
       </paper-button>
       <paper-button id="forward-button"
           slot="forward-button" class="action-button"
           disabled$="[[forwardButtonDisabled_]]">
-        [[forwardButtonText_]]
+        [[i18n(forwardButtonTextId_)]]
       </paper-button>
     </multidevice-setup>
   </template>
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.js b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.js
index 25e6ea2..1df4336 100644
--- a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.js
+++ b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_post_oobe.js
@@ -33,12 +33,11 @@
     delegate_: Object,
 
     /**
-     * Text to be shown on the forward navigation button.
+     * ID of loadTimeData string to be shown on the forward navigation button.
      * @private {string|undefined}
      */
-    forwardButtonText: {
+    forwardButtonTextId_: {
       type: String,
-      value: '',
     },
 
     /**
@@ -51,24 +50,24 @@
     },
 
     /**
-     * Text to be shown on the cancel navigation button.
+     * ID of loadTimeData string to be shown on the cancel navigation button.
      * @private {string|undefined}
      */
-    cancelButtonText_: {
+    cancelButtonTextId_: {
       type: String,
-      value: '',
     },
 
     /**
-     * Text to be shown on the backward navigation button.
+     * ID of loadTimeData string to be shown on the backward navigation button.
      * @private {string|undefined}
      */
-    backwardButtonText_: {
+    backwardButtonTextId_: {
       type: String,
-      value: '',
     },
   },
 
+  behaviors: [I18nBehavior],
+
   /** @override */
   attached: function() {
     this.delegate_ = new multidevice_setup.PostOobeDelegate();
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_localized_strings_provider.cc b/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_localized_strings_provider.cc
index 7d3874c..7a95459 100644
--- a/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_localized_strings_provider.cc
@@ -76,6 +76,9 @@
       localized_strings([] {
         std::vector<LocalizedStringWithName> localized_strings;
 
+        // TODO(crbug.com/964547): Refactor so that any change to these strings
+        // will surface in both the OOBE and post-OOBE UIs without having to
+        // adjust both localization calls separately.
         localized_strings.emplace_back(
             "startSetupPageMessage",
             l10n_util::GetStringFUTF16(
@@ -120,8 +123,26 @@
   for (const auto& entry : kLocalizedStringsWithoutPlaceholders)
     builder->Add(entry.name, entry.id);
 
-  for (const auto& entry : GetLocalizedStringsWithPlaceholders())
-    builder->Add(entry.name, entry.localized_string);
+  // TODO(crbug.com/964547): Refactor so that any change to these strings will
+  // surface in both the OOBE and post-OOBE UIs without having to adjust both
+  // localization calls separately.
+  builder->AddF(
+      "startSetupPageMessage", IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_MESSAGE,
+      base::ASCIIToUTF16(kFootnoteMarker),
+      base::UTF8ToUTF16(chromeos::multidevice_setup::
+                            GetBoardSpecificBetterTogetherSuiteLearnMoreUrl()
+                                .spec()));
+
+  builder->AddF("startSetupPageFootnote",
+                IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_FOOTNOTE,
+                base::ASCIIToUTF16(kFootnoteMarker));
+
+  builder->AddF(
+      "startSetupPageFeatureListAwm",
+      IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_AWM_DESCRIPTION,
+      base::UTF8ToUTF16(
+          chromeos::multidevice_setup::GetBoardSpecificMessagesLearnMoreUrl()
+              .spec()));
 }
 
 }  // namespace multidevice_setup
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html
index 8cce94f..78c43b6 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html
@@ -31,12 +31,14 @@
         padding: 0 32px;
       }
     </style>
-    <iron-pages attr-for-selected="is"
+    <iron-pages id="ironPages"
+        attr-for-selected="is"
         selected="[[visiblePageName]]"
         selected-item="{{visiblePage_}}">
       <template is="dom-if" if="[[shouldPasswordPageBeIncluded_(delegate)]]"
           restamp>
-        <password-page auth-token="{{authToken_}}"
+        <password-page class="ui-page"
+            auth-token="{{authToken_}}"
             forward-button-disabled="{{passwordPageForwardButtonDisabled_}}"
             password-field-valid="{{passwordFieldValid}}"
             on-user-submitted-password="onUserSubmittedPassword_">
@@ -44,17 +46,18 @@
       </template>
       <template is="dom-if"
           if="[[shouldSetupSucceededPageBeIncluded_(delegate)]]" restamp>
-        <setup-succeeded-page></setup-succeeded-page>
+        <setup-succeeded-page class="ui-page"></setup-succeeded-page>
       </template>
-      <start-setup-page devices="[[devices_]]"
+      <start-setup-page class="ui-page"
+          devices="[[devices_]]"
           selected-device-id="{{selectedDeviceId_}}"
           delegate="[[delegate]]">
       </start-setup-page>
     </iron-pages>
     <div class="flex"></div>
-    <button-bar forward-button-hidden="[[!forwardButtonText]]"
-        backward-button-hidden="[[!backwardButtonText]]"
-        cancel-button-hidden="[[!cancelButtonText]]">
+    <button-bar forward-button-hidden="[[!forwardButtonTextId]]"
+        backward-button-hidden="[[!backwardButtonTextId]]"
+        cancel-button-hidden="[[!cancelButtonTextId]]">
       <slot name="backward-button" slot="backward-button"></slot>
       <slot name="cancel-button" slot="cancel-button"></slot>
       <slot name="forward-button" slot="forward-button"></slot>
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js
index e97849f..8545122 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js
@@ -27,12 +27,12 @@
       delegate: Object,
 
       /**
-       * Text to be shown on the forward navigation button.
+       * ID of loadTimeData string to be shown on the forward navigation button.
        * @type {string|undefined}
        */
-      forwardButtonText: {
+      forwardButtonTextId: {
         type: String,
-        computed: 'getForwardButtonText_(visiblePage_)',
+        computed: 'getForwardButtonTextId_(visiblePage_)',
         notify: true,
       },
 
@@ -45,22 +45,23 @@
       },
 
       /**
-       * Text to be shown on the cancel button.
+       * ID of loadTimeData string to be shown on the cancel button.
        * @type {string|undefined}
        */
-      cancelButtonText: {
+      cancelButtonTextId: {
         type: String,
-        computed: 'getCancelButtonText_(visiblePage_)',
+        computed: 'getCancelButtonTextId_(visiblePage_)',
         notify: true,
       },
 
       /**
-       * Text to be shown on the backward navigation button.
+       * ID of loadTimeData string to be shown on the backward navigation
+       * button.
        * @type {string|undefined}
        */
-      backwardButtonText: {
+      backwardButtonTextId: {
         type: String,
-        computed: 'getBackwardButtonText_(visiblePage_)',
+        computed: 'getBackwardButtonTextId_(visiblePage_)',
         notify: true,
       },
 
@@ -142,6 +143,11 @@
           this.initializeSetupFlow.bind(this));
     },
 
+    updateLocalizedContent: function() {
+      this.$.ironPages.querySelectorAll('.ui-page')
+          .forEach(page => page.i18nUpdateLocale());
+    },
+
     initializeSetupFlow: function() {
       this.mojoInterfaceProvider_.getMojoServiceProxy()
           .getEligibleHostDevices()
@@ -240,15 +246,16 @@
     },
 
     /**
-     * @return {string|undefined} The forward button text, which is undefined
-     *     if no button should be displayed.
+     * @return {string|undefined} The ID of loadTimeData string for the
+     *     forward button text, which is undefined if no button should be
+     *     displayed.
      * @private
      */
-    getForwardButtonText_: function() {
+    getForwardButtonTextId_: function() {
       if (!this.visiblePage_) {
         return undefined;
       }
-      return this.visiblePage_.forwardButtonText;
+      return this.visiblePage_.forwardButtonTextId;
     },
 
     /**
@@ -261,27 +268,29 @@
     },
 
     /**
-     * @return {string|undefined} The cancel button text, which is undefined
-     *     if no button should be displayed.
+     * @return {string|undefined} The ID of loadTimeData string for the
+     *     cancel button text, which is undefined if no button should be
+     *     displayed.
      * @private
      */
-    getCancelButtonText_: function() {
+    getCancelButtonTextId_: function() {
       if (!this.visiblePage_) {
         return undefined;
       }
-      return this.visiblePage_.cancelButtonText;
+      return this.visiblePage_.cancelButtonTextId;
     },
 
     /**
-     * @return {string|undefined} The backward button text, which is undefined
-     *     if no button should be displayed.
+     * @return {string|undefined} The ID of loadTimeData string for the
+     *     backward button text, which is undefined if no button should be
+     *     displayed.
      * @private
      */
-    getBackwardButtonText_: function() {
+    getBackwardButtonTextId_: function() {
       if (!this.visiblePage_) {
         return undefined;
       }
-      return this.visiblePage_.backwardButtonText;
+      return this.visiblePage_.backwardButtonTextId;
     },
 
     /**
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/password_page.html b/ui/webui/resources/cr_components/chromeos/multidevice_setup/password_page.html
index b5c52c0..a12f238 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/password_page.html
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/password_page.html
@@ -18,7 +18,7 @@
       #profile-photo {
         border-radius: 50%;
         height: 20px;
-        margin-right: 8px;
+        margin-inline-end: 8px;
         width: 20px;
       }
 
@@ -28,16 +28,17 @@
         width: 560px;
       }
     </style>
-    <ui-page header-text="[[headerText]]" icon-name="google-g">
+    <ui-page header-text="[[i18nDynamic(locale, 'passwordPageHeader')]]"
+        icon-name="google-g">
       <div id="content-container" slot="additional-content">
         <div id="user-info-container">
           <img id="profile-photo" src="[[profilePhotoUrl_]]"></img>
           <span id="email">[[email_]]</span>
         </div>
         <cr-input id="passwordInput" type="password"
-            placeholder="[[i18n('enterPassword')]]"
+            placeholder="[[i18nDynamic(locale, 'enterPassword')]]"
             invalid="[[passwordInvalid_]]"
-            error-message="[[i18n('wrongPassword')]]"
+            error-message="[[i18nDynamic(locale, 'wrongPassword')]]"
             value="{{inputValue_}}"
             aria-disabled="false"
             on-keypress="onInputKeypress_"
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/password_page.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/password_page.js
index 80b4f75..8f73387 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/password_page.js
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/password_page.js
@@ -41,12 +41,6 @@
       value: 'back',
     },
 
-    /** Overridden from UiPageContainerBehavior. */
-    headerId: {
-      type: String,
-      value: 'passwordPageHeader',
-    },
-
     /**
      * Authentication token; retrieved using the quickUnlockPrivate API.
      * @type {string}
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.html b/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.html
index a899610..0606e52 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.html
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.html
@@ -23,8 +23,9 @@
         width: 416px;
       }
     </style>
-    <ui-page header-text="[[headerText]]" icon-name="google-g">
-      <span slot="message" inner-h-t-m-l="[[messageHtml]]"></span>
+    <ui-page header-text="[[i18nDynamic(locale, 'setupSucceededPageHeader')]]"
+        icon-name="google-g">
+      <span slot="message" inner-h-t-m-l="[[getMessageHtml_()]]"></span>
       <div id="page-icon-container" slot="additional-content">
         <div id="page-icon"></div>
       </div>
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.js
index e9c4eac..a679e6d 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.js
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.js
@@ -13,23 +13,9 @@
       type: String,
       value: 'done',
     },
-
-    /** Overridden from UiPageContainerBehavior. */
-    headerId: {
-      type: String,
-      value: 'setupSucceededPageHeader',
-    },
-
-    /** Overridden from UiPageContainerBehavior. */
-    messageId: {
-      type: String,
-      value: 'setupSucceededPageMessage',
-    },
   },
 
-  behaviors: [
-    UiPageContainerBehavior,
-  ],
+  behaviors: [UiPageContainerBehavior],
 
   /** @private {?multidevice_setup.BrowserProxy} */
   browserProxy_: null,
@@ -50,9 +36,17 @@
     this.fire('setup-exited');
   },
 
+  /** @private */
+  getMessageHtml_: function() {
+    const validNodeFn = (node, value) => node.tagName == 'A';
+    return this.i18nAdvanced(
+        'setupSucceededPageMessage',
+        {attrs: {'id': validNodeFn, 'href': validNodeFn}});
+  },
+
   /** @override */
   ready: function() {
-    let linkElement = this.$$('#settings-link');
+    const linkElement = this.$$('#settings-link');
     linkElement.setAttribute('href', '#');
     linkElement.addEventListener('click', () => this.onSettingsLinkClicked_());
   },
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html b/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html
index a410816b..4e8c569 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html
@@ -5,7 +5,6 @@
 <link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/ui_page.html">
 <link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/ui_page_container_behavior.html">
 <link rel="import" href="chrome://resources/html/cr.html">
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 
@@ -47,8 +46,8 @@
       #feature-details-container {
         @apply --layout-vertical;
         @apply --layout-center-justified;
-        border-left: 1px solid rgb(218, 220, 224);
-        padding-left: 24px;
+        border-inline-start: 1px solid rgb(218, 220, 224);
+        padding-inline-start: 24px;
       }
 
       #feature-details-container-header {
@@ -70,7 +69,7 @@
       }
 
       .feature-detail span {
-        margin-left: 8px;
+        margin-inline-start: 8px;
       }
 
       #footnote {
@@ -79,8 +78,11 @@
       }
     </style>
 
-    <ui-page header-text="[[headerText]]" icon-name="google-g">
-      <span slot="message" id="multidevice-summary-message" inner-h-t-m-l="[[messageHtml]]"></span>
+    <ui-page header-text="[[i18nDynamic(locale, 'startSetupPageHeader')]]"
+        icon-name="google-g">
+      <span slot="message" id="multidevice-summary-message" inner-h-t-m-l=
+          "[[i18nAdvancedDynamic_(locale, 'startSetupPageMessage')]]">
+      </span>
       <div slot="additional-content">
         <div id="selector-and-details-container">
           <div id="deviceSelectionContainer" class="flex">
@@ -107,26 +109,33 @@
           </div>
           <div id="feature-details-container" class="flex">
             <div id="feature-details-container-header">
-              [[i18n('startSetupPageFeatureListHeader')]]
+              [[i18nDynamic(locale, 'startSetupPageFeatureListHeader')]]
             </div>
             <div class="feature-detail">
               <iron-icon icon="multidevice-setup-icons-20:messages"></iron-icon>
               <span id="awm-summary-message" inner-h-t-m-l="
-                  [[i18nAdvanced('startSetupPageFeatureListAwm')]]">
+                  [[i18nAdvancedDynamic_(
+                      locale, 'startSetupPageFeatureListAwm')]]">
               </span>
             </div>
             <div class="feature-detail">
               <iron-icon icon="multidevice-setup-icons-20:downloads">
               </iron-icon>
-              <span>[[i18n('startSetupPageFeatureListInstallApps')]]</span>
+              <span>
+                [[i18nDynamic(locale, 'startSetupPageFeatureListInstallApps')]]
+              </span>
             </div>
             <div class="feature-detail">
               <iron-icon icon="multidevice-setup-icons-20:features"></iron-icon>
-              <span>[[i18n('startSetupPageFeatureListAddFeatures')]]</span>
+              <span>
+                [[i18nDynamic(locale, 'startSetupPageFeatureListAddFeatures')]]
+              </span>
             </div>
           </div>
         </div>
-        <div id="footnote">[[i18n('startSetupPageFootnote')]]</div>
+        <div id="footnote">
+          [[i18nAdvancedDynamic_(locale, 'startSetupPageFootnote')]]
+        </div>
       </div>
     </ui-page>
   </template>
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js
index f5445d9..b85f3724 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js
@@ -18,18 +18,6 @@
       computed: 'getCancelButtonTextId_(delegate)',
     },
 
-    /** Overridden from UiPageContainerBehavior. */
-    headerId: {
-      type: String,
-      value: 'startSetupPageHeader',
-    },
-
-    /** Overridden from UiPageContainerBehavior. */
-    messageId: {
-      type: String,
-      value: 'startSetupPageMessage',
-    },
-
     /**
      * Array of objects representing all potential MultiDevice hosts.
      *
@@ -63,7 +51,6 @@
 
   behaviors: [
     UiPageContainerBehavior,
-    I18nBehavior,
     WebUIListenerBehavior,
   ],
 
@@ -79,7 +66,7 @@
     // The "Learn More" links are inside a grdp string, so we cannot actually
     // add an onclick handler directly to the html. Instead, grab the two and
     // manaully add onclick handlers.
-    let helpArticleLinks = [
+    const helpArticleLinks = [
       this.$$('#multidevice-summary-message a'),
       this.$$('#awm-summary-message a')
     ];
@@ -153,4 +140,17 @@
   onDeviceDropdownSelectionChanged_: function() {
     this.selectedDeviceId = this.$.deviceDropdown.value;
   },
+
+  /**
+   * Wrapper for i18nAdvanced for binding to location updates in OOBE.
+   * @param {string} locale The language code (e.g. en, es) for the current
+   *     display language for CrOS. As with I18nBehavior.i18nDynamic(), the
+   *     parameter is not used directly but is provided to allow HTML binding
+   *     without passing an unexpected argument to I18nBehavior.i18nAdvanced().
+   * @param {string} textId The loadTimeData ID of the string to be translated.
+   * @private
+   */
+  i18nAdvancedDynamic_: function(locale, textId) {
+    return this.i18nAdvanced(textId);
+  },
 });
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page_container_behavior.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page_container_behavior.js
index cb31b51..35a578c 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page_container_behavior.js
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page_container_behavior.js
@@ -6,102 +6,28 @@
 const UiPageContainerBehaviorImpl = {
   properties: {
     /**
-     * ID for forward button label, which must be translated for display.
-     *
-     * Undefined if the visible page has no forward-navigation button.
-     *
+     * ID of loadTimeData string for forward button label, which must be
+     * translated for display. Undefined if the visible page has no
+     * forward-navigation button.
      * @type {string|undefined}
      */
     forwardButtonTextId: String,
 
     /**
-     * ID for cancel button label, which must be translated for display.
-     *
-     * Undefined if the visible page has no cancel button.
-     *
+     * ID of loadTimeData string for cancel button label, which must be
+     * translated for display. Undefined if the visible page has no
+     * cancel button.
      * @type {string|undefined}
      */
     cancelButtonTextId: String,
 
     /**
-     * ID for backward button label, which must be translated for display.
-     *
-     * Undefined if the visible page has no backward-navigation button.
-     *
+     * ID of loadTimeData string for backward button label, which must be
+     * translated for display. Undefined if the visible page has no
+     * backward-navigation button.
      * @type {string|undefined}
      */
     backwardButtonTextId: String,
-
-    /**
-     * ID for text of main UI Page heading.
-     *
-     * @type {string}
-     */
-    headerId: String,
-
-    /**
-     * ID for text of main UI Page message body.
-     *
-     * @type {string}
-     */
-    messageId: String,
-
-    /**
-     * Translated text to display on the forward-naviation button.
-     *
-     * Undefined if the visible page has no forward-navigation button.
-     *
-     * @type {string|undefined}
-     */
-    forwardButtonText: {
-      type: String,
-      computed: 'computeLocalizedText_(forwardButtonTextId)',
-    },
-
-    /**
-     * Translated text to display on the cancel button.
-     *
-     * Undefined if the visible page has no cancel button.
-     *
-     * @type {string|undefined}
-     */
-    cancelButtonText: {
-      type: String,
-      computed: 'computeLocalizedText_(cancelButtonTextId)',
-    },
-
-    /**
-     * Translated text to display on the backward-naviation button.
-     *
-     * Undefined if the visible page has no backward-navigation button.
-     *
-     * @type {string|undefined}
-     */
-    backwardButtonText: {
-      type: String,
-      computed: 'computeLocalizedText_(backwardButtonTextId)',
-    },
-
-    /**
-     * Translated text of main UI Page heading.
-     *
-     * @type {string|undefined}
-     */
-    headerText: {
-      type: String,
-      computed: 'computeLocalizedText_(headerId)',
-    },
-
-    /**
-     * Translated text of main UI Page heading. In general this can include
-     * some markup.
-     *
-     * @type {string|undefined}
-     */
-    messageHtml: {
-      type: String,
-      computed: 'computeLocalizedText_(messageId)',
-    },
   },
 
   /**
@@ -116,23 +42,6 @@
       resolve(true /* canNavigate */);
     });
   },
-
-  /**
-   * @param {string} textId Key for the localized string to appear on a
-   *     button.
-   * @return {string|undefined} The localized string corresponding to the key
-   *     textId. Return value is undefined if textId is not a key
-   *     for any localized string. Note: this includes the case in which
-   *     textId is undefined.
-   * @private
-   */
-  computeLocalizedText_: function(textId) {
-    if (!this.i18nExists(textId)) {
-      return;
-    }
-
-    return loadTimeData.getString(textId);
-  },
 };
 
 /** @polymerBehavior */