A zero-dependency, tree-shakeable TypeScript library that exposes structured, localized metadata for programming languages, compatible with Node.js and browser environments.
| Field | Value |
|---|---|
| Published version | 1.40.9 on npm |
| Languages in catalog | 286 (see the full table in README.md) |
| Locales | en (required fallback), es, it, fr, de, pt |
| Package entry points | index, api, i18n, detect, detect-slugs, plus one subpath per language (generated from src/languages/*.ts) |
| Website | Static catalog + live detector + benchmark/test summaries, deployed to GitHub Pages after every release |
| Automation | Daily job checks upstream language versions and opens issues/PRs for stale metadata |
The original plan below (Phases 1–5, target v1.0.0, 20 seed languages) shipped and was
superseded long ago — the project kept growing past that target. That history is kept at the
bottom of this document; the sections above it describe the system as it exists today.
code-languages solves a simple problem: there is no canonical, typed, npm-installable source of
truth for programming language metadata. Developers building IDEs, syntax highlighters,
documentation sites, developer tools, or learning platforms always end up reinventing the same
data layer.
This library provides that layer with:
- Subpath imports — import only what you need, nothing more
- Full TypeScript types — zero guesswork, first-class DX
- Universal — works in Node.js, Deno, Bun, and any modern bundler
- Tree-shakeable — bundlers drop unused language data automatically
- Localized — display content in six base locales with regional-locale fallback
- Queryable — a fluent API to look up languages by slug, filename, runtime, package manager, category, paradigm, or ecosystem
- Community-driven — easy PR template to add or improve languages
Every language exported from this library satisfies the Language interface defined in
src/types.ts:
export type BaseLocale = 'en' | 'es' | 'it' | 'fr' | 'de' | 'pt';
export type Locale = BaseLocale | `${BaseLocale}-${string}` | (string & {});
export interface LanguageContent {
/** Display name. e.g. "TypeScript" */
name: string;
/** One-line summary, max 160 characters. */
description: string;
/** Rich multi-paragraph description in plain text. */
longDescription: string;
}
export type LanguageTranslations = { en: LanguageContent } & Partial<
Record<BaseLocale, LanguageContent>
>;
export interface LanguageTooling {
/** Execution environments, virtual machines, shells, or platforms. e.g. ["Node.js", "Browser"] */
runtimes?: string[];
/** Dependency and package management tools. e.g. ["npm", "pnpm", "Yarn"] */
packageManagers?: string[];
/** Broader ecosystems where the language is commonly used. e.g. ["Web", "JVM"] */
ecosystems?: string[];
}
export interface Language {
/** URL-safe identifier. e.g. "typescript" */
slug: string;
/** ISO 8601 date of first public release. e.g. "2012-10-01" */
publishedDate: string;
/** All file extensions associated with the language. e.g. [".ts", ".tsx"] */
extensions: string[];
/** Original author or organization. e.g. "Anders Hejlsberg / Microsoft" */
author: string;
/** Official website URL. e.g. "https://www.typescriptlang.org" */
website: string;
/** Programming paradigms. e.g. ["object-oriented", "functional", "imperative"] */
paradigms: string[];
/** Common runtimes, package managers, and ecosystems related to this language. */
tooling?: LanguageTooling;
/** Latest stable version at time of last data update. e.g. "5.4.5" */
version: string;
/** URL to the official language logo or icon. */
logo: string;
/** Reference color for the language in #RRGGBB hexadecimal form. */
color: `#${string}`;
/** Localized display content. English is required as the fallback locale. */
i18n: LanguageTranslations;
}LocalizedLanguage (returned by localizeLanguage and the fluent API) is Language with i18n
replaced by the resolved name / description / longDescription, plus locale,
resolvedLocale, and availableLocales.
Note:
name,description, andlongDescriptionmoved from top-level fields (original design) intoi18n.<locale>when localization shipped.toolingwas added later and is optional.
// Import a single language (tree-shaken — only this module is bundled)
import { typescript } from 'code-languages/typescript';
import { localizeLanguage } from 'code-languages/i18n';
const localized = localizeLanguage(typescript, 'es-PE');
console.log(localized.name); // "TypeScript"
console.log(localized.resolvedLocale); // "es"
console.log(typescript.extensions); // [".ts", ".tsx"]
console.log(typescript.paradigms); // ["object-oriented", "functional"]
// Import multiple languages
import { javascript } from 'code-languages/javascript';
import { python } from 'code-languages/python';
// Import everything (not recommended for browser bundles)
import * as languages from 'code-languages';
// Filename detection without loading the full catalog
import { detectLanguageSlug, detectLanguageSlugs } from 'code-languages/detect-slugs';
detectLanguageSlug('src/index.ts'); // "typescript"
detectLanguageSlugs('include/config.h'); // ["c", "cpp"]import { api } from 'code-languages/api';
api.language('astro').locale('es-PE').get();
await api.language('vue').locale('en-US').load(); // dynamic import, code-splitting friendly
api.detect('src/App.vue').locale('es').get();
await api.detectAll('include/config.h').locale('en').load(); // ambiguous extensions -> multiple matches
api.runtime('node').langs().get();
api.packageManager('cargo').langs().get();
api.category('backend').langs().locale('es').get(); // frontend | backend | fullstack | systems | data-science | scripting | other
api.paradigm('functional').langs().get();
api.ecosystem('jvm').info();See the API section of README.md for the full list of supported aliases per lookup (runtimes, package managers, paradigms, ecosystems).
code-languages/
├── src/
│ ├── types.ts # Language interface (single source of truth)
│ ├── index.ts # Re-exports all languages + registry helpers
│ ├── api.ts # Fluent query API (language/detect/runtime/category/...)
│ ├── languages/ # 286 files, one Language object per language
│ │ ├── typescript.ts
│ │ ├── javascript.ts
│ │ ├── python.ts
│ │ └── ...
│ └── domain/
│ ├── language/ # catalog.ts, registry.ts — in-memory + lazy-loaded index
│ ├── i18n/ # localizeLanguage + locale resolution
│ ├── detection/ # detect.ts, detect-slugs.ts, match.ts
│ ├── category/ # frontend/backend/fullstack/systems/... classification
│ ├── ecosystem/ # web/jvm/dotnet/data-science/... registry + aliases
│ ├── paradigm/ # functional/oop/declarative/... registry + aliases
│ ├── package-manager/ # npm/cargo/maven/... registry + aliases
│ └── runtime/ # node/jvm/.net/... registry + aliases
├── dist/ # Generated — do not edit
├── tests/
│ ├── language-contract.ts # Shared shape validation (expectValidLanguage)
│ ├── languages/<slug>.test.ts # One contract test per language
│ ├── api*.test.ts # api.category / api.ecosystem / api.paradigm / api.runtime
│ └── detect.test.ts
├── bench/ # Vitest benchmarks (api, detect, i18n)
├── scripts/
│ ├── check-language-versions.mjs # Checks upstream sources, opens issues/PRs for drift
│ ├── translate-language-i18n.mjs
│ ├── generate-website-data.mjs
│ ├── run-website-benchmarks.mjs
│ ├── serve-website.mjs
│ ├── summarize-website-tests.mjs
│ └── ensure-website-data-dir.mjs
├── docs/ # Generated static website (GitHub Pages)
├── .github/
│ ├── workflows/
│ │ ├── pull-request.yml # format/lint/typecheck/test/build on every PR
│ │ ├── release.yml # semantic-release on push to main
│ │ ├── docs.yml # deploys docs/ to GitHub Pages after a successful release
│ │ ├── check-language-versions.yml # daily cron, opens issues/PRs for outdated metadata
│ │ └── publish-release-dev-to.yml # posts a DEV.to article after a successful release
│ └── PULL_REQUEST_TEMPLATE.md
├── tsconfig.json
├── tsup.config.ts # entries generated dynamically from src/languages/*.ts
├── vitest.config.ts
├── .releaserc.json
├── biome.json # format + most lint rules
├── eslint.config.js # additional lint rules
├── CONTRIBUTING.md
├── CHANGELOG.md # Auto-generated by semantic-release
├── ROADMAP.md
├── language-version-report.json # Latest output of check-language-versions.mjs
└── package.json # exports map has one entry per language subpath
| Tool | Purpose |
|---|---|
tsup |
Bundler — dual ESM + CJS output, one entry per language file (generated at build time), .d.ts generation |
vitest |
Test runner + benchmark runner (vitest bench) |
biome |
Formatter and primary linter |
eslint |
Additional lint rules on top of Biome |
husky + commitlint |
Enforce Conventional Commits on every local commit |
semantic-release |
Automated versioning, changelog, npm publish, and GitHub release from commit history |
| GitHub Pages | Hosts the generated static website (catalog browser, live detector, test/benchmark summaries) |
tsup.config.ts builds its entry map dynamically by reading src/languages/ at build time —
adding a language file is enough for it to get a build entry; nothing to hand-edit there. The
exports map in package.json, on the other hand, is still maintained per-language (see
"Adding a language" in CONTRIBUTING.md).
This library uses semantic-release with Conventional Commits.
| Commit prefix | Example | Version bump |
|---|---|---|
feat: |
feat: add Ruby language |
minor x.Y.0 |
fix: |
fix: correct Python publishedDate |
patch x.y.Z |
docs: |
docs: improve TypeScript description |
none |
chore: |
chore: update biome config |
none |
BREAKING CHANGE: |
rename longDescription field |
major X.0.0 |
.releaserc.json plugins: commit-analyzer → release-notes-generator → changelog
(CHANGELOG.md) → npm → github → git (commits CHANGELOG.md, package.json,
package-lock.json back to main as chore(release): ${version} [skip ci]).
| Workflow | Trigger | Does |
|---|---|---|
pull-request.yml |
PR opened/updated against main |
npm run format (diff must be clean) → lint → typecheck → test → build → npm pack --dry-run |
release.yml |
push to main |
lint, typecheck, test, build, then semantic-release (publishes to npm + GitHub releases + tags) |
docs.yml |
after release.yml succeeds (or manual) |
npm run website:prepare, deploys docs/ to GitHub Pages |
check-language-versions.yml |
daily cron 0 8 * * * (or manual) |
runs scripts/check-language-versions.mjs against upstream sources; on schedule it opens PRs for drifted version fields and uploads language-version-report.json |
publish-release-dev-to.yml |
after release.yml succeeds (or manual) |
posts a DEV.to article summarizing the new release, if DEVTO_API_KEY is configured |
All workflows run on Node.js 22.
File: .github/PULL_REQUEST_TEMPLATE.md
## Language: <Name>
**Source(s):**
---
### Checklist
#### Data file
- [ ] `src/languages/<slug>.ts` created
- [ ] `slug` lowercase, URL-safe, matches filename
- [ ] `publishedDate` valid ISO 8601 date: `YYYY-MM-DD`
- [ ] `extensions` array, each starts with `.`
- [ ] `author`
- [ ] `website` valid HTTPS URL
- [ ] `paradigms` at least one entry
- [ ] `version` latest stable version
- [ ] `logo` stable HTTPS URL, SVG preferred
- [ ] `i18n.en.name`
- [ ] `i18n.en.description` 160 characters or fewer, factual, no marketing language
- [ ] `i18n.en.longDescription` at least 2 paragraphs
#### Wiring
- [ ] Named export added to `src/index.ts`
- [ ] Entry added to `exports` in `package.json`
- [ ] Test added at `tests/languages/<slug>.test.ts`
#### Quality
- [ ] `npm run check` passes locally
- [ ] `npm run build` passes locally
#### Commit message
- [ ] Commit follows Conventional Commits: `feat: add <Language> language`Full field rules and sourcing guidance live in CONTRIBUTING.md.
Things that would meaningfully improve the project but aren't committed to a version yet:
- Machine-translated locales need human review. The
it,fr,de, andptcontent was initially generated withtranslategemma:4b(see README). Accuracy varies per language file; no tracking mechanism currently exists to mark which ones have been human-reviewed. - Some languages are excluded from automated version checks.
scripts/check-language-versions.mjscurrently skips entries whose upstream source can't be scraped reliably (e.g. ABAP, whose source page renders dynamically) — seelanguage-version-report.json'sskippedarray. These need periodic manual verification since the daily job won't catch drift for them. Additional languages may hit the same problem as the catalog grows. - No GitHub issue templates or CODEOWNERS. Only a PR template exists; bug reports and feature requests from the community currently have no structured form, and there's no documented review policy.
- No enforced coverage threshold.
npm run test:coverageexists, but nothing fails CI if coverage regresses. - Catalog growth is open-ended. The original "top 20 languages" target (below) was a v1.0.0 bootstrapping goal, not a ceiling — the vision has always been broad, community-driven coverage. There is no fixed list of "next languages to add"; PRs proposing new languages are the mechanism.
- Candidate languages proposed for future PRs (not committed): Scratch (visual/block-based,
MIT Media Lab — fills a gap in the visual-programming category), Emacs Lisp (Lisp dialect for
Emacs configuration/extension, distinct from the generic
lispentry), Vimscript/VimL (Vim/Neovim configuration and scripting language), and Delphi/Object Pascal (Embarcadero's commercial Pascal dialect, distinct from the genericpascalentry). None are scheduled; each still requires a PR following the "Adding a Language" steps in CONTRIBUTING.md. - Locale coverage is fixed at six base locales. Adding a new base locale (e.g.
ja,zh) would touchBaseLocaleinsrc/types.ts, the i18n domain,translate-language-i18n.mjs, and every language file'si18nobject — a larger, breaking-ish change that hasn't been scoped.
Everything below was the original bootstrapping plan targeting
v1.0.0. All five phases shipped; the project's actual scope now exceeds what's described here (see "Status snapshot" at the top). Kept for historical reference only — do not treat versions, file lists, or the 20-language target below as current.
-
tsconfig.json— strict, ESM, declaration files -
tsup.config.ts— dual format, per-language entries, tree-shaking -
package.json— exports map, scripts (build,test,lint,typecheck) -
src/types.ts—Languageinterface -
src/index.ts— barrel re-export - Seed languages: TypeScript, JavaScript, Python, Rust, Go
- devDependencies:
typescript,tsup,vitest,biome,@commitlint/cli,husky
- Per-language contract tests validating shape + field rules
-
biome.json— lint + format config -
husky+commitlint— block non-conventional commits locally -
.gitignore,.npmignore
- PR validation workflow — lint, typecheck, test, build on every PR
- Release workflow — semantic-release on push to main
-
.releaserc.json— semantic-release with changelog + npm + git plugins -
NPM_TOKENsecret documented inCONTRIBUTING.md
-
.github/PULL_REQUEST_TEMPLATE.md— full checklist -
CONTRIBUTING.md— setup, field rules, sourcing guidance, commit conventions
-
README.md— install, quick-start, full API table, language index - Expanded far past the original 20-language target — 286 languages and counting
- Tree-shaking verified via per-language subpath exports
- Tagged
v1.0.0and continued past it via semantic-release (v1.40.9at time of writing) - Localization (6 locales), fluent query API, filename detection, static website, and
automated upstream version checking all shipped after
v1.0.0— none of this was in the original plan
The fastest way to grow this library is to add a language via a PR. Follow the PR template and the steps in CONTRIBUTING.md.
Each new language is a feat: commit → triggers a minor version bump automatically on merge.