feat: Support RegExp translation (#11)

pull/13/head
Jad 2023-03-09 04:45:53 +08:00 committed by GitHub
parent dba18cb956
commit 28c16a04b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 113 additions and 15 deletions

View File

@ -10,12 +10,14 @@
## Features
- Bilingual translation, no need to worry about how to find the original button.
- Compatible with language pack extensions, no need to re-import.
- Support dynamic translation of title hints.
- Additional support RegExp pattern, more flexible translation.
## Installation
Choose one of the following methods, Need to use webui with extension support <sup>(Versions after 2023)</sup>
### Method 1
#### Method 1
Use the `Install from URL` provided by webui to install
@ -30,7 +32,7 @@ After that, switch to the <kbd>Installed</kbd> panel and click the <kbd>Apply an
![Snipaste_2023-02-28_00-29-14](https://user-images.githubusercontent.com/16256221/221625345-9e656f25-89dd-4361-8ee5-f4ab39d18ca4.png)
### Method 2
#### Method 2
Clone to your extension directory manually.
@ -47,6 +49,18 @@ In <kbd>Settings</kbd> - <kbd>Bilingual Localization</kbd> panel, select the loc
![Snipaste_2023-02-28_00-04-21](https://user-images.githubusercontent.com/16256221/221625729-73519629-8c1f-4eb5-99db-a1d3f4b58a87.png)
## RegExp pattern
Localization support RegExp pattern, syntax rule is `@@<REGEXP>`, capturing group is `$n`, doc: [String.prototype.replace()](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/replace)
```json
{
...
"@@/^(\\d+) images in this directory, divided into (\\d+) pages$/": "目录中有$1张图片共$2页",
"@@/^Favorites path from settings: (.*)$/": "设置的收藏夹目录:$1",
...
}
```
## How to get localization file
Localization files are no longer provided with the plugin, please install a third-party language extensions and set-up as described in the [Usage](#usage) section of this article.

View File

@ -9,14 +9,16 @@
## 特徴
- バイリンガル対応により、元のボタンを探す必要がありません。
- 言語パックの拡張機能に対応するので、再インストールが不要
- 言語パックの拡張機能に対応するので、再インストールが不要。
- 動的なタイトルヒントの翻訳をサポートします。
- 正規表現パターンによる柔軟な翻訳が可能です。
## Installation
以下の方法から選択します。
拡張機能に対応したWebUIが必要です。<sup>(Versions after 2023)</sup>
### Method 1
#### Method 1
webuiの`Install from URL`でインストール
@ -31,7 +33,7 @@ webuiの`Install from URL`でインストール
![Snipaste_2023-02-28_00-29-14](https://user-images.githubusercontent.com/16256221/221625345-9e656f25-89dd-4361-8ee5-f4ab39d18ca4.png)
### Method 2
#### Method 2
手動でExtensionディレクトリにcloneする
@ -48,6 +50,18 @@ In <kbd>Settings</kbd> - <kbd>Bilingual Localization</kbd>パネルで、有効
![Snipaste_2023-02-28_00-04-21](https://user-images.githubusercontent.com/16256221/221625729-73519629-8c1f-4eb5-99db-a1d3f4b58a87.png)
## RegExp pattern
言語対応の正規表現パターンで、構文ルールは`@@<REGEXP>`、キャプチャグループは`$n`です。ドキュメント:[String.prototype.replace()](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/replace)。
```json
{
...
"@@/^(\\d+) images in this directory, divided into (\\d+) pages$/": "このディレクトリには$1枚の画像、$2ページ",
"@@/^Favorites path from settings: (.*)$/": "お気に入りのディレクトリパス:$1",
...
}
```
## 言語ファイルの取得
内蔵の言語ファイルは提供されなくなりましたので、サードパーティの拡張機能をインストールし、[Usage](#usage)のように設定してください。

View File

@ -10,12 +10,14 @@
## 功能
- 全新实现的双语对照翻译功能,不必再担心切换翻译后找不到原始功能
- 兼容原生语言包扩展,无需重新导入多语言语料
- 支持动态title提示的翻译
- 额外支持正则表达式替换,翻译更加灵活
## 安装
以下方式选择其一,需要使用支持扩展功能的 webui <sup>(2023年之后的版本)</sup>
### 方式1
#### 方式1
使用 webui 提供的`Install from URL`功能安装
@ -28,7 +30,7 @@
![Snipaste_2023-02-28_00-29-14](https://user-images.githubusercontent.com/16256221/221625345-9e656f25-89dd-4361-8ee5-f4ab39d18ca4.png)
### 方式2
#### 方式2
手动克隆到你的扩展目录里
@ -44,6 +46,18 @@ git clone https://github.com/journey-ad/sd-webui-bilingual-localization extensio
<kbd>Settings</kbd> - <kbd>Bilingual Localization</kbd>中选择要启用的本地化文件,依次点击<kbd>Apply settings</kbd><kbd>Reload UI</kbd>按钮
![Snipaste_2023-02-28_00-04-21](https://user-images.githubusercontent.com/16256221/221625729-73519629-8c1f-4eb5-99db-a1d3f4b58a87.png)
## 正则表达式支持
本地化语料支持正则表达式替换,语法规则`@@<REGEXP>`,括号匹配变量`$n`,参考[String.prototype.replace()](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/replace)
```json
{
...
"@@/^(\\d+) images in this directory, divided into (\\d+) pages$/": "目录中有$1张图片共$2页",
"@@/^Favorites path from settings: (.*)$/": "设置的收藏夹目录:$1",
...
}
```
## 获取本地化文件
本地化文件不再随插件提供,请安装第三方语言包并按照本文[使用](#使用)部分的方式设置使用

View File

@ -21,6 +21,8 @@
#txtimg_hr_finalres .bilingual__trans_wrapper em,
#tab_ti .output-html .bilingual__trans_wrapper em,
#dynamic-prompting .output-html .bilingual__trans_wrapper em,
#txt2img_script_container .output-html .bilingual__trans_wrapper em,
#available_extensions .extension-tag .bilingual__trans_wrapper em {
display: none;
}
@ -28,7 +30,10 @@
#settings .bilingual__trans_wrapper:not(#settings .tabitem .bilingual__trans_wrapper),
label>span>.bilingual__trans_wrapper,
.w-full>span>.bilingual__trans_wrapper,
.output-html .bilingual__trans_wrapper:not(th .bilingual__trans_wrapper) {
.output-html .bilingual__trans_wrapper:not(th .bilingual__trans_wrapper),
.output-markdown .bilingual__trans_wrapper,
.posex_setting_cont .bilingual__trans_wrapper:not(.posex_bg .bilingual__trans_wrapper) /* Posex extension */
{
font-size: 12px;
align-items: flex-start;
}
@ -51,9 +56,15 @@
div[data-testid="image"]>div>div.touch-none>div {
background-color: rgba(255, 255, 255, .6);
color: #222;
}`
}
/* Posex extension */
.posex_bg {
white-space: nowrap;
}
`
let i18n = null, config = null;
let i18n = null, i18nRegex = {}, config = null;
// First load
function setup() {
@ -75,7 +86,19 @@
logger.log('Bilingual Localization initialized.')
// Load localization file
i18n = JSON.parse(readFile(dirs[file]))
i18n = JSON.parse(readFile(dirs[file]), (key, value) => {
// parse regex translations
if (key.startsWith('@@')) {
i18nRegex[key.slice(2)] = value
} else {
return value
}
})
logger.group('Localization file loaded.')
logger.log('i18n', i18n)
logger.log('i18nRegex', i18nRegex)
logger.groupEnd()
translatePage()
}
@ -90,7 +113,7 @@
"textarea[placeholder], select, option", // text box placeholder and select element
".transition > div > span:not([class])", // collapse panel added by extension
".tabitem .pointer-events-none", // upper left corner of image upload panel
".output-html:not(#footer)", // output html exclude footer
"#modelmerger_interp_description .output-html", // model merger description
"#lightboxModal span" // image preview lightbox
])
.forEach(el => translateEl(el, { deep: true }))
@ -98,6 +121,8 @@
querySelectorAll([
'div[data-testid="image"] > div > div', // description of image upload panel
'#extras_image_batch > div', // description of extras image batch file upload panel
".output-html:not(#footer), .output-markdown", // output html exclude footer
'#dynamic-prompting' // dynamic-prompting extension
])
.forEach(el => translateEl(el, { rich: true }))
@ -128,8 +153,6 @@
if (el.tagName === 'OPTION') {
doTranslate(el, el.textContent, 'option')
} else {
doTranslate(el, el.textContent, 'element')
}
if (deep || rich) {
@ -147,6 +170,8 @@
translateEl(node, { deep, rich })
}
})
} else {
doTranslate(el, el.textContent, 'element')
}
}
@ -158,9 +183,21 @@
source = source.trim()
if (!source) return
if (re_num.test(source)) return
if (re_emoji.test(source)) return
// if (re_emoji.test(source)) return
let translation = i18n[source]
if (!translation) {
for (let regex in i18nRegex) {
regex = getRegex(regex)
if (regex && regex.test(source)) {
logger.log('regex', regex, source)
translation = source.replace(regex, i18nRegex[regex])
break;
}
}
}
if (!translation) return
if (source === translation) return
@ -222,6 +259,25 @@
.replace(/"/g, '&quot;').replace(/'/g, '&#39;')
}
// get regex object from string
function getRegex(regex) {
try {
regex = regex.trim();
let parts = regex.split('/');
if (regex[0] !== '/' || parts.length < 3) {
regex = regex.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); //escap common string
return new RegExp(regex);
}
const option = parts[parts.length - 1];
const lastIndex = regex.lastIndexOf('/');
regex = regex.substring(1, lastIndex);
return new RegExp(regex, option);
} catch (e) {
return null
}
}
// Load file
function readFile(filePath) {
let request = new XMLHttpRequest();