Android Settings ——language / locale 修改语言

本文探讨了Android系统中修改语言的过程,涉及关键组件如AndroidManifest.xml、InputMethodAndLanguageSettings.java、LocalePicker及配置文件。从UI到系统底层的ActivityManagerService,详细解析了更新配置和locale文件的路径,包括persit.sys.locale和locale_config.xml的使用。

看过就忘!为了照顾自己的痴呆脑,劳累双手记录一下。


packages/apps/Settings/AndroidManifest.xml

        <activity android:name="Settings$InputMethodAndLanguageSettingsActivity"
            android:label="@string/language_keyboard_settings_title"
            android:icon="@drawable/ic_settings_language"
            android:taskAffinity="com.android.settings"
            android:parentActivityName="Settings">

            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                android:value="com.android.settings.inputmethod.InputMethodAndLanguageSettings" />

        </activity>

 

[UI 主view]

packages/apps/Settings/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java

public void onCreate(Bundle icicle) {
        addPreferencesFromResource(R.xml.language_settings);
}

packages/apps/Settings/res/xml/language_settings.xml/language_settings

   <PreferenceScreen
            android:key="phone_language"
            android:title="@string/phone_language"
            android:fragment="com.android.settings.localepicker.LocaleListEditor"
            />

packages/apps/Settings/src/com/android/settings/localepicker/LocaleListEditor.java

    @Override
    public void onLocaleSelected(LocaleStore.LocaleInfo locale) {
        mAdapter.addLocale(locale);
        updateVisibilityOfRemoveMenu();
    }

packages/apps/Settings/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java

    void addLocale(LocaleStore.LocaleInfo li) {
        mFeedItemList.add(li);
        notifyItemInserted(mFeedItemList.size() - 1);
        doTheUpdate();
    }
    public void updateLocalesWhenAnimationStops(final LocaleList localeList) {
    
        final RecyclerView.ItemAnimator itemAnimator = mParentView.getItemAnimator();
        itemAnimator.isRunning(new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
            @Override
            public void onAnimationsFinished() {

                LocalePicker.updateLocales(mLocalesToSetNext);

            }
        });
    }

 

frameworks/base/core/java/com/android/internalapp/LocalePicker.java

    public static void updateLocales(LocaleList locales) {
        try {
            final IActivityManager am = ActivityManagerNative.getDefault();
            final Configuration config = am.getConfiguration();

            config.setLocales(locales);
            config.userSetLocale = true;

            am.updatePersistentConfiguration(config);
            // Trigger the dirty bit for the Settings Provider.
            BackupManager.dataChanged("com.android.providers.settings");
        } catch (RemoteException e) {
            // Intentionally left blank
        }
    }

 

frameworks/base/core/java/android/app/ActivityManagerNative .java

    static public IActivityManager getDefault() {
        return gDefault.get();
    }

ActivityManagerNative 的 getDefault() ,返回的是远程服务对象 ActivityManagerServices.java 在本地的一个代理, 最终调用的是ActivityManagerServices.java 中的 updateCofiguration() 函数。

 

frameworks/base/core/java/android/app/IActivityManager.java

public void updatePersistentConfiguration(Configuration values) throws RemoteException;

 

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
 

  private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
            boolean initLocale, boolean persistent, int userId, boolean deferResume) {
  
        if (values != null) {
            Configuration newConfig = new Configuration(mConfiguration);
            changes = newConfig.updateFrom(values);
            if (changes != 0) {
            
                if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
                    final LocaleList locales = values.getLocales();
                    int bestLocaleIndex = 0;
                    if (locales.size() > 1) {
                        if (mSupportedSystemLocales == null) {
                            mSupportedSystemLocales =
                                    Resources.getSystem().getAssets().getLocales();
                        }
                        bestLocaleIndex = Math.max(0,
                                locales.getFirstMatchIndex(mSupportedSystemLocales));
                    }
                    SystemProperties.set("persist.sys.locale",
                            locales.get(bestLocaleIndex).toLanguageTag());
                    LocaleList.setDefault(locales, bestLocaleIndex);
                    mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
                            locales.get(bestLocaleIndex)));
                }

              
        }
        return kept;
    }

 

frameworks/base/core/java/android/content/res/Configuration.java

public @Config int updateFrom(@NonNull Configuration delta) {
        int changed = 0;

        fixUpLocaleList();
        delta.fixUpLocaleList();
        if (!delta.mLocaleList.isEmpty() && !mLocaleList.equals(delta.mLocaleList)) {
            changed |= ActivityInfo.CONFIG_LOCALE;
            mLocaleList = delta.mLocaleList;
            // delta.locale can't be null, since delta.mLocaleList is not empty.
            if (!delta.locale.equals(locale)) {
                locale = (Locale) delta.locale.clone();
                // If locale has changed, then layout direction is also changed ...
                changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
                // ... and we need to update the layout direction (represented by the first
                // 2 most significant bits in screenLayout).
                setLayoutDirection(locale);
            }
        }

        if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0)))
        {
            changed |= ActivityInfo.CONFIG_LOCALE;
            userSetLocale = true;
        }

        return changed;
    }

 

target locale file : "persist.sys.locale"       (data/property/persist.sys.locale)

 

2) 所有语言 文件

 

packages/apps/Settings/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java

  import android.os.LocaleList (全部 语言)

  import com.android.internal.app.LocaleHelper

  import com.android.internal.app.LocalePicker (已选 语言)

  import java.util.Locale


    private static String getLocaleNames(Context context) {
        final LocaleList locales = LocalePicker.getLocales();
        final Locale displayLocale = Locale.getDefault();
        return LocaleHelper.toSentenceCase(
                LocaleHelper.getDisplayLocaleList(
                        locales, displayLocale, 2 /* Show up to two locales from the list */),
                displayLocale);
    }

libcore/ojluni/src/main/java/java/util/Locale.java

external/libgdx/backends/gdx/-backends-gwt/src/com/badlogic/gdx/backends/gwt/emu/jaba/util/Locale.java

frameworks/base/core/java/com/android/internalapp/LocalePicker.java

 

 

packages/apps/Settings/src/com/android/settings/localepicker/LocaleListEditor.java

    private void configureDragAndDrop(View view) {

        mAddLanguage = view.findViewById(R.id.add_language);
        mAddLanguage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final LocalePickerWithRegion selector = LocalePickerWithRegion.createLanguagePicker(
                        getContext(), LocaleListEditor.this, false /* translate only */);

            }
        });
    }

 

frameworks/base/core/java/com/android/internalapp/LocalePickerWithRegion.java
 

    public static LocalePickerWithRegion createLanguagePicker(Context context,
            LocaleSelectedListener listener, boolean translatedOnly) {
        LocalePickerWithRegion localePicker = new LocalePickerWithRegion();
        localePicker.setListener(context, listener, /* parent */ null, translatedOnly);
        return localePicker;
    }

 

 private boolean setListener(Context context, LocaleSelectedListener listener,
            LocaleStore.LocaleInfo parent, boolean translatedOnly) {
      

        final HashSet<String> langTagsToIgnore = new HashSet<>();
        if (!translatedOnly) {
            final LocaleList userLocales = LocalePicker.getLocales();
            final String[] langTags = userLocales.toLanguageTags().split(",");
            Collections.addAll(langTagsToIgnore, langTags);
        }

        if (parent != null) {

        } else {
            mLocaleList = LocaleStore.getLevelLocales(context, langTagsToIgnore,
                    null /* no parent */, translatedOnly);
        }

        return true;
    }

 

frameworks/base/core/java/com/android/internalapp/LocaleStore.java
 

 public static Set<LocaleInfo> getLevelLocales(Context context, Set<String> ignorables,
            LocaleInfo parent, boolean translatedOnly) {
        fillCache(context);

        for (LocaleStore.LocaleInfo li : sLocaleCache.values()) {

        }
        return result;
    }
   public static void fillCache(Context context) {
   

        for (String localeId : LocalePicker.getSupportedLocales(context)) {
          
            LocaleInfo li = new LocaleInfo(localeId);
   
            sLocaleCache.put(li.getId(), li);
        }
    }

 

frameworks/base/core/java/com/android/internalapp/LocalePicker.java


    public static String[] getSupportedLocales(Context context) {
        return context.getResources().getStringArray(R.array.supported_locales);
    }

 

全部语言 file : frameworks/base/core/res/res/values/locale_config.xml

 

 

3) LocalePicker.getLocales()

frameworks/base/core/java/com/android/internalapp/LocalePicker.java

    public static LocaleList getLocales() {
        try {
            return ActivityManagerNative.getDefault()
                    .getConfiguration().getLocales();
        } catch (RemoteException e) {
            // If something went wrong
            return LocaleList.getDefault();
        }
    }

frameworks/base/core/java/android/content/res/Configuration.java

    public @NonNull LocaleList getLocales() {
        fixUpLocaleList();
        return mLocaleList;
    }
    private void fixUpLocaleList() {
        if ((locale == null && !mLocaleList.isEmpty()) ||
                (locale != null && !locale.equals(mLocaleList.get(0)))) {
            mLocaleList = locale == null ? LocaleList.getEmptyLocaleList() : new LocaleList(locale);
        }
    }

frameworks/base/core/java/android/os/LocaleList.java

private static final LocaleList sEmptyLocaleList = new LocaleList();




    public LocaleList(@NonNull Locale... list) {
        if (list.length == 0) {
            mList = sEmptyList;
            mStringRepresentation = "";
        } else {
            final Locale[] localeList = new Locale[list.length];
            final HashSet<Locale> seenLocales = new HashSet<Locale>();
            final StringBuilder sb = new StringBuilder();
            for (int i = 0; i < list.length; i++) {
                final Locale l = list[i];
                if (l == null) {
                    throw new NullPointerException("list[" + i + "] is null");
                } else if (seenLocales.contains(l)) {
                    throw new IllegalArgumentException("list[" + i + "] is a repetition");
                } else {
                    final Locale localeClone = (Locale) l.clone();
                    localeList[i] = localeClone;
                    sb.append(localeClone.toLanguageTag());
                    if (i < list.length - 1) {
                        sb.append(',');
                    }
                    seenLocales.add(localeClone);
                }
            }
            mList = localeList;
            mStringRepresentation = sb.toString();
        }
    }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

<think> 嗯,用户这次想了解Linux路径/usr/lib/locale/locale-archive的具体含义。结合之前的对话历史,用户似乎正在处理locale相关的系统配置问题,可能遇到了字符集缺失或编译错误。 从用户提供的四条引用来看,这个文件显然是locale系统的核心数据存储文件。引用[2]提到它有两种存储形式:归档文件或目录结构,而locale-archive正是归档格式的体现。引用[1]和[3]都涉及操作这个文件时的标准流程——需要先重命名再重建,说明这是个关键系统文件不能直接修改。 用户可能不只是想要基础定义,更关注实际操作场景。比如引用[3]展示的删除非中英文locale的技巧,引用[4]提到的交叉编译时避免覆盖系统的安装参数,这些都应该纳入解释范围。考虑到用户之前接触过编译问题,需要强调该文件与locale编译命令(localedef/locale-gen)的关联。 特别要注意引用[2]中man locale-gen的提示,这说明文件生成机制有官方文档支持。在解释时会重点说明:1)glibc优化的二进制格式特性 2)与localedef分目录存储的对比优势 3)重建操作的实际风险缓解方案(如备份必要性)。 用户没有明确场景,但四条引用都指向系统维护方向,所以补充"何时需要操作此文件"的注意事项会更实用。比如升级glibc后重建locale这类高频需求,正好对应引用[1][3]的命令示例。 </think> `/usr/lib/locale/locale-archive` 是 Linux 系统中存储本地化(locale)数据的核心文件,其作用与特性如下: --- ### **核心功能** 1. **集中式存储** 包含系统所有已安装的本地化数据(如字符编码、日期格式、货币符号等),替代传统的分散目录存储方式(如 `/usr/lib/locale/zh_CN.UTF-8`)。 2. **优化性能** 采用二进制归档格式(由 `localedef` 生成),加速程序对本地化数据的读取效率。 --- ### **关键特性** - **生成方式** 通过命令生成: ```bash sudo locale-gen # 调用 localedef 生成/更新归档文件 ``` 或手动重建(需先备份): ```bash sudo mv /usr/lib/locale/locale-archive /usr/lib/locale/locale-archive.bak sudo localedef --no-archive -i en_US -f UTF-8 /usr/lib/locale/en_US.UTF-8 ``` - **内容管理** - 查看归档内容: ```bash localedef --list-archive ``` - 删除特定语言(如仅保留中英文): ```bash localedef --list-archive | grep -vE "^(en_US|zh_CN)" | xargs sudo localedef --delete-from-archive ``` (引用[^3]) - **路径依赖性** Glibc 库直接依赖此路径,擅自删除或移动会导致系统语言功能异常。 --- ### **何时需要操作此文件?** 1. **空间优化** 删除无用语言包(如仅保留中英文)释放磁盘空间(引用[^3])。 2. **修复损坏** 当 `locale` 命令报错或程序显示异常时重建归档。 3. **交叉编译** 在构建嵌入式系统时,需指定 `install_root` 防止覆盖宿主机的归档文件(引用[^4]): ```bash make localedata/install-locales install_root=${TARGET_PATH} ``` --- ### **注意事项** - **操作前备份**: ```bash sudo cp /usr/lib/locale/locale-archive{,.bak} ``` - **避免直接编辑**:必须通过 `localedef` 或 `locale-gen` 工具维护。 - **重建后生效**:部分服务需重启或执行: ```bash sudo export LC_ALL="en_US.UTF-8" # 临时生效 ``` > 此文件是 Glibc 实现国际化(i18n)的基础组件,对多语言支持至关重要(引用[^2])。 --- ### 相关问题 1. 如何检查当前系统支持的 locale 列表? 2. 误删 `locale-archive` 后如何恢复系统语言功能? 3. 在 Docker 容器中如何最小化 locale 占用空间? 4. `locale-gen` 和 `localedef` 命令有何区别? [^1]: 归档文件操作需遵循重命名模板的规范流程 [^2]: 本地化数据存储的两种形式(归档 vs 目录) [^3]: 安全删除非关键语言包的方法 [^4]: 交叉编译时防止覆盖系统 locale 的防护措施
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值