The Daily Newsstand · Free, Always
Wednesday, November 4, 2026

macOSには単語が冒涜的かどうかを識別する仕組みがある

Translate

開発者のヘルゲ・スヴェレ氏は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	Trump

Appleはこれらのフラグを文書化していないものの、読み出した2つのフラグフィールドより、単語がどの「階層」に属し、どのような「トピック」に関連しているかを推測することができました。フラグフィールドのうち1つは階層を示します。

ビット階層en_USの単語数例
0x00800000下品1,050fuck・shit・cunt・cock・crap・wtf・fml・bullshitting・fucktard・shizzle・merde
0x01000000攻撃的663kike・wop・dago・fag・Paki・japs・Klansman・retard・Oriental・ass・asshole
0x02000000センシティブ2,558damn・bitch・idiot・LSD・abortion・gun・suicide・anus・pee・poo

もう1つのフラグフィールドはトピックビットマスクであり、1つの単語に複数のトピックを持たせることができます。

ビットトピックen_USの単語数例
0x00100排泄123feces・barf・crapper・piddle・pooped
0x00200体の部位958nipples・navel・buttock・perineum・triceps
0x00400侮辱・アイデンティティを煽る823idiot・cretin・hick・pleb・sexist・homophobic・blacklist
0x00800病気93Covid・herpes・PTSD・leprosy・monkeypox
0x01000薬物222heroin・shroom・crackhead・hotbox・OD
0x02000政治・過激主義266Hamas・Goebbels・Leninism・democrat・swastikas・Trump
0x04000宗教・破滅420Sikhism・jihadi・Beelzebub・damn・hell・jeez
0x08000セックス473nude・nookie・lecher・harlots・Pornhub・Hooters
0x10000暴力・死373suicide・lynch・murders・hangings・slave・slaveholder
0x20000武器・テロリズム268AK-47・grenadier・jihad・sarin・kamikaze・yellowcake
0x40000それ以外のセンシティブなコンテキスト1,080God・Allah・Jews・gay・trans・bi・crazy・fat・ugly・sex・die


奇妙なことに、「Oedipus(エディプス)」は下品、「Tom(トム)」や「Mick(ミック)」は攻撃的と分類されています。また「Trump(トランプ)」は、他のほとんどが故人であるナチス関係者やテロリストの名前が並ぶ中で、唯一の現役政治家としてセンシティブかつ政治トピックに分類されています。一方「Biden(バイデン)」「Obama(オバマ)」「Putin(プーチン)」はいずれも階層のフラグの立っていない「クリーン」と分類されています。ただしロケールによって違いが見られ、例えばドイツ語辞書ではトランプはクリーンに分類され、スウェーデン語辞書ではプーチンはセンシティブとされています。


注意すべきは、フラグが立てられたからといって検閲対象となるわけではないという点であり、フラグは「提案すべきでない」単語を決定するために使用されるのではないかとスヴェレ氏は推測しています。例えば、センシティブな単語はユーザーが直接入力しても自動修正されることはありませんが、QuickTypeバーに表示されにくくなるとのことです。

View the original on GIGAZINE →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.