<?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: 朱威坚</title>
    <description>The latest articles on DEV Community by 朱威坚 (@_2fa19d454855a7f7f6ff3).</description>
    <link>https://dev.to/_2fa19d454855a7f7f6ff3</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4214088%2Fb10e3916-04b1-40e3-a5df-334936cf3ddf.png</url>
      <title>DEV Community: 朱威坚</title>
      <link>https://dev.to/_2fa19d454855a7f7f6ff3</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/_2fa19d454855a7f7f6ff3"/>
    <language>en</language>
    <item>
      <title>Shipping a 16-language Android app: how we stopped translation drift</title>
      <dc:creator>朱威坚</dc:creator>
      <pubDate>Thu, 29 Oct 2026 02:17:52 +0000</pubDate>
      <link>https://dev.to/_2fa19d454855a7f7f6ff3/shipping-a-16-language-android-app-how-we-stopped-translation-drift-1ljn</link>
      <guid>https://dev.to/_2fa19d454855a7f7f6ff3/shipping-a-16-language-android-app-how-we-stopped-translation-drift-1ljn</guid>
      <description>&lt;p&gt;We ship a Bazi (Four Pillars) and feng shui app on Google Play. Sixteen languages, one codebase.&lt;/p&gt;

&lt;p&gt;The problem we hit was not translation itself. It was drift.&lt;/p&gt;

&lt;h2&gt;
  
  
  What went wrong
&lt;/h2&gt;

&lt;p&gt;Every screen rendered terms like Heavenly Stem and Earthly Branch from localized strings. Some code paths used a translated label as a map key. That works in Chinese. It explodes in Korean.&lt;/p&gt;

&lt;p&gt;We also had placeholder collisions: &lt;code&gt;$mingGong&lt;/code&gt; is a prefix of &lt;code&gt;$mingGongAnalysis&lt;/code&gt;, so the template engine truncated the longer name and users saw &lt;code&gt;병사Analysis&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we changed
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Server returns language-neutral codes, not text.&lt;/strong&gt; &lt;code&gt;gan:"bing"&lt;/code&gt;, &lt;code&gt;shiShen:"piancai"&lt;/code&gt;, &lt;code&gt;naYin:"luzhonghuo"&lt;/code&gt;. The value is stable across all 16 locales.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One client-side term database renders codes to text.&lt;/strong&gt; 75 term codes x 16 languages in a single lookup. Adding a column means adding entries, not touching screens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server only adds fields, never changes old ones.&lt;/strong&gt; Older app versions ignore unknown keys, so a field addition cannot break users on a previous release.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Braced placeholders only.&lt;/strong&gt; &lt;code&gt;${name}&lt;/code&gt;, never &lt;code&gt;$name&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;We audited the source: 1,267 localization call sites. Our coverage went from about 55% to 78%, and the remaining gaps are now enumerable instead of discovered by users.&lt;/p&gt;

&lt;p&gt;Current release fixes eight languages completely: English, Japanese, Korean, Traditional Chinese, Thai, Vietnamese, Indonesian, Malay. The other seven are in progress.&lt;/p&gt;

&lt;p&gt;If you maintain a multi-locale app: make your server emit codes, and keep exactly one rendering path on the client.&lt;/p&gt;

&lt;p&gt;mysticfate.net&lt;/p&gt;

</description>
      <category>android</category>
      <category>mobile</category>
      <category>softwaredevelopment</category>
    </item>
  </channel>
</rss>
