Skip to content

Commit 824be87

Browse files
committed
First commit
1 parent de828f6 commit 824be87

8 files changed

+359
-8
lines changed

README.md

+73-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010

1111
[![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/R6R3UQ8V)
1212

13-
Translate a URI or individual slugs, excluding route parameters.
13+
Translate a URI or individual slugs.
14+
15+
This package registers a macro for the Laravel `Translator` class.
16+
This will allow you to translate individual URI slugs, while ignoring parameter placeholders.
17+
Parameters will not be translated by this macro. That remains the responsibility of your code.
1418

1519
## ✅ Requirements
1620

@@ -27,9 +31,75 @@ composer require codezero/laravel-uri-translator
2731

2832
Laravel will automatically register the ServiceProvider.
2933

30-
### Usage
34+
Then in your app's `lang` folder, create subdirectories for every locale you want to have translations for.
35+
Next create a `routes.php` file in each of those directories.
36+
37+
```
38+
lang/
39+
├── en/
40+
│ └── routes.php
41+
└── nl/
42+
└── routes.php
43+
```
44+
45+
Return an array of translations from the `routes.php` file.
46+
47+
### 🚀 Usage
48+
49+
Use the `Lang::uri()` macro when registering routes:
50+
51+
```php
52+
Route::get(Lang::uri('hello/world'), [Controller::class, 'index']);
53+
```
54+
55+
The URI macro accepts 2 additional parameters:
56+
57+
1. A locale, in case you need translations to a locale other than the current app locale.
58+
2. A namespace, in case your translation files reside in a package.
59+
60+
```php
61+
Lang::uri('hello/world', 'fr', 'my-package');
62+
```
63+
64+
You can also use `trans()->uri('hello/world')` instead of `Lang::uri()`.
65+
66+
### 🔌 Example
67+
68+
Using these example translations:
69+
70+
```php
71+
// lang/nl/routes.php
72+
return [
73+
'hello' => 'hallo',
74+
'world' => 'wereld',
75+
'override/hello/world' => 'something/very/different',
76+
'hello/world/{parameter}' => 'uri/with/{parameter}',
77+
];
78+
```
3179

32-
...
80+
These are possible translation results:
81+
82+
```php
83+
// Translate every slug individually
84+
// Translates to: 'hallo/wereld'
85+
Lang::uri('hello/world');
86+
87+
// Keep original slug when missing translation
88+
// Translates to: 'hallo/big/wereld'
89+
Lang::uri('hello/big/world');
90+
91+
// Translate slugs, but not parameter placeholders
92+
// Translates to: 'hallo/{world}'
93+
Lang::uri('hello/{world}');
94+
95+
// Translate full URIs if an exact translation exists
96+
// Translates to: 'something/very/different'
97+
Lang::uri('override/hello/world');
98+
99+
// Translate full URIs if an exact translation exists (with placeholder)
100+
// Translates to: 'uri/with/{parameter}'
101+
Lang::uri('hello/world/{parameter}');
102+
```
33103

34104
## 🚧 Testing
35105

composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codezero/laravel-uri-translator",
3-
"description": "Translate a URI or individual slugs, excluding route parameters.",
3+
"description": "Translate a URI or individual slugs.",
44
"keywords": [
55
"php",
66
"laravel",

phpunit.xml.dist

-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@
99
processIsolation="false"
1010
stopOnFailure="false">
1111
<testsuites>
12-
<testsuite name="Unit">
13-
<directory suffix="Test.php">./tests/Unit</directory>
14-
</testsuite>
1512
<testsuite name="Feature">
1613
<directory suffix="Test.php">./tests/Feature</directory>
1714
</testsuite>

src/Macros/Lang/UriMacro.php

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace CodeZero\UriTranslator\Macros\Lang;
4+
5+
use CodeZero\UriTranslator\UriTranslator;
6+
use Illuminate\Support\Facades\App;
7+
use Illuminate\Support\Facades\Lang;
8+
9+
class UriMacro
10+
{
11+
/**
12+
* Register the macro.
13+
*
14+
* @return void
15+
*/
16+
public static function register()
17+
{
18+
Lang::macro('uri', function ($uri, $locale = null, $namespace = null) {
19+
return App::make(UriTranslator::class)->translate($uri, $locale, $namespace);
20+
});
21+
}
22+
}

src/UriTranslator.php

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
3+
namespace CodeZero\UriTranslator;
4+
5+
use Illuminate\Support\Collection;
6+
use Illuminate\Support\Facades\Lang;
7+
use Illuminate\Support\Str;
8+
9+
class UriTranslator
10+
{
11+
/**
12+
* Translate a URI.
13+
*
14+
* @param string $uri
15+
* @param string|null $locale
16+
* @param string|null $namespace
17+
*
18+
* @return string
19+
*/
20+
public function translate($uri, $locale = null, $namespace = null)
21+
{
22+
$fullUriKey = $this->buildTranslationKey($uri, $namespace);
23+
24+
// Attempt to translate the full URI.
25+
if (Lang::has($fullUriKey, $locale)) {
26+
return Lang::get($fullUriKey, [], $locale);
27+
}
28+
29+
$segments = $this->splitUriIntoSegments($uri);
30+
31+
// Attempt to translate each segment individually. If there is no translation
32+
// for a specific segment, then its original value will be used.
33+
$translations = $segments->map(function ($segment) use ($locale, $namespace) {
34+
$segmentKey = $this->buildTranslationKey($segment, $namespace);
35+
36+
// If the segment is not a placeholder and the segment
37+
// has a translation, then update the segment.
38+
if ( ! Str::startsWith($segment, '{') && Lang::has($segmentKey, $locale)) {
39+
$segment = Lang::get($segmentKey, [], $locale);
40+
}
41+
42+
return $segment;
43+
});
44+
45+
// Rebuild the URI from the translated segments.
46+
return $translations->implode('/');
47+
}
48+
49+
/**
50+
* Split the URI into a Collection of segments.
51+
*
52+
* @param string $uri
53+
*
54+
* @return \Illuminate\Support\Collection
55+
*/
56+
protected function splitUriIntoSegments($uri)
57+
{
58+
$uri = trim($uri, '/');
59+
$segments = explode('/', $uri);
60+
61+
return Collection::make($segments);
62+
}
63+
64+
/**
65+
* Build a translation key, including the namespace and file name.
66+
*
67+
* @param string $key
68+
* @param string|null $namespace
69+
*
70+
* @return string
71+
*/
72+
protected function buildTranslationKey($key, $namespace)
73+
{
74+
$namespace = $namespace ? "{$namespace}::" : '';
75+
$file = $this->getTranslationFileName();
76+
77+
return "{$namespace}{$file}.{$key}";
78+
}
79+
80+
/**
81+
* Get the file name that holds the URI translations.
82+
*
83+
* @return string
84+
*/
85+
protected function getTranslationFileName()
86+
{
87+
return 'routes';
88+
}
89+
}

src/UriTranslatorServiceProvider.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace CodeZero\UriTranslator;
44

5+
use CodeZero\UriTranslator\Macros\Lang\UriMacro;
56
use Illuminate\Support\ServiceProvider;
67

78
class UriTranslatorServiceProvider extends ServiceProvider
@@ -13,7 +14,7 @@ class UriTranslatorServiceProvider extends ServiceProvider
1314
*/
1415
public function boot()
1516
{
16-
//
17+
UriMacro::register();
1718
}
1819

1920
/**

tests/Feature/UriTranslatorTest.php

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
<?php
2+
3+
namespace CodeZero\UriTranslator\Tests\Feature;
4+
5+
use CodeZero\UriTranslator\Tests\TestCase;
6+
use Illuminate\Support\Facades\Lang;
7+
8+
class UriTranslatorTest extends TestCase
9+
{
10+
/** @test */
11+
public function it_translates_every_segment_in_a_uri_to_the_current_locale()
12+
{
13+
$this->setTranslations([
14+
'nl' => [
15+
'my' => 'mijn',
16+
'new' => 'nieuwe',
17+
'page' => 'pagina',
18+
]
19+
]);
20+
21+
$this->setAppLocale('en');
22+
$this->assertEquals('my/new/page', Lang::uri('my/new/page'));
23+
24+
$this->setAppLocale('nl');
25+
$this->assertEquals('mijn/nieuwe/pagina', Lang::uri('my/new/page'));
26+
$this->assertEquals('mijn/nieuwe/pagina', trans()->uri('my/new/page'));
27+
}
28+
29+
/** @test */
30+
public function it_translates_every_segment_in_a_uri_to_the_given_locale()
31+
{
32+
$this->setTranslations([
33+
'nl' => [
34+
'my' => 'mijn',
35+
'new' => 'nieuwe',
36+
'page' => 'pagina',
37+
]
38+
]);
39+
40+
$this->assertEquals('mijn/nieuwe/pagina', Lang::uri('my/new/page', 'nl'));
41+
}
42+
43+
/** @test */
44+
public function it_uses_the_original_values_if_a_translation_does_not_exist()
45+
{
46+
$this->setTranslations([
47+
'nl' => [
48+
'my' => 'mijn',
49+
'new' => 'nieuwe',
50+
]
51+
]);
52+
53+
$this->assertEquals('mijn/nieuwe/page', Lang::uri('my/new/page', 'nl'));
54+
$this->assertEquals('my/new/page', Lang::uri('my/new/page', 'fr'));
55+
}
56+
57+
/** @test */
58+
public function it_ignores_trailing_slashes()
59+
{
60+
$this->setTranslations([
61+
'nl' => [
62+
'my' => 'mijn',
63+
'new' => 'nieuwe',
64+
'page' => 'pagina',
65+
]
66+
]);
67+
68+
$this->assertEquals('mijn/nieuwe/pagina', Lang::uri('/my/new/page/', 'nl'));
69+
}
70+
71+
/** @test */
72+
public function it_skips_placeholders_in_a_uri()
73+
{
74+
$this->setTranslations([
75+
'nl' => [
76+
'articles' => 'artikels',
77+
]
78+
]);
79+
80+
$this->assertEquals('artikels/{articles}', Lang::uri('articles/{articles}', 'nl'));
81+
}
82+
83+
/** @test */
84+
public function you_can_translate_a_full_uri()
85+
{
86+
$this->setTranslations([
87+
'nl' => [
88+
'glass' => 'glas',
89+
'products' => 'producten',
90+
'products/glass' => 'producten/glazen'
91+
]
92+
]);
93+
94+
$this->assertEquals('producten/glazen', Lang::uri('products/glass', 'nl'));
95+
}
96+
97+
/** @test */
98+
public function you_can_translate_a_full_uri_with_placeholder()
99+
{
100+
$this->setTranslations([
101+
'nl' => [
102+
'glass' => 'glas',
103+
'products' => 'producten',
104+
'products/glass/{type}' => 'producten/glazen/{type}'
105+
]
106+
]);
107+
108+
$this->assertEquals('producten/glazen/{type}', Lang::uri('products/glass/{type}', 'nl'));
109+
}
110+
111+
/** @test */
112+
public function you_can_specify_a_namespace()
113+
{
114+
$this->setTranslations([
115+
'nl' => [
116+
'articles' => 'artikels',
117+
]
118+
], 'blog');
119+
120+
$this->assertEquals('artikels/{article}', Lang::uri('articles/{article}', 'nl', 'blog'));
121+
}
122+
123+
/** @test */
124+
public function the_uri_macro_is_available_via_the_trans_helper()
125+
{
126+
$this->setTranslations([
127+
'nl' => [
128+
'my' => 'mijn',
129+
'new' => 'nieuwe',
130+
'page' => 'pagina',
131+
]
132+
]);
133+
134+
$this->setAppLocale('en');
135+
$this->assertEquals('my/new/page', trans()->uri('my/new/page'));
136+
137+
$this->setAppLocale('nl');
138+
$this->assertEquals('mijn/nieuwe/pagina', trans()->uri('my/new/page'));
139+
}
140+
}

0 commit comments

Comments
 (0)