Skip to content
Merged
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
692 changes: 501 additions & 191 deletions css/style.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion data/metadata.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"version": "7.0",
"version": "7.1",
"name": "Pseudo Localization Demo"
}
40 changes: 23 additions & 17 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,9 @@ <h1 id="app-title" class="app-title"></h1>
</svg>
</a>
</div>
<button id="menu-btn" class="menu-btn">
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
</svg>
</button>
<md-icon-button id="menu-btn" class="menu-btn">
<span class="material-icons">menu</span>
</md-icon-button>
</div>

<div id="mobile-menu" class="mobile-menu">
Expand Down Expand Up @@ -165,10 +163,12 @@ <h1 id="app-title" class="app-title"></h1>

<div id="history-modal" class="modal">
<div class="modal-backdrop" id="history-backdrop"></div>
<div class="modal-panel">
<div class="modal-panel" role="dialog" aria-modal="true" aria-labelledby="history-title-text">
<div class="modal-header">
<span id="history-title-text">History</span>
<button class="modal-close" id="close-history-btn"><span class="material-icons">close</span></button>
<span id="history-title-text" class="modal-title">History</span>
<button class="modal-close" id="close-history-btn" aria-label="Close">
<span class="material-icons">close</span>
</button>
</div>
<div class="modal-content" id="history-content">
<p>No processing history yet.</p>
Expand All @@ -181,10 +181,12 @@ <h1 id="app-title" class="app-title"></h1>

<div id="library-modal" class="modal">
<div class="modal-backdrop" id="library-backdrop"></div>
<div class="modal-panel">
<div class="modal-panel" role="dialog" aria-modal="true" aria-labelledby="library-title-text">
<div class="modal-header">
<span id="library-title-text">Character Library</span>
<button class="modal-close" id="close-library-btn"><span class="material-icons">close</span></button>
<span id="library-title-text" class="modal-title">Character Library</span>
<button class="modal-close" id="close-library-btn" aria-label="Close">
<span class="material-icons">close</span>
</button>
</div>
<div class="modal-content" id="library-content">
<p>Loading...</p>
Expand All @@ -194,21 +196,25 @@ <h1 id="app-title" class="app-title"></h1>

<div id="about-modal" class="modal">
<div class="modal-backdrop" id="about-backdrop"></div>
<div class="modal-panel">
<div class="modal-panel" role="dialog" aria-modal="true" aria-labelledby="about-title-text">
<div class="modal-header">
<span id="about-title-text">About</span>
<button class="modal-close" id="close-about-btn"><span class="material-icons">close</span></button>
<span id="about-title-text" class="modal-title">About</span>
<button class="modal-close" id="close-about-btn" aria-label="Close">
<span class="material-icons">close</span>
</button>
</div>
<div class="modal-content" id="about-content"></div>
</div>
</div>

<div id="lang-modal" class="modal">
<div class="modal-backdrop" id="lang-backdrop"></div>
<div class="modal-panel lang-modal-panel">
<div class="modal-panel lang-modal-panel" role="dialog" aria-modal="true" aria-labelledby="lang-modal-title">
<div class="modal-header">
<span id="lang-modal-title">Language</span>
<button class="modal-close" id="close-lang-btn"><span class="material-icons">close</span></button>
<span id="lang-modal-title" class="modal-title">Language</span>
<button class="modal-close" id="close-lang-btn" aria-label="Close">
<span class="material-icons">close</span>
</button>
</div>
<div class="modal-content" id="lang-list"></div>
</div>
Expand Down
19 changes: 16 additions & 3 deletions src/app.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { state, loadHistory, loadMode, loadSession } from "./state.js";
import { loadI18n, applyLanguage, getSavedLang } from "./i18n.js";
import { initEvents, updateOptionVisibility } from "./events.js";
import { $ } from "./dom.js";
import { initEvents, updateOptionVisibility } from "./events.js";
import { applyLanguage, getSavedLang, loadI18n } from "./i18n.js";
import { loadHistory, loadMode, loadSession, state } from "./state.js";

async function initializeApp() {
await loadI18n();
Expand All @@ -19,6 +19,19 @@ async function initializeApp() {
applyLanguage();
initEvents();
updateOptionVisibility();

const topbar = $("app-title")?.closest(".topbar");
if (topbar) {
const updateTopbarShadow = () => {
if (window.scrollY > 0) {
topbar.classList.add("scrolled");
} else {
topbar.classList.remove("scrolled");
}
};
window.addEventListener("scroll", updateTopbarShadow, { passive: true });
updateTopbarShadow();
}
}

initializeApp();
13 changes: 12 additions & 1 deletion src/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ export function showToast(message) {
}
}

const closingModal = {};

export function showModal(id) {
const modal = $(id);
if (modal) {
if (modal && !closingModal[id]) {
modal.classList.remove("hiding");
modal.style.display = "";
modal.classList.add("show");
Expand All @@ -26,12 +28,21 @@ export function showModal(id) {

export function hideModal(id) {
const modal = $(id);
const modalPanel = modal?.querySelector(".modal-panel");
if (modal) {
closingModal[id] = true;
modal.classList.add("hiding");
modal.classList.remove("show");
if (modalPanel) {
modalPanel.style.animation = "dialog-exit 0.2s cubic-bezier(0.2, 0, 0, 1) forwards";
}
setTimeout(() => {
modal.classList.remove("hiding");
modal.style.display = "none";
if (modalPanel) {
modalPanel.style.animation = "";
}
closingModal[id] = false;
}, 250);
}
}
78 changes: 36 additions & 42 deletions src/events.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import { processText } from "./processor.js";
import { state, saveHistory, clearHistoryStorage, saveMode, saveSession } from "./state.js";
import { $, escapeHtml, hideModal, showModal, showToast } from "./dom.js";
import { setLang, t } from "./i18n.js";
import { $, escapeHtml, showToast, showModal, hideModal } from "./dom.js";

export async function loadCharLib() {
if (!state.charLib) {
const basePath = window.BASE_PATH || './';
state.charLib = await (await fetch(`${basePath}data/character.json`)).json();
}
return state.charLib;
import { processText } from "./processor.js";
import { clearHistoryStorage, loadCharLib, saveHistory, saveMode, saveSession, state } from "./state.js";

function renderLibraryModal() {
loadCharLib().then(lib => {
const txt = t();
let html = '<table class="library-table"><thead><tr>';
html += `<th>${txt.libraryCharacter}</th><th>${txt.libraryVariants}</th><th>${txt.libraryCount}</th></tr></thead><tbody>`;

for (const [char, variants] of Object.entries(lib)) {
if (Array.isArray(variants)) {
html += `<tr><td><strong>${escapeHtml(char)}</strong></td><td class="library-variants">${escapeHtml(variants.join(' '))}</td><td>${variants.length}</td></tr>`;
}
}
html += `</tbody></table><p class="library-total">${txt.libraryTotal.replace('{count}', Object.keys(lib).length)}</p>`;

$("library-content").innerHTML = html;
showModal("library-modal");
});
}

export function updateOptionVisibility() {
Expand Down Expand Up @@ -40,16 +50,26 @@ export function initEvents() {
updateMode();

$("menu-btn")?.addEventListener("click", () => {
$("mobile-menu")?.classList.toggle("show");
const menu = $("mobile-menu");
const menuBtn = $("menu-btn");
const iconSpan = menuBtn?.querySelector("span.material-icons") || menuBtn?.querySelector("md-icon");
const isShown = menu?.classList.toggle("show");
if (iconSpan) {
iconSpan.textContent = isShown ? "close" : "menu";
}
});

document.addEventListener("click", (e) => {
const menu = $("mobile-menu");
const btn = $("menu-btn");
const menuBtn = $("menu-btn");
const iconSpan = menuBtn?.querySelector("span.material-icons") || menuBtn?.querySelector("md-icon");
if (menu?.classList.contains("show") &&
!menu.contains(e.target) &&
!btn?.contains(e.target)) {
!menuBtn?.contains(e.target)) {
menu.classList.remove("show");
if (iconSpan) {
iconSpan.textContent = "menu";
}
}
});

Expand Down Expand Up @@ -142,39 +162,13 @@ export function initEvents() {
$("history-content").innerHTML = `<p>${t().historyCleared}</p>`;
});

$("library-btn-top")?.addEventListener("click", async () => {
const lib = await loadCharLib();
const txt = t();
let html = '<table class="library-table"><thead><tr>';
html += `<th>${txt.libraryCharacter}</th><th>${txt.libraryVariants}</th><th>${txt.libraryCount}</th></tr></thead><tbody>`;

for (const [char, variants] of Object.entries(lib)) {
if (Array.isArray(variants)) {
html += `<tr><td><strong>${escapeHtml(char)}</strong></td><td class="library-variants">${escapeHtml(variants.join(' '))}</td><td>${variants.length}</td></tr>`;
}
}
html += `</tbody></table><p class="library-total">${txt.libraryTotal.replace('{count}', Object.keys(lib).length)}</p>`;

$("library-content").innerHTML = html;
showModal("library-modal");
});
$("library-btn-top")?.addEventListener("click", () => renderLibraryModal());

$("about-btn")?.addEventListener("click", () => showModal("about-modal"));

$("library-btn-mobile")?.addEventListener("click", async () => {
$("library-btn-mobile")?.addEventListener("click", () => {
$("mobile-menu")?.classList.remove("show");
const lib = await loadCharLib();
const txt = t();
let html = '<table class="library-table"><thead><tr>';
html += `<th>${txt.libraryCharacter}</th><th>${txt.libraryVariants}</th><th>${txt.libraryCount}</th></tr></thead><tbody>`;
for (const [char, variants] of Object.entries(lib)) {
if (Array.isArray(variants)) {
html += `<tr><td><strong>${escapeHtml(char)}</strong></td><td class="library-variants">${escapeHtml(variants.join(' '))}</td><td>${variants.length}</td></tr>`;
}
}
html += `</tbody></table><p class="library-total">${txt.libraryTotal.replace('{count}', Object.keys(lib).length)}</p>`;
$("library-content").innerHTML = html;
showModal("library-modal");
renderLibraryModal();
});

$("about-btn-mobile")?.addEventListener("click", () => {
Expand Down
24 changes: 8 additions & 16 deletions src/processor.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
let charLib = null;

async function loadCharLib() {
if (!charLib) {
const basePath = window.BASE_PATH || './';
const response = await fetch(`${basePath}data/character.json`);
charLib = await response.json();
}
}
import { loadCharLib } from "./state.js";

export async function processText(text, options = {}) {
await loadCharLib();
const charLib = await loadCharLib();

if (!text) return "";

Expand Down Expand Up @@ -83,25 +75,25 @@ export async function processText(text, options = {}) {

// Add suffix
const suffixMode = options.suffix || "0";
result = addSuffix(result, oriLength, suffixMode, options);
result = addSuffix(result, oriLength, suffixMode, options, charLib);

// Add hash ID
if (options.addHash) {
const hashLength = options.hashLength || 6;
result = addHashID(result, hashLength);
result = addHashID(result, hashLength, charLib);
}

return result;
}

function addSuffix(result, oriLength, mode, options) {
function addSuffix(result, oriLength, mode, options, charLib) {
switch (mode) {
case "0": // None
return result;
case "1": // Microsoft style
return suffixMS(result, oriLength);
case "2": // Android style
return suffixAndroid(result, oriLength);
return suffixAndroid(result, oriLength, charLib);
case "3": // Numeric
return suffixNum(result, oriLength);
case "4": // Custom
Expand All @@ -123,7 +115,7 @@ function suffixMS(result, oriLength) {
return `[${result} ${suf}]`;
}

function suffixAndroid(result, oriLength) {
function suffixAndroid(result, oriLength, charLib) {
let suf = "";
const n = Math.floor(oriLength / 7);
const numbers = charLib.enNumber || ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"];
Expand Down Expand Up @@ -166,7 +158,7 @@ function suffixCustom(result, oriLength, options) {
return `${prefix + result} ${suf}${suffix}`;
}

function addHashID(result, length = 6) {
function addHashID(result, length = 6, charLib) {
let hash = "";
const alphabet = charLib.alphabet || "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
for (let i = 0; i < length; i++) {
Expand Down
10 changes: 10 additions & 0 deletions src/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ export function getState() {
return state;
}

export async function loadCharLib() {
if (state.charLib) {
return state.charLib;
}
const basePath = window.BASE_PATH || './';
const response = await fetch(`${basePath}data/character.json`);
state.charLib = await response.json();
return state.charLib;
}

const HISTORY_KEY = "pseudo-history";
const MODE_KEY = "pseudo-mode";
const SESSION_INPUT_KEY = "pseudo-session-input";
Expand Down