<?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: Akmal Urunboev</title>
    <description>The latest articles on DEV Community by Akmal Urunboev (@akmalurunboev).</description>
    <link>https://dev.to/akmalurunboev</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%2F4158673%2F46cd7a36-76f3-437e-b960-cd1276225bc6.jpg</url>
      <title>DEV Community: Akmal Urunboev</title>
      <link>https://dev.to/akmalurunboev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/akmalurunboev"/>
    <language>en</language>
    <item>
      <title>What breaks when you ship to ten locales at once</title>
      <dc:creator>Akmal Urunboev</dc:creator>
      <pubDate>Sat, 03 Oct 2026 00:16:32 +0000</pubDate>
      <link>https://dev.to/akmalurunboev/what-breaks-when-you-ship-to-ten-locales-at-once-3ehc</link>
      <guid>https://dev.to/akmalurunboev/what-breaks-when-you-ship-to-ten-locales-at-once-3ehc</guid>
      <description>&lt;p&gt;I localized a mobile app into ten languages. Most of what I'd read beforehand was about &lt;em&gt;how&lt;/em&gt; to&lt;br&gt;
translate. Almost none of it was about what goes wrong after, which turned out to be the harder half.&lt;/p&gt;

&lt;p&gt;These are the mistakes I actually made.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. I translated the content and left the chrome in English
&lt;/h2&gt;

&lt;p&gt;This is the one I'd undo first.&lt;/p&gt;

&lt;p&gt;The quiz content — the part users came for — got translated properly. The surrounding interface did&lt;br&gt;
not, or did only partly. Settings, error states, the subscription screens, legal copy, empty states.&lt;/p&gt;

&lt;p&gt;The result is worse than an English-only app, because it sets an expectation and then breaks it. A&lt;br&gt;
user who has been reading comfortably in their own language hits a wall precisely when something has&lt;br&gt;
gone wrong or something is being asked of them. The experience doesn't degrade gradually; it&lt;br&gt;
fractures at the exact moments that matter most.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; a locale is either done or it isn't. Partial coverage is not partial credit. If you&lt;br&gt;
can't finish a language, ship it later.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. I shipped locales before I knew anyone wanted them
&lt;/h2&gt;

&lt;p&gt;Adding a language feels like growth. It is cheap to start and expensive forever: every string you&lt;br&gt;
ever write afterwards is now ten strings, every feature ships at the speed of your slowest translator,&lt;br&gt;
every bug report might be a translation bug.&lt;/p&gt;

&lt;p&gt;I added languages because I &lt;em&gt;could&lt;/em&gt; reach those speakers, not because I had evidence they were&lt;br&gt;
waiting. Some were right. Some were an ongoing tax I'm still paying.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; treat each locale as a permanent operational commitment, not a one-time content cost.&lt;br&gt;
Ask what evidence would justify it, then go find that evidence first.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. I couldn't review what I couldn't read
&lt;/h2&gt;

&lt;p&gt;Obvious in hindsight. I shipped languages I don't speak, with no process for knowing whether they&lt;br&gt;
were any good beyond "the translator seemed competent."&lt;/p&gt;

&lt;p&gt;You cannot eyeball your way out of this. You need either a native reviewer who is separate from the&lt;br&gt;
translator, or a sampling process, or both — and you need it before launch, because the alternative is&lt;br&gt;
finding out from a one-star review written in a language you'll have to machine-translate to read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; budget for review as a separate line from translation. They are different jobs and&lt;br&gt;
the same person should not do both.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. I let terminology drift
&lt;/h2&gt;

&lt;p&gt;Several translators, one corpus, no glossary. The same domain term ended up rendered three different&lt;br&gt;
ways across the app.&lt;/p&gt;

&lt;p&gt;Each individual choice was defensible. Collectively it made the product feel like it had been&lt;br&gt;
assembled from parts, and worse, it made the material harder to learn from — the whole point of&lt;br&gt;
consistent terminology in educational content is that the learner builds one mental model, not three.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; the glossary comes before the first word is translated. Retrofitting consistency&lt;br&gt;
across a large corpus is enormously more expensive than establishing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. I assumed text length was roughly constant
&lt;/h2&gt;

&lt;p&gt;German and Russian run substantially longer than English. Some languages run shorter. Either way,&lt;br&gt;
layouts built around English string lengths break — truncation, wrapping into two lines, buttons&lt;br&gt;
growing past their container, labels colliding.&lt;/p&gt;

&lt;p&gt;This is not caught by review, because reviewers read text, they don't measure it. It is caught by&lt;br&gt;
pseudo-localization — rendering your UI with artificially lengthened strings before you have any real&lt;br&gt;
translations — which costs almost nothing and would have saved me a round of layout fixes.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. I treated RTL as "a language"
&lt;/h2&gt;

&lt;p&gt;Arabic isn't another locale in the list; it's a different layout direction. Mirrored navigation,&lt;br&gt;
flipped icons that indicate direction, number and punctuation handling inside mixed text.&lt;/p&gt;

&lt;p&gt;Most frameworks handle the basics. The basics are not the problem — the problem is every place&lt;br&gt;
someone hardcoded a left margin, every directional icon that shouldn't flip (a play button) sitting&lt;br&gt;
next to one that should (a back arrow), and every string that mixes scripts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; if RTL is in your plan, put it in early. Adding it after a year of LTR-only&lt;br&gt;
development means auditing every layout you've written.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;p&gt;Pick two languages. Do them completely — content, interface, store listing, support. Build the&lt;br&gt;
glossary, the review process and the pseudo-localization step while the surface area is small enough&lt;br&gt;
to hold in your head.&lt;/p&gt;

&lt;p&gt;Then add the third.&lt;/p&gt;

&lt;p&gt;The instinct is to go wide early because the marginal translation cost looks low. The translation is&lt;br&gt;
not the cost. Everything downstream of it is.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I build and operate multilingual mobile applications, shipped across ten languages. More at &lt;a href="https://akmalu.com" rel="noopener noreferrer"&gt;akmalu.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>localization</category>
      <category>i18n</category>
      <category>mobile</category>
      <category>lessonslearned</category>
    </item>
    <item>
      <title>Designing for people reading in their second language</title>
      <dc:creator>Akmal Urunboev</dc:creator>
      <pubDate>Sat, 03 Oct 2026 00:15:45 +0000</pubDate>
      <link>https://dev.to/akmalurunboev/designing-for-people-reading-in-their-second-language-lno</link>
      <guid>https://dev.to/akmalurunboev/designing-for-people-reading-in-their-second-language-lno</guid>
      <description>&lt;p&gt;Most people using the products I work on are preparing for a US licensing exam in a language that isn't&lt;br&gt;
their first. Many are studying after a full shift. Some have not sat a formal written test in twenty&lt;br&gt;
years.&lt;/p&gt;

&lt;p&gt;The thing I keep relearning: &lt;strong&gt;they are not bad at the test. The test is being delivered through a&lt;br&gt;
layer they have to fight.&lt;/strong&gt; Every bit of that layer you remove goes straight into their score.&lt;/p&gt;

&lt;p&gt;Here is what I've learned about removing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Negation is the single most expensive construction
&lt;/h2&gt;

&lt;p&gt;"Which of the following is &lt;strong&gt;not&lt;/strong&gt; required when…"&lt;/p&gt;

&lt;p&gt;A fluent reader handles this without noticing. A second-language reader has to hold the entire option&lt;br&gt;
list in working memory and invert it, in a language they are translating as they go, while a timer&lt;br&gt;
runs. The question stops testing the subject and starts testing processing load.&lt;/p&gt;

&lt;p&gt;Official exam material is full of negation and you often can't change that — but everything around it&lt;br&gt;
is yours. Explanations, hints, onboarding, settings labels. I write those in the positive wherever&lt;br&gt;
there's a choice, and when I can't, the negation gets visual weight so it is impossible to skim past.&lt;/p&gt;

&lt;p&gt;Double negation — "which is not permitted unless…" — I treat as a defect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Short sentences are not dumbing down
&lt;/h2&gt;

&lt;p&gt;There's a reflex, especially when the subject is regulatory, to write in the register of the source&lt;br&gt;
material. Long clauses, stacked conditions, formal connectives.&lt;/p&gt;

&lt;p&gt;That register exists for legal precision, not comprehension. You can keep the precision and lose the&lt;br&gt;
sentence length: one idea per sentence, conditions as a list rather than a chain, the subject near the&lt;br&gt;
verb.&lt;/p&gt;

&lt;p&gt;What this is &lt;strong&gt;not&lt;/strong&gt; is simplification of content. These are adults preparing for a professional&lt;br&gt;
qualification. Patronizing them is its own failure, and they notice it faster than you'd think. The&lt;br&gt;
goal is to make the language transparent so the difficulty lives entirely in the subject, which is&lt;br&gt;
where it belongs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Translation is not the same as localization
&lt;/h2&gt;

&lt;p&gt;Translating a question correctly and making it &lt;em&gt;answerable&lt;/em&gt; are different jobs.&lt;/p&gt;

&lt;p&gt;A term of art may have no equivalent in the target language. The honest handling is often to give the&lt;br&gt;
translation and keep the English term alongside it — because the test and the&lt;br&gt;
examiner will both be in English. A learner who only ever sees the translated term will meet the&lt;br&gt;
English one for the first time under exam conditions.&lt;/p&gt;

&lt;p&gt;That means some things deliberately stay untranslated. Working out which is domain judgment, not a&lt;br&gt;
language decision, and it's the part that can't be outsourced to a translator who doesn't know the&lt;br&gt;
exam.&lt;/p&gt;

&lt;h2&gt;
  
  
  Icons help less than you want them to
&lt;/h2&gt;

&lt;p&gt;Icons feel like the obvious fix for a language barrier. In practice they are their own vocabulary,&lt;br&gt;
and one that is strongly culturally conditioned. A floppy disk means save to people who have never&lt;br&gt;
seen a floppy disk, and means nothing to people who have never seen the convention.&lt;/p&gt;

&lt;p&gt;Where icons genuinely help: reinforcing a label you have already written in words. Where they fail:&lt;br&gt;
replacing it. I use them as redundancy, not as compression — the exception being domain symbols the&lt;br&gt;
learner must recognize anyway, where showing them &lt;em&gt;is&lt;/em&gt; the content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Time pressure multiplies everything
&lt;/h2&gt;

&lt;p&gt;Every problem above gets worse under a clock, and the exam has one. A sentence that costs a fluent&lt;br&gt;
reader two seconds and a second-language reader eight is a rounding error in practice and a&lt;br&gt;
catastrophe across a full test.&lt;/p&gt;

&lt;p&gt;So I treat reading time as a real budget. If a screen can be understood in one pass without&lt;br&gt;
re-reading, it's done. If my eye goes back, it isn't — and my eye is the easy case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't make them read anything they don't have to
&lt;/h2&gt;

&lt;p&gt;The highest-leverage work isn't rewriting sentences. It's removing screens.&lt;/p&gt;

&lt;p&gt;Every piece of interface copy is a tax paid in a second language. Onboarding that explains a feature&lt;br&gt;
the user could discover in two taps, a confirmation dialog for a reversible action, a tooltip&lt;br&gt;
restating the label it's attached to — in English these are mild clutter. Here they are a cost with&lt;br&gt;
no return.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters beyond this audience
&lt;/h2&gt;

&lt;p&gt;Everything above is just good interface writing with the volume turned up. Short sentences, positive&lt;br&gt;
constructions, words doing real work, fewer screens — none of it is special pleading for non-native&lt;br&gt;
speakers. It's what helps a tired native speaker on a phone at eleven at night too.&lt;/p&gt;

&lt;p&gt;Designing for the harder case makes the easy case better. It just makes the failures impossible to&lt;br&gt;
ignore, which is the useful part.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I build and operate multilingual mobile applications, shipped across ten languages.&lt;br&gt;
More at &lt;a href="https://akmalu.com" rel="noopener noreferrer"&gt;akmalu.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ux</category>
      <category>a11y</category>
      <category>localization</category>
      <category>writing</category>
    </item>
    <item>
      <title>Most languages don't have two plural forms. Your i18n setup assumes they do.</title>
      <dc:creator>Akmal Urunboev</dc:creator>
      <pubDate>Sat, 03 Oct 2026 00:08:54 +0000</pubDate>
      <link>https://dev.to/akmalurunboev/russian-has-four-plural-forms-your-i18n-setup-probably-handles-one-3ib6</link>
      <guid>https://dev.to/akmalurunboev/russian-has-four-plural-forms-your-i18n-setup-probably-handles-one-3ib6</guid>
      <description>&lt;p&gt;If you localize an app into Russian and your string files only have &lt;code&gt;one&lt;/code&gt; and &lt;code&gt;other&lt;/code&gt;, your Russian&lt;br&gt;
is wrong. Not awkward — wrong, in a way native speakers notice immediately and most English-speaking&lt;br&gt;
teams never see.&lt;/p&gt;

&lt;p&gt;I build multilingual mobile apps that run in ten languages, and this was the first thing that genuinely&lt;br&gt;
embarrassed me.&lt;/p&gt;
&lt;h2&gt;
  
  
  The thing English teaches you to assume
&lt;/h2&gt;

&lt;p&gt;English has two plural forms. One question, two questions. The rule is "is it 1?" and after that you&lt;br&gt;
stop thinking about it. Every i18n tutorial reflects that, so the ARB or JSON you write looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"questionCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{count, plural, one{{count} question} other{{count} questions}}"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a complete specification of English. It is not a complete specification of anything else, and&lt;br&gt;
the structure you write in your source language is the structure translators are handed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Russian actually needs
&lt;/h2&gt;

&lt;p&gt;Russian uses four CLDR plural categories: &lt;code&gt;one&lt;/code&gt;, &lt;code&gt;few&lt;/code&gt;, &lt;code&gt;many&lt;/code&gt;, &lt;code&gt;other&lt;/code&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Applies to&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;one&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1, 21, 31, 41, 101 …&lt;/td&gt;
&lt;td&gt;1 вопрос&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;few&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2–4, 22–24, 32–34 …&lt;/td&gt;
&lt;td&gt;2 вопроса&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;many&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0, 5–20, 25–30 …&lt;/td&gt;
&lt;td&gt;5 вопросов&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;other&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;fractions&lt;/td&gt;
&lt;td&gt;1.5 вопроса&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Read the &lt;code&gt;one&lt;/code&gt; row again, because it is the part that breaks code rather than translations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;21 is &lt;code&gt;one&lt;/code&gt; in Russian.&lt;/strong&gt; So is 31, 41, 101, 1021. If anywhere in your codebase you have written&lt;br&gt;
&lt;code&gt;if (count == 1)&lt;/code&gt; to decide singular versus plural, that branch is wrong in Russian for roughly one&lt;br&gt;
number in ten. It will look fine in every test you write, because your tests use 1, 2 and 5.&lt;/p&gt;

&lt;p&gt;Arabic goes further with six categories, including distinct &lt;code&gt;zero&lt;/code&gt; and &lt;code&gt;two&lt;/code&gt;. Polish, Czech,&lt;br&gt;
Ukrainian and Lithuanian each have their own arrangement. The point is not to memorize them. The&lt;br&gt;
point is that the number of forms is a property of the language, not of your code, and CLDR already&lt;br&gt;
knows all of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it actually goes wrong
&lt;/h2&gt;

&lt;p&gt;Three failure modes, in the order I hit them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The source string constrains the translation.&lt;/strong&gt; You ship an ARB with only &lt;code&gt;one&lt;/code&gt; and &lt;code&gt;other&lt;/code&gt;.&lt;br&gt;
The translator opens it and has nowhere to put &lt;code&gt;few&lt;/code&gt; and &lt;code&gt;many&lt;/code&gt;. A good translator flags it. A&lt;br&gt;
translator working through a spreadsheet at volume picks the closest slot and moves on, and you have&lt;br&gt;
now shipped "5 вопроса" — which reads roughly like "5 questions-of" — to every user who answers&lt;br&gt;
five of anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Manual pluralization in code.&lt;/strong&gt; Somewhere there is a helper like&lt;br&gt;
&lt;code&gt;count == 1 ? singular : plural&lt;/code&gt;. It predates the i18n work, it is three lines long, and nobody&lt;br&gt;
remembers it exists. It is correct in English forever and wrong in Russian from the moment you&lt;br&gt;
launch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Interpolated counts inside longer sentences.&lt;/strong&gt; The plural form often has to agree with a verb&lt;br&gt;
or adjective elsewhere in the sentence, which means the whole sentence is the translation unit, not&lt;br&gt;
the noun. Splitting a sentence into fragments and concatenating them produces text no amount of&lt;br&gt;
good translation can save.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I do now
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Write the source string with every category you might ever need&lt;/strong&gt;, even though English collapses&lt;br&gt;
most of them. &lt;code&gt;Intl.plural&lt;/code&gt; in Dart accepts &lt;code&gt;zero&lt;/code&gt;, &lt;code&gt;one&lt;/code&gt;, &lt;code&gt;two&lt;/code&gt;, &lt;code&gt;few&lt;/code&gt;, &lt;code&gt;many&lt;/code&gt;, &lt;code&gt;other&lt;/code&gt;. Fill in&lt;br&gt;
what English needs and leave the rest for the languages that use them, rather than handing&lt;br&gt;
translators a shape that cannot hold their grammar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Delete every manual plural branch.&lt;/strong&gt; Grep for &lt;code&gt;== 1&lt;/code&gt; near anything user-facing. This is a&lt;br&gt;
twenty-minute job and it finds things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never build a sentence by concatenation.&lt;/strong&gt; One string, one complete sentence, placeholders inside&lt;br&gt;
it. If that produces an awkward English string, the awkwardness is a signal about word order you'd&lt;br&gt;
otherwise discover in production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test with 1, 2, 5, 21 and 0.&lt;/strong&gt; Those five numbers hit every Russian category including the one&lt;br&gt;
that catches &lt;code&gt;== 1&lt;/code&gt;. Add 11 if you want to be thorough, since 11 is &lt;code&gt;many&lt;/code&gt; despite ending in 1.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part worth internalizing
&lt;/h2&gt;

&lt;p&gt;This is not really a Russian problem. It is what happens when a structure designed around one&lt;br&gt;
language's grammar becomes the container every other language has to fit into. Plurals are just the&lt;br&gt;
most legible instance — the same thing happens with date formats, name order, honorifics and text&lt;br&gt;
expansion.&lt;/p&gt;

&lt;p&gt;The fix is cheap if you do it before translation and expensive afterwards, because by then the wrong&lt;br&gt;
shape is baked into thousands of strings and a translation memory that will faithfully reproduce it.&lt;/p&gt;

&lt;p&gt;If you are about to localize something, spend an afternoon on CLDR plural rules first. It is the&lt;br&gt;
highest-return afternoon in the whole project.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I build and operate multilingual mobile applications, shipped across ten languages.&lt;br&gt;
More at &lt;a href="https://akmalu.com" rel="noopener noreferrer"&gt;akmalu.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>i18n</category>
      <category>flutter</category>
      <category>localization</category>
      <category>dart</category>
    </item>
  </channel>
</rss>
