How to Set Up i18n Localization for Cards in GitHub Readme Stats
GitHub Readme Stats includes a built-in i18n subsystem that enables multilingual card rendering by passing a locale parameter to the API or programmatic renderer, leveraging centralized translation dictionaries in src/translations.js.
The anuraghazra/github-readme-stats repository ships with a custom i18n implementation that supports dozens of languages across all card types including stats, repository, top languages, and WakaTime cards. Mastering i18n localization for cards allows contributors to extend language support and helps users display metrics in their native languages.
How the i18n System Works
The localization architecture consists of three core components working together to deliver translated content.
The I18n Class (src/common/I18n.js)
The I18n class serves as the translation engine. It accepts a locale string and a translations object during instantiation, then provides a t(key) method for retrieving localized strings.
// src/cards/stats.js – I18n instantiation pattern
// https://github.com/anuraghazra/github-readme-stats/blob/master/src/cards/stats.js#L14-L22
const i18n = new I18n({
locale,
translations: {
...statCardLocales({ name, apostrophe }),
...wakatimeCardLocales,
},
});
If a translation key is missing for the requested locale, the t() method throws an error, which surfaces as a clear error message in the generated card.
Translation Dictionaries (src/translations.js)
All locale strings live in src/translations.js, organized by card type:
statCardLocales– Labels for the stats card (stars, commits, PRs)repoCardLocales– Repository card strings (stars, forks, archived)langCardLocales– Top languages card textwakatimeCardLocales– Wakatime coding metrics labels
Each locale map follows a nested structure where keys like "statcard.totalstars" map to language-specific strings:
// Excerpt from src/translations.js
"statcard.totalstars": {
en: "Total Stars Earned",
fr: "Total d'étoiles",
es: "Estrellas Totales",
// ... additional locales
}
Implementing i18n in Card Renderers
Card renderers in src/cards/*.js files instantiate the I18n class and use it to localize all user-facing labels before generating SVG output.
Creating the I18n Instance
Each renderer merges the relevant locale maps and creates an I18n instance at the top of the rendering function:
// Pattern used across card renderers
const i18n = new I18n({
locale: options.locale || "en",
translations: {
...statCardLocales({ name, apostrophe }),
},
});
Translating Labels
To render localized text, call i18n.t() with the specific key:
// src/cards/stats.js – localized label usage
// https://github.com/anuraghazra/github-readme-stats/blob/master/src/cards/stats.js#L27-L33
label: i18n.t("statcard.totalstars"),
This pattern ensures that every label in the final SVG reflects the user's selected language while numeric values remain unchanged.
Validating Locales in the API Layer
The public API endpoints validate locale parameters before rendering to prevent errors from invalid language codes.
In src/api/index.js, the system checks locale availability using isLocaleAvailable:
// src/api/index.js – locale validation
// https://github.com/anuraghazra/github-readme-stats/blob/master/src/api/index.js#L70-L77
if (locale && !isLocaleAvailable(locale)) {
return renderErrorCard({
message: `Locale "${locale}" is not supported`,
// ... error details
});
}
If validation passes, the locale string flows into the card renderer. Available locales are derived from the keys in repoCardLocales["repocard.archived"] within src/translations.js.
Adding Custom Language Support
To extend the repository with a new language, modify src/translations.js and update the test suite.
- Add translations for each card type. For example, to add a custom locale
"xx":
// Extension in src/translations.js
const statCardLocales = ({ name, apostrophe }) => ({
"statcard.title": {
en: `${encodedName}'${apostrophe} GitHub Stats`,
xx: `${encodedName}'${apostrophe} Demo Stats`, // New entry
},
"statcard.totalstars": {
en: "Total Stars Earned",
xx: "Demo Stars", // New entry
},
// ... remaining keys
});
-
Verify availability by ensuring the new locale appears in the
availableLocalesarray, which the system derives fromrepoCardLocales. -
Test the implementation in
tests/i18n.test.js:
// Verification test
it("should translate to custom locale xx", () => {
const i18n = new I18n({
locale: "xx",
translations: statCardLocales({ name: "Test", apostrophe: "s" })
});
expect(i18n.t("statcard.title")).toBe("Test's Demo Stats");
});
Run npm test to confirm the key lookups function correctly.
Usage Examples
Public API Endpoint
Request localized cards via query parameter:
curl "https://github-readme-stats.vercel.app/api?username=anuraghazra&locale=es"
The locale=es parameter passes through src/api/index.js, validates against available locales, and renders the stats card with Spanish labels.
Programmatic Rendering
Render a stats card in French within a Node.js application:
import { renderStatsCard } from "./src/cards/stats.js";
const stats = {
name: "Jean Dupont",
totalStars: 42,
totalCommits: 128,
totalIssues: 3,
totalPRs: 7,
totalPRsMerged: 5,
mergedPRsPercentage: 71.4,
totalReviews: 2,
totalDiscussionsStarted: 1,
totalDiscussionsAnswered: 0,
contributedTo: 4,
rank: { percentile: 12, level: "A" },
};
const svg = renderStatsCard(stats, {
locale: "fr",
hide: [],
show_icons: true,
custom_title: "Mes stats GitHub",
});
The renderer creates an I18n instance with locale: "fr", retrieves French strings like "Total d'étoiles" for "statcard.totalstars", and generates the localized SVG.
Summary
- GitHub Readme Stats uses a custom
I18nclass insrc/common/I18n.jsto handle all card localization through a simplet(key)interface. - Translation dictionaries in
src/translations.jsorganize strings by card type (statCardLocales,repoCardLocales, etc.), supporting dozens of languages out of the box. - Card renderers instantiate
I18nwith merged locale maps and calli18n.t()for every label, ensuring consistent multilingual output. - The API layer validates locales using
isLocaleAvailableinsrc/api/index.js, returning error cards for unsupported language codes. - Adding new languages requires extending
src/translations.jswith new keys and runningnpm testto verify integration.
Frequently Asked Questions
What happens if I request an unsupported locale?
The API returns an error card. In src/api/index.js, the isLocaleAvailable function checks the requested locale against the supported list. If the check fails, the system calls renderErrorCard with the message Locale "${locale}" is not supported, preventing the renderer from attempting invalid translations.
How do I add a completely new language to the repository?
Extend src/translations.js by adding your language code as a key in each locale map (statCardLocales, repoCardLocales, etc.). Ensure you provide translations for every string key used by the cards you want to support. After adding the translations, run npm test to verify that the I18n class correctly retrieves your new strings without throwing missing-key errors.
Can I override translations for a specific card without modifying core files?
Currently, the system requires modifying src/translations.js to add or change locale data. The I18n constructor receives a static translations object built from the exported locale maps. To customize text for a single deployment, fork the repository and modify the locale dictionaries directly, then deploy your instance to Vercel.
Which cards support i18n localization?
All major card types support i18n localization: the stats card (src/cards/stats.js), repository card (src/cards/repo.js), top languages card (src/cards/top-languages.js), and WakaTime card (src/cards/wakatime.js). Each renderer imports the appropriate locale maps from src/translations.js and instantiates the I18n class to localize labels, titles, and descriptive text.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →