From f5a9f3201984aaff283dbd15f3fceac1ca9aae4f Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Mon, 27 Dec 2021 17:18:56 -0800 Subject: [PATCH 01/18] Revert previous solution, and do dropzone i18n the right way. (#572) --- src/web/templates/base.html | 5497 +--------------------------------- src/web/templates/index.html | 20 +- 2 files changed, 21 insertions(+), 5496 deletions(-) diff --git a/src/web/templates/base.html b/src/web/templates/base.html index 79bb96f5..da316687 100644 --- a/src/web/templates/base.html +++ b/src/web/templates/base.html @@ -34,5503 +34,10 @@ document.getElementById("flash").innerHTML = "
" + Notification + "{{ _(" The Web Interface will become unresponsive momentarily. Reload this page after the Pi has started up again.") }}
"; window.scrollTo(0,0); } - -/* -dropzone.min.js v5.9.3 - -Slightly modified for the RaSCSI project to work with jinja2 templatization and pybabel: - - Placeholder symbols changed to avoid interfering with the jinja2 template syntax - - Curly braces removed - - filesize -> FILE_SIZE - - maxFilesize -> MAX_FILE_SIZE - - UI strings enclosed in gettext calls and slightly reformatted to be easier to translate - -LICENSE - -(The MIT License) - -Copyright (c) 2021 Matias Meno -Logo (c) 2015 "1910" www.weare1910.com - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -! function(e, t) { - if ("object" == typeof exports && "object" == typeof module) module.exports = t(); - else if ("function" == typeof define && define.amd) define([], t); - else { - var n = t(); - for (var r in n)("object" == typeof exports ? exports : e)[r] = n[r] - } -}(self, (function() { - return function() { - var e = { - 3099: function(e) { - e.exports = function(e) { - if ("function" != typeof e) throw TypeError(String(e) + " is not a function"); - return e - } - }, - 6077: function(e, t, n) { - var r = n(111); - e.exports = function(e) { - if (!r(e) && null !== e) throw TypeError("Can't set " + String(e) + " as a prototype"); - return e - } - }, - 1223: function(e, t, n) { - var r = n(5112), - i = n(30), - o = n(3070), - a = r("unscopables"), - u = Array.prototype; - null == u[a] && o.f(u, a, { - configurable: !0, - value: i(null) - }), e.exports = function(e) { - u[a][e] = !0 - } - }, - 1530: function(e, t, n) { - "use strict"; - var r = n(8710).charAt; - e.exports = function(e, t, n) { - return t + (n ? r(e, t).length : 1) - } - }, - 5787: function(e) { - e.exports = function(e, t, n) { - if (!(e instanceof t)) throw TypeError("Incorrect " + (n ? n + " " : "") + "invocation"); - return e - } - }, - 9670: function(e, t, n) { - var r = n(111); - e.exports = function(e) { - if (!r(e)) throw TypeError(String(e) + " is not an object"); - return e - } - }, - 4019: function(e) { - e.exports = "undefined" != typeof ArrayBuffer && "undefined" != typeof DataView - }, - 260: function(e, t, n) { - "use strict"; - var r, i = n(4019), - o = n(9781), - a = n(7854), - u = n(111), - s = n(6656), - l = n(648), - c = n(8880), - f = n(1320), - p = n(3070).f, - h = n(9518), - d = n(7674), - v = n(5112), - y = n(9711), - g = a.Int8Array, - m = g && g.prototype, - b = a.Uint8ClampedArray, - x = b && b.prototype, - w = g && h(g), - E = m && h(m), - k = Object.prototype, - A = k.isPrototypeOf, - S = v("toStringTag"), - F = y("TYPED_ARRAY_TAG"), - T = i && !!d && "Opera" !== l(a.opera), - C = !1, - L = { - Int8Array: 1, - Uint8Array: 1, - Uint8ClampedArray: 1, - Int16Array: 2, - Uint16Array: 2, - Int32Array: 4, - Uint32Array: 4, - Float32Array: 4, - Float64Array: 8 - }, - R = { - BigInt64Array: 8, - BigUint64Array: 8 - }, - I = function(e) { - if (!u(e)) return !1; - var t = l(e); - return s(L, t) || s(R, t) - }; - for (r in L) a[r] || (T = !1); - if ((!T || "function" != typeof w || w === Function.prototype) && (w = function() { - throw TypeError("Incorrect invocation") - }, T)) - for (r in L) a[r] && d(a[r], w); - if ((!T || !E || E === k) && (E = w.prototype, T)) - for (r in L) a[r] && d(a[r].prototype, E); - if (T && h(x) !== E && d(x, E), o && !s(E, S)) - for (r in C = !0, p(E, S, { - get: function() { - return u(this) ? this[F] : void 0 - } - }), L) a[r] && c(a[r], F, r); - e.exports = { - NATIVE_ARRAY_BUFFER_VIEWS: T, - TYPED_ARRAY_TAG: C && F, - aTypedArray: function(e) { - if (I(e)) return e; - throw TypeError("Target is not a typed array") - }, - aTypedArrayConstructor: function(e) { - if (d) { - if (A.call(w, e)) return e - } else - for (var t in L) - if (s(L, r)) { - var n = a[t]; - if (n && (e === n || A.call(n, e))) return e - } throw TypeError("Target is not a typed array constructor") - }, - exportTypedArrayMethod: function(e, t, n) { - if (o) { - if (n) - for (var r in L) { - var i = a[r]; - i && s(i.prototype, e) && delete i.prototype[e] - } - E[e] && !n || f(E, e, n ? t : T && m[e] || t) - } - }, - exportTypedArrayStaticMethod: function(e, t, n) { - var r, i; - if (o) { - if (d) { - if (n) - for (r in L)(i = a[r]) && s(i, e) && delete i[e]; - if (w[e] && !n) return; - try { - return f(w, e, n ? t : T && g[e] || t) - } catch (e) {} - } - for (r in L) !(i = a[r]) || i[e] && !n || f(i, e, t) - } - }, - isView: function(e) { - if (!u(e)) return !1; - var t = l(e); - return "DataView" === t || s(L, t) || s(R, t) - }, - isTypedArray: I, - TypedArray: w, - TypedArrayPrototype: E - } - }, - 3331: function(e, t, n) { - "use strict"; - var r = n(7854), - i = n(9781), - o = n(4019), - a = n(8880), - u = n(2248), - s = n(7293), - l = n(5787), - c = n(9958), - f = n(7466), - p = n(7067), - h = n(1179), - d = n(9518), - v = n(7674), - y = n(8006).f, - g = n(3070).f, - m = n(1285), - b = n(8003), - x = n(9909), - w = x.get, - E = x.set, - k = "ArrayBuffer", - A = "DataView", - S = "Wrong index", - F = r.ArrayBuffer, - T = F, - C = r.DataView, - L = C && C.prototype, - R = Object.prototype, - I = r.RangeError, - U = h.pack, - O = h.unpack, - _ = function(e) { - return [255 & e] - }, - M = function(e) { - return [255 & e, e >> 8 & 255] - }, - z = function(e) { - return [255 & e, e >> 8 & 255, e >> 16 & 255, e >> 24 & 255] - }, - P = function(e) { - return e[3] << 24 | e[2] << 16 | e[1] << 8 | e[0] - }, - j = function(e) { - return U(e, 23, 4) - }, - D = function(e) { - return U(e, 52, 8) - }, - N = function(e, t) { - g(e.prototype, t, { - get: function() { - return w(this)[t] - } - }) - }, - B = function(e, t, n, r) { - var i = p(n), - o = w(e); - if (i + t > o.byteLength) throw I(S); - var a = w(o.buffer).bytes, - u = i + o.byteOffset, - s = a.slice(u, u + t); - return r ? s : s.reverse() - }, - q = function(e, t, n, r, i, o) { - var a = p(n), - u = w(e); - if (a + t > u.byteLength) throw I(S); - for (var s = w(u.buffer).bytes, l = a + u.byteOffset, c = r(+i), f = 0; f < t; f++) s[l + f] = c[o ? f : t - f - 1] - }; - if (o) { - if (!s((function() { - F(1) - })) || !s((function() { - new F(-1) - })) || s((function() { - return new F, new F(1.5), new F(NaN), F.name != k - }))) { - for (var W, H = (T = function(e) { - return l(this, T), new F(p(e)) - }).prototype = F.prototype, Y = y(F), G = 0; Y.length > G;)(W = Y[G++]) in T || a(T, W, F[W]); - H.constructor = T - } - v && d(L) !== R && v(L, R); - var Q = new C(new T(2)), - $ = L.setInt8; - Q.setInt8(0, 2147483648), Q.setInt8(1, 2147483649), !Q.getInt8(0) && Q.getInt8(1) || u(L, { - setInt8: function(e, t) { - $.call(this, e, t << 24 >> 24) - }, - setUint8: function(e, t) { - $.call(this, e, t << 24 >> 24) - } - }, { - unsafe: !0 - }) - } else T = function(e) { - l(this, T, k); - var t = p(e); - E(this, { - bytes: m.call(new Array(t), 0), - byteLength: t - }), i || (this.byteLength = t) - }, C = function(e, t, n) { - l(this, C, A), l(e, T, A); - var r = w(e).byteLength, - o = c(t); - if (o < 0 || o > r) throw I("Wrong offset"); - if (o + (n = void 0 === n ? r - o : f(n)) > r) throw I("Wrong length"); - E(this, { - buffer: e, - byteLength: n, - byteOffset: o - }), i || (this.buffer = e, this.byteLength = n, this.byteOffset = o) - }, i && (N(T, "byteLength"), N(C, "buffer"), N(C, "byteLength"), N(C, "byteOffset")), u(C.prototype, { - getInt8: function(e) { - return B(this, 1, e)[0] << 24 >> 24 - }, - getUint8: function(e) { - return B(this, 1, e)[0] - }, - getInt16: function(e) { - var t = B(this, 2, e, arguments.length > 1 ? arguments[1] : void 0); - return (t[1] << 8 | t[0]) << 16 >> 16 - }, - getUint16: function(e) { - var t = B(this, 2, e, arguments.length > 1 ? arguments[1] : void 0); - return t[1] << 8 | t[0] - }, - getInt32: function(e) { - return P(B(this, 4, e, arguments.length > 1 ? arguments[1] : void 0)) - }, - getUint32: function(e) { - return P(B(this, 4, e, arguments.length > 1 ? arguments[1] : void 0)) >>> 0 - }, - getFloat32: function(e) { - return O(B(this, 4, e, arguments.length > 1 ? arguments[1] : void 0), 23) - }, - getFloat64: function(e) { - return O(B(this, 8, e, arguments.length > 1 ? arguments[1] : void 0), 52) - }, - setInt8: function(e, t) { - q(this, 1, e, _, t) - }, - setUint8: function(e, t) { - q(this, 1, e, _, t) - }, - setInt16: function(e, t) { - q(this, 2, e, M, t, arguments.length > 2 ? arguments[2] : void 0) - }, - setUint16: function(e, t) { - q(this, 2, e, M, t, arguments.length > 2 ? arguments[2] : void 0) - }, - setInt32: function(e, t) { - q(this, 4, e, z, t, arguments.length > 2 ? arguments[2] : void 0) - }, - setUint32: function(e, t) { - q(this, 4, e, z, t, arguments.length > 2 ? arguments[2] : void 0) - }, - setFloat32: function(e, t) { - q(this, 4, e, j, t, arguments.length > 2 ? arguments[2] : void 0) - }, - setFloat64: function(e, t) { - q(this, 8, e, D, t, arguments.length > 2 ? arguments[2] : void 0) - } - }); - b(T, k), b(C, A), e.exports = { - ArrayBuffer: T, - DataView: C - } - }, - 1048: function(e, t, n) { - "use strict"; - var r = n(7908), - i = n(1400), - o = n(7466), - a = Math.min; - e.exports = [].copyWithin || function(e, t) { - var n = r(this), - u = o(n.length), - s = i(e, u), - l = i(t, u), - c = arguments.length > 2 ? arguments[2] : void 0, - f = a((void 0 === c ? u : i(c, u)) - l, u - s), - p = 1; - for (l < s && s < l + f && (p = -1, l += f - 1, s += f - 1); f-- > 0;) l in n ? n[s] = n[l] : delete n[s], s += p, l += p; - return n - } - }, - 1285: function(e, t, n) { - "use strict"; - var r = n(7908), - i = n(1400), - o = n(7466); - e.exports = function(e) { - for (var t = r(this), n = o(t.length), a = arguments.length, u = i(a > 1 ? arguments[1] : void 0, n), s = a > 2 ? arguments[2] : void 0, l = void 0 === s ? n : i(s, n); l > u;) t[u++] = e; - return t - } - }, - 8533: function(e, t, n) { - "use strict"; - var r = n(2092).forEach, - i = n(9341)("forEach"); - e.exports = i ? [].forEach : function(e) { - return r(this, e, arguments.length > 1 ? arguments[1] : void 0) - } - }, - 8457: function(e, t, n) { - "use strict"; - var r = n(9974), - i = n(7908), - o = n(3411), - a = n(7659), - u = n(7466), - s = n(6135), - l = n(1246); - e.exports = function(e) { - var t, n, c, f, p, h, d = i(e), - v = "function" == typeof this ? this : Array, - y = arguments.length, - g = y > 1 ? arguments[1] : void 0, - m = void 0 !== g, - b = l(d), - x = 0; - if (m && (g = r(g, y > 2 ? arguments[2] : void 0, 2)), null == b || v == Array && a(b)) - for (n = new v(t = u(d.length)); t > x; x++) h = m ? g(d[x], x) : d[x], s(n, x, h); - else - for (p = (f = b.call(d)).next, n = new v; !(c = p.call(f)).done; x++) h = m ? o(f, g, [c.value, x], !0) : c.value, s(n, x, h); - return n.length = x, n - } - }, - 1318: function(e, t, n) { - var r = n(5656), - i = n(7466), - o = n(1400), - a = function(e) { - return function(t, n, a) { - var u, s = r(t), - l = i(s.length), - c = o(a, l); - if (e && n != n) { - for (; l > c;) - if ((u = s[c++]) != u) return !0 - } else - for (; l > c; c++) - if ((e || c in s) && s[c] === n) return e || c || 0; - return !e && -1 - } - }; - e.exports = { - includes: a(!0), - indexOf: a(!1) - } - }, - 2092: function(e, t, n) { - var r = n(9974), - i = n(8361), - o = n(7908), - a = n(7466), - u = n(5417), - s = [].push, - l = function(e) { - var t = 1 == e, - n = 2 == e, - l = 3 == e, - c = 4 == e, - f = 6 == e, - p = 7 == e, - h = 5 == e || f; - return function(d, v, y, g) { - for (var m, b, x = o(d), w = i(x), E = r(v, y, 3), k = a(w.length), A = 0, S = g || u, F = t ? S(d, k) : n || p ? S(d, 0) : void 0; k > A; A++) - if ((h || A in w) && (b = E(m = w[A], A, x), e)) - if (t) F[A] = b; - else if (b) switch (e) { - case 3: - return !0; - case 5: - return m; - case 6: - return A; - case 2: - s.call(F, m) - } else switch (e) { - case 4: - return !1; - case 7: - s.call(F, m) - } - return f ? -1 : l || c ? c : F - } - }; - e.exports = { - forEach: l(0), - map: l(1), - filter: l(2), - some: l(3), - every: l(4), - find: l(5), - findIndex: l(6), - filterOut: l(7) - } - }, - 6583: function(e, t, n) { - "use strict"; - var r = n(5656), - i = n(9958), - o = n(7466), - a = n(9341), - u = Math.min, - s = [].lastIndexOf, - l = !!s && 1 / [1].lastIndexOf(1, -0) < 0, - c = a("lastIndexOf"), - f = l || !c; - e.exports = f ? function(e) { - if (l) return s.apply(this, arguments) || 0; - var t = r(this), - n = o(t.length), - a = n - 1; - for (arguments.length > 1 && (a = u(a, i(arguments[1]))), a < 0 && (a = n + a); a >= 0; a--) - if (a in t && t[a] === e) return a || 0; - return -1 - } : s - }, - 1194: function(e, t, n) { - var r = n(7293), - i = n(5112), - o = n(7392), - a = i("species"); - e.exports = function(e) { - return o >= 51 || !r((function() { - var t = []; - return (t.constructor = {})[a] = function() { - return { - foo: 1 - } - }, 1 !== t[e](Boolean).foo - })) - } - }, - 9341: function(e, t, n) { - "use strict"; - var r = n(7293); - e.exports = function(e, t) { - var n = [][e]; - return !!n && r((function() { - n.call(null, t || function() { - throw 1 - }, 1) - })) - } - }, - 3671: function(e, t, n) { - var r = n(3099), - i = n(7908), - o = n(8361), - a = n(7466), - u = function(e) { - return function(t, n, u, s) { - r(n); - var l = i(t), - c = o(l), - f = a(l.length), - p = e ? f - 1 : 0, - h = e ? -1 : 1; - if (u < 2) - for (;;) { - if (p in c) { - s = c[p], p += h; - break - } - if (p += h, e ? p < 0 : f <= p) throw TypeError("Reduce of empty array with no initial value") - } - for (; e ? p >= 0 : f > p; p += h) p in c && (s = n(s, c[p], p, l)); - return s - } - }; - e.exports = { - left: u(!1), - right: u(!0) - } - }, - 5417: function(e, t, n) { - var r = n(111), - i = n(3157), - o = n(5112)("species"); - e.exports = function(e, t) { - var n; - return i(e) && ("function" != typeof(n = e.constructor) || n !== Array && !i(n.prototype) ? r(n) && null === (n = n[o]) && (n = void 0) : n = void 0), new(void 0 === n ? Array : n)(0 === t ? 0 : t) - } - }, - 3411: function(e, t, n) { - var r = n(9670), - i = n(9212); - e.exports = function(e, t, n, o) { - try { - return o ? t(r(n)[0], n[1]) : t(n) - } catch (t) { - throw i(e), t - } - } - }, - 7072: function(e, t, n) { - var r = n(5112)("iterator"), - i = !1; - try { - var o = 0, - a = { - next: function() { - return { - done: !!o++ - } - }, - return: function() { - i = !0 - } - }; - a[r] = function() { - return this - }, Array.from(a, (function() { - throw 2 - })) - } catch (e) {} - e.exports = function(e, t) { - if (!t && !i) return !1; - var n = !1; - try { - var o = {}; - o[r] = function() { - return { - next: function() { - return { - done: n = !0 - } - } - } - }, e(o) - } catch (e) {} - return n - } - }, - 4326: function(e) { - var t = {}.toString; - e.exports = function(e) { - return t.call(e).slice(8, -1) - } - }, - 648: function(e, t, n) { - var r = n(1694), - i = n(4326), - o = n(5112)("toStringTag"), - a = "Arguments" == i(function() { - return arguments - }()); - e.exports = r ? i : function(e) { - var t, n, r; - return void 0 === e ? "Undefined" : null === e ? "Null" : "string" == typeof(n = function(e, t) { - try { - return e[t] - } catch (e) {} - }(t = Object(e), o)) ? n : a ? i(t) : "Object" == (r = i(t)) && "function" == typeof t.callee ? "Arguments" : r - } - }, - 9920: function(e, t, n) { - var r = n(6656), - i = n(3887), - o = n(1236), - a = n(3070); - e.exports = function(e, t) { - for (var n = i(t), u = a.f, s = o.f, l = 0; l < n.length; l++) { - var c = n[l]; - r(e, c) || u(e, c, s(t, c)) - } - } - }, - 8544: function(e, t, n) { - var r = n(7293); - e.exports = !r((function() { - function e() {} - return e.prototype.constructor = null, Object.getPrototypeOf(new e) !== e.prototype - })) - }, - 4994: function(e, t, n) { - "use strict"; - var r = n(3383).IteratorPrototype, - i = n(30), - o = n(9114), - a = n(8003), - u = n(7497), - s = function() { - return this - }; - e.exports = function(e, t, n) { - var l = t + " Iterator"; - return e.prototype = i(r, { - next: o(1, n) - }), a(e, l, !1, !0), u[l] = s, e - } - }, - 8880: function(e, t, n) { - var r = n(9781), - i = n(3070), - o = n(9114); - e.exports = r ? function(e, t, n) { - return i.f(e, t, o(1, n)) - } : function(e, t, n) { - return e[t] = n, e - } - }, - 9114: function(e) { - e.exports = function(e, t) { - return { - enumerable: !(1 & e), - configurable: !(2 & e), - writable: !(4 & e), - value: t - } - } - }, - 6135: function(e, t, n) { - "use strict"; - var r = n(7593), - i = n(3070), - o = n(9114); - e.exports = function(e, t, n) { - var a = r(t); - a in e ? i.f(e, a, o(0, n)) : e[a] = n - } - }, - 654: function(e, t, n) { - "use strict"; - var r = n(2109), - i = n(4994), - o = n(9518), - a = n(7674), - u = n(8003), - s = n(8880), - l = n(1320), - c = n(5112), - f = n(1913), - p = n(7497), - h = n(3383), - d = h.IteratorPrototype, - v = h.BUGGY_SAFARI_ITERATORS, - y = c("iterator"), - g = "keys", - m = "values", - b = "entries", - x = function() { - return this - }; - e.exports = function(e, t, n, c, h, w, E) { - i(n, t, c); - var k, A, S, F = function(e) { - if (e === h && I) return I; - if (!v && e in L) return L[e]; - switch (e) { - case g: - case m: - case b: - return function() { - return new n(this, e) - } - } - return function() { - return new n(this) - } - }, - T = t + " Iterator", - C = !1, - L = e.prototype, - R = L[y] || L["@@iterator"] || h && L[h], - I = !v && R || F(h), - U = "Array" == t && L.entries || R; - if (U && (k = o(U.call(new e)), d !== Object.prototype && k.next && (f || o(k) === d || (a ? a(k, d) : "function" != typeof k[y] && s(k, y, x)), u(k, T, !0, !0), f && (p[T] = x))), h == m && R && R.name !== m && (C = !0, I = function() { - return R.call(this) - }), f && !E || L[y] === I || s(L, y, I), p[t] = I, h) - if (A = { - values: F(m), - keys: w ? I : F(g), - entries: F(b) - }, E) - for (S in A)(v || C || !(S in L)) && l(L, S, A[S]); - else r({ - target: t, - proto: !0, - forced: v || C - }, A); - return A - } - }, - 9781: function(e, t, n) { - var r = n(7293); - e.exports = !r((function() { - return 7 != Object.defineProperty({}, 1, { - get: function() { - return 7 - } - })[1] - })) - }, - 317: function(e, t, n) { - var r = n(7854), - i = n(111), - o = r.document, - a = i(o) && i(o.createElement); - e.exports = function(e) { - return a ? o.createElement(e) : {} - } - }, - 8324: function(e) { - e.exports = { - CSSRuleList: 0, - CSSStyleDeclaration: 0, - CSSValueList: 0, - ClientRectList: 0, - DOMRectList: 0, - DOMStringList: 0, - DOMTokenList: 1, - DataTransferItemList: 0, - FileList: 0, - HTMLAllCollection: 0, - HTMLCollection: 0, - HTMLFormElement: 0, - HTMLSelectElement: 0, - MediaList: 0, - MimeTypeArray: 0, - NamedNodeMap: 0, - NodeList: 1, - PaintRequestList: 0, - Plugin: 0, - PluginArray: 0, - SVGLengthList: 0, - SVGNumberList: 0, - SVGPathSegList: 0, - SVGPointList: 0, - SVGStringList: 0, - SVGTransformList: 0, - SourceBufferList: 0, - StyleSheetList: 0, - TextTrackCueList: 0, - TextTrackList: 0, - TouchList: 0 - } - }, - 8113: function(e, t, n) { - var r = n(5005); - e.exports = r("navigator", "userAgent") || "" - }, - 7392: function(e, t, n) { - var r, i, o = n(7854), - a = n(8113), - u = o.process, - s = u && u.versions, - l = s && s.v8; - l ? i = (r = l.split("."))[0] + r[1] : a && (!(r = a.match(/Edge\/(\d+)/)) || r[1] >= 74) && (r = a.match(/Chrome\/(\d+)/)) && (i = r[1]), e.exports = i && +i - }, - 748: function(e) { - e.exports = ["constructor", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "toLocaleString", "toString", "valueOf"] - }, - 2109: function(e, t, n) { - var r = n(7854), - i = n(1236).f, - o = n(8880), - a = n(1320), - u = n(3505), - s = n(9920), - l = n(4705); - e.exports = function(e, t) { - var n, c, f, p, h, d = e.target, - v = e.global, - y = e.stat; - if (n = v ? r : y ? r[d] || u(d, {}) : (r[d] || {}).prototype) - for (c in t) { - if (p = t[c], f = e.noTargetGet ? (h = i(n, c)) && h.value : n[c], !l(v ? c : d + (y ? "." : "#") + c, e.forced) && void 0 !== f) { - if (typeof p == typeof f) continue; - s(p, f) - }(e.sham || f && f.sham) && o(p, "sham", !0), a(n, c, p, e) - } - } - }, - 7293: function(e) { - e.exports = function(e) { - try { - return !!e() - } catch (e) { - return !0 - } - } - }, - 7007: function(e, t, n) { - "use strict"; - n(4916); - var r = n(1320), - i = n(7293), - o = n(5112), - a = n(2261), - u = n(8880), - s = o("species"), - l = !i((function() { - var e = /./; - return e.exec = function() { - var e = []; - return e.groups = { - a: "7" - }, e - }, "7" !== "".replace(e, "$") - })), - c = "$0" === "a".replace(/./, "$0"), - f = o("replace"), - p = !!/./ [f] && "" === /./ [f]("a", "$0"), - h = !i((function() { - var e = /(?:)/, - t = e.exec; - e.exec = function() { - return t.apply(this, arguments) - }; - var n = "ab".split(e); - return 2 !== n.length || "a" !== n[0] || "b" !== n[1] - })); - e.exports = function(e, t, n, f) { - var d = o(e), - v = !i((function() { - var t = {}; - return t[d] = function() { - return 7 - }, 7 != "" [e](t) - })), - y = v && !i((function() { - var t = !1, - n = /a/; - return "split" === e && ((n = {}).constructor = {}, n.constructor[s] = function() { - return n - }, n.flags = "", n[d] = /./ [d]), n.exec = function() { - return t = !0, null - }, n[d](""), !t - })); - if (!v || !y || "replace" === e && (!l || !c || p) || "split" === e && !h) { - var g = /./ [d], - m = n(d, "" [e], (function(e, t, n, r, i) { - return t.exec === a ? v && !i ? { - done: !0, - value: g.call(t, n, r) - } : { - done: !0, - value: e.call(n, t, r) - } : { - done: !1 - } - }), { - REPLACE_KEEPS_$0: c, - REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE: p - }), - b = m[0], - x = m[1]; - r(String.prototype, e, b), r(RegExp.prototype, d, 2 == t ? function(e, t) { - return x.call(e, this, t) - } : function(e) { - return x.call(e, this) - }) - } - f && u(RegExp.prototype[d], "sham", !0) - } - }, - 9974: function(e, t, n) { - var r = n(3099); - e.exports = function(e, t, n) { - if (r(e), void 0 === t) return e; - switch (n) { - case 0: - return function() { - return e.call(t) - }; - case 1: - return function(n) { - return e.call(t, n) - }; - case 2: - return function(n, r) { - return e.call(t, n, r) - }; - case 3: - return function(n, r, i) { - return e.call(t, n, r, i) - } - } - return function() { - return e.apply(t, arguments) - } - } - }, - 5005: function(e, t, n) { - var r = n(857), - i = n(7854), - o = function(e) { - return "function" == typeof e ? e : void 0 - }; - e.exports = function(e, t) { - return arguments.length < 2 ? o(r[e]) || o(i[e]) : r[e] && r[e][t] || i[e] && i[e][t] - } - }, - 1246: function(e, t, n) { - var r = n(648), - i = n(7497), - o = n(5112)("iterator"); - e.exports = function(e) { - if (null != e) return e[o] || e["@@iterator"] || i[r(e)] - } - }, - 8554: function(e, t, n) { - var r = n(9670), - i = n(1246); - e.exports = function(e) { - var t = i(e); - if ("function" != typeof t) throw TypeError(String(e) + " is not iterable"); - return r(t.call(e)) - } - }, - 647: function(e, t, n) { - var r = n(7908), - i = Math.floor, - o = "".replace, - a = /\$([$&'`]|\d\d?|<[^>]*>)/g, - u = /\$([$&'`]|\d\d?)/g; - e.exports = function(e, t, n, s, l, c) { - var f = n + e.length, - p = s.length, - h = u; - return void 0 !== l && (l = r(l), h = a), o.call(c, h, (function(r, o) { - var a; - switch (o.charAt(0)) { - case "$": - return "$"; - case "&": - return e; - case "`": - return t.slice(0, n); - case "'": - return t.slice(f); - case "<": - a = l[o.slice(1, -1)]; - break; - default: - var u = +o; - if (0 === u) return r; - if (u > p) { - var c = i(u / 10); - return 0 === c ? r : c <= p ? void 0 === s[c - 1] ? o.charAt(1) : s[c - 1] + o.charAt(1) : r - } - a = s[u - 1] - } - return void 0 === a ? "" : a - })) - } - }, - 7854: function(e, t, n) { - var r = function(e) { - return e && e.Math == Math && e - }; - e.exports = r("object" == typeof globalThis && globalThis) || r("object" == typeof window && window) || r("object" == typeof self && self) || r("object" == typeof n.g && n.g) || function() { - return this - }() || Function("return this")() - }, - 6656: function(e) { - var t = {}.hasOwnProperty; - e.exports = function(e, n) { - return t.call(e, n) - } - }, - 3501: function(e) { - e.exports = {} - }, - 490: function(e, t, n) { - var r = n(5005); - e.exports = r("document", "documentElement") - }, - 4664: function(e, t, n) { - var r = n(9781), - i = n(7293), - o = n(317); - e.exports = !r && !i((function() { - return 7 != Object.defineProperty(o("div"), "a", { - get: function() { - return 7 - } - }).a - })) - }, - 1179: function(e) { - var t = Math.abs, - n = Math.pow, - r = Math.floor, - i = Math.log, - o = Math.LN2; - e.exports = { - pack: function(e, a, u) { - var s, l, c, f = new Array(u), - p = 8 * u - a - 1, - h = (1 << p) - 1, - d = h >> 1, - v = 23 === a ? n(2, -24) - n(2, -77) : 0, - y = e < 0 || 0 === e && 1 / e < 0 ? 1 : 0, - g = 0; - for ((e = t(e)) != e || e === 1 / 0 ? (l = e != e ? 1 : 0, s = h) : (s = r(i(e) / o), e * (c = n(2, -s)) < 1 && (s--, c *= 2), (e += s + d >= 1 ? v / c : v * n(2, 1 - d)) * c >= 2 && (s++, c /= 2), s + d >= h ? (l = 0, s = h) : s + d >= 1 ? (l = (e * c - 1) * n(2, a), s += d) : (l = e * n(2, d - 1) * n(2, a), s = 0)); a >= 8; f[g++] = 255 & l, l /= 256, a -= 8); - for (s = s << a | l, p += a; p > 0; f[g++] = 255 & s, s /= 256, p -= 8); - return f[--g] |= 128 * y, f - }, - unpack: function(e, t) { - var r, i = e.length, - o = 8 * i - t - 1, - a = (1 << o) - 1, - u = a >> 1, - s = o - 7, - l = i - 1, - c = e[l--], - f = 127 & c; - for (c >>= 7; s > 0; f = 256 * f + e[l], l--, s -= 8); - for (r = f & (1 << -s) - 1, f >>= -s, s += t; s > 0; r = 256 * r + e[l], l--, s -= 8); - if (0 === f) f = 1 - u; - else { - if (f === a) return r ? NaN : c ? -1 / 0 : 1 / 0; - r += n(2, t), f -= u - } - return (c ? -1 : 1) * r * n(2, f - t) - } - } - }, - 8361: function(e, t, n) { - var r = n(7293), - i = n(4326), - o = "".split; - e.exports = r((function() { - return !Object("z").propertyIsEnumerable(0) - })) ? function(e) { - return "String" == i(e) ? o.call(e, "") : Object(e) - } : Object - }, - 9587: function(e, t, n) { - var r = n(111), - i = n(7674); - e.exports = function(e, t, n) { - var o, a; - return i && "function" == typeof(o = t.constructor) && o !== n && r(a = o.prototype) && a !== n.prototype && i(e, a), e - } - }, - 2788: function(e, t, n) { - var r = n(5465), - i = Function.toString; - "function" != typeof r.inspectSource && (r.inspectSource = function(e) { - return i.call(e) - }), e.exports = r.inspectSource - }, - 9909: function(e, t, n) { - var r, i, o, a = n(8536), - u = n(7854), - s = n(111), - l = n(8880), - c = n(6656), - f = n(5465), - p = n(6200), - h = n(3501), - d = u.WeakMap; - if (a) { - var v = f.state || (f.state = new d), - y = v.get, - g = v.has, - m = v.set; - r = function(e, t) { - return t.facade = e, m.call(v, e, t), t - }, i = function(e) { - return y.call(v, e) || {} - }, o = function(e) { - return g.call(v, e) - } - } else { - var b = p("state"); - h[b] = !0, r = function(e, t) { - return t.facade = e, l(e, b, t), t - }, i = function(e) { - return c(e, b) ? e[b] : {} - }, o = function(e) { - return c(e, b) - } - } - e.exports = { - set: r, - get: i, - has: o, - enforce: function(e) { - return o(e) ? i(e) : r(e, {}) - }, - getterFor: function(e) { - return function(t) { - var n; - if (!s(t) || (n = i(t)).type !== e) throw TypeError("Incompatible receiver, " + e + " required"); - return n - } - } - } - }, - 7659: function(e, t, n) { - var r = n(5112), - i = n(7497), - o = r("iterator"), - a = Array.prototype; - e.exports = function(e) { - return void 0 !== e && (i.Array === e || a[o] === e) - } - }, - 3157: function(e, t, n) { - var r = n(4326); - e.exports = Array.isArray || function(e) { - return "Array" == r(e) - } - }, - 4705: function(e, t, n) { - var r = n(7293), - i = /#|\.prototype\./, - o = function(e, t) { - var n = u[a(e)]; - return n == l || n != s && ("function" == typeof t ? r(t) : !!t) - }, - a = o.normalize = function(e) { - return String(e).replace(i, ".").toLowerCase() - }, - u = o.data = {}, - s = o.NATIVE = "N", - l = o.POLYFILL = "P"; - e.exports = o - }, - 111: function(e) { - e.exports = function(e) { - return "object" == typeof e ? null !== e : "function" == typeof e - } - }, - 1913: function(e) { - e.exports = !1 - }, - 7850: function(e, t, n) { - var r = n(111), - i = n(4326), - o = n(5112)("match"); - e.exports = function(e) { - var t; - return r(e) && (void 0 !== (t = e[o]) ? !!t : "RegExp" == i(e)) - } - }, - 9212: function(e, t, n) { - var r = n(9670); - e.exports = function(e) { - var t = e.return; - if (void 0 !== t) return r(t.call(e)).value - } - }, - 3383: function(e, t, n) { - "use strict"; - var r, i, o, a = n(7293), - u = n(9518), - s = n(8880), - l = n(6656), - c = n(5112), - f = n(1913), - p = c("iterator"), - h = !1; - [].keys && ("next" in (o = [].keys()) ? (i = u(u(o))) !== Object.prototype && (r = i) : h = !0); - var d = null == r || a((function() { - var e = {}; - return r[p].call(e) !== e - })); - d && (r = {}), f && !d || l(r, p) || s(r, p, (function() { - return this - })), e.exports = { - IteratorPrototype: r, - BUGGY_SAFARI_ITERATORS: h - } - }, - 7497: function(e) { - e.exports = {} - }, - 133: function(e, t, n) { - var r = n(7293); - e.exports = !!Object.getOwnPropertySymbols && !r((function() { - return !String(Symbol()) - })) - }, - 590: function(e, t, n) { - var r = n(7293), - i = n(5112), - o = n(1913), - a = i("iterator"); - e.exports = !r((function() { - var e = new URL("b?a=1&b=2&c=3", "http://a"), - t = e.searchParams, - n = ""; - return e.pathname = "c%20d", t.forEach((function(e, r) { - t.delete("b"), n += r + e - })), o && !e.toJSON || !t.sort || "http://a/c%20d?a=1&c=3" !== e.href || "3" !== t.get("c") || "a=1" !== String(new URLSearchParams("?a=1")) || !t[a] || "a" !== new URL("https://a@b").username || "b" !== new URLSearchParams(new URLSearchParams("a=b")).get("a") || "xn--e1aybc" !== new URL("http://тест").host || "#%D0%B1" !== new URL("http://a#б").hash || "a1c3" !== n || "x" !== new URL("http://x", void 0).host - })) - }, - 8536: function(e, t, n) { - var r = n(7854), - i = n(2788), - o = r.WeakMap; - e.exports = "function" == typeof o && /native code/.test(i(o)) - }, - 1574: function(e, t, n) { - "use strict"; - var r = n(9781), - i = n(7293), - o = n(1956), - a = n(5181), - u = n(5296), - s = n(7908), - l = n(8361), - c = Object.assign, - f = Object.defineProperty; - e.exports = !c || i((function() { - if (r && 1 !== c({ - b: 1 - }, c(f({}, "a", { - enumerable: !0, - get: function() { - f(this, "b", { - value: 3, - enumerable: !1 - }) - } - }), { - b: 2 - })).b) return !0; - var e = {}, - t = {}, - n = Symbol(), - i = "abcdefghijklmnopqrst"; - return e[n] = 7, i.split("").forEach((function(e) { - t[e] = e - })), 7 != c({}, e)[n] || o(c({}, t)).join("") != i - })) ? function(e, t) { - for (var n = s(e), i = arguments.length, c = 1, f = a.f, p = u.f; i > c;) - for (var h, d = l(arguments[c++]), v = f ? o(d).concat(f(d)) : o(d), y = v.length, g = 0; y > g;) h = v[g++], r && !p.call(d, h) || (n[h] = d[h]); - return n - } : c - }, - 30: function(e, t, n) { - var r, i = n(9670), - o = n(6048), - a = n(748), - u = n(3501), - s = n(490), - l = n(317), - c = n(6200)("IE_PROTO"), - f = function() {}, - p = function(e) { - return " + + diff --git a/src/web/templates/index.html b/src/web/templates/index.html index 283e8b3b..7820afdc 100644 --- a/src/web/templates/index.html +++ b/src/web/templates/index.html @@ -372,7 +372,25 @@ forceChunking: true, url: '/files/upload', maxFilesize: {{ max_file_size }}, // MB - chunkSize: 1000000 // bytes + chunkSize: 1000000, // bytes + dictDefaultMessage: "{{ _("Drop files here to upload") }}", + dictFallbackMessage: "{{ _("Your browser does not support drag'n'drop file uploads.") }}", + dictFallbackText: "{{ _("Please use the fallback form below to upload your files like in the olden days.") }}", + dictFileTooBig: "{{ _("File is too big: {{filesize}}MB. Max filesize: {{maxFilesize}}MB.") }}", + dictInvalidFileType: "{{ _("You can't upload files of this type.") }}", + dictResponseError: "{{ _("Server responded with code: {{statusCode}}") }}", + dictCancelUpload:" {{ _("Cancel upload") }}", + dictUploadCanceled: "{{ _("Upload canceled.") }}", + dictCancelUploadConfirmation: "{{ _("Are you sure you want to cancel this upload?") }}", + dictRemoveFile: "{{ _("Remove file") }}", + dictMaxFilesExceeded: "{{ _("You can not upload any more files.") }}", + dictFileSizeUnits: { + tb: "{{ _("TB") }}", + gb: "{{ _("GB") }}", + mb: "{{ _("MB") }}", + kb: "{{ _("KB") }}", + b: "{{ _("b") }}" + } } From 93854836247f236e9be7f69a7db3c60e0596e18a Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Tue, 28 Dec 2021 12:08:48 -0800 Subject: [PATCH 02/18] Flesh out webapp localization readme (#578) --- src/web/README.md | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/src/web/README.md b/src/web/README.md index e5ea75bc..0a62552f 100644 --- a/src/web/README.md +++ b/src/web/README.md @@ -53,25 +53,59 @@ We use the Flask-Babel library and Flask/Jinja2 extension for i18n. It uses the 'pybabel' command line tool for extracting and compiling localizations. Activate the Python venv in src/web/ to use it: - +``` $ cd src/web/ $ source venv/bin/activate $ pybabel --help +``` -To create a new localization, it needs to be added to accept_languages in -the get_locale() method, and also to localizer.cpp in the RaSCSI C++ code. +To create a new localization, it needs to be added to the LANGAUGES constant in +web/settings.py. To localize messages coming from the RaSCSI backend, update also code in +raspberrypi/localizer.cpp in the RaSCSI C++ code. Once this is done, follow the steps in the [Flask-Babel documentation](https://flask-babel.tkte.ch/#translating-applications) to generate the messages.po for the new language. -Updating an existing messages.po is also covered above. +Updating the strings in an existing messages.po is also covered above. When you are ready to contribute new or updated localizations, use the same Gitflow Workflow as used for any code contributions to submit PRs against the develop branch. +### Working with PO files + +See the [GNU gettext documentation](https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html) for an introduction to the PO file format. + +We make heavy use of __python-format__ for formatting, for instance: +``` +#: file_cmds.py:353 +#, python-format +msgid "%(file_name)s downloaded to %(save_dir)s" +msgstr "Laddade ner %(file_name)s till %(save_dir)s" +``` + +There are also a few instances of formatting in JavaScript: +``` +#: templates/index.html:381 +msgid "Server responded with code: {{statusCode}}" +msgstr "Servern svarade med kod: {{statusCode}}" +``` + +And with html tags: +``` +#: templates/index.html:304 +#, python-format +msgid "" +"Emulates a SCSI DaynaPORT Ethernet Adapter. Host " +"drivers and configuration required." +msgstr "" +"Emulerar en SCSI DaynaPORT ethernet-adapter. Kräver " +"drivrutiner och inställningar." +``` + ### (Optional) See translation stats for a localization Install the gettext package and use msgfmt to see the translation progress. - +``` $ sudo apt install gettext $ cd src/web/ $ msgfmt --statistics translations/sv/LC_MESSAGES/messages.po 215 translated messages, 1 untranslated message. +``` From fecdc4462955cb6ed17e3b5b4653299b16a36a43 Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Tue, 28 Dec 2021 12:09:47 -0800 Subject: [PATCH 03/18] Resolve pylint warnings for rascsi-web (#579) * Add venv hook to .pylintrc * Add to readme about pylint-venv * Resolve pylint warnings * Flesh out readme --- src/.pylintrc | 9 ++++++- src/web/README.md | 3 +++ src/web/file_cmds.py | 32 +++++++++++++----------- src/web/ractl_cmds.py | 4 +-- src/web/socket_cmds.py | 2 +- src/web/web.py | 56 ++++++++++++++++++++++++++++++------------ 6 files changed, 72 insertions(+), 34 deletions(-) diff --git a/src/.pylintrc b/src/.pylintrc index 11110d09..37ed20d1 100644 --- a/src/.pylintrc +++ b/src/.pylintrc @@ -15,7 +15,14 @@ ignore-patterns= # Python code to execute, usually for sys.path manipulation such as # pygtk.require(). -#init-hook= +init-hook= +# venv hook for pylint +# Requires pylint-venv package: +# $ pip install pylint-venv + try: import pylint_venv + except ImportError: pass + else: pylint_venv.inithook() + # Use multiple processes to speed up Pylint. jobs=1 diff --git a/src/web/README.md b/src/web/README.md index 0a62552f..7616b450 100644 --- a/src/web/README.md +++ b/src/web/README.md @@ -24,9 +24,12 @@ A separate mocking solution will be needed for this interface. It is recommended to run pylint against new code to protect against bugs and keep the code readable and maintainable. The local pylint configuration lives in .pylintrc +In order for pylint to recognize venv libraries, the pylint-venv package is required. ``` sudo apt install pylint3 +sudo pip install pylint-venv +source venv/bin/activate pylint3 python_source_file.py ``` diff --git a/src/web/file_cmds.py b/src/web/file_cmds.py index a362100b..0605b30a 100644 --- a/src/web/file_cmds.py +++ b/src/web/file_cmds.py @@ -300,18 +300,16 @@ def download_file_to_iso(url, *iso_args): delete_file(tmp_full_path) try: - iso_proc = ( - run( - [ - "genisoimage", - *iso_args, - "-o", - iso_filename, - tmp_dir, - ], - capture_output=True, - check=True, - ) + run( + [ + "genisoimage", + *iso_args, + "-o", + iso_filename, + tmp_dir, + ], + capture_output=True, + check=True, ) except CalledProcessError as error: logging.warning("Executed shell command: %s", " ".join(error.cmd)) @@ -320,7 +318,10 @@ def download_file_to_iso(url, *iso_args): return { "status": True, - "msg": _(u"Created CD-ROM ISO image with arguments \"%(value)s\"", value=" ".join(iso_args)), + "msg": _( + u"Created CD-ROM ISO image with arguments \"%(value)s\"", + value=" ".join(iso_args), + ), "file_name": iso_filename, } @@ -393,7 +394,10 @@ def write_config(file_name): json_file, indent=4 ) - return {"status": True, "msg": _(u"Saved configuration file to %(file_name)s", file_name=file_name)} + return { + "status": True, + "msg": _(u"Saved configuration file to %(file_name)s", file_name=file_name), + } except (IOError, ValueError, EOFError, TypeError) as error: logging.error(str(error)) delete_file(file_name) diff --git a/src/web/ractl_cmds.py b/src/web/ractl_cmds.py index 79bb5f32..84371ac4 100644 --- a/src/web/ractl_cmds.py +++ b/src/web/ractl_cmds.py @@ -2,11 +2,11 @@ Module for commands sent to the RaSCSI backend service. """ -from settings import REMOVABLE_DEVICE_TYPES -from socket_cmds import send_pb_command from flask import current_app, session from flask_babel import _ import rascsi_interface_pb2 as proto +from settings import REMOVABLE_DEVICE_TYPES +from socket_cmds import send_pb_command def get_server_info(): diff --git a/src/web/socket_cmds.py b/src/web/socket_cmds.py index 710f1394..1fb7376a 100644 --- a/src/web/socket_cmds.py +++ b/src/web/socket_cmds.py @@ -3,9 +3,9 @@ Module for sending and receiving data over a socket connection with the RaSCSI b """ import logging +from time import sleep from flask import abort from flask_babel import _ -from time import sleep def send_pb_command(payload): """ diff --git a/src/web/web.py b/src/web/web.py index 57dd356f..33a1c5d4 100644 --- a/src/web/web.py +++ b/src/web/web.py @@ -4,7 +4,6 @@ Module for the Flask app rendering and endpoints import logging import argparse -from sys import argv from pathlib import Path from functools import wraps @@ -115,7 +114,13 @@ def index(): Sets up data structures for and renders the index page """ if not is_token_auth()["status"] and not APP.config["TOKEN"]: - abort(403, _(u"RaSCSI is password protected. Start the Web Interface with the --password parameter.")) + abort( + 403, + _( + u"RaSCSI is password protected. " + u"Start the Web Interface with the --password parameter." + ), + ) locales = get_supported_locales() server_info = get_server_info() @@ -211,7 +216,13 @@ def drive_list(): return redirect(url_for("index")) conf = process["conf"] else: - flash(_("Could not read drive properties from %(properties_file)s", properties_file=drive_properties), "error") + flash( + _( + "Could not read drive properties from %(properties_file)s", + properties_file=drive_properties, + ), + "error", + ) return redirect(url_for("index")) hd_conf = [] @@ -275,7 +286,13 @@ def login(): if authenticate(str(username), str(password)): session["username"] = request.form["username"] return redirect(url_for("index")) - flash(_(u"You must log in with credentials for a user in the '%(group)s' group", group=AUTH_GROUP), "error") + flash( + _( + u"You must log in with credentials for a user in the '%(group)s' group", + group=AUTH_GROUP, + ), + "error", + ) return redirect(url_for("index")) @@ -413,7 +430,7 @@ def config_load(): flash(process['msg'], "error") return redirect(url_for("index")) - elif "delete" in request.form: + if "delete" in request.form: process = delete_file(f"{CFG_DIR}/{file_name}") if process["status"]: flash(process["msg"]) @@ -609,8 +626,8 @@ def detach(): id_number=scsi_id, unit_number=unit)) return redirect(url_for("index")) - flash(_(u"Failed to detach %(file_name)s from SCSI ID %(id_number)s LUN %(unit_number)s", - file_name=file_name, id_number=scsi_id, unit_number=unit), "error") + flash(_(u"Failed to detach SCSI ID %(id_number)s LUN %(unit_number)s", + id_number=scsi_id, unit_number=unit), "error") flash(process["msg"], "error") return redirect(url_for("index")) @@ -630,8 +647,8 @@ def eject(): id_number=scsi_id, unit_number=unit)) return redirect(url_for("index")) - flash(_(u"Failed to eject %(file_name)s from SCSI ID %(id_number)s LUN %(unit_number)s", - file_name=file_name, id_number=scsi_id, unit_number=unit), "error") + flash(_(u"Failed to eject SCSI ID %(id_number)s LUN %(unit_number)s", + id_number=scsi_id, unit_number=unit), "error") flash(process["msg"], "error") return redirect(url_for("index")) @@ -837,10 +854,14 @@ def upload_file(): if current_chunk + 1 == total_chunks: # Validate the resulting file size after writing the last chunk if path.getsize(save_path) != int(request.form["dztotalfilesize"]): - log.error("Finished transferring %s, " - "but it has a size mismatch with the original file." - "Got %s but we expected %s.", - file_object.filename, path.getsize(save_path), request.form['dztotalfilesize']) + log.error( + "Finished transferring %s, " + "but it has a size mismatch with the original file. " + "Got %s but we expected %s.", + file_object.filename, + path.getsize(save_path), + request.form['dztotalfilesize'], + ) return make_response(_(u"Transferred file corrupted!"), 500) log.info("File %s has been uploaded successfully", file_object.filename) @@ -973,6 +994,9 @@ def unzip(): @APP.route("/language", methods=["POST"]) def change_language(): + """ + Changes the session language locale and refreshes the Flask app context + """ locale = request.form.get("locale") session["language"] = locale refresh() @@ -1013,9 +1037,9 @@ if __name__ == "__main__": action="store", help="Token password string for authenticating with RaSCSI", ) - args = parser.parse_args() - APP.config["TOKEN"] = args.password + arguments = parser.parse_args() + APP.config["TOKEN"] = arguments.password import bjoern print("Serving rascsi-web...") - bjoern.run(APP, "0.0.0.0", args.port) + bjoern.run(APP, "0.0.0.0", arguments.port) From 5f39afc5d1e652ed80091ba579fae7441ab8f07f Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Tue, 28 Dec 2021 12:10:42 -0800 Subject: [PATCH 04/18] Optimized oled script startup and shutdown (#573) * Optimize the oled script for quicker startup. Blank the screen instead of showing a splash. * Remove shutdown splash bitmap images * Cleanup * Resolve pylint warnings --- src/oled_monitor/rascsi_oled_monitor.py | 56 +++++++----------------- src/oled_monitor/splash_stop_32.bmp | Bin 574 -> 0 bytes src/oled_monitor/splash_stop_64.bmp | Bin 1086 -> 0 bytes 3 files changed, 17 insertions(+), 39 deletions(-) delete mode 100644 src/oled_monitor/splash_stop_32.bmp delete mode 100644 src/oled_monitor/splash_stop_64.bmp diff --git a/src/oled_monitor/rascsi_oled_monitor.py b/src/oled_monitor/rascsi_oled_monitor.py index 0d1dfaf0..32b290e3 100755 --- a/src/oled_monitor/rascsi_oled_monitor.py +++ b/src/oled_monitor/rascsi_oled_monitor.py @@ -31,8 +31,8 @@ THE SOFTWARE. """ import argparse +import sys from time import sleep -from sys import argv from collections import deque from board import I2C from adafruit_ssd1306 import SSD1306_I2C @@ -95,20 +95,22 @@ I2C = I2C() # 128x32 display with hardware I2C: OLED = SSD1306_I2C(WIDTH, HEIGHT, I2C, addr=0x3C, reset=OLED_RESET) +OLED.rotation = ROTATION print("Running with the following display:") print(OLED) print() print("Will update the OLED display every " + str(DELAY_TIME_MS) + "ms (approximately)") -# Clear display. -OLED.rotation = ROTATION -OLED.fill(0) +# Show a startup splash bitmap image before starting the main loop +# Convert the image to mode '1' for 1-bit color (monochrome) +# Make sure the splash bitmap image is in the same dir as this script +IMAGE = Image.open(f"splash_start_{HEIGHT}.bmp").convert("1") +OLED.image(IMAGE) OLED.show() -# Create blank image for drawing. -# Make sure to create image with mode '1' for 1-bit color. -IMAGE = Image.new("1", (OLED.width, OLED.height)) +# Keep the pretty splash on screen for a number of seconds +sleep(4) # Get drawing object to draw on image. DRAW = ImageDraw.Draw(IMAGE) @@ -138,11 +140,6 @@ LINE_SPACING = 8 # Some other nice fonts to try: http://www.dafont.com/bitmap.php FONT = ImageFont.truetype('type_writer.ttf', FONT_SIZE) -# Load a bitmap image for start and stop splash screens and convert to monocrome -# Make sure the splash bitmap image is in the same dir as this script -SPLASH_START = Image.open(f"splash_start_{HEIGHT}.bmp").convert("1") -SPLASH_STOP = Image.open(f"splash_stop_{HEIGHT}.bmp").convert("1") - IP_ADDR, HOSTNAME = get_ip_and_host() @@ -190,28 +187,7 @@ def formatted_output(): return output -def start_splash(): - """ - Displays a splash screen for the startup sequence - """ - OLED.image(SPLASH_START) - OLED.show() - sleep(4) - -def stop_splash(): - """ - Displays a splash screen for the shutdown sequence - """ - OLED.image(SPLASH_STOP) - OLED.show() - -# Show a startup splash bitmap image before starting the main loop -start_splash() - with GracefulInterruptHandler() as handler: - """ - The main loop of displaying attached device info, and other info - """ while True: # The reference snapshot of attached devices that will be compared against each cycle @@ -225,10 +201,10 @@ with GracefulInterruptHandler() as handler: while snapshot == ref_snapshot: # Draw a black filled box to clear the image. DRAW.rectangle((0, 0, WIDTH, HEIGHT), outline=0, fill=0) - y_pos = TOP + Y_POS = TOP for output_line in active_output: - DRAW.text((X_POS, y_pos), output_line, font=FONT, fill=255) - y_pos += LINE_SPACING + DRAW.text((X_POS, Y_POS), output_line, font=FONT, fill=255) + Y_POS += LINE_SPACING # Shift the index of the array by one to get a scrolling effect if len(active_output) > LINES: @@ -242,6 +218,8 @@ with GracefulInterruptHandler() as handler: snapshot = formatted_output() if handler.interrupted: - # Catch interrupt signals and show a shutdown splash bitmap image - stop_splash() - exit("Shutting down the OLED display...") + # Catch interrupt signals and blank out the screen + DRAW.rectangle((0, 0, WIDTH, HEIGHT), outline=0, fill=0) + OLED.image(IMAGE) + OLED.show() + sys.exit("Shutting down the OLED display...") diff --git a/src/oled_monitor/splash_stop_32.bmp b/src/oled_monitor/splash_stop_32.bmp deleted file mode 100644 index eac18956dd94f72e427ace7fa3d86246dc16991e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 574 zcmb7>y$ZrG6opeOiC{o*>gec0ln%~1_FcM}%_UFLmoOmMNara3mMXe5l28#) z`iuR%k1O-q=a~i0b$65K%+QPy5NZkB3%~%-mf4$O=)NrBT!;0pF61Zkjqc0-V$h&h lw4WatFQ^%~hJ?yBF?BOc>_QkqzthaW%#$~L%>AFa`V+0!(Gvgw diff --git a/src/oled_monitor/splash_stop_64.bmp b/src/oled_monitor/splash_stop_64.bmp deleted file mode 100644 index 511f821476e801c6079c57924510735283ea5119..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1086 zcmZ?rwPRrb13Mt80mKbJ>;S}!KnMdu+zbpL%mfqw;r~DYqE?}SP&9xhy$Ve_bfnAg z2L&V&s7GRBV>SQ-9SN{EAaQ_9Tzr@~jOI81(Ffx*!1>HQ#}D(IIey?r#ebeNXV@E9 z&YU?Wapuf{GiT29fcOju{+R Date: Tue, 28 Dec 2021 13:14:30 -0800 Subject: [PATCH 05/18] Final updates for Swedish l10n (#576) * Revert previous solution, and do dropzone i18n the right way. * Translate Swedish * Update Swedish localization * Update Swedish localization --- .../translations/sv/LC_MESSAGES/messages.po | 324 +++++++++++------- 1 file changed, 206 insertions(+), 118 deletions(-) diff --git a/src/web/translations/sv/LC_MESSAGES/messages.po b/src/web/translations/sv/LC_MESSAGES/messages.po index e774ad13..f9effe75 100644 --- a/src/web/translations/sv/LC_MESSAGES/messages.po +++ b/src/web/translations/sv/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: RaSCSI 68kmla Edition\n" "Report-Msgid-Bugs-To: https://github.com/akuker/RASCSI/issues\n" -"POT-Creation-Date: 2021-12-26 21:59-0800\n" +"POT-Creation-Date: 2021-12-27 18:04-0800\n" "PO-Revision-Date: 2021-12-24 16:16-0800\n" "Last-Translator: Daniel Markstedt \n" "Language: sv\n" @@ -132,7 +132,7 @@ msgstr "" "RaSCSIs webbgränssnitt fick en ogiltig respons från RaSCSI. Gå tillbaks " "och försök igen. Om samma fel upprepas så rapportera en bugg." -#: web.py:97 +#: web.py:118 msgid "" "RaSCSI is password protected. Start the Web Interface with the --password" " parameter." @@ -140,60 +140,60 @@ msgstr "" "RaSCSI är lösenordsskyddat. Start webbgränssnittet med parametern " "--password ." -#: web.py:191 +#: web.py:214 #, python-format msgid "Could not read drive properties from %(properties_file)s" msgstr "Kunde ej läsa egenskaper från %(properties_file)s" -#: web.py:255 +#: web.py:278 #, python-format msgid "You must log in with credentials for a user in the '%(group)s' group" msgstr "Du måste logga in som en användare som tillhör %(group)s-gruppen" -#: web.py:308 web.py:843 +#: web.py:331 web.py:866 #, python-format msgid "Image file created: %(file_name)s" -msgstr "Skapade skivbildfil: %(file_name)s" +msgstr "Skapade skivbildsfil: %(file_name)s" -#: web.py:433 +#: web.py:456 msgid "An error occurred when fetching logs." msgstr "Ett fel inträffade när vi skaffade loggar." -#: web.py:448 +#: web.py:471 #, python-format msgid "Log level set to %(value)s" msgstr "Ställde in loggnivån till %(value)s" -#: web.py:467 +#: web.py:490 #, python-format msgid "Please follow the instructions at %(url)s" msgstr "Följ instruktionerna på %(url)s" -#: web.py:471 +#: web.py:494 msgid "Configure IPv4 forwarding before using a wireless network device." msgstr "" "Ställ in IPv4-vidarebefodring innan du använder en trådlös " "nätverksadapter." -#: web.py:475 +#: web.py:498 msgid "Configure NAT before using a wireless network device." msgstr "Ställ in NAT innan du använder en trådlös nätverksadapter." -#: web.py:480 web.py:484 +#: web.py:503 web.py:507 msgid "Configure the network bridge before using a wired network device." msgstr "Ställ in nätverksbryggan innan du använder ett nätverksgränssnitt." -#: web.py:497 +#: web.py:520 #, python-format msgid "Attached DaynaPORT to SCSI ID %(id_number)s" msgstr "Anslöt DaynaPORT till SCSI-id %(id_number)s" -#: web.py:545 +#: web.py:568 #, python-format msgid "Attached %(file_name)s to SCSI ID %(id_number)s LUN %(unit_number)s" msgstr "Anslöt %(file_name)s till SCSI-id %(id_number)s LUN %(unit_number)s" -#: web.py:548 +#: web.py:571 #, python-format msgid "" "The image file size %(file_size)s bytes is not a multiple of " @@ -204,7 +204,7 @@ msgstr "" "RaSCSI ignorerar den överflödiga datan. Skivbilden är möjligen förstörd, " "så var försiktig när du använder den." -#: web.py:554 +#: web.py:577 #, python-format msgid "" "Failed to attach %(file_name)s to SCSI ID %(id_number)s LUN " @@ -213,16 +213,16 @@ msgstr "" "Kunde inte ansluta %(file_name)s till SCSI-id %(id_number)s LUN " "%(unit_number)s" -#: web.py:568 +#: web.py:591 msgid "Detached all SCSI devices" msgstr "Kopplade ifrån alla SCSI-enheter" -#: web.py:585 +#: web.py:608 #, python-format msgid "Detached SCSI ID %(id_number)s LUN %(unit_number)s" msgstr "Kopplade ifrån SCSI-id %(id_number)s LUN %(unit_number)s" -#: web.py:589 +#: web.py:612 #, python-format msgid "" "Failed to detach %(file_name)s from SCSI ID %(id_number)s LUN " @@ -231,12 +231,12 @@ msgstr "" "Kunde ej koppla ifrån %(file_name)s från SCSI-id %(id_number)s LUN " "%(unit_number)s" -#: web.py:606 +#: web.py:629 #, python-format msgid "Ejected SCSI ID %(id_number)s LUN %(unit_number)s" msgstr "Utmatade SCSI-id %(id_number)s LUN %(unit_number)s" -#: web.py:610 +#: web.py:633 #, python-format msgid "" "Failed to eject %(file_name)s from SCSI ID %(id_number)s LUN " @@ -245,101 +245,101 @@ msgstr "" "Kunde ej utmata %(file_name)s från SCSI-id %(id_number)s LUN " "%(unit_number)s" -#: web.py:633 +#: web.py:656 msgid "DEVICE INFO" msgstr "ENHETSDATA" -#: web.py:635 +#: web.py:658 #, python-format msgid "SCSI ID: %(id_number)s" msgstr "SCSI-id: %(id_number)s" -#: web.py:636 +#: web.py:659 #, python-format msgid "LUN: %(unit_number)s" msgstr "LUN: %(unit_number)s" -#: web.py:637 +#: web.py:660 #, python-format msgid "Type: %(device_type)s" msgstr "Typ: %(device_type)s" -#: web.py:638 +#: web.py:661 #, python-format msgid "Status: %(device_status)s" msgstr "Status: %(device_status)s" -#: web.py:639 +#: web.py:662 #, python-format msgid "File: %(image_file)s" msgstr "Fil: %(image_file)s" -#: web.py:640 +#: web.py:663 #, python-format msgid "Parameters: %(value)s" msgstr "Parametrar: %(value)s" -#: web.py:641 +#: web.py:664 #, python-format msgid "Vendor: %(value)s" msgstr "Tillverkare: %(value)s" -#: web.py:642 +#: web.py:665 #, python-format msgid "Product: %(value)s" msgstr "Produkt: %(value)s" -#: web.py:643 +#: web.py:666 #, python-format msgid "Revision: %(revision_number)s" msgstr "Revision: %(revision_number)s" -#: web.py:644 +#: web.py:667 #, python-format msgid "Block Size: %(value)s bytes" msgstr "Blockstorlek: %(value)s bytes" -#: web.py:645 +#: web.py:668 #, python-format msgid "Image Size: %(value)s bytes" msgstr "Skivbildsstorlek: %(value)s bytes" -#: web.py:664 +#: web.py:687 #, python-format msgid "Reserved SCSI ID %(id_number)s" msgstr "Reserverat SCSI-id %(id_number)s" -#: web.py:667 +#: web.py:690 #, python-format msgid "Failed to reserve SCSI ID %(id_number)s" msgstr "Kunde ej reservera SCSI-id %(id_number)s" -#: web.py:683 +#: web.py:706 #, python-format msgid "Released the reservation for SCSI ID %(id_number)s" msgstr "Avreserverade SCSI-id %(id_number)s" -#: web.py:686 +#: web.py:709 #, python-format msgid "Failed to release the reservation for SCSI ID %(id_number)s" msgstr "Kunde ej avreservera SCSI-id %(id_number)s" -#: web.py:724 +#: web.py:747 #, python-format msgid "Saved image as: %(file_name)s" msgstr "Sparade bildfilen som %(file_name)s" -#: web.py:726 +#: web.py:749 #, python-format msgid "Failed to create CD-ROM image from %(url)s" msgstr "Kunde ej skapa CD-ROM-bildfil från %(url)s" -#: web.py:732 +#: web.py:755 #, python-format msgid "Attached to SCSI ID %(id_number)s" msgstr "Anslöt till SCSI-id %(id_number)s" -#: web.py:735 +#: web.py:758 #, python-format msgid "" "Failed to attach image to SCSI ID %(id_number)s. Try attaching it " @@ -348,55 +348,60 @@ msgstr "" "Kunde ej ansluta bildfilen till SCSI-id %(id_number)s. Försök ansluta den" " manuellt." -#: web.py:754 web.py:771 +#: web.py:777 web.py:794 #, python-format msgid "Failed to download file from %(url)s" msgstr "Kunde ej ladda ner filen från %(url)s" -#: web.py:802 +#: web.py:825 msgid "The file already exists!" msgstr "Filen existerar redan!" -#: web.py:810 +#: web.py:833 msgid "Unable to write the file to disk!" msgstr "Kunde ej skriva filen till skivan!" -#: web.py:821 +#: web.py:844 msgid "Transferred file corrupted!" msgstr "Den överförda filen är skadad!" -#: web.py:827 +#: web.py:850 msgid "File upload successful!" msgstr "Filen har laddas upp!" -#: web.py:870 +#: web.py:893 #, python-format msgid "Image file deleted: %(file_name)s" msgstr "Skivbildfilen %(file_name)s har blivit raderad" -#: web.py:900 +#: web.py:923 #, python-format msgid "Image file renamed to: %(file_name)s" msgstr "Skivbildfilen har blivit omdöpt till %(file_name)s" -#: web.py:937 +#: web.py:960 msgid "Aborted unzip: File(s) with the same name already exists." msgstr "Uppackning stoppad: En eller flera filer med samma namn existerar." -#: web.py:939 +#: web.py:962 msgid "Unzipped the following files:" msgstr "Packade up dessa filer:" -#: web.py:943 +#: web.py:966 #, python-format msgid "Properties file(s) have been moved to %(directory)s" msgstr "En eller flera egenskapsfiler har blivit flyttade till %(directory)s" -#: web.py:946 +#: web.py:969 #, python-format msgid "Failed to unzip %(zip_file)s" msgstr "Kunde ej packa up %(zip_file)s" +#: web.py:980 +#, python-format +msgid "Changed Web Interface language to %(locale)s" +msgstr "Bytte språk för webbgränssnittet till %(locale)s" + #: templates/base.html:4 msgid "RaSCSI Control Page" msgstr "RaSCSI kontrollsida" @@ -514,7 +519,7 @@ msgid "Save as:" msgstr "Spara som:" #: templates/drives.html:41 templates/drives.html:88 templates/drives.html:131 -#: templates/index.html:531 +#: templates/index.html:549 msgid "Create" msgstr "Skapa" @@ -678,8 +683,8 @@ msgid "" "Manage image files in the active RaSCSI image directory: " "%(directory)s with a scan depth of %(scan_depth)s." msgstr "" -"Hantera filer i den aktiva skivbildsfilskatalogen: %(directory)s" -" med hierarkiskt djup %(scan_depth)s." +"Hantera filer i den aktiva skivbildskatalogen: %(directory)s med" +" hierarkiskt djup %(scan_depth)s." #: templates/index.html:156 #, python-format @@ -723,7 +728,7 @@ msgstr "Packa upp" msgid "Unzipping a single file..." msgstr "Packar upp en enda fil..." -#: templates/index.html:233 templates/index.html:530 +#: templates/index.html:233 templates/index.html:390 templates/index.html:548 msgid "MB" msgstr "MB" @@ -799,7 +804,7 @@ msgstr "Gränssnitt:" msgid "Static IP (optional):" msgstr "Statisk adress (tillval):" -#: templates/index.html:329 templates/index.html:455 +#: templates/index.html:329 templates/index.html:473 msgid "SCSI ID:" msgstr "SCSI-id:" @@ -845,36 +850,102 @@ msgstr "" msgid "Recognized file types: %(valid_file_suffix)s" msgstr "Kända filtyper: %(valid_file_suffix)s" +#: templates/index.html:376 +msgid "Drop files here to upload" +msgstr "Släpp filer här för att ladda upp" + +#: templates/index.html:377 +msgid "Your browser does not support drag'n'drop file uploads." +msgstr "Din webbläsare stöder ej filuppladdning via dra och släpp." + +#: templates/index.html:378 +msgid "" +"Please use the fallback form below to upload your files like in the olden" +" days." +msgstr "" +"Använd reservformuläret nedan för att ladda upp dina filer på gammaldags " +"vis." + +#: templates/index.html:379 +msgid "File is too big: {{filesize}}MB. Max filesize: {{maxFilesize}}MB." +msgstr "" +"Filen är för stor: {{filesize}}MB. Största möjliga storlek: " +"{{maxFilesize}}MB." + +#: templates/index.html:380 +msgid "You can't upload files of this type." +msgstr "Du kan ej ladda upp filer av den här typen." + +#: templates/index.html:381 +msgid "Server responded with code: {{statusCode}}" +msgstr "Servern svarade med kod: {{statusCode}}" + +#: templates/index.html:382 +msgid "Cancel upload" +msgstr "Avbryt uppladdning" + #: templates/index.html:383 -msgid "Download File to Images" -msgstr "Ladda ner fil till skivbildsfilskatalogen" +msgid "Upload canceled." +msgstr "Uppladdningen avbröts." + +#: templates/index.html:384 +msgid "Are you sure you want to cancel this upload?" +msgstr "Är du säker på att du vill avbryta uppladdningen?" + +#: templates/index.html:385 +msgid "Remove file" +msgstr "Radera fil" #: templates/index.html:386 +msgid "You can not upload any more files." +msgstr "Du kan inte ladda upp några fler filer." + +#: templates/index.html:388 +msgid "TB" +msgstr "TB" + +#: templates/index.html:389 +msgid "GB" +msgstr "GB" + +#: templates/index.html:391 +msgid "KB" +msgstr "KB" + +#: templates/index.html:392 +msgid "b" +msgstr "b" + +#: templates/index.html:401 +msgid "Download File to Images" +msgstr "Ladda ner fil till skivbildskatalogen" + +#: templates/index.html:404 #, python-format msgid "Given a URL, download that file to the %(directory)s directory." msgstr "Ta ett url och ladda ner en fil till katalogen %(directory)s" -#: templates/index.html:394 templates/index.html:420 templates/index.html:464 +#: templates/index.html:412 templates/index.html:438 templates/index.html:482 msgid "URL:" msgstr "Url:" -#: templates/index.html:395 templates/index.html:421 templates/index.html:465 +#: templates/index.html:413 templates/index.html:439 templates/index.html:483 msgid "URL" msgstr "Url" -#: templates/index.html:396 templates/index.html:422 +#: templates/index.html:414 templates/index.html:440 msgid "Download" msgstr "Ladda ner" -#: templates/index.html:396 +#: templates/index.html:414 msgid "Downloading File to Images..." -msgstr "Laddar ner filen till skivbildsfilskatalogen..." +msgstr "Laddar ner filen till skivbildskatalogen..." -#: templates/index.html:406 +#: templates/index.html:424 msgid "Download File to AppleShare" msgstr "Ladda ner fil till AppleShare" -#: templates/index.html:409 +#: templates/index.html:427 #, python-format msgid "" "Given a URL, download that file to the %(directory)s directory " @@ -883,11 +954,11 @@ msgstr "" "Ta ett url och ladda ner en fil till katalogen %(directory)s och" " fildela den över AFP." -#: templates/index.html:410 +#: templates/index.html:428 msgid "Manage the files you download here through AppleShare on your vintage Mac." msgstr "Hantera dessa filer via AppleShare på en klassisk Mac." -#: templates/index.html:411 +#: templates/index.html:429 #, python-format msgid "" "Requires Netatalk to be installed and configured " @@ -896,25 +967,25 @@ msgstr "" "Kräver att Netatalk är installerat och inställt " "på lämpligt vis för ditt nätverk." -#: templates/index.html:422 +#: templates/index.html:440 msgid "Downloading File to AppleShare..." msgstr "Laddar ner fil till AppleShare..." -#: templates/index.html:429 +#: templates/index.html:447 msgid "The AppleShare server is running. No active connections." msgstr "AppleShare-servern är aktiv. Inga klienter är anslutna." -#: templates/index.html:431 +#: templates/index.html:449 #, python-format msgid "%(value)d active AFP connection" msgstr "%(value)d aktiv AFP-klient" -#: templates/index.html:433 +#: templates/index.html:451 #, python-format msgid "%(value)d active AFP connections" msgstr "%(value)d aktiva AFP-klienter" -#: templates/index.html:436 +#: templates/index.html:454 #, python-format msgid "" "Install Netatalk to use the AppleShare File " @@ -923,11 +994,11 @@ msgstr "" "Installera Netatalk innan du kan använda " "AppleShare-fildelning." -#: templates/index.html:443 +#: templates/index.html:461 msgid "Download File and Create CD-ROM image" msgstr "Ladda ner fil och skapa en cd-bildfil" -#: templates/index.html:446 +#: templates/index.html:464 msgid "" "Create an ISO file system CD-ROM image with the downloaded file, and " "mount it on the given SCSI ID." @@ -935,16 +1006,16 @@ msgstr "" "Skapar en cd-bildfil med ISO-filsystem som innehåller den nedladdade " "filen. Sedan ansluts den till det angivna SCSI-idt." -#: templates/index.html:447 +#: templates/index.html:465 msgid "HFS is for Mac OS, Joliet for Windows, and Rock Ridge for POSIX." msgstr "HFS är för Mac OS, Joliet för Windows, samt Rock Ridge för POSIX." -#: templates/index.html:448 +#: templates/index.html:466 #, python-format msgid "On Mac OS, a compatible CD-ROM driver is required." msgstr "På Mac OS krävs kompatibla cd-drivrutiner." -#: templates/index.html:449 +#: templates/index.html:467 msgid "" "If the downloaded file is a zip archive, we will attempt to unzip it and " "store the resulting files." @@ -952,27 +1023,27 @@ msgstr "" "Om den nedladdade filen är en zip-fil så försöker vi packa up den och " "spara de uppackade filerna på cd-bildfilen" -#: templates/index.html:466 templates/index.html:511 +#: templates/index.html:484 templates/index.html:529 msgid "Type:" msgstr "Typ:" -#: templates/index.html:487 +#: templates/index.html:505 msgid "Download and Mount CD-ROM image" msgstr "Ladda ner och mata in cd-bildfil" -#: templates/index.html:487 +#: templates/index.html:505 msgid "Downloading File and generating CD-ROM image..." msgstr "Laddar ner fil och tillverkar cd-bildfil..." -#: templates/index.html:497 +#: templates/index.html:515 msgid "Create Empty Disk Image File" msgstr "Skapa en tom skivbilsdfil" -#: templates/index.html:500 +#: templates/index.html:518 msgid "The Generic image type is recommended for most computer platforms." msgstr "Bildfilsformatet 'Generic' är rekommederad för de flesta datorsystem." -#: templates/index.html:501 +#: templates/index.html:519 msgid "" "APPLE GENUINE (.hda) and NEC GENUINE (.hdn) image types will make RaSCSI " "behave as a particular drive type that are recognized by Mac and PC98 " @@ -982,7 +1053,7 @@ msgstr "" "RaSCSI beter sig som en typ av hårddisk som Macar och PC98-datorer känner" " igen." -#: templates/index.html:502 +#: templates/index.html:520 msgid "" "SASI images should only be used on the original Sharp X68000, or other " "legacy systems that utilize this pre-SCSI standard." @@ -991,43 +1062,43 @@ msgstr "" "X68000-modellen eller andra riktigt gamla system som använder denna " "föregångare till SCSI." -#: templates/index.html:509 +#: templates/index.html:527 msgid "File Name:" msgstr "Filnamn:" -#: templates/index.html:510 +#: templates/index.html:528 msgid "File Name" msgstr "Filnamn" -#: templates/index.html:514 +#: templates/index.html:532 msgid "SCSI Hard Disk image (Generic) [.hds]" msgstr "SCSI-hårddisk (Generic) [.hds]" -#: templates/index.html:517 +#: templates/index.html:535 msgid "SCSI Hard Disk image (APPLE GENUINE) [.hda]" msgstr "SCSI-hårddisk (APPLE GENUINE) [.hda]" -#: templates/index.html:520 +#: templates/index.html:538 msgid "SCSI Hard Disk image (NEC GENUINE) [.hdn]" msgstr "SCSI-hårddisk (NEC GENUINE) [.hdn]" -#: templates/index.html:523 +#: templates/index.html:541 msgid "SCSI Removable Media Disk image (Generic) [.hdr]" msgstr "SCSI utmatbart medium (Generic) [.hdr]" -#: templates/index.html:526 +#: templates/index.html:544 msgid "SASI Hard Disk image (Legacy) [.hdf]" msgstr "SASI-hårddisk (föråldrat) [.hdf]" -#: templates/index.html:529 +#: templates/index.html:547 msgid "Size:" msgstr "Storlek:" -#: templates/index.html:541 +#: templates/index.html:559 msgid "Create Named Drive" msgstr "Skapa benämnd skiva" -#: templates/index.html:544 +#: templates/index.html:562 msgid "" "Create pairs of images and properties files from a list of real-life " "drives." @@ -1035,7 +1106,7 @@ msgstr "" "Skapar ett par av skivbilds- och egenskapsfiler från en lista av verkliga" " enheter." -#: templates/index.html:545 +#: templates/index.html:563 msgid "" "This will make RaSCSI use certain vendor strings and block sizes that may" " improve compatibility with certain systems." @@ -1043,86 +1114,103 @@ msgstr "" "På så vis kommer RaSCSI använda vissa tillverkarattribut och " "blockstorlekar som kan hjälpa till med kompatibilitet." -#: templates/index.html:548 +#: templates/index.html:566 msgid "Create a named disk image that mimics real-life drives" msgstr "Skapa en benämnd skivbildfil som låstas vara en riktig enhet" -#: templates/index.html:554 +#: templates/index.html:572 msgid "Logging" msgstr "Loggar" -#: templates/index.html:557 +#: templates/index.html:575 msgid "Fetch a certain number of lines of system logs with the given scope." msgstr "Skaffar ett visst antal loggar för en viss systemprocess." -#: templates/index.html:564 +#: templates/index.html:582 msgid "Log Lines:" msgstr "Antal loggar:" -#: templates/index.html:566 +#: templates/index.html:584 msgid "Scope:" msgstr "Process:" -#: templates/index.html:578 +#: templates/index.html:596 msgid "Show Logs" msgstr "Skaffa loggar" -#: templates/index.html:588 +#: templates/index.html:606 msgid "Server Log Level" msgstr "Serverns loggnivå" -#: templates/index.html:591 +#: templates/index.html:609 msgid "Change the log level of the RaSCSI backend process." msgstr "Ändra loggnivån för RaSCSI-servern" -#: templates/index.html:592 +#: templates/index.html:610 msgid "The current dropdown selection indicates the active log level." msgstr "Det nuvarande valet i rullgardinsmenyn påvisar aktiv loggnivå." -#: templates/index.html:599 +#: templates/index.html:617 msgid "Log Level:" msgstr "Loggnivå:" -#: templates/index.html:607 +#: templates/index.html:625 msgid "Set Log Level" msgstr "Ställ in loggnivå" -#: templates/index.html:617 +#: templates/index.html:635 +msgid "Language" +msgstr "Språk" + +#: templates/index.html:638 +msgid "Change the Web Interface language." +msgstr "Byt webbgränssnittets språk." + +#: templates/index.html:645 +msgid "Language:" +msgstr "Språk:" + +#: templates/index.html:653 +msgid "Change Language" +msgstr "Byt språk" + +#: templates/index.html:663 msgid "Raspberry Pi Operations" msgstr "Raspberry Pi-kommandon" -#: templates/index.html:620 +#: templates/index.html:666 msgid "Reboot or shut down the Raspberry Pi that RaSCSI is running on." msgstr "Starta om eller stäng av Raspberry Pi-systemet som RaSCSI körs på." -#: templates/index.html:621 +#: templates/index.html:667 msgid "" "IMPORTANT: Always shut down the Pi before turning off the power. Failing " "to do so may lead to data loss." msgstr "" -"VIKTIGT: Stäng alltid av Pi-systemet innan du stänger av strömmen. Det " -"finns risk för dataförlust." +"VIKTIGT: Stäng alltid av Pi-systemet innan du kopplar från strömmen. På " +"så vis undviker du risken att förlora data." -#: templates/index.html:627 +#: templates/index.html:673 msgid "Reboot the Raspberry Pi?" msgstr "Vill du starta om din Raspberry Pi?" -#: templates/index.html:627 +#: templates/index.html:673 msgid "Rebooting the Raspberry Pi..." msgstr "Startar om Raspberry Pi..." -#: templates/index.html:628 +#: templates/index.html:674 msgid "Reboot Raspberry Pi" msgstr "Starta om Raspberry Pi" -#: templates/index.html:632 +#: templates/index.html:678 msgid "Shut down the Raspberry Pi?" msgstr "Vill du stänga av din Raspberry Pi?" -#: templates/index.html:632 +#: templates/index.html:678 msgid "Shutting down the Raspberry Pi..." msgstr "Stänger av Raspberry Pi..." -#: templates/index.html:633 +#: templates/index.html:679 msgid "Shut Down Raspberry Pi" msgstr "Stäng av Raspberry Pi" + From 2c60a848a8c377b61f67f0930b8bccb46c8881ea Mon Sep 17 00:00:00 2001 From: akuker <34318535+akuker@users.noreply.github.com> Date: Tue, 28 Dec 2021 18:30:20 -0600 Subject: [PATCH 06/18] Add Apple CD Drive as pre-defined drive (#580) * Add Apple CD Drive as pre-defined drive * Fix block size for Apple CD-ROM Co-authored-by: Tony Kuker --- src/web/drive_properties.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/web/drive_properties.json b/src/web/drive_properties.json index d784b5b9..eb3880fd 100644 --- a/src/web/drive_properties.json +++ b/src/web/drive_properties.json @@ -418,5 +418,17 @@ "file_type": null, "description": "Boots DECstations and VAXstations. Use only with workstations of this vintage.", "url": "" +}, +{ + "device_type": "SCCD", + "vendor": "MATSHITA", + "product": "CD-ROM CR-8005 ", + "revision": "1.0k", + "block_size": 2048, + "size": null, + "name": "Apple CD 600e", + "file_type": null, + "description": "Emulates Apple CD ROM drive for use with Macintosh computers.", + "url": "" } ] From 338b6af92cce5a2bae05e063019a53f4b494b2e2 Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Tue, 28 Dec 2021 17:44:12 -0800 Subject: [PATCH 07/18] Hardcoded locale fallback to English (#582) * Hardcoded locale fallback to English * Fallbacks for locale in proto requests in case the locale fallback fails --- src/web/file_cmds.py | 8 ++++---- src/web/ractl_cmds.py | 28 ++++++++++++++-------------- src/web/web.py | 9 ++++++--- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/web/file_cmds.py b/src/web/file_cmds.py index 0605b30a..be6094ca 100644 --- a/src/web/file_cmds.py +++ b/src/web/file_cmds.py @@ -66,7 +66,7 @@ def list_images(): command = proto.PbCommand() command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] + command.params["locale"] = session["language"] or "en" data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -123,7 +123,7 @@ def create_new_image(file_name, file_type, size): command = proto.PbCommand() command.operation = proto.PbOperation.CREATE_IMAGE command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] + command.params["locale"] = session["language"] or "en" command.params["file"] = file_name + "." + file_type command.params["size"] = str(size) @@ -144,7 +144,7 @@ def delete_image(file_name): command = proto.PbCommand() command.operation = proto.PbOperation.DELETE_IMAGE command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] + command.params["locale"] = session["language"] or "en" command.params["file"] = file_name @@ -163,7 +163,7 @@ def rename_image(file_name, new_file_name): command = proto.PbCommand() command.operation = proto.PbOperation.RENAME_IMAGE command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] + command.params["locale"] = session["language"] or "en" command.params["from"] = file_name command.params["to"] = new_file_name diff --git a/src/web/ractl_cmds.py b/src/web/ractl_cmds.py index 84371ac4..5373cd73 100644 --- a/src/web/ractl_cmds.py +++ b/src/web/ractl_cmds.py @@ -25,7 +25,7 @@ def get_server_info(): command = proto.PbCommand() command.operation = proto.PbOperation.SERVER_INFO command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] + command.params["locale"] = session["language"] or "en" data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -84,7 +84,7 @@ def get_reserved_ids(): command = proto.PbCommand() command.operation = proto.PbOperation.RESERVED_IDS_INFO command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] + command.params["locale"] = session["language"] or "en" data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -106,7 +106,7 @@ def get_network_info(): command = proto.PbCommand() command.operation = proto.PbOperation.NETWORK_INTERFACES_INFO command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] + command.params["locale"] = session["language"] or "en" data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -125,7 +125,7 @@ def get_device_types(): command = proto.PbCommand() command.operation = proto.PbOperation.DEVICE_TYPES_INFO command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] + command.params["locale"] = session["language"] or "en" data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -148,7 +148,7 @@ def get_image_files_info(): command = proto.PbCommand() command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] + command.params["locale"] = session["language"] or "en" data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -177,7 +177,7 @@ def attach_image(scsi_id, **kwargs): """ command = proto.PbCommand() command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] + command.params["locale"] = session["language"] or "en" devices = proto.PbDeviceDefinition() devices.id = int(scsi_id) @@ -253,7 +253,7 @@ def detach_by_id(scsi_id, unit=None): command.operation = proto.PbOperation.DETACH command.devices.append(devices) command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] + command.params["locale"] = session["language"] or "en" data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -269,7 +269,7 @@ def detach_all(): command = proto.PbCommand() command.operation = proto.PbOperation.DETACH_ALL command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] + command.params["locale"] = session["language"] or "en" data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -292,7 +292,7 @@ def eject_by_id(scsi_id, unit=None): command.operation = proto.PbOperation.EJECT command.devices.append(devices) command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] + command.params["locale"] = session["language"] or "en" data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -312,7 +312,7 @@ def list_devices(scsi_id=None, unit=None): command = proto.PbCommand() command.operation = proto.PbOperation.DEVICES_INFO command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] + command.params["locale"] = session["language"] or "en" # If method is called with scsi_id parameter, return the info on those devices # Otherwise, return the info on all attached devices @@ -390,7 +390,7 @@ def reserve_scsi_ids(reserved_scsi_ids): command.operation = proto.PbOperation.RESERVE_IDS command.params["ids"] = ",".join(reserved_scsi_ids) command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] + command.params["locale"] = session["language"] or "en" data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -408,7 +408,7 @@ def set_log_level(log_level): command.operation = proto.PbOperation.LOG_LEVEL command.params["level"] = str(log_level) command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] + command.params["locale"] = session["language"] or "en" data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -426,7 +426,7 @@ def shutdown_pi(mode): command.operation = proto.PbOperation.SHUT_DOWN command.params["mode"] = str(mode) command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] + command.params["locale"] = session["language"] or "en" data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -443,7 +443,7 @@ def is_token_auth(): command = proto.PbCommand() command.operation = proto.PbOperation.CHECK_AUTHENTICATION command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] + command.params["locale"] = session["language"] or "en" data = send_pb_command(command.SerializeToString()) result = proto.PbResult() diff --git a/src/web/web.py b/src/web/web.py index 33a1c5d4..26fa58d8 100644 --- a/src/web/web.py +++ b/src/web/web.py @@ -92,10 +92,13 @@ def get_locale(): try: language = session["language"] except KeyError: - language = None - if language is not None: + language = "" + logging.warning("The default locale could not be detected. Falling back to English.") + if language: return language - return request.accept_languages.best_match(LANGUAGES) + # Hardcoded fallback to "en" when the user agent does not send an accept-language header + language = request.accept_languages.best_match(LANGUAGES) or "en" + return language def get_supported_locales(): From a973485f8c860d89d928a3dca49f477ec46864c1 Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Tue, 28 Dec 2021 17:44:47 -0800 Subject: [PATCH 08/18] Update Swedish translations after string change (#581) --- .../translations/sv/LC_MESSAGES/messages.po | 208 +++++++++--------- 1 file changed, 101 insertions(+), 107 deletions(-) diff --git a/src/web/translations/sv/LC_MESSAGES/messages.po b/src/web/translations/sv/LC_MESSAGES/messages.po index f9effe75..e5ecaf25 100644 --- a/src/web/translations/sv/LC_MESSAGES/messages.po +++ b/src/web/translations/sv/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: RaSCSI 68kmla Edition\n" "Report-Msgid-Bugs-To: https://github.com/akuker/RASCSI/issues\n" -"POT-Creation-Date: 2021-12-27 18:04-0800\n" +"POT-Creation-Date: 2021-12-28 14:39-0800\n" "PO-Revision-Date: 2021-12-24 16:16-0800\n" "Last-Translator: Daniel Markstedt \n" "Language: sv\n" @@ -41,53 +41,53 @@ msgstr "Kunde ej flytta filen till %(target_path)s" #: file_cmds.py:323 #, python-format msgid "Created CD-ROM ISO image with arguments \"%(value)s\"" -msgstr "Skapade en CD-ROM ISO-fil med argumenten \"%(value)s\"" +msgstr "Skapade en cd-rom ISO-fil med argumentet \"%(value)s\"" -#: file_cmds.py:353 +#: file_cmds.py:354 #, python-format msgid "%(file_name)s downloaded to %(save_dir)s" msgstr "Laddade ner %(file_name)s till %(save_dir)s" -#: file_cmds.py:396 +#: file_cmds.py:399 #, python-format msgid "Saved configuration file to %(file_name)s" msgstr "Sparade konfigurationsfilen som %(file_name)s" -#: file_cmds.py:406 +#: file_cmds.py:410 #, python-format msgid "Could not write to file: %(file_name)s" msgstr "Kunde ej skriva till filen %(file_name)s" -#: file_cmds.py:464 +#: file_cmds.py:468 msgid "Invalid configuration file format" msgstr "Ogiltigt konfigurationsfilformat" -#: file_cmds.py:467 +#: file_cmds.py:471 #, python-format msgid "Loaded configurations from: %(file_name)s" msgstr "Laddade konfigurationer från %(file_name)s" -#: file_cmds.py:476 +#: file_cmds.py:480 #, python-format msgid "Could not read configuration file: %(file_name)s" msgstr "Kunde ej läsa konfigurationer från filen %(file_name)s" -#: file_cmds.py:493 +#: file_cmds.py:497 #, python-format msgid "Created properties file: %(file_path)s" msgstr "Skapade egenskapsfilen %(file_path)s" -#: file_cmds.py:504 +#: file_cmds.py:508 #, python-format msgid "Could not write to properties file: %(file_path)s" msgstr "Kunde ej spara egenskaper till filen %(file_path)s" -#: file_cmds.py:520 +#: file_cmds.py:524 #, python-format msgid "Read properties from file: %(file_path)s" msgstr "Läste egenskaper från filen %(file_path)s" -#: file_cmds.py:530 +#: file_cmds.py:534 #, python-format msgid "Could not read properties from file: %(file_path)s" msgstr "Kunde ej läsa egenskaper från filen %(file_path)s" @@ -113,7 +113,7 @@ msgid "" " crashed." msgstr "" "RaSCSIs webbgränssnitt kunde inte ansluta till RaSCSI på " -"%(host)s:%(port)s med felmeddelande %(error_msg)s. RaSCSI-processen är " +"%(host)s:%(port)s med felmeddelandet %(error_msg)s. RaSCSI-processen är " "antingen avslagen eller har krashat." #: socket_cmds.py:79 @@ -132,7 +132,7 @@ msgstr "" "RaSCSIs webbgränssnitt fick en ogiltig respons från RaSCSI. Gå tillbaks " "och försök igen. Om samma fel upprepas så rapportera en bugg." -#: web.py:118 +#: web.py:119 msgid "" "RaSCSI is password protected. Start the Web Interface with the --password" " parameter." @@ -140,71 +140,71 @@ msgstr "" "RaSCSI är lösenordsskyddat. Start webbgränssnittet med parametern " "--password ." -#: web.py:214 +#: web.py:220 #, python-format msgid "Could not read drive properties from %(properties_file)s" msgstr "Kunde ej läsa egenskaper från %(properties_file)s" -#: web.py:278 +#: web.py:290 #, python-format msgid "You must log in with credentials for a user in the '%(group)s' group" msgstr "Du måste logga in som en användare som tillhör %(group)s-gruppen" -#: web.py:331 web.py:866 +#: web.py:348 web.py:887 #, python-format msgid "Image file created: %(file_name)s" -msgstr "Skapade skivbildsfil: %(file_name)s" +msgstr "Skapade skivbildsfil %(file_name)s" -#: web.py:456 +#: web.py:473 msgid "An error occurred when fetching logs." msgstr "Ett fel inträffade när vi skaffade loggar." -#: web.py:471 +#: web.py:488 #, python-format msgid "Log level set to %(value)s" -msgstr "Ställde in loggnivån till %(value)s" +msgstr "Bytte loggnivån till %(value)s" -#: web.py:490 +#: web.py:507 #, python-format msgid "Please follow the instructions at %(url)s" msgstr "Följ instruktionerna på %(url)s" -#: web.py:494 +#: web.py:511 msgid "Configure IPv4 forwarding before using a wireless network device." msgstr "" -"Ställ in IPv4-vidarebefodring innan du använder en trådlös " -"nätverksadapter." +"Ställ in IPv4-vidarebefodring innan du använder ett trådlöst " +"nätverksgränssnitt." -#: web.py:498 +#: web.py:515 msgid "Configure NAT before using a wireless network device." -msgstr "Ställ in NAT innan du använder en trådlös nätverksadapter." +msgstr "Ställ in NAT innan du använder ett trådlöst nätverksgränssnitt." -#: web.py:503 web.py:507 +#: web.py:520 web.py:524 msgid "Configure the network bridge before using a wired network device." msgstr "Ställ in nätverksbryggan innan du använder ett nätverksgränssnitt." -#: web.py:520 +#: web.py:537 #, python-format msgid "Attached DaynaPORT to SCSI ID %(id_number)s" msgstr "Anslöt DaynaPORT till SCSI-id %(id_number)s" -#: web.py:568 +#: web.py:585 #, python-format msgid "Attached %(file_name)s to SCSI ID %(id_number)s LUN %(unit_number)s" msgstr "Anslöt %(file_name)s till SCSI-id %(id_number)s LUN %(unit_number)s" -#: web.py:571 +#: web.py:588 #, python-format msgid "" "The image file size %(file_size)s bytes is not a multiple of " "%(block_size)s. RaSCSI will ignore the trailing data. The image may be " "corrupted, so proceed with caution." msgstr "" -"Filstorleken %(file_size)s bytes är inte en multipel av %(block_size)s. " +"Filstorleken %(file_size)s byte är inte en multipel av %(block_size)s. " "RaSCSI ignorerar den överflödiga datan. Skivbilden är möjligen förstörd, " "så var försiktig när du använder den." -#: web.py:577 +#: web.py:594 #, python-format msgid "" "Failed to attach %(file_name)s to SCSI ID %(id_number)s LUN " @@ -213,133 +213,127 @@ msgstr "" "Kunde inte ansluta %(file_name)s till SCSI-id %(id_number)s LUN " "%(unit_number)s" -#: web.py:591 +#: web.py:608 msgid "Detached all SCSI devices" msgstr "Kopplade ifrån alla SCSI-enheter" -#: web.py:608 +#: web.py:625 #, python-format msgid "Detached SCSI ID %(id_number)s LUN %(unit_number)s" msgstr "Kopplade ifrån SCSI-id %(id_number)s LUN %(unit_number)s" -#: web.py:612 -#, python-format -msgid "" -"Failed to detach %(file_name)s from SCSI ID %(id_number)s LUN " -"%(unit_number)s" -msgstr "" -"Kunde ej koppla ifrån %(file_name)s från SCSI-id %(id_number)s LUN " -"%(unit_number)s" - #: web.py:629 #, python-format +msgid "Failed to detach SCSI ID %(id_number)s LUN %(unit_number)s" +msgstr "" +"Kunde ej koppla ifrån SCSI-id %(id_number)s LUN %(unit_number)s" + +#: web.py:646 +#, python-format msgid "Ejected SCSI ID %(id_number)s LUN %(unit_number)s" msgstr "Utmatade SCSI-id %(id_number)s LUN %(unit_number)s" -#: web.py:633 +#: web.py:650 #, python-format -msgid "" -"Failed to eject %(file_name)s from SCSI ID %(id_number)s LUN " -"%(unit_number)s" +msgid "Failed to eject SCSI ID %(id_number)s LUN %(unit_number)s" msgstr "" -"Kunde ej utmata %(file_name)s från SCSI-id %(id_number)s LUN " -"%(unit_number)s" +"Kunde ej mata ut skiva från SCSI-id %(id_number)s LUN %(unit_number)s" -#: web.py:656 +#: web.py:673 msgid "DEVICE INFO" msgstr "ENHETSDATA" -#: web.py:658 +#: web.py:675 #, python-format msgid "SCSI ID: %(id_number)s" msgstr "SCSI-id: %(id_number)s" -#: web.py:659 +#: web.py:676 #, python-format msgid "LUN: %(unit_number)s" msgstr "LUN: %(unit_number)s" -#: web.py:660 +#: web.py:677 #, python-format msgid "Type: %(device_type)s" msgstr "Typ: %(device_type)s" -#: web.py:661 +#: web.py:678 #, python-format msgid "Status: %(device_status)s" msgstr "Status: %(device_status)s" -#: web.py:662 +#: web.py:679 #, python-format msgid "File: %(image_file)s" msgstr "Fil: %(image_file)s" -#: web.py:663 +#: web.py:680 #, python-format msgid "Parameters: %(value)s" msgstr "Parametrar: %(value)s" -#: web.py:664 +#: web.py:681 #, python-format msgid "Vendor: %(value)s" msgstr "Tillverkare: %(value)s" -#: web.py:665 +#: web.py:682 #, python-format msgid "Product: %(value)s" msgstr "Produkt: %(value)s" -#: web.py:666 +#: web.py:683 #, python-format msgid "Revision: %(revision_number)s" msgstr "Revision: %(revision_number)s" -#: web.py:667 +#: web.py:684 #, python-format msgid "Block Size: %(value)s bytes" -msgstr "Blockstorlek: %(value)s bytes" +msgstr "Blockstorlek: %(value)s byte" -#: web.py:668 +#: web.py:685 #, python-format msgid "Image Size: %(value)s bytes" -msgstr "Skivbildsstorlek: %(value)s bytes" +msgstr "Skivbildsstorlek: %(value)s byte" -#: web.py:687 +#: web.py:704 #, python-format msgid "Reserved SCSI ID %(id_number)s" msgstr "Reserverat SCSI-id %(id_number)s" -#: web.py:690 +#: web.py:707 #, python-format msgid "Failed to reserve SCSI ID %(id_number)s" msgstr "Kunde ej reservera SCSI-id %(id_number)s" -#: web.py:706 +#: web.py:723 #, python-format msgid "Released the reservation for SCSI ID %(id_number)s" msgstr "Avreserverade SCSI-id %(id_number)s" -#: web.py:709 +#: web.py:726 #, python-format msgid "Failed to release the reservation for SCSI ID %(id_number)s" msgstr "Kunde ej avreservera SCSI-id %(id_number)s" -#: web.py:747 +#: web.py:764 #, python-format msgid "Saved image as: %(file_name)s" msgstr "Sparade bildfilen som %(file_name)s" -#: web.py:749 +#: web.py:766 #, python-format msgid "Failed to create CD-ROM image from %(url)s" msgstr "Kunde ej skapa CD-ROM-bildfil från %(url)s" -#: web.py:755 +#: web.py:772 #, python-format msgid "Attached to SCSI ID %(id_number)s" msgstr "Anslöt till SCSI-id %(id_number)s" -#: web.py:758 +#: web.py:775 #, python-format msgid "" "Failed to attach image to SCSI ID %(id_number)s. Try attaching it " @@ -348,59 +342,59 @@ msgstr "" "Kunde ej ansluta bildfilen till SCSI-id %(id_number)s. Försök ansluta den" " manuellt." -#: web.py:777 web.py:794 +#: web.py:794 web.py:811 #, python-format msgid "Failed to download file from %(url)s" msgstr "Kunde ej ladda ner filen från %(url)s" -#: web.py:825 +#: web.py:842 msgid "The file already exists!" msgstr "Filen existerar redan!" -#: web.py:833 +#: web.py:850 msgid "Unable to write the file to disk!" msgstr "Kunde ej skriva filen till skivan!" -#: web.py:844 +#: web.py:865 msgid "Transferred file corrupted!" msgstr "Den överförda filen är skadad!" -#: web.py:850 +#: web.py:871 msgid "File upload successful!" msgstr "Filen har laddas upp!" -#: web.py:893 +#: web.py:914 #, python-format msgid "Image file deleted: %(file_name)s" -msgstr "Skivbildfilen %(file_name)s har blivit raderad" +msgstr "Filen %(file_name)s har blivit raderad" -#: web.py:923 +#: web.py:944 #, python-format msgid "Image file renamed to: %(file_name)s" -msgstr "Skivbildfilen har blivit omdöpt till %(file_name)s" +msgstr "Filen har blivit omdöpt till %(file_name)s" -#: web.py:960 +#: web.py:981 msgid "Aborted unzip: File(s) with the same name already exists." -msgstr "Uppackning stoppad: En eller flera filer med samma namn existerar." +msgstr "Uppackning stoppad: En eller flera filer med samma namn existerar redan." -#: web.py:962 +#: web.py:983 msgid "Unzipped the following files:" msgstr "Packade up dessa filer:" -#: web.py:966 +#: web.py:987 #, python-format msgid "Properties file(s) have been moved to %(directory)s" msgstr "En eller flera egenskapsfiler har blivit flyttade till %(directory)s" -#: web.py:969 +#: web.py:990 #, python-format msgid "Failed to unzip %(zip_file)s" msgstr "Kunde ej packa up %(zip_file)s" -#: web.py:980 +#: web.py:1004 #, python-format msgid "Changed Web Interface language to %(locale)s" -msgstr "Bytte språk för webbgränssnittet till %(locale)s" +msgstr "Bytte webbgränssnittets språk till %(locale)s" #: templates/base.html:4 msgid "RaSCSI Control Page" @@ -562,7 +556,7 @@ msgid "" "Save and load device configurations, stored as json files in " "%(config_dir)s" msgstr "" -"Spara och ladda enhetskonfigurationer. Sparas som json-format i " +"Spara och ladda enhetskonfigurationer. Sparas i json-format vid " "%(config_dir)s" #: templates/index.html:11 @@ -631,7 +625,7 @@ msgstr "Anslut" #: templates/index.html:98 msgid "Eject Disk? WARNING: On Mac OS, eject the Disk in the Finder instead!" -msgstr "Mata ut skiva? VARNING: På Mac OS, mata ut skivan i Finder istället!" +msgstr "Mata ut skiva? VARNING: På Mac OS bör du mata ut skivan i Finder istället!" #: templates/index.html:101 msgid "Eject" @@ -726,7 +720,7 @@ msgstr "Packa upp" #: templates/index.html:204 templates/index.html:218 msgid "Unzipping a single file..." -msgstr "Packar upp en enda fil..." +msgstr "Packar upp endast en fil..." #: templates/index.html:233 templates/index.html:390 templates/index.html:548 msgid "MB" @@ -768,7 +762,7 @@ msgid "" "Emulates a SCSI DaynaPORT Ethernet Adapter. Host " "drivers and configuration required." msgstr "" -"Emulerar en SCSI DaynaPORT ethernet-adapter. Kräver " +"Emulerar ett SCSI DaynaPORT ethernet-gränssnitt. Kräver " "drivrutiner och inställningar." #: templates/index.html:306 @@ -811,7 +805,7 @@ msgstr "SCSI-id:" #: templates/index.html:343 #, python-format msgid "Macproxy is running at %(ip_addr)s (default port 5000)" -msgstr "Macproxy är tillgängligt på %(ip_addr)s (förvald port 5000)" +msgstr "Macproxy är tillgängligt på %(ip_addr)s (vanligtvis port 5000)" #: templates/index.html:345 #, python-format @@ -820,7 +814,7 @@ msgid "" "vintage browser. It's not just for Macs!" msgstr "" "Installera Macproxy och surfa på nätet med gamla " -"webbläsare. Den är inte bara för Macar!" +"webbläsare. Den är inte bara till för Macar!" #: templates/index.html:351 msgid "Upload File" @@ -843,7 +837,7 @@ msgid "" msgstr "" "Om RaSCSI inte känner igen en filtyp kan du försöka döpa om " "hårddiskbildfiler till '.hds', cd-bildfiler till '.iso', och utmatbara " -"bildfiler till '.hdr' innan du laddar upp den." +"bildfiler till '.hdr' innan du laddar upp dem." #: templates/index.html:356 #, python-format @@ -923,15 +917,15 @@ msgstr "Ladda ner fil till skivbildskatalogen" #: templates/index.html:404 #, python-format msgid "Given a URL, download that file to the %(directory)s directory." -msgstr "Ta ett url och ladda ner en fil till katalogen %(directory)s" +msgstr "Ta en webbadress och ladda ner en fil till katalogen %(directory)s" #: templates/index.html:412 templates/index.html:438 templates/index.html:482 msgid "URL:" -msgstr "Url:" +msgstr "Webbadress:" #: templates/index.html:413 templates/index.html:439 templates/index.html:483 msgid "URL" -msgstr "Url" +msgstr "Webbadress" #: templates/index.html:414 templates/index.html:440 msgid "Download" @@ -951,12 +945,12 @@ msgid "" "Given a URL, download that file to the %(directory)s directory " "and share it over AFP." msgstr "" -"Ta ett url och ladda ner en fil till katalogen %(directory)s och" +"Ta en webbadress och ladda ner en fil till katalogen %(directory)s och" " fildela den över AFP." #: templates/index.html:428 msgid "Manage the files you download here through AppleShare on your vintage Mac." -msgstr "Hantera dessa filer via AppleShare på en klassisk Mac." +msgstr "Hantera dessa filer via AppleShare på din gamla Mac." #: templates/index.html:429 #, python-format @@ -1004,7 +998,7 @@ msgid "" "mount it on the given SCSI ID." msgstr "" "Skapar en cd-bildfil med ISO-filsystem som innehåller den nedladdade " -"filen. Sedan ansluts den till det angivna SCSI-idt." +"filen. Sedan ansluts den till det angivna SCSI-id:t." #: templates/index.html:465 msgid "HFS is for Mac OS, Joliet for Windows, and Rock Ridge for POSIX." @@ -1021,7 +1015,7 @@ msgid "" "store the resulting files." msgstr "" "Om den nedladdade filen är en zip-fil så försöker vi packa up den och " -"spara de uppackade filerna på cd-bildfilen" +"spara de uppackade filerna på cd-bildfilen." #: templates/index.html:484 templates/index.html:529 msgid "Type:" @@ -1058,8 +1052,8 @@ msgid "" "SASI images should only be used on the original Sharp X68000, or other " "legacy systems that utilize this pre-SCSI standard." msgstr "" -"Bildfilsformatet SASI bör endast användas för den första Sharp " -"X68000-modellen eller andra riktigt gamla system som använder denna " +"Bildfilsformatet SASI bör endast användas med tidiga Sharp " +"X68000-modeller, eller andra riktigt gamla system som använder denna " "föregångare till SCSI." #: templates/index.html:527 @@ -1144,7 +1138,7 @@ msgstr "Serverns loggnivå" #: templates/index.html:609 msgid "Change the log level of the RaSCSI backend process." -msgstr "Ändra loggnivån för RaSCSI-servern" +msgstr "Byt loggnivån för RaSCSI-servern" #: templates/index.html:610 msgid "The current dropdown selection indicates the active log level." @@ -1156,7 +1150,7 @@ msgstr "Loggnivå:" #: templates/index.html:625 msgid "Set Log Level" -msgstr "Ställ in loggnivå" +msgstr "Byt loggnivå" #: templates/index.html:635 msgid "Language" @@ -1187,7 +1181,7 @@ msgid "" "IMPORTANT: Always shut down the Pi before turning off the power. Failing " "to do so may lead to data loss." msgstr "" -"VIKTIGT: Stäng alltid av Pi-systemet innan du kopplar från strömmen. På " +"VIKTIGT: Stäng alltid av Pi-systemet innan du kopplar ur strömmen. På " "så vis undviker du risken att förlora data." #: templates/index.html:673 From 40fe8d611e08d22b7e86560869537a910ad6fb6a Mon Sep 17 00:00:00 2001 From: Kamel Makhloufi <742898+melka@users.noreply.github.com> Date: Thu, 30 Dec 2021 02:56:44 +0100 Subject: [PATCH 09/18] Added french localization (#584) * French localization added * Updated french translation * Corrected small typo in french translation * Corrected small errors in fr translation --- src/raspberrypi/localizer.cpp | 23 +- src/web/settings.py | 2 +- .../translations/fr/LC_MESSAGES/messages.po | 1210 +++++++++++++++++ 3 files changed, 1233 insertions(+), 2 deletions(-) create mode 100644 src/web/translations/fr/LC_MESSAGES/messages.po diff --git a/src/raspberrypi/localizer.cpp b/src/raspberrypi/localizer.cpp index 36b4393f..7660c6de 100644 --- a/src/raspberrypi/localizer.cpp +++ b/src/raspberrypi/localizer.cpp @@ -19,72 +19,93 @@ using namespace std; Localizer::Localizer() { // Supported locales, always lower case - supported_languages = { "en", "de", "sv" }; + supported_languages = { "en", "de", "sv", "fr" }; // Positional string arguments are %1, %2, %3 Add(ERROR_AUTHENTICATION, "en", "Authentication failed"); Add(ERROR_AUTHENTICATION, "de", "Authentifizierung fehlgeschlagen"); Add(ERROR_AUTHENTICATION, "sv", "Autentisering misslyckades"); + Add(ERROR_AUTHENTICATION, "fr", "Authentification éronnée"); Add(ERROR_OPERATION, "en", "Unknown operation"); Add(ERROR_OPERATION, "de", "Unbekannte Operation"); Add(ERROR_OPERATION, "sv", "Okänd operation"); + Add(ERROR_OPERATION, "fr", "Opération inconnue"); Add(ERROR_LOG_LEVEL, "en", "Invalid log level %1"); Add(ERROR_LOG_LEVEL, "de", "Ungültiger Log-Level %1"); Add(ERROR_LOG_LEVEL, "sv", "Ogiltig loggnivå %1"); + Add(ERROR_LOG_LEVEL, "fr", "Niveau de journalisation invalide %1"); Add(ERROR_MISSING_DEVICE_ID, "en", "Missing device ID"); Add(ERROR_MISSING_DEVICE_ID, "de", "Fehlende Geräte-ID"); Add(ERROR_MISSING_DEVICE_ID, "sv", "Enhetens ID saknas"); + Add(ERROR_MISSING_DEVICE_ID, "fr", "ID de périphérique manquante"); Add(ERROR_MISSING_FILENAME, "en", "Missing filename"); Add(ERROR_MISSING_FILENAME, "de", "Fehlender Dateiname"); Add(ERROR_MISSING_FILENAME, "sv", "Filnamn saknas"); + Add(ERROR_MISSING_FILENAME, "fr", "Nom de fichier manquant"); Add(ERROR_IMAGE_IN_USE, "en", "Image file '%1' is already being used by ID %2, unit %3"); Add(ERROR_IMAGE_IN_USE, "de", "Image-Datei '%1' wird bereits von ID %2, Einheit %3 benutzt"); Add(ERROR_IMAGE_IN_USE, "sv", "Skivbildfilen '%1' används redan av ID %2, LUN %3"); + Add(ERROR_IMAGE_IN_USE, "fr", "Le fichier d'image '%1' est déjà utilisé par l'ID %2, unité %3"); Add(ERROR_RESERVED_ID, "en", "Device ID %1 is reserved"); Add(ERROR_RESERVED_ID, "de", "Geräte-ID %1 ist reserviert"); Add(ERROR_RESERVED_ID, "sv", "Enhets-ID %1 är reserverat"); + Add(ERROR_RESERVED_ID, "fr", "ID de périphérique %1 réservée"); Add(ERROR_NON_EXISTING_DEVICE, "en", "Command for non-existing ID %1"); Add(ERROR_NON_EXISTING_DEVICE, "de", "Kommando für nicht existente ID %1"); Add(ERROR_NON_EXISTING_DEVICE, "sv", "Kommando för avsaknat ID %1"); + Add(ERROR_NON_EXISTING_DEVICE, "fr", "Commande pour ID %1 non-existant"); Add(ERROR_NON_EXISTING_UNIT, "en", "Command for non-existing ID %1, unit %2"); Add(ERROR_NON_EXISTING_UNIT, "de", "Kommando für nicht existente ID %1, Einheit %2"); Add(ERROR_NON_EXISTING_UNIT, "sv", "Kommando för avsaknat ID %1, LUN %2"); + Add(ERROR_NON_EXISTING_UNIT, "fr", "Command pour ID %1, unité %2 non-existant"); Add(ERROR_UNKNOWN_DEVICE_TYPE, "en", "Unknown device type %1"); Add(ERROR_UNKNOWN_DEVICE_TYPE, "de", "Unbekannter Gerätetyp %1"); Add(ERROR_UNKNOWN_DEVICE_TYPE, "sv", "Obekant enhetstyp: %1"); + Add(ERROR_UNKNOWN_DEVICE_TYPE, "fr", "Type de périphérique inconnu %1"); Add(ERROR_MISSING_DEVICE_TYPE, "en", "Device type required for unknown extension of file '%1'"); Add(ERROR_MISSING_DEVICE_TYPE, "de", "Gerätetyp erforderlich für unbekannte Extension der Datei '%1'"); Add(ERROR_MISSING_DEVICE_TYPE, "sv", "Man måste ange enhetstyp för obekant filändelse '%1'"); + Add(ERROR_MISSING_DEVICE_TYPE, "fr", "Type de périphérique requis pour extension inconnue du fichier '%1'"); Add(ERROR_DUPLICATE_ID, "en", "Duplicate ID %1, unit %2"); Add(ERROR_DUPLICATE_ID, "de", "Doppelte ID %1, Einheit %2"); Add(ERROR_DUPLICATE_ID, "sv", "Duplikat ID %1, LUN %2"); + Add(ERROR_DUPLICATE_ID, "fr", "ID %1, unité %2 dupliquée"); Add(ERROR_SASI_SCSI, "en", "SASI and SCSI can't be used at the same time"); Add(ERROR_SASI_SCSI, "de", "SASI und SCSI können nicht gleichzeitig verwendet werden"); Add(ERROR_SASI_SCSI, "sv", "SASI och SCSI kan ej användas samtidigt"); + Add(ERROR_SASI_SCSI, "fr", "SASI et SCSI ne peuvent être utilisés en même temps"); Add(ERROR_EJECT_REQUIRED, "en", "Existing medium must first be ejected"); Add(ERROR_EJECT_REQUIRED, "de", "Das vorhandene Medium muss erst ausgeworfen werden"); Add(ERROR_EJECT_REQUIRED, "sv", "Nuvarande skiva måste utmatas först"); + Add(ERROR_EJECT_REQUIRED, "fr", "Media déjà existant doit d'abord être éjecté"); Add(ERROR_DEVICE_NAME_UPDATE, "en", "Once set the device name cannot be changed anymore"); Add(ERROR_DEVICE_NAME_UPDATE, "de", "Ein bereits gesetzter Gerätename kann nicht mehr geändert werden"); Add(ERROR_DEVICE_NAME_UPDATE, "sv", "Enhetsnamn kan ej ändras efter att ha fastställts en gång"); + Add(ERROR_DEVICE_NAME_UPDATE, "fr", "Une fois défini, le nom de périphérique ne peut plus être changé"); Add(ERROR_SHUTDOWN_MODE_MISSING, "en", "Missing shutdown mode"); Add(ERROR_SHUTDOWN_MODE_MISSING, "de", "Fehlender Shutdown-Modus"); Add(ERROR_SHUTDOWN_MODE_MISSING, "sv", "Avstängningsläge saknas"); + Add(ERROR_SHUTDOWN_MODE_MISSING, "fr", "Mode d'extinction manquant"); Add(ERROR_SHUTDOWN_MODE_INVALID, "en", "Invalid shutdown mode '%1'"); Add(ERROR_SHUTDOWN_MODE_INVALID, "de", "Ungültiger Shutdown-Modus '%1'"); Add(ERROR_SHUTDOWN_MODE_INVALID, "sv", "Ogiltigt avstängsningsläge: '%1'"); + Add(ERROR_SHUTDOWN_MODE_INVALID, "fr", "Mode d'extinction invalide '%1'"); Add(ERROR_SHUTDOWN_PERMISSION, "en", "Missing root permission for shutdown or reboot"); Add(ERROR_SHUTDOWN_PERMISSION, "de", "Fehlende Root-Berechtigung für Shutdown oder Neustart"); Add(ERROR_SHUTDOWN_PERMISSION, "sv", "Root-rättigheter saknas för att kunna stänga av eller starta om systemet"); + Add(ERROR_SHUTDOWN_PERMISSION, "fr", "Permissions root manquantes pour extinction ou redémarrage"); Add(ERROR_FILE_OPEN, "en", "Invalid or non-existing file '%1': %2"); Add(ERROR_FILE_OPEN, "de", "Ungültige oder fehlende Datei '%1': %2"); Add(ERROR_FILE_OPEN, "sv", "Ogiltig eller saknad fil '%1': %2"); + Add(ERROR_FILE_OPEN, "fr", "Fichier invalide ou non-existant '%1': %2"); Add(ERROR_BLOCK_SIZE, "en", "Invalid block size %1 bytes"); Add(ERROR_BLOCK_SIZE, "de", "Ungültige Blockgröße %1 Bytes"); Add(ERROR_BLOCK_SIZE, "sv", "Ogiltig blockstorlek: %1 byte"); + Add(ERROR_BLOCK_SIZE, "fr", "Taille de bloc invalide %1 octets"); Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "en", "Block size for device type %1 is not configurable"); Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "de", "Blockgröße für Gerätetyp %1 ist nicht konfigurierbar"); Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "sv", "Enhetstypen %1 kan inte använda andra blockstorlekar"); + Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "fr", "Taille de block pour le type de périphérique %1 non configurable"); } void Localizer::Add(LocalizationKey key, const string& locale, const string& value) diff --git a/src/web/settings.py b/src/web/settings.py index 3095b8f2..196c1756 100644 --- a/src/web/settings.py +++ b/src/web/settings.py @@ -32,4 +32,4 @@ RESERVATIONS = ["" for x in range(0, 8)] AUTH_GROUP = "rascsi" # The language locales supported by RaSCSI -LANGUAGES = ["en", "de", "sv"] +LANGUAGES = ["en", "de", "sv", "fr"] diff --git a/src/web/translations/fr/LC_MESSAGES/messages.po b/src/web/translations/fr/LC_MESSAGES/messages.po new file mode 100644 index 00000000..ae0ce7c5 --- /dev/null +++ b/src/web/translations/fr/LC_MESSAGES/messages.po @@ -0,0 +1,1210 @@ +# French translations for RaSCSI. +# Copyright (C) 2021 akuker +# This file is distributed under the same license as the RaSCSI project. +# Kamel Makhloufi , 2021. +# +msgid "" +msgstr "" +"Project-Id-Version: RaSCSI 68kmla Edition\n" +"Report-Msgid-Bugs-To: https://github.com/akuker/RASCSI/issues\n" +"POT-Creation-Date: 2021-12-29 15:51+0100\n" +"PO-Revision-Date: 2021-12-29 15:58+0100\n" +"Last-Translator: Kamel Makhloufi \n" +"Language-Team: N/A\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Generated-By: Babel 2.9.1\n" + +#: file_cmds.py:186 +#, python-format +msgid "File deleted: %(file_path)s" +msgstr "Fichier supprimé : %(file_path)s" + +#: file_cmds.py:190 +#, python-format +msgid "File to delete not found: %(file_path)s" +msgstr "Fichier à supprimer non trouvé : %(file_path)s" + +#: file_cmds.py:203 +#, python-format +msgid "File moved to: %(target_path)s" +msgstr "Fichier déplacé vers : %(target_path)s" + +#: file_cmds.py:207 +#, python-format +msgid "Unable to move file to: %(target_path)s" +msgstr "Echec lors du déplacement du fichier vers : %(target_path)s" + +#: file_cmds.py:323 +#, python-format +msgid "Created CD-ROM ISO image with arguments \"%(value)s\"" +msgstr "Image ISO CD-ROM créée avec les arguments \"%(value)s\"" + +#: file_cmds.py:354 +#, python-format +msgid "%(file_name)s downloaded to %(save_dir)s" +msgstr "%(file_name)s téléchargé dans %(save_dir)s" + +#: file_cmds.py:399 +#, python-format +msgid "Saved configuration file to %(file_name)s" +msgstr "Fichier de configuration sauvegardé dans %(file_name)s" + +#: file_cmds.py:410 +#, python-format +msgid "Could not write to file: %(file_name)s" +msgstr "Echec lors de l’écriture du fichier : %(file_name)s" + +#: file_cmds.py:468 +msgid "Invalid configuration file format" +msgstr "Format de fichier de configuration invalide" + +#: file_cmds.py:471 +#, python-format +msgid "Loaded configurations from: %(file_name)s" +msgstr "Configurations chargées depuis : %(file_name)s" + +#: file_cmds.py:480 +#, python-format +msgid "Could not read configuration file: %(file_name)s" +msgstr "Echec de lecture du fichier de configuration : %(file_name)s" + +#: file_cmds.py:497 +#, python-format +msgid "Created properties file: %(file_path)s" +msgstr "Fichier de propriétés créé : %(file_path)s" + +#: file_cmds.py:508 +#, python-format +msgid "Could not write to properties file: %(file_path)s" +msgstr "Echec d’écriture du fichier de propriétés : %(file_path)s" + +#: file_cmds.py:524 +#, python-format +msgid "Read properties from file: %(file_path)s" +msgstr "Lecture des propriétés depuis le fichier : %(file_path)s" + +#: file_cmds.py:534 +#, python-format +msgid "Could not read properties from file: %(file_path)s" +msgstr "Echec de lecture des propriétés depuis le fichier : %(file_path)s" + +#: pi_cmds.py:179 +msgid "You must log in to use this function" +msgstr "Vous devez vous connecter pour utiliser cette fonctionnalité" + +#: ractl_cmds.py:206 +#, python-format +msgid "" +"Cannot insert an image for %(device_type)s into a %(current_device_type)s device" +msgstr "" +"Ne peux pas introduire d'une image de type %(device_type)s dans un appareil de " +"type %(current_device_type)s" + +#: socket_cmds.py:39 +#, python-format +msgid "" +"The RaSCSI Web Interface failed to connect to RaSCSI at %(host)s:%(port)s with " +"error: %(error_msg)s. The RaSCSI process is not running or may have crashed." +msgstr "" +"L’interface web RaSCSI n’as pas pu se connecter à RaSCSI à l’adresse %(host)s:" +"%(port)s avec l’erreur : %(error_msg)s. Le processus RaSCSI n’est peut-être pas " +"en cours d’exécution ou a planté." + +#: socket_cmds.py:79 +msgid "" +"The RaSCSI Web Interface lost connection to RaSCSI. Please go back and try " +"again. If the issue persists, please report a bug." +msgstr "" +"L’interface web RaSCSI à perdu la connexion à RaSCSI. Merci de réessayer. Si le " +"problème persiste, merci de faire remonter un bug." + +#: socket_cmds.py:95 +msgid "" +"The RaSCSI Web Interface did not get a valid response from RaSCSI. Please go " +"back and try again. If the issue persists, please report a bug." +msgstr "" +"L’interface web RaSCSI à reçu une réponse invalide de RaSCSI. Merci de " +"réessayer. Si le problème persiste, merci de faire remonter un bug." + +#: web.py:122 +msgid "" +"RaSCSI is password protected. Start the Web Interface with the --password " +"parameter." +msgstr "" +"RaSCSI est protégé par un mot de passe. Merci de démarrer l’interface web avec " +"le paramètre --password" + +#: web.py:223 +#, python-format +msgid "Could not read drive properties from %(properties_file)s" +msgstr "Echec de lecture des propriétés du disque depuis %(properties_file)s" + +#: web.py:293 +#, python-format +msgid "You must log in with credentials for a user in the '%(group)s' group" +msgstr "" +"Vous devez vous connecter avec les identifiants d’un utilisateur appartenant au " +"groupe '%(group)s'" + +#: web.py:351 web.py:890 +#, python-format +msgid "Image file created: %(file_name)s" +msgstr "Fichier image créé : %(file_name)s" + +#: web.py:476 +msgid "An error occurred when fetching logs." +msgstr "" +"Une erreur s’est produite lors du chargement des fichiers de journalisation." + +#: web.py:491 +#, python-format +msgid "Log level set to %(value)s" +msgstr "Niveau de journalisation défini sur %(value)s" + +#: web.py:510 +#, python-format +msgid "Please follow the instructions at %(url)s" +msgstr "Merci de suivre les instructions à %(url)s" + +#: web.py:514 +msgid "Configure IPv4 forwarding before using a wireless network device." +msgstr "" +"Configurez le forwarding IPv4 avant d’utiliser un périphérique réseau sans fil." + +#: web.py:518 +msgid "Configure NAT before using a wireless network device." +msgstr "Configurez NAT avant d’utiliser un périphérique réseau sans fil." + +#: web.py:523 web.py:527 +msgid "Configure the network bridge before using a wired network device." +msgstr "" +"Configurez le pont réseau avant d’utiliser un périphérique réseau sans fil." + +#: web.py:540 +#, python-format +msgid "Attached DaynaPORT to SCSI ID %(id_number)s" +msgstr "DaynaPORT attaché au SCSI ID %(id_number)s" + +#: web.py:588 +#, python-format +msgid "Attached %(file_name)s to SCSI ID %(id_number)s LUN %(unit_number)s" +msgstr "%(file_name)s attaché au SCSI ID %(id_number)s LUN %(unit_number)s" + +#: web.py:591 +#, python-format +msgid "" +"The image file size %(file_size)s bytes is not a multiple of %(block_size)s. " +"RaSCSI will ignore the trailing data. The image may be corrupted, so proceed " +"with caution." +msgstr "" +"La taille du fichier image %(file_size)s octets n’est pas un multiple de " +"%(block_size)s. RaSCSI va ignorer les données de fin. L’image peut être " +"corrompue, merci de continuer avec précaution." + +#: web.py:597 +#, python-format +msgid "" +"Failed to attach %(file_name)s to SCSI ID %(id_number)s LUN %(unit_number)s" +msgstr "" +"Echec d'attachement de %(file_name)s au périphérique SCSI ID %(id_number)s LUN " +"%(unit_number)s" + +#: web.py:611 +msgid "Detached all SCSI devices" +msgstr "Tout les périphériques SCSI ont été détachés" + +#: web.py:628 +#, python-format +msgid "Detached SCSI ID %(id_number)s LUN %(unit_number)s" +msgstr "Périphérique SCSI ID %(id_number)s LUN %(unit_number)s détaché" + +#: web.py:632 +#, python-format +msgid "Failed to detach SCSI ID %(id_number)s LUN %(unit_number)s" +msgstr "Echec lors du détachement du SCSI ID %(id_number)s LUN %(unit_number)s" + +#: web.py:649 +#, python-format +msgid "Ejected SCSI ID %(id_number)s LUN %(unit_number)s" +msgstr "SCSI ID %(id_number)s LUN %(unit_number)s éjecté" + +#: web.py:653 +#, python-format +msgid "Failed to eject SCSI ID %(id_number)s LUN %(unit_number)s" +msgstr "Echec lors de l’éjection de SCSI ID %(id_number)s LUN %(unit_number)s" + +#: web.py:676 +msgid "DEVICE INFO" +msgstr "INFO PÉRIPHÉRIQUE" + +#: web.py:678 +#, python-format +msgid "SCSI ID: %(id_number)s" +msgstr "SCSI ID : %(id_number)s" + +#: web.py:679 +#, python-format +msgid "LUN: %(unit_number)s" +msgstr "LUN : %(unit_number)s" + +#: web.py:680 +#, python-format +msgid "Type: %(device_type)s" +msgstr "Type : %(device_type)s" + +#: web.py:681 +#, python-format +msgid "Status: %(device_status)s" +msgstr "Statut : %(device_status)s" + +#: web.py:682 +#, python-format +msgid "File: %(image_file)s" +msgstr "Fichier : %(image_file)s" + +#: web.py:683 +#, python-format +msgid "Parameters: %(value)s" +msgstr "Paramètres : %(value)s" + +#: web.py:684 +#, python-format +msgid "Vendor: %(value)s" +msgstr "Vendeur : %(value)s" + +#: web.py:685 +#, python-format +msgid "Product: %(value)s" +msgstr "Produit : %(value)s" + +#: web.py:686 +#, python-format +msgid "Revision: %(revision_number)s" +msgstr "Révision : %(revision_number)s" + +#: web.py:687 +#, python-format +msgid "Block Size: %(value)s bytes" +msgstr "Taille de bloc : %(value)s octets" + +#: web.py:688 +#, python-format +msgid "Image Size: %(value)s bytes" +msgstr "Taille de l’image : %(value)s octets" + +#: web.py:707 +#, python-format +msgid "Reserved SCSI ID %(id_number)s" +msgstr "ID SCSI réservés %(id_number)s" + +#: web.py:710 +#, python-format +msgid "Failed to reserve SCSI ID %(id_number)s" +msgstr "Echec de réservation du SCSI ID %(id_number)s" + +#: web.py:726 +#, python-format +msgid "Released the reservation for SCSI ID %(id_number)s" +msgstr "Réservation libérée pour SCSI ID %(id_number)s" + +#: web.py:729 +#, python-format +msgid "Failed to release the reservation for SCSI ID %(id_number)s" +msgstr "Echec de libération de la réservation pour SCSI ID %(id_number)s" + +#: web.py:767 +#, python-format +msgid "Saved image as: %(file_name)s" +msgstr "Image sauvegardée en : %(file_name)s" + +#: web.py:769 +#, python-format +msgid "Failed to create CD-ROM image from %(url)s" +msgstr "Echec de création de l’image CD-ROM depuis %(url)s" + +#: web.py:775 +#, python-format +msgid "Attached to SCSI ID %(id_number)s" +msgstr "Attaché au SCSI ID %(id_number)s" + +#: web.py:778 +#, python-format +msgid "" +"Failed to attach image to SCSI ID %(id_number)s. Try attaching it manually." +msgstr "" +"Echec lors de l'attachement de l’image au SCSI ID %(id_number)s. Essayez de " +"l’attacher manuellement." + +#: web.py:797 web.py:814 +#, python-format +msgid "Failed to download file from %(url)s" +msgstr "Echec de téléchargement du fichier depuis l’url %(url)s" + +#: web.py:845 +msgid "The file already exists!" +msgstr "Le fichier existe déjà !" + +#: web.py:853 +msgid "Unable to write the file to disk!" +msgstr "Echec d’écriture du fichier sur le disque !" + +#: web.py:868 +msgid "Transferred file corrupted!" +msgstr "Fichier transféré corrompu !" + +#: web.py:874 +msgid "File upload successful!" +msgstr "Transfert de fichier réussi !" + +#: web.py:917 +#, python-format +msgid "Image file deleted: %(file_name)s" +msgstr "Fichier image supprimé : %(file_name)s" + +#: web.py:947 +#, python-format +msgid "Image file renamed to: %(file_name)s" +msgstr "Fichier image renommé en : %(file_name)s" + +#: web.py:984 +msgid "Aborted unzip: File(s) with the same name already exists." +msgstr "Décompression avortée : Fichier(s) avec le même nom déjà existants." + +#: web.py:986 +msgid "Unzipped the following files:" +msgstr "Décompression des fichiers suivants terminée :" + +#: web.py:990 +#, python-format +msgid "Properties file(s) have been moved to %(directory)s" +msgstr "Fichier(s) de propriétés déplacés vers %(directory)s" + +#: web.py:993 +#, python-format +msgid "Failed to unzip %(zip_file)s" +msgstr "Echec lors de la décompression de %(zip_file)s" + +#: web.py:1007 +#, python-format +msgid "Changed Web Interface language to %(locale)s" +msgstr "Langue de l’interface web changée pour %(locale)s" + +#: templates/base.html:4 +msgid "RaSCSI Control Page" +msgstr "Page de contrôle de RaSCSI" + +#: templates/base.html:29 +msgid "" +" This process may take a while, and will continue in the background if you " +"navigate away from this page." +msgstr "" +" Ce processus peut prendre du temps, et continuera de s’exécuter en arrière " +"plan si vous quittez cette page." + +#: templates/base.html:34 +msgid "" +" The Web Interface will become unresponsive momentarily. Reload this page after " +"the Pi has started up again." +msgstr "" +" L’interface web ne répondras pas momentanément. Rechargez cette page après que " +"le Pi ai redémarré." + +#: templates/base.html:48 +#, python-format +msgid "Logged in as %(username)s" +msgstr "Connecté en tant que %(username)s" + +#: templates/base.html:48 +msgid "Log Out" +msgstr "Se déconnecter" + +#: templates/base.html:52 +msgid "Log In to Use Web Interface" +msgstr "Se connecter à l’interface web" + +#: templates/base.html:53 +msgid "Username" +msgstr "Utilisateur" + +#: templates/base.html:54 +msgid "Password" +msgstr "Mot de passe" + +#: templates/base.html:60 +msgid "Web Interface Authentication Disabled" +msgstr "Authentification sur l’interface web desactivée" + +#: templates/base.html:60 +#, python-format +msgid "See Wiki for more information" +msgstr "" +"Voir le Wiki pour plus d’informations" + +#: templates/base.html:87 +msgid "RaSCSI version: " +msgstr "RaSCSI version : " + +#: templates/base.html:88 +msgid "Pi environment: " +msgstr "Environnement Pi : " + +#: templates/drives.html:4 templates/drives.html:139 +msgid "Cancel" +msgstr "Annuler" + +#: templates/drives.html:5 +msgid "Disclaimer" +msgstr "Avertissement" + +#: templates/drives.html:6 +#, python-format +msgid "" +"These device profiles are provided as-is with no guarantee to work equally to " +"the actual physical device they are named after. You may need to provide " +"appropirate device drivers and/or configuration parameters for them to function " +"properly. If you would like to see data modified, or have additional devices to " +"add to the list, please raise an issue ticket at GitHub." +msgstr "" +"Ces profils de périphérique sont donnés tel-quels sans garantie que leur " +"fonctionnement soit égal au périphérique physique desquels ils tirent leur nom. " +"Vous pourriez avoir besoin d'ajouter les pilotes de périphérique appropriés " +"et / ou les paramètres de configuration nécessaires pour qu'ils fonctionnent " +"correctement. Si vous souhaitez voir les données modifiées ou avez des " +"périphériques additionnels à ajouter à la liste, merci d'ouvrir un billet de " +"support sur GitHub." + +#: templates/drives.html:7 +msgid "Hard Drives" +msgstr "Disques durs" + +#: templates/drives.html:12 templates/drives.html:56 templates/drives.html:102 +msgid "Name" +msgstr "Nom" + +#: templates/drives.html:13 templates/drives.html:57 templates/drives.html:103 +msgid "Size (MB)" +msgstr "Taille (MO)" + +#: templates/drives.html:14 templates/drives.html:58 templates/drives.html:104 +msgid "Description" +msgstr "Description" + +#: templates/drives.html:15 templates/drives.html:59 templates/drives.html:105 +msgid "Ref." +msgstr "Réf." + +#: templates/drives.html:16 templates/drives.html:60 templates/drives.html:106 +msgid "Action" +msgstr "Action" + +#: templates/drives.html:25 templates/drives.html:69 templates/drives.html:115 +msgid "Link" +msgstr "Lien" + +#: templates/drives.html:39 templates/drives.html:129 +msgid "Save as:" +msgstr "Sauvegarder sous :" + +#: templates/drives.html:41 templates/drives.html:88 templates/drives.html:131 +#: templates/index.html:549 +msgid "Create" +msgstr "Créer" + +#: templates/drives.html:51 +msgid "CD-ROM Drives" +msgstr "Lecteurs CD-ROM" + +#: templates/drives.html:52 +msgid "" +"This will create a properties file for the given CD-ROM image. No new image " +"file will be created." +msgstr "" +"Cela créera un fichier de propriété pour l’image CD-ROM donnée. Aucune nouveau " +"fichier d’image ne sera créé." + +#: templates/drives.html:80 +msgid "Create for:" +msgstr "Créer pour :" + +#: templates/drives.html:98 +msgid "Removable Drives" +msgstr "Disques amovibles" + +#: templates/drives.html:138 templates/index.html:295 +#, python-format +msgid "%(disk_space)s MB disk space remaining on the Pi" +msgstr "%(disk_space)s MO d’espace disque restant sur le Pi" + +#: templates/index.html:6 +msgid "Current RaSCSI Configuration" +msgstr "Configuration RaSCSI actuelle" + +#: templates/index.html:9 +msgid "Displays the currently attached devices for each available SCSI ID." +msgstr "Montre l’appareil actuellement attaché pour chaque ID SCSI disponible." + +#: templates/index.html:10 +#, python-format +msgid "" +"Save and load device configurations, stored as json files in " +"%(config_dir)s" +msgstr "" +"Sauvegarder et charger les fichiers de configuration de périphériques, stockés " +"en fichiers json dans %(config_dir)s" + +#: templates/index.html:11 +msgid "" +"To have a particular device configuration load when RaSCSI starts, save it as " +"default." +msgstr "" +"Pour avoir une configuration de périphérique en particulier chargée au " +"démarrage de RaSCSI, sauvegardez la en tant que default." + +#: templates/index.html:25 +msgid "No saved configurations" +msgstr "Aucune configuration sauvegardée" + +#: templates/index.html:29 +msgid "Load" +msgstr "Charger" + +#: templates/index.html:29 +msgid "Detach all current device and Load configuration?" +msgstr "Détacher tout les périphériques actuels et charger la configuration ?" + +#: templates/index.html:30 templates/index.html:287 +msgid "Delete" +msgstr "Supprimer" + +#: templates/index.html:30 +msgid "Delete configuration file?" +msgstr "Supprimer le fichier de configuration ?" + +#: templates/index.html:35 +msgid "Save" +msgstr "Sauvegarder" + +#: templates/index.html:41 templates/index.html:252 +msgid "ID" +msgstr "ID" + +#: templates/index.html:43 templates/index.html:260 +msgid "LUN" +msgstr "LUN" + +#: templates/index.html:45 templates/index.html:268 +msgid "Type" +msgstr "Type" + +#: templates/index.html:46 +msgid "Status" +msgstr "Statut" + +#: templates/index.html:47 templates/index.html:166 +msgid "File" +msgstr "Fichier" + +#: templates/index.html:48 +msgid "Product" +msgstr "Produit" + +#: templates/index.html:49 templates/index.html:168 +msgid "Actions" +msgstr "Actions" + +#: templates/index.html:84 templates/index.html:277 templates/index.html:337 +msgid "Attach" +msgstr "Attacher" + +#: templates/index.html:98 +msgid "Eject Disk? WARNING: On Mac OS, eject the Disk in the Finder instead!" +msgstr "" +"Ejecter le disque ? ATTENTION: Sur Mac OS, préférez ejecter le disque depuis le " +"Finder !" + +#: templates/index.html:101 +msgid "Eject" +msgstr "Ejecter" + +#: templates/index.html:104 +msgid "Detach Device?" +msgstr "Détacher le périphérique ?" + +#: templates/index.html:107 +msgid "Detach" +msgstr "Détacher" + +#: templates/index.html:113 +msgid "Info" +msgstr "Info" + +#: templates/index.html:116 +msgid "Enter a memo for this reservation" +msgstr "Entrez un mémo pour cette réservation" + +#: templates/index.html:119 +msgid "Reserve" +msgstr "Réserver" + +#: templates/index.html:129 +msgid "Reserved ID" +msgstr "ID réservé" + +#: templates/index.html:135 +msgid "Unreserve" +msgstr "Libérer" + +#: templates/index.html:144 +msgid "Detach all SCSI Devices?" +msgstr "Détacher tout les périphériques SCSI ?" + +#: templates/index.html:145 +msgid "Detach All Devices" +msgstr "Détacher tout les périphériques" + +#: templates/index.html:152 +msgid "Image File Management" +msgstr "Gestion des fichiers d'image" + +#: templates/index.html:155 +#, python-format +msgid "" +"Manage image files in the active RaSCSI image directory: %(directory)s " +"with a scan depth of %(scan_depth)s." +msgstr "" +"Gérer les fichiers image dans le dossier image RaSCSI actif : " +"%(directory)s avec un profondeur de scan de %(scan_depth)s." + +#: templates/index.html:156 +#, python-format +msgid "" +"Select a valid SCSI ID and LUN to attach to. Unless you " +"know what you're doing, always use LUN 0." +msgstr "" +"Sélectionner un SCSI ID et LUN valides pour attacher " +"l'image. A moins que vous sachiez ce que vous faites, toujours utiliser LUN 0." + +#: templates/index.html:158 +msgid "" +"If RaSCSI was unable to detect the device type associated with the image, you " +"can choose the type from the dropdown." +msgstr "" +"Si RaSCSI n'as pas pu détecter le type de périphérique associé à l'image, vous " +"pouvez choisir le type depuis le menu déroulant." + +#: templates/index.html:159 +msgid "" +"Types: SAHD = SASI HDD | SCHD = SCSI HDD | SCRM = Removable | SCMO = Magneto-" +"Optical | SCCD = CD-ROM | SCBR = Host Bridge | SCDP = DaynaPORT" +msgstr "" +"Types : SAHD = Disque dur SASI | SCHD = Disque dur SCSI | SCRM = Amovible | " +"SCMO = Magneto-Optique | SCCD = CD-ROM | SCBR = Pont d'hôte | SCDP = DaynaPORT" + +#: templates/index.html:167 +msgid "Size" +msgstr "Taille" + +#: templates/index.html:184 +msgid "Properties File" +msgstr "Fichier de propriété" + +#: templates/index.html:204 templates/index.html:218 +msgid "Unzip" +msgstr "Décompresser" + +#: templates/index.html:204 templates/index.html:218 +msgid "Unzipping a single file..." +msgstr "Décompression d'un fichier unique..." + +#: templates/index.html:233 templates/index.html:390 templates/index.html:548 +msgid "MB" +msgstr "MO" + +#: templates/index.html:239 +msgid "Attached!" +msgstr "Attaché!" + +#: templates/index.html:246 +msgid "Unzip All" +msgstr "Tout décompresser" + +#: templates/index.html:246 +msgid "Unzipping all files..." +msgstr "Décompression de tout les fichiers..." + +#: templates/index.html:280 +#, python-format +msgid "Enter new file name for: %(file_name)s" +msgstr "Entrez un nouveau nom de fichier pour : %(file_name)s" + +#: templates/index.html:283 +msgid "Rename" +msgstr "Renommer" + +#: templates/index.html:285 +#, python-format +msgid "Delete file: %(file_name)s?" +msgstr "Supprimer le fichier : %(file_name)s ?" + +#: templates/index.html:301 +msgid "Attach Ethernet Adapter" +msgstr "Attacher l'adaptateur Ethernet" + +#: templates/index.html:304 +#, python-format +msgid "" +"Emulates a SCSI DaynaPORT Ethernet Adapter. Host drivers " +"and configuration required." +msgstr "" +"Emules un adaptateur Ethernet SCSI DaynaPORT. Pilotes et " +"configuration requis pour l'hôte." + +#: templates/index.html:306 +msgid "" +"If you have a DHCP setup, choose only the interface you have configured the " +"bridge with. You can ignore the Static IP fields when attaching." +msgstr "" +"Si vous avez une configuration DHCP, choisir uniquement l'interface pour " +"laquelle le pont est configuré. Vous pouvez ignorer les champs IP statique lors " +"de l'attache." + +#: templates/index.html:307 +#, python-format +msgid "" +"Configure the network bridge by running easyinstall.sh, or follow the manual steps in the wiki." +msgstr "" +"Configurer le pont réseau en exécutant easyinstall.sh, ou suivre les étapes manuelles décrites dans le wiki." + +#: templates/index.html:310 +msgid "" +"The rascsi_bridge interface is active and ready to be used by " +"DaynaPORT!" +msgstr "" +"L'interface rascsi_bridge est active et prête à être utilisée par le " +"DaynaPORT!" + +#: templates/index.html:318 +msgid "Interface:" +msgstr "Interface :" + +#: templates/index.html:326 +msgid "Static IP (optional):" +msgstr "IP statique (optionnel) :" + +#: templates/index.html:329 templates/index.html:473 +msgid "SCSI ID:" +msgstr "SCSI ID :" + +#: templates/index.html:343 +#, python-format +msgid "Macproxy is running at %(ip_addr)s (default port 5000)" +msgstr "Macproxy s'exécute à l'adresse %(ip_addr)s (port par défaut 5000)" + +#: templates/index.html:345 +#, python-format +msgid "" +"Install Macproxy to browse the Web with any vintage " +"browser. It's not just for Macs!" +msgstr "" +"Installez Macproxy pour naviguer sur le web avec " +"n'importe quel navigateur ancien. Ce n'est pas que pour les Macs !" + +#: templates/index.html:351 +msgid "Upload File" +msgstr "Transférer fichier" + +#: templates/index.html:354 +#, python-format +msgid "" +"Uploads file to %(directory)s. The largest file size accepted is " +"%(max_file_size)s MB." +msgstr "" +"Transfert le fichier vers %(directory)s. La taille maximale de fichier " +"acceptée est de %(max_file_size)s MO." + +#: templates/index.html:355 +msgid "" +"For unrecognized file types, try renaming hard drive images to '.hds', CD-ROM " +"images to '.iso', and removable drive images to '.hdr' before uploading." +msgstr "" +"Pour les types de fichiers non reconnus, essayez de renommer les image disque " +"dur en '.hds', les images CD-ROM en '.iso', et les images de disques amovibles " +"en '.hdr' avant de les transférer." + +#: templates/index.html:356 +#, python-format +msgid "Recognized file types: %(valid_file_suffix)s" +msgstr "Types de fichiers reconnus : %(valid_file_suffix)s" + +#: templates/index.html:376 +msgid "Drop files here to upload" +msgstr "Déposez le fichier ici pour le transférer" + +#: templates/index.html:377 +msgid "Your browser does not support drag'n'drop file uploads." +msgstr "Votre navigateur ne supporte pas l'envoi de fichier par glisser-déposer." + +#: templates/index.html:378 +msgid "" +"Please use the fallback form below to upload your files like in the olden days." +msgstr "" +"Merci d'utiliser le formulaire de replis ci-dessous pour transférer vos " +"fichiers de manière classique." + +#: templates/index.html:379 +msgid "File is too big: {{filesize}}MB. Max filesize: {{maxFilesize}}MB." +msgstr "" +"Fichier trop gros : {{filesize}}MO. Taille de fichier maximale : {{maxFilesize}}" +"MO." + +#: templates/index.html:380 +msgid "You can't upload files of this type." +msgstr "Vous ne pouvez pas transférer de fichiers de ce type." + +#: templates/index.html:381 +msgid "Server responded with code: {{statusCode}}" +msgstr "Le serveur à répondu avec le code : {{statusCode}}" + +#: templates/index.html:382 +msgid "Cancel upload" +msgstr "Annuler le transfert" + +#: templates/index.html:383 +msgid "Upload canceled." +msgstr "Transfert annulé." + +#: templates/index.html:384 +msgid "Are you sure you want to cancel this upload?" +msgstr "Êtes vous sûr de vouloir annuler ce transfert ?" + +#: templates/index.html:385 +msgid "Remove file" +msgstr "Supprimer le fichier" + +#: templates/index.html:386 +msgid "You can not upload any more files." +msgstr "Vous ne pouvez plus transférer d'autres fichiers." + +#: templates/index.html:388 +msgid "TB" +msgstr "TO" + +#: templates/index.html:389 +msgid "GB" +msgstr "GO" + +#: templates/index.html:391 +msgid "KB" +msgstr "KO" + +#: templates/index.html:392 +msgid "b" +msgstr "o" + +#: templates/index.html:401 +msgid "Download File to Images" +msgstr "Transférer fichier vers les images" + +#: templates/index.html:404 +#, python-format +msgid "Given a URL, download that file to the %(directory)s directory." +msgstr "" +"A partir d’une URL, télécharge ce fichier dans le répertoire %(directory)s." + +#: templates/index.html:412 templates/index.html:438 templates/index.html:482 +msgid "URL:" +msgstr "URL :" + +#: templates/index.html:413 templates/index.html:439 templates/index.html:483 +msgid "URL" +msgstr "URL" + +#: templates/index.html:414 templates/index.html:440 +msgid "Download" +msgstr "Transférer" + +#: templates/index.html:414 +msgid "Downloading File to Images..." +msgstr "Transfert du fichier vers les images..." + +#: templates/index.html:424 +msgid "Download File to AppleShare" +msgstr "Transférer fichier vers AppleShare" + +#: templates/index.html:427 +#, python-format +msgid "" +"Given a URL, download that file to the %(directory)s directory and " +"share it over AFP." +msgstr "" +"A partir d'une URL, télécharger ce fichier dans le répertoire " +"%(directory)s et le partager via AFP." + +#: templates/index.html:428 +msgid "Manage the files you download here through AppleShare on your vintage Mac." +msgstr "" +"Gérer les fichiers que vous avez transférés ici via AppleShare sur votre Mac " +"vintage." + +#: templates/index.html:429 +#, python-format +msgid "" +"Requires Netatalk to be installed and configured " +"correctly for your network." +msgstr "" +"A besoin que Netatalk soit installé et configuré " +"correctement pour votre réseau." + +#: templates/index.html:440 +msgid "Downloading File to AppleShare..." +msgstr "Transfert du fichier vers AppleShare..." + +#: templates/index.html:447 +msgid "The AppleShare server is running. No active connections." +msgstr "Le serveur AppleShare s'exécute. Aucune connexion active." + +#: templates/index.html:449 +#, python-format +msgid "%(value)d active AFP connection" +msgstr "%(value)d connexion AFP active" + +#: templates/index.html:451 +#, python-format +msgid "%(value)d active AFP connections" +msgstr "%(value)d connexions AFP actives" + +#: templates/index.html:454 +#, python-format +msgid "" +"Install Netatalk to use the AppleShare File Server." +msgstr "" +"Installez Netatalk pour utiliser le serveur de fichiers " +"AppleShare." + +#: templates/index.html:461 +msgid "Download File and Create CD-ROM image" +msgstr "Transférer le fichier et créer une image CD-ROM" + +#: templates/index.html:464 +msgid "" +"Create an ISO file system CD-ROM image with the downloaded file, and mount it " +"on the given SCSI ID." +msgstr "" +"Créer une image CD-ROM ISO avec le fichier téléchargé et la monter sur le SCSI " +"ID donné." + +#: templates/index.html:465 +msgid "HFS is for Mac OS, Joliet for Windows, and Rock Ridge for POSIX." +msgstr "HFS est pour Mac OS, Joliet pour Windows, et Rock Ridge pour POSIX." + +#: templates/index.html:466 +#, python-format +msgid "On Mac OS, a compatible CD-ROM driver is required." +msgstr "" +"Sur Mac OS, un pilote CD-ROM compatible est requis." + +#: templates/index.html:467 +msgid "" +"If the downloaded file is a zip archive, we will attempt to unzip it and store " +"the resulting files." +msgstr "" +"Si le fichier téléchargé est une archive zip, nous allons tenter de la " +"décompresser et de stocker les fichiers résultants." + +#: templates/index.html:484 templates/index.html:529 +msgid "Type:" +msgstr "Type :" + +#: templates/index.html:505 +msgid "Download and Mount CD-ROM image" +msgstr "Télécharger et monter image CD-ROM" + +#: templates/index.html:505 +msgid "Downloading File and generating CD-ROM image..." +msgstr "Téléchargement du fichier et génération de l’image CD-ROM..." + +#: templates/index.html:515 +msgid "Create Empty Disk Image File" +msgstr "Créer un fichier d’image disque vide" + +#: templates/index.html:518 +msgid "The Generic image type is recommended for most computer platforms." +msgstr "Le type d'image générique est recommandé pour la plupart des plateformes." + +#: templates/index.html:519 +msgid "" +"APPLE GENUINE (.hda) and NEC GENUINE (.hdn) image types will make RaSCSI behave " +"as a particular drive type that are recognized by Mac and PC98 systems, " +"respectively." +msgstr "" +"Les types d'image APPLE AUTHENTIQUE (.hda) et NEC AUTHENTIQUE (.hdn) feront que " +"RaSCSI se comportera comme un type de disque particulier qui sont reconnus par " +"les systèmes Mac et PC98 respectivement." + +#: templates/index.html:520 +msgid "" +"SASI images should only be used on the original Sharp X68000, or other legacy " +"systems that utilize this pre-SCSI standard." +msgstr "" +"Les images SASI ne devraient être utilisées que sur le Sharp X68000 originel, " +"ou autre systèmes anciens qui utilisent ce standard pre-SCSI." + +#: templates/index.html:527 +msgid "File Name:" +msgstr "Nom de fichier :" + +#: templates/index.html:528 +msgid "File Name" +msgstr "Nom de fichier" + +#: templates/index.html:532 +msgid "SCSI Hard Disk image (Generic) [.hds]" +msgstr "Image de disque dur SCSI (Générique) [.hds]" + +#: templates/index.html:535 +msgid "SCSI Hard Disk image (APPLE GENUINE) [.hda]" +msgstr "Image de disque dur SCSI (APPLE AUTHENTIQUE) [.hda]" + +#: templates/index.html:538 +msgid "SCSI Hard Disk image (NEC GENUINE) [.hdn]" +msgstr "Image de disque dur SCSI (NEC AUTHENTIQUE) [.hdn]" + +#: templates/index.html:541 +msgid "SCSI Removable Media Disk image (Generic) [.hdr]" +msgstr "Image de disque amovible SCSI (Générique) [.hdr]" + +#: templates/index.html:544 +msgid "SASI Hard Disk image (Legacy) [.hdf]" +msgstr "Image de disque dur SASI (Héritée) [.hdf]" + +#: templates/index.html:547 +msgid "Size:" +msgstr "Taille :" + +#: templates/index.html:559 +msgid "Create Named Drive" +msgstr "Créer un disque nommé" + +#: templates/index.html:562 +msgid "" +"Create pairs of images and properties files from a list of real-life drives." +msgstr "" +"Créer une paire d'image et de fichier de propriétés depuis une liste de vrais " +"disques." + +#: templates/index.html:563 +msgid "" +"This will make RaSCSI use certain vendor strings and block sizes that may " +"improve compatibility with certain systems." +msgstr "" +"RaSCSI utilisera certains noms de vendeurs et tailles de blocs afin " +"d'améliorer la compatibilité avec certains systèmes." + +#: templates/index.html:566 +msgid "Create a named disk image that mimics real-life drives" +msgstr "Créer une image disque nommée imitant les disques réels" + +#: templates/index.html:572 +msgid "Logging" +msgstr "Journalisation" + +#: templates/index.html:575 +msgid "Fetch a certain number of lines of system logs with the given scope." +msgstr "" +"Charger un certain nombre de lignes depuis les journaux système avec la portée " +"donnée." + +#: templates/index.html:582 +msgid "Log Lines:" +msgstr "Lignes du journal :" + +#: templates/index.html:584 +msgid "Scope:" +msgstr "Portée :" + +#: templates/index.html:596 +msgid "Show Logs" +msgstr "Montrer les journaux" + +#: templates/index.html:606 +msgid "Server Log Level" +msgstr "Niveau de journalisation du serveur" + +#: templates/index.html:609 +msgid "Change the log level of the RaSCSI backend process." +msgstr "Changer le niveau de journalisation des processus principaux RaSCSI." + +#: templates/index.html:610 +msgid "The current dropdown selection indicates the active log level." +msgstr "Le menu déroulant indique le niveau de journalisation." + +#: templates/index.html:617 +msgid "Log Level:" +msgstr "Niveau de journalisation :" + +#: templates/index.html:625 +msgid "Set Log Level" +msgstr "Définir le niveau de journalisation" + +#: templates/index.html:635 +msgid "Language" +msgstr "Langue" + +#: templates/index.html:638 +msgid "Change the Web Interface language." +msgstr "Changer la langue de l'interface web." + +#: templates/index.html:645 +msgid "Language:" +msgstr "Langue :" + +#: templates/index.html:653 +msgid "Change Language" +msgstr "Changer la langue" + +#: templates/index.html:663 +msgid "Raspberry Pi Operations" +msgstr "Opérations Raspberry Pi" + +#: templates/index.html:666 +msgid "Reboot or shut down the Raspberry Pi that RaSCSI is running on." +msgstr "Redémarrer ou éteindre le Raspberry Pi sur lequel RaSCSI s'exécute." + +#: templates/index.html:667 +msgid "" +"IMPORTANT: Always shut down the Pi before turning off the power. Failing to do " +"so may lead to data loss." +msgstr "" +"IMPORTANT: Toujours éteindre le Raspberry Pi avant de couper l’alimentation. Ne " +"pas le faire peut entrainer la perte de données." + +#: templates/index.html:673 +msgid "Reboot the Raspberry Pi?" +msgstr "Redémarrer le Raspberry Pi ?" + +#: templates/index.html:673 +msgid "Rebooting the Raspberry Pi..." +msgstr "Redémarrage du Raspberry Pi..." + +#: templates/index.html:674 +msgid "Reboot Raspberry Pi" +msgstr "Redémarrer le Raspberry Pi" + +#: templates/index.html:678 +msgid "Shut down the Raspberry Pi?" +msgstr "Éteindre le Raspberry Pi ?" + +#: templates/index.html:678 +msgid "Shutting down the Raspberry Pi..." +msgstr "Raspberry Pi en cours d’extinction..." + +#: templates/index.html:679 +msgid "Shut Down Raspberry Pi" +msgstr "Éteindre le Raspberry Pi" From ce7e391a52cebb5b0b0bb19508a8320c9f76d73b Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Wed, 29 Dec 2021 18:54:59 -0800 Subject: [PATCH 10/18] More sturdy logic for when session language is unset (#583) --- src/web/file_cmds.py | 12 ++++++++---- src/web/ractl_cmds.py | 42 ++++++++++++++++++++++++++++-------------- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/web/file_cmds.py b/src/web/file_cmds.py index be6094ca..f80041cc 100644 --- a/src/web/file_cmds.py +++ b/src/web/file_cmds.py @@ -66,7 +66,8 @@ def list_images(): command = proto.PbCommand() command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] or "en" + if "language" in session.keys(): + command.params["locale"] = session["language"] data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -123,7 +124,8 @@ def create_new_image(file_name, file_type, size): command = proto.PbCommand() command.operation = proto.PbOperation.CREATE_IMAGE command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] or "en" + if "language" in session.keys(): + command.params["locale"] = session["language"] command.params["file"] = file_name + "." + file_type command.params["size"] = str(size) @@ -144,7 +146,8 @@ def delete_image(file_name): command = proto.PbCommand() command.operation = proto.PbOperation.DELETE_IMAGE command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] or "en" + if "language" in session.keys(): + command.params["locale"] = session["language"] command.params["file"] = file_name @@ -163,7 +166,8 @@ def rename_image(file_name, new_file_name): command = proto.PbCommand() command.operation = proto.PbOperation.RENAME_IMAGE command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] or "en" + if "language" in session.keys(): + command.params["locale"] = session["language"] command.params["from"] = file_name command.params["to"] = new_file_name diff --git a/src/web/ractl_cmds.py b/src/web/ractl_cmds.py index 5373cd73..ce67c0cf 100644 --- a/src/web/ractl_cmds.py +++ b/src/web/ractl_cmds.py @@ -25,7 +25,8 @@ def get_server_info(): command = proto.PbCommand() command.operation = proto.PbOperation.SERVER_INFO command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] or "en" + if "language" in session.keys(): + command.params["locale"] = session["language"] data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -84,7 +85,8 @@ def get_reserved_ids(): command = proto.PbCommand() command.operation = proto.PbOperation.RESERVED_IDS_INFO command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] or "en" + if "language" in session.keys(): + command.params["locale"] = session["language"] data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -106,7 +108,8 @@ def get_network_info(): command = proto.PbCommand() command.operation = proto.PbOperation.NETWORK_INTERFACES_INFO command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] or "en" + if "language" in session.keys(): + command.params["locale"] = session["language"] data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -125,7 +128,8 @@ def get_device_types(): command = proto.PbCommand() command.operation = proto.PbOperation.DEVICE_TYPES_INFO command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] or "en" + if "language" in session.keys(): + command.params["locale"] = session["language"] data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -148,7 +152,8 @@ def get_image_files_info(): command = proto.PbCommand() command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] or "en" + if "language" in session.keys(): + command.params["locale"] = session["language"] data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -177,7 +182,8 @@ def attach_image(scsi_id, **kwargs): """ command = proto.PbCommand() command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] or "en" + if "language" in session.keys(): + command.params["locale"] = session["language"] devices = proto.PbDeviceDefinition() devices.id = int(scsi_id) @@ -253,7 +259,8 @@ def detach_by_id(scsi_id, unit=None): command.operation = proto.PbOperation.DETACH command.devices.append(devices) command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] or "en" + if "language" in session.keys(): + command.params["locale"] = session["language"] data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -269,7 +276,8 @@ def detach_all(): command = proto.PbCommand() command.operation = proto.PbOperation.DETACH_ALL command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] or "en" + if "language" in session.keys(): + command.params["locale"] = session["language"] data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -292,7 +300,8 @@ def eject_by_id(scsi_id, unit=None): command.operation = proto.PbOperation.EJECT command.devices.append(devices) command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] or "en" + if "language" in session.keys(): + command.params["locale"] = session["language"] data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -312,7 +321,8 @@ def list_devices(scsi_id=None, unit=None): command = proto.PbCommand() command.operation = proto.PbOperation.DEVICES_INFO command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] or "en" + if "language" in session.keys(): + command.params["locale"] = session["language"] # If method is called with scsi_id parameter, return the info on those devices # Otherwise, return the info on all attached devices @@ -390,7 +400,8 @@ def reserve_scsi_ids(reserved_scsi_ids): command.operation = proto.PbOperation.RESERVE_IDS command.params["ids"] = ",".join(reserved_scsi_ids) command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] or "en" + if "language" in session.keys(): + command.params["locale"] = session["language"] data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -408,7 +419,8 @@ def set_log_level(log_level): command.operation = proto.PbOperation.LOG_LEVEL command.params["level"] = str(log_level) command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] or "en" + if "language" in session.keys(): + command.params["locale"] = session["language"] data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -426,7 +438,8 @@ def shutdown_pi(mode): command.operation = proto.PbOperation.SHUT_DOWN command.params["mode"] = str(mode) command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] or "en" + if "language" in session.keys(): + command.params["locale"] = session["language"] data = send_pb_command(command.SerializeToString()) result = proto.PbResult() @@ -443,7 +456,8 @@ def is_token_auth(): command = proto.PbCommand() command.operation = proto.PbOperation.CHECK_AUTHENTICATION command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = session["language"] or "en" + if "language" in session.keys(): + command.params["locale"] = session["language"] data = send_pb_command(command.SerializeToString()) result = proto.PbResult() From c5ed2b5b67ebe60c5a17847d75f1e194e2b33c0b Mon Sep 17 00:00:00 2001 From: cvictor Date: Fri, 31 Dec 2021 02:34:41 +0100 Subject: [PATCH 11/18] Cvictor update german (#585) * Added german translations * German translations edited header * Fixed typo Co-authored-by: Uwe Seimet --- .../translations/de/LC_MESSAGES/messages.po | 1201 +++++++++++++++++ 1 file changed, 1201 insertions(+) create mode 100644 src/web/translations/de/LC_MESSAGES/messages.po diff --git a/src/web/translations/de/LC_MESSAGES/messages.po b/src/web/translations/de/LC_MESSAGES/messages.po new file mode 100644 index 00000000..7e52a755 --- /dev/null +++ b/src/web/translations/de/LC_MESSAGES/messages.po @@ -0,0 +1,1201 @@ +# German translations for RaSCSI. +# Copyright (C) 2021 akuker +# This file is distributed under the same license as the RaSCSI project. +# Christian Victor , 2021. +# +msgid "" +msgstr "" +"Project-Id-Version: RaSCSI 68kmla Edition\n" +"Report-Msgid-Bugs-To: https://github.com/akuker/RASCSI/issues\n" +"POT-Creation-Date: 2021-12-29 08:38+0100\n" +"PO-Revision-Date: 2021-12-30 22:11+0100\n" +"Last-Translator: Christian Victor \n" +"Language-Team: de N/A\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Generated-By: Babel 2.9.1\n" +"X-Generator: Poedit 3.0.1\n" + +#: file_cmds.py:186 +#, python-format +msgid "File deleted: %(file_path)s" +msgstr "Datei %(file_path)s gelöscht" + +#: file_cmds.py:190 +#, python-format +msgid "File to delete not found: %(file_path)s" +msgstr "Datei zum Löschen nicht gefunden: %(file_path)s" + +#: file_cmds.py:203 +#, python-format +msgid "File moved to: %(target_path)s" +msgstr "Datei nach: %(target_path)s verschoben" + +#: file_cmds.py:207 +#, python-format +msgid "Unable to move file to: %(target_path)s" +msgstr "Unfähig Datei nach: %(target_path)s zu verschieben" + +#: file_cmds.py:323 +#, python-format +msgid "Created CD-ROM ISO image with arguments \"%(value)s\"" +msgstr "CD-ROM ISO-Image mit Parametern „%(value)s“ erzeugt" + +#: file_cmds.py:354 +#, python-format +msgid "%(file_name)s downloaded to %(save_dir)s" +msgstr "%(file_name)s heruntergeladen nach %(save_dir)s" + +#: file_cmds.py:399 +#, python-format +msgid "Saved configuration file to %(file_name)s" +msgstr "Konfigurationsdatei nach %(file_name)s gespeichert" + +#: file_cmds.py:410 +#, python-format +msgid "Could not write to file: %(file_name)s" +msgstr "Konnte nicht nach Datei %(file_name)s schreiben" + +#: file_cmds.py:468 +msgid "Invalid configuration file format" +msgstr "Ungültiges Konfigurationsdateiformat" + +#: file_cmds.py:471 +#, python-format +msgid "Loaded configurations from: %(file_name)s" +msgstr "Konfigurationen von %(file_name)s geladen" + +#: file_cmds.py:480 +#, python-format +msgid "Could not read configuration file: %(file_name)s" +msgstr "Konnte die Konfigurationsdatei %(file_name)s nicht lesen" + +#: file_cmds.py:497 +#, python-format +msgid "Created properties file: %(file_path)s" +msgstr "Eigenschaftendatei %(file_path)s erzeugt" + +#: file_cmds.py:508 +#, python-format +msgid "Could not write to properties file: %(file_path)s" +msgstr "Konnte Eigenschaftendatei %(file_path)s nicht schreiben" + +#: file_cmds.py:524 +#, python-format +msgid "Read properties from file: %(file_path)s" +msgstr "Eigenschaften von Datei %(file_path)s gelesen" + +#: file_cmds.py:534 +#, python-format +msgid "Could not read properties from file: %(file_path)s" +msgstr "Konnte Eigenschaften von Datei %(file_path)s nicht lesen" + +#: pi_cmds.py:179 +msgid "You must log in to use this function" +msgstr "Du musst dich einloggen um diese Funktion zu nutzen" + +#: ractl_cmds.py:206 +#, python-format +msgid "" +"Cannot insert an image for %(device_type)s into a %(current_device_type)s device" +msgstr "" +"Kann kein Image für %(device_type)s in ein %(current_device_type)s Gerät " +"einlegen" + +#: socket_cmds.py:39 +#, python-format +msgid "" +"The RaSCSI Web Interface failed to connect to RaSCSI at %(host)s:%(port)s with " +"error: %(error_msg)s. The RaSCSI process is not running or may have crashed." +msgstr "" +"Das RaSCSI-Webinterface konnte sich mit Fehler %(error_msg)s nicht mit RaSCSI " +"an %(host)s:%(port)s verbinden. Der RaSCSI-Prozess läuft nicht oder könnte " +"abgestürzt sein." + +#: socket_cmds.py:79 +msgid "" +"The RaSCSI Web Interface lost connection to RaSCSI. Please go back and try " +"again. If the issue persists, please report a bug." +msgstr "" +"Das RaSCSI-Webinterface hat die Verbindung verloren. Bitte gehe zurück und " +"versuche es nochmals. Wenn das Problem bestehen bleibt, melde bitte einen Bug." + +#: socket_cmds.py:95 +msgid "" +"The RaSCSI Web Interface did not get a valid response from RaSCSI. Please go " +"back and try again. If the issue persists, please report a bug." +msgstr "" +"Das RaSCSI-Webinterface hat keine gültige Antwort von RaSCSI bekommen. Bitte " +"gehe zurück und versuche es nochmals. Wenn das Problem bestehen bleibt, melde " +"bitte einen Bug." + +#: web.py:122 +msgid "" +"RaSCSI is password protected. Start the Web Interface with the --password " +"parameter." +msgstr "" +"RaSCSI ist passwortgeschützt. Starte das Webinterface mit dem —password " +"Parameter." + +#: web.py:223 +#, python-format +msgid "Could not read drive properties from %(properties_file)s" +msgstr "Konnte Laufwerkseigenschaften nicht von %(properties_file)s lesen" + +#: web.py:293 +#, python-format +msgid "You must log in with credentials for a user in the '%(group)s' group" +msgstr "" +"Du musst dich mit Zugangsdaten eines Users aus der Gruppe %(group)s’ einloggen" + +#: web.py:351 web.py:890 +#, python-format +msgid "Image file created: %(file_name)s" +msgstr "Imagedatei %(file_name)s erzeugt" + +#: web.py:476 +msgid "An error occurred when fetching logs." +msgstr "Beim Abrufen der Logs is ein Fehler aufgetreten." + +#: web.py:491 +#, python-format +msgid "Log level set to %(value)s" +msgstr "Log-Level auf %(value)s gesetzt" + +#: web.py:510 +#, python-format +msgid "Please follow the instructions at %(url)s" +msgstr "Bitte folge den Anweisungen auf %(url)s" + +#: web.py:514 +msgid "Configure IPv4 forwarding before using a wireless network device." +msgstr "" +"Konfiguriere IPv4-Forwarding bevor ein drahtloses Netzwerkgerät verwendet wird." + +#: web.py:518 +msgid "Configure NAT before using a wireless network device." +msgstr "Konfiguriere NAT bevor ein drahtloses Netzwerkgerät verwendet wird." + +#: web.py:523 web.py:527 +msgid "Configure the network bridge before using a wired network device." +msgstr "" +"Konfiguriere die Netzwerk-Bridge bevor ein drahtloses Netzwerkgerät verwendet " +"wird." + +#: web.py:540 +#, python-format +msgid "Attached DaynaPORT to SCSI ID %(id_number)s" +msgstr "DaynaPORT mit SCSI-ID %(id_number)s verbunden" + +#: web.py:588 +#, python-format +msgid "Attached %(file_name)s to SCSI ID %(id_number)s LUN %(unit_number)s" +msgstr "%(file_name)s mit SCSI-ID %(id_number)s LUN %(unit_number)s verbunden" + +#: web.py:591 +#, python-format +msgid "" +"The image file size %(file_size)s bytes is not a multiple of %(block_size)s. " +"RaSCSI will ignore the trailing data. The image may be corrupted, so proceed " +"with caution." +msgstr "" +"Die Imagedateigröße %(file_size)s Bytes ist kein Vielfaches von %(block_size)s. " +"RaSCSI wird die hinten überstehenden Daten ignorieren. Das Image könnte " +"beschädigt werden. Verfahre bitte mit Vorsicht." + +#: web.py:597 +#, python-format +msgid "" +"Failed to attach %(file_name)s to SCSI ID %(id_number)s LUN %(unit_number)s" +msgstr "" +"%(file_name)s konnte nicht an SCSI-ID %(id_number)s LUN %(unit_number)s " +"verbunden werden" + +#: web.py:611 +msgid "Detached all SCSI devices" +msgstr "Alle SCSI-Geräte getrennt" + +#: web.py:628 +#, python-format +msgid "Detached SCSI ID %(id_number)s LUN %(unit_number)s" +msgstr "SCSI-ID %(id_number)s LUN %(unit_number)s getrennt" + +#: web.py:632 +#, python-format +msgid "Failed to detach SCSI ID %(id_number)s LUN %(unit_number)s" +msgstr "SCSI-ID %(id_number)s LUN %(unit_number)s konnte nicht getrennt werden" + +#: web.py:649 +#, python-format +msgid "Ejected SCSI ID %(id_number)s LUN %(unit_number)s" +msgstr "SCSI-ID %(id_number)s LUN %(unit_number)s ausgeworfen" + +#: web.py:653 +#, python-format +msgid "Failed to eject SCSI ID %(id_number)s LUN %(unit_number)s" +msgstr "SCSI-ID %(id_number)s LUN %(unit_number)s getrennt" + +#: web.py:676 +msgid "DEVICE INFO" +msgstr "GERÄTEINFO" + +#: web.py:678 +#, python-format +msgid "SCSI ID: %(id_number)s" +msgstr "SCSI-ID: %(id_number)s" + +#: web.py:679 +#, python-format +msgid "LUN: %(unit_number)s" +msgstr "LUN: %(unit_number)s" + +#: web.py:680 +#, python-format +msgid "Type: %(device_type)s" +msgstr "Typ: %(device_type)s" + +#: web.py:681 +#, python-format +msgid "Status: %(device_status)s" +msgstr "Status: %(device_status)s" + +#: web.py:682 +#, python-format +msgid "File: %(image_file)s" +msgstr "Datei: %(image_file)s" + +#: web.py:683 +#, python-format +msgid "Parameters: %(value)s" +msgstr "Parameter: %(value)s" + +#: web.py:684 +#, python-format +msgid "Vendor: %(value)s" +msgstr "Anbieter: %(value)s" + +#: web.py:685 +#, python-format +msgid "Product: %(value)s" +msgstr "Produkt: %(value)s" + +#: web.py:686 +#, python-format +msgid "Revision: %(revision_number)s" +msgstr "Revision: %(revision_number)s" + +#: web.py:687 +#, python-format +msgid "Block Size: %(value)s bytes" +msgstr "Blockgröße: %(value)s Bytes" + +#: web.py:688 +#, python-format +msgid "Image Size: %(value)s bytes" +msgstr "Imagegröße: %(value)s Bytes" + +#: web.py:707 +#, python-format +msgid "Reserved SCSI ID %(id_number)s" +msgstr "Reservierte SCSI-ID %(id_number)s" + +#: web.py:710 +#, python-format +msgid "Failed to reserve SCSI ID %(id_number)s" +msgstr "Konnte SCSI-ID %(id_number)s nicht reservieren" + +#: web.py:726 +#, python-format +msgid "Released the reservation for SCSI ID %(id_number)s" +msgstr "Reservierung für SCSI-ID %(id_number)s entfernt" + +#: web.py:729 +#, python-format +msgid "Failed to release the reservation for SCSI ID %(id_number)s" +msgstr "Konnte die Reservierung für SCSI-ID %(id_number)s nicht entfernen" + +#: web.py:767 +#, python-format +msgid "Saved image as: %(file_name)s" +msgstr "Image gespeichert als: %(file_name)s" + +#: web.py:769 +#, python-format +msgid "Failed to create CD-ROM image from %(url)s" +msgstr "Erstellung des CD-ROM-Images von %(url)s fehlgeschlagen" + +#: web.py:775 +#, python-format +msgid "Attached to SCSI ID %(id_number)s" +msgstr "An SCSI-ID %(id_number)s angebunden" + +#: web.py:778 +#, python-format +msgid "" +"Failed to attach image to SCSI ID %(id_number)s. Try attaching it manually." +msgstr "" +"Anbindung des Images an SCSI-ID %(id_number)s gescheitert. Versuche es manuell." + +#: web.py:797 web.py:814 +#, python-format +msgid "Failed to download file from %(url)s" +msgstr "Datei herunterladen von %(url)s gescheitert" + +#: web.py:845 +msgid "The file already exists!" +msgstr "Die Datei existiert bereits!" + +#: web.py:853 +msgid "Unable to write the file to disk!" +msgstr "Kann die Datei nicht auf die Disk schreiben!" + +#: web.py:868 +msgid "Transferred file corrupted!" +msgstr "Übertragene Datei beschädigt!" + +#: web.py:874 +msgid "File upload successful!" +msgstr "Dateiupload erfolgreich!" + +#: web.py:917 +#, python-format +msgid "Image file deleted: %(file_name)s" +msgstr "Imagedatei %(file_name)s gelöscht" + +#: web.py:947 +#, python-format +msgid "Image file renamed to: %(file_name)s" +msgstr "Imagedatei in %(file_name)s umbenannt" + +#: web.py:984 +msgid "Aborted unzip: File(s) with the same name already exists." +msgstr "Unzip abgebrochen. Datei(en) mit dem gleichen Namen existieren bereits." + +#: web.py:986 +msgid "Unzipped the following files:" +msgstr "Folgende Dateien wurden entzipt:" + +#: web.py:990 +#, python-format +msgid "Properties file(s) have been moved to %(directory)s" +msgstr "Eigenschaftsdateien wurden nach %(directory)s verschoben" + +#: web.py:993 +#, python-format +msgid "Failed to unzip %(zip_file)s" +msgstr "%(zip_file)s konnte nicht anpackt werden" + +#: web.py:1007 +#, python-format +msgid "Changed Web Interface language to %(locale)s" +msgstr "Webinterface-Sprache auf %(locale)s geändert" + +#: templates/base.html:4 +msgid "RaSCSI Control Page" +msgstr "RaSCSI Kontrollseite" + +#: templates/base.html:29 +msgid "" +" This process may take a while, and will continue in the background if you " +"navigate away from this page." +msgstr "" +" Dieser Prozess dauert möglicherweise länger und wird im Hintergrund " +"fortgesetzt wenn Du von dieser Seite wegnavigierst." + +#: templates/base.html:34 +msgid "" +" The Web Interface will become unresponsive momentarily. Reload this page after " +"the Pi has started up again." +msgstr "" +" Das Webinterface wird gleich nicht mehr reagieren. Lade diese Seite erneut " +"wenn der Pi wieder hochgefahren ist." + +#: templates/base.html:48 +#, python-format +msgid "Logged in as %(username)s" +msgstr "Als %(username)s angemeldet" + +#: templates/base.html:48 +msgid "Log Out" +msgstr "Abmelden" + +#: templates/base.html:52 +msgid "Log In to Use Web Interface" +msgstr "Melde dich an um das Webinterface zu benutzen" + +#: templates/base.html:53 +msgid "Username" +msgstr "Username" + +#: templates/base.html:54 +msgid "Password" +msgstr "Passwort" + +#: templates/base.html:60 +msgid "Web Interface Authentication Disabled" +msgstr "Webinterface-Authentifizierung deaktiviert" + +#: templates/base.html:60 +#, python-format +msgid "See Wiki for more information" +msgstr "Siehe Wiki für mehr Informationen" + +#: templates/base.html:87 +msgid "RaSCSI version: " +msgstr "RaSCSI-Version: " + +#: templates/base.html:88 +msgid "Pi environment: " +msgstr "Pi-Umgebung " + +#: templates/drives.html:4 templates/drives.html:139 +msgid "Cancel" +msgstr "Abbrechen" + +#: templates/drives.html:5 +msgid "Disclaimer" +msgstr "Haftungsausschluss" + +#: templates/drives.html:6 +#, python-format +msgid "" +"These device profiles are provided as-is with no guarantee to work equally to " +"the actual physical device they are named after. You may need to provide " +"appropirate device drivers and/or configuration parameters for them to function " +"properly. If you would like to see data modified, or have additional devices to " +"add to the list, please raise an issue ticket at GitHub." +msgstr "" +"Diese Geräteprofile werden im Ist-Zuastand und ohne Garantie genauso wie das " +"tatsächliche physische Gerät nach dem sie benannt sind zu funktionieren " +"angeboten. Möglicherweise musst Du passende Gerätetreiber und/oder " +"Konfigurationsparameter liefern damit sie ordentlich funktionieren. Möchtest Du " +"gerne Daten geändert haben oder hast zusätzliche Geräte die der Liste " +"hinzugefügt werden sollen dann eröffne bitte ein Ticket auf GitHub." + +#: templates/drives.html:7 +msgid "Hard Drives" +msgstr "Festplatten" + +#: templates/drives.html:12 templates/drives.html:56 templates/drives.html:102 +msgid "Name" +msgstr "Name" + +#: templates/drives.html:13 templates/drives.html:57 templates/drives.html:103 +msgid "Size (MB)" +msgstr "Größe (MB)" + +#: templates/drives.html:14 templates/drives.html:58 templates/drives.html:104 +msgid "Description" +msgstr "Beschreibung" + +#: templates/drives.html:15 templates/drives.html:59 templates/drives.html:105 +msgid "Ref." +msgstr "Ref." + +#: templates/drives.html:16 templates/drives.html:60 templates/drives.html:106 +msgid "Action" +msgstr "Aktion" + +#: templates/drives.html:25 templates/drives.html:69 templates/drives.html:115 +msgid "Link" +msgstr "Link" + +#: templates/drives.html:39 templates/drives.html:129 +msgid "Save as:" +msgstr "Speichern als:" + +#: templates/drives.html:41 templates/drives.html:88 templates/drives.html:131 +#: templates/index.html:549 +msgid "Create" +msgstr "Erzeuge" + +#: templates/drives.html:51 +msgid "CD-ROM Drives" +msgstr "CD-ROM-Laifwerke" + +#: templates/drives.html:52 +msgid "" +"This will create a properties file for the given CD-ROM image. No new image " +"file will be created." +msgstr "" +"Dies wird eine Eigenschaften-Datei für das ausgewählte CD-ROM-Image erstellen. " +"Es wird keine neue Image-Datei erzeugt." + +#: templates/drives.html:80 +msgid "Create for:" +msgstr "Erstelle für:" + +#: templates/drives.html:98 +msgid "Removable Drives" +msgstr "Wechsellaufwerke" + +#: templates/drives.html:138 templates/index.html:295 +#, python-format +msgid "%(disk_space)s MB disk space remaining on the Pi" +msgstr "%(disk_space)s MB Festplattenplatz auf dem Pi übrig" + +#: templates/index.html:6 +msgid "Current RaSCSI Configuration" +msgstr "Aktuelle RaSCSI-Konfiguration" + +#: templates/index.html:9 +msgid "Displays the currently attached devices for each available SCSI ID." +msgstr "Zeigt die aktuell angeschlossenen Geräte für jede verfügbare SCSI-ID an." + +#: templates/index.html:10 +#, python-format +msgid "" +"Save and load device configurations, stored as json files in " +"%(config_dir)s" +msgstr "" +"Speichere und lade Gerätekonfigurationen die als JSON-Dateien in " +"%(config_dir)s abgelegt sind" + +#: templates/index.html:11 +msgid "" +"To have a particular device configuration load when RaSCSI starts, save it as " +"default." +msgstr "" +"Um eine bestimmte Gerätekonfiguration zu laden wenn RaSCSI startet, speichere " +"sie als default." + +#: templates/index.html:25 +msgid "No saved configurations" +msgstr "Keine gespeicherten Konfigurationen" + +#: templates/index.html:29 +msgid "Load" +msgstr "Laden" + +#: templates/index.html:29 +msgid "Detach all current device and Load configuration?" +msgstr "Alle derzeitigen Geräte trennen und Konfiguration laden?" + +#: templates/index.html:30 templates/index.html:287 +msgid "Delete" +msgstr "Löschen" + +#: templates/index.html:30 +msgid "Delete configuration file?" +msgstr "Konfigurationsdatei löschen?" + +#: templates/index.html:35 +msgid "Save" +msgstr "Speichern" + +#: templates/index.html:41 templates/index.html:252 +msgid "ID" +msgstr "ID" + +#: templates/index.html:43 templates/index.html:260 +msgid "LUN" +msgstr "LUN" + +#: templates/index.html:45 templates/index.html:268 +msgid "Type" +msgstr "Typ" + +#: templates/index.html:46 +msgid "Status" +msgstr "Status" + +#: templates/index.html:47 templates/index.html:166 +msgid "File" +msgstr "Datei" + +#: templates/index.html:48 +msgid "Product" +msgstr "Produkt" + +#: templates/index.html:49 templates/index.html:168 +msgid "Actions" +msgstr "Aktionen" + +#: templates/index.html:84 templates/index.html:277 templates/index.html:337 +msgid "Attach" +msgstr "Verbinden" + +#: templates/index.html:98 +msgid "Eject Disk? WARNING: On Mac OS, eject the Disk in the Finder instead!" +msgstr "Disk auswerfen? WARNUNG: Auf Mac OS Disk zuerst im Finder auswerfen!" + +#: templates/index.html:101 +msgid "Eject" +msgstr "Auswerfen" + +#: templates/index.html:104 +msgid "Detach Device?" +msgstr "Gerät trennen?" + +#: templates/index.html:107 +msgid "Detach" +msgstr "Trennen" + +#: templates/index.html:113 +msgid "Info" +msgstr "Info" + +#: templates/index.html:116 +msgid "Enter a memo for this reservation" +msgstr "Gib ein memo für diese Reservierung ein" + +#: templates/index.html:119 +msgid "Reserve" +msgstr "Reservieren" + +#: templates/index.html:129 +msgid "Reserved ID" +msgstr "Reservierte ID" + +#: templates/index.html:135 +msgid "Unreserve" +msgstr "Freigeben" + +#: templates/index.html:144 +msgid "Detach all SCSI Devices?" +msgstr "Alle SCSI-Geräte trennen?" + +#: templates/index.html:145 +msgid "Detach All Devices" +msgstr "Alle Geräte trennen" + +#: templates/index.html:152 +msgid "Image File Management" +msgstr "Image-Dateiverwaltung" + +#: templates/index.html:155 +#, python-format +msgid "" +"Manage image files in the active RaSCSI image directory: %(directory)s " +"with a scan depth of %(scan_depth)s." +msgstr "" +"Verwalte Imagedateien im aktiven RaSCSI Imageverzeichnis %(directory)s " +"mit einer Scantiefe von %(scan_depth)s." + +#: templates/index.html:156 +#, python-format +msgid "" +"Select a valid SCSI ID and LUN to attach to. Unless you " +"know what you're doing, always use LUN 0." +msgstr "" +"Wähle eine gültige SCSI-ID und LUN zum Verbinden. Wenn Du " +"nicht sicher bist dann benutze immer LUN 0." + +#: templates/index.html:158 +msgid "" +"If RaSCSI was unable to detect the device type associated with the image, you " +"can choose the type from the dropdown." +msgstr "" +"Wenn RaSCSI den zugehörigen Gerätetyp des Images nicht erkennen konnte kannst " +"Du ihn im Dropdown auswählen." + +#: templates/index.html:159 +msgid "" +"Types: SAHD = SASI HDD | SCHD = SCSI HDD | SCRM = Removable | SCMO = Magneto-" +"Optical | SCCD = CD-ROM | SCBR = Host Bridge | SCDP = DaynaPORT" +msgstr "" +"Typen: SAHD = SASI HDD | SCHD = SCSI HDD | SCRM = Wechselmedium | SCMO = " +"Magneto-Optisch | SCCD = CD-ROM | SCBR = Host-Bridge | SCDP = DaynaPORT" + +#: templates/index.html:167 +msgid "Size" +msgstr "Größe" + +#: templates/index.html:184 +msgid "Properties File" +msgstr "Eigenschaftendatei" + +#: templates/index.html:204 templates/index.html:218 +msgid "Unzip" +msgstr "Entzippen" + +#: templates/index.html:204 templates/index.html:218 +msgid "Unzipping a single file..." +msgstr "Entzippe eine einzelne Datei…" + +#: templates/index.html:233 templates/index.html:390 templates/index.html:548 +msgid "MB" +msgstr "MB" + +#: templates/index.html:239 +msgid "Attached!" +msgstr "Verbunden!" + +#: templates/index.html:246 +msgid "Unzip All" +msgstr "Alle entzippen" + +#: templates/index.html:246 +msgid "Unzipping all files..." +msgstr "Entzippe alle Dateien…" + +#: templates/index.html:280 +#, python-format +msgid "Enter new file name for: %(file_name)s" +msgstr "Gib einen neuen Dateinamen für %(file_name)s ein" + +#: templates/index.html:283 +msgid "Rename" +msgstr "Umbenennen" + +#: templates/index.html:285 +#, python-format +msgid "Delete file: %(file_name)s?" +msgstr "Datei %(file_name)s löschen?" + +#: templates/index.html:301 +msgid "Attach Ethernet Adapter" +msgstr "Verbinde Ethernet-Adapter" + +#: templates/index.html:304 +#, python-format +msgid "" +"Emulates a SCSI DaynaPORT Ethernet Adapter. Host drivers " +"and configuration required." +msgstr "" +"Emuliert einen SCSI DaynaPORT Ethernet-Adapter. Host-Treiber " +"und Konfiguration notwendig." + +#: templates/index.html:306 +msgid "" +"If you have a DHCP setup, choose only the interface you have configured the " +"bridge with. You can ignore the Static IP fields when attaching." +msgstr "" +"Wenn Du ein DHCP-Setup hast, wähle nur die Schnittstelle die Du für die Bridge " +"konfiguriert hast. Du kannst die statische IP beim Verbinden ignorieren." + +#: templates/index.html:307 +#, python-format +msgid "" +"Configure the network bridge by running easyinstall.sh, or follow the manual steps in the wiki." +msgstr "" +"Konfiguriere die Netzwerk-Bridge in dem Du easyinstall.sh laufen lässt, oder " +"folge den manuellen Schritten im Wiki." + +#: templates/index.html:310 +msgid "" +"The rascsi_bridge interface is active and ready to be used by " +"DaynaPORT!" +msgstr "" +"Die rascsi_bridge-Schnittstelle ist aktiv und bereit von DaynaPORT " +"verwendet zu werden!" + +#: templates/index.html:318 +msgid "Interface:" +msgstr "Schnittstelle:" + +#: templates/index.html:326 +msgid "Static IP (optional):" +msgstr "Statische IP (optional):" + +#: templates/index.html:329 templates/index.html:473 +msgid "SCSI ID:" +msgstr "SCSI-ID:" + +#: templates/index.html:343 +#, python-format +msgid "Macproxy is running at %(ip_addr)s (default port 5000)" +msgstr "Macproxy läuft auf %(ip_addr)s (Default-Port 5000)" + +#: templates/index.html:345 +#, python-format +msgid "" +"Install Macproxy to browse the Web with any vintage " +"browser. It's not just for Macs!" +msgstr "" +"Installiere Macproxy um das Web mit jedem Vintage-Browser " +"zu nutzen. Es is nicht nur für Macs!" + +#: templates/index.html:351 +msgid "Upload File" +msgstr "Datei hochladen" + +#: templates/index.html:354 +#, python-format +msgid "" +"Uploads file to %(directory)s. The largest file size accepted is " +"%(max_file_size)s MB." +msgstr "" +"Lädt eine Datei nach %(directory)s hoch. Die maximale Dateigröße ist " +"%(max_file_size)s MB." + +#: templates/index.html:355 +msgid "" +"For unrecognized file types, try renaming hard drive images to '.hds', CD-ROM " +"images to '.iso', and removable drive images to '.hdr' before uploading." +msgstr "" +"Für unerkannte Dateitypen, versuche vor dem Upload Festplattenimages in ‚.hds‘, " +"CD-ROM-Images in ‚.iso‘, und Wechseldatenträger-Images in ‚.hdr‘ umzubenennen." + +#: templates/index.html:356 +#, python-format +msgid "Recognized file types: %(valid_file_suffix)s" +msgstr "Erkannte Dateitypen: %(valid_file_suffix)s" + +#: templates/index.html:376 +msgid "Drop files here to upload" +msgstr "Dateien zum Hochladen hier ablegen" + +#: templates/index.html:377 +msgid "Your browser does not support drag'n'drop file uploads." +msgstr "Dein Browser unterstützt keine Drag’n’Drop Dateiuploads." + +#: templates/index.html:378 +msgid "" +"Please use the fallback form below to upload your files like in the olden days." +msgstr "" +"Bitte benutze das Formular unten als Ausweichlösung wie zur guten alten Zeit." + +#: templates/index.html:379 +msgid "File is too big: {{filesize}}MB. Max filesize: {{maxFilesize}}MB." +msgstr "Datei ist zu groß: {{filesize}}MB. Max. Dateigröße: {{maxFilesize}}MB." + +#: templates/index.html:380 +msgid "You can't upload files of this type." +msgstr "Du kannst diesen Dateityp nicht hochladen." + +#: templates/index.html:381 +msgid "Server responded with code: {{statusCode}}" +msgstr "Server hat mit Code {{statusCode}} geantwortet" + +#: templates/index.html:382 +msgid "Cancel upload" +msgstr "Hochladen abbrechen" + +#: templates/index.html:383 +msgid "Upload canceled." +msgstr "Hochladen abgebrochen." + +#: templates/index.html:384 +msgid "Are you sure you want to cancel this upload?" +msgstr "Bist Du sicher dass Du das Hochladen abbrechen möchtest?" + +#: templates/index.html:385 +msgid "Remove file" +msgstr "Datei entfernen" + +#: templates/index.html:386 +msgid "You can not upload any more files." +msgstr "Du kannst keine Dateien mehr hochladen." + +#: templates/index.html:388 +msgid "TB" +msgstr "TB" + +#: templates/index.html:389 +msgid "GB" +msgstr "GB" + +#: templates/index.html:391 +msgid "KB" +msgstr "KB" + +#: templates/index.html:392 +msgid "b" +msgstr "b" + +#: templates/index.html:401 +msgid "Download File to Images" +msgstr "Datei nach Images herunterladen" + +#: templates/index.html:404 +#, python-format +msgid "Given a URL, download that file to the %(directory)s directory." +msgstr "" +"Mit URL, lade die Datei in das Verzeichnis %(directory)s herunter." + +#: templates/index.html:412 templates/index.html:438 templates/index.html:482 +msgid "URL:" +msgstr "URL:" + +#: templates/index.html:413 templates/index.html:439 templates/index.html:483 +msgid "URL" +msgstr "URL" + +#: templates/index.html:414 templates/index.html:440 +msgid "Download" +msgstr "Herunterladen" + +#: templates/index.html:414 +msgid "Downloading File to Images..." +msgstr "Lade Datei nach Images herunter…" + +#: templates/index.html:424 +msgid "Download File to AppleShare" +msgstr "Lade Datei nach AppleShare herunter" + +#: templates/index.html:427 +#, python-format +msgid "" +"Given a URL, download that file to the %(directory)s directory and " +"share it over AFP." +msgstr "" +"Mit URL, lade die Datei in das Verzeichnis %(directory)s herunter und " +"teile sie über AFP." + +#: templates/index.html:428 +msgid "Manage the files you download here through AppleShare on your vintage Mac." +msgstr "" +"Verwalte die Dateien die Du hier herunterlädst mit AppleShare auf deinem " +"Vintage-Mac." + +#: templates/index.html:429 +#, python-format +msgid "" +"Requires Netatalk to be installed and configured " +"correctly for your network." +msgstr "" +"Benötigt Netatalk korrekt in deinem Netzwerk installiert " +"und konfiguriert." + +#: templates/index.html:440 +msgid "Downloading File to AppleShare..." +msgstr "Lade Datei nach AppleShare herunter…" + +#: templates/index.html:447 +msgid "The AppleShare server is running. No active connections." +msgstr "Der AppleShare-Server läuft. Keine aktiven Verbindungen." + +#: templates/index.html:449 +#, python-format +msgid "%(value)d active AFP connection" +msgstr "%(value)d aktive AFP-Verbindung" + +#: templates/index.html:451 +#, python-format +msgid "%(value)d active AFP connections" +msgstr "%(value)d aktive AFP-Verbindungen" + +#: templates/index.html:454 +#, python-format +msgid "" +"Install Netatalk to use the AppleShare File Server." +msgstr "" +"Installiere Netatalk um den AppleShare-Fileserver zu " +"benutzen." + +#: templates/index.html:461 +msgid "Download File and Create CD-ROM image" +msgstr "Lade Datei herunter und erzeuge CD-ROM-Image" + +#: templates/index.html:464 +msgid "" +"Create an ISO file system CD-ROM image with the downloaded file, and mount it " +"on the given SCSI ID." +msgstr "" +"Erzeuge ein ISO-Dateisystem CD-ROM-Image mit der heruntergeladenen Datei und " +"verbinde es mit der angegebenen SCSI-ID." + +#: templates/index.html:465 +msgid "HFS is for Mac OS, Joliet for Windows, and Rock Ridge for POSIX." +msgstr "HFS ist für Mac OS, Joliet für Windows, und Rock Ridge für POSIX." + +#: templates/index.html:466 +#, python-format +msgid "On Mac OS, a compatible CD-ROM driver is required." +msgstr "" +"Auf Mac OS wird ein kompatibler CD-ROM-Treiber benötigt." + +#: templates/index.html:467 +msgid "" +"If the downloaded file is a zip archive, we will attempt to unzip it and store " +"the resulting files." +msgstr "" +"Die heruntergeladene Datei ist ein Zip-Archiv. Wir versuchen es zu entziehen " +"und die enthaltenen Dateien abzulegen." + +#: templates/index.html:484 templates/index.html:529 +msgid "Type:" +msgstr "Typ:" + +#: templates/index.html:505 +msgid "Download and Mount CD-ROM image" +msgstr "Lade CD-ROM-Image herunter und verbinde es" + +#: templates/index.html:505 +msgid "Downloading File and generating CD-ROM image..." +msgstr "Lader Datei herunter und erzeuge CD-ROM-Image…" + +#: templates/index.html:515 +msgid "Create Empty Disk Image File" +msgstr "Erzeuge leere Diskimage-Datei" + +#: templates/index.html:518 +msgid "The Generic image type is recommended for most computer platforms." +msgstr "" +"Der generische Imagetyp wird für die meisten Computerplattformen empfohlen." + +#: templates/index.html:519 +msgid "" +"APPLE GENUINE (.hda) and NEC GENUINE (.hdn) image types will make RaSCSI behave " +"as a particular drive type that are recognized by Mac and PC98 systems, " +"respectively." +msgstr "" +"APPLE GENUINE (.hda) und NEC GENUINE (.hdn) Imagetypen lassen RaSCSI sich wie " +"ein bestimmter Laufwerkstyp, der von jeweils Mac und PC98-Systemen erkannt " +"wird, verhalten." + +#: templates/index.html:520 +msgid "" +"SASI images should only be used on the original Sharp X68000, or other legacy " +"systems that utilize this pre-SCSI standard." +msgstr "" +"SASI-Images sollten nur auf dem originalen Sharp X68000 und anderen älteren " +"Systemen verwendet werden die diesen Prä-SCSI-Standard verwenden." + +#: templates/index.html:527 +msgid "File Name:" +msgstr "Dateiname:" + +#: templates/index.html:528 +msgid "File Name" +msgstr "Dateiname" + +#: templates/index.html:532 +msgid "SCSI Hard Disk image (Generic) [.hds]" +msgstr "SCSI Harddisk-Image (Generisch) [.hds]" + +#: templates/index.html:535 +msgid "SCSI Hard Disk image (APPLE GENUINE) [.hda]" +msgstr "SCSI Harddisk-Image (APPLE GENUINE) [.hda]" + +#: templates/index.html:538 +msgid "SCSI Hard Disk image (NEC GENUINE) [.hdn]" +msgstr "SCSI Harddisk-Image (NEC GENUINE) [.hdn]" + +#: templates/index.html:541 +msgid "SCSI Removable Media Disk image (Generic) [.hdr]" +msgstr "SCSI Wechselmedium-Image (Generisch) [.hdr]" + +#: templates/index.html:544 +msgid "SASI Hard Disk image (Legacy) [.hdf]" +msgstr "SASI Harddisk-Image (Legacy) [.hdf]" + +#: templates/index.html:547 +msgid "Size:" +msgstr "Größe:" + +#: templates/index.html:559 +msgid "Create Named Drive" +msgstr "Erzeuge benanntes Laufwerk" + +#: templates/index.html:562 +msgid "" +"Create pairs of images and properties files from a list of real-life drives." +msgstr "" +"Erzeuge Paare von Images und Eingenschaftsdateien aus einer Liste existierender " +"Laufwerke." + +#: templates/index.html:563 +msgid "" +"This will make RaSCSI use certain vendor strings and block sizes that may " +"improve compatibility with certain systems." +msgstr "" +"Dies bewirkt daß RaSCSI bestimmte Vendor-Strings und Blockgrößen verwendet die " +"die Kompatibilität mit bestimmten Systemen verbessern könnten." + +#: templates/index.html:566 +msgid "Create a named disk image that mimics real-life drives" +msgstr "Erzeuge ein benanntes Diskimage das existierende Laufwerke nachahmt" + +#: templates/index.html:572 +msgid "Logging" +msgstr "Logging" + +#: templates/index.html:575 +msgid "Fetch a certain number of lines of system logs with the given scope." +msgstr "" +"Hole eine gewisse Nummer Zeilen der System-Logs mit dem angegebenen Bereich." + +#: templates/index.html:582 +msgid "Log Lines:" +msgstr "Log-Zeilen:" + +#: templates/index.html:584 +msgid "Scope:" +msgstr "Bereich:" + +#: templates/index.html:596 +msgid "Show Logs" +msgstr "Zeige Logs" + +#: templates/index.html:606 +msgid "Server Log Level" +msgstr "Server Log Level" + +#: templates/index.html:609 +msgid "Change the log level of the RaSCSI backend process." +msgstr "Ändere den Log-Level des RaSCSI Hintergrundprozesses." + +#: templates/index.html:610 +msgid "The current dropdown selection indicates the active log level." +msgstr "Die aktuelle Dropdown-Auswahl markiert den aktiven Log-Level." + +#: templates/index.html:617 +msgid "Log Level:" +msgstr "Log-Level:" + +#: templates/index.html:625 +msgid "Set Log Level" +msgstr "Setze Log-Level" + +#: templates/index.html:635 +msgid "Language" +msgstr "Sprache" + +#: templates/index.html:638 +msgid "Change the Web Interface language." +msgstr "Ändere die Webinterface-Sprache." + +#: templates/index.html:645 +msgid "Language:" +msgstr "Sprache:" + +#: templates/index.html:653 +msgid "Change Language" +msgstr "Sprache ändern" + +#: templates/index.html:663 +msgid "Raspberry Pi Operations" +msgstr "Raspberry Pi Operationen" + +#: templates/index.html:666 +msgid "Reboot or shut down the Raspberry Pi that RaSCSI is running on." +msgstr "Den Raspberry Pi auf dem RaSCSI läuft neustarten oder herunterfahren." + +#: templates/index.html:667 +msgid "" +"IMPORTANT: Always shut down the Pi before turning off the power. Failing to do " +"so may lead to data loss." +msgstr "" +"WICHTIG: Fahre den Raspberry Pi immer herunter bevor Du die Stromversorgung " +"ausschaltest. Andernfalls kann es zu Datenverlust kommen." + +#: templates/index.html:673 +msgid "Reboot the Raspberry Pi?" +msgstr "Raspberry Pi neustarten?" + +#: templates/index.html:673 +msgid "Rebooting the Raspberry Pi..." +msgstr "Raspberry Pi wird neugestartet…" + +#: templates/index.html:674 +msgid "Reboot Raspberry Pi" +msgstr "Raspberry Pi Neustarten" + +#: templates/index.html:678 +msgid "Shut down the Raspberry Pi?" +msgstr "Raspberry Pi herunterfahren?" + +#: templates/index.html:678 +msgid "Shutting down the Raspberry Pi..." +msgstr "Raspberry Pi wird heruntergefahren…" + +#: templates/index.html:679 +msgid "Shut Down Raspberry Pi" +msgstr "Raspberry Pi herunterfahren" From 3af3f3e2de54a063164dc9e0cbc717bf5dc58572 Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Fri, 31 Dec 2021 20:07:17 -0800 Subject: [PATCH 12/18] Bump macproxy version to 21.12.3 (#586) --- easyinstall.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easyinstall.sh b/easyinstall.sh index 129d7663..cd54f90c 100755 --- a/easyinstall.sh +++ b/easyinstall.sh @@ -724,7 +724,7 @@ function installMacproxy { ( sudo apt-get update && sudo apt-get install python3 python3-venv --assume-yes ) Date: Sat, 1 Jan 2022 09:38:10 -0800 Subject: [PATCH 13/18] Improve token auth and oled monitor install scripts; better venv cleanup logic (#587) * Trigger installation of the oled monitor script after installing rascsi; break out the token auth code and clarify wordings. * Better logic for venv cleanup --- easyinstall.sh | 57 ++++++++++++++++++++++++++------------- src/oled_monitor/start.sh | 10 ++++--- src/web/start.sh | 10 ++++--- 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/easyinstall.sh b/easyinstall.sh index cd54f90c..9c13d940 100755 --- a/easyinstall.sh +++ b/easyinstall.sh @@ -142,12 +142,14 @@ function installRaScsiScreen() { SCREEN_HEIGHT="32" fi - echo "" - echo "Is RaSCSI using token-based authentication? [y/N]" - read -r REPLY - if [ "$REPLY" == "y" ] || [ "$REPLY" == "Y" ]; then - echo -n "Enter the passphrase that you configured: " - read -r TOKEN + if [ -z "$TOKEN" ]; then + echo "" + echo "Did you protect your RaSCSI installation with a token password? [y/N]" + read -r REPLY + if [ "$REPLY" == "y" ] || [ "$REPLY" == "Y" ]; then + echo -n "Enter the password that you configured with RaSCSI at the time of installation: " + read -r TOKEN + fi fi stopRaScsiScreen @@ -178,7 +180,7 @@ function installRaScsiScreen() { if [ ! -z "$TOKEN" ]; then sudo sed -i "8 i ExecStart=$BASE/src/oled_monitor/start.sh --rotation=$ROTATION --height=$SCREEN_HEIGHT --password=$TOKEN" "$SYSTEMD_PATH/monitor_rascsi.service" sudo chmod 600 "$SYSTEMD_PATH/monitor_rascsi.service" - echo "Granted access to the OLED Monitor with the token passphrase that you configured for RaSCSI." + echo "Granted access to the OLED Monitor with the password that you configured for RaSCSI." else sudo sed -i "8 i ExecStart=$BASE/src/oled_monitor/start.sh --rotation=$ROTATION --height=$SCREEN_HEIGHT" "$SYSTEMD_PATH/monitor_rascsi.service" fi @@ -277,14 +279,14 @@ function backupRaScsiService() { fi } -# Modifies and installs the rascsi service -function enableRaScsiService() { +# Offers the choice of enabling token-based authentication for RaSCSI +function configureTokenAuth() { echo "" - echo "Do you want to enable token-based access control for RaSCSI? [y/N]" + echo "Do you want to protect your RaSCSI installation with a password? [y/N]" read REPLY if [ "$REPLY" == "y" ] || [ "$REPLY" == "Y" ]; then - echo -n "Enter the passphrase that you want to use: " + echo -n "Enter the password that you want to use: " read -r TOKEN if [ -f "$HOME/.rascsi_secret" ]; then sudo rm "$HOME/.rascsi_secret" @@ -293,10 +295,17 @@ function enableRaScsiService() { echo "$TOKEN" > "$HOME/.rascsi_secret" sudo chown root:root "$HOME/.rascsi_secret" sudo chmod 600 "$HOME/.rascsi_secret" + echo "" + echo "Configured RaSCSI to use $HOME/.rascsi_secret for authentication. This file is readable by root only." + echo "Make note of your password: you will need it to use rasctl and other RaSCSI clients." + fi +} + +# Modifies and installs the rascsi service +function enableRaScsiService() { + if [ ! -z "$TOKEN" ]; then sudo sed -i "s@^ExecStart.*@& -F $VIRTUAL_DRIVER_PATH -P $HOME/.rascsi_secret@" "$SYSTEMD_PATH/rascsi.service" sudo chmod 600 "$SYSTEMD_PATH/rascsi.service" - echo "Configured to use $HOME/.rascsi_secret to secure RaSCSI. This file is readable by root only." - echo "Make note of your passphrase; you will need it to use rasctl, and other RaSCSI clients." else sudo sed -i "s@^ExecStart.*@& -F $VIRTUAL_DRIVER_PATH@" "$SYSTEMD_PATH/rascsi.service" fi @@ -318,7 +327,7 @@ function installWebInterfaceService() { if [ ! -z "$TOKEN" ]; then sudo sed -i "8 i ExecStart=$WEB_INSTALL_PATH/start.sh --password=$TOKEN" "$SYSTEMD_PATH/rascsi-web.service" sudo chmod 600 "$SYSTEMD_PATH/rascsi-web.service" - echo "Granted access to the Web Interface with the token passphrase that you configured for RaSCSI." + echo "Granted access to the Web Interface with the token password that you configured for RaSCSI." else sudo sed -i "8 i ExecStart=$WEB_INSTALL_PATH/start.sh" "$SYSTEMD_PATH/rascsi-web.service" fi @@ -784,11 +793,13 @@ function runChoice() { echo "- Install additional packages with apt-get" echo "- Add and modify systemd services" echo "- Modify and enable Apache2 and Nginx web services" - echo "- Create directories and change permissions" + echo "- Create files and directories" + echo "- Change permissions of files and directories" echo "- Modify user groups and permissions" echo "- Install binaries to /usr/local/bin" echo "- Install manpages to /usr/local/man" sudoCheck + configureTokenAuth stopOldWebInterface updateRaScsiGit createImagesDir @@ -800,9 +811,13 @@ function runChoice() { backupRaScsiService installRaScsi enableRaScsiService - startRaScsiScreen + if [ -f "$SYSTEMD_PATH/monitor_rascsi.service" ]; then + echo "Detected monitor_rascsi.service; will run the installation steps for the OLED monitor." + installRaScsiScreen + fi installRaScsiWebInterface installWebInterfaceService + showRaScsiScreenStatus showRaScsiStatus showRaScsiWebStatus notifyBackup @@ -813,11 +828,13 @@ function runChoice() { echo "This script will make the following changes to your system:" echo "- Install additional packages with apt-get" echo "- Add and modify systemd services" - echo "- Create directories and change permissions" + echo "- Create files ans directories" + echo "- Change permissions of files and directories" echo "- Modify user groups and permissions" echo "- Install binaries to /usr/local/bin" echo "- Install manpages to /usr/local/man" sudoCheck + configureTokenAuth updateRaScsiGit createImagesDir installPackages @@ -827,7 +844,11 @@ function runChoice() { backupRaScsiService installRaScsi enableRaScsiService - startRaScsiScreen + if [ -f "$SYSTEMD_PATH/monitor_rascsi.service" ]; then + echo "Detected monitor_rascsi.service; will run the installation steps for the OLED monitor." + installRaScsiScreen + fi + showRaScsiScreenStatus showRaScsiStatus notifyBackup echo "Installing / Updating RaSCSI Service (${CONNECT_TYPE:-FULLSPEC}) - Complete!" diff --git a/src/oled_monitor/start.sh b/src/oled_monitor/start.sh index dbf2a330..8a69a436 100755 --- a/src/oled_monitor/start.sh +++ b/src/oled_monitor/start.sh @@ -66,9 +66,13 @@ fi # Test for two known broken venv states if test -e venv; then GOOD_VENV=true - ! test -e venv/bin/activate && GOOD_VENV=false - pip3 list 1> /dev/null - test $? -eq 1 && GOOD_VENV=false + if ! test -e venv/bin/activate; then + GOOD_VENV=false + else + source venv/bin/activate + pip3 list 1> /dev/null + test $? -eq 1 && GOOD_VENV=false + fi if ! "$GOOD_VENV"; then echo "Deleting bad python venv" sudo rm -rf venv diff --git a/src/web/start.sh b/src/web/start.sh index 4a6e3d36..82e48839 100755 --- a/src/web/start.sh +++ b/src/web/start.sh @@ -34,9 +34,13 @@ fi # Test for two known broken venv states if test -e venv; then GOOD_VENV=true - ! test -e venv/bin/activate && GOOD_VENV=false - pip3 list 1> /dev/null - test $? -eq 1 && GOOD_VENV=false + if ! test -e venv/bin/activate; then + GOOD_VENV=false + else + source venv/bin/activate + pip3 list 1> /dev/null + test $? -eq 1 && GOOD_VENV=false + fi if ! "$GOOD_VENV"; then echo "Deleting bad python venv" sudo rm -rf venv From 3cc04a8dcf68f5cdacb05b27eeee600a9e83de47 Mon Sep 17 00:00:00 2001 From: RaSCSI User Date: Sat, 1 Jan 2022 20:48:30 +0000 Subject: [PATCH 14/18] Update revision for release --- src/raspberrypi/rascsi_version.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raspberrypi/rascsi_version.cpp b/src/raspberrypi/rascsi_version.cpp index b054c416..28822e53 100644 --- a/src/raspberrypi/rascsi_version.cpp +++ b/src/raspberrypi/rascsi_version.cpp @@ -14,7 +14,7 @@ // The following should be updated for each release const int rascsi_major_version = 21; // Last two digits of year const int rascsi_minor_version = 12; // Month -const int rascsi_patch_version = -1; // Patch number - increment for each update +const int rascsi_patch_version = 01; // Patch number - increment for each update static char rascsi_version_string[30]; // Allow for string up to "XX.XX.XXX" + null character + "development build" From 7e1ca6a7c99b2a0f0eaadee85a19f7f55ab0c357 Mon Sep 17 00:00:00 2001 From: RaSCSI User Date: Sat, 1 Jan 2022 20:49:50 +0000 Subject: [PATCH 15/18] Update version for next development version --- src/raspberrypi/rascsi_version.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raspberrypi/rascsi_version.cpp b/src/raspberrypi/rascsi_version.cpp index 28822e53..15eb4607 100644 --- a/src/raspberrypi/rascsi_version.cpp +++ b/src/raspberrypi/rascsi_version.cpp @@ -12,8 +12,8 @@ #include // The following should be updated for each release -const int rascsi_major_version = 21; // Last two digits of year -const int rascsi_minor_version = 12; // Month +const int rascsi_major_version = 22; // Last two digits of year +const int rascsi_minor_version = 01; // Month const int rascsi_patch_version = 01; // Patch number - increment for each update static char rascsi_version_string[30]; // Allow for string up to "XX.XX.XXX" + null character + "development build" From a240198123d5983d81aa689f77857f1de0f48f47 Mon Sep 17 00:00:00 2001 From: n4al Date: Tue, 4 Jan 2022 15:04:54 +0000 Subject: [PATCH 16/18] N4al/spanish localisation (#594) * Adding Spanish localisation to localizer.cpp * Addind Spanish localisation for the Web Interface --- src/raspberrypi/localizer.cpp | 25 +- src/web/settings.py | 2 +- .../translations/es/LC_MESSAGES/messages.po | 1214 +++++++++++++++++ 3 files changed, 1238 insertions(+), 3 deletions(-) create mode 100644 src/web/translations/es/LC_MESSAGES/messages.po diff --git a/src/raspberrypi/localizer.cpp b/src/raspberrypi/localizer.cpp index 7660c6de..cb6b4bd8 100644 --- a/src/raspberrypi/localizer.cpp +++ b/src/raspberrypi/localizer.cpp @@ -19,93 +19,114 @@ using namespace std; Localizer::Localizer() { // Supported locales, always lower case - supported_languages = { "en", "de", "sv", "fr" }; + supported_languages = { "en", "de", "sv", "fr", "es" }; // Positional string arguments are %1, %2, %3 Add(ERROR_AUTHENTICATION, "en", "Authentication failed"); Add(ERROR_AUTHENTICATION, "de", "Authentifizierung fehlgeschlagen"); Add(ERROR_AUTHENTICATION, "sv", "Autentisering misslyckades"); Add(ERROR_AUTHENTICATION, "fr", "Authentification éronnée"); + Add(ERROR_AUTHENTICATION, "es", "Fallo de autentificación"); Add(ERROR_OPERATION, "en", "Unknown operation"); Add(ERROR_OPERATION, "de", "Unbekannte Operation"); Add(ERROR_OPERATION, "sv", "Okänd operation"); Add(ERROR_OPERATION, "fr", "Opération inconnue"); + Add(ERROR_OPERATION, "es", "Operación desconocida"); Add(ERROR_LOG_LEVEL, "en", "Invalid log level %1"); Add(ERROR_LOG_LEVEL, "de", "Ungültiger Log-Level %1"); Add(ERROR_LOG_LEVEL, "sv", "Ogiltig loggnivå %1"); - Add(ERROR_LOG_LEVEL, "fr", "Niveau de journalisation invalide %1"); + Add(ERROR_LOG_LEVEL, "fr", "Niveau de journalisation invalide %1"); + Add(ERROR_LOG_LEVEL, "es", "Nivel de registro %1 no válido"); Add(ERROR_MISSING_DEVICE_ID, "en", "Missing device ID"); Add(ERROR_MISSING_DEVICE_ID, "de", "Fehlende Geräte-ID"); Add(ERROR_MISSING_DEVICE_ID, "sv", "Enhetens ID saknas"); Add(ERROR_MISSING_DEVICE_ID, "fr", "ID de périphérique manquante"); + Add(ERROR_MISSING_DEVICE_ID, "es", "Falta el ID del dispositivo"); Add(ERROR_MISSING_FILENAME, "en", "Missing filename"); Add(ERROR_MISSING_FILENAME, "de", "Fehlender Dateiname"); Add(ERROR_MISSING_FILENAME, "sv", "Filnamn saknas"); Add(ERROR_MISSING_FILENAME, "fr", "Nom de fichier manquant"); + Add(ERROR_MISSING_FILENAME, "es", "Falta el nombre del archivo"); Add(ERROR_IMAGE_IN_USE, "en", "Image file '%1' is already being used by ID %2, unit %3"); Add(ERROR_IMAGE_IN_USE, "de", "Image-Datei '%1' wird bereits von ID %2, Einheit %3 benutzt"); Add(ERROR_IMAGE_IN_USE, "sv", "Skivbildfilen '%1' används redan av ID %2, LUN %3"); Add(ERROR_IMAGE_IN_USE, "fr", "Le fichier d'image '%1' est déjà utilisé par l'ID %2, unité %3"); + Add(ERROR_IMAGE_IN_USE, "es", "El archivo de imagen '%1' ya está siendo utilizado por el ID %2, unidad %3"); Add(ERROR_RESERVED_ID, "en", "Device ID %1 is reserved"); Add(ERROR_RESERVED_ID, "de", "Geräte-ID %1 ist reserviert"); Add(ERROR_RESERVED_ID, "sv", "Enhets-ID %1 är reserverat"); Add(ERROR_RESERVED_ID, "fr", "ID de périphérique %1 réservée"); + Add(ERROR_RESERVED_ID, "es", "El ID de dispositivo %1 está reservado"); Add(ERROR_NON_EXISTING_DEVICE, "en", "Command for non-existing ID %1"); Add(ERROR_NON_EXISTING_DEVICE, "de", "Kommando für nicht existente ID %1"); Add(ERROR_NON_EXISTING_DEVICE, "sv", "Kommando för avsaknat ID %1"); Add(ERROR_NON_EXISTING_DEVICE, "fr", "Commande pour ID %1 non-existant"); + Add(ERROR_NON_EXISTING_DEVICE, "es", "Comando para ID %1 no existente"); Add(ERROR_NON_EXISTING_UNIT, "en", "Command for non-existing ID %1, unit %2"); Add(ERROR_NON_EXISTING_UNIT, "de", "Kommando für nicht existente ID %1, Einheit %2"); Add(ERROR_NON_EXISTING_UNIT, "sv", "Kommando för avsaknat ID %1, LUN %2"); Add(ERROR_NON_EXISTING_UNIT, "fr", "Command pour ID %1, unité %2 non-existant"); + Add(ERROR_NON_EXISTING_UNIT, "es", "Comando para ID %1 inexistente, unidad %2"); Add(ERROR_UNKNOWN_DEVICE_TYPE, "en", "Unknown device type %1"); Add(ERROR_UNKNOWN_DEVICE_TYPE, "de", "Unbekannter Gerätetyp %1"); Add(ERROR_UNKNOWN_DEVICE_TYPE, "sv", "Obekant enhetstyp: %1"); Add(ERROR_UNKNOWN_DEVICE_TYPE, "fr", "Type de périphérique inconnu %1"); + Add(ERROR_UNKNOWN_DEVICE_TYPE, "es", "Tipo de dispositivo desconocido %1"); Add(ERROR_MISSING_DEVICE_TYPE, "en", "Device type required for unknown extension of file '%1'"); Add(ERROR_MISSING_DEVICE_TYPE, "de", "Gerätetyp erforderlich für unbekannte Extension der Datei '%1'"); Add(ERROR_MISSING_DEVICE_TYPE, "sv", "Man måste ange enhetstyp för obekant filändelse '%1'"); Add(ERROR_MISSING_DEVICE_TYPE, "fr", "Type de périphérique requis pour extension inconnue du fichier '%1'"); + Add(ERROR_MISSING_DEVICE_TYPE, "es", "Tipo de dispositivo requerido para la extensión desconocida del archivo '%1'"); Add(ERROR_DUPLICATE_ID, "en", "Duplicate ID %1, unit %2"); Add(ERROR_DUPLICATE_ID, "de", "Doppelte ID %1, Einheit %2"); Add(ERROR_DUPLICATE_ID, "sv", "Duplikat ID %1, LUN %2"); Add(ERROR_DUPLICATE_ID, "fr", "ID %1, unité %2 dupliquée"); + Add(ERROR_DUPLICATE_ID, "es", "ID duplicado %1, unidad %2"); Add(ERROR_SASI_SCSI, "en", "SASI and SCSI can't be used at the same time"); Add(ERROR_SASI_SCSI, "de", "SASI und SCSI können nicht gleichzeitig verwendet werden"); Add(ERROR_SASI_SCSI, "sv", "SASI och SCSI kan ej användas samtidigt"); Add(ERROR_SASI_SCSI, "fr", "SASI et SCSI ne peuvent être utilisés en même temps"); + Add(ERROR_SASI_SCSI, "es", "SASI y SCSI no pueden utilizarse al mismo tiempo"); Add(ERROR_EJECT_REQUIRED, "en", "Existing medium must first be ejected"); Add(ERROR_EJECT_REQUIRED, "de", "Das vorhandene Medium muss erst ausgeworfen werden"); Add(ERROR_EJECT_REQUIRED, "sv", "Nuvarande skiva måste utmatas först"); Add(ERROR_EJECT_REQUIRED, "fr", "Media déjà existant doit d'abord être éjecté"); + Add(ERROR_EJECT_REQUIRED, "es", "El medio existente debe ser expulsado primero"); Add(ERROR_DEVICE_NAME_UPDATE, "en", "Once set the device name cannot be changed anymore"); Add(ERROR_DEVICE_NAME_UPDATE, "de", "Ein bereits gesetzter Gerätename kann nicht mehr geändert werden"); Add(ERROR_DEVICE_NAME_UPDATE, "sv", "Enhetsnamn kan ej ändras efter att ha fastställts en gång"); Add(ERROR_DEVICE_NAME_UPDATE, "fr", "Une fois défini, le nom de périphérique ne peut plus être changé"); + Add(ERROR_DEVICE_NAME_UPDATE, "es", "Una vez establecido el nombre del dispositivo ya no se puede cambiar"); Add(ERROR_SHUTDOWN_MODE_MISSING, "en", "Missing shutdown mode"); Add(ERROR_SHUTDOWN_MODE_MISSING, "de", "Fehlender Shutdown-Modus"); Add(ERROR_SHUTDOWN_MODE_MISSING, "sv", "Avstängningsläge saknas"); Add(ERROR_SHUTDOWN_MODE_MISSING, "fr", "Mode d'extinction manquant"); + Add(ERROR_SHUTDOWN_MODE_MISSING, "es", "Falta el modo de apagado"); Add(ERROR_SHUTDOWN_MODE_INVALID, "en", "Invalid shutdown mode '%1'"); Add(ERROR_SHUTDOWN_MODE_INVALID, "de", "Ungültiger Shutdown-Modus '%1'"); Add(ERROR_SHUTDOWN_MODE_INVALID, "sv", "Ogiltigt avstängsningsläge: '%1'"); Add(ERROR_SHUTDOWN_MODE_INVALID, "fr", "Mode d'extinction invalide '%1'"); + Add(ERROR_SHUTDOWN_MODE_INVALID, "es", "Modo de apagado inválido '%1'"); Add(ERROR_SHUTDOWN_PERMISSION, "en", "Missing root permission for shutdown or reboot"); Add(ERROR_SHUTDOWN_PERMISSION, "de", "Fehlende Root-Berechtigung für Shutdown oder Neustart"); Add(ERROR_SHUTDOWN_PERMISSION, "sv", "Root-rättigheter saknas för att kunna stänga av eller starta om systemet"); Add(ERROR_SHUTDOWN_PERMISSION, "fr", "Permissions root manquantes pour extinction ou redémarrage"); + Add(ERROR_SHUTDOWN_PERMISSION, "es", "Falta el permiso de root para el apagado o el reinicio"); Add(ERROR_FILE_OPEN, "en", "Invalid or non-existing file '%1': %2"); Add(ERROR_FILE_OPEN, "de", "Ungültige oder fehlende Datei '%1': %2"); Add(ERROR_FILE_OPEN, "sv", "Ogiltig eller saknad fil '%1': %2"); Add(ERROR_FILE_OPEN, "fr", "Fichier invalide ou non-existant '%1': %2"); + Add(ERROR_FILE_OPEN, "es", "Archivo inválido o inexistente '%1': %2"); Add(ERROR_BLOCK_SIZE, "en", "Invalid block size %1 bytes"); Add(ERROR_BLOCK_SIZE, "de", "Ungültige Blockgröße %1 Bytes"); Add(ERROR_BLOCK_SIZE, "sv", "Ogiltig blockstorlek: %1 byte"); Add(ERROR_BLOCK_SIZE, "fr", "Taille de bloc invalide %1 octets"); + Add(ERROR_BLOCK_SIZE, "es", "Tamaño de bloque inválido %1 bytes"); Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "en", "Block size for device type %1 is not configurable"); Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "de", "Blockgröße für Gerätetyp %1 ist nicht konfigurierbar"); Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "sv", "Enhetstypen %1 kan inte använda andra blockstorlekar"); Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "fr", "Taille de block pour le type de périphérique %1 non configurable"); + Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "es", "El tamaño del bloque para el tipo de dispositivo %1 no es configurable"); } void Localizer::Add(LocalizationKey key, const string& locale, const string& value) diff --git a/src/web/settings.py b/src/web/settings.py index 196c1756..1781e4ff 100644 --- a/src/web/settings.py +++ b/src/web/settings.py @@ -32,4 +32,4 @@ RESERVATIONS = ["" for x in range(0, 8)] AUTH_GROUP = "rascsi" # The language locales supported by RaSCSI -LANGUAGES = ["en", "de", "sv", "fr"] +LANGUAGES = ["en", "de", "sv", "fr", "es"] diff --git a/src/web/translations/es/LC_MESSAGES/messages.po b/src/web/translations/es/LC_MESSAGES/messages.po new file mode 100644 index 00000000..63448c78 --- /dev/null +++ b/src/web/translations/es/LC_MESSAGES/messages.po @@ -0,0 +1,1214 @@ +# Spanish translations for PROJECT. +# Copyright (C) 2022 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2022. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2022-01-04 09:01+0100\n" +"PO-Revision-Date: 2022-01-04 13:21+0100\n" +"Last-Translator: n4al \n" +"Language-Team: es \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Generated-By: Babel 2.9.1\n" +"X-Generator: Poedit 3.0.1\n" + +#: file_cmds.py:190 +#, python-format +msgid "File deleted: %(file_path)s" +msgstr "Archivo eliminado: %(file_path)s" + +#: file_cmds.py:194 +#, python-format +msgid "File to delete not found: %(file_path)s" +msgstr "No se ha encontrado el archivo a eliminar: %(file_path)s" + +#: file_cmds.py:207 +#, python-format +msgid "File moved to: %(target_path)s" +msgstr "El archivo se ha trasladado a: %(target_path)s" + +#: file_cmds.py:211 +#, python-format +msgid "Unable to move file to: %(target_path)s" +msgstr "No se puede mover el archivo a: %(target_path)s" + +#: file_cmds.py:327 +#, python-format +msgid "Created CD-ROM ISO image with arguments \"%(value)s\"" +msgstr "Creada la imagen ISO del CD-ROM con argumentos “%(value)s”" + +#: file_cmds.py:358 +#, python-format +msgid "%(file_name)s downloaded to %(save_dir)s" +msgstr "%(file_name)s descargado en %(save_dir)s" + +#: file_cmds.py:403 +#, python-format +msgid "Saved configuration file to %(file_name)s" +msgstr "Archivo de configuración guardado en %(file_name)s" + +#: file_cmds.py:414 +#, python-format +msgid "Could not write to file: %(file_name)s" +msgstr "No se pudo escribir en el archivo: %(file_name)s" + +#: file_cmds.py:472 +msgid "Invalid configuration file format" +msgstr "Formato de archivo de configuración no válido" + +#: file_cmds.py:475 +#, python-format +msgid "Loaded configurations from: %(file_name)s" +msgstr "Configuraciones cargadas de: %(file_name)s" + +#: file_cmds.py:484 +#, python-format +msgid "Could not read configuration file: %(file_name)s" +msgstr "No se ha podido leer el archivo de configuración: %(file_name)s" + +#: file_cmds.py:501 +#, python-format +msgid "Created properties file: %(file_path)s" +msgstr "Creado el archivo de propiedades: %(file_path)s" + +#: file_cmds.py:512 +#, python-format +msgid "Could not write to properties file: %(file_path)s" +msgstr "No se pudo escribir en el archivo de propiedades: %(file_path)s" + +#: file_cmds.py:528 +#, python-format +msgid "Read properties from file: %(file_path)s" +msgstr "Leer las propiedades del archivo: %(file_path)s" + +#: file_cmds.py:538 +#, python-format +msgid "Could not read properties from file: %(file_path)s" +msgstr "No se han podido leer las propiedades del archivo: %(file_path)s" + +#: pi_cmds.py:179 +msgid "You must log in to use this function" +msgstr "Debe conectarse para utilizar esta función" + +#: ractl_cmds.py:212 +#, python-format +msgid "" +"Cannot insert an image for %(device_type)s into a %(current_device_type)s device" +msgstr "" +"No se puede insertar una imagen para %(device_type)s en un dispositivo " +"%(current_device_type)s" + +#: socket_cmds.py:39 +#, python-format +msgid "" +"The RaSCSI Web Interface failed to connect to RaSCSI at %(host)s:%(port)s with " +"error: %(error_msg)s. The RaSCSI process is not running or may have crashed." +msgstr "" +"La interfaz web de RaSCSI no pudo conectarse a RaSCSI en %(host)s:%(port)s con " +"el error: %(error_msg)s. El proceso RaSCSI no se está ejecutando o puede haber " +"fallado." + +#: socket_cmds.py:79 +msgid "" +"The RaSCSI Web Interface lost connection to RaSCSI. Please go back and try " +"again. If the issue persists, please report a bug." +msgstr "" +"La interfaz web de RaSCSI ha perdido la conexión con RaSCSI. Por favor, vuelva " +"a intentarlo. Si el problema persiste, informe de un error." + +#: socket_cmds.py:95 +msgid "" +"The RaSCSI Web Interface did not get a valid response from RaSCSI. Please go " +"back and try again. If the issue persists, please report a bug." +msgstr "" +"La interfaz web de RaSCSI no ha obtenido una respuesta válida de RaSCSI. Por " +"favor, vuelva a intentarlo. Si el problema persiste, informe de un error." + +#: web.py:122 +msgid "" +"RaSCSI is password protected. Start the Web Interface with the --password " +"parameter." +msgstr "" +"RaSCSI está protegido por contraseña. Inicie la Interfaz Web con el parámetro —" +"password." + +#: web.py:223 +#, python-format +msgid "Could not read drive properties from %(properties_file)s" +msgstr "" +"No se han podido leer las propiedades de la unidad desde %(properties_file)s" + +#: web.py:293 +#, python-format +msgid "You must log in with credentials for a user in the '%(group)s' group" +msgstr "Debe conectarse con las credenciales de un usuario del grupo ‘%(group)s’" + +#: web.py:351 web.py:890 +#, python-format +msgid "Image file created: %(file_name)s" +msgstr "Archivo de imagen creado: %(file_name)s" + +#: web.py:476 +msgid "An error occurred when fetching logs." +msgstr "Se ha producido un error al obtener los registros." + +#: web.py:491 +#, python-format +msgid "Log level set to %(value)s" +msgstr "Nivel de registro fijado en %(value)s" + +#: web.py:510 +#, python-format +msgid "Please follow the instructions at %(url)s" +msgstr "Siga las instrucciones en %(url)s" + +#: web.py:514 +msgid "Configure IPv4 forwarding before using a wireless network device." +msgstr "" +"Configure el reenvío de IPv4 antes de utilizar un dispositivo de red " +"inalámbrico." + +#: web.py:518 +msgid "Configure NAT before using a wireless network device." +msgstr "Configure NAT antes de utilizar un dispositivo de red inalámbrico." + +#: web.py:523 web.py:527 +msgid "Configure the network bridge before using a wired network device." +msgstr "" +"Configure el puente de red antes de utilizar un dispositivo de red por cable." + +#: web.py:540 +#, python-format +msgid "Attached DaynaPORT to SCSI ID %(id_number)s" +msgstr "Adjuntado DaynaPORT a SCSI ID %(id_number)s" + +#: web.py:588 +#, python-format +msgid "Attached %(file_name)s to SCSI ID %(id_number)s LUN %(unit_number)s" +msgstr "Adjuntado %(file_name)s a SCSI ID %(id_number)s LUN %(unit_number)s" + +#: web.py:591 +#, python-format +msgid "" +"The image file size %(file_size)s bytes is not a multiple of %(block_size)s. " +"RaSCSI will ignore the trailing data. The image may be corrupted, so proceed " +"with caution." +msgstr "" +"El tamaño del archivo de imagen %(file_size)s bytes no es un múltiplo de " +"%(block_size)s. RaSCSI ignorará los datos finales. La imagen puede estar " +"corrupta, así que proceda con precaución." + +#: web.py:597 +#, python-format +msgid "" +"Failed to attach %(file_name)s to SCSI ID %(id_number)s LUN %(unit_number)s" +msgstr "" +"No se ha podido conectar %(file_name)s a SCSI ID %(id_number)s LUN " +"%(unit_number)s" + +#: web.py:611 +msgid "Detached all SCSI devices" +msgstr "Se han desconectado todos los dispositivos SCSI" + +#: web.py:628 +#, python-format +msgid "Detached SCSI ID %(id_number)s LUN %(unit_number)s" +msgstr "SCSI ID desconectado %(id_number)s LUN %(unit_number)s" + +#: web.py:632 +#, python-format +msgid "Failed to detach SCSI ID %(id_number)s LUN %(unit_number)s" +msgstr "Fallo en la desconexión de SCSI ID %(id_number)s LUN %(unit_number)s" + +#: web.py:649 +#, python-format +msgid "Ejected SCSI ID %(id_number)s LUN %(unit_number)s" +msgstr "Expulsado SCSI ID %(id_number)s LUN %(unit_number)s" + +#: web.py:653 +#, python-format +msgid "Failed to eject SCSI ID %(id_number)s LUN %(unit_number)s" +msgstr "Fallo en la expulsión de SCSI ID %(id_number)s LUN %(unit_number)s" + +#: web.py:676 +#, fuzzy +msgid "DEVICE INFO" +msgstr "DEVICE INFO" + +#: web.py:678 +#, python-format +msgid "SCSI ID: %(id_number)s" +msgstr "SCSI ID: %(id_number)s" + +#: web.py:679 +#, python-format +msgid "LUN: %(unit_number)s" +msgstr "LUN: %(unit_number)s" + +#: web.py:680 +#, python-format +msgid "Type: %(device_type)s" +msgstr "Tipo: %(device_type)s" + +#: web.py:681 +#, python-format +msgid "Status: %(device_status)s" +msgstr "Estado: %(device_status)s" + +#: web.py:682 +#, python-format +msgid "File: %(image_file)s" +msgstr "Archivo: %(image_file)s" + +#: web.py:683 +#, python-format +msgid "Parameters: %(value)s" +msgstr "Parámetros: %(value)s" + +#: web.py:684 +#, python-format +msgid "Vendor: %(value)s" +msgstr "Proveedor: %(value)s" + +#: web.py:685 +#, python-format +msgid "Product: %(value)s" +msgstr "Producto: %(value)s" + +#: web.py:686 +#, python-format +msgid "Revision: %(revision_number)s" +msgstr "Revisión: %(revision_number)s" + +#: web.py:687 +#, python-format +msgid "Block Size: %(value)s bytes" +msgstr "Tamaño del bloque: %(value)s bytes" + +#: web.py:688 +#, python-format +msgid "Image Size: %(value)s bytes" +msgstr "Tamaño de la imagen: %(value)s bytes" + +#: web.py:707 +#, python-format +msgid "Reserved SCSI ID %(id_number)s" +msgstr "SCSI ID reservado %(id_number)s" + +#: web.py:710 +#, python-format +msgid "Failed to reserve SCSI ID %(id_number)s" +msgstr "No se ha podido reservar el SCSI ID %(id_number)s" + +#: web.py:726 +#, python-format +msgid "Released the reservation for SCSI ID %(id_number)s" +msgstr "Liberada la reserva para SCSI ID %(id_number)s" + +#: web.py:729 +#, python-format +msgid "Failed to release the reservation for SCSI ID %(id_number)s" +msgstr "No se ha podido liberar la reserva para el SCSI ID %(id_number)s" + +#: web.py:767 +#, python-format +msgid "Saved image as: %(file_name)s" +msgstr "Imagen guardada como: %(file_name)s" + +#: web.py:769 +#, python-format +msgid "Failed to create CD-ROM image from %(url)s" +msgstr "No se ha podido crear la imagen del CD-ROM desde %(url)s" + +#: web.py:775 +#, python-format +msgid "Attached to SCSI ID %(id_number)s" +msgstr "Conectado a SCSI ID %(id_number)s" + +#: web.py:778 +#, python-format +msgid "" +"Failed to attach image to SCSI ID %(id_number)s. Try attaching it manually." +msgstr "" +"No se ha podido conectar la imagen al SCSI ID %(id_number)s. Intente conectarla " +"manualmente." + +#: web.py:797 web.py:814 +#, python-format +msgid "Failed to download file from %(url)s" +msgstr "No se ha podido descargar el archivo de %(url)s" + +#: web.py:845 +msgid "The file already exists!" +msgstr "¡El archivo ya existe!" + +#: web.py:853 +msgid "Unable to write the file to disk!" +msgstr "¡No se puede escribir el archivo en el disco!" + +#: web.py:868 +msgid "Transferred file corrupted!" +msgstr "¡Archivo transferido corrompido!" + +#: web.py:874 +msgid "File upload successful!" +msgstr "¡La carga de archivos se ha realizado con éxito!" + +#: web.py:917 +#, python-format +msgid "Image file deleted: %(file_name)s" +msgstr "Archivo de imagen borrado: %(file_name)s" + +#: web.py:947 +#, python-format +msgid "Image file renamed to: %(file_name)s" +msgstr "Archivo de imagen renombrado a: %(file_name)s" + +#: web.py:984 +msgid "Aborted unzip: File(s) with the same name already exists." +msgstr "Descompresión abortada: Ya existe(n) archivo(s) con el mismo nombre." + +#: web.py:986 +msgid "Unzipped the following files:" +msgstr "Descomprime los siguientes archivos:" + +#: web.py:990 +#, python-format +msgid "Properties file(s) have been moved to %(directory)s" +msgstr "Los archivos de propiedades se han trasladado a %(directory)s" + +#: web.py:993 +#, python-format +msgid "Failed to unzip %(zip_file)s" +msgstr "No se puede descomprimir %(zip_file)s" + +#: web.py:1007 +#, python-format +msgid "Changed Web Interface language to %(locale)s" +msgstr "Se ha cambiado el lenguaje de la Interfaz Web a %(locale)s" + +#: templates/base.html:4 +msgid "RaSCSI Control Page" +msgstr "Página de control de RaSCSI" + +#: templates/base.html:29 +msgid "" +" This process may take a while, and will continue in the background if you " +"navigate away from this page." +msgstr "" +" Este proceso puede tardar un poco, y continuará en segundo plano si navega " +"fuera de esta página." + +#: templates/base.html:34 +msgid "" +" The Web Interface will become unresponsive momentarily. Reload this page after " +"the Pi has started up again." +msgstr "" +" La interfaz web dejará de responder momentáneamente. Vuelva a cargar esta " +"página después de que la Pi haya arrancado de nuevo." + +#: templates/base.html:48 +#, python-format +msgid "Logged in as %(username)s" +msgstr "Conectado como %(username)s" + +#: templates/base.html:48 +msgid "Log Out" +msgstr "Cerrar la sesión" + +#: templates/base.html:52 +msgid "Log In to Use Web Interface" +msgstr "Iniciar sesión para utilizar la interfaz web" + +#: templates/base.html:53 +msgid "Username" +msgstr "Nombre de usuario" + +#: templates/base.html:54 +msgid "Password" +msgstr "Contraseña" + +#: templates/base.html:60 +msgid "Web Interface Authentication Disabled" +msgstr "Autenticación de la interfaz web deshabilitada" + +#: templates/base.html:60 +#, python-format +msgid "See Wiki for more information" +msgstr "" +"Consulte Wiki para obtener más información" + +#: templates/base.html:87 +msgid "RaSCSI version: " +msgstr "Versión de RaSCSI: " + +#: templates/base.html:88 +msgid "Pi environment: " +msgstr "Entorno Pi: " + +#: templates/drives.html:4 templates/drives.html:139 +msgid "Cancel" +msgstr "Cancelar" + +#: templates/drives.html:5 +msgid "Disclaimer" +msgstr "Aviso legal" + +#: templates/drives.html:6 +#, python-format +msgid "" +"These device profiles are provided as-is with no guarantee to work equally to " +"the actual physical device they are named after. You may need to provide " +"appropirate device drivers and/or configuration parameters for them to function " +"properly. If you would like to see data modified, or have additional devices to " +"add to the list, please raise an issue ticket at GitHub." +msgstr "" +"Estos perfiles de dispositivos se proporcionan tal cual, sin garantía de que " +"funcionen igual que el dispositivo físico real al que dan nombre. Es posible " +"que tenga que proporcionar los controladores de dispositivo y/o los parámetros " +"de configuración adecuados para que funcionen correctamente. Si desea que se " +"modifiquen los datos, o si tiene otros dispositivos que añadir a la lista, cree " +"un ticket de incidencia en GitHub." + +#: templates/drives.html:7 +msgid "Hard Drives" +msgstr "Discos duros" + +#: templates/drives.html:12 templates/drives.html:56 templates/drives.html:102 +msgid "Name" +msgstr "Nombre" + +#: templates/drives.html:13 templates/drives.html:57 templates/drives.html:103 +msgid "Size (MB)" +msgstr "Tamaño (MB)" + +#: templates/drives.html:14 templates/drives.html:58 templates/drives.html:104 +msgid "Description" +msgstr "Descripción" + +#: templates/drives.html:15 templates/drives.html:59 templates/drives.html:105 +msgid "Ref." +msgstr "Ref." + +#: templates/drives.html:16 templates/drives.html:60 templates/drives.html:106 +msgid "Action" +msgstr "Acción" + +#: templates/drives.html:25 templates/drives.html:69 templates/drives.html:115 +msgid "Link" +msgstr "Enlace" + +#: templates/drives.html:39 templates/drives.html:129 +msgid "Save as:" +msgstr "Guardado como:" + +#: templates/drives.html:41 templates/drives.html:88 templates/drives.html:131 +#: templates/index.html:549 +msgid "Create" +msgstr "Crear" + +#: templates/drives.html:51 +msgid "CD-ROM Drives" +msgstr "Unidades de CD-ROM" + +#: templates/drives.html:52 +msgid "" +"This will create a properties file for the given CD-ROM image. No new image " +"file will be created." +msgstr "" +"Esto creará un archivo de propiedades para la imagen de CD-ROM dada. No se " +"creará ningún archivo de imagen nuevo." + +#: templates/drives.html:80 +msgid "Create for:" +msgstr "Crear para:" + +#: templates/drives.html:98 +msgid "Removable Drives" +msgstr "Unidades extraíbles" + +#: templates/drives.html:138 templates/index.html:295 +#, python-format +msgid "%(disk_space)s MB disk space remaining on the Pi" +msgstr "%(disk_space)s MB de espacio en disco restante en la Pi" + +#: templates/index.html:6 +msgid "Current RaSCSI Configuration" +msgstr "Configuración actual de RaSCSI" + +#: templates/index.html:9 +msgid "Displays the currently attached devices for each available SCSI ID." +msgstr "" +"Muestra los dispositivos actualmente conectados para cada SCSI ID disponible." + +#: templates/index.html:10 +#, python-format +msgid "" +"Save and load device configurations, stored as json files in " +"%(config_dir)s" +msgstr "" +"Guardar y cargar las configuraciones de los dispositivos, almacenadas como " +"archivos json en %(config_dir)s" + +#: templates/index.html:11 +msgid "" +"To have a particular device configuration load when RaSCSI starts, save it as " +"default." +msgstr "" +"Para que se cargue una configuración de dispositivo concreta cuando se inicie " +"RaSCSI, guárdela como default." + +#: templates/index.html:25 +msgid "No saved configurations" +msgstr "No hay configuraciones guardadas" + +#: templates/index.html:29 +msgid "Load" +msgstr "Cargar" + +#: templates/index.html:29 +msgid "Detach all current device and Load configuration?" +msgstr "¿Desconectar todo el dispositivo actual y cargar la configuración?" + +#: templates/index.html:30 templates/index.html:287 +msgid "Delete" +msgstr "Borrar" + +#: templates/index.html:30 +msgid "Delete configuration file?" +msgstr "¿Borrar el archivo de configuración?" + +#: templates/index.html:35 +msgid "Save" +msgstr "Guardar" + +#: templates/index.html:41 templates/index.html:252 +msgid "ID" +msgstr "ID" + +#: templates/index.html:43 templates/index.html:260 +msgid "LUN" +msgstr "LUN" + +#: templates/index.html:45 templates/index.html:268 +msgid "Type" +msgstr "Tipo" + +#: templates/index.html:46 +msgid "Status" +msgstr "Estado" + +#: templates/index.html:47 templates/index.html:166 +msgid "File" +msgstr "Archivo" + +#: templates/index.html:48 +msgid "Product" +msgstr "Producto" + +#: templates/index.html:49 templates/index.html:168 +msgid "Actions" +msgstr "Acciones" + +#: templates/index.html:84 templates/index.html:277 templates/index.html:337 +msgid "Attach" +msgstr "Conectar" + +#: templates/index.html:98 +msgid "Eject Disk? WARNING: On Mac OS, eject the Disk in the Finder instead!" +msgstr "" +"¿Expulsar el disco? ADVERTENCIA: ¡En Mac OS, expulse el disco en el Finder!" + +#: templates/index.html:101 +msgid "Eject" +msgstr "Expulsar" + +#: templates/index.html:104 +msgid "Detach Device?" +msgstr "¿Desconectar el dispositivo?" + +#: templates/index.html:107 +msgid "Detach" +msgstr "Desconectar" + +#: templates/index.html:113 +msgid "Info" +msgstr "Info" + +#: templates/index.html:116 +msgid "Enter a memo for this reservation" +msgstr "Introduzca una nota para esta reserva" + +#: templates/index.html:119 +msgid "Reserve" +msgstr "Reserva" + +#: templates/index.html:129 +msgid "Reserved ID" +msgstr "ID reservado" + +#: templates/index.html:135 +msgid "Unreserve" +msgstr "Liberar la reserva" + +#: templates/index.html:144 +msgid "Detach all SCSI Devices?" +msgstr "¿Desconectar todos los dispositivos SCSI?" + +#: templates/index.html:145 +msgid "Detach All Devices" +msgstr "Desconectar todos los dispositivos" + +#: templates/index.html:152 +msgid "Image File Management" +msgstr "Gestión de archivos de imagen" + +#: templates/index.html:155 +#, python-format +msgid "" +"Manage image files in the active RaSCSI image directory: %(directory)s " +"with a scan depth of %(scan_depth)s." +msgstr "" +"Gestionar los archivos de imagen en el directorio activo de las imágenes de " +"RaSCSI: %(directory)s con la profundidad de escaneo de %(scan_depth)s." + +#: templates/index.html:156 +#, python-format +msgid "" +"Select a valid SCSI ID and LUN to attach to. Unless you " +"know what you're doing, always use LUN 0." +msgstr "" +"Seleccione un SCSI ID válido y LUN al que conectar. A " +"menos que sepa lo que está haciendo, utilice siempre el LUN 0." + +#: templates/index.html:158 +msgid "" +"If RaSCSI was unable to detect the device type associated with the image, you " +"can choose the type from the dropdown." +msgstr "" +"Si RaSCSI no pudo detectar el tipo de dispositivo asociado a la imagen, puede " +"elegir el tipo en el menú desplegable." + +#: templates/index.html:159 +msgid "" +"Types: SAHD = SASI HDD | SCHD = SCSI HDD | SCRM = Removable | SCMO = Magneto-" +"Optical | SCCD = CD-ROM | SCBR = Host Bridge | SCDP = DaynaPORT" +msgstr "" +"Tipos: SAHD = SASI HDD | SCHD = SCSI HDD | SCRM = extraíble | SCMO = magneto-" +"optico | SCCD = CD-ROM | SCBR = Host Bridge | SCDP = DaynaPORT" + +#: templates/index.html:167 +msgid "Size" +msgstr "Tamaño" + +#: templates/index.html:184 +msgid "Properties File" +msgstr "Archivo de propiedades" + +#: templates/index.html:204 templates/index.html:218 +msgid "Unzip" +msgstr "Descomprimir" + +#: templates/index.html:204 templates/index.html:218 +msgid "Unzipping a single file..." +msgstr "Descomprimiendo un solo archivo…" + +#: templates/index.html:233 templates/index.html:390 templates/index.html:548 +msgid "MB" +msgstr "MB" + +#: templates/index.html:239 +msgid "Attached!" +msgstr "¡Conectado!" + +#: templates/index.html:246 +msgid "Unzip All" +msgstr "Descomprimir todo" + +#: templates/index.html:246 +msgid "Unzipping all files..." +msgstr "Descomprimiendo todos los archivos…" + +#: templates/index.html:280 +#, python-format +msgid "Enter new file name for: %(file_name)s" +msgstr "Introduzca un nuevo nombre de archivo para: %(file_name)s" + +#: templates/index.html:283 +msgid "Rename" +msgstr "Renombrar" + +#: templates/index.html:285 +#, python-format +msgid "Delete file: %(file_name)s?" +msgstr "¿Borrar el archivo: %(file_name)s?" + +#: templates/index.html:301 +msgid "Attach Ethernet Adapter" +msgstr "Conectar el adaptador Ethernet" + +#: templates/index.html:304 +#, python-format +msgid "" +"Emulates a SCSI DaynaPORT Ethernet Adapter. Host drivers " +"and configuration required." +msgstr "" +"Emula el adaptador ethernet SCSI DynaPORT. Controladores de " +"host y configuración necesaria." + +#: templates/index.html:306 +msgid "" +"If you have a DHCP setup, choose only the interface you have configured the " +"bridge with. You can ignore the Static IP fields when attaching." +msgstr "" +"Si tiene una configuración DHCP, elija sólo la interfaz con la que ha " +"configurado el puente. Puede ignorar los campos de IP estática al conectarla." + +#: templates/index.html:307 +#, python-format +msgid "" +"Configure the network bridge by running easyinstall.sh, or follow the manual steps in the wiki." +msgstr "" +"Configure el puente de red ejecutando easyinstall.sh, o siga los pasos manuales en la wiki." + +#: templates/index.html:310 +msgid "" +"The rascsi_bridge interface is active and ready to be used by " +"DaynaPORT!" +msgstr "" +"¡La interfaz rascsi_bridge está activa y lista para ser utilizada por " +"DaynaPORT!" + +#: templates/index.html:318 +msgid "Interface:" +msgstr "Interfaz:" + +#: templates/index.html:326 +msgid "Static IP (optional):" +msgstr "IP estática (opcional):" + +#: templates/index.html:329 templates/index.html:473 +msgid "SCSI ID:" +msgstr "SCSI ID:" + +#: templates/index.html:343 +#, python-format +msgid "Macproxy is running at %(ip_addr)s (default port 5000)" +msgstr "Macproxy se está ejecutando en %(ip_addr)s (puerto por defecto: 5000)" + +#: templates/index.html:345 +#, python-format +msgid "" +"Install Macproxy to browse the Web with any vintage " +"browser. It's not just for Macs!" +msgstr "" +"Instala Macproxy para navegar por la web con cualquier " +"navegador de época. ¡No es sólo para Macs!" + +#: templates/index.html:351 +msgid "Upload File" +msgstr "Subir archivo" + +#: templates/index.html:354 +#, python-format +msgid "" +"Uploads file to %(directory)s. The largest file size accepted is " +"%(max_file_size)s MB." +msgstr "" +"Sube un archivo a %(directory)s. El mayor tamaño de archivo aceptado " +"es %(max_file_size)s MB." + +#: templates/index.html:355 +msgid "" +"For unrecognized file types, try renaming hard drive images to '.hds', CD-ROM " +"images to '.iso', and removable drive images to '.hdr' before uploading." +msgstr "" +"En el caso de los tipos de archivo no reconocidos, intente cambiar el nombre de " +"las imágenes del disco duro a ‘.hds’, de las imágenes del CD-ROM a ‘.iso’ y de " +"las imágenes de las unidades extraíbles a ‘.hdr’ antes de cargarlas." + +#: templates/index.html:356 +#, python-format +msgid "Recognized file types: %(valid_file_suffix)s" +msgstr "Tipos de archivo reconocidos: %(valid_file_suffix)s" + +#: templates/index.html:376 +msgid "Drop files here to upload" +msgstr "Suelte los archivos aquí para cargarlos" + +#: templates/index.html:377 +msgid "Your browser does not support drag'n'drop file uploads." +msgstr "" +"Su navegador no admite la carga de archivos mediante la función de arrastrar y " +"soltar." + +#: templates/index.html:378 +msgid "" +"Please use the fallback form below to upload your files like in the olden days." +msgstr "" +"Por favor, utilice el siguiente formulario para subir sus archivos como en los " +"viejos tiempos." + +#: templates/index.html:379 +msgid "File is too big: {{filesize}}MB. Max filesize: {{maxFilesize}}MB." +msgstr "" +"El archivo es demasiado grande: {{filesize}}MB. Tamaño máximo del archivo: " +"{{maxFilesize}}MB." + +#: templates/index.html:380 +msgid "You can't upload files of this type." +msgstr "No se pueden subir archivos de este tipo." + +#: templates/index.html:381 +msgid "Server responded with code: {{statusCode}}" +msgstr "El servidor ha respondido con un código: {{statusCode}}" + +#: templates/index.html:382 +msgid "Cancel upload" +msgstr "Cancelar la subida" + +#: templates/index.html:383 +msgid "Upload canceled." +msgstr "Subida cancelada." + +#: templates/index.html:384 +msgid "Are you sure you want to cancel this upload?" +msgstr "¿Estás seguro de que quieres cancelar esta subida?" + +#: templates/index.html:385 +msgid "Remove file" +msgstr "Eliminar el archivo" + +#: templates/index.html:386 +msgid "You can not upload any more files." +msgstr "No puedes subir más archivos." + +#: templates/index.html:388 +msgid "TB" +msgstr "TB" + +#: templates/index.html:389 +msgid "GB" +msgstr "GB" + +#: templates/index.html:391 +msgid "KB" +msgstr "KB" + +#: templates/index.html:392 +msgid "b" +msgstr "b" + +#: templates/index.html:401 +msgid "Download File to Images" +msgstr "Descarga de archivos a imágenes" + +#: templates/index.html:404 +#, python-format +msgid "Given a URL, download that file to the %(directory)s directory." +msgstr "" +"Dada una URL, descarga ese archivo en el directorio de %(directory)s." + +#: templates/index.html:412 templates/index.html:438 templates/index.html:482 +msgid "URL:" +msgstr "URL:" + +#: templates/index.html:413 templates/index.html:439 templates/index.html:483 +msgid "URL" +msgstr "URL" + +#: templates/index.html:414 templates/index.html:440 +msgid "Download" +msgstr "Descargar" + +#: templates/index.html:414 +msgid "Downloading File to Images..." +msgstr "Descargando archivos a imágenes…" + +#: templates/index.html:424 +msgid "Download File to AppleShare" +msgstr "Descargar archivo a AppleShare" + +#: templates/index.html:427 +#, python-format +msgid "" +"Given a URL, download that file to the %(directory)s directory and " +"share it over AFP." +msgstr "" +"Dada una URL, descarga ese archivo al directorio de %(directory)s y lo " +"comparte a través de AFP." + +#: templates/index.html:428 +msgid "Manage the files you download here through AppleShare on your vintage Mac." +msgstr "" +"Gestiona los archivos que descargues aquí a través de AppleShare en tu Mac " +"vintage." + +#: templates/index.html:429 +#, python-format +msgid "" +"Requires Netatalk to be installed and configured " +"correctly for your network." +msgstr "" +"Requiere que Netatalk esté instalado y configurado " +"correctamente para su red." + +#: templates/index.html:440 +msgid "Downloading File to AppleShare..." +msgstr "Descargando archivos s AppleShare…" + +#: templates/index.html:447 +msgid "The AppleShare server is running. No active connections." +msgstr "El servidor AppleShare está funcionando. No hay conexiones activas." + +#: templates/index.html:449 +#, python-format +msgid "%(value)d active AFP connection" +msgstr "%(value)d conexión AFP activa" + +#: templates/index.html:451 +#, python-format +msgid "%(value)d active AFP connections" +msgstr "%(value)d conexiones AFP activas" + +#: templates/index.html:454 +#, python-format +msgid "" +"Install Netatalk to use the AppleShare File Server." +msgstr "" +"Instale Netatalk para utilizar el servidor de archivos " +"AppleShare." + +#: templates/index.html:461 +msgid "Download File and Create CD-ROM image" +msgstr "Descargar archivo y crear imagen de CD-ROM" + +#: templates/index.html:464 +msgid "" +"Create an ISO file system CD-ROM image with the downloaded file, and mount it " +"on the given SCSI ID." +msgstr "" +"Cree una imagen de CD-ROM del sistema de archivos ISO con el archivo descargado " +"y móntelo en la SCSI ID indicada." + +#: templates/index.html:465 +msgid "HFS is for Mac OS, Joliet for Windows, and Rock Ridge for POSIX." +msgstr "HFS es para Mac OS, Joliet para Windows y Rock Ridge para POSIX." + +#: templates/index.html:466 +#, python-format +msgid "On Mac OS, a compatible CD-ROM driver is required." +msgstr "" +"En Mac OS, se necesita un controlador de CD-ROM compatible." + +#: templates/index.html:467 +msgid "" +"If the downloaded file is a zip archive, we will attempt to unzip it and store " +"the resulting files." +msgstr "" +"Si el fichero descargado es un archivo zip, intentaremos descomprimirlo y " +"almacenar los ficheros resultantes." + +#: templates/index.html:484 templates/index.html:529 +msgid "Type:" +msgstr "Tipo:" + +#: templates/index.html:505 +msgid "Download and Mount CD-ROM image" +msgstr "Descargar y montar la imagen del CD-ROM" + +#: templates/index.html:505 +msgid "Downloading File and generating CD-ROM image..." +msgstr "Descargando el archivo y generando la imagen del CD-ROM…" + +#: templates/index.html:515 +msgid "Create Empty Disk Image File" +msgstr "Crear un archivo de imagen de disco vacío" + +#: templates/index.html:518 +msgid "The Generic image type is recommended for most computer platforms." +msgstr "" +"El tipo de imagen Genérico se recomienda para la mayoría de las plataformas " +"informáticas." + +#: templates/index.html:519 +msgid "" +"APPLE GENUINE (.hda) and NEC GENUINE (.hdn) image types will make RaSCSI behave " +"as a particular drive type that are recognized by Mac and PC98 systems, " +"respectively." +msgstr "" +"Los tipos de imagen APPLE GENUINE (.hda) y NEC GENUINE (.hdn) harán que RaSCSI " +"se comporte como un tipo de unidad particular reconocido por los sistemas Mac y " +"PC98, respectivamente." + +#: templates/index.html:520 +msgid "" +"SASI images should only be used on the original Sharp X68000, or other legacy " +"systems that utilize this pre-SCSI standard." +msgstr "" +"Las imágenes SASI sólo deben utilizarse en el Sharp X68000 original, o en otros " +"sistemas heredados que utilicen este estándar pre-SCSI." + +#: templates/index.html:527 +msgid "File Name:" +msgstr "Nombre del archivo:" + +#: templates/index.html:528 +msgid "File Name" +msgstr "Nombre del archivo" + +#: templates/index.html:532 +msgid "SCSI Hard Disk image (Generic) [.hds]" +msgstr "Imagen de disco duro SCSI (genérico) [.hds]" + +#: templates/index.html:535 +msgid "SCSI Hard Disk image (APPLE GENUINE) [.hda]" +msgstr "Imagen de disco duro SCSI (APPLE GENUINE) [.hda]" + +#: templates/index.html:538 +msgid "SCSI Hard Disk image (NEC GENUINE) [.hdn]" +msgstr "Imagen del disco duro SCSI (NEC GENUINE) [.hdn]" + +#: templates/index.html:541 +msgid "SCSI Removable Media Disk image (Generic) [.hdr]" +msgstr "Imagen de disco extraíble SCSI (genérico) [.hdr]" + +#: templates/index.html:544 +msgid "SASI Hard Disk image (Legacy) [.hdf]" +msgstr "Imagen de disco duro SASI (Legacy) [.hdf]" + +#: templates/index.html:547 +msgid "Size:" +msgstr "Tamaño:" + +#: templates/index.html:559 +msgid "Create Named Drive" +msgstr "Crear unidad con nombre" + +#: templates/index.html:562 +msgid "" +"Create pairs of images and properties files from a list of real-life drives." +msgstr "" +"Crear pares de imágenes y archivos de propiedades a partir de una lista de " +"unidades reales." + +#: templates/index.html:563 +msgid "" +"This will make RaSCSI use certain vendor strings and block sizes that may " +"improve compatibility with certain systems." +msgstr "" +"Esto hará que RaSCSI utilice ciertas cadenas de proveedor y tamaños de bloque " +"que pueden mejorar la compatibilidad con ciertos sistemas." + +#: templates/index.html:566 +msgid "Create a named disk image that mimics real-life drives" +msgstr "" +"Crear una imagen de disco con nombre que imite las unidades de la vida real" + +#: templates/index.html:572 +msgid "Logging" +msgstr "Registro" + +#: templates/index.html:575 +msgid "Fetch a certain number of lines of system logs with the given scope." +msgstr "" +"Obtiene un número determinado de líneas de registros del sistema con el rango " +"dado." + +#: templates/index.html:582 +msgid "Log Lines:" +msgstr "Líneas de registro:" + +#: templates/index.html:584 +msgid "Scope:" +msgstr "Rango:" + +#: templates/index.html:596 +msgid "Show Logs" +msgstr "Mostrar registros" + +#: templates/index.html:606 +msgid "Server Log Level" +msgstr "Nivel de registro del servidor" + +#: templates/index.html:609 +msgid "Change the log level of the RaSCSI backend process." +msgstr "Cambiar el nivel de registro del proceso RaSCSI backend." + +#: templates/index.html:610 +msgid "The current dropdown selection indicates the active log level." +msgstr "La selección desplegable actual indica el nivel de registro activo." + +#: templates/index.html:617 +msgid "Log Level:" +msgstr "Nivel de registro:" + +#: templates/index.html:625 +msgid "Set Log Level" +msgstr "Establecer el nivel de registro" + +#: templates/index.html:635 +msgid "Language" +msgstr "Idioma" + +#: templates/index.html:638 +msgid "Change the Web Interface language." +msgstr "Cambiar el idioma de la Interfaz Web." + +#: templates/index.html:645 +msgid "Language:" +msgstr "Idioma:" + +#: templates/index.html:653 +msgid "Change Language" +msgstr "Cambiar el idioma" + +#: templates/index.html:663 +msgid "Raspberry Pi Operations" +msgstr "Operaciones de Raspberry Pi" + +#: templates/index.html:666 +msgid "Reboot or shut down the Raspberry Pi that RaSCSI is running on." +msgstr "Reinicie o apague la Raspberry Pi en la que se está ejecutando RaSCSI." + +#: templates/index.html:667 +msgid "" +"IMPORTANT: Always shut down the Pi before turning off the power. Failing to do " +"so may lead to data loss." +msgstr "" +"IMPORTANTE: Apague siempre la Pi antes de quitarle la corriente. No hacerlo " +"puede provocar la pérdida de datos." + +#: templates/index.html:673 +msgid "Reboot the Raspberry Pi?" +msgstr "¿Reiniciar la Raspberry Pi?" + +#: templates/index.html:673 +msgid "Rebooting the Raspberry Pi..." +msgstr "Reiniciando la Raspberry Pi…" + +#: templates/index.html:674 +msgid "Reboot Raspberry Pi" +msgstr "Reiniciar la Raspberry Pi" + +#: templates/index.html:678 +msgid "Shut down the Raspberry Pi?" +msgstr "¿Apagar la Raspberry Pi?" + +#: templates/index.html:678 +msgid "Shutting down the Raspberry Pi..." +msgstr "Apagando la Raspberry Pi…" + +#: templates/index.html:679 +msgid "Shut Down Raspberry Pi" +msgstr "Apagar la Raspberry Pi" From 1c84bb34f95d8af38c34195ace05e18e46bc489b Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Tue, 4 Jan 2022 19:08:28 -0800 Subject: [PATCH 17/18] Shorten file names when downloading to AFP (#592) --- src/web/file_cmds.py | 7 +++---- src/web/web.py | 17 +++++++++++++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/web/file_cmds.py b/src/web/file_cmds.py index f80041cc..7744793b 100644 --- a/src/web/file_cmds.py +++ b/src/web/file_cmds.py @@ -279,7 +279,7 @@ def download_file_to_iso(url, *iso_args): tmp_full_path = tmp_dir + file_name iso_filename = f"{server_info['image_dir']}/{file_name}.iso" - req_proc = download_to_dir(url, tmp_dir) + req_proc = download_to_dir(url, tmp_dir, file_name) if not req_proc["status"]: return {"status": False, "msg": req_proc["msg"]} @@ -330,13 +330,12 @@ def download_file_to_iso(url, *iso_args): } -def download_to_dir(url, save_dir): +def download_to_dir(url, save_dir, file_name): """ - Takes (str) url, (str) save_dir + Takes (str) url, (str) save_dir, (str) file_name Returns (dict) with (bool) status and (str) msg """ import requests - file_name = PurePath(url).name logging.info("Making a request to download %s", url) try: diff --git a/src/web/web.py b/src/web/web.py index 26fa58d8..cde0231c 100644 --- a/src/web/web.py +++ b/src/web/web.py @@ -6,6 +6,7 @@ import logging import argparse from pathlib import Path from functools import wraps +from hashlib import md5 from flask import ( Flask, @@ -789,7 +790,7 @@ def download_img(): """ url = request.form.get("url") server_info = get_server_info() - process = download_to_dir(url, server_info["image_dir"]) + process = download_to_dir(url, server_info["image_dir"], Path(url).name) if process["status"]: flash(process["msg"]) return redirect(url_for("index")) @@ -806,7 +807,19 @@ def download_afp(): Downloads a remote file onto the AFP shared dir on the Pi """ url = request.form.get("url") - process = download_to_dir(url, AFP_DIR) + file_name = Path(url).name + # Shorten file names longer than 31 chars, the HFS upper limit + # Append a hash of the removed piece of the file name to make it unique + # The format of the hash is a # followed by the first four symbols of the md4 hash + if len(file_name) > 31: + chars_to_cut = len(file_name) - 31 + file_name_stem = Path(file_name).stem + file_name_suffix = Path(file_name).suffix + discarded_portion = file_name_stem[:-abs(chars_to_cut)] + stem_remainder = file_name_stem[:(26 - len(file_name_suffix))] + appendix_hash = (md5(discarded_portion.encode("utf-8"))).hexdigest().upper() + file_name = stem_remainder + "#" + appendix_hash[:4] + file_name_suffix + process = download_to_dir(url, AFP_DIR, file_name) if process["status"]: flash(process["msg"]) return redirect(url_for("index")) From b52abbfdc771896899d3567f258b823f1834b6bc Mon Sep 17 00:00:00 2001 From: akuker <34318535+akuker@users.noreply.github.com> Date: Fri, 7 Jan 2022 12:17:44 -0600 Subject: [PATCH 18/18] Output higher-level report from scsimon (#596) * Output JSON file for post-processing * Debug utility for parsing the SCSI data * Prototype app for parsing scsi captures * correct arg parsing * output html * Cleanupt html output * Add missing include * Allow compilation on non-Linux platforms * Refactored scsimon to be in multiple files * Refactored away * Restructured scsimon into smaller pieces * Added ability to read in pre-generated .json file and re-parse it * Delete scsiparse.cpp * Fix argument parsing and code cleanup * Ran vscode c++ formatting utility * Restore the -Wno-psabi flag for Linux only * Address compiler warnings * Updated to use C++ style ostreams * Cleanup conversion to c++ style ostreams * Updated to use ofstream instead of fprintf * Delete src/raspberrypi/scsimon directory Co-authored-by: akuker --- src/raspberrypi/.gitignore | 3 + src/raspberrypi/Makefile | 24 +- src/raspberrypi/gpiobus.cpp | 4 + src/raspberrypi/monitor/data_sample.cpp | 38 ++ src/raspberrypi/monitor/data_sample.h | 49 ++ src/raspberrypi/monitor/sm_html_report.cpp | 205 ++++++++ src/raspberrypi/monitor/sm_json_report.cpp | 98 ++++ src/raspberrypi/monitor/sm_reports.h | 17 + src/raspberrypi/monitor/sm_vcd_report.cpp | 187 +++++++ src/raspberrypi/os.h | 2 + src/raspberrypi/scsi.h | 1 + src/raspberrypi/scsimon.cpp | 555 ++++++++++----------- 12 files changed, 886 insertions(+), 297 deletions(-) create mode 100644 src/raspberrypi/monitor/data_sample.cpp create mode 100644 src/raspberrypi/monitor/data_sample.h create mode 100644 src/raspberrypi/monitor/sm_html_report.cpp create mode 100644 src/raspberrypi/monitor/sm_json_report.cpp create mode 100644 src/raspberrypi/monitor/sm_reports.h create mode 100644 src/raspberrypi/monitor/sm_vcd_report.cpp diff --git a/src/raspberrypi/.gitignore b/src/raspberrypi/.gitignore index d81380e1..321cbbb5 100644 --- a/src/raspberrypi/.gitignore +++ b/src/raspberrypi/.gitignore @@ -5,11 +5,14 @@ *.layout *.log *.vcd +*.json +*.html rascsi scsimon rasctl sasidump rasdump +scisparse obj bin /rascsi_interface.pb.cpp diff --git a/src/raspberrypi/Makefile b/src/raspberrypi/Makefile index 81ecefdc..e3c47d87 100644 --- a/src/raspberrypi/Makefile +++ b/src/raspberrypi/Makefile @@ -24,8 +24,13 @@ else CXXFLAGS += -O3 -Wall -Werror -DNDEBUG BUILD_TYPE = Release endif +ifeq ("$(shell uname -s)","Linux") + # -Wno-psabi might not work on non-Linux platforms + CXXFLAGS += -Wno-psabi +endif + CFLAGS += -iquote . -D_FILE_OFFSET_BITS=64 -MD -MP -CXXFLAGS += -std=c++17 -Wno-psabi -iquote . -D_FILE_OFFSET_BITS=64 -MD -MP +CXXFLAGS += -std=c++17 -iquote . -D_FILE_OFFSET_BITS=64 -MD -MP ## EXTRA_FLAGS : Can be used to pass special purpose flags CFLAGS += $(EXTRA_FLAGS) @@ -105,9 +110,8 @@ SRC_SCSIMON = \ scsimon.cpp \ scsi.cpp \ gpiobus.cpp \ - filepath.cpp \ - fileio.cpp \ rascsi_version.cpp +SRC_SCSIMON += $(shell find ./monitor -name '*.cpp') SRC_RASCTL = \ rasctl.cpp\ @@ -135,8 +139,8 @@ SRC_SASIDUMP = \ fileio.cpp\ rascsi_version.cpp -vpath %.h ./ ./controllers ./devices -vpath %.cpp ./ ./controllers ./devices +vpath %.h ./ ./controllers ./devices ./monitor +vpath %.cpp ./ ./controllers ./devices ./monitor vpath %.o ./$(OBJDIR) vpath ./$(BINDIR) @@ -193,6 +197,16 @@ $(BINDIR)/$(SASIDUMP): $(OBJ_SASIDUMP) | $(BINDIR) $(BINDIR)/$(SCSIMON): $(OBJ_SCSIMON) | $(BINDIR) $(CXX) $(CXXFLAGS) -o $@ $(OBJ_SCSIMON) -lpthread + +# Phony rules for building individual utilities +.PHONY: $(RASCSI) $(RASCTL) $(RASDUMP) $(SASIDUMP) $(SCSIMON) +$(RASCSI) : $(BINDIR)/$(RASCSI) +$(RASCTL) : $(BINDIR)/$(RASCTL) +$(RASDUMP) : $(BINDIR)/$(RASDUMP) +$(SASIDUMP): $(BINDIR)/$(SASIDUMP) +$(SCSIMON) : $(BINDIR)/$(SCSIMON) + + ## clean : Remove all of the object files, intermediate ## compiler files and executable files .PHONY: clean diff --git a/src/raspberrypi/gpiobus.cpp b/src/raspberrypi/gpiobus.cpp index a931f102..10a6f985 100644 --- a/src/raspberrypi/gpiobus.cpp +++ b/src/raspberrypi/gpiobus.cpp @@ -1385,6 +1385,7 @@ BOOL GPIOBUS::WaitSignal(int pin, BOOL ast) //--------------------------------------------------------------------------- void GPIOBUS::DisableIRQ() { +#ifdef __linux__ if (rpitype == 4) { // RPI4 is disabled by GICC giccpmr = gicc[GICC_PMR]; @@ -1399,6 +1400,9 @@ void GPIOBUS::DisableIRQ() irptenb = irpctl[IRPT_ENB_IRQ_1]; irpctl[IRPT_DIS_IRQ_1] = irptenb & 0xf; } +#else + (void)0; +#endif } //--------------------------------------------------------------------------- diff --git a/src/raspberrypi/monitor/data_sample.cpp b/src/raspberrypi/monitor/data_sample.cpp new file mode 100644 index 00000000..ea8ec048 --- /dev/null +++ b/src/raspberrypi/monitor/data_sample.cpp @@ -0,0 +1,38 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) for Raspberry Pi +// +// Copyright (C) 2020-2021 akuker +// +// [ SCSI Bus Monitor ] +// +//--------------------------------------------------------------------------- +#include "os.h" +#include "scsi.h" +#include "data_sample.h" + +const char *GetPhaseStr(const data_capture *sample) +{ + return BUS::GetPhaseStrRaw(GetPhase(sample)); +} + +BUS::phase_t GetPhase(const data_capture *sample) +{ + // Selection Phase + if (GetSel(sample)) + { + return BUS::selection; + } + + // Bus busy phase + if (!GetBsy(sample)) + { + return BUS::busfree; + } + + // Get target phase from bus signal line + DWORD mci = GetMsg(sample) ? 0x04 : 0x00; + mci |= GetCd(sample) ? 0x02 : 0x00; + mci |= GetIo(sample) ? 0x01 : 0x00; + return BUS::GetPhase(mci); +} diff --git a/src/raspberrypi/monitor/data_sample.h b/src/raspberrypi/monitor/data_sample.h new file mode 100644 index 00000000..e153dc2c --- /dev/null +++ b/src/raspberrypi/monitor/data_sample.h @@ -0,0 +1,49 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2020-2021 akuker +// +// [ SCSI Bus Monitor ] +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "scsi.h" +#include "gpiobus.h" + +typedef struct data_capture +{ + DWORD data; + uint64_t timestamp; +} data_capture_t; + +#define GET_PIN(SAMPLE, PIN) ((bool)((SAMPLE->data >> PIN) & 1)) + +inline bool GetBsy(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_BSY); } +inline bool GetSel(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_SEL); } +inline bool GetAtn(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_ATN); } +inline bool GetAck(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_ACK); } +inline bool GetRst(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_RST); } +inline bool GetMsg(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_MSG); } +inline bool GetCd(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_CD); } +inline bool GetIo(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_IO); } +inline bool GetReq(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_REQ); } +inline bool GetDp(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_DP); } +inline BYTE GetData(const data_capture *sample) +{ + DWORD data = sample->data; + return (BYTE)((data >> (PIN_DT0 - 0)) & (1 << 0)) | + ((data >> (PIN_DT1 - 1)) & (1 << 1)) | + ((data >> (PIN_DT2 - 2)) & (1 << 2)) | + ((data >> (PIN_DT3 - 3)) & (1 << 3)) | + ((data >> (PIN_DT4 - 4)) & (1 << 4)) | + ((data >> (PIN_DT5 - 5)) & (1 << 5)) | + ((data >> (PIN_DT6 - 6)) & (1 << 6)) | + ((data >> (PIN_DT7 - 7)) & (1 << 7)); +} + +const char *GetPhaseStr(const data_capture *sample); +BUS::phase_t GetPhase(const data_capture *sample); diff --git a/src/raspberrypi/monitor/sm_html_report.cpp b/src/raspberrypi/monitor/sm_html_report.cpp new file mode 100644 index 00000000..30f45b96 --- /dev/null +++ b/src/raspberrypi/monitor/sm_html_report.cpp @@ -0,0 +1,205 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Powered by XM6 TypeG Technology. +// Copyright (C) 2016-2020 GIMONS +// +//--------------------------------------------------------------------------- + +#include +#include +#include +#include "os.h" +#include "log.h" +#include "sm_reports.h" +#include "rascsi_version.h" + +using namespace std; + +const static string html_header = R"( + + + + +)"; + +static void print_copyright_info(ofstream& html_fp) +{ + html_fp << "" << endl \ + << "

RaSCSI scsimon Capture Tool

" << endl \ + << "
Version " << rascsi_get_version_string() \
+            << __DATE__ << " " << __TIME__ << endl \
+            << "Copyright (C) 2016-2020 GIMONS" << endl \
+            << "Copyright (C) 2020-2021 Contributors to the RaSCSI project" << endl \
+            << "
" << endl \ + << "
" << endl; +} + +static const string html_footer = R"( + + + +)"; + + +static void print_html_data(ofstream& html_fp, const data_capture *data_capture_array, DWORD capture_count) +{ + const data_capture *data; + bool prev_data_valid = false; + bool curr_data_valid; + DWORD selected_id = 0; + BUS::phase_t prev_phase = BUS::busfree; + bool close_row = false; + int data_space_count = 0; + bool collapsible_div_active = false; + bool button_active = false; + + html_fp << "" << endl; + + for (DWORD idx = 0; idx < capture_count; idx++) + { + data = &data_capture_array[idx]; + curr_data_valid = GetAck(data) && GetReq(data); + BUS::phase_t phase = GetPhase(data); + if (phase == BUS::selection && !GetBsy(data)) + { + selected_id = GetData(data); + } + if (prev_phase != phase) + { + if (close_row) + { + if (collapsible_div_active) + { + html_fp << ""; + } + else if (button_active) + { + html_fp << ""; + } + html_fp << ""; + if (data_space_count < 1) + { + html_fp << ""; + } + else + { + html_fp << ""; + } + html_fp << "" << endl; + data_space_count = 0; + } + html_fp << ""; + close_row = true; // Close the row the next time around + html_fp << ""; + html_fp << ""; + html_fp << ""; + html_fp << "
--wc: " << std::dec << "(0x" << std::hex << data_space_count << ")
" << (double)data->timestamp / 100000 << "" << GetPhaseStr(data) << "" << std::hex << selected_id << ""; + } + if (curr_data_valid && !prev_data_valid) + { + if (data_space_count == 0) + { + button_active = true; + html_fp << "
" << endl; + collapsible_div_active = true; + button_active = false; + } + if (((data_space_count % 16) == 0) && (data_space_count > 17)) + { + html_fp << "
" << endl; + } + } + prev_data_valid = curr_data_valid; + prev_phase = phase; + } +} + +void scsimon_generate_html(const char *filename, const data_capture *data_capture_array, DWORD capture_count) +{ + LOGINFO("Creating HTML report file (%s)", filename); + + ofstream html_ofstream; + + html_ofstream.open(filename, ios::out); + + html_ofstream << html_header; + print_copyright_info(html_ofstream); + print_html_data(html_ofstream, data_capture_array, capture_count); + + html_ofstream << html_footer; + + html_ofstream.close(); +} diff --git a/src/raspberrypi/monitor/sm_json_report.cpp b/src/raspberrypi/monitor/sm_json_report.cpp new file mode 100644 index 00000000..2da7942b --- /dev/null +++ b/src/raspberrypi/monitor/sm_json_report.cpp @@ -0,0 +1,98 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Powered by XM6 TypeG Technology. +// Copyright (C) 2016-2020 GIMONS +// +//--------------------------------------------------------------------------- + +#include "sm_reports.h" +#include "log.h" +#include "spdlog/spdlog.h" +#include "string.h" +#include +#include +using namespace std; + +const char timestamp_label[] = "\"timestamp\":\"0x"; +const char data_label[] = "\"data\":\"0x"; + +DWORD scsimon_read_json(const char *json_filename, data_capture *data_capture_array, DWORD max_sz) +{ + char str_buf[1024]; + FILE *fp = fopen(json_filename, "r"); + DWORD sample_count = 0; + + while (fgets(str_buf, sizeof(str_buf), fp)) + { + + char timestamp[1024]; + char data[1024]; + uint64_t timestamp_uint; + uint32_t data_uint; + char *ptr; + + char *timestamp_str; + char *data_str; + timestamp_str = strstr(str_buf, timestamp_label); + if (!timestamp_str) + continue; + strncpy(timestamp, ×tamp_str[strlen(timestamp_label)], 16); + timestamp[16] = '\0'; + timestamp_uint = strtoull(timestamp, &ptr, 16); + + data_str = strstr(str_buf, data_label); + if (!data_str) + continue; + strncpy(data, &data_str[strlen(data_label)], 8); + data[8] = '\0'; + data_uint = strtoul(data, &ptr, 16); + + // printf("Time: %016llX Data: %08X\n", timestamp_uint, data_uint); + + data_capture_array[sample_count].timestamp = timestamp_uint; + data_capture_array[sample_count].data = data_uint; + sample_count++; + if (sample_count >= max_sz) + { + LOGWARN("File exceeds maximum buffer size. Some data may be missing."); + LOGWARN("Try re-running the tool with a larger buffer size"); + break; + } + } + fclose(fp); + + return sample_count; +} + +//--------------------------------------------------------------------------- +// +// Generate JSON Output File +// +//--------------------------------------------------------------------------- +void scsimon_generate_json(const char *filename, const data_capture *data_capture_array, DWORD capture_count) +{ + LOGTRACE("Creating JSON file (%s)", filename); + ofstream json_ofstream; + json_ofstream.open(filename, ios::out); + + json_ofstream << "[" << endl; + + DWORD i = 0; + while (i < capture_count) + { + json_ofstream << fmt::format("{{\"id\": \"{0:d}\", \"timestamp\":\"{1:#016x}\", \"data\":\"{2:#08x}\"}}", i, data_capture_array[i].timestamp, data_capture_array[i].data); + + if (i != (capture_count - 1)) + { + json_ofstream << ","; + } + json_ofstream << endl; + i++; + } + json_ofstream << "]" << endl; + json_ofstream.close(); + +} diff --git a/src/raspberrypi/monitor/sm_reports.h b/src/raspberrypi/monitor/sm_reports.h new file mode 100644 index 00000000..3976e133 --- /dev/null +++ b/src/raspberrypi/monitor/sm_reports.h @@ -0,0 +1,17 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) for Raspberry Pi +// +// Copyright (C) 2020-2021 akuker +// +// [ SCSI Bus Monitor ] +// +//--------------------------------------------------------------------------- + +#include "data_sample.h" + +DWORD scsimon_read_json(const char *json_filename, data_capture *data_capture_array, DWORD max_sz); + +void scsimon_generate_html(const char *filename, const data_capture *data_capture_array, DWORD capture_count); +void scsimon_generate_json(const char *filename, const data_capture *data_capture_array, DWORD capture_count); +void scsimon_generate_value_change_dump(const char *filename, const data_capture *data_capture_array, DWORD capture_count); diff --git a/src/raspberrypi/monitor/sm_vcd_report.cpp b/src/raspberrypi/monitor/sm_vcd_report.cpp new file mode 100644 index 00000000..762a949b --- /dev/null +++ b/src/raspberrypi/monitor/sm_vcd_report.cpp @@ -0,0 +1,187 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) for Raspberry Pi +// +// Copyright (C) 2020-2021 akuker +// +// [ SCSI Bus Monitor ] +// +//--------------------------------------------------------------------------- + +#include "os.h" +#include "log.h" +#include "spdlog/spdlog.h" +#include +#include +#include +#include "data_sample.h" +#include "sm_reports.h" +#include "gpiobus.h" + +using namespace std; + +//--------------------------------------------------------------------------- +// +// Constant declarations +// +//--------------------------------------------------------------------------- + +// Symbol definition for the VCD file +// These are just arbitrary symbols. They can be anything allowed by the VCD file format, +// as long as they're consistently used. +const char SYMBOL_PIN_DAT = '#'; +const char SYMBOL_PIN_ATN = '+'; +const char SYMBOL_PIN_RST = '$'; +const char SYMBOL_PIN_ACK = '%'; +const char SYMBOL_PIN_REQ = '^'; +const char SYMBOL_PIN_MSG = '&'; +const char SYMBOL_PIN_CD = '*'; +const char SYMBOL_PIN_IO = '('; +const char SYMBOL_PIN_BSY = ')'; +const char SYMBOL_PIN_SEL = '-'; +const char SYMBOL_PIN_PHASE = '='; + +// We'll use position 0 in the prev_value array to store the previous phase +#define PIN_PHASE 0 + +//--------------------------------------------------------------------------- +// +// Variable declarations +// +//--------------------------------------------------------------------------- +static BYTE prev_value[32] = {0xFF}; + +extern double ns_per_loop; + +static BOOL get_pin_value(DWORD data, int pin) +{ + return (data >> pin) & 1; +} + +static BYTE get_data_field(DWORD data) +{ + DWORD data_out = + ((data >> (PIN_DT0 - 0)) & (1 << 7)) | + ((data >> (PIN_DT1 - 1)) & (1 << 6)) | + ((data >> (PIN_DT2 - 2)) & (1 << 5)) | + ((data >> (PIN_DT3 - 3)) & (1 << 4)) | + ((data >> (PIN_DT4 - 4)) & (1 << 3)) | + ((data >> (PIN_DT5 - 5)) & (1 << 2)) | + ((data >> (PIN_DT6 - 6)) & (1 << 1)) | + ((data >> (PIN_DT7 - 7)) & (1 << 0)); + + return (BYTE)data_out; +} + +static void vcd_output_if_changed_phase(ofstream& fp, DWORD data, int pin, char symbol) +{ + BUS::phase_t new_value = GPIOBUS::GetPhaseRaw(data); + if (prev_value[pin] != new_value) + { + prev_value[pin] = new_value; + fp << "s" << GPIOBUS::GetPhaseStrRaw(new_value) << " " << symbol << endl; + } +} + +static void vcd_output_if_changed_bool(ofstream& fp, DWORD data, int pin, char symbol) +{ + BOOL new_value = get_pin_value(data, pin); + if (prev_value[pin] != new_value) + { + prev_value[pin] = new_value; + fp << new_value << symbol << endl; + } +} + +static void vcd_output_if_changed_byte(ofstream& fp, DWORD data, int pin, char symbol) +{ + BYTE new_value = get_data_field(data); + if (prev_value[pin] != new_value) + { + prev_value[pin] = new_value; + fp << "b" + << fmt::format("{0:b}", get_pin_value(data, PIN_DT7)) + << fmt::format("{0:b}", get_pin_value(data, PIN_DT6)) + << fmt::format("{0:b}", get_pin_value(data, PIN_DT5)) + << fmt::format("{0:b}", get_pin_value(data, PIN_DT4)) + << fmt::format("{0:b}", get_pin_value(data, PIN_DT3)) + << fmt::format("{0:b}", get_pin_value(data, PIN_DT2)) + << fmt::format("{0:b}", get_pin_value(data, PIN_DT1)) + << fmt::format("{0:b}", get_pin_value(data, PIN_DT0)) + << " " << symbol << endl; + } +} + +void scsimon_generate_value_change_dump(const char *filename, const data_capture *data_capture_array, DWORD capture_count) +{ + LOGTRACE("Creating Value Change Dump file (%s)", filename); + ofstream vcd_ofstream; + vcd_ofstream.open(filename, ios::out); + + // Get the current time + time_t rawtime; + time(&rawtime); + struct tm *timeinfo = localtime(&rawtime); + char timestamp[256]; + strftime(timestamp, sizeof(timestamp), "%d-%m-%Y %H-%M-%S", timeinfo); + + vcd_ofstream + << "$date" << endl + << timestamp << endl + << "$end" << endl + << "$version" << endl + << " VCD generator tool version info text." << endl + << "$end" << endl + << "$comment" << endl + << " Tool build date:" << __TIMESTAMP__ << endl + << "$end" << endl + << "$timescale 1 ns $end" << endl + << "$scope module logic $end" << endl + << "$var wire 1 " << SYMBOL_PIN_BSY << " BSY $end" << endl + << "$var wire 1 " << SYMBOL_PIN_SEL << " SEL $end" << endl + << "$var wire 1 " << SYMBOL_PIN_CD << " CD $end" << endl + << "$var wire 1 " << SYMBOL_PIN_IO << " IO $end"<< endl + << "$var wire 1 " << SYMBOL_PIN_MSG << " MSG $end"<< endl + << "$var wire 1 " << SYMBOL_PIN_REQ << " REQ $end" << endl + << "$var wire 1 " << SYMBOL_PIN_ACK << " ACK $end" << endl + << "$var wire 1 " << SYMBOL_PIN_ATN << " ATN $end" << endl + << "$var wire 1 " << SYMBOL_PIN_RST << " RST $end" << endl + << "$var wire 8 " << SYMBOL_PIN_DAT << " data $end" << endl + << "$var string 1 " << SYMBOL_PIN_PHASE << " phase $end" << endl + << "$upscope $end" << endl + << "$enddefinitions $end" << endl; + + // Initial values - default to zeros + vcd_ofstream + << "$dumpvars" << endl + << "0" << SYMBOL_PIN_BSY << endl + << "0" << SYMBOL_PIN_SEL << endl + << "0" << SYMBOL_PIN_CD << endl + << "0" << SYMBOL_PIN_IO << endl + << "0" << SYMBOL_PIN_MSG << endl + << "0" << SYMBOL_PIN_REQ << endl + << "0" << SYMBOL_PIN_ACK << endl + << "0" << SYMBOL_PIN_ATN << endl + << "0" << SYMBOL_PIN_RST << endl + << "b00000000 " << SYMBOL_PIN_DAT << endl + << "$end" << endl; + + DWORD i = 0; + while (i < capture_count) + { + vcd_ofstream << "#" << (uint64_t)(data_capture_array[i].timestamp * ns_per_loop) << endl; + vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_BSY, SYMBOL_PIN_BSY); + vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_SEL, SYMBOL_PIN_SEL); + vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_CD, SYMBOL_PIN_CD); + vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_IO, SYMBOL_PIN_IO); + vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_MSG, SYMBOL_PIN_MSG); + vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_REQ, SYMBOL_PIN_REQ); + vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_ACK, SYMBOL_PIN_ACK); + vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_ATN, SYMBOL_PIN_ATN); + vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_RST, SYMBOL_PIN_RST); + vcd_output_if_changed_byte(vcd_ofstream, data_capture_array[i].data, PIN_DT0, SYMBOL_PIN_DAT); + vcd_output_if_changed_phase(vcd_ofstream, data_capture_array[i].data, PIN_PHASE, SYMBOL_PIN_PHASE); + i++; + } + vcd_ofstream.close(); +} diff --git a/src/raspberrypi/os.h b/src/raspberrypi/os.h index 4d6a1a34..29de7867 100644 --- a/src/raspberrypi/os.h +++ b/src/raspberrypi/os.h @@ -58,7 +58,9 @@ #include #include #include +#if defined(__linux__) #include +#endif #include #if defined(__linux__) diff --git a/src/raspberrypi/scsi.h b/src/raspberrypi/scsi.h index 7a6558bd..b911130a 100644 --- a/src/raspberrypi/scsi.h +++ b/src/raspberrypi/scsi.h @@ -10,6 +10,7 @@ //--------------------------------------------------------------------------- #pragma once +#include "os.h" //=========================================================================== // diff --git a/src/raspberrypi/scsimon.cpp b/src/raspberrypi/scsimon.cpp index 7c994280..d16fc893 100644 --- a/src/raspberrypi/scsimon.cpp +++ b/src/raspberrypi/scsimon.cpp @@ -9,8 +9,6 @@ //--------------------------------------------------------------------------- #include "os.h" -#include "filepath.h" -#include "fileio.h" #include "log.h" #include "gpiobus.h" #include "rascsi_version.h" @@ -19,7 +17,11 @@ #include #include #include +#include #include "rascsi.h" +#include +#include "monitor/sm_reports.h" +#include "monitor/data_sample.h" using namespace std; @@ -28,48 +30,47 @@ using namespace std; // Constant declarations // //--------------------------------------------------------------------------- -#define MAX_BUFF_SIZE 1000000 // Symbol definition for the VCD file // These are just arbitrary symbols. They can be anything allowed by the VCD file format, // as long as they're consistently used. -#define SYMBOL_PIN_DAT '#' -#define SYMBOL_PIN_ATN '+' -#define SYMBOL_PIN_RST '$' -#define SYMBOL_PIN_ACK '%' -#define SYMBOL_PIN_REQ '^' -#define SYMBOL_PIN_MSG '&' -#define SYMBOL_PIN_CD '*' -#define SYMBOL_PIN_IO '(' -#define SYMBOL_PIN_BSY ')' -#define SYMBOL_PIN_SEL '-' +#define SYMBOL_PIN_DAT '#' +#define SYMBOL_PIN_ATN '+' +#define SYMBOL_PIN_RST '$' +#define SYMBOL_PIN_ACK '%' +#define SYMBOL_PIN_REQ '^' +#define SYMBOL_PIN_MSG '&' +#define SYMBOL_PIN_CD '*' +#define SYMBOL_PIN_IO '(' +#define SYMBOL_PIN_BSY ')' +#define SYMBOL_PIN_SEL '-' #define SYMBOL_PIN_PHASE '=' -// We'll use position 0 in the prev_value array to store the previous phase -#define PIN_PHASE 0 - //--------------------------------------------------------------------------- // // Variable declarations // //--------------------------------------------------------------------------- -static BYTE prev_value[32] = {0xFF}; -static volatile bool running; // Running flag -GPIOBUS *bus; // GPIO Bus -typedef struct data_capture{ - DWORD data; - uint64_t timestamp; -} data_capture_t; +static volatile bool running; // Running flag +GPIOBUS *bus; // GPIO Bus -data_capture data_buffer[MAX_BUFF_SIZE]; +DWORD buff_size = 1000000; +data_capture *data_buffer; DWORD data_idx = 0; double ns_per_loop; +bool print_help = false; +bool import_data = false; + // We don't really need to support 256 character file names - this causes // all kinds of compiler warnings when the log filename can be up to 256 // characters. _MAX_FNAME/2 is just an arbitrary value. -char log_file_name[_MAX_FNAME/2] = "log.vcd"; +char file_base_name[_MAX_FNAME / 2] = "log"; +char vcd_file_name[_MAX_FNAME]; +char json_file_name[_MAX_FNAME]; +char html_file_name[_MAX_FNAME]; +char input_file_name[_MAX_FNAME]; //--------------------------------------------------------------------------- // @@ -78,8 +79,84 @@ char log_file_name[_MAX_FNAME/2] = "log.vcd"; //--------------------------------------------------------------------------- void KillHandler(int sig) { - // Stop instruction - running = false; + // Stop instruction + running = false; +} + +void parse_arguments(int argc, char *argv[]) +{ + int opt; + + while ((opt = getopt(argc, argv, "-Hhb:i:")) != -1) + { + switch (opt) + { + // The three options below are kind of a compound option with two letters + case 'h': + case 'H': + print_help = true; + break; + case 'b': + buff_size = atoi(optarg); + break; + case 'i': + strncpy(input_file_name, optarg, sizeof(input_file_name)-1); + import_data = true; + break; + case 1: + strncpy(file_base_name, optarg, sizeof(file_base_name) - 5); + break; + default: + cout << "default: " << optarg << endl; + break; + } + } + + /* Process any remaining command line arguments (not options). */ + if (optind < argc) + { + while (optind < argc) + strncpy(file_base_name, argv[optind++], sizeof(file_base_name)-1); + } + + strcpy(vcd_file_name, file_base_name); + strcat(vcd_file_name, ".vcd"); + strcpy(json_file_name, file_base_name); + strcat(json_file_name, ".json"); + strcpy(html_file_name, file_base_name); + strcat(html_file_name, ".html"); +} +//--------------------------------------------------------------------------- +// +// Copyright text +// +//--------------------------------------------------------------------------- +void print_copyright_text(int argc, char *argv[]) +{ + LOGINFO("SCSI Monitor Capture Tool - part of RaSCSI(*^..^*) "); + LOGINFO("version %s (%s, %s)", + rascsi_get_version_string(), + __DATE__, + __TIME__); + LOGINFO("Powered by XM6 TypeG Technology "); + LOGINFO("Copyright (C) 2016-2020 GIMONS"); + LOGINFO("Copyright (C) 2020-2021 Contributors to the RaSCSI project"); + LOGINFO(" "); +} + +//--------------------------------------------------------------------------- +// +// Help text +// +//--------------------------------------------------------------------------- +void print_help_text(int argc, char *argv[]) +{ + LOGINFO("%s -i [input file json] -b [buffer size] [output file]", argv[0]); + LOGINFO(" -i [input file json] - scsimon will parse the json file instead of capturing new data"); + LOGINFO(" If -i option is not specified, scsimon will read the gpio pins"); + LOGINFO(" -b [buffer size] - Override the default buffer size of %d.", buff_size); + LOGINFO(" [output file] - Base name of the output files. The file extension (ex: .json)"); + LOGINFO(" will be appended to this file name"); } //--------------------------------------------------------------------------- @@ -87,30 +164,23 @@ void KillHandler(int sig) // Banner Output // //--------------------------------------------------------------------------- -void Banner(int argc, char* argv[]) +void Banner(int argc, char *argv[]) { - LOGINFO("SCSI Monitor Capture Tool - part of RaSCSI(*^..^*) "); - LOGINFO("version %s (%s, %s)\n", - rascsi_get_version_string(), - __DATE__, - __TIME__); - LOGINFO("Powered by XM6 TypeG Technology "); - LOGINFO("Copyright (C) 2016-2020 GIMONS"); - LOGINFO("Copyright (C) 2020-2021 Contributors to the RaSCSI project"); - LOGINFO("Connect type : %s", CONNECT_DESC); - LOGINFO(" %s - Value Change Dump file that can be opened with GTKWave", log_file_name); - - if ((argc > 1 && strcmp(argv[1], "-h") == 0) || - (argc > 1 && strcmp(argv[1], "--help") == 0)){ - LOGINFO("Usage: %s [log filename]...", argv[0]); - exit(0); - } + if (import_data) + { + LOGINFO("Reading input file: %s", input_file_name); + } else { - LOGINFO(" "); - LOGINFO("Now collecting data.... Press CTRL-C to stop.") - LOGINFO(" "); + LOGINFO("Reading live data from the GPIO pins"); + LOGINFO(" Connection type : %s", CONNECT_DESC); } + LOGINFO(" Data buffer size: %u", buff_size); + LOGINFO(" "); + LOGINFO("Generating output files:"); + LOGINFO(" %s - Value Change Dump file that can be opened with GTKWave", vcd_file_name); + LOGINFO(" %s - JSON file with raw data", json_file_name); + LOGINFO(" %s - HTML file with summary of commands", html_file_name); } //--------------------------------------------------------------------------- @@ -120,246 +190,101 @@ void Banner(int argc, char* argv[]) //--------------------------------------------------------------------------- bool Init() { - // Interrupt handler settings - if (signal(SIGINT, KillHandler) == SIG_ERR) { - return FALSE; - } - if (signal(SIGHUP, KillHandler) == SIG_ERR) { - return FALSE; - } - if (signal(SIGTERM, KillHandler) == SIG_ERR) { - return FALSE; - } + // Interrupt handler settings + if (signal(SIGINT, KillHandler) == SIG_ERR) + { + return FALSE; + } + if (signal(SIGHUP, KillHandler) == SIG_ERR) + { + return FALSE; + } + if (signal(SIGTERM, KillHandler) == SIG_ERR) + { + return FALSE; + } - // GPIO Initialization - bus = new GPIOBUS(); - if (!bus->Init()) { + // GPIO Initialization + bus = new GPIOBUS(); + if (!bus->Init()) + { LOGERROR("Unable to intiailize the GPIO bus. Exiting...."); - return false; - } - - // Bus Reset - bus->Reset(); - - // Other - running = false; - - return true; -} - -BOOL get_pin_value(DWORD data, int pin) -{ - return (data >> pin) & 1; -} - -BYTE get_data_field(DWORD data) -{ - DWORD data_out = - ((data >> (PIN_DT0 - 0)) & (1 << 7)) | - ((data >> (PIN_DT1 - 1)) & (1 << 6)) | - ((data >> (PIN_DT2 - 2)) & (1 << 5)) | - ((data >> (PIN_DT3 - 3)) & (1 << 4)) | - ((data >> (PIN_DT4 - 4)) & (1 << 3)) | - ((data >> (PIN_DT5 - 5)) & (1 << 2)) | - ((data >> (PIN_DT6 - 6)) & (1 << 1)) | - ((data >> (PIN_DT7 - 7)) & (1 << 0)); - - return (BYTE)data_out; -} - -void vcd_output_if_changed_phase(FILE *fp, DWORD data, int pin, char symbol) -{ - BUS::phase_t new_value = GPIOBUS::GetPhaseRaw(data); - if (prev_value[pin] != new_value) - { - prev_value[pin] = new_value; - fprintf(fp, "s%s %c\n", GPIOBUS::GetPhaseStrRaw(new_value), symbol); + return false; } -} -void vcd_output_if_changed_bool(FILE *fp, DWORD data, int pin, char symbol) -{ - BOOL new_value = get_pin_value(data,pin); - if (prev_value[pin] != new_value) - { - prev_value[pin] = new_value; - fprintf(fp, "%d%c\n", new_value, symbol); - } -} + // Bus Reset + bus->Reset(); -void vcd_output_if_changed_byte(FILE *fp, DWORD data, int pin, char symbol) -{ - BYTE new_value = get_data_field(data); - if(prev_value[pin] != new_value) - { - prev_value[pin] = new_value; - fprintf(fp, "b%d%d%d%d%d%d%d%d %c\n", - get_pin_value(data,PIN_DT7), - get_pin_value(data,PIN_DT6), - get_pin_value(data,PIN_DT5), - get_pin_value(data,PIN_DT4), - get_pin_value(data,PIN_DT3), - get_pin_value(data,PIN_DT2), - get_pin_value(data,PIN_DT1), - get_pin_value(data,PIN_DT0), symbol); - } -} + // Other + running = false; -void create_value_change_dump() -{ - LOGINFO("Creating Value Change Dump file (%s)", log_file_name); - FILE *fp = fopen(log_file_name,"w"); - - // Get the current time - time_t rawtime; - time(&rawtime); - struct tm *timeinfo = localtime(&rawtime); - char timestamp[256]; - strftime (timestamp,sizeof(timestamp),"%d-%m-%Y %H-%M-%S",timeinfo); - - fprintf(fp, "$date\n"); - fprintf(fp, "%s\n", timestamp); - fprintf(fp, "$end\n"); - fprintf(fp, "$version\n"); - fprintf(fp, " VCD generator tool version info text.\n"); - fprintf(fp, "$end\n"); - fprintf(fp, "$comment\n"); - fprintf(fp, " Tool build date:%s\n", __TIMESTAMP__); - fprintf(fp, "$end\n"); - fprintf(fp, "$timescale 1 ns $end\n"); - fprintf(fp, "$scope module logic $end\n"); - fprintf(fp, "$var wire 1 %c BSY $end\n", SYMBOL_PIN_BSY); - fprintf(fp, "$var wire 1 %c SEL $end\n", SYMBOL_PIN_SEL); - fprintf(fp, "$var wire 1 %c CD $end\n", SYMBOL_PIN_CD); - fprintf(fp, "$var wire 1 %c IO $end\n", SYMBOL_PIN_IO); - fprintf(fp, "$var wire 1 %c MSG $end\n", SYMBOL_PIN_MSG); - fprintf(fp, "$var wire 1 %c REQ $end\n", SYMBOL_PIN_REQ); - fprintf(fp, "$var wire 1 %c ACK $end\n", SYMBOL_PIN_ACK); - fprintf(fp, "$var wire 1 %c ATN $end\n", SYMBOL_PIN_ATN); - fprintf(fp, "$var wire 1 %c RST $end\n", SYMBOL_PIN_RST); - fprintf(fp, "$var wire 8 %c data $end\n", SYMBOL_PIN_DAT); - fprintf(fp, "$var string 1 %c phase $end\n", SYMBOL_PIN_PHASE); - fprintf(fp, "$upscope $end\n"); - fprintf(fp, "$enddefinitions $end\n"); - - // Initial values - default to zeros - fprintf(fp, "$dumpvars\n"); - fprintf(fp, "0%c\n", SYMBOL_PIN_BSY); - fprintf(fp, "0%c\n", SYMBOL_PIN_SEL); - fprintf(fp, "0%c\n", SYMBOL_PIN_CD); - fprintf(fp, "0%c\n", SYMBOL_PIN_IO); - fprintf(fp, "0%c\n", SYMBOL_PIN_MSG); - fprintf(fp, "0%c\n", SYMBOL_PIN_REQ); - fprintf(fp, "0%c\n", SYMBOL_PIN_ACK); - fprintf(fp, "0%c\n", SYMBOL_PIN_ATN); - fprintf(fp, "0%c\n", SYMBOL_PIN_RST); - fprintf(fp, "b00000000 %c\n", SYMBOL_PIN_DAT); - fprintf(fp, "$end\n"); - - DWORD i = 0; - while (i < data_idx) - { - ostringstream s; - s << (uint64_t)(data_buffer[i].timestamp*ns_per_loop); - fprintf(fp, "#%s\n",s.str().c_str()); - vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_BSY, SYMBOL_PIN_BSY); - vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_SEL, SYMBOL_PIN_SEL); - vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_CD, SYMBOL_PIN_CD); - vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_IO, SYMBOL_PIN_IO); - vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_MSG, SYMBOL_PIN_MSG); - vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_REQ, SYMBOL_PIN_REQ); - vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_ACK, SYMBOL_PIN_ACK); - vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_ATN, SYMBOL_PIN_ATN); - vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_RST, SYMBOL_PIN_RST); - vcd_output_if_changed_byte(fp, data_buffer[i].data, PIN_DT0, SYMBOL_PIN_DAT); - vcd_output_if_changed_phase(fp, data_buffer[i].data, PIN_PHASE, SYMBOL_PIN_PHASE); - i++; - } - fclose(fp); + return true; } void Cleanup() { - LOGINFO("Stopping data collection...."); - create_value_change_dump(); + if (!import_data) + { + LOGINFO("Stopping data collection...."); + } + LOGINFO(" "); + LOGINFO("Generating %s...", vcd_file_name); + scsimon_generate_value_change_dump(vcd_file_name, data_buffer, data_idx); + LOGINFO("Generating %s...", json_file_name); + scsimon_generate_json(json_file_name, data_buffer, data_idx); + LOGINFO("Generating %s...", html_file_name); + scsimon_generate_html(html_file_name, data_buffer, data_idx); - // Cleanup the Bus - bus->Cleanup(); - - delete bus; + if (bus) + { + // Cleanup the Bus + bus->Cleanup(); + delete bus; + } } void Reset() { - // Reset the bus - bus->Reset(); + // Reset the bus + bus->Reset(); } //--------------------------------------------------------------------------- // -// Pin the thread to a specific CPU +// Pin the thread to a specific CPU (Only applies to Linux) // //--------------------------------------------------------------------------- +#ifdef __linux__ void FixCpu(int cpu) { - // Get the number of CPUs - cpu_set_t cpuset; - CPU_ZERO(&cpuset); - sched_getaffinity(0, sizeof(cpu_set_t), &cpuset); - int cpus = CPU_COUNT(&cpuset); + // Get the number of CPUs + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + sched_getaffinity(0, sizeof(cpu_set_t), &cpuset); + int cpus = CPU_COUNT(&cpuset); - // Set the thread affinity - if (cpu < cpus) { - CPU_ZERO(&cpuset); - CPU_SET(cpu, &cpuset); - sched_setaffinity(0, sizeof(cpu_set_t), &cpuset); - } + // Set the thread affinity + if (cpu < cpus) + { + CPU_ZERO(&cpuset); + CPU_SET(cpu, &cpuset); + sched_setaffinity(0, sizeof(cpu_set_t), &cpuset); + } } +#endif #ifdef DEBUG static DWORD high_bits = 0x0; static DWORD low_bits = 0xFFFFFFFF; -#endif +#endif //--------------------------------------------------------------------------- // // Main processing // //--------------------------------------------------------------------------- -int main(int argc, char* argv[]) +int main(int argc, char *argv[]) { - ostringstream s; - - #ifdef DEBUG - DWORD prev_high = high_bits; - DWORD prev_low = low_bits; -#endif - DWORD prev_sample = 0xFFFFFFFF; - DWORD this_sample = 0; - struct sched_param schparam; - timeval start_time; - timeval stop_time; - uint64_t loop_count = 0; - timeval time_diff; - uint64_t elapsed_us; - int str_len; - - // If there is an argument specified and it is NOT -h or --help - if((argc > 1) && (strcmp(argv[1], "-h")) && (strcmp(argv[1], "--help"))){ - str_len = strlen(argv[1]); - if ((str_len >= 1) && (str_len < _MAX_FNAME)) - { - strncpy(log_file_name, argv[1], sizeof(log_file_name)); - // Append .vcd if its not already there - if((str_len < 4) || strcasecmp(log_file_name + (str_len - 4), ".vcd")) { - strcat(log_file_name, ".vcd"); - } - } - else - { - printf("Invalid log name specified. Using log.vcd"); - } - } #ifdef DEBUG spdlog::set_level(spdlog::level::trace); @@ -367,53 +292,98 @@ int main(int argc, char* argv[]) spdlog::set_level(spdlog::level::info); #endif spdlog::set_pattern("%^[%l]%$ %v"); - // Output the Banner - Banner(argc, argv); - memset(data_buffer,0,sizeof(data_buffer)); - // Initialize - int ret = 0; - if (!Init()) { - ret = EPERM; - goto init_exit; - } + print_copyright_text(argc, argv); + parse_arguments(argc, argv); - // Reset - Reset(); +#ifdef DEBUG + DWORD prev_high = high_bits; + DWORD prev_low = low_bits; +#endif + ostringstream s; + DWORD prev_sample = 0xFFFFFFFF; + DWORD this_sample = 0; + timeval start_time; + timeval stop_time; + uint64_t loop_count = 0; + timeval time_diff; + uint64_t elapsed_us; + if (print_help) + { + print_help_text(argc, argv); + exit(0); + } + + // Output the Banner + Banner(argc, argv); + + data_buffer = (data_capture *)malloc(sizeof(data_capture_t) * buff_size); + bzero(data_buffer, sizeof(data_capture_t) * buff_size); + + if (import_data) + { + data_idx = scsimon_read_json(input_file_name, data_buffer, buff_size); + if (data_idx > 0) + { + LOGDEBUG("Read %d samples from %s", data_idx, input_file_name); + Cleanup(); + } + exit(0); + } + + LOGINFO(" "); + LOGINFO("Now collecting data.... Press CTRL-C to stop.") + LOGINFO(" "); + + // Initialize + int ret = 0; + if (!Init()) + { + ret = EPERM; + goto init_exit; + } + + // Reset + Reset(); + +#ifdef __linux__ // Set the affinity to a specific processor core - FixCpu(3); + FixCpu(3); - // Scheduling policy setting (highest priority) - schparam.sched_priority = sched_get_priority_max(SCHED_FIFO); - sched_setscheduler(0, SCHED_FIFO, &schparam); + // Scheduling policy setting (highest priority) + struct sched_param schparam; + schparam.sched_priority = sched_get_priority_max(SCHED_FIFO); + sched_setscheduler(0, SCHED_FIFO, &schparam); +#endif - // Start execution - running = true; - bus->SetACT(FALSE); + // Start execution + running = true; + bus->SetACT(FALSE); (void)gettimeofday(&start_time, NULL); - LOGDEBUG("ALL_SCSI_PINS %08X\n",ALL_SCSI_PINS); + LOGDEBUG("ALL_SCSI_PINS %08X\n", ALL_SCSI_PINS); // Main Loop - while (running) { - // Work initialization - this_sample = (bus->Aquire() & ALL_SCSI_PINS); + while (running) + { + // Work initialization + this_sample = (bus->Aquire() & ALL_SCSI_PINS); loop_count++; - if (loop_count > LLONG_MAX -1) + if (loop_count > LLONG_MAX - 1) { LOGINFO("Maximum amount of time has elapsed. SCSIMON is terminating."); - running=false; + running = false; } - if (data_idx >= (MAX_BUFF_SIZE-2)) + if (data_idx >= (buff_size - 2)) { LOGINFO("Internal data buffer is full. SCSIMON is terminating."); - running=false; + running = false; } - if (this_sample != prev_sample) - { + if (this_sample != prev_sample) + { #ifdef DEBUG // This is intended to be a debug check to see if every pin is set @@ -422,27 +392,28 @@ int main(int argc, char* argv[]) low_bits &= this_sample; if ((high_bits != prev_high) || (low_bits != prev_low)) { - LOGDEBUG(" %08X %08X\n",high_bits, low_bits); + LOGDEBUG(" %08X %08X\n", high_bits, low_bits); } prev_high = high_bits; prev_low = low_bits; - if((data_idx % 1000) == 0){ - s.str(""); - s << "Collected " << data_idx << " samples..."; - LOGDEBUG("%s", s.str().c_str()); + if ((data_idx % 1000) == 0) + { + s.str(""); + s << "Collected " << data_idx << " samples..."; + LOGDEBUG("%s", s.str().c_str()); } #endif data_buffer[data_idx].data = this_sample; data_buffer[data_idx].timestamp = loop_count; data_idx++; prev_sample = this_sample; - } + } - continue; - } + continue; + } // Collect one last sample, otherwise it looks like the end of the data was cut off - if (data_idx < MAX_BUFF_SIZE) + if (data_idx < buff_size) { data_buffer[data_idx].data = this_sample; data_buffer[data_idx].timestamp = loop_count; @@ -453,7 +424,7 @@ int main(int argc, char* argv[]) timersub(&stop_time, &start_time, &time_diff); - elapsed_us = ((time_diff.tv_sec*1000000) + time_diff.tv_usec); + elapsed_us = ((time_diff.tv_sec * 1000000) + time_diff.tv_usec); s.str(""); s << "Elapsed time: " << elapsed_us << " microseconds (" << elapsed_us / 1000000 << " seconds)"; LOGINFO("%s", s.str().c_str()); @@ -461,14 +432,14 @@ int main(int argc, char* argv[]) s << "Collected " << data_idx << " changes"; LOGINFO("%s", s.str().c_str()); - // Note: ns_per_loop is a global variable that is used by Cleanup() to printout the timestamps. + // Note: ns_per_loop is a global variable that is used by Cleanup() to printout the timestamps. ns_per_loop = (elapsed_us * 1000) / (double)loop_count; s.str(""); s << "Read the SCSI bus " << loop_count << " times with an average of " << ns_per_loop << " ns for each read"; LOGINFO("%s", s.str().c_str()); - Cleanup(); + Cleanup(); init_exit: - exit(ret); + exit(ret); }