Hello
I have tried the OpenThesaurus Deutsch.dictionary found at http://tekl.de/deutsch/Lexikon-Plugins.html and confirmed that the Automator action "Get Definition of Word" returns not the "best" match in the dictionary.
As far as I can tell, the Automator action uses an undocumented function DCSCopyRecordsForSearchString() of DictionaryServices framework and retrieve the first match result, which usually should be the primary match but not necessarily so when the OpenThesaurus Deutsch.dictionary returns results in strange order.
E.g., Given query = Beispiel, the headword retrieved from the DSCRecords returned by DCSCopyRecordsForSearchString() function is as follows.
leuchtendes Beispiel ← Beispiel
wie zum Beispiel ← Beispiel
nurals Beispiel ← Beispiel
schlagendes Beispiel ← Beispiel
Beispiel ⇒ abschreckendes Beispiel
Horror-Beispiel ← Beispiel
ein Beispiel nehmen an ← Beispiel
ohne Beispiel ← Beispiel
zum Beispiel ← Beispiel
Beispiel
mit gutem Beispiel vorangehen ← Beispiel
sehr gutes Beispiel ← Beispiel
And the Automator action returns the definition for the first record, whose headword = "leuchtendes Beispiel ← Beispiel", whereas the desired definition for the query would be for the headword = "Beispiel".
So the strange result we're seing is result of strange index order of OpenThesaurus Deutsch.dictionary and limitation of the "Get Definition of Word" action.
A remedy is to avoid the limited Automator action and use the DCSCopyRecordsForSearchString() function directly and select the record(s) whose headword matches the query word. The following RubyCocoa script demonstrates the method. However, RubyCocoa is no longer a part of standard installation of OSX 10.10 and you'd need to install it by yourself if you're running OSX 10.10.
cf.
http://rubycocoa.sourceforge.net/
* excerpt from RubyCocoa-1.2.0/doc/getting-started.md
## Build from Source
Extract RubyCocoa source from the '.tar.gz' file into a directory somewhere.
$ tar xzf RubyCocoa-x.x.x.tar.gz
$ cd RubyCocoa-x.x.x
$ ruby install.rb config
$ ruby install.rb setup
$ ruby install.rb test
$ sudo ruby install.rb intstall
Anyway, here's an Automator service workflow which should work under OSX 10.6 through 10.9.
0) Service receives selected [text] in [any application]
1) Run Shell Script action
- Shell = /bin/bash
- Pass input = as arguments
- Code = as follows
#!/bin/bash
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -w <<'EOF' - "$@"
#
# v0.10
# written by Hiroto, 2014-10
#
require 'osx/cocoa'
include OSX
# OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/DictionaryServices.framework' # [1]
while File.exist?(BSFILE = File.expand_path("/tmp/DictionaryServices.#{rand(1e10)}.bridgesupport")) do end
Signal.trap("EXIT") { File.delete BSFILE if File.exist?(BSFILE) }
File.open(BSFILE, "w") { |f| f.print DATA.read }
OSX.load_bridge_support_file BSFILE # [2]
File.delete BSFILE if File.exist?(BSFILE)
# -----------------------------------------------------
# * some DictionaryServices functions (OS X 10.6.8)
#
# (undocumented)
#
# extern CFArrayRef DCSGetActiveDictionaries (void)
# extern CFSetRef DCSCopyAvailableDictionaries (void)
# extern DCSDictionaryRef DCSGetDefaultDictionary (void)
# extern DCSDictionaryRef DCSGetDefaultThesaurus (void)
# extern DCSDictionaryRef DCSDictionaryCreate (CFURLRef)
# extern CFURLRef DCSDictionaryGetURL (DCSDictionaryRef)
# extern CFStringRef DCSDictionaryGetName (DCSDictionaryRef)
# extern CFStringRef DCSDictionaryGetIdentifier (DCSDictionaryRef)
#
# extern CFArrayRef DCSCopyRecordsForSearchString (DCSDictionaryRef, CFStringRef, unsigned long long, long long)
# unsigned long long method
# 0 = exact match
# 1 = forward match (prefix match)
# 2 = partial query match (matching (leading) part of query; including ignoring diacritics, four tones in Chinese, etc)
# >=3 = ? (exact match?)
#
# long long max_record_count
#
# extern CFStringRef DCSRecordGetString (DCSRecordRef)
# extern CFStringRef DCSRecordGetHeadword (DCSRecordRef)
# extern CFStringRef DCSRecordGetRawHeadword (DCSRecordRef)
# extern CFStringRef DCSRecordGetTitle (DCSRecordRef)
# extern CFStringRef DCSRecordGetAnchor (DCSRecordRef)
# extern CFURLRef DCSRecordGetDataURL (DCSRecordRef)
#
# extern CFStringRef DCSRecordCopyData (DCSRecordRef, long)
# long output_style
# 0 = XML XHTML <html> string
# 1 = XML XHTML <html> string
# 2 = XML XHTML <html> string
# 3 = plain text
# 4 = XML XHTML <text> string (single element)
# * corresponding to (?)
# Transform.xsl
# TransformApp.xsl
# TransformPanel.xsl
# TransformSimpleText.xsl
# TransformText.xsl
#
# (documented)
#
# CFStringRef DCSCopyTextDefinition (DCSDictionaryRef, CFStringRef, CFRange)
# CFRange DCSGetTermRangeInString (DCSDictionaryRef, CFStringRef, CFIndex)
#
# -----------------------------------------------------
def dict(argv)
#
# array argv : array of queries
#
dictf = '/Library/Dictionaries/OpenThesaurus Deutsch.dictionary'
url = NSURL.fileURLWithPath(dictf)
dct, = DCSGetActiveDictionaries().select { |d| DCSDictionaryGetURL(d).path == url.path }
argv.map {|a| a.to_ns }.each do |q| # [3]
rr = DCSCopyRecordsForSearchString(dct, q, 0, 200)
unless rr
puts "Not found: %s" % q
next
end
# q1 = nomalised query (NFKC, lowercase, w/o diacritical marks)
CFStringTransform(q1 = q.mutableCopy, nil, 'NFKD; [:M:] Remove; NFC; lower()', false)
rr.select { |r|
# compare normalised headword (h1) and normalised query (q1)
CFStringTransform(h1 = DCSRecordGetHeadword(r).mutableCopy, nil, 'NFKD; [:M:] Remove; NFC; lower()', false)
h1 == q1
}.each do |r| # r = DCSRecordRef
data = DCSRecordCopyData(r, 3)
puts data
puts
end
end
end
dict ARGV
#
# [1] DictionaryServices.framework/Resources/BridgeSupport/DictionaryServices.bridgesupport has problem to be fixed.
# I.e., in signatures of DCSCopyTextDefinition(), DCSGetTermRangeInString() function etc,
# {??=qq} should have been {_CFRange=qq}
# {??=ii} should have been {_CFRange=ii}
# [2] Fixed and extended bridgesupport file is loaded by OSX.load_bridge_support_file.
# It now includes signatures for several undocumented functions as well.
# [3] argv.to_ns is required to handle unicode characters correctly (in ruby 1.8).
#
__END__
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE signatures SYSTEM "file://localhost/System/Library/DTDs/BridgeSupport.dtd">
<signatures version="0.9">
<function name="DCSCopyTextDefinition">
<arg type="^{__DCSDictionary=}"></arg>
<arg type="^{__CFString=}"></arg>
<arg type64="{_CFRange=qq}" type="{_CFRange=ii}"></arg>
<retval type="^{__CFString=}"></retval>
</function>
<function name="DCSGetTermRangeInString">
<arg type="^{__DCSDictionary=}"></arg>
<arg type="^{__CFString=}"></arg>
<arg type64="q" type="l"></arg>
<retval type64="{_CFRange=qq}" type="{_CFRange=ii}"></retval>
</function>
<function name="DCSDictionaryCreate">
<arg type="^{__CFURL=}"></arg>
<retval type="^{__DCSDictionary=}"></retval>
</function>
<function name="DCSGetActiveDictionaries">
<retval type="^{__CFArray=}"></retval>
</function>
<function name="DCSCopyAvailableDictionaries">
<retval type="^{__CFSet=}"></retval>
</function>
<function name="DCSGetDefaultDictionary">
<retval type="^{__DCSDictionary=}"></retval>
</function>
<function name="DCSGetDefaultThesaurus">
<retval type="^{__DCSDictionary=}"></retval>
</function>
<function name="DCSDictionaryGetURL">
<arg type="^{__DCSDictionary=}"></arg>
<retval type="^{__CFURL=}"></retval>
</function>
<function name="DCSDictionaryGetName">
<arg type="^{__DCSDictionary=}"></arg>
<retval type="^{__CFString=}"></retval>
</function>
<function name="DCSDictionaryGetIdentifier">
<arg type="^{__DCSDictionary=}"></arg>
<retval type="^{__CFString=}"></retval>
</function>
<function name="DCSCopyRecordsForSearchString">
<arg type="^{__DCSDictionary=}"></arg>
<arg type="^{__CFString=}"></arg>
<arg type="l"></arg>
<arg type="l"></arg>
<retval type="^{__CFArray=}"></retval>
</function>
<function name="DCSRecordGetHeadword">
<arg type="^{__DCSRecord=}"></arg>
<retval type="^{__CFString=}"></retval>
</function>
<function name="DCSRecordGetString">
<arg type="^{__DCSRecord=}"></arg>
<retval type="^{__CFString=}"></retval>
</function>
<function name="DCSRecordGetRawHeadword">
<arg type="^{__DCSRecord=}"></arg>
<retval type="^{__CFString=}"></retval>
</function>
<function name="DCSRecordGetTitle">
<arg type="^{__DCSRecord=}"></arg>
<retval type="^{__CFString=}"></retval>
</function>
<function name="DCSRecordGetAnchor">
<arg type="^{__DCSRecord=}"></arg>
<retval type="^{__CFString=}"></retval>
</function>
<function name="DCSRecordGetDataURL">
<arg type="^{__DCSRecord=}"></arg>
<retval type="^{__CFURL=}"></retval>
</function>
<function name="DCSRecordCopyData">
<arg type="^{__DCSRecord=}"></arg>
<arg type="l"></arg>
<retval type="^{__CFString=}"></retval>
</function>
</signatures>
EOF
2) Run AppleScript action
- Code = as follows
on run argv
set s to _join(argv's item 1, linefeed)
activate
display alert s
end run
on _join(tt, d)
(*
list tt : source list
string d : separator
return string : tt joined with d
*)
local astid0, t
try
set {astid0, AppleScript's text item delimiters} to {AppleScript's text item delimiters, {} & d}
set t to "" & tt
set AppleScript's text item delimiters to astid0
on error errs number errn
set AppleScript's text item delimiters to astid0
error errs number errn
end try
return t
end _join
Automator Service workflow will look something like this.
Codes are tested under 10.6.8.
OpenThesaurus Deutsch.dictionary is assumed to have been installed as /Library/Dictionaries/OpenThesaurus Deutsch.dictionary
Regards,
H