问题
I'm curious about how to effectively generate emails in a multilingual application.
For the sake of getting all answers aligned: let's say you have a typical commercial newsletter with lots of images, markup, and of course many textual paragraphs. Assume that all text does NOT come from a database but should be hard-coded. Moreover, some words in these paragraphs can be bolded, emphasized,... The newsletter will be sent in the subscriber's preferred locale.
How can I build a system to handle this?
- Would you reference each paragraph with
trans()
and define all translations in Laravel's lang folder? How about the individual words markup part then? Incorporating HTML tags in language files feels somehow wrong to me. - Or would you create separate language folders for emails (e.g. views/emails/en) and make Laravel fetch the right one? But then lots of duplicate mail layout definition will probably exist...
- Or perhaps something completely else?
回答1:
For newsletters with static content:
- Avoid translation files
- Use template composition for reusable structural elements
Laravel's localization system works well for strings in an application, but, for the text-heavy content in an email, these definitions will be cumbersome to maintain, especially for content that contains inline markup or that changes regularly. Templates with many trans()
calls also render more slowly, especially when batching thousands of emails.
If somebody else writes the content, it's much easier to add a few Blade directives where needed than to extract every chunk of text into a localization entry. Remember, we'll need to repeat this work for every newsletter.
Instead, just write the content directly into the template, and use a naming hierarchy for messages translated to different languages. Extract structural and display elements into reusable components to minimize the amount of markup mixed in with the content.
Our template file hierarchy might look like this:
resources/views/newsletters/
├── components/
│ └── article_summary.blade.php
├── en/
│ └── 2018-01-01.blade.php
├── es/
│ └── 2018-01-01.blade.php
├── fr/
│ └── 2018-01-01.blade.php
└── newsletter.blade.php
The newsletter.blade.php master template contains basic elements displayed in every newsletter, like a header, logo, and footer. It will @include()
the appropriate newsletter body based on the user's locale and the date of publication:
{{-- ... common header markup ... --}}
@include("newsletters.$locale.$date")
{{-- ... common footer markup ... --}}
Each template in the localization folders contains the text for the message in that language, with a bit of inline HTML or markdown for formatting as needed. Here's an example:
# Newsletter Title
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc
finibus sapien nec sapien dictum, vitae dictum dolor mattis.
Nunc bibendum id augue...
@component('newsletters.components.article_summary', [
'title' => 'Article Title 1',
'fullArticleUrl' => 'http://...',
])
Ut aliquet lobortis leo, cursus volutpat mi convallis ut.
Vivamus id orci ut quam rhoncus eleifend. Nulla condimentum...
@endcomponent
@component('newsletters.components.article_summary', [
'title' => 'Article Title 2',
'fullArticleUrl' => 'http://...',
])
Donec non finibus tellus, efficitur tincidunt leo. Nam nibh
augue, consectetur tincidunt dui et, fermentum dictum eros...
@endcomponent
As we can see, there's very little to distract from the content of the message. The article_summary
component hides the markup from the message body:
<div class="summary">
<a href="{{ $fullArticleUrl }}">
<h2>{{ $title }}</h2>
</a>
<p>{{ $slot }}</p>
</div>
By using components, we're free to change the layout in each newsletter, and we avoid rewriting markup that we use often. Laravel includes some useful built-in components for markdown messages.
To send the message, we just pass in the locale and the newsletter date that our master template will resolve from the available languages:
public function build()
{
return $this->->markdown('newsletters.newsletter')->with([
'locale' => $this->user->locale,
'date' => '2018-01-01',
]);
}
As a final note, we don't need to skip using trans()
entirely. It may make sense in some cases, such as in shared components. For the majority of the content, we gain readability and maintainability by writing it directly in the template.
回答2:
I would go with the first option with some modification, by using a markdown parser in emails, so you won't be incorporating HTML inside language files, you will be sending parsed markdown strings to the blade template:
Each time a template change needed, you wouldn't have to update each template for each language.
Localization codes are already implemented in Laravel & Blade template engine.
For a daily campaign e-mail, you would only create one template (maybe one more for languages like RTL ones) and n x m translation strings where n stands for each string the template contains and m for each language.
You will be able to use string modifiers like singularity/plurality, replacing, and extend the system with your own helpers.
For example, a simple html email could be built like this (using Laravel 5.6):
Translation Files Structure
/resources
/lang
/en
/dailydigest.php
/de
/dailydigest.php
Mailable DailyDigest.php
<?php
namespace App\Mail;
use App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Markdown;
use Illuminate\Queue\SerializesModels;
class DailyDigest extends Mailable
{
use Queueable;
protected $user;
public function __construct($user)
{
$this->user= $user;
}
public function build()
{
return $this
->from("admin@example.com")
->view('emails.dailydigest', [
'user' => $this->user,
'messageSection1' => Markdown::parse(Lang::get("dailydigest.message1",[],$this->user->locale)),
'messageImage1' => 'http://example.com/images/test.png',
'messageSection2' => Markdown::parse(Lang::get("dailydigest.message2",[],$this->user->locale)),
// if it's a language specific image
'messageImage2' => Lang::get("dailydigest.image2",[],$this->user->locale),
]);
}
}
Template /resources/views/emails/dailydigest.blade.php
A standard blade template containing HTML and blade directives, created for daily digest email.
Controller SendDailyDigest.php
<?php
namespace App\Http\Controllers;
use App\User;
use App\Mail\DailyDigest;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use App\Http\Controllers\Controller;
class DailyDigestController extends Controller
{
/**
* Send the dailydigest to all members.
*
* @param Request $request
* @return Response
*/
public function send(Request $request)
{
$users = App\User::all();
// build the email message with the users locale and add to queue
foreach($users as $user){
Mail::to($user->email)
->send(new DailyDigest($user))
->queue("digest");
}
}
}
And there's a publication that I recently found:
https://laravel-news.com/laravel-campaign-monitor
来源:https://stackoverflow.com/questions/49327972/how-would-you-organize-mails-in-a-multilingual-laravel-application