msg-cli is a command-line tool for the msg library. It helps you scaffold internationalization (i18n) and localization (l10n) layout and wire up your project for use with msg.
Current status: CLI for the msg library (npm: @worldware/msg-cli, v0.2.2). Commands: init (scaffold i18n/l10n and config), create project (new MsgProject in i18n/projects), create resource (new MsgResource in i18n/resources), export (serialize MsgResources to XLIFF 2.2 in l10n/xliff), import (import translations from XLIFF 2.0/2.2 to JSON in l10n/translations).
Installation
Install globally:
npminstall-g@worldware/msg-cli
Or use via npx:
npxmsg <command>
For project-local setup, run msg init in your project root to add @worldware/msg and scaffold directories and config. Install msg-cli globally (as above) or as a dev dependency to run the commands.
Core Concepts
i18n (internationalization): Source message projects and resources. MsgProject and MsgResource files live under the i18n directory (default src/i18n).
l10n (localization): Exported XLIFF files and imported translation JSON. Exports and translation data live under the l10n directory (default res/l10n).
Import aliases: The init command adds #i18n/* and #l10n/* (and #root/*) to package.json so you can import with short paths like import project from '#i18n/projects/main'.
* source and targets are optional when --extend is passed; they are inherited from the base project.
Flag
Short
Description
--extend
-e
Extend an existing project.
--help
-h
Show help for create project.
Examples:
# Create project myApp with source en and targets fr, de msgcreateprojectmyAppenfrde
# Extend an existing project (inherits source and targets from base) msgcreateprojectextendedApp--extendbase
# Extend and add/override locales msgcreateprojectextendedAppende--extendbase
# Help msgcreateproject-h
Behavior:
Writes the file to i18n/projects/<projectName>.js (always .js).
Uses ES module or CommonJS export syntax based on package.json"type" or presence of tsconfig.json.
Generates a translation loader that imports from l10n/translations using the relative path from i18n/projects (from directories in package.json).
Includes pseudoLocale: 'en-XA' by default (or inherits from the base project when extending), for use with msg's getTranslation(pseudoLocale) pseudolocalization support.
With --extend <name>, merges target locales and pseudoLocale from the existing project. If source and targets are omitted, they are inherited from the base project.
Errors if the project name already exists, package.json is missing or invalid, or required directories are not configured.
create resource
Create a new MsgResource file in the i18n resources directory. Requires msg init and a project file in i18n/projects (run msg create project first).
msgcreateresource <projectName> <title> [-f] [-e]
Argument
Required
Description
projectName
Yes
Name of the project to import in the MsgResource.
title
Yes
Title of the resource and file name (e.g. messages → messages.msg.js).
Flag
Short
Description
--force
-f
Overwrite an existing resource file.
--edit
-e
Open the file for editing after creation.
--help
-h
Show help for create resource.
Examples:
# Create resource messages for project myProject msgcreateresourcemyProjectmessages
# Create and open in editor msgcreateresourcemyProjectmessages--edit
Behavior:
Writes the file to i18n/resources/<title>.msg.js (always .js).
Uses ES module or CommonJS export syntax based on package.json"type" or presence of tsconfig.json.
Sets lang from the project's sourceLocale and dir to rtl for Arabic/Hebrew, ltr otherwise.
Includes a minimal example message. Validates that the generated file is importable.
Errors if i18n/projects or i18n/resources does not exist, the project is not found, or the resource file already exists (unless --force).
export
Serialize all MsgResource files in i18n/resources to XLIFF 2.2 files in l10n/xliff, one file per project. Does not send files for translation; use your own translation workflow with the generated XLIFF. Requires package.json with directories.i18n and directories.l10n (run msg init first).
msgexport [-p <projectName>]
Flag
Short
Description
--project
-p
Export only the named project.
--help
-h
Show help for the export command.
Examples:
# Export all projects to l10n/xliff msgexport
# Export only project "myApp" msgexport--projectmyApp msgexport-pmyApp
Behavior:
Recursively finds all .msg.js and .msg.ts files under i18n/resources.
Imports each file as a MsgResource; errors if any file is invalid.
Groups resources by project name and writes one XLIFF 2.2 file per project to l10n/xliff (e.g. myApp.xliff).
With --project, only that project is exported; existing other files in l10n/xliff are not removed.
If no MsgResource files are found, exits with an informational message (no error).
Logs each major step (finding files, importing, grouping, writing).
What is preserved in XLIFF 2.2:
Message keys — Stored as unit id (sanitized for XML) and name (original key).
Resource notes — Emitted as file-level <notes> with category (e.g. description, comment).
Message notes — Emitted as unit-level <notes> with category (e.g. description, context, parameters).
Message attributes — dnt → unit translate="no"; message dir is serialized as the unit’s srcDir attribute (XLIFF text direction for the segment).
Message format — Resolved format (NONE / MF1 / MF2, including inheritance from resource/project) is written as the unit type attribute using the XLIFF custom form msg:NONE, msg:MF1, or msg:MF2.
Plural, gender, select (PGS): Classifiable plural/select messages are exported to the XLIFF 2.2 Plural, Gender, and Select module (xmlns:pgs="urn:oasis:names:tc:xliff:pgs:1.0"): pgs:switch on the <unit>, and one <segment> per variant with pgs:case.
MF2 — Unicode MessageFormat 2 .match messages (see pgs-mf2). Segment bodies keep MF2 pattern text. Unsupported .match shapes fall back to a single segment with the full string in <source>.
MF1 — ICU MessageFormat 1 plural / selectordinal / select messages (see pgs-mf1). Segment bodies use ICU-friendly text (# for the plural/ordinal variable, {name} for others).
NONE — Always a single segment with the raw string (no PGS).
import
Import translations from bilingual XLIFF 2.0 or 2.2 files in l10n/xliff to JSON files in l10n/translations. Expects XLIFF files with trgLang (target language) and translated content in <target> elements. Writes JSON files without notes for minimal size. Requires package.json with directories.i18n and directories.l10n (run msg init first).
Format and PGS on import:
Unit type of msg:NONE / msg:MF1 / msg:MF2 (or bare NONE / MF1 / MF2) is restored as the message attributes.format.
Units with pgs:switch are reassembled from segment bodies (<target> when present): MF1 → nested ICU MessageFormat 1; otherwise (including missing type) → MessageFormat 2 .match. Single-segment units are unchanged.
msgimport [-p <projectName>] [-l <locale>]
Flag
Short
Description
--project
-p
Import only the named project.
--language
-l
Import only the specified locale.
--help
-h
Show help for the import command.
Examples:
# Import all XLIFF files msgimport
# Import only project "myApp" msgimport--projectmyApp msgimport-pmyApp
# Import only locale "zh" msgimport--languagezh msgimport-lzh
Behavior:
Recursively finds all .xliff and .xlf files under l10n/xliff.
Project and target locale are taken from the filename (e.g. myApp.zh.xliff) or from path segments (e.g. myApp/zh/messages.xliff).
Dynamically imports MsgProject from i18n/projects to validate target locales.
Skips monolingual XLIFF files (no trgLang).
Skips files whose target locale is not in the project's targetLocales.
Skips files when the matching project file does not exist or cannot be loaded.
Writes JSON to l10n/translations/<project>/<locale>/<title>.json.
Preserves existing translation files for other projects or locales when filtering.
Errors on malformed XLIFF and logs each step.
Example: plural messages (XLIFF 2.2 PGS)
Plural (and other classifiable select) messages use the XLIFF 2.2 PGS module: export splits them into one <segment> per variant (pgs:case), and import merges <target> text back into a single message string per key. The unit type (msg:MF1 or msg:MF2) selects ICU MessageFormat 1 vs MessageFormat 2 reconstruction.
1. Message in the MsgResource (MF2 .match, or MF1 {count, plural, ...} with format: "MF1"):
The plural becomes a pgs:switch unit with one segment per case (abbreviated):
One item{$n} items
3. Bilingual file for translation — msg import needs trgLang and <target> elements. Typical workflow: copy the export to a locale-specific name such as myApp.fr.xliff, set trgLang="fr" on the root <xliff>, and add a <target> under each<segment> (preserving pgs:switch / pgs:case). The project’s MsgProject must list fr in targetLocales.
One itemUn élément{$n} items{$n} éléments
4. Import — writes minimal JSON under l10n/translations/<project>/<locale>/<resource>.json, with one message per key rebuilt from the PGS segments (MF2 .match when type is msg:MF2 or omitted; ICU MF1 when type is msg:MF1):