biblatex-csl-converter
Version:
Bibliography format converter: BibLaTeX, BibTeX, CSL-JSON, RIS, ENW, EndNote XML, Citavi, DOCX citations, ODT citations — parse, convert, and export with round-trip fidelity
237 lines (222 loc) • 6.24 kB
text/typescript
/**
* i18n module for biblatex-csl-converter
*
* Provides human-readable labels for reference types, field names, field help
* text, and option values in multiple languages.
*
* ## Structure of a locale object
* ```json
* {
* "fieldTitles": { "<fieldKey>": "<label>", … },
* "fieldHelp": { "<fieldKey>": "<help text>", … },
* "typeTitles": { "<typeKey>": "<label>", … },
* "fieldTitlesByType": { "<typeKey>": { "<fieldKey>": "<label>" }, … },
* "langidOptions": { "<langidKey>": "<label>", … },
* "otherOptions": { "<optionKey>": "<label>", … }
* }
* ```
*
* ## Per-type field overrides (`fieldTitlesByType`)
* Some field names carry a different human meaning depending on the reference
* type. For example, the `author` field of a `video` entry is conventionally
* labelled "Director(s)" rather than "Author(s)". Use `getFieldTitle()` which
* checks `fieldTitlesByType[type][field]` first and falls back to
* `fieldTitles[field]`.
*
* ## Option split
* `langidOptions` covers every value valid for the BibLaTeX `langid` field
* (including BibTeX-level aliases such as `pinyin`, `american`, `english`).
* `otherOptions` covers editortype values, pagination values, pubstate values,
* and `type` sub-field values (mathesis, phdthesis, techreport, …).
*
* ## Locale data
* The locale data lives in `src/i18n/locales.ts`, which is auto-generated by:
* npm run compile_i18n
*
* ## Supported languages
* ar, bg, cs, de, en, es, fr, it, ja, ko, nl, pl, pt-BR, pt-PT, ru, sv, tr, zh
*/
// ---------------------------------------------------------------------------
// Types (re-exported from ./types.ts to keep the public API stable)
// ---------------------------------------------------------------------------
export type {
FieldHelp,
FieldTitles,
FieldTitlesByType,
LangidOptions,
Locale,
OtherOptions,
TypeTitles,
} from "./types"
import type { Locale } from "./types"
// ---------------------------------------------------------------------------
// Locale registry
// ---------------------------------------------------------------------------
import {
ar,
bg,
cs,
da,
de,
en,
es,
fr,
it,
ja,
ko,
nb,
nl,
pl,
ptBR,
ptPT,
ru,
sv,
tr,
zh,
} from "./locales"
export {
ar,
bg,
cs,
da,
de,
en,
es,
fr,
it,
ja,
ko,
nb,
nl,
pl,
ptBR,
ptPT,
ru,
sv,
tr,
zh,
}
/**
* All built-in locales keyed by IETF language tag.
*
* Consumers that need a language not listed here can supply their own `Locale`
* object — every public helper accepts a `Locale` directly.
*/
export const locales: Readonly<Record<string, Locale>> = Object.freeze({
ar,
bg,
cs,
da,
de,
en,
es,
fr,
it,
ja,
ko,
nl,
nb,
pl,
"pt-BR": ptBR,
"pt-PT": ptPT,
ru,
sv,
tr,
zh,
})
// ---------------------------------------------------------------------------
// Helper functions
// ---------------------------------------------------------------------------
/**
* Return the `Locale` for *lang*, falling back to English when not available.
*
* Lookup order:
* 1. Exact tag (e.g. `"pt-BR"`)
* 2. Base subtag (e.g. `"pt"` from `"pt-BR"`)
* 3. English fallback
*
* @example
* getLocale("de") // → locales.de
* getLocale("pt-BR") // → locales["pt-BR"]
* getLocale("zh") // → locales.en (fallback)
*/
export function getLocale(lang: string): Locale {
if (lang in locales) {
return locales[lang]
}
const base = lang.split("-")[0]
if (base !== lang && base in locales) {
return locales[base]
}
return locales.en
}
/**
* Return the human-readable label for *fieldKey* in the context of *typeKey*,
* using *locale* for the translation.
*
* Checks `locale.fieldTitlesByType[typeKey][fieldKey]` first, then falls back
* to `locale.fieldTitles[fieldKey]`, and finally to the raw key itself.
*
* @example
* getFieldTitle(locales.en, "book", "author") // → "Author(s)"
* getFieldTitle(locales.en, "video", "author") // → "Director(s)"
* getFieldTitle(locales.de, "book", "author") // → "Autor(en)"
*/
export function getFieldTitle(
locale: Locale,
typeKey: string,
fieldKey: string,
): string {
return (
locale.fieldTitlesByType[typeKey]?.[fieldKey] ??
locale.fieldTitles[fieldKey] ??
fieldKey
)
}
/**
* Return the human-readable label for *typeKey* in *locale*, falling back to
* the raw key if not found.
*
* @example
* getTypeTitle(locales.fr, "article-journal") // → "Article de revue"
*/
export function getTypeTitle(locale: Locale, typeKey: string): string {
return locale.typeTitles[typeKey] ?? typeKey
}
/**
* Return the help/hint text for *fieldKey* in *locale*, or `undefined` when
* no help text is defined for that field.
*
* @example
* getFieldHelp(locales.en, "date") // → "In <em>Extended Date Time Format</em>…"
* getFieldHelp(locales.en, "title") // → undefined
*/
export function getFieldHelp(
locale: Locale,
fieldKey: string,
): string | undefined {
return locale.fieldHelp[fieldKey]
}
/**
* Return the human-readable label for a `langid` field value in *locale*,
* falling back to the raw key if not found.
*
* @example
* getLangidTitle(locales.de, "french") // → "Französisch"
* getLangidTitle(locales.en, "brportuguese") // → "Brazilian Portuguese"
*/
export function getLangidTitle(locale: Locale, langidKey: string): string {
return locale.langidOptions[langidKey] ?? langidKey
}
/**
* Return the human-readable label for a non-language option value in *locale*
* (i.e. an `editortype`, `pagination`, `pubstate`, or `type` sub-field value),
* falling back to the raw key if not found.
*
* @example
* getOtherOptionTitle(locales.de, "phdthesis") // → "Ph.D. These"
* getOtherOptionTitle(locales.en, "inpreparation") // → "In preparation"
*/
export function getOtherOptionTitle(locale: Locale, optionKey: string): string {
return locale.otherOptions[optionKey] ?? optionKey
}