A speculative polyfill to support i18n code translations in Angular
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Manages i18n message collections with support for template processing, message extraction, and translation file generation.
Central class for managing collections of i18n messages with template processing and serialization capabilities.
/**
* Manages a collection of i18n messages for a specific locale
* Provides methods for adding messages from templates and generating translation files
*/
class MessageBundle {
/**
* Creates a new MessageBundle instance
* @param locale - Optional locale string (default: null)
*/
constructor(locale?: string | null);
/**
* Adds messages from a template string or I18nDef object
* Parses the template for translatable content and extracts messages
* @param template - Template string or I18nDef object containing translatable content
* @param url - Source file URL/path for tracking message origins
* @returns Array of extracted i18n.Message objects
*/
updateFromTemplate(template: string | I18nDef, url: string): i18n.Message[];
/**
* Returns all messages currently in the bundle
* @returns Array of all i18n.Message objects in the bundle
*/
getMessages(): i18n.Message[];
/**
* Generates translation file content using specified serializer
* @param write - Serializer write function (e.g., xliffWrite, xliff2Write)
* @param digest - Digest function for message ID generation
* @param xmlMessagesById - Optional existing XML messages to merge
* @param createMapper - Optional placeholder mapper factory function
* @param filterSources - Optional source filtering function
* @returns Generated translation file content as string
*/
write(
write: (messages: i18n.Message[], locale: string | null, existingNodes?: xml.Node[]) => string,
digest: (message: i18n.Message) => string,
xmlMessagesById?: {[id: string]: xml.Node},
createMapper?: (message: i18n.Message) => PlaceholderMapper,
filterSources?: (path: string) => string
): string;
}Parses HTML templates to extract i18n messages and handle translation merging.
/**
* Parser for HTML templates with i18n message extraction capabilities
*/
class HtmlParser {
/**
* Creates a new HtmlParser instance with default interpolation configuration
*/
constructor();
/**
* Parses HTML content and extracts structure
* @param source - HTML source code to parse
* @param url - Source URL/path for error reporting
* @param parseExpansionForms - Whether to parse ICU expressions (default: false)
* @returns Parse result with nodes and any errors
*/
parse(source: string, url: string, parseExpansionForms?: boolean): html.ParseTreeResult;
/**
* Merges translations into parsed HTML nodes
* @param rootNodes - Parsed HTML root nodes
* @param translations - Translation bundle with message mappings
* @param params - Interpolation parameters
* @param metadata - Message metadata (id, meaning, description)
* @param implicitTags - Array of implicit HTML tags to handle
* @returns Merged nodes with translations applied
*/
mergeTranslations(
rootNodes: html.Node[],
translations: TranslationBundle,
params: {[key: string]: any},
metadata: MessageMetadata,
implicitTags: string[]
): ExtractionResult;
}Manages loaded translation data and provides translation lookup capabilities.
/**
* Container for loaded translation data with message lookup capabilities
*/
class TranslationBundle {
/**
* Loads translation data from various sources
* @param translations - Translation file content string
* @param url - Source URL for the translations
* @param digest - Message digest function
* @param createMapper - Placeholder mapper factory function
* @param loadFct - Loader function to parse translation content
* @param missingTranslationStrategy - Strategy for handling missing translations
* @returns TranslationBundle instance with loaded translations
*/
static load(
translations: string,
url: string,
digest: (message: i18n.Message) => string,
createMapper: (message: i18n.Message) => PlaceholderMapper,
loadFct: (content: string, url: string) => I18nMessagesById,
missingTranslationStrategy: MissingTranslationStrategy
): TranslationBundle;
/**
* Retrieves translation for a message
* @param srcMsg - Source message to translate
* @returns Translated message or null if not found
*/
get(srcMsg: i18n.Message): i18n.Message | null;
/**
* Checks if a translation exists for a message
* @param srcMsg - Source message to check
* @returns True if translation exists, false otherwise
*/
has(srcMsg: i18n.Message): boolean;
}Contains results from message extraction operations.
/**
* Results from i18n message extraction operations
*/
class ExtractionResult {
constructor(
rootNodes: html.Node[],
errors: i18n.I18nError[]
);
/** Extracted/processed HTML nodes */
rootNodes: html.Node[];
/** Any errors encountered during extraction */
errors: i18n.I18nError[];
}import { MessageBundle } from "@ngx-translate/i18n-polyfill/extractor";
import { xliffWrite, xliffDigest } from "@ngx-translate/i18n-polyfill/serializers";
// Create a message bundle for English locale
const bundle = new MessageBundle("en");
// Add simple string messages
bundle.updateFromTemplate("Hello world", "app.component.ts");
bundle.updateFromTemplate("Welcome to our application", "home.component.ts");
// Add messages with interpolation
bundle.updateFromTemplate("Hello {{name}}", "greeting.component.ts");
// Add I18nDef objects with metadata
bundle.updateFromTemplate({
value: "You have {{count}} new messages",
id: "notification.count",
meaning: "notification",
description: "Shows count of new messages to user"
}, "notification.component.ts");
// Get all messages
const messages = bundle.getMessages();
console.log(`Extracted ${messages.length} messages`);
// Generate XLIFF translation file
const xliffContent = bundle.write(xliffWrite, xliffDigest);
console.log(xliffContent);import { HtmlParser, TranslationBundle } from "@ngx-translate/i18n-polyfill/parser";
import { xliffLoadToI18n, xliffDigest } from "@ngx-translate/i18n-polyfill/serializers";
// Parse HTML template
const parser = new HtmlParser();
const parseResult = parser.parse(
'<div i18n="@@welcome">Hello {{name}}</div>',
'template.html',
true
);
if (parseResult.errors.length === 0) {
// Load existing translations
const translationBundle = TranslationBundle.load(
xliffTranslationContent,
'messages.xlf',
xliffDigest,
null,
xliffLoadToI18n,
MissingTranslationStrategy.Warning
);
// Merge translations with template
const mergedResult = parser.mergeTranslations(
parseResult.rootNodes,
translationBundle,
{ name: 'John' },
{ id: 'welcome' },
['div']
);
console.log('Merged content:', mergedResult.rootNodes);
}import { MessageBundle } from "@ngx-translate/i18n-polyfill/extractor";
import {
xliffWrite, xliffDigest,
xliff2Write, xliff2Digest,
xmbWrite, xmbDigest, xmbMapper
} from "@ngx-translate/i18n-polyfill/serializers";
const bundle = new MessageBundle("en");
// Add various message types
bundle.updateFromTemplate("Simple message", "app.ts");
bundle.updateFromTemplate("Hello {{user}}", "greeting.ts");
bundle.updateFromTemplate({
value: "Updated {count, plural, =0 {no items} =1 {one item} other {# items}}",
id: "item.count",
description: "Item count with pluralization"
}, "list.ts");
// Export to different formats
const xliffOutput = bundle.write(xliffWrite, xliffDigest);
const xliff2Output = bundle.write(xliff2Write, xliff2Digest);
const xmbOutput = bundle.write(xmbWrite, xmbDigest, undefined, xmbMapper);
// Save to files
require('fs').writeFileSync('messages.xlf', xliffOutput);
require('fs').writeFileSync('messages.xlf2', xliff2Output);
require('fs').writeFileSync('messages.xmb', xmbOutput);import { TranslationBundle } from "@ngx-translate/i18n-polyfill/parser";
import { xliffLoadToI18n, xliffDigest } from "@ngx-translate/i18n-polyfill/serializers";
// Load translation file content
const translationContent = require('fs').readFileSync('messages.fr.xlf', 'utf8');
// Create translation bundle
const bundle = TranslationBundle.load(
translationContent,
'messages.fr.xlf',
xliffDigest,
null, // No placeholder mapper needed
xliffLoadToI18n,
MissingTranslationStrategy.Warning
);
// Check for specific translations
const sourceMessage = createI18nMessage("Hello world");
if (bundle.has(sourceMessage)) {
const translation = bundle.get(sourceMessage);
console.log('Translation found:', translation);
} else {
console.log('Translation missing for:', sourceMessage);
}The typical workflow for message bundle processing:
updateFromTemplate() to add messages from source codewrite() method with appropriate serializerThe message bundle system provides comprehensive error handling: