makeChart(lang = "en", isOfficial = FALSE, filename = "english.png")
makeChart(lang = "es", isOfficial = FALSE, filename = "spanish-unofficial.png")
makeChart(lang = "es", isOfficial = TRUE, filename = "spanish-official.png")
Translations
Overview
In this tutorial, we build a two-panel fiscal chart of government revenue and capital spending. We want to support multiple languages without maintaining separate R scripts for each. A common—but brittle—approach is to copy your R file and replace all hard-coded English titles, subtitles, captions, and legends with translated strings.
A more robust workflow:
- Assign all text elements to variables instead of literals, so you can swap languages programmatically.
- Collect these variables in a central container (an R environment) for consistency and IDE support.
- Provide utilities to export/import this container to/from Excel for official translations.
This tutorial will show you how to make charts in various languages, using either unofficial translation (by ChatGPT) or official translation by language services. We encapsulate all steps—data reading, label assignment, and plotting—inside the a function makeChart()
function, which you call with:
Setup
First, load the required packages and helper scripts. These helpers live in your repo under utils/
and define the custom theme_imf_panel()
and color palette.
library(readxl)
library(writexl)
library(dplyr)
library(tidyr)
library(ggpubr)
library(patchwork)
library(ragg)
library(cowplot)
library(here)
library(ragg)
# Custom theme and annotation helpers
source(here("utils/theme_and_colors_IMF.R")) # see Chapter 7
source(here("utils/Add_text_to_figure_panel.R")) # see Chapter 5
Assign Labels to Variables, Not Literals
Instead of embedding string literals directly in your plotting code, assign all text elements (titles, subtitles, captions, legends) to variables or an environment. This allows you to:
- Swap languages easily by populating variables from different sources.
- Maintain consistency across multiple figures.
- Automate translation workflows via Excel export/import.
# NOT recommended:
ggplot(data) +
labs(title = "Ingresos del Gobierno Central")
# Recommended:
$chart1_title <- "Ingresos del Gobierno Central"
labelsggplot(data) +
labs(title = labels$chart1_title)
Centralizing Labels with Environments
An R environment is a mutable container for named objects. Use an environment to collect all your label variables:
<- new.env(parent = emptyenv())
labels $chart1_title <- "Central Government Revenue"
labels$chart1_legend <- c("Tax", "Non-Tax")
labels
# ... more labels ...
Benefits:
Encapsulation: Bundles related labels in one place.
Performance: Reference semantics avoid copying when modifying.
Scalability: Easily add or remove labels without changing function signatures.
IDE Autocomplete: Environments enable tab-completion (e.g.,
labels$<TAB>
) in RStudio. If you type in labels$ in RStudio, you get a popup:
Once you have defined all your English labels in labels
, export them to Excel for translation:
write_labels_to_excel(labels, "labels.xlsx")
Exporting and Importing Label Environments
To integrate professional translation workflows while keeping rapid prototyping simple, we provide two complementary helpers:
- Export your in-code English labels to an Excel template:
write_labels_to_excel(labels, "labels.xlsx")
This creates an XLSX with columns:
- `id`: label identifier (e.g. `chart1_title`)
- `english`: the original English text
- `translation`: an empty column for translators
Read the translated workbook back into R:
<- read_labels_from_excel("labels_translated.xlsx") labels
This recreates a
labels
environment with all translated strings, preserving vectors versus scalars.
Helper Function Definitions
write_labels_to_excel()
#' Write a labels environment to an Excel file
#'
#' @param labels_env An environment of atomic values (length-1 or character vectors)
#' @param path File path for the XLSX
<- function(labels_env, path) {
write_labels_to_excel <- as.list(labels_env)
labels_list <- data.frame(
labels_df id = names(labels_list),
english = sapply(labels_list, function(x) paste(x, collapse = ", ")),
translation = rep(NA_character_, length(labels_list)),
stringsAsFactors = FALSE
)::write_xlsx(labels_df, path = path)
writexl }
read_labels_from_excel()
#' Read labels from Excel and return an environment
#'
#' @param path Path to translated XLSX
#' @param sep Regex for splitting multi-element strings (default ",\\s*")
#' @return A new environment populated with the translated labels
<- function(path, sep = ",\\s*") {
read_labels_from_excel <- readxl::read_excel(here(path))
df <- new.env(parent = emptyenv())
env for (i in seq_len(nrow(df))) {
<- df$id[i]
key <- df$translation[i]
value <- if (grepl(sep, value)) strsplit(value, sep)[[1]] else as.character(value)
env[[key]]
}
env }
Official vs. Unofficial Translations
This workflow supports two translation modes:
Unofficial (rapid) Coders directly translate the hard-coded labels inside the
makeChart()
function or in your code environment. InmakeChart(lang = "es", isOfficial = FALSE)
, the function adds a diagonal watermark “Unofficial translation”:if (lang == "es" && !isOfficial) { <- ggdraw(pw) + pw draw_label( "Unofficial translation", x = 0.5, y = 0.5, angle = 45, fontface = "bold", colour = "grey80", size = 60, alpha = 0.30 ) }
Official (validated)
- Export English labels to Excel via
write_labels_to_excel(labels, "labels.xlsx")
. - Send
labels.xlsx
to professional language services. - When the translated file is ready, call
makeChart(lang = "es", isOfficial = TRUE)
. makeChart()
readslabels_translated.xlsx
withread_labels_from_excel()
, recreating thelabels
environment with official translations.
- Export English labels to Excel via
Producing the Fiscal Chart: makeChart()
#' Build and save the two-panel fiscal chart (Figure 7)
#'
#' @param lang "en" or "es"
#' @param isOfficial TRUE to use translated XLSX, FALSE for hard-coded Spanish
#' @param filename Output PNG file path
<- function(lang = "en", isOfficial = FALSE, filename) {
makeChart # 1. Read & pivot data
<- read_excel(
chart_1_fiscal here("databases/SR charts v1.xlsx"),
sheet = "Chart 1 Data"
)<- read_excel(
chart_2_fiscal here("databases/SR charts v1.xlsx"),
sheet = "Chart 5 Data"
)
<- function(data) {
chart_long_fiscal %>%
data rename(label = Year) %>% # keep original “Year” col
pivot_longer(-label,
names_to = "year",
values_to = "value") %>%
mutate(year = as.numeric(year))
}
<- chart_long_fiscal(chart_1_fiscal)
chart_1_fiscal_long <- chart_long_fiscal(chart_2_fiscal)
chart_2_fiscal_long
# 2. Build labels env
if (lang == "en") {
<- new.env(parent = emptyenv())
labels $chart1_title <- "Central Government Revenue"
labels$chart1_subtitle <- "(In percent of GDP)"
labels$chart1_caption <- "Source: Ministry of Finance and IMF staff estimates."
labels$chart1_legend <- c("Tax", "Non-Tax")
labels$chart2_title <- "Central Government Capital Spending"
labels$chart2_subtitle <- "(In percent of GDP)"
labels$chart2_legend <- c("Domestic", "External")
labels$main_title <- "Figure 7. Fiscal Sector Developments"
labels$main_caption <- "Sources: Bank of Jamaica and IMF staff estimates and projections."
labels
# To export English labels for translation uncomment the next line
# write_labels_to_excel(labels, "labels.xlsx")
else if (lang == "es") {
} if (isOfficial) {
<- read_labels_from_excel("labels_translated.xlsx")
labels else {
} <- new.env(parent = emptyenv())
labels $chart1_title <- "Ingresos del Gobierno Central"
labels$chart1_subtitle <- "(En porcentaje del PIB)"
labels$chart1_caption <- "Fuente: Ministerio de Hacienda y estimaciones del personal del FMI."
labels$chart1_legend <- c("Tributarios", "No tributarios")
labels$chart2_title <- "Gasto de Capital del Gobierno Central"
labels$chart2_subtitle <- "(En porcentaje del PIB)"
labels$chart2_legend <- c("Interna", "Externa")
labels$main_title <- "Figura 7. Evolución del Sector Fiscal"
labels$main_caption <- "Fuentes: Banco de Jamaica y estimaciones y proyecciones del personal del FMI.\nNote: unofficial translation"
labels
}
}
# ---------------------------------------------------------------------------#
# 3. First panel: Government revenue
# ---------------------------------------------------------------------------#
<-
fig1 %>%
chart_1_fiscal_long ggplot(aes(x = as.character(year), y = value)) +
geom_bar(stat = "identity",
fill = blue,
position = position_dodge(0.73),
color = "black",
width = 0.42) +
geom_hline(aes(yintercept = 0),
color = light_grey,
linetype = "solid") +
scale_x_discrete(expand = c(0.05, 0)) +
scale_y_continuous(limits = c(-8, 2),
breaks = seq(-8, 2, 2),
expand = c(0, 0)) +
labs(title = labels$chart1_title,
subtitle = labels$chart1_subtitle,
x = "", y = "") +
theme_imf_panel()
# ---------------------------------------------------------------------------#
# 4. Second panel: Capital spending ----------------------------------------
# ---------------------------------------------------------------------------#
<-
fig2 %>%
chart_2_fiscal_long ggplot(aes(x = as.character(year), y = value)) +
geom_bar(stat = "identity",
fill = blue,
position = position_dodge(0.73),
color = "black",
width = 0.42) +
geom_hline(aes(yintercept = 0),
color = light_grey,
linetype = "solid") +
scale_x_discrete(expand = c(0.05, 0)) +
scale_y_continuous(limits = c(0, 5),
breaks = seq(0, 5, 1),
expand = c(0, 0)) +
labs(title = labels$chart2_title,
subtitle = labels$chart2_subtitle,
x = "", y = "") +
theme_imf_panel()
# 5. Combine & annotate
<- (fig1 | fig2) +
pw plot_annotation(
title = labels$main_title,
caption = labels$main_caption,
theme = theme(
plot.title = element_text(color = blue, family = primary_font, face = "bold", size = 20, hjust = 0.5),
plot.caption = element_text(hjust = 0, family = primary_font, size = 12)
)
)
# 6. Watermark for unofficial Spanish
if (lang == "es" && !isOfficial) {
<- ggdraw(pw) +
pw draw_label("Unofficial translation", x = 0.5, y = 0.5, angle = 45,
fontface = "bold", colour = "grey80", size = 40, alpha = 0.3)
}
pw# 7. Save file
# ggsave(filename = filename, plot = pw, dpi = 600,
# height = 5.5 * 1.25, width = 9.5 * 1.25, units = "in")
}
Usage Examples
# Build English chart
<-makeChart("en", FALSE, "english.png")
fig1 fig1
# Spanish unofficial translation
<-makeChart("es", FALSE, "spanish_unofficial.png")
fig2 fig2
# Spanish official
<-makeChart("es", TRUE, "spanish_official.png")
fig3 fig3