Skip to content

Commit

Permalink
Merge pull request #7220 from savindi7/feat-support-rtl-langs
Browse files Browse the repository at this point in the history
Implement Right-to-Left (RTL) Language Support for Login and Recovery Screens
  • Loading branch information
savindi7 authored Jan 9, 2025
2 parents e094ba8 + 573d6bb commit d72031d
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 11 deletions.
6 changes: 6 additions & 0 deletions .changeset/hip-melons-crash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@wso2is/identity-apps-core": minor
"@wso2is/theme": minor
---

Add RTL support.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
# --------------------------------------------------------------------------------------

# This file contains the language switcher configurations
# The format of the file is <language switcher name>=<language code>,<language name>
# The format of the file is <language switcher name>=<language code>,<language name>,text direction(rtl/ltr)
# The default text direction is set to "ltr".
# Example: lang.switch.ar_AR=ar,Arabic - العربية,rtl
lang.switch.en_US=us,English - United States
lang.switch.fr_FR=fr,Français - France
lang.switch.es_ES=es,Español - España
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
<%@ page import="org.apache.commons.text.StringEscapeUtils" %>
<%@ page import="org.wso2.carbon.identity.application.authentication.endpoint.util.AuthenticationEndpointUtil" %>
<%@ page import="java.io.File" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.FileReader" %>

<%-- Include tenant context --%>
<jsp:directive.include file="init-url.jsp"/>
Expand All @@ -29,18 +31,69 @@
<%-- Branding Preferences --%>
<jsp:directive.include file="branding-preferences.jsp"/>


<%-- Extract the name of the stylesheet--%>
<%
String themeName = "wso2is";
String language = "en";
Cookie[] userCookies = request.getCookies();
if (userCookies != null) {
for (Cookie cookie : userCookies) {
if ("ui_lang".equals(cookie.getName())) {
language = cookie.getValue();
break;
}
}
}
String filePath = application.getRealPath("/") + "/WEB-INF/classes/LanguageOptions.properties";
Map<String, String> languageDirectionMap = new HashMap<>();
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
if (!line.trim().startsWith("#") && !line.trim().isEmpty()) {
String[] keyValue = line.split("=");
if (keyValue.length == 2) {
String[] keyParts = keyValue[0].split("\\.");
String languageCode = keyParts[keyParts.length - 1];
String[] valueParts = keyValue[1].split(",");
if (valueParts.length >= 3) {
String direction = valueParts[2].trim();
languageDirectionMap.put(languageCode, direction);
} else {
languageDirectionMap.put(languageCode, "ltr");
}
}
}
}
} catch (Exception e) {
throw e;
}
// Get the selected language's direction.
String direction = languageDirectionMap.getOrDefault(language, "ltr");
String themeSuffix = "";
if ("rtl".equals(languageDirectionMap.get(language))) {
themeSuffix = ".rtl";
}
File themeDir = new File(request.getSession().getServletContext().getRealPath("/")
+ "/" + "libs/themes/" + themeName + "/");
String[] fileNames = themeDir.list();
String themeFileName = "";
for(String file: fileNames) {
if(file.endsWith("min.css")) {
for (String file: fileNames) {
if (file.endsWith(themeSuffix + ".min.css")) {
themeFileName = file;
break;
}
}
%>
Expand Down Expand Up @@ -109,3 +162,11 @@
}
}
</style>

<script type="text/javascript">
const direction = "<%= direction %>";
if (direction) {
document.documentElement.setAttribute("dir", direction);
}
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
# --------------------------------------------------------------------------------------

# This file contains the language switcher configurations
# The format of the file is <language switcher name>=<language code>,<language name>
# The format of the file is <language switcher name>=<language code>,<language name>,text direction(rtl/ltr)
# The default text direction is set to "ltr".
# Example: lang.switch.ar_AR=ar,Arabic - العربية,rtl
lang.switch.en_US=us,English - United States
lang.switch.fr_FR=fr,Français - France
lang.switch.es_ES=es,Español - España
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
<%@ page import="org.wso2.carbon.identity.mgt.endpoint.util.IdentityManagementEndpointUtil" %>
<%@ page import="org.owasp.encoder.Encode" %>
<%@ page import="java.io.File" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.FileReader" %>

<%-- Localization --%>
<jsp:directive.include file="localize.jsp" />
Expand All @@ -33,14 +35,66 @@
<!-- Extract the name of the stylesheet-->
<%
String themeName = "wso2is";
String language = "en";
Cookie[] userCookies = request.getCookies();
if (userCookies != null) {
for (Cookie cookie : userCookies) {
if ("ui_lang".equals(cookie.getName())) {
language = cookie.getValue();
break;
}
}
}
String filePath = application.getRealPath("/") + "/WEB-INF/classes/LanguageOptions.properties";
Map<String, String> languageDirectionMap = new HashMap<>();
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
if (!line.trim().startsWith("#") && !line.trim().isEmpty()) {
String[] keyValue = line.split("=");
if (keyValue.length == 2) {
String[] keyParts = keyValue[0].split("\\.");
String languageCode = keyParts[keyParts.length - 1];
String[] valueParts = keyValue[1].split(",");
if (valueParts.length >= 3) {
String direction = valueParts[2].trim();
languageDirectionMap.put(languageCode, direction);
} else {
languageDirectionMap.put(languageCode, "ltr");
}
}
}
}
} catch (Exception e) {
throw e;
}
// Get the selected language's direction
String direction = languageDirectionMap.getOrDefault(language, "ltr");
String themeSuffix = "";
if ("rtl".equals(languageDirectionMap.get(language))) {
themeSuffix = ".rtl";
}
File themeDir = new File(request.getSession().getServletContext().getRealPath("/")
+ "/" + "libs/themes/" + themeName + "/");
String[] fileNames = themeDir.list();
String themeFileName = "";
for(String file: fileNames) {
if(file.endsWith("min.css")) {
for (String file: fileNames) {
if (file.endsWith(themeSuffix + ".min.css")) {
themeFileName = file;
break;
}
}
%>
Expand Down Expand Up @@ -96,3 +150,11 @@
<%
}
%>

<script type="text/javascript">
const direction = "<%= direction %>";
if (direction) {
document.documentElement.setAttribute("dir", direction);
}
</script>
1 change: 1 addition & 0 deletions modules/theme/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"rc-tree": "^4.0.0-beta.2",
"replace": "^1.1.5",
"rimraf": "^3.0.2",
"rtlcss": "^4.3.0",
"semantic-ui-css": "^2.4.1",
"semantic-ui-less": "^2.4.1",
"ts-jest": "^29.1.2",
Expand Down
20 changes: 18 additions & 2 deletions modules/theme/scripts/build.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020, WSO2 LLC. (https://www.wso2.com). All Rights Reserved.
* Copyright (c) 2020, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
Expand All @@ -25,6 +25,7 @@ const fs = require("fs-extra");
const lessToJson = require("less-to-json");
const mergeFiles = require("merge-files");
const replace = require("replace");
const rtlcss = require("rtlcss");
const { Theme } = require("../src/theme");

/**
Expand Down Expand Up @@ -147,6 +148,16 @@ const writeFile = (theme, file, content) => {
log.info(theme + "/" + "theme" + file + " generated.");
};

/**
* Generates RTL CSS files using rtlcss.
*
* @param {string} ltrCss - LTR CSS content.
* @returns {string} RTL-compatible CSS content.
*/
const generateRTLCSS = (ltrCss) => {
return rtlcss.process(ltrCss);
};

/*
* Copy semantic.js files to each theme to make them self contained
*
Expand Down Expand Up @@ -253,10 +264,15 @@ const generateThemes = () => {

return Theme.compile(themeIndexFile, {}).then((output) => {
const minifiedOutput = new CleanCSS().minify(output.css);
const rtlCSS = generateRTLCSS(output.css);
const rtlMinCSS = new CleanCSS().minify(rtlCSS);

const files = {
".css": output.css,
".css.map": output.map,
".min.css": minifiedOutput.styles
".min.css": minifiedOutput.styles,
".rtl.css": rtlCSS,
".rtl.min.css": rtlMinCSS.styles
};

Object.keys(files).map((key) => {
Expand Down
16 changes: 14 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit d72031d

Please sign in to comment.