mirror of
https://github.com/penpot/penpot.git
synced 2025-01-21 06:02:32 -05:00
🎉 Add new translations management script
This commit is contained in:
parent
62cea62356
commit
139dd7d80f
22 changed files with 677 additions and 308 deletions
|
@ -55,6 +55,7 @@
|
|||
"draft-js": "git+https://github.com/penpot/draft-js.git#commit=4a99b2a6020b2af97f6dc5fa1b4275ec16b559a0",
|
||||
"express": "^4.19.2",
|
||||
"fancy-log": "^2.0.0",
|
||||
"getopts": "^2.3.0",
|
||||
"gettext-parser": "^8.0.0",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-concat": "^2.6.1",
|
||||
|
|
377
frontend/scripts/translations.js
Executable file
377
frontend/scripts/translations.js
Executable file
|
@ -0,0 +1,377 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import getopts from "getopts";
|
||||
import { promises as fs, createReadStream } from "fs";
|
||||
import gt from "gettext-parser";
|
||||
import l from "lodash";
|
||||
import path from "path";
|
||||
import readline from "readline";
|
||||
|
||||
const baseLocale = "en";
|
||||
|
||||
async function* getFiles(dir) {
|
||||
// console.log("getFiles", dir)
|
||||
const dirents = await fs.readdir(dir, { withFileTypes: true });
|
||||
for (const dirent of dirents) {
|
||||
let res = path.resolve(dir, dirent.name);
|
||||
res = path.relative(".", res);
|
||||
|
||||
if (dirent.isDirectory()) {
|
||||
yield* getFiles(res);
|
||||
} else {
|
||||
yield res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function translationExists(locale) {
|
||||
const target = path.normalize("./translations/");
|
||||
const targetPath = path.join(target, `${locale}.po`);
|
||||
|
||||
try {
|
||||
const result = await fs.stat(targetPath);
|
||||
return true;
|
||||
} catch (cause) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function readLocaleByPath(path) {
|
||||
const content = await fs.readFile(path);
|
||||
return gt.po.parse(content, "utf-8");
|
||||
}
|
||||
|
||||
async function writeLocaleByPath(path, data) {
|
||||
const buff = gt.po.compile(data, { sort: true });
|
||||
await fs.writeFile(path, buff);
|
||||
}
|
||||
|
||||
async function readLocale(locale) {
|
||||
const target = path.normalize("./translations/");
|
||||
const targetPath = path.join(target, `${locale}.po`);
|
||||
return readLocaleByPath(targetPath);
|
||||
}
|
||||
|
||||
async function writeLocale(locale, data) {
|
||||
const target = path.normalize("./translations/");
|
||||
const targetPath = path.join(target, `${locale}.po`);
|
||||
return writeLocaleByPath(targetPath, data);
|
||||
}
|
||||
|
||||
async function* scanLocales() {
|
||||
const fileRe = /.+\.po$/;
|
||||
const target = path.normalize("./translations/");
|
||||
const parent = path.join(target, "..");
|
||||
|
||||
for await (const f of getFiles(target)) {
|
||||
if (!fileRe.test(f)) continue;
|
||||
const data = path.parse(f);
|
||||
yield data;
|
||||
}
|
||||
}
|
||||
|
||||
async function processLocale(options, f) {
|
||||
let locales = options.locale;
|
||||
if (typeof locales === "string") {
|
||||
locales = locales.split(/,/);
|
||||
} else if (Array.isArray(locales)) {
|
||||
} else if (locales === undefined) {
|
||||
} else {
|
||||
console.error(`Invalid value found on locales parameter: '${locales}'`);
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
for await (const { name } of scanLocales()) {
|
||||
if (locales === undefined || locales.includes(name)) {
|
||||
await f(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function processTranslation(data, prefix, f) {
|
||||
for (let key of Object.keys(data.translations[""])) {
|
||||
if (key === prefix || key.startsWith(prefix)) {
|
||||
let value = data.translations[""][key];
|
||||
value = await f(value);
|
||||
data.translations[""][key] = value;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
async function* readLines(filePath) {
|
||||
const fileStream = createReadStream(filePath);
|
||||
|
||||
const reader = readline.createInterface({
|
||||
input: fileStream,
|
||||
crlfDelay: Infinity,
|
||||
});
|
||||
|
||||
let counter = 1;
|
||||
|
||||
for await (const line of reader) {
|
||||
yield [counter, line];
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
const trRe1 = /\(tr\s+"([\w\.\-]+)"/g;
|
||||
|
||||
function getTranslationStrings(line) {
|
||||
const result = Array.from(line.matchAll(trRe1)).map((match) => {
|
||||
return match[1];
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async function deleteByPrefix(options, prefix, ...params) {
|
||||
if (!prefix) {
|
||||
console.error(`Prefix undefined`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
await processLocale(options, async (locale) => {
|
||||
const data = await readLocale(locale);
|
||||
let deleted = [];
|
||||
|
||||
for (const [key, value] of Object.entries(data.translations[""])) {
|
||||
if (key.startsWith(prefix)) {
|
||||
delete data.translations[""][key];
|
||||
deleted.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
await writeLocale(locale, data);
|
||||
|
||||
console.log(
|
||||
`=> Processed locale '${locale}': deleting prefix '${prefix}' (deleted=${deleted.length})`,
|
||||
);
|
||||
|
||||
if (options.verbose) {
|
||||
for (let key of deleted) {
|
||||
console.log(`-> Deleted key: ${key}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function markFuzzy(options, prefix, ...other) {
|
||||
if (!prefix) {
|
||||
console.error(`Prefix undefined`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
await processLocale(options, async (locale) => {
|
||||
let data = await readLocale(locale);
|
||||
data = await processTranslation(data, prefix, (translation) => {
|
||||
if (translation.comments === undefined) {
|
||||
translation.comments = {};
|
||||
}
|
||||
|
||||
const flagData = translation.comments.flag ?? "";
|
||||
const flags = flagData.split(/\s*,\s*/).filter((s) => s !== "");
|
||||
|
||||
if (!flags.includes("fuzzy")) {
|
||||
flags.push("fuzzy");
|
||||
}
|
||||
|
||||
translation.comments.flag = flags.join(", ");
|
||||
|
||||
console.log(
|
||||
`=> Processed '${locale}': marking fuzzy '${translation.msgid}'`,
|
||||
);
|
||||
|
||||
return translation;
|
||||
});
|
||||
|
||||
await writeLocale(locale, data);
|
||||
});
|
||||
}
|
||||
|
||||
async function rehash(options, ...other) {
|
||||
const fileRe = /.+\.(?:clj|cljs|cljc)$/;
|
||||
|
||||
// Iteration 1: process all locales and update it with existing
|
||||
// entries on the source code.
|
||||
|
||||
const used = await (async function () {
|
||||
const result = {};
|
||||
|
||||
for await (const f of getFiles("src")) {
|
||||
if (!fileRe.test(f)) continue;
|
||||
|
||||
for await (const [n, line] of readLines(f)) {
|
||||
const strings = getTranslationStrings(line);
|
||||
|
||||
strings.forEach((key) => {
|
||||
const entry = `${f}:${n}`;
|
||||
if (result[key] !== undefined) {
|
||||
result[key].push(entry);
|
||||
} else {
|
||||
result[key] = [entry];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await processLocale({ locale: baseLocale }, async (locale) => {
|
||||
const data = await readLocale(locale);
|
||||
|
||||
for (let [key, val] of Object.entries(result)) {
|
||||
let entry = data.translations[""][key];
|
||||
|
||||
if (entry === undefined) {
|
||||
entry = {
|
||||
msgid: key,
|
||||
comments: {
|
||||
reference: val.join(", "),
|
||||
flag: "fuzzy",
|
||||
},
|
||||
msgstr: [""],
|
||||
};
|
||||
} else {
|
||||
if (entry.comments === undefined) {
|
||||
entry.comments = {};
|
||||
}
|
||||
|
||||
entry.comments.reference = val.join(", ");
|
||||
|
||||
const flagData = entry.comments.flag ?? "";
|
||||
const flags = flagData.split(/\s*,\s*/).filter((s) => s !== "");
|
||||
|
||||
if (flags.includes("unused")) {
|
||||
flags = flags.filter((o) => o !== "unused");
|
||||
}
|
||||
|
||||
entry.comments.flag = flags.join(", ");
|
||||
}
|
||||
|
||||
data.translations[""][key] = entry;
|
||||
}
|
||||
|
||||
await writeLocale(locale, data);
|
||||
|
||||
const keys = Object.keys(data.translations[""]);
|
||||
console.log(`=> Found ${keys.length} used translations`);
|
||||
});
|
||||
|
||||
return result;
|
||||
})();
|
||||
|
||||
// Iteration 2: process only base locale and properly detect unused
|
||||
// translation strings.
|
||||
|
||||
await (async function () {
|
||||
let totalUnused = 0;
|
||||
|
||||
await processLocale({ locale: baseLocale }, async (locale) => {
|
||||
const data = await readLocale(locale);
|
||||
|
||||
for (let [key, val] of Object.entries(data.translations[""])) {
|
||||
if (key === "") continue;
|
||||
|
||||
if (!used.hasOwnProperty(key)) {
|
||||
totalUnused++;
|
||||
|
||||
const entry = data.translations[""][key];
|
||||
if (entry.comments === undefined) {
|
||||
entry.comments = {};
|
||||
}
|
||||
|
||||
const flagData = entry.comments.flag ?? "";
|
||||
const flags = flagData.split(/\s*,\s*/).filter((s) => s !== "");
|
||||
|
||||
if (!flags.includes("unused")) {
|
||||
flags.push("unused");
|
||||
}
|
||||
|
||||
entry.comments.flag = flags.join(", ");
|
||||
|
||||
data.translations[""][key] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
await writeLocale(locale, data);
|
||||
});
|
||||
|
||||
console.log(`=> Found ${totalUnused} unused strings`);
|
||||
})();
|
||||
}
|
||||
|
||||
async function synchronize(options, ...other) {
|
||||
const baseData = await readLocale(baseLocale);
|
||||
|
||||
await processLocale(options, async (locale) => {
|
||||
if (locale === baseLocale) return;
|
||||
|
||||
const data = await readLocale(locale);
|
||||
|
||||
for (let [key, val] of Object.entries(baseData.translations[""])) {
|
||||
if (key === "") continue;
|
||||
|
||||
const baseEntry = baseData.translations[""][key];
|
||||
const entry = data.translations[""][key];
|
||||
|
||||
if (entry === undefined) {
|
||||
// Do nothing
|
||||
} else {
|
||||
entry.comments = baseEntry.comments;
|
||||
data.translations[""][key] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
for (let [key, val] of Object.entries(data.translations[""])) {
|
||||
if (key === "") continue;
|
||||
|
||||
const baseEntry = baseData.translations[""][key];
|
||||
const entry = data.translations[""][key];
|
||||
|
||||
if (baseEntry === undefined) {
|
||||
delete data.translations[""][key];
|
||||
}
|
||||
}
|
||||
|
||||
await writeLocale(locale, data);
|
||||
});
|
||||
}
|
||||
|
||||
const options = getopts(process.argv.slice(2), {
|
||||
boolean: ["h", "v"],
|
||||
alias: {
|
||||
help: ["h"],
|
||||
locale: ["l"],
|
||||
verbose: ["v"],
|
||||
},
|
||||
stopEarly: true,
|
||||
});
|
||||
|
||||
const [command, ...params] = options._;
|
||||
|
||||
if (command === "rehash") {
|
||||
await rehash(options, ...params);
|
||||
} else if (command === "sync") {
|
||||
await synchronize(options, ...params);
|
||||
} else if (command === "delete") {
|
||||
await deleteByPrefix(options, ...params);
|
||||
} else if (command === "fuzzy") {
|
||||
await markFuzzy(options, ...params);
|
||||
} else {
|
||||
console.log(`Translations manipulation script.
|
||||
How to use:
|
||||
./scripts/translation.js <options> <subcommand>
|
||||
|
||||
Available options:
|
||||
|
||||
--locale -l : specify a concrete locale
|
||||
--verbose -v : enables verbose output
|
||||
--help -h : prints this help
|
||||
|
||||
Available subcommands:
|
||||
|
||||
rehash : reads and writes all translations files, sorting and validating
|
||||
sync : synchronize baselocale file with all other locale files
|
||||
delete <prefix> : delete all entries that matches the prefix
|
||||
fuzzy <prefix> : mark as fuzzy all entries that matches the prefix
|
||||
`);
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
import { promises as fs } from "fs";
|
||||
import gt from "gettext-parser";
|
||||
import l from "lodash";
|
||||
import path from "path";
|
||||
|
||||
async function* getFiles(dir) {
|
||||
const dirents = await fs.readdir(dir, { withFileTypes: true });
|
||||
for (const dirent of dirents) {
|
||||
const res = path.resolve(dir, dirent.name);
|
||||
if (dirent.isDirectory()) {
|
||||
yield* getFiles(res);
|
||||
} else {
|
||||
yield res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const fileRe = /.+\.po$/;
|
||||
const target = path.normalize("./translations/");
|
||||
const parent = path.join(target, "..");
|
||||
for await (const f of getFiles(target)) {
|
||||
if (!fileRe.test(f)) continue;
|
||||
const entry = path.relative(parent, f);
|
||||
console.log(`=> processing: ${entry}`);
|
||||
const content = await fs.readFile(f);
|
||||
const data = gt.po.parse(content, "utf-8");
|
||||
const buff = gt.po.compile(data, { sort: true });
|
||||
await fs.writeFile(f, buff);
|
||||
}
|
||||
})();
|
|
@ -18,7 +18,7 @@
|
|||
[app.main.ui.components.forms :as fm]
|
||||
[app.main.ui.components.link :as lk]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.i18n :refer [tr tr-html]]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.router :as rt]
|
||||
[app.util.storage :as sto]
|
||||
[beicon.v2.core :as rx]
|
||||
|
@ -197,10 +197,11 @@
|
|||
[]
|
||||
(let [terms-label
|
||||
(mf/html
|
||||
[:& tr-html
|
||||
[:> i18n/tr-html*
|
||||
{:tag-name "div"
|
||||
:label "auth.terms-and-privacy-agreement"
|
||||
:params [cf/terms-of-service-uri cf/privacy-policy-uri]}])]
|
||||
:content (tr "auth.terms-and-privacy-agreement"
|
||||
cf/terms-of-service-uri
|
||||
cf/privacy-policy-uri)}])]
|
||||
|
||||
[:div {:class (stl/css :fields-row :input-visible :accept-terms-and-privacy-wrapper)}
|
||||
[:& fm/input {:name :accept-terms-and-privacy
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
[app.main.store :as st]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr t]]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.keyboard :as k]
|
||||
[goog.events :as events]
|
||||
[rumext.v2 :as mf])
|
||||
|
@ -30,15 +30,13 @@
|
|||
cancel-label
|
||||
accept-label
|
||||
accept-style] :as props}]
|
||||
(let [locale (mf/deref i18n/locale)
|
||||
|
||||
on-accept (or on-accept identity)
|
||||
(let [on-accept (or on-accept identity)
|
||||
on-cancel (or on-cancel identity)
|
||||
message (or message (t locale "ds.confirm-title"))
|
||||
message (or message (tr "ds.confirm-title"))
|
||||
cancel-label (or cancel-label (tr "ds.confirm-cancel"))
|
||||
accept-label (or accept-label (tr "ds.confirm-ok"))
|
||||
accept-style (or accept-style :danger)
|
||||
title (or title (t locale "ds.confirm-title"))
|
||||
title (or title (tr "ds.confirm-title"))
|
||||
|
||||
accept-fn
|
||||
(mf/use-callback
|
||||
|
|
|
@ -167,7 +167,7 @@
|
|||
[:div {:class (stl/css :dashboard-fonts-hero)}
|
||||
[:div {:class (stl/css :desc)}
|
||||
[:h2 (tr "labels.upload-custom-fonts")]
|
||||
[:& i18n/tr-html {:label "dashboard.fonts.hero-text1"}]
|
||||
[:> i18n/tr-html* {:content (tr "dashboard.fonts.hero-text1")}]
|
||||
|
||||
[:button {:class (stl/css :btn-primary)
|
||||
:on-click on-click
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc empty-placeholder
|
||||
[{:keys [dragging? limit origin create-fn] :as props}]
|
||||
[{:keys [dragging? limit origin create-fn]}]
|
||||
(let [on-click
|
||||
(mf/use-fn
|
||||
(mf/deps create-fn)
|
||||
|
@ -29,7 +29,7 @@
|
|||
[:div {:class (stl/css :grid-empty-placeholder :libs)
|
||||
:data-testid "empty-placeholder"}
|
||||
[:div {:class (stl/css :text)}
|
||||
[:& i18n/tr-html {:label "dashboard.empty-placeholder-drafts"}]]]
|
||||
[:> i18n/tr-html* {:content (tr "dashboard.empty-placeholder-drafts")}]]]
|
||||
|
||||
:else
|
||||
[:div
|
||||
|
|
|
@ -693,8 +693,8 @@
|
|||
[:div {:class (stl/css :empty-invitations)}
|
||||
[:span (tr "labels.no-invitations")]
|
||||
(when can-invite?
|
||||
[:& i18n/tr-html {:label "labels.no-invitations-hint"
|
||||
:tag-name "span"}])])
|
||||
[:> i18n/tr-html* {:content (tr "labels.no-invitations-hint")
|
||||
:tag-name "span"}])])
|
||||
|
||||
(mf/defc invitation-section
|
||||
[{:keys [team invitations] :as props}]
|
||||
|
@ -878,8 +878,8 @@
|
|||
[:div {:class (stl/css :webhooks-hero-container)}
|
||||
[:h2 {:class (stl/css :hero-title)}
|
||||
(tr "labels.webhooks")]
|
||||
[:& i18n/tr-html {:class (stl/css :hero-desc)
|
||||
:label "dashboard.webhooks.description"}]
|
||||
[:> i18n/tr-html* {:class (stl/css :hero-desc)
|
||||
:content (tr "dashboard.webhooks.description")}]
|
||||
[:button {:class (stl/css :hero-btn)
|
||||
:on-click #(st/emit! (modal/show :webhook {}))}
|
||||
(tr "dashboard.webhooks.create")]])
|
||||
|
|
|
@ -432,12 +432,12 @@
|
|||
[:label {:for (str "export-" type)
|
||||
:class (stl/css-case :global/checked (= selected type))}
|
||||
;; Execution time translation strings:
|
||||
;; dashboard.export.options.all.message
|
||||
;; dashboard.export.options.all.title
|
||||
;; dashboard.export.options.detach.message
|
||||
;; dashboard.export.options.detach.title
|
||||
;; dashboard.export.options.merge.message
|
||||
;; dashboard.export.options.merge.title
|
||||
;; (tr "dashboard.export.options.all.message")
|
||||
;; (tr "dashboard.export.options.all.title")
|
||||
;; (tr "dashboard.export.options.detach.message")
|
||||
;; (tr "dashboard.export.options.detach.title")
|
||||
;; (tr "dashboard.export.options.merge.message")
|
||||
;; (tr "dashboard.export.options.merge.title")
|
||||
[:span {:class (stl/css-case :global/checked (= selected type))}
|
||||
(when (= selected type)
|
||||
i/status-tick)]
|
||||
|
|
|
@ -33,18 +33,16 @@
|
|||
(mf/defc settings
|
||||
[{:keys [route] :as props}]
|
||||
(let [section (get-in route [:data :name])
|
||||
profile (mf/deref refs/profile)
|
||||
locale (mf/deref i18n/locale)]
|
||||
profile (mf/deref refs/profile)]
|
||||
|
||||
(hooks/use-shortcuts ::dashboard sc/shortcuts)
|
||||
|
||||
(mf/use-effect
|
||||
#(when (nil? profile)
|
||||
(mf/with-effect [profile]
|
||||
(when (nil? profile)
|
||||
(st/emit! (rt/nav :auth-login))))
|
||||
|
||||
[:section {:class (stl/css :dashboard-layout-refactor :dashboard)}
|
||||
[:& sidebar {:profile profile
|
||||
:locale locale
|
||||
:section section}]
|
||||
|
||||
[:div {:class (stl/css :dashboard-content)}
|
||||
|
@ -52,16 +50,16 @@
|
|||
[:section {:class (stl/css :dashboard-container)}
|
||||
(case section
|
||||
:settings-profile
|
||||
[:& profile-page {:locale locale}]
|
||||
[:& profile-page]
|
||||
|
||||
:settings-feedback
|
||||
[:& feedback-page]
|
||||
|
||||
:settings-password
|
||||
[:& password-page {:locale locale}]
|
||||
[:& password-page]
|
||||
|
||||
:settings-options
|
||||
[:& options-page {:locale locale}]
|
||||
[:& options-page]
|
||||
|
||||
:settings-access-tokens
|
||||
[:& access-tokens-page])]]]))
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
[app.main.store :as st]
|
||||
[app.main.ui.components.forms :as fm]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [t tr]]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[cljs.spec.alpha :as s]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
|
@ -69,7 +69,7 @@
|
|||
::password-old]))
|
||||
|
||||
(mf/defc password-form
|
||||
[{:keys [locale] :as props}]
|
||||
[]
|
||||
(let [initial (mf/use-memo (constantly {:password-old nil}))
|
||||
form (fm/use-form :spec ::password-form
|
||||
:validators [(fm/validate-not-all-spaces :password-old (tr "auth.password-not-empty"))
|
||||
|
@ -86,35 +86,35 @@
|
|||
{:type "password"
|
||||
:name :password-old
|
||||
:auto-focus? true
|
||||
:label (t locale "labels.old-password")}]]
|
||||
:label (tr "labels.old-password")}]]
|
||||
|
||||
[:div {:class (stl/css :fields-row)}
|
||||
[:& fm/input
|
||||
{:type "password"
|
||||
:name :password-1
|
||||
:show-success? true
|
||||
:label (t locale "labels.new-password")}]]
|
||||
:label (tr "labels.new-password")}]]
|
||||
|
||||
[:div {:class (stl/css :fields-row)}
|
||||
[:& fm/input
|
||||
{:type "password"
|
||||
:name :password-2
|
||||
:show-success? true
|
||||
:label (t locale "labels.confirm-password")}]]
|
||||
:label (tr "labels.confirm-password")}]]
|
||||
|
||||
[:> fm/submit-button*
|
||||
{:label (t locale "dashboard.password-change")
|
||||
{:label (tr "dashboard.password-change")
|
||||
:data-testid "submit-password"
|
||||
:class (stl/css :update-btn)}]]))
|
||||
|
||||
;; --- Password Page
|
||||
|
||||
(mf/defc password-page
|
||||
[{:keys [locale]}]
|
||||
(mf/use-effect
|
||||
#(dom/set-html-title (tr "title.settings.password")))
|
||||
[]
|
||||
(mf/with-effect []
|
||||
(dom/set-html-title (tr "title.settings.password")))
|
||||
|
||||
[:section {:class (stl/css :dashboard-settings)}
|
||||
[:div {:class (stl/css :form-container)}
|
||||
[:h2 (t locale "dashboard.password-change")]
|
||||
[:& password-form {:locale locale}]]])
|
||||
[:h2 (tr "dashboard.password-change")]
|
||||
[:& password-form]]])
|
||||
|
|
|
@ -115,10 +115,9 @@
|
|||
(mf/defc sidebar
|
||||
{::mf/wrap [mf/memo]
|
||||
::mf/props :obj}
|
||||
[{:keys [profile locale section]}]
|
||||
[{:keys [profile section]}]
|
||||
[:div {:class (stl/css :dashboard-sidebar :settings)}
|
||||
[:& sidebar-content {:profile profile
|
||||
:section section}]
|
||||
[:& profile-section {:profile profile
|
||||
:locale locale}]])
|
||||
[:& profile-section {:profile profile}]])
|
||||
|
||||
|
|
|
@ -142,9 +142,9 @@
|
|||
[:div {:class (stl/css :global/attr-label)}
|
||||
(tr "inspect.attributes.typography.text-decoration")]
|
||||
;; Execution time translation strings:
|
||||
;; inspect.attributes.typography.text-decoration.none
|
||||
;; inspect.attributes.typography.text-decoration.strikethrough
|
||||
;; inspect.attributes.typography.text-decoration.underline
|
||||
;; (tr "inspect.attributes.typography.text-decoration.none")
|
||||
;; (tr "inspect.attributes.typography.text-decoration.strikethrough")
|
||||
;; (tr "inspect.attributes.typography.text-decoration.underline")
|
||||
[:div {:class (stl/css :global/attr-value)}
|
||||
[:& copy-button {:data (copy-style-data style :text-decoration)}
|
||||
[:div {:class (stl/css :button-children)}
|
||||
|
@ -155,11 +155,11 @@
|
|||
[:div {:class (stl/css :global/attr-label)}
|
||||
(tr "inspect.attributes.typography.text-transform")]
|
||||
;; Execution time translation strings:
|
||||
;; inspect.attributes.typography.text-transform.lowercase
|
||||
;; inspect.attributes.typography.text-transform.none
|
||||
;; inspect.attributes.typography.text-transform.titlecase
|
||||
;; inspect.attributes.typography.text-transform.uppercase
|
||||
;; inspect.attributes.typography.text-transform.unset
|
||||
;; (tr "inspect.attributes.typography.text-transform.lowercase")
|
||||
;; (tr "inspect.attributes.typography.text-transform.none")
|
||||
;; (tr "inspect.attributes.typography.text-transform.titlecase")
|
||||
;; (tr "inspect.attributes.typography.text-transform.uppercase")
|
||||
;; (tr "inspect.attributes.typography.text-transform.unset")
|
||||
[:div {:class (stl/css :global/attr-value)}
|
||||
[:& copy-button {:data (copy-style-data style :text-transform)}
|
||||
[:div {:class (stl/css :button-children)}
|
||||
|
|
|
@ -94,18 +94,18 @@
|
|||
[:*
|
||||
[:span {:class (stl/css :shape-icon)}
|
||||
[:& sir/element-icon {:shape first-shape :main-instance? main-instance?}]]
|
||||
;; Execution time translation strings:
|
||||
;; inspect.tabs.code.selected.circle
|
||||
;; inspect.tabs.code.selected.component
|
||||
;; inspect.tabs.code.selected.curve
|
||||
;; inspect.tabs.code.selected.frame
|
||||
;; inspect.tabs.code.selected.group
|
||||
;; inspect.tabs.code.selected.image
|
||||
;; inspect.tabs.code.selected.mask
|
||||
;; inspect.tabs.code.selected.path
|
||||
;; inspect.tabs.code.selected.rect
|
||||
;; inspect.tabs.code.selected.svg-raw
|
||||
;; inspect.tabs.code.selected.text
|
||||
;; Execution time translation strings:
|
||||
;; (tr "inspect.tabs.code.selected.circle")
|
||||
;; (tr "inspect.tabs.code.selected.component")
|
||||
;; (tr "inspect.tabs.code.selected.curve")
|
||||
;; (tr "inspect.tabs.code.selected.frame")
|
||||
;; (tr "inspect.tabs.code.selected.group")
|
||||
;; (tr "inspect.tabs.code.selected.image")
|
||||
;; (tr "inspect.tabs.code.selected.mask")
|
||||
;; (tr "inspect.tabs.code.selected.path")
|
||||
;; (tr "inspect.tabs.code.selected.rect")
|
||||
;; (tr "inspect.tabs.code.selected.svg-raw")
|
||||
;; (tr "inspect.tabs.code.selected.text")
|
||||
[:span {:class (stl/css :layer-title)} (:name first-shape)]])]
|
||||
[:div {:class (stl/css :inspect-content)}
|
||||
[:& tab-container {:on-change-tab handle-change-tab
|
||||
|
|
|
@ -474,7 +474,7 @@
|
|||
[:& menu-separator]
|
||||
(for [entry components-menu-entries :when (not (nil? entry))]
|
||||
[:& menu-entry {:key (uuid/next)
|
||||
:title (tr (:msg entry))
|
||||
:title (:title entry)
|
||||
:shortcut (when (contains? entry :shortcut) (sc/get-tooltip (:shortcut entry)))
|
||||
:on-click (:action entry)}])])]))
|
||||
|
||||
|
|
|
@ -421,27 +421,27 @@
|
|||
(ts/schedule 1000 do-show-component)))
|
||||
|
||||
menu-entries [(when (and (not multi) main-instance?)
|
||||
{:msg "workspace.shape.menu.show-in-assets"
|
||||
{:title (tr "workspace.shape.menu.show-in-assets")
|
||||
:action do-show-in-assets})
|
||||
(when (and (not multi) main-instance? local-component? lacks-annotation? components-v2)
|
||||
{:msg "workspace.shape.menu.create-annotation"
|
||||
{:title (tr "workspace.shape.menu.create-annotation")
|
||||
:action do-create-annotation})
|
||||
(when can-detach?
|
||||
{:msg (if (> (count copies) 1)
|
||||
"workspace.shape.menu.detach-instances-in-bulk"
|
||||
"workspace.shape.menu.detach-instance")
|
||||
{:title (if (> (count copies) 1)
|
||||
(tr "workspace.shape.menu.detach-instances-in-bulk")
|
||||
(tr "workspace.shape.menu.detach-instance"))
|
||||
:action do-detach-component
|
||||
:shortcut :detach-component})
|
||||
(when can-reset-overrides?
|
||||
{:msg "workspace.shape.menu.reset-overrides"
|
||||
{:title (tr "workspace.shape.menu.reset-overrides")
|
||||
:action do-reset-component})
|
||||
(when (and (seq restorable-copies) components-v2)
|
||||
{:msg "workspace.shape.menu.restore-main"
|
||||
{:title (tr "workspace.shape.menu.restore-main")
|
||||
:action do-restore-component})
|
||||
(when can-show-component?
|
||||
{:msg "workspace.shape.menu.show-main"
|
||||
{:title (tr "workspace.shape.menu.show-main")
|
||||
:action do-show-component})
|
||||
(when can-update-main?
|
||||
{:msg "workspace.shape.menu.update-main"
|
||||
{:title (tr "workspace.shape.menu.update-main")
|
||||
:action do-update-component})]]
|
||||
(filter (complement nil?) menu-entries)))
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
[app.main.store :as st]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [t] :as i18n]
|
||||
[app.util.i18n :refer [tr] :as i18n]
|
||||
[cuerdas.core :as str]
|
||||
[okulary.core :as l]
|
||||
[rumext.v2 :as mf]))
|
||||
|
@ -104,49 +104,49 @@
|
|||
|
||||
(defn entry-type->message
|
||||
"Formats the message that will be displayed to the user"
|
||||
[locale type multiple?]
|
||||
[type multiple?]
|
||||
(let [arity (if multiple? "multiple" "single")
|
||||
attribute (name (or type :multiple))]
|
||||
;; Execution time translation strings:
|
||||
;; workspace.undo.entry.multiple.circle
|
||||
;; workspace.undo.entry.multiple.color
|
||||
;; workspace.undo.entry.multiple.component
|
||||
;; workspace.undo.entry.multiple.curve
|
||||
;; workspace.undo.entry.multiple.frame
|
||||
;; workspace.undo.entry.multiple.group
|
||||
;; workspace.undo.entry.multiple.media
|
||||
;; workspace.undo.entry.multiple.multiple
|
||||
;; workspace.undo.entry.multiple.page
|
||||
;; workspace.undo.entry.multiple.path
|
||||
;; workspace.undo.entry.multiple.rect
|
||||
;; workspace.undo.entry.multiple.shape
|
||||
;; workspace.undo.entry.multiple.text
|
||||
;; workspace.undo.entry.multiple.typography
|
||||
;; workspace.undo.entry.single.circle
|
||||
;; workspace.undo.entry.single.color
|
||||
;; workspace.undo.entry.single.component
|
||||
;; workspace.undo.entry.single.curve
|
||||
;; workspace.undo.entry.single.frame
|
||||
;; workspace.undo.entry.single.group
|
||||
;; workspace.undo.entry.single.image
|
||||
;; workspace.undo.entry.single.media
|
||||
;; workspace.undo.entry.single.multiple
|
||||
;; workspace.undo.entry.single.page
|
||||
;; workspace.undo.entry.single.path
|
||||
;; workspace.undo.entry.single.rect
|
||||
;; workspace.undo.entry.single.shape
|
||||
;; workspace.undo.entry.single.text
|
||||
;; workspace.undo.entry.single.typography
|
||||
(t locale (str/format "workspace.undo.entry.%s.%s" arity attribute))))
|
||||
;; (tr "workspace.undo.entry.multiple.circle")
|
||||
;; (tr "workspace.undo.entry.multiple.color")
|
||||
;; (tr "workspace.undo.entry.multiple.component")
|
||||
;; (tr "workspace.undo.entry.multiple.curve")
|
||||
;; (tr "workspace.undo.entry.multiple.frame")
|
||||
;; (tr "workspace.undo.entry.multiple.group")
|
||||
;; (tr "workspace.undo.entry.multiple.media")
|
||||
;; (tr "workspace.undo.entry.multiple.multiple")
|
||||
;; (tr "workspace.undo.entry.multiple.page")
|
||||
;; (tr "workspace.undo.entry.multiple.path")
|
||||
;; (tr "workspace.undo.entry.multiple.rect")
|
||||
;; (tr "workspace.undo.entry.multiple.shape")
|
||||
;; (tr "workspace.undo.entry.multiple.text")
|
||||
;; (tr "workspace.undo.entry.multiple.typography")
|
||||
;; (tr "workspace.undo.entry.single.circle")
|
||||
;; (tr "workspace.undo.entry.single.color")
|
||||
;; (tr "workspace.undo.entry.single.component")
|
||||
;; (tr "workspace.undo.entry.single.curve")
|
||||
;; (tr "workspace.undo.entry.single.frame")
|
||||
;; (tr "workspace.undo.entry.single.group")
|
||||
;; (tr "workspace.undo.entry.single.image")
|
||||
;; (tr "workspace.undo.entry.single.media")
|
||||
;; (tr "workspace.undo.entry.single.multiple")
|
||||
;; (tr "workspace.undo.entry.single.page")
|
||||
;; (tr "workspace.undo.entry.single.path")
|
||||
;; (tr "workspace.undo.entry.single.rect")
|
||||
;; (tr "workspace.undo.entry.single.shape")
|
||||
;; (tr "workspace.undo.entry.single.text")
|
||||
;; (tr "workspace.undo.entry.single.typography")
|
||||
(tr (str/format "workspace.undo.entry.%s.%s" arity attribute))))
|
||||
|
||||
(defn entry->message [locale entry]
|
||||
(let [value (entry-type->message locale (:type entry) (= :multiple (:id entry)))]
|
||||
(defn entry->message [entry]
|
||||
(let [value (entry-type->message (:type entry) (= :multiple (:id entry)))]
|
||||
(case (:operation entry)
|
||||
:new (t locale "workspace.undo.entry.new" value)
|
||||
:modify (t locale "workspace.undo.entry.modify" value)
|
||||
:delete (t locale "workspace.undo.entry.delete" value)
|
||||
:move (t locale "workspace.undo.entry.move" value)
|
||||
(t locale "workspace.undo.entry.unknown" value))))
|
||||
:new (tr "workspace.undo.entry.new" value)
|
||||
:modify (tr "workspace.undo.entry.modify" value)
|
||||
:delete (tr "workspace.undo.entry.delete" value)
|
||||
:move (tr "workspace.undo.entry.move" value)
|
||||
(tr "workspace.undo.entry.unknown" value))))
|
||||
|
||||
(defn entry->icon [{:keys [type]}]
|
||||
(case type
|
||||
|
@ -284,8 +284,9 @@
|
|||
|
||||
nil)]))
|
||||
|
||||
(mf/defc history-entry [{:keys [locale entry idx-entry disabled? current?]}]
|
||||
(mf/defc history-entry
|
||||
{::mf/props :obj}
|
||||
[{:keys [entry idx-entry disabled? current?]}]
|
||||
(let [hover? (mf/use-state false)
|
||||
show-detail? (mf/use-state false)
|
||||
toggle-show-detail
|
||||
|
@ -309,7 +310,7 @@
|
|||
[:div {:class (stl/css :history-entry-summary)}
|
||||
[:div {:class (stl/css :history-entry-summary-icon)}
|
||||
(entry->icon entry)]
|
||||
[:div {:class (stl/css :history-entry-summary-text)} (entry->message locale entry)]
|
||||
[:div {:class (stl/css :history-entry-summary-text)} (entry->message entry)]
|
||||
(when (:detail entry)
|
||||
[:div {:class (stl/css-case :history-entry-summary-button true
|
||||
:button-opened @show-detail?)
|
||||
|
@ -320,9 +321,9 @@
|
|||
(when @show-detail?
|
||||
[:& history-entry-details {:entry entry}])]))
|
||||
|
||||
(mf/defc history-toolbox []
|
||||
(let [locale (mf/deref i18n/locale)
|
||||
objects (mf/deref refs/workspace-page-objects)
|
||||
(mf/defc history-toolbox
|
||||
[]
|
||||
(let [objects (mf/deref refs/workspace-page-objects)
|
||||
{:keys [items index]} (mf/deref workspace-undo)
|
||||
entries (parse-entries items objects)
|
||||
toggle-history
|
||||
|
@ -331,18 +332,17 @@
|
|||
(vary-meta assoc ::ev/origin "history-toolbox"))))]
|
||||
[:div {:class (stl/css :history-toolbox)}
|
||||
[:div {:class (stl/css :history-toolbox-title)}
|
||||
[:span (t locale "workspace.undo.title")]
|
||||
[:span (tr "workspace.undo.title")]
|
||||
[:div {:class (stl/css :close-button)
|
||||
:on-click toggle-history}
|
||||
i/close]]
|
||||
(if (empty? entries)
|
||||
[:div {:class (stl/css :history-entry-empty)}
|
||||
[:div {:class (stl/css :history-entry-empty-icon)} i/history]
|
||||
[:div {:class (stl/css :history-entry-empty-msg)} (t locale "workspace.undo.empty")]]
|
||||
[:div {:class (stl/css :history-entry-empty-msg)} (tr "workspace.undo.empty")]]
|
||||
[:ul {:class (stl/css :history-entries)}
|
||||
(for [[idx-entry entry] (->> entries (map-indexed vector) reverse)] #_[i (range 0 10)]
|
||||
[:& history-entry {:key (str "entry-" idx-entry)
|
||||
:locale locale
|
||||
:entry entry
|
||||
:idx-entry idx-entry
|
||||
:current? (= idx-entry index)
|
||||
|
|
|
@ -512,12 +512,12 @@
|
|||
[:& dropdown {:show show :on-close on-close}
|
||||
[:ul {:class (stl/css-case :custom-select-dropdown true
|
||||
:not-main (not main-instance))}
|
||||
(for [{:keys [msg] :as entry} menu-entries]
|
||||
(when (some? msg)
|
||||
[:li {:key msg
|
||||
(for [{:keys [title action]} menu-entries]
|
||||
(when (some? title)
|
||||
[:li {:key title
|
||||
:class (stl/css :dropdown-element)
|
||||
:on-click (partial do-action (:action entry))}
|
||||
[:span {:class (stl/css :dropdown-label)} (tr msg)]]))]]))
|
||||
:on-click (partial do-action action)}
|
||||
[:span {:class (stl/css :dropdown-label)} title]]))]]))
|
||||
|
||||
(mf/defc component-menu
|
||||
{::mf/props :obj}
|
||||
|
|
|
@ -52,144 +52,166 @@
|
|||
(defn translation-keyname
|
||||
[type keyname]
|
||||
;; Execution time translation strings:
|
||||
;; shortcut-subsection.alignment
|
||||
;; shortcut-subsection.edit
|
||||
;; shortcut-subsection.general-dashboard
|
||||
;; shortcut-subsection.general-viewer
|
||||
;; shortcut-subsection.main-menu
|
||||
;; shortcut-subsection.modify-layers
|
||||
;; shortcut-subsection.navigation-dashboard
|
||||
;; shortcut-subsection.navigation-viewer
|
||||
;; shortcut-subsection.navigation-workspace
|
||||
;; shortcut-subsection.panels
|
||||
;; shortcut-subsection.path-editor
|
||||
;; shortcut-subsection.shape
|
||||
;; shortcut-subsection.tools
|
||||
;; shortcut-subsection.zoom-viewer
|
||||
;; shortcut-subsection.zoom-workspace
|
||||
;; shortcuts.add-comment
|
||||
;; shortcuts.add-node
|
||||
;; shortcuts.align-bottom
|
||||
;; shortcuts.align-hcenter
|
||||
;; shortcuts.align-left
|
||||
;; shortcuts.align-right
|
||||
;; shortcuts.align-top
|
||||
;; shortcuts.align-vcenter
|
||||
;; shortcuts.artboard-selection
|
||||
;; shortcuts.bool-difference
|
||||
;; shortcuts.bool-exclude
|
||||
;; shortcuts.bool-intersection
|
||||
;; shortcuts.bool-union
|
||||
;; shortcuts.bring-back
|
||||
;; shortcuts.bring-backward
|
||||
;; shortcuts.bring-forward
|
||||
;; shortcuts.bring-front
|
||||
;; shortcuts.clear-undo
|
||||
;; shortcuts.copy
|
||||
;; shortcuts.create-component
|
||||
;; shortcuts.create-new-project
|
||||
;; shortcuts.cut
|
||||
;; shortcuts.decrease-zoom
|
||||
;; shortcuts.delete
|
||||
;; shortcuts.delete-node
|
||||
;; shortcuts.detach-component
|
||||
;; shortcuts.draw-curve
|
||||
;; shortcuts.draw-ellipse
|
||||
;; shortcuts.draw-frame
|
||||
;; shortcuts.draw-nodes
|
||||
;; shortcuts.draw-path
|
||||
;; shortcuts.draw-rect
|
||||
;; shortcuts.draw-text
|
||||
;; shortcuts.duplicate
|
||||
;; shortcuts.escape
|
||||
;; shortcuts.export-shapes
|
||||
;; shortcuts.fit-all
|
||||
;; shortcuts.flip-horizontal
|
||||
;; shortcuts.flip-vertical
|
||||
;; shortcuts.go-to-drafts
|
||||
;; shortcuts.go-to-libs
|
||||
;; shortcuts.go-to-search
|
||||
;; shortcuts.group
|
||||
;; shortcuts.h-distribute
|
||||
;; shortcuts.hide-ui
|
||||
;; shortcuts.increase-zoom
|
||||
;; shortcuts.insert-image
|
||||
;; shortcuts.join-nodes
|
||||
;; shortcuts.make-corner
|
||||
;; shortcuts.make-curve
|
||||
;; shortcuts.mask
|
||||
;; shortcuts.merge-nodes
|
||||
;; shortcuts.move
|
||||
;; shortcuts.move-fast-down
|
||||
;; shortcuts.move-fast-left
|
||||
;; shortcuts.move-fast-right
|
||||
;; shortcuts.move-fast-up
|
||||
;; shortcuts.move-nodes
|
||||
;; shortcuts.move-unit-down
|
||||
;; shortcuts.move-unit-left
|
||||
;; shortcuts.move-unit-right
|
||||
;; shortcuts.move-unit-up
|
||||
;; shortcuts.next-frame
|
||||
;; shortcuts.opacity-0
|
||||
;; shortcuts.opacity-1
|
||||
;; shortcuts.opacity-2
|
||||
;; shortcuts.opacity-3
|
||||
;; shortcuts.opacity-4
|
||||
;; shortcuts.opacity-5
|
||||
;; shortcuts.opacity-6
|
||||
;; shortcuts.opacity-7
|
||||
;; shortcuts.opacity-8
|
||||
;; shortcuts.opacity-9
|
||||
;; shortcuts.open-color-picker
|
||||
;; shortcuts.open-comments
|
||||
;; shortcuts.open-dashboard
|
||||
;; shortcuts.select-prev
|
||||
;; shortcuts.select-next
|
||||
;; shortcuts.open-inspect
|
||||
;; shortcuts.open-interactions
|
||||
;; shortcuts.open-viewer
|
||||
;; shortcuts.open-workspace
|
||||
;; shortcuts.paste
|
||||
;; shortcuts.prev-frame
|
||||
;; shortcuts.redo
|
||||
;; shortcuts.reset-zoom
|
||||
;; shortcuts.select-all
|
||||
;; shortcuts.separate-nodes
|
||||
;; shortcuts.show-pixel-grid
|
||||
;; shortcuts.show-shortcuts
|
||||
;; shortcuts.snap-nodes
|
||||
;; shortcuts.snap-pixel-grid
|
||||
;; shortcuts.start-editing
|
||||
;; shortcuts.start-measure
|
||||
;; shortcuts.stop-measure
|
||||
;; shortcuts.text-align-center
|
||||
;; shortcuts.text-align-left
|
||||
;; shortcuts.text-align-justify
|
||||
;; shortcuts.text-align-right
|
||||
;; shortcuts.thumbnail-set
|
||||
;; shortcuts.toggle-alignment
|
||||
;; shortcuts.toggle-assets
|
||||
;; shortcuts.toggle-colorpalette
|
||||
;; shortcuts.toggle-focus-mode
|
||||
;; shortcuts.toggle-guides
|
||||
;; shortcuts.toggle-history
|
||||
;; shortcuts.toggle-layers
|
||||
;; shortcuts.toggle-lock
|
||||
;; shortcuts.toggle-lock-size
|
||||
;; shortcuts.toggle-rules
|
||||
;; shortcuts.scale
|
||||
;; shortcuts.toggle-snap-guides
|
||||
;; shortcuts.toggle-snap-ruler-guide
|
||||
;; shortcuts.toggle-textpalette
|
||||
;; shortcuts.toggle-visibility
|
||||
;; shortcuts.toggle-zoom-style
|
||||
;; shortcuts.toggle-fullscreen
|
||||
;; shortcuts.undo
|
||||
;; shortcuts.ungroup
|
||||
;; shortcuts.unmask
|
||||
;; shortcuts.v-distribute
|
||||
;; shortcuts.zoom-selected
|
||||
;; shortcuts.toggle-layout-grid
|
||||
(comment
|
||||
(tr "shortcut-subsection.alignment")
|
||||
(tr "shortcut-subsection.edit")
|
||||
(tr "shortcut-subsection.general-dashboard")
|
||||
(tr "shortcut-subsection.general-viewer")
|
||||
(tr "shortcut-subsection.main-menu")
|
||||
(tr "shortcut-subsection.modify-layers")
|
||||
(tr "shortcut-subsection.navigation-dashboard")
|
||||
(tr "shortcut-subsection.navigation-viewer")
|
||||
(tr "shortcut-subsection.navigation-workspace")
|
||||
(tr "shortcut-subsection.panels")
|
||||
(tr "shortcut-subsection.path-editor")
|
||||
(tr "shortcut-subsection.shape")
|
||||
(tr "shortcut-subsection.text-editor")
|
||||
(tr "shortcut-subsection.tools")
|
||||
(tr "shortcut-subsection.zoom-viewer")
|
||||
(tr "shortcut-subsection.zoom-workspace")
|
||||
(tr "shortcuts.add-comment")
|
||||
(tr "shortcuts.add-node")
|
||||
(tr "shortcuts.align-bottom")
|
||||
(tr "shortcuts.align-center")
|
||||
(tr "shortcuts.align-hcenter")
|
||||
(tr "shortcuts.align-justify")
|
||||
(tr "shortcuts.align-left")
|
||||
(tr "shortcuts.align-right")
|
||||
(tr "shortcuts.align-top")
|
||||
(tr "shortcuts.align-vcenter")
|
||||
(tr "shortcuts.artboard-selection")
|
||||
(tr "shortcuts.bold")
|
||||
(tr "shortcuts.bool-difference")
|
||||
(tr "shortcuts.bool-exclude")
|
||||
(tr "shortcuts.bool-intersection")
|
||||
(tr "shortcuts.bool-union")
|
||||
(tr "shortcuts.bring-back")
|
||||
(tr "shortcuts.bring-backward")
|
||||
(tr "shortcuts.bring-forward")
|
||||
(tr "shortcuts.bring-front")
|
||||
(tr "shortcuts.clear-undo")
|
||||
(tr "shortcuts.copy")
|
||||
(tr "shortcuts.create-component")
|
||||
(tr "shortcuts.create-new-project")
|
||||
(tr "shortcuts.cut")
|
||||
(tr "shortcuts.decrease-zoom")
|
||||
(tr "shortcuts.delete")
|
||||
(tr "shortcuts.delete-node")
|
||||
(tr "shortcuts.detach-component")
|
||||
(tr "shortcuts.draw-curve")
|
||||
(tr "shortcuts.draw-ellipse")
|
||||
(tr "shortcuts.draw-frame")
|
||||
(tr "shortcuts.draw-nodes")
|
||||
(tr "shortcuts.draw-path")
|
||||
(tr "shortcuts.draw-rect")
|
||||
(tr "shortcuts.draw-text")
|
||||
(tr "shortcuts.duplicate")
|
||||
(tr "shortcuts.escape")
|
||||
(tr "shortcuts.export-shapes")
|
||||
(tr "shortcuts.fit-all")
|
||||
(tr "shortcuts.flip-horizontal")
|
||||
(tr "shortcuts.flip-vertical")
|
||||
(tr "shortcuts.font-size-dec")
|
||||
(tr "shortcuts.font-size-inc")
|
||||
(tr "shortcuts.go-to-drafts")
|
||||
(tr "shortcuts.go-to-libs")
|
||||
(tr "shortcuts.go-to-search")
|
||||
(tr "shortcuts.group")
|
||||
(tr "shortcuts.h-distribute")
|
||||
(tr "shortcuts.hide-ui")
|
||||
(tr "shortcuts.increase-zoom")
|
||||
(tr "shortcuts.insert-image")
|
||||
(tr "shortcuts.italic")
|
||||
(tr "shortcuts.join-nodes")
|
||||
(tr "shortcuts.letter-spacing-dec")
|
||||
(tr "shortcuts.letter-spacing-inc")
|
||||
(tr "shortcuts.line-height-dec")
|
||||
(tr "shortcuts.line-height-inc")
|
||||
(tr "shortcuts.line-through")
|
||||
(tr "shortcuts.make-corner")
|
||||
(tr "shortcuts.make-curve")
|
||||
(tr "shortcuts.mask")
|
||||
(tr "shortcuts.merge-nodes")
|
||||
(tr "shortcuts.move")
|
||||
(tr "shortcuts.move-fast-down")
|
||||
(tr "shortcuts.move-fast-left")
|
||||
(tr "shortcuts.move-fast-right")
|
||||
(tr "shortcuts.move-fast-up")
|
||||
(tr "shortcuts.move-nodes")
|
||||
(tr "shortcuts.move-unit-down")
|
||||
(tr "shortcuts.move-unit-left")
|
||||
(tr "shortcuts.move-unit-right")
|
||||
(tr "shortcuts.move-unit-up")
|
||||
(tr "shortcuts.next-frame")
|
||||
(tr "shortcuts.opacity-0")
|
||||
(tr "shortcuts.opacity-1")
|
||||
(tr "shortcuts.opacity-2")
|
||||
(tr "shortcuts.opacity-3")
|
||||
(tr "shortcuts.opacity-4")
|
||||
(tr "shortcuts.opacity-5")
|
||||
(tr "shortcuts.opacity-6")
|
||||
(tr "shortcuts.opacity-7")
|
||||
(tr "shortcuts.opacity-8")
|
||||
(tr "shortcuts.opacity-9")
|
||||
(tr "shortcuts.open-color-picker")
|
||||
(tr "shortcuts.open-comments")
|
||||
(tr "shortcuts.open-dashboard")
|
||||
(tr "shortcuts.open-inspect")
|
||||
(tr "shortcuts.open-interactions")
|
||||
(tr "shortcuts.open-viewer")
|
||||
(tr "shortcuts.open-workspace")
|
||||
(tr "shortcuts.paste")
|
||||
(tr "shortcuts.prev-frame")
|
||||
(tr "shortcuts.redo")
|
||||
(tr "shortcuts.reset-zoom")
|
||||
(tr "shortcuts.scale")
|
||||
(tr "shortcuts.search-placeholder")
|
||||
(tr "shortcuts.select-all")
|
||||
(tr "shortcuts.select-next")
|
||||
(tr "shortcuts.select-parent-layer")
|
||||
(tr "shortcuts.select-prev")
|
||||
(tr "shortcuts.separate-nodes")
|
||||
(tr "shortcuts.show-pixel-grid")
|
||||
(tr "shortcuts.show-shortcuts")
|
||||
(tr "shortcuts.snap-nodes")
|
||||
(tr "shortcuts.snap-pixel-grid")
|
||||
(tr "shortcuts.start-editing")
|
||||
(tr "shortcuts.start-measure")
|
||||
(tr "shortcuts.stop-measure")
|
||||
(tr "shortcuts.text-align-center")
|
||||
(tr "shortcuts.text-align-justify")
|
||||
(tr "shortcuts.text-align-left")
|
||||
(tr "shortcuts.text-align-right")
|
||||
(tr "shortcuts.thumbnail-set")
|
||||
(tr "shortcuts.toggle-alignment")
|
||||
(tr "shortcuts.toggle-assets")
|
||||
(tr "shortcuts.toggle-colorpalette")
|
||||
(tr "shortcuts.toggle-focus-mode")
|
||||
(tr "shortcuts.toggle-fullscreen")
|
||||
(tr "shortcuts.toggle-guides")
|
||||
(tr "shortcuts.toggle-history")
|
||||
(tr "shortcuts.toggle-layers")
|
||||
(tr "shortcuts.toggle-layout-flex")
|
||||
(tr "shortcuts.toggle-layout-grid")
|
||||
(tr "shortcuts.toggle-lock")
|
||||
(tr "shortcuts.toggle-lock-size")
|
||||
(tr "shortcuts.toggle-rulers")
|
||||
(tr "shortcuts.toggle-rules")
|
||||
(tr "shortcuts.toggle-snap-guides")
|
||||
(tr "shortcuts.toggle-snap-ruler-guide")
|
||||
(tr "shortcuts.toggle-textpalette")
|
||||
(tr "shortcuts.toggle-theme")
|
||||
(tr "shortcuts.toggle-visibility")
|
||||
(tr "shortcuts.toggle-zoom-style")
|
||||
(tr "shortcuts.underline")
|
||||
(tr "shortcuts.undo")
|
||||
(tr "shortcuts.ungroup")
|
||||
(tr "shortcuts.unmask")
|
||||
(tr "shortcuts.v-distribute")
|
||||
(tr "shortcuts.zoom-lense-decrease")
|
||||
(tr "shortcuts.zoom-lense-increase")
|
||||
(tr "shortcuts.zoom-selected"))
|
||||
|
||||
(let [translat-pre (case type
|
||||
:sc "shortcuts."
|
||||
:sec "shortcut-section."
|
||||
|
|
|
@ -30,8 +30,9 @@
|
|||
[:div {:class (stl/css :viewport-actions)}
|
||||
[:div {:class (stl/css :viewport-actions-container)}
|
||||
[:div {:class (stl/css :viewport-actions-title)}
|
||||
[:& i18n/tr-html {:tag-name "span"
|
||||
:label "workspace.top-bar.view-only"}]]
|
||||
[:> i18n/tr-html*
|
||||
{:tag-name "span"
|
||||
:content (tr "workspace.top-bar.view-only")}]]
|
||||
[:button {:class (stl/css :done-btn)
|
||||
:on-click handle-close-view-mode}
|
||||
(tr "workspace.top-bar.read-only.done")]]]))
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
[app.config :as cfg]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.globals :as globals]
|
||||
[app.util.object :as obj]
|
||||
[app.util.storage :refer [storage]]
|
||||
[cuerdas.core :as str]
|
||||
[goog.object :as gobj]
|
||||
|
@ -173,15 +172,11 @@
|
|||
([code] (t @locale code))
|
||||
([code & args] (apply t @locale code args)))
|
||||
|
||||
(mf/defc tr-html
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [label (obj/get props "label")
|
||||
class (obj/get props "class")
|
||||
tag-name (obj/get props "tag-name" "p")
|
||||
params (obj/get props "params" [])
|
||||
html (apply tr (d/concat-vec [label] params))]
|
||||
[:> tag-name {:dangerouslySetInnerHTML #js {:__html html}
|
||||
(mf/defc tr-html*
|
||||
{::mf/props :obj}
|
||||
[{:keys [content class tag-name]}]
|
||||
(let [tag-name (d/nilv tag-name "p")]
|
||||
[:> tag-name {:dangerouslySetInnerHTML #js {:__html content}
|
||||
:className class}]))
|
||||
|
||||
;; DEPRECATED
|
||||
|
|
|
@ -7923,6 +7923,7 @@ __metadata:
|
|||
eventsource-parser: "npm:^1.1.2"
|
||||
express: "npm:^4.19.2"
|
||||
fancy-log: "npm:^2.0.0"
|
||||
getopts: "npm:^2.3.0"
|
||||
gettext-parser: "npm:^8.0.0"
|
||||
gulp: "npm:4.0.2"
|
||||
gulp-concat: "npm:^2.6.1"
|
||||
|
@ -8236,6 +8237,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"getopts@npm:^2.3.0":
|
||||
version: 2.3.0
|
||||
resolution: "getopts@npm:2.3.0"
|
||||
checksum: 10c0/edbcbd7020e9d87dc41e4ad9add5eb3873ae61339a62431bd92a461be2c0eaa9ec33b6fd0d67fa1b44feedffcf1cf28d6f9dbdb7d604cb1617eaba146a33cbca
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"gettext-parser@npm:^8.0.0":
|
||||
version: 8.0.0
|
||||
resolution: "gettext-parser@npm:8.0.0"
|
||||
|
|
Loading…
Add table
Reference in a new issue