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

Commit def4e4d3 authored by Elliott Hughes's avatar Elliott Hughes Committed by Android (Google) Code Review
Browse files

Merge "Defer to ICU's knowledge of language-specific grammatical quantity rules."

parents 56c132c2 1ad636c3
Loading
Loading
Loading
Loading
+0 −111
Original line number Diff line number Diff line
/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.content.res;

import java.util.Locale;

/*
 * Yuck-o.  This is not the right way to implement this.  When the ICU PluralRules
 * object has been integrated to android, we should switch to that.  For now, yuck-o.
 */

abstract class PluralRules {

    static final int QUANTITY_OTHER = 0x0000;
    static final int QUANTITY_ZERO  = 0x0001;
    static final int QUANTITY_ONE   = 0x0002;
    static final int QUANTITY_TWO   = 0x0004;
    static final int QUANTITY_FEW   = 0x0008;
    static final int QUANTITY_MANY  = 0x0010;

    static final int ID_OTHER = 0x01000004;

    abstract int quantityForNumber(int n);

    final int attrForNumber(int n) {
        return PluralRules.attrForQuantity(quantityForNumber(n));
    }

    static final int attrForQuantity(int quantity) {
        // see include/utils/ResourceTypes.h
        switch (quantity) {
            case QUANTITY_ZERO: return 0x01000005;
            case QUANTITY_ONE:  return 0x01000006;
            case QUANTITY_TWO:  return 0x01000007;
            case QUANTITY_FEW:  return 0x01000008;
            case QUANTITY_MANY: return 0x01000009;
            default:            return ID_OTHER;
        }
    }

    static final String stringForQuantity(int quantity) {
        switch (quantity) {
            case QUANTITY_ZERO:
                return "zero";
            case QUANTITY_ONE:
                return "one";
            case QUANTITY_TWO:
                return "two";
            case QUANTITY_FEW:
                return "few";
            case QUANTITY_MANY:
                return "many";
            default:
                return "other";
        }
    }

    static final PluralRules ruleForLocale(Locale locale) {
        String lang = locale.getLanguage();
        if ("cs".equals(lang)) {
            if (cs == null) cs = new cs();
            return cs;
        }
        else {
            if (en == null) en = new en();
            return en;
        }
    }

    private static PluralRules cs;
    private static class cs extends PluralRules {
        int quantityForNumber(int n) {
            if (n == 1) {
                return QUANTITY_ONE;
            }
            else if (n >= 2 && n <= 4) {
                return QUANTITY_FEW;
            }
            else {
                return QUANTITY_OTHER;
            }
        }
    }

    private static PluralRules en;
    private static class en extends PluralRules {
        int quantityForNumber(int n) {
            if (n == 1) {
                return QUANTITY_ONE;
            }
            else {
                return QUANTITY_OTHER;
            }
        }
    }
}
+49 −9
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package android.content.res;


import com.android.internal.util.XmlUtils;

import org.xmlpull.v1.XmlPullParser;
@@ -41,6 +40,8 @@ import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.Locale;

import libcore.icu.NativePluralRules;

/**
 * Class for accessing an application's resources.  This sits on top of the
 * asset manager of the application (accessible through getAssets()) and
@@ -52,6 +53,8 @@ public class Resources {
    private static final boolean DEBUG_CONFIG = false;
    private static final boolean TRACE_FOR_PRELOAD = false;

    private static final int ID_OTHER = 0x01000004;

    // Use the current SDK version code.  If we are a development build,
    // also allow the previous SDK version + 1.
    private static final int sSdkVersion = Build.VERSION.SDK_INT
@@ -86,7 +89,7 @@ public class Resources {
    /*package*/ final AssetManager mAssets;
    private final Configuration mConfiguration = new Configuration();
    /*package*/ final DisplayMetrics mMetrics = new DisplayMetrics();
    PluralRules mPluralRule;
    private NativePluralRules mPluralRule;
    
    private CompatibilityInfo mCompatibilityInfo;
    private Display mDefaultDisplay;
@@ -203,9 +206,17 @@ public class Resources {
    }

    /**
     * Return the character sequence associated with a particular resource ID for a particular
     * numerical quantity.
     *
     * <p>See <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String
     * Resources</a> for more on quantity strings.
     *
     * @param id The desired resource identifier, as generated by the aapt
     *           tool. This integer encodes the package, type, and resource
     *           entry. The value 0 is an invalid identifier.
     * @param quantity The number used to get the correct string for the current language's
     *           plural rules.
     *
     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
     *
@@ -213,29 +224,52 @@ public class Resources {
     *         possibly styled text information.
     */
    public CharSequence getQuantityText(int id, int quantity) throws NotFoundException {
        PluralRules rule = getPluralRule();
        CharSequence res = mAssets.getResourceBagText(id, rule.attrForNumber(quantity));
        NativePluralRules rule = getPluralRule();
        CharSequence res = mAssets.getResourceBagText(id,
                attrForQuantityCode(rule.quantityForInt(quantity)));
        if (res != null) {
            return res;
        }
        res = mAssets.getResourceBagText(id, PluralRules.ID_OTHER);
        res = mAssets.getResourceBagText(id, ID_OTHER);
        if (res != null) {
            return res;
        }
        throw new NotFoundException("Plural resource ID #0x" + Integer.toHexString(id)
                + " quantity=" + quantity
                + " item=" + PluralRules.stringForQuantity(rule.quantityForNumber(quantity)));
                + " item=" + stringForQuantityCode(rule.quantityForInt(quantity)));
    }

    private PluralRules getPluralRule() {
    private NativePluralRules getPluralRule() {
        synchronized (mSync) {
            if (mPluralRule == null) {
                mPluralRule = PluralRules.ruleForLocale(mConfiguration.locale);
                mPluralRule = NativePluralRules.forLocale(mConfiguration.locale);
            }
            return mPluralRule;
        }
    }

    private static int attrForQuantityCode(int quantityCode) {
        switch (quantityCode) {
            case NativePluralRules.ZERO: return 0x01000005;
            case NativePluralRules.ONE:  return 0x01000006;
            case NativePluralRules.TWO:  return 0x01000007;
            case NativePluralRules.FEW:  return 0x01000008;
            case NativePluralRules.MANY: return 0x01000009;
            default:                     return ID_OTHER;
        }
    }

    private static String stringForQuantityCode(int quantityCode) {
        switch (quantityCode) {
            case NativePluralRules.ZERO: return "zero";
            case NativePluralRules.ONE:  return "one";
            case NativePluralRules.TWO:  return "two";
            case NativePluralRules.FEW:  return "few";
            case NativePluralRules.MANY: return "many";
            default:                     return "other";
        }
    }

    /**
     * Return the string value associated with a particular resource ID.  It
     * will be stripped of any styled text information.
@@ -290,6 +324,9 @@ public class Resources {
     * stripped of any styled text information.
     * {@more}
     *
     * <p>See <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String
     * Resources</a> for more on quantity strings.
     *
     * @param id The desired resource identifier, as generated by the aapt
     *           tool. This integer encodes the package, type, and resource
     *           entry. The value 0 is an invalid identifier.
@@ -312,6 +349,9 @@ public class Resources {
     * Return the string value associated with a particular resource ID for a particular
     * numerical quantity.
     *
     * <p>See <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String
     * Resources</a> for more on quantity strings.
     *
     * @param id The desired resource identifier, as generated by the aapt
     *           tool. This integer encodes the package, type, and resource
     *           entry. The value 0 is an invalid identifier.
@@ -1334,7 +1374,7 @@ public class Resources {
        }
        synchronized (mSync) {
            if (mPluralRule != null) {
                mPluralRule = PluralRules.ruleForLocale(config.locale);
                mPluralRule = NativePluralRules.forLocale(config.locale);
            }
        }
    }
+53 −14
Original line number Diff line number Diff line
@@ -12,8 +12,8 @@ your application with strings:</p>
    <dd>XML resource that provides a single string.</dd>
  <dt><a href="#StringArray">String Array</a></dt>
    <dd>XML resource that provides an array of strings.</dd>
  <dt><a href="#Plurals">Plurals</a></dt>
    <dd>XML resource that carries different strings for different pluralizations
  <dt><a href="#Plurals">Quantity Strings (Plurals)</a></dt>
    <dd>XML resource that carries different strings for different quantities
    of the same word or phrase.</dd>
</dl>

@@ -218,13 +218,30 @@ getStringArray}(R.array.planets_array);



<h2 id="Plurals">Plurals</h2>
<h2 id="Plurals">Quantity Strings (Plurals)</h2>

<p>A pair of strings that each provide a different plural form of the same word or phrase,
which you can collectively reference from the application. When you request the plurals
resource using a method such as {@link android.content.res.Resources#getQuantityString(int,int)
getQuantityString()}, you must pass a "count", which will determine the plural form you
require and return that string to you.</p>
<p>Different languages have different rules for grammatical agreement with quantity. In English,
for example, the quantity 1 is a special case. We write "1 book", but for any other quantity we'd
write "<i>n</i> books". This distinction between singular and plural is very common, but other
languages make finer distinctions. The full set supported by Android is <code>zero</code>,
<code>one</code>, <code>two</code>, <code>few</code>, <code>many</code>, and <code>other</code>.

<p>The rules for deciding which case to use for a given language and quantity can be very complex,
so Android provides you with methods such as
{@link android.content.res.Resources#getQuantityString(int,int) getQuantityString()} to select
the appropriate resource for you.

<p>Note that the selection is made based on grammatical necessity. A string for <code>zero</code>
in English will be ignored even if the quantity is 0, because 0 isn't grammatically different
from 2, or any other number except 1 ("zero books", "one book", "two books", et cetera).
Don't be misled either by the fact that, say, <code>two</code> sounds like it could only apply to
the quantity 2: a language may require that 2, 12, 102 (et cetera) are all treated like one
another but differently to other quantities. Rely on your translator to know what distinctions
their language actually insists upon.

<p>It's often possible to avoid quantity strings by using quantity-neutral formulations such as
"Books: 1". This will make your life and your translators' lives easier, if it's a style that's
in keeping with your application.

<p class="note"><strong>Note:</strong> A plurals collection is a simple resource that is
referenced using the value provided in the {@code name} attribute (not the name of the XML
@@ -251,7 +268,7 @@ In Java: <code>R.plurals.<em>plural_name</em></code>
    &lt;<a href="#plurals-element">plurals</a>
        name="<em>plural_name</em>">
        &lt;<a href="#plurals-item-element">item</a>
            quantity=["one" | "other"]
            quantity=["zero" | "one" | "two" | "few" | "many" | "other"]
            &gt;<em>text_string</em>&lt;/item>
    &lt;/plurals>
&lt;/resources>
@@ -285,16 +302,27 @@ Styling</a>, below, for information about to properly style and format your stri
      <p class="caps">attributes:</p>
      <dl class="atn-list">
        <dt><code>quantity</code></dt>
        <dd><em>Keyword</em>. A value indicating the case in which this string should be used. Valid
values:
        <dd><em>Keyword</em>. A value indicating when this string should be used. Valid
values, with non-exhaustive examples in parentheses:
          <table>
            <tr><th>Value</th><th>Description</th></tr>
            <tr>
              <td>{@code one}</td><td>When there is one (a singular string).</td>
              <td>{@code zero}</td><td>When the language requires special treatment of the number 0 (as in Arabic).</td>
            </tr>
            <tr>
              <td>{@code one}</td><td>When the language requires special treatment of numbers like one (as with the number 1 in English and most other languages; in Russian, any number ending in 1 but not ending in 11 is in this class).</td>
            </tr>
            <tr>
              <td>{@code two}</td><td>When the language requires special treatment of numbers like two (as in Welsh).</td>
            </tr>
            <tr>
              <td>{@code few}</td><td>When the language requires special treatment of "small" numbers (as with 2, 3, and 4 in Czech; or numbers ending 2, 3, or 4 but not 12, 13, or 14 in Polish).</td>
            </tr>
            <tr>
              <td>{@code other}</td><td>When the quantity is anything other than one (a plural
string, but also used when the count is zero).</td>
              <td>{@code many}</td><td>When the language requires special treatment of "large" numbers (as with numbers ending 11-99 in Maltese).</td>
            </tr>
            <tr>
              <td>{@code other}</td><td>When the language does not require special treatment of the given quantity.</td>
            </tr>
          </table>
        </dd>
@@ -314,6 +342,17 @@ string, but also used when the count is zero).</td>
        &lt;item quantity="other">%d songs found.&lt;/item>
    &lt;/plurals>
&lt;/resources>
</pre>
    <p>XML file saved at {@code res/values-pl/strings.xml}:</p>
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;resources>
    &lt;plurals name="numberOfSongsAvailable">
        &lt;item quantity="one">Znaleziono jedn&#x0105; piosenk&#x0119;.&lt;/item>
        &lt;item quantity="few">Znaleziono %d piosenki.&lt;/item>
        &lt;item quantity="other">Znaleziono %d piosenek.&lt;/item>
    &lt;/plurals>
&lt;/resources>
</pre>
    <p>Java code:</p>
<pre>