Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanvermeyen committed Mar 14, 2023
1 parent de828f6 commit 824be87
Show file tree
Hide file tree
Showing 8 changed files with 359 additions and 8 deletions.
76 changes: 73 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@

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

Translate a URI or individual slugs, excluding route parameters.
Translate a URI or individual slugs.

This package registers a macro for the Laravel `Translator` class.
This will allow you to translate individual URI slugs, while ignoring parameter placeholders.
Parameters will not be translated by this macro. That remains the responsibility of your code.

## ✅ Requirements

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

Laravel will automatically register the ServiceProvider.

### Usage
Then in your app's `lang` folder, create subdirectories for every locale you want to have translations for.
Next create a `routes.php` file in each of those directories.

```
lang/
├── en/
│ └── routes.php
└── nl/
└── routes.php
```

Return an array of translations from the `routes.php` file.

### 🚀 Usage

Use the `Lang::uri()` macro when registering routes:

```php
Route::get(Lang::uri('hello/world'), [Controller::class, 'index']);
```

The URI macro accepts 2 additional parameters:

1. A locale, in case you need translations to a locale other than the current app locale.
2. A namespace, in case your translation files reside in a package.

```php
Lang::uri('hello/world', 'fr', 'my-package');
```

You can also use `trans()->uri('hello/world')` instead of `Lang::uri()`.

### 🔌 Example

Using these example translations:

```php
// lang/nl/routes.php
return [
'hello' => 'hallo',
'world' => 'wereld',
'override/hello/world' => 'something/very/different',
'hello/world/{parameter}' => 'uri/with/{parameter}',
];
```

...
These are possible translation results:

```php
// Translate every slug individually
// Translates to: 'hallo/wereld'
Lang::uri('hello/world');

// Keep original slug when missing translation
// Translates to: 'hallo/big/wereld'
Lang::uri('hello/big/world');

// Translate slugs, but not parameter placeholders
// Translates to: 'hallo/{world}'
Lang::uri('hello/{world}');

// Translate full URIs if an exact translation exists
// Translates to: 'something/very/different'
Lang::uri('override/hello/world');

// Translate full URIs if an exact translation exists (with placeholder)
// Translates to: 'uri/with/{parameter}'
Lang::uri('hello/world/{parameter}');
```

## 🚧 Testing

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codezero/laravel-uri-translator",
"description": "Translate a URI or individual slugs, excluding route parameters.",
"description": "Translate a URI or individual slugs.",
"keywords": [
"php",
"laravel",
Expand Down
3 changes: 0 additions & 3 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
Expand Down
22 changes: 22 additions & 0 deletions src/Macros/Lang/UriMacro.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace CodeZero\UriTranslator\Macros\Lang;

use CodeZero\UriTranslator\UriTranslator;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Lang;

class UriMacro
{
/**
* Register the macro.
*
* @return void
*/
public static function register()
{
Lang::macro('uri', function ($uri, $locale = null, $namespace = null) {
return App::make(UriTranslator::class)->translate($uri, $locale, $namespace);
});
}
}
89 changes: 89 additions & 0 deletions src/UriTranslator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

namespace CodeZero\UriTranslator;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Str;

class UriTranslator
{
/**
* Translate a URI.
*
* @param string $uri
* @param string|null $locale
* @param string|null $namespace
*
* @return string
*/
public function translate($uri, $locale = null, $namespace = null)
{
$fullUriKey = $this->buildTranslationKey($uri, $namespace);

// Attempt to translate the full URI.
if (Lang::has($fullUriKey, $locale)) {
return Lang::get($fullUriKey, [], $locale);
}

$segments = $this->splitUriIntoSegments($uri);

// Attempt to translate each segment individually. If there is no translation
// for a specific segment, then its original value will be used.
$translations = $segments->map(function ($segment) use ($locale, $namespace) {
$segmentKey = $this->buildTranslationKey($segment, $namespace);

// If the segment is not a placeholder and the segment
// has a translation, then update the segment.
if ( ! Str::startsWith($segment, '{') && Lang::has($segmentKey, $locale)) {
$segment = Lang::get($segmentKey, [], $locale);
}

return $segment;
});

// Rebuild the URI from the translated segments.
return $translations->implode('/');
}

/**
* Split the URI into a Collection of segments.
*
* @param string $uri
*
* @return \Illuminate\Support\Collection
*/
protected function splitUriIntoSegments($uri)
{
$uri = trim($uri, '/');
$segments = explode('/', $uri);

return Collection::make($segments);
}

/**
* Build a translation key, including the namespace and file name.
*
* @param string $key
* @param string|null $namespace
*
* @return string
*/
protected function buildTranslationKey($key, $namespace)
{
$namespace = $namespace ? "{$namespace}::" : '';
$file = $this->getTranslationFileName();

return "{$namespace}{$file}.{$key}";
}

/**
* Get the file name that holds the URI translations.
*
* @return string
*/
protected function getTranslationFileName()
{
return 'routes';
}
}
3 changes: 2 additions & 1 deletion src/UriTranslatorServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace CodeZero\UriTranslator;

use CodeZero\UriTranslator\Macros\Lang\UriMacro;
use Illuminate\Support\ServiceProvider;

class UriTranslatorServiceProvider extends ServiceProvider
Expand All @@ -13,7 +14,7 @@ class UriTranslatorServiceProvider extends ServiceProvider
*/
public function boot()
{
//
UriMacro::register();
}

/**
Expand Down
140 changes: 140 additions & 0 deletions tests/Feature/UriTranslatorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<?php

namespace CodeZero\UriTranslator\Tests\Feature;

use CodeZero\UriTranslator\Tests\TestCase;
use Illuminate\Support\Facades\Lang;

class UriTranslatorTest extends TestCase
{
/** @test */
public function it_translates_every_segment_in_a_uri_to_the_current_locale()
{
$this->setTranslations([
'nl' => [
'my' => 'mijn',
'new' => 'nieuwe',
'page' => 'pagina',
]
]);

$this->setAppLocale('en');
$this->assertEquals('my/new/page', Lang::uri('my/new/page'));

$this->setAppLocale('nl');
$this->assertEquals('mijn/nieuwe/pagina', Lang::uri('my/new/page'));
$this->assertEquals('mijn/nieuwe/pagina', trans()->uri('my/new/page'));
}

/** @test */
public function it_translates_every_segment_in_a_uri_to_the_given_locale()
{
$this->setTranslations([
'nl' => [
'my' => 'mijn',
'new' => 'nieuwe',
'page' => 'pagina',
]
]);

$this->assertEquals('mijn/nieuwe/pagina', Lang::uri('my/new/page', 'nl'));
}

/** @test */
public function it_uses_the_original_values_if_a_translation_does_not_exist()
{
$this->setTranslations([
'nl' => [
'my' => 'mijn',
'new' => 'nieuwe',
]
]);

$this->assertEquals('mijn/nieuwe/page', Lang::uri('my/new/page', 'nl'));
$this->assertEquals('my/new/page', Lang::uri('my/new/page', 'fr'));
}

/** @test */
public function it_ignores_trailing_slashes()
{
$this->setTranslations([
'nl' => [
'my' => 'mijn',
'new' => 'nieuwe',
'page' => 'pagina',
]
]);

$this->assertEquals('mijn/nieuwe/pagina', Lang::uri('/my/new/page/', 'nl'));
}

/** @test */
public function it_skips_placeholders_in_a_uri()
{
$this->setTranslations([
'nl' => [
'articles' => 'artikels',
]
]);

$this->assertEquals('artikels/{articles}', Lang::uri('articles/{articles}', 'nl'));
}

/** @test */
public function you_can_translate_a_full_uri()
{
$this->setTranslations([
'nl' => [
'glass' => 'glas',
'products' => 'producten',
'products/glass' => 'producten/glazen'
]
]);

$this->assertEquals('producten/glazen', Lang::uri('products/glass', 'nl'));
}

/** @test */
public function you_can_translate_a_full_uri_with_placeholder()
{
$this->setTranslations([
'nl' => [
'glass' => 'glas',
'products' => 'producten',
'products/glass/{type}' => 'producten/glazen/{type}'
]
]);

$this->assertEquals('producten/glazen/{type}', Lang::uri('products/glass/{type}', 'nl'));
}

/** @test */
public function you_can_specify_a_namespace()
{
$this->setTranslations([
'nl' => [
'articles' => 'artikels',
]
], 'blog');

$this->assertEquals('artikels/{article}', Lang::uri('articles/{article}', 'nl', 'blog'));
}

/** @test */
public function the_uri_macro_is_available_via_the_trans_helper()
{
$this->setTranslations([
'nl' => [
'my' => 'mijn',
'new' => 'nieuwe',
'page' => 'pagina',
]
]);

$this->setAppLocale('en');
$this->assertEquals('my/new/page', trans()->uri('my/new/page'));

$this->setAppLocale('nl');
$this->assertEquals('mijn/nieuwe/pagina', trans()->uri('my/new/page'));
}
}
Loading

0 comments on commit 824be87

Please sign in to comment.