Migrate to zh-Hant / zh-Hans for Chinese language

Closes: #41239

Signed-off-by: 秉虎 <s96016641@gmail.com>
Signed-off-by: Allen <s96016641@gmail.com>
Signed-off-by: Alexander Schwartz <aschwart@redhat.com>
Co-authored-by: Alexander Schwartz <aschwart@redhat.com>
This commit is contained in:
秉虎
2025-07-28 17:47:55 +08:00
committed by GitHub
parent d97d27f827
commit d2e9b09ebc
17 changed files with 112 additions and 24 deletions

View File

@@ -48,6 +48,16 @@ Starting with this release, {project_name} will cache by default only 10000 entr
Use the options `cache-embedded-offline-sessions-max-count` and `cache-embedded-offline-client-sessions-max-count` to change size of the offline session caches.
=== Translation resource bundle file names
The naming of resource bundles in classloader and folder based themes is now aligned with Java https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/ResourceBundle.html#getBundle(java.lang.String,java.util.Locale,java.lang.ClassLoader)[ResourceBunndle#getBundle] file names.
For all included community languages like `de` or `pt-BR` a file is as before named `messages_de.properties` or `messages_pt_BR.properties`.
If you added custom language code, you should check if your file names are still the same.
The languages "Chinese (traditional)" and "Chinese (simplified)" are named for historical reasons `zh-TW` and `zh-CN` in the community themes of {project_name}.
As a start to migrate to the new language codes `zh-Hant` and `zh-Hans`, the classloader and folder based themes pick up for the old language codes `zh-TW` and `zh-CN` also the files `messages_zh_Hant.properties` and `messages_zh_Hant.properties`.
Entries in `messages_zh_Hant.properties` take precedence over entries in `messages_zh_TW.properties`, and entries in `messages_zh_Hans.properties` take precedence over entries in `messages_zh_CN.properties`.
=== Supported Update Email Feature
The `Update Email` is now a supported feature so it is no longer needed to enable the feature during the server startup.

View File

@@ -35,6 +35,7 @@ Texts of these message bundles can be overwritten by realm-specific values, whic
.Procedure
. Create the file `<THEME TYPE>/messages/messages_<LOCALE>.properties` in the directory of your theme.
The `<LOCALE>` follows the conventions of https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/ResourceBundle.html#getBundle(java.lang.String,java.util.Locale,java.lang.ClassLoader)[ResourceBunndle#getBundle].
. Add this file to the `locales` property in `<THEME TYPE>/theme.properties`.
For a language to be available to users in a realm, the login, account, and email theme types must support the language, so you need to add your language for those theme types.
@@ -72,8 +73,10 @@ locale_no=Norsk
By default, message properties files should be encoded using UTF-8.
{project_name} falls back to ISO-8859-1 handling if it cannot read the contents as UTF-8.
Unicode characters can be escaped as described in Java's documentation for https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/PropertyResourceBundle.html[PropertyResourceBundle].
Previous versions of {project_name} had support for specifying the encoding in the first line with a comment such as `# encoding: UTF-8`, which is no longer supported.
Unicode characters can be escaped as described in Java's documentation for https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/PropertyResourceBundle.html[PropertyResourceBundle].
To simplify migration to the new language codes `zh-Hant` and `zh-Hans`, the classloader and folder based themes pick up for the old language codes `zh-TW` and `zh-CN` also the files `messages_zh_Hant.properties` and `messages_zh_Hant.properties`.
Entries in `messages_zh_Hant.properties` take precedence over entries in `messages_zh_TW.properties`, and entries in `messages_zh_Hans.properties` take precedence over entries in `messages_zh_CN.properties`.
[role="_additional-resources"]
.Additional resources

View File

@@ -31,7 +31,7 @@ import java.util.Properties;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ClassLoaderTheme implements Theme {
public class ClassLoaderTheme extends FileBasedTheme {
private String name;
@@ -116,19 +116,13 @@ public class ClassLoaderTheme implements Theme {
}
@Override
public Properties getMessages(String baseBundlename, Locale locale) throws IOException {
if(locale == null){
return null;
}
Properties m = new Properties();
URL url = classLoader.getResource(this.messageRoot + baseBundlename + "_" + locale + ".properties");
protected void loadBundle(String baseBundlename, Locale locale, Properties m) throws IOException {
URL url = classLoader.getResource(this.messageRoot + toBundleName(baseBundlename, locale) + ".properties");
if (url != null) {
try (InputStream stream = url.openStream()) {
PropertiesUtil.readCharsetAware(m, stream);
}
}
return m;
}
@Override

View File

@@ -311,9 +311,9 @@ public class DefaultThemeManager implements ThemeManager {
// This is mapping old locale codes to the new locale codes for Simplified and Traditional Chinese.
// Once the existing locales have been moved, this code can be removed.
if (l.equals("zh-CN")) {
rl = "zh-HANS";
rl = "zh-Hans";
} else if (l.equals("zh-TW")) {
rl = "zh-HANT";
rl = "zh-Hans";
}
Locale loc = Locale.forLanguageTag(rl);
label = capitalize(loc.getDisplayName(locale), locale);

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2025 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.keycloak.theme;
import java.io.IOException;
import java.util.Locale;
import java.util.Properties;
public abstract class FileBasedTheme implements Theme {
abstract protected void loadBundle(String baseBundlename, Locale locale, Properties m) throws IOException;
@Override
public Properties getMessages(String baseBundlename, Locale locale) throws IOException {
if(locale == null){
return null;
}
Properties m = new Properties();
loadBundle(baseBundlename, locale, m);
// Chinese locales mapping
if (locale.getLanguage().equals("zh") && !locale.getCountry().isEmpty()) {
Locale l = switch (locale.getCountry()) {
case "TW" -> Locale.forLanguageTag("zh-Hant");
case "CN" -> Locale.forLanguageTag("zh-Hans");
default -> null;
};
if (l != null) {
loadBundle(baseBundlename, l, m);
}
}
return m;
}
// Logic as implemented by JDK's ResourceBundle
public String toBundleName(String baseName, Locale locale) {
if (locale == Locale.ROOT) {
return baseName;
}
String language = locale.getLanguage();
String script = locale.getScript();
String country = locale.getCountry();
String variant = locale.getVariant();
if (language.isEmpty() && country.isEmpty() && variant.isEmpty()) {
return baseName;
}
StringBuilder sb = new StringBuilder(baseName);
sb.append('_');
if (!script.isEmpty()) {
if (!variant.isEmpty()) {
sb.append(language).append('_').append(script).append('_').append(country).append('_').append(variant);
} else if (!country.isEmpty()) {
sb.append(language).append('_').append(script).append('_').append(country);
} else {
sb.append(language).append('_').append(script);
}
} else {
if (!variant.isEmpty()) {
sb.append(language).append('_').append(country).append('_').append(variant);
} else if (!country.isEmpty()) {
sb.append(language).append('_').append(country);
} else {
sb.append(language);
}
}
return sb.toString();
}
}

View File

@@ -34,7 +34,7 @@ import java.util.regex.Pattern;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class FolderTheme implements Theme {
public class FolderTheme extends FileBasedTheme {
private String parentName;
private String importName;
@@ -101,14 +101,8 @@ public class FolderTheme implements Theme {
private static final Pattern LEGAL_LOCALE = Pattern.compile("[a-zA-Z0-9-_#]*");
@Override
public Properties getMessages(String baseBundlename, Locale locale) throws IOException {
if (locale == null){
return null;
}
Properties m = new Properties();
String filename = baseBundlename + "_" + locale;
protected void loadBundle(String baseBundlename, Locale locale, Properties m) throws IOException {
String filename = toBundleName(baseBundlename, locale);
if (!LEGAL_LOCALE.matcher(filename).matches()) {
throw new RuntimeException("Found illegal characters in locale or bundle name: " + filename);
@@ -120,7 +114,6 @@ public class FolderTheme implements Theme {
PropertiesUtil.readCharsetAware(m, stream);
}
}
return m;
}
public Properties getEnhancedMessages(RealmModel realm, Locale locale) throws IOException {