initial working draft
This commit is contained in:
commit
fc99d36c55
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/.idea/
|
||||
/.vscode/
|
||||
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Jiahao Lee
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
49
assets/css/style.css
Normal file
49
assets/css/style.css
Normal file
@ -0,0 +1,49 @@
|
||||
.d-l10n-translate-template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.d-l10n-translated {
|
||||
text-decoration: underline dotted;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.d-l10n-translated-entity-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.d-l10n-translated-entity-name-cjk {
|
||||
text-decoration: underline solid;
|
||||
}
|
||||
|
||||
.d-l10n-translated-manuscript-name-cjk:before {
|
||||
content: '《';
|
||||
}
|
||||
|
||||
.d-l10n-translated-manuscript-name-cjk:after {
|
||||
content: '》';
|
||||
}
|
||||
|
||||
.d-l10n-translated-manuscript-name {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.d-l10n-translated-original:before {
|
||||
content: ' '
|
||||
}
|
||||
|
||||
.d-l10n-translated-original:after {
|
||||
content: ' '
|
||||
}
|
||||
|
||||
.d-l10n-color-box {
|
||||
display: inline-block;
|
||||
width: 0.6rem;
|
||||
height: 0.6rem;
|
||||
border: 1px solid #000;
|
||||
margin-left: 0.2rem;
|
||||
margin-right: -0.2rem;
|
||||
}
|
||||
|
||||
.d-l10n-color {
|
||||
display: inline-block;
|
||||
}
|
182
assets/js/work.js
Normal file
182
assets/js/work.js
Normal file
@ -0,0 +1,182 @@
|
||||
function isCjkContext(str) {
|
||||
// since js regex does not support 5-digit hex unicode
|
||||
// we will have to check for the range ourselves
|
||||
|
||||
// CJK Unified Ideographs
|
||||
const cjkRange1 = [0x4E00, 0x9FFF]
|
||||
// CJK Unified Ideographs Extension A
|
||||
const cjkRange2 = [0x3400, 0x4DBF]
|
||||
// CJK Unified Ideographs Extension B
|
||||
const cjkRange3 = [0x20000, 0x2A6DF]
|
||||
// CJK Unified Ideographs Extension C
|
||||
const cjkRange4 = [0x2A700, 0x2B73F]
|
||||
// CJK Unified Ideographs Extension D
|
||||
const cjkRange5 = [0x2B740, 0x2B81F]
|
||||
// CJK Unified Ideographs Extension E
|
||||
const cjkRange6 = [0x2B820, 0x2CEAF]
|
||||
// CJK Unified Ideographs Extension F
|
||||
const cjkRange7 = [0x2CEB0, 0x2EBEF]
|
||||
// CJK Unified Ideographs Extension G
|
||||
const cjkRange8 = [0x30000, 0x3134F]
|
||||
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const char = str.charCodeAt(i)
|
||||
if ((char >= cjkRange1[0] && char <= cjkRange1[1]) ||
|
||||
(char >= cjkRange2[0] && char <= cjkRange2[1]) ||
|
||||
(char >= cjkRange3[0] && char <= cjkRange3[1]) ||
|
||||
(char >= cjkRange4[0] && char <= cjkRange4[1]) ||
|
||||
(char >= cjkRange5[0] && char <= cjkRange5[1]) ||
|
||||
(char >= cjkRange6[0] && char <= cjkRange6[1]) ||
|
||||
(char >= cjkRange7[0] && char <= cjkRange7[1]) ||
|
||||
(char >= cjkRange8[0] && char <= cjkRange8[1])) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function processColorBoxes() {
|
||||
const allColorBoxes = document.querySelectorAll(".d-l10n-color");
|
||||
|
||||
const knownColors = {}
|
||||
|
||||
// first pass: collect all colors
|
||||
allColorBoxes.forEach((colorBox) => {
|
||||
const key = colorBox.innerHTML
|
||||
const value = colorBox.getAttribute('data-color')
|
||||
if (value == null || value.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
knownColors[key] = value
|
||||
});
|
||||
|
||||
// second pass: add a little box with the color
|
||||
allColorBoxes.forEach((colorBox) => {
|
||||
const key = colorBox.innerHTML
|
||||
const value = knownColors[key]
|
||||
if (value == null || value.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
colorBox.innerHTML = `<span class="d-l10n-color-box" style="background-color: ${value}" title="${value}"></span> ${key}`
|
||||
});
|
||||
}
|
||||
|
||||
function getPreferredTranslation() {
|
||||
if (localStorage) {
|
||||
return localStorage.getItem("d-l10n-preferred-translation") || 'writer'
|
||||
}
|
||||
|
||||
return 'writer';
|
||||
}
|
||||
|
||||
function changePreferredTranslation(mode) {
|
||||
// can be 'writer', 'canonical', 'original'
|
||||
if (mode !== 'writer' && mode !== 'canonical' && mode !== 'original') {
|
||||
mode = 'writer'
|
||||
}
|
||||
|
||||
if (localStorage) {
|
||||
localStorage.setItem("d-l10n-preferred-translation", mode)
|
||||
}
|
||||
}
|
||||
|
||||
function processTranslationBoxes() {
|
||||
const boxes = document.querySelectorAll(".d-l10n-translate-template");
|
||||
|
||||
const knownTranslations = {}
|
||||
|
||||
// first pass: collect all translations
|
||||
boxes.forEach((box) => {
|
||||
const key = box.innerHTML
|
||||
const originalValue = box.getAttribute('data-original')
|
||||
const value = box.getAttribute('data-translated')
|
||||
const canonicalValue = box.getAttribute('data-canonical-name')
|
||||
const fullFormValue = box.getAttribute('data-full-form')
|
||||
const rubyValue = box.getAttribute('data-ruby')
|
||||
|
||||
const isEntity = box.getAttribute('data-entity') === 'true'
|
||||
const isManuscript = box.getAttribute('data-manuscript') === 'true'
|
||||
const isUntranslatable = box.getAttribute('data-untranslatable') === 'true'
|
||||
|
||||
if (originalValue == null || originalValue.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
knownTranslations[key] = {
|
||||
original: originalValue,
|
||||
translated: value,
|
||||
canonical: canonicalValue,
|
||||
fullForm: fullFormValue,
|
||||
ruby: rubyValue,
|
||||
isEntity: isEntity,
|
||||
isManuscript: isManuscript,
|
||||
isUntranslatable: isUntranslatable
|
||||
}
|
||||
});
|
||||
|
||||
const currentPreferredTranslation = getPreferredTranslation()
|
||||
|
||||
// remove already generated boxes
|
||||
const generatedBoxes = document.querySelectorAll(".d-l10n-generated")
|
||||
generatedBoxes.forEach((box) => {
|
||||
box.remove()
|
||||
})
|
||||
|
||||
// second pass: generate the preferred translation
|
||||
boxes.forEach((box) => {
|
||||
const key = box.innerHTML
|
||||
const translation = knownTranslations[key]
|
||||
if (translation == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (translation.isUntranslatable) {
|
||||
box.innerHTML = `<em>${translation.original}</em>`
|
||||
return;
|
||||
}
|
||||
|
||||
let preferredTranslation
|
||||
|
||||
if (currentPreferredTranslation === 'writer') {
|
||||
preferredTranslation = translation.translated
|
||||
} else if (currentPreferredTranslation === 'canonical') {
|
||||
preferredTranslation = translation.canonical || translation.translated
|
||||
} else if (currentPreferredTranslation === 'original') {
|
||||
preferredTranslation = translation.original
|
||||
}
|
||||
|
||||
if (translation.isEntity) {
|
||||
if (isCjkContext(preferredTranslation)) {
|
||||
preferredTranslation = `<span class="d-l10n-translated-entity-name-cjk">${preferredTranslation}</span>`
|
||||
} else {
|
||||
preferredTranslation = `<span class="d-l10n-translated-entity-name">${preferredTranslation}</span>`
|
||||
}
|
||||
}
|
||||
|
||||
if (translation.isManuscript) {
|
||||
if (isCjkContext(preferredTranslation)) {
|
||||
preferredTranslation = `<span class="d-l10n-translated-manuscript-name-cjk">${preferredTranslation}</span>`
|
||||
} else {
|
||||
preferredTranslation = `<span class="d-l10n-translated-manuscript-name">${preferredTranslation}</span>`
|
||||
}
|
||||
}
|
||||
|
||||
if (translation.ruby) {
|
||||
preferredTranslation = `<ruby>${preferredTranslation}<rt>${translation.ruby}</rt></ruby>`
|
||||
}
|
||||
|
||||
if (currentPreferredTranslation === 'original') {
|
||||
box.insertAdjacentHTML('afterend', `<span class="d-l10n-generated d-l10n-translated-original">${translation.original}</span>`)
|
||||
} else {
|
||||
box.insertAdjacentHTML('afterend', `<span class="d-l10n-generated d-l10n-translated" title="${translation.original}">${preferredTranslation}</span>`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
processColorBoxes()
|
||||
processTranslationBoxes()
|
||||
});
|
147
shortcode.php
Normal file
147
shortcode.php
Normal file
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @wordpress-plugin
|
||||
* Plugin Name: Daniel's Localization Tool
|
||||
* Plugin URI: https://git.dsstudio.com/dousha/wp-l10n-tool
|
||||
* Description: A Simple Localization Tool
|
||||
* Version: 1.0.0
|
||||
* Author: dousha
|
||||
* Author URI: https://dsstudio.tech/
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
if (!defined("ABSPATH")) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function d_l10n_add_shortcodes(): void
|
||||
{
|
||||
add_shortcode('tr', 'd_l10n_translate_shortcode');
|
||||
add_shortcode('cl', 'd_l10n_color_shortcode');
|
||||
}
|
||||
|
||||
function d_l10n_enque_assets(): void
|
||||
{
|
||||
wp_enqueue_style('d-kbd-ctrl-style', plugins_url('/assets/css/style.css', __FILE__), [], '0.0.1');
|
||||
wp_enqueue_script('d-kbd-ctrl-script', plugins_url('/assets/js/work.js', __FILE__), [], '0.0.1');
|
||||
}
|
||||
|
||||
function d_l10n_evaluate_truthiness($val): bool
|
||||
{
|
||||
if (is_bool($val)) {
|
||||
return $val;
|
||||
}
|
||||
|
||||
if (is_string($val)) {
|
||||
return $val === 'true' || $val === '1';
|
||||
}
|
||||
|
||||
if (is_numeric($val)) {
|
||||
return $val > 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function d_l10n_compile_attributes($attr, $content): string
|
||||
{
|
||||
if (empty($attr)) {
|
||||
return 'data-ref="' . $content . '"';
|
||||
}
|
||||
|
||||
$original = $attr['o'];
|
||||
$translated = $attr['p'];
|
||||
if (empty($translated)) {
|
||||
$translated = $content;
|
||||
}
|
||||
|
||||
$isEntity = $attr['e'];
|
||||
if (empty($isEntity)) {
|
||||
$isEntity = $attr['ent'];
|
||||
}
|
||||
$isEntity = d_l10n_evaluate_truthiness($isEntity);
|
||||
|
||||
$isManuscript = $attr['m'];
|
||||
if (empty($isManuscript)) {
|
||||
$isManuscript = $attr['man'];
|
||||
}
|
||||
$isManuscript = d_l10n_evaluate_truthiness($isManuscript);
|
||||
|
||||
$canonicalName = $attr['cn'];
|
||||
|
||||
$fullForm = $attr['a'];
|
||||
$ruby = $attr['r'];
|
||||
if (empty($ruby)) {
|
||||
$ruby = $attr['ruby'];
|
||||
}
|
||||
if (empty($ruby)) {
|
||||
$ruby = $attr['pro'];
|
||||
}
|
||||
|
||||
$isUntranslatable = $attr['u'];
|
||||
if (empty($isUntranslatable)) {
|
||||
$isUntranslatable = $attr['un'];
|
||||
}
|
||||
$isUntranslatable = d_l10n_evaluate_truthiness($isUntranslatable);
|
||||
|
||||
// ok, now compile stuff
|
||||
$compiled = '';
|
||||
if (!empty($original)) {
|
||||
$compiled .= "data-original=\"" . $original . "\" ";
|
||||
}
|
||||
|
||||
if (!empty($translated)) {
|
||||
$compiled .= "data-translated=\"" . $translated . "\" ";
|
||||
}
|
||||
|
||||
if ($isEntity) {
|
||||
$compiled .= "data-entity=\"true\" ";
|
||||
}
|
||||
|
||||
if ($isManuscript) {
|
||||
$compiled .= "data-manuscript=\"true\" ";
|
||||
}
|
||||
|
||||
if (!empty($canonicalName)) {
|
||||
$compiled .= "data-canonical-name=\"" . $canonicalName . "\" ";
|
||||
}
|
||||
|
||||
if ($fullForm) {
|
||||
$compiled .= "data-full-form=\"" . $fullForm . "\" ";
|
||||
}
|
||||
|
||||
if ($ruby) {
|
||||
$compiled .= "data-ruby=\"" . $ruby . "\" ";
|
||||
}
|
||||
|
||||
if ($isUntranslatable) {
|
||||
$compiled .= "data-untranslatable=\"true\" ";
|
||||
}
|
||||
|
||||
return $compiled;
|
||||
}
|
||||
|
||||
function d_l10n_translate_shortcode($attr = array(), $content = null, $tag = ''): ?string
|
||||
{
|
||||
if (is_null($content)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
$compiledAttributes = d_l10n_compile_attributes($attr, $content);
|
||||
|
||||
return "<span class=\"d-l10n-translate-template\" " . $compiledAttributes . ">" . $content . "</span>";
|
||||
}
|
||||
|
||||
function d_l10n_color_shortcode($attr = array(), $content = null, $tag = ''): ?string
|
||||
{
|
||||
$color = $attr['v'];
|
||||
if (empty($color)) {
|
||||
return '<span class="d-l10n-color">' . $content . '</span>';
|
||||
}
|
||||
|
||||
return '<span class="d-l10n-color" data-color="' . $color . '">' . $content . '</span>';
|
||||
}
|
||||
|
||||
add_action('init', 'd_l10n_add_shortcodes');
|
||||
add_action('init', 'd_l10n_enque_assets');
|
Loading…
Reference in New Issue
Block a user