Parse and serialize Gettext PO files for JavaScript internationalization and localization.
npx @tessl/cli install tessl/npm-pofile@1.1.0Pofile is a JavaScript library for parsing and serializing GNU Gettext PO (Portable Object) files, widely used for internationalization (i18n) and localization (l10n) of software applications. It provides comprehensive support for all standard Gettext features including message contexts, plural forms, translator comments, extracted comments, reference strings, and message flags.
npm install pofilevar PO = require('pofile');For ES modules (modern environments):
import PO from 'pofile';The package exports a single PO class constructor function.
var PO = require('pofile');
// Create new empty PO file
var po = new PO();
// Parse PO file from string
var po = PO.parse(myString);
// Load PO file from disk (Node.js only)
PO.load('text.po', function (err, po) {
if (err) throw err;
// Use po object
});
// Create a translation item
var item = new PO.Item();
item.msgid = 'Hello';
item.msgstr = ['Hola'];
po.items.push(item);
// Save to disk (Node.js only)
po.save('out.po', function (err) {
if (err) throw err;
});
// Serialize to string
var poString = po.toString();Create, load, parse, and save PO files with full support for headers and comments.
/**
* PO constructor - creates new empty PO file instance
*/
function PO();
/**
* Load PO file from filesystem (Node.js only)
* @param filename - Path to PO file
* @param callback - Callback with (err, po) parameters
*/
PO.load = function(filename, callback);
/**
* Parse PO file content from string
* @param data - PO file content as string
* @returns PO instance
*/
PO.parse = function(data);
/**
* Parse Plural-Forms header value
* @param pluralFormsString - Plural-Forms header value
* @returns Object with nplurals and plural properties
*/
PO.parsePluralForms = function(pluralFormsString);Convert PO objects back to standard PO file format.
/**
* Write PO file to disk (Node.js only)
* @param filename - Output file path
* @param callback - Callback with (err) parameter
*/
po.save = function(filename, callback);
/**
* Serialize PO object to string format
* @returns String representation of PO file
*/
po.toString = function();Create and manage individual translation entries with full Gettext feature support.
/**
* PO.Item constructor - creates new translation item/entry
* @param options - Object with nplurals property
*/
function PO.Item(options);
/**
* Serialize item to PO file entry format
* @returns String representation of PO item
*/
item.toString = function();/**
* PO file instance properties
*/
interface PO {
/** Array of translator comments found at file header */
comments: string[];
/** Array of extracted comments from source code */
extractedComments: string[];
/** PO file headers with standard Gettext header fields */
headers: {
'Project-Id-Version': string;
'Report-Msgid-Bugs-To': string;
'POT-Creation-Date': string;
'PO-Revision-Date': string;
'Last-Translator': string;
'Language': string;
'Language-Team': string;
'Content-Type': string;
'Content-Transfer-Encoding': string;
'Plural-Forms': string;
[name: string]: string;
};
/** Order of headers as they appear in file (used to preserve header sequence when serializing) */
headerOrder: string[];
/** Collection of translation items/entries */
items: PO.Item[];
}/**
* Translation item/entry properties
*/
interface PO.Item {
/** Original message identifier */
msgid: string;
/** Message context for disambiguation */
msgctxt: string | null;
/** Plural form message identifier */
msgid_plural: string | null;
/** Translated message strings (plural forms) */
msgstr: string[];
/** Source code references where message appears */
references: string[];
/** Translator comments */
comments: string[];
/** Comments extracted from source code */
extractedComments: string[];
/** Message flags (e.g., fuzzy, c-format) */
flags: { [flag: string]: boolean };
/** Whether entry is marked obsolete (entries marked with #~ prefix) */
obsolete: boolean;
/** Number of plural forms (defaults to 2) */
nplurals: number;
}/**
* Result of parsing Plural-Forms header
*/
interface ParsedPluralForms {
/** Number of plural forms (undefined if not specified) */
nplurals: string | undefined;
/** Plural form expression (undefined if not specified) */
plural: string | undefined;
}var PO = require('pofile');
var po = new PO();
// Simple translation
var item1 = new PO.Item();
item1.msgid = 'Hello World';
item1.msgstr = ['Hola Mundo'];
po.items.push(item1);
// Translation with context
var item2 = new PO.Item();
item2.msgctxt = 'greeting';
item2.msgid = 'Hello';
item2.msgstr = ['Hola'];
po.items.push(item2);
// Plural translation
var item3 = new PO.Item();
item3.msgid = 'There is %d item';
item3.msgid_plural = 'There are %d items';
item3.msgstr = ['Hay %d elemento', 'Hay %d elementos'];
po.items.push(item3);
// Translation with comments and references
var item4 = new PO.Item();
item4.msgid = 'File';
item4.msgstr = ['Archivo'];
item4.comments = ['Menu item for File menu'];
item4.extractedComments = ['Translators: Keep this short'];
item4.references = ['src/menus.js:42'];
item4.flags = { fuzzy: true };
po.items.push(item4);var PO = require('pofile');
var po = new PO();
// Set standard headers
po.headers['Project-Id-Version'] = 'My App 1.0';
po.headers['Language'] = 'es';
po.headers['Content-Type'] = 'text/plain; charset=UTF-8';
po.headers['Plural-Forms'] = 'nplurals=2; plural=(n != 1);';
// Control header order
po.headerOrder = ['Project-Id-Version', 'Language', 'Content-Type', 'Plural-Forms'];var PO = require('pofile');
var fs = require('fs');
// Parse from string
var content = fs.readFileSync('messages.po', 'utf8');
var po = PO.parse(content);
// Process items
po.items.forEach(function(item) {
if (item.flags.fuzzy) {
console.log('Fuzzy translation:', item.msgid);
}
if (item.msgid_plural) {
console.log('Plural forms:', item.msgstr.length);
}
if (item.msgctxt) {
console.log('Context:', item.msgctxt, 'for', item.msgid);
}
});
// Filter untranslated items
var untranslated = po.items.filter(function(item) {
return item.msgstr.every(function(str) { return str === ''; });
});
console.log('Untranslated items:', untranslated.length);
// Filter obsolete items (marked with #~ prefix)
var obsolete = po.items.filter(function(item) {
return item.obsolete;
});
console.log('Obsolete items:', obsolete.length);var PO = require('pofile');
// Parse plural forms header
var pluralInfo = PO.parsePluralForms('nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;');
console.log('Number of plural forms:', pluralInfo.nplurals);
console.log('Plural expression:', pluralInfo.plural);
// Create item with proper plural count
var item = new PO.Item({ nplurals: parseInt(pluralInfo.nplurals) });
item.msgid = 'You have %d message';
item.msgid_plural = 'You have %d messages';
item.msgstr = ['Tienes %d mensaje', 'Tienes %d mensajes', 'Tienes %d mensajes'];PO.load, po.save)File I/O methods (PO.load and po.save) are only available in Node.js environments and will not work in browsers.
The library follows Node.js error handling conventions:
// Loading files
PO.load('messages.po', function(err, po) {
if (err) {
console.error('Failed to load PO file:', err.message);
return;
}
// Process po object
});
// Saving files
po.save('output.po', function(err) {
if (err) {
console.error('Failed to save PO file:', err.message);
return;
}
console.log('PO file saved successfully');
});Parsing operations (PO.parse) are synchronous and will throw exceptions on invalid input.