CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-formatjs--cli

A comprehensive CLI for internationalization workflows with message extraction, compilation, and verification capabilities.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

formatters.mddocs/

Format System

Pluggable formatter system for converting between different translation management system (TMS) formats, with built-in support for major TMS providers and the ability to create custom formatters.

Capabilities

Formatter Interface

Core interface that all formatters must implement for processing translation data.

/**
 * Formatter interface for processing translation data
 */
interface Formatter<T> {
  /** Optional serialization function for custom output formatting */
  serialize?: SerializeFn<T>;
  /** Format function for converting extracted messages to TMS format */
  format: FormatFn<T>;
  /** Compile function for converting TMS format back to react-intl format */
  compile: CompileFn<T>;
  /** Optional message comparison function for sorting/deduplication */
  compareMessages?: Comparator;
}

/** Format function type for converting messages to TMS format */
type FormatFn<T = Record<string, MessageDescriptor>> = (
  msgs: Record<string, MessageDescriptor>
) => T;

/** Compile function type for converting TMS format to react-intl format */
type CompileFn<T = Record<string, MessageDescriptor>> = (
  msgs: T
) => Record<string, string>;

/** Serialization function type for custom string output */
type SerializeFn<T = Record<string, MessageDescriptor>> = (
  msgs: T
) => string;

/** Message comparison function for sorting */
type Comparator = (a: {key: string, value: any}, b: {key: string, value: any}) => number;

/** Generic element type for JSON comparison */
type Element = any;

Formatter Resolution

Resolves built-in formatters by name or loads custom formatter modules.

/**
 * Resolve built-in formatter by name or load custom formatter
 * @param format - Formatter name or path to custom formatter, or Formatter object
 * @returns Promise resolving to formatter module
 */
function resolveBuiltinFormatter(
  format?: string | Formatter<unknown>
): Promise<any>;

Built-in Formatters

Default Formatter

Standard FormatJS format that preserves complete message descriptor structure.

// Default format functions
const format: FormatFn = msgs => msgs;

const compile: CompileFn = msgs => {
  const results: Record<string, string> = {};
  for (const k in msgs) {
    results[k] = msgs[k].defaultMessage!;
  }
  return results;
};

Usage:

import { extractAndWrite } from "@formatjs/cli-lib";

// Uses default formatter (no format specified)
await extractAndWrite(['src/**/*.tsx'], {
  outFile: 'messages/extracted.json'
});

Output Format:

{
  "greeting": {
    "id": "greeting",
    "defaultMessage": "Hello {name}!",
    "description": "Greeting message for users"
  }
}

Simple Formatter

Simplified key-value format without metadata.

// Simple formatter (built-in: 'simple')

Usage:

await extractAndWrite(['src/**/*.tsx'], {
  format: 'simple',
  outFile: 'messages/simple.json'
});

Output Format:

{
  "greeting": "Hello {name}!",
  "farewell": "Goodbye!"
}

Crowdin Formatter

Format compatible with Crowdin translation management system.

// Crowdin formatter (built-in: 'crowdin')

Usage:

await extractAndWrite(['src/**/*.tsx'], {
  format: 'crowdin',
  outFile: 'crowdin/source.json'
});

await compileAndWrite(['crowdin/translated-fr.json'], {
  format: 'crowdin',
  outFile: 'compiled/fr.json'
});

Lokalise Formatter

Format compatible with Lokalise translation management system.

// Lokalise formatter (built-in: 'lokalise')

Usage:

await extractAndWrite(['src/**/*.tsx'], {
  format: 'lokalise',
  outFile: 'lokalise/source.json'
});

Smartling Formatter

Format compatible with Smartling translation management system.

// Smartling formatter (built-in: 'smartling')

Usage:

await extractAndWrite(['src/**/*.tsx'], {
  format: 'smartling',
  outFile: 'smartling/source.json'
});

Transifex Formatter

Format compatible with Transifex translation management system.

// Transifex formatter (built-in: 'transifex')

Usage:

await extractAndWrite(['src/**/*.tsx'], {
  format: 'transifex',
  outFile: 'transifex/source.json'
});

Custom Formatters

Creating Custom Formatters

Create a custom formatter by implementing the Formatter interface:

// custom-formatter.js
export const format = (msgs) => {
  // Transform extracted messages to your custom format
  const customFormat = {};
  for (const [key, descriptor] of Object.entries(msgs)) {
    customFormat[key] = {
      text: descriptor.defaultMessage,
      context: descriptor.description,
      metadata: {
        id: descriptor.id,
        file: descriptor.file
      }
    };
  }
  return customFormat;
};

export const compile = (msgs) => {
  // Transform your custom format back to react-intl format
  const compiled = {};
  for (const [key, value] of Object.entries(msgs)) {
    compiled[key] = value.text;
  }
  return compiled;
};

export const serialize = (msgs) => {
  // Optional: custom string serialization
  return JSON.stringify(msgs, null, 2);
};

export const compareMessages = (a, b) => {
  // Optional: custom message comparison for sorting
  return a.key.localeCompare(b.key);
};

Using Custom Formatters

import { extractAndWrite, compileAndWrite } from "@formatjs/cli-lib";

// Use custom formatter for extraction
await extractAndWrite(['src/**/*.tsx'], {
  format: './formatters/my-formatter.js',
  outFile: 'custom/extracted.json'
});

// Use custom formatter for compilation
await compileAndWrite(['custom/translated-fr.json'], {
  format: './formatters/my-formatter.js',
  outFile: 'compiled/fr.json'
});

Usage Examples

Extraction with Different Formatters

import { extractAndWrite } from "@formatjs/cli-lib";

// Extract to different TMS formats
await Promise.all([
  // Crowdin format
  extractAndWrite(['src/**/*.tsx'], {
    format: 'crowdin',
    outFile: 'tms/crowdin/source.json'
  }),
  
  // Lokalise format
  extractAndWrite(['src/**/*.tsx'], {
    format: 'lokalise', 
    outFile: 'tms/lokalise/source.json'
  }),
  
  // Simple key-value format
  extractAndWrite(['src/**/*.tsx'], {
    format: 'simple',
    outFile: 'simple/messages.json'
  })
]);

Compilation with Formatters

import { compileAndWrite } from "@formatjs/cli-lib";

// Compile from different TMS formats
await compileAndWrite(['tms/crowdin/fr.json'], {
  format: 'crowdin',
  outFile: 'compiled/fr.json',
  ast: true
});

await compileAndWrite(['tms/smartling/es.json'], {
  format: 'smartling', 
  outFile: 'compiled/es.json',
  ast: true
});

Programmatic Formatter Usage

import { resolveBuiltinFormatter, extract } from "@formatjs/cli-lib";

// Get formatter and use programmatically
const crowdinFormatter = await resolveBuiltinFormatter('crowdin');

const result = await extract(['src/components/*.tsx'], {
  format: crowdinFormatter
});

// Apply custom processing
const customProcessed = crowdinFormatter.format(result.messages);

CLI Usage

Extraction with Formatters

# Extract to Crowdin format
formatjs extract 'src/**/*.tsx' --format crowdin --out-file crowdin/source.json

# Extract to Lokalise format
formatjs extract 'src/**/*.tsx' --format lokalise --out-file lokalise/source.json

# Extract with custom formatter
formatjs extract 'src/**/*.tsx' --format ./formatters/custom.js --out-file custom/source.json

# Extract to simple key-value format
formatjs extract 'src/**/*.tsx' --format simple --out-file simple/messages.json

Compilation with Formatters

# Compile from Crowdin format
formatjs compile crowdin/fr.json --format crowdin --out-file compiled/fr.json --ast

# Compile from Smartling format
formatjs compile smartling/es.json --format smartling --out-file compiled/es.json

# Compile with custom formatter
formatjs compile custom/de.json --format ./formatters/custom.js --out-file compiled/de.json

Batch Operations with Formatters

# Compile entire folder with specific formatter
formatjs compile-folder tms/lokalise/ compiled/ --format lokalise --ast

Formatter Comparison

FormatterExtractionCompilationMetadataUse Case
defaultFull descriptorsdefaultMessage onlyCompleteDevelopment, source format
simpleKey-value onlyDirect mappingNoneSimple workflows
crowdinCrowdin compatibleCrowdin compatibleLimitedCrowdin TMS
lokaliseLokalise compatibleLokalise compatibleLimitedLokalise TMS
smartlingSmartling compatibleSmartling compatibleLimitedSmartling TMS
transifexTransifex compatibleTransifex compatibleLimitedTransifex TMS
customUser definedUser definedUser definedCustom workflows

Error Handling

import { resolveBuiltinFormatter, extractAndWrite } from "@formatjs/cli-lib";

try {
  // This will throw if formatter doesn't exist
  const formatter = await resolveBuiltinFormatter('./nonexistent-formatter.js');
} catch (error) {
  console.error('Cannot resolve formatter:', error.message);
}

try {
  await extractAndWrite(['src/**/*.tsx'], {
    format: 'invalid-formatter',
    outFile: 'output.json'
  });
} catch (error) {
  console.error('Formatter error:', error.message);
}

Best Practices

Formatter Selection

  • Use default for development and when you need complete message metadata
  • Use simple for basic key-value workflows without metadata
  • Use TMS-specific formatters (crowdin, lokalise, etc.) when integrating with those services
  • Create custom formatters when you have specific format requirements

Custom Formatter Guidelines

  1. Implement all required functions: At minimum, implement format and compile
  2. Handle edge cases: Account for missing fields, malformed data
  3. Preserve message integrity: Ensure round-trip conversion preserves message content
  4. Document format: Clearly document your custom format structure
  5. Test thoroughly: Test extraction and compilation with real translation data

Integration Patterns

// Environment-specific formatter selection
const getFormatter = (env) => {
  switch (env) {
    case 'crowdin': return 'crowdin';
    case 'lokalise': return 'lokalise';
    case 'development': return 'default';
    default: return './formatters/production.js';
  }
};

await extractAndWrite(['src/**/*.tsx'], {
  format: getFormatter(process.env.TMS_PROVIDER),
  outFile: 'extracted.json'
});

docs

compilation.md

extraction.md

formatters.md

index.md

verification.md

tile.json