import * as fs from 'node:fs'; import * as path from 'node:path'; import * as process from 'node:process'; import { TextElement, Variant, SelectExpression, Placeable, FluentParser, FluentSerializer, Pattern, } from '@fluent/syntax'; import type { Expression, Message, Term } from '@fluent/syntax'; const VOWEL_MAP: Record = { a: 'ë', e: 'ά', i: 'ī', o: 'ů', u: 'ø', }; const CONSONANT_MAP: Record = { b: 'ß', c: 'ĉ', d: 'ḍ', f: 'ƒ', g: 'ğ', h: 'ħ', j: 'ʝ', k: 'ʞ', l: '£', m: 'ɱ', n: 'ŋ', p: 'þ', q: 'ʠ', r: 'ř', s: 'š', t: '†', v: 'ʋ', w: 'ŵ', x: 'χ', y: 'ý', z: 'ʐ', }; export function transformVowels(word: string): string { return [...word].map((letter) => VOWEL_MAP[letter] || letter).join(''); } export function transformConsonants(word: string): string { return [...word].map((letter) => CONSONANT_MAP[letter] || letter).join(''); } export function preserveCase(original: string, transformed: string): string { return [...original] .map((char, index) => char === char.toUpperCase() ? transformed[index].toUpperCase() : transformed[index].toLowerCase(), ) .join(''); } export function transformWord(word: string): string { const onlyAlphabets = word.replace(/[^A-Za-z]/g, ''); if (onlyAlphabets.length === 0) return word; const transformedAlphabets = preserveCase( onlyAlphabets, transformConsonants(transformVowels(onlyAlphabets.toLowerCase())), ); return [...word] .map((char, index) => /[A-Za-z]/.test(char) ? transformedAlphabets[index++] : char, ) .join(''); } function shouldTransformToken(token: string): boolean { if (token.startsWith('.')) { return false; } if (token === token.toUpperCase()) { return false; } const placeholderPatterns = ['{', '}', '[', ']', '$']; if (placeholderPatterns.some((pattern) => token.includes(pattern))) { return false; } return true; } export function transformPattern(pattern: Pattern): Pattern { const transformedElements = pattern.elements.map((element) => { if (element.type === 'TextElement') { const textElement = element; if (textElement.value.startsWith('#')) { return textElement; } const tokens = textElement.value.split(/\s+/); const transformedTokens = tokens.map((token) => { if (shouldTransformToken(token)) { return transformWord(token); } return token; }); return new TextElement(transformedTokens.join(' ')); } return transformPlaceable(element); }); return new Pattern(transformedElements); } function transformPlaceable(placeable: Placeable): Placeable { const transformedExpression = transformExpression(placeable.expression); return new Placeable(transformedExpression); } function transformExpression(expression: Expression): Expression { switch (expression.type) { case 'SelectExpression': { const selectExpr = expression; const transformedVariants = selectExpr.variants.map((variant) => { const transformedValue = transformPattern(variant.value); return new Variant(variant.key, transformedValue, variant.default); }); return new SelectExpression(selectExpr.selector, transformedVariants); } case 'StringLiteral': case 'NumberLiteral': case 'VariableReference': case 'FunctionReference': case 'MessageReference': case 'TermReference': { return expression; } default: throw new Error(`Unknown expression type: ${expression.type}`); } } export function main(): void { const inputFilePath = process.argv[2]; if (!inputFilePath) { const errorMessage = 'Please provide the input file path as a parameter.'; console.error(errorMessage); throw new Error(errorMessage); } const absoluteInputFilePath = inputFilePath; const inputDir = path.dirname(absoluteInputFilePath); const outputFilePath = path.join(inputDir, 'dp-DP.ftl'); fs.readFile(absoluteInputFilePath, 'utf8', (err, data) => { if (err) { throw new Error( `Could not read file ${absoluteInputFilePath}: ${err.message}`, ); } const parser = new FluentParser(); const resource = parser.parse(data); resource.body = resource.body.map((entry) => { if (entry.type === 'Message' || entry.type === 'Term') { return transformFluentMessage(entry); } return entry; }); const serializer = new FluentSerializer(); const transformedData = serializer.serialize(resource); fs.writeFile(outputFilePath, transformedData, 'utf8', (err) => { if (err) { console.error(`Could not write file ${outputFilePath}:`, err); return; } console.log(`File transformed and saved as ${outputFilePath}`); }); }); } function transformFluentMessage(message: Message | Term): Message | Term { if (message.value) { message.value = transformPattern(message.value); } message.attributes.forEach((attribute) => { attribute.value = transformPattern(attribute.value); }); return message; }