<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>7. Internationalization on BeamFoundry Rays Core</title><link>https://rays.bfdy.dev/rays/docs/7-i18n/</link><description>Recent content in 7. Internationalization on BeamFoundry Rays Core</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://rays.bfdy.dev/rays/docs/7-i18n/index.xml" rel="self" type="application/rss+xml"/><item><title>Message Catalogs &amp; Pluralization</title><link>https://rays.bfdy.dev/rays/docs/7-i18n/1-message-catalogs/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://rays.bfdy.dev/rays/docs/7-i18n/1-message-catalogs/</guid><description>&lt;h1 id="message-catalogs--pluralization"&gt;Message Catalogs &amp;amp; Pluralization&lt;a class="anchor" href="#message-catalogs--pluralization"&gt;&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;Rays simplifies message localization by letting you define translation catalogs directly in your YAML configuration files. Messages are automatically loaded into &lt;code&gt;LocaleMessageBundle&lt;/code&gt; instances, with full support for positional parameters, Unicode CLDR pluralization rules, and numeric ranges.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="defining-messages-in-yaml"&gt;Defining Messages in YAML&lt;a class="anchor" href="#defining-messages-in-yaml"&gt;&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;Message catalogs are organized by locale under the &lt;code&gt;localization.messages&lt;/code&gt; property in your &lt;code&gt;application.yaml&lt;/code&gt; file. Rays recognizes four distinct categories of localized messages:&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: left"&gt;Category&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: left"&gt;Description&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th style="text-align: left"&gt;Placeholders&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;&lt;code&gt;translation&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;Simple string translations. Positional parameters are denoted by &lt;code&gt;{0}&lt;/code&gt;, &lt;code&gt;{1}&lt;/code&gt;, &lt;code&gt;{2}&lt;/code&gt;, etc.&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;code&gt;{0}&lt;/code&gt;, &lt;code&gt;{1}&lt;/code&gt;, &lt;code&gt;{2}&lt;/code&gt;, &amp;hellip;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;&lt;code&gt;cardinal&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;Plural rules determined by item quantities (e.g., &amp;ldquo;1 day&amp;rdquo; vs. &amp;ldquo;5 days&amp;rdquo;).&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;code&gt;{0}&lt;/code&gt; (the formatted number)&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;&lt;code&gt;ordinal&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;Plural rules determined by position or rank (e.g., &amp;ldquo;1st&amp;rdquo;, &amp;ldquo;2nd&amp;rdquo;, &amp;ldquo;3rd&amp;rdquo;).&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;code&gt;{0}&lt;/code&gt; (the formatted number)&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;strong&gt;&lt;code&gt;ranges&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;Plural rules for intervals between two numbers (e.g., &amp;ldquo;1-3 days&amp;rdquo;).&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td style="text-align: left"&gt;&lt;code&gt;{0}&lt;/code&gt;, &lt;code&gt;{1}&lt;/code&gt; (the start and end numbers)&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h3 id="complete-yaml-example"&gt;Complete YAML Example&lt;a class="anchor" href="#complete-yaml-example"&gt;&lt;/a&gt;&lt;/h3&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;localization&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;messages&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;en&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;translation&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;welcome&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Welcome&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;welcome_user&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Welcome, {0} {1}!&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;fields.registration.username&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Username&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;fields.registration.password&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Password&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;fields.registration.confirm&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Confirm Password&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cardinal&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;days.left&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ONE&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;{0} day remaining&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;OTHER&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;{0} days remaining&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ordinal&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;competition.place&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ONE&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;{0}st place&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;TWO&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;{0}nd place&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;FEW&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;{0}rd place&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;OTHER&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;{0}th place&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ranges&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;days.range&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;OTHER&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;{0}-{1} days&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;fr&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;translation&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;welcome&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Bienvenue&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;welcome_user&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Bienvenue, {0} {1} !&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;fields.registration.username&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Nom d&amp;#39;utilisateur&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;fields.registration.password&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Mot de passe&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;fields.registration.confirm&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Confirmation du mot de passe&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cardinal&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;days.left&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ONE&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;{0} jour restant&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;OTHER&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;{0} jours restants&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ordinal&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;competition.place&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ONE&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;{0}er prix&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;OTHER&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;{0}e prix&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ranges&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;days.range&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ONE&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;{0}-{1} jour&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;OTHER&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;{0}-{1} jours&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#10;&lt;h2 id="cldr-plural-rules"&gt;CLDR Plural Rules&lt;a class="anchor" href="#cldr-plural-rules"&gt;&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;Different languages follow different rules for pluralization. In English, cardinal numbers only distinguish between &lt;code&gt;ONE&lt;/code&gt; (1) and &lt;code&gt;OTHER&lt;/code&gt; (0, 2, 3, &amp;hellip;). Other languages like Polish, Russian, or Arabic use additional categories such as &lt;code&gt;FEW&lt;/code&gt; or &lt;code&gt;MANY&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Container Configuration</title><link>https://rays.bfdy.dev/rays/docs/7-i18n/2-configuration/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://rays.bfdy.dev/rays/docs/7-i18n/2-configuration/</guid><description>&lt;h1 id="container-configuration"&gt;Container Configuration&lt;a class="anchor" href="#container-configuration"&gt;&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;Rays integrates internationalization cleanly into its Inversion of Control (IoC) container. By declaring an i18n &lt;code&gt;Configuration&lt;/code&gt; Beam, you can bind YAML configuration properties, configure default and auxiliary locales, load validator translation packs, and publish i18n services for injection throughout your application.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="1-defining-configuration-properties"&gt;1. Defining Configuration Properties&lt;a class="anchor" href="#1-defining-configuration-properties"&gt;&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;First, define a struct embedding &lt;code&gt;ConfigurationProperties&lt;/code&gt; to bind the &lt;code&gt;localization&lt;/code&gt; section from your &lt;code&gt;application.yaml&lt;/code&gt; file:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;package&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;config&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; (&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; . &lt;span style="color:#e6db74"&gt;&amp;#34;github.com/BeamFoundry/rays/pkg/core&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; . &lt;span style="color:#e6db74"&gt;&amp;#34;github.com/BeamFoundry/rays/pkg/i18n&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// LocalizationProperties binds messages from application.yaml under &amp;#34;localization&amp;#34;.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;type&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LocalizationProperties&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;ConfigurationProperties&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`Name:&amp;#34;localization&amp;#34;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;messages&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;map&lt;/span&gt;[&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;]&lt;span style="color:#a6e22e"&gt;LocalizedMessages&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Rays automatically maps the YAML properties to the internal &lt;code&gt;LocalizedMessages&lt;/code&gt; struct containing &lt;code&gt;translation&lt;/code&gt;, &lt;code&gt;cardinal&lt;/code&gt;, &lt;code&gt;ordinal&lt;/code&gt;, and &lt;code&gt;ranges&lt;/code&gt; maps.&lt;/p&gt;</description></item><item><title>Web &amp; Template Integration</title><link>https://rays.bfdy.dev/rays/docs/7-i18n/3-web-and-templates/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://rays.bfdy.dev/rays/docs/7-i18n/3-web-and-templates/</guid><description>&lt;h1 id="web--template-integration"&gt;Web &amp;amp; Template Integration&lt;a class="anchor" href="#web--template-integration"&gt;&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;Internationalized web applications must dynamically determine the user&amp;rsquo;s preferred language for each incoming HTTP request without requiring developers to manually pass a locale or context parameter through every service, repository, and template call.&lt;/p&gt;&#10;&lt;p&gt;Rays solves this using &lt;strong&gt;&lt;code&gt;ThreadLocaleContextHolder&lt;/code&gt;&lt;/strong&gt; for request-scoped locale management and &lt;strong&gt;&lt;code&gt;LocaleFuncMap&lt;/code&gt;&lt;/strong&gt; for seamless integration with Go&amp;rsquo;s standard &lt;code&gt;html/template&lt;/code&gt; package.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="1-request-scoped-locale-management"&gt;1. Request-Scoped Locale Management&lt;a class="anchor" href="#1-request-scoped-locale-management"&gt;&lt;/a&gt;&lt;/h2&gt;&#10;&lt;h3 id="how-it-works"&gt;How It Works&lt;a class="anchor" href="#how-it-works"&gt;&lt;/a&gt;&lt;/h3&gt;&#10;&lt;p&gt;&lt;code&gt;ThreadLocaleContextHolder&lt;/code&gt; is powered by thread-local storage (&lt;code&gt;routine.ThreadLocal&lt;/code&gt;). When an HTTP request enters your application:&lt;/p&gt;</description></item><item><title>Localized Struct Validation</title><link>https://rays.bfdy.dev/rays/docs/7-i18n/4-validation/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://rays.bfdy.dev/rays/docs/7-i18n/4-validation/</guid><description>&lt;h1 id="localized-struct-validation"&gt;Localized Struct Validation&lt;a class="anchor" href="#localized-struct-validation"&gt;&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;Web frameworks and API routers typically bind request payloads to structs and validate them using &lt;a href="https://github.com/go-playground/validator"&gt;Go-Playground Validator&lt;/a&gt;. However, default validation outputs suffer from two major problems:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Error descriptions are in English only.&lt;/li&gt;&#10;&lt;li&gt;Field names reflect raw Go struct fields (e.g., &lt;code&gt;&amp;quot;ConfirmPassword&amp;quot;&lt;/code&gt; or &lt;code&gt;&amp;quot;user_email&amp;quot;&lt;/code&gt;) rather than user-friendly UI labels.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Rays&amp;rsquo; &lt;strong&gt;&lt;code&gt;MessageSourceValidator&lt;/code&gt;&lt;/strong&gt; solves both problems by translating validation rules into the active request locale and replacing field names with localized labels defined in your message catalog.&lt;/p&gt;</description></item><item><title>Predefined Catalogs Reference</title><link>https://rays.bfdy.dev/rays/docs/7-i18n/5-catalogs-reference/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://rays.bfdy.dev/rays/docs/7-i18n/5-catalogs-reference/</guid><description>&lt;h1 id="predefined-catalogs-reference"&gt;Predefined Catalogs Reference&lt;a class="anchor" href="#predefined-catalogs-reference"&gt;&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;The &lt;code&gt;github.com/BeamFoundry/rays/pkg/i18n/locales&lt;/code&gt; package eliminates boilerplate by providing pre-instantiated singletons for standard locales and pre-registered validation translation functions.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="predefined-locales"&gt;Predefined Locales&lt;a class="anchor" href="#predefined-locales"&gt;&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;The &lt;code&gt;Locales&lt;/code&gt; struct contains pre-instantiated &lt;a href="https://github.com/go-playground/locales"&gt;Go-Playground Locales&lt;/a&gt; singletons for over 200 standard language and territory combinations.&lt;/p&gt;&#10;&lt;h3 id="common-locales"&gt;Common Locales&lt;a class="anchor" href="#common-locales"&gt;&lt;/a&gt;&lt;/h3&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;github.com/BeamFoundry/rays/pkg/i18n/locales&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// English&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;EN&lt;/span&gt; &lt;span style="color:#75715e"&gt;// English (base)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;EN_US&lt;/span&gt; &lt;span style="color:#75715e"&gt;// English (United States)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;EN_GB&lt;/span&gt; &lt;span style="color:#75715e"&gt;// English (United Kingdom)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;EN_CA&lt;/span&gt; &lt;span style="color:#75715e"&gt;// English (Canada)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;EN_AU&lt;/span&gt; &lt;span style="color:#75715e"&gt;// English (Australia)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// French&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;FR&lt;/span&gt; &lt;span style="color:#75715e"&gt;// French (base)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;FR_FR&lt;/span&gt; &lt;span style="color:#75715e"&gt;// French (France)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;FR_CA&lt;/span&gt; &lt;span style="color:#75715e"&gt;// French (Canada)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;FR_BE&lt;/span&gt; &lt;span style="color:#75715e"&gt;// French (Belgium)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;FR_CH&lt;/span&gt; &lt;span style="color:#75715e"&gt;// French (Switzerland)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// German&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;DE&lt;/span&gt; &lt;span style="color:#75715e"&gt;// German (base)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;DE_DE&lt;/span&gt; &lt;span style="color:#75715e"&gt;// German (Germany)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;DE_AT&lt;/span&gt; &lt;span style="color:#75715e"&gt;// German (Austria)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;DE_CH&lt;/span&gt; &lt;span style="color:#75715e"&gt;// German (Switzerland)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Spanish&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ES&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Spanish (base)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ES_ES&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Spanish (Spain)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ES_MX&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Spanish (Mexico)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Other European Languages&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;IT&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Italian&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;PT&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Portuguese&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;PT_BR&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Portuguese (Brazil)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;NL&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Dutch&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;PL&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Polish&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;RU&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Russian&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;UK&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Ukrainian&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Asian Languages&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;JA&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Japanese&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;KO&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Korean&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ZH&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Chinese (Simplified)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ZH_TW&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Chinese (Traditional)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;VI&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Vietnamese&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;TH&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Thai&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ID&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Indonesian&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Middle Eastern Languages&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;AR&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Arabic&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;FA&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Persian / Farsi&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Locales&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;TR&lt;/span&gt; &lt;span style="color:#75715e"&gt;// Turkish&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="usage-with-message-bundles"&gt;Usage with Message Bundles&lt;a class="anchor" href="#usage-with-message-bundles"&gt;&lt;/a&gt;&lt;/h3&gt;&#10;&lt;p&gt;Pass any &lt;code&gt;Locales&lt;/code&gt; singleton to &lt;code&gt;messageSource.NewMessageBundle()&lt;/code&gt; or &lt;code&gt;messageSource.FindMessageBundle()&lt;/code&gt;:&lt;/p&gt;</description></item></channel></rss>