# MultiLanguages applies an app-wide locale to Android contexts

> MultiLanguages is an Apache-2.0 Android framework that attaches a chosen locale to every Context, so an app can offer in-app language switching that follows the system by default. It suits Android teams shipping several locales, and it requires touching the base Activity and Application classes.

**getActivity/MultiLanguages** — Android 多语种适配框架，兼容高版本，适配第三方库语种

- Repository: https://github.com/getActivity/MultiLanguages
- Stars: 1,128 · Forks: 142
- Language: Java
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/getactivity-multilanguages

## The model is one locale bound to the Context, not per-Activity configuration

MultiLanguages is an Android multi-language adaptation framework, and the key design decision is where the language lives. Instead of resolving resources per Activity or per configuration, the framework binds a locale to the Context itself, and every Context subclass rebinds it in attachBaseContext.

The API surface shows this. There is a method to set the current language, one to get it, and one to clear it and follow the system again. Alongside those are a system language getter, an isSystemLanguage check, and two comparison helpers, equalsLanguage and equalsLanguageAndCountry, which decide whether two Locale objects represent the same language or the same language in the same place.

The distinction between those two comparison methods is the detail that shows the author has thought about the hard cases. The README gives the examples: Chinese has simplified and traditional forms, and English has American and British forms, so equalsLanguage asks whether they are the same language while equalsLanguageAndCountry asks whether they are the same language and the same place, for example simplified Chinese in mainland China against traditional Chinese in Taiwan.

The README is explicit that you should not use Locale's own equals method for this, because it compares more information and will therefore give you the wrong answer. It links a GitHub issue about the topic, which is the kind of detail that comes from a user filing a bug rather than from documentation written in advance.

For a framework whose whole job is getting the right strings, getting this comparison right is the difference between a settings screen that reads naturally and one that shows a language list with duplicate-looking entries.

## Integration starts with a JitPack dependency and an Application hook

The dependency comes from JitPack, and the repository declaration depends on your Gradle version, which is the first thing you will hit.

If your Gradle configuration is below 7.0, you add the remote repository in the build.gradle file:

```groovy
allprojects {
    repositories {
        // JitPack 远程仓库：https://jitpack.io
        maven { url 'https://jitpack.io' }
    }
}
```

If your Gradle configuration is 7.0 or above, the same repository goes into settings.gradle instead:

```groovy
dependencyResolutionManagement {
    repositories {
        // JitPack 远程仓库：https://jitpack.io
        maven { url 'https://jitpack.io' }
    }
}
```

Then the dependency itself is added in the app module's build.gradle:

```groovy
dependencies {
    // 语种切换框架：https://github.com/getActivity/MultiLanguages
    implementation 'com.github.getActivity:MultiLanguages:10.5'
}
```

The coordinate is the GitHub-style one, com.github.getActivity:MultiLanguages, at version 10.5, which is the current release. The repository has a jitpack.yml at the top level, which is what tells JitPack how to build the library.

The initialisation is in the Application class, and the locale binding happens in attachBaseContext:

```java
public final class XxxApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        // 初始化语种切换框架
        MultiLanguages.init(this);
    }
}
```

```java
@Override
protected void attachBaseContext(Context base) {
    // 绑定语种
    super.attachBaseContext(MultiLanguages.attach(base));
}
```

The base Activity needs the same treatment, and the README is clear that every Context subclass needs it, mentioning that a Service is the same case.

One exception is called out explicitly, and it saves people from an unnecessary override: Fragment does not need it, because it is not a Context subclass.

## Changing the language returns a boolean that tells you to restart

The core API is small enough to read in one go, and its most important feature is the return value.

```java
// 设置当前的语种（返回 true 表示需要重启 App）
MultiLanguages.setAppLanguage(@Nullable Context context, @Nullable Locale locale);

// 获取当前的语种
MultiLanguages.getAppLanguage(@Nullable Context context);

// 跟随系统语种（返回 true 表示需要重启 App）
MultiLanguages.clearAppLanguage(@Nullable Context context);
```

Both setAppLanguage and clearAppLanguage return true when the app needs to be restarted. That is a design decision with consequences, and the framework is upfront about it rather than hiding it. Because the locale is bound to the Context at attach time, a language change cannot retroactively re-resolve every string in every live Activity. The application has to be recreated for the new locale to take effect throughout.

The documented pattern in the README's usage example puts a language picker button handler together with the restart, and it uses Activity transition animations to soften the jump:

```java
if (restart) {
    // 我们可以充分运用 Activity 跳转动画，在跳转的时候设置一个渐变的效果
    Intent intent = new Intent(this, MainActivity.class);
    // Github 地址：https://github.com/getActivity/MultiLanguages/issues/55
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    startActivity(intent);
    overridePendingTransition(R.anim.activity_alpha_in, R.anim.activity_alpha_out);
    finish();
}
```

The intent flags clear the task and create it, so the app restarts into the new language rather than carrying old Activities forward. The README links a GitHub issue for that pattern.

The example also shows the language selection itself, dispatching on the button id and using LocaleContract helpers to get a specific locale:

```java
case R.id.btn_language_cn:
    restart = MultiLanguages.setAppLanguage(this, LocaleContract.getSimplifiedChineseLocale());
    break;
```

So the framework does not ship the list of languages an app should offer. That is the app's decision, which is the right division of labour for something that has to work with any set of locales you ship.

## A language change listener reports both app-level and system-level switches

There is a listener interface, and it distinguishes two events that are easy to conflate.

```java
// 设置语种变化监听器
MultiLanguages.setOnLanguageListener(new OnLanguageListener() {

    @Override
    public void onAppLocaleChange(@NonNull Locale oldLocale, @NonNull Locale newLocale) {
        Log.d("MultiLanguages", "监听到应用切换了语种，旧语种：" + oldLocale + "，新语种：" + newLocale);
    }

    @Override
    public void onSystemLocaleChange(@NonNull Locale oldLocale, @NonNull Locale newLocale) {
        Log.d("MultiLanguages", "监听到系统切换了语种，旧语种：" + oldLocale + "，新语种：" + newLocale + "，是否跟随系统：" + MultiLanguages.isSystemLanguage());
    }
});
```

onAppLocaleChange fires when the app switches language, which is the case the user triggered in your settings screen. onSystemLocaleChange fires when the system locale changes, which happens when the user changes the device language in system settings while your app is following the system.

The second callback carries an isSystemLanguage check in the example, which is the key detail. If the app has an explicit language set, a system change does not affect the app, and the framework needs to expose that fact so the app can decide what to do, for instance whether to prompt the user that the app is not following the system.

That two-event model is the practical answer to the classic in-app language problem. If your app pins a language, the system preference becomes irrelevant to it, and a good implementation reflects that in the settings screen rather than showing a system language that has no effect.

There are also lower level helpers for the cases where you need to work with a specific locale's resources directly, namely getLanguageString to fetch a string in a given locale, generateLanguageResources to build a Resources object for a locale, updateAppLanguage to refresh a Context or a Resources instance, and setDefaultLanguage to set the default locale, which the README says should be done as early as possible.

## The repository is a Gradle project with a JitPack build and a demo app

The repository layout shows what the project actually contains. There is a library/ directory holding the framework itself, an app/ directory that is the demo application, a build.gradle at the top level plus a common.gradle for shared configuration, gradle.properties, settings.gradle, the gradle/ wrapper directory with gradlew and gradlew.bat, and jitpack.yml which is the JitPack build configuration.

The wrapper scripts being committed is the right practice for a library, because it pins the Gradle version for anyone who builds the project, including JitPack itself when it produces the artifact you depend on.

The documentation is duplicated across languages. There is a README.md and a README-en.md, and a HelpDoc.md and a HelpDoc-en.md, and the README links to the Chinese help document for common questions. The English README is the one linked from the first line of the Chinese one, which is a small but noticeable effort at making the project reachable to non-Chinese developers.

There is also a picture/ directory holding the dynamic figure the README embeds, a logo.png, and a .github/ directory for the workflow configuration.

The demo app matters more than it looks. The README says you can scan a QR code to download the demo for demonstration or testing, and there is a direct download link to MultiLanguages.apk on the releases page for when the QR code path does not work. Shipping an installable demo alongside a framework that changes how your Application and Activity classes behave is the practical way to let someone feel the restart cost before they integrate it into a real project.

## Apache-2.0, version 10.5, and a fast release cadence from a single author

The licence is Apache-2.0, with the LICENSE file in the repository as the authoritative text, and this is a licensing statement rather than legal advice. For a library that other apps embed, a permissive licence with no copyleft obligations is the expected choice, and Apache-2.0 additionally grants patent rights.

The version record shows steady work. The recent releases are 10.5 on 2026-10-01, 10.2 on 2025-11-29 and 10.0 on 2025-11-19. The repository is not archived and the last push was on 2026-10-01, the same day as the 10.5 release, so the released artifact and the repository state are in step.

The major version number reaching 10 tells you this is a mature library rather than a new one, and the version is part of the dependency coordinate, so an upgrade is a one-line change plus a re-read of the release notes.

The author is the same person behind a set of other Android libraries, and the README lists them, including an Android project template in Java and in Kotlin, a permission framework, a toast framework, a networking framework, a title bar framework, a floating window framework, a device compatibility framework, a shape view and a fault-tolerant Gson parser. That list is also the best available signal about how the framework will be maintained: the same author is being asked to keep several libraries current, and the language switching framework is one of the more commonly used pieces of that set.

The README also points to a resource outside the repository, a list of language codes, which is the reference to consult when building the list of languages your app offers.

## Conclusion

Adopt MultiLanguages if your Android app ships more than one language and you want users to change language inside the app rather than only through system settings. Do not adopt it if your app already manages per-Context configuration overrides, because the framework's model is to bind the locale once at attach time rather than per screen. Before you integrate, note that the dependency comes from JitPack, that the framework has to be initialised in Application.onCreate and that every Context subclass needs its attachBaseContext overridden, and check the documented restart behaviour, because setting a language returns a boolean telling you whether the app must be restarted.

## FAQ

### How do I add the MultiLanguages dependency to an Android project?

Add the JitPack repository first, in build.gradle for Gradle below 7.0 or in settings.gradle for 7.0 and above, then add the dependency in the app module with implementation 'com.github.getActivity:MultiLanguages:10.5'. The framework is then initialised in Application.onCreate with MultiLanguages.init(this).

### Do I need to override attachBaseContext in every Activity?

Every Context subclass needs its attachBaseContext overridden to call super.attachBaseContext(MultiLanguages.attach(newBase)), and the README says the base Activity must do it, with a Service being the same case. The one exception called out is Fragment, because it is not a Context subclass.

### What happens when the user picks a different language in the app?

MultiLanguages.setAppLanguage returns true when the app needs to be restarted, and clearAppLanguage does the same for following the system again. The documented pattern is to restart the Activity task with FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_CLEAR_TASK and a transition animation.

### How do I tell whether two Locale objects are the same language?

Use MultiLanguages.equalsLanguage to compare the language and equalsLanguageAndCountry to compare both language and place, for example simplified Chinese against traditional Chinese. The README warns against Locale's own equals method, which compares more information and can give the wrong answer.

### How do I make the app follow the device language again?

Call MultiLanguages.clearAppLanguage, which returns true if the app needs to be restarted to pick up the system language. MultiLanguages.isSystemLanguage tells you whether the app is currently following the system, and the OnLanguageListener's onSystemLocaleChange callback reports a system locale change along with that flag.

## Sources

- [getActivity/MultiLanguages on GitHub](https://github.com/getActivity/MultiLanguages)
- [Issues](https://github.com/getActivity/MultiLanguages/issues)
- [License: Apache-2.0](https://github.com/getActivity/MultiLanguages/blob/master/LICENSE)
- [README](https://github.com/getActivity/MultiLanguages/blob/master/README.md)
- [Releases](https://github.com/getActivity/MultiLanguages/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/getactivity-multilanguages
