Search

Support this blog ?

Update (12/16) :

I'm canceling the e-junkie trail. Thank you to the one person who sent $5 5. I'm sending that money to support OmegaT (on a side note, the 5€ turned out to become 3.95€ after Paypal took its share both ways...)




I was reading a reply on the Script Debugger forum that mentioned a really cheap online shopping service: http://e-junkie.com.

And then I thought... I've run this blog for more than 10 years, without any Google Ad or any kind of promotional material, so why not add a button somewhere where readers are free to support the writing with the amount they want...


30 minutes later, I had that button installed, for you to click on it!


You support will go to the free and indies software that allow us to work on our macs!

No mojave on this machine

I bought this MacBook Pro in July 2011. It is a 13" machine, to which I added 16gb of ram from the start.

A few months ago I removed the hard disk that was getting really old and seemed to have issues, and put an SSD drive instead. The speed bump was immediately noticeable and it really felt like this machine was getting a second life.

Now I had one issue left. Battery life.

The original battery served me well for 7 years. After more than 1300 cycles, the capacity was down to about 50% of the original battery, which meant 2h30 of autonomy at best (unless you cut wifi and a number of services that run in the background). I started putting a recharger in my bag a few weeks ago but that was not a really satisfying situation.

This week, I decided to get a new battery. The difficult part is to identify a maker that sells good products, at a reasonable price. I eventually found a seemingly good product for about ¥8000 and installed it right away.

It looks like the battery sensors needed some time to get used to the new data. At first they were struggling to reconcile the legacy data with the new battery performance. Eventually I am at a point where I get at least 5 hours on a charge. That's a huge improvement since I'm seldom far from a power cord for more than 5 hours, and I usually don't work on the machine for that much time in a row.

So, now that my machine is almost like new (except for the bumps and scratches on the aluminium cover, for the keyboard keys that seem to have taken a hit from my nails after so many years, and for the screen that seems to have some imprinted key shapes on it that I can't remove), I was thinking that a software upgrade was in order. But I knew from when mojave was announced that High Sierra would be the last macos version I could install on that machine.

I think it's the first time my work machine is too old to support the latest version of macos. But after the hardware upgrades, and the fact that 16gb makes it run extremely smoothly even for a 7 years old machine, I don't feel like I need a new machine.

I heard stories about the touch bar and about the keyboard on the new MBPs. I wouldn't want to spend money on Apple hardware if I were not sure such issues were solved. Also, if I were to buy a new laptop, it would probably be a super light and thin machine. So unless something bad happens, I guess I'll wait for a few more years, when the applications I use require frameworks that do not support this High Sierra machine anymore. Then, I'll buy the equivalent of what the current Mac Book is. A super tight (is "tight" a contraction of "thin" and "light"?) machine, with nice specs (and they will feel especially nice after having worked on this MBP for about 10 years).

Easily launch scripts from Spotlight

I've been advertising AppleScript a lot here. Automating a task is something, but easy access to that automation is quite important too.

Since I try to stick to Apple solutions and free software* I prefer using Spotlight instead of all the smart launchers that we have for macOS.

What I do is that I call my script names that are easy to call first in Spotlight. A few screenshots speak louder than words so here we are:

The series that start with ">" usually is scripts that I use to open something.

See how just typing ">" suggests ">BB". That ">BB" is what I use to open the files selected in Finder with BBEdit.

The one below is ">Tedit", short for "TextEdit" and opens selected files in TextEdit, etc.



If I type "c" after ">", I get the following list of choices. ">command" is to launch an arbitrary command in Terminal, ">Capture" is to use org capture in Emacs (see here and here for more information) and ">cd" just opens a Terminal tab on the front Finder window, or selected Finder folder.


When I use "<" to initiate the search, I get a different list. That series is for scripts that usually act by themselves. "<text file" creates a text file in the front Finder window and proposes to open it in BBEdit for editing, "<facturation" is an invoicing script for the job selected in Finder, "<job" is a job managing script that creates a job hierarchy in Finder based on a mail, along with an event in calendar, and then "<xls2tmx" is a TMX converter for multicolumn Excel reference data (I'll publish it when it's more polished, but creating XML data with AppleScript is documented here).


I have a few more scripts (a dozen) that I routinely call with Spotlight, which I find totally sufficient for my needs.

As you know, hitting Command+Enter when you have a selection in Spotlight is a way to reveal that selection in the Finder if it is available. So when I want to edit a script, I start by calling it in Spotlight, I hit Command+Enter when it is selected and then I call ">SEditor" (Script Editor) or ">debugger" (Script Debugger) on the selection, to open it with the appropriate application...


Ok, I do have BBEdit, Microsoft Office and Illustrator... And maybe a few others...

Open a file in your editor of choice

[Update]
Chris Stone has a nice follow-up on the BBEdit user forum.


You know how it is. You double-click on a file thinking it will open in the application that you're working with at the moment and you forget that the file type was associated with a different application...

Back to Finder, right-click on the file, select "Open with..." and you're good. But mousing around macOS can be tedious at times so here is a simple script that I was pretty much given by an ASUL co-lister and that I barely had to adapt to my workflow to make it more general.


property targetApplication : "BBEdit"

tell application "Finder" to set mySelectionList to selection as alias list
if length of mySelectionList = 0 then error "No files were selected in the Finder!"

tell application targetApplication
repeat with myFile in mySelectionList
open myFile
end repeat
activate

end tell

You notice right away that the only reference to the opening application is in the first line. You can change the application name to anything you want and have multiple copies of the script, one for each application, with an appropriate name so that you can open any file with any supporting application you want.


Here is the exact same code for TextEdit:


property targetApplication : "TextEdit"

tell application "Finder" to set mySelectionList to selection as alias list
if length of mySelectionList = 0 then error "No files were selected in the Finder!"

tell application targetApplication
repeat with myFile in mySelectionList
open myFile
end repeat
activate

end tell


The same for Script Editor:


property targetApplication : "Script Editor"

tell application "Finder" to set mySelectionList to selection as alias list
if length of mySelectionList = 0 then error "No files were selected in the Finder!"

tell application targetApplication
repeat with myFile in mySelectionList
open myFile
end repeat
activate

end tell


The same for Script Debugger:

property targetApplication : "Script Debugger"

tell application "Finder" to set mySelectionList to selection as alias list
if length of mySelectionList = 0 then error "No files were selected in the Finder!"

tell application targetApplication
repeat with myFile in mySelectionList
open myFile
end repeat
activate
end tell


The same for Word:


property targetApplication : "Microsoft Word"

tell application "Finder" to set mySelectionList to selection as alias list
if length of mySelectionList = 0 then error "No files were selected in the Finder!"

tell application targetApplication
repeat with myFile in mySelectionList
open myFile
end repeat
activate

end tell


You get the drift.

Some applications don't work that way, so here is the code for Emacs.app:


use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

tell application "Finder" to set fileSelection to selection as alias list

try
tell application "System Events" to tell process "Emacs" to set frontmost to true
on error
tell application "/Users/suzume/Documents/Code/emacs/nextstep/Emacs.app"
activate
delay 1
end tell
end try

# return

repeat with selectedFile in fileSelection
set the clipboard to (POSIX path of selectedFile)
tell application "/Users/suzume/Documents/Code/emacs/nextstep/Emacs.app"
tell application "System Events"
delay 0.5
keystroke "x" using {control down}
keystroke "f" using {control down}
keystroke "a" using {control down}
keystroke "v" using {command down}
keystroke "k" using {control down}
key code 36 # Escape
delay 0.1
end tell
end tell

end repeat

The code makes use of UI scripting because Emacs does not support Applescript, but it works just as well.

What you do now, is create one script for each application you want to open your files with, save the script with a name that Spotlight will easily call first (something like ">BB" for opening in BBedit) and you're done!

Thank you Chris Stone on ASUL (and all the others) for your help !

Wordpress site translation (1) install a local copy

I'm currently working on 2 WP sites. One is the sub-site of a world-wide IT organization, the other one is the main site of a local business. I had forgotten how site localization can be a pain, so here is a summary of the solutions I'm using for the 2 sites.

The main issue with any kind of site (be it WP or any other system) is access rights. If you don't have full access rights (or only limited access), you can only do what the client allows you to do. Depending of the l10n savviness of the client, that can be a lot or very little.

So let's say that you work directly for the client and you have full access rights to the site.

First you don't want to work directly on the site. You want to install the site on your machine and have it run locally to be able to see all your changes and validate them before you reproduce them online.

Also, you want to be able to manage your changes and know exactly what was that change your made for. You want to have granular control of all the modifications and for that you'll need a version control system. That way you can revert any modification any time you want.

  1. Follow the instructions here to install a "blank" WP site locally and to be able to run it: https://codex.wordpress.org/Installing_WordPress_Locally_on_Your_Mac_With_MAMP
  2. (Install and) Use the "All-in-One WP Migration" plugin on the online site to export all the data (an FTP download only won't work, there are databases, etc. that you can't access from FTP)
  3. Use that same plugin on the local site to import the site
  4. Now you have a copy of your online site running locally. Changes that you'll do locally will be live right away so that you can test them. They will not affect the online site.
  5. To have granular control of your modifications, use git to create a version control repository for your site: enter the root of your site and run "git init", add all the files to the repository.
  6. You need a robust backup system. Something that does everything automatically so that you don't loose too much data (or any data at all) in case of problem. Time Machine is good but don't hesitate to use something more sophisticated.
  7. You can now start working on the site localization.

WP is a mess when it comes to translating a whole site. There are no integrated mechanisms to access all the localizable parts: you have to use plugins to create the l10n infrastructure, then plugins to export the theme strings, then whateverelse to actually access the textual contents of the site.

I'll write more later about the second phase of the l10n: the actual translation process.

From Finder to Terminal: cd anywhere

At a point in a Mac user life, the Terminal utility and all the command line applications that it brings to the game become a daily necessity. Typically, you work in a Finder window and want to work on the window items from the command line. For that, you can go up to the parent window, select the folder that you just exited and copy it, move to terminal, hit "cd", add a space, paste the folder and hit "Enter". That's a lot of fiddling around.


I just checked and found that I needed about 10 seconds to complete the task. 10 seconds, 4 times a day, 5 times a week, 40 times a year and you've lost 8,000 seconds = more than 2 hours. Eventually you'll notice that you're wasting time on this particular task and you end up either cursing yourself that you can't go faster, or start looking for a solution.
Here is my take on the problem.
On a side note, I spent way more than 2 hours to find that solution (and all the solutions that I write about here). Namely, I had to learn enough AppleScript, then I applied that knowledge to Finder and Terminal, then I found all the issues that made this a non trivial task, then I asked around (eternal thanks to the members of the AppleScript Users list hosted by Apple), then I tried a new implementation, then I was not satisfied with it, rewrote the whole thing and that's where I stand today. But the time spent on learning AppleScript and the workings of the various applications translates into knowledge (along with its own lot of frustration) that I can apply to other issues, while not spending that time only translates in frustration and a sense that you can't do much with computers...




use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

tell application "Finder"
activate
try # if what is selected in Finder is a folder, then use that folder
if class of item 1 of (selection as list) is folder then
set myFolder to item 1 of (selection as alias list)
else # if nothing is selected or if the selection is not a folder, use the parent folder
set myFolder to insertion location as alias
end if
on error # if nothing works, default to using the Desktop
set myFolder to desktop as alias
end try
set myPath to quoted form of (POSIX path of myFolder)
set myCommand to "cd " & myPath
end tell

# I use a separate "handler" to launch the command.
# That handler can be saved in a script library so as to be able to call it from other scripts

my launchMyCommand(myCommand)

# A handler is useful because what matters is what it outputs, not how it works.
# So, if at one point in the future I decide that this way of launching a command in Terminal is not efficient anymore, I can change the way the handler works but I won't have to change the scripts that call it.

on launchMyCommand(myCommand)
# First I use GUI scripting to create a new Terminal window
tell application "Terminal" to activate
tell application "System Events" to tell application process "Terminal"
set frontmost to true
delay 0.1
keystroke "n" using {command down}
end tell
# Then I ask the newly created window to run the command
tell application "Terminal"'s front window
delay 0.1
do script myCommand in its last tab
activate
end tell
# And I eventually merge that window to the other so as to keep everything tidy
tell application "System Events" to tell application process "Terminal"
set frontmost to true
delay 0.1
keystroke "m" using {control down, command down}
end tell
return

end launchMyCommand

I save this script as an AppleScript application and I call it ">cd". Now when I am working in Finder, I just call Spotlight with a system shortcut, I start hitting >c and Spotlight autocompletion proposes >cd.app, I hit Enter and I'm in Terminal with a window opened on the item I wanted.

Searching for empty translations in OmegaT

OmegaT searches are very powerful. One feature OmegaT does not have (yet?) is the ability to register common searches for later use.
There are at least 2 RFEs for that on the OmegaT development site, one registered in 2006 (by me) and one in 2014.

In the meanwhile, there are at least 2 searches that you want to remember:


  1. searches for non translated segments
  2. searches for segments that have been set to <EMPTY> (they are translated but the translation being empty, the source won't appear in the target document).

Searches for non translated segments

The logic is straightforward:

You want to search for any one character in source that is untranslated.


In a search, "any one character" can be expressed by the regular expression "." (period). So you put a period in the search field and you make sure you have selected "Regular expressions" below it. That "any one character" is to be found in "source", so you uncheck anything else but "source" in the line below that. Then you specify "Untranslated", since that's exactly what your looking for, and you can eventually select "all matching segments" to make sure non unique segments are all displayed.


The search scope will be "Project" and only "Memory".





Searches for <EMPTY> segments

In OmegaT, when a segment is left untranslated, the source text appears in the target document. An easy way to get around this is to translate the segment with a space. The problem is that sometimes you really want to not use anything in target. For this, OmegaT allows you to "Set [an] empty translation" in the Edit menu. Once you do that, the segment will be empty but translated (it appears in the same color as the other translated segments when you select "Mark translated segments" in the View menu), and OmegaT will display it with the <EMPTY> string when you've left it.

To search for such segments, the logic is a bit different:


You want to search for an empty string in target that is translated.


In a search, "an empty string" can be expressed by the regular expression "^$" (caret, followed by dollar). The ^ stands for the beginning of the line and the $ for its end, with nothing in between the string is empty. So you put a caret followed by a dollar sign in the search field and you make sure you have selected "Regular expressions" below it. That "empty string" is to be found in "target", so you uncheck anything else but "target" in the line below that. Then you specify "Translated", since that's exactly what your looking for, and you can eventually select "all matching segments" to make sure non unique segments are all displayed.


The search scope will be "Project" and only "Memory".




Selecting stuff with AppleScript

There was a tweet about Numbers the other day:

#applescript can be such a pain sometimes to use. Simple things, like getting the selection of the current cell in #numbers is not easy info to come by.

I totally understand the author's feeling and my first take was based on a misunderstanding: in the tweet where my reply is (that you can see below his) I took his request as looking for the value of the selected cell, but now that I think of it, he meant "the selected thing within the current cell". So let's take a minute to see how hard it is to find that information in Number's dictionary (hint: it's not there, as far as I can tell):

We have 2 items to base our investigation on: "selection" and "cell".

Looking for "selection" does not bring anything interesting, we only get "selection range" which is a property of table and returns the "cells currently selected in the table." So let's check "cell". We end up with 1 class and 2 elements (one of table and one of range). So let's check the class:

celln [inh. range] : A cell in a table

We get a number of properties for cell, including value that return various objects (number, date, text, boolean, or missing value) but further investigation does not help us finding something that returns the textual selection.

In fact, checking the dictionaries of the main applications delivered by Apple, only 2 seem to have a way to get a textual selection: Script Editor and Xcode. Page, Keynotes, Numbers, Notes, Preview, TextEdit, Contacts, Calendar, Reminders, Safari, etc. don't have anything. Finder, Mail, Terminal have a "selection" mechanism but only to select objects specific to their models and not to select a textual range.

Now, that doesn't mean it is not possible to get the textual selection in the above applications, just that a reasonable investigation into the various dictionaries did not provide us with that information.

So, now that we're stuck, the only path I'd think of to access that textual selection is to use GUI scripting to copy the selection, put it in the clipboard and access it from there.

tell application "Numbers"
activate
delay 0.1
tell application "System Events"
keystroke "c" using {command down}
end tell
end tell

set mySelection to the clipboard

Ok, it's ugly. But we got it.

Last but not least, I'd like to go back to that "selection" thing.

In Script Editor it's very easy to use:

tell application "Script Editor"
tell document "Untitled 3"
selection's properties
end tell
end tell

--> {class:selection-object, character range:{35, 60}, contents:"tell document \"Untitled 3\""}

We get textual information but only the actual text contents, nothing like what the clipboard tells us (with clipboard info for ex).

In Xcode we seem to be able to get the "selected character range" only of source files. RTF files return errors.

tell application "Xcode"
selected character range of document 4
end tell
--> {1, 5}

and it's easy to get mixed up since for each independent document opened, Xcode will create a "workspace document" that includes it. So if we just create a single source file outside of any project, we still need to refer to it as document 2 because document 1 will be the enclosing workspace...


Some applications I use frequently have good selection support: BBEdit works a bit like Script Editor where a simple "selection" in the application tell block will return the user visible selection. Word needs "selection's content" to return the textual value of the selection but selection itself offers plenty of properties including style, etc.

Finding one's way in an application's AppleScript dictionary...

The other day I wanted to find a way to accelerate the process of zooming a Word window so that it fits the width of the document. It's something I've done manually thousands of times over the years. Just go to Display, then Zoom, select the option and hit Enter.

I know that Word is scriptable but everything looked so complex that I never really tried. When I script in uncharted territory I usually go the top-bottom way: I get a document, check it's properties and go deeper and deeper until I find what I need. The issue with this approach is that there are so many different classes and sub-classes and properties all over that you easily get lost and frustrated.

I just tried the bottom-up approach with this Word feature and it worked pretty well: start from what you thing is the end property and go up the ladder to the highest level object.

In the case at hand, I suppose the element I'm looking for has "fit" in its description. That's my only assumption.

So, here we go. Open Script Editor, open the Microsoft Word dictionary and search for "fit".

In the "Microsoft Word Suite" we find 2 properties and 1 command:

fit text width is a property of "selection object"

fit text width (real) : Returns or sets the width in the current measurement units in which Microsoft Word fits the text in the current selection.

page fit is a property of "zoom"

page fit (page fit none/page fit full page/page fit best fit) : Returns or sets the view magnification of a window so that either the entire page is visible or the entire width of the page is visible.

fit to pagesv : Decreases the font size of text just enough so that the document will fit on one fewer pages. An error occurs if Word is unable to reduce the page count by one.

A cursory reading tells us that we'd like to try "page fit" first. We know it is a property of "zoom", which is defined as:

zoomn [inh. base object] : Contains magnification options, for example, the zoom percentage for a window or pane.

Now we're getting closer to bridging our "bottom" (fit) to some "up" (which would be a document, or a window). Since we do have a reference to a window zoom percentage in the definition let's check "window" and see what we come up with.

The first thing we get is "active window", and the first item is:

active window (window, r/o) : Returns the currently active window object.

which is a property of application. active window returns a window object, so let's check what window objects are made of.

A glance at the list of properties of a window does not give us any hints at how to link a window to a zoom... There are no zoom properties so we have to go back to zoom and find hints there.

There is a second item for zoom, it is a property of a view:

zoom (zoom, r/o) : Returns the zoom object associated with this view object.

We're one step higher now, and since the definition of view is:

viewn [inh. base object] : Contains the view attributes, show all, field shading, table gridlines, and so on, for a window or pane.

we have a connection to the window that we found earlier.

If we check window again, we see that it indeeds has a view property:

view (view, r/o) : Returns a view object that represents the view for the window.

So let's put this together:

active window is a window and has a view property that has a zoom property that has a page fit property which can hold one of the following 3 values: page fit none/page fit full page/page fit best fit

To make sure that we're not missing anything let's check the code item by item, I assume that you have a Word document open, otherwise the result will be "missing value".

tell application "Microsoft Word"
get active window
end tell
--> active window

so far, so good.

tell application "Microsoft Word"
get view of active window
end tell
--> view of active window

no issue with that

tell application "Microsoft Word"
get zoom of view of active window
end tell
--> zoom of view of active window

we're progressing

tell application "Microsoft Word"
get page fit of zoom of view of active window
end tell
--> page fit full page

Boom ! We made it. Our document is in full page view.

So, let's set it to "page fit best fit":

tell application "Microsoft Word"
set page fit of zoom of view of active window to page fit best fit
end tell

Et voilĂ ! The window is now in page width fit.

I'm not sure which is the fastest: checking the web for a "zoom my document to page width in AppleScript" answer or finding your way into the dictionary to find the answer yourself, but I know which will help you be proficient with AppleScript faster...