Repository: brave/brave-core
Pull request #28774: [locale]: Remove `getLocaleWithTags` and `splitStringForTag`
Author: login: fallaciousreasoning
Labels: name: CI/storybook-url
description: Deploy storybook and provide a unique URL for each build
name: feature/web3/wallet
name: feature/web3/wallet/core
name: puLL-Merge
Created: 2025-04-23T03:59:49Z | Merged: 2025-04-28T20:13:46Z

<!-- Add brave-browser issue below that this PR will resolve -->
Resolves https://github.com/brave/brave-browser/issues/45627

There a few main changes here:
`splitStringForTag` ==> `formatLocale`/`formatString`
`getLocaleWithTags` ==> `formatLocale`
`getLocaleWithTag` ==> `formatLocale`

`formatLocale` expects a duplicate placeholder before and after the text to wrap, so `$1Link$2` becomes `$1Link$1`.

`formatLocale` allows replacing arbitrary content, so

```tsx
// myString "Hi $1$3$2 from me"
const string = getLocale('myString').replaceAll('$3', "Max")
const { beforeTag, duringTag, afterTag } = splitStringForTag(string)

return <span>
  {beforeTag}
  <b>{duringTag}</b>
  {afterTag}
</span>

// becomes
// myString: "Hi $1 from me"
return <span>
  {formatLocale('myString', {
    $1: <b>Max</b>
  })
</span>
```

This becomes even simpler when there are multiple tags to replace.

Additionally, we often drop `beforeTag` or `afterTag`, which works fine in English but might not in other languages (i.e. if the placeholder is not at the beginning or end of the string). Using `formatLocale` will fix any such issues.

<!-- CI-related labels that can be applied to this PR:
* CI/run-audit-deps (1) - check for known npm/cargo vulnerabilities (audit_deps)
* CI/run-network-audit (1) - run network-audit
* CI/run-upstream-tests - run Chromium unit and browser tests on Linux and Windows (otherwise only on Linux)
* CI/run-linux-arm64, CI/run-macos-x64, CI/run-windows-arm64, CI/run-windows-x86 - run builds that would otherwise be skipped
* CI/skip - do not run CI builds (except noplatform)
* CI/skip-linux-x64, CI/skip-android, CI/skip-macos-arm64, CI/skip-ios, CI/skip-windows-x64 - skip CI builds for specific platforms
* CI/skip-upstream-tests - do not run Chromium unit, or browser tests (otherwise only on Linux)
* CI/skip-all-linters - do not run presubmit and lint checks
* CI/storybook-url (1) - deploy storybook and provide a unique URL for each build

(1) applied automatically when some files are changed (see: https://github.com/brave/brave-core/blob/master/.github/labeler.yml)
-->

<!--
## Checklist:

- Review design docs
  [Browser design principles](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/chrome_browser_design_principles.md)
  [Style guide](https://chromium.googlesource.com/chromium/src/+/main/styleguide/c++/c++.md)
  [Core principles](https://www.chromium.org/developers/core-principles/)
- Ensure there are (tests)[https://www.chromium.org/developers/testing/]. Unit test as much as possible (including edge cases), but also include browser tests covering high level functionality.
- Ensure that there are comments explaining what classes/methods are/do. The "why" is often more important than the "what" in comments. Also update any relevant docs (moving docs from wiki to brave-core if necessary).
- Request security or other review (third-party libraries, rust code, etc...) if applicable [security/privacy review is needed](https://github.com/brave/brave-browser/wiki/Security-reviews) [other review](https://github.com/brave/reviews/issues/new/choose)
  Also see [adding third-party libraries](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/adding_to_third_party.md) for general guidelines on using third party code
- Maks sure there is a [ticket](https://github.com/brave/brave-browser/issues) for your issue
- Use Github [auto-closing keywords](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue) in the PR description above
- Write a good [PR/commit description](https://google.github.io/eng-practices/review/developer/cl-descriptions.html)
- Squash any review feedback or "fixup" commits before merge, so that history is a record of what happened in the repo, not your PR
- Add appropriate labels (`QA/Yes` or `QA/No`; `release-notes/include` or `release-notes/exclude`; `OS/...`) to the associated issue
- Checked the PR locally:
  * `npm run test -- brave_browser_tests`, `npm run test -- brave_unit_tests` [wiki](https://github.com/brave/brave-browser/wiki/Tests)
  * `npm run presubmit` [wiki](https://github.com/brave/brave-browser/wiki/Presubmit-checks), `npm run gn_check`, `npm run tslint`
- Run `git rebase master` (if needed)
-->



Diff:
diff --git a/components/brave_new_tab_ui/components/default/braveNews/cards/cardOptIn.tsx b/components/brave_new_tab_ui/components/default/braveNews/cards/cardOptIn.tsx
index 43f664c8ab4a..0988cf495c0d 100644
--- a/components/brave_new_tab_ui/components/default/braveNews/cards/cardOptIn.tsx
+++ b/components/brave_new_tab_ui/components/default/braveNews/cards/cardOptIn.tsx
@@ -4,7 +4,7 @@
 // You can obtain one at https://mozilla.org/MPL/2.0/.
 
 import * as React from 'react'
-import { getLocale, getLocaleWithTag } from '../../../../../common/locale'
+import { getLocale, formatLocale } from '$web-common/locale'
 import * as Card from '../cardIntro'
 import BraveNewsLogoUrl from '../braveNewsLogo.svg'
 import { CardButton, TertiaryButton } from '../default'
@@ -15,7 +15,9 @@ type Props = {
   onDisable: () => unknown
 }
 
-const descriptionTwoTextParts = getLocaleWithTag('braveNewsIntroDescriptionTwo')
+const description = formatLocale('braveNewsIntroDescriptionTwo', {
+  $1: content => <a href='https://brave.com/privacy/browser/'>{content}</a>
+})
 
 export default function IntroCard (props: Props) {
   const introElementRef = React.useRef(null)
@@ -28,11 +30,7 @@ export default function IntroCard (props: Props) {
           {getLocale('braveNewsIntroDescription')}
         </Card.Paragraph>
         <Card.Paragraph>
-          {descriptionTwoTextParts.beforeTag}
-          <a href={'https://brave.com/privacy/browser/'}>
-            {descriptionTwoTextParts.duringTag}
-          </a>
-          {descriptionTwoTextParts.afterTag}
+          {description}
         </Card.Paragraph>
       </div>
       <CardButton onClick={props.onOptIn} isMainFocus={true}>
diff --git a/components/brave_new_tab_ui/components/default/braveNews/customize/DisabledPlaceholder.tsx b/components/brave_new_tab_ui/components/default/braveNews/customize/DisabledPlaceholder.tsx
index 2ea1b72d7755..ca47f5ff1a65 100644
--- a/components/brave_new_tab_ui/components/default/braveNews/customize/DisabledPlaceholder.tsx
+++ b/components/brave_new_tab_ui/components/default/braveNews/customize/DisabledPlaceholder.tsx
@@ -6,7 +6,7 @@
 import Button from '@brave/leo/react/button'
 import * as React from 'react'
 import styled from 'styled-components'
-import { getLocale, getLocaleWithTag } from '$web-common/locale'
+import { getLocale, formatLocale } from '$web-common/locale'
 import Flex from '$web-common/Flex'
 
 const TodayGraphic = <svg width="370" height="80" viewBox="0 0 370 80" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -57,7 +57,11 @@ const EnableButton = styled(Button)`
   margin-bottom: 48px;
 `
 
-const descriptionTwoTextParts = getLocaleWithTag('braveNewsIntroDescriptionTwo')
+const description = formatLocale('braveNewsIntroDescriptionTwo', {
+  $1: content => <a href='https://brave.com/privacy/browser/'>
+    {content}
+  </a>
+})
 
 export default function DisabledPlaceholder (props: { enableBraveNews: () => void }) {
   return (
@@ -71,11 +75,7 @@ export default function DisabledPlaceholder (props: { enableBraveNews: () => voi
           {getLocale('braveNewsIntroDescription')}
         </Subtitle>
         <Subtitle>
-          {descriptionTwoTextParts.beforeTag}
-            <a href={'https://brave.com/privacy/browser/'}>
-              {descriptionTwoTextParts.duringTag}
-            </a>
-          {descriptionTwoTextParts.afterTag}
+          {description}
         </Subtitle>
       </div>
       <EnableButton onClick={props.enableBraveNews}>
diff --git a/components/brave_new_tab_ui/stories/todayStrings.ts b/components/brave_new_tab_ui/stories/todayStrings.ts
index 7b2f16f06513..d39d1c715b08 100644
--- a/components/brave_new_tab_ui/stories/todayStrings.ts
+++ b/components/brave_new_tab_ui/stories/todayStrings.ts
@@ -11,5 +11,5 @@ provideStrings({
   braveNewsScrollHint: 'Scroll for Brave News',
   braveNewsIntroTitle: 'Turn on Brave News, and never miss a story',
   braveNewsIntroDescription: 'Follow your favorite sources, in a single feed. Just open a tab in Brave, scroll down, and…voila!',
-  braveNewsIntroDescriptionTwo: 'Brave News is ad-supported with private, anonymized ads. $1Learn more.$2.'
+  braveNewsIntroDescriptionTwo: 'Brave News is ad-supported with private, anonymized ads. $1Learn more.$1.'
 })
diff --git a/components/brave_rewards/resources/ui/components/modalRedirect/index.tsx b/components/brave_rewards/resources/ui/components/modalRedirect/index.tsx
index 31966155d4fd..1b547cac2a72 100644
--- a/components/brave_rewards/resources/ui/components/modalRedirect/index.tsx
+++ b/components/brave_rewards/resources/ui/components/modalRedirect/index.tsx
@@ -1,6 +1,7 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at https://mozilla.org/MPL/2.0/. */
+// Copyright (c) 2019 The Brave Authors. All rights reserved.
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at https://mozilla.org/MPL/2.0/.
 
 import * as React from 'react'
 import {
@@ -14,7 +15,7 @@ import {
 import Modal from 'brave-ui/components/popupModals/modal/index'
 import { LoaderIcon } from 'brave-ui/components/icons'
 import { Button } from 'brave-ui/components'
-import { getLocale, splitStringForTag } from '../../../../../common/locale'
+import { getLocale, formatString } from '$web-common/locale'
 
 export interface Props {
   id?: string
@@ -71,30 +72,21 @@ export default class ModalRedirect extends React.PureComponent<Props, {}> {
             errorText
               ? <StyledError>
                 {
-                  errorText.map((line, index) => {
-                    let lineLinkTags = null
-                    if (line && line.includes('$2')) {
-                      lineLinkTags = splitStringForTag(line, 2)
-                    }
-
-                    return <p key={index}>
-                      {
-                        lineLinkTags
-                          ? <>
-                            {lineLinkTags.beforeTag}
-                            {
-                              errorTextLink
-                                ? <a href={errorTextLink} target='_blank' rel='noopener noreferrer'>
-                                  {lineLinkTags.duringTag}
-                                </a>
-                                : lineLinkTags.duringTag
-                            }
-                            {lineLinkTags.afterTag}
-                          </>
-                          : line
-                      }
-                    </p>
-                  })
+                  errorText.map((line, index) => <p key={index}>
+                      {formatString(line, {
+                      $2: content => errorTextLink
+                        ? <a
+                          href={errorTextLink}
+                          target='_blank'
+                          rel='noopener noreferrer'
+                        >
+                          {content}
+                        </a>
+                        : content
+                    }, {
+                      noErrorOnMissingReplacement: true
+                    })}
+                    </p>)
                 }
                 {
                   learnMore &&
diff --git a/components/brave_wallet/browser/brave_wallet_constants.h b/components/brave_wallet/browser/brave_wallet_constants.h
index 0ed301200905..cb3f1e5c0244 100644
--- a/components/brave_wallet/browser/brave_wallet_constants.h
+++ b/components/brave_wallet/browser/brave_wallet_constants.h
@@ -614,7 +614,7 @@ inline constexpr webui::LocalizedString kLocalizedStrings[] = {
      IDS_BRAVE_WALLET_AUTHORIZE_HARDWARE_WALLET},
     {"braveWalletConnectHardwareWallet",
      IDS_BRAVE_WALLET_CONNECT_HARDWARE_WALLET},
-    {"braveWallectConnectHardwareDescription",
+    {"braveWalletConnectHardwareDescription",
      IDS_BRAVE_WALLET_CONNECT_HARDWARE_WALLET_DESCRIPTION},
     {"braveWalletImportHardwareWalletDescription",
      IDS_BRAVE_WALLET_IMPORT_HARDWARE_WALLET_DESCRIPTION},
diff --git a/components/brave_wallet_ui/components/desktop/popup-modals/account-settings-modal/account-settings-modal.tsx b/components/brave_wallet_ui/components/desktop/popup-modals/account-settings-modal/account-settings-modal.tsx
index 62f97f896451..2ce6d4cb2ae1 100644
--- a/components/brave_wallet_ui/components/desktop/popup-modals/account-settings-modal/account-settings-modal.tsx
+++ b/components/brave_wallet_ui/components/desktop/popup-modals/account-settings-modal/account-settings-modal.tsx
@@ -23,7 +23,7 @@ import { useSafeWalletSelector } from '../../../../common/hooks/use-safe-selecto
 import { WalletSelectors } from '../../../../common/selectors'
 
 // utils
-import { getLocale, getLocaleWithTag } from '../../../../../common/locale'
+import { getLocale, formatLocale } from '$web-common/locale'
 import getAPIProxy from '../../../../common/async/bridge'
 
 // constants
@@ -102,6 +102,18 @@ interface DepositModalProps {
   selectedAccount: BraveWallet.AccountInfo
 }
 
+const filPrivateKeyFormatDescription = formatLocale(
+  'braveWalletFilExportPrivateKeyFormatDescription', {
+    $1: content =>  <a
+        target='_blank'
+        href={FILECOIN_FORMAT_DESCRIPTION_URL}
+        rel='noopener noreferrer'
+      >
+        {content}
+      </a>
+  }
+)
+
 export const DepositModal = ({ selectedAccount }: DepositModalProps) => {
   // state
   const [selectedZCashAddressOption, setSelectedZCashAddressOption] =
@@ -342,10 +354,6 @@ export const AccountSettingsModal = () => {
     }
   }
 
-  const filPrivateKeyFormatDescriptionTextParts = getLocaleWithTag(
-    'braveWalletFilExportPrivateKeyFormatDescription'
-  )
-
   // memos
   const modalTitle = React.useMemo((): string => {
     if (accountModalType) {
@@ -415,15 +423,7 @@ export const AccountSettingsModal = () => {
                 {selectedAccount?.accountId.coin ===
                   BraveWallet.CoinType.FIL && (
                   <Alert type='warning'>
-                    {filPrivateKeyFormatDescriptionTextParts.beforeTag}
-                    <a
-                      target='_blank'
-                      href={FILECOIN_FORMAT_DESCRIPTION_URL}
-                      rel='noopener noreferrer'
-                    >
-                      {filPrivateKeyFormatDescriptionTextParts.duringTag}
-                    </a>
-                    {filPrivateKeyFormatDescriptionTextParts.afterTag}
+                    {filPrivateKeyFormatDescription}
                   </Alert>
                 )}
                 <CopyTooltip text={privateKey}>
diff --git a/components/brave_wallet_ui/components/desktop/popup-modals/add-account-modal/add-imported-account-modal.tsx b/components/brave_wallet_ui/components/desktop/popup-modals/add-account-modal/add-imported-account-modal.tsx
index 52100ddb9663..9b6decf3b7ff 100644
--- a/components/brave_wallet_ui/components/desktop/popup-modals/add-account-modal/add-imported-account-modal.tsx
+++ b/components/brave_wallet_ui/components/desktop/popup-modals/add-account-modal/add-imported-account-modal.tsx
@@ -15,7 +15,7 @@ import {
 
 // utils
 import { FILECOIN_FORMAT_DESCRIPTION_URL } from '../../../../common/constants/urls'
-import { getLocale, getLocaleWithTag } from '$web-common/locale'
+import { getLocale, formatLocale } from '$web-common/locale'
 import { copyToClipboard } from '../../../../utils/copy-to-clipboard'
 
 // options
@@ -77,6 +77,18 @@ const reduceFileName = (address: string) => {
   return reduced
 }
 
+const filPrivateKeyFormatDescription = formatLocale(
+  'braveWalletFilImportPrivateKeyFormatDescription', {
+    $1: content => <a
+        target='_blank'
+        href={FILECOIN_FORMAT_DESCRIPTION_URL}
+        rel='noopener noreferrer'
+      >
+        {content}
+      </a>
+  }
+)
+
 export const ImportAccountModal = () => {
   // refs
   const passwordInputRef = React.useRef<HTMLInputElement>(null)
@@ -140,10 +152,6 @@ export const ImportAccountModal = () => {
       )
     : getLocale('braveWalletAddAccountImport')
 
-  const filPrivateKeyFormatDescriptionTextParts = getLocaleWithTag(
-    'braveWalletFilImportPrivateKeyFormatDescription'
-  )
-
   // methods
   const onClickClose = React.useCallback(() => {
     setHasImportError(false)
@@ -351,15 +359,7 @@ export const ImportAccountModal = () => {
 
           {selectedAccountType.coin === BraveWallet.CoinType.FIL && (
             <Alert type='warning'>
-              {filPrivateKeyFormatDescriptionTextParts.beforeTag}
-              <a
-                target='_blank'
-                href={FILECOIN_FORMAT_DESCRIPTION_URL}
-                rel='noopener noreferrer'
-              >
-                {filPrivateKeyFormatDescriptionTextParts.duringTag}
-              </a>
-              {filPrivateKeyFormatDescriptionTextParts.afterTag}
+              {filPrivateKeyFormatDescription}
             </Alert>
           )}
           {selectedAccountType.coin === BraveWallet.CoinType.BTC && (
diff --git a/components/brave_wallet_ui/components/desktop/popup-modals/enable-nft-discovery-modal/enable-nft-discovery-modal.tsx b/components/brave_wallet_ui/components/desktop/popup-modals/enable-nft-discovery-modal/enable-nft-discovery-modal.tsx
index ddc051ff27ba..194728a33218 100644
--- a/components/brave_wallet_ui/components/desktop/popup-modals/enable-nft-discovery-modal/enable-nft-discovery-modal.tsx
+++ b/components/brave_wallet_ui/components/desktop/popup-modals/enable-nft-discovery-modal/enable-nft-discovery-modal.tsx
@@ -11,7 +11,7 @@ import {
 import { UISelectors } from '../../../../common/selectors'
 
 // utils
-import { getLocale, splitStringForTag } from '../../../../../common/locale'
+import { getLocale, formatLocale } from '$web-common/locale'
 import { loadTimeData } from '../../../../../common/loadTimeData'
 
 // components
@@ -35,20 +35,22 @@ interface Props {
 const LEARN_MORE_LINK =
   'https://github.com/brave/brave-browser/wiki/NFT-Discovery'
 
+const enableNftAutoDiscovery = formatLocale('braveWalletEnableNftAutoDiscoveryModalDescription', {
+  $1: content => <Underline>{content}</Underline>,
+  $2: content => <Link
+    target='_blank'
+    rel='noreferrer'
+    href={LEARN_MORE_LINK}
+  >
+    {content}
+  </Link>
+})
+
 export const EnableNftDiscoveryModal = ({ onConfirm, onCancel }: Props) => {
   // Selectors
   const isPanel = useSafeUISelector(UISelectors.isPanel)
   const isAndroid = loadTimeData.getBoolean('isAndroid') || false
 
-  const { beforeTag, duringTag, afterTag } = splitStringForTag(
-    getLocale('braveWalletEnableNftAutoDiscoveryModalDescription'),
-    1
-  )
-  const { beforeTag: beforeLink, duringTag: learnMore } = splitStringForTag(
-    afterTag || '',
-    3
-  )
-
   return (
     <PopupModal
       title=''
@@ -66,16 +68,7 @@ export const EnableNftDiscoveryModal = ({ onConfirm, onCancel }: Props) => {
           {getLocale('braveWalletEnableNftAutoDiscoveryModalHeader')}
         </Header>
         <Description>
-          {beforeTag}
-          <Underline>{duringTag}</Underline>
-          {beforeLink}
-          <Link
-            target='_blank'
-            rel='noreferrer'
-            href={LEARN_MORE_LINK}
-          >
-            {learnMore}
-          </Link>
+          {enableNftAutoDiscovery}
         </Description>
         <ButtonRow>
           <LeoSquaredButton
diff --git a/components/brave_wallet_ui/components/desktop/popup-modals/partners_consent_modal/partners_consent_modal.tsx b/components/brave_wallet_ui/components/desktop/popup-modals/partners_consent_modal/partners_consent_modal.tsx
index edc93bf3dea0..8a26a22865de 100644
--- a/components/brave_wallet_ui/components/desktop/popup-modals/partners_consent_modal/partners_consent_modal.tsx
+++ b/components/brave_wallet_ui/components/desktop/popup-modals/partners_consent_modal/partners_consent_modal.tsx
@@ -8,7 +8,7 @@ import Checkbox from '@brave/leo/react/checkbox'
 import Button from '@brave/leo/react/button'
 
 // Utils
-import { getLocale, splitStringForTag } from '../../../../../common/locale'
+import { getLocale, formatLocale } from '$web-common/locale'
 
 // Selectors
 import { useSafeUISelector } from '../../../../common/hooks/use-safe-selector'
@@ -30,6 +30,32 @@ import { Row } from '../../../shared/style'
 
 const MELD_TERMS_OF_USE_URL = 'https://www.meld.io/terms-of-use'
 
+const onClickTermsOfUse = () => {
+  if (chrome.tabs !== undefined) {
+    chrome.tabs.create(
+      {
+        url: MELD_TERMS_OF_USE_URL
+      },
+      () => {
+        if (chrome.runtime.lastError) {
+          console.error(
+            'tabs.create failed: ' + chrome.runtime.lastError.message
+          )
+        }
+      }
+    )
+    return
+  }
+  // Tabs.create is desktop specific. Using window.open for android.
+  window.open(MELD_TERMS_OF_USE_URL, '_blank', 'noopener noreferrer')
+}
+
+const meldTermsOfUse = formatLocale('braveWalletMeldTermsOfUse', {
+  $1: content => <TermsButton onClick={onClickTermsOfUse}>
+    {content}
+  </TermsButton>
+})
+
 interface PartnerConsentModalProps {
   isOpen: boolean
   onClose: () => void
@@ -47,31 +73,6 @@ export function PartnersConsentModal(
   // redux
   const isPanel = useSafeUISelector(UISelectors.isPanel)
 
-  const { beforeTag, duringTag } = splitStringForTag(
-    getLocale('braveWalletMeldTermsOfUse'),
-    1
-  )
-
-  const onClickTermsOfUse = () => {
-    if (chrome.tabs !== undefined) {
-      chrome.tabs.create(
-        {
-          url: MELD_TERMS_OF_USE_URL
-        },
-        () => {
-          if (chrome.runtime.lastError) {
-            console.error(
-              'tabs.create failed: ' + chrome.runtime.lastError.message
-            )
-          }
-        }
-      )
-      return
-    }
-    // Tabs.create is desktop specific. Using window.open for android.
-    window.open(MELD_TERMS_OF_USE_URL, '_blank', 'noopener noreferrer')
-  }
-
   return (
     <TermsDialog
       isOpen={isOpen}
@@ -93,8 +94,7 @@ export function PartnersConsentModal(
         onChange={(e) => setTermsAccepted(e.checked)}
       >
         <TermsLabel>
-          {beforeTag}
-          <TermsButton onClick={onClickTermsOfUse}>{duringTag}</TermsButton>
+          {meldTermsOfUse}
         </TermsLabel>
       </Checkbox>
       <Row
diff --git a/components/brave_wallet_ui/components/desktop/views/nfts/components/auto-discovery-empty-state/auto-discovery-empty-state.tsx b/components/brave_wallet_ui/components/desktop/views/nfts/components/auto-discovery-empty-state/auto-discovery-empty-state.tsx
index 6acdd7d9f90f..eb2ad7fe77b7 100644
--- a/components/brave_wallet_ui/components/desktop/views/nfts/components/auto-discovery-empty-state/auto-discovery-empty-state.tsx
+++ b/components/brave_wallet_ui/components/desktop/views/nfts/components/auto-discovery-empty-state/auto-discovery-empty-state.tsx
@@ -5,10 +5,7 @@
 import * as React from 'react'
 
 // utils
-import {
-  getLocale,
-  splitStringForTag
-} from '../../../../../../../common/locale'
+import { getLocale, formatLocale } from '$web-common/locale'
 
 // styles
 import {
@@ -32,13 +29,10 @@ export const AutoDiscoveryEmptyState = ({
   onImportNft,
   onRefresh
 }: Props) => {
-  const { duringTag: refreshBtnText, afterTag } = splitStringForTag(
-    getLocale('braveWalletAutoDiscoveryEmptyStateActions')
-  )
-  const { beforeTag: or, duringTag: importText } = splitStringForTag(
-    afterTag || '',
-    3
-  )
+  const emptyStateActions = formatLocale('braveWalletAutoDiscoveryEmptyStateActions', {
+    $1: content => <ActionButton onClick={onRefresh}>{content}</ActionButton>,
+    $2: content => <ActionButton onClick={onImportNft}>{content}</ActionButton>
+  })
 
   return (
     <StyledWrapper>
@@ -67,9 +61,7 @@ export const AutoDiscoveryEmptyState = ({
             </Description>
           </Row>
           <Description>
-            <ActionButton onClick={onRefresh}>{refreshBtnText}</ActionButton>
-            {or}
-            <ActionButton onClick={onImportNft}>{importText}</ActionButton>
+            {emptyStateActions}
           </Description>
         </>
       )}
diff --git a/components/brave_wallet_ui/components/extension/enable_transaction_simulations/enable_transaction_simulations.tsx b/components/brave_wallet_ui/components/extension/enable_transaction_simulations/enable_transaction_simulations.tsx
index 016154c0d09b..556cf97687c6 100644
--- a/components/brave_wallet_ui/components/extension/enable_transaction_simulations/enable_transaction_simulations.tsx
+++ b/components/brave_wallet_ui/components/extension/enable_transaction_simulations/enable_transaction_simulations.tsx
@@ -10,7 +10,7 @@ import Icon from '@brave/leo/react/icon'
 import { BraveWallet } from '../../../constants/types'
 
 // utils
-import { getLocale, getLocaleWithTags } from '../../../../common/locale'
+import { getLocale, formatLocale } from '$web-common/locale'
 import { openTab } from '../../../utils/routes-utils'
 
 // hooks
@@ -104,30 +104,10 @@ export const EnableTransactionSimulations: React.FC = () => {
 
             <TermsText>
               <span>
-                {getLocaleWithTags(
-                  'braveWalletTransactionSimulationTerms',
-                  3
-                ).map((text, index) => {
-                  return (
-                    <span key={text.duringTag ?? index}>
-                      {text.beforeTag}
-                      {index === 0 ? (
-                        <strong>{text.duringTag}</strong>
-                      ) : (
-                        <a
-                          href='#'
-                          onClick={() =>
-                            index === 1
-                              ? openTab(BLOWFISH_TERMS_URL)
-                              : openTab(BLOWFISH_PRIVACY_POLICY_URL)
-                          }
-                        >
-                          {text.duringTag}
-                        </a>
-                      )}
-                      {text.afterTag}
-                    </span>
-                  )
+                {formatLocale('braveWalletTransactionSimulationTerms', {
+                  $1: content => <strong>{content}</strong>,
+                  $2: content => <a href='#' onClick={() => openTab(BLOWFISH_TERMS_URL)}>{content}</a>,
+                  $3: content => <a href='#' onClick={() => openTab(BLOWFISH_PRIVACY_POLICY_URL)}>{content}</a>
                 })}
               </span>
             </TermsText>
diff --git a/components/brave_wallet_ui/components/extension/encryption-key-panel/index.tsx b/components/brave_wallet_ui/components/extension/encryption-key-panel/index.tsx
index 212bfd2413f3..3e6eb59ef77e 100644
--- a/components/brave_wallet_ui/components/extension/encryption-key-panel/index.tsx
+++ b/components/brave_wallet_ui/components/extension/encryption-key-panel/index.tsx
@@ -10,7 +10,7 @@ import { BraveWallet } from '../../../constants/types'
 
 // Utils
 import { reduceAccountDisplayName } from '../../../utils/reduce-account-name'
-import { getLocale, splitStringForTag } from '../../../../common/locale'
+import { getLocale, formatLocale } from '$web-common/locale'
 
 // Components
 import { NavButton } from '../buttons/nav-button/index'
@@ -53,10 +53,12 @@ export function ProvidePubKeyPanel({ payload }: ProvidePubKeyPanelProps) {
 
   const orb = useAccountOrb(account)
 
-  const descriptionString = getLocale(
-    'braveWalletProvideEncryptionKeyDescription'
-  ).replace('$url', payload.originInfo.originSpec)
-  const { duringTag, afterTag } = splitStringForTag(descriptionString)
+  const description = formatLocale('braveWalletProvideEncryptionKeyDescription', {
+    $1: <CreateSiteOrigin
+      originSpec={payload.originInfo.originSpec}
+      eTldPlusOne={payload.originInfo.eTldPlusOne}
+    />
+  })
 
   // methods
   const onProvideOrAllow = async () => {
@@ -91,11 +93,7 @@ export function ProvidePubKeyPanel({ payload }: ProvidePubKeyPanelProps) {
       </TabRow>
       <MessageBox needsCenterAlignment={false}>
         <MessageText>
-          <CreateSiteOrigin
-            originSpec={duringTag ?? ''}
-            eTldPlusOne={payload.originInfo.eTldPlusOne}
-          />
-          {afterTag}
+          {description}
         </MessageText>
       </MessageBox>
       <ButtonRow>
diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/common/transaction_intent.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/common/transaction_intent.tsx
index 024a1f145173..894c4f330f7d 100644
--- a/components/brave_wallet_ui/components/extension/post-confirmation/common/transaction_intent.tsx
+++ b/components/brave_wallet_ui/components/extension/post-confirmation/common/transaction_intent.tsx
@@ -12,7 +12,7 @@ import {
 } from '../../../../constants/types'
 
 // Utils
-import { getLocale, splitStringForTag } from '$web-common/locale'
+import { getLocale, formatLocale } from '$web-common/locale'
 import {
   findTransactionToken,
   getFormattedTransactionTransferredValue,
@@ -92,11 +92,11 @@ export const TransactionIntent = (props: Props) => {
       txAccount &&
       txToAddress
       ? {
-          accountId: txAccount.accountId,
-          testnet: transactionNetwork.chainId === BraveWallet.Z_CASH_TESTNET,
-          use_shielded_pool: transactionsToken.isShielded,
-          address: txToAddress
-        }
+        accountId: txAccount.accountId,
+        testnet: transactionNetwork.chainId === BraveWallet.Z_CASH_TESTNET,
+        use_shielded_pool: transactionsToken.isShielded,
+        address: txToAddress
+      }
       : skipToken
   )
 
@@ -125,9 +125,9 @@ export const TransactionIntent = (props: Props) => {
       transaction.swapInfo?.toChainId &&
       transaction.swapInfo.toCoin !== undefined
       ? {
-          chainId: transaction.swapInfo.toChainId,
-          coin: transaction.swapInfo.toCoin
-        }
+        chainId: transaction.swapInfo.toChainId,
+        coin: transaction.swapInfo.toCoin
+      }
       : skipToken
   )
 
@@ -139,13 +139,13 @@ export const TransactionIntent = (props: Props) => {
 
   const formattedSellAmount = sellToken
     ? sellAmountWei
-        .divideByDecimals(sellToken.decimals)
-        .formatAsAsset(6, sellToken.symbol)
+      .divideByDecimals(sellToken.decimals)
+      .formatAsAsset(6, sellToken.symbol)
     : ''
   const formattedBuyAmount = buyToken
     ? buyAmountWei
-        .divideByDecimals(buyToken.decimals)
-        .formatAsAsset(6, buyToken.symbol)
+      .divideByDecimals(buyToken.decimals)
+      .formatAsAsset(6, buyToken.symbol)
     : ''
 
   const transactionFailed =
@@ -167,8 +167,8 @@ export const TransactionIntent = (props: Props) => {
     isERC20Approval
       ? txApprovalTarget
       : isSwapOrBridge
-      ? swapOrBridgeRecipient
-      : txToAddress,
+        ? swapOrBridgeRecipient
+        : txToAddress,
     accountInfosRegistry
   )
 
@@ -190,9 +190,8 @@ export const TransactionIntent = (props: Props) => {
   }, [transactionsToken, normalizedTransferredValue])
 
   const formattedApprovalAmount = isTxApprovalUnlimited
-    ? `${getLocale('braveWalletTransactionApproveUnlimited')} ${
-        transactionsToken?.symbol ?? ''
-      }`
+    ? `${getLocale('braveWalletTransactionApproveUnlimited')} ${transactionsToken?.symbol ?? ''
+    }`
     : formattedSendAmount
 
   const sendSwapOrBridgeLocale = React.useMemo(() => {
@@ -312,90 +311,49 @@ export const TransactionIntent = (props: Props) => {
     isShieldingFunds
   ])
 
-  const descriptionString = getLocale(descriptionLocale)
-    .replace('$5', firstDuringValue)
-    .replace('$6', secondDuringValue)
-    .replace(
-      '$7',
-      transactionFailed ? sendSwapOrBridgeLocale : txNetwork?.chainName ?? ''
-    )
-
-  const {
-    beforeTag: firstBefore,
-    duringTag: firstDuring,
-    afterTag: firstAfter
-  } = splitStringForTag(descriptionString, 1)
-  const {
-    beforeTag: secondBefore,
-    duringTag: secondDuring,
-    afterTag: secondAfter
-  } = splitStringForTag(firstAfter ?? '', 3)
+  const description = formatLocale(descriptionLocale, {
+    $1: <Text
+      textColor='primary'
+      textSize='14px'
+      isBold
+    >
+      {firstDuringValue}
+    </Text>,
+    $2: <Row
+      width='unset'
+      gap='4px'
+    >
+      <Text
+        textColor='primary'
+        textSize='14px'
+        isBold
+      >
+        {secondDuringValue}
+      </Text>
+      <Button
+        onClick={onClickViewOnBlockExplorer(
+          isSwapOrBridge && transaction.swapInfo?.provider === 'lifi'
+            ? 'lifi'
+            : 'tx',
+          transaction.txHash
+        )}
+      >
+        <ExplorerIcon />
+      </Button>
+    </Row>,
+    $3: transactionFailed ? sendSwapOrBridgeLocale : txNetwork?.chainName ?? ''
+  }, {
+    noErrorOnMissingReplacement: true
+  })
 
   return (
     <Row
       gap='4px'
       flexWrap='wrap'
     >
-      {firstBefore && (
-        <Text
-          textColor='secondary'
-          textSize='14px'
-          isBold={false}
-        >
-          {firstBefore}
-        </Text>
-      )}
-      {firstDuring && (
-        <Text
-          textColor='primary'
-          textSize='14px'
-          isBold={true}
-        >
-          {firstDuring}
-        </Text>
-      )}
-      {secondBefore && (
-        <Text
-          textColor='secondary'
-          textSize='14px'
-          isBold={false}
-        >
-          {secondBefore}
-        </Text>
-      )}
-      {secondDuring && (
-        <Row
-          width='unset'
-          gap='4px'
-        >
-          <Text
-            textColor='primary'
-            textSize='14px'
-            isBold={true}
-          >
-            {secondDuring}
-          </Text>
-          <Button
-            onClick={onClickViewOnBlockExplorer(
-              isSwapOrBridge && transaction.swapInfo?.provider === 'lifi'
-                ? 'lifi'
-                : 'tx',
-              transaction.txHash
-            )}
-          >
-            <ExplorerIcon />
-          </Button>
-        </Row>
-      )}
-      {secondAfter && (
-        <Text
-          textColor='secondary'
-          textSize='14px'
-          isBold={false}
-        >
-          {secondAfter}
-        </Text>
-      )}
+      <Text textColor='secondary' textSize='14px'>
+        {description}
+      </Text>
     </Row>
   )
 }
diff --git a/components/brave_wallet_ui/page/screens/backup-wallet/verify-recovery-phrase/components/phrase_input.tsx b/components/brave_wallet_ui/page/screens/backup-wallet/verify-recovery-phrase/components/phrase_input.tsx
index fc5f10c04c63..5c70feee0af5 100644
--- a/components/brave_wallet_ui/page/screens/backup-wallet/verify-recovery-phrase/components/phrase_input.tsx
+++ b/components/brave_wallet_ui/page/screens/backup-wallet/verify-recovery-phrase/components/phrase_input.tsx
@@ -6,7 +6,7 @@
 import * as React from 'react'
 
 // utils
-import { getLocale, splitStringForTag } from '../../../../../../common/locale'
+import { getLocale, formatLocale } from '$web-common/locale'
 
 // styles
 import {
@@ -41,10 +41,9 @@ export const PhraseInput = ({
   onChange,
   onHideError
 }: Props) => {
-  const { beforeTag, afterTag } = splitStringForTag(
-    getLocale('braveWalletRecoveryWordInstructions'),
-    1
-  )
+  const recoveryInstructions = formatLocale('braveWalletRecoveryWordInstructions', {
+    $1: <Bold>{wordPosition}</Bold>
+  })
 
   return (
     <Column
@@ -52,9 +51,7 @@ export const PhraseInput = ({
       gap='25px'
     >
       <FormLabel>
-        {beforeTag}
-        <Bold>{wordPosition}</Bold>
-        {afterTag}
+        {recoveryInstructions}
       </FormLabel>
       <FormInput
         value={phrase}
diff --git a/components/brave_wallet_ui/page/screens/composer_ui/to_asset/to_asset.tsx b/components/brave_wallet_ui/page/screens/composer_ui/to_asset/to_asset.tsx
index 2ae1d98d2bc7..e53359640241 100644
--- a/components/brave_wallet_ui/page/screens/composer_ui/to_asset/to_asset.tsx
+++ b/components/brave_wallet_ui/page/screens/composer_ui/to_asset/to_asset.tsx
@@ -7,7 +7,7 @@ import * as React from 'react'
 import { skipToken } from '@reduxjs/toolkit/query/react'
 
 // Utils
-import { getLocale, splitStringForTag } from '../../../../../common/locale'
+import { getLocale, formatLocale } from '$web-common/locale'
 import {
   computeFiatAmount,
   getPriceIdForToken
@@ -155,11 +155,15 @@ export const ToAsset = (props: Props) => {
       ? millisecondToString(timeUntilNextQuote)
       : ''
 
-  const newQuoteString = getLocale('braveWalletNewQuoteIn').replace(
-    '$3',
-    countdown
-  )
-  const { beforeTag, duringTag } = splitStringForTag(newQuoteString)
+  const newQuote = formatLocale('braveWalletNewQuoteIn', {
+    $1: <Text
+      textSize='12px'
+      isBold
+      textColor='primary'
+    >
+      {countdown}
+    </Text>
+  })
 
   // render
   return (
@@ -197,15 +201,8 @@ export const ToAsset = (props: Props) => {
                   textSize='12px'
                   isBold={false}
                 >
-                  {beforeTag}
+                  {newQuote}
                 </ReceiveAndQuoteText>
-                <Text
-                  textSize='12px'
-                  isBold={true}
-                  textColor='primary'
-                >
-                  {duringTag}
-                </Text>
               </Row>
             )}
             {timeUntilNextQuote !== undefined && (
diff --git a/components/brave_wallet_ui/page/screens/onboarding/disclosures/disclosures.tsx b/components/brave_wallet_ui/page/screens/onboarding/disclosures/disclosures.tsx
index 4ced3146559b..c1833704b3c5 100644
--- a/components/brave_wallet_ui/page/screens/onboarding/disclosures/disclosures.tsx
+++ b/components/brave_wallet_ui/page/screens/onboarding/disclosures/disclosures.tsx
@@ -8,7 +8,7 @@ import { useDispatch } from 'react-redux'
 import { useHistory } from 'react-router'
 
 // utils
-import { getLocale, getLocaleWithTag } from '../../../../../common/locale'
+import { getLocale, formatLocale } from '$web-common/locale'
 import { PageSelectors } from '../../../selectors'
 import { useLocationPathName } from '../../../../common/hooks/use-pathname'
 import { getOnboardingTypeFromPath } from '../../../../utils/routes-utils'
@@ -28,11 +28,8 @@ import {
 } from '../components/onboarding_content_layout/content_layout'
 
 const TermsOfUseText: React.FC<{}> = () => {
-  const text = getLocaleWithTag('braveWalletTermsOfServiceCheckboxText')
-  return (
-    <p key={text.duringTag}>
-      {text.beforeTag}
-      <TermsLink
+  const text = formatLocale('braveWalletTermsOfServiceCheckboxText', {
+    $1: content => <TermsLink
         href='https://brave.com/terms-of-use/'
         target='_blank'
         rel='noopener noreferrer'
@@ -41,9 +38,12 @@ const TermsOfUseText: React.FC<{}> = () => {
           (e) => e.stopPropagation()
         }
       >
-        {text.duringTag}
+        {content}
       </TermsLink>
-      {text.afterTag}
+  })
+  return (
+    <p>
+      {text}
     </p>
   )
 }
diff --git a/components/brave_wallet_ui/page/screens/onboarding/import_hardware_wallet_welcome/import_hardware_wallet_welcome.tsx b/components/brave_wallet_ui/page/screens/onboarding/import_hardware_wallet_welcome/import_hardware_wallet_welcome.tsx
index ea9bb2bc66fe..2bc49047a1fe 100644
--- a/components/brave_wallet_ui/page/screens/onboarding/import_hardware_wallet_welcome/import_hardware_wallet_welcome.tsx
+++ b/components/brave_wallet_ui/page/screens/onboarding/import_hardware_wallet_welcome/import_hardware_wallet_welcome.tsx
@@ -7,7 +7,7 @@ import * as React from 'react'
 import { useHistory } from 'react-router'
 
 // utils
-import { getLocale, splitStringForTag } from '../../../../../common/locale'
+import { getLocale, formatLocale } from '$web-common/locale'
 import { WalletRoutes } from '../../../../constants/types'
 
 // components
@@ -24,6 +24,14 @@ import {
 import { ContinueButton } from '../onboarding.style'
 import { Column, VerticalSpace } from '../../../../components/shared/style'
 
+const walletHardwareDescription = formatLocale(
+  'braveWalletConnectHardwareDescription',
+  {
+    $1: <Bold>{getLocale('braveWalletConnectHardwareLedger')}</Bold>,
+    $2: <Bold>{getLocale('braveWalletConnectHardwareTrezor')}</Bold>
+  }
+)
+
 export const OnboardingImportHardwareWalletWelcome = () => {
   const history = useHistory()
 
@@ -31,10 +39,6 @@ export const OnboardingImportHardwareWalletWelcome = () => {
     history.push(WalletRoutes.OnboardingHardwareWalletCreatePassword)
   }
 
-  const { beforeTag, duringTag, afterTag } = splitStringForTag(
-    getLocale('braveWallectConnectHardwareDescription')
-  )
-
   return (
     <OnboardingContentLayout
       title={getLocale('braveWalletConnectHardwareWallet')}
@@ -45,11 +49,7 @@ export const OnboardingImportHardwareWalletWelcome = () => {
       </Description>
       <VerticalSpace space='28px' />
       <Description>
-        {beforeTag}
-        <Bold>{getLocale('braveWalletConnectHardwareLedger')}</Bold>
-        {duringTag}
-        <Bold>{getLocale('braveWalletConnectHardwareTrezor')}</Bold>
-        {afterTag}
+        {walletHardwareDescription}
       </Description>
       <Column margin='117px 0 88px 0'>
         <ContinueButton onClick={onClickContinue}>
diff --git a/components/brave_wallet_ui/page/screens/swap/components/swap/routes/route_option.tsx b/components/brave_wallet_ui/page/screens/swap/components/swap/routes/route_option.tsx
index 964a0d1eeb2b..4e4e7b3e80e0 100644
--- a/components/brave_wallet_ui/page/screens/swap/components/swap/routes/route_option.tsx
+++ b/components/brave_wallet_ui/page/screens/swap/components/swap/routes/route_option.tsx
@@ -31,8 +31,8 @@ import {
 // Utils
 import {
   getLocale,
-  splitStringForTag
-} from '../../../../../../../common/locale'
+  formatLocale
+} from '$web-common/locale'
 import {
   formatDateAsRelative //
 } from '../../../../../../utils/datetime-utils'
@@ -295,17 +295,24 @@ export const RouteOption = (props: Props) => {
               .reverse()
               .map(source => {
                 const lpIcon = getLPIcon(source)
-                const descriptionString = getLocale(
-                  'braveWalletExchangeViaProvider'
-                )
-                  .replace('$5', source.name)
-                  .replace('$6', SwapProviderNameMapping[option.provider] ?? '')
-                const { duringTag: exchange, afterTag } = splitStringForTag(
-                  descriptionString,
-                  1
-                )
-                const { beforeTag: via, duringTag: provider } =
-                  splitStringForTag(afterTag || '', 3)
+                const exchangeViaProvider = formatLocale('braveWalletExchangeViaProvider', {
+                  $1: <Text
+                      textSize='16px'
+                      isBold={true}
+                      textColor='primary'
+                    >
+                      {source.name}
+                    </Text>,
+                  $2: <Text
+                    textSize='12px'
+                    isBold={false}
+                    textColor='tertiary'
+                  >
+                    {SwapProviderNameMapping[option.provider] ?? ''}
+                  </Text>,
+                  $3: source.name,
+                  $4: SwapProviderNameMapping[option.provider] ?? ''
+                })
                 return (
                   <Column
                     fullWidth={true}
@@ -333,26 +340,13 @@ export const RouteOption = (props: Props) => {
                         width='unset'
                         gap='4px'
                       >
+
                         <Text
                           textSize='16px'
                           isBold={true}
                           textColor='primary'
                         >
-                          {exchange}
-                        </Text>
-                        <Text
-                          textSize='12px'
-                          isBold={false}
-                          textColor='tertiary'
-                        >
-                          {via}
-                        </Text>
-                        <Text
-                          textSize='16px'
-                          isBold={true}
-                          textColor='primary'
-                        >
-                          {provider}
+                          {exchangeViaProvider}
                         </Text>
                         <PercentBubble
                           padding='4px 6px'
diff --git a/components/brave_wallet_ui/stories/locale.ts b/components/brave_wallet_ui/stories/locale.ts
index 017c72c3f47c..5e7dcb7f8f74 100644
--- a/components/brave_wallet_ui/stories/locale.ts
+++ b/components/brave_wallet_ui/stories/locale.ts
@@ -109,7 +109,7 @@ provideStrings({
   braveWalletReceiveEstimate: 'Receive (est.)',
   braveWalletFromToken: 'From token',
   braveWalletToToken: 'To token',
-  braveWalletNewQuoteIn: 'New quote in $1$3$2',
+  braveWalletNewQuoteIn: 'New quote in $1',
 
   // Send Tab
   braveWalletChooseAssetToSend: 'Choose asset to send',
@@ -205,7 +205,7 @@ provideStrings({
     'regain access in the event of a ' +
     'lost password, stolen device, or similar circumstance.',
   braveWalletTermsOfServiceCheckboxText:
-    'I have read and agree to the $1Terms of use$2.',
+    'I have read and agree to the $1Terms of use$1.',
 
   // Onboarding import or restore wallet page
   braveWalletCheckingInstalledExtensions: 'Checking for wallet extensions...',
@@ -434,7 +434,7 @@ provideStrings({
   braveWalletPrivateKeyImportType: 'Import type',
   braveWalletFilImportPrivateKeyFormatDescription:
     'NOTE: Private key must be provided as a hex-encoded JSON that contains ' +
-    'private key and protocol information. $1Learn more$2.',
+    'private key and protocol information. $1Learn more$1.',
 
   // Import Account
   braveWalletImportAccountDisclaimer:
@@ -476,7 +476,7 @@ provideStrings({
   braveWalletConnectHardwareSearchNothingFound: 'No results found.',
   braveWalletAuthorizeHardwareWallet: 'Authorize your wallet',
   braveWalletConnectHardwareWallet: 'Connect hardware wallet',
-  braveWallectConnectHardwareDescription:
+  braveWalletConnectHardwareDescription:
     'We currently support $1 and $2 devices.',
   braveWalletImportHardwareWalletDescription:
     'Connect your hardware wallet to manage your assets directly ' +
@@ -641,7 +641,7 @@ provideStrings({
   braveWalletSwapOnNetworkViaExchange: 'Swap on $1 via $2',
   braveWalletBridgeFromNetworkToNetwork: 'Bridge from $1 to $2',
   braveWalletExchangeNamePlusSteps: '$1 + $2 $3',
-  braveWalletExchangeViaProvider: '$1$5$2 via $3$6$4',
+  braveWalletExchangeViaProvider: '$1 via $2',
   braveWalletSortBy: 'Sort by',
 
   // Buy
@@ -681,7 +681,7 @@ provideStrings({
     'Meld.io to complete the transaction, including your wallet address ' +
     'and country code. For more information please read Meld’s terms of use.',
   braveWalletMeldTermsOfUse:
-    'I have read and agree to the $1Meld’s Terms of use$2',
+    'I have read and agree to the $1Meld’s Terms of use$1',
   braveWalletBestOption: 'Best Option',
   braveWalletExchangeRateWithFees: 'Exchange rate with fees',
   braveWalletFees: 'Fees',
@@ -762,7 +762,7 @@ provideStrings({
   braveWalletProvideEncryptionKeyTitle:
     'A DApp is requesting your public encryption key',
   braveWalletProvideEncryptionKeyDescription:
-    '$1$url$2 is requesting your wallets public encryption key. ' +
+    '$1 is requesting your wallets public encryption key. ' +
     'If you consent to providing this key, the site will be able to ' +
     'compose encrypted messages to you.',
   braveWalletProvideEncryptionKeyButton: 'Provide',
@@ -1241,26 +1241,26 @@ provideStrings({
   braveWalletTransactionTakingLongTime: 'Taking longer than expected?',
   braveWalletViewInActivity: 'View in activity',
   braveWalletSafelyDismissWindow: 'You can safely dismiss this window.',
-  braveWalletSendingAmountToAccount: 'Sending $1$5$2 to $3$6$4',
-  braveWalletAmountSentToAccount: '$1$5$2 has been sent to account $3$6$4',
-  braveWalletSwappingAmountToAmountOnNetwork: 'Swapping $1$5$2 to $3$6$4 on $7',
+  braveWalletSendingAmountToAccount: 'Sending $1 to $2',
+  braveWalletAmountSentToAccount: '$1 has been sent to account $2',
+  braveWalletSwappingAmountToAmountOnNetwork: 'Swapping $1 to $2 on $3',
   braveWalletAmountAddedToAccount:
-    'The amount of $1$5$2 has been added to your account $3$6$4',
-  braveWalletBridgingAmountToNetwork: 'Bridging $1$5$2 to $3$6$4',
+    'The amount of $1 has been added to your account $2',
+  braveWalletBridgingAmountToNetwork: 'Bridging $1 to $2',
   braveWalletUnableToSendSwapOrBridge: 'Unable to $1',
   braveWalletErrorAttemptingToTransact:
-    'There was an error attempting to $7 $1$5$2 to $3$6$4',
-  braveWalletApprovingAmountOnExchange: 'Approving $1$5$2 to $3$6$4',
+    'There was an error attempting to $3 $1 to $2',
+  braveWalletApprovingAmountOnExchange: 'Approving $1 to $2',
   braveWalletCancelTransactionDescription:
     'A new transaction will be created to cancel your existing transaction.',
   braveWalletPendingTransactions: '$1 more transactions pending.',
   braveWalletSwapping: 'Swapping',
   braveWalletBridging: 'Bridging',
-  braveWalletSwappingOrBridgingOnNetwork: '$1$5$2 on $3$6$4',
+  braveWalletSwappingOrBridgingOnNetwork: '$1 on $2',
   braveWalletErrorAttemptingToTransactOnNetwork:
-    'There was an error attempting to $1$5$2 on $3$6$4',
-  braveWalletShieldingAmount: 'Shielding $1$5$2',
-  braveWalletAmountHasBeenShielded: '$1$5$2 has been shielded',
+    'There was an error attempting to $1 on $2',
+  braveWalletShieldingAmount: 'Shielding $1',
+  braveWalletAmountHasBeenShielded: '$1 has been shielded',
   // Submitted
   braveWalletTransactionSubmittedTitle: 'Transaction submitted',
   braveWalletTransactionSignedTitle: 'Transaction signed',
@@ -1459,7 +1459,7 @@ provideStrings({
   braveWalletEnableNftAutoDiscoveryModalDescription:
     'Brave Wallet can use a third-party service to automatically display ' +
     'your NFTs. Brave will share your wallet addresses with ' +
-    '$1SimpleHash$2 to provide this service. $3Learn more.$4',
+    '$1SimpleHash$1 to provide this service. $2Learn more.$2',
   braveWalletEnableNftAutoDiscoveryModalConfirm: 'Yes, proceed',
   braveWalletEnableNftAutoDiscoveryModalCancel:
     "No thanks, I'll do it manually",
@@ -1468,7 +1468,7 @@ provideStrings({
     'Once an NFT is detected, it’ll be displayed here.',
   braveWalletAutoDiscoveryEmptyStateFooter: 'Can’t see your NFTs?',
   braveWalletAutoDiscoveryEmptyStateActions:
-    '$1Refresh$2 or $3Import Manually$4',
+    '$1Refresh$1 or $2Import Manually$2',
   braveWalletAutoDiscoveryEmptyStateRefresh: 'Refreshing',
 
   // Brave Wallet Rewards
diff --git a/components/brave_welcome_ui/components/help-improve/index.tsx b/components/brave_welcome_ui/components/help-improve/index.tsx
index d8197eb7831f..f07cf35970f9 100644
--- a/components/brave_welcome_ui/components/help-improve/index.tsx
+++ b/components/brave_welcome_ui/components/help-improve/index.tsx
@@ -8,7 +8,7 @@ import * as React from 'react'
 import * as S from './style'
 import Button from '$web-components/button'
 import { P3APhase, WelcomeBrowserProxyImpl } from '../../api/welcome_browser_proxy'
-import { getLocale, splitStringForTag } from '$web-common/locale'
+import { getLocale, formatLocale } from '$web-common/locale'
 
 interface InputCheckboxProps {
   onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void
@@ -17,7 +17,33 @@ interface InputCheckboxProps {
   isChecked: boolean
 }
 
-function InputCheckbox (props: InputCheckboxProps) {
+const changeSettingsNote = formatLocale('braveWelcomeChangeSettingsNote', {
+  $1: content => <a href="brave://settings/privacy" onClick={() => {
+    WelcomeBrowserProxyImpl.getInstance().openSettingsPage()
+  }}>
+    {content}
+  </a>
+})
+
+const readPrivacyPolicy = formatLocale('braveWelcomePrivacyPolicyNote', {
+  $1: content => <a href='https://brave.com/privacy/browser' target='_blank'>
+    {content}
+  </a>
+})
+
+const diagnosticReportsLabel = formatLocale('braveWelcomeSendReportsLabel', {
+  $1: content => <a href='https://support.brave.com/hc/en-us/articles/360017905872-How-do-I-enable-or-disable-automatic-crash-reporting' target='_blank'>
+    {content}
+  </a>
+})
+
+const braveProductUsageDataLabel = formatLocale('braveWelcomeSendInsightsLabel', {
+  $1: content => <a href='https://support.brave.com/hc/en-us/articles/9140465918093-What-is-P3A-in-Brave-' target='_blank'>
+    {content}
+  </a>
+})
+
+function InputCheckbox(props: InputCheckboxProps) {
   return (
     <label className="item">
       <input
@@ -32,7 +58,7 @@ function InputCheckbox (props: InputCheckboxProps) {
   )
 }
 
-function HelpImprove () {
+function HelpImprove() {
   const [isMetricsReportingEnabled, setMetricsReportingEnabled] = React.useState(true)
   const [isP3AEnabled, setP3AEnabled] = React.useState(true)
   const [completeURLPromise] = React.useState(() => {
@@ -56,15 +82,6 @@ function HelpImprove () {
     })
   }
 
-  const handleOpenSettingsPage = () => {
-    WelcomeBrowserProxyImpl.getInstance().openSettingsPage()
-  }
-
-  const changeSettingsNote = splitStringForTag(getLocale('braveWelcomeChangeSettingsNote'))
-  const readPrivacyPolicy = splitStringForTag(getLocale('braveWelcomePrivacyPolicyNote'))
-  const diagnosticReportsLabel = splitStringForTag(getLocale('braveWelcomeSendReportsLabel'))
-  const braveProductUsageDataLabel = splitStringForTag(getLocale('braveWelcomeSendInsightsLabel'))
-
   return (
     <S.MainBox>
       <div className="view-header-box">
@@ -79,22 +96,14 @@ function HelpImprove () {
             onChange={handleMetricsReportingChange}
             isChecked={isMetricsReportingEnabled}
           >
-            {diagnosticReportsLabel.beforeTag}
-            <a href="https://support.brave.com/hc/en-us/articles/360017905872-How-do-I-enable-or-disable-automatic-crash-reporting" target="_blank">
-              {diagnosticReportsLabel.duringTag}
-            </a>
-            {diagnosticReportsLabel.afterTag}
+            {diagnosticReportsLabel}
           </InputCheckbox>
           <InputCheckbox
             id="p3a"
             onChange={handleP3AChange}
             isChecked={isP3AEnabled}
           >
-            {braveProductUsageDataLabel.beforeTag}
-            <a href="https://support.brave.com/hc/en-us/articles/9140465918093-What-is-P3A-in-Brave-" target="_blank">
-              {braveProductUsageDataLabel.duringTag}
-            </a>
-            {braveProductUsageDataLabel.afterTag}
+            {braveProductUsageDataLabel}
           </InputCheckbox>
         </div>
       </S.Grid>
@@ -108,17 +117,9 @@ function HelpImprove () {
             {getLocale('braveWelcomeFinishButtonLabel')}
           </Button>
           <S.FootNote>
-            {changeSettingsNote.beforeTag}
-            <a href="brave://settings/privacy" onClick={handleOpenSettingsPage}>
-              {changeSettingsNote.duringTag}
-            </a>
-            {changeSettingsNote.afterTag}
+            {changeSettingsNote}
             <span>
-              {readPrivacyPolicy.beforeTag}
-              <a href="https://brave.com/privacy/browser" target="_blank">
-                {readPrivacyPolicy.duringTag}
-              </a>
-              {readPrivacyPolicy.afterTag}
+              {readPrivacyPolicy}
             </span>
           </S.FootNote>
         </div>
diff --git a/components/brave_welcome_ui/stories/locale.ts b/components/brave_welcome_ui/stories/locale.ts
index 7d8809832adb..01ced07468e5 100644
--- a/components/brave_welcome_ui/stories/locale.ts
+++ b/components/brave_welcome_ui/stories/locale.ts
@@ -21,11 +21,11 @@ provideStrings({
   braveWelcomeSetDefaultButtonLabel: 'Set Brave as default browser',
   braveWelcomeSelectAllButtonLabel: 'Select All',
   braveWelcomeHelpImproveBraveTitle: 'Help make Brave better.',
-  braveWelcomeSendReportsLabel: 'Send diagnostic reports if you experience a crash or freeze. $1Learn more.$2',
-  braveWelcomeSendInsightsLabel: 'Share private and anonymous product insights about what features are being used by Brave\'s users. $1Learn more.$2',
+  braveWelcomeSendReportsLabel: 'Send diagnostic reports if you experience a crash or freeze. $1Learn more.$1',
+  braveWelcomeSendInsightsLabel: 'Share private and anonymous product insights about what features are being used by Brave\'s users. $1Learn more.$1',
   braveWelcomeSetupCompleteLabel: 'Setup complete',
-  braveWelcomeChangeSettingsNote: ' Change these choices at any time in Brave at $1brave://settings/privacy$2.',
-  braveWelcomePrivacyPolicyNote: 'Read our full $1Privacy Policy$2',
+  braveWelcomeChangeSettingsNote: ' Change these choices at any time in Brave at $1brave://settings/privacy$1.',
+  braveWelcomePrivacyPolicyNote: 'Read our full $1Privacy Policy$1',
   braveWelcomeSelectThemeLabel: 'Choose your theme',
   braveWelcomeSelectThemeNote: 'You can change this at any time in Brave settings.',
   braveWelcomeSelectThemeSystemLabel: 'Match system setting',
diff --git a/components/common/locale.ts b/components/common/locale.ts
index 4068126542a0..db12328f274d 100644
--- a/components/common/locale.ts
+++ b/components/common/locale.ts
@@ -39,104 +39,11 @@ export const getLocale = (
   return returnVal
 }
 
-interface SplitStringForTagResult {
-  beforeTag: string
-  duringTag: string | null
-  afterTag: string | null
-}
-
-/**
- * Returns text for translations with a single HTML tag
- * (like a link or button)
- * @deprecated Use formatLocale instead.
- * @param {string} key - translation identifier
- * @param {object} replacements - replacements for specific translation, replacement should be defined as {{key}}
- * @returns {SplitStringForTagResult}
- */
-export const getLocaleWithTag = (
-  key: string,
-  replacements?: Record<string, string>
-) => {
-  const text = getLocale(key, replacements)
-  return splitStringForTag(text)
-}
-
-/**
- * Returns text for translations with a multiple HTML tags
- * (like a link or button)
- * @deprecated Use formatLocale instead.
- * @param {string} key - translation identifier
- * @param {number} tags - how many tags is in translation
- * @param {object} replacements - replacements for specific translation, replacement should be defined as {{key}}
- * @returns {SplitStringForTagResult}
- */
-export const getLocaleWithTags = (
-  key: string,
-  tags: number,
-  replacements?: Record<string, string>
-) => {
-  let text = getLocale(key, replacements)
-  let result = []
-  for (let i = 1; i <= tags; i++) {
-    const split = splitStringForTag(text, i * 2 - 1)
-    text = split.afterTag || ''
-    if (i !== tags) {
-      split.afterTag = ''
-    }
-    result.push(split)
-  }
-  return result
-}
-
-/**
- * Allows an html or xml tag, or React component etc., to be injected in to a string by extracting
- * the components of the string before, during and after the tag.
- * Usage:
- *    splitStringForTag('my string with some $1bold text$2')
- *    splitStringForTag('Get more info at $1')
- *
- * @export
- * @deprecated Use formatString instead.
- * @param {string} text
- * @param {number} tagStartNumber - starting number for the tag
- * @returns {SplitStringForTagResult}
- */
-export function splitStringForTag(
-  text: string,
-  tagStartNumber: number = 1
-): SplitStringForTagResult {
-  const tagOpenPlaceholder = `$${tagStartNumber}`
-  const tagClosePlaceholder = `$${tagStartNumber + 1}`
-  const tagStartIndex: number = text.indexOf(tagOpenPlaceholder)
-  const tagEndIndex: number = text.lastIndexOf(tagClosePlaceholder)
-  const isValid = tagStartIndex !== -1
-  let beforeTag = text
-  let duringTag: string | null = null
-  let afterTag: string | null = null
-  if (isValid) {
-    beforeTag = text.substring(0, tagStartIndex)
-    if (tagEndIndex !== -1) {
-      // Handle we have 'open' and 'close' tags
-      duringTag = text.substring(
-        tagStartIndex + tagOpenPlaceholder.length,
-        tagEndIndex
-      )
-      afterTag = text.substring(tagEndIndex + tagClosePlaceholder.length)
-    } else {
-      // Handle we have a single replacement block
-      afterTag = text.substring(tagStartIndex + tagOpenPlaceholder.length)
-    }
-  }
-
-  return {
-    beforeTag,
-    duringTag,
-    afterTag
-  }
-}
-
 type Replacement = string | React.ReactNode | ((contents: string) => React.ReactNode | string)
 type ReturnType<T extends Replacement> = T extends (string | ((contents: string) => string)) ? string : React.ReactNode
+type Options = {
+  noErrorOnMissingReplacement?: boolean
+}
 
 /**
  * Formats a string with replacements. Replacements can be strings or React
@@ -155,7 +62,7 @@ type ReturnType<T extends Replacement> = T extends (string | ((contents: string)
  *   $1: (content) => <a href="#">{content}</a>
  * }) // 'Hello <a href="#">world</a>'
  */
-export function formatString<T extends Replacement>(text: string, replacements: Record<`$${number}`, T>): ReturnType<T> {
+export function formatString<T extends Replacement>(text: string, replacements: Record<`$${string}`, T>, options?: Options): ReturnType<T> {
   const keysToReplace = Object.keys(replacements) as (keyof typeof replacements)[]
   const replacedRanges: ({
     start: number,
@@ -170,7 +77,12 @@ export function formatString<T extends Replacement>(text: string, replacements:
     const start = text.indexOf(key)
 
     // If we couldn't find the key the string or replacement is wrong.
-    if (start === -1) throw new Error(`Replacement key ${key} not found in text`)
+    if (start === -1) {
+      if (options?.noErrorOnMissingReplacement) {
+        continue
+      }
+      throw new Error(`Replacement key ${key} not found in text`)
+    }
 
     // If we find an end tag, we need to replace the entire range.
     let end = text.indexOf(key, start + key.length)
@@ -242,6 +154,6 @@ export function formatString<T extends Replacement>(text: string, replacements:
  *   $1: status => <b>${status}</b>
  * }) // <>Shields are <b>UP</b></>
  */
-export function formatLocale<T extends Replacement>(key: string, replacements: Record<`$${number}`, T>): ReturnType<T> {
-  return formatString(getLocale(key), replacements)
+export function formatLocale<T extends Replacement>(key: string, replacements: Record<`$${string}`, T>, options?: Options): ReturnType<T> {
+  return formatString(getLocale(key), replacements, options)
 }
diff --git a/components/resources/brave_news_strings.grdp b/components/resources/brave_news_strings.grdp
index 266fb1a8b494..f6023fca291d 100644
--- a/components/resources/brave_news_strings.grdp
+++ b/components/resources/brave_news_strings.grdp
@@ -7,7 +7,7 @@
     Follow your favorite sources, in a single feed. Just open a tab in Brave, scroll down, and… voila!
   </message>
   <message name="IDS_BRAVE_NEWS_INTRO_DESCRIPTION_TWO" desc="Description second paragraph for introduction to Brave News news feed section. Do not translate the title Brave News.">
-    Brave News is ad-supported with private, anonymized ads. <ph name="LINK_START">$1<ex>{</ex></ph>Learn more.<ph name="LINK_END">$2<ex>}</ex></ph>.
+    Brave News is ad-supported with private, anonymized ads. <ph name="LINK_START">$1</ph>Learn more.<ph name="LINK_END">$1</ph>.
   </message>
   <message name="IDS_BRAVE_NEWS_OPT_IN_ACTION_LABEL" desc="Button text for opting in to Brave News">
     Turn on Brave News
diff --git a/components/resources/brave_welcome_strings.grdp b/components/resources/brave_welcome_strings.grdp
index 6946cdc7deb2..84dbf0203609 100644
--- a/components/resources/brave_welcome_strings.grdp
+++ b/components/resources/brave_welcome_strings.grdp
@@ -61,11 +61,11 @@
 </message>
 
 <message name="IDS_BRAVE_WELCOME_SEND_REPORTS_LABEL" desc="A checkbox label for user to opt-in opt-out to send diagnostic reports">
-  Send diagnostic reports if you experience a crash or freeze. <ph name="LINK_BEFORE">$1</ph>Learn more.<ph name="LINK_AFTER">$2</ph>
+  Send diagnostic reports if you experience a crash or freeze. <ph name="LINK_BEFORE">$1</ph>Learn more.<ph name="LINK_AFTER">$1</ph>
 </message>
 
 <message name="IDS_BRAVE_WELCOME_SEND_INSIGHTS_LABEL" desc="A checkbox label for user to opt-in opt-out to share anonymous, product insights information">
-  Share private and anonymous product insights about what features are being used by Brave's users. <ph name="LINK_BEFORE">$1</ph>Learn more.<ph name="LINK_AFTER">$2</ph>
+  Share private and anonymous product insights about what features are being used by Brave's users. <ph name="LINK_BEFORE">$1</ph>Learn more.<ph name="LINK_AFTER">$1</ph>
 </message>
 
 <message name="IDS_BRAVE_WELCOME_SETUP_COMPLETE_LABEL" desc="A title for screen when user has completed all the steps">
@@ -73,11 +73,11 @@
 </message>
 
 <message name="IDS_BRAVE_WELCOME_CHANGE_SETTINGS_NOTE" desc="A footnote with a link to change settings.">
-  Change these choices at any time in Brave at <ph name="LINK_BEFORE1">$1</ph>brave://settings/privacy<ph name="LINK_AFTER2">$2</ph>.
+  Change these choices at any time in Brave at <ph name="LINK_BEFORE1">$1</ph>brave://settings/privacy<ph name="LINK_AFTER2">$1</ph>.
 </message>
 
 <message name="IDS_BRAVE_WELCOME_PRIVACY_POLICY_NOTE" desc="A footnote with a link to Brave's privacy polcy.">
-  Read our full <ph name="LINK_BEFORE">$1</ph>Privacy Policy<ph name="LINK_AFTER">$2</ph>
+  Read our full <ph name="LINK_BEFORE">$1</ph>Privacy Policy<ph name="LINK_AFTER">$1</ph>
 </message>
 
 <message name="IDS_BRAVE_WELCOME_SELECT_THEME_LABEL" desc="A title for screen to select color theme">
diff --git a/components/resources/rewards_strings.grdp b/components/resources/rewards_strings.grdp
index 52923e4e6f3d..450119a2c215 100644
--- a/components/resources/rewards_strings.grdp
+++ b/components/resources/rewards_strings.grdp
@@ -654,7 +654,7 @@
     It looks like connecting <ph name="PROVIDER">$1<ex>Uphold</ex></ph> to Brave Rewards is temporarily unavailable. Please try again later.
   </message>
   <message name="IDS_REWARDS_AUTHORIZE_PROVIDER_UNAVAILABLE_TEXT_2" desc="">
-    You can view <ph name="BEGIN_LINK">$2</ph>service status here<ph name="END_LINK">$3</ph>.
+    You can view <ph name="BEGIN_LINK">$2</ph>service status here<ph name="END_LINK">$2</ph>.
   </message>
   <message name="IDS_REWARDS_AUTHORIZE_PROVIDER_UNAVAILABLE_TITLE" desc="">
     Connecting to selected custodian temporarily unavailable
@@ -663,7 +663,7 @@
     Your <ph name="PROVIDER">$1<ex>Uphold</ex></ph> account can't be connected to your Brave Rewards profile at this time. Your <ph name="PROVIDER">$1<ex>Uphold</ex></ph> account is registered in a country that's not currently supported for connecting to Brave Rewards.
   </message>
   <message name="IDS_REWARDS_AUTHORIZE_REGION_NOT_SUPPORTED_TEXT_2" desc="">
-    See the <ph name="LINK_BEGIN">$2</ph>current list of supported regions and learn more<ph name="END_LINK">$3</ph> about connecting a custodial account to Brave Rewards.
+    See the <ph name="LINK_BEGIN">$2</ph>current list of supported regions and learn more<ph name="END_LINK">$2</ph> about connecting a custodial account to Brave Rewards.
   </message>
   <message name="IDS_REWARDS_AUTHORIZE_REGION_NOT_SUPPORTED_TITLE" desc="">
     Region currently not supported
@@ -681,7 +681,7 @@
     Limited Uphold account functionality
   </message>
   <message name="IDS_REWARDS_AUTHORIZE_SIGNATURE_VERIFICATION_ERROR_TEXT" desc="">
-    There was a problem validating your request to connect your <ph name="PROVIDER">$1<ex>Uphold</ex></ph> account to Brave Rewards. You can <ph name="BEGIN_LINK">$2</ph>contact support<ph name="END_LINK">$3</ph> for more help.
+    There was a problem validating your request to connect your <ph name="PROVIDER">$1<ex>Uphold</ex></ph> account to Brave Rewards. You can <ph name="BEGIN_LINK">$2</ph>contact support<ph name="END_LINK">$2</ph> for more help.
   </message>
   <message name="IDS_REWARDS_AUTHORIZE_SIGNATURE_VERIFICATION_ERROR_TITLE" desc="">
     Request could not be validated
@@ -690,7 +690,7 @@
     Something went wrong
   </message>
   <message name="IDS_REWARDS_AUTHORIZE_UNEXPECTED_ERROR_TEXT" desc="">
-    We weren’t able to complete your request. Please try again, and <ph name="LINK_BEGIN">$2</ph>contact support<ph name="LINK_END">$3</ph> if the problem persists.
+    We weren’t able to complete your request. Please try again, and <ph name="LINK_BEGIN">$2</ph>contact support<ph name="LINK_END">$2</ph> if the problem persists.
   </message>
   <message name="IDS_REWARDS_AUTHORIZE_PROCESSING_TEXT" desc="">
     Your request is still being processed, please wait.
diff --git a/components/resources/wallet_strings.grdp b/components/resources/wallet_strings.grdp
index fb15f619ae02..a1a8ba81347c 100644
--- a/components/resources/wallet_strings.grdp
+++ b/components/resources/wallet_strings.grdp
@@ -144,7 +144,7 @@
       other {<ph name="EXCHANGE_NAME"><ex>0X</ex>$1</ph> + {SWAP_EXCHANGE_STEPS} steps}
     }
   </message>
-  <message name="IDS_BRAVE_WALLET_EXCHANGE_VIA_PROVIDER" desc="Exchange being used via API provider"><ph name="EXCHANGE_BEFORE">$1</ph><ph name="EXCHANGE_NAME"><ex>0x</ex>$5</ph><ph name="EXCHANGE_AFTER">$2</ph> via <ph name="PROVIDER_BEFORE">$3</ph><ph name="PROVIDER_NAME"><ex>Li.Fi</ex>$6</ph><ph name="PROVIDER_AFTER">$4</ph></message>
+  <message name="IDS_BRAVE_WALLET_EXCHANGE_VIA_PROVIDER" desc="Exchange being used via API provider"><ph name="EXCHANGE_NAME"><ex>0x</ex>$1</ph> via <ph name="PROVIDER_NAME"><ex>Li.Fi</ex>$2</ph></message>
   <message name="IDS_BRAVE_WALLET_SORT_BY" desc="Sort by button label">Sort by</message>
   <message name="IDS_BRAVE_WALLET_REVIEW_BRIDGE" desc="Review bridge button">Review bridge</message>
   <message name="IDS_BRAVE_WALLET_EST_TIME" desc="Bridge and Swap estimated time label">Est time</message>
@@ -324,8 +324,8 @@
   <message name="IDS_BRAVE_WALLET_ICON_URL" desc="Visible assets modal Icon URL label">Icon URL</message>
   <message name="IDS_BRAVE_WALLET_ADD_ASSET" desc="Asset Panel add asset button">Add asset</message>
   <message name="IDS_BRAVE_WALLET_ACCOUNT_SETTINGS_DISCLAIMER" desc="Account settings modal warning">WARNING: Never share your private key. Anyone with this private key can take your assets forever.</message>
-  <message name="IDS_BRAVE_WALLET_FIL_EXPORT_PRIVATE_KEY_FORMAT_DESCRIPTION" desc="Filecoin private key format description">NOTE: Private key is provided as a hex-encoded JSON that contains private key and protocol information. <ph name="LINK_START">$1</ph>Learn more<ph name="LINK_END">$2</ph>.</message>
-  <message name="IDS_BRAVE_WALLET_FIL_IMPORT_PRIVATE_KEY_FORMAT_DESCRIPTION" desc="Filecoin private key format description">NOTE: Private key must be provided as a hex-encoded JSON that contains private key and protocol information. <ph name="LINK_START">$1</ph>Learn more<ph name="LINK_END">$2</ph>.</message>
+  <message name="IDS_BRAVE_WALLET_FIL_EXPORT_PRIVATE_KEY_FORMAT_DESCRIPTION" desc="Filecoin private key format description">NOTE: Private key is provided as a hex-encoded JSON that contains private key and protocol information. <ph name="LINK_START">$1</ph>Learn more<ph name="LINK_END">$1</ph>.</message>
+  <message name="IDS_BRAVE_WALLET_FIL_IMPORT_PRIVATE_KEY_FORMAT_DESCRIPTION" desc="Filecoin private key format description">NOTE: Private key must be provided as a hex-encoded JSON that contains private key and protocol information. <ph name="LINK_START">$1</ph>Learn more<ph name="LINK_END">$1</ph>.</message>
   <message name="IDS_BRAVE_WALLET_BTC_IMPORT_PRIVATE_KEY_FORMAT_DESCRIPTION" desc="Bitcoin private key input format description">NOTE: Only <ph name="FORMAT"><ex>zprv</ex>$1</ph> extended keys are supported.</message>
   <message name="IDS_BRAVE_WALLET_ACCOUNTS_EXPORT" desc="Account list item Export button">Export</message>
   <message name="IDS_BRAVE_WALLET_ACCOUNTS_DEPOSIT" desc="Account list item Deposit button">Deposit</message>
@@ -368,7 +368,7 @@
   <message name="IDS_BRAVE_WALLET_SIGN_TRANSACTION_EIP712_MESSAGE_DOMAIN" desc="Sign panel EIP-712 message domain">Domain</message>
   <message name="IDS_BRAVE_WALLET_SIGN_TRANSACTION_BUTTON" desc="Sign panel sign button">Sign</message>
   <message name="IDS_BRAVE_WALLET_PROVIDE_ENCRYPTION_KEY_TITLE" desc="Provide Encryption Key Panel Title">A DApp is requesting your public encryption key</message>
-  <message name="IDS_BRAVE_WALLET_PROVIDE_ENCRYPTION_KEY_DESCRIPTION" desc="Provide Encryption Key Panel Details"><ph name="URL_BEFORE">$1</ph>$url<ph name="URL_AFTER">$2</ph> is requesting your wallet's public encryption key. If you consent to providing this key, the site will be able to compose encrypted messages to you.</message>
+  <message name="IDS_BRAVE_WALLET_PROVIDE_ENCRYPTION_KEY_DESCRIPTION" desc="Provide Encryption Key Panel Details"><ph name="URL">$1</ph> is requesting your wallet's public encryption key. If you consent to providing this key, the site will be able to compose encrypted messages to you.</message>
   <message name="IDS_BRAVE_WALLET_PROVIDE_ENCRYPTION_KEY_BUTTON" desc="Provide Encryption Key Panel Provide Button">Provide</message>
   <message name="IDS_BRAVE_WALLET_READ_ENCRYPTED_MESSAGE_TITLE" desc="Read Encrypted Message Panel Title">This DApp would like to read this message to complete your request</message>
   <message name="IDS_BRAVE_WALLET_READ_ENCRYPTED_MESSAGE_DECRYPT_BUTTON" desc="Read Encrypted Message Panel Decrypt Button">Decrypt message</message>
@@ -639,7 +639,7 @@
   <message name="IDS_BRAVE_WALLET_BUY_DISCLAIMER" desc="Brave disclaimer text in buy screen">Financial and transaction data is processed by our onramp partners. Brave does not collect or have access to such data.</message>
   <message name="IDS_BRAVE_WALLET_TRANSACTIONS_PARTNER" desc="Transactions partner modal title">Transactions partner</message>
   <message name="IDS_BRAVE_WALLET_TRANSACTION_PARTNER_CONSENT" desc="Meld consent description">Brave Wallet uses Meld.io to help aggregate and surface various crypto providers for your region. We will share information with Meld.io to complete the transaction, including your wallet address and country code. For more information please read Meld’s terms of use.</message>
-  <message name="IDS_BRAVE_WALLET_MELD_TERMS_OF_USE" desc="Meld terms of use checkbox label">I have read and agree to the <ph name="LINK_BEFORE">$1</ph>Meld's Terms of use<ph name="LINK_AFTER">$2</ph></message>
+  <message name="IDS_BRAVE_WALLET_MELD_TERMS_OF_USE" desc="Meld terms of use checkbox label">I have read and agree to the <ph name="LINK_BEFORE">$1</ph>Meld's Terms of use<ph name="LINK_AFTER">$1</ph></message>
   <message name="IDS_BRAVE_WALLET_BEST_OPTION" desc="Best option label for buy option">Best Option</message>
   <message name="IDS_BRAVE_WALLET_EXCHANGE_RATE_WITH_FEES" desc="Buy exchange rate with fees label">Exchange rate with fees</message>
   <message name="IDS_BRAVE_WALLET_FEES" desc="Buy fees label">Fees</message>
@@ -710,21 +710,21 @@
   <message name="IDS_BRAVE_WALLET_TRANSACTION_TAKING_LONG_TIME" desc="A transaction is taking a long time label">Taking longer than expected?</message>
   <message name="IDS_BRAVE_WALLET_VIEW_IN_ACTIVITY" desc="View in activity tab button">View in activity</message>
   <message name="IDS_BRAVE_WALLET_SAFELY_DISMISS_WINDOW" desc="You can safely dismiss this window message">You can safely dismiss this window.</message>
-  <message name="IDS_BRAVE_WALLET_SENDING_AMOUNT_TO_ACCOUNT" desc="Transaction intent sending amount to account label">Sending <ph name="AMOUNT_BEFORE">$1</ph><ph name="AMOUNT"><ex>20 ETH</ex>$5</ph><ph name="AMOUNT_AFTER">$2</ph> to <ph name="ACCOUNT_BEFORE">$3</ph><ph name="ACCOUNT"><ex>Ethereum Account 2</ex>$6</ph><ph name="ACCOUNT_AFTER">$4</ph></message>
-  <message name="IDS_BRAVE_WALLET_AMOUNT_SENT_TO_ACCOUNT" desc="Transaction intent amount sent to account label"><ph name="AMOUNT_BEFORE">$1</ph><ph name="AMOUNT"><ex>20 ETH</ex>$5</ph><ph name="AMOUNT_AFTER">$2</ph> has been sent to account <ph name="ACCOUNT_BEFORE">$3</ph><ph name="ACCOUNT"><ex>Ethereum Account 2</ex>$6</ph><ph name="ACCOUNT_AFTER">$4</ph></message>
-  <message name="IDS_BRAVE_WALLET_SWAPPING_AMOUNT_TO_ACCOUNT_ON_NETWORK" desc="Transaction intent swapping amount to account on network label">Swapping <ph name="AMOUNT_BEFORE">$1</ph><ph name="AMOUNT"><ex>20 ETH</ex>$5</ph><ph name="AMOUNT_AFTER">$2</ph> to <ph name="ACCOUNT_BEFORE">$3</ph><ph name="ACCOUNT"><ex>Ethereum Account 2</ex>$6</ph><ph name="ACCOUNT_AFTER">$4</ph> on <ph name="NETWORK"><ex>Ethereum Mainnet</ex>$7</ph></message>
-  <message name="IDS_BRAVE_WALLET_AMOUNT_ADDED_TO_ACCOUNT" desc="Transaction intent amount added to account label">The amount of <ph name="AMOUNT_BEFORE">$1</ph><ph name="AMOUNT"><ex>20 ETH</ex>$5</ph><ph name="AMOUNT_AFTER">$2</ph> has been added to your account <ph name="ACCOUNT_BEFORE">$3</ph><ph name="ACCOUNT"><ex>Ethereum Account 2</ex>$6</ph><ph name="ACCOUNT_AFTER">$4</ph></message>
-  <message name="IDS_BRAVE_WALLET_BRIDGING_AMOUNT_TO_NETWORK" desc="Transaction intent bridging amount to network label">Bridging <ph name="AMOUNT_BEFORE">$1</ph><ph name="AMOUNT"><ex>20 ETH</ex>$5</ph><ph name="AMOUNT_AFTER">$2</ph> to <ph name="NETWORK_BEFORE">$3</ph><ph name="NETWORK"><ex>Polygon Mainnet</ex>$6</ph><ph name="NETWORK_AFTER">$4</ph></message>
+  <message name="IDS_BRAVE_WALLET_SENDING_AMOUNT_TO_ACCOUNT" desc="Transaction intent sending amount to account label">Sending <ph name="AMOUNT"><ex>20 ETH</ex>$1</ph> to <ph name="ACCOUNT"><ex>Ethereum Account 2</ex>$2</ph></message>
+  <message name="IDS_BRAVE_WALLET_AMOUNT_SENT_TO_ACCOUNT" desc="Transaction intent amount sent to account label"><ph name="AMOUNT"><ex>20 ETH</ex>$1</ph> has been sent to account <ph name="ACCOUNT"><ex>Ethereum Account 2</ex>$2</ph></message>
+  <message name="IDS_BRAVE_WALLET_SWAPPING_AMOUNT_TO_ACCOUNT_ON_NETWORK" desc="Transaction intent swapping amount to account on network label">Swapping <ph name="AMOUNT"><ex>20 ETH</ex>$1</ph> to <ph name="ACCOUNT"><ex>Ethereum Account 2</ex>$2</ph> on <ph name="NETWORK"><ex>Ethereum Mainnet</ex>$3</ph></message>
+  <message name="IDS_BRAVE_WALLET_AMOUNT_ADDED_TO_ACCOUNT" desc="Transaction intent amount added to account label">The amount of <ph name="AMOUNT"><ex>20 ETH</ex>$1</ph> has been added to your account <ph name="ACCOUNT"><ex>Ethereum Account 2</ex>$2</ph></message>
+  <message name="IDS_BRAVE_WALLET_BRIDGING_AMOUNT_TO_NETWORK" desc="Transaction intent bridging amount to network label">Bridging <ph name="AMOUNT"><ex>20 ETH</ex>$1</ph> to <ph name="NETWORK"><ex>Polygon Mainnet</ex>$2</ph></message>
   <message name="IDS_BRAVE_WALLET_UNABLE_TO_SEND_SWAP_OR_BRIDGE" desc="Transaction intent bridging amount to network label">Unable to <ph name="SEND_SWAP_OR_BRIDGE"><ex>Bridge</ex>$1</ph></message>
-  <message name="IDS_BRAVE_WALLET_ERROR_ATTEMPTING_TO_TRANSACT" desc="Transaction intent bridging amount to network label">There was an error attempting to <ph name="SEND_SWAP_OR_BRIDGE"><ex>Bridge</ex>$7</ph> <ph name="AMOUNT_BEFORE">$1</ph><ph name="AMOUNT"><ex>20 ETH</ex>$5</ph><ph name="AMOUNT_AFTER">$2</ph> to <ph name="VALUE_BEFORE">$3</ph><ph name="VALUE"><ex>Polygon Mainnet</ex>$6</ph><ph name="VALUE_AFTER">$4</ph></message>
-  <message name="IDS_BRAVE_WALLET_APPROVING_AMOUNT_ON_EXCHANGE" desc="Transaction intent bridging amount to network label">Approving <ph name="AMOUNT_BEFORE">$1</ph><ph name="AMOUNT"><ex>20 ETH</ex>$5</ph><ph name="AMOUNT_AFTER">$2</ph> to <ph name="EXCHANGE_BEFORE">$3</ph><ph name="EXCHANGE"><ex>Li.Fi Excahnge</ex>$6</ph><ph name="EXCHANGE_AFTER">$4</ph></message>
+  <message name="IDS_BRAVE_WALLET_ERROR_ATTEMPTING_TO_TRANSACT" desc="Transaction intent bridging amount to network label">There was an error attempting to <ph name="SEND_SWAP_OR_BRIDGE"><ex>Bridge</ex>$3</ph> <ph name="AMOUNT"><ex>20 ETH</ex>$1</ph> to <ph name="VALUE"><ex>Polygon Mainnet</ex>$2</ph></message>
+  <message name="IDS_BRAVE_WALLET_APPROVING_AMOUNT_ON_EXCHANGE" desc="Transaction intent bridging amount to network label">Approving <ph name="AMOUNT"><ex>20 ETH</ex>$1</ph> to <ph name="EXCHANGE"><ex>Li.Fi Excahnge</ex>$2</ph></message>
   <message name="IDS_BRAVE_WALLET_CANCEL_TRANSACTION_DESCRIPTION" desc="Cancel transaction panel description">A new transaction will be created to cancel your existing transaction.</message>
   <message name="IDS_BRAVE_WALLET_SWAPPING" desc="Transaction intent Swapping label">Swapping</message>
   <message name="IDS_BRAVE_WALLET_BRIDGING" desc="Transaction intent Bridging label">Bridging</message>
-  <message name="IDS_BRAVE_WALLET_SWAPPING_OR_BRIDGING_ON_NETWORK" desc="Transaction intent Swapping or Bridging on network description"><ph name="SWAPPING_OR_BRIDGING_BEFORE">$1</ph><ph name="SWAPPING_OR_BRIDGING"><ex>Swapping</ex>$5</ph><ph name="SWAPPING_OR_BRIDGING_AFTER">$2</ph> on <ph name="NETWORK_BEFORE">$3</ph><ph name="NETWORK"><ex>Solana Mainnet Beta</ex>$6</ph><ph name="NETWORK_AFTER">$4</ph></message>
-  <message name="IDS_BRAVE_WALLET_ERROR_ATTEMPTING_TO_TRANSACT_ON_NETWORK" desc="Transaction intent error attempting to Swap or Bride on network description">There was an error attempting to <ph name="SWAP_OR_BRIDGE_BEFORE">$1</ph><ph name="SWAP_OR_BRIDGE"><ex>swap</ex>$5</ph><ph name="SWAP_OR_BRIDGE_AFTER">$2</ph> on <ph name="NETWORK_BEFORE">$3</ph><ph name="NETWORK"><ex>Solana Mainnet Beta</ex>$6</ph><ph name="NETWORK_AFTER">$4</ph></message>
-  <message name="IDS_BRAVE_WALLET_SHIELDING_AMOUNT" desc="Transaction intent shielding amount description">Shielding <ph name="AMOUNT_BEFORE">$1</ph><ph name="AMOUNT"><ex>1.5 ZEC</ex>$5</ph><ph name="AMOUNT_AFTER">$2</ph></message>
-  <message name="IDS_BRAVE_WALLET_AMOUNT_HAS_BEEN_SHIELDED" desc="Transaction intent amount has been shielded description"><ph name="AMOUNT_BEFORE">$1</ph><ph name="AMOUNT"><ex>1.5 ZEC</ex>$5</ph><ph name="AMOUNT_AFTER">$2</ph> has been shielded</message>
+  <message name="IDS_BRAVE_WALLET_SWAPPING_OR_BRIDGING_ON_NETWORK" desc="Transaction intent Swapping or Bridging on network description"><ph name="SWAPPING_OR_BRIDGING"><ex>Swapping</ex>$1</ph> on <ph name="NETWORK"><ex>Solana Mainnet Beta</ex>$2</ph></message>
+  <message name="IDS_BRAVE_WALLET_ERROR_ATTEMPTING_TO_TRANSACT_ON_NETWORK" desc="Transaction intent error attempting to Swap or Bride on network description">There was an error attempting to <ph name="SWAP_OR_BRIDGE"><ex>swap</ex>$1</ph> on <ph name="NETWORK"><ex>Solana Mainnet Beta</ex>$2</ph></message>
+  <message name="IDS_BRAVE_WALLET_SHIELDING_AMOUNT" desc="Transaction intent shielding amount description">Shielding <ph name="AMOUNT"><ex>1.5 ZEC</ex>$1</ph></message>
+  <message name="IDS_BRAVE_WALLET_AMOUNT_HAS_BEEN_SHIELDED" desc="Transaction intent amount has been shielded description"><ph name="AMOUNT"><ex>1.5 ZEC</ex>$1</ph> has been shielded</message>
   <message name="IDS_BRAVE_WALLET_PENDING_TRANSACTIONS" desc="Transaction complete panel, amount of transactions still pending.">
     {PENDING_TRANSACTIONS, plural,
       =1 {{PENDING_TRANSACTIONS} more transaction pending.}
@@ -926,7 +926,7 @@
   <message name="IDS_BRAVE_WALLET_DISCLOSURES_TITLE" desc="Title text for Wallet Disclosures page">Before we begin</message>
   <message name="IDS_BRAVE_WALLET_DISCLOSURES_DESCRIPTION" desc="Description text for Wallet Disclosures page">We require that you acknowledge the items below</message>
   <message name="IDS_BRAVE_WALLET_SELF_CUSTODY_DISCLOSURE_CHECKBOX_TEXT" desc="Self-custody wallet responsibility acknowledgement text">I understand that this is a self-custody wallet, and that I am solely responsible for any associated funds, assets, or accounts, and for taking any appropriate action to secure, protect, and back up my wallet. I understand that Brave cannot access my wallet or reverse transactions on my behalf, and that my recovery phrase is the ONLY way to regain access in the event of a lost password, stolen device, or similar circumstance.</message>
-  <message name="IDS_BRAVE_WALLET_TERMS_OF_SERVICE_CHECKBOX_TEXT" desc="Wallet Terms of use acknowledgement text">I have read and agree to the <ph name="LINK_BEFORE">$1</ph>Terms of use<ph name="LINK_AFTER">$2</ph>.</message>
+  <message name="IDS_BRAVE_WALLET_TERMS_OF_SERVICE_CHECKBOX_TEXT" desc="Wallet Terms of use acknowledgement text">I have read and agree to the <ph name="LINK_BEFORE">$1</ph>Terms of use<ph name="LINK_AFTER">$1</ph>.</message>
   <message name="IDS_BRAVE_WALLET_BUY_CRYPTO_BUTTON" desc="Buy crypto button text">Buy crypto</message>
   <message name="IDS_BRAVE_WALLET_DEPOSIT_CRYPTO_BUTTON" desc="Deposit Crypto button text">Deposit</message>
   <message name="IDS_BRAVE_WALLET_CHECKING_INSTALLED_EXTENSIONS" desc="Loading text while checking for installed crypto wallet extensions">Checking for wallet extensions...</message>
@@ -1006,7 +1006,7 @@
   <message name="IDS_BRAVE_WALLET_RECEIVE_ESTIMATE" desc="Swap receive estimate label">Receive (est.)</message>
   <message name="IDS_BRAVE_WALLET_FROM_TOKEN" desc="Swap from token label">From token</message>
   <message name="IDS_BRAVE_WALLET_TO_TOKEN" desc="Swap to token label">To token</message>
-  <message name="IDS_BRAVE_WALLET_NEW_QUOTE_IN" desc="Swap new quote in label">New quote in <ph name="COUNTDOWN_BEFORE">$1</ph><ph name="COUNTDOWN">$3<ex>00:05</ex></ph><ph name="COUNTDOWN_AFTER">$2</ph></message>
+  <message name="IDS_BRAVE_WALLET_NEW_QUOTE_IN" desc="Swap new quote in label">New quote in <ph name="COUNTDOWN">$1<ex>00:05</ex></ph></message>
   <message name="IDS_BRAVE_SWAP_BALANCE" desc="Text indicating account balance">Balance:</message>
   <message name="IDS_BRAVE_SWAP_SELECT_A_TOKEN" desc="Text prompting to select a token to swap">Select a token to swap</message>
   <message name="IDS_BRAVE_SWAP_SHOW_TOKENS_WITH_ZERO_BALANCES" desc="Button text to show tokens with zero balance">Show tokens with no balance</message>
@@ -1081,13 +1081,13 @@
   <message name="IDS_BRAVE_WALLET_REMOVE_NFT_MODAL_CONFIRM" desc="Remove NFT modal confirm action">Remove</message>
   <message name="IDS_BRAVE_WALLET_EDIT_NFT_MODAL_TITLE" desc="NFT Import NFT modal title">Edit NFT</message>
   <message name="IDS_BRAVE_WALLET_ENABLE_NFT_AUTODISCOVERY_MODAL_HEADER" desc="Enable NFT auto-discovery modal header">Want your NFTs displayed automatically?</message>
-  <message name="IDS_BRAVE_WALLET_ENABLE_NFT_AUTODISCOVERY_MODAL_DESCRIPTION" desc="Enable NFT auto-discovery modal description">Brave Wallet can use a third-party service to automatically display your NFTs. Brave will share your wallet addresses with <ph name="BEFORE_UNDERLINE">$1</ph>SimpleHash<ph name="AFTER_UNDERLINE">$2</ph> to provide this service. <ph name="BEFORE_URL">$3</ph>Learn more.<ph name="AFTER_URL">$4</ph></message>
+  <message name="IDS_BRAVE_WALLET_ENABLE_NFT_AUTODISCOVERY_MODAL_DESCRIPTION" desc="Enable NFT auto-discovery modal description">Brave Wallet can use a third-party service to automatically display your NFTs. Brave will share your wallet addresses with <ph name="BEFORE_UNDERLINE">$1</ph>SimpleHash<ph name="AFTER_UNDERLINE">$1</ph> to provide this service. <ph name="BEFORE_URL">$2</ph>Learn more.<ph name="AFTER_URL">$2</ph></message>
   <message name="IDS_BRAVE_WALLET_ENABLE_NFT_AUTODISCOVERY_MODAL_CONFIRM" desc="Enable NFT auto-discovery modal confirm button text">Yes, proceed</message>
   <message name="IDS_BRAVE_WALLET_ENABLE_NFT_AUTODISCOVERY_MODAL_CANCEL" desc="Enable NFT auto-discovery modal cancel button text">No thanks, I’ll do it manually</message>
   <message name="IDS_BRAVE_WALLET_AUTO_DISCOVERY_EMPTY_STATE_HEADING" desc="NFTs tab empty state heading when auto discovery is enabled">No NFTs to display</message>
   <message name="IDS_BRAVE_WALLET_AUTO_DISCOVERY_EMPTY_STATE_SUB_HEADING" desc="NFTs tab empty state sub heading when auto discovery is enabled">Once an NFT is detected, it’ll be displayed here.</message>
   <message name="IDS_BRAVE_WALLET_AUTO_DISCOVERY_EMPTY_STATE_FOOTER" desc="NFTs tab empty state footer when auto discovery is enabled">Can’t see your NFTs?</message>
-  <message name="IDS_BRAVE_WALLET_AUTO_DISCOVERY_EMPTY_STATE_ACTIONS" desc="NFTs tab empty state action button text when auto discovery is enabled"><ph name="REFRESH">$1</ph>Refresh<ph name="REFRESH_END">$2</ph> or <ph name="IMPORT">$3</ph>Import Manually<ph name="IMPORT_END">$4</ph></message>
+  <message name="IDS_BRAVE_WALLET_AUTO_DISCOVERY_EMPTY_STATE_ACTIONS" desc="NFTs tab empty state action button text when auto discovery is enabled"><ph name="REFRESH_START">$1</ph>Refresh<ph name="REFRESH_END">$1</ph> or <ph name="IMPORT_START">$2</ph>Import Manually<ph name="IMPORT_END">$2</ph></message>
   <message name="IDS_BRAVE_WALLET_AUTO_DISCOVERY_EMPTY_STATE_REFRESH" desc="NFTs tab empty state refreshing text">Refreshing</message>
   <message name="IDS_BRAVE_WALLET_NETWORK_FEES" desc="Label for blockchain network fees">Network fees</message>
   <message name="IDS_WALLET_EIP6963_PROVIDER_NAME" desc="Provider name which will be return when announce ethereum provider">Brave Wallet</message>
@@ -1124,7 +1124,7 @@
   <message name="IDS_BRAVE_WALLET_TRANSACTION_SIMULATION_DETECT_PHISHING" desc="Bullet point for transaction-simulation feature (detect phishing attempts)">Help detect phishing attempts</message>
   <message name="IDS_BRAVE_WALLET_BUTTON_NO_THANKS" desc="Button text for declining an action">No thanks</message>
   <message name="IDS_BRAVE_WALLET_BUTTON_ENABLE" desc="Button text for enabling a feature">Enable</message>
-  <message name="IDS_BRAVE_WALLET_TRANSACTION_SIMULATION_TERMS" desc="Explanation of terms of service for transaction simulations">This service is provided by <ph name="BOLD_BEFORE">$1</ph>Blowfish.xyz<ph name="BOLD_AFTER">$2</ph> and is subject to their <ph name="TERMS_LINK_BEFORE">$3</ph>Terms of Service<ph name="TERMS_LINK_AFTER">$4</ph> and <ph name="PRIVACY_POLICY_LINK_BEFORE">$5</ph>Privacy Policy<ph name="PRIVACY_POLICY_LINK_AFTER">$6</ph></message>
+  <message name="IDS_BRAVE_WALLET_TRANSACTION_SIMULATION_TERMS" desc="Explanation of terms of service for transaction simulations">This service is provided by <ph name="BOLD_BEFORE">$1</ph>Blowfish.xyz<ph name="BOLD_AFTER">$1</ph> and is subject to their <ph name="TERMS_LINK_BEFORE">$2</ph>Terms of Service<ph name="TERMS_LINK_AFTER">$2</ph> and <ph name="PRIVACY_POLICY_LINK_BEFORE">$3</ph>Privacy Policy<ph name="PRIVACY_POLICY_LINK_AFTER">$3</ph></message>
   <message name="IDS_BRAVE_WALLET_AVAILABLE" desc="Available balance label">Available</message>
   <message name="IDS_BRAVE_WALLET_AVAILABLE_BALANCE_DESCRIPTION" desc="Available balance tooltip description">Funds available for you to use.</message>
   <message name="IDS_BRAVE_WALLET_PENDING" desc="Pending balance label">Pending</message>


Reviews:
user: login: fallaciousreasoning
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T04:01:11Z
user: login: fallaciousreasoning
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T04:05:04Z
user: login: mkarolin
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-23T04:24:50Z
user: login: mkarolin
body: `strings`++
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-23T04:25:05Z
user: login: fallaciousreasoning
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T04:31:38Z
user: login: emerick
body: Rewards ++
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T13:11:47Z
user: login: yrliou
body: wallet-core++
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-28T16:37:50Z
user: login: Douglashdaniel
body: Wallet Front-end changes look good to me! 
Nice work!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T19:29:56Z

Review comments:
path: components/resources/rewards_strings.grdp
user: login: fallaciousreasoning
body: These strings are all the errors provided to  `components/brave_rewards/resources/ui/components/modalRedirect/index.tsx`

**Note:** $1 is the provider and is `replaceAll`'d in the parent component, if it exists
created_at: 2025-04-23T04:01:11Z
author_association: CONTRIBUTOR
path: components/resources/wallet_strings.grdp
user: login: fallaciousreasoning
body: Previously there was no good way to inject a string and wrap it in a tag - now its easy. A few of these had three placeholders in a row - these are replaced with a single placeholder.

// The amount
`$1$5$2` ==> `$1`

// The exchange
`$3$6$4` ==> `$2`

// The bridge
`$7` ==> `$3`
created_at: 2025-04-23T04:05:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -7,7 +7,7 @@
     Follow your favorite sources, in a single feed. Just open a tab in Brave, scroll down, and… voila!
   </message>
   <message name="IDS_BRAVE_NEWS_INTRO_DESCRIPTION_TWO" desc="Description second paragraph for introduction to Brave News news feed section. Do not translate the title Brave News.">
-    Brave News is ad-supported with private, anonymized ads. <ph name="LINK_START">$1<ex>{</ex></ph>Learn more.<ph name="LINK_END">$2<ex>}</ex></ph>.
+    Brave News is ad-supported with private, anonymized ads. <ph name="LINK_START">$1<ex>{</ex></ph>Learn more.<ph name="LINK_END">$1<ex>}</ex></ph>.
path: components/resources/brave_news_strings.grdp
user: login: mkarolin
body: I am not sure why we have `{` and `}` as `<ex>`amples here. I think we can just get rid of them.
created_at: 2025-04-23T04:24:50Z
author_association: COLLABORATOR
diff_hunk: @@ -7,7 +7,7 @@
     Follow your favorite sources, in a single feed. Just open a tab in Brave, scroll down, and… voila!
   </message>
   <message name="IDS_BRAVE_NEWS_INTRO_DESCRIPTION_TWO" desc="Description second paragraph for introduction to Brave News news feed section. Do not translate the title Brave News.">
-    Brave News is ad-supported with private, anonymized ads. <ph name="LINK_START">$1<ex>{</ex></ph>Learn more.<ph name="LINK_END">$2<ex>}</ex></ph>.
+    Brave News is ad-supported with private, anonymized ads. <ph name="LINK_START">$1<ex>{</ex></ph>Learn more.<ph name="LINK_END">$1<ex>}</ex></ph>.
path: components/resources/brave_news_strings.grdp
user: login: fallaciousreasoning
body: Removed!
created_at: 2025-04-23T04:31:38Z
author_association: CONTRIBUTOR

PR comments:
user: login: fallaciousreasoning
created_at: 2025-04-23T04:30:28Z
body: @emerick for @brave/rewards-client 
@yrliou for @brave/crypto-wallets-core 
@Douglashdaniel for @brave/crypto-wallets-front-end 
author_association: CONTRIBUTOR
user: login: brave-builds
created_at: 2025-04-23T05:08:57Z
body: A Storybook has been deployed to [preview UI for the latest push](https://build-artifacts.brave.com/brave-browser-build-pr/locale-strat/storybook.html)
author_association: COLLABORATOR
user: login: github-actions[bot]
created_at: 2025-04-23T21:52:08Z
body: [[puLL-Merge](https://github.com/brave/pull-merge)] - [brave/brave-core@28774](https://github.com/brave/brave-core/pull/28774)



### Description
This PR replaces the deprecated string tag parsing functions (`getLocaleWithTag`, `splitStringForTag`) in the Brave codebase with the new `formatLocale` and `formatString` functions. These new functions provide a more type-safe and maintainable way to format strings with dynamic content and embed React components within localized strings.

<details>
<summary><i>Changes</i></summary>

### Changes
1. **Locale utility functions**:
   - Removed deprecated functions from `components/common/locale.ts`:
     - `getLocaleWithTag`, `splitStringForTag`, and `getLocaleWithTags`
   - Updated the `formatLocale` and `formatString` functions to support more flexible replacement patterns
   - Added an optional `options` parameter to silence errors on missing replacements

2. **Updated string replacement syntax in components**:
   - Multiple components were updated to use the new `formatLocale` function, replacing direct string manipulation and deprecated tag parsing
   - Changed string templates in resource files (.grdp) to use consistent tag patterns
   - Fixed a typo in `braveWalletConnectHardwareDescription` (was `braveWallectConnectHardwareDescription`)
   - Updated string IDs to match the new replacement pattern (e.g., replacing `$1...$2` pairs with just `$1`)

3. **Brave News components**:
   - Updated `cardOptIn.tsx` to use the new string formatting API
   - Updated `DisabledPlaceholder.tsx` to use the new formatting approach

4. **Brave Wallet UI components**:
   - Fixed several components to use the new string formatting patterns, including:
     - `account-settings-modal.tsx`
     - `add-imported-account-modal.tsx`
     - `enable-nft-discovery-modal.tsx`
     - `partners_consent_modal.tsx`
     - `transaction_intent.tsx` 
     - And many more wallet-related UI components

5. **Brave Welcome UI components**:
   - Updated help-improve component to use the new string formatting API

```mermaid
sequenceDiagram
    participant Component as UI Component
    participant Formatter as String Formatter
    participant Locale as Locale System
    participant Resource as Resource Files
    
    Note over Component,Resource: Before PR
    Component->>Locale: getLocaleWithTag("key")
    Locale->>Resource: Request string "key"
    Resource-->>Locale: Return raw string with ### Changes
1. **Locale utility functions**:
   - Removed deprecated functions from `components/common/locale.ts`:
     - `getLocaleWithTag`, `splitStringForTag`, and `getLocaleWithTags`
   - Updated the `formatLocale` and `formatString` functions to support more flexible replacement patterns
   - Added an optional `options` parameter to silence errors on missing replacements

2. **Updated string replacement syntax in components**:
   - Multiple components were updated to use the new `formatLocale` function, replacing direct string manipulation and deprecated tag parsing
   - Changed string templates in resource files (.grdp) to use consistent tag patterns
   - Fixed a typo in `braveWalletConnectHardwareDescription` (was `braveWallectConnectHardwareDescription`)
   - Updated string IDs to match the new replacement pattern (e.g., replacing `$1...$2` pairs with just `$1`)

3. **Brave News components**:
   - Updated `cardOptIn.tsx` to use the new string formatting API
   - Updated `DisabledPlaceholder.tsx` to use the new formatting approach

4. **Brave Wallet UI components**:
   - Fixed several components to use the new string formatting patterns, including:
     - `account-settings-modal.tsx`
     - `add-imported-account-modal.tsx`
     - `enable-nft-discovery-modal.tsx`
     - `partners_consent_modal.tsx`
     - `transaction_intent.tsx` 
     - And many more wallet-related UI components

5. **Brave Welcome UI components**:
   - Updated help-improve component to use the new string formatting API

...$2 markers
    Locale->>Locale: splitStringForTag() to parse markers
    Locale-->>Component: Return {beforeTag, duringTag, afterTag}
    Component->>Component: Manually compose elements with React
    
    Note over Component,Resource: After PR
    Component->>Locale: formatLocale("key", {### Changes
1. **Locale utility functions**:
   - Removed deprecated functions from `components/common/locale.ts`:
     - `getLocaleWithTag`, `splitStringForTag`, and `getLocaleWithTags`
   - Updated the `formatLocale` and `formatString` functions to support more flexible replacement patterns
   - Added an optional `options` parameter to silence errors on missing replacements

2. **Updated string replacement syntax in components**:
   - Multiple components were updated to use the new `formatLocale` function, replacing direct string manipulation and deprecated tag parsing
   - Changed string templates in resource files (.grdp) to use consistent tag patterns
   - Fixed a typo in `braveWalletConnectHardwareDescription` (was `braveWallectConnectHardwareDescription`)
   - Updated string IDs to match the new replacement pattern (e.g., replacing `$1...$2` pairs with just `$1`)

3. **Brave News components**:
   - Updated `cardOptIn.tsx` to use the new string formatting API
   - Updated `DisabledPlaceholder.tsx` to use the new formatting approach

4. **Brave Wallet UI components**:
   - Fixed several components to use the new string formatting patterns, including:
     - `account-settings-modal.tsx`
     - `add-imported-account-modal.tsx`
     - `enable-nft-discovery-modal.tsx`
     - `partners_consent_modal.tsx`
     - `transaction_intent.tsx` 
     - And many more wallet-related UI components

5. **Brave Welcome UI components**:
   - Updated help-improve component to use the new string formatting API

: content => <Component>{content}</Component>})
    Locale->>Resource: Request string "key"
    Resource-->>Locale: Return raw string with ### Changes
1. **Locale utility functions**:
   - Removed deprecated functions from `components/common/locale.ts`:
     - `getLocaleWithTag`, `splitStringForTag`, and `getLocaleWithTags`
   - Updated the `formatLocale` and `formatString` functions to support more flexible replacement patterns
   - Added an optional `options` parameter to silence errors on missing replacements

2. **Updated string replacement syntax in components**:
   - Multiple components were updated to use the new `formatLocale` function, replacing direct string manipulation and deprecated tag parsing
   - Changed string templates in resource files (.grdp) to use consistent tag patterns
   - Fixed a typo in `braveWalletConnectHardwareDescription` (was `braveWallectConnectHardwareDescription`)
   - Updated string IDs to match the new replacement pattern (e.g., replacing `$1...$2` pairs with just `$1`)

3. **Brave News components**:
   - Updated `cardOptIn.tsx` to use the new string formatting API
   - Updated `DisabledPlaceholder.tsx` to use the new formatting approach

4. **Brave Wallet UI components**:
   - Fixed several components to use the new string formatting patterns, including:
     - `account-settings-modal.tsx`
     - `add-imported-account-modal.tsx`
     - `enable-nft-discovery-modal.tsx`
     - `partners_consent_modal.tsx`
     - `transaction_intent.tsx` 
     - And many more wallet-related UI components

5. **Brave Welcome UI components**:
   - Updated help-improve component to use the new string formatting API

 markers
    Locale->>Formatter: formatString(rawString, replacements)
    Formatter->>Formatter: Process replacements with callback functions
    Formatter-->>Locale: Return formatted React elements/string
    Locale-->>Component: Return formatted content
```
</details>

### Possible Issues
1. The string format changes in resource files could potentially break translations if translators are expecting the old format with paired tag markers (like `$1....$2`).
2. Some complex string formatting might behave differently with the new system, especially in cases where there were multiple nested tags.
3. One typo fix (`braveWalletExchangeViaProvider` has a comma in the middle of the ID in the resource file) might cause issues if not corrected.



<!-- Generated by claude-3-7-sonnet-20250219 -->
author_association: CONTRIBUTOR
user: login: brave-builds
created_at: 2025-04-23T21:57:13Z
body: > [!WARNING]
> You have got a presubmit warning. Please address it if possible.

```
Found 15 lines longer than 80 characters (first 5 shown).
```

Items:
```
components/brave_new_tab_ui/stories/todayStrings.ts, line 14, 107 chars
components/brave_wallet_ui/components/desktop/popup-modals/enable-nft-discovery-modal/enable-nft-discovery-modal.tsx, line 38, 98 chars
components/brave_wallet_ui/components/desktop/views/nfts/components/auto-discovery-empty-state/auto-discovery-empty-state.tsx, line 32, 87 chars
components/brave_wallet_ui/components/extension/enable_transaction_simulations/enable_transaction_simulations.tsx, line 109, 103 chars
components/brave_wallet_ui/components/extension/enable_transaction_simulations/enable_transaction_simulations.tsx, line 110, 111 chars
```

<!-- presubmit-hash=7e5a01aa488eeb22af548ed1edcb7f2f36b5ba854faf35dce2241635eb7b3b87 -->
author_association: COLLABORATOR
user: login: brave-builds
created_at: 2025-04-29T00:04:11Z
body: Released in [v1.80.14](https://github.com/brave/brave-browser/releases/tag/v1.80.14)
author_association: COLLABORATOR