mirror of
https://github.com/penpot/penpot.git
synced 2025-03-28 15:41:25 -05:00
♻️ Refactor all i18n subsystem.
This commit is contained in:
parent
b13488404e
commit
f2d475d3d3
18 changed files with 1829 additions and 228 deletions
|
@ -2,3 +2,117 @@
|
|||
|
||||
This guide intends to explain the essential details of the frontend
|
||||
application.
|
||||
|
||||
**TODO**
|
||||
|
||||
|
||||
## Translations (I18N) ##
|
||||
|
||||
### How it Works ###
|
||||
|
||||
All the translation strings of this application are stored in
|
||||
`resources/locales.json` file. It has a self explanatory format that
|
||||
looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"auth.email-or-username" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/auth/login.cljs:61" ],
|
||||
"translations" : {
|
||||
"en" : "Email or Username",
|
||||
"fr" : "adresse email ou nom d'utilisateur"
|
||||
}
|
||||
},
|
||||
"ds.num-projects" : {
|
||||
"translations": {
|
||||
"en": ["1 project", "%s projects"]
|
||||
}
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
For development convenience, you can forget about the specific format
|
||||
of that file, and just add a simple key-value entry pairs like this:
|
||||
|
||||
```
|
||||
{
|
||||
[...],
|
||||
"foo1": "bar1",
|
||||
"foo2": "bar2"
|
||||
}
|
||||
```
|
||||
|
||||
The file is automatically bundled into the `index.html` file on
|
||||
compile time (in development and production). The bundled content is a
|
||||
simplified version of this data structure for avoid load unnecesary
|
||||
data.
|
||||
|
||||
The development environment has a watch process that detect changes on
|
||||
that file and recompiles the `index.html`. **There are no hot reload
|
||||
for translations strings**, you just need to refresh the browser tab
|
||||
for refresh the translations in the running the application.
|
||||
|
||||
If you have used the short (key-value) format, the watch process will
|
||||
automatically convert it to the apropriate format before generate the
|
||||
`index.html`.
|
||||
|
||||
Finally, when you have finished to adding texts, execute the following
|
||||
command for reformat the file, and track the usage locations (the
|
||||
"used-in" list) before commiting the file into the repository:
|
||||
|
||||
```bash
|
||||
clojure -Adev translations.clj collectmessages src/uxbox/main/ resources/locales.json
|
||||
```
|
||||
|
||||
NOTE: Later, we will need to think and implement the way to export and
|
||||
import to other formats (mainly for transifex and similar services
|
||||
compatibility).
|
||||
|
||||
|
||||
### How to use it ###
|
||||
|
||||
You have two aproaches for translate strings: one for general purpose
|
||||
and other specific for React components (that leverages reactivity for
|
||||
language changes).
|
||||
|
||||
The `uxbox.util.i18n/tr` is the general purpose function. This is a
|
||||
simple use case example:
|
||||
|
||||
```clojure
|
||||
(require '[uxbox.util.i18n :as i18n])
|
||||
|
||||
(i18n/tr "auth.email-or-username")
|
||||
;; => "Email or Username"
|
||||
```
|
||||
|
||||
If you have defined plurals for some translation resource, then you
|
||||
need to pass an additional parameter marked as counter in order to
|
||||
allow the system know when to show the plural:
|
||||
|
||||
```clojure
|
||||
(require '[uxbox.util.i18n :as i18n])
|
||||
|
||||
(i18n/tr "ds.num-projects" (i18n/c 10))
|
||||
;; => "10 projects"
|
||||
|
||||
(i18n/tr "ds.num-projects" (i18n/c 1))
|
||||
;; => "1 project"
|
||||
```
|
||||
|
||||
For React components, you have `uxbox.util.i18n/use-translations` hook:
|
||||
|
||||
```clojure
|
||||
(mf/defc my-component
|
||||
[props]
|
||||
(let [tr (i18n/use-translations)]
|
||||
[:div
|
||||
[:span (tr "auth.email-or-username")]]))
|
||||
```
|
||||
|
||||
You can use the general purpose function in React component but when
|
||||
language is changed the component will not be rerendered
|
||||
automatically.
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
funcool/cuerdas {:mvn/version "2.2.0"}
|
||||
funcool/lentes {:mvn/version "1.4.0-SNAPSHOT"}
|
||||
funcool/potok {:mvn/version "2.8.0-SNAPSHOT"}
|
||||
funcool/promesa {:mvn/version "4.0.2"}
|
||||
funcool/promesa {:mvn/version "5.0.0"}
|
||||
funcool/rumext {:mvn/version "2.0.0-SNAPSHOT"}
|
||||
}
|
||||
:paths ["src" "vendor" "resources" "../common"]
|
||||
|
@ -25,7 +25,9 @@
|
|||
{com.bhauman/rebel-readline-cljs {:mvn/version "0.1.4"}
|
||||
com.bhauman/rebel-readline {:mvn/version "0.1.4"}
|
||||
com.bhauman/figwheel-main {:mvn/version "0.2.3"}
|
||||
org.clojure/tools.namespace {:mvn/version "0.3.1"}}
|
||||
org.clojure/tools.namespace {:mvn/version "0.3.1"}
|
||||
metosin/jsonista {:mvn/version "0.2.5"}
|
||||
funcool/datoteka {:mvn/version "1.2.0"}}
|
||||
:extra-paths ["test"]}
|
||||
|
||||
:repl
|
||||
|
|
|
@ -7,6 +7,8 @@ const rename = require("gulp-rename");
|
|||
const gulpif = require("gulp-if");
|
||||
const gzip = require("gulp-gzip");
|
||||
const cleancss = require("gulp-clean-css");
|
||||
const fs = require("fs");
|
||||
const l = require("lodash");
|
||||
|
||||
const paths = {};
|
||||
paths.app = "./resources/";
|
||||
|
@ -79,6 +81,23 @@ gulp.task("scss:view", scssPipeline({
|
|||
|
||||
gulp.task("scss", gulp.parallel("scss:main", "scss:view"));
|
||||
|
||||
function readLocales() {
|
||||
const path = __dirname + "/resources/locales.json";
|
||||
const content = JSON.parse(fs.readFileSync(path, {encoding: "utf8"}));
|
||||
|
||||
let result = {};
|
||||
for (let key of Object.keys(content)) {
|
||||
const item = content[key];
|
||||
if (l.isString(item)) {
|
||||
result[key] = {"en": item};
|
||||
} else if (l.isPlainObject(item) && l.isPlainObject(item.translations)) {
|
||||
result[key] = item.translations;
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify(result);
|
||||
}
|
||||
|
||||
// Templates
|
||||
|
||||
function templatePipeline(options) {
|
||||
|
@ -86,8 +105,12 @@ function templatePipeline(options) {
|
|||
const input = options.input;
|
||||
const output = options.output;
|
||||
const ts = Math.floor(new Date());
|
||||
|
||||
const locales = readLocales();
|
||||
|
||||
const tmpl = mustache({
|
||||
ts: ts
|
||||
ts: ts,
|
||||
tr: JSON.stringify(locales),
|
||||
});
|
||||
|
||||
return gulp.src(input)
|
||||
|
@ -98,27 +121,28 @@ function templatePipeline(options) {
|
|||
}
|
||||
|
||||
gulp.task("template:main", templatePipeline({
|
||||
input: paths.app + "index.mustache",
|
||||
output: paths.output,
|
||||
input: paths.app + "templates/index.mustache",
|
||||
output: paths.output
|
||||
}));
|
||||
|
||||
gulp.task("template:view", templatePipeline({
|
||||
input: paths.app + "view.mustache",
|
||||
output: paths.output + "view/",
|
||||
jspath: "/js/view.js",
|
||||
csspath: "/css/view.css"
|
||||
input: paths.app + "templates/view.mustache",
|
||||
output: paths.output + "view/"
|
||||
}));
|
||||
|
||||
gulp.task("template", gulp.parallel("template:view", "template:main"));
|
||||
gulp.task("templates", gulp.parallel("template:view", "template:main"));
|
||||
|
||||
// Entry Point
|
||||
|
||||
gulp.task("watch:main", function() {
|
||||
gulp.watch(paths.scss, gulp.task("scss"));
|
||||
gulp.watch([paths.app + "templates/*.mustache",
|
||||
paths.app + "locales.json"],
|
||||
gulp.task("templates"));
|
||||
});
|
||||
|
||||
gulp.task("watch", gulp.series(
|
||||
gulp.parallel("scss", "template"),
|
||||
gulp.parallel("scss", "templates"),
|
||||
gulp.task("watch:main")
|
||||
));
|
||||
|
||||
|
@ -142,7 +166,7 @@ gulp.task("dist:template:view", templatePipeline({
|
|||
output: paths.dist + "view/",
|
||||
}));
|
||||
|
||||
gulp.task("dist:template", gulp.parallel("dist:template:view", "dist:template:main"));
|
||||
gulp.task("dist:templates", gulp.parallel("dist:template:view", "dist:template:main"));
|
||||
|
||||
// Styles
|
||||
|
||||
|
@ -184,7 +208,7 @@ gulp.task("dist:gzip", function() {
|
|||
// Entry Point
|
||||
|
||||
gulp.task("dist", gulp.parallel(
|
||||
gulp.task("dist:template"),
|
||||
gulp.task("dist:templates"),
|
||||
gulp.task("dist:scss"),
|
||||
gulp.task("dist:copy")
|
||||
));
|
||||
|
|
223
frontend/legacy.edn
Normal file
223
frontend/legacy.edn
Normal file
|
@ -0,0 +1,223 @@
|
|||
{"ds.projects" "PROJECTS"
|
||||
"ds.new-file" "+ New File"
|
||||
"ds.project-title" "Your projects"
|
||||
"ds.project-new" "+ New project"
|
||||
"ds.project-thumbnail.alt" "Project title"
|
||||
|
||||
"ds.ordering" "Sort by"
|
||||
"ds.ordering.by-name" "name"
|
||||
"ds.ordering.by-last-update" "last update"
|
||||
"ds.ordering.by-creation-date" "creation date"
|
||||
"ds.search.placeholder" "Search..."
|
||||
"ds.uploaded-at" "Uploaded at %s"
|
||||
"ds.updated-at" "Updated %s"
|
||||
|
||||
"ds.confirm-title" "Are you sure?"
|
||||
"ds.confirm-ok" "Ok"
|
||||
"ds.confirm-cancel" "Cancel"
|
||||
|
||||
"ds.multiselect-bar.copy" "Copy"
|
||||
"ds.multiselect-bar.copy-to-library" "Copy to library"
|
||||
"ds.multiselect-bar.move" "Move"
|
||||
"ds.multiselect-bar.move-to-library" "Move to library"
|
||||
"ds.multiselect-bar.rename" "Rename"
|
||||
"ds.multiselect-bar.delete" "Delete"
|
||||
|
||||
"ds.elements" "ELEMENTS"
|
||||
"ds.icons" "ICONS"
|
||||
"ds.your-icons-title" "YOUR ICONS"
|
||||
"ds.store-icons-title" "ICONS STORE"
|
||||
"ds.icons-collection.new" "+ New collection"
|
||||
"ds.icon-new" "+ New icon"
|
||||
|
||||
"ds.images" "IMAGES"
|
||||
"ds.your-images-title" "YOUR IMAGES"
|
||||
"ds.store-images-title" "IMAGES STORE"
|
||||
"ds.images-collection.new" "+ New library"
|
||||
"ds.image-new" "+ New image"
|
||||
|
||||
"ds.colors" "COLORS"
|
||||
"ds.your-colors-title" "YOUR COLORS"
|
||||
"ds.store-colors-title" "COLORS STORE"
|
||||
"ds.colors-collection.new" "+ New library"
|
||||
"ds.color-new" "+ New color"
|
||||
"ds.color-lightbox.title" "New color"
|
||||
"ds.color-lightbox.add" "+ Add color"
|
||||
|
||||
"ds.library-title" "Library: "
|
||||
"ds.standard-title" "STANDARD"
|
||||
"ds.your-libraries-title" "YOUR LIBRARIES"
|
||||
"ds.default-library-title" "Unnamed Collection (%s)"
|
||||
|
||||
"ds.project.placeholder" "New project name"
|
||||
"ds.project.new" "New project"
|
||||
|
||||
"common.accept" "Accept"
|
||||
"common.cancel" "Cancel"
|
||||
|
||||
"workspace.sidebar.icons" "Icons"
|
||||
"workspace.sidebar.element-options" "Element options"
|
||||
"workspace.sidebar.draw-tools" "Draw tools"
|
||||
"workspace.sidebar.sitemap" "Sitemap"
|
||||
"workspace.sidebar.layers" "Layers"
|
||||
"workspace.sidebar.document-history" "Document history"
|
||||
|
||||
"ds.page.placeholder" "Page name"
|
||||
"ds.page.new" "New page"
|
||||
"ds.page.edit" "Edit page"
|
||||
|
||||
"ds.history.versions" "History"
|
||||
"ds.history.pinned" "Pinned"
|
||||
|
||||
"workspace.header.rect" "Box (Ctrl + B)"
|
||||
"workspace.header.circle" "Circle (Ctrl + E)"
|
||||
"workspace.header.line" "Line (Ctrl + L)"
|
||||
"workspace.header.text" "Text"
|
||||
"workspace.header.path" "Path"
|
||||
"workspace.header.curve" "Curve"
|
||||
"workspace.header.ruler" "Ruler"
|
||||
"workspace.header.canvas" "Canvas"
|
||||
|
||||
"ds.user.profile" "Profile"
|
||||
"ds.user.password" "Password"
|
||||
"ds.user.notifications" "Notifications"
|
||||
"ds.user.exit" "Exit"
|
||||
|
||||
"workspace.header.sitemap" "Sitemap (Ctrl + Shift + M)"
|
||||
"workspace.header.draw-tools" "Draw tools (Ctrl + Shift + S)"
|
||||
"workspace.header.color-palette" "Color Palette (---)"
|
||||
"workspace.header.icons" "Icons (Ctrl + Shift + I)"
|
||||
"workspace.header.layers" "Layers (Ctrl + Shift + L)"
|
||||
"workspace.header.element-options" "Element options (Ctrl + Shift + O)"
|
||||
"workspace.header.document-history" "History (Ctrl + Shift + H)"
|
||||
"workspace.header.undo" "Undo (Ctrl + Z)"
|
||||
"workspace.header.redo" "Redo (Ctrl + Shift + Z)"
|
||||
"workspace.header.download" "Download (Ctrl + E)"
|
||||
"workspace.header.image" "Image (Ctrl + I)"
|
||||
"workspace.header.rules" "Rules"
|
||||
"workspace.header.grid" "Grid (Ctrl + G)"
|
||||
"workspace.header.grid-snap" "Snap to grid"
|
||||
"workspace.header.align" "Align (Ctrl + A)"
|
||||
"workspace.header.view-mode" "View mode (Ctrl + P)"
|
||||
|
||||
"workspace.options.radius" "Radius"
|
||||
"workspace.options.size" "Size"
|
||||
"workspace.options.width" "Width"
|
||||
"workspace.options.height" "Height"
|
||||
"workspace.options.stroke.style" "Style"
|
||||
"workspace.options.stroke.none" "None"
|
||||
"workspace.options.stroke.solid" "Solid"
|
||||
"workspace.options.stroke.dotted" "Dotted"
|
||||
"workspace.options.stroke.dashed" "Dashed"
|
||||
"workspace.options.stroke.mixed" "Mixed"
|
||||
"workspace.options.position" "Position"
|
||||
"workspace.options.rotation" "Rotation"
|
||||
"workspace.options.opacity" "Opacity"
|
||||
"workspace.options.color" "Color"
|
||||
"workspace.options.background-color" "Background color"
|
||||
"workspace.options.font-family" "Font Family"
|
||||
"workspace.options.font-weight" "Font Size & Weight"
|
||||
"workspace.options.font-size" "Font Size"
|
||||
"workspace.options.line-height-letter-spacing" "Line height and Letter spacing"
|
||||
"workspace.options.line-height" "Line Height"
|
||||
"workspace.options.letter-spacing" "Letter Spacing"
|
||||
"workspace.options.text-align" "Text Alignment"
|
||||
"workspace.options.name" "Name"
|
||||
"workspace.options.go" "Go go go!"
|
||||
"workspace.options.measures" "Size, position & rotation"
|
||||
"workspace.options.font-options" "Fonts & Font Size"
|
||||
"workspace.options.rotation-radius" "Rotation & Radius"
|
||||
"workspace.options.stroke" "Stroke"
|
||||
"workspace.options.grid-options" "Grid settings"
|
||||
|
||||
"element.fill" "Fill"
|
||||
"element.text" "Text"
|
||||
"element.interactions" "Interactions"
|
||||
|
||||
"image.new" "New image"
|
||||
"image.select" "Select from library"
|
||||
"image.upload" "Upload file"
|
||||
"image.import-library" "Import image from library"
|
||||
|
||||
"auth.email-or-username" "Email or Username"
|
||||
"auth.password" "Password"
|
||||
"auth.signin" "Sign in"
|
||||
"auth.forgot-password" "Forgot your password?"
|
||||
"auth.no-account" "Don't have an account?"
|
||||
"auth.message.recovery-token-sent" "Password recovery link sent to your inbox."
|
||||
"auth.message.password-recovered" "Password successfully recovered."
|
||||
|
||||
"register.fullname.placeholder" "Full Name"
|
||||
"register.username.placeholder" "Username"
|
||||
"register.email.placeholder" "Email"
|
||||
"register.password.placeholder" "Password"
|
||||
"register.get-started" "Get started"
|
||||
"register.already-have-account" "Already have an account?"
|
||||
|
||||
"recovery-request.username-or-email.placeholder" "username or email address"
|
||||
"recovery-request.recover-password" "Recover password"
|
||||
"recovery-request.go-back" "Go back!"
|
||||
|
||||
"recover.password.placeholder" "Password"
|
||||
"recover.recover-password" "Recover password"
|
||||
"recover.go-back" "Go back!"
|
||||
|
||||
"settings.profile" "PROFILE"
|
||||
"settings.password" "PASSWORD"
|
||||
"settings.notifications" "NOTIFICATIONS"
|
||||
"settings.exit" "EXIT"
|
||||
|
||||
"settings.profile.profile-saved" "Profile saved successfully!"
|
||||
"settings.profile.section-basic-data" "Name, username and email"
|
||||
"settings.profile.section-i18n-data" "Default language"
|
||||
"settings.profile.your-name" "Your name"
|
||||
"settings.profile.your-username" "Your username"
|
||||
"settings.profile.your-email" "Your email"
|
||||
|
||||
"settings.choose-color-theme" "Choose a theme"
|
||||
"settings.profile.light-theme" "Light theme"
|
||||
"settings.profile.dark-theme" "Dark theme"
|
||||
"settings.profile.high-contrast-theme" "High-contrast theme"
|
||||
"settings.profile.your-avatar" "Your avatar"
|
||||
|
||||
"settings.password.password-saved" "Password saved successfully!"
|
||||
"settings.password.wrong-old-password" "Wrong old password"
|
||||
"settings.password.change-password" "Change password"
|
||||
"settings.password.old-password" "Old password"
|
||||
"settings.password.new-password" "New password"
|
||||
"settings.password.confirm-password" "Confirm password"
|
||||
|
||||
"settings.notifications.notifications-saved" "Notifications preferences saved successfully!"
|
||||
"settings.notifications.prototype-notifications" "Prototype notifications"
|
||||
"settings.notifications.description" "Get a roll up of prototype changes in your inbox."
|
||||
"settings.notifications.none" "None"
|
||||
"settings.notifications.every-hour" "Every hour"
|
||||
"settings.notifications.every-day" "Every day"
|
||||
|
||||
"settings.update-settings" "Update settings"
|
||||
|
||||
"history.alert-message" "You are seeing version %s"
|
||||
|
||||
"errors.api.form.unexpected-error" "An unexpected error occurred."
|
||||
"errors.api.form.old-password-not-match" "Incorrect old password"
|
||||
"errors.api.form.registration-disabled" "The registration is currently disabled."
|
||||
"errors.api.form.email-already-exists" "The email is already in use by another user."
|
||||
"errors.api.form.username-already-exists" "The username is already in use by another user."
|
||||
"errors.api.form.user-not-exists" "Username or email does not matches any existing user."
|
||||
"errors.form.required" "This field is mandatory"
|
||||
"errors.form.string" "Should be string"
|
||||
"errors.form.number" "Invalid number"
|
||||
"errors.form.integer" "Invalid integer"
|
||||
"errors.form.bool" "Should be boolean"
|
||||
"errors.form.min-len" "Should be greater than %s"
|
||||
"errors.form.max-len" "Should be lesser than %s"
|
||||
"errors.form.color" "Should be a valid color string"
|
||||
"errors.form.password-not-match" "Password does not match"
|
||||
"errors.auth.unauthorized" "Username or password seems to be wrong."
|
||||
"errors.auth.invalid-recovery-token" "The recovery token is invalid."
|
||||
"errors.profile.update-password" "Error updating password, probably your old password is wrong."
|
||||
|
||||
"errors.network" "Unable to connect to backend server."
|
||||
"errors.generic" "Something wrong has happened."
|
||||
"errors.conflict" "Conflict on saving data, please refresh and try again."
|
||||
}
|
223
frontend/legacy.fr.edn
Normal file
223
frontend/legacy.fr.edn
Normal file
|
@ -0,0 +1,223 @@
|
|||
{"ds.projects" "PROJETS"
|
||||
"ds.project-title" "Vos projets"
|
||||
"ds.project-new" "+ Nouveau projet"
|
||||
"ds.project-thumbnail.alt" "Titre du projet"
|
||||
|
||||
"ds.ordering" "Trier par"
|
||||
"ds.ordering.by-name" "nom"
|
||||
"ds.ordering.by-last-update" "dernière mise à jour"
|
||||
"ds.ordering.by-creation-date" "date de création"
|
||||
"ds.search.placeholder" "Rechercher..."
|
||||
"ds.uploaded-at" "Mise en ligne : %s"
|
||||
"ds.updated-at" "Mis à jour %s"
|
||||
|
||||
"ds.confirm-title" "Êtes-vous sûr ?"
|
||||
"ds.confirm-ok" "Ok"
|
||||
"ds.confirm-cancel" "Annuler"
|
||||
|
||||
"ds.multiselect-bar.copy" "Copier"
|
||||
"ds.multiselect-bar.copy-to-library" "Copier vers la librairie"
|
||||
"ds.multiselect-bar.move" "Déplacer"
|
||||
"ds.multiselect-bar.move-to-library" "Déplacer vers la librairie"
|
||||
"ds.multiselect-bar.rename" "Renommer"
|
||||
"ds.multiselect-bar.delete" "Supprimer"
|
||||
|
||||
"ds.elements" "ÉLÉMENTS"
|
||||
"ds.icons" "ICÔNES"
|
||||
"ds.your-icons-title" "VOS ICÔNES"
|
||||
"ds.store-icons-title" "BOUTIQUE"
|
||||
"ds.icons-collection.new" "+ Nouvelle collection"
|
||||
"ds.icon-new" "+ Nouvel icône"
|
||||
|
||||
"ds.images" "IMAGES"
|
||||
"ds.your-images-title" "VOS IMAGES"
|
||||
"ds.store-images-title" "BOUTIQUE"
|
||||
"ds.images-collection.new" "+ Nouvelle librairie"
|
||||
"ds.image-new" "+ Nouvelle image"
|
||||
|
||||
"ds.colors" "COULEURS"
|
||||
"ds.your-colors-title" "VOS COULEURS"
|
||||
"ds.store-colors-title" "BOUTIQUE"
|
||||
"ds.colors-collection.new" "+ Nouvelle librairie"
|
||||
"ds.color-new" "+ Nouvelle couleur"
|
||||
"ds.color-lightbox.title" "Nouvelle couleur"
|
||||
"ds.color-lightbox.add" "+ Ajouter couleur"
|
||||
|
||||
"ds.library-title" "Librairie : "
|
||||
"ds.standard-title" "STANDARD"
|
||||
"ds.your-libraries-title" "VOS LIBRAIRIES"
|
||||
"ds.default-library-title" "Collection sans nom (%s)"
|
||||
|
||||
"ds.project.placeholder" "Nom du nouveau projet"
|
||||
"ds.project.new" "Nouveau projet"
|
||||
|
||||
|
||||
"ds.accept" "Accepter"
|
||||
"ds.cancel" "Annuler"
|
||||
|
||||
"workspace.sidebar.icons" "Icônes"
|
||||
"workspace.sidebar.element-options" "Options d'élément"
|
||||
"workspace.sidebar.draw-tools" "Outils de dessin"
|
||||
"workspace.sidebar.sitemap" "Plan du site"
|
||||
"workspace.sidebar.layers" "Couches"
|
||||
"workspace.sidebar.document-history" "Historique du document"
|
||||
|
||||
"ds.page.placeholder" "Nom de la page"
|
||||
"ds.page.new" "Nouvelle page"
|
||||
"ds.page.edit" "Éditer la page"
|
||||
|
||||
"ds.history.versions" "Historique"
|
||||
"ds.history.pinned" "Épinglés"
|
||||
|
||||
"workspace.header.rect" "Boîte (Ctrl + B)"
|
||||
"workspace.header.circle" "Cercle (Ctrl + E)"
|
||||
"workspace.header.line" "Ligne (Ctrl + L)"
|
||||
"workspace.header.text" "Texte"
|
||||
"workspace.header.path" "Chemin"
|
||||
"workspace.header.curve" "Courbe"
|
||||
"workspace.header.ruler" "Règle"
|
||||
"workspace.header.canvas" "Calque"
|
||||
|
||||
"ds.user.profile" "Profil"
|
||||
"ds.user.password" "Mot de passe"
|
||||
"ds.user.notifications" "Notifications"
|
||||
"ds.user.exit" "Quitter"
|
||||
|
||||
"workspace.header.sitemap" "Plan du site (Ctrl + Maj + M)"
|
||||
"workspace.header.draw-tools" "Outils de dessin (Ctrl + Maj + S)"
|
||||
"workspace.header.color-palette" "Palette de couleurs (---)"
|
||||
"workspace.header.icons" "Icônes (Ctrl + Maj + I)"
|
||||
"workspace.header.layers" "Couches (Ctrl + Maj + L)"
|
||||
"workspace.header.element-options" "Options d'élément (Ctrl + Maj + O)"
|
||||
"workspace.header.document-history" "Historique du document (Ctrl + Maj + H)"
|
||||
"workspace.header.undo" "Annuler (Ctrl + Z)"
|
||||
"workspace.header.redo" "Rétablir (Ctrl + Maj + Z)"
|
||||
"workspace.header.download" "Télécharger (Ctrl + E)"
|
||||
"workspace.header.image" "Image (Ctrl + I)"
|
||||
"workspace.header.rules" "Règles"
|
||||
"workspace.header.grid" "Grille (Ctrl + G)"
|
||||
"workspace.header.grid-snap" "Coller à la grille"
|
||||
"workspace.header.align" "Aligner (Ctrl + A)"
|
||||
"workspace.header.view-mode" "Mode visualisation (Ctrl + P)"
|
||||
|
||||
"workspace.options.radius" "Rayon"
|
||||
"workspace.options.size" "Taille"
|
||||
"workspace.options.width" "Largeur"
|
||||
"workspace.options.height" "Hauteur"
|
||||
"workspace.options.stroke.style" "Style"
|
||||
"workspace.options.stroke.none" "Aucun"
|
||||
"workspace.options.stroke.solid" "Solide"
|
||||
"workspace.options.stroke.dotted" "Pointillé"
|
||||
"workspace.options.stroke.dashed" "Tiré"
|
||||
"workspace.options.stroke.mixed" "Mixte"
|
||||
"workspace.options.position" "Position"
|
||||
"workspace.options.rotation" "Rotation"
|
||||
"workspace.options.opacity" "Opacité"
|
||||
"workspace.options.color" "Couleur"
|
||||
"workspace.options.background-color" "Couleur d'arrière-plan"
|
||||
"workspace.options.font-family" "Police de caractères"
|
||||
"workspace.options.font-weight" "Taille et graisse"
|
||||
"workspace.options.font-size" "Taille de police"
|
||||
"workspace.options.line-height-letter-spacing" "Hauteur de ligne et Espacement de caractères"
|
||||
"workspace.options.line-height" "Hauteur de ligne"
|
||||
"workspace.options.letter-spacing" "Espacement de caractères"
|
||||
"workspace.options.text-align" "Alignement de texte"
|
||||
"workspace.options.name" "Nom"
|
||||
"workspace.options.go" "C'est parti !"
|
||||
"workspace.options.measures" "Taille, position et rotation"
|
||||
"workspace.options.font-options" "TODO"
|
||||
"workspace.options.rotation-radius" "TODO"
|
||||
"workspace.options.strokestroke" "Contour"
|
||||
"workspace.options.grid-options" "Paramètres de la grille"
|
||||
|
||||
"element.fill" "Fond"
|
||||
"element.text" "Texte"
|
||||
"element.interactions" "Interactions"
|
||||
|
||||
"image.new" "Nouvelle image"
|
||||
"image.select" "Choisir depuis une librairie"
|
||||
"image.upload" "Envoyer un fichier"
|
||||
"image.import-library" "Importer une image depuis une librairie"
|
||||
|
||||
"auth.email-or-username" "adresse email ou nom d'utilisateur"
|
||||
"auth.password" "Mot de passe"
|
||||
"auth.signin" "Se connecter"
|
||||
"auth.forgot-password" "Mot de passe oublié ?"
|
||||
"auth.no-account" "Vous n'avez pas de compte ?"
|
||||
"auth.message.recovery-token-sent" "Lien de récupération de mot de passe envoyé."
|
||||
"auth.message.password-recovered" "Mot de passe récupéré avec succès."
|
||||
|
||||
"register.fullname.placeholder" "Nom complet"
|
||||
"register.username.placeholder" "Nom d'utilisateur"
|
||||
"register.email.placeholder" "Adresse email"
|
||||
"register.password.placeholder" "Mot de passe"
|
||||
"register.get-started" "Commencer"
|
||||
"register.already-have-account" "Vous avez déjà un compte ?"
|
||||
|
||||
"recovery-request.username-or-email.placeholder" "nom d'utilisateur ou adresse email"
|
||||
"recovery-request.recover-password" "Récupérer le mot de passe"
|
||||
"recovery-request.go-back" "Retour!"
|
||||
|
||||
"recover.password.placeholder" "Mot de passe"
|
||||
"recover.recover-password" "Récupérer le mot de passe"
|
||||
"recover.go-back" "Retour!"
|
||||
|
||||
"settings.profile" "PROFIL"
|
||||
"settings.password" "MOT DE PASSE"
|
||||
"settings.notifications" "NOTIFICATIONS"
|
||||
"settings.exit" "QUITTER"
|
||||
|
||||
"settings.profile.profile-saved" "Profil enregistré avec succès !"
|
||||
"settings.profile.section-basic-data" "Nom, nom d'utilisateur et adresse email"
|
||||
"settings.profile.section-i18n-data" "Langue par défaut"
|
||||
"settings.profile.your-name" "Votre nom complet"
|
||||
"settings.profile.your-username" "Votre nom d'utilisateur"
|
||||
"settings.profile.your-email" "Votre adresse email"
|
||||
|
||||
"settings.choose-color-theme" "Choisissez un thème"
|
||||
"settings.profile.light-theme" "Thème Jour"
|
||||
"settings.profile.dark-theme" "Thème Nuit"
|
||||
"settings.profile.high-contrast-theme" "Thème Contraste élevé"
|
||||
"settings.profile.your-avatar" "Votre avatar"
|
||||
|
||||
"settings.password.password-saved" "Mot de passe enregistré avec succès !"
|
||||
"settings.password.wrong-old-password" "Ancien mot de passe incorrect"
|
||||
"settings.password.change-password" "Changement de mot de passe"
|
||||
"settings.password.old-password" "Ancien mot de passe"
|
||||
"settings.password.new-password" "Nouveau mot de passe"
|
||||
"settings.password.confirm-password" "Confirmez mot de passe"
|
||||
|
||||
"settings.notifications.notifications-saved" "Préférences de notifications enregistrées avec succès !"
|
||||
"settings.notifications.prototype-notifications" "Notifications de prototypage"
|
||||
"settings.notifications.description" "Obtenez un résumé des modifications apportées aux prototypes à votre adresse email."
|
||||
"settings.notifications.none" "Aucune"
|
||||
"settings.notifications.every-hour" "Chaque heure"
|
||||
"settings.notifications.every-day" "Chaque jour"
|
||||
|
||||
"settings.update-settings" "Mettre à jour les paramètres"
|
||||
|
||||
"history.alert-message" "Vous voyez la version %s"
|
||||
|
||||
"errors.api.form.unexpected-error" "Une erreur inattendue c'est produite"
|
||||
"errors.api.form.old-password-not-match" "Ancien mot de passe incorrect"
|
||||
"errors.api.form.registration-disabled" "L'enregistrement est actuellement désactivé."
|
||||
"errors.api.form.email-already-exists" "L'email est déjà utilisé par un autre utilisateur."
|
||||
"errors.api.form.username-already-exists" "Le nom d'utilisateur est déjà utilisé par un autre utilisateur."
|
||||
"errors.api.form.user-not-exists" "Le nom d'utilisateur ou l'e-mail ne correspond à aucun utilisateur existant."
|
||||
"errors.form.required" "Ce champ est obligatoire"
|
||||
"errors.form.string" "Devrait être une chaîne de caractères"
|
||||
"errors.form.number" "Nombre invalide"
|
||||
"errors.form.integer" "Entier invalide"
|
||||
"errors.form.bool" "Devrait être un booléen"
|
||||
"errors.form.min-len" "Devrait être supérieur à %s"
|
||||
"errors.form.max-len" "Devrait être inférieur à %s"
|
||||
"errors.form.color" "Devrait être une couleur valide"
|
||||
"errors.form.password-not-match" "Le mot de passe ne correspond pas"
|
||||
"errors.auth.unauthorized" "Le nom d'utilisateur ou le mot de passe semble être faux."
|
||||
"errors.auth.invalid-recovery-token" "Le jeton de récupération n'est pas valide."
|
||||
"errors.profile.update-password" "Erreur lors de la mise à jour du mot de passe, votre ancien mot de passe est probablement incorrect."
|
||||
|
||||
"errors.network" "Impossible de se connecter au serveur principal."
|
||||
"errors.generic" "Quelque chose c'est mal passé."
|
||||
"errors.conflict" "Conflit sur la sauvegarde des données, actualisez et réessayez."
|
||||
}
|
891
frontend/resources/locales.json
Normal file
891
frontend/resources/locales.json
Normal file
|
@ -0,0 +1,891 @@
|
|||
{
|
||||
"auth.email-or-username" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/auth/login.cljs:61" ],
|
||||
"translations" : {
|
||||
"en" : "Email or Username",
|
||||
"fr" : "adresse email ou nom d'utilisateur"
|
||||
}
|
||||
},
|
||||
"auth.forgot-password" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/auth/login.cljs:83" ],
|
||||
"translations" : {
|
||||
"en" : "Forgot your password?",
|
||||
"fr" : "Mot de passe oublié ?"
|
||||
}
|
||||
},
|
||||
"auth.message.password-recovered" : {
|
||||
"used-in" : [ "src/uxbox/main/data/auth.cljs:178" ],
|
||||
"translations" : {
|
||||
"en" : "Password successfully recovered.",
|
||||
"fr" : "Mot de passe récupéré avec succès."
|
||||
}
|
||||
},
|
||||
"auth.message.recovery-token-sent" : {
|
||||
"used-in" : [ "src/uxbox/main/data/auth.cljs:141" ],
|
||||
"translations" : {
|
||||
"en" : "Password recovery link sent to your inbox.",
|
||||
"fr" : "Lien de récupération de mot de passe envoyé."
|
||||
}
|
||||
},
|
||||
"auth.no-account" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/auth/login.cljs:86" ],
|
||||
"translations" : {
|
||||
"en" : "Don't have an account?",
|
||||
"fr" : "Vous n'avez pas de compte ?"
|
||||
}
|
||||
},
|
||||
"auth.password" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/auth/login.cljs:70" ],
|
||||
"translations" : {
|
||||
"en" : "Password",
|
||||
"fr" : "Mot de passe"
|
||||
}
|
||||
},
|
||||
"auth.signin" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/auth/login.cljs:77" ],
|
||||
"translations" : {
|
||||
"en" : "Sign in",
|
||||
"fr" : "Se connecter"
|
||||
}
|
||||
},
|
||||
"ds.accept" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/history.cljs:113" ],
|
||||
"translations" : {
|
||||
"en" : null,
|
||||
"fr" : "Accepter"
|
||||
}
|
||||
},
|
||||
"ds.cancel" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/history.cljs:114" ],
|
||||
"translations" : {
|
||||
"en" : null,
|
||||
"fr" : "Annuler"
|
||||
}
|
||||
},
|
||||
"ds.color-lightbox.add" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/colors.cljs:52" ],
|
||||
"translations" : {
|
||||
"en" : "+ Add color",
|
||||
"fr" : "+ Ajouter couleur"
|
||||
}
|
||||
},
|
||||
"ds.color-new" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/colors.cljs:149" ],
|
||||
"translations" : {
|
||||
"en" : "+ New color",
|
||||
"fr" : "+ Nouvelle couleur"
|
||||
}
|
||||
},
|
||||
"ds.colors" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/header.cljs:48" ],
|
||||
"translations" : {
|
||||
"en" : "COLORS",
|
||||
"fr" : "COULEURS"
|
||||
}
|
||||
},
|
||||
"ds.colors-collection.new" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/colors.cljs:134" ],
|
||||
"translations" : {
|
||||
"en" : "+ New library",
|
||||
"fr" : "+ Nouvelle librairie"
|
||||
}
|
||||
},
|
||||
"ds.confirm-cancel" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/confirm.cljs:38" ],
|
||||
"translations" : {
|
||||
"en" : "Cancel",
|
||||
"fr" : "Annuler"
|
||||
}
|
||||
},
|
||||
"ds.confirm-ok" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/confirm.cljs:34" ],
|
||||
"translations" : {
|
||||
"en" : "Ok",
|
||||
"fr" : "Ok"
|
||||
}
|
||||
},
|
||||
"ds.confirm-title" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/confirm.cljs:28" ],
|
||||
"translations" : {
|
||||
"en" : "Are you sure?",
|
||||
"fr" : "Êtes-vous sûr ?"
|
||||
}
|
||||
},
|
||||
"ds.default-library-title" : {
|
||||
"used-in" : [ "src/uxbox/main/data/images.cljs:99", "src/uxbox/main/data/icons.cljs:87", "src/uxbox/main/data/colors.cljs:68" ],
|
||||
"translations" : {
|
||||
"en" : "Unnamed Collection (%s)",
|
||||
"fr" : "Collection sans nom (%s)"
|
||||
}
|
||||
},
|
||||
"ds.foobar" : {
|
||||
"translations" : {
|
||||
"en" : null,
|
||||
"fr" : null
|
||||
},
|
||||
"permanent" : true
|
||||
},
|
||||
"ds.height" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/icon_measures.cljs:46" ],
|
||||
"translations" : {
|
||||
"en" : null,
|
||||
"fr" : null
|
||||
}
|
||||
},
|
||||
"ds.history.pinned" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/history.cljs:95" ],
|
||||
"translations" : {
|
||||
"en" : "Pinned",
|
||||
"fr" : "Épinglés"
|
||||
}
|
||||
},
|
||||
"ds.history.versions" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/history.cljs:92" ],
|
||||
"translations" : {
|
||||
"en" : "History",
|
||||
"fr" : "Historique"
|
||||
}
|
||||
},
|
||||
"ds.icon-new" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/icons.cljs:158" ],
|
||||
"translations" : {
|
||||
"en" : "+ New icon",
|
||||
"fr" : "+ Nouvel icône"
|
||||
}
|
||||
},
|
||||
"ds.icons" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/header.cljs:42" ],
|
||||
"translations" : {
|
||||
"en" : "ICONS",
|
||||
"fr" : "ICÔNES"
|
||||
}
|
||||
},
|
||||
"ds.icons-collection.new" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/icons.cljs:137" ],
|
||||
"translations" : {
|
||||
"en" : "+ New collection",
|
||||
"fr" : "+ Nouvelle collection"
|
||||
}
|
||||
},
|
||||
"ds.image-new" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/images.cljs:252" ],
|
||||
"translations" : {
|
||||
"en" : "+ New image",
|
||||
"fr" : "+ Nouvelle image"
|
||||
}
|
||||
},
|
||||
"ds.images" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/header.cljs:45" ],
|
||||
"translations" : {
|
||||
"en" : "IMAGES",
|
||||
"fr" : "IMAGES"
|
||||
}
|
||||
},
|
||||
"ds.images-collection.new" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/images.cljs:108" ],
|
||||
"translations" : {
|
||||
"en" : "+ New library",
|
||||
"fr" : "+ Nouvelle librairie"
|
||||
}
|
||||
},
|
||||
"ds.multiselect-bar.copy" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/images.cljs:196", "src/uxbox/main/ui/dashboard/images.cljs:171", "src/uxbox/main/ui/dashboard/colors.cljs:224", "src/uxbox/main/ui/dashboard/colors.cljs:201" ],
|
||||
"translations" : {
|
||||
"en" : "Copy",
|
||||
"fr" : "Copier"
|
||||
}
|
||||
},
|
||||
"ds.multiselect-bar.copy-to-library" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/images.cljs:200", "src/uxbox/main/ui/dashboard/images.cljs:175", "src/uxbox/main/ui/dashboard/colors.cljs:228", "src/uxbox/main/ui/dashboard/colors.cljs:205" ],
|
||||
"translations" : {
|
||||
"en" : "Copy to library",
|
||||
"fr" : "Copier vers la librairie"
|
||||
}
|
||||
},
|
||||
"ds.multiselect-bar.delete" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/images.cljs:190", "src/uxbox/main/ui/dashboard/colors.cljs:217" ],
|
||||
"translations" : {
|
||||
"en" : "Delete",
|
||||
"fr" : "Supprimer"
|
||||
}
|
||||
},
|
||||
"ds.multiselect-bar.move" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/images.cljs:179", "src/uxbox/main/ui/dashboard/colors.cljs:209" ],
|
||||
"translations" : {
|
||||
"en" : "Move",
|
||||
"fr" : "Déplacer"
|
||||
}
|
||||
},
|
||||
"ds.multiselect-bar.move-to-library" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/images.cljs:183", "src/uxbox/main/ui/dashboard/colors.cljs:213" ],
|
||||
"translations" : {
|
||||
"en" : "Move to library",
|
||||
"fr" : "Déplacer vers la librairie"
|
||||
}
|
||||
},
|
||||
"ds.multiselect-bar.rename" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/images.cljs:187" ],
|
||||
"translations" : {
|
||||
"en" : "Rename",
|
||||
"fr" : "Renommer"
|
||||
}
|
||||
},
|
||||
"ds.new-file" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/projects.cljs:143" ],
|
||||
"translations" : {
|
||||
"en" : "+ New File",
|
||||
"fr" : null
|
||||
}
|
||||
},
|
||||
"ds.position" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/icon_measures.cljs:52" ],
|
||||
"translations" : {
|
||||
"en" : null,
|
||||
"fr" : null
|
||||
}
|
||||
},
|
||||
"ds.projects" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/header.cljs:39" ],
|
||||
"translations" : {
|
||||
"en" : "PROJECTS",
|
||||
"fr" : "PROJETS"
|
||||
}
|
||||
},
|
||||
"ds.rotation" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/icon_measures.cljs:67" ],
|
||||
"translations" : {
|
||||
"en" : null,
|
||||
"fr" : null
|
||||
}
|
||||
},
|
||||
"ds.search.placeholder" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/projects.cljs:192" ],
|
||||
"translations" : {
|
||||
"en" : "Search...",
|
||||
"fr" : "Rechercher..."
|
||||
}
|
||||
},
|
||||
"ds.settings.document-history" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/history.cljs:86" ],
|
||||
"translations" : {
|
||||
"en" : null,
|
||||
"fr" : null
|
||||
}
|
||||
},
|
||||
"ds.settings.layers" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/layers.cljs:260" ],
|
||||
"translations" : {
|
||||
"en" : null,
|
||||
"fr" : null
|
||||
}
|
||||
},
|
||||
"ds.size" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/icon_measures.cljs:33" ],
|
||||
"translations" : {
|
||||
"en" : null,
|
||||
"fr" : null
|
||||
}
|
||||
},
|
||||
"ds.store-colors-title" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/colors.cljs:129" ],
|
||||
"translations" : {
|
||||
"en" : "COLORS STORE",
|
||||
"fr" : "BOUTIQUE"
|
||||
}
|
||||
},
|
||||
"ds.store-icons-title" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/icons.cljs:131" ],
|
||||
"translations" : {
|
||||
"en" : "ICONS STORE",
|
||||
"fr" : "BOUTIQUE"
|
||||
}
|
||||
},
|
||||
"ds.store-images-title" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/images.cljs:146", "src/uxbox/main/ui/dashboard/images.cljs:102" ],
|
||||
"translations" : {
|
||||
"en" : "IMAGES STORE",
|
||||
"fr" : "BOUTIQUE"
|
||||
}
|
||||
},
|
||||
"ds.updated-at" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/projects.cljs:110" ],
|
||||
"translations" : {
|
||||
"en" : "Updated %s",
|
||||
"fr" : "Mis à jour %s"
|
||||
}
|
||||
},
|
||||
"ds.uploaded-at" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/images.cljs:237", "src/uxbox/main/ui/dashboard/icons.cljs:303" ],
|
||||
"translations" : {
|
||||
"en" : "Uploaded at %s",
|
||||
"fr" : "Mise en ligne : %s"
|
||||
}
|
||||
},
|
||||
"ds.user.exit" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/users.cljs:43" ],
|
||||
"translations" : {
|
||||
"en" : "Exit",
|
||||
"fr" : "Quitter"
|
||||
}
|
||||
},
|
||||
"ds.user.notifications" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/users.cljs:40" ],
|
||||
"translations" : {
|
||||
"en" : "Notifications",
|
||||
"fr" : "Notifications"
|
||||
}
|
||||
},
|
||||
"ds.user.password" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/users.cljs:37" ],
|
||||
"translations" : {
|
||||
"en" : "Password",
|
||||
"fr" : "Mot de passe"
|
||||
}
|
||||
},
|
||||
"ds.user.profile" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/users.cljs:34" ],
|
||||
"translations" : {
|
||||
"en" : "Profile",
|
||||
"fr" : "Profil"
|
||||
}
|
||||
},
|
||||
"ds.width" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/icon_measures.cljs:36" ],
|
||||
"translations" : {
|
||||
"en" : null,
|
||||
"fr" : null
|
||||
}
|
||||
},
|
||||
"ds.your-colors-title" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/colors.cljs:126" ],
|
||||
"translations" : {
|
||||
"en" : "YOUR COLORS",
|
||||
"fr" : "VOS COULEURS"
|
||||
}
|
||||
},
|
||||
"ds.your-icons-title" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/dashboard/icons.cljs:128" ],
|
||||
"translations" : {
|
||||
"en" : "YOUR ICONS",
|
||||
"fr" : "VOS ICÔNES"
|
||||
}
|
||||
},
|
||||
"ds.your-images-title" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/images.cljs:143", "src/uxbox/main/ui/dashboard/images.cljs:99" ],
|
||||
"translations" : {
|
||||
"en" : "YOUR IMAGES",
|
||||
"fr" : "VOS IMAGES"
|
||||
}
|
||||
},
|
||||
"element.fill" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/fill.cljs:44" ],
|
||||
"translations" : {
|
||||
"en" : "Fill",
|
||||
"fr" : "Fond"
|
||||
}
|
||||
},
|
||||
"errors.api.form.registration-disabled" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/auth/register.cljs:39" ],
|
||||
"translations" : {
|
||||
"en" : "The registration is currently disabled.",
|
||||
"fr" : "L'enregistrement est actuellement désactivé."
|
||||
}
|
||||
},
|
||||
"errors.api.form.unexpected-error" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/auth/register.cljs:51" ],
|
||||
"translations" : {
|
||||
"en" : "An unexpected error occurred.",
|
||||
"fr" : "Une erreur inattendue c'est produite"
|
||||
}
|
||||
},
|
||||
"errors.auth.invalid-recovery-token" : {
|
||||
"used-in" : [ "src/uxbox/main/data/auth.cljs:174", "src/uxbox/main/data/auth.cljs:151" ],
|
||||
"translations" : {
|
||||
"en" : "The recovery token is invalid.",
|
||||
"fr" : "Le jeton de récupération n'est pas valide."
|
||||
}
|
||||
},
|
||||
"errors.auth.unauthorized" : {
|
||||
"used-in" : [ "src/uxbox/main/data/auth.cljs:59" ],
|
||||
"translations" : {
|
||||
"en" : "Username or password seems to be wrong.",
|
||||
"fr" : "Le nom d'utilisateur ou le mot de passe semble être faux."
|
||||
}
|
||||
},
|
||||
"errors.generic" : {
|
||||
"used-in" : [ "src/uxbox/main/ui.cljs:86" ],
|
||||
"translations" : {
|
||||
"en" : "Something wrong has happened.",
|
||||
"fr" : "Quelque chose c'est mal passé."
|
||||
}
|
||||
},
|
||||
"errors.network" : {
|
||||
"used-in" : [ "src/uxbox/main/ui.cljs:80" ],
|
||||
"translations" : {
|
||||
"en" : "Unable to connect to backend server.",
|
||||
"fr" : "Impossible de se connecter au serveur principal."
|
||||
}
|
||||
},
|
||||
"header.sitemap" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:83" ],
|
||||
"translations" : {
|
||||
"en" : null,
|
||||
"fr" : null
|
||||
}
|
||||
},
|
||||
"history.alert-message" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/history.cljs:111" ],
|
||||
"translations" : {
|
||||
"en" : "You are seeing version %s",
|
||||
"fr" : "Vous voyez la version %s"
|
||||
}
|
||||
},
|
||||
"image.import-library" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/images.cljs:137" ],
|
||||
"translations" : {
|
||||
"en" : "Import image from library",
|
||||
"fr" : "Importer une image depuis une librairie"
|
||||
}
|
||||
},
|
||||
"image.new" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/images.cljs:71" ],
|
||||
"translations" : {
|
||||
"en" : "New image",
|
||||
"fr" : "Nouvelle image"
|
||||
}
|
||||
},
|
||||
"image.select" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/images.cljs:75" ],
|
||||
"translations" : {
|
||||
"en" : "Select from library",
|
||||
"fr" : "Choisir depuis une librairie"
|
||||
}
|
||||
},
|
||||
"image.upload" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/images.cljs:80" ],
|
||||
"translations" : {
|
||||
"en" : "Upload file",
|
||||
"fr" : "Envoyer un fichier"
|
||||
}
|
||||
},
|
||||
"register.already-have-account" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/auth/register.cljs:131" ],
|
||||
"translations" : {
|
||||
"en" : "Already have an account?",
|
||||
"fr" : "Vous avez déjà un compte ?"
|
||||
}
|
||||
},
|
||||
"register.fullname.placeholder" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/auth/register.cljs:72" ],
|
||||
"translations" : {
|
||||
"en" : "Full Name",
|
||||
"fr" : "Nom complet"
|
||||
}
|
||||
},
|
||||
"register.get-started" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/auth/register.cljs:127" ],
|
||||
"translations" : {
|
||||
"en" : "Get started",
|
||||
"fr" : "Commencer"
|
||||
}
|
||||
},
|
||||
"register.password.placeholder" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/auth/register.cljs:115" ],
|
||||
"translations" : {
|
||||
"en" : "Password",
|
||||
"fr" : "Mot de passe"
|
||||
}
|
||||
},
|
||||
"settings.notifications" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/header.cljs:43" ],
|
||||
"translations" : {
|
||||
"en" : "NOTIFICATIONS",
|
||||
"fr" : "NOTIFICATIONS"
|
||||
}
|
||||
},
|
||||
"settings.notifications.description" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/notifications.cljs:19" ],
|
||||
"translations" : {
|
||||
"en" : "Get a roll up of prototype changes in your inbox.",
|
||||
"fr" : "Obtenez un résumé des modifications apportées aux prototypes à votre adresse email."
|
||||
}
|
||||
},
|
||||
"settings.notifications.every-day" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/notifications.cljs:38" ],
|
||||
"translations" : {
|
||||
"en" : "Every day",
|
||||
"fr" : "Chaque jour"
|
||||
}
|
||||
},
|
||||
"settings.notifications.every-hour" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/notifications.cljs:32" ],
|
||||
"translations" : {
|
||||
"en" : "Every hour",
|
||||
"fr" : "Chaque heure"
|
||||
}
|
||||
},
|
||||
"settings.notifications.none" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/notifications.cljs:26" ],
|
||||
"translations" : {
|
||||
"en" : "None",
|
||||
"fr" : "Aucune"
|
||||
}
|
||||
},
|
||||
"settings.notifications.notifications-saved" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/notifications.cljs:18" ],
|
||||
"translations" : {
|
||||
"en" : "Notifications preferences saved successfully!",
|
||||
"fr" : "Préférences de notifications enregistrées avec succès !"
|
||||
}
|
||||
},
|
||||
"settings.password" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/header.cljs:40" ],
|
||||
"translations" : {
|
||||
"en" : "PASSWORD",
|
||||
"fr" : "MOT DE PASSE"
|
||||
}
|
||||
},
|
||||
"settings.password.change-password" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/password.cljs:50" ],
|
||||
"translations" : {
|
||||
"en" : "Change password",
|
||||
"fr" : "Changement de mot de passe"
|
||||
}
|
||||
},
|
||||
"settings.password.confirm-password" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/password.cljs:79" ],
|
||||
"translations" : {
|
||||
"en" : "Confirm password",
|
||||
"fr" : "Confirmez mot de passe"
|
||||
}
|
||||
},
|
||||
"settings.password.new-password" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/password.cljs:69" ],
|
||||
"translations" : {
|
||||
"en" : "New password",
|
||||
"fr" : "Nouveau mot de passe"
|
||||
}
|
||||
},
|
||||
"settings.password.old-password" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/password.cljs:58" ],
|
||||
"translations" : {
|
||||
"en" : "Old password",
|
||||
"fr" : "Ancien mot de passe"
|
||||
}
|
||||
},
|
||||
"settings.password.password-saved" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/password.cljs:33" ],
|
||||
"translations" : {
|
||||
"en" : "Password saved successfully!",
|
||||
"fr" : "Mot de passe enregistré avec succès !"
|
||||
}
|
||||
},
|
||||
"settings.profile" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/header.cljs:37" ],
|
||||
"translations" : {
|
||||
"en" : "PROFILE",
|
||||
"fr" : "PROFIL"
|
||||
}
|
||||
},
|
||||
"settings.profile.profile-saved" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:68" ],
|
||||
"translations" : {
|
||||
"en" : "Profile saved successfully!",
|
||||
"fr" : "Profil enregistré avec succès !"
|
||||
}
|
||||
},
|
||||
"settings.profile.section-basic-data" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:78" ],
|
||||
"translations" : {
|
||||
"en" : "Name, username and email",
|
||||
"fr" : "Nom, nom d'utilisateur et adresse email"
|
||||
}
|
||||
},
|
||||
"settings.profile.section-i18n-data" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:117" ],
|
||||
"translations" : {
|
||||
"en" : "Default language",
|
||||
"fr" : "Langue par défaut"
|
||||
}
|
||||
},
|
||||
"settings.profile.your-avatar" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:159" ],
|
||||
"translations" : {
|
||||
"en" : "Your avatar",
|
||||
"fr" : "Votre avatar"
|
||||
}
|
||||
},
|
||||
"settings.profile.your-email" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:111", "src/uxbox/main/ui/auth/register.cljs:101" ],
|
||||
"translations" : {
|
||||
"en" : "Your email",
|
||||
"fr" : "Votre adresse email"
|
||||
}
|
||||
},
|
||||
"settings.profile.your-name" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:86" ],
|
||||
"translations" : {
|
||||
"en" : "Your name",
|
||||
"fr" : "Votre nom complet"
|
||||
}
|
||||
},
|
||||
"settings.profile.your-username" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:98", "src/uxbox/main/ui/auth/register.cljs:87" ],
|
||||
"translations" : {
|
||||
"en" : "Your username",
|
||||
"fr" : "Votre nom d'utilisateur"
|
||||
}
|
||||
},
|
||||
"settings.update-settings" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:130", "src/uxbox/main/ui/settings/password.cljs:86", "src/uxbox/main/ui/settings/notifications.cljs:42" ],
|
||||
"translations" : {
|
||||
"en" : "Update settings",
|
||||
"fr" : "Mettre à jour les paramètres"
|
||||
}
|
||||
},
|
||||
"workspace.header.canvas" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:93" ],
|
||||
"translations" : {
|
||||
"en" : "Canvas",
|
||||
"fr" : "Calque"
|
||||
}
|
||||
},
|
||||
"workspace.header.circle" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:103" ],
|
||||
"translations" : {
|
||||
"en" : "Circle (Ctrl + E)",
|
||||
"fr" : "Cercle (Ctrl + E)"
|
||||
}
|
||||
},
|
||||
"workspace.header.color-palette" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:123" ],
|
||||
"translations" : {
|
||||
"en" : "Color Palette (---)",
|
||||
"fr" : "Palette de couleurs (---)"
|
||||
}
|
||||
},
|
||||
"workspace.header.curve" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:118" ],
|
||||
"translations" : {
|
||||
"en" : "Curve",
|
||||
"fr" : "Courbe"
|
||||
}
|
||||
},
|
||||
"workspace.header.document-history" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:143" ],
|
||||
"translations" : {
|
||||
"en" : "History (Ctrl + Shift + H)",
|
||||
"fr" : "Historique du document (Ctrl + Maj + H)"
|
||||
}
|
||||
},
|
||||
"workspace.header.download" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:156" ],
|
||||
"translations" : {
|
||||
"en" : "Download (Ctrl + E)",
|
||||
"fr" : "Télécharger (Ctrl + E)"
|
||||
}
|
||||
},
|
||||
"workspace.header.grid" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:170" ],
|
||||
"translations" : {
|
||||
"en" : "Grid (Ctrl + G)",
|
||||
"fr" : "Grille (Ctrl + G)"
|
||||
}
|
||||
},
|
||||
"workspace.header.grid-snap" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:175" ],
|
||||
"translations" : {
|
||||
"en" : "Snap to grid",
|
||||
"fr" : "Coller à la grille"
|
||||
}
|
||||
},
|
||||
"workspace.header.icons" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:128" ],
|
||||
"translations" : {
|
||||
"en" : "Icons (Ctrl + Shift + I)",
|
||||
"fr" : "Icônes (Ctrl + Maj + I)"
|
||||
}
|
||||
},
|
||||
"workspace.header.image" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:161" ],
|
||||
"translations" : {
|
||||
"en" : "Image (Ctrl + I)",
|
||||
"fr" : "Image (Ctrl + I)"
|
||||
}
|
||||
},
|
||||
"workspace.header.path" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:113" ],
|
||||
"translations" : {
|
||||
"en" : "Path",
|
||||
"fr" : "Chemin"
|
||||
}
|
||||
},
|
||||
"workspace.header.rect" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:98" ],
|
||||
"translations" : {
|
||||
"en" : "Box (Ctrl + B)",
|
||||
"fr" : "Boîte (Ctrl + B)"
|
||||
}
|
||||
},
|
||||
"workspace.header.rules" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:165" ],
|
||||
"translations" : {
|
||||
"en" : "Rules",
|
||||
"fr" : "Règles"
|
||||
}
|
||||
},
|
||||
"workspace.header.text" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:108" ],
|
||||
"translations" : {
|
||||
"en" : "Text",
|
||||
"fr" : "Texte"
|
||||
}
|
||||
},
|
||||
"workspace.header.view-mode" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:186" ],
|
||||
"translations" : {
|
||||
"en" : "View mode (Ctrl + P)",
|
||||
"fr" : "Mode visualisation (Ctrl + P)"
|
||||
}
|
||||
},
|
||||
"workspace.options.color" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:81", "src/uxbox/main/ui/workspace/sidebar/options/page.cljs:125", "src/uxbox/main/ui/workspace/sidebar/options/fill.cljs:47" ],
|
||||
"translations" : {
|
||||
"en" : "Color",
|
||||
"fr" : "Couleur"
|
||||
}
|
||||
},
|
||||
"workspace.options.font-family" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:204" ],
|
||||
"translations" : {
|
||||
"en" : "Font Family",
|
||||
"fr" : "Police de caractères"
|
||||
}
|
||||
},
|
||||
"workspace.options.font-options" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:202" ],
|
||||
"translations" : {
|
||||
"en" : "Fonts & Font Size",
|
||||
"fr" : "TODO"
|
||||
}
|
||||
},
|
||||
"workspace.options.font-weight" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:213" ],
|
||||
"translations" : {
|
||||
"en" : "Font Size & Weight",
|
||||
"fr" : "Taille et graisse"
|
||||
}
|
||||
},
|
||||
"workspace.options.grid-options" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/page.cljs:113" ],
|
||||
"translations" : {
|
||||
"en" : "Grid settings",
|
||||
"fr" : "Paramètres de la grille"
|
||||
}
|
||||
},
|
||||
"workspace.options.line-height-letter-spacing" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:245" ],
|
||||
"translations" : {
|
||||
"en" : "Line height and Letter spacing",
|
||||
"fr" : "Hauteur de ligne et Espacement de caractères"
|
||||
}
|
||||
},
|
||||
"workspace.options.measures" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:70", "src/uxbox/main/ui/workspace/sidebar/options/rect.cljs:64", "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:65", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:62", "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:115" ],
|
||||
"translations" : {
|
||||
"en" : "Size, position & rotation",
|
||||
"fr" : "Taille, position et rotation"
|
||||
}
|
||||
},
|
||||
"workspace.options.opacity" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:89", "src/uxbox/main/ui/workspace/sidebar/options/fill.cljs:58" ],
|
||||
"translations" : {
|
||||
"en" : "Opacity",
|
||||
"fr" : "Opacité"
|
||||
}
|
||||
},
|
||||
"workspace.options.position" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:99", "src/uxbox/main/ui/workspace/sidebar/options/rect.cljs:93", "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:93", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:91", "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:74" ],
|
||||
"translations" : {
|
||||
"en" : "Position",
|
||||
"fr" : "Position"
|
||||
}
|
||||
},
|
||||
"workspace.options.rotation-radius" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:116", "src/uxbox/main/ui/workspace/sidebar/options/rect.cljs:110", "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:108", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:108", "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:92" ],
|
||||
"translations" : {
|
||||
"en" : "Rotation & Radius",
|
||||
"fr" : "TODO"
|
||||
}
|
||||
},
|
||||
"workspace.options.size" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:72", "src/uxbox/main/ui/workspace/sidebar/options/rect.cljs:66", "src/uxbox/main/ui/workspace/sidebar/options/page.cljs:115", "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:69", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:64", "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:38" ],
|
||||
"translations" : {
|
||||
"en" : "Size",
|
||||
"fr" : "Taille"
|
||||
}
|
||||
},
|
||||
"workspace.options.stroke" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:58" ],
|
||||
"translations" : {
|
||||
"en" : "Stroke",
|
||||
"fr" : null
|
||||
}
|
||||
},
|
||||
"workspace.options.stroke.dashed" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:69" ],
|
||||
"translations" : {
|
||||
"en" : "Dashed",
|
||||
"fr" : "Tiré"
|
||||
}
|
||||
},
|
||||
"workspace.options.stroke.dotted" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:68" ],
|
||||
"translations" : {
|
||||
"en" : "Dotted",
|
||||
"fr" : "Pointillé"
|
||||
}
|
||||
},
|
||||
"workspace.options.stroke.mixed" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:70" ],
|
||||
"translations" : {
|
||||
"en" : "Mixed",
|
||||
"fr" : "Mixte"
|
||||
}
|
||||
},
|
||||
"workspace.options.stroke.none" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:66" ],
|
||||
"translations" : {
|
||||
"en" : "None",
|
||||
"fr" : "Aucun"
|
||||
}
|
||||
},
|
||||
"workspace.options.stroke.solid" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:67" ],
|
||||
"translations" : {
|
||||
"en" : "Solid",
|
||||
"fr" : "Solide"
|
||||
}
|
||||
},
|
||||
"workspace.options.stroke.style" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:62" ],
|
||||
"translations" : {
|
||||
"en" : "Style",
|
||||
"fr" : "Style"
|
||||
}
|
||||
},
|
||||
"workspace.options.text-align" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:264" ],
|
||||
"translations" : {
|
||||
"en" : "Text Alignment",
|
||||
"fr" : "Alignement de texte"
|
||||
}
|
||||
},
|
||||
"workspace.sidebar.sitemap" : {
|
||||
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/sitemap.cljs:134" ],
|
||||
"translations" : {
|
||||
"en" : "Sitemap",
|
||||
"fr" : "Plan du site"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,6 +15,6 @@
|
|||
|
||||
<script src="/js/cljs_base.js?ts={{& ts}}"></script>
|
||||
<script src="/js/main.js?ts={{& ts}}"></script>
|
||||
<script>uxbox.main.init()</script>
|
||||
<script>uxbox.main.init({{& tr }})</script>
|
||||
</body>
|
||||
</html>
|
|
@ -10,8 +10,6 @@
|
|||
[rumext.alpha :as mf]
|
||||
[uxbox.main.data.auth :refer [logout]]
|
||||
[uxbox.main.data.users :as udu]
|
||||
[uxbox.main.locales.en :as en]
|
||||
[uxbox.main.locales.fr :as fr]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.ui :as ui]
|
||||
[uxbox.main.ui.lightbox :refer [lightbox]]
|
||||
|
@ -19,7 +17,7 @@
|
|||
[uxbox.main.ui.loader :refer [loader]]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.html.history :as html-history]
|
||||
[uxbox.util.i18n :as i18n :refer [tr]]
|
||||
[uxbox.util.i18n :as i18n]
|
||||
[uxbox.util.messages :as uum]
|
||||
[uxbox.util.router :as rt]
|
||||
[uxbox.util.storage :refer [storage]]
|
||||
|
@ -30,15 +28,15 @@
|
|||
(declare reinit)
|
||||
(s/check-asserts true)
|
||||
|
||||
(i18n/update-locales! (fn [locales]
|
||||
(-> locales
|
||||
(assoc "en" en/locales)
|
||||
(assoc "fr" fr/locales))))
|
||||
;; (i18n/update-locales! (fn [locales]
|
||||
;; (-> locales
|
||||
;; (assoc "en" en/locales)
|
||||
;; (assoc "fr" fr/locales))))
|
||||
|
||||
(i18n/on-locale-change!
|
||||
(fn [new old]
|
||||
(println "Locale changed from" old " to " new)
|
||||
(reinit)))
|
||||
;; (i18n/on-locale-change!
|
||||
;; (fn [new old]
|
||||
;; (println "Locale changed from" old " to " new)
|
||||
;; (reinit)))
|
||||
|
||||
;; --- Error Handling
|
||||
|
||||
|
@ -81,7 +79,8 @@
|
|||
(def app-sym (.for js/Symbol "uxbox.app"))
|
||||
|
||||
(defn ^:export init
|
||||
[]
|
||||
[translations]
|
||||
(i18n/init! (js/JSON.parse translations))
|
||||
(unchecked-set js/window app-sym "main")
|
||||
(st/init)
|
||||
(init-ui))
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
[{:keys [on-submit value] :as props}]
|
||||
(let [local (mf/use-var value)]
|
||||
[:div.lightbox-body
|
||||
[:h3 (tr "ds.color-lightbox.title")]
|
||||
[:h3 (tr "ds.color-lightbox.title" )]
|
||||
[:form
|
||||
[:div.row-flex.center
|
||||
[:& colorpicker {:value (or @local "#00ccff")
|
||||
|
|
|
@ -190,75 +190,75 @@
|
|||
[:li {:key (pr-str id)}
|
||||
[:a {:on-click #(on-select % id)} name]])])))
|
||||
|
||||
(mf/def grid-options
|
||||
:mixins [(mf/local) mf/memo]
|
||||
;; (mf/def grid-options
|
||||
;; :mixins [(mf/local) mf/memo]
|
||||
|
||||
:render
|
||||
(fn [{:keys [::mf/local] :as own}
|
||||
{:keys [id type selected] :as props}]
|
||||
(letfn [(delete []
|
||||
(st/emit! (di/delete-selected)))
|
||||
(on-delete [event]
|
||||
(modal/show! confirm-dialog {:on-accept delete}))
|
||||
(on-toggle-copy [event]
|
||||
(swap! local update :show-copy-tooltip not))
|
||||
(on-toggle-move [event]
|
||||
(swap! local update :show-move-tooltip not))
|
||||
(on-copy [selected]
|
||||
(swap! local assoc
|
||||
:show-move-tooltip false
|
||||
:show-copy-tooltip false)
|
||||
(st/emit! (di/copy-selected selected)))
|
||||
(on-move [selected]
|
||||
(swap! local assoc
|
||||
:show-move-tooltip false
|
||||
:show-copy-tooltip false)
|
||||
(st/emit! (di/move-selected selected)))
|
||||
(on-rename [event]
|
||||
(let [selected (first selected)]
|
||||
(st/emit! (di/update-opts :edition selected))))]
|
||||
;; :render
|
||||
;; (fn [{:keys [::mf/local] :as own}
|
||||
;; {:keys [id type selected] :as props}]
|
||||
;; (letfn [(delete []
|
||||
;; (st/emit! (di/delete-selected)))
|
||||
;; (on-delete [event]
|
||||
;; (modal/show! confirm-dialog {:on-accept delete}))
|
||||
;; (on-toggle-copy [event]
|
||||
;; (swap! local update :show-copy-tooltip not))
|
||||
;; (on-toggle-move [event]
|
||||
;; (swap! local update :show-move-tooltip not))
|
||||
;; (on-copy [selected]
|
||||
;; (swap! local assoc
|
||||
;; :show-move-tooltip false
|
||||
;; :show-copy-tooltip false)
|
||||
;; (st/emit! (di/copy-selected selected)))
|
||||
;; (on-move [selected]
|
||||
;; (swap! local assoc
|
||||
;; :show-move-tooltip false
|
||||
;; :show-copy-tooltip false)
|
||||
;; (st/emit! (di/move-selected selected)))
|
||||
;; (on-rename [event]
|
||||
;; (let [selected (first selected)]
|
||||
;; (st/emit! (di/update-opts :edition selected))))]
|
||||
|
||||
;; MULTISELECT OPTIONS BAR
|
||||
[:div.multiselect-bar
|
||||
(if (or (= type :own) (nil? id))
|
||||
;; if editable
|
||||
[:div.multiselect-nav {}
|
||||
[:span.move-item.tooltip.tooltip-top
|
||||
{:alt (tr "ds.multiselect-bar.copy")
|
||||
:on-click on-toggle-copy}
|
||||
(when (:show-copy-tooltip @local)
|
||||
(grid-options-tooltip {:selected id
|
||||
:title (tr "ds.multiselect-bar.copy-to-library")
|
||||
:on-select on-copy}))
|
||||
i/copy]
|
||||
[:span.move-item.tooltip.tooltip-top
|
||||
{:alt (tr "ds.multiselect-bar.move")
|
||||
:on-click on-toggle-move}
|
||||
(when (:show-move-tooltip @local)
|
||||
(grid-options-tooltip {:selected id
|
||||
:title (tr "ds.multiselect-bar.move-to-library")
|
||||
:on-select on-move}))
|
||||
i/move]
|
||||
(when (= 1 (count selected))
|
||||
[:span.move-item.tooltip.tooltip-top
|
||||
{:alt (tr "ds.multiselect-bar.rename")
|
||||
:on-click on-rename}
|
||||
i/pencil])
|
||||
[:span.delete.tooltip.tooltip-top
|
||||
{:alt (tr "ds.multiselect-bar.delete")
|
||||
:on-click on-delete}
|
||||
i/trash]]
|
||||
;; ;; MULTISELECT OPTIONS BAR
|
||||
;; [:div.multiselect-bar
|
||||
;; (if (or (= type :own) (nil? id))
|
||||
;; ;; if editable
|
||||
;; [:div.multiselect-nav {}
|
||||
;; [:span.move-item.tooltip.tooltip-top
|
||||
;; {:alt (tr "ds.multiselect-bar.copy")
|
||||
;; :on-click on-toggle-copy}
|
||||
;; (when (:show-copy-tooltip @local)
|
||||
;; (grid-options-tooltip {:selected id
|
||||
;; :title (tr "ds.multiselect-bar.copy-to-library")
|
||||
;; :on-select on-copy}))
|
||||
;; i/copy]
|
||||
;; [:span.move-item.tooltip.tooltip-top
|
||||
;; {:alt (tr "ds.multiselect-bar.move")
|
||||
;; :on-click on-toggle-move}
|
||||
;; (when (:show-move-tooltip @local)
|
||||
;; (grid-options-tooltip {:selected id
|
||||
;; :title (tr "ds.multiselect-bar.move-to-library")
|
||||
;; :on-select on-move}))
|
||||
;; i/move]
|
||||
;; (when (= 1 (count selected))
|
||||
;; [:span.move-item.tooltip.tooltip-top
|
||||
;; {:alt (tr "ds.multiselect-bar.rename")
|
||||
;; :on-click on-rename}
|
||||
;; i/pencil])
|
||||
;; [:span.delete.tooltip.tooltip-top
|
||||
;; {:alt (tr "ds.multiselect-bar.delete")
|
||||
;; :on-click on-delete}
|
||||
;; i/trash]]
|
||||
|
||||
;; if not editable
|
||||
[:div.multiselect-nav
|
||||
[:span.move-item.tooltip.tooltip-top
|
||||
{:alt (tr "ds.multiselect-bar.copy")
|
||||
:on-click on-toggle-copy}
|
||||
(when (:show-copy-tooltip @local)
|
||||
(grid-options-tooltip {:selected id
|
||||
:title (tr "ds.multiselect-bar.copy-to-library")
|
||||
:on-select on-copy}))
|
||||
i/organize]])])))
|
||||
;; ;; if not editable
|
||||
;; [:div.multiselect-nav
|
||||
;; [:span.move-item.tooltip.tooltip-top
|
||||
;; {:alt (tr "ds.multiselect-bar.copy")
|
||||
;; :on-click on-toggle-copy}
|
||||
;; (when (:show-copy-tooltip @local)
|
||||
;; (grid-options-tooltip {:selected id
|
||||
;; :title (tr "ds.multiselect-bar.copy-to-library")
|
||||
;; :on-select on-copy}))
|
||||
;; i/organize]])])))
|
||||
|
||||
;; --- Grid Item
|
||||
|
||||
|
@ -349,7 +349,7 @@
|
|||
:coll coll
|
||||
:opts opts}]
|
||||
(when (seq (:selected opts))
|
||||
[:& grid-options {:id id :type type :selected (:selected opts)}])]]))
|
||||
#_[:& grid-options {:id id :type type :selected (:selected opts)}])]]))
|
||||
|
||||
;; --- Icons Page
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
[uxbox.main.ui.modal :as modal]
|
||||
[uxbox.main.ui.keyboard :as kbd]
|
||||
[uxbox.main.ui.confirm :refer [confirm-dialog]]
|
||||
[uxbox.main.ui.dashboard.projects-forms :refer [create-project-dialog]]
|
||||
[uxbox.main.ui.dashboard.common :as common]
|
||||
[uxbox.util.data :refer [read-string]]
|
||||
[uxbox.util.dom :as dom]
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2015-2019 Andrey Antukh <niwi@niwi.nz>
|
||||
;; Copyright (c) 2015-2019 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(ns uxbox.main.ui.dashboard.projects-forms
|
||||
(:require
|
||||
[cljs.spec.alpha :as s]
|
||||
[rumext.alpha :as mf]
|
||||
[uxbox.builtins.icons :as i]
|
||||
[uxbox.main.data.projects :as udp]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.ui.modal :as modal]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.forms :as fm]
|
||||
[uxbox.util.i18n :as t :refer [tr]]))
|
||||
|
||||
(s/def ::name ::fm/not-empty-string)
|
||||
(s/def ::width ::fm/number-str)
|
||||
(s/def ::height ::fm/number-str)
|
||||
|
||||
(s/def ::project-form
|
||||
(s/keys :req-un [::name]))
|
||||
|
||||
(def defaults {:name ""})
|
||||
|
||||
;; --- Create Project Form
|
||||
|
||||
(defn- on-submit
|
||||
[event form]
|
||||
(dom/prevent-default event)
|
||||
(let [data (:clean-data form)]
|
||||
(st/emit! (udp/create-project data))
|
||||
(modal/hide!)))
|
||||
|
||||
(defn- swap-size
|
||||
[event {:keys [data] :as form}]
|
||||
(swap! data assoc
|
||||
:width (:height data)
|
||||
:height (:width data)))
|
||||
|
||||
(mf/defc create-project-form
|
||||
[props]
|
||||
(let [{:keys [data] :as form} (fm/use-form ::project-form defaults)]
|
||||
[:form {:on-submit #(on-submit % form)}
|
||||
[:input.input-text
|
||||
{:placeholder (tr "ds.project.placeholder")
|
||||
:type "text"
|
||||
:name "name"
|
||||
:value (:name data)
|
||||
:class (fm/error-class form :name)
|
||||
:on-blur (fm/on-input-blur form :name)
|
||||
:on-change (fm/on-input-change form :name)
|
||||
:auto-focus true}]
|
||||
;; Submit
|
||||
[:input#project-btn.btn-primary
|
||||
{:value (tr "ds.go")
|
||||
:class (when-not (:valid form) "btn-disabled")
|
||||
:disabled (not (:valid form))
|
||||
:type "submit"}]]))
|
||||
|
||||
;; --- Create Project Lightbox
|
||||
|
||||
(mf/defc create-project-dialog
|
||||
[props]
|
||||
[:div.lightbox-body
|
||||
[:h3 (tr "ds.project.new")]
|
||||
[:& create-project-form]
|
||||
[:a.close {:on-click modal/hide!} i/close]])
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
[uxbox.main.ui.workspace.sortable :refer [use-sortable]]
|
||||
[uxbox.util.data :refer [classnames enumerate]]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.i18n :refer [tr]]
|
||||
[uxbox.util.i18n :as i18n :refer [tr]]
|
||||
[uxbox.util.router :as rt]))
|
||||
|
||||
;; --- Page Item
|
||||
|
@ -128,7 +128,8 @@
|
|||
|
||||
(mf/defc sitemap-toolbox
|
||||
[{:keys [file page] :as props}]
|
||||
(let [on-create-click #(st/emit! dp/create-empty-page)]
|
||||
(let [on-create-click #(st/emit! dp/create-empty-page)
|
||||
tr (i18n/use-translations)]
|
||||
[:div.sitemap.tool-window
|
||||
[:div.tool-window-bar
|
||||
[:span (tr "workspace.sidebar.sitemap")]
|
||||
|
|
|
@ -7,72 +7,86 @@
|
|||
|
||||
(ns uxbox.util.i18n
|
||||
"A i18n foundation."
|
||||
(:require [cuerdas.core :as str]
|
||||
[uxbox.config :as cfg]
|
||||
[uxbox.util.storage :refer [storage]]))
|
||||
(:require
|
||||
[cuerdas.core :as str]
|
||||
[rumext.alpha :as mf]
|
||||
[beicon.core :as rx]
|
||||
[goog.object :as gobj]
|
||||
[uxbox.config :as cfg]
|
||||
[uxbox.util.transit :as t]
|
||||
[uxbox.util.storage :refer [storage]]))
|
||||
|
||||
(defonce locale (atom (get storage ::locale cfg/default-language)))
|
||||
(defonce state (atom {}))
|
||||
(defonce locale (get storage ::locale cfg/default-language))
|
||||
(defonce locale-sub (rx/subject))
|
||||
(defonce translations #js {})
|
||||
|
||||
(defn update-locales!
|
||||
[callback]
|
||||
(swap! state callback))
|
||||
;; The traslations `data` is a javascript object and should be treated
|
||||
;; with `goog.object` namespace functions instead of a standart
|
||||
;; clojure functions. This is for performance reasons because this
|
||||
;; code is executed in the critical part (application bootstrap) and
|
||||
;; used in many parts of the application.
|
||||
|
||||
(defn init!
|
||||
[data]
|
||||
(set! translations data))
|
||||
|
||||
(defn set-current-locale!
|
||||
[v]
|
||||
(swap! storage assoc ::locale v)
|
||||
(reset! locale v))
|
||||
(set! locale v)
|
||||
(rx/push! locale-sub v))
|
||||
|
||||
(defn set-default-locale!
|
||||
[]
|
||||
(set-current-locale! cfg/default-language))
|
||||
|
||||
(defn on-locale-change!
|
||||
[callback]
|
||||
(add-watch locale ::main (fn [_ _ old-locale new-locale]
|
||||
(when (not= old-locale new-locale)
|
||||
(callback new-locale old-locale)))))
|
||||
|
||||
;; A marker type that is used just for mark
|
||||
;; a parameter that reprsentes the counter.
|
||||
|
||||
(deftype C [val]
|
||||
IDeref
|
||||
(-deref [o] val))
|
||||
|
||||
(defn c
|
||||
[x]
|
||||
(C. x))
|
||||
|
||||
(defn ^boolean c?
|
||||
[r]
|
||||
(instance? C r))
|
||||
|
||||
(defn- internal-tr
|
||||
([locale code]
|
||||
(let [code (name code)
|
||||
value (gobj/getValueByKeys translations code locale)
|
||||
value (if (nil? value) code value)]
|
||||
(if (array? value)
|
||||
(aget value 0)
|
||||
value)))
|
||||
([locale code & args]
|
||||
(let [code (name code)
|
||||
value (gobj/getValueByKeys translations code locale)
|
||||
value (if (nil? value) code value)
|
||||
plural (first (filter c? args))
|
||||
value (if (array? value)
|
||||
(if (= @plural 1) (aget value 0) (aget value 1))
|
||||
value)]
|
||||
(apply str/format value (map #(if (c? %) @% %) args)))))
|
||||
|
||||
;; A main public api for translate strings.
|
||||
|
||||
;; A marker type that is used just for mark
|
||||
;; a parameter that reprsentes the counter.
|
||||
|
||||
(defn c
|
||||
[x]
|
||||
(C. x))
|
||||
|
||||
(defn tr
|
||||
"Translate the string."
|
||||
([t]
|
||||
(let [default (name t)
|
||||
locale (deref locale)
|
||||
value (or (get-in @state [locale t])
|
||||
default)]
|
||||
(if (vector? value)
|
||||
(or (second value) default)
|
||||
value)))
|
||||
([t & args]
|
||||
(let [locale (deref locale)
|
||||
value (get-in @state [locale t] (name t))
|
||||
plural (first (filter c? args))
|
||||
args (mapv #(if (c? %) @% %) args)
|
||||
value (cond
|
||||
(and (vector? value)
|
||||
(= 3 (count value)))
|
||||
(nth value (min 2 @plural))
|
||||
([code] (internal-tr locale code))
|
||||
([code & args] (apply internal-tr locale code args)))
|
||||
|
||||
(vector? value)
|
||||
(if (= @plural 1) (first value) (second value))
|
||||
(defn use-translations
|
||||
[]
|
||||
(let [[locale set-locale] (mf/useState locale)
|
||||
tr-fn (mf/useMemo (fn [] (partial internal-tr locale))
|
||||
#js [locale])]
|
||||
(mf/useEffect (fn []
|
||||
(let [sub (rx/sub! locale-sub #(set-locale %))]
|
||||
#(rx/dispose! sub)))
|
||||
#js [])
|
||||
tr-fn))
|
||||
|
||||
:else
|
||||
value)]
|
||||
(apply str/format value args))))
|
||||
|
|
|
@ -19,17 +19,17 @@
|
|||
[uxbox.view.ui.lightbox :refer [lightbox]]
|
||||
[uxbox.view.ui.loader :refer [loader]]))
|
||||
|
||||
(i18n/update-locales! (fn [locales]
|
||||
(-> locales
|
||||
(assoc "en" en/locales)
|
||||
(assoc "fr" fr/locales))))
|
||||
;; (i18n/update-locales! (fn [locales]
|
||||
;; (-> locales
|
||||
;; (assoc "en" en/locales)
|
||||
;; (assoc "fr" fr/locales))))
|
||||
|
||||
(declare reinit)
|
||||
|
||||
(i18n/on-locale-change!
|
||||
(fn [new old]
|
||||
(println "Locale changed from" old " to " new)
|
||||
(reinit)))
|
||||
;; (i18n/on-locale-change!
|
||||
;; (fn [new old]
|
||||
;; (println "Locale changed from" old " to " new)
|
||||
;; (reinit)))
|
||||
|
||||
(defn- on-navigate
|
||||
[router path]
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
(require '[clojure.pprint :refer [pprint]])
|
||||
(require '[clojure.java.shell :as shell])
|
||||
(require '[figwheel.main.api :as figwheel])
|
||||
(require '[environ.core :refer [env]])
|
||||
(require '[clojure.pprint :refer [pprint]]
|
||||
'[clojure.java.shell :as shell]
|
||||
'[clojure.java.io :as io]
|
||||
'[figwheel.main.api :as figwheel]
|
||||
'[environ.core :refer [env]]
|
||||
'[jsonista.core :as json]
|
||||
'[cognitect.transit :as t])
|
||||
(require '[cljs.build.api :as api]
|
||||
'[cljs.repl :as repl]
|
||||
'[cljs.repl.node :as node])
|
||||
|
@ -12,6 +15,8 @@
|
|||
'[rebel-readline.cljs.service.local]
|
||||
'[rebel-readline.cljs.repl])
|
||||
|
||||
(import 'java.io.ByteArrayOutputStream)
|
||||
|
||||
(defmulti task first)
|
||||
|
||||
(defmethod task :default
|
||||
|
@ -39,7 +44,7 @@
|
|||
:closure-defines closure-defines
|
||||
:optimizations :none
|
||||
:infer-externs true
|
||||
:verbose true
|
||||
:verbose false
|
||||
:source-map true
|
||||
:static-fns false
|
||||
:pretty-print true
|
||||
|
@ -153,7 +158,7 @@
|
|||
(api/build (api/inputs "src" "test")
|
||||
(assoc default-build-options
|
||||
:main 'uxbox.tests.main
|
||||
:verbose true
|
||||
:verbose false
|
||||
:target :nodejs
|
||||
:source-map true
|
||||
:output-to "target/tests/main.js"
|
||||
|
|
178
frontend/translations.clj
Normal file
178
frontend/translations.clj
Normal file
|
@ -0,0 +1,178 @@
|
|||
(require '[clojure.pprint :as pp :refer [pprint]])
|
||||
(require '[clojure.java.shell :as shell])
|
||||
(require '[environ.core :refer [env]])
|
||||
|
||||
(require '[clojure.walk :as walk]
|
||||
'[clojure.edn :as edn]
|
||||
'[clojure.set :as set])
|
||||
|
||||
(require '[datoteka.core :as fs]
|
||||
'[jsonista.core :as json])
|
||||
(require '[clojure.java.io :as io]
|
||||
'[clojure.tools.reader :as r]
|
||||
'[clojure.tools.reader.reader-types :as rt])
|
||||
|
||||
(import 'java.nio.file.Paths
|
||||
'java.nio.file.Path
|
||||
'java.nio.file.Files
|
||||
'java.nio.file.SimpleFileVisitor
|
||||
'java.nio.file.FileVisitResult)
|
||||
|
||||
(extend-protocol io/Coercions
|
||||
Path
|
||||
(as-file [it] (.toFile it))
|
||||
(as-url [it] (io/as-url (.toFile it))))
|
||||
|
||||
(defmulti task first)
|
||||
|
||||
(defn- find-translations-in-form
|
||||
[env form]
|
||||
(->> form
|
||||
(walk/postwalk
|
||||
(fn [fm]
|
||||
(when (and (list? fm)
|
||||
(= (first fm) 'tr)
|
||||
(string? (second fm)))
|
||||
(let [m (meta (first fm))]
|
||||
(swap! env conj {:code (second fm)
|
||||
:file (:file m)
|
||||
:line (:line m)})))
|
||||
fm))))
|
||||
|
||||
(defn- find-translations-in-file
|
||||
[env file]
|
||||
(let [rdr (-> (io/as-file file)
|
||||
(io/reader)
|
||||
(rt/source-logging-push-back-reader 1 file))]
|
||||
(try
|
||||
(binding [r/*default-data-reader-fn* (constantly nil)
|
||||
r/*alias-map* {'dw (create-ns 'user)
|
||||
'fm (create-ns 'user)
|
||||
'us (create-ns 'user)
|
||||
'dp (create-ns 'user)
|
||||
'cp (create-ns 'user)}]
|
||||
(loop []
|
||||
(let [form (r/read {:eof ::end} rdr)]
|
||||
(when (not= ::end form)
|
||||
(find-translations-in-form env form)
|
||||
(recur)))))
|
||||
(catch Exception e
|
||||
;; (.printStackTrace e)
|
||||
(println (str "ERROR: on procesing " file "; ignoring..."))))))
|
||||
|
||||
(defn- find-translations-in-directory
|
||||
[env file]
|
||||
(->> (proxy [SimpleFileVisitor] []
|
||||
(visitFile [path attrs]
|
||||
(when (= (fs/ext path) "cljs")
|
||||
(find-translations-in-file env path))
|
||||
FileVisitResult/CONTINUE)
|
||||
(postVisitDirectory [dir exc]
|
||||
FileVisitResult/CONTINUE))
|
||||
(Files/walkFileTree (fs/path file))))
|
||||
|
||||
(defn- collect-translations
|
||||
[path]
|
||||
(let [env (atom [])]
|
||||
(find-translations-in-directory env path)
|
||||
@env))
|
||||
|
||||
(defn- read-json-file
|
||||
[path]
|
||||
(when (fs/regular-file? path)
|
||||
(let [content (json/read-value (slurp (io/as-file path)))]
|
||||
(into (sorted-map) content))))
|
||||
|
||||
(defn- read-edn-file
|
||||
[path]
|
||||
(when (fs/regular-file? path)
|
||||
(let [content (edn/read-string (slurp (io/as-file path)))]
|
||||
(into (sorted-map) content))))
|
||||
|
||||
|
||||
(defn- add-translation
|
||||
[data {:keys [code file line] :as translation}]
|
||||
(let [rpath (str file ":" line)]
|
||||
(if (contains? data code)
|
||||
(update data code (fn [state]
|
||||
(if (get state "permanent")
|
||||
state
|
||||
(-> state
|
||||
(dissoc "unused")
|
||||
(assoc "used-in" (->> (get state "used-in" [])
|
||||
(remove #(= rpath %))
|
||||
(into [rpath])))))))
|
||||
(assoc data code {"translations" {"en" nil "fr" nil}
|
||||
"used-in" [rpath]}))))
|
||||
|
||||
(defn- clean-removed-translations
|
||||
[data imported]
|
||||
(let [existing (into #{} (keys data))
|
||||
toremove (set/difference existing imported)]
|
||||
(reduce (fn [data code]
|
||||
(if (get-in data [code "permanent"])
|
||||
data
|
||||
(-> data
|
||||
(update code dissoc "used-in")
|
||||
(update code assoc "unused" true))))
|
||||
data
|
||||
toremove)))
|
||||
|
||||
(defn- ensure-translations-format
|
||||
[data]
|
||||
(reduce-kv (fn [data k v]
|
||||
(if (string? v)
|
||||
(assoc data k {:translations {:en v}})
|
||||
data))
|
||||
data
|
||||
data))
|
||||
|
||||
(defn- synchronize-translations
|
||||
[data translations]
|
||||
(loop [data (ensure-translations-format data)
|
||||
imported #{}
|
||||
c (first translations)
|
||||
r (rest translations)]
|
||||
(if (nil? c)
|
||||
(clean-removed-translations data imported)
|
||||
(recur (add-translation data c)
|
||||
(conj imported (:code c))
|
||||
(first r)
|
||||
(rest r)))))
|
||||
|
||||
(defn- synchronize-legacy-translations
|
||||
[data legacy-data lang]
|
||||
(reduce-kv (fn [data k v]
|
||||
(if (contains? data k)
|
||||
(update-in data [k "translations"] assoc lang v)
|
||||
data))
|
||||
data
|
||||
legacy-data))
|
||||
|
||||
(defn- write-result!
|
||||
[data output-path]
|
||||
(binding [*out* (io/writer (fs/path output-path))]
|
||||
(let [mapper (json/object-mapper {:pretty true})]
|
||||
(println (json/write-value-as-string data mapper))
|
||||
(flush))))
|
||||
|
||||
(defn- update-translations
|
||||
[{:keys [find-directory output-path] :as props}]
|
||||
(let [data (read-json-file output-path)
|
||||
translations (collect-translations find-directory)
|
||||
data (synchronize-translations data translations)]
|
||||
(write-result! data output-path)))
|
||||
|
||||
(defmethod task "collectmessages"
|
||||
[[_ in-path out-path]]
|
||||
(update-translations {:find-directory in-path
|
||||
:output-path out-path}))
|
||||
|
||||
(defmethod task "merge-with-legacy"
|
||||
[[_ path lang legacy-path]]
|
||||
(let [ldata (read-edn-file legacy-path)
|
||||
data (read-json-file path)
|
||||
data (synchronize-legacy-translations data ldata lang)]
|
||||
(write-result! data path)))
|
||||
|
||||
(task *command-line-args*)
|
Loading…
Add table
Reference in a new issue