macOSには単語が冒涜的かどうかを識別する仕組みがある
開発者のヘルゲ・スヴェレ氏はmacOSのプライベートフレームワーク「Lexicon.framework」にアクセスし、Appleのシステム辞書が単語をどのように分類し、それがキーボードの予測変換や自動修正にどのように影響するかを調査しました。
I Dumped Apple's Fucking Word List - Helge Sverre
https://helgesver.re/articles/macos-vulgar-word-flags
macOSには「~/Library/Keyboard/VulgarWordUsage.db」というデータベースが存在し、ユーザーが「どのアプリで」「誰に対して」「どのような」冒涜的な言葉を使用しているかを記録しています。データベースのスキーマは以下の通りでした。
$ sqlite3 -readonly ~/Library/Keyboard/VulgarWordUsage.db .schema
CREATE TABLE properties (ROWID INTEGER PRIMARY KEY, key, value, UNIQUE (key));
CREATE TABLE IF NOT EXISTS "vword_usage" (
ROWID INTEGER PRIMARY KEY AUTOINCREMENT,
app TEXT, -- app identifier
recipient TEXT, -- recipient identifier
vword TEXT, -- vulgar word
word_reading TEXT, -- the reading form of a word (relevant for Japanese)
usage_count INTEGER DEFAULT 0,
last_use_timestamp REAL DEFAULT 0, -- last use timestamp (seconds since the reference date)
journaled
);
CREATE UNIQUE INDEX app_recipient_vword_word_reading_idx ON vword_usage(app, recipient, vword, word_reading);テーブルレイアウトからは、例えば「fuck」という単語が「友人に向けたメッセージ」で使われたのか「上司へのメール」で使われたのかを識別できるということが読み取れます。また ユニークインデックスが「アプリ・受信者・冒涜的単語・単語の読み」となっていることから、これらの情報を「粒度」として扱うことがわかります。しかしスヴェレ氏のMacではこのデータベースは空であり、ネイティブアプリで冒涜的な言葉を入力しても記録されることはありませんでした。また上記のテーブルレイアウトでは単語が冒涜的であるかどうかを知ることができず、他の場所で既に冒涜的な言葉としてマークされている単語をカウントするしかありません。
スヴェレ氏は手掛かりを求めて dyldの共有キャッシュで「vulgar」の文字列を検索してみたところ、「LX」プレフィックスを持つAPI「LXVulgarWordUsageDatabaseCreate」「LXVulgarWordUsageDatabaseCanAutocorrect」などを見つけました。
さらに調査を進めた結果、辞書エントリに関する2つの関数を見つけることができました。
_LXEntryGetCategoryFlags
_LXEntryGetMetaFlags
「LX」は「Lexicon.framework」を意味し、「/System/Library/LinguisticData/」の下で言語ごとの辞書を読み込むプライベートフレームワークです。辞書に含まれるすべての単語は、32ビットのフラグフィールドを2つ持つ「LXEntry」構造体と対応しており、単語がどれだけ冒涜的なのかをどこかに保持しているとすればLXEntryのビット中ということになります。ビットにアクセス可能な公開APIは存在していませんでしたが、C関数はエクスポートされておりCoreFoundationの規約に従っていたため、スヴェレ氏はシグネチャを推測しフラグを読み取るプログラムを作成しました。
// lx.c — dump every word in a system lexicon with its flags.
// clang -O1 -o lx lx.c -F/System/Library/PrivateFrameworks -framework Lexicon -framework CoreFoundation
#include <CoreFoundation/CoreFoundation.h>
#include <stdbool.h>
#include <stdio.h>
typedef const void *LXLexiconRef, *LXCursorRef, *LXEntryRef;
extern CFStringRef kLXLexiconLocaleKey;
LXLexiconRef LXLexiconCreate(CFDictionaryRef options, CFErrorRef *error);
LXCursorRef LXLexiconCreateRootCursor(LXLexiconRef);
void LXCursorEnumerateEntriesRecursively(LXCursorRef, void (^)(LXEntryRef, bool *stop));
CFStringRef LXEntryCopyString(LXEntryRef);
uint32_t LXEntryGetCategoryFlags(LXEntryRef);
uint32_t LXEntryGetMetaFlags(LXEntryRef);
int main(int argc, char **argv) {
CFStringRef locale = CFStringCreateWithCString(NULL, argc > 1 ? argv[1] : "en_US", kCFStringEncodingUTF8);
const void *keys[] = {kLXLexiconLocaleKey}, *values[] = {locale};
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
LXLexiconRef lexicon = LXLexiconCreate(options, NULL);
if (!lexicon) return 1;
LXCursorEnumerateEntriesRecursively(LXLexiconCreateRootCursor(lexicon), ^(LXEntryRef entry, bool *stop) {
char word[512] = "";
CFStringRef s = LXEntryCopyString(entry);
if (s) { CFStringGetCString(s, word, sizeof word, kCFStringEncodingUTF8); CFRelease(s); }
printf("%08x\t%08x\t%s\n", LXEntryGetCategoryFlags(entry), LXEntryGetMetaFlags(entry), word);
});
}
上記のプログラムは指定したロケールの辞書のすべてのエントリーを走査し、LXEntryの2つのフラグフィールドを出力します。
$ ./lx en_US | wc -l
199019
$ ./lx en_US | awk -F'\t' '$3 == "fuck" || $3 == "table" || $3 == "Trump"'
00000000 00800002 fuck
00000000 00000000 table
00002000 02000081 TrumpAppleはこれらのフラグを文書化していないものの、読み出した2つのフラグフィールドより、単語がどの「階層」に属し、どのような「トピック」に関連しているかを推測することができました。フラグフィールドのうち1つは階層を示します。
| ビット | 階層 | en_USの単語数 | 例 |
|---|---|---|---|
0x00800000 | 下品 | 1,050 | fuck・shit・cunt・cock・crap・wtf・fml・bullshitting・fucktard・shizzle・merde |
0x01000000 | 攻撃的 | 663 | kike・wop・dago・fag・Paki・japs・Klansman・retard・Oriental・ass・asshole |
0x02000000 | センシティブ | 2,558 | damn・bitch・idiot・LSD・abortion・gun・suicide・anus・pee・poo |
もう1つのフラグフィールドはトピックビットマスクであり、1つの単語に複数のトピックを持たせることができます。
| ビット | トピック | en_USの単語数 | 例 |
|---|---|---|---|
0x00100 | 排泄 | 123 | feces・barf・crapper・piddle・pooped |
0x00200 | 体の部位 | 958 | nipples・navel・buttock・perineum・triceps |
0x00400 | 侮辱・アイデンティティを煽る | 823 | idiot・cretin・hick・pleb・sexist・homophobic・blacklist |
0x00800 | 病気 | 93 | Covid・herpes・PTSD・leprosy・monkeypox |
0x01000 | 薬物 | 222 | heroin・shroom・crackhead・hotbox・OD |
0x02000 | 政治・過激主義 | 266 | Hamas・Goebbels・Leninism・democrat・swastikas・Trump |
0x04000 | 宗教・破滅 | 420 | Sikhism・jihadi・Beelzebub・damn・hell・jeez |
0x08000 | セックス | 473 | nude・nookie・lecher・harlots・Pornhub・Hooters |
0x10000 | 暴力・死 | 373 | suicide・lynch・murders・hangings・slave・slaveholder |
0x20000 | 武器・テロリズム | 268 | AK-47・grenadier・jihad・sarin・kamikaze・yellowcake |
0x40000 | それ以外のセンシティブなコンテキスト | 1,080 | God・Allah・Jews・gay・trans・bi・crazy・fat・ugly・sex・die |
奇妙なことに、「Oedipus(エディプス)」は下品、「Tom(トム)」や「Mick(ミック)」は攻撃的と分類されています。また「Trump(トランプ)」は、他のほとんどが故人であるナチス関係者やテロリストの名前が並ぶ中で、唯一の現役政治家としてセンシティブかつ政治トピックに分類されています。一方「Biden(バイデン)」「Obama(オバマ)」「Putin(プーチン)」はいずれも階層のフラグの立っていない「クリーン」と分類されています。ただしロケールによって違いが見られ、例えばドイツ語辞書ではトランプはクリーンに分類され、スウェーデン語辞書ではプーチンはセンシティブとされています。
注意すべきは、フラグが立てられたからといって検閲対象となるわけではないという点であり、フラグは「提案すべきでない」単語を決定するために使用されるのではないかとスヴェレ氏は推測しています。例えば、センシティブな単語はユーザーが直接入力しても自動修正されることはありませんが、QuickTypeバーに表示されにくくなるとのことです。
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.


