typedoc
Version:
Create api documentation for TypeScript projects.
230 lines (229 loc) • 9.77 kB
JavaScript
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();
}
}