Skip to content

Commit 99a2e9e

Browse files
committed
Initial commit
1 parent e31cf22 commit 99a2e9e

7 files changed

+314
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/vendor
2+
composer.lock
3+
.idea

composer.json

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "hpolthof/laravel-translations-db",
3+
"description": "A database translations implementation for Laravel 5.",
4+
"license": "GPL2",
5+
"keywords": ["laravel", "translation", "localization", "database"],
6+
"authors": [
7+
{
8+
"name": "Paul Olthof",
9+
"email": "[email protected]"
10+
}
11+
],
12+
"require": {
13+
"php": ">=5.4.0",
14+
"illuminate/translation": "5.*",
15+
"illuminate/database": "5.*",
16+
"illuminate/cache": "5.*"
17+
},
18+
19+
"autoload": {
20+
"psr-4": {
21+
"Hpolthof\\Translation\\": "src"
22+
}
23+
},
24+
"extra": {
25+
"branch-alias": {
26+
"dev-master": "0.1.x-dev"
27+
}
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
use Illuminate\Database\Schema\Blueprint;
4+
use Illuminate\Database\Migrations\Migration;
5+
6+
class AddTranslationsTable extends Migration {
7+
8+
/**
9+
* Run the migrations.
10+
*
11+
* @return void
12+
*/
13+
public function up()
14+
{
15+
Schema::create('translations', function(Blueprint $table)
16+
{
17+
$table->increments('id');
18+
$table->string('locale');
19+
$table->string('group');
20+
$table->string('name');
21+
$table->text('value')->nullable();
22+
$table->timestamp('viewed_at')->nullable();
23+
$table->timestamps();
24+
25+
$table->index(['locale', 'group']);
26+
$table->unique(['locale', 'group', 'name']);
27+
});
28+
}
29+
30+
/**
31+
* Reverse the migrations.
32+
*
33+
* @return void
34+
*/
35+
public function down()
36+
{
37+
Schema::drop('translations');
38+
}
39+
40+
}

src/DatabaseLoader.php

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php namespace Hpolthof\Translation;
2+
3+
use Illuminate\Database\Eloquent\ModelNotFoundException;
4+
use Illuminate\Translation\LoaderInterface;
5+
6+
class DatabaseLoader implements LoaderInterface {
7+
8+
/**
9+
* Load the messages for the given locale.
10+
*
11+
* @param string $locale
12+
* @param string $group
13+
* @param string $namespace
14+
* @return array
15+
*/
16+
public function load($locale, $group, $namespace = null)
17+
{
18+
return \DB::table('translations')
19+
->where('locale', $locale)
20+
->where('group', $group)
21+
->lists('value', 'name');
22+
}
23+
24+
/**
25+
* Add a new namespace to the loader.
26+
* This function will not be used but is required
27+
* due to the LoaderInterface.
28+
* We'll just leave it here as is.
29+
*
30+
* @param string $namespace
31+
* @param string $hint
32+
* @return void
33+
*/
34+
public function addNamespace($namespace, $hint) {}
35+
36+
/**
37+
* Adds a new translation to the database or
38+
* updates an existing record if the viewed_at
39+
* updates are allowed.
40+
*
41+
* @param string $locale
42+
* @param string $group
43+
* @param string $name
44+
* @return void
45+
*/
46+
public function addTranslation($locale, $group, $key)
47+
{
48+
if(!\Config::get('app.debug')) return;
49+
50+
// Extract the real key from the translation.
51+
if (preg_match("/^{$group}\.(.*?)$/sm", $key, $match)) {
52+
$name = $match[1];
53+
} else {
54+
throw new TranslationException('Could not extract key from translation.');
55+
}
56+
57+
$item = \DB::table('translations')
58+
->where('locale', $locale)
59+
->where('group', $group)
60+
->where('name', $name)->first();
61+
62+
$data = compact('locale', 'group', 'name');
63+
$data = array_merge($data, [
64+
'viewed_at' => date_create(),
65+
'updated_at' => date_create(),
66+
]);
67+
68+
if($item === null) {
69+
$data = array_merge($data, [
70+
'created_at' => date_create(),
71+
]);
72+
\DB::table('translations')->insert($data);
73+
} else {
74+
\DB::table('translations')->where('id', $item->id)->update($data);
75+
}
76+
}
77+
}

src/ServiceProvider.php

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php namespace Hpolthof\Translation;
2+
3+
use Illuminate\Translation\FileLoader;
4+
5+
class ServiceProvider extends \Illuminate\Translation\TranslationServiceProvider {
6+
7+
/**
8+
* Indicates if loading of the provider is deferred.
9+
*
10+
* @var bool
11+
*/
12+
protected $defer = true;
13+
14+
/**
15+
* Register the service provider.
16+
*
17+
* @return void
18+
*/
19+
public function register()
20+
{
21+
$this->registerDatabase();
22+
$this->registerLoader();
23+
24+
$this->app->singleton('translator', function($app)
25+
{
26+
$loader = $app['translation.loader'];
27+
$database = $app['translation.database'];
28+
29+
// When registering the translator component, we'll need to set the default
30+
// locale as well as the fallback locale. So, we'll grab the application
31+
// configuration so we can easily get both of these values from there.
32+
$locale = $app['config']['app.locale'];
33+
34+
$trans = new Translator($database, $loader, $locale);
35+
36+
$trans->setFallback($app['config']['app.fallback_locale']);
37+
38+
return $trans;
39+
});
40+
}
41+
42+
public function boot()
43+
{
44+
$this->publishes([
45+
__DIR__.'/../database/migrations/' => database_path('/migrations')
46+
], 'migrations');
47+
}
48+
49+
/**
50+
* Register the translation line loader.
51+
*
52+
* @return void
53+
*/
54+
protected function registerLoader()
55+
{
56+
$this->app->singleton('translation.loader', function($app)
57+
{
58+
return new FileLoader($app['files'], $app['path.lang']);
59+
});
60+
}
61+
62+
protected function registerDatabase()
63+
{
64+
$this->app->singleton('translation.database', function($app)
65+
{
66+
return new DatabaseLoader();
67+
});
68+
}
69+
70+
/**
71+
* Get the services provided by the provider.
72+
*
73+
* @return array
74+
*/
75+
public function provides()
76+
{
77+
return array('translator', 'translation.loader', 'translation.database');
78+
}
79+
80+
}

src/TranslationException.php

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php namespace Hpolthof\Translation;;
2+
3+
class TranslationException extends \RuntimeException {
4+
5+
}

src/Translator.php

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php namespace Hpolthof\Translation;;
2+
3+
use Illuminate\Translation\LoaderInterface;
4+
use Symfony\Component\Translation\TranslatorInterface;
5+
6+
class Translator extends \Illuminate\Translation\Translator implements TranslatorInterface {
7+
8+
public function __construct(LoaderInterface $database, LoaderInterface $loader, $locale)
9+
{
10+
$this->database = $database;
11+
parent::__construct($loader, $locale);
12+
}
13+
14+
protected static function isNamespaced($namespace)
15+
{
16+
return !(is_null($namespace) || $namespace == '*');
17+
}
18+
19+
/**
20+
* Get the translation for the given key.
21+
*
22+
* @param string $key
23+
* @param array $replace
24+
* @param string $locale
25+
* @return string
26+
*/
27+
public function get($key, array $replace = array(), $locale = null)
28+
{
29+
list($namespace, $group, $item) = $this->parseKey($key);
30+
31+
// Here we will get the locale that should be used for the language line. If one
32+
// was not passed, we will use the default locales which was given to us when
33+
// the translator was instantiated. Then, we can load the lines and return.
34+
foreach ($this->parseLocale($locale) as $locale)
35+
{
36+
if(!self::isNamespaced($namespace)) {
37+
// Database stuff
38+
$this->database->addTranslation($locale, $group, $key);
39+
}
40+
41+
$this->load($namespace, $group, $locale);
42+
43+
$line = $this->getLine(
44+
$namespace, $group, $locale, $item, $replace
45+
);
46+
47+
if ( ! is_null($line)) break;
48+
}
49+
50+
// If the line doesn't exist, we will return back the key which was requested as
51+
// that will be quick to spot in the UI if language keys are wrong or missing
52+
// from the application's language files. Otherwise we can return the line.
53+
if ( ! isset($line)) return $key;
54+
55+
return $line;
56+
}
57+
58+
public function load($namespace, $group, $locale)
59+
{
60+
if ($this->isLoaded($namespace, $group, $locale)) return;
61+
62+
// If a Namespace is give the Filesystem will be used
63+
// otherwise we'll use our database.
64+
// This will allow legacy support.
65+
if(!self::isNamespaced($namespace)) {
66+
// If debug is off then cache the result forever to ensure high performance.
67+
if(!\Config::get('app.debug')) {
68+
$that = $this;
69+
$lines = \Cache::rememberForever('__translations.'.$locale.'.'.$group, function() use ($that, $locale, $group, $namespace) {
70+
return $this->database->load($locale, $group, $namespace);
71+
});
72+
} else {
73+
$lines = $this->database->load($locale, $group, $namespace);
74+
}
75+
} else {
76+
$lines = $this->loader->load($locale, $group, $namespace);
77+
}
78+
$this->loaded[$namespace][$group][$locale] = $lines;
79+
}
80+
}

0 commit comments

Comments
 (0)