') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Add support for translating command description and usage by pop4959 · Pull Request #3284 · EssentialsX/Essentials · GitHub
Skip to content

Add support for translating command description and usage - #3284

Merged
pop4959 merged 5 commits into
EssentialsX:2.xfrom
pop4959:tl-command
Jun 16, 2020
Merged

Add support for translating command description and usage#3284
pop4959 merged 5 commits into
EssentialsX:2.xfrom
pop4959:tl-command

Conversation

@pop4959

@pop4959pop4959 commented May 15, 2020

Copy link
Copy Markdown
Member

As the title implies, this PR allows translators and server owners to change the command description and usage messages. This avoids the need for people to maintain their own versions of Essentials with custom plugin.yml files. The implementation here uses empty values to indicate that default description or usage from plugin.yml should be used.

Example usage

config.yml

locale: de

messages_de.properties

banCommandDescription=Verbietet einen Spieler.
banCommandUsage=/verbot <spieler> [grund]

commands.yml

aliases:
verbot: ban $1-

197262f830561fecebc872a882a1c961

65557841cc919d66c48a3b5fe6433ec2

Closes#3376

@pop4959pop4959 added the type: enhancement Features and feature requests. label May 15, 2020
@mdcfe

Copy link
Copy Markdown
Member

I think the description and usage messages should be included in messages.properties by default. Otherwise, Crowdin translators won't have any reference for translating these messages (unless they go and dig through the plugin.yml themselves).

@pop4959

Copy link
Copy Markdown
MemberAuthor

I thought about that, but I also thought it would be valuable to not have the English version duplicated potentially 3 times (one in plugin.yml, one in messages.properties, and one in messages_en.properties) when we could avoid duplicating it by letting plugin.yml take control of it. I guess it's probably an inevitability though. Also, it was actually the easiest way to implement this feature (not having to copy all of the messages).

On second thought though, how about we just remove all of the messages from plugin.yml? Then, we have them consolidated solely in the message files. If I can get a thumbs up for this idea I'll go ahead and do that (this will probably be a bit of somewhat manual work).

@mbax

mbax commented May 26, 2020

Copy link
Copy Markdown
Member

plugin.yml should always be as descriptive as possible. If message duplication is a concern, add the messages to plugin.yml and messages.properties during building, pulling from messages_en.properties! :)

@pop4959

Copy link
Copy Markdown
MemberAuthor

Pulling from messages_en.properties is not an option, as that file is exposed to translators, who should not be able to directly change, for example, the syntax of commands.

plugin.yml currently houses the description and usage strings for commands, not exposed to translation.
messages.properties contains defaults.
message_<lang>.properties contains translations.

The only concern is with having to manually sync description/usage strings between plugin.yml and messages.properties. It'd probably be better to consolidate them in one place, and in which case, I'd argue it's better put in messages.properties since these are not touched by translators.

@mbax

mbax commented May 31, 2020

Copy link
Copy Markdown
Member

as that file is exposed to translators, who should not be able to directly change, for example, the syntax of commands

I don't see any syntax in messages_en?

@mdcfe

mdcfe commented May 31, 2020

Copy link
Copy Markdown
Member

@mbaxmessages_<locale>.properties files are automatically generated by Crowdin and only contains messages that have been translated on Crowdin and approved. en only exists for legacy reasons (haven't gotten around to removing it) but it's intended to be identical to messages.properties once Crowdin has updated it.

edit:

It'd probably be better to consolidate them in one place, and in which case, I'd argue it's better put in messages.properties since these are not touched by translators.

plugin.yml also isn't touched by translators. The main reason for keeping them in both is that third-party plugins might attempt to read command usages/descriptions directly from the plugin.yml or PluginDescriptionFile instead of from the command map.

@pop4959pop4959 mentioned this pull request Jun 13, 2020
@pop4959
pop4959 requested a review from mdcfeJune 15, 2020 07:16
mdcfe
mdcfe previously approved these changes Jun 15, 2020
@pop4959
pop4959 requested a review from mbaxJune 15, 2020 23:21
@pop4959
pop4959 merged commit d64f73e into EssentialsX:2.xJun 16, 2020
JRoy pushed a commit to JRoy/Essentials-PR that referenced this pull request Aug 30, 2020
ressidell pushed a commit to ressidell/Essentials that referenced this pull request Apr 27, 2025
rewsdjad1 pushed a commit to rewsdjad1/EssentialsXPlus that referenced this pull request Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: enhancementFeatures and feature requests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Helpop translate

4 participants

@pop4959@mdcfe@mbax@JRoy