Summary
Phrase provides no mechanism to produce valid Android BCP 47 resource directory names for locales that use numeric UN M.49 region codes, such as es-419 (Latin America & Caribbean). This makes it impossible to use Phrase's pull config to deliver Latin American Spanish — or any other numeric-region locale — to an Android project.
Background: Android locale directory naming
Android supports two naming schemes for locale resource directories:
Legacy (deprecated):
values-fr-rCA — language + -r prefix + ISO 3166-1 alpha-2 region code. Limited to two-letter region codes only.
BCP 47 (recommended, required for numeric regions):
values-b+es+419 — b+ prefix with + as separators. This is the only valid syntax for numeric region subtags and is what Android Studio itself generates (see screenshot below).
Per the Android documentation: "To use a BCP 47 language tag, combine the language tag with the b+ prefix and replace each subtag separator with +."
The two Phrase placeholders and where each one fails
<locale_name> — works for simple regions, breaks for es-419
Since Phrase allows you to set a custom locale name, you can rename fr-CA → fr-rCA and get a valid (if deprecated) values-fr-rCA directory. This is a known workaround.
However, Phrase does not allow + characters in locale names, and the legacy -r format cannot represent numeric region codes at all. There is no name you can set in Phrase that produces values-b+es+419.
<locale_code> — substitutes the raw BCP 47 code directly
Phrase substitutes es-419 as-is, producing values-es-419, which is not a valid Android qualifier and is silently ignored at runtime.
Summary of workarounds and why they all fail for es-419
| Approach |
fr-CA |
es-419 |
<locale_name> renamed to fr-rCA |
✅ Deprecated but works |
❌ Impossible — + not allowed in names, -r can't express numeric regions |
<locale_code> substitution |
❌ Produces values-fr-CA (invalid) |
❌ Produces values-es-419 (invalid) |
Manual per-locale file: entries |
✅ Works but doesn't scale |
❌ Still no way to express b+es+419 as an output path |
Expected behaviour
Phrase should provide a placeholder — e.g. <locale_android_bcp47> — that converts the locale ID into a valid Android BCP 47 directory qualifier:
| Locale ID |
Expected output path |
es-419 |
values-b+es+419 |
fr-CA |
values-b+fr+CA |
da-DK |
values-b+da+DK |
Impact
Any Android project targeting Latin American Spanish — a major market — is completely blocked. There is no workaround available regardless of how the locale is configured in Phrase.
Environment
- Platform: Android
- Pull config placeholder:
<locale_name> / <locale_code>
- Affected locales: all UN M.49 numeric region codes (
es-419, en-001, etc.)
- Resource type:
values XML
References
phrase:
project_id: <REDACTED>
pull:
targets:
# ── ENGLISH (base locale) ──────────────────────────────────────
# Path: values/ (no locale suffix)
# Pulls the English master strings back into the source tree.
- file: ./app/src/main/res/values/strings.xml
params:
tags: app
file_format: xml
locale_id: en # single locale
translation_key_prefix: app_
filter_by_prefix: true
include_empty_translations: true # keep untranslated keys
include_unverified_translations: false
format_options:
enclose_in_cdata: true
# ── OTHER LANGUAGES ───────────────────────────────────────────
# Path: values-<locale_name>/ (Phrase substitutes the locale)
# One entry covers all non-English locales at once.
- file: ./app/src/main/res/values-<locale_name>/strings.xml
params:
tags: app
file_format: xml
locale_ids: # list of target locales
- da-DK
- el
- es
- es-419
- fr
- fr-CA
- nl-NL
translation_key_prefix: app_
filter_by_prefix: true
include_empty_translations: false # skip missing translations
include_unverified_translations: false
format_options:
enclose_in_cdata: true
The same two-entry pattern repeats for every module (plurals.xml + strings.xml × each module).
We currently rely on a custom GitHub Action to handle path resolution as a workaround. Are we missing a more native or built-in solution?
Summary
Phrase provides no mechanism to produce valid Android BCP 47 resource directory names for locales that use numeric UN M.49 region codes, such as
es-419(Latin America & Caribbean). This makes it impossible to use Phrase's pull config to deliver Latin American Spanish — or any other numeric-region locale — to an Android project.Background: Android locale directory naming
Android supports two naming schemes for locale resource directories:
Legacy (deprecated):
values-fr-rCA— language +-rprefix + ISO 3166-1 alpha-2 region code. Limited to two-letter region codes only.BCP 47 (recommended, required for numeric regions):
values-b+es+419—b+prefix with+as separators. This is the only valid syntax for numeric region subtags and is what Android Studio itself generates (see screenshot below).The two Phrase placeholders and where each one fails
<locale_name>— works for simple regions, breaks fores-419Since Phrase allows you to set a custom locale name, you can rename
fr-CA→fr-rCAand get a valid (if deprecated)values-fr-rCAdirectory. This is a known workaround.However, Phrase does not allow
+characters in locale names, and the legacy-rformat cannot represent numeric region codes at all. There is no name you can set in Phrase that producesvalues-b+es+419.<locale_code>— substitutes the raw BCP 47 code directlyPhrase substitutes
es-419as-is, producingvalues-es-419, which is not a valid Android qualifier and is silently ignored at runtime.Summary of workarounds and why they all fail for
es-419fr-CAes-419<locale_name>renamed tofr-rCA+not allowed in names,-rcan't express numeric regions<locale_code>substitutionvalues-fr-CA(invalid)values-es-419(invalid)file:entriesb+es+419as an output pathExpected behaviour
Phrase should provide a placeholder — e.g.
<locale_android_bcp47>— that converts the locale ID into a valid Android BCP 47 directory qualifier:es-419values-b+es+419fr-CAvalues-b+fr+CAda-DKvalues-b+da+DKImpact
Any Android project targeting Latin American Spanish — a major market — is completely blocked. There is no workaround available regardless of how the locale is configured in Phrase.
Environment
<locale_name>/<locale_code>es-419,en-001, etc.)valuesXMLReferences
The same two-entry pattern repeats for every module (plurals.xml + strings.xml × each module).
We currently rely on a custom GitHub Action to handle path resolution as a workaround. Are we missing a more native or built-in solution?