mirror of
https://github.com/NaitLee/Cat-Printer.git
synced 2025-05-28 05:00:25 -07:00
I18n update; also apply special CSS to iframe
This commit is contained in:
parent
b97fd7022e
commit
bf81524083
@ -79,9 +79,7 @@ If there are something better to organize these, feel free to discuss in issue.
|
||||
- Bundled all required scripts, see file `0-transpile.sh`
|
||||
- Is not there by default. Transpile it yourself
|
||||
- `www/i18n*` - Scripts about I18n:
|
||||
- TODO. In fact it worth a dedicated document to describe it
|
||||
- (Mostly) Depends on "extensions" to work in the correct way,
|
||||
feel free to extend, as it's *your* turn
|
||||
- See [i18n.md](i18n.i18n/i18n.md)
|
||||
- `www/*.js` - Other scripts:
|
||||
- Small but useful, just look at them directly
|
||||
- `www/jslicense.html` - Dedicated JavaScript License information
|
||||
|
@ -7,10 +7,12 @@ Currently User Interface related files are stored in `www/lang`
|
||||
As well as `readme.i18n` and `i18n.i18n` for readme.
|
||||
In the future, there can be other manual files.
|
||||
|
||||
*Note: I'm thinking about putting this part to dedicated repo.*
|
||||
|
||||
## What to do
|
||||
|
||||
In simple cases, you just make a copy of already-there language file, and modify it to your local language.
|
||||
You should know what's your locale "code", for example "English (US)" is `en-US`. You can look at your browser locale configuration, or gather on the Web.
|
||||
You should know what's your locale "code", for example "English (US)" is `en-US`. You can look at your browser locale configuration, or gather from the Web.
|
||||
After that, add an entry in `list.json`.
|
||||
|
||||
## It seems can't satisfy another grammar
|
||||
@ -23,7 +25,22 @@ Instead the grammar details is all done by code.
|
||||
That doesn't mean difficulty:
|
||||
|
||||
- You can simply describe how should the grammar work, by communicating with natual language, with someone that could write code, at here or around you.
|
||||
- At here almost everyone could do programming. Coding grammar needs much less work -- it's what a junior programmer could do to improve his/her skill.
|
||||
- Here almost everyone could do programming. Coding grammar needs much less work -- it's what a junior programmer could do to improve his/her skill.
|
||||
- Don't forget me! There are Issue and Discussion.
|
||||
|
||||
- Oh you actually can do code? Look at file `www/i18n-ext.js` then you could get the point. Also see `www/i18n.d.ts` for typing details. You may modify both.
|
||||
- Or you actually can do code? Look at file `www/i18n-ext.js` then you could get the point. Also see `www/i18n.d.ts` for typing details. You may modify *anything*, **to be better**.
|
||||
|
||||
- You can also make "example" files (like `en-US-ex.jsonc`), for testing grammar points, or showing what could be done.
|
||||
The `jsonc` format is JSON allowing comments. At the moment please use only `//`
|
||||
- Use a test file:
|
||||
- Load project Web Interface
|
||||
- Open DevTools in browser (usually press F12)
|
||||
- go to console, inside type like:
|
||||
|
||||
```js
|
||||
testI18n('en-US'); // lang code
|
||||
i18n('0-apples', [1]); // key ('conditions') and values ('things')
|
||||
// ...
|
||||
```
|
||||
|
||||
- See if it's correct
|
||||
|
@ -35,9 +35,9 @@
|
||||
<span>Copyright © 2021-2022 NaitLee Soft.</span>
|
||||
<span data-i18n="some-rights-reserved">Some rights reserved.</span>
|
||||
</p>
|
||||
<p>This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.</p>
|
||||
<p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.</p>
|
||||
<p>You should have received a copy of the GNU General Public License along with this program. If not, see <<a target="_blank" href="https://www.gnu.org/licenses/">https://www.gnu.org/licenses/</a>>.</p>
|
||||
<p class="force-ltr">This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.</p>
|
||||
<p class="force-ltr">This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.</p>
|
||||
<p class="force-ltr">You should have received a copy of the GNU General Public License along with this program. If not, see <<a target="_blank" href="https://www.gnu.org/licenses/">https://www.gnu.org/licenses/</a>>.</p>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
|
117
www/i18n-ext.js
117
www/i18n-ext.js
@ -9,53 +9,118 @@ var I18nExtensions = (function() {
|
||||
|
||||
// Don't forget to register your extension first!
|
||||
var registers = {
|
||||
'en-US': english,
|
||||
'zh-CN': chinese,
|
||||
'de-DE': german
|
||||
'en-US': English,
|
||||
'zh-CN': Chinese,
|
||||
'de-DE': German,
|
||||
'ar': Arabic
|
||||
};
|
||||
|
||||
/**
|
||||
* Here's especially useful for showing what can be done!
|
||||
* @type {ExtensionOf<'en-US'>}
|
||||
*/
|
||||
function english(things, conditions) {
|
||||
let text = conditions;
|
||||
for (let index in things) {
|
||||
let value = things[index];
|
||||
if (value == 1) text = conditions['single'];
|
||||
else text = conditions['multiple'];
|
||||
if (!text && typeof value === 'number') {
|
||||
if (value < 10 || value > 20) {
|
||||
if (value % 10 === 1) text = conditions['1st'];
|
||||
else if (value % 10 === 2) text = conditions['2nd'];
|
||||
else if (value % 10 === 3) text = conditions['3rd'];
|
||||
else text = conditions['nth'];
|
||||
} else text = conditions['nth'];
|
||||
function English(things, conditions) {
|
||||
if (typeof conditions === 'string')
|
||||
return conditions;
|
||||
for (let key in things) {
|
||||
let value = things[key];
|
||||
if (typeof value === 'number') {
|
||||
if (conditions['nth']) {
|
||||
// You can also replace what would be shown!
|
||||
if (value < 10 || value > 20) {
|
||||
if (value % 10 === 1) things[key] = value + 'st';
|
||||
else if (value % 10 === 2) things[key] = value + 'nd';
|
||||
else if (value % 10 === 3) things[key] = value + 'rd';
|
||||
else things[key] = value + 'th';
|
||||
} else things[key] = value + 'th';
|
||||
return conditions['nth'];
|
||||
} else {
|
||||
if (value == 1) return conditions['single'];
|
||||
else return conditions['multiple'];
|
||||
}
|
||||
} else {
|
||||
if (conditions['an']) {
|
||||
if ('aeiouAEIOU'.includes(value[0]))
|
||||
things[key] = 'an ' + things[key];
|
||||
else things[key] = 'a ' + things[key];
|
||||
return conditions['an'];
|
||||
}
|
||||
}
|
||||
// There are many thing else. Eg. Floor counting of en-US versus en-GB
|
||||
// But in a project we should take just enough
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* 精辟。不解释。
|
||||
* 不过,量词什么的,以后再说……
|
||||
* @type {ExtensionOf<'zh-CN'>}
|
||||
*/
|
||||
function chinese(things, conditions) {
|
||||
return conditions;
|
||||
function Chinese(things, conditions) {
|
||||
if (typeof conditions === 'string')
|
||||
return conditions;
|
||||
for (let key in things) {
|
||||
function prepend(value) { things[key] = value + things[key]; }
|
||||
let value = things[key];
|
||||
if (typeof value === 'number') {
|
||||
// 精辟。不解释。
|
||||
return conditions;
|
||||
} else {
|
||||
if (conditions['measure']) {
|
||||
// 不过要说量词的话……
|
||||
switch (value) {
|
||||
case '狗':
|
||||
case '蛇':
|
||||
prepend('条'); break;
|
||||
case '猫':
|
||||
case '鸡':
|
||||
prepend('只'); break;
|
||||
case '牛':
|
||||
case '猪':
|
||||
prepend('头'); break;
|
||||
case '马':
|
||||
case '狼':
|
||||
prepend('匹'); break;
|
||||
case '香蕉':
|
||||
case '烧烤':
|
||||
prepend('串'); break;
|
||||
case '股债':
|
||||
prepend('屁'); break;
|
||||
default:
|
||||
prepend('个');
|
||||
}
|
||||
// 还有很多。最好放在另一个文件
|
||||
return conditions['measure'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {ExtensionOf<'de-DE'>}
|
||||
*/
|
||||
function german(things, conditions) {
|
||||
let text = conditions;
|
||||
function German(things, conditions) {
|
||||
if (typeof conditions === 'string')
|
||||
return conditions;
|
||||
for (let index in things) {
|
||||
let value = things[index];
|
||||
if (value == 1) text = conditions['single'];
|
||||
else text = conditions['multiple'];
|
||||
if (typeof value === 'number') {
|
||||
if (value == 1) return conditions['single'];
|
||||
else return conditions['multiple'];
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {ExtensionOf<'ar'>}
|
||||
*/
|
||||
function Arabic(things, conditions) {
|
||||
// Another example of replacement
|
||||
// const arabic_numbers = '٠١٢٣٤٥٦٧٨٩';
|
||||
for (let key in things) {
|
||||
let value = things[key];
|
||||
if (typeof value === 'number')
|
||||
things[key] = value.toLocaleString('ar');
|
||||
}
|
||||
return conditions;
|
||||
}
|
||||
|
||||
return registers;
|
||||
|
13
www/i18n.d.ts
vendored
13
www/i18n.d.ts
vendored
@ -8,7 +8,7 @@ interface Extension {
|
||||
(things: Things, conditions: Conditions): string;
|
||||
}
|
||||
interface ExtensionOf<K extends Languages> {
|
||||
(things: Things, conditions: ConditionsOf<K>): string;
|
||||
(things: Things, conditions: ConditionsOf<K> | string): string;
|
||||
}
|
||||
type Languages = keyof AllConditions;
|
||||
|
||||
@ -21,16 +21,17 @@ type AllConditions = {
|
||||
'en-US': {
|
||||
'single': string,
|
||||
'multiple': string,
|
||||
'1st': string,
|
||||
'2nd': string,
|
||||
'3rd': string,
|
||||
'nth': string
|
||||
'nth': string,
|
||||
'a': string,
|
||||
'an': string
|
||||
},
|
||||
'de-DE': {
|
||||
'single': string,
|
||||
'multiple': string
|
||||
},
|
||||
'zh-CN': {}
|
||||
'zh-CN': {
|
||||
'measure': string
|
||||
}
|
||||
};
|
||||
|
||||
interface I18nCallable extends I18n {
|
||||
|
13
www/i18n.js
13
www/i18n.js
@ -68,13 +68,11 @@ class I18n {
|
||||
* Translate a string ("text"), using "things" such as numbers
|
||||
* @param {string} text
|
||||
* @param {Things} things
|
||||
* @param {boolean} can_change_things
|
||||
*/
|
||||
translate(text, things, can_change_things = true) {
|
||||
translate(text, things) {
|
||||
let conditions = this.database[this.language][text] || text;
|
||||
if (!things) return conditions;
|
||||
if (!can_change_things) things = { ... things };
|
||||
if (this.extensions[this.language] && typeof conditions !== 'string')
|
||||
if (this.extensions[this.language])
|
||||
text = this.extensions[this.language](things, conditions);
|
||||
else text = conditions;
|
||||
for (let key in things) {
|
||||
@ -85,7 +83,7 @@ class I18n {
|
||||
}
|
||||
|
||||
/**
|
||||
* A i18n instance that is directly callable
|
||||
* An i18n instance that is directly callable
|
||||
* @type {I18nCallable}
|
||||
*/
|
||||
var i18n = (function() {
|
||||
@ -95,10 +93,9 @@ var i18n = (function() {
|
||||
/**
|
||||
* @param {string} text
|
||||
* @param {Things} things
|
||||
* @param {boolean} can_change_things
|
||||
*/
|
||||
let i18n_callable = function(text, things, can_change_things = true) {
|
||||
return instance.translate.call(i18n_callable, text, things, can_change_things);
|
||||
let i18n_callable = function(text, things) {
|
||||
return instance.translate.call(i18n_callable, text, things);
|
||||
}
|
||||
|
||||
Object.setPrototypeOf(i18n_callable, instance);
|
||||
|
16
www/lang/en-US-ex.jsonc
Normal file
16
www/lang/en-US-ex.jsonc
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"$language": "English (US) i18n example",
|
||||
// plural
|
||||
"0-apples": {
|
||||
"single": "{0} apple",
|
||||
"multiple": "{0} apples"
|
||||
},
|
||||
// order
|
||||
"0th-apple": {
|
||||
"nth": "{0} apple"
|
||||
},
|
||||
// a or an
|
||||
"there-is-a-0": {
|
||||
"an": "There is {0}"
|
||||
}
|
||||
}
|
10
www/lang/zh-CN-ex.jsonc
Normal file
10
www/lang/zh-CN-ex.jsonc
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"$language": "中文(简体)国际化样例",
|
||||
// 比起英文,这就简单了……
|
||||
"0-apples": "{0} 个苹果",
|
||||
"0th-apple": "第 {0} 个苹果",
|
||||
// ……直到碰见量词
|
||||
"there-is-a-0": {
|
||||
"measure": "有一{0}"
|
||||
}
|
||||
}
|
11
www/main.css
11
www/main.css
@ -52,11 +52,12 @@ a+a {
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
button, input, select, textarea {
|
||||
button, input, select, textarea, label {
|
||||
font: inherit;
|
||||
color: var(--fore-color);
|
||||
/* background-color: var(--back-color); */
|
||||
background-color: transparent;
|
||||
display: inline-block;
|
||||
}
|
||||
select[multiple] {
|
||||
width: 8em;
|
||||
@ -70,7 +71,6 @@ button, input[type="number"], input[type="text"], select {
|
||||
transition: all var(--anim-time) var(--curve);
|
||||
cursor: pointer;
|
||||
min-width: 6em;
|
||||
display: inline-block;
|
||||
line-height: calc(var(--font-size) + var(--span));
|
||||
}
|
||||
input[type="number"], input[type="text"] {
|
||||
@ -491,10 +491,6 @@ a {
|
||||
a:hover, a:active { color: #77f; }
|
||||
canvas#preview, canvas#control-canvas,
|
||||
#control-document, .logo { filter: brightness(0.6); }
|
||||
body.high-contrast.dark {
|
||||
--fore-color: #fff;
|
||||
--back-color: #000;
|
||||
}
|
||||
}
|
||||
/* so silly... */
|
||||
body.dark { --fore-color: #eee; --back-color: #333; }
|
||||
@ -533,6 +529,9 @@ body.force-rtl,
|
||||
#force-rtl+label {
|
||||
direction: rtl;
|
||||
}
|
||||
.force-ltr {
|
||||
direction: ltr;
|
||||
}
|
||||
body.high-contrast {
|
||||
--border: 2px;
|
||||
--fore-color: #fff;
|
||||
|
30
www/main.js
30
www/main.js
@ -447,6 +447,14 @@ async function initI18n() {
|
||||
}
|
||||
}
|
||||
|
||||
async function testI18n(lang) {
|
||||
i18n.useLanguage(lang);
|
||||
i18n.add(lang, await fetch(`/lang/${lang}-ex.jsonc`)
|
||||
.then(r => r.text()) // jsonc: JSON with comment
|
||||
.then(t => JSON.parse(t.replace(/\s*\/\/.*/g, '')))
|
||||
, true);
|
||||
}
|
||||
|
||||
class Main {
|
||||
promise;
|
||||
/** @type {CanvasController} */
|
||||
@ -472,8 +480,15 @@ class Main {
|
||||
/** @type {HTMLIFrameElement} */
|
||||
let iframe = document.getElementById('frame');
|
||||
iframe.addEventListener('load', () => {
|
||||
iframe.contentDocument.body.classList.value = document.body.classList.value;
|
||||
applyI18nToDom(iframe.contentDocument);
|
||||
});
|
||||
function apply_class(class_name, value) {
|
||||
[document, iframe.contentDocument].forEach(d => value ?
|
||||
d.body.classList.add(class_name) :
|
||||
d.body.classList.remove(class_name)
|
||||
);
|
||||
}
|
||||
this.canvasController = new CanvasController();
|
||||
putEvent('#button-exit', 'click', this.exit, this);
|
||||
putEvent('#button-print', 'click', this.print, this);
|
||||
@ -489,24 +504,19 @@ class Main {
|
||||
(checked) => checked && Notice.note('dry-run-test-print-process-only')
|
||||
);
|
||||
this.attachSetter('#no-animation', 'change', 'no_animation',
|
||||
(checked) => checked ? document.body.classList.add('no-animation')
|
||||
: document.body.classList.remove('no-animation')
|
||||
(checked) => apply_class('no-animation', checked)
|
||||
);
|
||||
this.attachSetter('#large-font', 'change', 'large_font',
|
||||
(checked) => checked ? document.body.classList.add('large-font')
|
||||
: document.body.classList.remove('large-font')
|
||||
(checked) => apply_class('large-font', checked)
|
||||
);
|
||||
this.attachSetter('#force-rtl', 'change', 'force_rtl',
|
||||
(checked) => checked ? document.body.classList.add('force-rtl')
|
||||
: document.body.classList.remove('force-rtl')
|
||||
(checked) => apply_class('force-rtl', checked)
|
||||
);
|
||||
this.attachSetter('#dark-theme', 'change', 'dark_theme',
|
||||
(checked) => checked ? document.body.classList.add('dark')
|
||||
: document.body.classList.remove('dark')
|
||||
(checked) => apply_class('dark', checked)
|
||||
);
|
||||
this.attachSetter('#high-contrast', 'change', 'high_contrast',
|
||||
(checked) => checked ? document.body.classList.add('high-contrast')
|
||||
: document.body.classList.remove('high-contrast')
|
||||
(checked) => apply_class('high-contrast', checked)
|
||||
);
|
||||
this.attachSetter('#threshold', 'change', 'threshold',
|
||||
(value) => this.canvasController.threshold = value
|
||||
|
Loading…
x
Reference in New Issue
Block a user