diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/personname/PersonNameFormatterImpl.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/personname/PersonNameFormatterImpl.java index 6ac4806d1a0..45772fe3c44 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/personname/PersonNameFormatterImpl.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/personname/PersonNameFormatterImpl.java @@ -29,7 +29,6 @@ public class PersonNameFormatterImpl { private final boolean capitalizeSurname; private final String foreignSpaceReplacement; private final String nativeSpaceReplacement; - private final boolean formatterLocaleUsesSpaces; private final PersonNameFormatter.Length length; private final PersonNameFormatter.Usage usage; private final PersonNameFormatter.Formality formality; @@ -54,8 +53,7 @@ public class PersonNameFormatterImpl { this.initialPattern = rb.getStringWithFallback("personNames/initialPattern/initial"); this.initialSequencePattern = rb.getStringWithFallback("personNames/initialPattern/initialSequence"); this.foreignSpaceReplacement = rb.getStringWithFallback("personNames/foreignSpaceReplacement"); - this.formatterLocaleUsesSpaces = !LOCALES_THAT_DONT_USE_SPACES.contains(locale.getLanguage()); - this.nativeSpaceReplacement = formatterLocaleUsesSpaces ? " " : ""; + this.nativeSpaceReplacement = rb.getStringWithFallback("personNames/nativeSpaceReplacement"); // asjust for combinations of parameters that don't make sense in practice if (usage == PersonNameFormatter.Usage.MONOGRAM) { @@ -72,9 +70,12 @@ public class PersonNameFormatterImpl { // different for different names), load patterns for both given-first and surname-first names. (If the user has // specified SORTING, we don't need to do this-- we just load the "sorting" patterns and ignore the name's order.) final String RESOURCE_PATH_PREFIX = "personNames/namePattern/"; - String resourceNameBody = length.toString().toLowerCase() + "-" + usage.toString().toLowerCase() + "-" - + formality.toString().toLowerCase(); - if (displayOrder == PersonNameFormatter.DisplayOrder.DEFAULT) { + String lengthStr = (length != PersonNameFormatter.Length.DEFAULT) ? length.toString().toLowerCase() + : rb.getStringWithFallback("personNames/parameterDefault/length"); + String formalityStr = (formality != PersonNameFormatter.Formality.DEFAULT) ? formality.toString().toLowerCase() + : rb.getStringWithFallback("personNames/parameterDefault/formality"); + String resourceNameBody = lengthStr + "-" + usage.toString().toLowerCase() + "-" + formalityStr; + if (displayOrder != PersonNameFormatter.DisplayOrder.SORTING) { ICUResourceBundle gnFirstResource = rb.getWithFallback(RESOURCE_PATH_PREFIX + "givenFirst-" + resourceNameBody); ICUResourceBundle snFirstResource = rb.getWithFallback(RESOURCE_PATH_PREFIX + "surnameFirst-" + resourceNameBody); @@ -109,7 +110,6 @@ public class PersonNameFormatterImpl { capitalizeSurname = false; foreignSpaceReplacement = " "; nativeSpaceReplacement = " "; - formatterLocaleUsesSpaces = true; // then, set values for the fields we actually care about (all but gnFirstPatterns are optional) this.locale = locale; @@ -200,8 +200,6 @@ public class PersonNameFormatterImpl { return capitalizeSurname; } - private final Set LOCALES_THAT_DONT_USE_SPACES = new HashSet<>(Arrays.asList("ja", "zh", "yue", "km", "lo", "my")); - static final Set NON_DEFAULT_SCRIPTS = new HashSet<>(Arrays.asList("Hani", "Hira", "Kana")); /** @@ -227,6 +225,14 @@ public class PersonNameFormatterImpl { * @return If true, use given-first order to format the name; if false, use surname-first order. */ private boolean nameIsGnFirst(PersonName name) { + // if the formatter has its display order set to one of the "force" values, that overrides + // all this logic and the name's preferred-order property + if (this.displayOrder == PersonNameFormatter.DisplayOrder.FORCE_GIVEN_FIRST) { + return true; + } else if (this.displayOrder == PersonNameFormatter.DisplayOrder.FORCE_SURNAME_FIRST) { + return false; + } + // the name can declare its order-- check that first (it overrides any locale-based calculation) if (name.getPreferredOrder() == PersonName.PreferredOrder.GIVEN_FIRST) { return true; diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/PersonNameFormatter.java b/icu4j/main/classes/core/src/com/ibm/icu/text/PersonNameFormatter.java index 112b58d128f..edb119eab21 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/PersonNameFormatter.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/PersonNameFormatter.java @@ -92,7 +92,13 @@ public class PersonNameFormatter { * When Formality is INFORMAL, may only include one field. * @draft ICU 73 */ - SHORT + SHORT, + + /** + * The default name length for the locale. For most locales, this is the same as MEDIUM. + * @draft ICU 74 + */ + DEFAULT } /** @@ -138,7 +144,14 @@ public class PersonNameFormatter { * of the given name. * @draft ICU 73 */ - INFORMAL + INFORMAL, + + /** + * The default formality for the locale. For most locales, this is the same as FORMAL, but for English, + * this is the same as INFORMAL. + * @draft ICU 74 + */ + DEFAULT } /** @@ -158,7 +171,21 @@ public class PersonNameFormatter { * of the name: "Smith, John". * @draft ICU 73 */ - SORTING + SORTING, + + /** + * Forces the formatter to format the name in given-first order. If the name itself specifies + * a display order, this overrides it. + * @draft ICU 74 + */ + FORCE_GIVEN_FIRST, + + /** + * Forces the formatter to format the name in surname-first order. If the name itself specifies + * a display order, this overrides it. + * @draft ICU 74 + */ + FORCE_SURNAME_FIRST, } private final PersonNameFormatterImpl impl; @@ -260,9 +287,9 @@ public class PersonNameFormatter { } private Locale locale = Locale.getDefault(); - private Length length = Length.MEDIUM; + private Length length = Length.DEFAULT; private Usage usage = Usage.REFERRING; - private Formality formality = Formality.FORMAL; + private Formality formality = Formality.DEFAULT; private DisplayOrder displayOrder = DisplayOrder.DEFAULT; private boolean surnameAllCaps = false; } diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/PersonNameFormatterTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/PersonNameFormatterTest.java index 6dafbf4f7f0..851e1c77fa5 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/PersonNameFormatterTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/PersonNameFormatterTest.java @@ -102,19 +102,35 @@ public class PersonNameFormatterTest extends TestFmwk{ executeTestCases(new NameAndTestCases[]{ new NameAndTestCases("locale=en_US,title=Mr.,given=Richard,given-informal=Rich,given2=Theodore,surname=Gillam", new String[][] { // test all the different combinations of parameters with the normal name order - { "en_US", "LONG", "REFERRING", "FORMAL", "DEFAULT", "", "Mr. Richard Theodore Gillam" }, - { "en_US", "LONG", "REFERRING", "INFORMAL", "DEFAULT", "", "Rich Gillam" }, - { "en_US", "LONG", "ADDRESSING", "FORMAL", "DEFAULT", "", "Mr. Gillam" }, - { "en_US", "LONG", "ADDRESSING", "INFORMAL", "DEFAULT", "", "Rich" }, - { "en_US", "MEDIUM", "REFERRING", "FORMAL", "DEFAULT", "", "Richard T. Gillam" }, - { "en_US", "MEDIUM", "REFERRING", "INFORMAL", "DEFAULT", "", "Rich Gillam" }, - { "en_US", "MEDIUM", "ADDRESSING", "FORMAL", "DEFAULT", "", "Mr. Gillam" }, - { "en_US", "MEDIUM", "ADDRESSING", "INFORMAL", "DEFAULT", "", "Rich" }, - //{ "en_US", "SHORT", "REFERRING", "FORMAL", "DEFAULT", "", "R. T. Gillam" }, - { "en_US", "SHORT", "REFERRING", "FORMAL", "DEFAULT", "", "R.T. Gillam" }, // result changed with CLDR 43-alpha1 - { "en_US", "SHORT", "REFERRING", "INFORMAL", "DEFAULT", "", "Rich G." }, - { "en_US", "SHORT", "ADDRESSING", "FORMAL", "DEFAULT", "", "Mr. Gillam" }, - { "en_US", "SHORT", "ADDRESSING", "INFORMAL", "DEFAULT", "", "Rich" }, + { "en_US", "LONG", "REFERRING", "FORMAL", "DEFAULT", "", "Mr. Richard Theodore Gillam" }, + { "en_US", "LONG", "REFERRING", "INFORMAL", "DEFAULT", "", "Rich Gillam" }, + { "en_US", "LONG", "ADDRESSING", "FORMAL", "DEFAULT", "", "Mr. Gillam" }, + { "en_US", "LONG", "ADDRESSING", "INFORMAL", "DEFAULT", "", "Rich" }, + { "en_US", "MEDIUM", "REFERRING", "FORMAL", "DEFAULT", "", "Richard T. Gillam" }, + { "en_US", "MEDIUM", "REFERRING", "INFORMAL", "DEFAULT", "", "Rich Gillam" }, + { "en_US", "MEDIUM", "ADDRESSING", "FORMAL", "DEFAULT", "", "Mr. Gillam" }, + { "en_US", "MEDIUM", "ADDRESSING", "INFORMAL", "DEFAULT", "", "Rich" }, + //{ "en_US", "SHORT", "REFERRING", "FORMAL", "DEFAULT", "", "R. T. Gillam" }, + { "en_US", "SHORT", "REFERRING", "FORMAL", "DEFAULT", "", "R.T. Gillam" }, // result changed with CLDR 43-alpha1 + { "en_US", "SHORT", "REFERRING", "INFORMAL", "DEFAULT", "", "Rich G." }, + { "en_US", "SHORT", "ADDRESSING", "FORMAL", "DEFAULT", "", "Mr. Gillam" }, + { "en_US", "SHORT", "ADDRESSING", "INFORMAL", "DEFAULT", "", "Rich" }, + + // test DEFAULT length (in English [and all other current locales], this is the same as MEDIUM) + { "en_US", "DEFAULT", "REFERRING", "FORMAL", "DEFAULT", "", "Richard T. Gillam" }, + { "en_US", "DEFAULT", "REFERRING", "INFORMAL", "DEFAULT", "", "Rich Gillam" }, + { "en_US", "DEFAULT", "ADDRESSING", "FORMAL", "DEFAULT", "", "Mr. Gillam" }, + { "en_US", "DEFAULT", "ADDRESSING", "INFORMAL", "DEFAULT", "", "Rich" }, + + // test DEFAULT formality (in English, this is the same as INFORMAL) + { "en_US", "LONG", "REFERRING", "DEFAULT", "DEFAULT", "", "Rich Gillam" }, + { "en_US", "LONG", "ADDRESSING", "DEFAULT", "DEFAULT", "", "Rich" }, + { "en_US", "MEDIUM", "REFERRING", "DEFAULT", "DEFAULT", "", "Rich Gillam" }, + { "en_US", "MEDIUM", "ADDRESSING", "DEFAULT", "DEFAULT", "", "Rich" }, + { "en_US", "SHORT", "REFERRING", "DEFAULT", "DEFAULT", "", "Rich G." }, + { "en_US", "SHORT", "ADDRESSING", "DEFAULT", "DEFAULT", "", "Rich" }, + { "en_US", "DEFAULT", "REFERRING", "DEFAULT", "DEFAULT", "", "Rich Gillam" }, + { "en_US", "DEFAULT", "ADDRESSING", "DEFAULT", "DEFAULT", "", "Rich" }, // test all the different combinations of parameters for "sorting" order { "en_US", "LONG", "REFERRING", "FORMAL", "SORTING", "", "Gillam, Richard Theodore" }, @@ -331,6 +347,35 @@ public class PersonNameFormatterTest extends TestFmwk{ new NameAndTestCases("locale=ja_JP,given=Shinzo,surname=Abe,preferredOrder=givenFirst", new String[][] { { "en_US", "LONG", "REFERRING", "FORMAL", "DEFAULT", "", "Shinzo Abe" }, }), + + // the formatter can also override the ordering and always format names in GN-first or SN-first order + // (this repeats some of the test cases above for clarity) + new NameAndTestCases("locale=en_US,given=Shinzo,surname=Abe", new String[][] { + { "en_US", "LONG", "REFERRING", "FORMAL", "DEFAULT", "", "Shinzo Abe" }, + { "en_US", "LONG", "REFERRING", "FORMAL", "FORCE_GIVEN_FIRST", "", "Shinzo Abe" }, + { "en_US", "LONG", "REFERRING", "FORMAL", "FORCE_SURNAME_FIRST", "", "Abe Shinzo" }, + }), + new NameAndTestCases("locale=ja_JP,given=Shinzo,surname=Abe", new String[][] { + { "en_US", "LONG", "REFERRING", "FORMAL", "DEFAULT", "", "Abe Shinzo" }, + { "en_US", "LONG", "REFERRING", "FORMAL", "FORCE_GIVEN_FIRST", "", "Shinzo Abe" }, + { "en_US", "LONG", "REFERRING", "FORMAL", "FORCE_SURNAME_FIRST", "", "Abe Shinzo" }, + }), + new NameAndTestCases("locale=ja_JP,given=晋三,surname=安倍", new String[][] { + { "en_US", "LONG", "REFERRING", "FORMAL", "DEFAULT", "", "安倍晋三" }, + { "en_US", "LONG", "REFERRING", "FORMAL", "FORCE_GIVEN_FIRST", "", "晋三安倍" }, + { "en_US", "LONG", "REFERRING", "FORMAL", "FORCE_SURNAME_FIRST", "", "安倍晋三" }, + }), + new NameAndTestCases("locale=en_US,given=Shinzo,surname=Abe,preferredOrder=surnameFirst", new String[][] { + { "en_US", "LONG", "REFERRING", "FORMAL", "DEFAULT", "", "Abe Shinzo" }, + { "en_US", "LONG", "REFERRING", "FORMAL", "FORCE_GIVEN_FIRST", "", "Shinzo Abe" }, + { "en_US", "LONG", "REFERRING", "FORMAL", "FORCE_SURNAME_FIRST", "", "Abe Shinzo" }, + }), + new NameAndTestCases("locale=ja_JP,given=Shinzo,surname=Abe,preferredOrder=givenFirst", new String[][] { + { "en_US", "LONG", "REFERRING", "FORMAL", "DEFAULT", "", "Shinzo Abe" }, + { "en_US", "LONG", "REFERRING", "FORMAL", "FORCE_GIVEN_FIRST", "", "Shinzo Abe" }, + { "en_US", "LONG", "REFERRING", "FORMAL", "FORCE_SURNAME_FIRST", "", "Abe Shinzo" }, + }), + }, false); } @@ -387,13 +432,12 @@ public class PersonNameFormatterTest extends TestFmwk{ { "ja_JP", "LONG", "REFERRING", "FORMAL", "DEFAULT", "", "宮崎駿" }, { "zh_CN", "LONG", "REFERRING", "FORMAL", "DEFAULT", "", "宮崎駿" }, }), - // (Thai, despite not using spaces between words, DOES use spaces between the given name and surname_ + // (Thai and Lao, despite not using spaces between words, DO use spaces between the given name and surname new NameAndTestCases("locale=th_TH,given=ไอริณ,surname=กล้าหาญ", new String[][] { { "th_TH", "LONG", "REFERRING", "FORMAL", "DEFAULT", "", "ไอริณ กล้าหาญ" }, }), - // (Lao, on the other hand, does NOT put a space between the given name and surname) new NameAndTestCases("locale=lo_LA,given=ໄອຣີນ,surname=ແອດເລີ", new String[][] { - { "lo_LA", "LONG", "REFERRING", "FORMAL", "DEFAULT", "", "ໄອຣີນແອດເລີ" }, + { "lo_LA", "LONG", "REFERRING", "FORMAL", "DEFAULT", "", "ໄອຣີນ ແອດເລີ" }, }), }, false); } @@ -598,4 +642,22 @@ public class PersonNameFormatterTest extends TestFmwk{ assertEquals("Wrong result for " + localeID, expectedResult, actualResult); } } + + @Test + public void TestDefaultFormality() { + executeTestCases(new NameAndTestCases[]{ + // in English, DEFAULT formality is the same as INFORMAL + new NameAndTestCases("locale=en_US,title=Mr.,given=Richard,given-informal=Rich,given2=Theodore,surname=Gillam", new String[][] { + {"en_US", "DEFAULT", "REFERRING", "FORMAL", "DEFAULT", "", "Richard T. Gillam"}, + {"en_US", "DEFAULT", "REFERRING", "INFORMAL", "DEFAULT", "", "Rich Gillam"}, + {"en_US", "DEFAULT", "REFERRING", "DEFAULT", "DEFAULT", "", "Rich Gillam"}, + }), + // in other languages (we're using German here), DEFAULT formality is the same as FORMAL + new NameAndTestCases("locale=de_DE,title=Herr,given=Friedrich,given-informal=Fritz,given2=Georg,surname=Schellhammer", new String[][] { + {"de_DE", "DEFAULT", "REFERRING", "FORMAL", "DEFAULT", "", "Friedrich G. Schellhammer"}, + {"de_DE", "DEFAULT", "REFERRING", "INFORMAL", "DEFAULT", "", "Fritz Schellhammer"}, + {"de_DE", "DEFAULT", "REFERRING", "DEFAULT", "DEFAULT", "", "Friedrich G. Schellhammer"}, + }) + }, false); + } }