Apps include resources that can be specific to a particular culture. For example, an app can include culture-specific strings that are translated to the language of the current locale. It's a good practice to keep culture-specific resources separated from the rest of your app. Android resolves language- and culture-specific resources based on the system locale setting. You can provide support for different locales by using the resources directory in your Android project.
You can specify resources tailored to the culture of the people who
use your app. You can provide any resource type that is
appropriate for the language and culture of your users. For example, the
following screenshot shows an app displaying string and drawable resources in
the device's default (en_US
) locale and the Spanish
(es_ES
) locale.

Figure 1. App using different resources depending on the current locale
If you created your project using the Android SDK
Tools (read Creating an
Android Project), the tools create a res/
directory in the top level of
the project. Within this res/
directory are subdirectories for various resource
types. There are also a few default files such as res/values/strings.xml
, which holds
your string values.
Supporting different languages goes beyond using locale-specific resources. Some users choose a language that uses right-to-left (RTL) scripts, such as Arabic or Hebrew, for their UI locale. Other users view or generate content in a language that uses RTL scripts, even though they've set a language that uses LTR scripts, such as English, as their UI locale. To support both types of users, your app needs to do the following:
- Employ an RTL UI layout for RTL locales.
- Detect and declare the direction of text data that's displayed inside formatted messages. Usually, you can just call a method that determines the direction of text data for you.
Create locale directories and resource files
To add support for more locales, create additional directories inside
res/
. Each directory's name should adhere to the following format:
<resource type>-b+<language code>[+<country code>]
For example, values-b+es/
contains string
resources for locales with the language code es
. Similarly,
mipmap-b+es+ES/
contains icons for locales with the es
language code and the ES
country code.
Android loads the appropriate resources according to the locale settings of the
device at runtime. For more information, see
Providing Alternative Resources.
After you’ve decided on the locales to support, create the resource subdirectories and files. For example:
MyProject/ res/ values/ strings.xml values-b+es/ strings.xml mipmap/ country_flag.png mipmap-b+es+ES/ country_flag.png
For example, the following are some different resource files for different languages:
English strings (default locale), /values/strings.xml
:
<resources> <string name="hello_world">Hello World!</string> </resources>
Spanish strings (es
locale), /values-es/strings.xml
:
<resources> <string name="hello_world">¡Hola Mundo!</string> </resources>
United States' flag icon (default locale),
/mipmap/country_flag.png
:

Figure 2. Icon used for the default (en_US) locale
Spain's flag icon (es_ES
locale),
/mipmap-b+es+ES/country_flag.png
:

Figure 3. Icon used for the es_ES
locale
Note: You can use the locale qualifier (or any configuration qualifier) on any resource type, such as if you want to provide localized versions of your bitmap drawable. For more information, see Localization.
Use the resources in your app
You can reference the resources in your source code and other XML files using
each resource's name
attribute.
In your source code, you can refer to a resource using the syntax
R.<resource type>.<resource name>
. There are a variety
of methods that accept a resource this way.
For example:
Kotlin
// Get a string resource from your app's Resources val hello = resources.getString(R.string.hello_world) // Or supply a string resource to a method that requires a string TextView(this).apply { setText(R.string.hello_world) }
Java
// Get a string resource from your app's Resources String hello = getResources().getString(R.string.hello_world); // Or supply a string resource to a method that requires a string TextView textView = new TextView(this); textView.setText(R.string.hello_world);
In other XML files, you can refer to a resource with the syntax
@<resource type>/<resource name>
whenever the XML attribute accepts a compatible value.
For example:
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/country_flag" />
Important: To ensure that user language settings are prioritized
correctly, specify the languages your app supports using the resConfigs
property. For
more information, see
Specify the languages your app supports.
Format text in messages
One of the most common tasks in an app is formatting text. Localized messages get formatted by inserting text and numeric data into the appropriate positions. Unfortunately, when dealing with an RTL UI or RTL data, simple formatting can display incorrect or even unreadable text output.
Languages such as Arabic, Hebrew, Persian, and Urdu are written in an RTL direction overall. Some of their elements, however, such as numbers and embedded LTR text, are written in the LTR direction within the otherwise RTL text. Languages that use LTR scripts, including English, are also bidirectional because they can contain embedded RTL scripts that need to be displayed in an RTL direction.
Most of the time, it's the apps themselves that generate such instances of embedded opposite-direction text. They insert text data of an arbitrary language—and an arbitrary text direction—into localized messages. This mixing of directions often doesn't include a clear indication of where opposite-direction text starts and ends. These characteristics of app-generated text cause most of the problems.
Although the system's default handling of bidirectional text usually renders text as expected, it's possible that text won't render properly when your app inserts it into a localized message. The following situations present examples of cases where it's more likely that text won't appear correctly:
-
Inserted at the very start of the message:
PERSON_NAME is calling you
-
Starts with a number, such as in addresses or telephone numbers:
987 654-3210
-
Starts with punctuation, such as in a phone number:
+19876543210
-
Ends with punctuation:
Are you sure?
-
Contains both directions already:
The word בננה is Hebrew for banana.
Example
For example, assume that an app sometimes needs to display the message "Did you mean %s?", with an address inserted in place of the %s at runtime. Because the app supports different UI locales, the message comes from a locale-specific resource and uses the RTL direction when an RTL locale is in use. For a Hebrew UI, it should appear as follows:
האם התכוונת ל %s?
The suggestion, however, might come from a database that doesn't include text in the locale's language. For example, if the address in question is for a place in California, it appears in the database using English text. If you insert the address "15 Bay Street, Laurel, CA" into the RTL message without providing any hints regarding text direction, the result isn't expected or correct:
האם התכוונת ל 15 Bay Street, Laurel, CA?
Note that the house number appears to the right of the address, not to the left as intended, and makes the house number look more like a strange postal code. The same problem may occur if you include RTL text within a message that uses the LTR text direction.
Explanation and solution
The problem in previous example occurs because the text formatter doesn't specify that "15" is part of the address, so the system cannot determine whether the "15" is part of the RTL text that comes before it or the LTR text that comes after it.
To solve this problem, use the unicodeWrap()
method, found in the BidiFormatter
class, on
every piece of text that you insert into a localized message.
The only times when you shouldn't use
unicodeWrap()
include the
following:
- The text is being inserted into a machine-readable string, such as a URI or a SQL query.
- You already know that the piece of text is properly wrapped.
The unicodeWrap()
method
detects the direction of a string and wraps it in Unicode formatting characters
that declare that direction. Because the "15" now appears inside text that is
declared as LTR, it's displayed in the correct position:
האם התכוונת ל 15 Bay Street, Laurel, CA?
The following code snippet demonstrates how to use
unicodeWrap()
:
Kotlin
val mySuggestion = "15 Bay Street, Laurel, CA" val myBidiFormatter: BidiFormatter = BidiFormatter.getInstance() // The "did_you_mean" localized string resource includes // a "%s" placeholder for the suggestion. String.format(getString(R.string.did_you_mean), myBidiFormatter.unicodeWrap(mySuggestion))
Java
String mySuggestion = "15 Bay Street, Laurel, CA"; BidiFormatter myBidiFormatter = BidiFormatter.getInstance(); // The "did_you_mean" localized string resource includes // a "%s" placeholder for the suggestion. String.format(getString(R.string.did_you_mean), myBidiFormatter.unicodeWrap(mySuggestion));
Note: If your app targets Android 4.3 (API level 18) or
higher, use the version of BidiFormatter
found in the
Android Framework. Otherwise, use the version of
BidiFormatter
found in the Support Library.
Format numbers
Use format strings, not method calls, to convert numbers to strings in your app's logic:
Kotlin
var myIntAsString = "$myInt"
Java
String myIntAsString = String.format("%d", myInt);
This will format the numbers appropriately for your locale, which may include using a different set of digits.
When you use
String.format()
to create a
SQL query on a device whose locale uses its own set of digits, such as Persian
and most Arabic locales, problems occur if any of the parameters to the query
are numbers. This is because the number is formatted in the locale's digits, and
these digits are invalid in SQL.
To preserve ASCII-formatted numbers and keep the SQL query valid, you should
instead use the overloaded version of
String.format()
that
includes a locale as the first parameter. The locale argument should be
Locale.US
.
Support layout mirroring
People who use RTL scripts prefer an RTL user interface, which includes right-aligned menus, right-aligned text, and forward arrows pointing to the left.
Figure 4 shows the contrast between the LTR version of a screen within the Settings app and its RTL counterpart:


When adding RTL support to your app, it's particularly important to keep the following points in mind:
- RTL text mirroring is only supported in apps when used on devices running Android 4.2 (API level 17) or higher. To learn how to support text mirroring on older devices, see Provide support for legacy apps.
- To test whether your app supports an RTL text direction, test using developer options and invite people who use RTL scripts to use your app.
Note: To view additional design guidelines related to layout mirroring, including a list of elements that you should and shouldn't mirror, see the Bidirectionality material design guidelines.
To mirror the UI layout in your app so that it appears RTL in an RTL locale, complete the steps in the following sections.
Modify the build and manifest files
Modify your app module's build.gradle
file and app manifest file
as follows:
build.gradle (Module: app)
Groovy
android { ... defaultConfig { targetSdkVersion 17 // Or higher ... } }
Kotlin
android { ... defaultConfig { targetSdkVersion(17) // Or higher ... } }
AndroidManifest.xml
<manifest ... > ... <application ... android:supportsRtl="true"> </application> </manifest>
Note: If your app targets Android 4.1.1 (API level 16) or
lower, the android:supportsRtl
attribute is ignored, along with any
start
and end
attribute values that appear in your
app's layout files. In this case, RTL layout mirroring doesn't happen
automatically in your app.
Update existing resources
Convert left
and right
to start
and
end
, respectively, in each of your existing layout resource files.
By doing this, you allow the framework to align your app's UI elements based on
the user's language settings.
Note: Before updating your resources, learn how to provide support for legacy apps, or apps that target Android 4.1.1 (API level 16) and lower.
To use the framework's RTL alignment capabilities, change the attributes in your layout files that appear in Table 1.
Table 1. Attributes to use when your app supports multiple text directions
Table 2 shows how the system handles UI alignment attributes based on the
target SDK version, whether left
and right
attributes
are defined, and whether start
and end
attributes are
defined.
Table 2. UI element alignment behavior based on the target SDK version and defined attributes
|
Left and right defined? | Start and end defined? | Result |
---|---|---|---|
Yes | Yes | Yes |
start and end resolved, and override
left and right
|
Yes | Yes | No | Only left and right are used |
Yes | No | Yes | Only start and end are used |
No | Yes | Yes |
left and right are used (start and
end are ignored)
|
No | Yes | No | Only left and right are used |
No | No | Yes |
start and end resolved to left and
right
|
Add direction- and language-specific resources
This step involves adding specific versions of your layout, drawables, and values resource files that contain customized values for different languages and text directions.
In Android 4.2 (API level 17) and higher, you can use the -ldrtl
(layout-direction-right-to-left) and -ldltr
(layout-direction-left-to-right) resource qualifiers. To maintain backward
compatibility with loading existing resources, older versions of Android use a
resource's language qualifiers to infer the correct text direction.
Suppose that you want to add a specific layout file to support RTL scripts,
such as the Hebrew, Arabic, and Persian languages. To do this, you add a
layout-ldrtl/
directory in your res/
directory, as
shown in the following example:
res/ layout/ main.xml This layout file is loaded by default. layout-ldrtl/ main.xml This layout file is loaded for languages using an RTL text direction, including Arabic, Persian, and Hebrew.
If you want to add a specific version of the layout that is designed for only Arabic text, your directory structure becomes the following:
res/ layout/ main.xml This layout file is loaded by default. layout-ar/ main.xml This layout file is loaded for Arabic text. layout-ldrtl/ main.xml This layout file is loaded only for non-Arabic languages that use an RTL text direction.
Note: Language-specific resources take precedence over layout-direction-specific resources, which take precedence over the default resources.
Use supported widgets
As of Android 4.2 (API level 17), most framework UI elements support the RTL
text direction automatically. However, several framework elements, such as
ViewPager
, don't support the RTL text
direction.
Home-screen widgets support the RTL text direction as long as their
corresponding manifest files include the attribute assignment
android:supportsRtl="true"
.
Provide support for legacy apps
If your app targets Android 4.1.1 (API level 16) or lower, also include
left
and right
attributes, in addition to
start
and end
.
To check whether your layout should use the RTL text direction, use the following logic:
Kotlin
private fun shouldUseLayoutRtl(): Boolean { return if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { View.LAYOUT_DIRECTION_RTL == layoutDirection } else { false } }
Java
private boolean shouldUseLayoutRtl() { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { return View.LAYOUT_DIRECTION_RTL == getLayoutDirection(); } else { return false; } }
Note: To avoid compatibility issues, use version 23.0.1 or higher of the Android SDK Build Tools.
Test using developer options
On devices running Android 4.4 (API level 19) or higher, you can enable Force RTL layout direction in the on-device developer options. This setting allows you to see text that uses LTR scripts, such as English text, in RTL mode.
Update app logic
This section describes specific places in your app's logic that you should update when adapting your app for handling multiple text directions.
Property changes
To handle a change in any RTL-related property—such as layout
direction, layout parameters, padding, text direction, text alignment, or
drawable positioning—you can use the
onRtlPropertiesChanged()
callback. This callback allows you to get the current layout direction and
update an activity's View
objects accordingly.
Views
If you are creating a UI widget that is not directly part of an activity's view hierarchy, such as a dialog or a toast-like UI element, set the correct layout direction depending on the context. The following code snippet demonstrates how to complete this process:
Kotlin
val config: Configuration = context.resources.configuration view.layoutDirection = config.layoutDirection
Java
final Configuration config = getContext().getResources().getConfiguration(); view.setLayoutDirection(config.getLayoutDirection());
Several methods of the View
class require additional
consideration:
onMeasure()
- View measurements might vary depending on text direction.
onLayout()
- If you create your own layout implementation, then you'll need to call
super()
in your version ofonLayout()
and adapt your custom logic to support RTL scripts. onDraw()
- If you're implementing a custom view or adding advanced functionality to a
drawing, you'll need to update your code to support RTL scripts. Use the
following code to determine whether your widget is in RTL mode:
Kotlin
// On devices running Android 4.1.1 (API level 16) and lower, // you can call the isLayoutRtl() system method directly. fun isLayoutRtl(): Boolean = layoutDirection == LAYOUT_DIRECTION_RTL
Java
// On devices running Android 4.1.1 (API level 16) and lower, // you can call the isLayoutRtl() system method directly. public boolean isLayoutRtl() { return (getLayoutDirection() == LAYOUT_DIRECTION_RTL); }
Drawables
If you have a drawable that needs to be mirrored for an RTL layout, complete one of these steps based on the version of Android running on the device:
-
On devices running Android 4.3 (API level 18) and lower, you need to
add and define the
-ldrtl
resource files. -
On Android 4.4 (API level 19) and higher, you can use
android:autoMirrored="true"
when defining your drawable, which allows the system to handle RTL layout mirroring for you.Note: The
android:autoMirrored
attribute only works for simple drawables whose bidirectional mirroring is simply a graphical mirroring of the entire drawable. If your drawable contains multiple elements, or if reflecting your drawable would change its interpretation, you should perform the mirroring yourself. Whenever possible, check with a bidirectional expert to determine whether your mirrored drawables make sense to users.
Gravity
If your app's code is using Gravity.LEFT
or
Gravity.RIGHT
, you will need to change these
values to Gravity.START
and
Gravity.END
, respectively.
For example, if you're using the following code:
Kotlin
when (gravity and Gravity.HORIZONTAL_GRAVITY_MASK) { Gravity.LEFT -> { // Handle objects that are left-aligned. } Gravity.RIGHT -> { // Handle objects that are right-aligned. } }
Java
switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.LEFT: // Handle objects that are left-aligned. break; case Gravity.RIGHT: // Handle objects that are right-aligned. break; }
...you need to change it to the following:
Kotlin
val absoluteGravity: Int = Gravity.getAbsoluteGravity(gravity, layoutDirection) when (absoluteGravity and Gravity.HORIZONTAL_GRAVITY_MASK) { Gravity.LEFT -> { // Handle objects that are left-aligned. } Gravity.RIGHT -> { // Handle objects that are right-aligned. } }
Java
final int layoutDirection = getLayoutDirection(); final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.LEFT: // Handle objects that are left-aligned. break; case Gravity.RIGHT: // Handle objects that are right-aligned. break; }
This means that you can keep your existing code that handles left-aligned and
right-aligned values, even if you are using start
and
end
for your gravity values.
Note: When applying your gravity settings, use an
overloaded version of Gravity.apply()
that includes a
layoutDirection
argument.
Margin and padding
To support RTL scripts in your app, follow these best practices related to margin and padding values:
-
Use
getMarginStart()
andgetMarginEnd()
instead of the direction-specific attribute equivalents,leftMargin
andrightMargin
. -
When using
setMargins()
, swap the values of theleft
andright
arguments if your app detects RTL scripts. -
If your app includes custom padding logic, override
setPadding()
andsetPaddingRelative()
.
Support per-app language preferences
In many cases, multilingual users set their system language to one language—such as English—but they want to select other languages for specific apps, such as Dutch, Chinese, or Hindi. To help apps provide a better experience for these users, Android 13 introduces the following features for apps that support multiple languages:
-
System settings: A centralized location where users can select a preferred language for each app.
Your app must declare the
android:localeConfig
attribute in your app's manifest to tell the system that it supports multiple languages. To learn more, see the instructions for creating a resource file and declaring it in your app's manifest file. -
Additional APIs: These public APIs, such as the
setApplicationLocales()
andgetApplicationLocales()
methods inLocaleManager
, let apps set a different language from the system language at runtime.Apps that use custom in-app language pickers should use these APIs to ensure that users have a consistent user experience regardless of where they select their language preferences. The public APIs also help you reduce the amount of boilerplate code, they support split APKs, and they support Auto Backup for Apps to store app-level user language settings.
For backward compatibility with previous Android versions, equivalent APIs are also available in AndroidX. We recommend using Appcompat 1.6.0-beta01 or higher.
To learn more, see the instructions for implementing the new APIs.
See also
Additional resources
To learn more about supporting older devices, view the following resources: