Relay users come from several regions and speak various languages. Relay developers need to prepare for these variations when implementing features. Specialists call this design discipline internationalization and localization (see Terms). This overview includes Mozilla- and Relay-specific notes.
- How a User Experiences Relay
- Development Workflows
- The Technical Details of Translation
- The Technical Details of Region Selection
- Terms
- Standards for Identifiers
Localization affects the user's experience in several ways.
When a user first visits the Relay website, their browser gets the page in English. The English page is pre-rendered for fast loading. The page is then re-rendered into their preferred language. When none of the user's preferred languages are available, then the user gets English.
The user sees different plan details based on their region. On the homepage there is a table comparing Relay premium plans. If a premium plan is available in their region, they see a price in their local currency. If a premium plan is not available, the user sees a prompt to join a waitlist. The FAQ also changes, to omit entries for unavailable plans.
A new or logged-out user sees the sign-in buttons in the page header. The button text is "Sign Up" and "Sign In" in the English localization. The text will be different if the user's preferred language is not English. When a user selects a sign-in button, they go to the Accounts website. This website is also in their preferred language. If they enter a new email, they go through account creation. Their new account profile stores their real email and preferred language.
The user generates a Relay email mask, which they use instead of their real email on other services. When that service sends an email to the Relay email mask, Relay forwards the email to the user's real email. The forwarded email has header and footer sections surrounding the original content. These sections appear in the user's preferred language from their Mozilla account.
When a user selects a premium plan, they go to the Subscription Platform website. Most of the content appears in their preferred language. The product details are in the primary supported language for their region. This may be different from the user's preferred language. The price is in their region's currency.
The Relay phone plan is a premium plan that is available in Canada and the United States. The user cannot buy a phone plan unless they are in one of those countries. A new premium user submits their real number. Relay sends a message to the user's number with a verification code. After verifying their real number, the user picks a Relay mask number. Relay only suggests mask numbers from the same country as the user's real phone. After the user selects a Relay mask number, Relay sends a welcome message from their mask number. Relay sends all the messages with English text.
When an external contact sends an SMS text to a mask number, the service forwards the message to the Relay user. The forwarded message has a prefix identifying the sender's number. The content is not translated. When an external contact calls the mask number, Relay starts a call with the Relay user.
When a user installs the Relay Add-On, the content is also in their preferred language. The Add-On reflects the Relay premium plan availability in their region.
The project README.md
has basic instructions on working with translations. This
section has more details.
The translations are in a separate repository, and included as a submodule. This references a specific commit in a separate repository.
When cloning a repo, you can also fetch the translations:
git clone --recurse-submodules https://github.com/mozilla/fx-private-relay.git
If you didn't use --recurse-submodules
when cloning, you can setup up translations
with:
git submodule init
git submodule update
When checking out a branch, git status
may show privaterelay/locales
has changed.
This is because the submodule commit is out of sync with the branch. To sync the
submodule with the branch, run:
git submodule update --remote
To automate syncing the translations submodule, set the configuration:
git config --global submodule.recurse true
When rebasing a branch to pick up changes from main
, the submodule commit may now be
out of sync. You may see privaterelay/locales
in the "Changes not staged for commit".
Do not add this directory, as it may revert translations to an earlier version.
If you already added it, for example with git add -u
, it is now in the staging area.
This is the "official" name of the "Changes to be committed" section. You can remove it
with:
git restore --staged privaterelay/locales
To remove it from the changed files, you can run one of:
git submodule update --remote
git restore privaterelay/locales
git checkout -- privaterelay/locales/
If you commit an update to privaterelay/locales
, you can remove it with git rebase
.
This is a powerful tool that requires deeper understanding of git
. See About Git
for a tutorial. See On undoing, fixing, or removing commits in git for a guided
experience. See Oh Shit, Git!?! for an emotional experience.
The process is:
- Run
git rebase origin/main
, and take care of any conflicts. - Find the commit that changed the submodule with
git log -- privaterelay/locales
. - Start an interactive rebase with
git rebase -i origin/main
. A rebase plan will open in your editor. - Edit the commit line, changing from
pick
toedit
. Save the rebase plan and exit the editor to start the rebase. - When rebasing gets to the target the commit, run
git checkout head~1 -- privaterelay/locales
. This sets the submodule version before your change and stages it. You can rungit status
to confirm it is in "Changes to be committed". - Run
git rebase --continue
to continue the rebase.
If the only change in the commit was updating the submodule, you'll now have an empty
commit. You can remove it with another interactive rebase by removing the commit line.
In the rebase planner, git
will annotate the line with an # empty
suffix.
A GitHub action, Fetch latest strings from the l10n repo, updates
translations. The action runs at midnight UTC. It updates the privaterelay/locales
submodule to the latest commit. The commit message is "Merge in latest l10n strings". If
there are no translation updates, then there will be no commit. The action status is
still "success".
The action uses a Personal Access Token (PAT), required to create the commit. If the nightly update fails, check that the PAT is still valid, and regenerate as needed.
Relay translations use the Fluent localization system. To start working with translations, read the documentation from the Fluent team.
Read the Fluent Syntax Guide while looking at some Relay .ftl
files. This guide
describes the syntax and promotes specific usage, such as:
- Variables for strings with inserted values
- References for product names inside other strings
- Selectors for including a number or count
The Fluent document Good Practices for Developers has useful tips.
Use pending translations when developing content for Relay. This is because the English text can change many times during development. Strings that are still changing are not a good fit for the translation process. The Localization team reviews translation changes. Translation teams spend time and effort translating strings. Development progress would slow down, and translators would waste effort. Pending translations avoid these issues.
The pending translations for the front end are in frontend/pendingTranslations.ftl. The back end strings are in privaterelay/pending_locales/en/pending.ftl. If both the front and back ends need the string, duplicate it to both files. These files are in the Relay codebase, not the translations submodule. Include pending translation changes with the pull request that uses them.
Do not re-use IDs for new or updated strings. The translations of existing strings
are valid and used in live content. When updating content, add a digit to the string ID
to signal it will replace another string. For example,
premium-promo-availability-warning-4
replaces premium-promo-availability-warning-3
.
Remove the old strings when they are no longer referenced in the code.
Translation is simple when the string has no embedded formatting. Some Relay strings are
more natural with embedded HTML. For example, prose that links to other content may have
an embedded <a>
element.
For Relay strings that include HTML, set the string to the complete sentence or UI element. Inject element attributes as variables.
For example, if the rendered HTML is:
<p class="relay-footnote">
To change your email preference, see
<a class="a-ext-link" href="https://relay.firefox.com/accounts/settings/">
Relay Settings
</a>
</p>
The Fluent string, with helper comments, could be:
# { $settings_url } (url) - full link to the settings page
# { $link_attrs } (string) - specific attributes added to links
# { settings-headline } (string) - the title of the settings page
email-footer-pref-link =
To change your email preference, see
<a href="{ $settings_url }" { $link_attrs }>{ settings-headline }</a>
Translators can re-arrange the sentence as needed for their language. The Pontoon UI helps translators with HTML strings. Pontoon linters detect some translation issues such as malformed HTML and missing variables. The URL and attributes are not part of the string. Translators cannot break them by changing them like translations. The values can change in the future without requiring re-translation.
The Django email template could be:
<p class="relay-footnote">
{% ftlmsg 'email-footer-pref-link'
settings_url=SITE_ORIGIN|add:'/accounts/settings/'
link_attrs='class="a-ext-link"' %}
</p>
The front end uses the <Localized>
component for strings with embedded HTML. See the
profile-label-welcome-html code for an example.
The privaterelay/locales
directory is a checkout of the git repository
mozilla-l10n/fx-private-relay-l10n. To add new strings for translation,
open a pull request against that repository.
The privaterelay/locales
checkout is an https
checkout by default. You will need to
authenticate with a GitHub password when pushing the branch. To switch to an ssh
checkout and authenticate with your SSH key:
cd privaterelay/locales
git remote set-url origin [email protected]:mozilla-l10n/fx-private-relay-l10n.git
git remote set-url origin --push [email protected]:mozilla-l10n/fx-private-relay-l10n.git
To prepare translatable strings for a pull request, first checkout the main branch:
cd privaterelay/locales
git fetch
git checkout main
Copy translations from the pending translations to the proper files in
privaterelay/locales/en/
. Do not change the translation files for other languages. Pontoon
handles those during import and export. The pending translations for the front end are
in frontend/pendingTranslations.ftl. The same for the back end are in
privaterelay/pending_locales/en/pending.ftl. A change can include strings from both
files.
When ready, create and push a branch:
cd privaterelay/locales
git branch message-updates-yyymmdd
git status
git add -u
git commit
git push -u origin message-updates-yyymmdd
You can then visit mozilla-l10n/fx-private-relay-l10n to create the pull request. You can make changes in the local submodule branch for any code review feedback.
After approval, the Localization team merges the new strings. The next nightly
translation update brings the new strings into Relay's main
branch.
At this point, the pending translations have redundant strings. The Fluent code may log warnings about these strings. The duplicate strings may confuse Relay engineers. Create a new Relay pull request to remove the migrated strings. The pending translations for the front end are in frontend/pendingTranslations.ftl. The same for the back end are in privaterelay/pending_locales/en/pending.ftl. A change can include removing strings from both files.
Adding new regions to a premium plan is a cross-company effort. The Legal team ensures Mozilla can do business in the new region. The Subscription Platform team integrates the expanded tax requirements. The Localization team identifies languages for the new region. Quality Assurance tests the experience in the new region and language. Customer Support expands support documents. Managers at several levels coordinate the work.
Premium plan expansion can take months. A Relay engineer should create a waffle flag for the expansion effort. This allows engineers to ship partial changes without affecting current users.
The Subscription Platform (version 2) uses Stripe for paid services. A Stripe Price tracks currency, taxes, and a subscription interval. Relay subscription intervals are monthly or yearly. There are usually two prices per region, one for each interval. The Subscription Platform also uses the price to track language and region. The product details are in the selected language. The Localization team helps pick the region's primary language. The product reports use the price to segment purchases by region.
The Relay product manager create the prices on the Stripe webpage. The product manager shares the new price IDs with the Relay engineers. In some cases, complex criteria selects the price for a region. For example, a region can use the prices of a different region. Another case is when a region has per-language prices. The manager highlights these complex cases.
The Relay engineer updates the data structures in privaterelay/plans.py:
- Add new currencies in
CurrencyStr
- Add new regions in
CountryStr
- Add new prices details in
_STRIPE_PLAN_DATA
- Add a new section in
_RELAY_PLANS
, such aspremium_expansion
, with the expanded regions - Update the relevant functions to take a boolean signaling the waffle flag is on or off. See PR #3745, which retired the 2023 expansion flag, for details.
Relay staff can turn on the expansion flag for themselves and others. With an enabled flag, users can view new content and test buying a subscription.
Note
Testing subscriptions in a new region requires setting the preferred language.
Change the browser language preference, intl.accept_languagues
in about:config
.
Use a Mozilla account created with that language preference.
Do not use a language-switcher extension. Many extensions are not allowed to run on the Accounts website. Inconsistent language preferences may invalidate testing. See "Identifying The User's Preferred Languages" for more information.
When the expansion ships to all users, a Relay engineer can cleanup the data structures:
- Merge the expanded section in
_RELAY_PLANS
, and re-sort - Drop the expansion boolean. Or, add a comment for the unused variable. Or, if time allows, leave flexible code for future expansions.
- Remove other code and documentation references to the expansion waffle flag
The back-end library django-ftl logs when a translation is not available. The log
message writes to stderr
with severity ERROR
and name django_ftl.message_errors
.
The log message looks like:
FTL exception for locale [es-mx], message 'relay-email-your-dashboard', args {}: KeyError('relay-email-your-dashboard')"
The report "FTL Errors in Relay Production" gathers and categorizes recent errors. The filter supported means Relay has an assigned language team in Pontoon. The other filters are by locale and key (or string ID). A Relay engineer uses this report to detect issues with a team's translation. The report also measures the traffic volume for unsupported languages.
The source query defines the list of supported languages. The list needs an update when a new language team takes on the Relay project. A Relay engineer can update the query, called a "data source" in Looker, to add the new team.
There are no error reports for the front end or the add-on. Relay engineers or QA catch issues in translated strings with manual testing.
Relay supports over 20 languages. This section details the technologies used to deploy those translations.
The browser communicates the user's preferred languages with each web request. The
Accept-Language
header encodes this preference. The default header for
Firefox downloaded in the US is "en-US, en
". This header specifies a preference for
English as spoken in the United States. If that is not available, fallback to generic
English.
Browsers detect a user's desired language when setting up for the first time. For
example, a browser could ask the operating system. The browser defaults change based on
the language. A Firefox user could change the defaults by typing about:config
in the
location bar. After accepting the risk, they can find and change the key
intl.accept_languages
. This changes the Accept-Language
header sent to websites.
Most users do not change their language settings and keep the browser defaults.
Translators use Pontoon to set the value for intl.accept_languages
. See the
intl.properties group, LOCALES tab, for values for each language.
Note that many end in "en-US, en
" as fallback languages. All include "en
" for
generic English.
Some browser extensions allow changing Accept-Language
. This can be useful for testing
translations on the Relay front end. Extensions are disabled on some websites due to
security concerns. These websites include Accounts and the Subscription Platform. Do not
use language-switcher extensions when testing user flows for buying subscriptions.
Relay may not support the user's top preferred language, but picks the best match. Different contexts use different implementations:
Context | Implementation | Accept-Language source |
Content Scope |
---|---|---|---|
API and Web Server | Django middleware | Web Browser | Error messages |
Background Tasks | Django's parse_accept_lang_header() | Mozilla account (at sign-up) | Emails |
Website | @fluent/langneg | Web Browser | Relay Website |
Add-On | i18n API | Web Browser | Add-on |
Each implementation parses the Accept-Language
header into languages. They start at
the first entry and continue until there is a match to a supported language. If there
are no supported languages, the code falls back to English ("en"
).
Mozilla uses Pontoon to identify supported languages and coordinate translations. Volunteer translation teams coordinate their own translation work. They set standards and decide which projects to support.
Language tags identify translation teams. Many team identifiers are a language code
(such as "fr
" for French, or "scn"
for Sicilian). Other teams use a
language-plus-region code, such as "es-ES"
for Spanish spoken in Spain. A longer
code is "ca-valencia"
for Catalan spoken in the Valencian Community of Spain.
Some teams are active and quick to translate, such as the French and German
teams. Some teams volunteer to translate a project, such as Interlingua. Other teams
have few or no active members, such as Luxembourgish. These small teams focus their
resources on a few projects. The Localization team (Slack channel #l10n
) is familiar
with team capacity. This team decides when to expand a project to new languages. They
reach out to the language team leads to ask for expansion.
Language support varies among Mozilla projects. Some relevant projects, and the supported languages as of September 2023, include:
- Firefox Relay Website (26 languages)
- Firefox Relay Add-on (26 languages)
- Accounts (78 languages)
- Mozilla.org (98 languages)
- SUMO for support.mozilla.org (87 languages)
- AMO Frontend for addons.mozilla.org (64 languages)
Relay staff can check translation status on the Pontoon project sites. They may use the completeness of translated strings to decide to turn on a feature for a region. They may focus on completeness of the Relay-specific project. These are the projects for the Firefox Relay Website and the Add-on. Relay staff may identify critical strings in other projects for integration efforts.
Pontoon syncs translations between its database and code repositories. The Mozilla Localization Team GitHub organization hosts most project translation repositories. The Pontoon project page has a link to the project repository. Some relevant repositories include:
- mozilla-l10n/fx-private-relay-l10n for the Firefox Relay Website
- mozilla-l10n/fx-private-relay-add-on-l10n for the Firefox Relay Add-on
- mozilla/fxa-content-server-l10n for Accounts, including subscription pages
- mozilla-l10n/www-l10n for mozilla.org
- mozilla-l10n/sumo-l10n for support.mozilla.org
- mozilla/addons-frontend for addons.mozilla.org
Pontoon writes translation updates as new commits to their repository. Relay projects include the translation repositories as git submodules, such as privaterelay/locales. A submodule references a specific commit in a separate repository. A GitHub action updates this commit to the latest each night. This action synchronizes the main Relay branch with the latest translations.
This synchronization strategy works best for Relay. It balances up-to-date translation changes against the complexity of submodules. There are other strategies. Some projects allow Pontoon to write to their code repository. This clutters commit history with translations. It also runs continuous integration tests for each translation update. Other projects keep a copy of translations and sync them when needed. This requires manual work, documentation, and discipline. It often falls to a single engineer, and is not done when they are busy or out of office.
Translations for Relay, and many projects at Mozilla, use the Fluent format. The
"en" bundle (American / generic English) has the source translations. Developers
split the translations into functional areas, such as faq.ftl for the FAQ page.
The other language bundles are translations of the "en"
bundle, coordinated in
Pontoon. Relay's continuous testing creates Docker images that include the translations.
The Django back end uses select Fluent files. The front end uses translations
embedded into the JavaScript during Docker image creation.
Many teams and systems work together to get new translations to Relay users. The high-level steps are:
- A Relay designer and copy-editor create designs with new strings.
- A Relay developer adds pending strings to the Relay repository.
- Relay staff refine the strings during feature development.
- A Relay developer submits the strings to the translation repository.
- The Localization team reviews the strings.
- The Localization team merges the accepted strings into the translation repository.
- Pontoon imports the new strings.
- Translators translate the new strings.
- Pontoon exports translations to the translation repository.
- The nightly GitHub action updates the Relay repository with the latest translations.
- The continuous testing process builds a release Docker image with the translations.
- The continuous integration process releases the Docker image to the stage environment.
- The Quality Assurance (QA) team checks the stage environment. They verify the features and translations.
- A Service Reliability Engineer (SRE) releases the Docker image to the production environment. Users see the new content in their language.
- A Relay developer removes the pending strings from the Relay repository.
The section Submitting Pending Translations details the development process.
All Relay-supported languages use a left-to-right (LTR) script. For example, the default Relay language is English, the same language as this document. English uses the Latin script, written left-to-right.
Warning
None of Relay's supported languages use right-to-left scripts. Other Mozilla projects support right-to-left (RTL) languages. They spent significant time and effort to add the first RTL language.
To add the first RTL script, developers must educate themselves on internationalization techniques. Designers will learn RTL conventions. Developers examine every webpage, email, and UI element. The content should work regardless of text direction. Buttons should flow with the text direction. Some existing translation strings will change and need re-translation. The new language translation team will want to review strings in the context of the website.
The first right-to-left language is the hardest. The next languages are as easy to add as other left-to-right languages.
Hebrew, Arabic, and Persian are the most widespread right-to-left modern writing systems. The support website has localized content in Hebrew, Arabic, and Persian. These pages show right-to-left layouts. Compare them to a left-to-right layout such as the French localization.
Relay premium plans are not available world-wide, only in some regions. As of September 2023, the premium email service is available in 34 regions. Phone infrastructure varies, so the phone service is available in only 2 regions. This section details the technologies used to distinguish users by region.
The stage and production deployments use similar load balancers. The Service Reliability
Engineers (SREs) configured these to add an X-Client-Region
header. The header value
is the visitor's region, based on their IP address. There is no way for a user to
override this region selection. A tester can use a VPN to change their IP address
and their detected region.
The X-Client-Region
header is sometimes missing. In stage and production, some IP
addresses do not have a clear region. The development deployment does not have the
header. Local development also omits the header.
Without a X-Client-Region
header, the code guesses a region from the Accept-Language
header. The language fallback looks for a language code with a regional variant. For
example, the language code "es-MX"
(Mexican Spanish) implies region code "MX"
(Mexico). When there is no regional variant, the code guesses a probable region from a
lookup table. The lookup table uses Unicode Common Locale Data Repository (CLDR)
Supplemental Data. The Language-Territory Information data includes estimates of
language speakers by region.
The user's region determines what premium plans are available. The API sends this data in /api/v1/runtime_data. The front end adjusts content based on availability in the user's region.
The Subscription Platform (version 2) uses Stripe. A Stripe product represents each Relay plan, such as the premium email service. A Stripe price represents how a user will pay. The price has an associated product, a currency, tax details, and a subscription interval. Many Relay plans have monthly and yearly subscription intervals. The VPN bundle only has a yearly subscription interval.
The Subscription Platform associates a price with a region. Product reports may use this to segment purchasers by region. Relay detects the user's region and determines what plans are available. Relay selects the relevant price ID when sending a user to the Subscription Platform. The Subscription Platform does not confirm the user's region.
The Subscription Platform associates a price with a language. On the subscription page, product details appear in the price language. The rest of the subscription page appears in the user's preferred language. For most users, the price language and preferred language should be the same.
Many regions have their own price, such as Denmark, which uses the Danish language and the krone. Others share the price of a nearby region, such as Austria, which uses Germany's prices in German and Euros. A minority are more complex, choosing a price based on language. Belgium users share the price in Euros of a neighbor, based on language. Switzerland has separate prices for German, French, and Italian speakers. All the Swiss prices are in Swiss Francs. See privaterelay/plans.py for details.
The Subscription Platform supports certain markets and currencies. The Subscription Platform has configured Stripe to reject payments from outside these markets. A Relay user's payment method, such as a credit card, has a payment region. The user region detected by Relay may differ from the user's payment region. Relay may say a user can buy a plan, and then Stripe may reject payment.
In development and stage, there are test prices connected to the Stripe test mode. A test visitor from the United States will get these prices. Other regions get the production prices. One feature of test mode is test credit cards. These cards allow testing without paying real money, testing failure modes, and testing payments linked to other countries. A test card associated with the United States or Canada can be used with the test price. A test card associated with a European or other region will be rejected with a message like "The currency of this subscription is not valid for the country associated with your payment".
Relay developers will see these terms as they work in this topic:
- Internationalization: Designing a service to adapt to different locales without per-locale code changes. This is often abbreviated i18n, to stand for the starting letter "i", the next 18 letters, and the ending letter "n". This abbreviation also avoids the spelling differences between American and British English.
- Language: A spoken or written system of communication. English is a broad language category. A language can be specific to a region, such as German spoken in Switzerland. In localization, the written system can be important as well. For example, Chinese can vary between mainland China and other regions. It can also use Simplified or Traditional Chinese script when written.
- Locale: Shared cultural conventions that appear in a user interface. This can include language, letters, and currencies. It can also include formats for prices, numbers, dates, and times. For example, American English can be a locale. This locale uses the English alphabet in left-to-right text. Prices are in US Dollars (USD, "$5.99"). Numbers use commas as thousands separators (525,960 minutes in a year). The first number in a short date is the month ("9/12/2023" for September 12, 2023). English-speaking users in other regions share some of these conventions. Other conventions, like currency and date formats, will vary.
- Localization: Adapting software to a specific locale. This includes translating text and using locale-specific formats. This is often abbreviated L10n, in a similar way to i18n. A capital "L" avoids confusing a lowercase "l" with an uppercase "I".
- Region: An administrative division that can be the basis for a locale. Mozilla prefers to use "Region" instead of "Country" when talking about localization. A region can include areas within a country, as well as areas that span countries.
- Translation: The process of adapting language strings or patterns to a second language. This is one aspect of Localization. At Mozilla, American English is the source language for interface text.
There are internet standard identifiers for regions, languages, locales, and currency. In most cases, Mozilla uses the standard identifiers. There are exceptions for common usage and legacy cases. Relay users should follow the standards, unless Mozilla has an established exception.
- Currency: ISO 4217 defines codes for currencies.
- The Subscription Platform and Relay use the upper-case, three-letter codes.
For example,
"USD"
for United States dollars and"EUR"
for Euros.
- The Subscription Platform and Relay use the upper-case, three-letter codes.
For example,
- Language: ISO 639 is a multi-part standard for identifying languages.
Wikipedia has a useful table of ISO 639-1 codes (two lowercase letters). The
Library of Congress has a table of languages with ISO 639-2 codes (three
lowercase letters). This table has the related ISO 639-1 codes, when available.
- Mozilla uses the ISO 639-1 two-letter code when available. For example,
"en"
identifies English, and"fr"
identifies French. Mozilla falls back to the ISO 639-2 three-letter code (such as"yue"
for Cantonese).
- Mozilla uses the ISO 639-1 two-letter code when available. For example,
- Locale: Locales are usually identified by a language tag. This is a language
identifier, plus extra identifiers as needed. RFC 5646, "Tags for Identifying
Languages", defines language tags. This RFC is the second half of BCP 47.
The "Accept-Language" header (RFC 9110) uses language tags. This
header is how a web browser communicates the user's preferred language.
- Mozilla uses language tags to identify locales. For
example,
"fr"
identifies generic French, and"es-ES"
for Spanish in Spain. - Pontoon uses
"en"
, rather than"en-US"
, for English as written in the United States. Other English-speaking locales include the region. For example,"en-GB"
identifies English as used in the United Kingdom . - The default Firefox
Accept-Language
header for every language includes"en"
. Most also include "en-US
". See "Identifying the User's Preferred Languages" for more details. - Mozilla uses the language tag
"zh-CN"
for Simplified Chinese as common in China. The language tag"zh-TW"
stands for Traditional Chinese as common in Taiwan. Recent standards may instead emphasize the script. For example,"zh-Hans"
means Han Simplified, and"zh-Hant"
means Han Traditional. A region can make the tag more specific, such as"zh-Hans-CN"
. Most browsers, including Firefox, use"zh-CN"
and"zh-TW"
in theAccept-Language
header. Mozilla web services may need to handle the-Hans
and-Hant
variants. - RFC 9110 specifies "quality values", or "qvalues" to weight language
preferences. The header
"Accept-Language: en-US,en;q=0.5"
uses qvalues. Some websites break when parsing a header with qvalues. Firefox and other browsers do not use qvalues inAccept-Language
defaults. Instead, the order of languages is the order of preference.
- Mozilla uses language tags to identify locales. For
example,
- Region: ISO 3166 is a multi-part standard for identifying countries,
territories, and subdivisions. The ISO 3166-1 alpha-2 code (two uppercase
letters) is common as a region identifier.
- Mozilla uses the ISO 3166-1 alpha-2 code for a region when available. For example,
"US"
identifies the United States, and"DE"
identifies Germany. An exception is Valencia, which uses"valencia"
. Another is the Surselva Region, which uses"sursilv"
.
- Mozilla uses the ISO 3166-1 alpha-2 code for a region when available. For example,