Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7b867499 authored by Neil Fuller's avatar Neil Fuller
Browse files

Capitalize TZ display name strings

Capitalize Settings UI time zone display name strings for languanges
like Polish for standalone locations like summaries and lists. The
motivating example case is the string for "Coordinated Universal Time"
in Polish, which is not capitalized in CLDR, as they capitalize for the
middle of sentences by default. In English, Coordinated Universal Time
is already capitalized.

With this commit all "display name"-like strings have been capitalized
(region names, exemplar locations, time zone names like
"Coordinated Universal Time" and "British Summer Time") for
completeness. For the Polish case, many are already capitalized, but
capitalizing the first letter is therefore a no-op. The
"GMT+xx:xx"-style strings have not been changed.

Also see the associated change in packages/apps/Settings.

Bug: 190109975
Test: Visual inspection in English and Polish of UTC, United States,
Russia in the time zone picker and the date & time screen on mobile
Change-Id: Ib20248769bf8c8cc065d9b210fce4cfc14c91261

Change-Id: I93af68b82f30872c5ddb5c2618900b433d86314a
parent 9b420e66
Loading
Loading
Loading
Loading
+36 −12
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.settingslib.datetime;

import android.annotation.Nullable;
import android.content.Context;
import android.content.res.XmlResourceParser;
import android.icu.text.TimeZoneFormat;
@@ -35,6 +36,7 @@ import androidx.core.text.TextDirectionHeuristicsCompat;
import com.android.i18n.timezone.CountryTimeZones;
import com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping;
import com.android.i18n.timezone.TimeZoneFinder;
import com.android.internal.app.LocaleHelper;
import com.android.settingslib.R;

import org.xmlpull.v1.XmlPullParserException;
@@ -99,7 +101,8 @@ public class ZoneGetter {
        TimeZoneFormat tzFormatter = TimeZoneFormat.getInstance(locale);
        CharSequence gmtText = getGmtOffsetText(tzFormatter, locale, tz, now);
        TimeZoneNames timeZoneNames = TimeZoneNames.getInstance(locale);
        String zoneNameString = getZoneLongName(timeZoneNames, tz, now);
        String zoneNameString = capitalizeForStandaloneDisplay(
                locale, getZoneLongName(locale, timeZoneNames, tz, now));
        if (zoneNameString == null) {
            return gmtText;
        }
@@ -108,6 +111,24 @@ public class ZoneGetter {
        return TextUtils.concat(gmtText, " ", zoneNameString);
    }

    /**
     * Capitalizes {@code toCapitalize} for standalone display, i.e. in lists. This is intended for
     * use with "display name" strings from sources like ICU/CLDR which typically capitalize strings
     * for the inclusion in the middle of sentences. Some locales (such as Polish) do not capitalize
     * terms like "Coordinated Universal Time" as in English but do capitalize the first letter for
     * standalone locations like lists, and so must be explicitly capitalized.
     *
     * @return the capitalized string, or {@code null} if the argument is null
     */
    @Nullable
    public static String capitalizeForStandaloneDisplay(
            Locale locale, @Nullable String toCapitalize) {
        if (TextUtils.isEmpty(toCapitalize)) {
            return toCapitalize;
        }
        return LocaleHelper.toSentenceCase(toCapitalize, locale);
    }

    public static List<Map<String, Object>> getZonesList(Context context) {
        final Locale locale = context.getResources().getConfiguration().locale;
        final Date now = new Date();
@@ -116,7 +137,7 @@ public class ZoneGetter {

        // Work out whether the display names we would show by default would be ambiguous.
        final boolean useExemplarLocationForLocalNames =
                shouldUseExemplarLocationForLocalNames(data, timeZoneNames);
                shouldUseExemplarLocationForLocalNames(locale, data, timeZoneNames);

        // Generate the list of zone entries to return.
        List<Map<String, Object>> zones = new ArrayList<Map<String, Object>>();
@@ -124,7 +145,7 @@ public class ZoneGetter {
            TimeZone tz = data.timeZones[i];
            CharSequence gmtOffsetText = data.gmtOffsetTexts[i];

            CharSequence displayName = getTimeZoneDisplayName(data, timeZoneNames,
            CharSequence displayName = getTimeZoneDisplayName(locale, data, timeZoneNames,
                    useExemplarLocationForLocalNames, tz, data.olsonIdsToDisplay[i]);
            if (TextUtils.isEmpty(displayName)) {
                displayName = gmtOffsetText;
@@ -181,15 +202,15 @@ public class ZoneGetter {
        return olsonIds;
    }

    private static boolean shouldUseExemplarLocationForLocalNames(ZoneGetterData data,
            TimeZoneNames timeZoneNames) {
    private static boolean shouldUseExemplarLocationForLocalNames(Locale locale,
            ZoneGetterData data, TimeZoneNames timeZoneNames) {
        final Set<CharSequence> localZoneNames = new HashSet<>();
        final Date now = new Date();
        for (int i = 0; i < data.zoneCount; i++) {
            final String olsonId = data.olsonIdsToDisplay[i];
            if (data.localZoneIds.contains(olsonId)) {
                final TimeZone tz = data.timeZones[i];
                CharSequence displayName = getZoneLongName(timeZoneNames, tz, now);
                CharSequence displayName = getZoneLongName(locale, timeZoneNames, tz, now);
                if (displayName == null) {
                    displayName = data.gmtOffsetTexts[i];
                }
@@ -203,7 +224,7 @@ public class ZoneGetter {
        return false;
    }

    private static CharSequence getTimeZoneDisplayName(ZoneGetterData data,
    private static CharSequence getTimeZoneDisplayName(Locale locale, ZoneGetterData data,
            TimeZoneNames timeZoneNames, boolean useExemplarLocationForLocalNames, TimeZone tz,
            String olsonId) {
        final Date now = new Date();
@@ -212,7 +233,7 @@ public class ZoneGetter {
        String displayName;

        if (preferLongName) {
            displayName = getZoneLongName(timeZoneNames, tz, now);
            displayName = getZoneLongName(locale, timeZoneNames, tz, now);
        } else {
            // Canonicalize the zone ID for ICU. It will only return valid strings for zone IDs
            // that match ICUs zone IDs (which are similar but not guaranteed the same as those
@@ -223,10 +244,11 @@ public class ZoneGetter {
            if (canonicalZoneId == null) {
                canonicalZoneId = tz.getID();
            }
            displayName = timeZoneNames.getExemplarLocationName(canonicalZoneId);
            displayName = capitalizeForStandaloneDisplay(
                    locale, timeZoneNames.getExemplarLocationName(canonicalZoneId));
            if (displayName == null || displayName.isEmpty()) {
                // getZoneExemplarLocation can return null. Fall back to the long name.
                displayName = getZoneLongName(timeZoneNames, tz, now);
                displayName = getZoneLongName(locale, timeZoneNames, tz, now);
            }
        }

@@ -237,11 +259,13 @@ public class ZoneGetter {
     * Returns the long name for the timezone for the given locale at the time specified.
     * Can return {@code null}.
     */
    private static String getZoneLongName(TimeZoneNames names, TimeZone tz, Date now) {
    private static String getZoneLongName(
            Locale locale, TimeZoneNames names, TimeZone tz, Date now) {
        final TimeZoneNames.NameType nameType =
                tz.inDaylightTime(now) ? TimeZoneNames.NameType.LONG_DAYLIGHT
                        : TimeZoneNames.NameType.LONG_STANDARD;
        return names.getDisplayName(getCanonicalZoneId(tz), nameType, now.getTime());
        return capitalizeForStandaloneDisplay(locale,
                names.getDisplayName(getCanonicalZoneId(tz), nameType, now.getTime()));
    }

    private static String getCanonicalZoneId(TimeZone timeZone) {