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
13 changes: 13 additions & 0 deletions wiki/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import frappe
from frappe.translate import get_all_translations


@frappe.whitelist(allow_guest=True)
def get_translations() -> dict[str, str]:
if frappe.session.user != "Guest":
language = frappe.db.get_value("User", frappe.session.user, "language")

if not language:
language = frappe.db.get_single_value("System Settings", "language") or "en"

return get_all_translations(language)
7 changes: 4 additions & 3 deletions wiki/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@


import frappe
from frappe import _


def after_install():
# create the wiki homepage
page = frappe.new_doc("Wiki Page")
page.title = "Home"
page.title = _("Home")
page.route = "wiki/home"
page.content = "Welcome to the homepage of your wiki!"
page.content = _("Welcome to the homepage of your wiki!")
page.published = True
page.insert()

Expand All @@ -22,7 +23,7 @@ def after_install():
# create the wiki sidebar
sidebar = frappe.new_doc("Wiki Group Item")
sidebar.wiki_page = page.name
sidebar.parent_label = "Wiki"
sidebar.parent_label = _("Wiki")
sidebar.parent = space.name
sidebar.parenttype = "Wiki Space"
sidebar.parentfield = "wiki_sidebars"
Expand Down
41 changes: 23 additions & 18 deletions wiki/public/js/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import * as Ace from "ace-builds";
import "ace-builds/src-noconflict/mode-markdown";
import "ace-builds/src-noconflict/theme-tomorrow_night";

const __ = globalThis.__;

const editorContainer = document.getElementById("wiki-editor");
const previewContainer = $("#preview-container");
const previewToggleBtn = $("#toggle-btn");
Expand All @@ -19,7 +21,6 @@ let showPreview = false;

let editor = Ace.edit(editorContainer, {
mode: "ace/mode/markdown",
placeholder: "Write your content here...",
theme: "ace/theme/tomorrow_night",
});

Expand All @@ -42,7 +43,7 @@ $(document).ready(() => {
previewContainer.hide();
previewToggleBtn.on("click", function () {
showPreview = !showPreview;
previewToggleBtn.text(showPreview ? "Edit" : "Preview");
previewToggleBtn.text(showPreview ? __("Edit") : __("Preview"));
if (showPreview) {
previewContainer.show();
$(".wiki-editor-container").hide();
Expand Down Expand Up @@ -70,6 +71,7 @@ function setEditor() {
wrap: true,
showPrintMargin: true,
theme: "ace/theme/tomorrow_night",
placeholder: __("Write your content here..."),
});
editor.renderer.lineHeight = 20;

Expand Down Expand Up @@ -193,7 +195,7 @@ function saveWikiPage(draft = false) {
method: "wiki.wiki.doctype.wiki_page.wiki_page.update",
args: {
name: $('[name="wiki-page-name"]').val(),
message: `${isEmptyEditor ? "Created" : "Edited"} ${title}`,
message: `${isEmptyEditor ? __("Created") : __("Edited")} ${title}`,
content,
new: isEmptyEditor,
new_sidebar_items: isEmptyEditor ? getSidebarItems() : "",
Expand Down Expand Up @@ -276,11 +278,11 @@ function validateAndUploadFiles(files, event) {
);

if (invalidFiles.length > 0) {
const action = event === "paste" ? "paste" : "insert";
const action = event === "paste" ? __("paste") : __("insert");
frappe.show_alert({
message: __(
`You can only {0} images, videos and GIFs in Markdown fields. Invalid file(s): {1}`,
[__(action), invalidFiles.map((f) => f.name).join(", ")],
[action, invalidFiles.map((f) => f.name).join(", ")],
),
indicator: "orange",
});
Expand All @@ -289,7 +291,7 @@ function validateAndUploadFiles(files, event) {

uploadMedia(
["image/*", "video/mp4", "video/quicktime"],
"Insert Media in Markdown",
__("Insert Media in Markdown"),
files,
);
}
Expand All @@ -300,39 +302,42 @@ function insertMarkdown(type) {

switch (type) {
case "bold":
insertion = `**${selection || "bold text"}**`;
insertion = `**${selection || __("bold text")}**`;
break;
case "italic":
insertion = `*${selection || "italic text"}*`;
insertion = `*${selection || __("italic text")}*`;
break;
case "heading":
insertion = `\n# ${selection || "Heading"}`;
insertion = `\n# ${selection || __("Heading")}`;
break;
case "quote":
insertion = `\n> ${selection || "Quote"}`;
insertion = `\n> ${selection || __("Quote")}`;
break;
case "olist":
insertion = `\n1. ${selection || "List item"}`;
insertion = `\n1. ${selection || __("List item")}`;
break;
case "ulist":
insertion = `\n* ${selection || "List item"}`;
insertion = `\n* ${selection || __("List item")}`;
break;
case "link":
insertion = `[${selection || "link text"}](url)`;
insertion = `[${selection || __("link text")}](url)`;
break;
case "image":
uploadMedia(["image/*"], "Insert Image in Markdown");
uploadMedia(["image/*"], __("Insert Image in Markdown"));
break;
case "video":
uploadMedia(["video/mp4", "video/quicktime"], "Insert Video in Markdown");
uploadMedia(
["video/mp4", "video/quicktime"],
__("Insert Video in Markdown"),
);
break;
case "table":
insertion = `${selection}\n| Header 1 | Header 2 |\n| -------- | -------- |\n| Row 1 | Row 1 |\n| Row 2 | Row 2 |`;
insertion = `${selection}\n| ${__("Header 1")} | ${__("Header 2")} |\n| -------- | -------- |\n| ${__("Row 1")} | ${__("Row 1")} |\n| ${__("Row 2")} | ${__("Row 2")} |`;
break;
case "disclosure":
insertion = `\n<details>\n<summary>${
selection || "Title"
}</summary>\nContent\n</details>`;
selection || __("Title")
}</summary>\n${__("Content")}\n</details>`;
break;
}

Expand Down
32 changes: 20 additions & 12 deletions wiki/public/js/render_wiki.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import HtmlDiff from "htmldiff-js";
const __ = globalThis.__;

function setSortable() {
if (window.innerWidth < 768) {
Expand Down Expand Up @@ -153,7 +154,7 @@ window.RenderWiki = class RenderWiki extends Wiki {
}

set_nav_buttons() {
var current_index = -1;
let current_index = -1;
const sidebar_items = $(".sidebar-column").find("a").not(".navbar-brand");

sidebar_items.each(function (index) {
Expand Down Expand Up @@ -236,7 +237,7 @@ window.RenderWiki = class RenderWiki extends Wiki {
indicator: "red",
message: __(`Are you sure you want to <b>discard</b> the changes?`),
primary_action: {
label: "Yes",
label: __("Yes"),
action() {
// clear draft from localstorage
const urlParams = new URLSearchParams(window.location.search);
Expand Down Expand Up @@ -274,7 +275,7 @@ window.RenderWiki = class RenderWiki extends Wiki {
const newSidebarItem = $(`
<li class="sidebar-item sidebar-group-item active" data-type="Wiki Page" data-name="new-wiki-page" data-group-name="${groupName}">
<div>
<a href="#">New Wiki Page</a>
<a href="#">${__("New Wiki Page")}</a>
</div>
</li>
`);
Expand Down Expand Up @@ -321,7 +322,10 @@ window.RenderWiki = class RenderWiki extends Wiki {
add_trash_icon() {
const trashIcon = `<div class="text-muted hide remove-sidebar-item small">
<span class="trash-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-trash"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-trash">
<polyline points="3 6 5 6 21 6"></polyline>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
</svg>
</span>
</div>`;

Expand All @@ -343,10 +347,11 @@ window.RenderWiki = class RenderWiki extends Wiki {
title: __("Delete Wiki Page"),
indicator: "red",
message: __(
`Are you sure you want to <b>delete</b> the Wiki Page <b>${title}</b>?`,
`Are you sure you want to <b>delete</b> the Wiki Page <b>{0}</b>?`,
[title],
),
primary_action: {
label: "Yes",
label: __("Yes"),
action() {
frappe.call({
method:
Expand All @@ -359,7 +364,7 @@ window.RenderWiki = class RenderWiki extends Wiki {
sidebar_item.remove();

frappe.show_alert({
message: `Wiki Page <b>${title}</b> deleted`,
message: __("Wiki Page <b>{0}</b> deleted", [title]),
indicator: "green",
});
dialog.hide();
Expand All @@ -374,12 +379,13 @@ window.RenderWiki = class RenderWiki extends Wiki {
}

set_revisions() {
const initial_content = $(".revision-content").html().trim();
const isEmpty = $(".revision-content").data("empty");
let revisions = [];
let currentRevisionIndex = 1;
let message = __("No Revisions");

// set initial revision
if (initial_content !== "<h3>No Revisions</h3>") {
if (!isEmpty) {
$(".revision-content")[0].innerHTML = HtmlDiff.execute(
$(".revision-content").html(),
$(".from-markdown .wiki-content")
Expand All @@ -389,7 +395,7 @@ window.RenderWiki = class RenderWiki extends Wiki {
$(".previous-revision").removeClass("hide");
} else {
$(".revision-content")[0].innerHTML =
`<div class="no-revision">No Revisions</div>`;
`<div class="no-revision">${message}</div>`;
$(".revision-time").hide();
$(".revisions-modal .modal-header").hide();
}
Expand Down Expand Up @@ -494,7 +500,9 @@ window.RenderWiki = class RenderWiki extends Wiki {
<div class="collapsible">
<span class="text-sm">${title}</span>
<span class='add-sidebar-page'>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-plus"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-plus">
<line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
</span>
<span class='drop-icon hide'>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
Expand Down Expand Up @@ -584,7 +592,7 @@ window.RenderWiki = class RenderWiki extends Wiki {
})
.then((res) => {
let results = res.message.docs || [];
let dropdown_html = `<div style="margin: 0.8rem;text-align: center;">No results found</div>`;
let dropdown_html = `<div style="margin: 0.8rem;text-align: center;">${__("No results found")}</div>`;
if (results.length > 0) {
dropdown_html = results
.map((r) => {
Expand Down
60 changes: 60 additions & 0 deletions wiki/public/js/translation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
export { translate as __ };

globalThis.translatedMessages = globalThis.translatedMessages || null;

let translationsPromise = null;

globalThis.__ = translate;

ensureTranslationsLoaded();

function ensureTranslationsLoaded() {
if (!translationsPromise) {
translationsPromise = fetchTranslations();
}
return translationsPromise;
}

function translate(message, replace = [], context = null) {
const messages = globalThis.translatedMessages;

if (!messages) {
return format(message, replace);
}

let translatedMessage = "";

if (context) {
const contextualKey = `${message}:${context}`;
translatedMessage = messages[contextualKey] || "";
}

if (!translatedMessage) {
translatedMessage = messages[message] || message;
}

return hasPlaceholders(translatedMessage)
? format(translatedMessage, replace)
: translatedMessage;
}

function format(message, replace) {
if (!replace?.length) return message;

return message.replace(/{(\d+)}/g, (match, index) => replace[index] ?? match);
}

function hasPlaceholders(message) {
return /{\d+}/.test(message);
}

async function fetchTranslations() {
try {
const response = await fetch("/api/method/wiki.api.get_translations");
const data = await response.json();
globalThis.translatedMessages = data.message || {};
} catch (error) {
console.error("Failed to load translations", error);
globalThis.translatedMessages = {};
}
}
1 change: 1 addition & 0 deletions wiki/public/js/wiki.bundle.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import "./translation.js";
import "./wiki";
import "./render_wiki";
import "./editor";
4 changes: 3 additions & 1 deletion wiki/public/js/wiki.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const __ = globalThis.__;

function add_link_to_headings() {
$(".from-markdown")
.not(".revision-content")
Expand Down Expand Up @@ -215,7 +217,7 @@ window.Wiki = class Wiki {
const lastUpdatedDate = frappe.datetime.prettyDate(
$(".user-contributions").data("date"),
);
$(".user-contributions").append(`last updated ${lastUpdatedDate}`);
$(".user-contributions").append(`${__("last updated")} ${lastUpdatedDate}`);
}

set_darkmode_button() {
Expand Down
7 changes: 4 additions & 3 deletions wiki/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import json

import frappe
from frappe import _
from frappe.utils import cstr
from redis.commands.search.field import TagField, TextField
from redis.commands.search.query import Query
Expand Down Expand Up @@ -87,9 +88,9 @@ def search(

out = frappe._dict(docs=[], total=result.total, duration=result.duration)
for doc in result.docs:
id = doc.id.split(":", 1)[1]
doc_id = doc.id.split(":", 1)[1]
_doc = frappe._dict(doc.__dict__)
_doc.id = id
_doc.id = doc_id
_doc.payload = json.loads(doc.payload) if doc.payload else None
out.docs.append(_doc)
return out
Expand All @@ -99,7 +100,7 @@ def spellcheck(self, query, **kwargs):

def drop_index(self):
if self.index_exists():
print(f"Dropping index {self.index_name}")
print(_("Dropping index {0}").format(self.index_name))
self.redis.ft(self.index_name).dropindex(delete_documents=True)

def index_exists(self):
Expand Down
Loading