-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
[Android 13+] Support per-app language preferences #12093
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Android 13+] Support per-app language preferences #12093
Conversation
I'll paste my comment from #10994 here:
Is it possible to check whether the per-app language is set? If it is, we might want to disable the in-app setting and display a hint that the per-app language preference is used instead. |
thanks @TobiGr! I looked briefly for an existing in-app language picker but didn't check in the "content" settings. Yeah, I'll need to make some more changes so they don't conflict. I should be able to keep the in-app language picker and migrate it to use the new APIs. |
If it's just a matter of hiding the settings screen language toggle and showing a message instead, in case the language has been changed from outside, then it's ok on the |
<Preference | ||
android:key="@string/app_language_android_13_and_up_key" | ||
android:title="@string/app_language_title" | ||
app:isPreferenceVisible="false" | ||
app:singleLineTitle="false" | ||
app:iconSpaceReserved="false" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a separate Preference
for the Android 13+ behavior only because for some reason when I set an OnPreferenceClickListener
on the existing preference that returns true
(indicating "the click was handled"), it still opens the existing dialog.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you. There is still a toast displayed that the language change is going to take effect with the next start of the app when changing the language on SDK 33+ which is obviously false.
|
||
if (Build.VERSION.SDK_INT >= 33) { | ||
ensureAppLanguagePreferenceIsMigrated(prefs); | ||
} | ||
} | ||
|
||
private void ensureAppLanguagePreferenceIsMigrated(final SharedPreferences prefs) { | ||
final String appLanguageDefaultValue = getString(R.string.default_localization_key); | ||
final String appLanguageKey = getString(R.string.app_language_key); | ||
final String appLanguageCurrentValue = prefs.getString(appLanguageKey, null); | ||
if (appLanguageCurrentValue != null) { | ||
// Migrate to Android per-app language settings | ||
prefs.edit().remove(appLanguageKey).apply(); | ||
if (!appLanguageCurrentValue.equals(appLanguageDefaultValue)) { | ||
try { | ||
AppCompatDelegate.setApplicationLocales( | ||
LocaleListCompat.forLanguageTags(appLanguageCurrentValue) | ||
); | ||
} catch (final RuntimeException e) { | ||
Log.e(TAG, "Error migrating to Android 13+ per-app language settings"); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That looks like it should be moved and converted to a Migration
in org.schabi.newpipe.settings.SettingMigrations
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My only concern is that if we convert this to a Migration
, my understanding is that it will only run once. So in theory, the following scenario is possible:
- Someone running NewPipe on Android <13 sets a custom app language
- They update to the new version of NewPipe, where this migration is run but doesn't do anything because they're on Android <13
- They update to Android 13+
- Their custom app language pref isn't used (because it's running the Android 13+ code path) and it's not migrated from their old preference (because there's nothing triggering the migration to run again)
I recognize this will probably be a pretty rare scenario, just wanted to flag it. But whether or not we make it a Migration
, I do recognize that this code belongs somewhere outside of the Application
subclass for sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've moved it to a Migration
. If we want to prevent the hypothetical app language setting loss I described above, maybe we could add a new migrations file for "migrations that should always be run on app startup because they depend on the current Android version".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmmh you are right, this shouldn't be a migration as it needs to run every time the app notices it has been updated to Android 13+ from a lower Android version. What do you think @TobiGr?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you are right. My bad. I'd move it to initSettings
in NewPipeSettings
then. Does that make sense @Stypox ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes
My bad, totally forgot to fix the toast! So prior to this PR, we've been showing this toast whenever the app language, content language, or content country is changed. But are we sure that changes to content language/country have ever required a restart? When building from the latest commit on content.language.changing.without.restart.mp4If changes to the content language and country are reflected without having to restart, then the only situation where we need to show the toast is when app language is changed on a device running Android <13. Let me know if that sounds correct! |
The most recent commit I pushed assumes this is the case, but we can revert it if not. Video showing new behavior (no toast when changing only content language): dont.show.toast.when.changing.content.language.or.country.mp4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looks good to me, thank you! I have a comment here that could possibly be solved in a separate PR if you prefer. Also, see my comment about the migration.
Also, could you take a video of the updated code on pre-Android-13, so we can see if it still works as expected there?
androidResources { | ||
generateLocaleConfig = true | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we don't pay attention to new languages being added from Weblate, this might lead to half-translated being proposed to the user. Currently the list of languages is manually kept in settings_keys.xml
, so when new languages are added through Weblate they don't automatically appear in the app menu. However, I'm fine with switching to the new behavior, as it would avoid work to keep the language list up to date. Is it possible to access the locale config from within the app, so we can delete the list of languages in settings_keys.xml
and use the autogenerated locale config even for the pre-Android-13 language picker?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found LocaleManager#getOverrideLocaleConfig but it sounds like that method is only to get a LocaleConfig
that's already been set with setOverrideLocaleConfig()
. As far as I can tell, we would have to manually generate a LocaleConfig
instead of using the auto-generated one if we wanted to use it for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could find the data under app/build/generated/res/localeConfig/debug/xml/_generated_res_locale_config.xml
after running the build, with this data inside:
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="en-US"/>
<locale android:name="ace"/>
...
So in theory the data is there, but in practice I think we better not access it by manually opening the xml since it's not officially supported. So yeah let's keep the previous behavior.
@Stypox I tried it on an emulator running Android 12 and it seems fine: changing.language.on.android.12.mp4 |
Thank you! I tested to import an export from a previous version of NewPipe into this PR's build on Android 13 and unfortunately it did not work: the app language remained the one that was there previously before importing settings. This is the database I imported (it should set the language to italian): NewPipeData-20250324_172806.zip |
thanks for catching that and for sharing the settings export @Stypox! It turns out
I've moved the migration method to fixed.migration.mp4 |
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested again and it all seems to work as expected. Thank you for making the requested changes!
Log.e(TAG, "Failed to migrate previous custom app language " | ||
+ "setting to public per-app language APIs" | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't the error be logged?
final Intent intent = new Intent(Settings.ACTION_APP_LOCALE_SETTINGS) | ||
.setData(Uri.fromParts("package", requireContext().getPackageName(), null)); | ||
startActivity(intent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a very edge case, but if the user has disabled settings or if some ROMs are broken (hello weird Android TV devices), this would crash the app due to no app being able to handle the intent.
This PR introduced a regression: locals which are not available in Android are not supported by the per-app language preference. E.g. #12476 adds Latin / Latina as language. It's a dead language and thus not available in Android and cannot be selected in the per-app language preferences. However, it can be selected by users with Android 12 and earlier. This might be related to the fact that not all locals have an ISO 639-1 code. Possible workaround: Set |
So I did a quick comparison of the languages available in the per-app languages vs those available in the in-app chooser. The below lists are not comprehensive of non-latin scripts (there are only a few such languages) because I can't neither write nor compare non-latin scripts 😅. In any case, I think it's pretty clear Android is filtering out a whole bunch of languages. I would say we revert this PR. Our in-app picker additionally has
I did try setting locales_config.xml<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="en-US"/>
<locale android:name="it-IT"/>
<locale android:name="la-Latn"/>
</locale-config> I took a look at the current automatically generated Android per-app picker additionally has
|
I think some parts of the list are incorrect. Here is the full list of available languages on Android 16. There is e.g. korean, bulgarian, indonesian and nepalese (random sampling) inside Android's list and I could also locate these languages on my phone (Android 15; CalyxOs on FP5) for NewPipe (nightly) and apply them successfully. Maybe they have slightly different names? However I couldn't find latin - most likely because it's dead for ~1200 years and not supported (at this point this stuff is just like technical debt). Anyway I don't think that we should revert this because of 1 dead language, that is used by maybe 2-5? users. |
Seems like you randomly sampled exactly those that are indeed in a different place. But these are definitely unavailable (I searched for them): Esperanto, Acehnese, Occitan, Toki Pona.
Yeah but per-app languages are already a more complex system than the simple setting we used to have, and if it's not even on par with previous behavior for the stupid reason it lacks lesser-known languages then it's not so worth it... Why does Android just not include all available languages?!? If they don't have a name for "Latin" they could just show the locale name, "la", and call it a day, instead of hiding the locale completely... |
Oh wait:
|
Yes, definitely more than one reason to revert this PR. |
I was sarcastic about the latin language being present in Java and not our code ;) So I checked again on my phone because the emulator is unreliable: Toki Pona
Acehnese
I also crunched some numbers:
So I think we should draw a line here and simply ignore, block and remove these languages:
PS: All this translation stuff wouldn't be required if the Tower of Babel was still standing :P |
Unfortunately, no. If setting the locales manually doesn't work either, then it would indeed be best to undo the change and use the in-app picker. |
Thank you for your analysis! Strangely enough neither Esperanto nor Occitan exist on the emulator I was using and on my FP3 with /e/OS. Maybe it's an OS issue? Supporting languages with only 1% of translated strings is indeed not very useful, and all other languages seem to be present, so we should be fine. I will close #12478 but still add a warning in the release notes so users can report any issue they have. |
I just tested on an emulator with Google Play Services and Esperanto and Occitan are available there. Neither of them are available on my phone (with MicroG) nor on emulators without Google Play Services. So my guess is that Google Play Services somehow adds support for more languages (?). |
Acehnese, Latin and Toki Pona are not supported by Android: https://developer.android.com/guide/topics/resources/app-languages#locale-names They are also completely outdated and have a low amount of translated keys See TeamNewPipe#12093 (comment) for further details
Be careful with locale support on Android,. As I already said in another issue, some manifacturers or ROMs can expand or reduce locale compatibility. On the other hand, most OEM ROMs are only compatible with the locales they are translated to, but using apps like "MoreLocale2" can show magically those hidden locales in different ways. EDITED 2025-08-14:
Talking from the linguistic scope, some of you don't know the difference between a language, a variant and a dialect. Spoiler, they are all the same.
From my point of view, I would get mad if I see my contributions getting removed because NewPipe is unable to find people who can continue my work. Someone spends time for free translating NewPipe for getting their tranlsations removed? No, thanks!
The right question is: What is the NewPipe we want? A NewPipe for the 4 most spoken worldwide languages or an accessible NewPipe for all languages, big, endangered or minorized? My choice is clear, I bet for an inclusive and truly open NewPipe which gives visibility to the real linguistic diversity. You cannot force a user to speak or select the languages you want, that would break the spirit of any free (as freedom) license. The user needs free of choice and right to chose, even more with endangered languages.
"Why would you speak your ancient and ugly language instead of speaking a GrEaT, uNiVeRsAl aNd MoDeRn language like (insert language)??"
Let me tell you NewPipe suffers from worse issues other than localization...
Why not to make an hybrid system? NewPipe could honor system locale while having a proper language selector for all "unsupported" Android locales. |
Acehnese, Latin and Toki Pona are not supported by Android: https://developer.android.com/guide/topics/resources/app-languages#locale-names They are also completely outdated and have a low amount of translated keys See TeamNewPipe#12093 (comment) for further details
What is it?
Description of the changes in your PR
Enables automatic per-app language support.
On Android 13+, the preference in Settings -> Content shows any locale that has been set by the user through Android's per-app language settings. When tapped, it opens NewPipe's per-app language settings screen.
Also added a method to migrate any existing app language preference from
SharedPreferences
to the Android per-app language settings when we run the app on Android 13+.Before/After Screenshots/Screen Record
per-app.language.settings.integrated.with.existing.ui.without.toast.mp4
APK testing
The APK can be found by going to the "Checks" tab below the title. On the left pane, click on "CI", scroll down to "artifacts" and click "app" to download the zip file which contains the debug APK of this PR. You can find more info and a video demonstration on this wiki page.
Due diligence