定制 teknyo/laravel-locale-helper 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

teknyo/laravel-locale-helper

Composer 安装命令:

composer require teknyo/laravel-locale-helper

包简介

A simple Laravel package for languages, timezones, currencies, date formats, first day of week, number formatting, countries, and phone numbers.

README 文档

README

A simple Laravel package providing languages, timezones, currencies, date formats, first-day-of-week management, and number formatting.

Installation

composer require teknyo/laravel-locale-helper
Publish the config:
php artisan vendor:publish --tag=locale-helper-config

Usage

  • Using the Facade
use Teknyo\LocaleHelper\Facades\LocaleHelper;
  • Using Dependency Injection
use Teknyo\LocaleHelper\Services\LocaleService;

public function __construct(protected LocaleService $locale) {}
  • Languages
// All languages (keyed by ISO 639-1 code)
$all = LocaleHelper::getLanguages();
// ['en' => ['name' => 'English', 'native' => 'English', 'rtl' => false], ...]

// Single language
LocaleHelper::getLanguage('fr');
// ['name' => 'French', 'native' => 'Français', 'rtl' => false]

// Simple name list for dropdowns
LocaleHelper::getLanguageNames();        // English names
LocaleHelper::getLanguageNames('native'); // Native names
  • Timezones
// All timezones with offset metadata
LocaleHelper::getTimezones();

// Single timezone
LocaleHelper::getTimezone('America/New_York');
// ['identifier' => 'America/New_York', 'offset_label' => 'UTC-05:00', ...]

// Grouped by continent
LocaleHelper::getTimezonesGroupedByRegion();

// Ready for <select> dropdown
LocaleHelper::getTimezonesForSelect();
  • Currencies
// All currencies
LocaleHelper::getCurrencies();

// Single currency
LocaleHelper::getCurrency('EUR');
// ['name' => 'Euro', 'symbol' => '€', 'decimals' => 2, 'symbol_first' => true]

// Just the symbol
LocaleHelper::getCurrencySymbol('GBP'); // '£'

// For dropdown
LocaleHelper::getCurrenciesForSelect();
  • Date Formats
// All formats with labels and examples
LocaleHelper::getDateFormats();

// For dropdown
LocaleHelper::getDateFormatsForSelect();

// Format a date
LocaleHelper::formatDate('2026-06-29', 'd/m/Y'); // '29/06/2026'
LocaleHelper::formatDate(now()); // uses config default
  • First Day of Week
// Get configured day (0=Sun .. 6=Sat)
LocaleHelper::getFirstDayOfWeek(); // 1 (Monday)

// Change at runtime
LocaleHelper::setFirstDayOfWeek(0);

// Get day name
LocaleHelper::getFirstDayOfWeekName(); // 'Sunday'

// Get ordered days starting from configured first day
LocaleHelper::getDaysOfWeek();
// [0 => 'Sunday', 1 => 'Monday', ..., 6 => 'Saturday']

// Auto-detect from locale
LocaleHelper::resolveFirstDayOfWeekForLocale('ar_SA'); // 6 (Saturday)
LocaleHelper::resolveFirstDayOfWeekForLocale('en_US'); // 0 (Sunday)
LocaleHelper::resolveFirstDayOfWeekForLocale('de');    // 1 (Monday)
  • Number Formatting
// Basic number
LocaleHelper::formatNumber(1234567.891);
// '1,234,567.89'

// Custom separators (European style)
LocaleHelper::formatNumber(1234567.89, 2, ',', '.');
// '1.234.567,89'

// Currency (uses intl if available)
LocaleHelper::formatCurrency(1234.5, 'USD', 'en_US'); // '$1,234.50'
LocaleHelper::formatCurrency(1234.5, 'EUR', 'de_DE'); // '1.234,50 €'

// Percentage
LocaleHelper::formatPercentage(85.456, 1); // '85.5%'

// Parse formatted number back to float
LocaleHelper::parseNumber('1,234,567.89'); // 1234567.89
LocaleHelper::parseNumber('1.234.567,89', ',', '.'); // 1234567.89
  • Countries
use Teknyo\LocaleHelper\Facades\CountryHelper;

// All countries
CountryHelper::getCountries();

// Single country
CountryHelper::getCountry('FR');
// ['name' => 'France', 'native' => 'France', 'phone' => '33', 'currency' => 'EUR', ...]

// Specific fields
CountryHelper::getCountryName('JP');           // 'Japan'
CountryHelper::getCountryName('JP', native: true); // '日本'
CountryHelper::getCountryFlag('BR');           // '🇧🇷'
CountryHelper::getCountryPhoneCode('DE');      // '49'
CountryHelper::getCountryCurrency('GB');       // 'GBP'

// Reverse lookups
CountryHelper::findByAlpha3('CAN');            // Canada data
CountryHelper::findByPhoneCode('1');           // ['US' => ..., 'CA' => ...]

// For dropdowns
CountryHelper::getCountriesForSelect();
// ['US' => '🇺🇸 United States (+1)', 'FR' => '🇫🇷 France (+33)', ...]

CountryHelper::getCountriesForSelect(withFlag: false, withPhone: false);
// ['US' => 'United States', 'FR' => 'France', ...]
  • Phone Numbers
use Teknyo\LocaleHelper\Facades\PhoneHelper;

// Get codes
PhoneHelper::getPhoneCodes();           // ['US' => '1', 'GB' => '44', ...]
PhoneHelper::getPhoneCodesGrouped();    // ['1' => ['US', 'CA'], '44' => ['GB'], ...]
PhoneHelper::getPhoneCode('FR');        // '33'

// Dropdown
PhoneHelper::getPhoneCodesForSelect();
// ['US' => '🇺🇸 United States (+1)', ...]

// Formatting
PhoneHelper::formatE164('(201) 555-0123', 'US');
// '+12015550123'

PhoneHelper::formatInternational('2015550123', 'US');
// '+1 (201) 555-0123'

PhoneHelper::formatNational('+12015550123', 'US');
// '(201) 555-0123'

PhoneHelper::formatNational('0612345678', 'FR');
// '06 12 34 56 78'

PhoneHelper::formatNational('0791234567', 'GB');
// '07912 345678'

// Validation
PhoneHelper::validate('2015550123', 'US');   // true  (10 digits)
PhoneHelper::validate('12345', 'US');        // false (too short)
PhoneHelper::validate('0612345678', 'FR');   // true  (9 digits)

PhoneHelper::isE164('+12015550123');         // true
PhoneHelper::isE164('2015550123');           // false

// Guess country from full number
PhoneHelper::guessCountry('+33612345678');   // 'FR'
PhoneHelper::guessCountry('+442071234567');  // 'GB'
PhoneHelper::guessCountry('+14155552671');   // 'US' (first match)

Include/Exclude Usage

  • Config - only expose a few currencies
// config/locale-helper.php
'currencies' => [
    'include' => ['USD', 'EUR', 'GBP'],
    'exclude' => [],
],
LocaleHelper::getCurrencies();
// → only USD, EUR, GBP

LocaleHelper::getCurrency('JPY');
// → null  (filtered out)

LocaleHelper::getCurrenciesForSelect();
// → 3 options only

// But formatting still works — uses raw data internally
LocaleHelper::formatCurrency(1000, 'JPY', 'ja_JP');
// → '¥1,000'
  • Config - exclude a few languages
'languages' => [
    'include' => [],
    'exclude' => ['la', 'sa', 'eo'],
],
LocaleHelper::getLanguageNames();
// → all languages EXCEPT Latin, Sanskrit, Esperanto

LocaleHelper::getLanguage('la');
// → null
  • Config - exclude some timezones
'timezones' => [
    'include' => [],
    'exclude' => [
        'Antarctica/McMurdo',
        'Antarctica/South_Pole',
    ],
],
LocaleHelper::getTimezonesGroupedByRegion();
// → all regions, minus the two excluded zones
  • Config - include/exclude countries and phone Restrict to EU countries only
'countries' => [
    'include' => [
        'AT','BE','BG','HR','CY','CZ','DK','EE','FI','FR','DE','GR',
        'HU','IE','IT','LV','LT','LU','MT','NL','PL','PT','RO','SK',
        'SI','ES','SE',
    ],
],
'phones' => [
    'include' => [
        'AT','BE','BG','HR','CY','CZ','DK','EE','FI','FR','DE','GR',
        'HU','IE','IT','LV','LT','LU','MT','NL','PL','PT','RO','SK',
        'SI','ES','SE',
    ],
],

Exclude specific countries

'countries' => [
    'exclude' => ['KP', 'IR', 'SY'],
],

Runtime Override

If you need to change filters at runtime, you can merge config on the fly:

config([
    'locale-helper.currencies.include' => ['USD', 'AED'],
]);

// Rebuild the singleton so it picks up new config
app()->forgetInstance('locale-helper');

LocaleHelper::getCurrencies(); // now only USD + AED

Or expose a fluent helper on the service:

public function withCurrencies(array $codes): static
{
    $clone = clone $this;
    $clone->config['currencies']['include'] = $codes;
    $clone->config['currencies']['exclude'] = [];
    return $clone;
}

// Usage
$clone = LocaleHelper::withCurrencies(['USD', 'EUR']);
$clone->getCurrencies(); // only USD + EUR

Quick-Start Blade Example

{{-- Language selector --}}
<select name="language">
    @foreach(\Teknyo\LocaleHelper\Facades\LocaleHelper::getLanguageNames('native') as $code => $name)
        <option value="{{ $code }}" @selected(app()->getLocale() === $code)>
            {{ $name }}
        </option>
    @endforeach
</select>

{{-- Timezone selector --}}
<select name="timezone">
    @foreach(\Teknyo\LocaleHelper\Facades\LocaleHelper::getTimezonesForSelect() as $id => $label)
        <option value="{{ $id }}">{{ $label }}</option>
    @endforeach
</select>

{{-- Currency display --}}
<span>{{ \Teknyo\LocaleHelper\Facades\LocaleHelper::formatCurrency($order->total, 'USD') }}</span>
  • Phone Input Integration

This guide explains how to implement a user-friendly phone number input that combines a country code selector with a local number input, ultimately submitting a standardized E.164 formatted phone number to your backend.

  1. The HTML Structure (Required for both methods) Regardless of which JavaScript method you choose, you need the following HTML structure in your Blade template. The <select> element must include a data-phone-code attribute containing the numeric dialing code, which the JavaScript will use to construct the final E.164 number.
<div class="phone-input">
    <select name="phone_country" id="phone_country">
        @foreach(\Teknyo\LocaleHelper\Facades\PhoneHelper::getPhoneCodesForSelect() as $code => $label)
            <option value="{{ $code }}" data-phone-code="{{ \Teknyo\LocaleHelper\Facades\CountryHelper::getCountryPhoneCode($code) }}">
                {{ $label }}
            </option>
        @endforeach
    </select>

    <input type="tel" name="phone_number" id="phone_number" placeholder="Phone number">
</div>

Method 1: Using the @localhelperscripts Blade Directive (Recommended) The package provides a custom Blade directive that automatically injects the necessary JavaScript to handle the input, format the number, and inject a hidden field containing the final E.164 formatted number.

Basic Usage If your HTML elements use the default IDs (phone_country and phone_number), simply add the directive at the bottom of your form or page:

<form action="/submit" method="POST">
    <!-- HTML Structure from above -->
    
    @csrf
    
    <!-- Injects the script with default settings -->
    @localhelperscripts
    
    <button type="submit">Submit</button>
</form>

What it does: Listens for changes on the select and input fields. Creates a hidden input named phone (by default). Populates it with the E.164 format (e.g., +12015550123) right before the form submits.

Advanced Usage (Custom IDs & Names) If you have multiple phone inputs on the same page (e.g., Billing and Shipping) or need to change the hidden field name, pass parameters to the directive:

@localhelperscripts('select_id', 'input_id', 'hidden_field_name')
  • Example: Billing Phone
<select id="billing_phone_country">...</select>
<input type="tel" id="billing_phone_number">

@localhelperscripts('billing_phone_country', 'billing_phone_number', 'billing_phone')
  • Example: Shipping Phone
<select id="shipping_phone_country">...</select>
<input type="tel" id="shipping_phone_number">

@localhelperscripts('shipping_phone_country', 'shipping_phone_number', 'shipping_phone')

Method 2: Manual Script Injection (Normal Script Way) If you prefer not to use the Blade directive, or if you are working in an environment where Blade directives aren't available (e.g., a standalone Vue/React component or a raw HTML file), you can inject the JavaScript manually.

Basic Implementation Add the following script block to your Blade template, typically inside an @push('scripts') block if you are using a layout that yields scripts at the bottom of the page.

@push('scripts')
<script>
(function() {
    // 1. Define your element IDs and hidden field name
    const SELECT_ID   = 'phone_country';
    const INPUT_ID    = 'phone_number';
    const HIDDEN_NAME = 'phone';

    const selectEl = document.getElementById(SELECT_ID);
    const inputEl  = document.getElementById(INPUT_ID);
    const form     = selectEl ? selectEl.closest('form') : null;

    if (!selectEl || !inputEl || !form) return;

    // 2. Function to calculate and update the E.164 number
    function updateHiddenField() {
        const selectedOption = selectEl.options[selectEl.selectedIndex];
        const code = selectedOption ? (selectedOption.dataset.phoneCode || '') : '';
        const digits = inputEl.value.replace(/\D/g, '');

        // Find existing hidden field or create a new one
        let hidden = form.querySelector(`input[name="${HIDDEN_NAME}"]`);
        if (!hidden) {
            hidden = document.createElement('input');
            hidden.type = 'hidden';
            hidden.name = HIDDEN_NAME;
            form.appendChild(hidden);
        }
        
        // Set the value (E.164 format)
        hidden.value = code ? '+' + code + digits : digits;
    }

    // 3. Attach event listeners
    form.addEventListener('submit', updateHiddenField); // Safety net on submit
    inputEl.addEventListener('input', updateHiddenField); // Real-time update
    selectEl.addEventListener('change', updateHiddenField); // Update on country change
    
    // 4. Initial run (useful for validation error page reloads)
    updateHiddenField();
})();
</script>
@endpush

Customizing the Manual Script To use this for a different set of inputs, simply change the constants at the top of the script:

const SELECT_ID   = 'billing_phone_country';
const INPUT_ID    = 'billing_phone_number';
const HIDDEN_NAME = 'billing_phone';
Note: If you have multiple phone inputs on the same page using the manual method, you
must duplicate this script block for each input and ensure the IDs and HIDDEN_NAME are
unique for each block to avoid conflicts.

Handling the Data in Laravel (Backend) Regardless of which frontend method you use, the form will submit a hidden field containing the fully formatted E.164 phone number. Here is how to handle it in your Laravel Controller:

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Teknyo\LocaleHelper\Facades\PhoneHelper;

class UserController extends Controller
{
    public function store(Request $request)
    {
        $request->validate([
            'phone_country' => 'required|string|max:2',
            'phone_number'  => 'required|string',
            'phone'         => 'required|string', // The hidden E.164 field
        ]);

        $e164Number  = $request->input('phone');
        $countryCode = $request->input('phone_country');

        // Optional: Validate the phone number length/format using the package
        if (! PhoneHelper::validate($e164Number, $countryCode)) {
            return back()->withErrors(['phone' => 'The phone number format is invalid for the selected country.']);
        }

        // Optional: Verify it's strictly E.164
        if (! PhoneHelper::isE164($e164Number)) {
             return back()->withErrors(['phone' => 'Invalid phone format.']);
        }

        // Save the user
        // User::create(['phone' => $e164Number, ...]);

        return redirect()->back()->with('success', 'Phone number saved!');
    }
}

License

MIT

统计信息

  • 总下载量: 1
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 1
  • 点击次数: 1
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-06-29