[PHP]setlocale関数を完全解説!ロケール情報を設定する方法

PHP

こんにちは!今回は、PHPの標準関数であるsetlocale()について詳しく解説していきます。日付や通貨、数値などのフォーマットを地域に合わせて設定できる重要な関数です!

setlocale関数とは?

setlocale()関数は、PHPのロケール(地域・言語設定)を変更する関数です。

ロケールを設定することで、日付・時刻、通貨、数値、文字列の比較などを、指定した地域の慣習に従ってフォーマットできます!

基本的な構文

setlocale(int $category, string|array $locales, string ...$rest): string|false
  • $category: ロケールカテゴリ(LC_ALL、LC_TIME、LC_MONETARYなど)
  • $locales: ロケール名(配列または文字列)
  • $rest: 追加のロケール名(フォールバック用)
  • 戻り値: 設定されたロケール名、失敗時はfalse

ロケールカテゴリ

// すべてのカテゴリ
LC_ALL          // すべてのロケール設定

// 個別カテゴリ
LC_COLLATE      // 文字列比較
LC_CTYPE        // 文字の分類と変換
LC_MONETARY     // 通貨フォーマット
LC_NUMERIC      // 数値フォーマット
LC_TIME         // 日付・時刻フォーマット
LC_MESSAGES     // システムメッセージ

基本的な使用例

シンプルな設定

// 日本語ロケールに設定
setlocale(LC_ALL, 'ja_JP.UTF-8');

// 日付をフォーマット
echo strftime('%Y年%m月%d日 %A') . "\n";
// 出力例: 2026年03月24日 火曜日

// アメリカ英語ロケールに設定
setlocale(LC_ALL, 'en_US.UTF-8');

// 日付をフォーマット
echo strftime('%B %d, %Y (%A)') . "\n";
// 出力例: March 24, 2026 (Tuesday)

複数のロケールを試す(フォールバック)

// 複数のロケールを配列で指定(最初に見つかったものが使用される)
$locale = setlocale(
    LC_ALL,
    ['ja_JP.UTF-8', 'ja_JP', 'japanese']
);

if ($locale === false) {
    echo "ロケールの設定に失敗しました\n";
} else {
    echo "設定されたロケール: {$locale}\n";
}

// 可変引数で指定
setlocale(LC_ALL, 'ja_JP.UTF-8', 'ja_JP', 'japanese');

現在のロケールを取得

// 現在のロケールを取得(引数に0を渡す)
$currentLocale = setlocale(LC_ALL, 0);
echo "現在のロケール: {$currentLocale}\n";

// カテゴリごとに取得
$timeLocale = setlocale(LC_TIME, 0);
echo "時刻ロケール: {$timeLocale}\n";

カテゴリ別の設定

// 時刻だけを日本語に設定
setlocale(LC_TIME, 'ja_JP.UTF-8');

// 数値はアメリカ式に設定
setlocale(LC_NUMERIC, 'en_US.UTF-8');

// 通貨はユーロ圏に設定
setlocale(LC_MONETARY, 'de_DE.UTF-8');

実践的な使用例

例1: 多言語日付フォーマッター

class LocalizedDateFormatter {
    private $originalLocale;
    
    /**
     * 日付を指定ロケールでフォーマット
     */
    public static function format($timestamp, $locale, $format = '%c') {
        // 元のロケールを保存
        $originalLocale = setlocale(LC_TIME, 0);
        
        // 指定ロケールに変更
        $result = setlocale(LC_TIME, $locale);
        
        if ($result === false) {
            // ロケール設定失敗
            setlocale(LC_TIME, $originalLocale);
            return false;
        }
        
        // フォーマット
        $formatted = strftime($format, $timestamp);
        
        // ロケールを元に戻す
        setlocale(LC_TIME, $originalLocale);
        
        return $formatted;
    }
    
    /**
     * 複数のロケールで日付を表示
     */
    public static function formatMultiple($timestamp, $locales) {
        $results = [];
        
        foreach ($locales as $localeName => $locale) {
            $formatted = self::format($timestamp, $locale, '%A, %B %d, %Y');
            
            if ($formatted !== false) {
                $results[$localeName] = $formatted;
            }
        }
        
        return $results;
    }
    
    /**
     * 長い日付形式
     */
    public static function formatLong($timestamp, $locale) {
        return self::format($timestamp, $locale, '%A, %B %d, %Y');
    }
    
    /**
     * 短い日付形式
     */
    public static function formatShort($timestamp, $locale) {
        return self::format($timestamp, $locale, '%x');
    }
    
    /**
     * 時刻形式
     */
    public static function formatTime($timestamp, $locale) {
        return self::format($timestamp, $locale, '%X');
    }
    
    /**
     * 完全形式(日付+時刻)
     */
    public static function formatFull($timestamp, $locale) {
        return self::format($timestamp, $locale, '%c');
    }
    
    /**
     * カスタム形式
     */
    public static function formatCustom($timestamp, $locale, $format) {
        return self::format($timestamp, $locale, $format);
    }
}

// 使用例
echo "=== 多言語日付フォーマット ===\n";

$timestamp = time();

$locales = [
    '日本語' => 'ja_JP.UTF-8',
    '英語(米国)' => 'en_US.UTF-8',
    '英語(英国)' => 'en_GB.UTF-8',
    'フランス語' => 'fr_FR.UTF-8',
    'ドイツ語' => 'de_DE.UTF-8',
    '中国語' => 'zh_CN.UTF-8'
];

$results = LocalizedDateFormatter::formatMultiple($timestamp, $locales);

foreach ($results as $name => $formatted) {
    echo "{$name}: {$formatted}\n";
}

echo "\n=== 様々なフォーマット ===\n";
$locale = 'ja_JP.UTF-8';

echo "長い形式: " . LocalizedDateFormatter::formatLong($timestamp, $locale) . "\n";
echo "短い形式: " . LocalizedDateFormatter::formatShort($timestamp, $locale) . "\n";
echo "時刻: " . LocalizedDateFormatter::formatTime($timestamp, $locale) . "\n";
echo "完全形式: " . LocalizedDateFormatter::formatFull($timestamp, $locale) . "\n";

echo "\n=== カスタム形式 ===\n";
echo LocalizedDateFormatter::formatCustom($timestamp, 'ja_JP.UTF-8', '%Y年%m月%d日(%a)') . "\n";
echo LocalizedDateFormatter::formatCustom($timestamp, 'en_US.UTF-8', '%B %d, %Y at %I:%M %p') . "\n";

例2: 通貨フォーマッター

class CurrencyFormatter {
    /**
     * 金額を指定ロケールでフォーマット
     */
    public static function format($amount, $locale) {
        // 元のロケールを保存
        $originalLocale = setlocale(LC_MONETARY, 0);
        
        // 指定ロケールに変更
        $result = setlocale(LC_MONETARY, $locale);
        
        if ($result === false) {
            setlocale(LC_MONETARY, $originalLocale);
            return false;
        }
        
        // ロケール情報を取得
        $localeInfo = localeconv();
        
        // フォーマット
        $formatted = money_format('%n', $amount);
        
        // ロケールを元に戻す
        setlocale(LC_MONETARY, $originalLocale);
        
        return [
            'formatted' => $formatted,
            'currency_symbol' => $localeInfo['currency_symbol'],
            'decimal_point' => $localeInfo['mon_decimal_point'],
            'thousands_sep' => $localeInfo['mon_thousands_sep']
        ];
    }
    
    /**
     * 複数の通貨でフォーマット
     */
    public static function formatMultipleCurrencies($amount) {
        $currencies = [
            'JPY (日本円)' => 'ja_JP.UTF-8',
            'USD (米ドル)' => 'en_US.UTF-8',
            'EUR (ユーロ)' => 'de_DE.UTF-8',
            'GBP (英ポンド)' => 'en_GB.UTF-8',
            'CNY (人民元)' => 'zh_CN.UTF-8'
        ];
        
        $results = [];
        
        foreach ($currencies as $name => $locale) {
            $formatted = self::format($amount, $locale);
            
            if ($formatted !== false) {
                $results[$name] = $formatted;
            }
        }
        
        return $results;
    }
    
    /**
     * ロケール情報を取得
     */
    public static function getLocaleInfo($locale) {
        $originalLocale = setlocale(LC_MONETARY, 0);
        setlocale(LC_MONETARY, $locale);
        
        $info = localeconv();
        
        setlocale(LC_MONETARY, $originalLocale);
        
        return $info;
    }
}

// 使用例
echo "=== 通貨フォーマット ===\n";

$amount = 1234567.89;

// Note: money_format()はPHP 7.4で非推奨、PHP 8.0で削除されました
// 以下は概念的な例です

echo "金額: {$amount}\n\n";

$results = CurrencyFormatter::formatMultipleCurrencies($amount);

foreach ($results as $currency => $data) {
    echo "{$currency}:\n";
    if (isset($data['formatted'])) {
        echo "  フォーマット済み: {$data['formatted']}\n";
    }
    echo "  通貨記号: {$data['currency_symbol']}\n";
    echo "  小数点: {$data['decimal_point']}\n";
    echo "  千の位区切り: {$data['thousands_sep']}\n\n";
}

echo "\n=== ロケール詳細情報(日本) ===\n";
$info = CurrencyFormatter::getLocaleInfo('ja_JP.UTF-8');
print_r($info);

例3: 数値フォーマッター

class NumberFormatter {
    /**
     * 数値を指定ロケールでフォーマット
     */
    public static function format($number, $locale, $decimals = 2) {
        // 元のロケールを保存
        $originalLocale = setlocale(LC_NUMERIC, 0);
        
        // 指定ロケールに変更
        $result = setlocale(LC_NUMERIC, $locale);
        
        if ($result === false) {
            setlocale(LC_NUMERIC, $originalLocale);
            return false;
        }
        
        // ロケール情報を取得
        $localeInfo = localeconv();
        
        // 数値をフォーマット
        $formatted = number_format(
            $number,
            $decimals,
            $localeInfo['decimal_point'],
            $localeInfo['thousands_sep']
        );
        
        // ロケールを元に戻す
        setlocale(LC_NUMERIC, $originalLocale);
        
        return $formatted;
    }
    
    /**
     * 複数のロケールで数値を表示
     */
    public static function formatMultiple($number) {
        $locales = [
            '日本' => 'ja_JP.UTF-8',
            'アメリカ' => 'en_US.UTF-8',
            'ドイツ' => 'de_DE.UTF-8',
            'フランス' => 'fr_FR.UTF-8'
        ];
        
        $results = [];
        
        foreach ($locales as $name => $locale) {
            $formatted = self::format($number, $locale);
            
            if ($formatted !== false) {
                $results[$name] = $formatted;
            }
        }
        
        return $results;
    }
    
    /**
     * パーセンテージをフォーマット
     */
    public static function formatPercentage($number, $locale) {
        $formatted = self::format($number, $locale, 2);
        return $formatted !== false ? $formatted . '%' : false;
    }
}

// 使用例
echo "=== 数値フォーマット ===\n";

$number = 1234567.89;

echo "元の数値: {$number}\n\n";

$results = NumberFormatter::formatMultiple($number);

foreach ($results as $locale => $formatted) {
    echo "{$locale}: {$formatted}\n";
}

echo "\n=== パーセンテージ ===\n";
echo "日本: " . NumberFormatter::formatPercentage(45.67, 'ja_JP.UTF-8') . "\n";
echo "アメリカ: " . NumberFormatter::formatPercentage(45.67, 'en_US.UTF-8') . "\n";

例4: 文字列比較(ロケール依存)

class LocalizedStringComparator {
    /**
     * ロケールに基づいて文字列を比較
     */
    public static function compare($str1, $str2, $locale) {
        // 元のロケールを保存
        $originalLocale = setlocale(LC_COLLATE, 0);
        
        // 指定ロケールに変更
        setlocale(LC_COLLATE, $locale);
        
        // 比較
        $result = strcoll($str1, $str2);
        
        // ロケールを元に戻す
        setlocale(LC_COLLATE, $originalLocale);
        
        return $result;
    }
    
    /**
     * ロケールに基づいて配列をソート
     */
    public static function sort($array, $locale) {
        // 元のロケールを保存
        $originalLocale = setlocale(LC_COLLATE, 0);
        
        // 指定ロケールに変更
        setlocale(LC_COLLATE, $locale);
        
        // ソート
        usort($array, function($a, $b) {
            return strcoll($a, $b);
        });
        
        // ロケールを元に戻す
        setlocale(LC_COLLATE, $originalLocale);
        
        return $array;
    }
    
    /**
     * 複数のロケールでソート結果を比較
     */
    public static function sortMultiple($array, $locales) {
        $results = [];
        
        foreach ($locales as $name => $locale) {
            $results[$name] = self::sort($array, $locale);
        }
        
        return $results;
    }
}

// 使用例
echo "=== ロケール依存の文字列比較 ===\n";

// 日本語の文字列
$strings = ['あいうえお', 'かきくけこ', 'さしすせそ', 'たちつてと'];

$locales = [
    '日本語' => 'ja_JP.UTF-8',
    'C(デフォルト)' => 'C'
];

$results = LocalizedStringComparator::sortMultiple($strings, $locales);

foreach ($results as $locale => $sorted) {
    echo "\n{$locale}:\n";
    foreach ($sorted as $str) {
        echo "  {$str}\n";
    }
}

// アルファベットの比較
echo "\n=== アルファベットの比較 ===\n";
$words = ['école', 'zebra', 'apple', 'Über'];

$results = LocalizedStringComparator::sortMultiple($words, [
    'フランス語' => 'fr_FR.UTF-8',
    'ドイツ語' => 'de_DE.UTF-8',
    'アメリカ英語' => 'en_US.UTF-8'
]);

foreach ($results as $locale => $sorted) {
    echo "\n{$locale}:\n";
    echo "  " . implode(', ', $sorted) . "\n";
}

例5: ロケールマネージャー

class LocaleManager {
    private static $stack = [];
    
    /**
     * 現在のロケールを保存してスタックにプッシュ
     */
    public static function push($category = LC_ALL) {
        $current = setlocale($category, 0);
        self::$stack[] = [
            'category' => $category,
            'locale' => $current
        ];
        
        return $current;
    }
    
    /**
     * スタックからロケールをポップして復元
     */
    public static function pop() {
        if (empty(self::$stack)) {
            return false;
        }
        
        $saved = array_pop(self::$stack);
        return setlocale($saved['category'], $saved['locale']);
    }
    
    /**
     * 一時的にロケールを変更して処理を実行
     */
    public static function withLocale($locale, $callback, $category = LC_ALL) {
        self::push($category);
        
        setlocale($category, $locale);
        
        try {
            $result = $callback();
        } finally {
            self::pop();
        }
        
        return $result;
    }
    
    /**
     * 利用可能なロケールを検出
     */
    public static function getAvailableLocales($testLocales) {
        $available = [];
        $originalLocale = setlocale(LC_ALL, 0);
        
        foreach ($testLocales as $locale) {
            if (setlocale(LC_ALL, $locale) !== false) {
                $available[] = $locale;
            }
        }
        
        setlocale(LC_ALL, $originalLocale);
        
        return $available;
    }
    
    /**
     * ロケール情報を取得
     */
    public static function getInfo($category = LC_ALL) {
        return [
            'current_locale' => setlocale($category, 0),
            'category' => self::getCategoryName($category),
            'locale_conv' => localeconv()
        ];
    }
    
    /**
     * カテゴリ名を取得
     */
    private static function getCategoryName($category) {
        $names = [
            LC_ALL => 'LC_ALL',
            LC_COLLATE => 'LC_COLLATE',
            LC_CTYPE => 'LC_CTYPE',
            LC_MONETARY => 'LC_MONETARY',
            LC_NUMERIC => 'LC_NUMERIC',
            LC_TIME => 'LC_TIME',
            LC_MESSAGES => 'LC_MESSAGES'
        ];
        
        return $names[$category] ?? 'UNKNOWN';
    }
}

// 使用例
echo "=== ロケールマネージャー ===\n";

// 現在のロケールを保存
$originalLocale = LocaleManager::push(LC_TIME);
echo "元のロケール: {$originalLocale}\n";

// 日本語に変更
setlocale(LC_TIME, 'ja_JP.UTF-8');
echo "変更後: " . strftime('%Y年%m月%d日') . "\n";

// 元に戻す
LocaleManager::pop();
echo "復元後のロケール: " . setlocale(LC_TIME, 0) . "\n";

// withLocaleを使用
echo "\n=== withLocale使用例 ===\n";

$result = LocaleManager::withLocale('ja_JP.UTF-8', function() {
    return strftime('%Y年%m月%d日 %A');
}, LC_TIME);

echo "一時的な日本語: {$result}\n";
echo "現在のロケール: " . setlocale(LC_TIME, 0) . "\n";

// 利用可能なロケールを検出
echo "\n=== 利用可能なロケール ===\n";
$testLocales = [
    'ja_JP.UTF-8',
    'en_US.UTF-8',
    'fr_FR.UTF-8',
    'de_DE.UTF-8',
    'invalid_locale'
];

$available = LocaleManager::getAvailableLocales($testLocales);
echo "利用可能: " . implode(', ', $available) . "\n";

// ロケール情報を取得
echo "\n=== ロケール情報 ===\n";
$info = LocaleManager::getInfo(LC_NUMERIC);
print_r($info);

例6: 国際化ヘルパー

class I18nHelper {
    /**
     * ユーザーの言語設定に基づいてロケールを設定
     */
    public static function setUserLocale($languageCode) {
        $localeMap = [
            'ja' => 'ja_JP.UTF-8',
            'en' => 'en_US.UTF-8',
            'fr' => 'fr_FR.UTF-8',
            'de' => 'de_DE.UTF-8',
            'es' => 'es_ES.UTF-8',
            'zh' => 'zh_CN.UTF-8',
            'ko' => 'ko_KR.UTF-8'
        ];
        
        $locale = $localeMap[$languageCode] ?? 'en_US.UTF-8';
        
        return setlocale(LC_ALL, $locale);
    }
    
    /**
     * ブラウザの言語設定からロケールを検出
     */
    public static function detectFromBrowser() {
        if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
            return 'en_US.UTF-8';
        }
        
        $languages = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
        $primaryLang = strtolower(substr($languages[0], 0, 2));
        
        return self::setUserLocale($primaryLang);
    }
    
    /**
     * 地域別の挨拶を取得
     */
    public static function getGreeting($locale) {
        $originalLocale = setlocale(LC_ALL, 0);
        setlocale(LC_ALL, $locale);
        
        $greetings = [
            'ja_JP' => 'こんにちは',
            'en_US' => 'Hello',
            'fr_FR' => 'Bonjour',
            'de_DE' => 'Guten Tag',
            'es_ES' => 'Hola',
            'zh_CN' => '你好',
            'ko_KR' => '안녕하세요'
        ];
        
        $currentLocale = setlocale(LC_ALL, 0);
        $localePrefix = substr($currentLocale, 0, 5);
        
        $greeting = $greetings[$localePrefix] ?? $greetings['en_US'];
        
        setlocale(LC_ALL, $originalLocale);
        
        return $greeting;
    }
    
    /**
     * ロケールに応じた日付表示
     */
    public static function formatLocalizedDate($timestamp, $languageCode) {
        self::setUserLocale($languageCode);
        
        $formats = [
            'ja' => '%Y年%m月%d日(%a)',
            'en' => '%B %d, %Y (%a)',
            'fr' => '%d %B %Y (%a)',
            'de' => '%d. %B %Y (%a)'
        ];
        
        $format = $formats[$languageCode] ?? $formats['en'];
        
        return strftime($format, $timestamp);
    }
}

// 使用例
echo "=== 国際化ヘルパー ===\n";

// 言語コードからロケール設定
$languages = ['ja', 'en', 'fr', 'de'];

foreach ($languages as $lang) {
    $locale = I18nHelper::setUserLocale($lang);
    $greeting = I18nHelper::getGreeting($locale);
    $date = I18nHelper::formatLocalizedDate(time(), $lang);
    
    echo "{$lang} ({$locale}):\n";
    echo "  挨拶: {$greeting}\n";
    echo "  日付: {$date}\n\n";
}

注意点と制限事項

// ロケールの利用可能性はシステムに依存
$result = setlocale(LC_ALL, 'ja_JP.UTF-8');

if ($result === false) {
    echo "このロケールはシステムにインストールされていません\n";
    
    // フォールバック
    setlocale(LC_ALL, 'C');  // Cロケール(最小限の標準ロケール)
}

// マルチスレッド環境での注意
// setlocale()はプロセス全体に影響するため、
// マルチスレッド環境では問題が発生する可能性がある

// strftime()はPHP 8.1で非推奨
// 代わりにIntlDateFormatterやDateTimeクラスの使用を推奨

代替手段(IntlDateFormatter)

// より推奨される方法(PHP 5.3+)
$formatter = new IntlDateFormatter(
    'ja_JP',
    IntlDateFormatter::LONG,
    IntlDateFormatter::NONE
);

echo $formatter->format(time()) . "\n";

// 通貨フォーマット
$formatter = new NumberFormatter('ja_JP', NumberFormatter::CURRENCY);
echo $formatter->formatCurrency(1234567, 'JPY') . "\n";

まとめ

setlocale()関数の特徴をまとめると:

できること:

  • 地域・言語設定の変更
  • 日付・時刻のローカライズ
  • 通貨・数値のフォーマット
  • 文字列比較の調整

推奨される使用場面:

  • 多言語対応アプリケーション
  • 国際化(i18n)
  • 地域別フォーマット
  • 日付・通貨表示

カテゴリ:

  • LC_ALL: すべて
  • LC_TIME: 日付・時刻
  • LC_MONETARY: 通貨
  • LC_NUMERIC: 数値
  • LC_COLLATE: 文字列比較

注意点:

  • システムにロケールがインストールされている必要がある
  • プロセス全体に影響する
  • マルチスレッド環境では注意が必要
  • strftime()はPHP 8.1で非推奨

代替手段:

  • IntlDateFormatter: 日付フォーマット
  • NumberFormatter: 数値・通貨フォーマット
  • Collator: 文字列比較

使い分け:

// シンプルな日付表示: setlocale() + strftime()
setlocale(LC_TIME, 'ja_JP.UTF-8');
echo strftime('%Y年%m月%d日');

// より高機能: IntlDateFormatter
$formatter = new IntlDateFormatter('ja_JP', IntlDateFormatter::LONG, IntlDateFormatter::NONE);
echo $formatter->format(time());

setlocale()は、地域に応じたフォーマットを簡単に実現できる便利な関数ですが、システム依存性が高いため、新しいプロジェクトではIntl拡張の使用を検討しましょう!

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