Archive for the 'NSCountedSet' Category

2017/09/08 iTunes Library上のsongでライブラリへの追加年を集計してKeynote書類上にグラフ作成

iTunesのMusic Libraryで楽曲のライブラリへの追加年で集計を行ってKeynote上にグラフを作成するAppleScriptです。6,775曲が登録されている自分のライブラリで集計→グラフ作成で3秒程度です。

itunes_usic.png

iTunes LibraryへのアクセスをiTunesLibrary framework経由で行い、NSCountedSetで集計を行うことで高速に処理を行ないます。

iTunesの基礎的な操作については電子書籍「iTunes Control」にて、Keynoteのグラフ作成については、電子書籍「Keynote Control with AppleScript」 △脳楮戮望匆陲靴討い泙后6縮がある方はぜひお買い求めください。

年々、楽曲を聴かない&買わなくなっている様子が見てとれますが、ほかの人はどんなもんなのか興味があります。

AppleScript名:iTunes Library上のsongでライブラリへの追加年で集計してKeynote書類上にグラフ作成
– Created 2017-09-06 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “iTunesLibrary”
–http://piyocast.com/as/archives/4816

property NSString : a reference to current application’s NSString
property NSArray : a reference to current application’s NSArray
property NSSortDescriptor : a reference to current application’s NSSortDescriptor
property NSDictionary : a reference to current application’s NSDictionary
property NSCountedSet : a reference to current application’s NSCountedSet
property ITLibrary : a reference to current application’s ITLibrary
property NSMutableArray : a reference to current application’s NSMutableArray

set yRec to retSongAddedYear() of me
–>  {{theName:2005, numberOfTimes:1907}….}

–楽曲のiTunesライブラリへの追加「年」のみ抽出
set yList to ((NSArray’s arrayWithArray:yRec)’s valueForKeyPath:“theName”) as list
–>  {2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017}

–楽曲のiTunesライブラリへの追加「年」ごとのカウントを抽出
set vList to ((NSArray’s arrayWithArray:yRec)’s valueForKeyPath:“numberOfTimes”) as list
–>  {1907, 1125, 853, 319, 638, 353, 351, 241, 605, 344, 71, 76, 28}

tell application “Keynote”
  set adoc to (make new document with properties {document theme:theme “ホワイト”, height:768, width:1024}) –Caution: theme name is **localized** (”White”)
  
tell front document
    set base slide of current slide to master slide “空白” –Caution: master slide name is **localized** (”Blank”)
    
tell current slide
      add chart row names {“ライブラリ追加年”} column names yList data {vList} type vertical_bar_2d group by chart row –row name is “iTunes Library Added Year”
    end tell
  end tell
  
activate
end tell

on retSongAddedYear()
  set library to ITLibrary’s libraryWithAPIVersion:“1.0″ |error|:(missing value)
  
if library is equal to missing value then return {}
  
  
set allTracks to library’s allMediaItems()
  
set allCount to allTracks’s |count|()
  
  
set anEnu to allTracks’s objectEnumerator()
  
set newArray to NSMutableArray’s alloc()’s init()
  
  
repeat
    set aPL to anEnu’s nextObject()
    
if aPL = missing value then exit repeat
    
try
      set aKind to (aPL’s mediaKind) as integer
      
if (aKind as integer) is equal to 2 then –Music, Song
        set pMonth to ((aPL’s addedDate() as date)’s year) as integer
        
newArray’s addObject:(pMonth)
      end if
    on error
      set aLoc to (aPL’s location’s |path|()) as string
    end try
  end repeat
  
  
return countItemsByItsAppearance(newArray) of me
end retSongAddedYear

–出現回数で集計
on countItemsByItsAppearance(aList)
  set aSet to NSCountedSet’s alloc()’s initWithArray:aList
  
set bArray to NSMutableArray’s array()
  
set theEnumerator to aSet’s objectEnumerator()
  
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
bArray’s addObject:(NSDictionary’s dictionaryWithObjects:{aValue, (aSet’s countForObject:aValue)} forKeys:{“theName”, “numberOfTimes”})
  end repeat
  
  
–出現回数(numberOfTimes)で降順ソート
  
set theDesc to NSSortDescriptor’s sortDescriptorWithKey:“theName” ascending:true
  
bArray’s sortUsingDescriptors:{theDesc}
  
  
return bArray as list
end countItemsByItsAppearance

★Click Here to Open This Script 

2017/03/18 文字を集合(CountedSet)に変換して文字列同士の近似度を計算する v2

複数の文字列同士の近似度を擬似的に計算できないかと考えて、文字列をCountedSetに変換して、CountedSet同士でand演算(intersectSet)を実行。その計算結果が大きいほど「文字列中に含まれている文字列の傾向が似ている」と判断するテストのAppleScriptです。

countedset_intersect_resized.png

とりあえずはintersectSetで積集合を計算しています。重複している部分を求めているわけです。

 「This is an apple.」と「This is a pinapple.」–> 11
 「This is a pinapple.」と「Piyomaru San Dayo.」–> 5
 「Piyomaru San Dayo.」と「This is an apple.」–> 5

v1とv2の計算結果を合わせて、両方の傾向を反映させるようにするとよいのかもしれません。

AppleScript名:文字を集合(CountedSet)に変換して文字列同士の近似度を計算する v2
– Created 2017-03-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/4532

set aStr to "This is a pinnapple."
set bStr to "This is an apple."
set cStr to "Piyomaru San Dayo."

set a1Res to getApproximationBetweenStringsIntersect(aStr, bStr) of me
–>  11

set bRes to getApproximationBetweenStringsIntersect(bStr, cStr) of me
–>  5

set cRes to getApproximationBetweenStringsIntersect(cStr, aStr) of me
–>  5

on getApproximationBetweenStringsIntersect(aStr, bStr)
  set aList to current application’s NSMutableArray’s arrayWithArray:(characters of aStr)
  
set bList to current application’s NSMutableArray’s arrayWithArray:(characters of bStr)
  
  
set aIndex to current application’s NSCountedSet’s alloc()’s initWithArray:aList
  
set bIndex to current application’s NSCountedSet’s alloc()’s initWithArray:bList
  
  
aIndex’s intersectSet:bIndex
  
set aRes to aIndex’s allObjects()’s |count|()
  
return aRes
end getApproximationBetweenStringsIntersect

★Click Here to Open This Script 

2017/03/18 文字を集合(CountedSet)に変換して文字列同士の近似度を計算する v1

複数の文字列同士の近似度を擬似的に計算できないかと考えて、文字列をCountedSetに変換して、CountedSet同士で減算を実行。その計算結果が少ないほど「文字列中に含まれている文字列の傾向が似ている」と判断するテストのAppleScriptです。

countedset.png

とりあえずはminusSetで減算を行なっていますが、ほかの方法も試してみたいところです。

本Scriptでは、得られた結果の数値が小さければ重複している文字が多いということで、計算結果そのものにはあまり意味はありませんが、複数の結果を大小比較して、数値が小さいもののペアが「似たような傾向を持つもの」として期待できます。

 「This is an apple.」と「This is a pinapple.」–> 3
 「This is a pinapple.」と「Piyomaru San Dayo.」–> 8
 「Piyomaru San Dayo.」と「This is an apple.」–> 9

ということで、これらの間では「This is an apple.」と「This is a pinapple.」の近似度が一番高いといえることになります。

AppleScript名:文字を集合(CountedSet)に変換して文字列同士の近似度を計算する v1
– Created 2017-03-18 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4530

set aStr to “This is a pinnapple.”
set bStr to “This is an apple.”
set cStr to “Piyomaru San Dayo.”

set a1Res to getApproximationBetweenStrings(aStr, bStr) of me
–>  3

set bRes to getApproximationBetweenStrings(bStr, cStr) of me
–>  8

set cRes to getApproximationBetweenStrings(cStr, aStr) of me
–>  9

on getApproximationBetweenStrings(aStr, bStr)
  set aList to current application’s NSMutableArray’s arrayWithArray:(characters of aStr)
  
set bList to current application’s NSMutableArray’s arrayWithArray:(characters of bStr)
  
  
set aIndex to current application’s NSCountedSet’s alloc()’s initWithArray:aList
  
set bIndex to current application’s NSCountedSet’s alloc()’s initWithArray:bList
  
  
aIndex’s minusSet:bIndex
  
set aRes to aIndex’s allObjects()’s |count|()
  
  
bIndex’s minusSet:aIndex
  
set bRes to bIndex’s allObjects()’s |count|()
  
  
if aRes bRes then
    return bRes
  else
    return aRes
  end if
end getApproximationBetweenStrings

★Click Here to Open This Script 

2017/02/23 リストから重複要素のみを返す v2

Shane Stanleyによる「リストから重複する要素のみを抽出して返すAppleScript」の改良版です。シンプルで美しい処理になっています。

この手の処理でNSSetやNSCountedSetを使うのは「そういうものだろー」とすぐに思いつきますが、「minusSet:」というのはドキュメント中から見つけ切れませんでした。これは深い(^ー^;;。

また、ループ処理や条件判断の処理が入っていないため、自分の書いたバージョンよりも2.3倍ぐらい高速でした(100回実行時の平均)。

AppleScript名:リストから重複要素のみを返す v2
– Created 2017-02-23 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4469

set aList to {“4efa7f9f587f3d1dbc4a1f6ebff92ef5″, “59cd07ea69acc2c9004dc815803cf184″, “5841f4bcc13e85c3b8ba1eafcacd43be”, “dfb393cd3c0eb3f228236367f171cd01″, “59cd07ea69acc2c9004dc815803cf184″, “59cd07ea69acc2c9004dc815803cf184″}
set dList to returnDuplicatesOnly(aList) of me
–> {”59cd07ea69acc2c9004dc815803cf184″}

on returnDuplicatesOnly(aList)
  set countedSet to current application’s NSCountedSet’s alloc()’s initWithArray:aList
  
set simpleSet to current application’s NSSet’s setWithArray:aList
  
countedSet’s minusSet:simpleSet
  
return countedSet’s allObjects() as list
end returnDuplicatesOnly

★Click Here to Open This Script 

2017/02/22 リストから重複要素のみを返す

リストから重複する要素のみを抽出して返すAppleScriptです。

Pure AppleScriptでは単純ループでひたすら「is in」演算子を使って求める処理ですが、リストの要素数が増えた場合(数万項目とか)でも速度が低下しないようにCocoaの機能を使って処理してみました。

AppleScript名:リストから重複要素のみを返す
– Created 2017-02-22 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4468

set aList to {“4efa7f9f587f3d1dbc4a1f6ebff92ef5″, “59cd07ea69acc2c9004dc815803cf184″, “5841f4bcc13e85c3b8ba1eafcacd43be”, “dfb393cd3c0eb3f228236367f171cd01″, “59cd07ea69acc2c9004dc815803cf184″, “59cd07ea69acc2c9004dc815803cf184″}
set dList to returnDuplicatesOnly(aList) of me
–> {”59cd07ea69acc2c9004dc815803cf184″}

–リストから重複要素のみを返す
on returnDuplicatesOnly(aList)
  set aSet to current application’s NSCountedSet’s |set|()
  
aSet’s addObjectsFromArray:aList
  
  
set theEnumerator to aSet’s objectEnumerator()
  
set anArray to current application’s NSMutableArray’s alloc()’s init()
  
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
set aCount to (aSet’s countForObject:aValue) as integer
    
if aCount > 1 then
      anArray’s addObject:aValue
    end if
  end repeat
  
  
return anArray as list
end returnDuplicatesOnly

★Click Here to Open This Script 

2017/01/07 iTunesライブラリの曲のアーティスト名を集計

iTunesライブラリ中に入っている「曲」(song)のアーティスト名を集計するAppleScriptです。

iTunesLibraryフレームワークを用いてライブラリにアクセスしているので、iTunes.appが起動している必要はありません。

アーティスト名については、ライブラリ中にかなりイレギュラーなデータが存在しているため、対策が必要でした。

・初期のiTunesで、CDからリッピングしたものの、CDDBに登録がなかったため、アーティスト名などが登録されていない→ エラートラップで対処

・アーティスト名で姓(last name)と名(first name)の間に空白が入っていたりいなかったりするなど、表記ゆらぎが存在していたため、ゆらぎを吸収

もう少し高速に実行できてもよさそうですが、ライブラリ中のtrackが8,100程度のときに、10回実行時の平均は2.8秒程度です(MacBook Pro Retina 2012)。

AppleScript名:iTunesライブラリの曲のアーティスト名を集計
– Created 2017-01-07 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “iTunesLibrary”
–http://piyocast.com/as/archives/4379

set library to current application’s ITLibrary’s libraryWithAPIVersion:“1.0″ |error|:(missing value)
if library is equal to missing value then return

set allTracks to library’s allMediaItems()
set allCount to allTracks’s |count|()

set anEnu to allTracks’s objectEnumerator()
set newArray to current application’s NSMutableArray’s alloc()’s init()

repeat
  set aPL to anEnu’s nextObject()
  
if aPL = missing value then exit repeat
  
try
    set aKind to (aPL’s mediaKind) as integer
    
    
if (aKind as integer) is equal to 2 then –Music, Song
      set plName to aPL’s artist’s |name| as string
      
set pl2Name to (my changeThis:” “ toThat:“” inString:plName) –日本語アーティスト名で姓と名の間にスペースが入っているものがある(表記ゆらぎ)ので対策
      
newArray’s addObject:(pl2Name)
    end if
  on error
    set aLoc to (aPL’s location’s |path|()) as string
    
log aLoc
  end try
end repeat

set aRes to countItemsByItsAppearance(newArray) of me
–>  {{theName:”浜田省吾”, numberOfTimes:442}, {theName:”B’z”, numberOfTimes:379}, {theName:”渡辺岳夫・松山祐士”, numberOfTimes:199}, {theName:”VariousArtists”, numberOfTimes:192}, {theName:”菅野よう子”, numberOfTimes:108}, {theName:”布袋寅泰”, numberOfTimes:100}, {theName:”三枝成彰”, numberOfTimes:95}, {theName:”宇多田ヒカル”, numberOfTimes:94}, {theName:”宮川泰”, numberOfTimes:81}, {theName:”MichaelJackson”, numberOfTimes:78}, {theName:”稲葉浩志”, numberOfTimes:73}, …

–出現回数で集計
on countItemsByItsAppearance(aList)
  set aSet to current application’s NSCountedSet’s alloc()’s initWithArray:aList
  
set bArray to current application’s NSMutableArray’s array()
  
set theEnumerator to aSet’s objectEnumerator()
  
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
bArray’s addObject:(current application’s NSDictionary’s dictionaryWithObjects:{aValue, (aSet’s countForObject:aValue)} forKeys:{“theName”, “numberOfTimes”})
  end repeat
  
  
–出現回数(numberOfTimes)で降順ソート
  
set theDesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“numberOfTimes” ascending:false
  
bArray’s sortUsingDescriptors:{theDesc}
  
  
return bArray as list
end countItemsByItsAppearance

on changeThis:findString toThat:repString inString:someText
  set theString to current application’s NSString’s stringWithString:someText
  
set theString to theString’s stringByReplacingOccurrencesOfString:findString withString:repString options:(current application’s NSRegularExpressionSearch) range:{location:0, |length|:length of someText}
  
return theString as text
end changeThis:toThat:inString:

★Click Here to Open This Script 

2016/11/02 iTunesライブラリ中の楽曲のジャンルを集計して多い順にソートして出力

iTunesライブラリ中の楽曲のジャンルを集計して多い順にソートして返すAppleScriptです。

集計部分をCocoaで行っているので、集計部分自体が相当に高速なのですが、手元のMacBook Pro Retina 2012(Core i7 2.6GHz)で音声データが6,861件iTunesに登録されている環境で処理したところ、

  アプリケーション(iTunes)を直接呼び出し:3.5秒
  すべてフレームワーク経由で処理:0.03秒

と、アプリケーションを直接操作しないでフレームワーク経由で楽曲の情報を取得して処理するとアプリケーションへの問い合わせを行うよりも100倍以上高速です。

iTunes Music Library.xmlを読み取って処理してもだいたいフレームワーク経由の処理と同じかやや高速なぐらいに落ち着くと思われます。
補足:XML経由の処理はAppleScriptだと(Cocoaの機能を使っても)ムチャクチャ時間がかかりました。参考までに

ただ、処理結果が方法によってほんの微妙に変わるのはジャンル判定処理が異なるためでしょうか。やや、気になります。

GUIアプリ経由で情報を取得するのと、フレームワークを呼び出して情報を取得するのと、XMLを自力で解析するのとでは、提供してくれる機能が違うので、用途に応じて適切なものを取捨選択することになります。「現在再生中の曲」「曲リスト中で選択中のもの」といった情報が必要な場合にはGUIアプリ(iTunes)に問い合わせるのが正解です。

AppleScript名:iTunesライブラリ中の楽曲のジャンルを集計して多い順にソートして出力(アプリケーション呼び出し)
– Created 2016-10-30 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4301

tell application “iTunes”
  –set aList to genre of (every file track whose media kind is equal to song and genre is not equal to “”)
  
set aList to genre of every file track
end tell

set aRes to countItemsByItsAppearance(aList) of me
return aRes
–> {{theName:”サウンドトラック”, numberOfTimes:1721}, {theName:”ロック”, numberOfTimes:942}, {theName:”クラシック”, numberOfTimes:539},

–ジャンルのリストを出現回数で集計
on countItemsByItsAppearance(aList)
  set aSet to current application’s NSCountedSet’s alloc()’s initWithArray:aList
  
set bArray to current application’s NSMutableArray’s array()
  
set theEnumerator to aSet’s objectEnumerator()
  
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
bArray’s addObject:(current application’s NSDictionary’s dictionaryWithObjects:{aValue, (aSet’s countForObject:aValue)} forKeys:{“theName”, “numberOfTimes”})
  end repeat
  
  
–出現回数(numberOfTimes)で降順ソート
  
set theDesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“numberOfTimes” ascending:false
  
bArray’s sortUsingDescriptors:{theDesc}
  
  
return bArray as list
end countItemsByItsAppearance

★Click Here to Open This Script 

AppleScript名:iTunesライブラリ中の楽曲のジャンルを集計して多い順にソートして出力(フレームワーク呼び出し)
– Created 2016-11-02 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “iTunesLibrary”
–http://piyocast.com/as/archives/4301

set library to current application’s ITLibrary’s libraryWithAPIVersion:“1.0″ |error|:(missing value)
if library is equal to missing value then return

set playLists to library’s allPlaylists()
set gArray to library’s allMediaItems()’s genre
set aRes to countItemsByItsAppearance(gArray) of me
–> {{theName:”サウンドトラック”, numberOfTimes:1722}, {theName:”ロック”, numberOfTimes:956}, {theName:”Podcast”, numberOfTimes:755},

–ジャンルのリストを出現回数で集計
on countItemsByItsAppearance(aList)
  set aSet to current application’s NSCountedSet’s alloc()’s initWithArray:aList
  
set bArray to current application’s NSMutableArray’s array()
  
set theEnumerator to aSet’s objectEnumerator()
  
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
bArray’s addObject:(current application’s NSDictionary’s dictionaryWithObjects:{aValue, (aSet’s countForObject:aValue)} forKeys:{“theName”, “numberOfTimes”})
  end repeat
  
  
–出現回数(numberOfTimes)で降順ソート
  
set theDesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“numberOfTimes” ascending:false
  
bArray’s sortUsingDescriptors:{theDesc}
  
  
return bArray as list
end countItemsByItsAppearance

★Click Here to Open This Script 

2015/09/15 NSCountedSetでNSDictionaryの登場頻度集計

Cocoaの機能を用いて、recordの登場頻度集計を行うAppleScriptです。

{{aName:”Piyomaru”, aFavorite:”Sleeping”}, {aName:”Piyoko”, aFavorite:”TV”}, {aName:”Piyomaru”, aFavorite:”Sleeping”}}

というレコード(のリスト)があった場合に、この各要素、

  {aName:”Piyomaru”, aFavorite:”Sleeping”}
  {aName:”Piyoko”, aFavorite:”TV”}

のそれぞれの出現頻度のカウントを行ってみました。Pure AppleScriptのrecordだと、

  {ラベル:属性値}

のような構成で、「ラベル」の部分はテキスト(かつ、AppleScript処理系の持っている予約語、▲▲廛螢院璽轡腑鵑陵縮鷂譟↓Script Editorの予約語・・などなど(OSAXの予約語もあったかも)とかぶらないもの)である必要がありますが、NSDictionaryでは「ラベル」の部分にオブジェクトを指定できるため、

  {{aName:”Piyoko”, aFavorite:”TV”}:属性値}

のようなPure AppleScriptからすると無茶な構造のデータも持てるわけで、

strangedict.png

それならばNSCountedSetでrecordの登場頻度の集計も普通に行えるだろう、と考えてためしてみました。

昨日の段階でNSCountedSetにrecordを突っ込んで集計らしきものはできていたのですが、集計データの取り出しに失敗(おしい!)。一晩ぐっすり寝たら翌朝問題なくできました。

{​​​​​{​​​​​​​aCount:1, ​​​​​​​aData:{​​​​​​​​​aName:”Piyoko”, ​​​​​​​​​aFavorite:”TV”​​​​​​​}​​​​​}, ​​​​​{​​​​​​​aCount:2, ​​​​​​​aData:{​​​​​​​​​aName:”Piyomaru”, ​​​​​​​​​aFavorite:”Sleeping”​​​​​​​}​​​​​}​​​}

という結果を出力します。

AppleScript名:NSCountedSetでNSDictionaryの頻度集計 v2
– Created 2015-09-14 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aRecList to {{aName:“Piyomaru”, aFavorite:“Sleeping”}, {aName:“Piyoko”, aFavorite:“TV”}, {aName:“Piyomaru”, aFavorite:“Sleeping”}}
set aCountedList to countEachRecord(aRecList)
–>  (NSArray) {​​​​​{​​​​​​​aCount:1, ​​​​​​​aData:{​​​​​​​​​aName:”Piyoko”, ​​​​​​​​​aFavorite:”TV”​​​​​​​}​​​​​}, ​​​​​{​​​​​​​aCount:2, ​​​​​​​aData:{​​​​​​​​​aName:”Piyomaru”, ​​​​​​​​​aFavorite:”Sleeping”​​​​​​​}​​​​​}​​​}

on countEachRecord(aRecList)
  set theCountedSet to current application’s NSCountedSet’s |set|()
  
repeat with i in aRecList
    set j to contents of i
    (
theCountedSet’s addObject:j)
  end repeat
  
  
set theEnumerator to theCountedSet’s objectEnumerator()
  
set anArray to current application’s NSMutableArray’s alloc()’s init()
  
  
repeat
    set aDict to current application’s NSMutableDictionary’s alloc()’s init()
    
    
set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
    
set aCount to theCountedSet’s countForObject:aValue
    
    
aDict’s setObject:aCount forKey:“aCount”
    
aDict’s setObject:aValue forKey:“aData”
    
anArray’s addObject:aDict
  end repeat
  
  
return anArray
end countEachRecord

★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