README.md

R-CMD-check CRAN
status Codecov test
coverage

countryscales extends scales and ggplot2 by providing functions to make it easy to display numbers or label axis text on positional scales in decimal format, as percentages or currencies using country- or locale-specific style conventions.

See vignette("background", package = "countryscales") for the story behind why this package exists.

Installation

You can install countryscales from CRAN using:

install.packages("countryscales")

You can install the development version of countryscales from GitHub using:

remotes::install_github("trekonom/countryscales")

Usage

The most common use case for countryscales is to customize the appearance of axis and legend labels or format numbers added as labels to a plot using country-specific style conventions.

Here’s an example showing how 1 million USD are formatted in the G20 countries:

library(countryscales)
library(ggplot2)
library(dplyr, warn.conflicts = FALSE)

g20 <- countryscales::g20 |>
  # India is not supported
  filter(iso2c != "IN")

# gapminder's classic per-country colors (Hans Rosling's bubble charts),
# keyed to match g20$country: "Korea, Rep." is gapminder's name for South
# Korea, and gapminder's 142-country palette doesn't cover Russia at all,
# so it gets a manually chosen fallback color
country_colors <- gapminder::country_colors[
  recode(g20$country, "South Korea" = "Korea, Rep.")
] |>
  setNames(g20$country)
country_colors[["Russia"]] <- "#707070"

g20 <- g20 |>
  mutate(
    x = factor(rep(1:2, 9)),
    y = factor(rep(9:1, each = 2)),
    country = if_else(iso2c %in% c("US", "GB"), paste("the", country), country),
    locale = if_else(iso2c == "CN", "zh-Hans-CN", locale),
    value = purrr::map_chr(
      locale,
      ~ label_currency_locale(locale = .x, currency = "USD")(1e6)
    )
  )

names(country_colors)[names(country_colors) == "United States"] <- "the United States"
names(country_colors)[names(country_colors) == "United Kingdom"] <- "the United Kingdom"

ggplot(g20, aes(x = x, y = y)) +
  geom_label(
    aes(label = paste(value, "in", country), fill = country),
    label.padding = unit(5, "pt"), label.r = unit(8, "pt"),
    color = "white"
  ) +
  scale_fill_manual(values = country_colors) +
  theme_void() +
  labs(
    title = "1 million USD are formatted as"
  ) +
  guides(fill = "none")

Grid of 18 colored labels arranged two per row, one for each G20 country except India. Each label shows how 1,000,000 US dollars is written using that country's own number and currency conventions, for example '1.000.000 $' for Germany and '1,000,000 US$' for Saudi Arabia. Labels are colored individually per country using gapminder's classic palette. Currency symbol placement, thousands-separator choice, and spacing all differ across countries even though the underlying value is identical.

As another example, let’s look at formatting a chart according to German style conventions, where a dot (.) is used as the big mark.

base <- gapminder15 |>
  count(region, wt = pop) |>
  ggplot(
    aes(n, reorder(region, n),
      fill = region
    )
  ) +
  scale_fill_brewer(palette = "Dark2") +
  geom_col(width = .6) +
  theme_minimal() +
  labs(
    x = NULL, y = NULL,
    title = "Default"
  ) +
  guides(fill = "none")

With the countryscales package we can use the scale_x/y_xxx_locale and label_xxx_locale functions to add labels and format the axis of the base plot according to German style conventions like this:

base +
  geom_label(
    aes(
      label = label_number_locale(
        locale = "de-DE", accuracy = 1000
      )(n)
    ),
    hjust = 1, fill = NA,
    label.size = NA, color = "white"
  ) +
  scale_x_number_locale(
    locale = "de-DE",
    expand = expansion(mult = c(0, .05))
  ) +
  labs(title = "German style conventions.")

Horizontal bar chart titled 'German style conventions.' showing total 2015 population by region, from Asia (about 4.3 billion, longest bar) down to Europe (about 830 million, shortest bar), with Africa and the Americas in between. Axis tick labels and the value label on each bar are formatted with German number conventions, using a period as the thousands separator, for example '4.306.430.000' for Asia.

countryscales also ships ready-to-use functions for 27 countries — from Argentina to the United States — each with a label_number_xx()/ scale_x_number_xx() family pinned to that country’s own locale (see the full list in the reference index). For instance, you can use label_number_ch and scale_x_number_ch to format the plot using Swiss style conventions:

base +
  geom_label(
    aes(
      label = label_number_ch(accuracy = 1000)(n)
    ),
    hjust = 1, fill = NA,
    label.size = NA, color = "white"
  ) +
  scale_x_number_ch(
    expand = expansion(mult = c(0, .05))
  ) +
  labs(title = "Swiss style conventions.")

Horizontal bar chart titled 'Swiss style conventions.', showing the same 2015 population-by-region comparison as the German-style chart above, but formatted with Swiss number conventions: an apostrophe as the thousands separator, for example '4'306'430'000' for Asia, used for both the axis tick labels and the value label on each bar.

Note on supported locales

countryscales uses data on locale-specific numbering formats from the Common Locale Data Repository (CLDR) provided for easy use in R by the i18n package. i18n lists 574 locales; countryscales supports 552 of them directly, plus a further set of bare-language-code aliases (e.g. "en", which resolves to a sensible default regional variant), for 764 usable locale codes in total — run show_locales() to list them all. Not supported are locales which deviate from the international norm for grouping digits by threes. This includes locales using the Indian numbering system which

groups the rightmost three digits together (until the hundreds place), and thereafter groups by sets of two digits.

Note on tests

The label_xxx_locale family of functions are tested against the output of Intl.NumberFormat to ensure correctness for each supported locale. For example, to test that label_currency_locale correctly formats numbers as currencies in the German locale, the output is checked against the output of the JS code

const number = 123456;

console.log(
  new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'USD' }).format(
    number,
  ),
);

Credits

countryscales would not be possible without the work by other people:



Try the countryscales package in your browser

Any scripts or data that you put into this service are public.

countryscales documentation built on Sept. 29, 2026, 5:09 p.m.