Home » Android » android – Retrofit 2: Default Accept-Language

android – Retrofit 2: Default Accept-Language

Posted by: admin June 15, 2020 Leave a comment

Questions:

Is there any way to automatically add Accept-Language header based on OS settings?

For example: I have English (US) as my system lang, it would be great to have Accept-Language: en-us added in some simple way…

Also Android N allows to select multiple locales in settings, so it would be great to use this like: Accept-Language: da, en-gb;q=0.8, en;q=0.7

Thanks.

How to&Answers:

In case someone is looking for a solution how to provide a list of preferred languages as accept-language, in Android here is how to do that.
Note: Setting a list of preferred languages is only available since API Level 24

  public class AcceptLanguageHeaderInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
      Request originalRequest = chain.request();
      Request requestWithHeaders = originalRequest.newBuilder()
        .header("Accept-Language", getLanguage())
        .build();
      return chain.proceed(requestWithHeaders);
    }

    private String getLanguage() {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        return LocaleList.getDefault().toLanguageTags();
      } else {
        return Locale.getDefault().getLanguage();
      }
    }
  }

Answer:

For anyone who’s wondering I ended up with this Interceptor:

package common.network;

import java.io.IOException;
import java.util.Locale;

import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

/**
 * Created by oleynikd on 6/9/16.
 */
public class AcceptLanguageHeaderInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request originalRequest = chain.request();
        Request requestWithHeaders = originalRequest.newBuilder()
                .header("Accept-Language", localeToBcp47Language(Locale.getDefault()))
                .build();
        return chain.proceed(requestWithHeaders);
    }

    /*
     * From https://github.com/apache/cordova-plugin-globalization/blob/master/src/android/Globalization.java#L140
     * @Description: Returns a well-formed ITEF BCP 47 language tag representing
     * the locale identifier for the client's current locale
     *
     * @Return: String: The BCP 47 language tag for the current locale
     */
    private static String localeToBcp47Language(Locale loc) {
        final char SEP = '-';       // we will use a dash as per BCP 47
        String language = loc.getLanguage();
        String region = loc.getCountry();
        String variant = loc.getVariant();

        // special case for Norwegian Nynorsk since "NY" cannot be a variant as per BCP 47
        // this goes before the string matching since "NY" wont pass the variant checks
        if( language.equals("no") && region.equals("NO") && variant.equals("NY")){
            language = "nn";
            region = "NO";
            variant = "";
        }

        if( language.isEmpty() || !language.matches("\p{Alpha}{2,8}")){
            language = "und";       // Follow the Locale#toLanguageTag() implementation
            // which says to return "und" for Undetermined
        }else if(language.equals("iw")){
            language = "he";        // correct deprecated "Hebrew"
        }else if(language.equals("in")){
            language = "id";        // correct deprecated "Indonesian"
        }else if(language.equals("ji")){
            language = "yi";        // correct deprecated "Yiddish"
        }

        // ensure valid country code, if not well formed, it's omitted
        if (!region.matches("\p{Alpha}{2}|\p{Digit}{3}")) {
            region = "";
        }

        // variant subtags that begin with a letter must be at least 5 characters long
        if (!variant.matches("\p{Alnum}{5,8}|\p{Digit}\p{Alnum}{3}")) {
            variant = "";
        }

        StringBuilder bcp47Tag = new StringBuilder(language);
        if (!region.isEmpty()) {
            bcp47Tag.append(SEP).append(region);
        }
        if (!variant.isEmpty()) {
            bcp47Tag.append(SEP).append(variant);
        }

        return bcp47Tag.toString();
    }
}

Then you can use it like:

private static final OkHttpClient client = new OkHttpClient.Builder()
            .addInterceptor(new AcceptLanguageHeaderInterceptor())
            .build();

Answer:

If you are using API 21 you can use Locale.toLanguageTag().

toLanguageTag
Added in API level 21
String toLanguageTag ()
Returns a well-formed IETF BCP 47 language tag representing this locale.

Sample code:

private class AcceptLanguageInterceptor(val locale: Locale) : Interceptor {

        @Throws(IOException::class)
        override fun intercept(chain: Interceptor.Chain): okhttp3.Response = chain.proceed(chain.request().newBuilder()
                .header("Accept-Language", locale.toLanguageTag())
                .build())
}

Answer:

When you create your Retrofit object you can customise the headers sent by the OkHttp network library.
Consider the example below:

Retrofit ret = new Retrofit.Builder()
                .client(new OkHttpClient.Builder()
                .addNetworkInterceptor(new Interceptor() {

                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request.Builder builder = chain.request().newBuilder();

                        builder.addHeader("Accept-Language", "Your value");
                        Request request = builder.build();
                        Response response = chain.proceed(request);

                        return response;
                    }
                }).build())
                .build();

In the “Your value” string you can tweak the value of your header and put for example: builder.addHeader("Accept-Language", Locale.getDefault().getDisplayLanguage())

Answer:

Android API 24 introduced multi locales and LocaleList API. Here is my code snippet which takes it into consideration and creates a string for Accept-Langauge header,

fun getAcceptedLanguageHeaderValue(): String {
     return LocaleListCompat.getAdjustedDefault().toLanguageTags()
                .map { it.toLanguageTag() }
                .reduce { accumulator, languageTag ->
                    weight -= 0.001F
                    "$accumulator,$languageTag;q=$weight"
                }
}

Ouput example: “en-US, ta-IN;q=0.999, it-IT;q=0.998”

NOTE: getAdjustedDefault() adds the default locale as the first item in the list followed by user-preferred locales ordered by their priority.

An interceptor would be a really good way to add this Accept-Language header to all the network requests.

For more information about multi-locale – https://developer.android.com/guide/topics/resources/multilingual-support

Answer:

if you want to see selected language of your device

Use this code:

Locale.getDefault().getDisplayLanguage();

Hope this will help ! cheers!