Internacionalizar, localizar y dar formato a cadenas, números, importes y fechas en Java

Escrito por picodotdev el .
java planeta-codigo
Enlace permanente Comentarios

La internacionalización y localización son funcionalidades habitualmente necesarias de implementar en una aplicación. La localización ha de tener en cuenta los diferentes literales para cada idioma y no hardcodearlos en el código fuente de la aplicación, las diferentes formas plurales de los lenguajes, el formato de números e importes, fechas e incluso utilizar la forma correcta para ordenar cadenas alfabéticamente.

Java

Muchas aplicaciones necesitan soportar múltiples idiomas, es imprescindible en aquellas multinacionales dirigidas a usuarios con diferentes idiomas, país, cultura o región. Aunque la aplicación no sea multinacional también se necesita si en el mismo país hay múltiples idiomas oficiales en diferentes regiones como en España que el idioma oficial es el español pero comparte oficialidad con Euskera en el País Vasco, con el catalán en Catalunya y el gallego en Galicia. Y aunque no sea imprescindible soportar múltiples idiomas es aconsejable usar las facilidades del lenguaje, librerías o framework para no hardcodear en el código cadenas de literales, no cuesta mucho hacerlo si es desde un principio pero que añadirlo con posterioridad significa revisar el código fuente completo de la aplicación.

Añadir la funcionalidad de internacionalización (i18n) a una aplicación requiere de código específico, la internacionalización genera uno o varios archivos de recursos que posteriormente con el proceso de localización (l10n) genera archivos de recursos adicionales para los diferentes idiomas, países, culturas y regiones. Por otro lado, la funcionalidad de localización está relacionada con la funcionalidad de dar formato a cadenas, números, importes y fechas.

Para generar los archivos localizados se suele utilizar una herramienta especializada en realizar traducciones como Weblate de software libre que permite coordinar el trabajo de internacionalización de los desarrolladores proporcionando el archivo de recurso de la aplicación con las cadenas originales a localizar y con el trabajo de localización de los traductores que proporcionan los recursos localizados.

Archivos de cadenas y recursos localizados con Java

El lenguaje Java ofrece soporte para la internacionalización y localización con varias clases incluidas en el JDK y la API. La clase Locale identifica un idioma, país, región y cultura. La clase ResourceBundle ofrece acceso a los archivos de recursos y properties adecuado según el Locale para el que se quieran obtener los recursos.

Los archivos de recursos de Java en formato properties son archivos de texto con una cadena por línea, cada cadena está identificada con una clave. La clave de la cadena es la misma en todos los archivos localizados de la cadena, varía el literal asociado de la cadena. Es recomendable que los archivos de recursos utilicen el formato de caracteres UTF-8 par soportar los caracteres de todos los alfabetos como cirílicos, kanji y árabes.

1
2
3
4
message=Hello World!
message_0=Hello {0}!
elements=0#There are {0} elements.|1#There is {0} element.|1<There are {0} elements.
fallback=Fallback
resource.properties
1
2
3
message=Hello World!
message_0=Hello {0}!
elements=0#There are {0} elements.|1#There is {0} element.|1<There are {0} elements.
resource_en.properties
1
2
3
message=¡Hola mundo!
message_0=¡Hola {0}!
elements=0#Hay {0} elementos.|1#Hay {0} elemento.|1<Hay {0} elementos.
resource_es.properties

La clase Locale y Locales soportados por Java

El JDK de Java proporciona soporte para múltiples locales con los idiomas mayoritarios en el mundo. La lista completa de locales para los que Java incluye soporte en el JDK se obtiene con la clase Locale. En caso de que el Locale no tenga una variable con el locale estática se ofrece un constructor para indicar el código del lenguaje, el país y su variante.

1
2
3
4
5
6
7
8
...
Locale enLocale = Locale.ENGLISH;
Locale esLocale = new Locale("es");

String locales = Arrays.stream(Locale.getAvailableLocales()).map(it -> it.toLanguageTag()).sorted().collect(Collectors.joining(", "));
System.out.println(locales);
...

Main-locales-1.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# Supported locales
af, af-NA, af-ZA, agq, agq-CM, ak, ak-GH, am, am-ET, ar, ar-001, ar-AE, ar-BH, ar-DJ, ar-DZ, ar-EG,
ar-EH, ar-ER, ar-IL, ar-IQ, ar-JO, ar-KM, ar-KW, ar-LB, ar-LY, ar-MA, ar-MR, ar-OM, ar-PS, ar-QA,
ar-SA, ar-SD, ar-SO, ar-SS, ar-SY, ar-TD, ar-TN, ar-YE, as, as-IN, asa, asa-TZ, ast, ast-ES, az,
az-Cyrl, az-Cyrl-AZ, az-Latn, az-Latn-AZ, bas, bas-CM, be, be-BY, bem, bem-ZM, bez, bez-TZ, bg,
bg-BG, bm, bm-ML, bn, bn-BD, bn-IN, bo, bo-CN, bo-IN, br, br-FR, brx, brx-IN, bs, bs-Cyrl,
bs-Cyrl-BA, bs-Latn, bs-Latn-BA, ca, ca-AD, ca-ES, ca-ES-VALENCIA, ca-FR, ca-IT, ccp, ccp-BD,
ccp-IN, ce, ce-RU, cgg, cgg-UG, chr, chr-US, ckb, ckb-IQ, ckb-IR, cs, cs-CZ, cu, cu-RU, cy, cy-GB,
da, da-DK, da-GL, dav, dav-KE, de, de-AT, de-BE, de-CH, de-DE, de-IT, de-LI, de-LU, dje, dje-NE,
dsb, dsb-DE, dua, dua-CM, dyo, dyo-SN, dz, dz-BT, ebu, ebu-KE, ee, ee-GH, ee-TG, el, el-CY, el-GR,

en, en-001, en-150, en-AG, en-AI, en-AS, en-AT, en-AU, en-BB, en-BE, en-BI, en-BM, en-BS, en-BW, en-BZ,
en-CA, en-CC, en-CH, en-CK, en-CM, en-CX, en-CY, en-DE, en-DG, en-DK, en-DM, en-ER, en-FI, en-FJ, en-FK,
en-FM, en-GB, en-GD, en-GG, en-GH, en-GI, en-GM, en-GU, en-GY, en-HK, en-IE, en-IL, en-IM, en-IN, en-IO,
en-JE, en-JM, en-KE, en-KI, en-KN, en-KY, en-LC, en-LR, en-LS, en-MG, en-MH, en-MO, en-MP, en-MS, en-MT,
en-MU, en-MW, en-MY, en-NA, en-NF, en-NG, en-NL, en-NR, en-NU, en-NZ, en-PG, en-PH, en-PK, en-PN, en-PR,
en-PW, en-RW, en-SB, en-SC, en-SD, en-SE, en-SG, en-SH, en-SI, en-SL, en-SS, en-SX, en-SZ, en-TC, en-TK,
en-TO, en-TT, en-TV, en-TZ, en-UG, en-UM, en-US, en-US-POSIX, en-VC, en-VG, en-VI, en-VU, en-WS, en-ZA,
en-ZM, en-ZW,
eo, eo-001,
es, es-419, es-AR, es-BO, es-BR, es-BZ, es-CL, es-CO, es-CR, es-CU, es-DO, es-EA, es-EC, es-ES, es-GQ,
es-GT, es-HN, es-IC, es-MX, es-NI, es-PA, es-PE, es-PH, es-PR, es-PY, es-SV, es-US, es-UY, es-VE,

et, et-EE, eu, eu-ES, ewo, ewo-CM, fa, fa-AF, fa-IR, ff, ff-CM, ff-GN, ff-MR, ff-SN, fi, fi-FI, fil,
fil-PH, fo, fo-DK, fo-FO, fr, fr-BE, fr-BF, fr-BI, fr-BJ, fr-BL, fr-CA, fr-CD, fr-CF, fr-CG, fr-CH,
fr-CI, fr-CM, fr-DJ, fr-DZ, fr-FR, fr-GA, fr-GF, fr-GN, fr-GP, fr-GQ, fr-HT, fr-KM, fr-LU, fr-MA,
fr-MC, fr-MF, fr-MG, fr-ML, fr-MQ, fr-MR, fr-MU, fr-NC, fr-NE, fr-PF, fr-PM, fr-RE, fr-RW, fr-SC, fr-SN,
fr-SY, fr-TD, fr-TG, fr-TN, fr-VU, fr-WF, fr-YT, fur, fur-IT, fy, fy-NL, ga, ga-IE, gd, gd-GB, gl, gl-ES,
gsw, gsw-CH, gsw-FR, gsw-LI,
gu, gu-IN, guz, guz-KE, gv, gv-IM, ha, ha-GH, ha-NE, ha-NG, haw, haw-US, he, he-IL, hi, hi-IN, hr, hr-BA,
hr-HR, hsb, hsb-DE, hu, hu-HU, hy, hy-AM, id, id-ID, ig, ig-NG, ii, ii-CN, is, is-IS, it, it-CH, it-IT,
it-SM, it-VA, ja, ja-JP, ja-JP-u-ca-japanese-x-lvariant-JP, jgo, jgo-CM, jmc, jmc-TZ, ka, ka-GE, kab,
kab-DZ, kam, kam-KE, kde, kde-TZ, kea, kea-CV, khq, khq-ML, ki, ki-KE, kk, kk-KZ, kkj, kkj-CM, kl, kl-GL,
kln, kln-KE, km, km-KH, kn, kn-IN, ko, ko-KP, ko-KR, kok, kok-IN, ks, ks-IN, ksb, ksb-TZ, ksf, ksf-CM, ksh,
ksh-DE, kw, kw-GB, ky, ky-KG, lag, lag-TZ, lb, lb-LU, lg, lg-UG, lkt, lkt-US, ln, ln-AO, ln-CD, ln-CF,
ln-CG, lo, lo-LA, lrc, lrc-IQ, lrc-IR, lt, lt-LT, lu, lu-CD, luo, luo-KE, luy, luy-KE, lv, lv-LV, mas,
mas-KE, mas-TZ, mer, mer-KE, mfe, mfe-MU, mg, mg-MG, mgh, mgh-MZ, mgo, mgo-CM, mk, mk-MK, ml, ml-IN, mn,
mn-MN, mr, mr-IN, ms, ms-BN, ms-MY, ms-SG, mt, mt-MT, mua, mua-CM, my, my-MM, mzn, mzn-IR, naq, naq-NA,
nb, nb-NO, nb-SJ, nd, nd-ZW, nds, nds-DE, nds-NL, ne, ne-IN, ne-NP, nl, nl-AW, nl-BE, nl-BQ, nl-CW, nl-NL,
nl-SR, nl-SX, nmg, nmg-CM, nn, nn-NO, nn-NO, nnh, nnh-CM, no, no-NO, nus, nus-SS, nyn, nyn-UG, om, om-ET,
om-KE, or, or-IN, os, os-GE, os-RU, pa, pa-Arab, pa-Arab-PK, pa-Guru, pa-Guru-IN, pl, pl-PL, prg, prg-001,
ps, ps-AF, pt, pt-AO, pt-BR, pt-CH, pt-CV, pt-GQ, pt-GW, pt-LU, pt-MO, pt-MZ, pt-PT, pt-ST, pt-TL, qu,
qu-BO, qu-EC, qu-PE, rm, rm-CH, rn, rn-BI, ro, ro-MD, ro-RO, rof, rof-TZ, ru, ru-BY, ru-KG, ru-KZ, ru-MD,
ru-RU, ru-UA, rw, rw-RW, rwk, rwk-TZ, sah, sah-RU, saq, saq-KE, sbp, sbp-TZ, sd, sd-PK, se, se-FI, se-NO,
se-SE, seh, seh-MZ, ses, ses-ML, sg, sg-CF, shi, shi-Latn, shi-Latn-MA, shi-Tfng, shi-Tfng-MA, si, si-LK,
sk, sk-SK, sl, sl-SI, smn, smn-FI, sn, sn-ZW, so, so-DJ, so-ET, so-KE, so-SO, sq, sq-AL, sq-MK, sq-XK, sr,
sr-BA, sr-CS, sr-Cyrl, sr-Cyrl-BA, sr-Cyrl-ME, sr-Cyrl-RS, sr-Cyrl-XK, sr-Latn, sr-Latn-BA, sr-Latn-ME,
sr-Latn-RS, sr-Latn-XK, sr-ME, sr-RS, sv, sv-AX, sv-FI, sv-SE, sw, sw-CD, sw-KE, sw-TZ, sw-UG, ta, ta-IN,
ta-LK, ta-MY, ta-SG, te, te-IN, teo, teo-KE, teo-UG, tg, tg-TJ, th, th-TH, th-TH-u-nu-thai-x-lvariant-TH,
ti, ti-ER, ti-ET, tk, tk-TM, to, to-TO, tr, tr-CY, tr-TR, tt, tt-RU, twq, twq-NE, tzm, tzm-MA, ug, ug-CN,
uk, uk-UA, und, ur, ur-IN, ur-PK, uz, uz-Arab, uz-Arab-AF, uz-Cyrl, uz-Cyrl-UZ, uz-Latn, uz-Latn-UZ, vai,
vai-Latn, vai-Latn-LR, vai-Vaii, vai-Vaii-LR, vi, vi-VN, vo, vo-001, vun, vun-TZ, wae, wae-CH, wo, wo-SN,
xog, xog-UG, yav, yav-CM, yi, yi-001, yo, yo-BJ, yo-NG, yue, yue-Hans, yue-Hans-CN, yue-Hant, yue-Hant-HK,
zgh, zgh-MA, zh, zh-CN, zh-HK, zh-Hans, zh-Hans-CN, zh-Hans-HK, zh-Hans-MO, zh-Hans-SG, zh-Hant, zh-Hant-HK,
zh-Hant-MO, zh-Hant-TW, zh-SG, zh-TW, zu, zu-ZA
System-locales-1.out

La clase Locale está formada por el identificador de dos letras del idioma y opcionalmente adicionalmente por el identificador del país y opcionalmente adicionalmente un código de variante. Estos tres identificadores separados por un guión forman el identificador del Locale. En los archivos de recursos los guiones se ponen con barras bajas.

1
2
3
4
es -> español
es-ES -> español de España
es-MX -> español de México
es-AR -> español de Argentina
Main-locales-2.txt

Localización de cadenas

La clase ResourceBundle busca y devuelve el literal adecuando según la clave que lo identifica y el Locale deseado.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
...
ResourceBundle enBundle = ResourceBundle.getBundle("resource", enLocale);
ResourceBundle esBundle = ResourceBundle.getBundle("resource", esLocale);

// Literales
System.out.println();
System.out.println("# Literals");
System.out.println("Message (en): " + enBundle.getString("message"));
System.out.println("Message (es): " + esBundle.getString("message"));
...
Main-literals-1.java
1
2
3
# Literals
Message (en): Hello World!
Message (es): ¡Hola mundo!
System-literals-1.out

Cadenas con argumentos

Algunas cadenas tienen datos o placeholders que son insertados en tiempo de ejecución como un número, fecha, importe u otra pequeña cadena como un nombre, para insertar estos datos se utiliza la clase MessageFomat que sustituye los argumentos o placeholders por los valores proporcionados.

Los argumentos o placeholders están numerados entre llaves.

1
2
3
4
...
System.out.println("Message with argument (en): " + MessageFormat.format(enBundle.getString("message_0"), "picodotdev"));
System.out.println("Message with argument (es): " + MessageFormat.format(esBundle.getString("message_0"), "picodotdev"));
...
Main-literals-2.java
1
2
Message with argument (en): Hello picodotdev!
Message with argument (es): ¡Hola picodotdev!
System-literals-2.out

Herencia de cadenas

La clase ResourceBundle implementa un mecanismo de herencia por el que si no existe el archivo o clave de la cadena de la clave solicitada en el archivo de recursos del locale indicado se obtiene la cadena del siguiente locale más general. Por ejemplo, si se desea obtener una cadena para el Locale es_ES (español de España) o es_MX (español de México) para el que no existe su propio archivo de recursos o la clave de la cadena entonces se obtiene la cadena del archivo de recurso español (es), si incluso no existiese en el locale es se obtendría la cadena del recurso base resource.properties.

1
2
3
4
5
6
...
Locale esesLocale = new Locale("es", "ES");
ResourceBundle esesBundle = ResourceBundle.getBundle("resource", esesLocale);
...
System.out.println("Fallback (es-ES): " + esesBundle.getString("fallback"));
...
Main-literals-3.java
1
2
Fallback (es-ES): Fallback

System-literals-3.out

Localización de cadenas con múltiples formas plurales

La localización de las cadenas también ha de tener en cuenta que los lenguajes usan varias formas plurales, cada lenguaje tiene su propio número de formas plurales. El español tiene dos formas plurales, la de un elemento y más de un elemento. Otros lenguajes tiene tres o más formas plurales según las propias reglas del lenguaje y número de elementos a los que se refiera la expresión, por ejemplo el ruso tiene tres formas plurales y el chino una sola forma plural.

En español dependiendo del número de elementos a los que nos referimos se utiliza el singular o el plural.

1
2
3
Hay 0 archivos.
Hay 1 archivo.
Hay 2 archivos.
Main-pluralforms-1.txt

Las aplicaciones que no soportan diferentes normas plurales en las cadenas suelen utilizar expresiones como las siguientes, la cadena es la misma tanto para el singular como para el plural.

1
2
3
Hay 0 archivo(s).
Hay 1 archivo(s).
Hay 2 archivo(s).
Main-pluralforms-2.txt

En Java la clase que permite utilizar diferentes formas plurales es ChoiceFormat. Utiliza un formato especial para que cadena elegir según la forma plural, está formada por una colección de número de forma plural y la cadena a utilizar. Para elegir la cadena adecuada según la forma plural se ha de proporcionar el número de elementos a los que se refiere la expresión, este número selecciona la cadena de la forma plural.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
System.out.println();
System.out.println("# Plural forms");

String enPluralForm = enBundle.getString("elements");
String esPluralForm = esBundle.getString("elements");

ChoiceFormat enChoiceFormat = new ChoiceFormat(enPluralForm);
ChoiceFormat esChoiceFormat = new ChoiceFormat(esPluralForm);

String enPluralForm0 = enChoiceFormat.format(0);
String enPluralForm1 = enChoiceFormat.format(1);
String enPluralForm2 = enChoiceFormat.format(2);
String esPluralForm0 = esChoiceFormat.format(0);
String esPluralForm1 = esChoiceFormat.format(1);
String esPluralForm2 = esChoiceFormat.format(2);

System.out.println("Plural form (en, " + 0 + "): " + MessageFormat.format(enPluralForm0, 0));
System.out.println("Plural form (en, " + 1 + "): " + MessageFormat.format(enPluralForm1, 1));
System.out.println("Plural form (en, " + 2 + "): " + MessageFormat.format(enPluralForm2, 2));
System.out.println("Plural form (es, " + 0 + "): " + MessageFormat.format(esPluralForm0, 0));
System.out.println("Plural form (es, " + 1 + "): " + MessageFormat.format(esPluralForm1, 1));
System.out.println("Plural form (es, " + 2 + "): " + MessageFormat.format(esPluralForm2, 2));
...
Main-pluralforms-3.java
1
2
3
4
5
6
7
# Plural forms
Plural form (en, 0): There are 0 elements.
Plural form (en, 1): There is 1 element.
Plural form (en, 2): There are 2 elements.
Plural form (es, 0): Hay 0 elementos.
Plural form (es, 1): Hay 1 elemento.
Plural form (es, 2): Hay 2 elementos.
System-pluralforms-3.out

Localización de fechas

Las fechas también son textos que varían según el Locale ya sea según el formato que se utilicen para convertir las fechas a texto como por incluir nombres de semana, nombres de los meses e intercalar algunas preposiciones. También según el país varía el formato, en Estados Unidos el mes se indica antes que el día, con el formato MM/dd/yyyy cuando en España y muchos otros países se utiliza el formato dd/MM/yyyy con el día primero.

Al usar fechas también suele ser necesario convertir fechas y husos horarios y es recomendable guardar las fechas en formato UTC en la base de datos porque este huso horario no varía con el horario de verano e invierno.

1
2
3
4
5
6
...
System.out.println("# Dates");
System.out.println("Date (iso): " + DateTimeFormatter.ISO_ZONED_DATE_TIME.format(ZonedDateTime.now()));
System.out.println("Date (en): " + DateTimeFormatter.ofPattern("EEEE d, LLLL YYYY", enLocale).format(ZonedDateTime.now()));
System.out.println("Date (es): " + DateTimeFormatter.ofPattern("EEEE d, LLLL YYYY", esLocale).format(ZonedDateTime.now()));
...
Main-dates-1.java
1
2
3
4
# Dates
Date (iso): 2020-12-12T18:17:54.305805+01:00[Europe/Madrid]
Date (en): Saturday 12, December 2020
Date (es): sábado 12, diciembre 2020
System-dates-1.out

Localización de números e importes

Al igual que con las fechas hay que tener algunas consideraciones al trabajar con importes como utilizar la clase BigDecimal en vez de un float o double y en ocasiones es necesario realizar conversiones de importes entre diferentes divisas con ratios de conversión. Java tiene una especificación que define una API para tareas monetarias, la librería Moneta es la implementación de referencia.

Los números e importes también varían en formato según el Locale. En los números en algunos países se utiliza el punto para separar los millares y la coma para los decimales y en otros es al revés se utiliza la coma para separar los millares y el punto para los decimales.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
...
long millionNumber = 1_000_000;
double decimalNumber = 1000.35d;

System.out.println();
System.out.println("# Numbers");
System.out.println("Number (en): " + NumberFormat.getInstance(enLocale).format(millionNumber));
System.out.println("Decimal number (en): " + NumberFormat.getInstance(enLocale).format(decimalNumber));
System.out.println("Number (es): " + NumberFormat.getInstance(esLocale).format(millionNumber));
System.out.println("Decimal number (es): " + NumberFormat.getInstance(esLocale).format(decimalNumber));
...
Main-numbers-1.java
1
2
3
4
5
# Numbers
Number (en): 1,000,000
Decimal number (en): 1,000.35
Number (es): 1.000.000
Decimal number (es): 1.000,35
System-numbers-1.out

Los importes suelen incluir el símbolo de la moneda y dependiendo del Locale el símbolo puede estar antes de la cifra, después de la cifra, con el código de la moneda o el símbolo de la moneda.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import org.javamoney.moneta.Money;
import org.javamoney.moneta.format.CurrencyStyle;

import javax.money.format.AmountFormatQueryBuilder;
import javax.money.format.MonetaryAmountFormat;
...
System.out.println();
System.out.println("# Money");

Money millionUSD = Money.of(new BigDecimal("1000000"), "USD");
Money millionEUR = Money.of(new BigDecimal("1000000"), "EUR");
Money decimalUSD = Money.of(new BigDecimal("1000.35"), "USD");
Money decimalEUR = Money.of(new BigDecimal("1000.35"), "EUR");

MonetaryAmountFormat enMonetaryFormat = MonetaryFormats.getAmountFormat(AmountFormatQueryBuilder.of(enLocale).set(CurrencyStyle.SYMBOL).build());
MonetaryAmountFormat esMonetaryFormat = MonetaryFormats.getAmountFormat(AmountFormatQueryBuilder.of(esLocale).set(CurrencyStyle.SYMBOL).build());

System.out.println("Million USD (en): " + enMonetaryFormat.format(millionUSD));
System.out.println("Million EUR (es): " + esMonetaryFormat.format(millionEUR));
System.out.println("Decimal USD (en): " + enMonetaryFormat.format(decimalUSD));
System.out.println("Decimal EUR (es): " + esMonetaryFormat.format(decimalEUR));
Main-money-1.java
1
2
3
4
5
# Money
Million USD (en): $1,000,000.00
Million EUR (es): 1.000.000,00 €
Decimal USD (en): $1,000.35
Decimal EUR (es): 1.000,35 €
System-money-1.out

Ordenar cadenas correctamente de forma alfabética

Incluso ordenar cadenas alfabéticamente correctamente no es una tarea tan simple como parece a primera vista. Para ordenar correctamente cadenas hay que tener en cuenta o considerar como iguales ciertos caracteres cómo la letra a o la letra á con tilde. Si la ordenación se hace únicamente por el código numérico del caracter dependiendo de la lista de cadenas la lista supuestamente ordenada quizá no sea la que debería.

Al ordenar cadenas también es posible diferenciar las letras en mayúsculas o considerarlas como iguales al ordenarlas. En el artículo la forma correcta de ordenar alfabéticamente cadenas en Java explico esta funcionalidad y que soporte ofrece Java.

Terminal

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando siguiente comando:
./gradlew run

Comparte el artículo: