Misskey の言語検出と設定システム#
概要#
Misskey は、ブラウザの言語設定に基づく自動言語検出システムを実装しており、日本語と韓国語に対して特別な処理を行います。
ブラウザ言語設定からの自動検出#
初回検出プロセス#
言語設定が未設定のユーザーが初めて Misskey にアクセスすると、システムはブラウザの言語設定から投稿言語を自動的に検出します。
検出ロジックの動作:
- ブラウザの
navigator.language値を読み取る - ユーザーに既存の言語設定がない場合のみ投稿言語を決定する
i/updateAPI エンドポイント経由でユーザー設定を更新する- 検出メタデータを localStorage に保存して将来の参照に備える
ブラウザ言語変更の検出#
システムは以下を比較することでブラウザ言語の変更を追跡します:
postingLangAutoDetectedに保存された初回検出言語postingLangAutoDetectBaseに保存された検出時のブラウザ言語- 現在のブラウザ言語
ブラウザ言語の変更が検出されると、設定 > プリファレンスに警告メッセージが表示され、ユーザーに言語設定の見直しを促します。
言語コードの標準化#
正規化ルール#
getAutoPostingLang()関数は、ブラウザの言語コードを標準化します:
変換ロジック:
koで始まるブラウザ言語 →ko-KRjaで始まるブラウザ言語 →ja-JP- その他すべての言語 →
ja-JP(デフォルトフォールバック)
実装:
export function getAutoPostingLang(browserLanguage?: string | null): string {
if (browserLanguage) {
const normalized = browserLanguage.toLowerCase();
if (normalized.startsWith('ko')) return 'ko-KR';
if (normalized.startsWith('ja')) return 'ja-JP';
}
return 'ja-JP';
}
サポートされる言語コード#
システムは投稿言語を3 つのオプションに制限しています:
ja-JP(日本語 - 日本)ko-KR(韓国語 - 韓国)other(その他すべての言語)
バックエンドでは、短縮形と完全なロケールコードの両方がサポートされています:['ja', 'ja-JP', 'ko', 'ko-KR', 'other']。
特殊な方言の処理#
関西弁(ja-KS)は、Intl API との互換性のために標準日本語に自動的にマッピングされます:
export const versatileLang = (lang ?? 'ja-JP').replace('ja-KS', 'ja-JP');
多言語コンテンツ表示の UI オプション#
メディア表示のオーバーライド#
showMediaInAllLanguagesは、言語設定に関係なく、メディア添付のある投稿を表示できるようにします。
詳細:
user_langデータベーステーブルに保存- デフォルト値:
true(オプトアウト方式) - マイグレーション
1768824121553で追加
ハッシュタグ表示のオーバーライド#
showHashtagsInAllLanguagesは、言語設定に関係なく、ハッシュタグ付きの投稿を表示できるようにします。
詳細:
user_langデータベーステーブルに保存- デフォルト値:
true(オプトアウト方式) - マイグレーション
1769296855147で追加
設定 UI 上の場所#
これらのオプションは設定 > プリファレンス > タイムラインとノート > 投稿言語と閲覧言語に表示され、言語フィルタリングが有効な場合のみ表示されます(すべての言語を表示する設定の場合は表示されません)。
言語のフォールバックと優先順位#
日本語優先アーキテクチャ#
Misskey は日本語優先の言語システムを実装しており、ja-JPがマッチしないすべての言語と未指定のケースに対する最終的なフォールバックとして機能します。
優先順位ルール#
自動検出の優先順位:
- 韓国語(
ko*) →ko-KR - 日本語(
ja*) →ja-JP - その他すべての言語 →
ja-JP(デフォルト)
閲覧言語のデフォルト:
getDefaultViewingLangs()関数は常に日本語を含みます:
export function getDefaultViewingLangs(postingLang: string): string[] {
return Array.from(new Set([postingLang, 'ja-JP', 'unknown', 'remote']));
}
優先順位:
- ユーザーが選択した投稿言語
ja-JP(常に含まれる)unknown(検出されなかった言語)remote(リモートサーバーのコンテンツ)
ロケール継承階層#
ロケール読み込みシステムは、翻訳文字列に対して階層的なフォールバックを適用します:
継承チェーン:
ja-JP(すべてのロケールの基礎レイヤー)en-US(第 2 レイヤー)- 言語固有の地域バリアント
- 最終的なロケールオーバーライド
これは、すべての非日本語ロケールが日本語を基盤として継承し、その後に英語を継承することを意味します。
Service Worker のデフォルト#
Service Worker も日本語をデフォルトとして使用します(言語設定が保存されていない場合):
public lang: Promise<string> = get('lang').then(async prelang => {
if (!prelang) return 'ja-JP';
return prelang;
});
言語変更の通知#
アチーブメントベースの通知#
Misskey は、言語設定の変更に対して従来の通知ではなくアチーブメントシステムを使用します。
アチーブメントトリガー:
ユーザーが言語設定の変更を保存すると:
if (isPostingLangChanged) claimAchievement('postingLanguageConfigured');
if (isViewingLangsChanged || isShowMediaInAllLanguagesChanged || isShowHashtagsInAllLanguagesChanged) {
claimAchievement('viewingLanguagesConfigured');
}
アチーブメントタイプ:
postingLanguageConfigured:投稿言語を設定した時に解除viewingLanguagesConfigured:閲覧言語、メディア、またはハッシュタグ設定を変更した時に解除
これらはユーザーの通知フィードに **achievementEarned** タイプの通知として表示されます。
ブラウザ言語変更の警告#
ブラウザ言語が変更されると、Misskey はプッシュ通知を送る代わりに設定画面に警告メッセージを表示します。
警告条件:
- 投稿言語が最初に自動検出されている
- ユーザーがまだ自動検出された言語を使用している
- 初回検出以降にブラウザ言語が変更されている
警告メッセージ: 警告にはpostingLanguageBrowserLangChangedロケールキーが使用されます(日本語:「ブラウザーの言語が変更されています。投稿する言語の設定を確認してください。」、英語:「Your browser language has been changed. Please check your posting language settings.」)。
初期設定#
初回起動時、言語設定は通知なしで自動的に行われ、後の比較のためにメタデータが保存されます。
タイムラインコンテンツのフィルタリング#
コアフィルタリングロジック#
NoteEntityService のisLanguageVisibleToMe()メソッドが、言語設定に基づいてノートの表示可否を判定します。
常に表示されるケース:
以下の場合、ノートは言語フィルタリングをバイパスします:
- 閲覧者がログインしていない
- ユーザーに言語設定がない(
viewingLangsが null または空) - ノートが閲覧者にメンションまたは宛先指定されている
- ノートにメディアファイルが含まれかつ
showMediaInAllLanguagesが有効 - ノートにハッシュタグが含まれかつ
showHashtagsInAllLanguagesが有効
言語検出の階層#
システムは以下の順序でノートの言語を判定します:
- リモートインスタンスの投稿 →
'remote' - ノート単位の言語オーバーライド(
note_langテーブルから) - 投稿者のデフォルト投稿言語(
postingLang) - 未指定の場合は
'unknown'にフォールバック
マッチングアルゴリズム#
ノートの言語が以下のいずれかに該当する場合、表示されます:
viewingLangs配列内のコードと完全一致する、または- コードと前方一致する(例:
jaはja-JPにマッチ)
この前方一致により、言語バリアントを柔軟に処理できます。
タイムラインとの統合#
言語フィルタリングは、すべてのタイムラインタイプで一貫して適用されます:
- ホームタイムライン
- ローカルタイムライン
- ハイブリッドタイムライン
- グローバルタイムライン
- ユーザーリスト、ハッシュタグ、アンテナ、チャンネル、ロールタイムライン
フィルタータイミング: 言語フィルタリングは、可視性、ブロック、ミュートのチェックの後、クライアントへの配信前にストリーミングレイヤーで実行されます。
データストレージ#
ユーザー設定: user_langテーブルに保存:
viewingLangs:表示する言語コードの配列postingLang:デフォルト投稿言語showMediaInAllLanguages:メディアバイパスフラグshowHashtagsInAllLanguages:ハッシュタグバイパスフラグ
ノート言語オーバーライド: note_langテーブルにノート単位の言語指定を保存。
パフォーマンス最適化#
ユーザーとノートの言語データは、繰り返しのデータベースルックアップを避けるため、Redis キャッシュ(userLanguageCacheとnoteLanguageCache)を利用します。
テストカバレッジ#
実装には、以下を検証する包括的なテストケースが含まれています:
- 言語が一致するノートが表示される
- 言語なしのノートには
viewingLangsに'unknown'が必要 - 言語フィルタリングが
viewingLangs設定を尊重する
特殊な言語コード#
システム定義コード#
unknown:検出または指定されていない言語の投稿remote:リモート連合インスタンスからの投稿other:ja-JP/ko-KR 以外の言語の包括的カテゴリ
デフォルト設定での使用#
デフォルトの閲覧言語には常に以下が含まれます:
- ユーザーの投稿言語
ja-JP(日本語フォールバック)unknown(未分類コンテンツ)remote(連合コンテンツ)
これにより、明示的に設定された場合は言語設定を尊重しつつ、デフォルトではほとんどのコンテンツをユーザーが閲覧できるようになっています。