<?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: Stefan Wintermeyer</title>
    <description>The latest articles on DEV Community by Stefan Wintermeyer (@wintermeyer).</description>
    <link>https://dev.to/wintermeyer</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%2F401415%2F13a87dca-62f9-4083-b69d-d066794931d2.jpg</url>
      <title>DEV Community: Stefan Wintermeyer</title>
      <link>https://dev.to/wintermeyer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wintermeyer"/>
    <language>en</language>
    <item>
      <title>4 Step WireGuard Tutorial</title>
      <dc:creator>Stefan Wintermeyer</dc:creator>
      <pubDate>Sat, 27 Mar 2021 15:49:03 +0000</pubDate>
      <link>https://dev.to/wintermeyer/how-multiple-wireguard-peers-can-talk-to-each-other-3nl1</link>
      <guid>https://dev.to/wintermeyer/how-multiple-wireguard-peers-can-talk-to-each-other-3nl1</guid>
      <description>&lt;p&gt;WireGuard can be used to quickly setup a private tunnel/network between one server with a public IP address and one or multiple peers which might be behind a NAT. Let me show you how.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All commands in this tutorial have to be run with root privileges.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. IP-Address Setup
&lt;/h2&gt;

&lt;p&gt;We assume that the public IP address of the server is &lt;code&gt;172.16.187.4&lt;/code&gt; and that the new WireGuard setup uses IP addresses within the &lt;code&gt;10.0.0.1/24&lt;/code&gt; range. Please change those values according your setup.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Internal IP Address&lt;/th&gt;
&lt;th&gt;Public IP Address&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Server (Debian)&lt;/td&gt;
&lt;td&gt;10.0.0.1&lt;/td&gt;
&lt;td&gt;172.16.187.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peer 1 (Debian)&lt;/td&gt;
&lt;td&gt;10.0.0.2&lt;/td&gt;
&lt;td&gt;unknown behind NAT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peer 2 (Debian)&lt;/td&gt;
&lt;td&gt;10.0.0.3&lt;/td&gt;
&lt;td&gt;unknown behind NAT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peer 3 (macOS)&lt;/td&gt;
&lt;td&gt;10.0.0.4&lt;/td&gt;
&lt;td&gt;unknown behind NAT&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  2. Install WireGuard on Debian GNU/Linux
&lt;/h2&gt;

&lt;p&gt;Install the WireGuard software on the server and all clients. For Debian 10.x systems (find out which version you run with &lt;code&gt;lsb_release -a&lt;/code&gt;) you have to use &lt;code&gt;buster-backports&lt;/code&gt; to do so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ echo 'deb http://deb.debian.org/debian buster-backports main' \ 
&amp;gt;&amp;gt; /etc/apt/sources.list
$ apt-get update
$ apt-get install wireguard/buster-backports
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are running Debian 11 (Bullseye) or later you don't need the backport and can just run &lt;code&gt;apt-get install wireguard&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After that create a private and public key on each system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ umask 077
$ cd /etc/wireguard
$ wg genkey | tee privatekey | wg pubkey &amp;gt; publickey
$ cat privatekey 
uA3OFbVYW7QP4qDQlqIzrie2B3+kX+0NcxkY2XEMBl0=
$ cat publickey 
2M1BpjkzBU5z9/NyFy+661rhKLA+1hKKV7KI8J/SB18=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Firewall
&lt;/h3&gt;

&lt;p&gt;If you are running a firewall on your server have to open a port for WireGuard. In our setup we use UDP port 51820. For the popular UFW firewall this is the needed command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ufw allow 51820/udp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Server Configuration
&lt;/h2&gt;

&lt;p&gt;For the clients to be able to communicate to each other you have to make sure that you have &lt;code&gt;ip_forward&lt;/code&gt; activated in &lt;code&gt;/etc/sysctl.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sed -i 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/g' \ 
/etc/sysctl.conf
$ sysctl --system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use your favorite editor to create a file &lt;code&gt;/etc/wireguard/wg0.conf&lt;/code&gt; with the following content. Use the private and public keys you just created on the server and all the peers. &lt;strong&gt;Do not use the example keys from this tutorial!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Adjust the peer entries according to your own needs. One entry per peer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = uA3OFbVYW7QP4qDQlqIzrie2B3+kX+0NcxkY2XEMBl0=

[Peer]
PublicKey = 2KyqIPdsgroQZvucFVF2VEy64zeR1744gqwcXzqBfhw=
AllowedIPs = 10.0.0.2/32

[Peer]
PublicKey = 0qDtefB2NZTNZ3uYEOmmR7J7VUdzONqWfvZ+ZGpnDQU=
AllowedIPs = 10.0.0.3/32

[Peer]
PublicKey = 3ayqIPdsgroQZvucEOmmR7J7VUdzONqWfvZ+ZGpnDQU=
AllowedIPs = 10.0.0.4/32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fire it up the first time manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.0.0.1/24 dev wg0
[#] ip link set mtu 1420 up dev wg0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stop it manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ wg-quick down wg0
[#] ip link delete dev wg0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to have it activated all the time (which you probably do):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ systemctl enable wg-quick@wg0
$ systemctl start wg-quick@wg0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the command &lt;code&gt;wg&lt;/code&gt; to get an overview:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ wg
interface: wg0
  public key: 2M1BpjkzBU5z9/NyFy+661rhKLA+1hKKV7KI8J/SB18=
  private key: (hidden)
  listening port: 51820

peer: 2KyqIPdsgroQZvucFVF2VEy64zeR1744gqwcXzqBfhw=
  allowed ips: 10.0.0.2/32

peer: 0qDtefB2NZTNZ3uYEOmmR7J7VUdzONqWfvZ+ZGpnDQU=
  allowed ips: 10.0.0.3/32

peer: 3ayqIPdsgroQZvucEOmmR7J7VUdzONqWfvZ+ZGpnDQU=
  allowed ips: 10.0.0.4/32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You are now all set on the server side.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Peer configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Debian Linux
&lt;/h3&gt;

&lt;p&gt;On each peer you have to create a &lt;code&gt;/etc/wireguard/wg0.conf&lt;/code&gt; file with this content. Please replace the keys with your own ones. Use the ones you created with &lt;code&gt;wg genkey | tee privatekey | wg pubkey &amp;gt; publickey&lt;/code&gt; during the installation of WireGuard on the peers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Interface]
PrivateKey = oH+eQXaI3VMj9XDaUWmAghPXmsLTDAjzONPWwISw1no=
Address = 10.0.0.2/32

[Peer]
PublicKey = 2M1BpjkzBU5z9/NyFy+661rhKLA+1hKKV7KI8J/SB18=
Endpoint = 172.16.187.4:51820
AllowedIPs = 10.0.0.1/24
PersistentKeepalive = 25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;10.0.0.2&lt;/code&gt; with the internal WireGuard IP of your peer (e.g. &lt;code&gt;10.0.0.3&lt;/code&gt; or &lt;code&gt;10.0.0.4&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;If your peer is not behind a NAT you don't need the &lt;code&gt;PersistentKeepalive = 25&lt;/code&gt; line which just configures a keep alive ping to the server.&lt;/p&gt;

&lt;p&gt;Fire it up the first time manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.0.0.1/24 dev wg0
[#] ip link set mtu 1420 up dev wg0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stop it manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ wg-quick down wg0
[#] ip link delete dev wg0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to have it activated all the time (which you probably do):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ systemctl enable wg-quick@wg0
$ systemctl start wg-quick@wg0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might need to run a &lt;code&gt;systemctl restart wg-quick@wg0&lt;/code&gt; on the server in case you ran into a chicken-egg-problem with the key configuration.&lt;/p&gt;

&lt;p&gt;Use the &lt;code&gt;wg&lt;/code&gt; command to get an overview:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ wg
interface: wg0
  public key: 2KyqIPdsgroQZvucFVF2VEy64zeR1744gqwcXzqBfhw=
  private key: (hidden)
  listening port: 33067

peer: 2M1BpjkzBU5z9/NyFy+661rhKLA+1hKKV7KI8J/SB18=
  endpoint: 172.16.187.4:51820
  allowed ips: 10.0.0.0/24
  latest handshake: 1 hour, 18 minutes, 37 seconds ago
  transfer: 8.40 KiB received, 7.52 KiB sent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  macOS
&lt;/h3&gt;

&lt;p&gt;Install the WireGuard software from the app store at &lt;a href="https://apps.apple.com/us/app/wireguard/id1451685025"&gt;https://apps.apple.com/us/app/wireguard/id1451685025&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use the same config in the macOS peer as in the Debian peer.&lt;/p&gt;

</description>
      <category>network</category>
      <category>wireguard</category>
      <category>vpn</category>
    </item>
    <item>
      <title>I18n with Phoenix LiveView</title>
      <dc:creator>Stefan Wintermeyer</dc:creator>
      <pubDate>Fri, 12 Mar 2021 12:38:57 +0000</pubDate>
      <link>https://dev.to/wintermeyer/i18n-with-phoenix-liveview-28mj</link>
      <guid>https://dev.to/wintermeyer/i18n-with-phoenix-liveview-28mj</guid>
      <description>&lt;p&gt;You want to use I81n in Phoenix LiveView? So did I. But one can't just use &lt;code&gt;gettext/1&lt;/code&gt; in Phoenix LiveView out of the box. It took me a couple of hours to find out how to do it. Here is the recipe. You can copy and paste all of it. Just make sure to search and replace for &lt;code&gt;example&lt;/code&gt; and &lt;code&gt;Example&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix phx.new example --live
cd example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;My example application features English (en) and German (de) and defaults to English. So we have to add this at the end of &lt;code&gt;config/config.ex&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:example&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ExampleWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Gettext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;default_locale:&lt;/span&gt; &lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;locales:&lt;/span&gt; &lt;span class="sx"&gt;~w(en de)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Ask the browser
&lt;/h2&gt;

&lt;p&gt;There are a couple of different ways of deciding which language to use. In my opinion the &lt;code&gt;"accept-language"&lt;/code&gt; header from the browser is a good way to go. If you want to go a different direction this plug is a good starting point. To use &lt;code&gt;"accept-language"&lt;/code&gt; and set the used language we create a new plug:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;lib/example_web/plug/local_plug.ex&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;ExampleWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Plugs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Locale&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Conn&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_opts&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;accepted_languages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;extract_accept_language&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;known_locales&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Gettext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;known_locales&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ExampleWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Gettext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;accepted_languages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="n"&gt;known_locales&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;known_locales&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="n"&gt;accepted_languages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;accepted_languages&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;locale&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="no"&gt;Gettext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put_locale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ExampleWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Gettext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;conn&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;put_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:locale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Copied from&lt;/span&gt;
  &lt;span class="c1"&gt;# https://raw.githubusercontent.com/smeevil/set_locale/fd35624e25d79d61e70742e42ade955e5ff857b8/lib/headers.ex&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;extract_accept_language&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_req_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"accept-language"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;","&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;parse_language_option&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quality&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;&amp;amp;2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;is_nil&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ensure_language_fallbacks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

      &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;parse_language_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;captures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Regex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;named_captures&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;~r/^\s?(?&amp;lt;tag&amp;gt;[\w\-]+)(?:;q=(?&amp;lt;quality&amp;gt;[\d\.]+))?$/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;quality&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Float&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;captures&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"quality"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;tag:&lt;/span&gt; &lt;span class="n"&gt;captures&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"tag"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;quality:&lt;/span&gt; &lt;span class="n"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;ensure_language_fallbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flat_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"-"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_country_variant&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;member?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_language&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have to include this plug in the pipeline:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;lib/example_web/router.ex&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;ExampleWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Router&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;ExampleWeb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:router&lt;/span&gt;

  &lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="ss"&gt;:browser&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="ss"&gt;:accepts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"html"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="ss"&gt;:fetch_session&lt;/span&gt;
    &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="ss"&gt;:fetch_live_flash&lt;/span&gt;
    &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="ss"&gt;:put_root_layout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;ExampleWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;LayoutView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:root&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="ss"&gt;:protect_from_forgery&lt;/span&gt;
    &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="ss"&gt;:put_secure_browser_headers&lt;/span&gt;
    &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="no"&gt;ExampleWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Plugs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Locale&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;-----&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The LiveView Hello World with I18n
&lt;/h2&gt;

&lt;p&gt;A nice Hello World! example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;lib/example_web/live/page_live.ex&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;ExampleWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PageLive&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;ExampleWeb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:live_view&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Gettext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;with_locale:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nv"&gt;@impl&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;locale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"locale"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;locale&lt;/span&gt;
      &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"en"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="n"&gt;socket&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;locale:&lt;/span&gt; &lt;span class="n"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="sx"&gt;~L""&lt;/span&gt;&lt;span class="s2"&gt;"
    &amp;lt;%= with_locale(@locale, fn -&amp;gt; %&amp;gt;
      &amp;lt;%= gettext "&lt;/span&gt;&lt;span class="no"&gt;Hello&lt;/span&gt; &lt;span class="no"&gt;World&lt;/span&gt;&lt;span class="n"&gt;!&lt;/span&gt;&lt;span class="s2"&gt;" %&amp;gt;&amp;lt;/h2&amp;gt;
    &amp;lt;% end) %&amp;gt;
    """&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Translations
&lt;/h2&gt;

&lt;p&gt;We need to generate the config files for the &lt;code&gt;gettext&lt;/code&gt; translations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mix gettext.extract --merge&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mix gettext.merge priv/gettext --locale de&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are the files which need to be used for the translations:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;priv/gettext
├── de
│   └── LC_MESSAGES
│       ├── default.po
│       └── errors.po
├── default.pot
└── errors.pot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And here's the content of &lt;code&gt;priv/gettext/de/LC_MESSAGES/default.po&lt;/code&gt; (the German translation):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## "msgid"s in this file come from POT (.pot) files.
##
## Do not add, change, or remove "msgid"s manually here as
## they're tied to the ones in the corresponding POT file
## (with the same domain).
##
## Use "mix gettext.extract --merge" or "mix gettext.merge"
## to merge POT files into PO files.
msgid ""
msgstr ""
"Language: de\n"
"Plural-Forms: nplurals=2\n"

#, elixir-format
#: lib/example_web/live/page_live.ex:17
msgid "Hello World!"
msgstr "Hallo Welt!"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here's some more docu about using Gettext:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://phrase.com/blog/posts/i18n-for-phoenix-applications-with-gettext/" rel="noopener noreferrer"&gt;https://phrase.com/blog/posts/i18n-for-phoenix-applications-with-gettext/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.plataformatec.com.br/2016/03/using-gettext-to-internationalize-a-phoenix-application/" rel="noopener noreferrer"&gt;http://blog.plataformatec.com.br/2016/03/using-gettext-to-internationalize-a-phoenix-application/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After all this you can fire up your application:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix phx.server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That's all! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuuof44ngu5vm62gpbglt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuuof44ngu5vm62gpbglt.png" alt="Browser screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My Twitter account: &lt;a href="https://twitter.com/wintermeyer" rel="noopener noreferrer"&gt;https://twitter.com/wintermeyer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PS: In case you want to tackle LiveView components have a look at &lt;a href="https://www.paulfioravanti.com/blog/internationalisation-phoenix-live-components/https://www.paulfioravanti.com/blog/internationalisation-phoenix-live-components/" rel="noopener noreferrer"&gt;https://www.paulfioravanti.com/blog/internationalisation-phoenix-live-components/https://www.paulfioravanti.com/blog/internationalisation-phoenix-live-components/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>phoenix</category>
      <category>liveview</category>
    </item>
  </channel>
</rss>
