Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/generator/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { revealSpoiler, scrollToMessage } from '../static/client';
import { readFileSync } from 'fs';
import path from 'path';
import { renderToString } from '@derockdev/discord-components-core/hydrate';
import type { Language } from '../languages';

// read the package.json file and get the @derockdev/discord-components-core version
let discordComponentsVersion = '^3.6.1';
Expand All @@ -35,6 +36,8 @@ export type RenderMessageContext = {
saveImages: boolean;
favicon: 'guild' | string;
hydrate: boolean;

language: Language;
};

export default async function renderMessages({ messages, channel, callbacks, ...options }: RenderMessageContext) {
Expand Down
6 changes: 3 additions & 3 deletions src/generator/renderers/reply.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ export default async function renderReply(message: Message, context: RenderMessa
<span data-goto={referencedMessage.id}>
{await renderContent(referencedMessage.content, { ...context, type: RenderType.REPLY })}
</span>
) : isCommand ? (
<em data-goto={referencedMessage.id}>Click to see command.</em>
) : (
<em data-goto={referencedMessage.id}>Click to see attachment.</em>
<em data-goto={referencedMessage.id}>
{context.language.format(isCommand ? 'reply.command' : 'reply.attachment')}
</em>
)}
</DiscordReply>
);
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
type GenerateFromMessagesOptions,
type ObjectType,
} from './types';
import { Language } from './languages';

// version check
const versionPrefix = version.split('.')[0];
Expand Down Expand Up @@ -51,6 +52,7 @@ export async function generateFromMessages<T extends ExportReturnType = ExportRe
footerText: options.footerText ?? 'Exported {number} message{s}.',
favicon: options.favicon ?? 'guild',
hydrate: options.hydrate ?? false,
language: new Language(options.language ?? 'en-US'),
});

// get the time it took to render the messages
Expand Down
8 changes: 8 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { Language } from './types';

export const en_US = {
reply: {
attachment: 'Click to see attachment.',
command: 'Click to see command.',
},
} satisfies Language;
73 changes: 73 additions & 0 deletions src/languages/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { en_US } from './en';
import type { Language as LanguageType } from './types';
import { format } from 'node:util';

/**
* Object containing all supported languages. Key is the language code, value is the language object.
*/
const languages = {
'en-US': en_US,
};

/**
* All supported languages
*/
export type Languages = keyof typeof languages;

/**
* Turns a type into a dot-notation string
*/
type DotNotation<T> = T extends Array<string>
? ''
: T extends object
? {
[K in keyof T]: T[K] extends object ? `${K & string}.${DotNotation<T[K]>}` : K & string;
}[keyof T]
: T & string;

/**
* A class to handle language strings
*/
export class Language {
private readonly language: LanguageType;

constructor(public readonly languageName: Languages) {
if (languageName in languages) {
this.language = languages[languageName];
}

console.warn(`[discord-html-transcripts] Language "${languageName}" not found, defaulting to "en-US".`);
this.language = languages['en-US'];
}

/**
* Return a formatted string with the given language path (e.g. `reply.command`)
* @param path The path to the string
* @param args The arguments to pass to the string
*/
public format(path: DotNotation<LanguageType>, ...args: unknown[]): string {
let lang = this.language;
const keys = path.split('.');

// traverse the language object
for (const key of keys) {
if (key in lang) {
// @ts-expect-error - key narrowing for ts not implemented (issue #43284)
lang = lang[key];
} else {
if (this.languageName === 'en-US') {
throw new Error(`[discord-html-transcripts] Language path "${path}" not found.`);
}

return new Language('en-US').format(path, ...args);
}
}

// if array, choose a random element
if (Array.isArray(lang)) {
lang = lang[Math.floor(Math.random() * lang.length)];
}

return format(lang, ...args);
}
}
19 changes: 19 additions & 0 deletions src/languages/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Common language type
*/
export type Language = {
reply: {
command: string;
attachment: string;
};

system: {
joinMessages: string[];
messagePinned: string;
};

// /**
// * Server boosts
// */
// boosts
};
7 changes: 7 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { AttachmentBuilder } from 'discord.js';
import type { RenderMessageContext } from './generator';
import type { Languages } from './languages';

export type AttachmentTypes = 'audio' | 'video' | 'image' | 'file';

Expand Down Expand Up @@ -64,6 +65,12 @@ export type GenerateFromMessagesOptions<T extends ExportReturnType> = Partial<{
* @default false - the returned html will be hydrated client-side
*/
hydrate: boolean;

/**
* The language to use, e.g. 'en-US'. See documentation for list of supported languages.
* @default 'en-US'
*/
language: Languages;
}>;

export type CreateTranscriptOptions<T extends ExportReturnType> = Partial<
Expand Down