mirror of
https://github.com/InvoiceShelf/InvoiceShelf.git
synced 2026-07-16 13:55:21 +00:00
feat: localize country names in PDFs via symfony/intl (#681)
Ports #639 to 3.x — the original targets the now feature-frozen 2.x line. Address::country_name now resolves the localized country name for the current app locale via Symfony\Component\Intl\Countries, falling back to the stored name on lookup failure. Adds symfony/intl + a unit test. Co-authored-by: Lukas Selch <selchlukas@icloud.com>
This commit is contained in:
committed by
GitHub
parent
acbb1f040c
commit
217deb0bf9
@@ -5,6 +5,7 @@ namespace App\Models;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Symfony\Component\Intl\Countries;
|
||||
|
||||
class Address extends Model
|
||||
{
|
||||
@@ -18,9 +19,18 @@ class Address extends Model
|
||||
|
||||
public function getCountryNameAttribute(): ?string
|
||||
{
|
||||
$name = $this->country ? $this->country->name : null;
|
||||
if (! $this->country) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $name;
|
||||
try {
|
||||
return Countries::getName(
|
||||
$this->country->code,
|
||||
app()->getLocale()
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
return $this->country->name;
|
||||
}
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"spatie/laravel-backup": "^10.0",
|
||||
"spatie/laravel-medialibrary": "^11.11",
|
||||
"symfony/http-client": "^7.3",
|
||||
"symfony/intl": "^7.3",
|
||||
"symfony/mailer": "^7.3",
|
||||
"symfony/mailgun-mailer": "^7.3",
|
||||
"symfony/postmark-mailer": "^7.3"
|
||||
|
||||
92
composer.lock
generated
92
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "bc1914fedd271f614d922d716ab0e3be",
|
||||
"content-hash": "c1dac5a39ce45f742d64a45877782655",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aws/aws-crt-php",
|
||||
@@ -6751,6 +6751,96 @@
|
||||
],
|
||||
"time": "2026-05-29T08:46:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/intl",
|
||||
"version": "v7.4.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/intl.git",
|
||||
"reference": "7cfb7792d580dea833647420afd5f2f98df8457b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/intl/zipball/7cfb7792d580dea833647420afd5f2f98df8457b",
|
||||
"reference": "7cfb7792d580dea833647420afd5f2f98df8457b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"symfony/deprecation-contracts": "^2.5|^3"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/string": "<7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/filesystem": "^6.4|^7.0|^8.0",
|
||||
"symfony/var-exporter": "^6.4|^7.0|^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Intl\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/",
|
||||
"/Resources/data/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Bernhard Schussek",
|
||||
"email": "bschussek@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Eriksen Costa",
|
||||
"email": "eriksen.costa@infranology.com.br"
|
||||
},
|
||||
{
|
||||
"name": "Igor Wiedler",
|
||||
"email": "igor@wiedler.ch"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Provides access to the localization data of the ICU library",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"i18n",
|
||||
"icu",
|
||||
"internationalization",
|
||||
"intl",
|
||||
"l10n",
|
||||
"localization"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/intl/tree/v7.4.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-03-30T12:55:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/mailer",
|
||||
"version": "v7.4.12",
|
||||
|
||||
57
tests/Unit/AddressCountryLocalizationTest.php
Normal file
57
tests/Unit/AddressCountryLocalizationTest.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Address;
|
||||
use App\Models\Country;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
|
||||
beforeEach(function () {
|
||||
Artisan::call('db:seed', ['--class' => 'DatabaseSeeder', '--force' => true]);
|
||||
Artisan::call('db:seed', ['--class' => 'DemoSeeder', '--force' => true]);
|
||||
});
|
||||
|
||||
it('returns localized country name for German locale', function () {
|
||||
App::setLocale('de');
|
||||
|
||||
$address = Address::factory()->create([
|
||||
'country_id' => Country::where('code', 'DE')->first()->id,
|
||||
]);
|
||||
|
||||
expect($address->country_name)->toBe('Deutschland');
|
||||
});
|
||||
|
||||
it('returns localized country name for French locale', function () {
|
||||
App::setLocale('fr');
|
||||
|
||||
$address = Address::factory()->create([
|
||||
'country_id' => Country::where('code', 'DE')->first()->id,
|
||||
]);
|
||||
|
||||
expect($address->country_name)->toBe('Allemagne');
|
||||
});
|
||||
|
||||
it('returns null when address has no country', function () {
|
||||
$address = Address::factory()->create([
|
||||
'country_id' => null,
|
||||
]);
|
||||
|
||||
expect($address->country_name)->toBeNull();
|
||||
});
|
||||
|
||||
it('falls back to database name when intl lookup fails', function () {
|
||||
App::setLocale('de');
|
||||
|
||||
// Create a country with a code that won't be found in ICU data
|
||||
$country = new Country;
|
||||
$country->timestamps = false;
|
||||
$country->code = 'XX';
|
||||
$country->name = 'Unknown Land';
|
||||
$country->phonecode = 0;
|
||||
$country->save();
|
||||
|
||||
$address = Address::factory()->create([
|
||||
'country_id' => $country->id,
|
||||
]);
|
||||
|
||||
expect($address->country_name)->toBe('Unknown Land');
|
||||
});
|
||||
Reference in New Issue
Block a user