IntlFormatter
This library is a work-in-progress. We are releasing it early to gather feedback, but it is not ready for production.
A comprehensive, framework-agnostic web component that wraps the browser's native Intl.DateTimeFormat and Intl.NumberFormat APIs for formatting dates, numbers, currencies, and percentages with full localization support.
Examples
Live Preview
Date Formatting
Date Localization
Number Formatting
Number Localization
Currency Formatting
Currency Localization
Percentage Formatting
Real-World Example: Dashboard Stats
CSS Parts Customization
Customize formatter appearance using CSS Shadow Parts without breaking encapsulation.
View Vue Code
<template>
<section>
<!-- Date Formatting -->
<div class="mbe2">
<h2>Date Formatting</h2>
</div>
<div
style="display: flex; flex-direction: column; gap: 1rem;"
class="mbe4"
>
<div>
<strong>Default: </strong>
<VueIntlFormatter
type="date"
date="2024-01-15T14:30:00"
lang="en-US"
/>
</div>
<div>
<strong>Full Date with Short Time: </strong>
<VueIntlFormatter
type="date"
date="2024-01-15T14:30:00"
dateStyle="full"
timeStyle="short"
lang="en-US"
/>
</div>
<div>
<strong>Custom Format: </strong>
<VueIntlFormatter
type="date"
date="2024-01-15T14:30:00"
weekday="long"
year="numeric"
month="long"
day="numeric"
lang="en-US"
/>
</div>
<div>
<strong>24-Hour Format: </strong>
<VueIntlFormatter
type="date"
date="2024-01-15T14:30:00"
hour="2-digit"
minute="2-digit"
second="2-digit"
hourFormat="24"
lang="en-US"
/>
</div>
</div>
<!-- Date Localization -->
<div class="mbe2">
<h2>Date Localization</h2>
</div>
<div
style="display: flex; flex-direction: column; gap: 1rem;"
class="mbe4"
>
<div>
<strong>English (US): </strong>
<VueIntlFormatter
type="date"
date="2024-01-15T14:30:00"
dateStyle="full"
timeStyle="short"
lang="en-US"
/>
</div>
<div>
<strong>French: </strong>
<VueIntlFormatter
type="date"
date="2024-01-15T14:30:00"
dateStyle="full"
timeStyle="short"
lang="fr-FR"
/>
</div>
<div>
<strong>German: </strong>
<VueIntlFormatter
type="date"
date="2024-01-15T14:30:00"
dateStyle="full"
timeStyle="short"
lang="de-DE"
/>
</div>
<div>
<strong>Japanese: </strong>
<VueIntlFormatter
type="date"
date="2024-01-15T14:30:00"
dateStyle="full"
timeStyle="short"
lang="ja-JP"
/>
</div>
</div>
<!-- Number Formatting -->
<div class="mbe2">
<h2>Number Formatting</h2>
</div>
<div
style="display: flex; flex-direction: column; gap: 1rem;"
class="mbe4"
>
<div>
<strong>Default: </strong>
<VueIntlFormatter
type="number"
:value="1234567.89"
lang="en-US"
/>
</div>
<div>
<strong>No Grouping: </strong>
<VueIntlFormatter
type="number"
:value="1234567.89"
:noGrouping="true"
lang="en-US"
/>
</div>
<div>
<strong>Fixed Decimals (2): </strong>
<VueIntlFormatter
type="number"
:value="100"
:minimumFractionDigits="2"
:maximumFractionDigits="2"
lang="en-US"
/>
</div>
</div>
<!-- Number Localization -->
<div class="mbe2">
<h2>Number Localization</h2>
</div>
<div
style="display: flex; flex-direction: column; gap: 1rem;"
class="mbe4"
>
<div>
<strong>English (US): </strong>
<VueIntlFormatter
type="number"
:value="1234567.89"
lang="en-US"
/>
</div>
<div>
<strong>French (space separator, comma decimal): </strong>
<VueIntlFormatter
type="number"
:value="1234567.89"
lang="fr-FR"
/>
</div>
<div>
<strong>German (dot separator, comma decimal): </strong>
<VueIntlFormatter
type="number"
:value="1234567.89"
lang="de-DE"
/>
</div>
<div>
<strong>Indian (lakh grouping): </strong>
<VueIntlFormatter
type="number"
:value="1234567.89"
lang="hi-IN"
/>
</div>
</div>
<!-- Currency Formatting -->
<div class="mbe2">
<h2>Currency Formatting</h2>
</div>
<div
style="display: flex; flex-direction: column; gap: 1rem;"
class="mbe4"
>
<div>
<strong>USD (symbol): </strong>
<VueIntlFormatter
type="currency"
:value="1234.56"
currency="USD"
lang="en-US"
/>
</div>
<div>
<strong>EUR (symbol): </strong>
<VueIntlFormatter
type="currency"
:value="1234.56"
currency="EUR"
lang="en-US"
/>
</div>
<div>
<strong>Currency Code Display: </strong>
<VueIntlFormatter
type="currency"
:value="1234.56"
currency="USD"
currencyDisplay="code"
lang="en-US"
/>
</div>
<div>
<strong>Currency Name Display: </strong>
<VueIntlFormatter
type="currency"
:value="1234.56"
currency="USD"
currencyDisplay="name"
lang="en-US"
/>
</div>
</div>
<!-- Currency Localization -->
<div class="mbe2">
<h2>Currency Localization</h2>
</div>
<div
style="display: flex; flex-direction: column; gap: 1rem;"
class="mbe4"
>
<div>
<strong>USD in US: </strong>
<VueIntlFormatter
type="currency"
:value="1234.56"
currency="USD"
lang="en-US"
/>
</div>
<div>
<strong>EUR in Germany: </strong>
<VueIntlFormatter
type="currency"
:value="1234.56"
currency="EUR"
lang="de-DE"
/>
</div>
<div>
<strong>GBP in UK: </strong>
<VueIntlFormatter
type="currency"
:value="1234.56"
currency="GBP"
lang="en-GB"
/>
</div>
<div>
<strong>JPY in Japan (no decimals): </strong>
<VueIntlFormatter
type="currency"
:value="123456"
currency="JPY"
lang="ja-JP"
/>
</div>
</div>
<!-- Percentage Formatting -->
<div class="mbe2">
<h2>Percentage Formatting</h2>
</div>
<div
style="display: flex; flex-direction: column; gap: 1rem;"
class="mbe4"
>
<div>
<strong>Basic: </strong>
<VueIntlFormatter
type="percent"
:value="0.75"
lang="en-US"
/>
</div>
<div>
<strong>With Decimals: </strong>
<VueIntlFormatter
type="percent"
:value="0.8532"
:minimumFractionDigits="2"
:maximumFractionDigits="2"
lang="en-US"
/>
</div>
</div>
<!-- Real-World Example: Dashboard Stats -->
<div class="mbe2">
<h2>Real-World Example: Dashboard Stats</h2>
</div>
<div
style="
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 1rem;
max-width: 900px;
"
class="mbe4"
>
<div class="stat-card">
<div class="stat-label">Total Revenue</div>
<div class="stat-value">
<VueIntlFormatter
type="currency"
:value="1234567.89"
currency="USD"
lang="en-US"
/>
</div>
<div class="stat-change positive">
↑
<VueIntlFormatter
type="percent"
:value="0.1234"
:minimumFractionDigits="2"
lang="en-US"
/>
</div>
</div>
<div class="stat-card">
<div class="stat-label">Total Users</div>
<div class="stat-value">
<VueIntlFormatter
type="number"
:value="45678"
lang="en-US"
/>
</div>
<div class="stat-change positive">
↑
<VueIntlFormatter
type="percent"
:value="0.0856"
:minimumFractionDigits="2"
lang="en-US"
/>
</div>
</div>
<div class="stat-card">
<div class="stat-label">Conversion Rate</div>
<div class="stat-value">
<VueIntlFormatter
type="percent"
:value="0.0342"
:minimumFractionDigits="2"
lang="en-US"
/>
</div>
<div class="stat-change positive">
↑
<VueIntlFormatter
type="percent"
:value="0.0523"
:minimumFractionDigits="2"
lang="en-US"
/>
</div>
</div>
</div>
<!-- CSS Parts Customization -->
<div class="mbe2">
<h2>CSS Parts Customization</h2>
<p
class="mbe2"
style="color: var(--ag-text-secondary); font-size: 0.875rem;"
>
Customize formatter appearance using CSS Shadow Parts without breaking encapsulation.
</p>
</div>
<div
style="display: flex; flex-direction: column; gap: 1rem;"
class="mbe4"
>
<div>
<strong>Styled Currency: </strong>
<VueIntlFormatter
type="currency"
:value="1234.56"
currency="USD"
lang="en-US"
class="custom-currency"
/>
</div>
<div>
<strong>Styled Number: </strong>
<VueIntlFormatter
type="number"
:value="1234567.89"
lang="en-US"
class="custom-number"
/>
</div>
<div>
<strong>Styled Date: </strong>
<VueIntlFormatter
type="date"
date="2024-01-15T14:30:00"
dateStyle="long"
timeStyle="short"
lang="en-US"
class="custom-date"
/>
</div>
<div>
<strong>Styled Percent: </strong>
<VueIntlFormatter
type="percent"
:value="0.75"
lang="en-US"
class="custom-percent"
/>
</div>
</div>
</section>
</template>
<script>
import { VueIntlFormatter } from "agnosticui-core/intl-formatter/vue";
export default {
name: "IntlFormatterExamples",
components: {
VueIntlFormatter,
},
};
</script>
<style scoped>
/* Dashboard stats styling */
.stat-card {
background: var(--ag-background);
border: 1px solid var(--ag-border);
border-radius: var(--ag-radius-md);
padding: 1.5rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.stat-label {
font-size: 0.875rem;
color: var(--ag-text-secondary);
margin-bottom: 0.5rem;
}
.stat-value {
font-size: clamp(1.25rem, 4vw, 1.875rem);
font-weight: 700;
color: var(--ag-text);
overflow-wrap: break-word;
word-break: break-word;
line-height: 1.2;
}
.stat-change {
font-size: 0.875rem;
margin-top: 0.5rem;
}
.stat-change.positive {
color: var(--ag-success-text);
}
/* CSS Parts customization examples */
/* Custom currency style */
.custom-currency::part(currency) {
color: var(--ag-success-text);
font-weight: bold;
font-size: 1.25rem;
}
/* Custom number style */
.custom-number::part(number) {
font-family: "Courier New", monospace;
background: var(--ag-background-subtle);
padding: 0.25rem 0.5rem;
border-radius: var(--ag-radius-sm);
}
/* Custom date style */
.custom-date::part(date-time) {
color: var(--ag-primary-text);
font-style: italic;
}
/* Custom percent style */
.custom-percent::part(percent) {
color: var(--ag-danger-text);
font-weight: 600;
}
</style>
Live Preview
View Lit / Web Component Code
import { LitElement, html, css } from 'lit';
import 'agnosticui-core/intl-formatter';
export class IntlFormatterLitExamples extends LitElement {
createRenderRoot() {
return this;
}
static styles = css`
/* Dashboard stats styling */
.stat-card {
background: var(--ag-background);
border: 1px solid var(--ag-border);
border-radius: var(--ag-radius-md);
padding: 1.5rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.stat-label {
font-size: 0.875rem;
color: var(--ag-text-secondary);
margin-bottom: 0.5rem;
}
.stat-value {
font-size: clamp(1.25rem, 4vw, 1.875rem);
font-weight: 700;
color: var(--ag-text);
overflow-wrap: break-word;
word-break: break-word;
line-height: 1.2;
}
.stat-change {
font-size: 0.875rem;
margin-top: 0.5rem;
}
.stat-change.positive {
color: var(--ag-success-text);
}
/* CSS Parts customization examples */
/* Custom currency style */
.custom-currency::part(currency) {
color: var(--ag-success-text);
font-weight: bold;
font-size: 1.25rem;
}
/* Custom number style */
.custom-number::part(number) {
font-family: "Courier New", monospace;
background: var(--ag-background-subtle);
padding: 0.25rem 0.5rem;
border-radius: var(--ag-radius-sm);
}
/* Custom date style */
.custom-date::part(date-time) {
color: var(--ag-primary-text);
font-style: italic;
}
/* Custom percent style */
.custom-percent::part(percent) {
color: var(--ag-danger-text);
font-weight: 600;
}
`;
render() {
return html`
<section>
<!-- Date Formatting -->
<div class="mbe2">
<h2>Date Formatting</h2>
</div>
<div style="display: flex; flex-direction: column; gap: 1rem;" class="mbe4">
<div>
<strong>Default: </strong>
<ag-intl-formatter
type="date"
date="2024-01-15T14:30:00"
lang="en-US"
></ag-intl-formatter>
</div>
<div>
<strong>Full Date with Short Time: </strong>
<ag-intl-formatter
type="date"
date="2024-01-15T14:30:00"
date-style="full"
time-style="short"
lang="en-US"
></ag-intl-formatter>
</div>
<div>
<strong>Custom Format: </strong>
<ag-intl-formatter
type="date"
date="2024-01-15T14:30:00"
weekday="long"
year="numeric"
month="long"
day="numeric"
lang="en-US"
></ag-intl-formatter>
</div>
<div>
<strong>24-Hour Format: </strong>
<ag-intl-formatter
type="date"
date="2024-01-15T14:30:00"
hour="2-digit"
minute="2-digit"
second="2-digit"
hour-format="24"
lang="en-US"
></ag-intl-formatter>
</div>
</div>
<!-- Date Localization -->
<div class="mbe2">
<h2>Date Localization</h2>
</div>
<div style="display: flex; flex-direction: column; gap: 1rem;" class="mbe4">
<div>
<strong>English (US): </strong>
<ag-intl-formatter
type="date"
date="2024-01-15T14:30:00"
date-style="full"
time-style="short"
lang="en-US"
></ag-intl-formatter>
</div>
<div>
<strong>French: </strong>
<ag-intl-formatter
type="date"
date="2024-01-15T14:30:00"
date-style="full"
time-style="short"
lang="fr-FR"
></ag-intl-formatter>
</div>
<div>
<strong>German: </strong>
<ag-intl-formatter
type="date"
date="2024-01-15T14:30:00"
date-style="full"
time-style="short"
lang="de-DE"
></ag-intl-formatter>
</div>
<div>
<strong>Japanese: </strong>
<ag-intl-formatter
type="date"
date="2024-01-15T14:30:00"
date-style="full"
time-style="short"
lang="ja-JP"
></ag-intl-formatter>
</div>
</div>
<!-- Number Formatting -->
<div class="mbe2">
<h2>Number Formatting</h2>
</div>
<div style="display: flex; flex-direction: column; gap: 1rem;" class="mbe4">
<div>
<strong>Default: </strong>
<ag-intl-formatter
type="number"
.value=${1234567.89}
lang="en-US"
></ag-intl-formatter>
</div>
<div>
<strong>No Grouping: </strong>
<ag-intl-formatter
type="number"
.value=${1234567.89}
no-grouping
lang="en-US"
></ag-intl-formatter>
</div>
<div>
<strong>Fixed Decimals (2): </strong>
<ag-intl-formatter
type="number"
.value=${100}
.minimumFractionDigits=${2}
.maximumFractionDigits=${2}
lang="en-US"
></ag-intl-formatter>
</div>
</div>
<!-- Number Localization -->
<div class="mbe2">
<h2>Number Localization</h2>
</div>
<div style="display: flex; flex-direction: column; gap: 1rem;" class="mbe4">
<div>
<strong>English (US): </strong>
<ag-intl-formatter
type="number"
.value=${1234567.89}
lang="en-US"
></ag-intl-formatter>
</div>
<div>
<strong>French (space separator, comma decimal): </strong>
<ag-intl-formatter
type="number"
.value=${1234567.89}
lang="fr-FR"
></ag-intl-formatter>
</div>
<div>
<strong>German (dot separator, comma decimal): </strong>
<ag-intl-formatter
type="number"
.value=${1234567.89}
lang="de-DE"
></ag-intl-formatter>
</div>
<div>
<strong>Indian (lakh grouping): </strong>
<ag-intl-formatter
type="number"
.value=${1234567.89}
lang="hi-IN"
></ag-intl-formatter>
</div>
</div>
<!-- Currency Formatting -->
<div class="mbe2">
<h2>Currency Formatting</h2>
</div>
<div style="display: flex; flex-direction: column; gap: 1rem;" class="mbe4">
<div>
<strong>USD (symbol): </strong>
<ag-intl-formatter
type="currency"
.value=${1234.56}
currency="USD"
lang="en-US"
></ag-intl-formatter>
</div>
<div>
<strong>EUR (symbol): </strong>
<ag-intl-formatter
type="currency"
.value=${1234.56}
currency="EUR"
lang="en-US"
></ag-intl-formatter>
</div>
<div>
<strong>Currency Code Display: </strong>
<ag-intl-formatter
type="currency"
.value=${1234.56}
currency="USD"
currency-display="code"
lang="en-US"
></ag-intl-formatter>
</div>
<div>
<strong>Currency Name Display: </strong>
<ag-intl-formatter
type="currency"
.value=${1234.56}
currency="USD"
currency-display="name"
lang="en-US"
></ag-intl-formatter>
</div>
</div>
<!-- Currency Localization -->
<div class="mbe2">
<h2>Currency Localization</h2>
</div>
<div style="display: flex; flex-direction: column; gap: 1rem;" class="mbe4">
<div>
<strong>USD in US: </strong>
<ag-intl-formatter
type="currency"
.value=${1234.56}
currency="USD"
lang="en-US"
></ag-intl-formatter>
</div>
<div>
<strong>EUR in Germany: </strong>
<ag-intl-formatter
type="currency"
.value=${1234.56}
currency="EUR"
lang="de-DE"
></ag-intl-formatter>
</div>
<div>
<strong>GBP in UK: </strong>
<ag-intl-formatter
type="currency"
.value=${1234.56}
currency="GBP"
lang="en-GB"
></ag-intl-formatter>
</div>
<div>
<strong>JPY in Japan (no decimals): </strong>
<ag-intl-formatter
type="currency"
.value=${123456}
currency="JPY"
lang="ja-JP"
></ag-intl-formatter>
</div>
</div>
<!-- Percentage Formatting -->
<div class="mbe2">
<h2>Percentage Formatting</h2>
</div>
<div style="display: flex; flex-direction: column; gap: 1rem;" class="mbe4">
<div>
<strong>Basic: </strong>
<ag-intl-formatter
type="percent"
.value=${0.75}
lang="en-US"
></ag-intl-formatter>
</div>
<div>
<strong>With Decimals: </strong>
<ag-intl-formatter
type="percent"
.value=${0.8532}
.minimumFractionDigits=${2}
.maximumFractionDigits=${2}
lang="en-US"
></ag-intl-formatter>
</div>
</div>
<!-- Real-World Example: Dashboard Stats -->
<div class="mbe2">
<h2>Real-World Example: Dashboard Stats</h2>
</div>
<div
style="
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 1rem;
max-width: 900px;
"
class="mbe4"
>
<div class="stat-card">
<div class="stat-label">Total Revenue</div>
<div class="stat-value">
<ag-intl-formatter
type="currency"
.value=${1234567.89}
currency="USD"
lang="en-US"
></ag-intl-formatter>
</div>
<div class="stat-change positive">
↑
<ag-intl-formatter
type="percent"
.value=${0.1234}
.minimumFractionDigits=${2}
lang="en-US"
></ag-intl-formatter>
</div>
</div>
<div class="stat-card">
<div class="stat-label">Total Users</div>
<div class="stat-value">
<ag-intl-formatter
type="number"
.value=${45678}
lang="en-US"
></ag-intl-formatter>
</div>
<div class="stat-change positive">
↑
<ag-intl-formatter
type="percent"
.value=${0.0856}
.minimumFractionDigits=${2}
lang="en-US"
></ag-intl-formatter>
</div>
</div>
<div class="stat-card">
<div class="stat-label">Conversion Rate</div>
<div class="stat-value">
<ag-intl-formatter
type="percent"
.value=${0.0342}
.minimumFractionDigits=${2}
lang="en-US"
></ag-intl-formatter>
</div>
<div class="stat-change positive">
↑
<ag-intl-formatter
type="percent"
.value=${0.0523}
.minimumFractionDigits=${2}
lang="en-US"
></ag-intl-formatter>
</div>
</div>
</div>
<!-- CSS Parts Customization -->
<div class="mbe2">
<h2>CSS Parts Customization</h2>
<p class="mbe2" style="color: var(--ag-text-secondary); font-size: 0.875rem;">
Customize formatter appearance using CSS Shadow Parts without breaking encapsulation.
</p>
</div>
<div style="display: flex; flex-direction: column; gap: 1rem;" class="mbe4">
<div>
<strong>Styled Currency: </strong>
<ag-intl-formatter
type="currency"
.value=${1234.56}
currency="USD"
lang="en-US"
class="custom-currency"
></ag-intl-formatter>
</div>
<div>
<strong>Styled Number: </strong>
<ag-intl-formatter
type="number"
.value=${1234567.89}
lang="en-US"
class="custom-number"
></ag-intl-formatter>
</div>
<div>
<strong>Styled Date: </strong>
<ag-intl-formatter
type="date"
date="2024-01-15T14:30:00"
date-style="long"
time-style="short"
lang="en-US"
class="custom-date"
></ag-intl-formatter>
</div>
<div>
<strong>Styled Percent: </strong>
<ag-intl-formatter
type="percent"
.value=${0.75}
lang="en-US"
class="custom-percent"
></ag-intl-formatter>
</div>
</div>
</section>
`;
}
}
// Register the custom element
customElements.define('intl-formatter-lit-examples', IntlFormatterLitExamples);
Interactive Preview: Click the "Open in StackBlitz" button below to see this example running live in an interactive playground.
View React Code
import { ReactIntlFormatter } from "agnosticui-core/intl-formatter/react";
export default function IntlFormatterReactExamples() {
return (
<section>
<style>{`
/* Dashboard stats styling */
.stat-card {
background: var(--ag-background);
border: 1px solid var(--ag-border);
border-radius: var(--ag-radius-md);
padding: 1.5rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.stat-label {
font-size: 0.875rem;
color: var(--ag-text-secondary);
margin-bottom: 0.5rem;
}
.stat-value {
font-size: clamp(1.25rem, 4vw, 1.875rem);
font-weight: 700;
color: var(--ag-text);
overflow-wrap: break-word;
word-break: break-word;
line-height: 1.2;
}
.stat-change {
font-size: 0.875rem;
margin-top: 0.5rem;
}
.stat-change.positive {
color: var(--ag-success-text);
}
/* CSS Parts customization examples */
/* Custom currency style */
.custom-currency::part(currency) {
color: var(--ag-success-text);
font-weight: bold;
font-size: 1.25rem;
}
/* Custom number style */
.custom-number::part(number) {
font-family: "Courier New", monospace;
background: var(--ag-background-subtle);
padding: 0.25rem 0.5rem;
border-radius: var(--ag-radius-sm);
}
/* Custom date style */
.custom-date::part(date-time) {
color: var(--ag-primary-text);
font-style: italic;
}
/* Custom percent style */
.custom-percent::part(percent) {
color: var(--ag-danger-text);
font-weight: 600;
}
`}</style>
{/* Date Formatting */}
<div className="mbe2">
<h2>Date Formatting</h2>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }} className="mbe4">
<div>
<strong>Default: </strong>
<ReactIntlFormatter
type="date"
date="2024-01-15T14:30:00"
lang="en-US"
/>
</div>
<div>
<strong>Full Date with Short Time: </strong>
<ReactIntlFormatter
type="date"
date="2024-01-15T14:30:00"
dateStyle="full"
timeStyle="short"
lang="en-US"
/>
</div>
<div>
<strong>Custom Format: </strong>
<ReactIntlFormatter
type="date"
date="2024-01-15T14:30:00"
weekday="long"
year="numeric"
month="long"
day="numeric"
lang="en-US"
/>
</div>
<div>
<strong>24-Hour Format: </strong>
<ReactIntlFormatter
type="date"
date="2024-01-15T14:30:00"
hour="2-digit"
minute="2-digit"
second="2-digit"
hourFormat="24"
lang="en-US"
/>
</div>
</div>
{/* Date Localization */}
<div className="mbe2">
<h2>Date Localization</h2>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }} className="mbe4">
<div>
<strong>English (US): </strong>
<ReactIntlFormatter
type="date"
date="2024-01-15T14:30:00"
dateStyle="full"
timeStyle="short"
lang="en-US"
/>
</div>
<div>
<strong>French: </strong>
<ReactIntlFormatter
type="date"
date="2024-01-15T14:30:00"
dateStyle="full"
timeStyle="short"
lang="fr-FR"
/>
</div>
<div>
<strong>German: </strong>
<ReactIntlFormatter
type="date"
date="2024-01-15T14:30:00"
dateStyle="full"
timeStyle="short"
lang="de-DE"
/>
</div>
<div>
<strong>Japanese: </strong>
<ReactIntlFormatter
type="date"
date="2024-01-15T14:30:00"
dateStyle="full"
timeStyle="short"
lang="ja-JP"
/>
</div>
</div>
{/* Number Formatting */}
<div className="mbe2">
<h2>Number Formatting</h2>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }} className="mbe4">
<div>
<strong>Default: </strong>
<ReactIntlFormatter
type="number"
value={1234567.89}
lang="en-US"
/>
</div>
<div>
<strong>No Grouping: </strong>
<ReactIntlFormatter
type="number"
value={1234567.89}
noGrouping={true}
lang="en-US"
/>
</div>
<div>
<strong>Fixed Decimals (2): </strong>
<ReactIntlFormatter
type="number"
value={100}
minimumFractionDigits={2}
maximumFractionDigits={2}
lang="en-US"
/>
</div>
</div>
{/* Number Localization */}
<div className="mbe2">
<h2>Number Localization</h2>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }} className="mbe4">
<div>
<strong>English (US): </strong>
<ReactIntlFormatter
type="number"
value={1234567.89}
lang="en-US"
/>
</div>
<div>
<strong>French (space separator, comma decimal): </strong>
<ReactIntlFormatter
type="number"
value={1234567.89}
lang="fr-FR"
/>
</div>
<div>
<strong>German (dot separator, comma decimal): </strong>
<ReactIntlFormatter
type="number"
value={1234567.89}
lang="de-DE"
/>
</div>
<div>
<strong>Indian (lakh grouping): </strong>
<ReactIntlFormatter
type="number"
value={1234567.89}
lang="hi-IN"
/>
</div>
</div>
{/* Currency Formatting */}
<div className="mbe2">
<h2>Currency Formatting</h2>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }} className="mbe4">
<div>
<strong>USD (symbol): </strong>
<ReactIntlFormatter
type="currency"
value={1234.56}
currency="USD"
lang="en-US"
/>
</div>
<div>
<strong>EUR (symbol): </strong>
<ReactIntlFormatter
type="currency"
value={1234.56}
currency="EUR"
lang="en-US"
/>
</div>
<div>
<strong>Currency Code Display: </strong>
<ReactIntlFormatter
type="currency"
value={1234.56}
currency="USD"
currencyDisplay="code"
lang="en-US"
/>
</div>
<div>
<strong>Currency Name Display: </strong>
<ReactIntlFormatter
type="currency"
value={1234.56}
currency="USD"
currencyDisplay="name"
lang="en-US"
/>
</div>
</div>
{/* Currency Localization */}
<div className="mbe2">
<h2>Currency Localization</h2>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }} className="mbe4">
<div>
<strong>USD in US: </strong>
<ReactIntlFormatter
type="currency"
value={1234.56}
currency="USD"
lang="en-US"
/>
</div>
<div>
<strong>EUR in Germany: </strong>
<ReactIntlFormatter
type="currency"
value={1234.56}
currency="EUR"
lang="de-DE"
/>
</div>
<div>
<strong>GBP in UK: </strong>
<ReactIntlFormatter
type="currency"
value={1234.56}
currency="GBP"
lang="en-GB"
/>
</div>
<div>
<strong>JPY in Japan (no decimals): </strong>
<ReactIntlFormatter
type="currency"
value={123456}
currency="JPY"
lang="ja-JP"
/>
</div>
</div>
{/* Percentage Formatting */}
<div className="mbe2">
<h2>Percentage Formatting</h2>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }} className="mbe4">
<div>
<strong>Basic: </strong>
<ReactIntlFormatter
type="percent"
value={0.75}
lang="en-US"
/>
</div>
<div>
<strong>With Decimals: </strong>
<ReactIntlFormatter
type="percent"
value={0.8532}
minimumFractionDigits={2}
maximumFractionDigits={2}
lang="en-US"
/>
</div>
</div>
{/* Real-World Example: Dashboard Stats */}
<div className="mbe2">
<h2>Real-World Example: Dashboard Stats</h2>
</div>
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(240px, 1fr))",
gap: "1rem",
maxWidth: "900px",
}}
className="mbe4"
>
<div className="stat-card">
<div className="stat-label">Total Revenue</div>
<div className="stat-value">
<ReactIntlFormatter
type="currency"
value={1234567.89}
currency="USD"
lang="en-US"
/>
</div>
<div className="stat-change positive">
↑{" "}
<ReactIntlFormatter
type="percent"
value={0.1234}
minimumFractionDigits={2}
lang="en-US"
/>
</div>
</div>
<div className="stat-card">
<div className="stat-label">Total Users</div>
<div className="stat-value">
<ReactIntlFormatter
type="number"
value={45678}
lang="en-US"
/>
</div>
<div className="stat-change positive">
↑{" "}
<ReactIntlFormatter
type="percent"
value={0.0856}
minimumFractionDigits={2}
lang="en-US"
/>
</div>
</div>
<div className="stat-card">
<div className="stat-label">Conversion Rate</div>
<div className="stat-value">
<ReactIntlFormatter
type="percent"
value={0.0342}
minimumFractionDigits={2}
lang="en-US"
/>
</div>
<div className="stat-change positive">
↑{" "}
<ReactIntlFormatter
type="percent"
value={0.0523}
minimumFractionDigits={2}
lang="en-US"
/>
</div>
</div>
</div>
{/* CSS Parts Customization */}
<div className="mbe2">
<h2>CSS Parts Customization</h2>
<p className="mbe2" style={{ color: "var(--ag-text-secondary)", fontSize: "0.875rem" }}>
Customize formatter appearance using CSS Shadow Parts without breaking encapsulation.
</p>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }} className="mbe4">
<div>
<strong>Styled Currency: </strong>
<ReactIntlFormatter
type="currency"
value={1234.56}
currency="USD"
lang="en-US"
className="custom-currency"
/>
</div>
<div>
<strong>Styled Number: </strong>
<ReactIntlFormatter
type="number"
value={1234567.89}
lang="en-US"
className="custom-number"
/>
</div>
<div>
<strong>Styled Date: </strong>
<ReactIntlFormatter
type="date"
date="2024-01-15T14:30:00"
dateStyle="long"
timeStyle="short"
lang="en-US"
className="custom-date"
/>
</div>
<div>
<strong>Styled Percent: </strong>
<ReactIntlFormatter
type="percent"
value={0.75}
lang="en-US"
className="custom-percent"
/>
</div>
</div>
</section>
);
}
Features
- Full Internationalization - Uses browser's native Intl API
- Performance Optimized - Built-in formatter caching
- Type Safe - Complete TypeScript definitions
- Styleable - CSS parts for custom styling
- Error Handling - Custom events for validation errors
- Framework Support - React and Vue wrappers included
- Accessible - Semantic HTML with proper attributes
- Zero Dependencies - Built on Lit Element
Usage
TIP
The framework examples below import AgnosticUI as an npm package. Alternatively, you can use the CLI for complete control, AI/LLM visibility, and full code ownership:
npx ag init --framework FRAMEWORK # react, vue, lit, svelte, etc.
npx ag add IntlFormatterThe CLI copies source code directly into your project, giving you full visibility and control. After running npx ag add, you'll receive exact import instructions.
Vue
<template>
<section>
<VueIntlFormatter
type="date"
date="2024-01-15"
dateStyle="long"
lang="en-US"
/>
<VueIntlFormatter
type="currency"
:value="1234.56"
currency="USD"
lang="en-US"
/>
<VueIntlFormatter
type="number"
:value="1234567.89"
:minimumFractionDigits="2"
lang="en-US"
/>
<VueIntlFormatter
type="percent"
:value="0.75"
lang="en-US"
/>
</section>
</template>
<script setup>
import { VueIntlFormatter } from "agnosticui-core/intl-formatter/vue";
</script>React
import { ReactIntlFormatter } from "agnosticui-core/intl-formatter/react";
function App() {
const handleError = (event) => {
console.error('Formatting error:', event.detail);
};
return (
<div>
<ReactIntlFormatter
type="date"
date="2024-01-15"
dateStyle="full"
timeStyle="short"
/>
<ReactIntlFormatter
type="currency"
value={1234.56}
currency="EUR"
lang="de-DE"
onFormatError={handleError}
/>
<ReactIntlFormatter
type="number"
value={1234567.89}
minimumFractionDigits={2}
maximumFractionDigits={2}
noGrouping={false}
/>
<ReactIntlFormatter
type="percent"
value={0.8532}
minimumFractionDigits={2}
/>
</div>
);
}Vanilla HTML/JavaScript
<script type="module">
import "agnosticui-core/intl-formatter";
</script>
<ag-intl-formatter
type="date"
date="2024-01-15"
date-style="long"
lang="en-US">
</ag-intl-formatter>
<ag-intl-formatter
type="currency"
value="1234.56"
currency="USD"
lang="en-US">
</ag-intl-formatter>
<ag-intl-formatter
type="number"
value="1234567.89"
minimum-fraction-digits="2"
lang="en-US">
</ag-intl-formatter>
<ag-intl-formatter
type="percent"
value="0.75"
lang="en-US">
</ag-intl-formatter>API Reference
Common Properties
| Property | Type | Default | Description |
|---|---|---|---|
type | 'date' | 'number' | 'percent' | 'currency' | 'date' | The type of formatting to apply |
value | number | string | Date | - | The value to format (for numbers/currency/percent) |
lang | string | Browser locale | The locale to use (e.g., 'en-US', 'fr-FR') |
Date Properties
| Property | Type | Default | Description |
|---|---|---|---|
date | Date | string | - | The date value to format |
dateStyle | 'full' | 'long' | 'medium' | 'short' | - | Shortcut for date formatting style |
timeStyle | 'full' | 'long' | 'medium' | 'short' | - | Shortcut for time formatting style |
weekday | 'narrow' | 'short' | 'long' | - | Weekday representation |
era | 'narrow' | 'short' | 'long' | - | Era representation (e.g., 'AD') |
year | 'numeric' | '2-digit' | - | Year representation |
month | 'numeric' | '2-digit' | 'narrow' | 'short' | 'long' | - | Month representation |
day | 'numeric' | '2-digit' | - | Day representation |
hour | 'numeric' | '2-digit' | - | Hour representation |
minute | 'numeric' | '2-digit' | - | Minute representation |
second | 'numeric' | '2-digit' | - | Second representation |
timeZoneName | 'short' | 'long' | - | Time zone name representation |
timeZone | string | - | IANA time zone identifier |
hourFormat | 'auto' | '12' | '24' | 'auto' | Hour format preference |
Number Properties
| Property | Type | Default | Description |
|---|---|---|---|
noGrouping | boolean | false | Disable grouping separators (e.g., 1000 vs 1,000) |
minimumIntegerDigits | number | - | Minimum number of integer digits |
minimumFractionDigits | number | - | Minimum number of fraction digits |
maximumFractionDigits | number | - | Maximum number of fraction digits |
minimumSignificantDigits | number | - | Minimum number of significant digits |
maximumSignificantDigits | number | - | Maximum number of significant digits |
Currency Properties
| Property | Type | Default | Description |
|---|---|---|---|
currency | string | 'USD' | ISO 4217 currency code (e.g., 'USD', 'EUR') |
currencyDisplay | 'symbol' | 'narrowSymbol' | 'code' | 'name' | 'symbol' | How to display the currency |
Events
format-error
Dispatched when formatting fails due to validation or runtime errors.
interface FormatErrorDetail {
type: 'date' | 'number';
error: string;
}Example:
element.addEventListener("format-error", (event) => {
console.error(`${event.detail.type} formatting error:`, event.detail.error);
});CSS Parts
Style the component using CSS ::part() selectors:
ag-intl-formatter::part(date-time) {
color: blue;
font-weight: bold;
}
ag-intl-formatter::part(number) {
font-family: monospace;
}
ag-intl-formatter::part(currency) {
color: green;
}
ag-intl-formatter::part(percent) {
font-weight: bold;
}
ag-intl-formatter::part(error) {
color: red;
}Examples
Date Formatting
<ag-intl-formatter
type="date"
date="2024-01-15T14:30:00"
date-style="full"
time-style="short"
lang="en-US">
</ag-intl-formatter>
<ag-intl-formatter
type="date"
date="2024-01-15"
weekday="long"
year="numeric"
month="long"
day="numeric"
lang="en-US">
</ag-intl-formatter>
<ag-intl-formatter
type="date"
date="2024-01-15T14:30:00"
hour="2-digit"
minute="2-digit"
hour-format="24"
lang="en-US">
</ag-intl-formatter>Number Formatting
<ag-intl-formatter
type="number"
value="1234567.89"
lang="en-US">
</ag-intl-formatter>
<ag-intl-formatter
type="number"
value="1234567.89"
no-grouping
lang="en-US">
</ag-intl-formatter>
<ag-intl-formatter
type="number"
value="100"
minimum-fraction-digits="2"
maximum-fraction-digits="2"
lang="en-US">
</ag-intl-formatter>Currency Formatting
<ag-intl-formatter
type="currency"
value="1234.56"
currency="USD"
lang="en-US">
</ag-intl-formatter>
<ag-intl-formatter
type="currency"
value="1234.56"
currency="EUR"
currency-display="code"
lang="en-US">
</ag-intl-formatter>
<ag-intl-formatter
type="currency"
value="1234"
currency="JPY"
lang="ja-JP">
</ag-intl-formatter>Percentage Formatting
<ag-intl-formatter
type="percent"
value="0.75"
lang="en-US">
</ag-intl-formatter>
<ag-intl-formatter
type="percent"
value="0.8532"
minimum-fraction-digits="2"
maximum-fraction-digits="2"
lang="en-US">
</ag-intl-formatter>Localization Examples
<ag-intl-formatter
type="number"
value="1234.56"
lang="fr-FR">
</ag-intl-formatter>
<ag-intl-formatter
type="currency"
value="1234.56"
currency="EUR"
lang="de-DE">
</ag-intl-formatter>
<ag-intl-formatter
type="date"
date="2024-01-15"
date-style="long"
lang="ja-JP">
</ag-intl-formatter>Performance
The component uses intelligent formatter caching to improve performance:
- Formatters are cached based on type and options
- Cache is automatically cleared when the component is removed
- Repeated renders with the same options reuse cached formatters
This makes the component suitable for rendering large lists or frequently updating values.
Browser Support
Works in all modern browsers that support:
- Web Components (Custom Elements v1)
- Shadow DOM v1
- Intl.DateTimeFormat
- Intl.NumberFormat
For older browsers, consider using polyfills:
TypeScript Support
Full TypeScript definitions are included. Import types as needed:
import type { IntlFormatterProps } from "agnosticui-core/intl-formatter";Testing
The component includes a comprehensive test suite with 50+ tests covering:
- All formatting types
- Validation and error handling
- Localization
- Edge cases
- Performance
- Accessibility
Run tests with:
npm testContributing
Contributions are welcome! Please ensure:
- All tests pass
- New features include tests
- Code follows the existing style
- TypeScript types are updated
License
MIT