-
Notifications
You must be signed in to change notification settings - Fork 336
Load supported languages dynamically from LanguageOptions.properties #9543
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
0ac30ab
97c9ede
99221ad
f7d45f9
d6fac48
3bb4ec5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -20,6 +20,8 @@ | |||||||||||||||||||||||||||||||
| <%@ page import="org.wso2.carbon.identity.application.authentication.endpoint.util.EncodedControl" %> | ||||||||||||||||||||||||||||||||
| <%@ page import="java.nio.charset.StandardCharsets" %> | ||||||||||||||||||||||||||||||||
| <%@ page import="java.util.*" %> | ||||||||||||||||||||||||||||||||
| <%@ page import="java.io.FileReader" %> | ||||||||||||||||||||||||||||||||
| <%@ page import="java.io.BufferedReader" %> | ||||||||||||||||||||||||||||||||
| <%@ page import="org.json.JSONObject" %> | ||||||||||||||||||||||||||||||||
| <%@ page import="java.util.Calendar" %> | ||||||||||||||||||||||||||||||||
| <%@ page import="org.apache.commons.lang.StringUtils" %> | ||||||||||||||||||||||||||||||||
|
|
@@ -99,6 +101,8 @@ | |||||||||||||||||||||||||||||||
| Locale userLocale = browserLocale; | ||||||||||||||||||||||||||||||||
| String localeFromCookie = null; | ||||||||||||||||||||||||||||||||
| String BUNDLE = "org.wso2.carbon.identity.application.authentication.endpoint.i18n.Resources"; | ||||||||||||||||||||||||||||||||
| String SUPPORTED_LANGUAGES_ATTR = "supportedLanguages"; | ||||||||||||||||||||||||||||||||
| String LANGUAGE_SUPPORTED_COUNTRIES_ATTR = "languageSupportedCountries"; | ||||||||||||||||||||||||||||||||
| // List of screen names for retrieving text branding customizations. | ||||||||||||||||||||||||||||||||
| List<String> screenNames = new ArrayList<>(); | ||||||||||||||||||||||||||||||||
|
|
@@ -107,23 +111,58 @@ | |||||||||||||||||||||||||||||||
| // Map to store default supported language codes. | ||||||||||||||||||||||||||||||||
| // TODO: Use this map to generate the `language-switcher.jsp`. | ||||||||||||||||||||||||||||||||
| Map<String, String> supportedLanguages = new HashMap<>(); | ||||||||||||||||||||||||||||||||
| supportedLanguages.put("en", "US"); | ||||||||||||||||||||||||||||||||
| supportedLanguages.put("fr", "FR"); | ||||||||||||||||||||||||||||||||
| supportedLanguages.put("es", "ES"); | ||||||||||||||||||||||||||||||||
| supportedLanguages.put("pt", "PT"); | ||||||||||||||||||||||||||||||||
| supportedLanguages.put("de", "DE"); | ||||||||||||||||||||||||||||||||
| supportedLanguages.put("zh", "CN"); | ||||||||||||||||||||||||||||||||
| supportedLanguages.put("ja", "JP"); | ||||||||||||||||||||||||||||||||
| List<String> languageSupportedCountries = new ArrayList<>(); | ||||||||||||||||||||||||||||||||
| languageSupportedCountries.add("US"); | ||||||||||||||||||||||||||||||||
| languageSupportedCountries.add("FR"); | ||||||||||||||||||||||||||||||||
| languageSupportedCountries.add("ES"); | ||||||||||||||||||||||||||||||||
| languageSupportedCountries.add("PT"); | ||||||||||||||||||||||||||||||||
| languageSupportedCountries.add("DE"); | ||||||||||||||||||||||||||||||||
| languageSupportedCountries.add("CN"); | ||||||||||||||||||||||||||||||||
| languageSupportedCountries.add("JP"); | ||||||||||||||||||||||||||||||||
| languageSupportedCountries.add("BR"); | ||||||||||||||||||||||||||||||||
| // Dynamically load supported languages from languageOptions.properties | ||||||||||||||||||||||||||||||||
| // Use application scope to cache the parsed language options | ||||||||||||||||||||||||||||||||
| Map<String, String> cachedSupportedLanguages = (Map<String, String>) request.getAttribute(SUPPORTED_LANGUAGES_ATTR); | ||||||||||||||||||||||||||||||||
| List<String> cachedLanguageSupportedCountries = (List<String>) request.getAttribute(LANGUAGE_SUPPORTED_COUNTRIES_ATTR); | ||||||||||||||||||||||||||||||||
| if (cachedSupportedLanguages != null && cachedLanguageSupportedCountries != null) { | ||||||||||||||||||||||||||||||||
| supportedLanguages = cachedSupportedLanguages; | ||||||||||||||||||||||||||||||||
| languageSupportedCountries = cachedLanguageSupportedCountries; | ||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||
pavinduLakshan marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+116
to
+124
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical mismatch: Comment indicates application scope but implementation uses request scope. The comment on line 117 states "Use application scope to cache" but the implementation uses Impact:
Recommendation: ♻️ Proposed fix using application scope with thread-safe collections- // Dynamically load supported languages from languageOptions.properties
- // Use application scope to cache the parsed language options
- Map<String, String> cachedSupportedLanguages = (Map<String, String>) request.getAttribute(SUPPORTED_LANGUAGES_ATTR);
- List<String> cachedLanguageSupportedCountries = (List<String>) request.getAttribute(LANGUAGE_SUPPORTED_COUNTRIES_ATTR);
+ // Dynamically load supported languages from languageOptions.properties
+ // Use application scope to cache the parsed language options
+ Map<String, String> cachedSupportedLanguages = (Map<String, String>) application.getAttribute(SUPPORTED_LANGUAGES_ATTR);
+ List<String> cachedLanguageSupportedCountries = (List<String>) application.getAttribute(LANGUAGE_SUPPORTED_COUNTRIES_ATTR);At line 113, initialize with thread-safe collections: - Map<String, String> supportedLanguages = new HashMap<>();
- List<String> languageSupportedCountries = new ArrayList<>();
+ Map<String, String> supportedLanguages = new java.util.concurrent.ConcurrentHashMap<>();
+ List<String> languageSupportedCountries = new java.util.concurrent.CopyOnWriteArrayList<>();And at lines 160-161: - request.setAttribute(SUPPORTED_LANGUAGES_ATTR, supportedLanguages);
- request.setAttribute(LANGUAGE_SUPPORTED_COUNTRIES_ATTR, languageSupportedCountries);
+ application.setAttribute(SUPPORTED_LANGUAGES_ATTR, supportedLanguages);
+ application.setAttribute(LANGUAGE_SUPPORTED_COUNTRIES_ATTR, languageSupportedCountries);
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
| // Specify the file path | ||||||||||||||||||||||||||||||||
| String filePath = application.getRealPath("/") + "/WEB-INF/classes/LanguageOptions.properties"; | ||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. File path construction may fail in certain deployment scenarios.
Recommendation: ♻️ Proposed fix using getResourceAsStream- // Specify the file path
- String filePath = application.getRealPath("/") + "/WEB-INF/classes/LanguageOptions.properties";
-
- // Use a BufferedReader to read the file content
- try (BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath))) {
+ // Load the properties file as a resource stream
+ try (InputStream inputStream = application.getResourceAsStream("/WEB-INF/classes/LanguageOptions.properties");
+ BufferedReader bufferedReader = new BufferedReader(
+ new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
+ if (inputStream == null) {
+ throw new IOException("LanguageOptions.properties not found");
+ }Note: This also requires adding the InputStream import: <%@ page import="java.io.InputStream" %>
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
| // Use a BufferedReader to read the file content | ||||||||||||||||||||||||||||||||
| try (BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath))) { | ||||||||||||||||||||||||||||||||
| String line; | ||||||||||||||||||||||||||||||||
| while ((line = bufferedReader.readLine()) != null) { | ||||||||||||||||||||||||||||||||
| // Ignore comments and empty lines | ||||||||||||||||||||||||||||||||
| if (!line.trim().startsWith("#") && !line.trim().isEmpty()) { | ||||||||||||||||||||||||||||||||
| // Split the line into key and value using '=' as the delimiter | ||||||||||||||||||||||||||||||||
| String[] keyValue = line.split("=", 2); | ||||||||||||||||||||||||||||||||
| if (keyValue.length < 2) { | ||||||||||||||||||||||||||||||||
| continue; // Skip malformed lines | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| // Split the key further using '.' as the delimiter | ||||||||||||||||||||||||||||||||
| String[] parts = keyValue[0].split("\\."); | ||||||||||||||||||||||||||||||||
| if (parts.length == 0) { | ||||||||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| String languageCode = parts[parts.length - 1]; | ||||||||||||||||||||||||||||||||
| // Split the value further using ',' as the delimiter | ||||||||||||||||||||||||||||||||
| String[] values = keyValue[1].split(","); | ||||||||||||||||||||||||||||||||
| if (values.length < 2) { | ||||||||||||||||||||||||||||||||
| continue; // Skip lines without proper country,displayName format | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| String country = values[0]; | ||||||||||||||||||||||||||||||||
| // displayName is available in values[1] if needed in the future | ||||||||||||||||||||||||||||||||
| // Add the values to the list. | ||||||||||||||||||||||||||||||||
| supportedLanguages.put(languageCode, country); | ||||||||||||||||||||||||||||||||
| if (!languageSupportedCountries.contains(country)) { | ||||||||||||||||||||||||||||||||
| languageSupportedCountries.add(country); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| request.setAttribute(SUPPORTED_LANGUAGES_ATTR, supportedLanguages); | ||||||||||||||||||||||||||||||||
| request.setAttribute(LANGUAGE_SUPPORTED_COUNTRIES_ATTR, languageSupportedCountries); | ||||||||||||||||||||||||||||||||
| } catch (Exception e) { | ||||||||||||||||||||||||||||||||
| throw e; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+162
to
+164
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Improve exception handling with logging and consider fallback behavior. The current exception handling simply rethrows the exception without logging or providing a fallback. This creates several issues:
Recommendation: ♻️ Proposed fix with logging and empty collection handling } catch (Exception e) {
- throw e;
+ // Log the error for debugging
+ System.err.println("Failed to load LanguageOptions.properties: " + e.getMessage());
+ e.printStackTrace();
+
+ // Ensure collections are initialized even on failure
+ // Application will fall back to default "en_US" locale
+ if (supportedLanguages.isEmpty()) {
+ supportedLanguages.put("en", "US");
+ languageSupportedCountries.add("US");
+ }
}
}Alternatively, if failure to load the properties file should prevent application startup: } catch (Exception e) {
+ System.err.println("Critical: Failed to load LanguageOptions.properties: " + e.getMessage());
+ e.printStackTrace();
throw e;
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| // Check cookie for the user selected language first | ||||||||||||||||||||||||||||||||
| Cookie[] cookies = request.getCookies(); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use explicit character encoding when reading the properties file.
FileReaderuses the platform default encoding, which can vary across systems and lead to incorrect parsing of non-ASCII characters in language names or values.📝 Proposed fix using explicit UTF-8 encoding
Replace the FileReader import and usage with InputStreamReader or use java.nio:
Then update line 129 to:
🤖 Prompt for AI Agents