如何使用括号格式化负数货币金额以符合会计规范?

使用 currencySign 选项以会计记账格式显示负数货币值

简介

财务报表、会计报告和资产负债表通常用括号而不是负号来表示负数货币金额。例如,五百美元的亏损会显示为 ($500.00),而不是 -$500.00。这种做法让负数金额在视觉上更加醒目,降低了遗漏亏损的风险,并且符合既定的会计标准。

JavaScript 的 Intl.NumberFormat API 提供了 currencySign 选项,可以用会计记账格式显示负数货币金额。当你将 currencySign 设置为 "accounting" 时,负数金额会根据本地化规范自动用括号显示。

为什么会计记账格式使用括号

在计算机普及之前,会计行业就已经采用用括号包裹负数金额的做法。纸质报表上的负号可能很小、很淡,或者在浏览大量数字时容易被忽略。括号为负数金额提供了清晰的视觉边界,使其一目了然。

括号还能避免与其他场景下出现的连字符或破折号混淆。在包含大量行和列的密集财务表格中,括号比单独的负号字符更醒目。这种视觉上的区分有助于在阅读、录入或分析财务数据时减少错误。

这种惯例已成为会计行业的标准做法,并沿用至今。大多数会计软件、财务报告和资产负债表都会用括号显示负数金额。熟悉财务文件的用户也更习惯这种格式,并认为比负号更易于阅读。

使用 currencySign 的会计格式

在创建 Intl.NumberFormat 实例进行货币格式化时,传递 currencySign 选项并设置为 "accounting"。这样可以让格式化器对负数金额使用会计记账法。

const formatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencySign: 'accounting'
});

console.log(formatter.format(-1234.56));
// Output: "($1,234.56)"

console.log(formatter.format(1234.56));
// Output: "$1,234.56"

负数金额会用括号包裹显示,不显示负号。正数金额则正常显示,不加括号。格式化器会根据数值的正负自动应用相应的会计记账格式。

currencySign 选项仅影响货币格式化。必须设置 style: 'currency' 并提供 currency 代码,该选项才会生效。

标准格式与会计格式对比

默认的货币格式会对负值使用负号。标准格式适用于一般场景,而会计格式则遵循财务报表规范。

const standard = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencySign: 'standard'
});

const accounting = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencySign: 'accounting'
});

console.log(standard.format(-500));
// Output: "-$500.00"

console.log(accounting.format(-500));
// Output: "($500.00)"

标准格式会在货币符号前加负号。会计格式则将整个金额(包括货币符号)用括号包裹。对于正数,两种格式的输出相同。

如果省略 currencySign 选项,格式化器会默认采用 "standard" 行为。

const formatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
});

console.log(formatter.format(-500));
// Output: "-$500.00"

会计记账格式在不同地区的差异

不同地区会根据本地习惯将货币符号和括号放在不同的位置。Intl.NumberFormat API 会自动处理这些差异。

const currencies = [
  { locale: 'en-US', currency: 'USD' },
  { locale: 'de-DE', currency: 'EUR' },
  { locale: 'fr-FR', currency: 'EUR' },
  { locale: 'ja-JP', currency: 'JPY' }
];

currencies.forEach(({ locale, currency }) => {
  const formatter = new Intl.NumberFormat(locale, {
    style: 'currency',
    currency: currency,
    currencySign: 'accounting'
  });

  console.log(`${locale}: ${formatter.format(-1234.56)}`);
});

// Output:
// en-US: ($1,234.56)
// de-DE: (-1.234,56 €)
// fr-FR: (1 234,56 €)
// ja-JP: (¥-1,235)

每个地区都会根据本地的货币符号位置、小数点分隔符、千位分隔符和括号用法来格式化负数金额。你无需了解或手动实现这些规则,API 会根据 locale 标识符自动应用正确的格式。

在某些语言环境中,负号会放在括号内、靠近数字的位置;而在其他语言环境中,负号则可能靠近货币符号。格式化程序会根据不同语言环境的规则自动处理这些细节。

结合会计记账格式与符号显示选项

currencySign 选项可与 signDisplay 选项配合使用,以控制货币格式中符号的显示方式。通过这种组合,您可以精细控制正负金额的显示效果。

const always = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencySign: 'accounting',
  signDisplay: 'always'
});

console.log(always.format(500));
// Output: "+$500.00"

console.log(always.format(-500));
// Output: "($500.00)"

使用 signDisplay: 'always' 时,正数金额会显示加号,负数金额仍然使用括号。这使得收益和损失都能被明确区分。

const never = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencySign: 'accounting',
  signDisplay: 'never'
});

console.log(never.format(500));
// Output: "$500.00"

console.log(never.format(-500));
// Output: "$500.00"

使用 signDisplay: 'never' 时,正负金额都不显示符号或括号,仅显示数值本身,无论是正数还是负数。

const exceptZero = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencySign: 'accounting',
  signDisplay: 'exceptZero'
});

console.log(exceptZero.format(500));
// Output: "+$500.00"

console.log(exceptZero.format(-500));
// Output: "($500.00)"

console.log(exceptZero.format(0));
// Output: "$0.00"

使用 signDisplay: 'exceptZero' 时,正数金额显示加号,负数金额用括号,零则不显示任何符号。这种格式适用于需要突出变化、同时保持零值中性的损益报表。

何时使用会计格式

在需要遵循会计规范展示财务数据的场景下,应使用会计格式。这包括财务报表、资产负债表、损益表、利润与损失报告以及会计软件界面等。

会计格式比标准格式更能突出负数金额。括号为负数创建了视觉边界,有助于突出损失、负债或负余额。这样用户在浏览财务数据时能更快识别出问题区域。

const accountingFormatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencySign: 'accounting'
});

const accounts = [
  { name: 'Cash', balance: 15000 },
  { name: 'Accounts Receivable', balance: -3500 },
  { name: 'Inventory', balance: 12000 },
  { name: 'Accounts Payable', balance: -8000 }
];

accounts.forEach(account => {
  const formatted = accountingFormatter.format(account.balance);
  console.log(`${account.name}: ${formatted}`);
});

// Output:
// Cash: $15,000.00
// Accounts Receivable: ($3,500.00)
// Inventory: $12,000.00
// Accounts Payable: ($8,000.00)

括号让负余额更加醒目,帮助用户一眼识别出数值为负的账户。

对于一般电商、面向消费者的界面或不涉及会计规范的场景,请使用标准格式。除会计和金融领域外,大多数用户对括号表示负数的记账法不熟悉,更习惯于看到负号表示负金额。

const standardFormatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
});

// For a shopping cart or refund:
console.log(standardFormatter.format(-25.50));
// Output: "-$25.50"

当用户期望遵循财务报表规范时,请选择会计格式;当用户期望面向消费者的展示时,请选择标准格式。请根据用户的期望和数字出现的场景匹配合适的格式。

会计格式的浏览器支持情况

currencySign: "accounting" 选项在所有现代浏览器中均受支持。Chrome、Edge、Firefox、Safari 及其移动端自 2019 年发布的版本起均支持此功能。

如果需要支持旧版浏览器,请测试该功能并提供降级方案。

function formatCurrency(amount) {
  try {
    const formatter = new Intl.NumberFormat('en-US', {
      style: 'currency',
      currency: 'USD',
      currencySign: 'accounting'
    });
    return formatter.format(amount);
  } catch (error) {
    // Fallback for browsers without accounting support
    const formatter = new Intl.NumberFormat('en-US', {
      style: 'currency',
      currency: 'USD'
    });
    return formatter.format(amount);
  }
}

如果会计格式不可用,此降级方案将返回标准货币格式。实际上,会计格式的支持已非常普及,对于仅支持现代浏览器的应用来说,通常无需使用此降级方案。