I'm building a multi language website. In this case just 2 languages, it's Indonesian and English using Laravel. I have posts table, it will store id of each post and post_translations table is to store local, title, and description of post. I got the problem when storing data into database. I don't have any idea how to store post without inc the id except I've added 2 same post with Indonesian and English.
This is the result (wrong)
posts table
id
1
2
post_translations table
id post_id locale title
1 1 en fisrt post
2 2 id post yang pertama
Expexted result
posts table
id
1
post_translations table
id post_id locale title
1 1 en fisrt post
2 1 id post yang pertama
PostController
public function store(Request $request) {
$this->validate($request, [
'title' => 'required',
'slug' => 'required',
'content' => 'required'
]);
$post = new Post;
$post->title = $request->title;
$post->slug = $request->slug;
$post->content = $request->content;
$post->save();
return redirect()->route('post.index');
}
Ok, so here we go (please note that this is not the only way):
install spatie/laravel-translatable with
composer require spatie/laravel-translatable
**Note: for native spatie/laravel-translatable go to version 2 **
create a table with this structure:
CREATE TABLE `articles` (
`id` int(10) UNSIGNED NOT NULL,
`title` text COLLATE utf8_unicode_ci,
`slug` text COLLATE utf8_unicode_ci,
`content` text COLLATE utf8_unicode_ci,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Note: beter use a migration. I just exported a table I did earlier to test
Insert the data in the datatabase in json format like this:
INSERT INTO `pages` (`id`, `title`, `slug`, `content`, `created_at`, `updated_at`) VALUES
(1, '{"ro":"Acas\\u0103","en":"Home"}', NULL, '{"ro":"<p><strong>Test title<\\/strong><\\/p>\\r\\n\\r\\n<p>Test content romanian<\\/p>\\r\\n","en":"<p><strong>test title english<\\/strong><\\/p>\\r\\n\\r\\n<p>test content english.<\\/p>\\r\\n"}', '2017-04-03 11:45:56', '2017-04-03 12:15:16');
Now create the blade to edit, update, create show etc. To get the language do something like this in the blade:
{{ $data->title }}
{!! nl2br($data->content) !!}
And in the controller: add something like this:
/**
* Generate the field by language
*
* @param Model $entry the item selected from the database
*
* @return array
*/
public function getTranslatableFields($fields)
{
$inputs = [];
$languages = $this->getLanguages();
foreach ($languages as $language) {
foreach ($fields as $field) {
$inputs[] = [
'name' => "{$field['name']}[{$language->abbr}]",
'label' => $field['label'] . " ($language->abbr)",
'lang' => $language->abbr,
'type' => array_key_exists('type', $field) ? $field['type'] : 'text'
];
}
}
return $inputs;
}
I added this function in a LangTrait. Since I also use backpack for laravel I did some more things.
For edit I added this method in the trait:
/**
* Show the form for editing the specified resource.
*
* @param int $id the item's identifier
*
* @return Response
*/
public function edit($id)
{
$data['entry'] = Model::find($id);
$data['title'] = trans('lang_file.edit').' '.$this->entity_name; // name of the page
$data['fields'] = $this->getMultiLangFields($data['entry']);
$data['id'] = $id;
return view('crud::edit', $data);
}
/**
* Generate the field by language
*
* @param Model $entry the item selected from the database
*
* @return array
*/
protected function getMultiLangFields($entry)
{
$fields['id'] = ['name' => 'id', 'type' => 'hidden', 'value' => $entry->id];
foreach ($this->crud->update_fields as $key => $field) {
$value = null;
if (array_key_exists('lang', $field)) {
$name = preg_replace('/(\[\w{2}\])$/i', '', $field['name']);
$value = $entry->getTranslation($name, $field['lang']);
}
$fields[$key] = array_merge($field, ['value' => $value]);
}
return $fields;
}
/**
* Get the application active languages
*
* @return \Backpack\LangFileManager\app\Models\Language
*/
protected function getLanguages()
{
return Language::whereActive(1)->orderBy('default', 'desc')->get();
}
In my main controller I did: use LangTrait; (contains everything above)
In construct I added this:
$this->getTranslatableFields($fields)
where $fields it's the list of fields I need
All methods should be adapted to you html format. As I said I use backpack for Laravel and fields are formatted accordingly.
And finally for the getLanguage file to work I created a new table and a model in the DB:
Model:
class Language extends Model
{
protected $table = 'languages';
protected $fillable = ['name', 'flag', 'abbr', 'native', 'active', 'default'];
public $timestamps = false;
public static function getActiveLanguagesArray()
{
$active_languages = self::where('active', 1)->get()->toArray();
$localizable_languages_array = [];
if (count($active_languages)) {
foreach ($active_languages as $key => $lang) {
$localizable_languages_array[$lang['abbr']] = $lang;
}
return $localizable_languages_array;
}
return config('laravellocalization.supportedLocales');
}
public static function findByAbbr($abbr = false)
{
return self::where('abbr', $abbr)->first();
}
}
Table:
CREATE TABLE `languages` (
`id` int(10) UNSIGNED NOT NULL,
`name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`app_name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`flag` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`abbr` varchar(3) COLLATE utf8_unicode_ci NOT NULL,
`script` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
`native` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
`active` tinyint(3) UNSIGNED NOT NULL DEFAULT '1',
`default` tinyint(3) UNSIGNED NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Data in the table:
INSERT INTO `languages` (`id`, `name`, `app_name`, `flag`, `abbr`, `script`, `native`, `active`, `default`, `created_at`, `updated_at`, `deleted_at`) VALUES
(1, 'English', 'english', '', 'en', 'Latn', 'English', 1, 0, NULL, NULL, NULL),
(2, 'Romanian', 'romanian', '', 'ro', 'Latn', 'română', 1, 1, NULL, NULL, NULL);
Since I did this through a package I kind of messed around a little bit with the code.
Now, for the spatie/laravel-translatable package version:
set up the service provider in config/app.php add this in the providers array:
Spatie\Translatable\TranslatableServiceProvider::class,
In the model Articles add use HasTranslations;
like this:
use Illuminate\Database\Eloquent\Model;
use Spatie\Translatable\HasTranslations;
class NewsItem extends Model
{
use HasTranslations;
public $translatable = ['name']; // list the columns you want to be translatable (will have json format)
}
save a new entry and use it:
$article = new Article;
$article->setTranslation('name', 'en', 'Updated name in English')
->setTranslation('name', 'nl', 'Naam in het Nederlands');
$article->save();
$article->name; // Returns 'Name in English' given that the current app locale is 'en'
$article->getTranslation('name', 'nl'); // returns 'Naam in het Nederlands'
app()->setLocale('nl');
$article->name; // Returns 'Naam in het Nederlands'
examples from: https://github.com/spatie/laravel-translatable
Database table format is as stated above in the first version
If it does not work out let me know and I'll look at your code.