UNPKG

typedoc

Version:

Create api documentation for TypeScript projects.

230 lines (229 loc) 9.77 kB
import { ok } from "assert"; import { DefaultMap, unique } from "../utils/index.js"; import { readdirSync } from "fs"; import { join } from "path"; import { ReflectionKind } from "../models/reflections/kind.js"; import { ReflectionFlag } from "../models/index.js"; import {} from "./translatable.js"; import translatable from "./locales/en.cjs"; import { createRequire } from "node:module"; import { fileURLToPath } from "node:url"; /** * Simple internationalization module which supports placeholders. * See {@link TranslatableStrings} for a description of how this module works and how * plugins should add translations. */ export class Internationalization { application; allTranslations = new DefaultMap((lang) => { const req = createRequire(fileURLToPath(import.meta.url)); // Make sure this isn't abused to load some random file by mistake ok(/^[A-Za-z-]+$/.test(lang), "Locale names may only contain letters and dashes"); try { return new Map(Object.entries(req(`./locales/${lang}.cjs`))); } catch { return new Map(); } }); /** * Proxy object which supports dynamically translating * all supported keys. This is generally used rather than the translate * method so that renaming a key on the `translatable` object that contains * all of the default translations will automatically update usage locations. */ proxy = new Proxy(this, { get(internationalization, key) { return (...args) => internationalization.translate(key, ...args); }, }); /** * If constructed without an application, will use the default language. * Intended for use in unit tests only. * @internal */ constructor(application) { this.application = application; } /** * Get the translation of the specified key, replacing placeholders * with the arguments specified. */ translate(key, ...args) { return (this.allTranslations.get(this.application?.lang ?? "en").get(key) ?? translatable[key]).replace(/\{(\d+)\}/g, (_, index) => { return args[+index] ?? "(no placeholder)"; }); } kindSingularString(kind) { switch (kind) { case ReflectionKind.Project: return this.proxy.kind_project(); case ReflectionKind.Module: return this.proxy.kind_module(); case ReflectionKind.Namespace: return this.proxy.kind_namespace(); case ReflectionKind.Enum: return this.proxy.kind_enum(); case ReflectionKind.EnumMember: return this.proxy.kind_enum_member(); case ReflectionKind.Variable: return this.proxy.kind_variable(); case ReflectionKind.Function: return this.proxy.kind_function(); case ReflectionKind.Class: return this.proxy.kind_class(); case ReflectionKind.Interface: return this.proxy.kind_interface(); case ReflectionKind.Constructor: return this.proxy.kind_constructor(); case ReflectionKind.Property: return this.proxy.kind_property(); case ReflectionKind.Method: return this.proxy.kind_method(); case ReflectionKind.CallSignature: return this.proxy.kind_call_signature(); case ReflectionKind.IndexSignature: return this.proxy.kind_index_signature(); case ReflectionKind.ConstructorSignature: return this.proxy.kind_constructor_signature(); case ReflectionKind.Parameter: return this.proxy.kind_parameter(); case ReflectionKind.TypeLiteral: return this.proxy.kind_type_literal(); case ReflectionKind.TypeParameter: return this.proxy.kind_type_parameter(); case ReflectionKind.Accessor: return this.proxy.kind_accessor(); case ReflectionKind.GetSignature: return this.proxy.kind_get_signature(); case ReflectionKind.SetSignature: return this.proxy.kind_set_signature(); case ReflectionKind.TypeAlias: return this.proxy.kind_type_alias(); case ReflectionKind.Reference: return this.proxy.kind_reference(); case ReflectionKind.Document: return this.proxy.kind_document(); } } kindPluralString(kind) { switch (kind) { case ReflectionKind.Project: return this.proxy.kind_plural_project(); case ReflectionKind.Module: return this.proxy.kind_plural_module(); case ReflectionKind.Namespace: return this.proxy.kind_plural_namespace(); case ReflectionKind.Enum: return this.proxy.kind_plural_enum(); case ReflectionKind.EnumMember: return this.proxy.kind_plural_enum_member(); case ReflectionKind.Variable: return this.proxy.kind_plural_variable(); case ReflectionKind.Function: return this.proxy.kind_plural_function(); case ReflectionKind.Class: return this.proxy.kind_plural_class(); case ReflectionKind.Interface: return this.proxy.kind_plural_interface(); case ReflectionKind.Constructor: return this.proxy.kind_plural_constructor(); case ReflectionKind.Property: return this.proxy.kind_plural_property(); case ReflectionKind.Method: return this.proxy.kind_plural_method(); case ReflectionKind.CallSignature: return this.proxy.kind_plural_call_signature(); case ReflectionKind.IndexSignature: return this.proxy.kind_plural_index_signature(); case ReflectionKind.ConstructorSignature: return this.proxy.kind_plural_constructor_signature(); case ReflectionKind.Parameter: return this.proxy.kind_plural_parameter(); case ReflectionKind.TypeLiteral: return this.proxy.kind_plural_type_literal(); case ReflectionKind.TypeParameter: return this.proxy.kind_plural_type_parameter(); case ReflectionKind.Accessor: return this.proxy.kind_plural_accessor(); case ReflectionKind.GetSignature: return this.proxy.kind_plural_get_signature(); case ReflectionKind.SetSignature: return this.proxy.kind_plural_set_signature(); case ReflectionKind.TypeAlias: return this.proxy.kind_plural_type_alias(); case ReflectionKind.Reference: return this.proxy.kind_plural_reference(); case ReflectionKind.Document: return this.proxy.kind_plural_document(); } } flagString(flag) { switch (flag) { case ReflectionFlag.None: throw new Error("Should be unreachable"); case ReflectionFlag.Private: return this.proxy.flag_private(); case ReflectionFlag.Protected: return this.proxy.flag_protected(); case ReflectionFlag.Public: return this.proxy.flag_public(); case ReflectionFlag.Static: return this.proxy.flag_static(); case ReflectionFlag.External: return this.proxy.flag_external(); case ReflectionFlag.Optional: return this.proxy.flag_optional(); case ReflectionFlag.Rest: return this.proxy.flag_rest(); case ReflectionFlag.Abstract: return this.proxy.flag_abstract(); case ReflectionFlag.Const: return this.proxy.flag_const(); case ReflectionFlag.Readonly: return this.proxy.flag_readonly(); case ReflectionFlag.Inherited: return this.proxy.flag_inherited(); } } translateTagName(tag) { const tagName = tag.substring(1); const translations = this.allTranslations.get(this.application?.lang ?? "en"); if (translations.has(`tag_${tagName}`)) { return translations.get(`tag_${tagName}`); } // In English, the tag names are the translated names, once turned // into title case. return (tagName.substring(0, 1).toUpperCase() + tagName .substring(1) .replace(/[a-z][A-Z]/g, (x) => `${x[0]} ${x[1]}`)); } /** * Add translations for a string which will be displayed to the user. */ addTranslations(lang, translations, override = false) { const target = this.allTranslations.get(lang); for (const [key, val] of Object.entries(translations)) { if (!target.has(key) || override) { target.set(key, val); } } } /** * Checks if we have any translations in the specified language. */ hasTranslations(lang) { return this.allTranslations.get(lang).size > 0; } /** * Gets a list of all languages with at least one translation. */ getSupportedLanguages() { return unique([ ...readdirSync(join(fileURLToPath(import.meta.url), "../locales")).map((x) => x.substring(0, x.indexOf("."))), ...this.allTranslations.keys(), ]).sort(); } }