Utilities for managing translations and localization of Charts-as-Code and Dashboards-as-Code. These utilities enable extracting translatable content, creating language maps, and merging translations back into content definitions.
The i18n system for Charts and Dashboards as Code:
Manages translations for Chart-as-Code content including chart names, descriptions, axis labels, series names, and reference line labels.
/**
* Handles internationalization for Chart-as-Code content
* Extracts translatable fields and merges translations back
*/
class ChartAsCodeInternalization {
/**
* Extract translatable content into a language map
* @param chartAsCode - Chart-as-Code content to extract from
* @returns Language map with chart slug as key and translatable fields
*/
getLanguageMap(chartAsCode: ChartAsCode): ChartAsCodeLanguageMap;
/**
* Merge translated content back into chart definition
* @param internalizationMap - Translated content map
* @param content - Original chart content
* @returns Updated chart with merged translations
*/
merge(
internalizationMap: ChartAsCodeLanguageMap['chart'][string],
content: ChartAsCode
): ChartAsCode;
}
/**
* Language map structure for charts
* Contains translatable fields organized by chart slug
*/
type ChartAsCodeLanguageMap = {
chart: {
[slug: string]: {
name?: string;
description?: string;
chartConfig?: {
type: ChartType;
config?: {
// Cartesian chart labels
eChartsConfig?: {
xAxis?: Array<{ name?: string }>;
yAxis?: Array<{ name?: string }>;
series?: Array<{
name?: string;
markLine?: {
data?: Array<{ name?: string }>;
};
}>;
};
// Pie chart labels
groupLabelOverrides?: Record<string, string>;
// Funnel chart labels
labelOverrides?: Record<string, string>;
// Big number labels
label?: string;
comparisonLabel?: string;
// Table column names
columns?: Record<string, { name: string }>;
};
};
};
};
};Usage Example:
import {
ChartAsCodeInternalization,
type ChartAsCodeLanguageMap,
type ChartAsCode,
} from '@lightdash/common';
const internalization = new ChartAsCodeInternalization();
// Extract translatable content from a chart
const chart: ChartAsCode = {
slug: 'revenue-trend',
name: 'Revenue Trend',
description: 'Monthly revenue analysis',
chartConfig: {
type: 'cartesian',
config: {
eChartsConfig: {
xAxis: [{ name: 'Month' }],
yAxis: [{ name: 'Revenue' }],
series: [{ name: 'Total Revenue' }],
},
},
},
// ... other properties
};
// Get language map for translation
const languageMap = internalization.getLanguageMap(chart);
/* Returns:
{
chart: {
'revenue-trend': {
name: 'Revenue Trend',
description: 'Monthly revenue analysis',
chartConfig: {
config: {
eChartsConfig: {
xAxis: [{ name: 'Month' }],
yAxis: [{ name: 'Revenue' }],
series: [{ name: 'Total Revenue' }]
}
}
}
}
}
}
*/
// After translation, merge back
const translatedMap = {
name: 'Tendencia de Ingresos',
description: 'Análisis mensual de ingresos',
chartConfig: {
config: {
eChartsConfig: {
xAxis: [{ name: 'Mes' }],
yAxis: [{ name: 'Ingresos' }],
series: [{ name: 'Ingresos Totales' }],
},
},
},
};
const translatedChart = internalization.merge(translatedMap, chart);
// Returns chart with Spanish translations merged inManages translations for Dashboard-as-Code content including dashboard names, descriptions, and tile titles.
/**
* Handles internationalization for Dashboard-as-Code content
* Extracts translatable fields and merges translations back
*/
class DashboardAsCodeInternalization {
/**
* Extract translatable content into a language map
* @param dashboardAsCode - Dashboard-as-Code content to extract from
* @returns Language map with dashboard slug as key and translatable fields
*/
getLanguageMap(dashboardAsCode: DashboardAsCode): DashboardAsCodeLanguageMap;
/**
* Merge translated content back into dashboard definition
* @param internalizationMap - Translated content map
* @param content - Original dashboard content
* @returns Updated dashboard with merged translations
*/
merge(
internalizationMap: DashboardAsCodeLanguageMap['dashboard'][string],
content: DashboardAsCode
): DashboardAsCode;
}
/**
* Language map structure for dashboards
* Contains translatable fields organized by dashboard slug
*/
type DashboardAsCodeLanguageMap = {
dashboard: {
[slug: string]: {
name?: string;
description?: string;
tiles?: Array<{
properties?: {
title?: string;
chartName?: string;
content?: string; // Markdown content
};
}>;
};
};
};Usage Example:
import {
DashboardAsCodeInternalization,
type DashboardAsCodeLanguageMap,
type DashboardAsCode,
} from '@lightdash/common';
const internalization = new DashboardAsCodeInternalization();
// Extract translatable content from a dashboard
const dashboard: DashboardAsCode = {
slug: 'sales-overview',
name: 'Sales Overview',
description: 'Key sales metrics and trends',
tiles: [
{
type: 'saved_chart',
properties: {
title: 'Revenue Chart',
chartName: 'revenue-trend',
},
},
{
type: 'markdown',
properties: {
title: 'Summary',
content: '## Overview\nSales performance summary',
},
},
],
// ... other properties
};
// Get language map for translation
const languageMap = internalization.getLanguageMap(dashboard);
/* Returns:
{
dashboard: {
'sales-overview': {
name: 'Sales Overview',
description: 'Key sales metrics and trends',
tiles: [
{ properties: { title: 'Revenue Chart', chartName: 'revenue-trend' } },
{ properties: { title: 'Summary', content: '## Overview\nSales performance summary' } }
]
}
}
}
*/
// After translation, merge back
const translatedMap = {
name: 'Resumen de Ventas',
description: 'Métricas clave y tendencias de ventas',
tiles: [
{ properties: { title: 'Gráfico de Ingresos' } },
{ properties: { title: 'Resumen', content: '## Resumen\nResumen del rendimiento de ventas' } },
],
};
const translatedDashboard = internalization.merge(translatedMap, dashboard);
// Returns dashboard with Spanish translations merged inDeep merge utility for combining translation maps with original content.
/**
* Merge translated content into original structure
* Only updates fields that exist in the translation map
* Recursively merges nested objects
* @param left - Original content object
* @param right - Translation map with updates
* @returns Merged object with translations applied
*/
function mergeExisting(left: any, right: any): any;Usage Example:
import { mergeExisting } from '@lightdash/common';
const original = {
name: 'Dashboard',
description: 'Original description',
settings: {
theme: 'light',
title: 'Original Title',
},
otherField: 'preserved',
};
const translations = {
name: 'Tableau de Bord',
description: 'Description traduite',
settings: {
title: 'Titre Traduit',
// theme not included - will be preserved
},
// otherField not included - will be preserved
};
const result = mergeExisting(original, translations);
/* Returns:
{
name: 'Tableau de Bord',
description: 'Description traduite',
settings: {
theme: 'light', // preserved
title: 'Titre Traduit' // updated
},
otherField: 'preserved' // preserved
}
*//**
* Combined type for all language maps
* Used when managing translations for multiple content types
*/
type LanguageMap = Partial<
ChartAsCodeLanguageMap & DashboardAsCodeLanguageMap
>;import {
ChartAsCodeInternalization,
DashboardAsCodeInternalization,
} from '@lightdash/common';
// Initialize internalization handlers
const chartI18n = new ChartAsCodeInternalization();
const dashboardI18n = new DashboardAsCodeInternalization();
// Extract from multiple charts and dashboards
const languageMap = {
...chartI18n.getLanguageMap(chart1),
...chartI18n.getLanguageMap(chart2),
...dashboardI18n.getLanguageMap(dashboard1),
};
// Export for translation
console.log(JSON.stringify(languageMap, null, 2));Send the language map to translators or translation service. The structure makes it easy to identify what needs translation.
import { mergeExisting } from '@lightdash/common';
// Load translated language map
const translatedMap = loadTranslations('es');
// Merge back into content
const translatedChart = chartI18n.merge(
translatedMap.chart['chart-slug'],
originalChart
);
const translatedDashboard = dashboardI18n.merge(
translatedMap.dashboard['dashboard-slug'],
originalDashboard
);The Chart i18n system handles all chart types: