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.
- 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
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-29