If a larger TMX file just has, for example, a longer chain of <tu\>
units, then you may be able to extract a JSON Array
version in this pattern:
[
{
"en": "Hello world!",
"fr": "Bonjour tout le monde!"
},
{
"en": "spring",
"fr": "printemps"
}
]
and then extract the parts you want with Keyboard Maestro's JSON substring notation:
manual:JSON [Keyboard Maestro Wiki]
token:JSONValue [Keyboard Maestro Wiki]
Elements extracted from TMX file.kmmacros (8.2 KB)
Expand disclosure triangle to view JS Source
(() => {
"use strict";
// Rob Trew @2022
// First guess at reading TMX files.
const main = () => {
const
kme = Application("Keyboard Maestro Engine"),
xml = kme.getvariable("tmxSample");
return either(
alert("Reading TMX files")
)(x => x)(
bindLR(
xmlNodeFromXmlStringLR(xml)
)(
node => Right(transUnitsFromNode(node))
)
);
};
// ----------------------- XML -----------------------
// transUnitsFromNode :: XMLNode -> [Dict]
const transUnitsFromNode = xmlNode => {
const
unWrap = ObjC.unwrap,
tmx = xmlNode.childAtIndex(0),
body = tmx.childAtIndex(1),
tuList = unWrap(body.children);
return tuList.map(
tu => unWrap(tu.children).reduce(
(a, tuv) => Object.assign(a, {
[
unWrap(
tuv
.attributeForName("xml:lang")
.stringValue
)
]: unWrap(
tuv.childAtIndex(0).stringValue
)
}), {}
)
);
};
// xmlNodeFromXmlStringLR :: XML String ->
// Either String NSXMLNode
const xmlNodeFromXmlStringLR = s => {
const
error = $(),
node = $.NSXMLDocument.alloc
.initWithXMLStringOptionsError(
s, 0, error
);
return node.isNil() ? (() => {
const problem = ObjC.unwrap(
error.localizedDescription
);
return Left(
`Not parseable as XML:\n\n${problem}`
);
})() : Right(node);
};
// ----------------------- JXA -----------------------
// alert :: String => String -> IO String
const alert = title =>
s => {
const sa = Object.assign(
Application("System Events"), {
includeStandardAdditions: true
});
return (
sa.activate(),
sa.displayDialog(s, {
withTitle: title,
buttons: ["OK"],
defaultButton: "OK"
}),
s
);
};
// --------------------- GENERIC ---------------------
// Left :: a -> Either a b
const Left = x => ({
type: "Either",
Left: x
});
// Right :: b -> Either a b
const Right = x => ({
type: "Either",
Right: x
});
// bindLR (>>=) :: Either a ->
// (a -> Either b) -> Either b
const bindLR = m =>
mf => m.Left ? (
m
) : mf(m.Right);
// either :: (a -> c) -> (b -> c) -> Either a b -> c
const either = fl =>
// Application of the function fl to the
// contents of any Left value in e, or
// the application of fr to its Right value.
fr => e => e.Left ? (
fl(e.Left)
) : fr(e.Right);
// sj :: a -> String
const sj = (...args) =>
// Abbreviation of showJSON for quick testing.
// Default indent size is two, which can be
// overriden by any integer supplied as the
// first argument of more than one.
JSON.stringify.apply(
null,
1 < args.length && !isNaN(args[0]) ? [
args[1], null, args[0]
] : [args[0], null, 2]
);
return sj(main());
})();