VIEW THIS AS

Auto mode follows the Route Engine until you choose a viewpoint.

YOU ARE HERE

ROUTE CHECK

CONNECTED TO

WHAT NEXT

Use the canonical route for this room, or HELP if you are unsure.

How People Translate Quickly | BCP 47 Language Tag Validation: Route Every Translation to the Right Language, Script, Region and Locale

People searching BCP 47 language tags, language tag validation, locale identifier, BCP 47 locale, language region code, script subtag, zh-Hant vs zh-Hans, or how to choose the right locale code for localization are usually trying to solve a routing problem that masquerades as a translation problem. A target can be perfectly translated and still fail if the system labels it with the wrong language identifier, falls back to the wrong regional variant, applies the wrong plural rules, chooses the wrong script, or stores pt-BR content under pt-PT.

Current IETF BCP 47 identifies languages with structured tags, while Unicode CLDR and LDML use compatible language and locale identifiers for software internationalization. Current Unicode guidance shows familiar forms such as en, fr-CA, and zh-Hant, and also supports extensions for locale behavior. The search language around language subtag, script, region, variant, Unicode locale extension, canonical locale ID, and language matching points to one dominant reader job: use the most specific identifier the product actually knows, canonicalize it consistently, and validate every localization handoff so language data reaches the correct users.

This article has one dominant job: build a BCP 47 and locale-identifier validation workflow that prevents translated content from being routed to the wrong language, script, region, fallback chain, plural system, date/number formatter, or search index. It does not replace locale fallback, which decides what users see when a preferred target is unavailable. It does not replace target-language script QA, which checks the content itself. This page owns the identifier layer: the tags attached to locales and the matching rules that software uses to interpret them.

Quick answer

A reliable language-tag workflow is:

  1. use BCP 47/Unicode-style language identifiers rather than homemade locale strings;
  2. store the base language only when that is genuinely all the product knows;
  3. add a script subtag when script distinction matters, such as zh-Hans or zh-Hant;
  4. add a region when language behavior, market, regulation, spelling, terminology, or formatting is region-specific;
  5. canonicalize deprecated or alternate forms through a trusted locale library;
  6. reject or quarantine malformed tags before resource import;
  7. validate that TMS, repository, runtime, analytics, fallback, and formatting layers agree on the same identifier mapping;
  8. test likely-subtag and fallback behavior rather than guessing what zh, sr, or az means in a specific product;
  9. keep display names separate from machine identifiers;
  10. version locale-routing changes because one tag edit can reroute thousands of strings.

The central rule is:

a locale tag is not decoration; it is routing metadata that can change which language rules and translations the product applies.

What a BCP 47 tag represents

A simple tag can be:

en

Language: English.

A more specific tag:

en-GB

Language: English.

Region: United Kingdom.

Another:

zh-Hant

Language: Chinese.

Script: Traditional Han.

The tag can carry exactly the distinctions the product needs.

Why not use names such as “English_UK”?

Homemade identifiers create interoperability problems.

Examples:

  • English_UK;
  • ENGLISH-UK;
  • en_UK;
  • eng-GB.

One service accepts one.

Another expects en-GB.

A third silently maps to en.

Standard identifiers reduce translation between systems.

Language, script, and region are different dimensions

Language

en, fr, zh, sr.

Script

Latn, Cyrl, Hans, Hant.

Region

US, GB, CA, SG.

Do not use a region when what you really mean is a script.

Do not use a script when the distinction is truly market-specific terminology.

Tags should reflect the semantic difference the product needs.

Worked example 1: Chinese script

A product supports:

  • Simplified Chinese;
  • Traditional Chinese.

Weak identifiers:

  • zh-CN for all Simplified;
  • zh-TW for all Traditional.

That may work for specific markets.

But script and region are not identical concepts.

A user in Singapore may read Simplified Chinese.

A user in Hong Kong may use Traditional Chinese with regional differences.

A more deliberate model can use:

  • zh-Hans;
  • zh-Hant;
  • or region-specific forms such as zh-Hans-SG, zh-Hant-HK where the product truly needs them.

The right tag depends on the product’s localization distinctions.

Worked example 2: Serbian scripts

Serbian can be written in:

  • Cyrillic;
  • Latin.

A product that stores only:

sr

may not be able to distinguish the intended script resource.

Tags such as:

  • sr-Cyrl;
  • sr-Latn

make the script distinction explicit.

Step 1: identify the actual product distinction

Before creating locales, ask:

What must differ?

Possibilities:

  • language;
  • script;
  • spelling;
  • terminology;
  • legal text;
  • date format;
  • currency;
  • marketing content.

Then choose a tag that represents that distinction.

Do not create regional locales merely because the marketing team can name many countries.

Every locale creates:

  • translation work;
  • review work;
  • fallback logic;
  • release testing.

Use specificity where it has product value.

Step 2: distinguish language from market

A market can contain several languages.

Singapore:

  • English;
  • Chinese;
  • Malay;
  • Tamil.

A language can span several markets.

English:

  • US;
  • GB;
  • Singapore;
  • Australia;
  • Canada.

Do not use one field called locale to mean an unclear mixture of:

  • market;
  • language;
  • currency.

Separate dimensions in the product model when needed.

Step 3: keep display names separate

Machine identifier:

de-DE

Display name:

Deutsch (Deutschland)

Do not use the user-facing name as the system key.

Display names can:

  • translate;
  • change style;
  • use native/endonym form.

The machine identifier should remain stable.

Step 4: canonicalize input

Users or integrations may supply tags in different casing:

EN-us

en-US

BCP 47 tag matching is not about preserving arbitrary case style.

A locale library can canonicalize to conventional casing.

Store one canonical representation.

This reduces duplicate locale records.

Step 5: validate syntax

A valid-looking locale field can contain:

en_US

Some frameworks use underscores internally.

BCP 47 uses hyphen-separated subtags.

Know which layer expects which syntax.

If the API contract says BCP 47: reject or convert underscore forms at the boundary.

Do not let mixed syntax flow through the system.

Step 6: validate registered subtags

A tag can be syntactically well formed and still contain an invalid or unknown subtag.

Use:

  • IANA language-subtag registry;
  • Unicode/CLDR locale libraries.

Do not maintain your own list by memory.

Standards evolve.

Deprecated language codes

Some codes are deprecated or have preferred replacements.

A locale library can canonicalize or warn.

Do not silently rewrite without recording the mapping if historical data depends on the old identifier.

A migration should preserve identity.

Step 7: understand macrolanguage and naming complexity

Human language names do not always map neatly to one code.

Unicode’s guidance on choosing language identifiers explicitly notes cases where ordinary language names can be ambiguous or differ from registry names.

When a market requests:

Punjabi

ask:

  • which linguistic variety?
  • which script?
  • which market?

Do not infer solely from an English label.

Step 8: use script when it matters

Script is useful for:

  • Chinese Simplified/Traditional;
  • Serbian Latin/Cyrillic;
  • Azerbaijani script distinctions;
  • Kurdish script distinctions;
  • other multi-script languages.

A script subtag should describe writing system, not font.

Latn means Latin script.

It does not mean a particular typeface.

Step 9: use region when regional language differs materially

Examples:

  • en-US vs en-GB;
  • pt-BR vs pt-PT;
  • fr-FR vs fr-CA.

Differences can include:

  • spelling;
  • terminology;
  • legal language;
  • date and number conventions;
  • style.

If the product promises a regional locale, translations should reflect it.

Step 10: avoid fake region specificity

Do not label a generic translation:

fr-CA

merely because Canadian users receive it.

If the text is actually generic French from France, the identifier should not create false confidence.

Either:

  • localize it properly;
  • use a generic French resource;
  • use a documented fallback.

Metadata should tell the truth.

Step 11: likely subtags are useful but not absolute product truth

Unicode CLDR provides likely-subtag data to infer common script/region information.

For example, a library can expand an underspecified locale into a likely fuller form.

This is useful for:

  • formatting;
  • matching;
  • defaults.

It does not mean the inferred locale is what your customer contract promised.

Product policy can override likely defaults.

Step 12: minimize tags carefully

Locale libraries can sometimes remove subtags that can be inferred without losing likely identity.

This can be useful for canonical storage.

But do not minimize away a distinction the product intentionally uses.

Example: if sr-Latn is a separate product locale, keep the script explicit even if a tool has another default assumption.

Step 13: fallback matching is not identity

If fr-CA falls back to fr-FR, they are still different locales.

Do not merge them in the database just because fallback exists.

Identity: what locale the content belongs to.

Fallback: what to show when content is missing.

Keep these layers separate.

Step 14: locale matching should be deliberate

Suppose a TMX contains:

en

Destination expects:

en-US.

Should it match?

Possible answers:

  • yes, for generic source;
  • no, for strict regulatory content.

This is a trust/mapping policy.

The locale tag provides evidence.

It does not make the decision automatically.

Step 15: BCP 47 extensions

BCP 47 supports extensions.

Unicode maintains the u extension for locale behavior.

Example concepts can include:

  • collation;
  • numbering system;
  • calendar.

A locale identifier can therefore carry behavior beyond language/region.

Do not store these extensions unless the application actually uses them.

Specificity without behavior is just complexity.

Example: collation extension

A locale can request a particular collation order.

That is useful when the same language supports:

  • dictionary order;
  • phonebook order;
  • numeric sorting.

The sort behavior belongs in locale configuration, not translation strings.

Step 16: private-use subtags

BCP 47 includes private-use mechanisms.

They can support internal distinctions that standards do not define.

Use cautiously.

Private tags reduce interoperability because external systems do not know their semantics.

Document them.

Prefer registered or Unicode-standard mechanisms when available.

Step 17: variants

Some language forms use variant subtags.

Do not invent variant codes casually.

Check registry and library support.

A custom product edition may be better represented as:

  • content variant metadata;
  • feature flag;

rather than pretending it is a language variant.

Step 18: locale identifiers and plural rules

Plural rules depend on locale/language data.

If a target resource is mislabeled:

pl content as en,

runtime plural selection can be wrong even if the translation text itself is perfect.

Tag validation therefore affects grammar.

Step 19: locale identifiers and number formatting

A number such as:

1,234.56

can display differently under another locale.

The translation string may contain only a placeholder.

The locale tag tells the formatter how to render the number.

Wrong tag = wrong numeric convention.

Step 20: locale identifiers and date formatting

Same principle.

A wrong region can alter:

  • date order;
  • month names;
  • calendar defaults.

The translator may never see the formatted value in the TMS.

Locale routing is part of localization quality.

Step 21: locale identifiers and currency

Currency should usually come from business data, not simply from locale.

But locale influences:

  • symbol placement;
  • decimal style;
  • spacing.

Do not infer transaction currency solely from language tag.

Language/locale and business market are related but distinct.

Step 22: locale identifiers and collation

Sorting depends on locale.

If a list is tagged: sv vs en, letters such as Å/Ä/Ö can sort differently.

Wrong locale can make an otherwise translated product feel foreign.

Locale-aware collation should use the intended identifier.

Step 23: locale identifiers and fonts

A language/script tag can help the application choose:

  • shaping engine;
  • font fallback;
  • glyph coverage.

Do not rely on language tags alone for font selection, but they are useful routing metadata.

Wrong script tag can trigger the wrong font stack.

Step 24: locale identifiers and line breaking

Line-breaking rules can vary by script/language.

A correct tag gives text engines better context.

This matters in:

  • Thai;
  • Japanese;
  • Chinese;
  • Khmer;
  • other scripts with language-sensitive breaking.

Step 25: locale identifiers and spellcheck

A spellchecker loaded under: en-US

may flag valid British forms: colour, organise.

If the target is en-GB, the resource and QA locale should match.

QA depends on correct routing.

Step 26: TMS locale and runtime locale must map explicitly

A TMS may call a locale:

Portuguese (Brazil)

and export code:

pt-BR.

The runtime may use:

pt_BR internally.

Create a mapping table.

Do not let each integration guess.

A locale mapping table

Product/TMSCanonical BCP 47Runtime code
English USen-USen_US
Portuguese Brazilpt-BRpt_BR
Chinese Traditionalzh-Hantzh-Hant

The canonical column becomes the shared reference.

Step 27: validate repository paths

Locale directories often encode identifiers:

locales/en-US/

locales/fr-CA/

A typo creates a new directory rather than an obvious error.

CI should compare directory names against the supported-locale manifest.

Reject unknown tags.

Step 28: validate filenames

Files:

messages.en-US.json

messages.fr-FR.json

should use the same canonical tag set.

Do not permit:

  • fr_FR;
  • FR-fr;
  • french;

unless the build adapter deliberately maps them.

Consistency reduces routing defects.

Step 29: supported-locale manifest

Keep one authoritative list:

  • locale tag;
  • display name;
  • fallback;
  • script;
  • product tier;
  • status.

Every service should consume or derive from it.

Avoid five independent locale lists across:

  • TMS;
  • app;
  • website;
  • analytics;
  • support portal.

Drift is inevitable otherwise.

Step 30: test tag round trips

A locale can travel:

TMS → API → repository → build → runtime → analytics.

At every boundary, assert that:

fr-CA

remains the intended locale.

If one layer reduces it to:

fr,

you may lose regional routing.

Worked example 3: locale collapsed during export

TMS stores:

pt-BR and pt-PT.

A legacy exporter emits both as:

pt.

Now the repository has a collision.

One target overwrites the other.

No linguistic review can repair this after the fact.

Locale-code validation must happen before merge.

Step 31: analytics tags

If analytics records:

locale=fr

while product supports:

  • fr-FR;
  • fr-CA,

the team cannot measure fallback or quality by region.

Analytics should preserve the level of specificity needed for product decisions.

Privacy policy still applies.

Step 32: support and bug reports

A user reports:

French translation is wrong.

Which French?

A bug report should include canonical locale:

fr-CA.

This shortens diagnosis.

Display the locale in debug/about screens if appropriate.

Step 33: A/B tests and locale

An experiment may run only in:

en-US.

If matching uses base en, it may accidentally include:

  • en-GB;
  • en-AU.

Locale matching semantics matter beyond translation.

Use exact or fallback-aware matching deliberately.

Step 34: server content negotiation

HTTP uses language negotiation mechanisms such as Accept-Language.

User preferences can contain ordered language ranges.

Do not simply take the first raw tag and look for an exact file.

Use a standards-aware matching strategy.

The application may need:

  • exact match;
  • script-aware match;
  • regional fallback;
  • base-language fallback.

Step 35: user preference versus device locale

A device locale may be:

en-SG.

The user chooses app language:

zh-Hans.

Respect the explicit app choice.

Locale source priority should be documented.

Do not overwrite user choice with inferred market.

Step 36: account locale versus content locale

A user may prefer UI in English while reading content in Japanese.

One locale field cannot always represent both.

Separate:

  • interface locale;
  • content language;
  • market/region

when product requirements need it.

Step 37: database design

Store canonical tags in one field.

Avoid:

  • language name free text;
  • separate region field without validation;
  • inconsistent case.

Add a constraint or validation library at write time.

Garbage locale data spreads quickly.

Step 38: migrations

Changing locale ID:

iwhe

or a custom tag to a canonical tag can affect:

  • URLs;
  • resource paths;
  • user preferences;
  • TM/TB mappings;
  • analytics history.

Treat it as a migration.

Do not rename production locale identifiers casually.

Step 39: aliases

During migration, accept old tag as alias.

Canonicalize to the new tag internally.

Record:

  • old;
  • canonical.

Eventually remove the alias when clients have migrated.

This preserves compatibility.

Step 40: version the locale manifest

A locale configuration is production data.

Version changes:

  • new locale;
  • retired locale;
  • fallback change;
  • tag migration.

Review them like code.

One line can reroute a market.

Failure mode 1: homemade locale codes

Result: integrations disagree.

Repair: BCP 47/Unicode identifiers.

Failure mode 2: region used as script

Result: Chinese or Serbian routing becomes inaccurate.

Repair: script subtag.

Failure mode 3: generic locale labeled as regional

Result: false quality promise.

Repair: truthful identity/fallback.

Failure mode 4: tag lowercased or reformatted inconsistently

Result: duplicate locale records.

Repair: canonicalization.

Failure mode 5: exporter collapses regional variants

Result: one locale overwrites another.

Repair: round-trip tag validation.

Failure mode 6: unknown tag silently accepted

Result: resource never loads.

Repair: supported-locale manifest.

Failure mode 7: language tag used as currency market

Result: wrong business behavior.

Repair: separate dimensions.

Failure mode 8: analytics truncates region

Result: quality data loses specificity.

Repair: preserve canonical tag.

Failure mode 9: migration renames tag without aliases

Result: old clients/preferences break.

Repair: controlled migration.

Failure mode 10: fallback and identity conflated

Result: fr-CA content treated as fr-FR permanently.

Repair: separate requested locale and served locale.

A language-tag validation checklist

Syntax:

  • BCP 47 shape valid?
  • registered language?
  • script valid?
  • region valid?
  • extensions understood?

Product:

  • locale distinction real?
  • display name separate?
  • fallback explicit?
  • plural/formatting data available?
  • script/font behavior correct?

Integration:

  • TMS mapping?
  • repo path?
  • runtime mapping?
  • analytics mapping?
  • support debug value?

Migration:

  • aliases?
  • user preferences?
  • URLs?
  • history?

A supported-locale manifest example

en-US | English (United States) | script=Latn | fallback=en | active
fr-CA | Français (Canada)       | script=Latn | fallback=fr-FR | active
zh-Hans-SG | 简体中文(新加坡) | script=Hans | fallback=zh-Hans | active

The exact file format is unimportant.

The single source of truth is important.

A 30-minute locale audit

10 minutes

Export every locale code from:

  • TMS;
  • repo;
  • runtime.

10 minutes

Canonicalize and diff.

10 minutes

Check:

  • missing;
  • extra;
  • collapsed;
  • aliases.

This simple audit often reveals hidden routing debt.

Search-intent transfer

People searching:

  • “BCP 47 language tags”
  • “locale identifier”
  • “language region code”
  • “script subtag”
  • “zh-Hant vs zh-Hans”

need one operational answer:

use canonical standards-based language identifiers and validate them across every localization boundary so content, formatting, fallback, and QA route to the intended locale.

That is the job this URL owns.

Transfer to documentation portals

A documentation site may use paths:

Discover more from eduKate Singapore

Subscribe now to keep reading and get access to the full archive.

Continue reading