summaryrefslogtreecommitdiff
path: root/packages/frontend/src/scripts
diff options
context:
space:
mode:
authorかっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>2025-03-09 14:21:23 +0900
committerGitHub <noreply@github.com>2025-03-09 14:21:23 +0900
commitf35eb0f6d9330924513a5b6eded04c9b71196f0d (patch)
tree707fa354414ca2327e70f244409a4117e2e182f3 /packages/frontend/src/scripts
parentenhance(frontend): tweak settings page (diff)
downloadsharkey-f35eb0f6d9330924513a5b6eded04c9b71196f0d.tar.gz
sharkey-f35eb0f6d9330924513a5b6eded04c9b71196f0d.tar.bz2
sharkey-f35eb0f6d9330924513a5b6eded04c9b71196f0d.zip
enhnace(frontend): 文字列比較のためのローマナイズを強化(設定の検索) (#15632)
* enhnace(frontend): 文字列比較のためのローマナイズを強化 * docs * fix * fix * fix * comment * wanakanaの初回ロードをコンポーネント内に移動 * comment * fix * add tests --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
Diffstat (limited to 'packages/frontend/src/scripts')
-rw-r--r--packages/frontend/src/scripts/intl-string.ts97
1 files changed, 97 insertions, 0 deletions
diff --git a/packages/frontend/src/scripts/intl-string.ts b/packages/frontend/src/scripts/intl-string.ts
new file mode 100644
index 0000000000..a5b5bbb592
--- /dev/null
+++ b/packages/frontend/src/scripts/intl-string.ts
@@ -0,0 +1,97 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { versatileLang } from '@@/js/intl-const.js';
+import type { toHiragana as toHiraganaType } from 'wanakana';
+
+let toHiragana: typeof toHiraganaType = (str?: string) => str ?? '';
+let isWanakanaLoaded = false;
+
+/**
+ * ローマ字変換のセットアップ(日本語以外の環境で読み込まないのでlazy-loading)
+ *
+ * ここの比較系関数を使う際は事前に呼び出す必要がある
+ */
+export async function initIntlString(forceWanakana = false) {
+ if ((!versatileLang.includes('ja') && !forceWanakana) || isWanakanaLoaded) return;
+ const { toHiragana: _toHiragana } = await import('wanakana');
+ toHiragana = _toHiragana;
+ isWanakanaLoaded = true;
+}
+
+/**
+ * - 全角英数字を半角に
+ * - 半角カタカナを全角に
+ * - 濁点・半濁点がリガチャになっている(例: `か` + `゛` )ひらがな・カタカナを結合
+ * - 異体字を正規化
+ * - 小文字に揃える
+ * - 文字列のトリム
+ */
+export function normalizeString(str: string) {
+ const segmenter = new Intl.Segmenter(versatileLang, { granularity: 'grapheme' });
+ return [...segmenter.segment(str)].map(({ segment }) => segment.normalize('NFKC')).join('').toLowerCase().trim();
+}
+
+// https://qiita.com/non-caffeine/items/77360dda05c8ce510084
+const hyphens = [
+ 0x002d, // hyphen-minus
+ 0x02d7, // modifier letter minus sign
+ 0x1173, // hangul jongseong eu
+ 0x1680, // ogham space mark
+ 0x1b78, // balinese musical symbol left-hand open pang
+ 0x2010, // hyphen
+ 0x2011, // non-breaking hyphen
+ 0x2012, // figure dash
+ 0x2013, // en dash
+ 0x2014, // em dash
+ 0x2015, // horizontal bar
+ 0x2043, // hyphen bullet
+ 0x207b, // superscript minus
+ 0x2212, // minus sign
+ 0x25ac, // black rectangle
+ 0x2500, // box drawings light horizontal
+ 0x2501, // box drawings heavy horizontal
+ 0x2796, // heavy minus sign
+ 0x30fc, // katakana-hiragana prolonged sound mark
+ 0x3161, // hangul letter eu
+ 0xfe58, // small em dash
+ 0xfe63, // small hyphen-minus
+ 0xff0d, // fullwidth hyphen-minus
+ 0xff70, // halfwidth katakana-hiragana prolonged sound mark
+ 0x10110, // aegean number ten
+ 0x10191, // roman uncia sign
+];
+
+const hyphensCodePoints = hyphens.map(code => `\\u{${code.toString(16).padStart(4, '0')}}`);
+
+/** ハイフンを統一(ローマ字半角入力時に`ー`と`-`が判定できない問題の調整) */
+export function normalizeHyphens(str: string) {
+ return str.replace(new RegExp(`[${hyphensCodePoints.join('')}]`, 'ug'), '\u002d');
+}
+
+/**
+ * `normalizeString` に加えて、カタカナ・ローマ字をひらがなに揃え、ハイフンを統一
+ *
+ * (ローマ字じゃないものもローマ字として認識され変換されるので、文字列比較の際は `normalizeString` を併用する必要あり)
+ */
+export function normalizeStringWithHiragana(str: string) {
+ return normalizeHyphens(toHiragana(normalizeString(str), { convertLongVowelMark: false }));
+}
+
+/** aとbが同じかどうか */
+export function compareStringEquals(a: string, b: string) {
+ return (
+ normalizeString(a) === normalizeString(b) ||
+ normalizeStringWithHiragana(a) === normalizeStringWithHiragana(b)
+ );
+}
+
+/** baseにqueryが含まれているかどうか */
+export function compareStringIncludes(base: string, query: string) {
+ return (
+ normalizeString(base).includes(normalizeString(query)) ||
+ normalizeStringWithHiragana(base).includes(normalizeStringWithHiragana(query))
+ );
+}