Archive for 12月, 2014

2014/12/30 NSIndexSetに値を入れる、取り出す

ユニークな整数値のみをソートされた状態で保持するNSIndexSetを作成し、値を取り出すAppleScriptです。

NSIndexSetは値の重複を許さず、整数値のみ保持し、値をソートされた状態で保持するクラスで・・・AppleScriptから作成、値の追加、値の取り出しを行います。

正確にいえば、ここで扱っているのはNSIndexSetではなくNSMutableIndexSetです。

v2の方は、ASObjCExtras.frameworkの機能を利用して、一括でNSIndexSetから値を取り出しています。

AppleのReferenceを読むと、NSMutableIndexSetは10.9までしかinit()できないと書いてありますが・・・できているような、、、

AppleScript名:indexSetに値を入れる、取り出す
– Created 2014-12-30 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set aindexSet to current application’s NSMutableIndexSet’s alloc()’s init()

repeat 100 times
  set aRandom to random number from 1 to 100
  
aindexSet’s addIndex:aRandom
end repeat

set aList to {}
set aInd to aindexSet’s firstIndex()

repeat
  if aInd = current application’s NSNotFound then exit repeat
  
set the end of aList to aInd as integer
  
set bInd to aindexSet’s indexGreaterThanIndex:aInd
  
copy bInd to aInd
end repeat

aList
–> {2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 28, 29, 30, 31, 32, 33, 34, 37, 38, 39, 40, 43, 52, 53, 56, 57, 59, 61, 63, 67, 70, 74, 76, 78, 79, 80, 81, 83, 84, 85, 87, 88, 89, 90, 91, 92, 94, 96, 97}

★Click Here to Open This Script 

AppleScript名:indexSetに値を入れる、取り出す v2
– Created 2014-12-30 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set aindexSet to current application’s NSMutableIndexSet’s alloc()’s init()

repeat 100 times
  set aRandom to random number from 1 to 100
  
aindexSet’s addIndex:aRandom
end repeat

set aList to (current application’s SMSFord’s arrayWithIndexSet:aindexSet) as list

aList
–> {2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 28, 29, 30, 31, 32, 33, 34, 37, 38, 39, 40, 43, 52, 53, 56, 57, 59, 61, 63, 67, 70, 74, 76, 78, 79, 80, 81, 83, 84, 85, 87, 88, 89, 90, 91, 92, 94, 96, 97}

★Click Here to Open This Script 

AppleScript名:Listに値を入れる、取り出す(通常ASで書いた場合)
– Created 2014-12-31 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions

set aIndexSet to {}

repeat 100 times
  set aRandom to random number from 1 to 100
  
  
–ユニークな値のみをストアし、つねに昇順ソートされた状態を維持する
  
if aRandom is not in aIndexSet then –ユニーク判定
    set the end of aIndexSet to aRandom
    
set aIndexSet to shellSortAscending(aIndexSet) of me –昇順ソート
  end if
  
end repeat

aIndexSet
–> {1, 3, 4, 5, 6, 7, 8, 9, 11, 12, 14, 16, 17, 18, 19, 20, 21, 23, 26, 27, 28, 29, 30, 31, 34, 36, 37, 39, 40, 41, 43, 47, 49, 53, 55, 57, 58, 60, 63, 65, 66, 68, 71, 72, 73, 74, 76, 77, 80, 81, 82, 83, 84, 86, 87, 88, 91, 92, 95}

–1D Listの昇順ソート
on shellSortAscending(aSortList)
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j gap) and (oBj’s list’s item (j - gap + 1) > temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j - gap + 1)
          
set j to j - gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSortAscending

★Click Here to Open This Script 

2014/12/29 文字エンコーディングを自動判別してファイル読み込み v1.1

Cocoaの機能を用いて、テキストファイルの文字エンコーディングを自動判別してテキストファイルを読み込むScriptの第3弾です。

最初に作ったv1と、Shane Stanleyから提案のあったYosemiteの新機能によるv2で、10KBぐらいの長めの日本語テキストをあらためて用意してテストしてみたところ、v2がUTF16BEとUTF16LEにまったく反応しないことが判明。

table2.png

ちょうどv1で仕方なく行っていたhexdumpの処理をShaneから提案のあった処理に置き換えてv1.1としたところ・・・v1系の方が反応できる文字エンコーディングが多いということに。

とりあえずこんな感じでしょうか?

AppleScript名:文字エンコーディングを自動判別してファイル読み込み v1.1
– Created 2014-12-28 by Takaaki Naganoya
– Changed 2014-12-29 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set aPath to POSIX path of (choose file)
set aRes to readTextFile(aPath) of me

–Read Japanese text with detecting its text encoding
on readTextFile(aPath as string)
  
  
set aEnc to detectJapaneseEncodingAt(aPath) of me
  
if aEnc = false then return false
  
  
set aEncNum to aEnc as number
  
  
set encList to {“”, “”, current application’s NSJapaneseEUCStringEncoding, current application’s NSUTF8StringEncoding, “”, “”, “”, current application’s NSShiftJISStringEncoding, “”, current application’s NSUnicodeStringEncoding, current application’s NSWindowsCP1251StringEncoding, current application’s NSWindowsCP1252StringEncoding, current application’s NSWindowsCP1253StringEncoding, current application’s NSWindowsCP1254StringEncoding, current application’s NSWindowsCP1250StringEncoding, “”, “”, “”, “”, “”, current application’s NSISO2022JPStringEncoding}
  
  
if aEncNum > 21 then return false
  
  
set curEnc to contents of item aEncNum of encList
  
  
set {resValue, theError} to current application’s NSString’s stringWithContentsOfFile:aPath encoding:curEnc |error|:(reference)
  
return {aEncNum, resValue’s ASify() as string}
  
end readTextFile

–Auto detect text encoding
on detectJapaneseEncodingAt(aPOSIXpath as string)
  
  
–ISO2022JP check
  
set aNSData to current application’s NSData’s dataWithContentsOfFile:aPOSIXpath
  
set aDataLength to aNSData’s |length|()
  
if aDataLength > 1024 then set aDataLength to 1024
  
  
–0×1B check
  
set anNSString to current application’s NSString’s stringWithString:(character id 27) – 0×1B
  
set theData to anNSString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set theRange to aNSData’s rangeOfData:theData options:0 range:(current application’s NSMakeRange(0, aDataLength))
  
  
–found 0×1B in aNSData
  
if |length| of theRange = 1 and location of theRange < aDataLength then
    
    
set aStr to (current application’s NSString’s alloc()’s initWithData:aNSData encoding:21)
    
    
if aStr is not equal to missing value then
      return 21 – ISO2022JP
    end if
  end if
  
  
–Other Text Encodings
  
set encList to {“3″, “4″, “8″, “2415919360″, “2483028224″, “10″, “11″, “12″, “13″, “14″, “15″}
  
repeat with i in encList
    –UTF-16LE & BE parameters out of AppleScript number’s range. So, I held it in string data (But could not detect them….why?)
    
set j to contents of i
    
set aStr to (current application’s NSString’s stringWithString:j)
    
set aNum to aStr’s intValue()
    
    
set aStr to (current application’s NSString’s alloc()’s initWithData:aNSData encoding:aNum)
    
if aStr is not equal to missing value then
      return j as number
    end if
    
  end repeat
  
  
return false
  
end detectJapaneseEncodingAt

★Click Here to Open This Script 

2014/12/28 文字エンコーディングを自動判別してテキストファイル読み込み v2

Cocoaの機能を用いて、テキストファイルの文字エンコーディングを自動判別してテキストファイルを読み込むScriptの第2弾です。

Shane Stanleyから(many thanks!)「Yosemiteで追加された機能で、文字エンコーディングの自動検出ができるよ?」とScriptごとツッコミがあり、実際に試してみました。

・・・・・まあ、短い(汗)

EUC、SJIS、ISO2022-JP、UTF-8あたりはぜんぜん大丈夫です。UTF-16LE no BOMあたりがあやしいですが、Made In Japanの定番テキストエディタ「mi」v3がUTF-16LE no BOMは正しく読めなかった(逆に、海外産のTextWranglerで正しく読めたり)ぐらいなので、別によいのかもしれません。あと、UTF-16BEも怪しいです。

table1.png

AppleScriptのファイル書き出しでas unicode textを指定すると暗黙のうちにUTF-16BEが指定されるので、ASで書き出したテキスト「以外」なら自動判別させても大丈夫な感じがします。このあたり、どちらのルーチンを使うかについてはいろいろテストを行ってみるべきなんでしょう。

AppleScript名:Auto Text Encodings Detection
– Created 2014-12-28 by Shane Stanley
use AppleScript version “2.4″ – requires Yosemite
use scripting additions
use framework “Foundation”

set aPOSIXpath to POSIX path of (choose file)
set aRes to readTextDetectingTextEncodings(aPOSIXpath)
if aRes = false then return false

copy aRes to {theEncoding, theString, wasLossy}

–文字エンコーディングを自動判別してテキストファイル読み込み
on readTextDetectingTextEncodings(aPOSIXpath)
  
  
set anNSData to current application’s NSData’s dataWithContentsOfFile:aPOSIXpath
  
  
set theOptions to current application’s NSDictionary’s dictionaryWithObjects:{false, “ja”, {3, 4, 8, 10, 11, 12, 13, 14, 15, 21}} forKeys:{(current application’s NSStringEncodingDetectionAllowLossyKey), (current application’s NSStringEncodingDetectionLikelyLanguageKey), (current application’s NSStringEncodingDetectionSuggestedEncodingsKey)}
  
  
set {theEncoding, theString, wasLossy} to current application’s NSString’s stringEncodingForData:anNSData encodingOptions:theOptions convertedString:(reference) usedLossyConversion:(reference)
  
  
if theEncoding is 0 then return false
  
  
return {theEncoding, theString as string, wasLossy}
  
end readTextDetectingTextEncodings

★Click Here to Open This Script 

2014/12/28 文字エンコーディングを自動判別してテキストファイル読み込み v1

指定のテキストファイルの文字エンコーディングを自動判別してテキストを読み込むAppleScriptです。

シフトJIS、UTF-8、EUC、ISO2022JP、UTF16、UTF16LE、UTF16(BOMなし)などで動作を確認しています(が、まだ実験段階です。それほどイジめていないので)。

UTF16LE、UTF16(BOMなし)については、エンコーディングの自動判定ルーチンで「UTF-16」として判定され、正直「使えないかな?」という気もしていたのですが、UTF-16として読み込ませてみたら、とくに問題なく(文字化けもなく)読み込めてしまったので、結果オーライです(?)。

本Scriptは対象のテキストファイルを複数回走査しています。SSDのマシンではまったく気になりませんが、HDD経由だとやや遅いと感じるかもしれません。

 1回目:とりあえず読み込んでデータサイズを取得
 2回目:hexdumpコマンドで先頭から1024バイトをダンプ
 3回目:ISO2022JPで読み込みテスト
 4回目:他のエンコーディングで読み込みテスト×複数回

ライブラリに放り込んで使えば、割と便利に使えることでしょう。最終的には、テキストファイルのオープンを行うためだけにテキストエディタを併用しなくても済むようにしたいところです(そんなに作り込む気はないんですけれど)。

AppleScript名:文字エンコーディングを自動判別してファイル読み込み
– Created 2014-12-28 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set aPath to POSIX path of (choose file)
set aRes to readTextFile(aPath) of me

–指定パスのテキストファイルを、文字エンコーディングを判定しつつ読み込む
on readTextFile(aPath as string)
  
  
set aEnc to detectJapaneseEncodingAt(aPath) of me
  
if aEnc = false then return false
  
  
set aEncNum to aEnc as number
  
  
set encList to {“”, “”, current application’s NSJapaneseEUCStringEncoding, current application’s NSUTF8StringEncoding, “”, “”, “”, current application’s NSShiftJISStringEncoding, “”, current application’s NSUnicodeStringEncoding, current application’s NSWindowsCP1251StringEncoding, current application’s NSWindowsCP1252StringEncoding, current application’s NSWindowsCP1253StringEncoding, current application’s NSWindowsCP1254StringEncoding, current application’s NSWindowsCP1250StringEncoding, “”, “”, “”, “”, “”, current application’s NSISO2022JPStringEncoding}
  
  
if aEncNum > 21 then return false
  
  
set curEnc to contents of item aEncNum of encList
  
  
set {resValue, theError} to current application’s NSString’s stringWithContentsOfFile:aPath encoding:curEnc |error|:(reference)
  
return resValue as string
  
end readTextFile

–指定パスのテキストファイルの文字エンコーディングを自動判定する
on detectJapaneseEncodingAt(aPOSIXpath as string)
  
  
–ISO2022JPのチェック
  
set aNSData to current application’s NSData’s dataWithContentsOfFile:aPOSIXpath
  
set aDataLength to aNSData’s |length|()
  
if aDataLength > 0 then
    
    
if aDataLength > 1024 then
      set aDataLength to 1024
    end if
    
    
set aRes to do shell script “/usr/bin/hexdump -n “ & (aDataLength as string) & ” “ & quoted form of aPOSIXpath
    
set bList to (current application’s SMSFord’s arrayFromCSV:aRes commaIs:” “) as list
    
set cList to (current application’s SMSFord’s arrayByFlattening:bList) as list
    
set dList to (current application’s SMSFord’s indexesOfItems:{“1b”} inArray:cList inverting:false)
    
set dLen to dList’s |count|()
    
    
if dLen > 1 then –1以上でもよかったが、なんとなく
      –NSISO2022JPStringEncodingチェック
      
set aStr to (current application’s NSString’s alloc()’s initWithData:aNSData encoding:21)
      
if aStr is not equal to missing value then
        return 21 – ISO2022JP
      end if
    end if
  else
    return false
  end if
  
  
–他のテキストエンコーディングのチェック
  
set encList to {“3″, “4″, “8″, “10″, “11″, “12″, “13″, “14″, “15″, “2415919360″, “2483028224″}
  
repeat with i in encList
    –数字でパラメータを持っておくと、AppleScriptの数値の上限を超えて指数表示になるため、あえて文字列で保持
    
set j to contents of i
    
set aStr to (current application’s NSString’s stringWithString:j)
    
set aNum to aStr’s intValue()
    
    
set aStr to (current application’s NSString’s alloc()’s initWithData:aNSData encoding:aNum)
    
if aStr is not equal to missing value then
      return j
    end if
    
  end repeat
  
  
return false
  
end detectJapaneseEncodingAt

★Click Here to Open This Script 

2014/12/28 テキストから文字エンコーディングを推測する v3

(日本語の)テキストファイルから、文字エンコーディングを推測するAppleScriptの改良版です。

v1では判定できなかったEUCとISO2022JPの区別を行ってみました。とりあえず理解するために、ISO2022JPのファイルを作成してバイナリエディタ「0xED」でバイナリダンプ。

bindump.png

データの傾向を把握して、さまざまなサイトのObjective-Cのサンプルコードを検討。0×1bのコードの存在確認を行う必要があるんだな、ということはわかりました。

本当はv1とv3の間に失敗作のv2があり、NSData経由でテキストファイルをバイナリとしてアクセスしてみたのですが・・・やり方がよくなかったのか、実行するとエディタ(ASObjCExplorer 4)がコケまくります。さすがに、Cocoaオブジェクトのログが出力できるASObjCExplorer 4でも、誤った記述を行うとエディタごと落ちます。

というわけで、格好つけずにhexdumpして文字として存在確認という「いつもの手口」で処理してみたのが、このv3です。

結果は良好で、ISO2022JP、EUC、UTF8、SJIS、UTF16については検出できるようになりました。

UTF16-BEとUTF-16LEについては、なぜか見てはいけない気がして試していません。

AppleScript名:テキストから文字エンコーディングを推測する v3
– Created 2014-12-28 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

(*

property NSJapaneseEUCStringEncoding : 3 –EUC
property NSUTF8StringEncoding : 4 –UTF8
property NSShiftJISStringEncoding : 8 –SJIS
property NSUnicodeStringEncoding : 10 –UTF16

property NSWindowsCP1251StringEncoding : 11
property NSWindowsCP1252StringEncoding : 12
property NSWindowsCP1253StringEncoding : 13
property NSWindowsCP1254StringEncoding : 14
property NSWindowsCP1250StringEncoding : 15
property NSISO2022JPStringEncoding : 21
property NSUTF16BigEndianStringEncoding : 2415919360 —UTF16BE
property NSUTF16LittleEndianStringEncoding : 2483028224 —UTF16LE

*)

set a to POSIX path of (choose file)
set aRes to detectJapaneseEncodingAt(a) of me
return aRes

on detectJapaneseEncodingAt(aPOSIXpath as string)
  
  
set encList to {“3″, “4″, “8″, “10″, “11″, “12″, “13″, “14″, “15″, “2415919360″, “2483028224″}
  
set hitList to {}
  
  
set aNSData to current application’s NSData’s dataWithContentsOfFile:aPOSIXpath
  
  
  
–ISO2022JPのチェック
  
set aDataLength to aNSData’s |length|()
  
if aDataLength > 0 then
    
    
if aDataLength < 64 then
      set aMax to aDataLength
    else
      set aMax to 64
    end if
    
    
–先頭から64バイトだけチェックすればいいだろー(NSData経由でバイナリサーチを試してダメだったのでshell経由で)
    
set aRes to do shell script “/usr/bin/hexdump -n “ & (aMax as string) & ” “ & quoted form of aPOSIXpath
    
set bList to (current application’s SMSFord’s arrayFromCSV:aRes commaIs:” “) as list
    
set cList to (current application’s SMSFord’s arrayByFlattening:bList) as list
    
set dList to (current application’s SMSFord’s indexesOfItems:{“1b”} inArray:cList inverting:false) as list
    
set dLen to length of dList
    
    
if dLen > 2 then –1以上でもよかったが、なんとなく
      –NSISO2022JPStringEncodingチェック
      
set aStr to (current application’s NSString’s alloc()’s initWithData:aNSData encoding:21)
      
if aStr is not equal to missing value then
        return {21, aStr}
      end if
    end if
  else
    return false
  end if
  
  
  
–他のテキストエンコーディングのチェック
  
repeat with i in encList
    –数字でパラメータを持っておくと、AppleScriptの数値の上限を超えて指数表示になるため、あえて文字列で保持
    
set j to contents of i
    
set aStr to (current application’s NSString’s stringWithString:j)
    
set aNum to aStr’s intValue()
    
    
set aStr to (current application’s NSString’s alloc()’s initWithData:aNSData encoding:aNum)
    
if aStr is not equal to missing value then
      return {j, aStr}
    end if
    
  end repeat
  
  
return hitList
  
end detectJapaneseEncodingAt

★Click Here to Open This Script 

2014/12/28 テキストから文字エンコーディングを推測する v1

(日本語の)テキストファイルから、文字エンコーディングを推測するAppleScriptです。

テキストエディタをAppleScriptで利用するケースはほとんどなくなっており、対象のテキストファイルの文字エンコーディングが不明の場合にのみ使ったりする程度でしょうか。

Cocoaの機能を用いてAppleScript単体で文字エンコーディングが判定できれば、テキストエディタを併用する必要性はほとんどなくなります。

・・・で、調べてみたところ・・・片っ端から総当たりでエンコーディングを指定してエラーになるかどうかテストするというのが主流のようで、

texts.png

かんたんな内容の、1つの文字エンコーディングで統一されている(同一ファイル中に複数のエンコーディングが存在しているケースは除外)ファイルを用意して、テストしてみました。

UTF8、UTF16、SJIS、EUCはヒットしましたが、ISO2022JPについてはEUCと判定されるので、別途判定ロジックが必要なようです。あと、UTF16BEとかUTF16LEについてはまだ調査していません。

とりあえず、テストを行ってみたという段階です。

AppleScript名:テキストから文字エンコーディングを推測する v1
– Created 2014-12-28 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "ASObjCExtras"

(*

property NSJapaneseEUCStringEncoding : 3 –EUC
property NSUTF8StringEncoding : 4 –UTF8
property NSShiftJISStringEncoding : 8 –SJIS
property NSUnicodeStringEncoding : 10 –UTF16

property NSWindowsCP1251StringEncoding : 11
property NSWindowsCP1252StringEncoding : 12
property NSWindowsCP1253StringEncoding : 13
property NSWindowsCP1254StringEncoding : 14
property NSWindowsCP1250StringEncoding : 15
property NSISO2022JPStringEncoding : 21
property NSUTF16BigEndianStringEncoding : 2415919360 —UTF16BE
property NSUTF16LittleEndianStringEncoding : 2483028224 —UTF16LE

*)

set a to POSIX path of (choose file)
set aData to current application’s NSData’s dataWithContentsOfFile:a
set aRes to detectJapaneseEncoding(aData) of me
return aRes

on detectJapaneseEncoding(aNSData)
  
  
set encList to {"3", "4", "8", "10", "11", "12", "13", "14", "15", "21", "2415919360", "2483028224"}
  
set hitList to {}
  
  
repeat with i in encList
    –数字でパラメータを持っておくと、AppleScriptの数値の上限を超えて指数表示になるため、あえて文字列で保持
    
set j to contents of i
    
set aStr to (current application’s NSString’s stringWithString:j)
    
set aNum to aStr’s intValue()
    
    
set aStr to (current application’s NSString’s alloc()’s initWithData:aNSData encoding:aNum)
    
if aStr is not equal to missing value then
      return {j, aStr}
    end if
    
  end repeat
  
  
return hitList
  
end detectJapaneseEncoding

★Click Here to Open This Script 

2014/12/27 PDFをページごとに分解する

指定したPDFファイルをページごとに分解して保存するAppleScriptです。

非常によくあるScript(PDFpenやSkimなどを使って分割)ですが、他のアプリを必要としません。

MacBook Pro Retina 2012(Core i7 2.66GHz)で277ページのPDFをページごとに分解するのに12秒でした。

AppleScript名:PDFをページごとに分解する
– Created 2014-12-26 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”
use framework “QuartzCore”

property PDFDocument : class “PDFDocument”

set aHFSPath to (choose file of type {“com.adobe.pdf”} with prompt “ページごとに分解するPDFを指定してください”) as string
set aURL to (current application’s SMSFord’s URLFrom:aHFSPath)

set aPOSIXpath to POSIX path of aHFSPath —書き出し先パスをPOSIX pathで用意しておく(あとで加工)

set aPDFdoc to PDFDocument’s alloc()’s initWithURL:aURL
set pCount to aPDFdoc’s pageCount()

–PDFをページごとに分割してファイル書き出し
repeat with i from 0 to (pCount - 1)
  
  
set thisPage to (aPDFdoc’s pageAtIndex:(i))
  
set thisDoc to (PDFDocument’s alloc()’s initWithData:(thisPage’s dataRepresentation()))
  
set outPath to addString_beforeExtensionIn_(“_” & (i + 1) as string, aPOSIXpath)
  
  (
thisDoc’s writeToFile:outPath) –書き出し
  
end repeat

–ファイルパス(POSIX path)に対して、文字列(枝番)を追加。拡張子はそのまま
on addString:extraString beforeExtensionIn:aPath
  set pathString to current application’s NSString’s stringWithString:aPath
  
set theExtension to pathString’s pathExtension()
  
set thePathNoExt to pathString’s stringByDeletingPathExtension()
  
  
set newPath to (thePathNoExt’s stringByAppendingString:extraString)’s stringByAppendingPathExtension:theExtension
  
return newPath as string
end addString:beforeExtensionIn:

★Click Here to Open This Script 

2014/12/26 record内のListにデータが入っているかどうか確認

record内のlistにデータが入っているか存在確認を行うAppleScriptです。

Cocoaの機能を用いて、入り組んだデータ構造にアクセスする練習の一環です。

まずは、list内の存在確認を行い……record内のlistへのアクセスを実施。

さらに、recordのlistへの一括チェックが行えるといいかも。

AppleScript名:List内に指定データが入っているかどうか確認
– Created 2014-12-26 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "ASObjCExtras"

set aList to {1, 2, 3, 4, 5}
set aArray to current application’s SMSFord’s Cocoaify:aList

set aRes to (aArray’s containsObject:2) as boolean
–> true

set bRes to (aArray’s containsObject:0) as boolean
–> false

★Click Here to Open This Script 

AppleScript名:record内のListにデータが入っているかどうか確認
– Created 2014-12-26 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "ASObjCExtras"

set aRec to {abc:100, bcd:{1, 2, 3, 4, 5}}
set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec

set aVal to ((aDic’s valueForKeyPath:"bcd")’s containsObject:2) as boolean
–> true

set bVal to ((aDic’s valueForKeyPath:"bcd")’s containsObject:100) as boolean
–> false

★Click Here to Open This Script 

2014/12/26 aListのうち、bListに入っていない項目を返す

1つのリスト(aList)の項目のうち、もう1つのリスト(bList)に入っていない項目を返すAppleScriptです。

プレーンなAppleScriptと、Cocoaの機能を利用したAppleScriptObjC(ASOC)で記述してみました。

プレーンなAppleScriptでは、「そもそもそういう機能はAppleScriptの処理系にはない」ので「全部自分で書く」ことになります。

ASOCでは、Cocoaが用意している(はずの)機能から探して呼び出すことになります。

プレーンなAppleScriptの場合は、処理を行うデータが膨大になればなるほど処理時間が長くなります。高速化する手段はいろいろありますが、高速化するとプログラムがものすごく長くなって読みにくくなります。

ASOCでは、データが増えてもプレーンなAppleScriptほどには処理時間は長くなりません。さらに高速化する手段はありませんが、最初から速いので問題はあまりありません。

xor_lists.png
▲ASとASOCの処理速度結果比較(10回実行した平均値)。10〜100倍以上ASOCが高速。ベンチマーク用データ作成部分を含む

いままで、プレーンなAppleScriptとASOCは記述環境が明確に分かれていました(AppleScriptエディタ、Xcode)が、OS X 10.10からはScript Editor上で普通に書いて使えるようになったので、場合に応じて使い分けるのがよいでしょう。

AppleScript名:aListのうち、bListに入っていない項目を返す
– Created 2014-12-26 by Takaaki Naganoya
– 2014 Piyomaru Software
set aList to {1, 2, 3, 4, 5, 6, 7, 8, 9}
set bList to {2, 5, 7}

set cList to exclusiveListUp(aList, bList)
–> {1, 3, 4, 6, 8, 9}

–aListのうち、bListに入っていない項目を返す
on exclusiveListUp(aList as list, bList as list)
  set newList to {}
  
repeat with i in aList
    set j to contents of i
    
if j is not in bList then
      set the end of newList to j
    end if
  end repeat
  
return newList
end exclusiveListUp

★Click Here to Open This Script 

AppleScript名:aListのうち、bListに入っていない項目を返す(ASOC)
– Created 2014-12-26 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set aList to {1, 2, 3, 4, 5, 6, 7, 8, 9}
set bList to {2, 5, 7}

set cList to exclusiveListUp(aList, bList) of me
–> {1, 3, 4, 6, 8, 9}

–aListのうち、bListに入っていない項目を返す
on exclusiveListUp(aList as list, bList as list)
  set aArray to current application’s NSMutableArray’s arrayWithArray:aList
  
set bArray to current application’s SMSFord’s Cocoaify:bList
  
aArray’s removeObjectsInArray:bArray
  
return (aArray’s ASify() as list)
end exclusiveListUp

★Click Here to Open This Script 

2014/12/25 オーディオファイルのチャンネル数と再生時間を取得する

オーディオファイル(QuickTime Playerによるオーディオ収録ファイル、m4a)から情報を取得するAppleScriptです。

オーディオチャンネル数と、duration(再生時間:秒)を取得します。

AVAudioPlayerについては、playAtTime:がうまく動かなくて悩んでいます、、、

AppleScript名:オーディオファイルのチャンネル数と再生時間を取得する
– Created 2014-12-25 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”
use framework “AVFoundation”

set a to choose file of type {“com.apple.m4a-audio”}
set aURL to (current application’s SMSFord’s URLFrom:a)

set aAudioPlayer to current application’s AVAudioPlayer’s alloc()’s initWithContentsOfURL:aURL |error|:(missing value)
aAudioPlayer’s prepareToPlay()

set channelCount to aAudioPlayer’s numberOfChannels()
log {“channelCount”, channelCount}

set aDuration to aAudioPlayer’s duration()
–> 7355.9873015873 (Unit:Second)

★Click Here to Open This Script 

2014/12/25 アプリケーションごとのローカライズ言語数を求める

/Applicationsフォルダ以下の全アプリケーションのローカライズ言語数を求めて多い順にソートして返すAppleScriptです。

便利な部品ができてきたので、本来の用途以外に転用するのも簡単になってきました。

ただ、こういう「調査活動」以外にこの処理が生きるかどうかは未知数です、、、

OS X自体のローカライズ言語数=35ということが見て取れますが(ただし、対応言語は幾つかのレベルに分けられており、ユーザー数によって対応度が違うはず)、それ以上のローカライズ言語を持っているアプリケーションの存在が気になります。

AppleScript名:アプリケーションごとのローカライズ言語数を求める
– Created 2014-12-25 by Takaaki Naganoya

use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

–/Applications以下のアプリケーションのパスをすべて求める
set apPath1 to (path to applications folder) as string
set aList to getFileListWithSpotLight(“kMDItemContentType”, “com.apple.application-bundle”, apPath1) of me

–各アプリケーションのローカリゼーション情報を取得(重複情報はカウントしつつ保持)
set aaList to {}

repeat with i in aList
  set j to contents of i
  
  
–アプリケーションのローカリゼーションを取得する(重複言語の除去ずみ)
  
set locList to getSpecifiedAppFilesLocalizationList(j) of me
  
  
tell application “System Events”
    set aName to name of j
  end tell
  
  
set the end of aaList to {aName, length of locList}
  
end repeat

–Sort Result (Many->Little)
set sortIndexes to {1} –Key Item id: begin from 0
set sortOrders to {false} –Descending Sort Order
set sortTypes to {“compare:”}
set rList to (current application’s SMSFord’s subarraysIn:aaList sortedByIndexes:sortIndexes ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list

return rList
–> {{”VLC.app”, 93}, {”Creative Cloud.app”, 61}, {”Adobe Application Manager.app”, 61}, {”Google Drive.app”, 53}, {”Google Chrome.app”, 53}, {”Uninstall Product.app”, 43}, {”Uninstall Product.app”, 43}, {”Google Earth.app”, 42}, {”VueScan.app”, 40}, {”Evernote.app”, 40}, {”Adobe Muse CC 2014.app”, 37}, {”iTunes.app”, 35}, {”Digital Color Meter.app”, 35}, {”Image Capture.app”, 35}, {”Grab.app”, 35}, {”Game Center.app”, 35}, {”iBooks.app”, 35}, {”Boot Camp Assistant.app”, 35}, {”Bluetooth File Exchange.app”, 35}, {”System Information.app”, 35}, {”Safari.app”, 35}, {”Terminal.app”, 35}, {”Contacts.app”, 35}, {”Preview.app”, 35}, {”App Store.app”, 35}, {”Automator.app”, 35}, ……

–指定アプリケーションファイルの、指定Localeにおけるローカライズ言語リストを求める。重複を排除
on getSpecifiedAppFilesLocalizationList(anAppAlias as alias)
  
  
set aURL to (current application’s SMSFord’s URLFrom:anAppAlias)
  
set aBundle to current application’s NSBundle’s bundleWithURL:aURL
  
  
set theNSLocale to current application’s NSLocale’s localeWithLocaleIdentifier:“en” –Output Locale Name in English (en)
  
  
–Get Localization Info
  
set locList to aBundle’s localizations()
  
–> {”de”, “English”, “fr”, “French”, “German”, “ja”, “Japanese”}
  
set theEnumerator to locList’s objectEnumerator()
  
  
set theSet to current application’s NSMutableSet’s |set|()
  
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
    
set theName to (theNSLocale’s displayNameForKey:(current application’s NSLocaleIdentifier) value:aValue)
    
    
if theName is missing value then
      –”English”や”French”などは、missing valueが返ってくるので、”English”や”French”などをそのまま追加
      
theSet’s addObject:aValue
    else
      –”de”や”fr”などは、”German”や”French”が返ってくるので、それを追加
      
theSet’s addObject:theName
    end if
  end repeat
  
  
–NSCountedSetをNSMutableArrayに変換
  
set theArray to current application’s NSMutableArray’s array()
  
set theEnumerator to theSet’s objectEnumerator()
  
  
–NSMutableArrayから順次取り出して、NSArrayに出力
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
theArray’s addObject:aValue
  end repeat
  
  
return theArray as list
  
end getSpecifiedAppFilesLocalizationList

–指定階層下で、指定メタデータが指定パラメータであるファイルを取得(alias list)
on getFileListWithSpotLight(aMetaDataItem as string, aParam as string, startDir as {alias, string})
  set sDirText to quoted form of POSIX path of startDir
  
set shellText to “/usr/bin/mdfind ’” & aMetaDataItem & ” == \”" & aParam & “\”’ -onlyin “ & sDirText
  
try
    set aRes to do shell script shellText
  on error
    return {}
  end try
  
set pList to paragraphs of aRes
  
set aList to {}
  
repeat with i in pList
    set aPath to POSIX file i
    
set aPath to aPath as alias
    
set the end of aList to aPath
  end repeat
  
return aList
end getFileListWithSpotLight

★Click Here to Open This Script 

2014/12/25 アプリケーションのローカライズ分布を取得する v5

/Applicationsフォルダ内に入っているアプリケーションのローカライズ言語の度数分布を取得するAppleScriptのバージョンアップ版です。

1つのアプリケーションが複数の重複するローカライズ情報を持っているケース({”de”, “English”, “fr”, “French”, “German”, “ja”, “Japanese”})があり、1つのアプリケーションから情報を取得する段階で重複を除去するルーチンを作成。重複を排除して({”German”, “English”, “French”,”Japanese”})のように整理して取得するように変更しました。

また、/Applicationsフォルダの直下のアプリケーションのファイルしか取得していなかったので、mdfindで/Applicationsフォルダ以下すべてのアプリケーションを取得するように変更。

また、言語名ではなく出現頻度でソートするように変更しました。

途中で、mdfindでアプリケーションのファイルを抽出する処理を試していたら、サブフォルダ内のアプリケーションが出てこないという問題があり・・・開発マシンのSSD内のspotlightの検索辞書が壊れていたようで、再生成したら正常に動作するようになりました。mdfindの処理の落とし穴です。

マイナー言語(失礼!)をこまかくフォローしているのはGoogleアプリかと思っていたら、VLCでした。OSがサポートしていない言語用のローカライズを行っても意味はないのですが、ムービーの字幕表示などの関係でしょうか?

AppleScript名:アプリケーションのローカライズ分布を取得する v5
– Created 2014-12-23 by Takaaki Naganoya
– Changed 2014-12-24 by Shane Stanley
– Changed 2014-12-25 by Takaaki Naganoya

use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

–mdfindで指定フォルダ以下にあるすべてのアプリケーションファイルを取得
set apPath1 to (path to applications folder) as string
set apList to getFileListWithSpotLight(“kMDItemContentType”, “com.apple.application-bundle”, apPath1) of me

set theCountedSet to current application’s NSCountedSet’s |set|()

–各アプリケーションのローカリゼーション情報を取得する
repeat with i in apList
  set j to contents of i
  
  
–指定アプリケーションのローカライズ言語を取得。重複を除去
  
set locList to getSpecifiedAppFilesLocalizationList(j) of me
  
–> {”de”, “English”, “fr”, “French”, “German”, “ja”, “Japanese”}
  
—-> {”German”, “English”, “French”,”Japanese”}
  
  
set locArray to (current application’s SMSFord’s Cocoaify:locList)
  
  
set theEnumerator to locArray’s objectEnumerator()
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
    (
theCountedSet’s addObject:aValue)
  end repeat
end repeat

–NSCountedSetをNSMutableArrayに変換
set theArray to current application’s NSMutableArray’s array()
set theEnumerator to theCountedSet’s objectEnumerator()

–NSMutableArrayから順次取り出して、NSArrayに対してDictionaryを追加
repeat
  set aValue to theEnumerator’s nextObject()
  
if aValue is missing value then exit repeat
  
theArray’s addObject:(current application’s NSDictionary’s dictionaryWithObjects:{aValue, (theCountedSet’s countForObject:aValue)} forKeys:{“theName”, “numberOfTimes”})
end repeat

–出現回数(numberOfTimes)で降順ソート
set theDesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“numberOfTimes” ascending:false
theArray’s sortUsingDescriptors:{theDesc}

return theArray as list

–> {{theName:”English”, numberOfTimes:733}, {theName:”Japanese”, numberOfTimes:404}, {theName:”French”, numberOfTimes:266}, {theName:”German”, numberOfTimes:265}, {theName:”Italian”, numberOfTimes:219}, {theName:”Spanish”, numberOfTimes:203}, {theName:”Dutch”, numberOfTimes:165}, {theName:”Chinese (China)”, numberOfTimes:149}, {theName:”Swedish”, numberOfTimes:133}, {theName:”Korean”, numberOfTimes:131}, {theName:”Russian”, numberOfTimes:129}, {theName:”Chinese (Taiwan)”, numberOfTimes:127}, {theName:”Portuguese”, numberOfTimes:124}, {theName:”Polish”, numberOfTimes:118}, {theName:”Danish”, numberOfTimes:117}, {theName:”Finnish”, numberOfTimes:110}, {theName:”Norwegian”, numberOfTimes:100}, {theName:”Czech”, numberOfTimes:92}, {theName:”Turkish”, numberOfTimes:90}, {theName:”Hungarian”, numberOfTimes:87}, {theName:”Greek”, numberOfTimes:76}, {theName:”Ukrainian”, numberOfTimes:75}, {theName:”Romanian”, numberOfTimes:74}, {theName:”Portuguese (Portugal)”, numberOfTimes:70}, {theName:”Slovak”, numberOfTimes:69}, {theName:”Catalan”, numberOfTimes:63}, {theName:”Croatian”, numberOfTimes:63}, {theName:”Arabic”, numberOfTimes:61}, {theName:”Thai”, numberOfTimes:61}, {theName:”Hebrew”, numberOfTimes:60}, {theName:”Indonesian”, numberOfTimes:58}, {theName:”Vietnamese”, numberOfTimes:57}, {theName:”Malay”, numberOfTimes:56}, {theName:”Spanish (Mexico)”, numberOfTimes:49}, {theName:”Base”, numberOfTimes:46}, {theName:”Portuguese (Brazil)”, numberOfTimes:22}, {theName:”Norwegian Bokmål”, numberOfTimes:21}, {theName:”Chinese (Simplified)”, numberOfTimes:19}, {theName:”Chinese”, numberOfTimes:14}, {theName:”English (United Kingdom)”, numberOfTimes:12}, {theName:”Bulgarian”, numberOfTimes:12}, {theName:”Chinese (Traditional)”, numberOfTimes:11}, {theName:”Latvian”, numberOfTimes:10}, {theName:”Lithuanian”, numberOfTimes:9}, {theName:”Slovenian”, numberOfTimes:9}, {theName:”Japanese (Japan)”, numberOfTimes:9}, {theName:”Czech (Czech Republic)”, numberOfTimes:9}, {theName:”Estonian”, numberOfTimes:8}, {theName:”Ukrainian (Ukraine)”, numberOfTimes:8}, {theName:”Spanish (Spain)”, numberOfTimes:7}, {theName:”Turkish (Turkey)”, numberOfTimes:7}, {theName:”Hungarian (Hungary)”, numberOfTimes:6}, {theName:”Danish (Denmark)”, numberOfTimes:6}, {theName:”Russian (Russia)”, numberOfTimes:6}, {theName:”French (XM)”, numberOfTimes:6}, {theName:”Swedish (Sweden)”, numberOfTimes:6}, {theName:”Serbian”, numberOfTimes:6}, {theName:”Norwegian Bokmål (Norway)”, numberOfTimes:6}, {theName:”Dutch (Netherlands)”, numberOfTimes:6}, {theName:”Persian”, numberOfTimes:5}, {theName:”Korean (South Korea)”, numberOfTimes:5}, {theName:”Finnish (Finland)”, numberOfTimes:5}, {theName:”Italian (Italy)”, numberOfTimes:5}, {theName:”German (Germany)”, numberOfTimes:5}, {theName:”French (Canada)”, numberOfTimes:5}, {theName:”French (France)”, numberOfTimes:5}, {theName:”Polish (Poland)”, numberOfTimes:5}, {theName:”Hindi”, numberOfTimes:5}, {theName:”English (United States)”, numberOfTimes:5}, {theName:”Hebrew (Israel)”, numberOfTimes:4}, {theName:”Arabic (United Arab Emirates)”, numberOfTimes:4}, {theName:”Spanish (Latin America)”, numberOfTimes:4}, {theName:”Romanian (Romania)”, numberOfTimes:4}, {theName:”Spanish (Namibia)”, numberOfTimes:4}, {theName:”Filipino”, numberOfTimes:4}, {theName:”Spanish (Laos)”, numberOfTimes:4}, {theName:”empty”, numberOfTimes:4}, {theName:”Greek (Greece)”, numberOfTimes:4}, {theName:”Telugu”, numberOfTimes:3}, {theName:”Malayalam”, numberOfTimes:3}, {theName:”Icelandic”, numberOfTimes:3}, {theName:”Kannada”, numberOfTimes:3}, {theName:”Galician”, numberOfTimes:3}, {theName:”Bengali”, numberOfTimes:3}, {theName:”Gujarati”, numberOfTimes:3}, {theName:”Marathi”, numberOfTimes:3}, {theName:”Tamil”, numberOfTimes:3}, {theName:”Belarusian”, numberOfTimes:3}, {theName:”Albanian”, numberOfTimes:2}, {theName:”Mongolian”, numberOfTimes:2}, {theName:”French (Morocco)”, numberOfTimes:2}, {theName:”Georgian”, numberOfTimes:2}, {theName:”Sinhala”, numberOfTimes:2}, {theName:”jp”, numberOfTimes:2}, {theName:”ua”, numberOfTimes:2}, {theName:”Amharic”, numberOfTimes:2}, {theName:”English (United Arab Emirates)”, numberOfTimes:2}, {theName:”Finnish (FL)”, numberOfTimes:2}, {theName:”Catalan (Spain)”, numberOfTimes:2}, {theName:”Basque”, numberOfTimes:2}, {theName:”English (Israel)”, numberOfTimes:2}, {theName:”Norwegian (NB)”, numberOfTimes:2}, {theName:”Oriya”, numberOfTimes:1}, {theName:”Punjabi”, numberOfTimes:1}, {theName:”Serbo-Croatian”, numberOfTimes:1}, {theName:”Walloon”, numberOfTimes:1}, {theName:”Corsican”, numberOfTimes:1}, {theName:”Sorani Kurdish”, numberOfTimes:1}, {theName:”Chinese (Hong Kong SAR China)”, numberOfTimes:1}, {theName:”Breton”, numberOfTimes:1}, {theName:”Cornish”, numberOfTimes:1}, {theName:”Chiga”, numberOfTimes:1}, {theName:”Aragonese”, numberOfTimes:1}, {theName:”Macedonian”, numberOfTimes:1}, {theName:”Japan”, numberOfTimes:1}, {theName:”Bosnian”, numberOfTimes:1}, {theName:”Ganda”, numberOfTimes:1}, {theName:”Tetum”, numberOfTimes:1}, {theName:”Welsh”, numberOfTimes:1}, {theName:”Armenian”, numberOfTimes:1}, {theName:”Kyrgyz”, numberOfTimes:1}, {theName:”Scottish Gaelic”, numberOfTimes:1}, {theName:”Afrikaans”, numberOfTimes:1}, {theName:”Zulu”, numberOfTimes:1}, {theName:”Swahili”, numberOfTimes:1}, {theName:”Burmese”, numberOfTimes:1}, {theName:”Chinese (Traditional, Taiwan)”, numberOfTimes:1}, {theName:”ct”, numberOfTimes:1}, {theName:”Tagalog”, numberOfTimes:1}, {theName:”Kazakh”, numberOfTimes:1}, {theName:”Bengali (India)”, numberOfTimes:1}, {theName:”Pashto”, numberOfTimes:1}, {theName:”Nepali”, numberOfTimes:1}, {theName:”Acoli”, numberOfTimes:1}, {theName:”Friulian”, numberOfTimes:1}, {theName:”Norwegian Nynorsk”, numberOfTimes:1}, {theName:”Asturian”, numberOfTimes:1}, {theName:”Thai (Thailand)”, numberOfTimes:1}, {theName:”Khmer”, numberOfTimes:1}, {theName:”Irish”, numberOfTimes:1}, {theName:”American”, numberOfTimes:1}, {theName:”Fulah”, numberOfTimes:1}, {theName:”Chinese (Traditional, Hong Kong SAR China)”, numberOfTimes:1}, {theName:”Occitan”, numberOfTimes:1}, {theName:”Interlingua”, numberOfTimes:1}, {theName:”Uzbek”, numberOfTimes:1}, {theName:”Azerbaijani”, numberOfTimes:1}}

–指定アプリケーションファイルの、指定Localeにおけるローカライズ言語リストを求める。重複を排除
on getSpecifiedAppFilesLocalizationList(anAppAlias as alias)
  
  
set aURL to (current application’s SMSFord’s URLFrom:anAppAlias)
  
set aBundle to current application’s NSBundle’s bundleWithURL:aURL
  
  
set theNSLocale to current application’s NSLocale’s localeWithLocaleIdentifier:“en” –Output Locale Name in English (en)
  
  
–Get Localization Info
  
set locList to aBundle’s localizations()
  
–> {”de”, “English”, “fr”, “French”, “German”, “ja”, “Japanese”}
  
set theEnumerator to locList’s objectEnumerator()
  
  
set theSet to current application’s NSMutableSet’s |set|()
  
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
    
set theName to (theNSLocale’s displayNameForKey:(current application’s NSLocaleIdentifier) value:aValue)
    
    
if theName is missing value then
      –”English”や”French”などは、missing valueが返ってくるので、”English”や”French”などをそのまま追加
      
theSet’s addObject:aValue
    else
      –”de”や”fr”などは、”German”や”French”が返ってくるので、それを追加
      
theSet’s addObject:theName
    end if
  end repeat
  
  
–NSCountedSetをNSMutableArrayに変換
  
set theArray to current application’s NSMutableArray’s array()
  
set theEnumerator to theSet’s objectEnumerator()
  
  
–NSMutableArrayから順次取り出して、NSArrayに対してDictionaryを追加
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
theArray’s addObject:aValue
  end repeat
  
  
return theArray as list
  
end getSpecifiedAppFilesLocalizationList

–指定階層下で、指定メタデータが指定パラメータであるファイルを取得(alias list)
on getFileListWithSpotLight(aMetaDataItem as string, aParam as string, startDir as {alias, string})
  set sDirText to quoted form of POSIX path of startDir
  
set shellText to “/usr/bin/mdfind ’” & aMetaDataItem & ” == \”" & aParam & “\”’ -onlyin “ & sDirText
  
try
    set aRes to do shell script shellText
  on error
    return {}
  end try
  
set pList to paragraphs of aRes
  
set aList to {}
  
repeat with i in pList
    set aPath to POSIX file i
    
set aPath to aPath as alias
    
set the end of aList to aPath
  end repeat
  
return aList
end getFileListWithSpotLight

★Click Here to Open This Script 

2014/12/24 アプリケーションのローカライズ分布を取得する v4

/Applicationsフォルダ内に入っているアプリケーションのローカライズ言語の度数分布を取得するAppleScriptのバージョンアップ版です。

Shane Stanleyからのクリスマスプレゼントともいえるもので、実に処理内容がクールです。いや、これはいい。

v3で言語表記の「ゆらぎ」をカバーしようとしましたが、このv4はOSのサービスを用いて、”jp”や”fr”といった短縮表記からフルネームの”Japanese”や”French”といった名称を展開し、そのうえで重複を排除しています。

AppleScript名:アプリケーションのローカライズ分布を取得する v4
– v1 Created 2014-12-24 By Takaaki Naganoya
– v2 Changed 2014-12-24 by Shane Stanley
– v3 Changed 2014-12-24 by Takaaki Naganoya
– v4 Changed 2014-12-24 by Shane Stanley

use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set apPath to (path to applications folder) as string

tell application “Finder”
  tell folder apPath
    set aList to (every application file) as alias list
  end tell
end tell

set theCountedSet to current application’s NSCountedSet’s |set|()
set theNSLocale to current application’s NSLocale’s localeWithLocaleIdentifier:“en” –Output Locale Name in English (en)
–set theNSLocale to current application’s NSLocale’s currentLocale() –Output Locale Name in current Locale Language (ex. Japanese)

repeat with i in aList
  set j to contents of i
  
set aURL to (current application’s SMSFord’s URLFrom:j)
  
set aBundle to (current application’s NSBundle’s bundleWithURL:aURL)
  
  
–アプリケーションのローカリゼーションを取得する
  
set locList to aBundle’s localizations()
  
set theEnumerator to locList’s objectEnumerator()
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
set theName to (theNSLocale’s displayNameForKey:(current application’s NSLocaleIdentifier) value:aValue)
    
if theName is missing value then
      (theCountedSet’s addObject:aValue)
    else
      (theCountedSet’s addObject:theName)
    end if
  end repeat
  
  
log {“theCountedSet”, theCountedSet} –ASObjC Explorer 4だとCocoaのObjectも読める形式でログ出力可能
  
end repeat

–NSCountedSetをNSMutableDictionaryに変換してからASのrecordに変換
set theArray to current application’s NSMutableArray’s array()
set theEnumerator to theCountedSet’s objectEnumerator()
repeat
  set aValue to theEnumerator’s nextObject()
  
if aValue is missing value then exit repeat
  
theArray’s addObject:(current application’s NSDictionary’s dictionaryWithObjects:{aValue, (theCountedSet’s countForObject:aValue)} forKeys:{“theName”, “numberOfTimes”})
end repeat
set theDesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“theName” ascending:true
theArray’s sortUsingDescriptors:{theDesc}

return theArray as list
–> {{theName:”Acoli”, numberOfTimes:1}, {theName:”Afrikaans”, numberOfTimes:1}, {theName:”Albanian”, numberOfTimes:1}, {theName:”American”, numberOfTimes:1}, {theName:”Amharic”, numberOfTimes:2}, {theName:”Arabic”, numberOfTimes:36}, {theName:”Aragonese”, numberOfTimes:1}, {theName:”Armenian”, numberOfTimes:1}, {theName:”Asturian”, numberOfTimes:1}, {theName:”Azerbaijani”, numberOfTimes:1}, {theName:”Base”, numberOfTimes:30}, {theName:”Basque”, numberOfTimes:2}, {theName:”Belarusian”, numberOfTimes:2}, {theName:”Bengali”, numberOfTimes:3}, {theName:”Bengali (India)”, numberOfTimes:1}, {theName:”Bosnian”, numberOfTimes:1}, {theName:”Breton”, numberOfTimes:1}, {theName:”Bulgarian”, numberOfTimes:8}, {theName:”Burmese”, numberOfTimes:1}, {theName:”Catalan”, numberOfTimes:39}, {theName:”Chiga”, numberOfTimes:1}, {theName:”Chinese”, numberOfTimes:8}, {theName:”Chinese (China)”, numberOfTimes:69}, {theName:”Chinese (Hong Kong SAR China)”, numberOfTimes:1}, {theName:”Chinese (Simplified)”, numberOfTimes:14}, {theName:”Chinese (Taiwan)”, numberOfTimes:64}, {theName:”Chinese (Traditional)”, numberOfTimes:8}, {theName:”Chinese (Traditional, Hong Kong SAR China)”, numberOfTimes:1}, {theName:”Cornish”, numberOfTimes:1}, {theName:”Corsican”, numberOfTimes:1}, {theName:”Croatian”, numberOfTimes:40}, {theName:”Czech”, numberOfTimes:48}, {theName:”Danish”, numberOfTimes:62}, {theName:”Danish (Denmark)”, numberOfTimes:1}, {theName:”Dutch”, numberOfTimes:77}, {theName:”Dutch (Netherlands)”, numberOfTimes:1}, {theName:”English”, numberOfTimes:307}, {theName:”English (United Kingdom)”, numberOfTimes:7}, {theName:”English (United States)”, numberOfTimes:1}, {theName:”Estonian”, numberOfTimes:6}, {theName:”Filipino”, numberOfTimes:4}, {theName:”Finnish”, numberOfTimes:56}, {theName:”French”, numberOfTimes:130}, {theName:”Friulian”, numberOfTimes:1}, {theName:”Fulah”, numberOfTimes:1}, {theName:”Galician”, numberOfTimes:2}, {theName:”Ganda”, numberOfTimes:1}, {theName:”Georgian”, numberOfTimes:2}, {theName:”German”, numberOfTimes:135}, {theName:”Greek”, numberOfTimes:43}, {theName:”Gujarati”, numberOfTimes:3}, {theName:”Hebrew”, numberOfTimes:36}, {theName:”Hindi”, numberOfTimes:5}, {theName:”Hungarian”, numberOfTimes:44}, {theName:”Icelandic”, numberOfTimes:3}, {theName:”Indonesian”, numberOfTimes:38}, {theName:”Interlingua”, numberOfTimes:1}, {theName:”Irish”, numberOfTimes:1}, {theName:”Italian”, numberOfTimes:98}, {theName:”Japanese”, numberOfTimes:147}, {theName:”Japanese (Japan)”, numberOfTimes:1}, {theName:”Kannada”, numberOfTimes:3}, {theName:”Kazakh”, numberOfTimes:1}, {theName:”Khmer”, numberOfTimes:1}, {theName:”Korean”, numberOfTimes:67}, {theName:”Kyrgyz”, numberOfTimes:1}, {theName:”Latvian”, numberOfTimes:7}, {theName:”Lithuanian”, numberOfTimes:6}, {theName:”Macedonian”, numberOfTimes:1}, {theName:”Malay”, numberOfTimes:36}, {theName:”Malayalam”, numberOfTimes:3}, {theName:”Marathi”, numberOfTimes:3}, {theName:”Mongolian”, numberOfTimes:2}, {theName:”Nepali”, numberOfTimes:1}, {theName:”Norwegian”, numberOfTimes:49}, {theName:”Norwegian Bokmål”, numberOfTimes:10}, {theName:”Norwegian Bokmål (Norway)”, numberOfTimes:1}, {theName:”Norwegian Nynorsk”, numberOfTimes:1}, {theName:”Occitan”, numberOfTimes:1}, {theName:”Oriya”, numberOfTimes:1}, {theName:”Pashto”, numberOfTimes:1}, {theName:”Persian”, numberOfTimes:4}, {theName:”Polish”, numberOfTimes:60}, {theName:”Portuguese”, numberOfTimes:61}, {theName:”Portuguese (Brazil)”, numberOfTimes:12}, {theName:”Portuguese (Portugal)”, numberOfTimes:44}, {theName:”Punjabi”, numberOfTimes:1}, {theName:”Romanian”, numberOfTimes:42}, {theName:”Russian”, numberOfTimes:70}, {theName:”Russian (Russia)”, numberOfTimes:1}, {theName:”Scottish Gaelic”, numberOfTimes:1}, {theName:”Serbian”, numberOfTimes:5}, {theName:”Serbo-Croatian”, numberOfTimes:1}, {theName:”Sinhala”, numberOfTimes:2}, {theName:”Slovak”, numberOfTimes:42}, {theName:”Slovenian”, numberOfTimes:7}, {theName:”Sorani Kurdish”, numberOfTimes:1}, {theName:”Spanish”, numberOfTimes:95}, {theName:”Spanish (Latin America)”, numberOfTimes:2}, {theName:”Spanish (Mexico)”, numberOfTimes:28}, {theName:”Spanish (Spain)”, numberOfTimes:2}, {theName:”Swahili”, numberOfTimes:1}, {theName:”Swedish”, numberOfTimes:64}, {theName:”Swedish (Sweden)”, numberOfTimes:1}, {theName:”Tagalog”, numberOfTimes:1}, {theName:”Tamil”, numberOfTimes:3}, {theName:”Telugu”, numberOfTimes:3}, {theName:”Tetum”, numberOfTimes:1}, {theName:”Thai”, numberOfTimes:40}, {theName:”Thai (Thailand)”, numberOfTimes:1}, {theName:”Turkish”, numberOfTimes:46}, {theName:”Ukrainian”, numberOfTimes:39}, {theName:”Ukrainian (Ukraine)”, numberOfTimes:2}, {theName:”Uzbek”, numberOfTimes:1}, {theName:”Vietnamese”, numberOfTimes:37}, {theName:”Walloon”, numberOfTimes:1}, {theName:”Welsh”, numberOfTimes:1}, {theName:”Zulu”, numberOfTimes:1}, {theName:”empty”, numberOfTimes:3}}

–> {{theName:”American”, numberOfTimes:1}, {theName:”Base”, numberOfTimes:30}, {theName:”Chinese”, numberOfTimes:2}, {theName:”Czech”, numberOfTimes:1}, {theName:”Danish”, numberOfTimes:4}, {theName:”Dutch”, numberOfTimes:38}, {theName:”English”, numberOfTimes:212}, {theName:”Finnish”, numberOfTimes:1}, {theName:”French”, numberOfTimes:67}, {theName:”German”, numberOfTimes:72}, {theName:”Greek”, numberOfTimes:1}, {theName:”Hungarian”, numberOfTimes:1}, {theName:”Italian”, numberOfTimes:50}, {theName:”Japanese”, numberOfTimes:80}, {theName:”Korean”, numberOfTimes:3}, {theName:”Polish”, numberOfTimes:3}, {theName:”Portuguese”, numberOfTimes:2}, {theName:”Russian”, numberOfTimes:3}, {theName:”Spanish”, numberOfTimes:45}, {theName:”Swedish”, numberOfTimes:5}, {theName:”empty”, numberOfTimes:3}, {theName:”アイスランド語”, numberOfTimes:3}, {theName:”アイルランド語”, numberOfTimes:1}, {theName:”アストゥリアス語”, numberOfTimes:1}, {theName:”アゼルバイジャン語”, numberOfTimes:1}, {theName:”アチョリ語”, numberOfTimes:1}, {theName:”アフリカーンス語”, numberOfTimes:1}, {theName:”アムハラ語”, numberOfTimes:2}, {theName:”アラゴン語”, numberOfTimes:1}, {theName:”アラビア語”, numberOfTimes:36}, {theName:”アルバニア語”, numberOfTimes:1}, {theName:”アルメニア語”, numberOfTimes:1}, {theName:”イタリア語”, numberOfTimes:48}, {theName:”インターリングア”, numberOfTimes:1}, {theName:”インドネシア語”, numberOfTimes:38}, {theName:”ウェールズ語”, numberOfTimes:1}, {theName:”ウクライナ語”, numberOfTimes:39}, {theName:”ウクライナ語 (ウクライナ)”, numberOfTimes:2}, {theName:”ウズベク語”, numberOfTimes:1}, {theName:”エストニア語”, numberOfTimes:6}, {theName:”オック語”, numberOfTimes:1}, {theName:”オランダ語”, numberOfTimes:39}, {theName:”オランダ語 (オランダ)”, numberOfTimes:1}, {theName:”オリヤー語”, numberOfTimes:1}, {theName:”ガリシア語”, numberOfTimes:2}, {theName:”ガンダ語”, numberOfTimes:1}, {theName:”カザフ語”, numberOfTimes:1}, {theName:”カタロニア語”, numberOfTimes:39}, {theName:”カンナダ語”, numberOfTimes:3}, {theName:”ギリシャ語”, numberOfTimes:42}, {theName:”キルギス語”, numberOfTimes:1}, {theName:”グジャラート語”, numberOfTimes:3}, {theName:”グルジア語”, numberOfTimes:2}, {theName:”クメール語”, numberOfTimes:1}, {theName:”クルド語(ソラニー)”, numberOfTimes:1}, {theName:”クロアチア語”, numberOfTimes:40}, {theName:”コルシカ語”, numberOfTimes:1}, {theName:”コーンウォール語”, numberOfTimes:1}, {theName:”シンハラ語”, numberOfTimes:2}, {theName:”ズールー語”, numberOfTimes:1}, {theName:”スウェーデン語”, numberOfTimes:59}, {theName:”スウェーデン語 (スウェーデン)”, numberOfTimes:1}, {theName:”スコットランド・ゲール語”, numberOfTimes:1}, {theName:”スペイン語”, numberOfTimes:50}, {theName:”スペイン語 (スペイン)”, numberOfTimes:2}, {theName:”スペイン語 (メキシコ)”, numberOfTimes:28}, {theName:”スペイン語 (ラテンアメリカ)”, numberOfTimes:2}, {theName:”スロバキア語”, numberOfTimes:42}, {theName:”スロベニア語”, numberOfTimes:7}, {theName:”スワヒリ語”, numberOfTimes:1}, {theName:”セルビア語”, numberOfTimes:5}, {theName:”セルボ・クロアチア語”, numberOfTimes:1}, {theName:”タイ語”, numberOfTimes:40}, {theName:”タイ語 (タイ)”, numberOfTimes:1}, {theName:”タガログ語”, numberOfTimes:1}, {theName:”タミル語”, numberOfTimes:3}, {theName:”チェコ語”, numberOfTimes:47}, {theName:”チガ語”, numberOfTimes:1}, {theName:”デンマーク語”, numberOfTimes:58}, {theName:”デンマーク語 (デンマーク)”, numberOfTimes:1}, {theName:”テトゥン語”, numberOfTimes:1}, {theName:”テルグ語”, numberOfTimes:3}, {theName:”ドイツ語”, numberOfTimes:63}, {theName:”トルコ語”, numberOfTimes:46}, {theName:”ネパール語”, numberOfTimes:1}, {theName:”ノルウェー語”, numberOfTimes:49}, {theName:”ノルウェー語(ニーノシュク)”, numberOfTimes:1}, {theName:”ノルウェー語(ブークモール)”, numberOfTimes:10}, {theName:”ノルウェー語(ブークモール) (ノルウェー)”, numberOfTimes:1}, {theName:”バスク語”, numberOfTimes:2}, {theName:”パシュトゥー語”, numberOfTimes:1}, {theName:”パンジャブ語”, numberOfTimes:1}, {theName:”ハンガリー語”, numberOfTimes:43}, {theName:”ビルマ語”, numberOfTimes:1}, {theName:”ヒンディー語”, numberOfTimes:5}, {theName:”ブルガリア語”, numberOfTimes:8}, {theName:”ブルトン語”, numberOfTimes:1}, {theName:”フィリピノ語”, numberOfTimes:4}, {theName:”フィンランド語”, numberOfTimes:55}, {theName:”フラニ語”, numberOfTimes:1}, {theName:”フランス語”, numberOfTimes:63}, {theName:”フリウリ語”, numberOfTimes:1}, {theName:”ベトナム語”, numberOfTimes:37}, {theName:”ベラルーシ語”, numberOfTimes:2}, {theName:”ベンガル語”, numberOfTimes:3}, {theName:”ベンガル語 (インド)”, numberOfTimes:1}, {theName:”ヘブライ語”, numberOfTimes:36}, {theName:”ペルシア語”, numberOfTimes:4}, {theName:”ボスニア語”, numberOfTimes:1}, {theName:”ポルトガル語”, numberOfTimes:59}, {theName:”ポルトガル語 (ブラジル)”, numberOfTimes:12}, {theName:”ポルトガル語 (ポルトガル)”, numberOfTimes:44}, {theName:”ポーランド語”, numberOfTimes:57}, {theName:”マケドニア語”, numberOfTimes:1}, {theName:”マラヤーラム語”, numberOfTimes:3}, {theName:”マラーティー語”, numberOfTimes:3}, {theName:”マレー語”, numberOfTimes:36}, {theName:”モンゴル語”, numberOfTimes:2}, {theName:”ラトビア語”, numberOfTimes:7}, {theName:”リトアニア語”, numberOfTimes:6}, {theName:”ルーマニア語”, numberOfTimes:42}, {theName:”ロシア語”, numberOfTimes:67}, {theName:”ロシア語 (ロシア)”, numberOfTimes:1}, {theName:”ワロン語”, numberOfTimes:1}, {theName:”中国語”, numberOfTimes:6}, {theName:”中国語 (中国)”, numberOfTimes:69}, {theName:”中国語 (中華人民共和国香港特別行政区)”, numberOfTimes:1}, {theName:”中国語 (台湾)”, numberOfTimes:64}, {theName:”中国語 (簡体字)”, numberOfTimes:14}, {theName:”中国語 (繁体字)”, numberOfTimes:8}, {theName:”中国語 (繁体字、中華人民共和国香港特別行政区)”, numberOfTimes:1}, {theName:”日本語”, numberOfTimes:67}, {theName:”日本語 (日本)”, numberOfTimes:1}, {theName:”英語”, numberOfTimes:95}, {theName:”英語 (アメリカ合衆国)”, numberOfTimes:1}, {theName:”英語 (イギリス)”, numberOfTimes:7}, {theName:”韓国語”, numberOfTimes:64}}

★Click Here to Open This Script 

動作原理を理解するために、指定アプリ1つのローカライズ言語を取得するScriptを単体で動作するようにしてみました。

AppleScript名:選択したアプリケーションのローカライズ言語名称を求める
– Created 2014-12-23 by Takaaki Naganoya
– Changed 2014-12-24 by Shane Stanley
– Created 2014-12-24 by Takaaki Naganoya
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set a to choose file of type {“com.apple.application-bundle”}
set aRes to getSpecifiedAppFilesLocalizationList(a) of me
–> {”German”, “English”, “French”, “Japanese”}

–指定アプリケーションファイルの、指定Localeにおけるローカライズ言語リストを求める
on getSpecifiedAppFilesLocalizationList(anApp)
  
  
set aURL to (current application’s SMSFord’s URLFrom:anApp)
  
set aBundle to current application’s NSBundle’s bundleWithURL:aURL
  
  
set theNSLocale to current application’s NSLocale’s localeWithLocaleIdentifier:“en” –Output Locale Name in English (en)
  
  
–Get Localization Info
  
set locList to aBundle’s localizations()
  
–> {”de”, “English”, “fr”, “French”, “German”, “ja”, “Japanese”}
  
  
set outList to {}
  
  
repeat with i in locList
    set j to contents of i
    
set theName to (theNSLocale’s displayNameForKey:(current application’s NSLocaleIdentifier) value:j)
    
    
if theName is missing value then
      –”English”や”French”などは、missing valueが返ってくるので、”English”や”French”などをそのまま追加
      
if (j as string) is not in outList then
        set the end of outList to j as string
      end if
    else
      –”de”や”fr”などは、”German”や”French”が返ってくるので、それを追加
      
if theName as string is not in outList then
        set the end of outList to theName as string
      end if
    end if
  end repeat
  
  
return outList
  
end getSpecifiedAppFilesLocalizationList

★Click Here to Open This Script 

2014/12/24 アプリケーションのローカライズ分布を取得する v3

/Applicationsフォルダ内に入っているアプリケーションのローカライズ言語の度数分布を取得するAppleScriptのバージョンアップ版です。

言語の識別子がアプリケーションごとにゆらいでおり(Japanese/ja/ja_JPなど)、ゆらぎを吸収する処理を付加してみました。

通常のAppleScriptであれば、2D Listをループで回して、is in演算子で複数の値にヒットするかどうか処理を行うところですが、Cocoaの機能とASObjCExtras.frameworkの機能を活用して処理しました。おかげで、開発環境(MacBook Pro Retina 2012)では1秒以下で処理が終了します。

これは、処理速度もさることながら・・・AppleScriptのrecordに変換するとハイフン(「ー」)を含むようなラベルは処理がまったくできなくなってしまう(エラーになる)ため、それを回避する意味もあります。

AppleScript名:アプリケーションのローカライズ分布を取得する v3
– Created 2014-12-23 by Takaaki Naganoya
– Changed 2014-12-24 by Shane Stanley
– Changed 2014-12-24 by Takaaki Naganoya

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "ASObjCExtras"

–/Applications以下のアプリケーションのパスをすべて求める
set apPath to (path to applications folder) as string

tell application "Finder"
  tell folder apPath
    set aList to (every application file) as alias list
  end tell
end tell

–各アプリケーションのローカリゼーション情報を取得(重複情報はカウントしつつ保持)
set aDict to current application’s NSMutableDictionary’s alloc()’s init()
set theCountedSet to current application’s NSCountedSet’s |set|()

repeat with i in aList
  set j to contents of i
  
set aURL to (current application’s SMSFord’s URLFrom:j)
  
set aBundle to (current application’s NSBundle’s bundleWithURL:aURL)
  
  
–アプリケーションのローカリゼーションを取得する
  
set locList to aBundle’s localizations()’s ASify() as list
  (
theCountedSet’s addObjectsFromArray:locList)
  
  
–log {"theCountedSet", theCountedSet} –ASObjC Explorer 4だとCocoaのObjectも読める形式でログ出力可能
end repeat

–NSCountedSetをNSMutableDictionaryに変換
set theEnumerator to theCountedSet’s objectEnumerator()
repeat
  set aValue to theEnumerator’s nextObject()
  
if aValue is missing value then exit repeat
  
aDict’s setObject:(theCountedSet’s countForObject:aValue) forKey:aValue
end repeat

–言語区分リスト(実際には同一視してまとめる)。ものすごくざっくり。Googleのアプリだけものすごく狭い言語へのローカライズを行っているもよう
set langList to {{"Japanese", "ja", "ja_JP"}, {"English", "en", "Base", "American"}, {"Arabic", "ar"}, {"Arabic", "ar"}, {"Catalan", "ca"}, {"Czech", "cs"}, {"Danish", "da"}, {"Dutch", "nl"}, {"Greek", "el"}, {"Spanish", "es"}, {"Spanish@Latin America", "es_419"}, {"Finnish", "fi"}, {"French", "fr"}, {"German", "de"}, {"Hebrew", "he"}, {"Croatian", "hr"}, {"Hungarian", "hu"}, {"Italian", "it"}, {"Korean", "ko"}, {"Norwegian", "no"}, {"Polish", "pl"}, {"Portuguese@Brazil", "pt", "pt-BR"}, {"Portuguese@Portugal", "pt_PT"}, {"Romanian", "ro"}, {"Russian", "ru"}, {"Slovak", "sk"}, {"Swedish", "sv"}, {"Thai", "th"}, {"Setswana", "tr"}, {"Ukrainian", "uk"}, {"Chinese@China", "Chinese", "zh_CN", "zh", "zh-Hans"}, {"Chinese@HongKong", "zh_HK"}, {"Chinese@Taiwan", "zh_TW"}, {"Gujarati@India", "Gujarati", "gu"}, {"Bulgarian", "bg"}, {"Amharic@Ethiopia", "am"}, {"Asturian", "ast"}, {"Kazakh@Kazakhstan", "kk"}, {"Zulu, Bantu language spoken in South Africa", "zu"}, {"Filipino", "fil"}, {"Luganda", "lg"}, {"Danish@Denmark", "da_DK"}}
–すべてではないので、ちょっといい加減

–NSMutableDictionaryをArrayに入れて、複数ラベルの同一視処理を行いローカライズ言語ごとの実数の集計を行う
set bList to current application’s NSArray’s arrayWithObject:aDict
set cList to {}

repeat with i in langList
  set j to contents of i
  
  
set aArray to (current application’s SMSFord’s subarraysFrom:bList usingKeys:j outKeys:j |error|:(missing value)) –指定ラベルの項目の数値のみ抽出
  
set bArray to aArray’s lastObject() –2D Arrayで返ってくるので、1Dに
  
set cArray to (current application’s SMSFord’s arrayByReplacingNullsIn:bArray withItem:0) –Hitしなかった値がmissing valueで返るので、0に置換
  
  
–ヒットした数を合計
  
set sumNum to (cArray’s valueForKeyPath:"@sum.self")’s integerValue() as number
  
set the end of cList to {contents of first item of j, sumNum}
  
end repeat

–Sort Result (Many->Little)
set sortIndexes to {1} –Key Item id: begin from 0
set sortOrders to {false} –Descending Sort Order
set sortTypes to {"compare:"}
set rList to (current application’s SMSFord’s subarraysIn:cList sortedByIndexes:sortIndexes ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list

return rList

–> {{"English", 338}, {"Japanese", 148}, {"German", 135}, {"French", 130}, {"Italian", 98}, {"Spanish", 95}, {"Chinese@China", 91}, {"Dutch", 77}, {"Russian", 70}, {"Korean", 67}, {"Swedish", 64}, {"Chinese@Taiwan", 64}, {"Danish", 62}, {"Portuguese@Brazil", 62}, {"Polish", 60}, {"Finnish", 56}, {"Norwegian", 49}, {"Czech", 48}, {"Setswana", 46}, {"Hungarian", 44}, {"Greek", 43}, {"Romanian", 42}, {"Slovak", 42}, {"Portuguese@Portugal", 41}, {"Croatian", 40}, {"Thai", 40}, {"Catalan", 39}, {"Ukrainian", 39}, {"Arabic", 36}, {"Arabic", 36}, {"Hebrew", 36}, {"Bulgarian", 8}, {"Filipino", 4}, {"Gujarati@India", 3}, {"Amharic@Ethiopia", 2}, {"Spanish@Latin America", 1}, {"Chinese@HongKong", 1}, {"Asturian", 1}, {"Kazakh@Kazakhstan", 1}, {"Zulu, Bantu language spoken in South Africa", 1}, {"Luganda", 1}, {"Danish@Denmark", 1}}

★Click Here to Open This Script 

2014/12/24 アプリケーションのローカライズ分布を取得するv2

/Applicationsフォルダ内に入っているアプリケーションのローカライズ言語の度数分布を取得するAppleScriptのバージョンアップ版です。

Shane Stanleyから指摘があり、データ数が増えた場合にはNSCountedSetを利用した方が有利とのこと。NSCountedSetの内容を(前バージョンよりも読みやすい形式で)ログ出力して確認したりと、ASObjCExplorer 4の新バージョンの4.1の威力を実感できました(^ー^)

asoe4.png

AppleScript名:アプリケーションのローカライズ分布を取得する v2
– Created 2014-12-23 by Takaaki Naganoya
– Changed 2014-12-24 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set apPath to (path to applications folder) as string

tell application “Finder”
  tell folder apPath
    set aList to (every application file) as alias list
  end tell
end tell

set aDict to current application’s NSMutableDictionary’s alloc()’s init()
set theCountedSet to current application’s NSCountedSet’s |set|()

repeat with i in aList
  set j to contents of i
  
set aURL to (current application’s SMSFord’s URLFrom:j)
  
set aBundle to (current application’s NSBundle’s bundleWithURL:aURL)
  
  
–アプリケーションのローカリゼーションを取得する
  
set locList to aBundle’s localizations()’s ASify() as list
  (
theCountedSet’s addObjectsFromArray:locList)
  
  
log {“theCountedSet”, theCountedSet} –ASObjC Explorer 4だとCocoaのObjectも読める形式でログ出力可能
  
end repeat

–NSCountedSetをNSMutableDictionaryに変換してからASのrecordに変換
set theEnumerator to theCountedSet’s objectEnumerator()
repeat
  set aValue to theEnumerator’s nextObject()
  
if aValue is missing value then exit repeat
  
aDict’s setObject:(theCountedSet’s countForObject:aValue) forKey:aValue
end repeat

set bRec to aDict as record
–> {zh-Hant-HK:1, pt_BR:9, Finnish:1, Portuguese:2, ja:67, af:1, English:212, gu:3, es_419:1, zh:6, ps:1, Italian:50, uk_UA:1, pt:59, hr:40, ja_JP:1, bn_IN:1, Czech:1, ka:2, be:2, fur:1, en_GB:6, Russian:3, Korean:3, hu:43, bg:8, am:2, ca:39, an:1, ta:3, sh:1, si:2, is:3, es-419:1, hy:1, ro:42, it:48, ar:36, te:3, sk:42, da:58, Hungarian:1, pt_PT:41, sl:7, bn:3, ast:1, pt-BR:3, kk:1, zu:1, fil:4, th:40, ru:67, lg:1, de:63, zh_CN:69, da_DK:1, km:1, Base:30, br:1, kn:3, sq:1, ko:64, en_US:1, bs:1, tl:1, zh-Hant:8, sr:5, az:1, uk-UA:1, ach:1, co:1, nb-NO:1, Greek:1, en-GB:1, sv:59, nb:10, Dutch:38, ru-RU:1, uk:39, sw:1, cs:47, ckb:1, fa:4, Danish:4, tr:46, wa:1, kw:1, mk:1, ne:1, ml:3, vi:37, ky:1, mn:2, pt-PT:3, el:42, Spanish:45, th_TH:1, oc:1, cy:1, ga:1, lt:6, ff:1, lv:7, en:96, sv_SE:1, nl_NL:1, fi:55, mr:3, gd:1, nl:39, pa:1, ms:36, nn:1, German:72, es_MX:28, no:49, es:50, Polish:3, es_ES:2, French:67, et:6, eu:2, Swedish:5, uz:1, American:1, tet:1, my:1, he:36, cgg:1, fr:63, gl:2, ia:1, Chinese:2, zh_HK:1, zh-Hans:14, empty:3, hi:5, Japanese:80, pl:57, id:38, or:1, zh_TW:64}

★Click Here to Open This Script 

2014/12/23 アプリケーションのローカライズ分布を取得する

/Applicationsフォルダ内に入っているアプリケーションのローカライズ言語の度数分布を取得するAppleScriptです。

こうしてLocalize情報を取得すると、「empty」「base」とか「id」とかいう怪しい言語情報が返ってくることがあり、一筋縄で行かない感じがします。これでも、Carbonバイナリのアプリケーションが混ざっている頃には(Xcode以外で開発されたアプリケーションが流通していた時代には)こうした情報がキレイに取得できず(とくにバージョン情報)、楽にはなってきてはいるのですが、、、、

AppleScript名:アプリケーションのローカライズ分布を取得する
– Created 2014-12-23 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set apPath to (path to applications folder) as string

tell application “Finder”
  tell folder apPath
    set aList to (every application file) as alias list
  end tell
end tell

set aDict to current application’s NSMutableDictionary’s alloc()’s init()

repeat with i in aList
  set j to contents of i
  
set aURL to (current application’s SMSFord’s URLFrom:j)
  
set aBundle to (current application’s NSBundle’s bundleWithURL:aURL)
  
  
–アプリケーションのローカリゼーションを取得する
  
set locList to aBundle’s localizations()’s ASify() as list
  
log {locList}
  
repeat with ii in locList
    set jj to contents of ii
    
try
      set cVal to (aDict’s valueForKeyPath:jj)
      
set bVal to cVal as integer
      
set aVal to bVal + 1
    on error
      set aVal to 1
    end try
    
    (
aDict’s setValue:aVal forKeyPath:jj)
    
  end repeat
  
end repeat

set bRec to aDict’s ASify() as record
–> {zh-Hant-HK:1, pt_BR:9, Finnish:1, Portuguese:2, ja:67, af:1, English:212, gu:3, es_419:1, zh:6, ps:1, Italian:50, uk_UA:1, pt:59, hr:40, ja_JP:1, bn_IN:1, Czech:1, ka:2, be:2, fur:1, en_GB:6, Russian:3, Korean:3, hu:43, bg:8, am:2, ca:39, an:1, ta:3, sh:1, si:2, is:3, es-419:1, ro:42, hy:1, it:48, ar:36, te:3, sk:42, da:58, Hungarian:1, pt_PT:41, sl:7, bn:3, ast:1, pt-BR:3, kk:1, zu:1, fil:4, th:40, ru:67, zh_CN:69, de:63, km:1, da_DK:1, lg:1, Base:30, br:1, kn:3, sq:1, ko:64, en_US:1, bs:1, sr:5, zh-Hant:8, az:1, tl:1, nb-NO:1, ach:1, co:1, uk-UA:1, Greek:1, en-GB:1, sv:59, nb:10, Dutch:38, ru-RU:1, uk:39, sw:1, cs:47, ckb:1, fa:4, Danish:4, tr:46, wa:1, kw:1, mk:1, ne:1, ml:3, vi:37, ky:1, mn:2, pt-PT:3, el:42, Spanish:45, th_TH:1, lt:6, cy:1, ga:1, ff:1, sv_SE:1, lv:7, en:96, oc:1, nl_NL:1, fi:55, mr:3, gd:1, nl:39, ms:36, pa:1, nn:1, German:72, es_MX:28, no:49, es:50, Polish:3, es_ES:2, French:67, et:6, eu:2, Swedish:5, uz:1, American:1, tet:1, my:1, he:36, cgg:1, fr:63, gl:2, ia:1, Chinese:2, zh_HK:1, zh-Hans:14, empty:3, hi:5, Japanese:80, pl:57, id:38, or:1, zh_TW:64}

★Click Here to Open This Script 

2014/12/23 選択したアプリケーションの実行ファイルを求める

選択したアプリケーションの実行ファイルを求め、ローカリゼーションの言語種別を求めるAppleScriptです。

AppleScript名:選択したアプリケーションの実行ファイルを求める
– Created 2014-12-23 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set anApp to choose file of type {“com.apple.application-bundle”}
set aURL to (current application’s SMSFord’s URLFrom:anApp)
set aBundle to current application’s NSBundle’s bundleWithURL:aURL

–実行ファイル本体のパスを求める
set theBundlePath to aBundle’s executablePath()
–> “/Applications/Transmit.app/Contents/MacOS/Transmit”

–アプリケーションのローカリゼーションを取得する
set localizationList to aBundle’s localizations()’s ASify() as list
–> {”de”, “English”, “fr”, “French”, “German”, “ja”, “Japanese”}

★Click Here to Open This Script 

2014/12/20 SafariのWebViewのGUI Scripting的な参照を取得する

SafariのWebViewのGUI Scriptingからのオブジェクト参照を取得するAppleScriptです。

Safariで表示したWebコンテンツを操作する方法はいくつかあり、

(1)GUI Scripting経由で操作

(2)JavaScript(not JXA)経由で(do javascriptで)操作

だいたい、この両方を組み合わせて実行することになります。

safari_view_menu.png

ただし(1)は、Safariの各種要素(お気に入りバー、サイドバー、タブバー、ステータスバー)の表示状態によって参照要素番号が変わるため、これらの部品の表示・非表示に左右されないように対応しておく必要があります。

こうした柔軟な処理を行うためのサンプルです。ただし、Safariのメジャーバージョンが上がったような場合まではカバーしていません。GUI Scriptingの宿命で、バージョンが上がったら手直しせずに動くことは期待できません。

特定GUI部品以下のすべてのGUI部品を取得する処理(entire contents)もできなくはないのですが、安定性がいまひとつなのと、実行時間が読めないのと、グループ化されている部品には無意味なのと、動的にメニューを生成しているアプリなどでは危ないので避けています。

AppleScript名:SafariのWebViewのGUI Scripting的な参照を取得する
– Created 2014-12-20 by Takaaki Naganoya
– 2014 Piyomaru Software

set aRes to retSafariHTMLViewReference() of me
if aRes = false then return

tell aRes
  properties
end tell
–> {minimum value:missing value, orientation:missing value, position:{0, 85}, class:UI element, role description:”HTML コンテンツ”, accessibility description:”AS Hole(AppleScriptの穴) By Piyomaru Software”, focused:false, title:”", size:{900, -25413}, value:”", help:”", enabled:true, maximum value:missing value, role:”AXWebArea”, entire contents:{}, subrole:missing value, selected:false, name:missing value, description:”AS Hole(AppleScriptの穴) By Piyomaru Software”}

–SafariのWebViewへのGUI Scripting的な参照を取得する
on retSafariHTMLViewReference()
  tell application “System Events”
    tell process “Safari”
      
      
set wList to every window
      
set wLen to length of wList
      
if wLen = 0 then return false
      
      
tell window 1
        repeat with i from 1 to 10 –Usually from 1 to 5
          try
            tell group i
              tell UI element 1 of scroll area 1 of group 1 of group 1
                set aProp to role
                
if aProp = “AXWebArea” then
                  return (UI element 1 of scroll area 1 of group 1 of group 1 of group i of window 1 of process “Safari” of application “System Events”)
                end if
              end tell
            end tell
          end try
          
        end repeat
      end tell
      
    end tell
  end tell
end retSafariHTMLViewReference

★Click Here to Open This Script 

2014/12/20 JXAでAppleScriptを実行する

OS XのJavaScript for Automation(JXA)でAppleScriptを実行するJavaScriptです。

JavaScript名:JXAでAppleScriptを実行する
var app = Application.currentApplication()
app.includeStandardAdditions =
true
app.strictParameterType =
false
app.runScript(
’display dialog \"test\"’,{in:’AppleScript’})

★Click Here to Open This Script 

2014/12/19 AppleScriptでJavaScriptを実行する

AppleScriptからJavaScriptを実行する方法がUS AppleのAppleScript Users MLで議論されていたので、いろいろとテストしたうえでまとめてみました。

なお、本AppleScriptは明示的にメインスレッドで実行させるために、Script Editor上で通常の「Command-R」ではなく、「Control-Command-R」で実行する必要があります(この操作を行わないと、サブスレッド上で実行されている旨のエラーが出ます)。ASObjCExplorer 4上で実行する場合には、ウィンドウ左下にある「Run in foreground」のチェックボックスにチェックを入れてから実行します。

(1)run script xxxxx in “OSA-Language Name”

こんな方法で実行できたとは、、、。正直言って驚きました。これまで、OSに複数のOSA言語がインストールされてリリースされたことが(ほとんど)なかったので、他のOSA言語を呼び出すという場面がなかったわけですが、こういう呼び出し方で呼び出せるんですね。リリースノートに書かれているんだかいないんだか。この呼び出し方でJXA(JavaScript for Automation)のScriptをAppleScript側から実行できます。同様に、JXA側からAppleScriptを実行することも可能と思われます。

(2)WebViewを動的に生成してJavaScriptを実行

WebViewを動的に生成して操作する場合には、JavaScriptのコマンドを実行することになります。つまり、Webブラウザ上と同様にJavaScriptを実行できるわけですが、これはJXAではありません。このため、Webブラウザ上で実行できるJavaScriptはこの方法で呼び出せますが、JXAで書かれたScriptを実行することはできません。

(3)JavaScript CoreでJavaScriptを実行

WebKitでJS実行するのと速度差はありません。同様に、ここで実行できるJavaScriptはJXAではありません。

js_table1.png
▲OS X上で実行可能な(主な)JavaScript処理系

AppleScript名:ASでJavaScriptを実行する
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “JavaScriptCore”
use framework “WebKit”

–実行するJavaScriptのテキスト
set jsText to
var app = Application.currentApplication()
app.includeStandardAdditions = true
app.displayDialog(’How are you?’)

–run scriptでAppleScriptからJavaScriptを実行する(JXA:アプリケーション操作可能)
set k to run script jsText in “Javascript” –AppleScriptから一番手軽なJXA呼び出し
log {“1″}

–WebKitでJavaScriptを実行する(高速だが、アプリケーション操作不可)
set theWebView to current application’s WebView’s alloc()’s init()
set x to (theWebView’s stringByEvaluatingJavaScriptFromString:jsText) as text
log {“2″}

–JavaScript CoreでJavaScriptを実行する(高速だが、アプリケーション操作不可)
set theContext to current application’s JSContext’s alloc()’s init()
set theJSValue to theContext’s evaluateScript:jsText
log {“3″}

★Click Here to Open This Script 

2014/12/16 指定ロケールの月名、曜日名を取得する

指定ロケールの月名、曜日名を取得するAppleScriptです。

曜日名などはローカライズされた呼称がけっこう言語ごとに異なるので、重要です。香港のクライアントと仕事をしたときに、1週間の開始曜日が月曜日だったのと、曜日が中国語の(漢字なのに)異なる表記で驚かされました。

OS X自体がサポートしている言語の数だけ月および曜日呼称を取得できるはずですが、話者人口が少ない言語だと英語表記がそのまま表示されるかもしれません。

AppleScript名:指定ロケールの月名、曜日名を取得する
– Created 2014-01-26 Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set mList to monthNames(“ja”) –Japanese
–> {”1月”, “2月”, “3月”, “4月”, “5月”, “6月”, “7月”, “8月”, “9月”, “10月”, “11月”, “12月”}

set mList to monthNames(“zh_CN”) –Chinese
–> {”一月”, “二月”, “三月”, “四月”, “五月”, “六月”, “七月”, “八月”, “九月”, “十月”, “十一月”, “十二月”}

set mList to monthNames(“us”) –US English
–> {”January”, “February”, “March”, “April”, “May”, “June”, “July”, “August”, “September”, “October”, “November”, “December”}

set mList to monthNames(“fr”) –French
–> {”janvier”, “février”, “mars”, “avril”, “mai”, “juin”, “juillet”, “août”, “septembre”, “octobre”, “novembre”, “décembre”}

set mList to monthNames(“ru”) –Russian
–> {”января”, “февраля”, “марта”, “апреля”, “мая”, “июня”, “июля”, “августа”, “сентября”, “октября”, “ноября”, “декабря”}

set dList to dayNames(“ja”) –Japanese
–> {”日曜日”, “月曜日”, “火曜日”, “水曜日”, “木曜日”, “金曜日”, “土曜日”}

set dList to dayNames(“zh_CN”) –Chinese
–> {”星期日”, “星期一”, “星期二”, “星期三”, “星期四”, “星期五”, “星期六”}

set dList to dayNames(“us”) –US English
–> {”Sunday”, “Monday”, “Tuesday”, “Wednesday”, “Thursday”, “Friday”, “Saturday”}

set dList to dayNames(“fr”) –French
–> {”dimanche”, “lundi”, “mardi”, “mercredi”, “jeudi”, “vendredi”, “samedi”}

set dList to dayNames(“ru”) –Russian
–> {”воскресенье”, “понедельник”, “вторник”, “среда”, “четверг”, “пятница”, “суббота”}

on monthNames(aLocStr as string)
  set fm to current application’s NSDateFormatter’s new()
  
fm’s setLocale:(current application’s NSLocale’s localeWithLocaleIdentifier:aLocStr)
  
return fm’s monthSymbols() as list
end monthNames

on dayNames(aLocStr as string)
  set fm to current application’s NSDateFormatter’s new()
  
fm’s setLocale:(current application’s NSLocale’s localeWithLocaleIdentifier:aLocStr)
  
return fm’s weekdaySymbols() as list
end dayNames

★Click Here to Open This Script 

2014/12/16 指定AppleScriptをしらべたり実行したりする

Cocoaの機能を用いて、AppleScript(の書類やテキスト)を実行したり調べたりするAppleScriptです。

日本語として間違った文に見えますが、正しい内容です。

(1)TEST1:ASOCのプログラムをテキストで組み立てて実行
普通のAppleScriptを実行できることは容易に想像できるのですが、OS X 10.10から利用可能になったを機能を使いまくったScriptを渡して実行できるか試してみました。海外でもサンプルコードをチラホラ見かけるのですが、お行儀のよい最低限の内容だったりしていただけません(限界まで切り込んでない感が、、、)。

問題なく実行できるものの、display dialogを実行させたら無視されたりと、制約はあるようです(コマンドラインからosascriptコマンドで実行しているかのよーだ)。choose fileは無視されません。

(2)TEST2:指定ASがコンパイルずみかどうか調べる
コンパイル(中間言語翻訳)ずみかどうかを調べます。

(3)TEST3:指定ASのソースを取得
指定のAppleScriptのソースコードを取得します。実行専用で保存されている場合には取得できません。

AppleScript名:指定AppleScriptの各種属性を取得
– Created 2014-12-16 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”
use framework “OSAKit”

–TEST1: ASOCのプログラムをテキストで組み立てて実行
set asText to
use AppleScript version \”2.4\”
use framework \”Foundation\”
use framework \”ASObjCExtras\”
use framework \”QuartzCore\”
use scripting additions

property PDFDocument : class \”PDFDocument\”

set aHFSPath to (choose file of type {\”com.adobe.pdf\”} with prompt \”ページ数をかぞえるPDFを指定してください\”) as string
set aURL to (current application’s SMSFord’s URLFrom:aHFSPath)

set aPDFdoc to PDFDocument’s alloc()’s initWithURL:aURL
set aRes to aPDFdoc’s pageCount()
say (aRes as string)


set aRes to compileAndExecuteASstring(asText)

–TEST2: 指定AppleScriptがコンパイルずみ(中間コードへの翻訳の意)かどうかをしらべる
set a to (choose file of type {“com.apple.applescript.script”})
set asRes to chkAScompiled(a)
–> true or false

–TEST3: 指定AppleScriptのソースを取得
set asSource to getASsourceFor(a)
–> AppleScriptのソースがテキストで返る

–指定のAppleScriptファイルがコンパイル(構文確認+中間言語化)ずみかどうかを取得する
on chkAScompiled(anAlias as {alias, string})
  set anHFSpath to anAlias as string
  
set aURL to (current application’s SMSFord’s URLFrom:anHFSpath)
  
set asO to current application’s OSAScript’s alloc()’s initWithContentsOfURL:aURL |error|:(missing value)
  
try
    set compF to asO’s isCompiled() as boolean
  on error
    return false
  end try
  
  
return compF
end chkAScompiled

–指定AppleScriptファイルのソースコードを取得する(実行専用Scriptからは取得できない)
– Original Created 2014-02-23 Shane Stanley
on getASsourceFor(anAlias as {alias, string})
  set anHFSpath to anAlias as string
  
set aURL to current application’s SMSFord’s URLFrom:anHFSpath
  
set theScript to current application’s OSAScript’s alloc()’s initWithContentsOfURL:aURL |error|:(missing value)
  
return theScript’s source() as text
end getASsourceFor

–与えられた文字列をAppleScriptとして評価してエラーになるかどうかを返す(falseでエラー)
on compileASstring(asSourceStr as string)
  set aNSString to current application’s NSString’s stringWithString:asSourceStr
  
set asObj to current application’s OSAScript’s alloc()’s initWithSource:aNSString
  
set asRes to asObj’s compileAndReturnError:(missing value)
  
return asRes as boolean
end compileASstring

–与えられた文字列をAppleScriptとして評価して正しければ実行
on compileAndExecuteASstring(asSourceStr as string)
  set aNSString to current application’s NSString’s stringWithString:asSourceStr
  
set asObj to current application’s OSAScript’s alloc()’s initWithSource:aNSString
  
set asRes to asObj’s compileAndReturnError:(missing value)
  
if asRes = false then return false –エラーの場合falseを返す
  
  
set aErrorRec to current application’s NSMutableDictionary
  
asObj’s executeAndReturnError:aErrorRec
  
return aErrorRec’s ASify() as record –Resultだけちょっといただけない
end compileAndExecuteASstring

★Click Here to Open This Script 

2014/12/14 AppleScriptのカラーフォーマットを読み込む

Script Editorで設定したAppleScriptの構文色分けのフォーマットを読み込むAppleScriptです。

as_editor.png
▲Piyomaru Softwareが使用しており、本Blogにも反映させている構文色分け設定。似た色でも個別に識別できるよう、わざわざRGB値を微妙に変えてある

このように、Script Editor上で指定したカラー値(R、G、B)、フォント名、フォントサイズ(point)をまとめて返します。

オリジナルはShaneがAppleScript Users MLに投稿したScript(AppleScript Libraries用)だったのですが、普通のAppleScriptで使いやすいように(若干)書き換えました。

同様の機能を持つルーチンをCocoaの機能を使わずに(数年前に)実現していたのですが、このようにまっとうな方法で調査ができるのであれば、安心感がアップします。

# 実際に既存のAppleScript(構文カラーリングを検出して、構文要素区分にもとづいた処理を行うもの)に入れ替えてみたら・・・設定値と実際のカラー値が合わないらしくて、そのままでは使えませんでした。もう少し調べてみる必要があるようで、、、

AppleScript名:AppleScriptのカラーフォーマットを読み込む
– Created 2013-11-11 by Shane Stanley
– Changed 2014-12-14 by Takaaki Naganoya
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set cList to getAppleScriptSourceColors()
–> {{redValue:145, greenValue:40, blueValue:144, fontName:”Osaka”, fontSize:12.0}, {redValue:95, greenValue:94, blueValue:94, fontName:”Osaka”, fontSize:12.0}, {redValue:14, greenValue:62, blueValue:251, fontName:”Osaka”, fontSize:12.0}, {redValue:120, greenValue:52, blueValue:203, fontName:”Osaka”, fontSize:12.0}, {redValue:252, greenValue:42, blueValue:28, fontName:”Osaka”, fontSize:12.0}, {redValue:0, greenValue:0, blueValue:0, fontName:”Osaka”, fontSize:12.0}, {redValue:145, greenValue:82, blueValue:17, fontName:”Osaka”, fontSize:12.0}, {redValue:0, greenValue:0, blueValue:0, fontName:”Osaka”, fontSize:12.0}, {redValue:39, greenValue:201, blueValue:201, fontName:”Osaka”, fontSize:12.0}, {redValue:15, greenValue:62, blueValue:251, fontName:”Osaka”, fontSize:12.0}, {redValue:31, greenValue:182, blueValue:252, fontName:”Osaka”, fontSize:12.0}, {redValue:129, greenValue:58, blueValue:217, fontName:”Osaka”, fontSize:12.0}, {redValue:93, greenValue:54, blueValue:146, fontName:”Osaka”, fontSize:12.0}, {redValue:9, greenValue:55, blueValue:187, fontName:”Osaka”, fontSize:12.0}, {redValue:8, greenValue:55, blueValue:187, fontName:”Osaka”, fontSize:12.0}, {redValue:9, greenValue:55, blueValue:186, fontName:”Osaka”, fontSize:12.0}, {redValue:86, greenValue:55, blueValue:189, fontName:”Osaka”, fontSize:12.0}, {redValue:52, greenValue:32, blueValue:99, fontName:”Osaka”, fontSize:12.0}}

–AppleScriptの構文色分けのカラー値をRGBで取得する
on getAppleScriptSourceColors()
  
  
– get the info as a dictionary
  
set thePath to current application’s NSString’s stringWithString:“~/Library/Preferences/com.apple.applescript.plist”
  
set thePath to thePath’s stringByExpandingTildeInPath()
  
set theInfo to current application’s NSDictionary’s dictionaryWithContentsOfFile:thePath
  
  
– extract relevant part and loop through
  
set theArray to (theInfo’s valueForKey:“AppleScriptSourceAttributes”) as list
  
  
set colList to {}
  
  
repeat with i from 1 to count of theArray
    set anEntry to item i of theArray
    
    
set colorData to NSColor of anEntry
    
set theColor to (current application’s NSUnarchiver’s unarchiveObjectWithData:colorData)
    
    
set rVal to round ((theColor’s redComponent()) * 255) rounding down
    
set gVal to round ((theColor’s greenComponent()) * 255) rounding down
    
set bVal to round ((theColor’s blueComponent()) * 255) rounding down
    
    
    
set fontData to NSFont of anEntry
    
set theFont to (current application’s NSUnarchiver’s unarchiveObjectWithData:fontData)
    
    
set aFontName to theFont’s displayName() as text
    
set aFontSize to theFont’s pointSize()
    
    
set aColRec to {redValue:rVal, greenValue:gVal, blueValue:bVal, fontName:aFontName, fontSize:aFontSize}
    
    
set the end of colList to aColRec
  end repeat
  
  
return colList
end getAppleScriptSourceColors

★Click Here to Open This Script 

2014/12/12 バージョン番号文字列からメジャーバージョンを求める v2v3

バージョン番号文字列(”10.10.1”とか”4.1.1”とか)から、メジャーバージョン番号の数値(10とか4とか)を求めるAppleScriptです。

最初のバージョンでは、アプリケーション名称(display name)と本当の名称(プロセス名)が異なるものについては、AppleScript側から「これはなんですか?」といちいち聞かれていたので、nameではなくBundle IDを取得してバージョン番号を求めるようにしてみました(Safariだけ確認したかったので、最初のバージョンは手抜き感が、、、visible processのみ処理していたのは、手抜きゆえに ^ー^;;)。

しかも、本ルーチン・・・作っていたシステムで「バージョンを確認しなくてもいい処理方法」を見つけたので、おクラ入りしたモノだったり。いつか、使うことになる日もくるでしょう、きっと。

AppleScript名:バージョン番号文字列からメジャーバージョンを求める v2
tell application “System Events”
  set bList to bundle identifier of every process whose visible is true
  
set nList to name of every process whose visible is true
end tell

set nnList to somelistAdd({nList, bList}) of me

set vList to {}
repeat with i in nnList
  set {aName, aBundleID} to contents of i
  
tell application id aBundleID
    set aVerStr to version
  end tell
  
set the end of vList to {aName, getMajorVersionNum(aVerStr) of me}
end repeat

return vList
–> {{”Finder”, 10}, {”Activity Monitor”, 10}, {”DragThing”, 5}, {”Keynote”, 6}, {”Safari”, 8}, {”Calendar”, 8}, {”Mail”, 8}, {”ASObjC Explorer 4″, 4}, {”iTunes”, 12}, {”Script Editor”, 2}, {”UI Browser”, 2}, {”Skim”, 1}}

–バージョンNo文字列のメジャー番号を取得する
on getMajorVersionNum(vString)
  set aPos to offset of “.” in vString
  
if aPos = 0 then
    set vStr to vString
  else
    set vStr to text 1 thru (aPos - 1) of vString
  end if
  
  
try
    set vNum to vStr as number
  on error
    return false
  end try
  
  
return vNum
end getMajorVersionNum

–複数のリストを合成
–すべて同じ要素数である必要がある。また、複数のリストを{aList,bList,cList}とペアにして
–本ルーチンに渡す必要がある
on somelistAdd(aList)
  set aCount to count every item of aList
  
–> 3
  
  
–渡された入れ子リスト内の各要素の数が同じかどうかチェック
  
set countList to {}
  
repeat with i from 1 to aCount
    set bCount to count every item of item i of aList
    
set the end of countList to bCount
  end repeat
  
–> {3, 3, 3}
  
set {aResF, aResN} to retSameEveryItem(countList) of me
  
if aResF = false then return –合成する要素内の要素数が合っていなかったら処理を行わない
  
  
–合成を行う
  
set newList to {}
  
repeat with i from 1 to aResN
    set anItem to {}
    
repeat with ii from 1 to aCount
      set jj to contents of (item i of item ii of aList)
      
set the end of anItem to jj
    end repeat
    
set the end of newList to anItem
  end repeat
  
  
return newList
end somelistAdd

–リスト中の全要素が同じかどうかをbooleanで返す
on retSameEveryItem(aList)
  set a to item 1 of aList
  
set aList to rest of aList
  
set aFlag to true
  
repeat with i in aList
    if a is not equal to contents of i then
      set aFlag to false
      
exit repeat
    end if
  end repeat
  
return {aFlag, a} –Booleanとそのデータを返す
end retSameEveryItem

★Click Here to Open This Script 

で、Shaneからツッコミがあって「こっちのほうがいいよ」と送ってもらったのがこちらです(^ー^;

AppleScript名:バージョン番号文字列からメジャーバージョンを求める v3
– Created 2014-12-12 by Shane Stanley
– 2014 Shane Stanley
use AppleScript version “2.3.1″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set theProcesses to current application’s NSWorkspace’s sharedWorkspace()’s runningApplications() as list
set theResult to {}

repeat with aProcess in theProcesses
  set anNSURL to aProcess’s bundleURL()
  
  
if anNSURL is not missing value then
    set theNSBundle to (current application’s NSBundle’s bundleWithURL:anNSURL)
    
    
if theNSBundle is not missing value then
      set theVersionString to ((theNSBundle’s infoDictionary())’s objectForKey:“CFBundleShortVersionString”)
      
      
if theVersionString is not missing value then
        set end of theResult to {aProcess’s localizedName() as text, (theVersionString’s componentsSeparatedByString:“.”)’s firstObject()’s integerValue()}
      end if
      
    end if
  end if
  
end repeat

return theResult

–> {{”loginwindow”, 9}, {”SystemUIServer”, 1}, {”Dock”, 1}, {”AirPlayUIAgent”, 2}, {”Finder”, 10}, {”Spotlight”, 1}, {”com.apple.internetaccounts”, 1}, {”CoreServicesUIAgent”, 134}, {”com.apple.dock.extra”, 1}, {”日本語入力プログラム”, 5}, {”アクティビティモニタ”, 10}, {”Folder Actions Dispatcher”, 1}, {”", 1}, {”QuickRes”, 4}, {”TISwitcher”, 1}, {”通知センター”, 1}, {”Keychain Circle Notification”, 1}, {”DragThing”, 5}, {”Colors for Hue”, 1}, {”EventScripts”, 1}, {”AAM Updates Notifier”, 8}, {”iCloud フォト”, 2}, {”AdobeIPCBroker”, 5}, {”universalAccessAuthWarn”, 1}, {”Wi-Fi”, 1}, {”System Events”, 1}, {”DragThing Helper”, 1}, {”Core Sync”, 1}, {”LaterAgent”, 1}, {”メール”, 8}, {”Mail Web コンテンツ”, 10600}, {”com.apple.MailServiceAgent”, 8}, {”AppleSpell.service”, 2}, {”Mail Web コンテンツ”, 10600}, {”Safari”, 8}, {”Safari Web コンテンツ”, 10600}, {”Safari Networking”, 10600}, {”スクリプトエディタ”, 2}, {”ASObjC Explorer 4″, 4}, {”ASObjCExplorerUIRunner”, 1}}

★Click Here to Open This Script 

2014/12/11 バージョン番号文字列からメジャーバージョンを求める

バージョン番号文字列(”10.10.1”とか”4.1.1”とか)から、メジャーバージョン番号の数値(10とか4とか)を求めるAppleScriptです。

バージョン番号のような文字列を考慮して、数値に変換する機能がAppleScriptに搭載されていたような気がしていたのですが、Mac OS X 10.4のときに「considering numeric strings」が搭載された、ということでした。仕方なく作ることに。

AppleScript名:considering numeric strings
set aVer to “10.10.1″
set bVer to “10.9.5″

–OS X 10.4で導入されたconsidering numeric strings
considering numeric strings
  if aVer > bVer then
    display dialog “Yosemite is newer than Mavericks”
  else
    display dialog “Whoa!”
  end if
end considering

if aVer > bVer then
  display dialog “Yosemite is newer than Mavericks”
else
  display dialog “Whoa!”
end if

★Click Here to Open This Script 

とりあえず、可視状態にあるプロセスのバージョン文字列を求めて、メジャーバージョンを求めます。

AppleScript名:バージョンNo文字列のメジャー番号を取得する
tell application “System Events”
  set nList to name of every process whose visible is true
end tell

set vList to {}
repeat with i in nList
  set j to contents of i
  
tell application j
    set aVerStr to version
  end tell
  
set the end of vList to {j, getMajorVersionNum(aVerStr) of me}
end repeat

return vList
–> {{”Finder”, 10}, {”Activity Monitor”, 10}, {”DragThing”, 5}, {”Keynote”, 6}, {”Safari”, 8}, {”Calendar”, 8}, {”Mail”, 8}, {”ASObjC Explorer 4″, 4}, {”iTunes”, 12}, {”Script Editor”, 2}, {”UI Browser”, 2}, {”Skim”, 1}}

–バージョンNo文字列のメジャー番号を取得する
on getMajorVersionNum(vString)
  set aPos to offset of “.” in vString
  
if aPos = 0 then
    set vStr to vString
  else
    set vStr to text 1 thru (aPos - 1) of vString
  end if
  
  
try
    set vNum to vStr as number
  on error
    return false
  end try
  
  
return vNum
end getMajorVersionNum

★Click Here to Open This Script 

2014/12/11 Bluetoothに接続中のデバイス名を取得するv3

Bluetoothに接続中のデバイス名を取得するAppleScriptの修正版です。

v2が「ペアリングされたデバイス名の一覧を取得する」という動作を行っていましたが、実際に接続中かどうかを確認して名称のリストを返すように修正しました。

ただ、

bt10.png

のように、PlayStation 3のコントローラーもつながっている状態なんですが、これだけ名前が返ってきません。

キーボードやマウスほどきちんとサポートされていない雰囲気なので、まーそんなもんなのかな、と思わないではないですが、まだいまひとつな感じがします。

AppleScript名:Bluetoothに接続中のデバイス名を取得するv3
– Created 2014-12-09 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “IOBluetooth”

set aList to currentBluetoothDeviceNames(“displayName”) –Bluetoothメニューに出てくる名称
–> {”Piyomaru Mouse”, “Takaaki Naganoya のキーボード”, “AirTurn105v128-E36E”}

set bList to currentBluetoothDeviceNames(“realName”) –ほんまもんの名称
–> {”System Administrator’s Mouse”, “Takaaki Naganoya のキーボード”, “AirTurn105v128-E36E”}

—Bluetoothで接続中のデバイス名をリストで返す
on currentBluetoothDeviceNames(aParam)
  
  
–Bluetoothでペアリングされたデバイス一覧を取得
  
set bList to current application’s IOBluetoothDevice’s pairedDevices() –ペアリングしたデバイスを取得
  
set cList to bList as list
  
  
–デバイス名称を実際に取得  
  
set dNameList to {}
  
  
repeat with i in cList
    set j to contents of i
    
set aName to |name| of j
    
    
set aConnect to j’s isConnected() –接続中かどうかを確認
    
if aConnect = true then
      
      
if aParam = “displayName” then
        set dName to “”
        
try
          set dName to |displayName| of j
        end try
        
        
if dName is not equal to “” then
          copy dName to aName
        end if
      end if
      
      
set the end of dNameList to aName as string
      
    end if
    
  end repeat
  
  
return dNameList
  
end currentBluetoothDeviceNames

★Click Here to Open This Script 

2014/12/11 NSSoundで音声を再生 v2

NSSoundで音声を再生するAppleScriptのバージョンアップ版です。

警告音のNSSoundオブジェクトをlistに入れ、NSArrayに変換したあとで一斉にplay()のメッセージを送って再生させています。

この件に関して、やりたいと思っていた処理がだいたいできました。

AppleScript名:NSSoundで音声を再生 v2
– Created 2014-12-11 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”
use framework “AppKit”

set aList to {“Basso”, “Blow”, “Bottle”, “Frog”, “Funk”, “Glass”, “Hero”, “Morse”, “Ping”, “Pop”, “Purr”, “Sosumi”, “Submarine”, “Tink”}

set sList to {}
repeat with i in aList
  set j to contents of i
  
set aSound to (current application’s NSSound’s soundNamed:j)
  
set the end of sList to aSound
end repeat

set aArray to current application’s SMSFord’s Cocoaify:sList

aArray’s makeObjectsPerformSelector:“play” withObject:0

★Click Here to Open This Script 

2014/12/10 NSSoundで音声を再生

NSSoundを用いて、音声を再生するAppleScriptです。

こういうのを大量に作って、NSArrayか何かに入れて同時にplay()させてみたいのですが、こういうてきとーでどーでもいい実装になってしまいました。まだいろいろ調べています。

複数のサウンドをいっぺんに鳴らすと、ただbeepで単音を鳴らすよりもインパクトがあります。

AppleScript名:NSSoundで音声を再生
– Created 2014-12-10 Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "ASObjCExtras"
use framework "AppKit"

set aSound to current application’s NSSound’s soundNamed:"Glass"
set bSound to current application’s NSSound’s soundNamed:"Frog"
set cSound to current application’s NSSound’s soundNamed:"Basso"
set dSound to current application’s NSSound’s soundNamed:"Blow"
set eSound to current application’s NSSound’s soundNamed:"Hero"
set fSound to current application’s NSSound’s soundNamed:"Ping"

ignoring application responses
  aSound’s play()
  
bSound’s play()
  
cSound’s play()
  
dSound’s play()
  
eSound’s play()
  
fSound’s play()
end ignoring

★Click Here to Open This Script 

2014/12/09 Bluetoothに接続中のデバイス名を取得するv2

Bluetoothに接続中のデバイス名を取得するAppleScriptの修正版です。

最初のバージョンでは、「recentDevices:」でBluetoothデバイスの接続ヒストリーから過去の履歴を取得して、接続ないしは接近したことのあるものだけを取り出していました。

本バージョンでは、「pairedDevices()」でMac本体とペアリングしたBluetoothデバイス名を取得します。本プログラムの名称を正しく表現すると「Bluetoothにペアリングしたデバイス名一覧を取得」ということになるでしょう。

また、実際にBluetoothデバイスが名乗っている名前(realName)と、名称変更して付けられた名前(Bluetoothデバイス一覧に出てくる名前、displayName)のどちらを取得するかを指定できるようにしました。

まだ、いろいろ試行錯誤中です。

bt1.png

bt2.png

AppleScript名:Bluetoothに接続中のデバイス名を取得するv2
– Created 2014-12-09 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “IOBluetooth”

set aList to currentBluetoothDeviceNames(“displayName”) –Bluetoothメニューに出てくる名称
–> {”Piyomaru Mouse”, “Piyomaru iPad mini”, “Takaaki Naganoya のキーボード”, “Piyomaru iPhone”, “AirTurn105v128-E36E”, “Beats Wireless”}

set bList to currentBluetoothDeviceNames(“realName”) –ほんまもんの名称
–> {”System Administrator’s Mouse”, “Piyomaru iPad mini”, “Takaaki Naganoya のキーボード”, “Piyomaru iPhone”, “AirTurn105v128-E36E”, “Beats Wireless”}

—Bluetoothで接続中のデバイス名をリストで返す
on currentBluetoothDeviceNames(aParam)
  
  
–Bluetoothでペアリングされたデバイス一覧を取得
  
set bList to current application’s IOBluetoothDevice’s pairedDevices() –ペアリングしたデバイスを取得
  
set cList to bList as list
  
  
–デバイス名称を実際に取得  
  
set dNameList to {}
  
  
repeat with i in cList
    set j to contents of i
    
    
set aName to |name| of j
    
    
if aParam = “displayName” then
      set dName to “”
      
try
        set dName to |displayName| of j
      end try
      
      
if dName is not equal to “” then
        copy dName to aName
      end if
    end if
    
    
set the end of dNameList to aName as string
    
  end repeat
  
  
return dNameList
  
end currentBluetoothDeviceNames

★Click Here to Open This Script 

2014/12/09 CoreImageでフィルタしまくり

指定画像に対して、CoreImageのFilterをかけまくるAppleScriptです。オリジナルの画像は破壊せず、

 「オリジナルファイル名」_「Filter名」.jpg

の画像を(オリジナル画像と同じフォルダ内に)生成します。オリジナルはbadcharanさんのScriptですが、いろいろ(どうでもいいところを)書き換えました。主に、AppleScript Libraries用だったものを、Yosemite以降の普通のAppleScriptのフォーマットにして、ASObjCExtrasの機能を用いて簡略化。

OS X上で実行可能なフィルタのうち、リストの上の方から(いやになるまで)順にテストしてみました。Transitionなどの明らかにアニメーション用と思われるもの以外はほとんど実行可能なようです。

ただし、各フィルタのパラメータはデフォルトのままなので、各フィルタごとのパラメータを明示的に指定するような処理を付加すべきところでしょう。

現在、本Blogに画面キャプチャを掲載するさいに、Photoshopでぼかしフィルタをかけてからリサイズし、リサイズ後にシャープフィルタをかけています(びみょ〜に、JavaScriptでWebブラウザ上でサムネールを生成させたほうがいいような気もしないではないですが)。かような処理をPhotoshopなしで行う際に、このような機能が利用できることでしょう。

AppleScript名:CoreImageでフィルタしまくり
– Created 2014-12-09 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”
use framework “QuartzCore”

–CIFilters (Not everything!!) From Apple’s Core Image Filter Reference
set ciList to {“CIColorInvert”, “CIBoxBlur”, “CIDiscBlur”, “CIGaussianBlur”, “CIMedianFilter”, “CIMotionBlur”, “CINoiseReduction”, “CIZoomBlur”, “CIColorMonochrome”, “CIColorPosterize”, “CIPhotoEffectChrome”, “CISepiaTone”, “CISharpenLuminance”, “CIUnsharpMask”, “CIKaleidoscope”}

–画像を選択
set aPath to choose file of type {“public.image”}

–すべてのフィルタを実行
repeat with i in ciList
  set j to contents of i
  
set aRes to convAsFilteredJPEG(aPath, j) of me
end repeat

–CIFilterをかけたJPEG画像を生成
–参照:http://ashplanning.blogspot.jp/ のうちのどこか
on convAsFilteredJPEG(aPath, aFilterName)
  
  
–aliasをURL(input)とPOSIX path(output) に変換
  
set aURL to (current application’s SMSFord’s URLFrom:aPath) –Input
  
set aPOSIX to (POSIX path of aPath) & “_” & aFilterName & “.jpg” –Output
  
  
–CIImageを生成
  
set aCIImage to current application’s CIImage’s alloc()’s initWithContentsOfURL:aURL
  
  
– CIFilter をフィルタの名前で生成
  
set aFilter to current application’s CIFilter’s filterWithName:aFilterName
  
aFilter’s setDefaults() –各フィルタのパラメータはデフォルト
  
  
–Filterを実行
  
aFilter’s setValue:aCIImage forKey:“inputImage”
  
set aOutImage to aFilter’s valueForKey:“outputImage”
  
  
– NSBitmapImageRep を CIImage から生成
  
set aRep to current application’s NSBitmapImageRep’s alloc()’s initWithCIImage:aOutImage
  
  
– NSBitmapImageRep から JPEG データを取得
  
set jpegData to aRep’s representationUsingType:(current application’s NSJPEGFileType) |properties|:(missing value)
  
  
– ファイルに保存
  
set fsRes to jpegData’s writeToFile:aPOSIX atomically:true
  
return fsRes as boolean
  
end convAsFilteredJPEG

★Click Here to Open This Script