@locamorph/translation-processor
v1.0.0
Published
Parse and export translation files in multiple formats
Downloads
26
Maintainers
Readme
@locamorph/translation-processor
Parse and export translation files in multiple formats. Works in both browser and Node.js.
Installation
npm install @locamorph/translation-processorSupported Formats
| Format | Parser | Exporter | |--------|--------|----------| | JSON | ✅ | ✅ | | ARB | ✅ | - | | YAML | ✅ | - | | Android XML | ✅ | - | | iOS Strings | ✅ | - | | Properties | ✅ | - | | XLIFF | ✅ | - |
Usage
Parsing Content
import { parseContent, detectFileFormat } from '@locamorph/translation-processor';
// Auto-detect format and parse
const format = detectFileFormat(content);
const keys = parseContent(content, format);
// => [{ key: 'greeting', value: 'Hello' }, ...]Exporting Translations
import { exportContent } from '@locamorph/translation-processor';
const translations = {
greeting: {
file: 'default',
key: 'greeting',
val: 'Hello',
opts: [1, 0, null],
meta: ['2024-01-01', '2024-01-01'],
},
};
const baseTranslations = translations; // For base language, same as translations
const content = exportContent(translations, {
format: 'json',
options: {
pluralFormat: 'icu',
indentation: '2',
nestedKeysEnabled: true,
nestedKeysSeparator: '.',
sortKeysBy: 'key-name-az',
emptyTranslations: 'dont-export',
convertLineBreaks: true,
addNewlineAtEof: true,
},
baseTranslations,
});Generating Multiple Files
import { generateExportFiles, LanguageExportData } from '@locamorph/translation-processor';
const baseTranslations = {
common: {
greeting: { file: 'common', key: 'greeting', val: 'Hello', opts: [1, 0, null], meta: ['2024-01-01', '2024-01-01'] },
},
};
const languagesData: LanguageExportData[] = [
{
languageCode: 'en',
translations: baseTranslations,
baseTranslations: baseTranslations,
},
{
languageCode: 'es',
translations: {
common: {
greeting: { file: 'common', key: 'greeting', val: 'Hola', opts: [1, 0, null], meta: ['2024-01-02', '2024-01-02'] },
},
},
baseTranslations: baseTranslations,
},
];
// Single file per language (no {FILE} in pattern)
const files = generateExportFiles(languagesData, {
format: 'json',
options: exportOptions,
fileStructure: 'locale/{LANG_ISO}.{FORMAT}',
});
// => [{ path: 'locale/en.json', content: '{"greeting":"Hello"}' }, ...]
// Multiple files per language (pattern contains {FILE})
const multiFiles = generateExportFiles(languagesData, {
format: 'json',
options: exportOptions,
fileStructure: '{LANG_ISO}/{FILE}',
});
// => [{ path: 'en/common.json', content: '...' }, ...]Two-Step Export (process then serialize)
import { generateExportData, serializeTranslations } from '@locamorph/translation-processor';
// Step 1: Get processed data objects
const files = generateExportData(languagesData, config);
// => [{ path: 'locale/en.json', data: { greeting: 'Hello' } }, ...]
// Step 2: Serialize to target format
files.forEach(file => {
const content = serializeTranslations(file.data, {
format: 'json',
options: { indentation: '2', addNewlineAtEof: true }
});
// write content to file.path...
});API Reference
Detection Functions
detectFileFormat(content)- Detect format from contentdetectFileFormatWithConfidence(content)- Detect with confidence scoredetectLanguageFromContent(content, format)- Extract language codedetectFormatFromExtension(extension)- Get format from file extensiondetectLanguageFromFilename(filename)- Extract language from filename
Parsing Functions
parseContent(content, format)- Parse content to translation keys
Import Functions
parseFileToTranslations(content, filePath, fileName?)- Parse file content and extract translations as flat key-value pairs
Export Functions
generateExportFiles(languagesData, config)- Generate serialized files for multiple languagesgenerateExportData(languagesData, config)- Generate processed data objects (not serialized)serializeTranslations(data, config)- Serialize processed data to target format (JSON, etc.)exportContent(translations, config)- Export single file to specified formatexportToJson(translations, options, baseTranslations)- Direct JSON exportgetDefaultFilePattern(single)- Get default file path pattern (true= single file,false= multiple files)validateFilePattern(pattern)- Validate a file path patternvalidateSourceFilePattern(pattern)- Validate a source file glob patternresolveFilePath(pattern, languageCode, format, fileName?)- Resolve placeholders in a patternparseFilePath(filePath, basePath, fileStructure)- Extract values from a file path (reverse of resolveFilePath)createNamespace(input, basePath?)- Create a namespace from a file path for use in translation file paths
File Path Placeholders
Use getDefaultFilePattern(single) to get the default pattern:
import { getDefaultFilePattern } from '@locamorph/translation-processor';
getDefaultFilePattern(true) // => '{LANG_ISO}.{FORMAT}'
getDefaultFilePattern(false) // => '{LANG_ISO}/{FILE}'When configuring fileStructure in ExportFileConfig, use these placeholders:
| Placeholder | Description | Example |
|-------------|-------------|---------|
| {LANG_ISO} | Language code | en, es, fr |
| {FORMAT} | File format extension | json, yaml |
| {FILE} | File name with format extension | common.json |
The mode is automatically determined:
- Single file mode: Pattern does NOT contain
{FILE}→ all translations merged into one file per language - Multiple file mode: Pattern contains
{FILE}→ one file per source file per language
Examples:
// Single file per language: locale/en.json, locale/es.json
fileStructure: 'locale/{LANG_ISO}.{FORMAT}'
// Multiple files per language: en/common.json, en/errors.json
fileStructure: '{LANG_ISO}/{FILE}'
// Custom structures
fileStructure: 'i18n/{LANG_ISO}/messages.{FORMAT}' // Single: i18n/en/messages.json
fileStructure: 'locales/{LANG_ISO}/{FILE}' // Multiple: locales/en/common.jsonValidation & Parsing:
import { validateFilePattern, resolveFilePath, parseFilePath } from '@locamorph/translation-processor';
// Validate patterns before use
const result = validateFilePattern('{LANG_ISO}/{FILE}');
if (!result.valid) {
console.error(result.error);
}
// Resolve placeholders (pattern → path)
resolveFilePath('{LANG_ISO}.{FORMAT}', 'en', 'json') // => 'en.json'
resolveFilePath('{LANG_ISO}/{FILE}', 'en', 'json', 'common') // => 'en/common.json'
// Parse file paths (path → values) - reverse of resolveFilePath
parseFilePath('/project/locales/de/default.json', '/project/locales', '{LANG_ISO}/{FILE}')
// => { langIso: 'de', file: 'default', format: 'json' }
parseFilePath('/project/locales/de/nested/path/file.json', '/project/locales', '{LANG_ISO}/{FILE}')
// => { langIso: 'de', file: 'nested/path/file', format: 'json' }
parseFilePath('/project/i18n/en.json', '/project/i18n', '{LANG_ISO}.{FORMAT}')
// => { langIso: 'en', format: 'json' }Validation rules:
- No leading or trailing
/or.characters - Must contain
{LANG_ISO}or{FILE}placeholder - Only valid placeholders:
{LANG_ISO},{FORMAT},{FILE} - Only alphanumeric, hyphen, underscore, dot, slash characters allowed
Namespace Creation:
Use createNamespace(input, basePath?) to create a namespace from a file path:
import { createNamespace } from '@locamorph/translation-processor';
createNamespace('nested/path/file') // => 'nested/path/file'
createNamespace('nested/path/file.json') // => 'nested/path/file'
createNamespace('/nested/path/') // => 'nested/path'
createNamespace('test.json') // => 'test'
createNamespace('.hidden') // => 'hidden'
createNamespace('file name') // => 'file-name'
createNamespace('path//double//slash') // => 'path/double/slash'
// With basePath (strips prefix before normalizing):
createNamespace('nested/path/file', 'nested') // => 'path/file'
createNamespace('nested/path/file.json', '/nested') // => 'path/file'
createNamespace('nested/path/file.json', './nested') // => 'path/file'
createNamespace('nested/path/file.json', 'nested/') // => 'path/file'
createNamespace('/nested/path/', '/nested') // => 'path'
createNamespace('nested/test.json', '/nested') // => 'test'Normalization rules (applied in order):
- Strip basePath prefix if provided
- Trim whitespace
- Replace spaces with hyphens
- Remove invalid characters (keep only alphanumeric, hyphen, underscore, forward slash)
- Remove file extension if present (
.json,.yaml,.xml, etc.) - Remove leading/trailing
/and.characters - Collapse multiple consecutive slashes
Importing Translations:
Use parseFileToTranslations(content, filePath, fileName?) to parse file content and extract translations:
import { parseFileToTranslations } from '@locamorph/translation-processor';
const content = '{ "hello": "world", "greeting": "Hello!" }';
const translations = parseFileToTranslations(content, '/locales/en/common.json');
// Returns:
// [
// { key: 'hello', value: 'world', file: 'common' },
// { key: 'greeting', value: 'Hello!', file: 'common' }
// ]
// Override the file namespace:
const translations2 = parseFileToTranslations(content, '/locales/en/common.json', 'messages');
// Returns:
// [
// { key: 'hello', value: 'world', file: 'messages' },
// { key: 'greeting', value: 'Hello!', file: 'messages' }
// ]- Format is auto-detected from file extension
- Returns empty array if format is unsupported or parsing fails
- The
fileproperty is extracted from the filename (without extension) unless overridden
Source File Pattern Validation:
Use validateSourceFilePattern(pattern) to validate glob patterns for source file matching:
import { validateSourceFilePattern } from '@locamorph/translation-processor';
// Valid patterns
validateSourceFilePattern('translations/**/*.json') // => { valid: true }
validateSourceFilePattern('**/*.{json,yaml}') // => { valid: true }
validateSourceFilePattern('src/locales/**/*.yaml') // => { valid: true }
validateSourceFilePattern('!node_modules/**/*.json') // => { valid: true }
// Invalid patterns
validateSourceFilePattern('*.*') // => { valid: false, error: '... too broad ...' }
validateSourceFilePattern('src/*') // => { valid: false, error: '... must specify extension ...' }
validateSourceFilePattern('src/') // => { valid: false, error: '... cannot end with "/" ...' }
validateSourceFilePattern('$HOME/*.json') // => { valid: false, error: '... invalid characters ...' }
validateSourceFilePattern('**/*.txt') // => { valid: false, error: '... Unsupported file extension ...' }Validation rules:
- Cannot be empty
- No trailing
/character - Cannot use overly broad patterns (
*.*,**/*.*,*,**) - Must specify a file extension (e.g.,
*.jsonnot just*) - Extension must be a supported format (json, yaml, yml, xml, properties, strings, arb, xliff, xlf)
- Only safe characters allowed: alphanumeric,
-,_,.,/, and glob chars (*,?,[],{},!,,)
Format Configuration
FILE_FORMATS- All supported format configsgetSupportedFormats()- Formats with parsersgetExportableFormats()- Formats with exportersgetFormatById(id)- Get format config by ID
License
MIT
