Internationalization

Internationalization (i18n)
Why does it matter? Internationalization maximizes your application’s reach among a global user base, giving you the largest available addressable market. Without internationalization, users may be unable to fully utilize an otherwise valuable service.
What is i18n? Internationalization (abbreviated i18n) encompasses translation, iconography, colors, formatting, information architecture, and anything that enables users from different countries, cultures, and who speak different languages, to understand and use your product.
Translations
Translations are the first and most obvious component of Internationalization. Translations are stored as Unicode strings either bundled into application code or dynamically loaded from an external service.
Cloud Phone has the same extensive Unicode support as Chrome.
Bundled Strings
Bundled strings are stored and packaged within your app’s codebase at build time. Each locale is compiled into the final bundle.
Advantages
- Simplicity: No asynchronous calls or error handling needed
- No Latency: No waiting for HTTP requests to load strings
Disadvantages
- Increased Bundle Size: Bundling all locales can significantly increase application file size
- Memory Overhead: Unused locales consume memory without user benefit
Lazy Loading
Lazy loading fetches locale-specific strings over HTTPS at runtime based on app settings or browser locale.
Advantages
- Smaller Payload: Only the necessary strings are loaded
- Flexibility: New translations can be added without rebuilding the application
Disadvantages
- HTTP Latency: Network requests can introduce unpreditcable delays
- Error Handling: Error handling is needed for failed or slow network responses
- Cache Management: Requires proper cache invalidation to receive latest strings
Recommendation
Use the method that best suits your needs. Because Cloud Phone is rendered on powerful servers, the increased bundle size and memory footprint are imperceivable to end users. Smaller apps find bundled strings easier to implement, while large cross-platform apps may prefer to manage translations independently without rebuilding and publishing updates.
Language

Detecting User Language
navigator.language
The navigator
global includes two properties to detect the user’s language: navigator.language
and navigator.languages
.
console.log(navigator.language); // e.g., 'en-US'
console.log(navigator.languages); // e.g., ['en-US', 'fr-FR']
Multiple Languages
43% of the global population is bilingual, speaking two languages. A further 17% are multilingual, speaking three or more languages. In recognition of multilingualism, the navigator.languages
(plural) property was introduced to Chrome 37 in 2014.
The navigator.languages
(plural) array is ordered by preference, with the most preferred language first, matching the value of navigator.language
(singular).
Accept-Language
Header
The Accept-Language
header, sent with all HTTP requests, provides information about the user’s language preferences, including quality values. Servers can parse this sorted list of languages when responding to requests.
// Example of parsing Accept-Language header on the server (Node.js)
const parseAcceptLanguage = (header) => {
// i.e. en;q=0.9 -> en
const dropQualityValue = (lang) => lang.split(';')[0].trim();
return header
.split(',')
.map(dropQualityValue);
};
const acceptLanguageHeader = 'en-US,en;q=0.9,fr;q=0.8';
console.log(parseAcceptLanguage(acceptLanguageHeader)); // ['en-US', 'en', 'fr']
By combining navigator.language
and the Accept-Language
header, developers can create a robust language detection mechanism that accommodates client-side and server-side preferences.
languagechange
Event
The languagechange
) event is fired when the user’s preferred languages are changed. Cloud Phone uses language preferences from system settings and does not provide its own setting.
The languagechange
event is never triggered on Cloud Phone, because the user must exit Cloud Phone in order to access system settings.
GeoIP
Another method to approximate user language is IP address geolocation (GeoIP). Use the X-Forwarded-For
header sent with each request to access the user’s IP address since traffic originates from CloudMosa data centers.
Use GeoIP for default language selection only as a fallback for locations where there is a sensible default.
✅ Netherlands
The primary language for 97.5% in the Netherlands is Dutch.
Recommendation: Dutch is a reasonable default for the Netherlands.
❎ India
India has 15+ languages with 1M+ speakers. Hindi is the most common language in India nationally, but its use varies between states. For example, only ~0.5% of the 72M+ people in the state of Tamil Nadu speak Hindi.
Recommendation: Use more granular state-level GeoIP for default language selection in large, linguistically-diverse countries India.
CSS :lang() pseudo-class
Adapt styles for specific languages using the :lang() pseudo-class selector.
/* Applies to elements with the matching lang attribute, and descendents */
*:lang(en-US) {
color: deeppink;
}
/* Only applies directly to elements with the matching lang attribute */
[lang="en-US"] {
color: blue;
}
Styles are applied when the CSS selector language code matches the corresponding HTML lang
attribute.
<p lang="en-US">Hello, World</p>
<p lang="es-MX">Hola, Mundo</p>
Set language on any HTMLElement
in JavaScript using the lang
property.
document.body.lang = 'en-US';
The [lang]
attribute selector only works if the lang
attribute is explicitly set and exactly matches the HTML. On the other hand, the :lang()
pseudo-class matches the inherited or implicit language, is case insensitive, and performs fuzzy matching.
- Dynamic match:
:lang()
matches both explicitly setlang
attributes and inherited language contexts - Fuzzy matching: Matches broader language codes based on the prefix. For example,
:lang(en-US)
matcheslang="en-US"
as well aslang="en"
, becauseen-US
is a subtype ofen
. - Case-insensitive:
:lang(en-US)
matchesen-US
,EN-us
,EN-US
, etc.
Nesting
Set the lang
attributes within the document for partially-translated websites, user-generated content (UGC), or third-party content. For example, PodLP displays both English and non-English podcasts like No es el fin del mundo, which sets its language to Spanish in the RSS feed using <language>es</language>
.
<html lang="en-US">
<head>
<title>PodLP - Podcasts for the next billion</title>
</head>
<body>
<header>
<h1>Top Podcasts</h1>
</header>
<section lang="es" dir="ltr">
<h3>No es el fin del mundo</h3>
<h5>El Orden Mundial</h5>
</section>
<footer>
<button>Select</button>
<button>Subscribe</button>
</footer>
</body>
</html>
Set the lang
attribute whenever your app displays external content.
Content Direction
The default content direction is left-to-right (LTR), used by languages like English, Spanish, and Hindi. Right-to-left (RTL) is used in languages like Arabic, Urdu, and Hebrew.
<html dir="ltr" lang="en">
</html>
Use the [dir]
attribute selector or :dir()
pseudo-class to apply styles conditionally:
/* Applies to elements with the matching lang attribute, and descendents */
*:dir(ltr) {
color: green;
}
*:dir(rtl) {
color: blue;
}
[dir="rtl"], [dir="rtl"] * {
color: blue;
}
/* Only applies directly to elements with the matching lang attribute */
[dir="ltr"] {
color: green;
}
The [dir]
attribute selector only works if the dir
attribute is explicitly set in the HTML. On the other hand, the :dir()
pseudo-class considers the computed text direction including inherited or programmatically determined directions.
Set the text direction in JavaScript using the dir
property on any HTMLElement
.
// Sets the text direction on the root <html> element
document.documentElement.dir = 'rtl';
Block and Inline Layouts
Use logical properties and values so the browser can automatically adapt layouts to different languages and text directions without restyling individual elements.
- Use
text-align: start
instead oftext-align: left
- Use
margin-inline-start
instead ofmargin-left
- Use
padding-block-end
instead ofpadding-bottom
Iconography
Mirroring for RTL Locales
Icons and UI elements should often be mirrored for RTL locales, including symbols representing direction, motion, or flow. Cloud Phone provides native CSS support for mirroring.
[dir="rtl"] .icon {
transform: scaleX(-1);
}
Some icons should not be mirrored including checkmarks, slashes, and icons depicting time like a clock.
Intl API
Cloud Phone natively supports the Intl
API without polyfills for robust internationalization.
Formatting Dates and Times
The Intl.DateTimeFormat
object provides language-sensitive date and time formatting, similar to libraries like Moment.js.
const today = new Date();
const dateParams = {
weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'
};
const fmtNZ = new Intl.DateTimeFormat('en-NZ', dateParams);
const fmtUS = new Intl.DateTimeFormat('en-US', dateParams);
console.log(fmtNZ.format(today)); // Monday, 6 January 2025
console.log(fmtUS.format(today)); // Monday, January 6, 2025
Number Formatting
The Intl.NumberFormat
object provides formatting utilities for numbers and numeric languages.
const fmtEur = new Intl.NumberFormat('fr-FR', {
style: 'currency', currency: 'EUR'
});
console.log(fmtEur.format(12345.67)); // 12 345,67 €
Intl.NumberFormat
supports decimal
(default), currency
, unit
, and percent
styles.
Collation for Sorting
The Intl.Collator
object provides compare
functions to Array.sort
for language-sensitive sorting.
const items = ['z', 'a', 'Z', 'A'];
const collator = new Intl.Collator('en', {
sensitivity: 'base'
});
console.log(items.sort(collator.compare)); // ["a", "A", "z", "Z"]
Pluralization and Message Formatting
Use libraries like Intl.MessageFormat for complex plural rules and interpolations.
Top Markets
Cloud Phone is currently available in 90+ markets around the world.
Countries
Cloud Phone has a strong and growing presence in emerging markets around the world. Top countries include:
- India
- Vietnam
- Bangladesh
- Philippines
- Pakistan
- South Africa
- Thailand
Languages
Cloud Phone is used in 25+ languages, as of January 2025. The language table below is sorted from most to least common.
Code | Language |
---|---|
en | English |
hi | Hindi |
vi | Vietnamese |
mr | Marathi |
bn | Bengali |
gu | Gujarati |
ru | Russian |
ar | Arabic |
ta | Tamil |
th | Thai |
fr | French |
te | Telugu |
kn | Kannada |
zh-TW | Traditional Chinese |
ur | Urdu |
pa | Punjabi |
he | Hebrew |
ml | Malayalam |
as | Assamese |
tl-TL | Tagalog (Philippines) |
fa | Persian (Farsi) |
pt-PT | Portuguese |
zh-CN | Simplified Chinese |
ms | Malay |
es-419 | Spanish (Latin America) |
sw | Swahili |
uz | Uzbek |
ISO Codes
Cloud Phone directly returns the language reported by the underlying operating system. Language and country codes may vary between manufacturers. Country codes may be omitted, or may be inaccurate.
For example, in the table above the language code for Tagalog in the Philippines is reported as tl-TL
, instead of tl-PH
. tl
is the ISO 639-1 language code for Tagalog and TL
is the ISO 3166-2 country code for Timor-Leste, not the Philippines.
i18n Tips
- Localize content for languages common in emerging markets
- Always let users choose their preferred language
- Automatically select a reasonable default language
- Prefer the
:lang()
and:dir()
pseudo-classes because they handle inheritance, are case insensitive, and allow fuzzy matching - Use logical properties and values to easily internationalize layout designs
- Take advantage of the native
Intl
API for formatting dates, times, quantities, and more
Conclusion
Internationalization enhances user experience and broadens the total addressable market. With extensive native i18n support, Cloud Phone helps you reach the next billion users seamlessly, no matter where in the world they are.