Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
287 changes: 145 additions & 142 deletions PSFree.manifest

Large diffs are not rendered by default.

25 changes: 19 additions & 6 deletions includes/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -608,19 +608,32 @@ function loadGoldHENVer(){


function loadLanguage() {
document.querySelector(`input[name="language"][value="${user.currentLanguage}"]`).checked = true;
const radio = document.querySelector(
`input[name="language"][value="${user.currentLanguage}"]`
);

// prevent crash if radios aren't created yet
if (radio) {
radio.checked = true;
}

const langScript = document.getElementById("langScript");
if(langScript) langScript.remove();
if (langScript) langScript.remove();

// load language file
return new Promise((resolve, reject) => {
const script = document.createElement('script');
const script = document.createElement("script");
script.src = `./includes/js/languages/${user.currentLanguage}.js`;
script.onload = () => resolve(window.lang);
script.id = "langScript";
script.onerror = () => reject(new Error(`Failed to load ${user.currentLanguage}`));

script.onload = () => resolve(window.lang);
script.onerror = () =>
reject(new Error(`Failed to load ${user.currentLanguage}`));

document.head.appendChild(script);
});
}

// Apply lanuage after loading the language file
async function initLanguage() {
try {
Expand Down Expand Up @@ -668,7 +681,7 @@ function applyLanguage(lang) {

// Document Properties
document.title = strings.title || "PSFree Enhanced";
document.dir = (user.currentLanguage === 'ar') ? 'rtl' : 'ltr';
document.dir = window.lang?.type === "rtl" ? "rtl" : "ltr";
document.lang = user.currentLanguage;


Expand Down
133 changes: 133 additions & 0 deletions includes/js/langhandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Define the available language files
const languageFiles = ["en", "fa", "ar", "ru"];

var user = user || {};
user.currentLanguage = sessionStorage.getItem("language") || "en";

async function initLanguageUI() {
const list = document.getElementById("language-list");
if (!list) return;

list.innerHTML = ""; // clear

for (const code of languageFiles) {
try {
const lang = await loadLanguageFile(code);

// create radio
const label = document.createElement("label");
label.className = "flex items-center text-white/80";

const input = document.createElement("input");
input.type = "radio";
input.name = "language";
input.value = code;
input.className = "ml-2";
input.checked = (code === user.currentLanguage);

input.onchange = () => setLanguage(code);

const p = document.createElement("p");
p.textContent = lang.langname;

label.appendChild(input);
label.appendChild(p);

list.appendChild(label);
} catch (e) {
console.warn("Failed to load language", code, e);
}
}

// Add reload notice button
let reloadBtn = document.getElementById("reload-language-btn");
if (!reloadBtn) {
reloadBtn = document.createElement("button");
reloadBtn.id = "reload-language-btn";
reloadBtn.className = "mt-4 flex items-center justify-center w-10 h-10 bg-blue-600 rounded hover:bg-blue-700";
reloadBtn.title = "Changing languages requires a reload";

reloadBtn.innerHTML = `
<svg width="24" height="24" viewBox="0 0 60.369 60.369" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-446.571 -211.615)">
<path d="M504.547,265.443h-9.019a30.964,30.964,0,0,0-29.042-52.733,1.5,1.5,0,1,0,.792,2.894,27.955,27.955,0,0,1,25.512,48.253l0-10.169h-.011a1.493,1.493,0,0,0-2.985,0h0v13.255a1.5,1.5,0,0,0,1.5,1.5h13.256a1.5,1.5,0,1,0,0-3Z" fill="#fff"/>
<path d="M485.389,267.995a27.956,27.956,0,0,1-25.561-48.213l0,10.2h.015a1.491,1.491,0,0,0,2.978,0h.007V216.791a1.484,1.484,0,0,0-1.189-1.532l-.018-.005a1.533,1.533,0,0,0-.223-.022c-.024,0-.046-.007-.07-.007H448.071a1.5,1.5,0,0,0,0,3h8.995a30.963,30.963,0,0,0,29.115,52.664,1.5,1.5,0,0,0-.792-2.894Z" fill="#fff"/>
</g>
</svg>
`;

reloadBtn.onclick = () => location.reload();
list.parentNode.appendChild(reloadBtn);
}
}

// Load a language JS file dynamically
function loadLanguageFile(code) {
return new Promise((resolve, reject) => {
const existing = document.getElementById("langScript_" + code);
if (existing) {
resolve(window.lang);
return;
}

const script = document.createElement("script");
script.src = `./includes/js/languages/${code}.js`;
script.id = "langScript_" + code;

script.onload = () => resolve(window.lang);
script.onerror = () => reject(new Error("Failed to load " + code));

document.head.appendChild(script);
});
}

function setLanguage(code) {
user.currentLanguage = code;
sessionStorage.setItem("language", code);

// remove old lang script
document.querySelectorAll('script[id^="langScript_"]').forEach(s => s.remove());

// load new language
loadLanguageFile(code).then(lang => {
window.lang = lang;
updateTextWithLang();

// start pulsing reload button
const reloadBtn = document.getElementById("reload-language-btn");
if (reloadBtn) reloadBtn.classList.add("pulse-lang-infinite");
});
}

// Optional: update UI texts after switching language
function updateTextWithLang() {
if (!window.lang) return;
const clickText = document.getElementById("click-to-start-text");
if (clickText) clickText.textContent = window.lang.clickToStart || clickText.textContent;

const payloadsTitle = document.getElementById("payloads-section-title");
if (payloadsTitle) payloadsTitle.textContent = window.lang.payloadsHeader || payloadsTitle.textContent;

const ghVerTitle = document.getElementById("ghVer");
if (ghVerTitle) ghVerTitle.textContent = window.lang.ghVer || ghVerTitle.textContent;

const languageHeader = document.querySelector("#chooselang h3");
if (languageHeader) languageHeader.textContent = window.lang.languageHeader || languageHeader.textContent;
}

// Init after DOM ready
document.addEventListener("DOMContentLoaded", initLanguageUI);

// Add CSS for pulsing animation
const style = document.createElement("style");
style.textContent = `
@keyframes pulseLangInfinite {
0% { background-color: #facc15; } /* yellow */
50% { background-color: #3b82f6; } /* blue */
100% { background-color: #facc15; }
}
.pulse-lang-infinite {
animation: pulseLangInfinite 1.5s ease-in-out infinite;
}
`;
document.head.appendChild(style);
97 changes: 97 additions & 0 deletions includes/js/languages/! sample.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Template language file for PSFree Enhanced
// Copy this file, rename it (e.g., fr.js), and fill in translations
// DO NOT remove or rename keys, only replace empty strings
// (Check en.js for Examples.)
window.lang = {
// Language display name (shows in the language selector)
"langname": "",
"type": "", // Specify RTL if its read from RIGHT TO LEFT or LTR if its read from LEFT TO RIGHT
// Main UI title
"title": "",

// PS4 firmware compatibility messages
"ps4FwCompatible": "", // Message for compatible firmware, {ps4fw} is replaced dynamically
"ps4FwIncompatible": "", // Message for incompatible firmware, {ps4fw} is replaced dynamically
"notPs4": "", // Message when the page is loaded on a non-PS4 device
"clickToStart": "", // Text for the main "Click to start" button
"chooseHEN": "", // Header for HEN selection section
"exploitStatusHeader": "", // Header for the exploit console panel
"payloadsHeader": "", // Header for the payloads panel
"settingsBtnTitle": "", // Tooltip for settings button
"aboutMenu": "", // Text for the About button
"payloadsToolsHeader": "", // Tab title: Tools
"payloadsGameHeader": "", // Tab title: Games
"payloadsLinuxHeader": "", // Tab title: Linux

// About popup
"aboutPsfreeHeader": "", // About popup header
"aboutVersion": "", // Version text in About popup
"aboutDescription": "", // Description text in About popup
"closeButton": "", // "Close" button text

// Settings popup
"settingsPsfreeHeader": "", // Settings popup header
"ps4FirmwareSupportedHeader": "", // "Supported PS4 firmware" text
"languageHeader": "", // Language section header

// Warning messages (nested object)
"warnings": {
"note1": "", // "Make sure to delete cache data before running exploit..."
"note2": "", // "Allow caching process to complete..."
"note3": "" // "It might take multiple tries to achieve the jailbreak"
},

"secondHostBtn": "", // External payload link button text
"alert": "", // Heading for important notice box
"waitingUserInput": "", // Text when waiting for user input
"cache": "", // Cache installation progress text
"httpsHost": "", // Message for HTTPS fallback host scenario
"ghVer": "", // GoldHEN Versions header in settings
"otherVer": "", // Text for "Other versions" dropdown
"latestVer": "", // Text for "Latest" label

// Fan section
"fanTitle": "", // "Fan Threshold" title
"fanDescription": "", // Description under fan threshold
"selectTemp": "", // Header for temperature selection
"default": "", // Text label for default fan temperature

// GoldHEN support message
"goldhenFirmwareSemiSupported": "",

// Southbridge & Model section
"southbridgeHeader": "", // Header for Southbridge & Model section
"southbridgeHelp": "", // "Need help?" text
"southbridgeHelp1": "", // Instruction: How to find Southbridge on PS4
"southbridgeHelp2": "", // Warning about wrong selection causing kernel panic
"selectSouthbridge": "", // Text for button: "Select your Southbridge and PS4 Model"

// Payloads-specific messages
"linuxOnlyWithGoldHEN": "", // Warning that Linux payloads require GoldHEN
"busyBinLoader": "", // Error: BinLoader server is busy
"binLoaderNotDetected": "", // Error: BinLoader not running or detected
"disabledBinloader": "", // Message suggesting fallback if BinLoader is off
"unsupportedFirmware": "" // Error when firmware is unsupported
};

/*
GUIDE FOR CREATING A LANGUAGE FILE:

1. Copy this file into /includes/js/languages/
e.g., fr.js, de.js, tr.js

2. Set "langname" to the native name of the language
e.g., "Français", "Deutsch", "Türkçe"

3. Fill in translations for each key.
- Do NOT remove or rename keys.
- Leave nested objects intact (like "warnings").

4. Preserve placeholders:
- {ps4fw}, {user}, etc., must remain exactly as-is.

5. Add the filename to the languages array in langhandler.js:
const languageFiles = ["en", "ar", "ru", "fr"];

6. Reload the page and select the language to test.
*/
2 changes: 2 additions & 0 deletions includes/js/languages/ar.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
window.lang = {
"langname": "العربية",
"type": "rtl",
"title": "PSFree محسن",
"ps4FwCompatible": `بلايستايشن 4 إصدار {ps4fw} | مدعوم`,
"ps4FwIncompatible": `بلايستايشن 4 إصدار {ps4fw} | غير مدعوم`,
Expand Down
2 changes: 2 additions & 0 deletions includes/js/languages/en.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
window.lang = {
"langname": "English",
"type": "ltr",
"title": "PSFree Enhanced",
"ps4FwCompatible": `PS4 FW: {ps4fw} | Compatible`,
"ps4FwIncompatible": `PS4 FW: {ps4fw} | Incompatible`,
Expand Down
52 changes: 52 additions & 0 deletions includes/js/languages/fa.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
window.lang = {
"langname": "فارسی",
"type": "rtl",
"title": "PSFree پیشرفته",
"ps4FwCompatible": `PS4 FW: {ps4fw} | سازگار`,
"ps4FwIncompatible": `PS4 FW: {ps4fw} | ناسازگار`,
"notPs4": "شما روی PS4 نیستید، پلتفرم: ",
"clickToStart": "برای شروع کلیک کنید",
"chooseHEN": "نسخه HEN خود را انتخاب کنید",
"exploitStatusHeader": "وضعیت اکسپلویت",
"payloadsHeader": "Payloadها",
"settingsBtnTitle": "تنظیمات",
"aboutMenu": "درباره",
"payloadsToolsHeader": "ابزارها",
"payloadsGameHeader": "بازی",
"payloadsLinuxHeader": "لینوکس",
"aboutPsfreeHeader": "درباره PSFree پیشرفته",
"aboutVersion": "نسخه: 1.5.1",
"aboutDescription": "یک رابط وب برای جلبریک PS4 با استفاده از PSFree همراه با اکسپلویت کرنل Lapse.",
"closeButton": "بستن",
"settingsPsfreeHeader": "تنظیمات",
"ps4FirmwareSupportedHeader": "فریمورهای پشتیبانی شده PS4",
"languageHeader": "زبان",
"warnings": {
"note1": "قبل از اجرای اکسپلویت برای اولین بار، حتماً داده‌های کش را پاک کنید",
"note2": "برای پایداری بهتر اجازه دهید فرآیند کش کامل شود",
"note3": "ممکن است چندین بار تلاش کنید تا جلبریک موفق شود"
},
"secondHostBtn": "بارگذاری Payloadها با استفاده از BinLoader GoldHEN - لینک خارجی",
"alert": "اطلاعیه مهم",
"waitingUserInput": "در انتظار اقدام کاربر",
"cache": "در حال نصب کش: ",
"httpsHost": "بارگذاری Payloadها از طریق BinLoader GoldHEN در حال حاضر امکان‌پذیر نیست، روی دکمه آبی زیر کلیک کنید تا از هاست پشتیبانی شده استفاده شود.",
"ghVer": "نسخه‌های GoldHEN",
"otherVer": "نسخه‌های دیگر",
"latestVer": "جدیدترین",
"fanTitle": "آستانه فن",
"fanDescription": "تنظیم دمایی که فن در حالت توربو فعال شود",
"selectTemp": "انتخاب دما",
"default": "پیش‌فرض",
"goldhenFirmwareSemiSupported": "* بارگذاری Payloadها از طریق BinLoader GoldHEN روی تمام فریمورها پشتیبانی می‌شود.",
"southbridgeHeader": "Southbridge و مدل",
"southbridgeHelp": "نیاز به راهنما؟",
"southbridgeHelp1": "GoldHEN را بارگذاری کنید و به تنظیمات -> سیستم -> اطلاعات سیستم بروید و Southbridge را بررسی کنید.",
"southbridgeHelp2": "انتخاب گزینه اشتباه ممکن است باعث کرنل پنیک شود!",
"selectSouthbridge": "Southbridge و مدل PS4 خود را انتخاب کنید",
"linuxOnlyWithGoldHEN": "Payloadهای لینوکس فقط باید از طریق BinLoader GoldHEN بارگذاری شوند!",
"busyBinLoader": "بارگذاری Payload امکان‌پذیر نیست، زیرا سرور BinLoader شلوغ است",
"binLoaderNotDetected": "BinLoader GoldHEN شناسایی نشد، آیا فعال است؟!",
"disabledBinloader": "BinLoader GoldHEN در حال اجرا نیست، آیا می‌خواهید Payload را با استفاده از اکسپلویت بارگذاری کنید؟",
"unsupportedFirmware": "فریمور پشتیبانی نشده "
};
2 changes: 2 additions & 0 deletions includes/js/languages/ru.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
window.lang = {
"langname": "Русский",
"type": "ltr",
"title": "PSFree Enhanced",
"ps4FwCompatible": `Версия ПО: {ps4fw} | Совместима`,
"ps4FwIncompatible": `ерсия ПО: {ps4fw} | Несовместима`,
Expand Down
23 changes: 6 additions & 17 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -233,23 +233,10 @@ <h3 class="text-l font-bold mb-4">Supported PS4 firmware</h3>
<h2 class="text-2xl font-bold mb-4">PSFree settings</h2>
<!-- Language chooser -->
<div id="chooselang" class="mb-6">
<h3 class="text-lg font-semibold mb-2">Language</h3>
<div class="radio-group space-y-2">
<!-- Language options -->
<label class="flex items-center text-white/80">
<input type="radio" name="language" value="en" class="ml-2" onchange="saveLanguage(this.value);">
<p>English</p>
</label>
<label class="flex items-center text-white/80">
<input type="radio" name="language" value="ar" class="ml-2" onchange="saveLanguage(this.value); ">
<p>العربية</p>
</label>
<label class="flex items-center text-white/80">
<input type="radio" name="language" value="ru" class="ml-2" onchange="saveLanguage(this.value); ">
<p>Русский</p>
</label>
</div>
</div>
<h3 class="text-lg font-semibold mb-2">Language</h3>
<div id="language-list" class="radio-group space-y-2"></div>
</div>

<!-- Choose GoldHEN version -->
<div id="chooseGoldHEN" class="mb-6">
<h3 class="text-lg font-semibold mb-2" id="ghVer">GoldHEN versions</h3>
Expand Down Expand Up @@ -378,6 +365,8 @@ <h3 class="text-l font-bold mb-4">Select a temperature</h3>
</script>
<!-- Main JS file -->
<script src='./includes/js/index.js'></script>
<!-- If you're planning to make a translation, edit this file. -->
<script src='./includes/js/langhandler.js'></script>
</body>

</html>