TypeScript interfaces for implementing MIME renderer extensions in JupyterLab
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Internationalization interfaces providing gettext-based translation support for JupyterLab extensions.
Central interface for accessing translation services and language information.
/**
* Translation provider interface
*/
interface ITranslator {
/** The code of the language in use */
readonly languageCode: string;
/**
* Load translation bundles for a given domain
* @param domain The translation domain to use for translations
* @returns The translation bundle if found, or the English bundle
*/
load(domain: string): TranslationBundle;
}Usage Example:
import { IRenderMime } from "@jupyterlab/rendermime-interfaces";
class InternationalizedRenderer implements IRenderMime.IRenderer {
private translator?: IRenderMime.ITranslator;
constructor(options: IRenderMime.IRendererOptions) {
this.translator = options.translator;
}
async renderModel(model: IRenderMime.IMimeModel): Promise<void> {
if (this.translator) {
// Load translation bundle for this extension
const bundle = this.translator.load('my-renderer-extension');
// Get current language
const lang = this.translator.languageCode;
console.log(`Rendering in language: ${lang}`);
// Create localized UI elements
const title = bundle.__('Data Visualization');
const loadingText = bundle.__('Loading data...');
const errorText = bundle.__('Failed to load data');
// Use translations in UI
this.node.innerHTML = `
<div class="renderer-header">
<h3>${title}</h3>
</div>
<div class="renderer-content">
<p>${loadingText}</p>
</div>
`;
}
}
}Comprehensive set of translation functions supporting various gettext patterns.
/**
* Bundle of gettext-based translation functions for a specific domain
*/
type TranslationBundle = {
/**
* Alias for `gettext` (translate strings without number inflection)
* @param msgid message (text to translate)
* @param args additional values for interpolation
* @returns A translated string if found, or the original string
*/
__(msgid: string, ...args: any[]): string;
/**
* Alias for `ngettext` (translate accounting for plural forms)
* @param msgid message for singular
* @param msgid_plural message for plural
* @param n determines which plural form to use
* @param args additional values for interpolation
* @returns A translated string if found, or the original string
*/
_n(msgid: string, msgid_plural: string, n: number, ...args: any[]): string;
/**
* Alias for `pgettext` (translate in given context)
* @param msgctxt context
* @param msgid message (text to translate)
* @param args additional values for interpolation
* @returns A translated string if found, or the original string
*/
_p(msgctxt: string, msgid: string, ...args: any[]): string;
/**
* Alias for `npgettext` (translate accounting for plural forms in given context)
* @param msgctxt context
* @param msgid message for singular
* @param msgid_plural message for plural
* @param n number used to determine which plural form to use
* @param args additional values for interpolation
* @returns A translated string if found, or the original string
*/
_np(msgctxt: string, msgid: string, msgid_plural: string, n: number, ...args: any[]): string;
/**
* Look up the message id in the catalog and return the corresponding message string.
* Otherwise, the message id is returned.
* @param msgid message (text to translate)
* @param args additional values for interpolation
* @returns A translated string if found, or the original string
*/
gettext(msgid: string, ...args: any[]): string;
/**
* Do a plural-forms lookup of a message id. msgid is used as the message id for
* purposes of lookup in the catalog, while n is used to determine which plural form
* to use. Otherwise, when n is 1 msgid is returned, and msgid_plural is returned in
* all other cases.
* @param msgid message for singular
* @param msgid_plural message for plural
* @param n determines which plural form to use
* @param args additional values for interpolation
* @returns A translated string if found, or the original string
*/
ngettext(msgid: string, msgid_plural: string, n: number, ...args: any[]): string;
/**
* Look up the context and message id in the catalog and return the corresponding
* message string. Otherwise, the message id is returned.
* @param msgctxt context
* @param msgid message (text to translate)
* @param args additional values for interpolation
* @returns A translated string if found, or the original string
*/
pgettext(msgctxt: string, msgid: string, ...args: any[]): string;
/**
* Do a plural-forms lookup of a message id. msgid is used as the message id for
* purposes of lookup in the catalog, while n is used to determine which plural
* form to use. Otherwise, when n is 1 msgid is returned, and msgid_plural is
* returned in all other cases.
* @param msgctxt context
* @param msgid message for singular
* @param msgid_plural message for plural
* @param n number used to determine which plural form to use
* @param args additional values for interpolation
* @returns A translated string if found, or the original string
*/
npgettext(msgctxt: string, msgid: string, msgid_plural: string, n: number, ...args: any[]): string;
/**
* Do a plural-forms lookup of a message id with domain and context support.
* @param domain - The translations domain
* @param msgctxt - The message context
* @param msgid - The singular string to translate
* @param msgid_plural - The plural string to translate
* @param n - The number for pluralization
* @param args - Any additional values to use with interpolation
* @returns A translated string if found, or the original string
*/
dcnpgettext(domain: string, msgctxt: string, msgid: string, msgid_plural: string, n: number, ...args: any[]): string;
};import { IRenderMime } from "@jupyterlab/rendermime-interfaces";
function createLocalizedRenderer(translator?: IRenderMime.ITranslator): IRenderMime.IRenderer {
return {
async renderModel(model: IRenderMime.IMimeModel): Promise<void> {
if (translator) {
const bundle = translator.load('my-extension');
// Simple string translation
const title = bundle.__('Data Viewer');
const subtitle = bundle.__('Showing %s items', model.data.length);
this.node.innerHTML = `
<h2>${title}</h2>
<p>${subtitle}</p>
`;
}
}
} as IRenderMime.IRenderer;
}function createItemCounter(translator?: IRenderMime.ITranslator): string {
if (!translator) return 'Items';
const bundle = translator.load('my-extension');
return (count: number) => {
// Handle plural forms
return bundle._n(
'%d item found', // singular
'%d items found', // plural
count, // count for pluralization
count // value for substitution
);
};
}
// Usage
const counter = createItemCounter(translator);
console.log(counter(1)); // "1 item found"
console.log(counter(5)); // "5 items found"function createStatusMessages(translator?: IRenderMime.ITranslator) {
if (!translator) return { processing: 'Processing', complete: 'Complete' };
const bundle = translator.load('my-extension');
return {
// Same word, different contexts
processing: bundle._p('status', 'Processing'),
complete: bundle._p('status', 'Complete'),
// Buttons might have different translations
processingButton: bundle._p('button', 'Processing'),
completeButton: bundle._p('button', 'Complete')
};
}function createFileMessages(translator?: IRenderMime.ITranslator) {
if (!translator) return (count: number) => `${count} files`;
const bundle = translator.load('my-extension');
return {
selected: (count: number) => bundle._np(
'file-selection', // context
'%d file selected', // singular
'%d files selected', // plural
count, // count for pluralization
count // value for substitution
),
processed: (count: number) => bundle._np(
'file-processing',
'%d file processed',
'%d files processed',
count,
count
)
};
}import { IRenderMime } from "@jupyterlab/rendermime-interfaces";
function createInternationalizedExtension(
rendererFactory: IRenderMime.IRendererFactory,
translator?: IRenderMime.ITranslator
): IRenderMime.IExtension {
let bundle: IRenderMime.TranslationBundle | undefined;
if (translator) {
bundle = translator.load('my-data-renderer');
}
// Helper function for translations
const __ = (msgid: string, ...args: any[]) =>
bundle ? bundle.__(msgid, ...args) : msgid;
return {
id: 'my-org:data-renderer',
description: __('Advanced data visualization renderer'),
rendererFactory,
documentWidgetFactoryOptions: {
name: __('Data Visualizer'),
label: __('Data Viz'),
primaryFileType: 'data-viz',
fileTypes: ['data-viz'],
translator,
toolbarFactory: (widget) => [
{
name: 'refresh',
widget: createButton(__('Refresh'), () => {
console.log(__('Refreshing data...'));
})
},
{
name: 'export',
widget: createButton(__('Export'), () => {
console.log(__('Exporting data...'));
})
}
]
},
fileTypes: [
{
name: 'data-viz',
mimeTypes: ['application/data-viz'],
extensions: ['.dviz'],
displayName: __('Data Visualization')
}
]
};
}
function createButton(label: string, onClick: () => void) {
// Create a simple button widget
const button = document.createElement('button');
button.textContent = label;
button.onclick = onClick;
// Return as Widget-like object
return { node: button } as any;
}class AdaptiveRenderer implements IRenderMime.IRenderer {
constructor(private options: IRenderMime.IRendererOptions) {}
async renderModel(model: IRenderMime.IMimeModel): Promise<void> {
const translator = this.options.translator;
if (translator) {
const bundle = translator.load('my-renderer');
const lang = translator.languageCode;
// Adapt rendering based on language
const isRTL = ['ar', 'he', 'fa'].includes(lang);
if (isRTL) {
this.node.style.direction = 'rtl';
this.node.style.textAlign = 'right';
}
// Use appropriate number formatting
const formatter = new Intl.NumberFormat(lang);
const data = model.data as { count: number };
this.node.innerHTML = `
<div class="data-display">
<h3>${bundle.__('Data Summary')}</h3>
<p>${bundle.__('Count: %s', formatter.format(data.count))}</p>
</div>
`;
}
}
}