Hello,
Sometimes when I need to write in Greek I keep writing just to realize that I haven't switch the keyboard layout to Greek, but I write in English and sometimes the opposite. So, I would like to ask you if there is a way to select all what I have already written (usually not more than 20 characters including spacing) and replace the letters with the other language's letters by simulating the same keystrokes by typing.
For example
I write βοοκ instead of book
and
I write Kalhmera instead of Καλημερα
I don't know if that's possible...
Thanks in advance!
One approach might be to create a map of corresponding characters, and a macro which copied selected text, translated character by character and then repasted.
In the draft below, I've made a first guess at the mappings, but you would need to check both dictionaries below – enGk
, and its twin gkEn
:
selected text Hellenized.kmmacros (9.6 KB)
Updated JS:
Expand disclosure triangle to view JS Source
(() => {
"use strict";
ObjC.import("AppKit");
const gkEn = {
"α": "a",
"β": "b",
"γ": "g",
"δ": "d",
"ε": "e",
"ζ": "z",
"η": "h",
"θ": "u",
"ι": "i",
"κ": "k",
"λ": "l",
"μ": "m",
"ν": "n",
"ξ": "x",
"ο": "o",
"π": "p",
"ρ": "r",
"σ": "s",
"ς": "s",
"τ": "t",
"υ": "u",
"φ": "f",
"χ": "x",
"ψ": "c",
"ω": "v",
"Α": "A",
"Β": "B",
"Γ": "G",
"Δ": "D",
"Ε": "E",
"Ζ": "Z",
"Η": "H",
"Θ": "U",
"Ι": "I",
"Κ": "K",
"Λ": "L",
"Μ": "M",
"Ν": "N",
"Ξ": "X",
"Ο": "O",
"Π": "P",
"Ρ": "R",
"Σ": "S",
"Τ": "T",
"Υ": "Y",
"Φ": "F",
"Χ": "X",
"Ψ": "C",
"Ω": "V"
};
const enGk = {
"a": "α",
"b": "β",
"g": "γ",
"d": "δ",
"e": "ε",
"z": "ζ",
"h": "η",
"u": "υ",
"i": "ι",
"k": "κ",
"l": "λ",
"m": "μ",
"n": "ν",
"x": "χ",
"o": "ο",
"p": "π",
"r": "ρ",
"s": "ς",
"t": "τ",
"f": "φ",
"c": "ψ",
"v": "ω",
"A": "Α",
"B": "Β",
"G": "Γ",
"D": "Δ",
"E": "Ε",
"Z": "Ζ",
"H": "Η",
"U": "Θ",
"I": "Ι",
"K": "Κ",
"L": "Λ",
"M": "Μ",
"N": "Ν",
"X": "Χ",
"O": "Ο",
"P": "Π",
"R": "Ρ",
"S": "Σ",
"T": "Τ",
"Y": "Υ",
"F": "Φ",
"C": "Ψ",
"V": "Ω"
};
const main = () => {
const
kme = Application("Keyboard Maestro Engine"),
kmVar = kme.getvariable,
dict = Boolean(parseInt(kmVar("toGreek"), 10)) ? (
enGk
) : gkEn;
return either(
() => ""
)(
cs => cs.join("")
)(
bindLR(
clipTextLR()
)(
clipText => Right(
[...clipText].map(
c => dict[c] || c
)
)
)
);
};
// --------------------- 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 = lr =>
// Bind operator for the Either option type.
// If lr has a Left value then lr unchanged,
// otherwise the function mf applied to the
// Right value in lr.
mf => "Left" in lr ? (
lr
) : mf(lr.Right);
// clipTextLR :: () -> Either String String
const clipTextLR = () => {
// Either a message, (if no clip text is found),
// or the string contents of the clipboard.
const
v = ObjC.unwrap(
$.NSPasteboard.generalPasteboard
.stringForType($.NSPasteboardTypeString)
);
return Boolean(v) && 0 < v.length ? (
Right(v)
) : Left("No utf8-plain-text found in clipboard.");
};
// 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 => "Left" in e ? (
fl(e.Left)
) : fr(e.Right);
// MAIN ---
return main();
})();
Works great, thank you! Although I didn’t get the difference between the two JavaScripts…
Is there any chance that it recognizes the language (or the keyboard’s layout) and trigger the right “translate”?
Trivial difference, one just fractionally more efficient than the other.
recognizes the language (or the keyboard’s layout) and trigger the right “translate”?
Is that what you are after ?
Approaching family supper here, but I can look at that on Saturday evening.
(In the meanwhile, could you check the mappings to see that there are no glitches ?)
Ohh, I see!
Yes! I couldn’t find a way to check the spelling or the keyboard’s layout to decide the correct “translation”, so I can trigger both with the same keystroke.
I am already trying out the macro you posted! Thanks again! I will wait for your response, when you have time! I am not in hurry!
As for the substitution part, you may be able to use the "tr" command which can substitute one set of characters for another all in a single command, like this:
I am not sure if you can put Greek characters into that command; you would have to test that yourself.
Thank you for your suggestion! It is very helpful. I will try it, although the solution from ComplexPoint works great!
If you would be so kind, let me know if it works on other character sets.
Took a quick look – this version aims to transcribe alphabetically:
- to English if the clipboard contains any Greek characters,
- otherwise to Greek.
selected text toggled Anglo ⇄ Greek letters.kmmacros (9.8 KB)
Expand disclosure triangle to view JS Source
(() => {
"use strict";
ObjC.import("AppKit");
// Rob Trew @2022
// Converting clipboard characters EN ⇄ Greek
const gkEn = {
"α": "a",
"β": "b",
"γ": "g",
"δ": "d",
"ε": "e",
"ζ": "z",
"η": "h",
"θ": "u",
"ι": "i",
"κ": "k",
"λ": "l",
"μ": "m",
"ν": "n",
"ξ": "x",
"ο": "o",
"π": "p",
"ρ": "r",
"σ": "s",
"ς": "w",
"τ": "t",
"υ": "u",
"φ": "f",
"χ": "x",
"ψ": "c",
"ω": "v",
"Α": "A",
"Β": "B",
"Γ": "G",
"Δ": "D",
"Ε": "E",
"Ζ": "Z",
"Η": "H",
"Θ": "U",
"Ι": "I",
"Κ": "K",
"Λ": "L",
"Μ": "M",
"Ν": "N",
"Ξ": "X",
"Ο": "O",
"Π": "P",
"Ρ": "R",
"Σ": "S",
"Τ": "T",
"Υ": "Y",
"Φ": "F",
"Χ": "X",
"Ψ": "C",
"Ω": "V"
};
const enGk = {
"a": "α",
"b": "β",
"g": "γ",
"d": "δ",
"e": "ε",
"z": "ζ",
"h": "η",
"u": "υ",
"i": "ι",
"k": "κ",
"l": "λ",
"m": "μ",
"n": "ν",
"x": "χ",
"o": "ο",
"p": "π",
"r": "ρ",
"s": "σ",
"w": "ς",
"t": "τ",
"f": "φ",
"c": "ψ",
"v": "ω",
"A": "Α",
"B": "Β",
"G": "Γ",
"D": "Δ",
"E": "Ε",
"Z": "Ζ",
"H": "Η",
"U": "Θ",
"I": "Ι",
"K": "Κ",
"L": "Λ",
"M": "Μ",
"N": "Ν",
"X": "Χ",
"O": "Ο",
"P": "Π",
"R": "Ρ",
"S": "Σ",
"T": "Τ",
"Y": "Υ",
"F": "Φ",
"C": "Ψ",
"V": "Ω"
};
const main = () =>
either(
() => ""
)(
cs => cs.join("")
)(
bindLR(
clipTextLR()
)(
clipText => {
const
dict = containsGreek(clipText) ? (
gkEn
) : enGk;
return Right(
[...clipText].map(
c => dict[c] || c
)
);
}
)
);
// --------------------- 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 = lr =>
// Bind operator for the Either option type.
// If lr has a Left value then lr unchanged,
// otherwise the function mf applied to the
// Right value in lr.
mf => "Left" in lr ? (
lr
) : mf(lr.Right);
// clipTextLR :: () -> Either String String
const clipTextLR = () => {
// Either a message, (if no clip text is found),
// or the string contents of the clipboard.
const
v = ObjC.unwrap(
$.NSPasteboard.generalPasteboard
.stringForType($.NSPasteboardTypeString)
);
return Boolean(v) && 0 < v.length ? (
Right(v)
) : Left("No utf8-plain-text found in clipboard.");
};
// containsGreek :: String -> Bool
const containsGreek = s =>
(/[\u0370-\u1FFF]/u).test(s);
// 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 => "Left" in e ? (
fl(e.Left)
) : fr(e.Right);
// MAIN ---
return main();
})();
Just updated one mapping issue in the code above (macro and listing)
on this system at least:
- whereas medial
sigma σ
is mapped to Anglo 's
' - final
sigma ς
is mapped to Anglo 'w
'
(not sure whether that is what you are seeing in the keyboard layouts you are using)
Works great my friend! Thank you so much! Have a nice weekend!