<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Trevor Rawls</title>
    <description>The latest articles on DEV Community by Trevor Rawls (@trevor_rawls_b670c3832b98).</description>
    <link>https://dev.to/trevor_rawls_b670c3832b98</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1622193%2Fd0d55b6d-d155-4243-8620-d4eaeaf73016.jpg</url>
      <title>DEV Community: Trevor Rawls</title>
      <link>https://dev.to/trevor_rawls_b670c3832b98</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/trevor_rawls_b670c3832b98"/>
    <language>en</language>
    <item>
      <title>This is going to be fun</title>
      <dc:creator>Trevor Rawls</dc:creator>
      <pubDate>Wed, 21 May 2025 15:49:30 +0000</pubDate>
      <link>https://dev.to/trevor_rawls_b670c3832b98/this-is-going-to-be-fun-32pd</link>
      <guid>https://dev.to/trevor_rawls_b670c3832b98/this-is-going-to-be-fun-32pd</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/devteam/join-the-postmark-challenge-inbox-innovators-3000-in-prizes-497l" class="crayons-story__hidden-navigation-link"&gt;Join the Postmark Challenge: Inbox Innovators - $3,000 in Prizes!&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;
          &lt;a class="crayons-logo crayons-logo--l" href="/devteam"&gt;
            &lt;img alt="The DEV Team logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F1%2Fd908a186-5651-4a5a-9f76-15200bc6801f.jpg" class="crayons-logo__image"&gt;
          &lt;/a&gt;

          &lt;a href="/thepracticaldev" class="crayons-avatar  crayons-avatar--s absolute -right-2 -bottom-2 border-solid border-2 border-base-inverted  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3%2F13d3b32a-d381-4549-b95e-ec665768ce8f.png" alt="thepracticaldev profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/thepracticaldev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              dev.to staff
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                dev.to staff
                
              
              &lt;div id="story-author-preview-content-2485301" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/thepracticaldev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3%2F13d3b32a-d381-4549-b95e-ec665768ce8f.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;dev.to staff&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

            &lt;span&gt;
              &lt;span class="crayons-story__tertiary fw-normal"&gt; for &lt;/span&gt;&lt;a href="/devteam" class="crayons-story__secondary fw-medium"&gt;The DEV Team&lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;a href="https://dev.to/devteam/join-the-postmark-challenge-inbox-innovators-3000-in-prizes-497l" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 21 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/devteam/join-the-postmark-challenge-inbox-innovators-3000-in-prizes-497l" id="article-link-2485301"&gt;
          Join the Postmark Challenge: Inbox Innovators - $3,000 in Prizes!
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/postmarkchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;postmarkchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/api"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;api&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/devteam/join-the-postmark-challenge-inbox-innovators-3000-in-prizes-497l" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;148&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/devteam/join-the-postmark-challenge-inbox-innovators-3000-in-prizes-497l#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              84&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>devchallenge</category>
      <category>postmarkchallenge</category>
      <category>webdev</category>
      <category>api</category>
    </item>
    <item>
      <title>Sending Multilingual Emails with Postmark and Gitloc</title>
      <dc:creator>Trevor Rawls</dc:creator>
      <pubDate>Fri, 21 Jun 2024 15:14:54 +0000</pubDate>
      <link>https://dev.to/trevor_rawls_b670c3832b98/sending-multilingual-emails-with-postmark-and-gitloc-je5</link>
      <guid>https://dev.to/trevor_rawls_b670c3832b98/sending-multilingual-emails-with-postmark-and-gitloc-je5</guid>
      <description>&lt;p&gt;In today's global landscape, connecting with customers in their preferred language is crucial. Whether through emails, messaging apps, or websites, communicating in a customer's native language builds stronger relationships and boosts transaction success. This is particularly true for transactional or notification emails, where crucial information can be easily missed if not presented in the recipient's language.&lt;/p&gt;

&lt;p&gt;In this post, we’ll dive into our journey of implementing multilingual transactional emails for spotsmap.com, exploring how our approach has evolved over time. Each method has its merits depending on your specific needs. Note: While we use Node.js for the examples, the concepts can be applied across various programming languages.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://spotsmap.com"&gt;Spotsmap.com&lt;/a&gt; required a localized booking confirmation email that included details like sport types, dates, costs, and course information. The HTML template alone was over 500 lines of code.&lt;/p&gt;

&lt;h2&gt;
  
  
  First Approach: Language-Specific Templates
&lt;/h2&gt;

&lt;p&gt;Initially, we created separate templates for each supported language. Our code included a configuration object to select the appropriate template based on the user's language. Here’s a simplified example of how it looked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;html
&amp;lt;h2&amp;gt;Arrival date:&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;{{ArrivalDate}}&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;javascript
// config.js
export default {
  services: {
    email: {
      postmark: {
        apiKey: process.env.POSTMARK_API_KEY,
        templates: {
          bookingConfirmation: {
            'en': 123456,
            'de': 123457,
            'es': 123458
          }
        }
      }
    }
  }
}

// services/postmark.js
export const sendMail = ({ from, to, template, locale, params }) =&amp;gt; {
  const request = client.sendEmailWithTemplate({
    From: from,
    To: to,
    MessageStream: 'outbound',
    TemplateId: postmarkConfig.templates[template][locale],
    TemplateModel: params || {},
  });

  request
    .then(result =&amp;gt; {
      console.log(`Email sent to ${to}`);
    })
    .catch(err =&amp;gt; {
      console.log(`Email was not sent, with error code ${err.statusCode}, ${err.message}`);
    });
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While straightforward, this approach became cumbersome as we had to replicate changes across multiple templates. When we added a feature to book multiple courses in a single order, updating each language-specific template became a headache.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second Approach: Conditional Templates&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We then moved to a single template with conditionals to handle multiple languages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;html
&amp;lt;h2&amp;gt;
  {{#en}}Arrival date:{{/en}}
  {{#de}}Ankunftsdatum:{{/de}}
  {{#es}}Fecha de llegada:{{/es}}
&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;{{ArrivalDate}}&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the updated code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;javascript
// config.js
export default {
  services: {
    email: {
      postmark: {
        apiKey: process.env.POSTMARK_API_KEY,
        templates: {
          bookingConfirmation: 123456
        }
      }
    }
  }
}

// services/postmark.js
export const sendMail = ({ from, to, template, locale, params }) =&amp;gt; {
  const request = client.sendEmailWithTemplate({
    From: from,
    To: to,
    MessageStream: 'outbound',
    TemplateId: postmarkConfig.templates[template],
    TemplateModel: { ...params, [locale]: true },
  });

  request
    .then(result =&amp;gt; {
      console.log(`Email sent to ${to}`);
    })
    .catch(err =&amp;gt; {
      console.log(`Email was not sent, with error code ${err.statusCode}, ${err.message}`);
    });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This solution worked well initially but soon became unwieldy as the number of languages and localized strings grew. For 40 languages, this method would lead to a bloated and unmanageable template.&lt;/p&gt;

&lt;h2&gt;
  
  
  Third Approach: Code-Based Localization
&lt;/h2&gt;

&lt;p&gt;To streamline our process, we shifted localization strings from the template to our codebase, utilizing a standardized translation process similar to what we use for website UI localization.&lt;/p&gt;

&lt;p&gt;Our simplified template now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;html
&amp;lt;h2&amp;gt;{{ArrivalDateTitle}}&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;{{ArrivalDate}}&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We created JSON files for localization strings, for example, &lt;code&gt;templates/en/bookingConfirmation.json&lt;/code&gt; for English:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;json
{
  "ArrivalDateTitle": "Arrival date"
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the updated &lt;code&gt;sendMail&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;javascript
// services/postmark.js
export const sendMail = ({ from, to, template, locale, params }) =&amp;gt; {
  const strings = require(`../templates/${locale}/${template}.json`);
  const request = client.sendEmailWithTemplate({
    From: from,
    To: to,
    MessageStream: 'outbound',
    TemplateId: postmarkConfig.templates[template],
    TemplateModel: { ...params, ...strings },
  });

  request
    .then(result =&amp;gt; {
      console.log(`Email sent to ${to}`);
    })
    .catch(err =&amp;gt; {
      console.log(`Email was not sent, with error code ${err.statusCode}, ${err.message}`);
    });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To automate the translation process using Gitloc, we created a &lt;code&gt;gitloc.yaml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;defaultLocale&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en&lt;/span&gt;
  &lt;span class="na"&gt;locales&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;en&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;de&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;es&lt;/span&gt;
  &lt;span class="na"&gt;directories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;templates&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach allowed us to efficiently manage translations by simply updating the &lt;code&gt;gitloc.yaml&lt;/code&gt; file and pushing changes to our remote repository. Gitloc handled the rest, ensuring we could support numerous languages without compromising our codebase’s maintainability.&lt;/p&gt;

&lt;p&gt;For more details, check out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://postmarkapp.com/developer"&gt;Postmark Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.gitloc.org/"&gt;Gitloc Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We hope these insights help you streamline your multilingual email process!&lt;/p&gt;

</description>
      <category>postmark</category>
      <category>multilingual</category>
      <category>email</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
