JavaScript Tips | 文字列ユーティリティ:業務用 - スラッグ生成

JavaScript JavaScript
スポンサーリンク

何をしたいユーティリティか:「スラッグ生成」

ここでの「スラッグ生成」は、人間が読めて、URL や識別子としても扱いやすい「きれいな文字列」を作ることです。
ブログ記事の URL、商品ページのパス、カテゴリコードなどでよく使われます。

例えば、こんな変換をしたいイメージです。

  • "はじめての JavaScript 入門""hajimete-no-javascript-nyumon"
  • " Hello, World! ""hello-world"
  • "商品#123_特価!!""shang-pin-123-te-jia"(ローマ字化や簡略化の方針次第)

ここでは、まず「英数字ベースのスラッグ」を作る基本形を押さえ、そのうえで日本語を含む場合の考え方も触れます。


スラッグの基本ルールを決める

どんな文字を許可するか

まず、「スラッグに含めてよい文字」を決めます。
よくあるルールはこんな感じです。

  • 小文字の英字 a-z
  • 数字 0-9
  • 区切り用のハイフン -

それ以外の文字(スペース、記号、日本語など)は、

  • 区切り(スペースなど)はハイフンに変換
  • それ以外は削除 or ローマ字化

という方針を取ります。

ここではまず、「英数字以外は区切り扱いにしてハイフンにする → 連続ハイフンを 1 個にまとめる → 端のハイフンを削る」というシンプルな版を作ります。


英数字ベースのシンプルなスラッグ生成

基本実装

function slugifyBasic(input) {
  if (!input) {
    return "";
  }

  let str = String(input).trim().toLowerCase();

  str = str.replace(/[^a-z0-9]+/g, "-");

  str = str.replace(/^-+|-+$/g, "");

  return str;
}
JavaScript

重要なポイントをかみ砕いて説明する

1. 文字列化して、前後の空白を削り、小文字にする

let str = String(input).trim().toLowerCase();
JavaScript

ここでやっていることは 3 つです。

  • String(input) で、数値なども含めて文字列に統一
  • trim() で前後の空白を削除
  • toLowerCase() で小文字に統一

スラッグは「大文字小文字を区別しない」ことが多いので、最初に小文字にしてしまうのが定番です。

2. 英数字以外を「区切り」としてハイフンにする

str = str.replace(/[^a-z0-9]+/g, "-");
JavaScript

[^a-z0-9]+ は、「英小文字と数字以外が 1 文字以上続く部分」を意味します。
それを全部 "-" に置き換えています。

つまり、

  • スペース " "
  • 記号 "!", "?", ",", "_" など
  • 日本語 "あ", "漢" など

は、いったん全部「区切り」とみなして "-" に変わります。

例:

  • "Hello, World!""hello--world-"(この時点ではハイフンが連続したり末尾に付いたりする)

3. 先頭と末尾のハイフンを削る

str = str.replace(/^-+|-+$/g, "");
JavaScript

/^-+|-+$/g は、「先頭の連続ハイフン」または「末尾の連続ハイフン」を意味します。
それを空文字に置き換えることで、端の余計なハイフンを削ります。

ただ、このままだと "hello--world" のように「中に連続ハイフン」が残ることがあります。
気になる場合は、もう一段階整形します。


連続ハイフンを 1 個にまとめる改良版

改良した slugify

function slugify(input) {
  if (!input) {
    return "";
  }

  let str = String(input).trim().toLowerCase();

  str = str.replace(/[^a-z0-9]+/g, "-");

  str = str.replace(/-+/g, "-");

  str = str.replace(/^-+|-+$/g, "");

  return str;
}
JavaScript

追加したポイント

str = str.replace(/-+/g, "-");
JavaScript

ここで、「連続したハイフン -+ を 1 個の - にまとめる」処理を入れています。

これで、

  • "Hello, World!""hello-world"
  • "---A---B---""a-b"

のように、スラッグとしてきれいな形になります。


実際の動きを例で確認する

slugify("Hello, World!");
// "hello-world"

slugify("  JavaScript 入門 2024!!  ");
// "javascript-2024"(日本語部分は区切り扱いで落ちる)

slugify("商品#123_特価!!");
// "123"(英数字以外は区切り扱い)
JavaScript

この「英数字以外を全部区切り扱いにする」方針だと、
日本語はすべて落ちてしまいます。
それでもよいケース(内部コード用など)もありますが、
「日本語タイトルからそれっぽいスラッグを作りたい」場合は、もう一歩踏み込みが必要です。


日本語を含むスラッグをどう扱うか

方針を決めるのが一番大事

日本語を含むスラッグについては、ざっくり次のような選択肢があります。

  1. 日本語をそのまま許可する(URL エンコードに任せる)
  2. 日本語をローマ字化する
  3. 日本語部分は捨てて、別の識別子(ID など)を使う

ここでは、シンプルに「日本語も含めて、英数字+ハイフン+日本語を許可する」版を一つ示します。


日本語を残すスラッグ生成(簡易版)

実装例

function slugifyWithJapanese(input) {
  if (!input) {
    return "";
  }

  let str = String(input).trim().toLowerCase();

  str = str.replace(/[\s_]+/g, "-");

  str = str.replace(/[^a-z0-9\u3040-\u30ff\u4e00-\u9faf\-]+/g, "");

  str = str.replace(/-+/g, "-");
  str = str.replace(/^-+|-+$/g, "");

  return str;
}
JavaScript

重要ポイント

str = str.replace(/[\s_]+/g, "-");
JavaScript

まず、スペースやアンダースコアを「区切り」としてハイフンに変えています。

str = str.replace(/[^a-z0-9\u3040-\u30ff\u4e00-\u9faf\-]+/g, "");
JavaScript

ここで、「許可する文字」以外を削除しています。

  • a-z0-9 → 英数字
  • \u3040-\u30ff → ひらがな・カタカナ
  • \u4e00-\u9faf → CJK 統合漢字(ざっくり漢字)
  • \- → ハイフン

これに当てはまらない記号などは削除されます。

実際の動き

slugifyWithJapanese("はじめての JavaScript 入門");
// "はじめての-javascript-入門"

slugifyWithJapanese("商品#123_特価!!");
// "商品123-特価"

slugifyWithJapanese("  令和6年度_売上レポート  ");
// "令和6年度-売上レポート"
JavaScript

このように、「日本語+英数字+ハイフン」で構成されたスラッグが作れます。
URL として使うときは、ブラウザやサーバが自動的にパーセントエンコードしてくれます。


実務で意識してほしい設計のポイント

「スラッグの仕様」を先に決める

スラッグは、あとから仕様変更が入りやすい部分です。

  • 英数字だけにしたいのか
  • 日本語を許可するのか
  • 大文字小文字を区別するのか
  • 最大長をどれくらいにするのか

これを曖昧にしたまま使い始めると、
途中で「やっぱり日本語も残したい」「やっぱり英数字だけにしたい」となって、
既存データとの整合性が大変になります。

だからこそ、ユーティリティを作るタイミングで、
「このプロジェクトのスラッグはこういうルールで作る」
と決めておくのが重要です。

「表示用」と「内部識別子」を分ける

スラッグは「人間が読む URL の一部」として使われることが多いですが、
DB の主キーや内部識別子としては、別の ID(数値 ID や UUID)を使うのが安全です。

URL 例:

/articles/12345-hajimete-no-javascript-nyumon

ここで、

  • 12345 → 内部の記事 ID(DB の主キー)
  • hajimete-no-javascript-nyumon → 表示用スラッグ

という分担にしておけば、
スラッグの仕様を変えたくなっても、ID さえ変わらなければリンクは生き続けます。

スラッグの一意性は別で担保する

スラッグ生成ユーティリティは、「同じ入力から同じスラッグを作る」ことはできますが、
「全体で一意かどうか」は保証しません。

"JavaScript 入門""javascript-nyumon"
"JavaScript 入門(改訂版)""javascript-nyumon"(同じになる可能性)

このような場合は、

  • 末尾に ID を付ける("javascript-nyumon-12345"
  • 既存スラッグと重複していたら -2, -3 を付ける

などのロジックを、アプリ側で追加する必要があります。


少し手を動かして感覚をつかむ

コンソールで、次のようなコードを実際に打ってみてください。

slugify("Hello, World!");
slugify("  JavaScript 入門 2024!!  ");
slugify("商品#123_特価!!");

slugifyWithJapanese("はじめての JavaScript 入門");
slugifyWithJapanese("商品#123_特価!!");
slugifyWithJapanese("  令和6年度_売上レポート  ");
JavaScript

どの文字が残り、どの文字がハイフンになり、どの文字が削除されるかを、
自分の目で確認してみてください。

そのうえで、自分のプロジェクトに

export function slugify(...) { ... }
export function slugifyWithJapanese(...) { ... }
JavaScript

のような関数を置き、

「URL やコード用のスラッグが欲しくなったら、必ずこの“スラッグ生成ユーティリティ”を通す」

というルールを作ってみてください。
それだけで、あなたのシステムのスラッグは、場当たり的な置換の寄せ集めから、
意図と一貫性を備えた「業務レベルのスラッグ生成」に変わっていきます。

タイトルとURLをコピーしました